Commit 3368c48e authored by Jean-Paul Smets's avatar Jean-Paul Smets

Fixed the new implementatoin of workflow methods. There were two issues. The...

Fixed the new implementatoin of workflow methods. There were two issues. The first one is that "once only" was method based rather than transition based. It is now transition based which is the only way which makes sense (method based in not deterministic). The second issue is that we did not call isWorkflowMethodSupported and that changed the original behaviour. We now call isWorkflowMethodSupported to stick to original behaviour and added a comment to suggest providing an option for "strict" behaviour. The code still needs to be tested and is commited now because it is already a kind of improvement.

git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@15973 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 528d066f
...@@ -124,37 +124,55 @@ class WorkflowMethod(Method): ...@@ -124,37 +124,55 @@ class WorkflowMethod(Method):
# should ne executed. - XXX # should ne executed. - XXX
return apply(self._m, (instance,) + args, kw) return apply(self._m, (instance,) + args, kw)
call_method = 0 # By default, this method was never called # New implementation does not use any longer wrapWorkflowMethod
if self._invoke_once: # but directly calls the workflow methods
# Check if this method has already been called in this transaction wf = getToolByName(instance, 'portal_workflow', None)
# (only check this if we use once only workflow methods)
call_method_key = ('Products.ERP5Type.Base.WorkflowMethod.__call__', self._id, instance.getPhysicalPath()) # Build a list of transitions which may need to be invoked
instance_path = instance.getPhysicalPath()
portal_type = instance.portal_type
transactional_variable = getTransactionalVariable(instance) transactional_variable = getTransactionalVariable(instance)
invoke_once_item_list = self._invoke_once.get(portal_type, {}).items()
valid_invoke_once_item_list = []
# Only keep those transitions which were never invoked
for wf_id, transition_list in invoke_once_item_list:
valid_transition_list = []
for transition_id in transition_list:
once_transition_key = ('Products.ERP5Type.Base.WorkflowMethod.__call__',
wf_id, transition_id, instance_path)
try: try:
call_method = transactional_variable[call_method_key] already_called_transition = transactional_variable[once_transition_key]
except KeyError: except KeyError:
transactional_variable[call_method_key] = 1 already_called_transition = 0
transactional_variable[once_transition_key] = 1
if not already_called_transition:
valid_transition_list.append(transition_id)
if valid_transition_list:
valid_invoke_once_item_list.append((wf_id, valid_transition_list))
candidate_transition_item_list = valid_invoke_once_item_list + \
self._invoke_once.get(portal_type, {}).items()
if call_method and not self._invoke_always: # Try to return immediately if there are no transition to invoke
# Try to return immediately if there are no invoke always workflow methods if not candidate_transition_item_list:
return apply(self.__dict__['_m'], (instance,) + args, kw) return apply(self.__dict__['_m'], (instance,) + args, kw)
# Invoke transitions on appropriate workflow # Prepare a list of transitions which should be invoked
portal_type = instance.portal_type # Access by attribute to prevent recursion # this list is based on the results of isWorkflowMethodSupported
if call_method: # XXX - the behaviour of isWorkflowMethodSupported should be extended
candidate_transition_item_list = self._invoke_always.get(portal_type, {}).items() # some day so that a workflow method raises an exception
else: # when it is invoked from a workflow state which does
candidate_transition_item_list = self._invoke_always.get(portal_type, {}).items() + \ # not support it or whenever guards reject it
self._invoke_once.get(portal_type, {}).items() valid_transition_item_list = []
for wf_id, transition_list in candidate_transition_item_list:
# New implementation does not use any longer wrapWorkflowMethod candidate_workflow = wf[wf_id]
wf = getToolByName(instance, 'portal_workflow', None) valid_list = [transition_id for transition_id in transition_list
if candidate_workflow.isWorkflowMethodSupported(instance, transition_id)]
if valid_list:
valid_transition_item_list.append((wf_id, valid_list))
# Call whatever must be called before changing states # Call whatever must be called before changing states
after_invoke_once = {}
for wf_id, transition_list in candidate_transition_item_list: for wf_id, transition_list in candidate_transition_item_list:
after_invoke_once[wf_id] = wf[wf_id].notifyBefore(instance, self._id, wf[wf_id].notifyBefore(instance, self._id, args=args, kw=kw, transition_list=transition_list)
args=args, kw=kw, transition_list=transition_list)
# Compute expected result # Compute expected result
result = apply(self.__dict__['_m'], (instance,) + args, kw) result = apply(self.__dict__['_m'], (instance,) + args, kw)
...@@ -184,14 +202,16 @@ class WorkflowMethod(Method): ...@@ -184,14 +202,16 @@ class WorkflowMethod(Method):
""" """
Transitions registered as always will be invoked always Transitions registered as always will be invoked always
""" """
self._invoke_always.setdefault(portal_type, {}).setdefault(workflow_id, []).append(transition_id) transition_list = self._invoke_always.setdefault(portal_type, {}).setdefault(workflow_id, [])
if transition_id not in transition_list: transition_list.append(transition_id)
def registerTransitionOncePerTransaction(self, portal_type, workflow_id, transition_id): def registerTransitionOncePerTransaction(self, portal_type, workflow_id, transition_id):
""" """
Transitions registered as one per transactions will be invoked Transitions registered as one per transactions will be invoked
only once per transaction only once per transaction
""" """
self._invoke_once.setdefault(portal_type, {}).setdefault(workflow_id, []).append(transition_id) transition_list = self._invoke_once.setdefault(portal_type, {}).setdefault(workflow_id, [])
if transition_id not in transition_list: transition_list.append(transition_id)
class ActionMethod(Method): class ActionMethod(Method):
...@@ -506,7 +526,9 @@ def initializePortalTypeDynamicWorkflowMethods(self, klass, ptype, prop_holder): ...@@ -506,7 +526,9 @@ def initializePortalTypeDynamicWorkflowMethods(self, klass, ptype, prop_holder):
(method_id, str(work_method_holder))) (method_id, str(work_method_holder)))
# XXX This part is (more or less...) a copy and paste # XXX This part is (more or less...) a copy and paste
# We need to run this part twice in order to handle interactions of interactions # We need to run this part twice in order to handle interactions of interactions
for wf in portal_workflow.getWorkflowsFor(self) * 2: # ex. an interaction workflow creates a workflow method which matches
# the regexp of another interaction workflow
for wf in portal_workflow.getWorkflowsFor(self) * 2: # This is really necesary
wf_id = wf.id wf_id = wf.id
if wf.__class__.__name__ in ('InteractionWorkflowDefinition', ): if wf.__class__.__name__ in ('InteractionWorkflowDefinition', ):
for tr_id in wf.interactions.objectIds(): for tr_id in wf.interactions.objectIds():
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment