[Zope-CVS] CVS: Packages3/workflow/stateful - configure.zcml:1.2 definition.py:1.3 instance.py:1.3

Ulrich Eck ueck@net-labs.de
Thu, 6 Feb 2003 17:03:40 -0500


Update of /cvs-repository/Packages3/workflow/stateful
In directory cvs.zope.org:/tmp/cvs-serv23752/stateful

Modified Files:
	configure.zcml definition.py instance.py 
Log Message:
Next round in Stateful workflow implementation:
- Transitions:
  + Conditions based on TALES Expressions work
  - Permissions are implemented but tests do not work yet (tests disabled)
- Instance:
  + Relevant-Data based on Schema basically works (schema written as persistent module)
  - Permissions for dataaccess not yet implemented
- BrowserViews:
 - basic setup for ManagementInterface made, no functionality yet
- extensive tests for the instance written.


=== Packages3/workflow/stateful/configure.zcml 1.1 => 1.2 ===
--- Packages3/workflow/stateful/configure.zcml:1.1	Tue Feb  4 16:42:18 2003
+++ Packages3/workflow/stateful/configure.zcml	Thu Feb  6 17:03:06 2003
@@ -1,15 +1,34 @@
 <zopeConfigure
    xmlns="http://namespaces.zope.org/zope">
 
-  <content class="zope.app.workflow.stateful.contentworkflow.ContentWorkflowsUtility">
-    <require
+<content class="zope.app.workflow.stateful.definition.StatefulProcessDefinition">
+  <factory
+      id="StatefulProcessDefinition"
       permission="zope.ManageServices"
-      interface="zope.app.interfaces.workflow.stateful.IContentWorkflowsUtility"
       />
-    <factory
-      id="zope.app.workflow.stateful.contentworkflow.ContentWorkflowsUtility"
+  <require
       permission="zope.ManageServices"
+      interface="zope.app.interfaces.workflow.stateful.IStatefulProcessDefinition" 
       />
-  </content>
+  <implements 
+      interface="zope.app.interfaces.annotation.IAttributeAnnotatable" 
+      />
+</content>
+
+
+<!-- ContentWorkflowsUtility
+
+<content class="zope.app.workflow.stateful.contentworkflow.ContentWorkflowsUtility">
+  <require
+    permission="zope.ManageServices"
+    interface="zope.app.interfaces.workflow.stateful.IContentWorkflowsUtility"
+    />
+  <factory
+    id="zope.app.workflow.stateful.contentworkflow.ContentWorkflowsUtility"
+    permission="zope.ManageServices"
+    />
+</content>
+
+-->
 
 </zopeConfigure>


=== Packages3/workflow/stateful/definition.py 1.2 => 1.3 ===
--- Packages3/workflow/stateful/definition.py:1.2	Wed Feb  5 20:09:31 2003
+++ Packages3/workflow/stateful/definition.py	Thu Feb  6 17:03:06 2003
@@ -18,12 +18,15 @@
 """
 __metaclass__ = type
 
+from types import StringTypes
 from persistence import Persistent
 from persistence.dict import PersistentDict
 
 from zope.proxy.context import ContextMethod, ContextWrapper
 from zope.proxy.context import getWrapperData
 
+from zope.component import getServiceManager
+
 from zope.app.interfaces.workflow import IProcessDefinition
 from zope.app.interfaces.workflow.stateful import IStatefulProcessDefinition
 from zope.app.interfaces.workflow.stateful import IState, ITransition
@@ -42,6 +45,7 @@
     __implements__ = IState
 
 
+
 class StatesContainer(ProcessDefinitionElementContainer):
     """Container that stores States.
     """
@@ -54,11 +58,13 @@
 
     __implements__ = ITransition
 
-    def __init__(self, source, destination, condition):
+    def __init__(self, source, destination, condition=None, permission=None,
+                 name=None, description=None):
         super(Transition, self).__init__()
         self.__source = source
         self.__destination = destination
         self.__condition = condition
+        self.__permission = permission
 
     sourceState = property(lambda self: self.__source)
 
@@ -66,6 +72,10 @@
 
     condition = property(lambda self: self.__condition)
 
+    permission = property(lambda self: self.__permission)
+
+
+                    
 
 class TransitionsContainer(ProcessDefinitionElementContainer):
     """Container that stores Transitions.
@@ -86,13 +96,14 @@
         initial = State()
         self.__states.setObject(self.getInitialStateName(), initial)
         self.__transitions = TransitionsContainer()
-
+        self.__schema = None
 
 
     ############################################################
     # Implementation methods for interface
     # zope.app.interfaces.workflow.stateful.IStatefulProcessDefinition
 
