hooks/workflow.py
author Sylvain Thénault <sylvain.thenault@logilab.fr>
Wed, 23 Sep 2009 19:36:31 +0200
changeset 3449 063488f14a0f
parent 3376 f5c69485381f
child 3536 f6c9a5df80fb
permissions -rw-r--r--
no need to access to _ through entity.req...
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
2835
04034421b072 [hooks] major refactoring:
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
     1
"""Core hooks: workflow related hooks
04034421b072 [hooks] major refactoring:
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
     2
04034421b072 [hooks] major refactoring:
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
     3
:organization: Logilab
04034421b072 [hooks] major refactoring:
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
     4
:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
04034421b072 [hooks] major refactoring:
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
     5
:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
04034421b072 [hooks] major refactoring:
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
     6
:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
04034421b072 [hooks] major refactoring:
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
     7
"""
04034421b072 [hooks] major refactoring:
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
     8
__docformat__ = "restructuredtext en"
04034421b072 [hooks] major refactoring:
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
     9
2841
107ba1c45227 rewrite hooks in sobjects as new Hook style into hooks sub-package
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2835
diff changeset
    10
from datetime import datetime
107ba1c45227 rewrite hooks in sobjects as new Hook style into hooks sub-package
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2835
diff changeset
    11
107ba1c45227 rewrite hooks in sobjects as new Hook style into hooks sub-package
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2835
diff changeset
    12
from cubicweb import RepositoryError, ValidationError
2835
04034421b072 [hooks] major refactoring:
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
    13
from cubicweb.interfaces import IWorkflowable
04034421b072 [hooks] major refactoring:
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
    14
from cubicweb.selectors import entity_implements
2841
107ba1c45227 rewrite hooks in sobjects as new Hook style into hooks sub-package
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2835
diff changeset
    15
from cubicweb.server import hook
3024
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
    16
from cubicweb.entities.wfobjs import WorkflowTransition
2841
107ba1c45227 rewrite hooks in sobjects as new Hook style into hooks sub-package
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2835
diff changeset
    17
107ba1c45227 rewrite hooks in sobjects as new Hook style into hooks sub-package
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2835
diff changeset
    18
2968
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
    19
def _change_state(session, x, oldstate, newstate):
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
    20
    nocheck = session.transaction_data.setdefault('skip-security', set())
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
    21
    nocheck.add((x, 'in_state', oldstate))
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
    22
    nocheck.add((x, 'in_state', newstate))
3072
6fb42c53f6df backport 3.5 branch
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 3024
diff changeset
    23
    # delete previous state first in case we're using a super session,
6fb42c53f6df backport 3.5 branch
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 3024
diff changeset
    24
    # unless in_state isn't stored in the system source
6fb42c53f6df backport 3.5 branch
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 3024
diff changeset
    25
    fromsource = session.describe(x)[1]
6fb42c53f6df backport 3.5 branch
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 3024
diff changeset
    26
    if fromsource == 'system' or \
6fb42c53f6df backport 3.5 branch
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 3024
diff changeset
    27
           not session.repo.sources_by_uri[fromsource].support_relation('in_state'):
6fb42c53f6df backport 3.5 branch
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 3024
diff changeset
    28
        session.delete_relation(x, 'in_state', oldstate)
2968
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
    29
    session.add_relation(x, 'in_state', newstate)
2835
04034421b072 [hooks] major refactoring:
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
    30
04034421b072 [hooks] major refactoring:
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
    31
3024
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
    32
# operations ###################################################################
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
    33
2841
107ba1c45227 rewrite hooks in sobjects as new Hook style into hooks sub-package
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2835
diff changeset
    34
class _SetInitialStateOp(hook.Operation):
2835
04034421b072 [hooks] major refactoring:
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
    35
    """make initial state be a default state"""
04034421b072 [hooks] major refactoring:
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
    36
04034421b072 [hooks] major refactoring:
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
    37
    def precommit_event(self):
04034421b072 [hooks] major refactoring:
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
    38
        session = self.session
04034421b072 [hooks] major refactoring:
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
    39
        entity = self.entity
04034421b072 [hooks] major refactoring:
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
    40
        # if there is an initial state and the entity's state is not set,
