From 2160a8045db55ab74bffba77153d5980d1f8157c Mon Sep 17 00:00:00 2001
From: Jean-Paul Smets <jp@nexedi.com>
Date: Thu, 1 Mar 2007 22:45:53 +0000
Subject: [PATCH] Pass method variables to workflow history. This change may be
 good or not. I do not know yet. Reviewing this code and patches is required.

git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@13171 20353a03-c40f-0410-a6d1-a30d3c3de9de
---
 product/ERP5Type/patches/DCWorkflow.py   | 44 +++++++++++++++++++-----
 product/ERP5Type/patches/WorkflowTool.py | 30 ++++++++++------
 2 files changed, 55 insertions(+), 19 deletions(-)

diff --git a/product/ERP5Type/patches/DCWorkflow.py b/product/ERP5Type/patches/DCWorkflow.py
index 92248918e2..450223389c 100644
--- a/product/ERP5Type/patches/DCWorkflow.py
+++ b/product/ERP5Type/patches/DCWorkflow.py
@@ -16,7 +16,8 @@
 
 from Globals import DTMLFile
 from Products.ERP5Type import _dtmldir
-from Products.DCWorkflow.DCWorkflow import DCWorkflowDefinition, StateChangeInfo, ObjectMoved, createExprContext, aq_parent, aq_inner
+from Products.DCWorkflow.DCWorkflow import DCWorkflowDefinition, StateChangeInfo, createExprContext
+from Products.DCWorkflow.DCWorkflow import ObjectDeleted, ObjectMoved, aq_parent, aq_inner
 from Products.DCWorkflow import DCWorkflow
 from Products.DCWorkflow.Transitions import TRIGGER_WORKFLOW_METHOD, TransitionDefinition
 from AccessControl import getSecurityManager, ClassSecurityInfo, ModuleSecurityInfo
@@ -30,8 +31,6 @@ from Products.ERP5Type.Utils import convertToMixedCase
 from string import join
 from zLOG import LOG
 
-
-
 # Patch WorkflowUIMixin to add description on workflows
 from Products.DCWorkflow.WorkflowUIMixin import WorkflowUIMixin as WorkflowUIMixin_class
 from Products.DCWorkflow.Guard import Guard
@@ -57,7 +56,6 @@ WorkflowUIMixin_class.setProperties = WorkflowUIMixin_setProperties
 WorkflowUIMixin_class.manage_properties = DTMLFile('workflow_properties', _dtmldir)
 
 