+
     states = property(lambda self: ContextWrapper(self.__states, self))
 
     transitions = property(lambda self: ContextWrapper(self.__transitions,
@@ -112,7 +123,8 @@
     
     def getStateNames(self):
         return self.states.keys()
-    
+
+    # XXX This shouldn't be hardcoded
     def getInitialStateName(self):
         return 'INITIAL'
     
@@ -140,6 +152,16 @@
         return pi_obj
     createProcessInstance = ContextMethod(createProcessInstance)
 
+    def setSchema(self, schema):
+        self.__schema = schema
+
+    def getSchema(self):
+        schema = self.__schema
+        if type(schema) in StringTypes:
+            sm = getServiceManager(self)
+            return sm.resolve(schema)
+        return schema
+    getSchema = ContextMethod(getSchema)
 
     #
     ############################################################


=== Packages3/workflow/stateful/instance.py 1.2 => 1.3 ===
--- Packages3/workflow/stateful/instance.py:1.2	Wed Feb  5 20:09:31 2003
+++ Packages3/workflow/stateful/instance.py	Thu Feb  6 17:03:06 2003
@@ -21,14 +21,32 @@
 
 
 from persistence import Persistent
-
+from zope.exceptions import Unauthorized
 from zope.component import getService
 from zope.proxy.context import ContextMethod
+from zope.proxy.context import ContextWrapper,ContextAware
+from zope.security.management import getSecurityManager
+from zope.pagetemplate.engine import Engine
+from zope.schema import getFields
 
 from zope.app.interfaces.workflow.stateful import IStatefulProcessInstance
 from zope.app.workflow.instance import ProcessInstance
 
 
+class StateChangeInfo:
+    """Immutable StateChangeInfo.
+    """
+
+    def __init__(self, transition):
+        self.__old_state = transition.sourceState
+        self.__new_state = transition.destinationState
+
+    old_state = property(lambda self: self.__old_state)
+
+    new_state = property(lambda self: self.__new_state)
+
+
+
 class StatefulProcessInstance(ProcessInstance, Persistent):
     """Stateful Workflow ProcessInstance.
     """
@@ -39,23 +57,44 @@
     ############################################################
     # Implementation methods for interface
     # zope.app.interfaces.workflow.IStatefulProcessInstance
+
+
+    data = property(lambda self: ContextWrapper(self._data, self))
     
     def initialize(self):
         pd = self._getProcessDefinition()
         self._status = pd.getInitialStateName()
-        # create relevant-data, schema and permissions here
+        # create relevant-data
+        schema = pd.getSchema()
+        self._data = self._buildRelevantData(schema)
+        # setup permission on data
+       
     initialize = ContextMethod(initialize)
         
 
     def getOutgoingTransitions(self):
         ret = []
         pd = self._getProcessDefinition()
+        sm = getSecurityManager()
+        
         for name, trans in pd.transitions.items():
             if self.status == trans.sourceState:
-                # evaluate trans.condition
-                # and check permission
-                if 1:
-                    ret.append(name)
+                # check permissions
+                if trans.permission:
+                    if not sm.checkPermission(trans.permission, self):
+                        continue
+
+                # evaluate conditions
+                if trans.condition is not None:
+                    try:
+                      include = self._evaluateCondition(trans)
+                    except Unauthorized:
+                        include = 0
+                    if not include:
+                        continue
+                
+                # append transition name
+                ret.append(name)
         return ret
     getOutgoingTransitions = ContextMethod(getOutgoingTransitions)
 
@@ -78,3 +117,33 @@
         svc =  getService(self, "Workflows")
         return svc.getProcessDefinition(self.processDefinitionName)
     _getProcessDefinition = ContextMethod(_getProcessDefinition)
+
+
+
+    def _getContext(self, transition):
+        ctx = {}
+        # data should be readonly for condition-evaluation
+        ctx['data'] = self.data
+        ctx['principal'] = getSecurityManager().getPrincipal()
+        ctx['state_change'] = StateChangeInfo(transition)
+        return ctx
+
+    def _evaluateCondition(self, transition):
+        """Evaluate a condition in context of relevant-data.
+        """
+        expr = Engine.compile(transition.condition)
+        return expr(Engine.getContext( contexts=self._getContext(transition) ))
+
+
+    def _buildRelevantData(self, schema):
+        """Create a new data object and initialize with Schema defaults.
+        """
+        class RelevantData(ContextAware):
+            __implements__ = schema
+
+        data = RelevantData()
+        if schema is not None:
+            for name, field in getFields(schema).items():
+                setattr(data, name, field.default)
+        return data
+