hooks/workflow.py
changeset 2841 107ba1c45227
parent 2835 04034421b072
child 2847 c2ee28f4d4b1
--- 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)