-
 def DCWorkflowDefinition_listGlobalActions(self, info):
     '''
     Allows this workflow to
@@ -144,7 +142,6 @@ DCWorkflow.ValidationFailed = ValidationFailed
 ModuleSecurityInfo('Products.DCWorkflow.DCWorkflow').declarePublic('ValidationFailed')
 
 
-
 # Patch excecuteTransition from DCWorkflowDefinition, to put ValidationFailed
 # error messages in workflow history.
 def DCWorkflowDefinition_executeTransition(self, ob, tdef=None, kwargs=None):
@@ -271,10 +268,41 @@ def DCWorkflowDefinition_executeTransition(self, ob, tdef=None, kwargs=None):
     else:
         return new_sdef
 
-
 DCWorkflowDefinition._executeTransition = DCWorkflowDefinition_executeTransition
 from Products.DCWorkflow.utils import modifyRolesForPermission
 
+
+def DCWorkflowDefinition_wrapWorkflowMethod(self, ob, method_id, func, args, kw):
+    '''
+    Allows the user to request a workflow action.  This method
+    must perform its own security checks.
+    '''
+    sdef = self._getWorkflowStateOf(ob)
+    if sdef is None:
+        raise WorkflowException, 'Object is in an undefined state'
+    if method_id not in sdef.transitions:
+        raise Unauthorized(method_id)
+    tdef = self.transitions.get(method_id, None)
+    if tdef is None or tdef.trigger_type != TRIGGER_WORKFLOW_METHOD:
+        raise WorkflowException, (
+            'Transition %s is not triggered by a workflow method'
+            % method_id)
+    if not self._checkTransitionGuard(tdef, ob):
+        raise Unauthorized(method_id)
+    res = func(*args, **kw)
+    try:
+        self._changeStateOf(ob, tdef, kw)
+    except ObjectDeleted:
+        # Re-raise with a different result.
+        raise ObjectDeleted(res)
+    except ObjectMoved, ex:
+        # Re-raise with a different result.
+        raise ObjectMoved(ex.getNewObject(), res)
+    return res
+
+DCWorkflowDefinition.wrapWorkflowMethod = DCWorkflowDefinition_wrapWorkflowMethod
+
+
 # Patch updateRoleMappingsFor so that if 2 workflows define security, then we
 # should do an AND operation between each permission
 def updateRoleMappingsFor(self, ob):
@@ -453,6 +481,4 @@ def createERP5Workflow(id):
 
 addWorkflowFactory(createERP5Workflow,
                    id='erp5_workflow',
-                   title='ERP5-style empty workflow')
-
-
+                   title='ERP5-style empty workflow')
\ No newline at end of file
diff --git a/product/ERP5Type/patches/WorkflowTool.py b/product/ERP5Type/patches/WorkflowTool.py
index 6838ca6063..f20c0eb7d2 100644
--- a/product/ERP5Type/patches/WorkflowTool.py
+++ b/product/ERP5Type/patches/WorkflowTool.py
@@ -12,6 +12,8 @@
 #
 ##############################################################################
 
+from zLOG import LOG
+
 # Make sure Interaction Workflows are called even if method not wrapped
 
 from Products.CMFCore.WorkflowTool import WorkflowTool
@@ -25,24 +27,25 @@ def WorkflowTool_wrapWorkflowMethod(self, ob, method_id, func, args, kw):
         By default, the workflow tool takes the first workflow wich
         support the method_id. In ERP5, with Interaction Worfklows, we
         may have many workflows wich can support a worfklow method,
-        that's why we need this patch
+        that's why we need this patch.
+
+        Current implementation supports:
+        - at most 1 DCWorkflow per portal type per method_id
+        - as many Interaction workflows as needed per portal type
+
+        NOTE: automatic transitions are invoked through
+        _findAutomaticTransition in DC Workflows.
 
-        We should have 1 or 0 classic workflow (ie a DCWorkflow), and
-        0 or many Interaction workflows. We should take care that the
-        method will be called once
+        TODO: make it possible to have multiple DC Workflow
+        per portal type per method_id
     """
     # Check workflow containing the workflow method
     wf_list = []
     wfs = self.getWorkflowsFor(ob)
     if wfs:
       for w in wfs:
-#         LOG('ERP5WorkflowTool.wrapWorkflowMethod, is wfMSupported', 0, 
-#              repr((w.isWorkflowMethodSupported(ob, method_id), 
-#                    w.getId(), ob, method_id )))
         if (hasattr(w, 'isWorkflowMethodSupported')
-          and w.isWorkflowMethodSupported(ob, method_id)):
-          #wf = w
-          #break
+            and w.isWorkflowMethodSupported(ob, method_id)):
           wf_list.append(w)
     else:
       wfs = ()
@@ -58,6 +61,13 @@ def WorkflowTool_wrapWorkflowMethod(self, ob, method_id, func, args, kw):
     for w in wf_list:
       if w.__class__.__name__ != 'InteractionWorkflowDefinition':
         only_interaction_defined = 0
+        # XXX - There is a problem here if the same workflow method
+        # is used by multiple workflows. Function "func" will be
+        # called multiple times. Patch or changes required to mak
+        # sure func is only called once.
+        # Solution consists in reimplementing _invokeWithNotification
+        # at the level of each workflow without notification
+        # (ex. _invokeWithoutNotification)
         result = self._invokeWithNotification(
             [], ob, method_id, w.wrapWorkflowMethod,
             (ob, method_id, func, args, kw), {})
-- 
2.30.9