--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hooks/bookmark.py Fri Aug 14 11:14:10 2009 +0200
@@ -0,0 +1,31 @@
+"""bookmark related hooks
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
+__docformat__ = "restructuredtext en"
+
+from cubicweb.selectors import entity_implements
+from cubicweb.server import hook
+
+
+class AutoDeleteBookmarkOp(hook.Operation):
+ bookmark = None # make pylint happy
+ def precommit_event(self):
+ if not self.session.deleted_in_transaction(self.bookmark.eid):
+ if not self.bookmark.bookmarked_by:
+ self.bookmark.delete()
+
+
+class DelBookmarkedByHook(hook.Hook):
+ """ensure user logins are stripped"""
+ __id__ = 'autodelbookmark'
+ __select__ = hook.Hook.__select__ & entity_implements('bookmarked_by',)
+ category = 'bookmark'
+ events = ('after_delete_relation',)
+
+ def __call__(self):
+ AutoDeleteBookmarkOp(self.cw_req,
+ bookmark=self.cw_req.entity_from_eid(self.eidfrom))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hooks/email.py Fri Aug 14 11:14:10 2009 +0200
@@ -0,0 +1,62 @@
+"""hooks to ensure use_email / primary_email relations consistency
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
+__docformat__ = "restructuredtext en"
+
+from cubicweb.server import hook
+
+class SetUseEmailRelationOp(hook.Operation):
+ """delay this operation to commit to avoid conflict with a late rql query
+ already setting the relation
+ """
+ rtype = 'use_email'
+ entity = email = None # make pylint happy
+
+ def condition(self):
+ """check entity has use_email set for the email address"""
+ return not any(e for e in self.entity.use_email
+ if self.email.eid == e.eid)
+
+ def precommit_event(self):
+ if self.condition():
+ self.session.unsafe_execute(
+ 'SET X %s Y WHERE X eid %%(x)s, Y eid %%(y)s' % self.rtype,
+ {'x': self.entity.eid, 'y': self.email.eid}, 'x')
+
+class SetPrimaryEmailRelationOp(SetUseEmailRelationOp):
+ rtype = 'primary_email'
+
+ def condition(self):
+ """check entity has no primary_email set"""
+ return not self.entity.primary_email
+
+
+class SetPrimaryEmailHook(hook.Hook):
+ """notify when a bug or story or version has its state modified"""
+ __id__ = 'setprimaryemail'
+ __select__ = hook.Hook.__select__ & hook.match_rtype('use_email')
+ category = 'email'
+ events = ('after_add_relation',)
+
+ def call(self, session, eidfrom, rtype, eidto):
+ entity = self.cw_req.entity_from_eid(self.eidfrom)
+ if 'primary_email' in entity.e_schema.subject_relations():
+ SetPrimaryEmailRelationOp(self.cw_req, entity=entity,
+ email=self.cw_req.entity_from_eid(self.eidto))
+
+class SetUseEmailHook(hook.Hook):
+ """notify when a bug or story or version has its state modified"""
+ __id__ = 'setprimaryemail'
+ __select__ = hook.Hook.__select__ & hook.match_rtype('primary_email')
+ category = 'email'
+ events = ('after_add_relation',)
+
+ def __call__(self):
+ entity = self.cw_req.entity_from_eid(self.eidfrom)
+ if 'use_email' in entity.e_schema.subject_relations():
+ SetUseEmailRelationOp(self.cw_req, entity=entity,
+ email=self.cw_req.entity_from_eid(self.eidto))
--- a/hooks/integrity.py Fri Aug 14 11:13:18 2009 +0200
+++ b/hooks/integrity.py Fri Aug 14 11:14:10 2009 +0200
@@ -10,9 +10,9 @@
from cubicweb import ValidationError
from cubicweb.selectors import entity_implements
-from cubicweb.server.hook import Hook
+from cubicweb.common.uilib import soup2xhtml
+from cubicweb.server import hook
from cubicweb.server.pool import LateOperation, PreCommitOperation
-from cubicweb.server.hookhelper import rproperty
# special relations that don't have to be checked for integrity, usually
# because they are handled internally by hooks (so we trust ourselves)
@@ -23,7 +23,7 @@
'wf_info_for', 'from_state', 'to_state'))
-class _CheckRequiredRelationOperation(LateOperation):
+class _CheckRequiredRelationOperation(hook.LateOperation):
"""checking relation cardinality has to be done after commit in
case the relation is being replaced
"""
@@ -31,7 +31,7 @@
def precommit_event(self):
# recheck pending eids
- if self.eid in self.session.transaction_data.get('pendingeids', ()):
+ if self.session.deleted_in_transaction(self.eid):
return
if self.session.unsafe_execute(*self._rql()).rowcount < 1:
etype = self.session.describe(self.eid)[0]
@@ -59,10 +59,14 @@
return 'Any S WHERE O eid %%(x)s, S %s O' % self.rtype, {'x': self.eid}, 'x'
-class CheckCardinalityHook(Hook):
+class IntegrityHook(hook.Hook):
+ __abstract__ = True
+ category = 'integrity'
+
+
+class CheckCardinalityHook(IntegrityHook):
"""check cardinalities are satisfied"""
__id__ = 'checkcard'
- category = 'integrity'
events = ('after_add_entity', 'before_delete_relation')
def __call__(self):
@@ -103,28 +107,26 @@
return
session = self.cw_req
eidfrom, eidto = self.eidfrom, self.eidto
- card = rproperty(session, rtype, eidfrom, eidto, 'cardinality')
+ card = session.schema_rproperty(rtype, eidfrom, eidto, 'cardinality')
pendingrdefs = session.transaction_data.get('pendingrdefs', ())
if (session.describe(eidfrom)[0], rtype, session.describe(eidto)[0]) in pendingrdefs:
return
- pendingeids = session.transaction_data.get('pendingeids', ())
- if card[0] in '1+' and not eidfrom in pendingeids:
+ if card[0] in '1+' and not session.deleted_in_transaction(eidfrom):
self.checkrel_if_necessary(_CheckSRelationOp, rtype, eidfrom)
- if card[1] in '1+' and not eidto in pendingeids:
+ if card[1] in '1+' and not session.deleted_in_transaction(eidto):
self.checkrel_if_necessary(_CheckORelationOp, rtype, eidto)
-class _CheckConstraintsOp(LateOperation):
+class _CheckConstraintsOp(hook.LateOperation):
"""check a new relation satisfy its constraints
"""
def precommit_event(self):
eidfrom, rtype, eidto = self.rdef
# first check related entities have not been deleted in the same
# transaction
- pending = self.session.transaction_data.get('pendingeids', ())
- if eidfrom in pending:
+ if self.session.deleted_in_transaction(eidfrom):
return
- if eidto in pending:
+ if self.session.deleted_in_transaction(eidto):
return
for constraint in self.constraints:
try:
@@ -137,25 +139,25 @@
pass
-class CheckConstraintHook(Hook):
+class CheckConstraintHook(IntegrityHook):
"""check the relation satisfy its constraints
this is delayed to a precommit time operation since other relation which
will make constraint satisfied may be added later.
"""
__id__ = 'checkconstraint'
- category = 'integrity'
events = ('after_add_relation',)
+
def __call__(self):
- constraints = rproperty(self.cw_req, self.rtype, self.eidfrom, self.eidto,
+ constraints = self.cw_req.schema_rproperty(self.rtype, self.eidfrom, self.eidto,
'constraints')
if constraints:
_CheckConstraintsOp(self.cw_req, constraints=constraints,
rdef=(self.eidfrom, self.rtype, self.eidto))
-class CheckUniqueHook(Hook):
+
+class CheckUniqueHook(IntegrityHook):
__id__ = 'checkunique'
- category = 'integrity'
events = ('before_add_entity', 'before_update_entity')
def __call__(self):
@@ -174,7 +176,7 @@
raise ValidationError(entity.eid, {attr: msg % val})
-class _DelayedDeleteOp(PreCommitOperation):
+class _DelayedDeleteOp(hook.Operation):
"""delete the object of composite relation except if the relation
has actually been redirected to another composite
"""
@@ -182,23 +184,23 @@
def precommit_event(self):
session = self.session
# don't do anything if the entity is being created or deleted
- if not (self.eid in session.transaction_data.get('pendingeids', ()) or
- self.eid in session.transaction_data.get('neweids', ())):
+ if not (session.deleted_in_transaction(self.eid) or
+ session.added_in_transaction(self.eid)):
etype = session.describe(self.eid)[0]
session.unsafe_execute('DELETE %s X WHERE X eid %%(x)s, NOT %s'
% (etype, self.relation),
{'x': self.eid}, 'x')
-class DeleteCompositeOrphanHook(Hook):
+class DeleteCompositeOrphanHook(IntegrityHook):
"""delete the composed of a composite relation when this relation is deleted
"""
__id__ = 'deletecomposite'
- category = 'integrity'
events = ('before_delete_relation',)
+
def __call__(self):
- composite = rproperty(self.cw_req, self.rtype, self.eidfrom, self.eidto,
- 'composite')
+ composite = self.cw_req.schema_rproperty(self.rtype, self.eidfrom, self.eidto,
+ 'composite')
if composite == 'subject':
_DelayedDeleteOp(self.cw_req, eid=self.eidto,
relation='Y %s X' % self.rtype)
@@ -207,12 +209,11 @@
relation='X %s Y' % self.rtype)
-class DontRemoveOwnersGroupHook(Hook):
+class DontRemoveOwnersGroupHook(IntegrityHook):
"""delete the composed of a composite relation when this relation is deleted
"""
__id__ = 'checkownersgroup'
- __select__ = Hook.__select__ & entity_implements('CWGroup')
- category = 'integrity'
+ __select__ = IntegrityHook.__select__ & entity_implements('CWGroup')
events = ('before_delete_entity', 'before_update_entity')
def __call__(self):
@@ -226,3 +227,31 @@
self.entity['name'] = newname
+class TidyHtmlFields(IntegrityHook):
+ """tidy HTML in rich text strings"""
+ __id__ = 'htmltidy'
+ events = ('before_add_entity', 'before_update_entity')
+
+ def __call__(self):
+ entity = self.entity
+ metaattrs = entity.e_schema.meta_attributes()
+ for metaattr, (metadata, attr) in metaattrs.iteritems():
+ if metadata == 'format' and attr in entity.edited_attributes:
+ try:
+ value = entity[attr]
+ except KeyError:
+ continue # no text to tidy
+ if isinstance(value, unicode): # filter out None and Binary
+ if getattr(entity, str(metaattr)) == 'text/html':
+ entity[attr] = soup2xhtml(value, self.cw_req.encoding)
+
+
+class StripCWUserLoginHook(IntegrityHook):
+ """ensure user logins are stripped"""
+ __id__ = 'stripuserlogin'
+ __select__ = IntegrityHook.__select__ & entity_implements('CWUser')
+ events = ('before_add_entity', 'before_update_entity',)
+
+ def call(self, session, entity):
+ if 'login' in entity.edited_attributes and entity['login']:
+ entity['login'] = entity['login'].strip()
--- a/hooks/metadata.py Fri Aug 14 11:13:18 2009 +0200
+++ b/hooks/metadata.py Fri Aug 14 11:14:10 2009 +0200
@@ -11,9 +11,7 @@
from datetime import datetime
from cubicweb.selectors import entity_implements
-from cubicweb.server.hook import Hook
-from cubicweb.server.pool import Operation, LateOperation, PreCommitOperation
-from cubicweb.server.hookhelper import rproperty
+from cubicweb.server import hook
from cubicweb.server.repository import FTIndexEntityOp
@@ -28,14 +26,18 @@
return eschema.eid
-class InitMetaAttrsHook(Hook):
+class MetaDataHook(hook.Hook):
+ __abstract__ = True
+ category = 'metadata'
+
+
+class InitMetaAttrsHook(MetaDataHook):
"""before create a new entity -> set creation and modification date
this is a conveniency hook, you shouldn't have to disable it
"""
- id = 'metaattrsinit'
+ __id__ = 'metaattrsinit'
events = ('before_add_entity',)
- category = 'metadata'
def __call__(self):
timestamp = datetime.now()
@@ -46,31 +48,31 @@
self.entity.setdefault('cwuri', cwuri)
-class UpdateMetaAttrsHook(Hook):
+class UpdateMetaAttrsHook(MetaDataHook):
"""update an entity -> set modification date"""
- id = 'metaattrsupdate'
+ __id__ = 'metaattrsupdate'
events = ('before_update_entity',)
- category = 'metadata'
+
def __call__(self):
self.entity.setdefault('modification_date', datetime.now())
-class _SetCreatorOp(PreCommitOperation):
+class _SetCreatorOp(hook.Operation):
def precommit_event(self):
session = self.session
- if self.entity.eid in session.transaction_data.get('pendingeids', ()):
+ if session.deleted_in_transaction(self.entity.eid):
# entity have been created and deleted in the same transaction
return
if not self.entity.created_by:
session.add_relation(self.entity.eid, 'created_by', session.user.eid)
-class SetIsHook(Hook):
+class SetIsHook(MetaDataHook):
"""create a new entity -> set is relation"""
- id = 'setis'
+ __id__ = 'setis'
events = ('after_add_entity',)
- category = 'metadata'
+
def __call__(self):
if hasattr(self.entity, '_cw_recreating'):
return
@@ -87,11 +89,11 @@
eschema_type_eid(session, etype))
-class SetOwnershipHook(Hook):
+class SetOwnershipHook(MetaDataHook):
"""create a new entity -> set owner and creator metadata"""
- id = 'setowner'
+ __id__ = 'setowner'
events = ('after_add_entity',)
- category = 'metadata'
+
def __call__(self):
asession = self.cw_req.actual_session()
if not asession.is_internal_session:
@@ -99,48 +101,48 @@
_SetCreatorOp(asession, entity=self.entity)
-class _SyncOwnersOp(PreCommitOperation):
+class _SyncOwnersOp(hook.Operation):
def precommit_event(self):
self.session.unsafe_execute('SET X owned_by U WHERE C owned_by U, C eid %(c)s,'
'NOT EXISTS(X owned_by U, X eid %(x)s)',
{'c': self.compositeeid, 'x': self.composedeid},
('c', 'x'))
-class SyncCompositeOwner(Hook):
+
+class SyncCompositeOwner(MetaDataHook):
"""when adding composite relation, the composed should have the same owners
has the composite
"""
- id = 'synccompositeowner'
+ __id__ = 'synccompositeowner'
events = ('after_add_relation',)
- category = 'metadata'
+
def __call__(self):
if self.rtype == 'wf_info_for':
# skip this special composite relation # XXX (syt) why?
return
eidfrom, eidto = self.eidfrom, self.eidto
- composite = rproperty(self.cw_req, self.rtype, eidfrom, eidto, 'composite')
+ composite = self.cw_req.schema_rproperty(self.rtype, eidfrom, eidto, 'composite')
if composite == 'subject':
_SyncOwnersOp(self.cw_req, compositeeid=eidfrom, composedeid=eidto)
elif composite == 'object':
_SyncOwnersOp(self.cw_req, compositeeid=eidto, composedeid=eidfrom)
-class FixUserOwnershipHook(Hook):
+class FixUserOwnershipHook(MetaDataHook):
"""when a user has been created, add owned_by relation on itself"""
- id = 'fixuserowner'
- __select__ = Hook.__select__ & entity_implements('CWUser')
+ __id__ = 'fixuserowner'
+ __select__ = MetaDataHook.__select__ & entity_implements('CWUser')
events = ('after_add_entity',)
- category = 'metadata'
+
def __call__(self):
self.cw_req.add_relation(self.entity.eid, 'owned_by', self.entity.eid)
-class UpdateFTIHook(Hook):
+class UpdateFTIHook(MetaDataHook):
"""sync fulltext index when relevant relation is added / removed
"""
- id = 'updateftirel'
+ __id__ = 'updateftirel'
events = ('after_add_relation', 'after_delete_relation')
- category = 'metadata'
def __call__(self):
rtype = self.rtype
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hooks/notification.py Fri Aug 14 11:14:10 2009 +0200
@@ -0,0 +1,133 @@
+"""some hooks to handle notification on entity's changes
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
+__docformat__ = "restructuredtext en"
+
+from logilab.common.textutils import normalize_text
+
+from cubicweb import RegistryException
+from cubicweb.selectors import entity_implements
+from cubicweb.server import hook
+
+
+class RenderAndSendNotificationView(hook.Operation):
+ """delay rendering of notification view until precommit"""
+ def precommit_event(self):
+ view = self.view
+ if view.cw_rset and self.session.deleted_in_transaction(view.cw_rset[cw_rset.cw_row or 0][cw_rset.cw_col or 0]):
+ return # entity added and deleted in the same transaction
+ self.view.render_and_send(**getattr(self, 'viewargs', {}))
+
+
+class NotificationHook(hook.Hook):
+ __abstract__ = True
+ category = 'notification'
+
+ def select_view(self, vid, rset, row=0, col=0):
+ return self.cw_req.vreg['views'].select_object(vid, self.cw_req,
+ rset=rset, row=0, col=0)
+
+
+class StatusChangeHook(NotificationHook):
+ """notify when a workflowable entity has its state modified"""
+ __id__ = 'notifystatuschange'
+ __select__ = NotificationHook.__select__ & entity_implements('TrInfo')
+ events = ('after_add_entity',)
+
+ def __call__(self):
+ entity = self.entity
+ if not entity.from_state: # not a transition
+ return
+ rset = entity.related('wf_info_for')
+ view = self.select_view('notif_status_change', rset=rset, row=0)
+ if view is None:
+ return
+ comment = entity.printable_value('comment', format='text/plain')
+ if comment:
+ comment = normalize_text(comment, 80,
+ rest=entity.comment_format=='text/rest')
+ RenderAndSendNotificationView(self.cw_req, view=view, viewargs={
+ 'comment': comment, 'previous_state': entity.previous_state.name,
+ 'current_state': entity.new_state.name})
+
+
+class RelationChangeHook(NotificationHook):
+ __id__ = 'notifyrelationchange'
+ events = ('before_add_relation', 'after_add_relation',
+ 'before_delete_relation', 'after_delete_relation')
+
+ def __call__(self):
+ """if a notification view is defined for the event, send notification
+ email defined by the view
+ """
+ rset = self.cw_req.eid_rset(self.eidfrom)
+ view = self.select_view('notif_%s_%s' % (self.event, self.rtype),
+ rset=rset, row=0)
+ if view is None:
+ return
+ RenderAndSendNotificationView(self.cw_req, view=view)
+
+
+class EntityChangeHook(NotificationHook):
+ """if a notification view is defined for the event, send notification
+ email defined by the view
+ """
+ __id__ = 'notifyentitychange'
+ events = ('after_add_entity', 'after_update_entity')
+
+ def __call__(self):
+ rset = self.entity.as_rset()
+ view = self.select_view('notif_%s' % self.event, rset=rset, row=0)
+ if view is None:
+ return
+ RenderAndSendNotificationView(self.cw_req, view=view)
+
+
+# supervising ##################################################################
+
+class SomethingChangedHook(NotificationHook):
+ __id__ = 'supervising'
+ events = ('before_add_relation', 'before_delete_relation',
+ 'after_add_entity', 'before_update_entity')
+
+ def __call__(self):
+ dest = self.cw_req.vreg.config['supervising-addrs']
+ if not dest: # no supervisors, don't do this for nothing...
+ return
+ if self._call():
+ SupervisionMailOp(self.cw_req)
+
+ def _call(self):
+ event = self.event.split('_', 1)[1]
+ if event == 'update_entity':
+ if self.cw_req.added_in_transaction(self.entity.eid):
+ return False
+ if self.entity.e_schema == 'CWUser':
+ if not (self.entity.edited_attributes - frozenset(('eid', 'modification_date',
+ 'last_login_time'))):
+ # don't record last_login_time update which are done
+ # automatically at login time
+ return False
+ self.cw_req.transaction_data.setdefault('pendingchanges', []).append(
+ (event, self))
+ return True
+
+
+class EntityDeleteHook(SomethingChangedHook):
+ __id__ = 'supervisingentitydel'
+ events = ('before_delete_entity',)
+
+ def _call(self):
+ try:
+ title = self.entity.dc_title()
+ except:
+ # may raise an error during deletion process, for instance due to
+ # missing required relation
+ title = '#%s' % eid
+ self.cw_req.transaction_data.setdefault('pendingchanges', []).append(
+ ('delete_entity', (self.eid, str(self.entity.e_schema), title)))
+ return True
--- a/hooks/syncschema.py Fri Aug 14 11:13:18 2009 +0200
+++ b/hooks/syncschema.py Fri Aug 14 11:14:10 2009 +0200
@@ -453,7 +453,7 @@
# when the relation is added in the same transaction, the constraint
# object is created by the operation adding the attribute or relation,
# so there is nothing to do here
- if rdef.eid in session.transaction_data.get('neweids', ()):
+ if session.added_in_transaction(rdef.eid):
return
subjtype, rtype, objtype = session.schema.schema_by_eid(rdef.eid)
cstrtype = self.entity.type
@@ -602,7 +602,7 @@
# when the relation is added in the same transaction, the constraint
# object is created by the operation adding the attribute or relation,
# so there is nothing to do here
- if rdef.eid in self.session.transaction_data.get('neweids', ()):
+ if session.added_in_transaction(rdef.eid):
self.cancelled = True
return
subjtype, rtype, objtype = self.session.schema.schema_by_eid(rdef.eid)
@@ -729,17 +729,20 @@
erschema.set_rqlexprs(self.perm, rqlexprs)
-# deletion hooks ###############################################################
+class SyncSchemaHook(hook.Hook):
+ __abstract__ = True
+ category = 'syncschema'
-class DelCWETypeHook(hook.Hook):
+# CWEType hooks ################################################################
+
+class DelCWETypeHook(SyncSchemaHook):
"""before deleting a CWEType entity:
* check that we don't remove a core entity type
* cascade to delete related CWAttribute and CWRelation entities
* instantiate an operation to delete the entity type on commit
"""
__id__ = 'syncdelcwetype'
- __select__ = hook.Hook.__select__ & entity_implements('CWEType')
- category = 'syncschema'
+ __select__ = SyncSchemaHook.__select__ & entity_implements('CWEType')
events = ('before_delete_entity',)
def __call__(self):
@@ -831,16 +834,19 @@
MemSchemaCWETypeRename(self.cw_req, oldname=oldname, newname=newname)
entity['name'] = newname
-class DelCWRTypeHook(hook.Hook):
+
+# CWRType hooks ################################################################
+
+class DelCWRTypeHook(SyncSchemaHook):
"""before deleting a CWRType entity:
* check that we don't remove a core relation type
* cascade to delete related CWAttribute and CWRelation entities
* instantiate an operation to delete the relation type on commit
"""
__id__ = 'syncdelcwrtype'
- __select__ = hook.Hook.__select__ & entity_implements('CWRType')
- category = 'syncschema'
+ __select__ = SyncSchemaHook.__select__ & entity_implements('CWRType')
events = ('before_delete_entity',)
+
def __call__(self):
name = self.entity.name
if name in CORE_ETYPES:
@@ -900,8 +906,9 @@
entity=entity)
+# relation_type hooks ##########################################################
-class AfterDelRelationTypeHook(hook.Hook):
+class AfterDelRelationTypeHook(SyncSchemaHook):
"""before deleting a CWAttribute or CWRelation entity:
* if this is a final or inlined relation definition, instantiate an
operation to drop necessary column, else if this is the last instance
@@ -911,8 +918,7 @@
* delete the associated relation type when necessary
"""
__id__ = 'syncdelrelationtype'
- __select__ = hook.Hook.__select__ & hook.match_rtype('relation_type')
- category = 'syncschema'
+ __select__ = SyncSchemaHook.__select__ & hook.match_rtype('relation_type')
events = ('after_delete_relation',)
def __call__(self):
@@ -954,10 +960,11 @@
MemSchemaRDefDel(session, (subjschema, rschema, objschema))
-class AfterAddCWAttributeHook(hook.Hook):
+# CWAttribute / CWRelation hooks ###############################################
+
+class AfterAddCWAttributeHook(SyncSchemaHook):
__id__ = 'syncaddcwattribute'
- __select__ = hook.Hook.__select__ & entity_implements('CWAttribute')
- category = 'syncschema'
+ __select__ = SyncSchemaHook.__select__ & entity_implements('CWAttribute')
events = ('after_add_entity',)
def __call__(self):
@@ -966,21 +973,21 @@
class AfterAddCWRelationHook(AfterAddCWAttributeHook):
__id__ = 'syncaddcwrelation'
- __select__ = hook.Hook.__select__ & entity_implements('CWRelation')
+ __select__ = SyncSchemaHook.__select__ & entity_implements('CWRelation')
def __call__(self):
SourceDbCWRelationAdd(self.cw_req, entity=self.entity)
-class AfterUpdateCWRDefHook(hook.Hook):
+class AfterUpdateCWRDefHook(SyncSchemaHook):
__id__ = 'syncaddcwattribute'
- __select__ = hook.Hook.__select__ & entity_implements('CWAttribute', 'CWRelation')
- category = 'syncschema'
+ __select__ = SyncSchemaHook.__select__ & entity_implements('CWAttribute',
+ 'CWRelation')
events = ('after_update_entity',)
def __call__(self):
entity = self.entity
- if entity.eid in self.cw_req.transaction_data.get('pendingeids', ()):
+ if self.cw_req.deleted_in_transaction(entity.eid):
return
desttype = entity.otype.name
rschema = self.cw_req.schema[entity.rtype.name]
@@ -1002,10 +1009,9 @@
# constraints synchronization hooks ############################################
-class AfterAddCWConstraintHook(hook.Hook):
+class AfterAddCWConstraintHook(SyncSchemaHook):
__id__ = 'syncaddcwconstraint'
- __select__ = hook.Hook.__select__ & entity_implements('CWConstraint')
- category = 'syncschema'
+ __select__ = SyncSchemaHook.__select__ & entity_implements('CWConstraint')
events = ('after_add_entity', 'after_update_entity')
def __call__(self):
@@ -1013,14 +1019,13 @@
SourceDbCWConstraintAdd(self.cw_req, entity=self.entity)
-class AfterAddConstrainedByHook(hook.Hook):
+class AfterAddConstrainedByHook(SyncSchemaHook):
__id__ = 'syncdelconstrainedby'
- __select__ = hook.Hook.__select__ & hook.match_rtype('constrainted_by')
- category = 'syncschema'
+ __select__ = SyncSchemaHook.__select__ & hook.match_rtype('constrainted_by')
events = ('after_add_relation',)
def __call__(self):
- if self.eidfrom in self.cw_req.transaction_data.get('neweids', ()):
+ if self.cw_req.added_in_transaction(self.eidfrom):
self.cw_req.transaction_data.setdefault(self.eidfrom, []).append(self.eidto)
@@ -1029,7 +1034,7 @@
events = ('before_delete_relation',)
def __call__(self):
- if self.eidfrom in self.cw_req.transaction_data.get('pendingeids', ()):
+ if self.cw_req.deleted_in_transaction(self.eidfrom):
return
schema = self.cw_req.schema
entity = self.cw_req.entity_from_eid(self.eidto)
@@ -1049,13 +1054,12 @@
# permissions synchronization hooks ############################################
-class AfterAddPermissionHook(hook.Hook):
+class AfterAddPermissionHook(SyncSchemaHook):
"""added entity/relation *_permission, need to update schema"""
__id__ = 'syncaddperm'
- __select__ = hook.Hook.__select__ & hook.match_rtype(
+ __select__ = SyncSchemaHook.__select__ & hook.match_rtype(
'read_permission', 'add_permission', 'delete_permission',
'update_permission')
- category = 'syncschema'
events = ('after_add_relation',)
def __call__(self):
@@ -1076,7 +1080,7 @@
events = ('before_delete_relation',)
def __call__(self):
- if self.eidfrom in self.cw_req.transaction_data.get('pendingeids', ()):
+ if self.cw_req.deleted_in_transaction(self.eidfrom):
return
perm = self.rtype.split('_', 1)[0]
if self.cw_req.describe(self.eidto)[0] == 'CWGroup':
@@ -1087,10 +1091,9 @@
-class ModifySpecializesHook(hook.Hook):
+class ModifySpecializesHook(SyncSchemaHook):
__id__ = 'syncspecializes'
- __select__ = hook.Hook.__select__ & hook.match_rtype('specializes')
- category = 'syncschema'
+ __select__ = SyncSchemaHook.__select__ & hook.match_rtype('specializes')
events = ('after_add_relation', 'after_delete_relation')
def __call__(self):
--- a/hooks/syncsession.py Fri Aug 14 11:13:18 2009 +0200
+++ b/hooks/syncsession.py Fri Aug 14 11:14:10 2009 +0200
@@ -9,14 +9,23 @@
from cubicweb import UnknownProperty, ValidationError, BadConnectionId
from cubicweb.selectors import entity_implements
-from cubicweb.server.hook import Hook, match_rtype
-from cubicweb.server.pool import Operation
-from cubicweb.server.hookhelper import get_user_sessions
+from cubicweb.server import hook
+
+
+def get_user_sessions(repo, ueid):
+ for session in repo._sessions.values():
+ if ueid == session.user.eid:
+ yield session
+
+
+class SyncSessionHook(hook.Hook):
+ __abstract__ = True
+ category = 'syncsession'
# user/groups synchronisation #################################################
-class _GroupOperation(Operation):
+class _GroupOperation(hook.Operation):
"""base class for group operation"""
geid = None
def __init__(self, session, *args, **kwargs):
@@ -27,7 +36,7 @@
"""
rql = 'Any N WHERE G eid %(x)s, G name N'
result = session.execute(rql, {'x': kwargs['geid']}, 'x', build_descr=False)
- Operation.__init__(self, session, *args, **kwargs)
+ hook.Operation.__init__(self, session, *args, **kwargs)
self.group = result[0][0]
@@ -55,11 +64,10 @@
groups.add(self.group)
-class SyncInGroupHook(Hook):
+class SyncInGroupHook(SyncSessionHook):
__id__ = 'syncingroup'
- __select__ = Hook.__select__ & match_rtype('in_group')
+ __select__ = SyncSessionHook.__select__ & hook.match_rtype('in_group')
events = ('after_delete_relation', 'after_add_relation')
- category = 'syncsession'
def __call__(self):
if self.event == 'after_delete_relation':
@@ -70,11 +78,11 @@
opcls(self.cw_req, cnxuser=session.user, geid=self.eidto)
-class _DelUserOp(Operation):
+class _DelUserOp(hook.Operation):
"""close associated user's session when it is deleted"""
def __init__(self, session, cnxid):
self.cnxid = cnxid
- Operation.__init__(self, session)
+ hook.Operation.__init__(self, session)
def commit_event(self):
"""the observed connections pool has been commited"""
@@ -84,11 +92,10 @@
pass # already closed
-class CloseDeletedUserSessionsHook(Hook):
+class CloseDeletedUserSessionsHook(SyncSessionHook):
__id__ = 'closession'
- __select__ = Hook.__select__ & entity_implements('CWUser')
+ __select__ = SyncSessionHook.__select__ & entity_implements('CWUser')
events = ('after_delete_entity',)
- category = 'syncsession'
def __call__(self):
"""modify user permission, need to update users"""
@@ -99,7 +106,7 @@
# CWProperty hooks #############################################################
-class _DelCWPropertyOp(Operation):
+class _DelCWPropertyOp(hook.Operation):
"""a user's custom properties has been deleted"""
def commit_event(self):
@@ -110,7 +117,7 @@
self.error('%s has no associated value', self.key)
-class _ChangeCWPropertyOp(Operation):
+class _ChangeCWPropertyOp(hook.Operation):
"""a user's custom properties has been added/changed"""
def commit_event(self):
@@ -118,7 +125,7 @@
self.epropdict[self.key] = self.value
-class _AddCWPropertyOp(Operation):
+class _AddCWPropertyOp(hook.Operation):
"""a user's custom properties has been added/changed"""
def commit_event(self):
@@ -129,10 +136,9 @@
# if for_user is set, update is handled by a ChangeCWPropertyOp operation
-class AddCWPropertyHook(Hook):
+class AddCWPropertyHook(SyncSessionHook):
__id__ = 'addcwprop'
- __select__ = Hook.__select__ & entity_implements('CWProperty')
- category = 'syncsession'
+ __select__ = SyncSessionHook.__select__ & entity_implements('CWProperty')
events = ('after_add_entity',)
def __call__(self):
@@ -194,11 +200,10 @@
_DelCWPropertyOp(session, epropdict=session.vreg.eprop_values, key=entity.pkey)
-class AddForUserRelationHook(Hook):
+class AddForUserRelationHook(SyncSessionHook):
__id__ = 'addcwpropforuser'
- __select__ = Hook.__select__ & match_rtype('for_user')
+ __select__ = SyncSessionHook.__select__ & hook.match_rtype('for_user')
events = ('after_add_relation',)
- category = 'syncsession'
def __call__(self):
session = self.cw_req
--- a/hooks/workflow.py Fri Aug 14 11:13:18 2009 +0200
+++ b/hooks/workflow.py Fri Aug 14 11:14:10 2009 +0200
@@ -7,12 +7,32 @@
"""
__docformat__ = "restructuredtext en"
-from cubicweb import ValidationError
+from datetime import datetime
+
+from cubicweb import RepositoryError, ValidationError
from cubicweb.interfaces import IWorkflowable
from cubicweb.selectors import entity_implements
-from cubicweb.server.hook import Hook, match_rtype
-from cubicweb.server.pool import PreCommitOperation
-from cubicweb.server.hookhelper import previous_state
+from cubicweb.server import hook
+
+
+def previous_state(session, eid):
+ """return the state of the entity with the given eid,
+ usually since it's changing in the current transaction. Due to internal
+ relation hooks, the relation may has been deleted at this point, so
+ we have handle that
+ """
+ if session.added_in_transaction(eid):
+ return
+ pending = session.transaction_data.get('pendingrelations', ())
+ for eidfrom, rtype, eidto in reversed(pending):
+ if rtype == 'in_state' and eidfrom == eid:
+ rset = session.execute('Any S,N WHERE S eid %(x)s, S name N',
+ {'x': eidto}, 'x')
+ return rset.get_entity(0, 0)
+ rset = session.execute('Any S,N WHERE X eid %(x)s, X in_state S, S name N',
+ {'x': eid}, 'x')
+ if rset:
+ return rset.get_entity(0, 0)
def relation_deleted(session, eidfrom, rtype, eidto):
@@ -20,7 +40,7 @@
(eidfrom, rtype, eidto))
-class _SetInitialStateOp(PreCommitOperation):
+class _SetInitialStateOp(hook.Operation):
"""make initial state be a default state"""
def precommit_event(self):
@@ -28,29 +48,30 @@
entity = self.entity
# if there is an initial state and the entity's state is not set,
# use the initial state as a default state
- pendingeids = session.transaction_data.get('pendingeids', ())
- if not entity.eid in pendingeids and not entity.in_state:
+ if not session.deleted_in_transaction(entity.eid) and not entity.in_state:
rset = session.execute('Any S WHERE ET initial_state S, ET name %(name)s',
{'name': entity.id})
if rset:
session.add_relation(entity.eid, 'in_state', rset[0][0])
+class WorkflowHook(hook.Hook):
+ __abstract__ = True
+ category = 'worfklow'
-class SetInitialStateHook(Hook):
+
+class SetInitialStateHook(WorkflowHook):
__id__ = 'wfsetinitial'
- __select__ = Hook.__select__ & entity_implements(IWorkflowable)
- category = 'worfklow'
+ __select__ = WorkflowHook.__select__ & entity_implements(IWorkflowable)
events = ('after_add_entity',)
def __call__(self):
_SetInitialStateOp(self.cw_req, entity=self.entity)
-class PrepareStateChangeHook(Hook):
+class PrepareStateChangeHook(WorkflowHook):
"""record previous state information"""
__id__ = 'cwdelstate'
- __select__ = Hook.__select__ & match_rtype('in_state')
- category = 'worfklow'
+ __select__ = WorkflowHook.__select__ & hook.match_rtype('in_state')
events = ('before_delete_relation',)
def __call__(self):
@@ -104,3 +125,22 @@
args['fs'] = state.eid
rql = '%s WHERE %s' % (rql, ', '.join(restriction))
session.unsafe_execute(rql, args, 'e')
+
+
+class SetModificationDateOnStateChange(WorkflowHook):
+ """update entity's modification date after changing its state"""
+ __id__ = 'wfsyncmdate'
+ __select__ = WorkflowHook.__select__ & hook.match_rtype('in_state')
+ events = ('after_add_relation',)
+
+ def __call__(self):
+ if self.cw_req.added_in_transaction(self.eidfrom):
+ # new entity, not needed
+ return
+ entity = self.cw_req.entity_from_eid(self.eidfrom)
+ try:
+ entity.set_attributes(modification_date=datetime.now())
+ except RepositoryError, ex:
+ # usually occurs if entity is coming from a read-only source
+ # (eg ldap user)
+ self.warning('cant change modification date for %s: %s', entity, ex)
--- a/sobjects/email.py Fri Aug 14 11:13:18 2009 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,65 +0,0 @@
-"""hooks to ensure use_email / primary_email relations consistency
-
-:organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
-:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
-:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
-"""
-__docformat__ = "restructuredtext en"
-
-from cubicweb.server.hooksmanager import Hook
-from cubicweb.server.pool import PreCommitOperation
-
-class SetUseEmailRelationOp(PreCommitOperation):
- """delay this operation to commit to avoid conflict with a late rql query
- already setting the relation
- """
- rtype = 'use_email'
- fromeid = toeid = None # make pylint happy
-
- def condition(self):
- """check entity has use_email set for the email address"""
- return not self.session.unsafe_execute(
- 'Any X WHERE X eid %(x)s, X use_email Y, Y eid %(y)s',
- {'x': self.fromeid, 'y': self.toeid}, 'x')
-
- def precommit_event(self):
- session = self.session
- if self.condition():
- session.unsafe_execute(
- 'SET X %s Y WHERE X eid %%(x)s, Y eid %%(y)s' % self.rtype,
- {'x': self.fromeid, 'y': self.toeid}, 'x')
-
-class SetPrimaryEmailRelationOp(SetUseEmailRelationOp):
- rtype = 'primary_email'
-
- def condition(self):
- """check entity has no primary_email set"""
- return not self.session.unsafe_execute(
- 'Any X WHERE X eid %(x)s, X primary_email Y',
- {'x': self.fromeid}, 'x')
-
-
-class SetPrimaryEmailHook(Hook):
- """notify when a bug or story or version has its state modified"""
- events = ('after_add_relation',)
- accepts = ('use_email',)
-
- def call(self, session, fromeid, rtype, toeid):
- subjtype = session.describe(fromeid)[0]
- eschema = self.vreg.schema[subjtype]
- if 'primary_email' in eschema.subject_relations():
- SetPrimaryEmailRelationOp(session, vreg=self.vreg,
- fromeid=fromeid, toeid=toeid)
-
-class SetUseEmailHook(Hook):
- """notify when a bug or story or version has its state modified"""
- events = ('after_add_relation',)
- accepts = ('primary_email',)
-
- def call(self, session, fromeid, rtype, toeid):
- subjtype = session.describe(fromeid)[0]
- eschema = self.vreg.schema[subjtype]
- if 'use_email' in eschema.subject_relations():
- SetUseEmailRelationOp(session, vreg=self.vreg,
- fromeid=fromeid, toeid=toeid)
--- a/sobjects/hooks.py Fri Aug 14 11:13:18 2009 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,84 +0,0 @@
-"""various library content hooks
-
-:organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
-:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
-:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
-"""
-__docformat__ = "restructuredtext en"
-
-from datetime import datetime
-
-from cubicweb import RepositoryError
-from cubicweb.common.uilib import soup2xhtml
-from cubicweb.server.hooksmanager import Hook
-from cubicweb.server.pool import PreCommitOperation
-
-
-class SetModificationDateOnStateChange(Hook):
- """update entity's modification date after changing its state"""
- events = ('after_add_relation',)
- accepts = ('in_state',)
-
- def call(self, session, fromeid, rtype, toeid):
- if fromeid in session.transaction_data.get('neweids', ()):
- # new entity, not needed
- return
- entity = session.entity_from_eid(fromeid)
- try:
- entity.set_attributes(modification_date=datetime.now())
- except RepositoryError, ex:
- # usually occurs if entity is coming from a read-only source
- # (eg ldap user)
- self.warning('cant change modification date for %s: %s', entity, ex)
-
-
-class AddUpdateCWUserHook(Hook):
- """ensure user logins are stripped"""
- events = ('before_add_entity', 'before_update_entity',)
- accepts = ('CWUser',)
-
- def call(self, session, entity):
- if 'login' in entity and entity['login']:
- entity['login'] = entity['login'].strip()
-
-
-class AutoDeleteBookmark(PreCommitOperation):
- beid = None # make pylint happy
- def precommit_event(self):
- session = self.session
- if not self.beid in session.transaction_data.get('pendingeids', ()):
- if not session.unsafe_execute('Any X WHERE X bookmarked_by U, X eid %(x)s',
- {'x': self.beid}, 'x'):
- session.unsafe_execute('DELETE Bookmark X WHERE X eid %(x)s',
- {'x': self.beid}, 'x')
-
-class DelBookmarkedByHook(Hook):
- """ensure user logins are stripped"""
- events = ('after_delete_relation',)
- accepts = ('bookmarked_by',)
-
- def call(self, session, subj, rtype, obj):
- AutoDeleteBookmark(session, beid=subj)
-
-
-class TidyHtmlFields(Hook):
- """tidy HTML in rich text strings"""
- events = ('before_add_entity', 'before_update_entity')
- accepts = ('Any',)
-
- def call(self, session, entity):
- metaattrs = entity.e_schema.meta_attributes()
- for metaattr, (metadata, attr) in metaattrs.iteritems():
- if metadata == 'format':
- try:
- value = entity[attr]
- except KeyError:
- continue # no text to tidy
- if isinstance(value, unicode): # filter out None and Binary
- if self.event == 'before_add_entity':
- fmt = entity.get(metaattr)
- else:
- fmt = entity.get_value(metaattr)
- if fmt == 'text/html':
- entity[attr] = soup2xhtml(value, session.encoding)
--- a/sobjects/notification.py Fri Aug 14 11:13:18 2009 +0200
+++ b/sobjects/notification.py Fri Aug 14 11:14:10 2009 +0200
@@ -1,4 +1,4 @@
-"""some hooks and views to handle notification on entity's changes
+"""some views to handle notification on data changes
:organization: Logilab
:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
@@ -18,16 +18,12 @@
return 'XXX'
from logilab.common.textutils import normalize_text
-from logilab.common.deprecation import class_renamed
-from cubicweb import RegistryException
-from cubicweb.selectors import implements, yes
+from cubicweb.selectors import yes
from cubicweb.view import EntityView, Component
from cubicweb.common.mail import format_mail
-from cubicweb.server.pool import PreCommitOperation
from cubicweb.server.hookhelper import SendMailOp
-from cubicweb.server.hooksmanager import Hook
class RecipientsFinder(Component):
@@ -57,72 +53,6 @@
return dests
-# hooks #######################################################################
-
-class RenderAndSendNotificationView(PreCommitOperation):
- """delay rendering of notification view until precommit"""
- def precommit_event(self):
- if self.view.rset and self.view.rset[0][0] in self.session.transaction_data.get('pendingeids', ()):
- return # entity added and deleted in the same transaction
- self.view.render_and_send(**getattr(self, 'viewargs', {}))
-
-class StatusChangeHook(Hook):
- """notify when a workflowable entity has its state modified"""
- events = ('after_add_entity',)
- accepts = ('TrInfo',)
-
- def call(self, session, entity):
- if not entity.from_state: # not a transition
- return
- rset = entity.related('wf_info_for')
- try:
- view = session.vreg['views'].select('notif_status_change', session,
- rset=rset, row=0)
- except RegistryException:
- return
- comment = entity.printable_value('comment', format='text/plain')
- if comment:
- comment = normalize_text(comment, 80,
- rest=entity.comment_format=='text/rest')
- RenderAndSendNotificationView(session, view=view, viewargs={
- 'comment': comment, 'previous_state': entity.previous_state.name,
- 'current_state': entity.new_state.name})
-
-
-class RelationChangeHook(Hook):
- events = ('before_add_relation', 'after_add_relation',
- 'before_delete_relation', 'after_delete_relation')
- accepts = ('Any',)
- def call(self, session, fromeid, rtype, toeid):
- """if a notification view is defined for the event, send notification
- email defined by the view
- """
- rset = session.eid_rset(fromeid)
- vid = 'notif_%s_%s' % (self.event, rtype)
- try:
- view = session.vreg['views'].select(vid, session, rset=rset, row=0)
- except RegistryException:
- return
- RenderAndSendNotificationView(session, view=view)
-
-
-class EntityChangeHook(Hook):
- events = ('after_add_entity',
- 'after_update_entity')
- accepts = ('Any',)
- def call(self, session, entity):
- """if a notification view is defined for the event, send notification
- email defined by the view
- """
- rset = entity.as_rset()
- vid = 'notif_%s' % self.event
- try:
- view = session.vreg['views'].select(vid, session, rset=rset, row=0)
- except RegistryException:
- return
- RenderAndSendNotificationView(session, view=view)
-
-
# abstract or deactivated notification views and mixin ########################
class NotificationView(EntityView):
@@ -296,4 +226,7 @@
return u'%s #%s (%s)' % (self.req.__('New %s' % entity.e_schema),
entity.eid, self.user_login())
+from logilab.common.deprecation import class_renamed, class_moved
NormalizedTextView = class_renamed('NormalizedTextView', ContentAddedView)
+from cubicweb.hooks.notification import RenderAndSendNotificationView
+RenderAndSendNotificationView = class_moved(RenderAndSendNotificationView)
--- a/sobjects/supervising.py Fri Aug 14 11:13:18 2009 +0200
+++ b/sobjects/supervising.py Fri Aug 14 11:14:10 2009 +0200
@@ -13,58 +13,9 @@
from cubicweb.schema import display_name
from cubicweb.view import Component
from cubicweb.common.mail import format_mail
-from cubicweb.server.hooksmanager import Hook
from cubicweb.server.hookhelper import SendMailOp
-class SomethingChangedHook(Hook):
- events = ('before_add_relation', 'before_delete_relation',
- 'after_add_entity', 'before_update_entity')
- accepts = ('Any',)
-
- def call(self, session, *args):
- dest = session.vreg.config['supervising-addrs']
- if not dest: # no supervisors, don't do this for nothing...
- return
- self.session = session
- if self._call(*args):
- SupervisionMailOp(session)
-
- def _call(self, *args):
- if self._event() == 'update_entity':
- if args[0].eid in self.session.transaction_data.get('neweids', ()):
- return False
- if args[0].e_schema == 'CWUser':
- updated = set(args[0].iterkeys())
- if not (updated - frozenset(('eid', 'modification_date',
- 'last_login_time'))):
- # don't record last_login_time update which are done
- # automatically at login time
- return False
- self.session.transaction_data.setdefault('pendingchanges', []).append(
- (self._event(), args))
- return True
-
- def _event(self):
- return self.event.split('_', 1)[1]
-
-
-class EntityDeleteHook(SomethingChangedHook):
- events = ('before_delete_entity',)
-
- def _call(self, eid):
- entity = self.session.entity_from_eid(eid)
- try:
- title = entity.dc_title()
- except:
- # may raise an error during deletion process, for instance due to
- # missing required relation
- title = '#%s' % eid
- self.session.transaction_data.setdefault('pendingchanges', []).append(
- ('delete_entity', (eid, str(entity.e_schema), title)))
- return True
-
-
def filter_changes(changes):
"""
* when an entity has been deleted:
@@ -79,7 +30,7 @@
for change in changes[:]:
event, changedescr = change
if event == 'add_entity':
- entity = changedescr[0]
+ entity = changedescr.entity
added.add(entity.eid)
if entity.e_schema == 'TrInfo':
changes.remove(change)
@@ -111,14 +62,14 @@
changedescr = change[1]
# skip meta-relations which are set automatically
# XXX generate list below using rtags (category = 'generated')
- if changedescr[1] in ('created_by', 'owned_by', 'is', 'is_instance_of',
+ if changedescr.rtype in ('created_by', 'owned_by', 'is', 'is_instance_of',
'from_state', 'to_state', 'wf_info_for',) \
- and changedescr[0] == eid:
+ and changedescr.eidfrom == eid:
index['add_relation'].remove(change)
# skip in_state relation if the entity is being created
# XXX this may be automatized by skipping all mandatory relation
# at entity creation time
- elif changedescr[1] == 'in_state' and changedescr[0] in added:
+ elif changedescr.rtype == 'in_state' and changedescr.eidfrom in added:
index['add_relation'].remove(change)
except KeyError:
@@ -126,15 +77,14 @@
for eid in deleted:
try:
for change in index['delete_relation'].copy():
- fromeid, rtype, toeid = change[1]
- if fromeid == eid:
+ if change.eidfrom == eid:
index['delete_relation'].remove(change)
- elif toeid == eid:
+ elif change.eidto == eid:
index['delete_relation'].remove(change)
- if rtype == 'wf_info_for':
- for change in index['delete_entity'].copy():
- if change[1][0] == fromeid:
- index['delete_entity'].remove(change)
+ if change.rtype == 'wf_info_for':
+ for change_ in index['delete_entity'].copy():
+ if change_[1].eidfrom == change.eidfrom:
+ index['delete_entity'].remove(change_)
except KeyError:
break
for change in changes:
@@ -161,7 +111,7 @@
% user.login)
for event, changedescr in filter_changes(changes):
self.w(u'* ')
- getattr(self, event)(*changedescr)
+ getattr(self, event)(changedescr)
self.w(u'\n\n')
def _entity_context(self, entity):
@@ -169,30 +119,30 @@
'etype': entity.dc_type().lower(),
'title': entity.dc_title()}
- def add_entity(self, entity):
+ def add_entity(self, changedescr):
msg = self.req._('added %(etype)s #%(eid)s (%(title)s)')
- self.w(u'%s\n' % (msg % self._entity_context(entity)))
- self.w(u' %s' % entity.absolute_url())
+ self.w(u'%s\n' % (msg % self._entity_context(changedescr.entity)))
+ self.w(u' %s' % changedescr.entity.absolute_url())
- def update_entity(self, entity):
+ def update_entity(self, changedescr):
msg = self.req._('updated %(etype)s #%(eid)s (%(title)s)')
- self.w(u'%s\n' % (msg % self._entity_context(entity)))
+ self.w(u'%s\n' % (msg % self._entity_context(changedescr.entity)))
# XXX print changes
- self.w(u' %s' % entity.absolute_url())
+ self.w(u' %s' % changedescr.entity.absolute_url())
- def delete_entity(self, eid, etype, title):
+ def delete_entity(self, (eid, etype, title)):
msg = self.req._('deleted %(etype)s #%(eid)s (%(title)s)')
etype = display_name(self.req, etype).lower()
self.w(msg % locals())
- def change_state(self, entity, fromstate, tostate):
+ def change_state(self, (entity, fromstate, tostate)):
msg = self.req._('changed state of %(etype)s #%(eid)s (%(title)s)')
self.w(u'%s\n' % (msg % self._entity_context(entity)))
self.w(_(' from state %(fromstate)s to state %(tostate)s\n' %
{'fromstate': _(fromstate.name), 'tostate': _(tostate.name)}))
self.w(u' %s' % entity.absolute_url())
- def _relation_context(self, fromeid, rtype, toeid):
+ def _relation_context(self, changedescr):
_ = self.req._
session = self.req.actual_session()
def describe(eid):
@@ -202,19 +152,20 @@
# may occurs when an entity has been deleted from an external
# source and we're cleaning its relation
return _('unknown external entity')
+ eidfrom, rtype, eidto = changedescr.eidfrom, changedescr.rtype, changedescr.eidto
return {'rtype': _(rtype),
- 'fromeid': fromeid,
- 'frometype': describe(fromeid),
- 'toeid': toeid,
- 'toetype': describe(toeid)}
+ 'eidfrom': eidfrom,
+ 'frometype': describe(eidfrom),
+ 'eidto': eidto,
+ 'toetype': describe(eidto)}
- def add_relation(self, fromeid, rtype, toeid):
- msg = self.req._('added relation %(rtype)s from %(frometype)s #%(fromeid)s to %(toetype)s #%(toeid)s')
- self.w(msg % self._relation_context(fromeid, rtype, toeid))
+ def add_relation(self, changedescr):
+ msg = self.req._('added relation %(rtype)s from %(frometype)s #%(eidfrom)s to %(toetype)s #%(eidto)s')
+ self.w(msg % self._relation_context(changedescr))
- def delete_relation(self, fromeid, rtype, toeid):
- msg = self.req._('deleted relation %(rtype)s from %(frometype)s #%(fromeid)s to %(toetype)s #%(toeid)s')
- self.w(msg % self._relation_context(fromeid, rtype, toeid))
+ def delete_relation(self, eidfrom, rtype, eidto):
+ msg = self.req._('deleted relation %(rtype)s from %(frometype)s #%(eidfrom)s to %(toetype)s #%(eidto)s')
+ self.w(msg % self._relation_context(changedescr))
class SupervisionMailOp(SendMailOp):