entities/wfobjs.py
brancholdstable
changeset 4985 02b52bf9f5f8
parent 4716 55b6a3262071
child 4835 13b0b96d7982
--- a/entities/wfobjs.py	Fri Feb 12 15:18:00 2010 +0100
+++ b/entities/wfobjs.py	Wed Mar 24 10:23:31 2010 +0100
@@ -15,12 +15,12 @@
 
 from cubicweb.entities import AnyEntity, fetch_config
 from cubicweb.interfaces import IWorkflowable
-from cubicweb.common.mixins import MI_REL_TRIGGERS
+from cubicweb.mixins import MI_REL_TRIGGERS
 
 class WorkflowException(Exception): pass
 
 class Workflow(AnyEntity):
-    id = 'Workflow'
+    __regid__ = 'Workflow'
 
     @property
     def initial(self):
@@ -52,7 +52,7 @@
             _done = set()
         yield self
         _done.add(self.eid)
-        for tr in self.req.execute('Any T WHERE T is WorkflowTransition, '
+        for tr in self._cw.execute('Any T WHERE T is WorkflowTransition, '
                                    'T transition_of WF, WF eid %(wf)s',
                                    {'wf': self.eid}).entities():
             if tr.subwf.eid in _done:
@@ -63,7 +63,7 @@
     # state / transitions accessors ############################################
 
     def state_by_name(self, statename):
-        rset = self.req.execute('Any S, SN WHERE S name SN, S name %(n)s, '
+        rset = self._cw.execute('Any S, SN WHERE S name SN, S name %(n)s, '
                                 'S state_of WF, WF eid %(wf)s',
                                 {'n': statename, 'wf': self.eid}, 'wf')
         if rset:
@@ -71,7 +71,7 @@
         return None
 
     def state_by_eid(self, eid):
-        rset = self.req.execute('Any S, SN WHERE S name SN, S eid %(s)s, '
+        rset = self._cw.execute('Any S, SN WHERE S name SN, S eid %(s)s, '
                                 'S state_of WF, WF eid %(wf)s',
                                 {'s': eid, 'wf': self.eid}, ('wf', 's'))
         if rset:
@@ -79,7 +79,7 @@
         return None
 
     def transition_by_name(self, trname):
-        rset = self.req.execute('Any T, TN WHERE T name TN, T name %(n)s, '
+        rset = self._cw.execute('Any T, TN WHERE T name TN, T name %(n)s, '
                                 'T transition_of WF, WF eid %(wf)s',
                                 {'n': trname, 'wf': self.eid}, 'wf')
         if rset:
@@ -87,7 +87,7 @@
         return None
 
     def transition_by_eid(self, eid):
-        rset = self.req.execute('Any T, TN WHERE T name TN, T eid %(t)s, '
+        rset = self._cw.execute('Any T, TN WHERE T name TN, T eid %(t)s, '
                                 'T transition_of WF, WF eid %(wf)s',
                                 {'t': eid, 'wf': self.eid}, ('wf', 't'))
         if rset:
@@ -98,20 +98,20 @@
 
     def add_state(self, name, initial=False, **kwargs):
         """add a state to this workflow"""
-        state = self.req.create_entity('State', name=unicode(name), **kwargs)
-        self.req.execute('SET S state_of WF WHERE S eid %(s)s, WF eid %(wf)s',
+        state = self._cw.create_entity('State', name=unicode(name), **kwargs)
+        self._cw.execute('SET S state_of WF WHERE S eid %(s)s, WF eid %(wf)s',
                          {'s': state.eid, 'wf': self.eid}, ('s', 'wf'))
         if initial:
             assert not self.initial, "Initial state already defined as %s" % self.initial
-            self.req.execute('SET WF initial_state S '
+            self._cw.execute('SET WF initial_state S '
                              'WHERE S eid %(s)s, WF eid %(wf)s',
                              {'s': state.eid, 'wf': self.eid}, ('s', 'wf'))
         return state
 
     def _add_transition(self, trtype, name, fromstates,
                         requiredgroups=(), conditions=(), **kwargs):
-        tr = self.req.create_entity(trtype, name=unicode(name), **kwargs)
-        self.req.execute('SET T transition_of WF '
+        tr = self._cw.create_entity(trtype, name=unicode(name), **kwargs)
+        self._cw.execute('SET T transition_of WF '
                          'WHERE T eid %(t)s, WF eid %(wf)s',
                          {'t': tr.eid, 'wf': self.eid}, ('t', 'wf'))
         assert fromstates, fromstates
@@ -120,10 +120,10 @@
         for state in fromstates:
             if hasattr(state, 'eid'):
                 state = state.eid
-            self.req.execute('SET S allowed_transition T '
+            self._cw.execute('SET S allowed_transition T '
                              'WHERE S eid %(s)s, T eid %(t)s',
                              {'s': state, 't': tr.eid}, ('s', 't'))
-        tr.set_transition_permissions(requiredgroups, conditions, reset=False)
+        tr.set_permissions(requiredgroups, conditions, reset=False)
         return tr
 
     def add_transition(self, name, fromstates, tostate=None,
@@ -134,7 +134,7 @@
         if tostate is not None:
             if hasattr(tostate, 'eid'):
                 tostate = tostate.eid
-            self.req.execute('SET T destination_state S '
+            self._cw.execute('SET T destination_state S '
                              'WHERE S eid %(s)s, T eid %(t)s',
                              {'t': tr.eid, 's': tostate}, ('s', 't'))
         return tr
@@ -146,7 +146,7 @@
                                   requiredgroups, conditions, **kwargs)
         if hasattr(subworkflow, 'eid'):
             subworkflow = subworkflow.eid
-        assert self.req.execute('SET T subworkflow WF WHERE WF eid %(wf)s,T eid %(t)s',
+        assert self._cw.execute('SET T subworkflow WF WHERE WF eid %(wf)s,T eid %(t)s',
                                 {'t': tr.eid, 'wf': subworkflow}, ('wf', 't'))
         for fromstate, tostate in exitpoints:
             tr.add_exit_point(fromstate, tostate)
@@ -161,9 +161,9 @@
         execute = self._cw.unsafe_execute
         execute('SET X in_state S WHERE S eid %(s)s', {'s': todelstate.eid}, 's')
         execute('SET X from_state NS WHERE X to_state OS, OS eid %(os)s, NS eid %(ns)s',
-                {'os': todelstate.eid, 'ns': newstate.eid}, 's')
+                {'os': todelstate.eid, 'ns': replacement.eid}, 's')
         execute('SET X to_state NS WHERE X to_state OS, OS eid %(os)s, NS eid %(ns)s',
-                {'os': todelstate.eid, 'ns': newstate.eid}, 's')
+                {'os': todelstate.eid, 'ns': replacement.eid}, 's')
         todelstate.delete()
 
 
@@ -173,11 +173,11 @@
     provides a specific may_be_fired method to check if the relation may be
     fired by the logged user
     """
-    id = 'BaseTransition'
+    __regid__ = 'BaseTransition'
     fetch_attrs, fetch_order = fetch_config(['name'])
 
     def __init__(self, *args, **kwargs):
-        if self.id == 'BaseTransition':
+        if self.__regid__ == 'BaseTransition':
             raise WorkflowException('should not be instantiated')
         super(BaseTransition, self).__init__(*args, **kwargs)
 
@@ -195,7 +195,7 @@
 
         `eid` is the eid of the object on which we may fire the transition
         """
-        user = self.req.user
+        user = self._cw.user
         # check user is at least in one of the required groups if any
         groups = frozenset(g.name for g in self.require_group)
         if groups:
@@ -207,7 +207,7 @@
         # check one of the rql expression conditions matches if any
         if self.condition:
             for rqlexpr in self.condition:
-                if rqlexpr.check_expression(self.req, eid):
+                if rqlexpr.check_expression(self._cw, eid):
                     return True
         if self.condition or groups:
             return False
@@ -219,20 +219,19 @@
         """
         if self.transition_of:
             return self.transition_of[0].rest_path(), {}
-        return super(Transition, self).after_deletion_path()
+        return super(BaseTransition, self).after_deletion_path()
 
-    def set_transition_permissions(self, requiredgroups=(), conditions=(),
-                                   reset=True):
+    def set_permissions(self, requiredgroups=(), conditions=(), reset=True):
         """set or add (if `reset` is False) groups and conditions for this
         transition
         """
         if reset:
-            self.req.execute('DELETE T require_group G WHERE T eid %(x)s',
+            self._cw.execute('DELETE T require_group G WHERE T eid %(x)s',
                              {'x': self.eid}, 'x')
-            self.req.execute('DELETE T condition R WHERE T eid %(x)s',
+            self._cw.execute('DELETE T condition R WHERE T eid %(x)s',
                              {'x': self.eid}, 'x')
         for gname in requiredgroups:
-            rset = self.req.execute('SET T require_group G '
+            rset = self._cw.execute('SET T require_group G '
                                     'WHERE T eid %(x)s, G name %(gn)s',
                                     {'x': self.eid, 'gn': gname}, 'x')
             assert rset, '%s is not a known group' % gname
@@ -246,18 +245,35 @@
                 kwargs = expr
             kwargs['x'] = self.eid
             kwargs.setdefault('mainvars', u'X')
-            self.req.execute('INSERT RQLExpression X: X exprtype "ERQLExpression", '
+            self._cw.execute('INSERT RQLExpression X: X exprtype "ERQLExpression", '
                              'X expression %(expr)s, X mainvars %(mainvars)s, '
-                             'T condition X WHERE T eid %(x)s', kwargs, 'x')
+                             'T condition X WHERE T eid %(x)s',kwargs, 'x')
         # XXX clear caches?
 
+    @deprecated('[3.6.1] use set_permission')
+    def set_transition_permissions(self, requiredgroups=(), conditions=(),
+                                   reset=True):
+        return self.set_permissions(requiredgroups, conditions, reset)
+
 
 class Transition(BaseTransition):
     """customized class for Transition entities"""
-    id = 'Transition'
+    __regid__ = 'Transition'
+
+    def destination(self, entity):
+        try:
+            return self.destination_state[0]
+        except IndexError:
+            return entity.latest_trinfo().previous_state
 
-    def destination(self):
-        return self.destination_state[0]
+    def potential_destinations(self):
+        try:
+            yield self.destination_state[0]
+        except IndexError:
+            for incomingstate in self.reverse_allowed_transition:
+                for tr in incomingstate.reverse_destination_state:
+                    for previousstate in tr.reverse_allowed_transition:
+                        yield previousstate
 
     def parent(self):
         return self.workflow
@@ -265,26 +281,29 @@
 
 class WorkflowTransition(BaseTransition):
     """customized class for WorkflowTransition entities"""
-    id = 'WorkflowTransition'
+    __regid__ = 'WorkflowTransition'
 
     @property
     def subwf(self):
         return self.subworkflow[0]
 
-    def destination(self):
+    def destination(self, entity):
         return self.subwf.initial
 
+    def potential_destinations(self):
+        yield self.subwf.initial
+
     def add_exit_point(self, fromstate, tostate):
         if hasattr(fromstate, 'eid'):
             fromstate = fromstate.eid
         if tostate is None:
-            self.req.execute('INSERT SubWorkflowExitPoint X: T subworkflow_exit X, '
+            self._cw.execute('INSERT SubWorkflowExitPoint X: T subworkflow_exit X, '
                              'X subworkflow_state FS WHERE T eid %(t)s, FS eid %(fs)s',
                              {'t': self.eid, 'fs': fromstate}, ('t', 'fs'))
         else:
             if hasattr(tostate, 'eid'):
                 tostate = tostate.eid
-            self.req.execute('INSERT SubWorkflowExitPoint X: T subworkflow_exit X, '
+            self._cw.execute('INSERT SubWorkflowExitPoint X: T subworkflow_exit X, '
                              'X subworkflow_state FS, X destination_state TS '
                              'WHERE T eid %(t)s, FS eid %(fs)s, TS eid %(ts)s',
                              {'t': self.eid, 'fs': fromstate, 'ts': tostate},
@@ -301,7 +320,7 @@
         if tostateeid is None:
             # go back to state from which we've entered the subworkflow
             return entity.subworkflow_input_trinfo().previous_state
-        return self.req.entity_from_eid(tostateeid)
+        return self._cw.entity_from_eid(tostateeid)
 
     @cached
     def exit_points(self):
@@ -311,13 +330,13 @@
         return result
 
     def clear_all_caches(self):
-        super(WorkflowableMixIn, self).clear_all_caches()
+        super(WorkflowTransition, self).clear_all_caches()
         clear_cache(self, 'exit_points')
 
 
 class SubWorkflowExitPoint(AnyEntity):
     """customized class for SubWorkflowExitPoint entities"""
-    id = 'SubWorkflowExitPoint'
+    __regid__ = 'SubWorkflowExitPoint'
 
     @property
     def subwf_state(self):
@@ -333,7 +352,7 @@
 
 class State(AnyEntity):
     """customized class for State entities"""
-    id = 'State'
+    __regid__ = 'State'
     fetch_attrs, fetch_order = fetch_config(['name'])
     rest_attr = 'eid'
 
@@ -349,7 +368,7 @@
 class TrInfo(AnyEntity):
     """customized class for Transition information entities
     """
-    id = 'TrInfo'
+    __regid__ = 'TrInfo'
     fetch_attrs, fetch_order = fetch_config(['creation_date', 'comment'],
                                             pclass=None) # don't want modification_date
     @property
@@ -410,7 +429,7 @@
         """return current state name translated to context's language"""
         state = self.current_state
         if state:
-            return self.req._(state.name)
+            return self._cw._(state.name)
         return u''
 
     @property
@@ -430,11 +449,12 @@
     @cached
     def cwetype_workflow(self):
         """return the default workflow for entities of this type"""
-        wfrset = self.req.execute('Any WF WHERE ET default_workflow WF, '
-                                  'ET name %(et)s', {'et': self.id})
+        # XXX CWEType method
+        wfrset = self._cw.execute('Any WF WHERE ET default_workflow WF, '
+                                  'ET name %(et)s', {'et': self.__regid__})
         if wfrset:
             return wfrset.get_entity(0, 0)
-        self.warning("can't find any workflow for %s", self.id)
+        self.warning("can't find any workflow for %s", self.__regid__)
         return None
 
     def possible_transitions(self, type='normal'):
@@ -444,7 +464,7 @@
         """
         if self.current_state is None or self.current_workflow is None:
             return
-        rset = self.req.execute(
+        rset = self._cw.execute(
             'Any T,TT, TN WHERE S allowed_transition T, S eid %(x)s, '
             'T type TT, T type %(type)s, '
             'T name TN, T transition_of WF, WF eid %(wfeid)s',
@@ -462,10 +482,10 @@
                 kwargs['comment_format'] = commentformat
         kwargs['wf_info_for'] = self
         if treid is not None:
-            kwargs['by_transition'] = self.req.entity_from_eid(treid)
+            kwargs['by_transition'] = self._cw.entity_from_eid(treid)
         if tseid is not None:
-            kwargs['to_state'] = self.req.entity_from_eid(tseid)
-        return self.req.create_entity('TrInfo', **kwargs)
+            kwargs['to_state'] = self._cw.entity_from_eid(tseid)
+        return self._cw.create_entity('TrInfo', **kwargs)
 
     def fire_transition(self, tr, comment=None, commentformat=None):
         """change the entity's state by firing transition of the given name in
@@ -474,7 +494,8 @@
         assert self.current_workflow
         if isinstance(tr, basestring):
             _tr = self.current_workflow.transition_by_name(tr)
-            assert _tr is not None, 'not a %s transition: %s' % (self.id, tr)
+            assert _tr is not None, 'not a %s transition: %s' % (
+                self.__regid__, tr)
             tr = _tr
         return self._add_trinfo(comment, commentformat, tr.eid)
 
@@ -494,7 +515,7 @@
             else:
                 state = self.current_workflow.state_by_name(statename)
             if state is None:
-                raise WorkflowException('not a %s state: %s' % (self.id,
+                raise WorkflowException('not a %s state: %s' % (self.__regid__,
                                                                 statename))
             stateeid = state.eid
         # XXX try to find matching transition?
@@ -532,7 +553,7 @@
         super(WorkflowableMixIn, self).clear_all_caches()
         clear_cache(self, 'cwetype_workflow')
 
-    @deprecated('get transition from current workflow and use its may_be_fired method')
+    @deprecated('[3.5] get transition from current workflow and use its may_be_fired method')
     def can_pass_transition(self, trname):
         """return the Transition instance if the current user can fire the
         transition with the given name, else None
@@ -542,8 +563,8 @@
             return tr
 
     @property
-    @deprecated('use printable_state')
+    @deprecated('[3.5] use printable_state')
     def displayable_state(self):
-        return self.req._(self.state)
+        return self._cw._(self.state)
 
 MI_REL_TRIGGERS[('in_state', 'subject')] = WorkflowableMixIn