src/pyams_workflow/workflow.py
changeset 37 2b8636f18d39
parent 25 8a67d57d53c6
child 41 19622ab599fd
equal deleted inserted replaced
36:a84e933260c6 37:2b8636f18d39
    21     WorkflowTransitionEvent, ConditionFailedError, NoTransitionAvailableError, AmbiguousTransitionError
    21     WorkflowTransitionEvent, ConditionFailedError, NoTransitionAvailableError, AmbiguousTransitionError
    22 
    22 
    23 # import packages
    23 # import packages
    24 from pyams_utils.adapter import adapter_config
    24 from pyams_utils.adapter import adapter_config
    25 from pyams_utils.registry import get_utility
    25 from pyams_utils.registry import get_utility
    26 from pyams_utils.request import check_request
    26 from pyams_utils.request import check_request, query_request
    27 from pyams_utils.traversing import get_parent
    27 from pyams_utils.traversing import get_parent
    28 from pyams_utils.vocabulary import vocabulary_config
    28 from pyams_utils.vocabulary import vocabulary_config
    29 from pyramid.httpexceptions import HTTPUnauthorized
    29 from pyramid.httpexceptions import HTTPUnauthorized
    30 from pyramid.threadlocal import get_current_registry
    30 from zope.component.globalregistry import getGlobalSiteManager
    31 from zope.componentvocabulary.vocabulary import UtilityVocabulary
    31 from zope.componentvocabulary.vocabulary import UtilityVocabulary
    32 from zope.interface import implementer
    32 from zope.interface import implementer
    33 from zope.lifecycleevent import ObjectModifiedEvent
    33 from zope.lifecycleevent import ObjectModifiedEvent
    34 
    34 
    35 from pyams_workflow import _
    35 from pyams_workflow import _
    85                  readonly_states=None,
    85                  readonly_states=None,
    86                  protected_states=None,
    86                  protected_states=None,
    87                  manager_states=None,
    87                  manager_states=None,
    88                  published_states=None,
    88                  published_states=None,
    89                  waiting_states=None,
    89                  waiting_states=None,
    90                  retired_states=None):
    90                  retired_states=None,
       
    91                  auto_retired_state=None):
    91         self.refresh(transitions)
    92         self.refresh(transitions)
    92         self.states = states
    93         self.states = states
    93         self.initial_state = initial_state
    94         self.initial_state = initial_state
    94         self.update_states = set(update_states) if update_states else set()
    95         self.update_states = set(update_states) if update_states else set()
    95         self.readonly_states = set(readonly_states) if readonly_states else set()
    96         self.readonly_states = set(readonly_states) if readonly_states else set()
    96         self.protected_states = set(protected_states) if protected_states else set()
    97         self.protected_states = set(protected_states) if protected_states else set()
    97         self.manager_states = set(manager_states) if manager_states else set()
    98         self.manager_states = set(manager_states) if manager_states else set()
    98         self.published_states = set(published_states) if published_states else set()
    99         self.published_states = set(published_states) if published_states else set()
    99         self.waiting_states = set(waiting_states) if waiting_states else set()
   100         self.waiting_states = set(waiting_states) if waiting_states else set()
   100         self.retired_states = set(retired_states) if retired_states else set()
   101         self.retired_states = set(retired_states) if retired_states else set()
       
   102         self.auto_retired_state = auto_retired_state
   101 
   103 
   102     def _register(self, transition):
   104     def _register(self, transition):
   103         transitions = self._sources.setdefault(transition.source, {})
   105         transitions = self._sources.setdefault(transition.source, {})
   104         transitions[transition.transition_id] = transition
   106         transitions[transition.transition_id] = transition
   105         self._id_transitions[transition.transition_id] = transition
   107         self._id_transitions[transition.transition_id] = transition
   149 
   151 
   150     @property
   152     @property
   151     def name(self):
   153     def name(self):
   152         return self.parent.workflow_name
   154         return self.parent.workflow_name
   153 
   155 
   154     def fire_transition(self, transition_id, comment=None, side_effect=None, check_security=True):
   156     def fire_transition(self, transition_id, comment=None, side_effect=None, check_security=True, principal=None):
   155         versions = IWorkflowVersions(self.parent)
   157         versions = IWorkflowVersions(self.parent)
   156         state = IWorkflowState(self.context)
   158         state = IWorkflowState(self.context)
   157         # this raises InvalidTransitionError if id is invalid for current state
   159         # this raises InvalidTransitionError if id is invalid for current state
   158         transition = self.wf.get_transition(state.state, transition_id)
   160         transition = self.wf.get_transition(state.state, transition_id)
   159         # check whether we may execute this workflow transition
   161         # check whether we may execute this workflow transition
   163                 raise HTTPUnauthorized()
   165                 raise HTTPUnauthorized()
   164         # now make sure transition can still work in this context
   166         # now make sure transition can still work in this context
   165         if not transition.condition(self, self.context):
   167         if not transition.condition(self, self.context):
   166             raise ConditionFailedError()
   168             raise ConditionFailedError()
   167         # perform action, return any result as new version
   169         # perform action, return any result as new version
       
   170         if principal is None:
       
   171             request = query_request()
       
   172             if request is not None:
       
   173                 principal = request.principal
   168         result = transition.action(self, self.context)
   174         result = transition.action(self, self.context)
   169         if result is not None:
   175         if result is not None:
   170             # clear result history
       
   171             IWorkflowState(result).history.clear()
       
   172             # stamp it with version
   176             # stamp it with version
   173             versions.add_version(result, transition.destination)
   177             versions.add_version(result, transition.destination, principal)
   174             # execute any side effect:
   178             # execute any side effect:
   175             if side_effect is not None:
   179             if side_effect is not None:
   176                 side_effect(result)
   180                 side_effect(result)
   177             event = WorkflowVersionTransitionEvent(result, self.wf, self.context,
   181             event = WorkflowVersionTransitionEvent(result, self.wf, principal, self.context,
   178                                                    transition.source,
   182                                                    transition.source,
   179                                                    transition.destination,
   183                                                    transition.destination,
   180                                                    transition, comment)
   184                                                    transition, comment)
   181         else:
   185         else:
   182             versions.set_state(state.version_id, transition.destination)
   186             versions.set_state(state.version_id, transition.destination, principal)
   183             # execute any side effect
   187             # execute any side effect
   184             if side_effect is not None:
   188             if side_effect is not None:
   185                 side_effect(self.context)
   189                 side_effect(self.context)
   186             event = WorkflowTransitionEvent(self.context, self.wf,
   190             event = WorkflowTransitionEvent(self.context, self.wf, principal,
   187                                             transition.source,
   191                                             transition.source,
   188                                             transition.destination,
   192                                             transition.destination,
   189                                             transition, comment)
   193                                             transition, comment)
   190         # change state of context or new object
   194         # change state of context or new object
   191         registry = get_current_registry()
   195         registry = getGlobalSiteManager()
   192         registry.notify(event)
   196         registry.notify(event)
   193         # send modified event for original or new object
   197         # send modified event for original or new object
   194         if result is None:
   198         if result is None:
   195             registry.notify(ObjectModifiedEvent(self.context))
   199             registry.notify(ObjectModifiedEvent(self.context))
   196         else:
   200         else:
   197             registry.notify(ObjectModifiedEvent(result))
   201             registry.notify(ObjectModifiedEvent(result))
   198         return result
   202         return result
   199 
   203 
   200     def fire_transition_toward(self, state, comment=None, side_effect=None, check_security=True):
   204     def fire_transition_toward(self, state, comment=None, side_effect=None, check_security=True, principal=None):
       
   205         current_state = IWorkflowState(self.context)
   201         transition_ids = self.get_fireable_transition_ids_toward(state, check_security)
   206         transition_ids = self.get_fireable_transition_ids_toward(state, check_security)
   202         if not transition_ids:
   207         if not transition_ids:
   203             raise NoTransitionAvailableError(self.state(self.context).get_state(), state)
   208             raise NoTransitionAvailableError(current_state.state, state)
   204         if len(transition_ids) != 1:
   209         if len(transition_ids) != 1:
   205             raise AmbiguousTransitionError(self.state(self.context).get_state(), state)
   210             raise AmbiguousTransitionError(current_state.state, state)
   206         return self.fire_transition(transition_ids[0], comment, side_effect, check_security)
   211         return self.fire_transition(transition_ids[0], comment, side_effect, check_security, principal)
   207 
   212 
   208     def fire_transition_for_versions(self, state, transition_id, comment=None):
   213     def fire_transition_for_versions(self, state, transition_id, comment=None, principal=None):
   209         versions = IWorkflowVersions(self.parent)
   214         versions = IWorkflowVersions(self.parent)
   210         for version in versions.get_versions(state):
   215         for version in versions.get_versions(state):
   211             IWorkflowInfo(version).fire_transition(transition_id, comment)
   216             IWorkflowInfo(version).fire_transition(transition_id, comment, principal=principal)
   212 
   217 
   213     def fire_automatic(self):
   218     def fire_automatic(self):
   214         for transition_id in self.get_automatic_transition_ids():
   219         for transition_id in self.get_automatic_transition_ids():
   215             try:
   220             try:
   216                 self.fire_transition(transition_id)
   221                 self.fire_transition(transition_id)