04034421b072 [hooks] major refactoring:
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
    41
        # use the initial state as a default state
2968
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
    42
        if not (session.deleted_in_transaction(entity.eid) or entity.in_state) \
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
    43
               and entity.current_workflow:
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
    44
            state = entity.current_workflow.initial
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
    45
            if state:
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
    46
                # use super session to by-pass security checks
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
    47
                session.super_session.add_relation(entity.eid, 'in_state',
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
    48
                                                   state.eid)
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
    49
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
    50
class _WorkflowChangedOp(hook.Operation):
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
    51
    """fix entity current state when changing its workflow"""
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
    52
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
    53
    def precommit_event(self):
3024
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
    54
        # notice that enforcement that new workflow apply to the entity's type is
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
    55
        # done by schema rule, no need to check it here
2968
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
    56
        session = self.session
3024
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
    57
        pendingeids = session.transaction_data.get('pendingeids', ())
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
    58
        if self.eid in pendingeids:
2968
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
    59
            return
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
    60
        entity = session.entity_from_eid(self.eid)
3024
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
    61
        # check custom workflow has not been rechanged to another one in the same
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
    62
        # transaction
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
    63
        mainwf = entity.main_workflow
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
    64
        if mainwf.eid == self.wfeid:
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
    65
            deststate = mainwf.initial
2968
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
    66
            if not deststate:
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
    67
                msg = session._('workflow has no initial state')
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
    68
                raise ValidationError(entity.eid, {'custom_workflow': msg})
3024
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
    69
            if mainwf.state_by_eid(entity.current_state.eid):
2968
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
    70
                # nothing to do
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
    71
                return
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
    72
            # if there are no history, simply go to new workflow's initial state
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
    73
            if not entity.workflow_history:
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
    74
                if entity.current_state.eid != deststate.eid:
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
    75
                    _change_state(session, entity.eid,
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
    76
                                  entity.current_state.eid, deststate.eid)
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
    77
                return
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
    78
            msg = session._('workflow changed to "%s"')
3024
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
    79
            msg %= session._(mainwf.name)
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
    80
            session.transaction_data[(entity.eid, 'customwf')] = self.wfeid
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
    81
            entity.change_state(deststate, msg, u'text/plain')
2968
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
    82
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
    83
3024
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
    84
class _CheckTrExitPoint(hook.Operation):
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
    85
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
    86
    def precommit_event(self):
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
    87
        tr = self.session.entity_from_eid(self.treid)
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
    88
        outputs = set()
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
    89
        for ep in tr.subworkflow_exit:
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
    90
            if ep.subwf_state.eid in outputs:
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
    91
                msg = self.session._("can't have multiple exits on the same state")
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
    92
                raise ValidationError(self.treid, {'subworkflow_exit': msg})
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
    93
            outputs.add(ep.subwf_state.eid)
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
    94
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
    95
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
    96
# hooks ########################################################################
2835
04034421b072 [hooks] major refactoring:
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
    97
2841
107ba1c45227 rewrite hooks in sobjects as new Hook style into hooks sub-package
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2835
diff changeset
    98
class WorkflowHook(hook.Hook):
107ba1c45227 rewrite hooks in sobjects as new Hook style into hooks sub-package
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2835
diff changeset
    99
    __abstract__ = True
107ba1c45227 rewrite hooks in sobjects as new Hook style into hooks sub-package
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2835
diff changeset
   100
    category = 'worfklow'
2835
04034421b072 [hooks] major refactoring:
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
   101
2841
107ba1c45227 rewrite hooks in sobjects as new Hook style into hooks sub-package
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2835
diff changeset
   102
107ba1c45227 rewrite hooks in sobjects as new Hook style into hooks sub-package
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2835
diff changeset
   103
class SetInitialStateHook(WorkflowHook):
3376
f5c69485381f [appobjects] use __regid__ instead of __id__, more explicit
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 3072
diff changeset
   104
    __regid__ = 'wfsetinitial'
2841
107ba1c45227 rewrite hooks in sobjects as new Hook style into hooks sub-package
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2835
diff changeset
   105
    __select__ = WorkflowHook.__select__ & entity_implements(IWorkflowable)
