hooks/workflow.py
branchstable
changeset 5030 5238d9a8dfee
parent 4835 13b0b96d7982
child 5056 5de07c77d73f
equal deleted inserted replaced
5029:f7709d28fb79 5030:5238d9a8dfee
     6 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
     6 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
     7 """
     7 """
     8 __docformat__ = "restructuredtext en"
     8 __docformat__ = "restructuredtext en"
     9 
     9 
    10 from datetime import datetime
    10 from datetime import datetime
       
    11 
       
    12 from yams.schema import role_name
    11 
    13 
    12 from cubicweb import RepositoryError, ValidationError
    14 from cubicweb import RepositoryError, ValidationError
    13 from cubicweb.interfaces import IWorkflowable
    15 from cubicweb.interfaces import IWorkflowable
    14 from cubicweb.selectors import implements
    16 from cubicweb.selectors import implements
    15 from cubicweb.server import hook
    17 from cubicweb.server import hook
    71         # transaction
    73         # transaction
    72         mainwf = entity.main_workflow
    74         mainwf = entity.main_workflow
    73         if mainwf.eid == self.wfeid:
    75         if mainwf.eid == self.wfeid:
    74             deststate = mainwf.initial
    76             deststate = mainwf.initial
    75             if not deststate:
    77             if not deststate:
       
    78                 qname = role_name('custom_workflow', 'subject')
    76                 msg = session._('workflow has no initial state')
    79                 msg = session._('workflow has no initial state')
    77                 raise ValidationError(entity.eid, {'custom_workflow': msg})
    80                 raise ValidationError(entity.eid, {qname: msg})
    78             if mainwf.state_by_eid(entity.current_state.eid):
    81             if mainwf.state_by_eid(entity.current_state.eid):
    79                 # nothing to do
    82                 # nothing to do
    80                 return
    83                 return
    81             # if there are no history, simply go to new workflow's initial state
    84             # if there are no history, simply go to new workflow's initial state
    82             if not entity.workflow_history:
    85             if not entity.workflow_history:
    95     def precommit_event(self):
    98     def precommit_event(self):
    96         tr = self.session.entity_from_eid(self.treid)
    99         tr = self.session.entity_from_eid(self.treid)
    97         outputs = set()
   100         outputs = set()
    98         for ep in tr.subworkflow_exit:
   101         for ep in tr.subworkflow_exit:
    99             if ep.subwf_state.eid in outputs:
   102             if ep.subwf_state.eid in outputs:
       
   103                 qname = role_name('subworkflow_exit', 'subject')
   100                 msg = self.session._("can't have multiple exits on the same state")
   104                 msg = self.session._("can't have multiple exits on the same state")
   101                 raise ValidationError(self.treid, {'subworkflow_exit': msg})
   105                 raise ValidationError(self.treid, {qname: msg})
   102             outputs.add(ep.subwf_state.eid)
   106             outputs.add(ep.subwf_state.eid)
   103 
   107 
   104 
   108 
   105 class _SubWorkflowExitOp(hook.Operation):
   109 class _SubWorkflowExitOp(hook.Operation):
   106 
   110 
   110         trinfo = self.trinfo
   114         trinfo = self.trinfo
   111         # we're in a subworkflow, check if we've reached an exit point
   115         # we're in a subworkflow, check if we've reached an exit point
   112         wftr = forentity.subworkflow_input_transition()
   116         wftr = forentity.subworkflow_input_transition()
   113         if wftr is None:
   117         if wftr is None:
   114             # inconsistency detected
   118             # inconsistency detected
       
   119             qname = role_name('to_state', 'subject')
   115             msg = session._("state doesn't belong to entity's current workflow")
   120             msg = session._("state doesn't belong to entity's current workflow")
   116             raise ValidationError(self.trinfo.eid, {'to_state': msg})
   121             raise ValidationError(self.trinfo.eid, {'to_state': msg})
   117         tostate = wftr.get_exit_point(forentity, trinfo['to_state'])
   122         tostate = wftr.get_exit_point(forentity, trinfo['to_state'])
   118         if tostate is not None:
   123         if tostate is not None:
   119             # reached an exit point
   124             # reached an exit point
   164         entity = self.entity
   169         entity = self.entity
   165         # first retreive entity to which the state change apply
   170         # first retreive entity to which the state change apply
   166         try:
   171         try:
   167             foreid = entity['wf_info_for']
   172             foreid = entity['wf_info_for']
   168         except KeyError:
   173         except KeyError:
       
   174             qname = role_name('wf_info_for', 'subject')
   169             msg = session._('mandatory relation')
   175             msg = session._('mandatory relation')
   170             raise ValidationError(entity.eid, {'wf_info_for': msg})
   176             raise ValidationError(entity.eid, {qname: msg})
   171         forentity = session.entity_from_eid(foreid)
   177         forentity = session.entity_from_eid(foreid)
   172         # then check it has a workflow set, unless we're in the process of changing
   178         # then check it has a workflow set, unless we're in the process of changing
   173         # entity's workflow
   179         # entity's workflow
   174         if session.transaction_data.get((forentity.eid, 'customwf')):
   180         if session.transaction_data.get((forentity.eid, 'customwf')):
   175             wfeid = session.transaction_data[(forentity.eid, 'customwf')]
   181             wfeid = session.transaction_data[(forentity.eid, 'customwf')]
   193             treid = entity['by_transition']
   199             treid = entity['by_transition']
   194         except KeyError:
   200         except KeyError:
   195             # no transition set, check user is a manager and destination state
   201             # no transition set, check user is a manager and destination state
   196             # is specified (and valid)
   202             # is specified (and valid)
   197             if not cowpowers:
   203             if not cowpowers:
       
   204                 qname = role_name('by_transition', 'subject')
   198                 msg = session._('mandatory relation')
   205                 msg = session._('mandatory relation')
   199                 raise ValidationError(entity.eid, {'by_transition': msg})
   206                 raise ValidationError(entity.eid, {qname: msg})
   200             deststateeid = entity.get('to_state')
   207             deststateeid = entity.get('to_state')
   201             if not deststateeid:
   208             if not deststateeid:
       
   209                 qname = role_name('by_transition', 'subject')
   202                 msg = session._('mandatory relation')
   210                 msg = session._('mandatory relation')
   203                 raise ValidationError(entity.eid, {'by_transition': msg})
   211                 raise ValidationError(entity.eid, {qname: msg})
   204             deststate = wf.state_by_eid(deststateeid)
   212             deststate = wf.state_by_eid(deststateeid)
   205             if deststate is None:
   213             if deststate is None:
       
   214                 qname = role_name('to_state', 'subject')
   206                 msg = session._("state doesn't belong to entity's workflow")
   215                 msg = session._("state doesn't belong to entity's workflow")
   207                 raise ValidationError(entity.eid, {'to_state': msg})
   216                 raise ValidationError(entity.eid, {qname: msg})
   208         else:
   217         else:
   209             # check transition is valid and allowed, unless we're coming back
   218             # check transition is valid and allowed, unless we're coming back
   210             # from subworkflow
   219             # from subworkflow
   211             tr = session.entity_from_eid(treid)
   220             tr = session.entity_from_eid(treid)
   212             if swtr is None:
   221             if swtr is None:
       
   222                 qname = role_name('by_transition', 'subject')
   213                 if tr is None:
   223                 if tr is None:
   214                     msg = session._("transition doesn't belong to entity's workflow")
   224                     msg = session._("transition doesn't belong to entity's workflow")
   215                     raise ValidationError(entity.eid, {'by_transition': msg})
   225                     raise ValidationError(entity.eid, {qname: msg})
   216                 if not tr.has_input_state(fromstate):
   226                 if not tr.has_input_state(fromstate):
   217                     msg = session._("transition %(tr)s isn't allowed from %(st)s") % {
   227                     msg = session._("transition %(tr)s isn't allowed from %(st)s") % {
   218                         'tr': session._(tr.name), 'st': session._(fromstate.name)}
   228                         'tr': session._(tr.name), 'st': session._(fromstate.name)}
   219                     raise ValidationError(entity.eid, {'by_transition': msg})
   229                     raise ValidationError(entity.eid, {qname: msg})
   220                 if not tr.may_be_fired(foreid):
   230                 if not tr.may_be_fired(foreid):
   221                     msg = session._("transition may not be fired")
   231                     msg = session._("transition may not be fired")
   222                     raise ValidationError(entity.eid, {'by_transition': msg})
   232                     raise ValidationError(entity.eid, {qname: msg})
   223             if entity.get('to_state'):
   233             if entity.get('to_state'):
   224                 deststateeid = entity['to_state']
   234                 deststateeid = entity['to_state']
   225                 if not cowpowers and deststateeid != tr.destination(forentity).eid:
   235                 if not cowpowers and deststateeid != tr.destination(forentity).eid:
       
   236                     qname = role_name('by_transition', 'subject')
   226                     msg = session._("transition isn't allowed")
   237                     msg = session._("transition isn't allowed")
   227                     raise ValidationError(entity.eid, {'by_transition': msg})
   238                     raise ValidationError(entity.eid, {qname: msg})
   228                 if swtr is None:
   239                 if swtr is None:
   229                     deststate = session.entity_from_eid(deststateeid)
   240                     deststate = session.entity_from_eid(deststateeid)
   230                     if not cowpowers and deststate is None:
   241                     if not cowpowers and deststate is None:
       
   242                         qname = role_name('to_state', 'subject')
   231                         msg = session._("state doesn't belong to entity's workflow")
   243                         msg = session._("state doesn't belong to entity's workflow")
   232                         raise ValidationError(entity.eid, {'to_state': msg})
   244                         raise ValidationError(entity.eid, {qname: msg})
   233             else:
   245             else:
   234                 deststateeid = tr.destination(forentity).eid
   246                 deststateeid = tr.destination(forentity).eid
   235         # everything is ok, add missing information on the trinfo entity
   247         # everything is ok, add missing information on the trinfo entity
   236         entity['from_state'] = fromstate.eid
   248         entity['from_state'] = fromstate.eid
   237         entity['to_state'] = deststateeid
   249         entity['to_state'] = deststateeid
   277             raise ValidationError(entity.eid, {None: msg})
   289             raise ValidationError(entity.eid, {None: msg})
   278         for wf in mainwf.iter_workflows():
   290         for wf in mainwf.iter_workflows():
   279             if wf.state_by_eid(self.eidto):
   291             if wf.state_by_eid(self.eidto):
   280                 break
   292                 break
   281         else:
   293         else:
       
   294             qname = role_name('in_state', 'subject')
   282             msg = session._("state doesn't belong to entity's workflow. You may "
   295             msg = session._("state doesn't belong to entity's workflow. You may "
   283                             "want to set a custom workflow for this entity first.")
   296                             "want to set a custom workflow for this entity first.")
   284             raise ValidationError(self.eidfrom, {'in_state': msg})
   297             raise ValidationError(self.eidfrom, {qname: msg})
   285         if entity.current_workflow and wf.eid != entity.current_workflow.eid:
   298         if entity.current_workflow and wf.eid != entity.current_workflow.eid:
       
   299             qname = role_name('in_state', 'subject')
   286             msg = session._("state doesn't belong to entity's current workflow")
   300             msg = session._("state doesn't belong to entity's current workflow")
   287             raise ValidationError(self.eidfrom, {'in_state': msg})
   301             raise ValidationError(self.eidfrom, {qname: msg})
   288 
   302 
   289 
   303 
   290 class SetModificationDateOnStateChange(WorkflowHook):
   304 class SetModificationDateOnStateChange(WorkflowHook):
   291     """update entity's modification date after changing its state"""
   305     """update entity's modification date after changing its state"""
   292     __regid__ = 'wfsyncmdate'
   306     __regid__ = 'wfsyncmdate'