Commit bf54b83b authored by Jean-Paul Smets's avatar Jean-Paul Smets

New WorkflowMethod implementation. This new implementation solves most issues...

New WorkflowMethod implementation. This new implementation solves most issues which existed previously and related to multiple workflows for a single workflow method ID. It should also be faster but will require some more work so that changes in workflow definition are reflected automatically in workflow methods registration. Added support in interaction workflows for once per transaction execution and regular expressions in trigger method IDs.

git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@15825 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 843d058b
...@@ -32,7 +32,7 @@ from Products.ERP5Type.Permissions import ManagePortal ...@@ -32,7 +32,7 @@ from Products.ERP5Type.Permissions import ManagePortal
from Products.DCWorkflow.ContainerTab import ContainerTab from Products.DCWorkflow.ContainerTab import ContainerTab
from Products.DCWorkflow.Guard import Guard from Products.DCWorkflow.Guard import Guard
from Products.DCWorkflow.Expression import Expression from Products.DCWorkflow.Expression import Expression
from Products.DCWorkflow.Transitions import TRIGGER_AUTOMATIC, TRIGGER_WORKFLOW_METHOD from Products.DCWorkflow.Transitions import TRIGGER_WORKFLOW_METHOD
from Products.ERP5 import _dtmldir from Products.ERP5 import _dtmldir
from Products.ERP5Type.Base import _aq_reset from Products.ERP5Type.Base import _aq_reset
...@@ -57,6 +57,7 @@ class InteractionDefinition (SimpleItem): ...@@ -57,6 +57,7 @@ class InteractionDefinition (SimpleItem):
activate_script_name = () # Executed as activity activate_script_name = () # Executed as activity
method_id = () method_id = ()
portal_type_filter = None portal_type_filter = None
once_per_transaction = False
manage_options = ( manage_options = (
{'label': 'Properties', 'action': 'manage_properties'}, {'label': 'Properties', 'action': 'manage_properties'},
...@@ -116,6 +117,7 @@ class InteractionDefinition (SimpleItem): ...@@ -116,6 +117,7 @@ class InteractionDefinition (SimpleItem):
def setProperties(self, title, def setProperties(self, title,
portal_type_filter=None, portal_type_filter=None,
trigger_type=TRIGGER_WORKFLOW_METHOD, trigger_type=TRIGGER_WORKFLOW_METHOD,
once_per_transaction=False,
script_name=(), script_name=(),
after_script_name=(), after_script_name=(),
activate_script_name=(), activate_script_name=(),
...@@ -144,6 +146,7 @@ class InteractionDefinition (SimpleItem): ...@@ -144,6 +146,7 @@ class InteractionDefinition (SimpleItem):
self.title = str(title) self.title = str(title)
self.description = str(description) self.description = str(description)
self.trigger_type = int(trigger_type) self.trigger_type = int(trigger_type)
self.once_per_transaction = bool(once_per_transaction)
self.script_name = script_name self.script_name = script_name
self.after_script_name = after_script_name self.after_script_name = after_script_name
self.activate_script_name = activate_script_name self.activate_script_name = activate_script_name
......
...@@ -21,7 +21,7 @@ import App ...@@ -21,7 +21,7 @@ import App
from AccessControl import getSecurityManager, ClassSecurityInfo from AccessControl import getSecurityManager, ClassSecurityInfo
from Products.CMFCore.utils import getToolByName from Products.CMFCore.utils import getToolByName
from Products.DCWorkflow.DCWorkflow import DCWorkflowDefinition from Products.DCWorkflow.DCWorkflow import DCWorkflowDefinition
from Products.DCWorkflow.Transitions import TRIGGER_AUTOMATIC, TRIGGER_WORKFLOW_METHOD from Products.DCWorkflow.Transitions import TRIGGER_WORKFLOW_METHOD
from Products.DCWorkflow.Expression import StateChangeInfo, createExprContext from Products.DCWorkflow.Expression import StateChangeInfo, createExprContext
from Products.CMFCore.WorkflowTool import addWorkflowFactory from Products.CMFCore.WorkflowTool import addWorkflowFactory
from Products.CMFActivity.ActiveObject import ActiveObject from Products.CMFActivity.ActiveObject import ActiveObject
...@@ -168,21 +168,23 @@ class InteractionWorkflowDefinition (DCWorkflowDefinition, ActiveObject): ...@@ -168,21 +168,23 @@ class InteractionWorkflowDefinition (DCWorkflowDefinition, ActiveObject):
value = vdef.default_value value = vdef.default_value
return value return value
security.declarePrivate('isWorkflowMethodSupported') security.declarePrivate('isWorkflowMethodSupported')
def isWorkflowMethodSupported(self, ob, method_id): def isWorkflowMethodSupported(self, ob, method_id):
''' '''
Returns a true value if the given workflow is Returns a true value if the given workflow is
automatic with the propper method_id automatic with the propper method_id
NOTE: this method is not used in ERP5 because
of transition_list approach
''' '''
#return 0 # Why this line ??? # I guess it should be used
for t in self.interactions.values(): for t in self.interactions.values():
if t.trigger_type == TRIGGER_WORKFLOW_METHOD: if t.trigger_type == TRIGGER_WORKFLOW_METHOD:
if method_id in t.method_id: if method_id in t.method_id:
if ((t.portal_type_filter is None or ob.getPortalType() in t.portal_type_filter) if ((t.portal_type_filter is None or ob.getPortalType() in t.portal_type_filter)
and self._checkTransitionGuard(t, ob)): and self._checkTransitionGuard(t, ob)):
return 1 return 1
return 0 return 0
security.declarePrivate('wrapWorkflowMethod') security.declarePrivate('wrapWorkflowMethod')
...@@ -193,104 +195,111 @@ class InteractionWorkflowDefinition (DCWorkflowDefinition, ActiveObject): ...@@ -193,104 +195,111 @@ class InteractionWorkflowDefinition (DCWorkflowDefinition, ActiveObject):
''' '''
return return
security.declarePrivate('notifyWorkflowMethod')
def notifyWorkflowMethod(self, ob, action, args=None, kw=None, transition_list=None):
"""
InteractionWorkflow is stateless. Thus, this function should do nothing.
"""
return
security.declarePrivate('notifyBefore') security.declarePrivate('notifyBefore')
def notifyBefore(self, ob, action, args=None, kw=None): def notifyBefore(self, ob, action, args=None, kw=None, transition_list=None):
''' '''
Notifies this workflow of an action before it happens, Notifies this workflow of an action before it happens,
allowing veto by exception. Unless an exception is thrown, either allowing veto by exception. Unless an exception is thrown, either
a notifySuccess() or notifyException() can be expected later on. a notifySuccess() or notifyException() can be expected later on.
The action usually corresponds to a method name. The action usually corresponds to a method name.
''' '''
for t in self.interactions.values(): if not transition_list: return
tdef = None # Wrap args into kw since this is the only way
if t.trigger_type == TRIGGER_AUTOMATIC: # to be compatible with DCWorkflow
if t.portal_type_filter is None: # A better approach consists in extending DCWorkflow
tdef = t kw = kw.copy()
elif ob.getPortalType() in t.portal_type_filter: kw['workflow_method_args'] = args
tdef = t filtered_transition_list = []
elif t.trigger_type == TRIGGER_WORKFLOW_METHOD:
if action in t.method_id: for t_id in transition_list:
if t.portal_type_filter is None: tdef = self.interactions[t_id]
tdef = t if tdef.trigger_type == TRIGGER_WORKFLOW_METHOD:
elif ob.getPortalType() in t.portal_type_filter: if (tdef.portal_type_filter is None or \
tdef = t ob.getPortalType() in tdef.portal_type_filter) and \
if tdef is not None: self._checkTransitionGuard(tdef, ob, **kw):
former_status = self._getStatusOf(ob) filtered_transition_list.append(tdef.id)
# Execute the "before" script. former_status = self._getStatusOf(ob)
for script_name in tdef.script_name: # Execute the "before" script.
script = self.scripts[script_name] for script_name in tdef.script_name:
# Pass lots of info to the script in a single parameter. script = self.scripts[script_name]
sci = StateChangeInfo( # Pass lots of info to the script in a single parameter.
ob, self, former_status, tdef, None, None, kwargs=kw) sci = StateChangeInfo(
script(sci) # May throw an exception. ob, self, former_status, tdef, None, None, kwargs=kw)
script(sci) # May throw an exception
return filtered_transition_list
security.declarePrivate('notifySuccess') security.declarePrivate('notifySuccess')
def notifySuccess(self, ob, action, result, args=None, kw=None): def notifySuccess(self, ob, action, result, args=None, kw=None, transition_list=None):
''' '''
Notifies this workflow that an action has taken place. Notifies this workflow that an action has taken place.
''' '''
# initialize variables if not transition_list: return
econtext = None
sci = None kw = kw.copy()
for t in self.interactions.values(): kw['workflow_method_args'] = args
tdef = None kw['workflow_method_result'] = result
if t.trigger_type == TRIGGER_AUTOMATIC:
if t.portal_type_filter is None: for t_id in transition_list:
tdef = t tdef = self.interactions[t_id]
elif ob.getPortalType() in t.portal_type_filter: if tdef.trigger_type == TRIGGER_WORKFLOW_METHOD:
tdef = t if (tdef.portal_type_filter is None or \
elif t.trigger_type == TRIGGER_WORKFLOW_METHOD: ob.getPortalType() in tdef.portal_type_filter):
if action in t.method_id: # Initialize variables
if t.portal_type_filter is None: former_status = self._getStatusOf(ob)
tdef = t econtext = None
elif ob.getPortalType() in t.portal_type_filter: sci = None
tdef = t
if tdef is not None: # Update variables.
# Update variables. tdef_exprs = tdef.var_exprs
former_status = self._getStatusOf(ob) if tdef_exprs is None: tdef_exprs = {}
tdef_exprs = tdef.var_exprs status = {}
if tdef_exprs is None: tdef_exprs = {} for id, vdef in self.variables.items():
status = {} if not vdef.for_status:
for id, vdef in self.variables.items(): continue
if not vdef.for_status: expr = None
continue if tdef_exprs.has_key(id):
expr = None expr = tdef_exprs[id]
if tdef_exprs.has_key(id): elif not vdef.update_always and former_status.has_key(id):
expr = tdef_exprs[id] # Preserve former value
elif not vdef.update_always and former_status.has_key(id): value = former_status[id]
# Preserve former value else:
value = former_status[id] if vdef.default_expr is not None:
else: expr = vdef.default_expr
if vdef.default_expr is not None: else:
expr = vdef.default_expr value = vdef.default_value
else: if expr is not None:
value = vdef.default_value # Evaluate an expression.
if expr is not None: if econtext is None:
# Evaluate an expression. # Lazily create the expression context.
if econtext is None: if sci is None:
# Lazily create the expression context. sci = StateChangeInfo(
if sci is None: ob, self, former_status, tdef,
sci = StateChangeInfo( None, None, None)
ob, self, former_status, tdef, econtext = createExprContext(sci)
None, None, None) value = expr(econtext)
econtext = createExprContext(sci) status[id] = value
value = expr(econtext)
status[id] = value # Execute the "after" script.
for script_name in tdef.after_script_name:
# Execute "after" scripts script = self.scripts[script_name]
for script_name in tdef.after_script_name: # Pass lots of info to the script in a single parameter.
script = self.scripts[script_name] sci = StateChangeInfo(
# Pass lots of info to the script in a single parameter. ob, self, former_status, tdef, None, None, kwargs=kw)
sci = StateChangeInfo( script(sci) # May throw an exception
ob, self, status, tdef, None, None, kwargs=kw)
script(sci) # May throw an exception. # Execute "activity" scripts
for script_name in tdef.activate_script_name:
# Execute "activity" scripts self.activate(activity='SQLQueue')\
for script_name in tdef.activate_script_name: .activeScript(script_name, ob.getRelativeUrl(), status, tdef.id)
self.activate(activity='SQLQueue')\
.activeScript(script_name, ob.getRelativeUrl(), status, tdef.id)
security.declarePrivate('activeScript') security.declarePrivate('activeScript')
def activeScript(self, script_name, ob_url, status, tdef_id): def activeScript(self, script_name, ob_url, status, tdef_id):
script = self.scripts[script_name] script = self.scripts[script_name]
...@@ -302,7 +311,7 @@ class InteractionWorkflowDefinition (DCWorkflowDefinition, ActiveObject): ...@@ -302,7 +311,7 @@ class InteractionWorkflowDefinition (DCWorkflowDefinition, ActiveObject):
def _getWorkflowStateOf(self, ob, id_only=0): def _getWorkflowStateOf(self, ob, id_only=0):
return None return None
Globals.InitializeClass(InteractionWorkflowDefinition) Globals.InitializeClass(InteractionWorkflowDefinition)
addWorkflowFactory(InteractionWorkflowDefinition, id='interaction_workflow', addWorkflowFactory(InteractionWorkflowDefinition, id='interaction_workflow',
......
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