hooks/workflow.py
changeset 2835 04034421b072
child 2841 107ba1c45227
equal deleted inserted replaced
2834:7df3494ae657 2835:04034421b072
       
     1 """Core hooks: workflow related hooks
       
     2 
       
     3 :organization: Logilab
       
     4 :copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
       
     5 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
       
     6 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
       
     7 """
       
     8 __docformat__ = "restructuredtext en"
       
     9 
       
    10 from cubicweb import ValidationError
       
    11 from cubicweb.interfaces import IWorkflowable
       
    12 from cubicweb.selectors import entity_implements
       
    13 from cubicweb.server.hook import Hook, match_rtype
       
    14 from cubicweb.server.pool import PreCommitOperation
       
    15 from cubicweb.server.hookhelper import previous_state
       
    16 
       
    17 
       
    18 def relation_deleted(session, eidfrom, rtype, eidto):
       
    19     session.transaction_data.setdefault('pendingrelations', []).append(
       
    20         (eidfrom, rtype, eidto))
       
    21 
       
    22 
       
    23 class _SetInitialStateOp(PreCommitOperation):
       
    24     """make initial state be a default state"""
       
    25 
       
    26     def precommit_event(self):
       
    27         session = self.session
       
    28         entity = self.entity
       
    29         # if there is an initial state and the entity's state is not set,
       
    30         # use the initial state as a default state
       
    31         pendingeids = session.transaction_data.get('pendingeids', ())
       
    32         if not entity.eid in pendingeids and not entity.in_state:
       
    33             rset = session.execute('Any S WHERE ET initial_state S, ET name %(name)s',
       
    34                                    {'name': entity.id})
       
    35             if rset:
       
    36                 session.add_relation(entity.eid, 'in_state', rset[0][0])
       
    37 
       
    38 
       
    39 class SetInitialStateHook(Hook):
       
    40     __id__ = 'wfsetinitial'
       
    41     __select__ = Hook.__select__ & entity_implements(IWorkflowable)
       
    42     category = 'worfklow'
       
    43     events = ('after_add_entity',)
       
    44 
       
    45     def __call__(self):
       
    46         _SetInitialStateOp(self.cw_req, entity=self.entity)
       
    47 
       
    48 
       
    49 class PrepareStateChangeHook(Hook):
       
    50     """record previous state information"""
       
    51     __id__ = 'cwdelstate'
       
    52     __select__ = Hook.__select__ & match_rtype('in_state')
       
    53     category = 'worfklow'
       
    54     events = ('before_delete_relation',)
       
    55 
       
    56     def __call__(self):
       
    57         self.cw_req.transaction_data.setdefault('pendingrelations', []).append(
       
    58             (self.eidfrom, self.rtype, self.eidto))
       
    59 
       
    60 
       
    61 class FireTransitionHook(PrepareStateChangeHook):
       
    62     """check the transition is allowed and record transition information"""
       
    63     __id__ = 'wffiretransition'
       
    64     events = ('before_add_relation',)
       
    65 
       
    66     def __call__(self):
       
    67         session = self.cw_req
       
    68         eidfrom = self.eidfrom
       
    69         eidto = self.eidto
       
    70         state = previous_state(session, eidfrom)
       
    71         etype = session.describe(eidfrom)[0]
       
    72         if not (session.is_super_session or 'managers' in session.user.groups):
       
    73             if not state is None:
       
    74                 entity = session.entity_from_eid(eidfrom)
       
    75                 # we should find at least one transition going to this state
       
    76                 try:
       
    77                     iter(state.transitions(entity, eidto)).next()
       
    78                 except StopIteration:
       
    79                     msg = session._('transition is not allowed')
       
    80                     raise ValidationError(eidfrom, {'in_state': msg})
       
    81             else:
       
    82                 # not a transition
       
    83                 # check state is initial state if the workflow defines one
       
    84                 isrset = session.unsafe_execute('Any S WHERE ET initial_state S, ET name %(etype)s',
       
    85                                                 {'etype': etype})
       
    86                 if isrset and not eidto == isrset[0][0]:
       
    87                     msg = session._('not the initial state for this entity')
       
    88                     raise ValidationError(eidfrom, {'in_state': msg})
       
    89         eschema = session.repo.schema[etype]
       
    90         if not 'wf_info_for' in eschema.object_relations():
       
    91             # workflow history not activated for this entity type
       
    92             return
       
    93         rql = 'INSERT TrInfo T: T wf_info_for E, T to_state DS, T comment %(comment)s'
       
    94         args = {'comment': session.get_shared_data('trcomment', None, pop=True),
       
    95                 'e': eidfrom, 'ds': eidto}
       
    96         cformat = session.get_shared_data('trcommentformat', None, pop=True)
       
    97         if cformat is not None:
       
    98             args['comment_format'] = cformat
       
    99             rql += ', T comment_format %(comment_format)s'
       
   100         restriction = ['DS eid %(ds)s, E eid %(e)s']
       
   101         if not state is None: # not a transition
       
   102             rql += ', T from_state FS'
       
   103             restriction.append('FS eid %(fs)s')
       
   104             args['fs'] = state.eid
       
   105         rql = '%s WHERE %s' % (rql, ', '.join(restriction))
       
   106         session.unsafe_execute(rql, args, 'e')