2835
04034421b072 [hooks] major refactoring:
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
   106
    events = ('after_add_entity',)
04034421b072 [hooks] major refactoring:
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
   107
04034421b072 [hooks] major refactoring:
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
   108
    def __call__(self):
2847
c2ee28f4d4b1 use ._cw instead of .cw_req
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2841
diff changeset
   109
        _SetInitialStateOp(self._cw, entity=self.entity)
2835
04034421b072 [hooks] major refactoring:
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
   110
04034421b072 [hooks] major refactoring:
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
   111
2841
107ba1c45227 rewrite hooks in sobjects as new Hook style into hooks sub-package
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2835
diff changeset
   112
class PrepareStateChangeHook(WorkflowHook):
2835
04034421b072 [hooks] major refactoring:
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
   113
    """record previous state information"""
3376
f5c69485381f [appobjects] use __regid__ instead of __id__, more explicit
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 3072
diff changeset
   114
    __regid__ = 'cwdelstate'
2841
107ba1c45227 rewrite hooks in sobjects as new Hook style into hooks sub-package
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2835
diff changeset
   115
    __select__ = WorkflowHook.__select__ & hook.match_rtype('in_state')
2835
04034421b072 [hooks] major refactoring:
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
   116
    events = ('before_delete_relation',)
04034421b072 [hooks] major refactoring:
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
   117
04034421b072 [hooks] major refactoring:
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
   118
    def __call__(self):
2847
c2ee28f4d4b1 use ._cw instead of .cw_req
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2841
diff changeset
   119
        self._cw.transaction_data.setdefault('pendingrelations', []).append(
2835
04034421b072 [hooks] major refactoring:
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
   120
            (self.eidfrom, self.rtype, self.eidto))
04034421b072 [hooks] major refactoring:
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
   121
04034421b072 [hooks] major refactoring:
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
   122
2968
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   123
class FireTransitionHook(WorkflowHook):
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   124
    """check the transition is allowed, add missing information. Expect that:
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   125
    * wf_info_for inlined relation is set
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   126
    * by_transition or to_state (managers only) inlined relation is set
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   127
    """
3376
f5c69485381f [appobjects] use __regid__ instead of __id__, more explicit
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 3072
diff changeset
   128
    __regid__ = 'wffiretransition'
2968
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   129
    __select__ = WorkflowHook.__select__ & entity_implements('TrInfo')
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   130
    events = ('before_add_entity',)
2835
04034421b072 [hooks] major refactoring:
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
   131
04034421b072 [hooks] major refactoring:
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
diff changeset
   132
    def __call__(self):
2847
c2ee28f4d4b1 use ._cw instead of .cw_req
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2841
diff changeset
   133
        session = self._cw
2968
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   134
        entity = self.entity
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   135
        # first retreive entity to which the state change apply
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   136
        try:
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   137
            foreid = entity['wf_info_for']
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   138
        except KeyError:
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   139
            msg = session._('mandatory relation')
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   140
            raise ValidationError(entity.eid, {'wf_info_for': msg})
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   141
        forentity = session.entity_from_eid(foreid)
3024
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   142
        # then check it has a workflow set, unless we're in the process of changing
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   143
        # entity's workflow
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   144
        if session.transaction_data.get((forentity.eid, 'customwf')):
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   145
            wfeid = session.transaction_data[(forentity.eid, 'customwf')]
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   146
            wf = session.entity_from_eid(wfeid)
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   147
        else:
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   148
            wf = forentity.current_workflow
2968
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   149
        if wf is None:
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   150
            msg = session._('related entity has no workflow set')
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   151
            raise ValidationError(entity.eid, {None: msg})
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   152
        # then check it has a state set
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   153
        fromstate = forentity.current_state
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   154
        if fromstate is None:
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   155
            msg = session._('related entity has no state')
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   156
            raise ValidationError(entity.eid, {None: msg})
3024
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   157
        # True if we are coming back from subworkflow
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   158
        swtr = session.transaction_data.pop((forentity.eid, 'subwfentrytr'), None)
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   159
        cowpowers = session.is_super_session or 'managers' in session.user.groups
2968
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   160
        # no investigate the requested state change...
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   161
        try:
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   162
            treid = entity['by_transition']
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   163
        except KeyError:
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   164
            # no transition set, check user is a manager and destination state is
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   165
            # specified (and valid)
3024
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   166
            if not cowpowers:
2968
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   167
                msg = session._('mandatory relation')
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   168
                raise ValidationError(entity.eid, {'by_transition': msg})
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   169
            deststateeid = entity.get('to_state')
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   170
            if not deststateeid:
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   171
                msg = session._('mandatory relation')
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   172
                raise ValidationError(entity.eid, {'by_transition': msg})
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   173
            deststate = wf.state_by_eid(deststateeid)
3024
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   174
            if not cowpowers and deststate is None:
3449
063488f14a0f no need to access to _ through entity.req...
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 3376
diff changeset
   175
                msg = session._("state doesn't belong to entity's workflow")
2968
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   176
                raise ValidationError(entity.eid, {'to_state': msg})
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   177
        else:
3024
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   178
            # check transition is valid and allowed, unless we're coming back from
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   179
            # subworkflow
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   180
            tr = session.entity_from_eid(treid)
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   181
            if swtr is None:
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   182
                if tr is None:
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   183
                    msg = session._("transition doesn't belong to entity's workflow")
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   184
                    raise ValidationError(entity.eid, {'by_transition': msg})
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   185
                if not tr.has_input_state(fromstate):
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   186
                    msg = session._("transition isn't allowed")
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   187
                    raise ValidationError(entity.eid, {'by_transition': msg})
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   188
                if not tr.may_be_fired(foreid):
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   189
                    msg = session._("transition may not be fired")
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   190
                    raise ValidationError(entity.eid, {'by_transition': msg})
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   191
            if entity.get('to_state'):
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   192
                deststateeid = entity['to_state']
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   193
                if not cowpowers and deststateeid != tr.destination().eid:
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   194
                    msg = session._("transition isn't allowed")
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   195
                    raise ValidationError(entity.eid, {'by_transition': msg})
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   196
                if swtr is None:
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   197
                    deststate = session.entity_from_eid(deststateeid)
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   198
                    if not cowpowers and deststate is None:
3449
063488f14a0f no need to access to _ through entity.req...
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 3376
diff changeset
   199
                        msg = session._("state doesn't belong to entity's workflow")
3024
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   200
                        raise ValidationError(entity.eid, {'to_state': msg})
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   201
            else:
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   202
                deststateeid = tr.destination().eid
2968
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   203
        # everything is ok, add missing information on the trinfo entity
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   204
        entity['from_state'] = fromstate.eid
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   205
        entity['to_state'] = deststateeid
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   206
        nocheck = session.transaction_data.setdefault('skip-security', set())
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   207
        nocheck.add((entity.eid, 'from_state', fromstate.eid))
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   208
        nocheck.add((entity.eid, 'to_state', deststateeid))
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   209
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   210
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   211
class FiredTransitionHook(WorkflowHook):
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   212
    """change related entity state"""
3376
f5c69485381f [appobjects] use __regid__ instead of __id__, more explicit
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 3072
diff changeset
   213
    __regid__ = 'wffiretransition'
2968
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   214
    __select__ = WorkflowHook.__select__ & entity_implements('TrInfo')
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   215
    events = ('after_add_entity',)
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   216
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   217
    def __call__(self):
3024
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   218
        session = self._cw
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   219
        entity = self.entity
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   220
        _change_state(session, entity['wf_info_for'],
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   221
                      entity['from_state'], entity['to_state'])
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   222
        forentity = session.entity_from_eid(entity['wf_info_for'])
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   223
        assert forentity.current_state.eid == entity['to_state']
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   224
        if forentity.main_workflow.eid != forentity.current_workflow.eid:
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   225
            # we're in a subworkflow, check if we've reached an exit point
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   226
            wftr = forentity.subworkflow_input_transition()
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   227
            if wftr is None:
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   228
                # inconsistency detected
3449
063488f14a0f no need to access to _ through entity.req...
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 3376
diff changeset
   229
                msg = session._("state doesn't belong to entity's current workflow")
3024
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   230
                raise ValidationError(entity.eid, {'to_state': msg})
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   231
            tostate = wftr.get_exit_point(entity['to_state'])
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   232
            if tostate is not None:
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   233
                # reached an exit point
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   234
                msg = session._('exiting from subworkflow %s')
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   235
                msg %= session._(forentity.current_workflow.name)
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   236
                session.transaction_data[(forentity.eid, 'subwfentrytr')] = True
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   237
                # XXX iirk
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   238
                req = forentity._cw
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   239
                forentity._cw = session.super_session
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   240
                try:
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   241
                    trinfo = forentity.change_state(tostate, msg, u'text/plain',
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   242
                                                    tr=wftr)
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   243
                finally:
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   244
                    forentity._cw = req
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   245
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   246
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   247
class CheckInStateChangeAllowed(WorkflowHook):
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   248
    """check state apply, in case of direct in_state change using unsafe_execute
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   249
    """
3376
f5c69485381f [appobjects] use __regid__ instead of __id__, more explicit
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 3072
diff changeset
   250
    __regid__ = 'wfcheckinstate'
3024
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   251
    __select__ = WorkflowHook.__select__ & hook.match_rtype('in_state')
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   252
    events = ('before_add_relation',)
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   253
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   254
    def __call__(self):
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   255
        session = self._cw
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   256
        nocheck = session.transaction_data.setdefault('skip-security', ())
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   257
        if (self.eidfrom, 'in_state', self.eidto) in nocheck:
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   258
            # state changed through TrInfo insertion, so we already know it's ok
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   259
            return
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   260
        entity = session.entity_from_eid(self.eidfrom)
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   261
        mainwf = entity.main_workflow
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   262
        if mainwf is None:
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   263
            msg = session._('entity has no workflow set')
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   264
            raise ValidationError(entity.eid, {None: msg})
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   265
        for wf in mainwf.iter_workflows():
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   266
            if wf.state_by_eid(self.eidto):
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   267
                break
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   268
        else:
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   269
            msg = session._("state doesn't belong to entity's workflow. You may "
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   270
                            "want to set a custom workflow for this entity first.")
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   271
            raise ValidationError(self.eidfrom, {'in_state': msg})
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   272
        if entity.current_workflow and wf.eid != entity.current_workflow.eid:
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   273
            msg = session._("state doesn't belong to entity's current workflow")
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   274
            raise ValidationError(self.eidfrom, {'in_state': msg})
2841
107ba1c45227 rewrite hooks in sobjects as new Hook style into hooks sub-package
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2835
diff changeset
   275
107ba1c45227 rewrite hooks in sobjects as new Hook style into hooks sub-package
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2835
diff changeset
   276
107ba1c45227 rewrite hooks in sobjects as new Hook style into hooks sub-package
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2835
diff changeset
   277
class SetModificationDateOnStateChange(WorkflowHook):
107ba1c45227 rewrite hooks in sobjects as new Hook style into hooks sub-package
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2835
diff changeset
   278
    """update entity's modification date after changing its state"""
3376
f5c69485381f [appobjects] use __regid__ instead of __id__, more explicit
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 3072
diff changeset
   279
    __regid__ = 'wfsyncmdate'
2841
107ba1c45227 rewrite hooks in sobjects as new Hook style into hooks sub-package
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2835
diff changeset
   280
    __select__ = WorkflowHook.__select__ & hook.match_rtype('in_state')
107ba1c45227 rewrite hooks in sobjects as new Hook style into hooks sub-package
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2835
diff changeset
   281
    events = ('after_add_relation',)
107ba1c45227 rewrite hooks in sobjects as new Hook style into hooks sub-package
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2835
diff changeset
   282
107ba1c45227 rewrite hooks in sobjects as new Hook style into hooks sub-package
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2835
diff changeset
   283
    def __call__(self):
2847
c2ee28f4d4b1 use ._cw instead of .cw_req
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2841
diff changeset
   284
        if self._cw.added_in_transaction(self.eidfrom):
2841
107ba1c45227 rewrite hooks in sobjects as new Hook style into hooks sub-package
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2835
diff changeset
   285
            # new entity, not needed
107ba1c45227 rewrite hooks in sobjects as new Hook style into hooks sub-package
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2835
diff changeset
   286
            return
2847
c2ee28f4d4b1 use ._cw instead of .cw_req
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2841
diff changeset
   287
        entity = self._cw.entity_from_eid(self.eidfrom)
2841
107ba1c45227 rewrite hooks in sobjects as new Hook style into hooks sub-package
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2835
diff changeset
   288
        try:
2880
bfc8e1831290 backport stable branch
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2847
diff changeset
   289
            entity.set_attributes(modification_date=datetime.now(),
bfc8e1831290 backport stable branch
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2847
diff changeset
   290
                                  _cw_unsafe=True)
2841
107ba1c45227 rewrite hooks in sobjects as new Hook style into hooks sub-package
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2835
diff changeset
   291
        except RepositoryError, ex:
107ba1c45227 rewrite hooks in sobjects as new Hook style into hooks sub-package
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2835
diff changeset
   292
            # usually occurs if entity is coming from a read-only source
107ba1c45227 rewrite hooks in sobjects as new Hook style into hooks sub-package
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2835
diff changeset
   293
            # (eg ldap user)
107ba1c45227 rewrite hooks in sobjects as new Hook style into hooks sub-package
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2835
diff changeset
   294
            self.warning('cant change modification date for %s: %s', entity, ex)
2968
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   295
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   296
3024
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   297
class CheckWorkflowTransitionExitPoint(WorkflowHook):
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   298
    """check that there is no multiple exits from the same state"""
3376
f5c69485381f [appobjects] use __regid__ instead of __id__, more explicit
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 3072
diff changeset
   299
    __regid__ = 'wfcheckwftrexit'
3024
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   300
    __select__ = WorkflowHook.__select__ & hook.match_rtype('subworkflow_exit')
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   301
    events = ('after_add_relation',)
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   302
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   303
    def __call__(self):
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   304
        _CheckTrExitPoint(self._cw, treid=self.eidfrom)
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   305
bfaf056f1029 backport 3.5 step 2, backport wf changes in hooks/workflow.py
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2968
diff changeset
   306
2968
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   307
class SetCustomWorkflow(WorkflowHook):
3376
f5c69485381f [appobjects] use __regid__ instead of __id__, more explicit
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 3072
diff changeset
   308
    __regid__ = 'wfsetcustom'
2968
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   309
    __select__ = WorkflowHook.__select__ & hook.match_rtype('custom_workflow')
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   310
    events = ('after_add_relation',)
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   311
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   312
    def __call__(self):
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   313
        _WorkflowChangedOp(self._cw, eid=self.eidfrom, wfeid=self.eidto)
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   314
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   315
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   316
class DelCustomWorkflow(SetCustomWorkflow):
3376
f5c69485381f [appobjects] use __regid__ instead of __id__, more explicit
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 3072
diff changeset
   317
    __regid__ = 'wfdelcustom'
2968
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   318
    events = ('after_delete_relation',)
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   319
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   320
    def __call__(self):
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   321
        entity = self._cw.entity_from_eid(self.eidfrom)
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   322
        typewf = entity.cwetype_workflow()
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   323
        if typewf is not None:
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   324
            _WorkflowChangedOp(self._cw, eid=self.eidfrom, wfeid=typewf.eid)
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   325
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   326
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   327
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   328
class DelWorkflowHook(WorkflowHook):
3376
f5c69485381f [appobjects] use __regid__ instead of __id__, more explicit
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 3072
diff changeset
   329
    __regid__ = 'wfdel'
2968
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   330
    __select__ = WorkflowHook.__select__ & entity_implements('Workflow')
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   331
    events = ('after_delete_entity',)
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   332
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   333
    def __call__(self):
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   334
        # cleanup unused state and transition
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   335
        self._cw.execute('DELETE State X WHERE NOT X state_of Y')
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   336
        self._cw.execute('DELETE Transition X WHERE NOT X transition_of Y')
0e3460341023 somewhat painful backport of 3.5 branch, should mostly be ok
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents: 2880
diff changeset
   337