[Zope3-checkins]
SVN: Zope3/branches/jack-e_interfacebased_workflow/
very basic implementation of jim's idea of implementing
state-based workflow with interfaces.
Ulrich Eck
ueck at net-labs.de
Sat Sep 18 17:54:14 EDT 2004
Log message for revision 27636:
very basic implementation of jim's idea of implementing state-based workflow with interfaces.
everyone interested in workflow is invited to join this effort and try/discuss this way of
implementing state machines.
Changed:
A Zope3/branches/jack-e_interfacebased_workflow/package-includes/wfproto-configure.zcml
A Zope3/branches/jack-e_interfacebased_workflow/src/wfproto/
A Zope3/branches/jack-e_interfacebased_workflow/src/wfproto/__init__.py
A Zope3/branches/jack-e_interfacebased_workflow/src/wfproto/configure.zcml
A Zope3/branches/jack-e_interfacebased_workflow/src/wfproto/interfaces.py
A Zope3/branches/jack-e_interfacebased_workflow/src/wfproto/myprocess.py
A Zope3/branches/jack-e_interfacebased_workflow/src/wfproto/statea.pt
A Zope3/branches/jack-e_interfacebased_workflow/src/wfproto/stateb.pt
A Zope3/branches/jack-e_interfacebased_workflow/src/wfproto/statec.pt
A Zope3/branches/jack-e_interfacebased_workflow/src/wfproto/userevent.py
A Zope3/branches/jack-e_interfacebased_workflow/src/wfproto/wfproto.pt
U Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/browser/definition.py
U Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/configure.zcml
U Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/definition.py
U Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/instance.py
U Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/interfaces/__init__.py
U Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/stateful/browser/configure.zcml
U Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/stateful/browser/definition.py
U Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/stateful/browser/filteradapter.py
U Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/stateful/browser/instance.py
U Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/stateful/configure.zcml
U Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/stateful/contentworkflow.py
U Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/stateful/definition.py
U Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/stateful/instance.py
U Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/stateful/interfaces.py
-=-
Added: Zope3/branches/jack-e_interfacebased_workflow/package-includes/wfproto-configure.zcml
===================================================================
--- Zope3/branches/jack-e_interfacebased_workflow/package-includes/wfproto-configure.zcml 2004-09-18 21:49:13 UTC (rev 27635)
+++ Zope3/branches/jack-e_interfacebased_workflow/package-includes/wfproto-configure.zcml 2004-09-18 21:54:13 UTC (rev 27636)
@@ -0,0 +1 @@
+<include package="wfproto"/>
Added: Zope3/branches/jack-e_interfacebased_workflow/src/wfproto/__init__.py
===================================================================
Added: Zope3/branches/jack-e_interfacebased_workflow/src/wfproto/configure.zcml
===================================================================
--- Zope3/branches/jack-e_interfacebased_workflow/src/wfproto/configure.zcml 2004-09-18 21:49:13 UTC (rev 27635)
+++ Zope3/branches/jack-e_interfacebased_workflow/src/wfproto/configure.zcml 2004-09-18 21:54:13 UTC (rev 27636)
@@ -0,0 +1,82 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ xmlns:browser="http://namespaces.zope.org/browser"
+ i18n_domain="zope"
+ >
+
+<interface
+ interface="buddydemo.interfaces.IBuddy"
+ type="zope.app.content.interfaces.IContentType"
+ />
+
+<adapter
+ factory="zope.app.workflow.stateful.instance.StatefulPIAdapter"
+ provides="zope.app.workflow.stateful.interfaces.IStatefulPIAdapter"
+ for=".interfaces.IMyProcess"
+ />
+
+<interface
+ interface=".interfaces.IMyStateINITIAL" />
+
+<interface
+ interface=".interfaces.IMyStateA" />
+
+<interface
+ interface=".interfaces.IMyStateB" />
+
+<interface
+ interface=".interfaces.IMyStateC" />
+
+
+
+
+<subscriber
+ factory=".userevent.UserEventSubscriber"
+ for=".interfaces.IUserEvent"
+ />
+
+
+<browser:page
+ name="state.html"
+ for=".interfaces.IMyStateA"
+ template="statea.pt"
+ menu="zmi_views" title="State"
+ permission="zope.Public"
+ />
+
+<browser:page
+ name="state.html"
+ for=".interfaces.IMyStateB"
+ template="stateb.pt"
+ menu="zmi_views" title="State"
+ permission="zope.Public"
+ />
+
+<browser:page
+ name="state.html"
+ for=".interfaces.IMyStateC"
+ template="statec.pt"
+ menu="zmi_views" title="State"
+ permission="zope.Public"
+ />
+
+
+
+
+<browser:page
+ name="wfproto.html"
+ for=".interfaces.IMyProcess"
+ template="wfproto.pt"
+ menu="zmi_views" title="Process"
+ permission="zope.Public"
+ />
+
+<browser:page
+ name="submit.html"
+ for=".interfaces.IMyProcess"
+ class=".myprocess.FormSubmitView"
+ attribute="submit"
+ permission="zope.Public"
+ />
+
+</configure>
Added: Zope3/branches/jack-e_interfacebased_workflow/src/wfproto/interfaces.py
===================================================================
--- Zope3/branches/jack-e_interfacebased_workflow/src/wfproto/interfaces.py 2004-09-18 21:49:13 UTC (rev 27635)
+++ Zope3/branches/jack-e_interfacebased_workflow/src/wfproto/interfaces.py 2004-09-18 21:54:13 UTC (rev 27636)
@@ -0,0 +1,35 @@
+from zope import interface
+from zope.app.event.interfaces import IObjectEvent
+
+
+class IMyProcess(interface.Interface):
+ """ just my process. """
+
+ __processdefinition_name__ = interface.Attribute('temporary helper to find pd')
+
+
+class IMyStateINITIAL(IMyProcess):
+ """ the INITIAL state. """
+
+class IMyStateA(IMyProcess):
+ """ the A state. """
+
+class IMyStateB(IMyProcess):
+ """ the B state. """
+
+class IMyStateC(IMyProcess):
+ """ the C state. """
+
+
+
+
+
+
+class IUserEvent(IObjectEvent):
+ """ an informal UserEvent Interface. """
+
+
+ formData = interface.Attribute('form data')
+ method = interface.Attribute('submit method')
+
+
Added: Zope3/branches/jack-e_interfacebased_workflow/src/wfproto/myprocess.py
===================================================================
--- Zope3/branches/jack-e_interfacebased_workflow/src/wfproto/myprocess.py 2004-09-18 21:49:13 UTC (rev 27635)
+++ Zope3/branches/jack-e_interfacebased_workflow/src/wfproto/myprocess.py 2004-09-18 21:54:13 UTC (rev 27636)
@@ -0,0 +1,18 @@
+from zope.event import notify
+
+from userevent import UserEvent
+
+
+
+class FormSubmitView:
+
+ def __init__(self, context, request):
+ self.context=context
+ self.request=request
+
+ # XXX easy way .. not final !!!
+ def submit(self, transition):
+ notify(UserEvent(self.context, transition, self.request.form))
+
+ # XXX how do we control this ??
+ self.request.response.redirect(self.request.get('REFERER', '') or 'wfproto.html')
Added: Zope3/branches/jack-e_interfacebased_workflow/src/wfproto/statea.pt
===================================================================
--- Zope3/branches/jack-e_interfacebased_workflow/src/wfproto/statea.pt 2004-09-18 21:49:13 UTC (rev 27635)
+++ Zope3/branches/jack-e_interfacebased_workflow/src/wfproto/statea.pt 2004-09-18 21:54:13 UTC (rev 27636)
@@ -0,0 +1,10 @@
+<html metal:use-macro='context/@@standard_macros/view'
+ i18n:domain='wfproto'>
+<body>
+<div metal:fill-slot='body'>
+<h1>I'm State A Template</h1>
+<br/>
+i'm the 1st page.
+</div>
+</body>
+</html>
Added: Zope3/branches/jack-e_interfacebased_workflow/src/wfproto/stateb.pt
===================================================================
--- Zope3/branches/jack-e_interfacebased_workflow/src/wfproto/stateb.pt 2004-09-18 21:49:13 UTC (rev 27635)
+++ Zope3/branches/jack-e_interfacebased_workflow/src/wfproto/stateb.pt 2004-09-18 21:54:13 UTC (rev 27636)
@@ -0,0 +1,11 @@
+<html metal:use-macro='context/@@standard_macros/view'
+ i18n:domain='wfproto'>
+<body>
+<div metal:fill-slot='body'>
+<h1>I'm State B Template</h1>
+
+<br/>
+i'm the 2nd page.
+</div>
+</body>
+</html>
\ No newline at end of file
Added: Zope3/branches/jack-e_interfacebased_workflow/src/wfproto/statec.pt
===================================================================
--- Zope3/branches/jack-e_interfacebased_workflow/src/wfproto/statec.pt 2004-09-18 21:49:13 UTC (rev 27635)
+++ Zope3/branches/jack-e_interfacebased_workflow/src/wfproto/statec.pt 2004-09-18 21:54:13 UTC (rev 27636)
@@ -0,0 +1,10 @@
+<html metal:use-macro='context/@@standard_macros/view'
+ i18n:domain='wfproto'>
+<body>
+<div metal:fill-slot='body'>
+<h1>I'm State __C__ Template</h1>
+<br/>
+I'm the 3rd page.
+</div>
+</body>
+</html>
\ No newline at end of file
Added: Zope3/branches/jack-e_interfacebased_workflow/src/wfproto/userevent.py
===================================================================
--- Zope3/branches/jack-e_interfacebased_workflow/src/wfproto/userevent.py 2004-09-18 21:49:13 UTC (rev 27635)
+++ Zope3/branches/jack-e_interfacebased_workflow/src/wfproto/userevent.py 2004-09-18 21:54:13 UTC (rev 27636)
@@ -0,0 +1,31 @@
+from zope.interface import implements
+from zope.app import zapi
+from zope.app.event.objectevent import ObjectEvent
+
+from interfaces import IUserEvent
+from zope.app.workflow.interfaces import IProcessDefinition
+from zope.app.workflow.stateful.interfaces import IStatefulPIAdapter
+
+
+class UserEvent(ObjectEvent):
+
+ implements(IUserEvent)
+
+ def __init__(self, object, method, formData):
+ super(UserEvent, self).__init__(object)
+ self.method = method
+ self.formData = formData
+
+
+
+
+def UserEventSubscriber(event):
+ # XXX for now we use __processdefiniton_name__
+ pd_name = getattr(event.object, '__processdefinition_name__', '')
+ pia = zapi.queryAdapter(event.object, IStatefulPIAdapter, pd_name)
+ if pia is not None:
+ #try:
+ pia.fireTransition(event.method, event)
+ #except Exception, e:
+ # print 'invalid Transition', event.method, str(e)
+
Added: Zope3/branches/jack-e_interfacebased_workflow/src/wfproto/wfproto.pt
===================================================================
--- Zope3/branches/jack-e_interfacebased_workflow/src/wfproto/wfproto.pt 2004-09-18 21:49:13 UTC (rev 27635)
+++ Zope3/branches/jack-e_interfacebased_workflow/src/wfproto/wfproto.pt 2004-09-18 21:54:13 UTC (rev 27636)
@@ -0,0 +1,23 @@
+<html metal:use-macro='context/@@standard_macros/view'
+ i18n:domain='wfproto'>
+<body>
+<div metal:fill-slot='body'>
+<form action='submit.html' method='POST'>
+<table>
+ <caption i18n:translate=''>Workflow Prototype Testpage</caption>
+ <tr>
+ <td i18n:translate=''>Text:</td>
+ <td><input type='text' name='text'/></td>
+ </tr>
+ <tr>
+ <td i18n:translate=''>Transition:</td>
+ <td><input type='text' name='transition'/></td>
+ </tr>
+ <tr>
+ <td colspan='2'><input type='submit' value='Submit'/></td>
+ </tr>
+</table>
+</form>
+</div>
+</body>
+</html>
Modified: Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/browser/definition.py
===================================================================
--- Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/browser/definition.py 2004-09-18 21:49:13 UTC (rev 27635)
+++ Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/browser/definition.py 2004-09-18 21:54:13 UTC (rev 27636)
@@ -37,4 +37,5 @@
self.request.response.redirect('@@importexport.html?success=1')
def exportDefinition(self):
+ return ''
return IProcessDefinitionExportHandler(self.context).doExport()
Modified: Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/configure.zcml
===================================================================
--- Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/configure.zcml 2004-09-18 21:49:13 UTC (rev 27635)
+++ Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/configure.zcml 2004-09-18 21:54:13 UTC (rev 27636)
@@ -82,14 +82,6 @@
/>
</content>
- <adapter
- factory="zope.app.workflow.instance.ProcessInstanceContainerAdapter"
- provides="zope.app.workflow.interfaces.IProcessInstanceContainer"
- for="zope.app.annotation.interfaces.IAnnotatable"
- permission="zope.workflow.UseProcessInstances"
- trusted="true"
- />
-
<include package=".stateful" />
<include package=".browser" />
Modified: Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/definition.py
===================================================================
--- Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/definition.py 2004-09-18 21:49:13 UTC (rev 27635)
+++ Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/definition.py 2004-09-18 21:54:13 UTC (rev 27636)
@@ -29,6 +29,7 @@
from zope.app.workflow.interfaces import IProcessDefinitionElementContainer
from zope.app.workflow.interfaces import IProcessDefinition
+
class ProcessDefinition(Persistent, Contained):
"""Abstract Process Definition class.
@@ -38,9 +39,6 @@
name = None
- def createProcessInstance(self, definition_name):
- """See zope.app.workflow.interfaces.IProcessDefinition"""
- return None
class ProcessDefinitionElementContainer(Persistent, Contained):
Modified: Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/instance.py
===================================================================
--- Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/instance.py 2004-09-18 21:49:13 UTC (rev 27635)
+++ Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/instance.py 2004-09-18 21:54:13 UTC (rev 27636)
@@ -19,114 +19,39 @@
from persistent.dict import PersistentDict
from zope.proxy import removeAllProxies
+from zope.interface import providedBy
+
from zope.app import zapi
from zope.app.annotation.interfaces import IAnnotatable, IAnnotations
from zope.app.container.interfaces import IContained
-from zope.app.servicenames import Utilities
-from zope.app.workflow.interfaces import IProcessInstance, IProcessDefinition
-from zope.app.workflow.interfaces import IProcessInstanceContainer
+from zope.app.servicenames import Utilities, Adapters
+from zope.app.workflow.interfaces import IPIAdapter, IProcessDefinition
from zope.interface import implements
-from zope.app.container.contained import Contained, setitem, uncontained
-class ProcessInstance(Contained):
- """Process Instance implementation.
- Process instances are always added to a process instance container. This
- container lives in an annotation of the object and is commonly stored in
- the ZODB. Therefore a process instance should be persistent.
+class PIAdapter(object):
+ """Adapter to interpret ProcessDefinitions with ContentObjects as Context.
+
"""
- implements(IProcessInstance)
+ implements(IPIAdapter)
- def __init__(self, pd_name):
- self._pd_name = pd_name
- self._status = None
-
- processDefinitionName = property(lambda self: self._pd_name)
-
- status = property(lambda self: self._status)
-
- ## should probably have a method "getProcessDefinition"
-
-
-def createProcessInstance(context, name):
- """Helper function to create a process instance from a process definition
- name."""
- utils = zapi.getService(Utilities, context)
- pd = utils.getUtility(IProcessDefinition, name)
- return pd.createProcessInstance(name)
-
-
-_marker = object()
-
-WFKey = "zope.app.worfklow.ProcessInstanceContainer"
-
-class ProcessInstanceContainerAdapter(object):
-
- implements(IProcessInstanceContainer)
-
- __used_for__ = IAnnotatable
-
def __init__(self, context):
self.context = context
- annotations = IAnnotations(context)
- wfdata = annotations.get(WFKey)
- if not wfdata:
- wfdata = PersistentDict()
- annotations[WFKey] = wfdata
- self.wfdata = wfdata
- def __getitem__(self, key):
- "See IProcessInstanceContainer"
- value = self.wfdata[key]
- return value
+ def _getProcessDefinition(self):
+ # we have an Interface (ISomeState) that represents the actual
+ # state of the object and extends our IMyProcessInstance Interface.
+ # we should register the ProcessDefinition as an Adapter for IMyProcessInstance
+ #return zapi.getService(Adapters).lookup([...how to define this?...],
+ # IProcessDefinition, '', None)
+
+ # XXX but for now we just use the __processdefinition_name__ attribute on components.
+ pd_name = getattr(removeAllProxies(self.context), '__processdefinition_name__', '')
+ return zapi.getUtility(IProcessDefinition, pd_name)
+
+ processDefinition = property(_getProcessDefinition)
- def get(self, key, default=None):
- "See IProcessInstanceContainer"
- value = self.wfdata.get(key, _marker)
- if value is not _marker:
- return value
- else:
- return default
- def __contains__(self, key):
- "See IProcessInstanceContainer"
- return key in self.wfdata
- def values(self):
- "See IProcessInstanceContainer"
- return self.wfdata.values()
-
- def keys(self):
- "See IProcessInstanceContainer"
- return self.wfdata.keys()
-
- def __len__(self):
- "See IProcessInstanceContainer"
- return len(self.wfdata)
-
- def items(self):
- "See IProcessInstanceContainer"
- return self.wfdata.items()
-
- def __setitem__(self, key, object):
- "See IProcessInstanceContainer"
- # We cannot make the message the parent right away, since it is not
- # added to any message board yet;
- setitem(self, self.wfdata.__setitem__, key, object)
- # Set the final parent to be the message.
- if IContained.providedBy(object):
- object.__parent__ = self.context
-
- def __delitem__(self, key):
- "See IZopeWriteContainer"
- container = self.wfdata
- # publish event ?
- uncontained(container[key], self, key)
- del container[key]
-
- def __iter__(self):
- '''See interface IReadContainer'''
- return iter(self.wfdata)
-
Modified: Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/interfaces/__init__.py
===================================================================
--- Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/interfaces/__init__.py 2004-09-18 21:49:13 UTC (rev 27635)
+++ Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/interfaces/__init__.py 2004-09-18 21:54:13 UTC (rev 27636)
@@ -43,43 +43,26 @@
"""Return the ProcessDefinition Object."""
-class IProcessInstance(Interface):
- """Workflow process instance.
- Represents the instance of a process defined by a ProcessDefinition."""
+class IPIAdapter(Interface):
+ """PI Adapter that does the hard work."""
- status = Attribute("The status in which the workitem is.")
+ status = Attribute('the current status of the PI')
+ processDefinition = Attribute('the ProcessDefinition')
- processDefinitionName = Attribute("The process definition Name.")
+ # specify the methods that are used !!!
-class IProcessInstanceContainer(IContainer):
- """Workflow process instance container."""
+class IProcessInstance(Interface):
+ """Workflow process instance.
-class IProcessInstanceContainerAdaptable(Interface):
- """Marker interface for components that can be adapted to a process
- instance container."""
+ Represents the instance of a process defined by a ProcessDefinition."""
-class IProcessInstanceControl(Interface):
- """Interface to interact with a process instance."""
- def start():
- """Start a process instance."""
- def finish():
- """Finish a process instance."""
-
-
-class IWorklistHandler(Interface):
- """Base interface for Workflow Worklist Handler."""
-
- def getWorkitems():
- """Return a sequence of workitem."""
-
-
class IProcessDefinitionImportHandler(Interface):
"""Handler for Import of ProcessDefinitions."""
@@ -92,6 +75,7 @@
Returns a ProcessDefinition Instance."""
+
class IProcessDefinitionExportHandler(Interface):
"""Handler for Export of ProcessDefinitions."""
Modified: Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/stateful/browser/configure.zcml
===================================================================
--- Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/stateful/browser/configure.zcml 2004-09-18 21:49:13 UTC (rev 27635)
+++ Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/stateful/browser/configure.zcml 2004-09-18 21:54:13 UTC (rev 27636)
@@ -11,6 +11,14 @@
permission="zope.ManageServices"
/>
+ <editform
+ schema="zope.app.workflow.stateful.interfaces.IStatefulProcessDefinition"
+ name="edit.html"
+ menu="zmi_views"
+ label="Edit a Stateful ProcessDefinition"
+ class=".definition.StatefulProcessDefinitionEdit"
+ permission="zope.workflow.ManageProcessDefinitions" />
+
<page
for="zope.app.workflow.stateful.interfaces.IStatefulProcessDefinition"
name="index.html"
@@ -18,14 +26,6 @@
permission="zope.ManageServices"
template="definition_index.pt" />
- <editform
- schema="zope.app.workflow.stateful.interfaces.IStatefulProcessDefinition"
- name="edit.html"
- template="definition_edit.pt"
- class=".definition.RelevantDataSchemaEdit"
- menu="zmi_views" title="Relevant Data Schema"
- permission="zope.workflow.ManageProcessDefinitions"/>
-
<menuItems
for="zope.app.workflow.stateful.interfaces.IStatefulProcessDefinition"
menu="zmi_actions">
@@ -60,14 +60,12 @@
<!-- State -->
- <!-- nothing to edit yet
<editform
schema="zope.app.workflow.stateful.interfaces.IState"
name="edit.html"
menu="zmi_views"
label="Edit a State"
permission="zope.workflow.ManageProcessDefinitions" />
- -->
<addform
name="AddState"
@@ -135,25 +133,6 @@
menu="zmi_views" title="Content/Process Registry"/>
-<!-- ProcessInstanceContainerAdaptable -->
- <pages
- for="zope.app.workflow.interfaces.IProcessInstanceContainerAdaptable"
- permission="zope.workflow.UseProcessInstances"
- class=".instance.ManagementView">
-
- <page name="workflows.html" template="instance_manage.pt"
- menu="zmi_views" title="Workflows"/>
- <page name="fireTransition.html" attribute="fireTransition" />
- </pages>
-
-
- <page
- for="zope.app.container.interfaces.IContentContainer"
- permission="zope.View"
- class=".content_filter.FilterList"
- name="published_content.html"
- attribute="published_content" />
-
<!-- uhm ... this seems to be too generic in its definition
and not really nice as well. -->
<zope:adapter
Modified: Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/stateful/browser/definition.py
===================================================================
--- Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/stateful/browser/definition.py 2004-09-18 21:49:13 UTC (rev 27635)
+++ Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/stateful/browser/definition.py 2004-09-18 21:54:13 UTC (rev 27636)
@@ -30,6 +30,14 @@
from zope.security.proxy import removeSecurityProxy
from zope.app.form.utility import setUpWidget
+class StatefulProcessDefinitionEdit(object):
+ """ Custom adding for StatefulProcessDefinitions. """
+
+ def changed(self):
+ # XXX provide Adapter for this instance
+ print "REGISTER ADAPTER"
+
+
class StatesContainerAdding(Adding):
"""Custom adding view for StatesContainer objects."""
menu_id = "add_stateful_states"
@@ -60,100 +68,7 @@
return """I'm a stateful ProcessInstance"""
-class RelevantDataSchemaEdit(EditView):
- def __init__(self, context, request):
- super(RelevantDataSchemaEdit, self).__init__(context, request)
- self.buildPermissionWidgets()
-
- def buildPermissionWidgets(self):
- schema = self.context.relevantDataSchema
- if schema is not None:
- for name, field in getFields(schema).items():
-
- if self.context.schemaPermissions.has_key(name):
- get_perm, set_perm = self.context.schemaPermissions[name]
- try:
- get_perm_id = get_perm.id
- except:
- get_perm_id = None
- try:
- set_perm_id = set_perm.id
- except:
- set_perm_id = None
- else:
- get_perm_id, set_perm_id = None, None
-
- # Create the Accessor Permission Widget for this field
- permField = Choice(
- __name__=name+'_get_perm',
- title=u"Accessor Permission",
- vocabulary="Permission Ids",
- default=CheckerPublic,
- required=False)
- setUpWidget(self, name + '_get_perm', permField, IInputWidget,
- value=get_perm_id)
-
- # Create the Mutator Permission Widget for this field
- permField = Choice(
- __name__=name+'_set_perm',
- title=u"Mutator Permission",
- default=CheckerPublic,
- vocabulary="Permission Ids",
- required=False)
- setUpWidget(self, name+'_set_perm', permField, IInputWidget,
- value=set_perm_id)
-
- def update(self):
- status = ''
-
- if Update in self.request:
- status = super(RelevantDataSchemaEdit, self).update()
- self.buildPermissionWidgets()
- elif 'CHANGE' in self.request:
- schema = self.context.relevantDataSchema
- perms = removeSecurityProxy(self.context.schemaPermissions)
- for name, field in getFields(schema).items():
-
- getPermWidget = getattr(self, name+'_get_perm_widget')
- setPermWidget = getattr(self, name+'_set_perm_widget')
-
- # get the selected permission id from the from request
- get_perm_id = getPermWidget.getInputValue()
- set_perm_id = setPermWidget.getInputValue()
-
- # get the right permission from the given id
- get_perm = zapi.getUtility(IPermission, get_perm_id)
- set_perm = zapi.getUtility(IPermission, set_perm_id)
-
- # set the permission back to the instance
- perms[name] = (get_perm, set_perm)
-
- # update widget ohterwise we see the old value
- getPermWidget.setRenderedValue(get_perm_id)
- setPermWidget.setRenderedValue(set_perm_id)
-
-
-
- status = 'Fields permissions mapping updated.'
-
- return status
-
- def getPermissionWidgets(self):
- schema = self.context.relevantDataSchema
- if schema is None:
- return None
- info = []
- for name, field in getFields(schema).items():
- field = removeSecurityProxy(field)
- info.append(
- {'fieldName': name,
- 'fieldTitle': field.title,
- 'getter': getattr(self, name+'_get_perm_widget'),
- 'setter': getattr(self, name+'_set_perm_widget')} )
- return info
-
-
class AddState(BrowserView):
def action(self, id):
Modified: Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/stateful/browser/filteradapter.py
===================================================================
--- Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/stateful/browser/filteradapter.py 2004-09-18 21:49:13 UTC (rev 27635)
+++ Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/stateful/browser/filteradapter.py 2004-09-18 21:54:13 UTC (rev 27636)
@@ -16,14 +16,10 @@
$Id$
"""
from zope.interface import implements
-from zope.app.workflow.interfaces import IProcessInstanceContainerAdaptable
-from zope.app.workflow.interfaces import IProcessInstanceContainer
-
from interfaces import IContentFilterAdapter
class FilterAdapter(object):
- __used_for__ = IProcessInstanceContainerAdaptable
implements(IContentFilterAdapter)
def __init__(self, context):
@@ -41,6 +37,7 @@
def filterObjectByState(self, object, state, workflow='default'):
"""See IContentFilterAdapter"""
+ # XXX
adapter = IProcessInstanceContainer(object, None)
if not adapter:
return False
Modified: Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/stateful/browser/instance.py
===================================================================
--- Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/stateful/browser/instance.py 2004-09-18 21:49:13 UTC (rev 27635)
+++ Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/stateful/browser/instance.py 2004-09-18 21:54:13 UTC (rev 27636)
@@ -29,12 +29,9 @@
from zope.app.servicenames import Utilities
from zope.app.workflow.interfaces import IProcessDefinition
-from zope.app.workflow.interfaces import IProcessInstanceContainer
-from zope.app.workflow.interfaces import IProcessInstanceContainerAdaptable
class ManagementView(BrowserView):
- __used_for__ = IProcessInstanceContainerAdaptable
def __init__(self, context, request):
super(ManagementView, self).__init__(context, request)
Modified: Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/stateful/configure.zcml
===================================================================
--- Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/stateful/configure.zcml 2004-09-18 21:49:13 UTC (rev 27635)
+++ Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/stateful/configure.zcml 2004-09-18 21:54:13 UTC (rev 27636)
@@ -91,8 +91,9 @@
/>
</content>
-<!-- ContentWorkflowsManager -->
+
+<!-- ContentWorkflowsManager -->
<content
class=".contentworkflow.ContentWorkflowsManager">
<factory
@@ -113,14 +114,14 @@
/>
</content>
-<class class=".instance.StatefulProcessInstance">
- <require
- permission="zope.workflow.UseProcessInstances"
- interface=".interfaces.IStatefulProcessInstance"
- />
-</class>
+<subscriber
+ factory=".contentworkflow.NewObjectStatefulProcessInitializer"
+ for="zope.app.event.interfaces.IObjectCreatedEvent"/>
+
+
<!-- Stateful workflow import/Export -->
+<!--
<adapter
for=".interfaces.IStatefulProcessDefinition"
provides="zope.app.workflow.interfaces.IProcessDefinitionExportHandler"
@@ -132,18 +133,9 @@
provides="zope.app.workflow.interfaces.IProcessDefinitionImportHandler"
factory=".xmlimportexport.XMLImportHandler"
/>
+ -->
-<subscriber
- for="..interfaces.IProcessInstanceContainerAdaptable
- zope.app.event.objectevent.IObjectCreatedEvent"
- factory=".contentworkflow.NewObjectProcessInstanceCreator"
- >
-
- Cause workflow instances to be added to content objects when they
- are created.
-</subscriber>
-
<!-- Test Object for testing Stateful Workflows -->
<!--include file="testobject.zcml"/-->
Modified: Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/stateful/contentworkflow.py
===================================================================
--- Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/stateful/contentworkflow.py 2004-09-18 21:49:13 UTC (rev 27635)
+++ Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/stateful/contentworkflow.py 2004-09-18 21:54:13 UTC (rev 27636)
@@ -24,19 +24,17 @@
from zope.app.event.interfaces import IObjectCreatedEvent
from zope.app.servicenames import Utilities
-from zope.app.workflow.interfaces import IProcessInstanceContainer
-from zope.app.workflow.interfaces import IProcessInstanceContainerAdaptable
from zope.app.workflow.stateful.interfaces import IContentWorkflowsManager
-from zope.app.workflow.instance import createProcessInstance
+from zope.app.workflow.stateful.instance import initializeStatefulProcessFor
from zope.interface import implements, providedBy
from zope.app.container.contained import Contained
-def NewObjectProcessInstanceCreator(obj, event):
- # used for: IProcessInstanceContainerAdaptable, IObjectCreatedEvent
+def NewObjectStatefulProcessInitializer(event):
+ # used for IContentType?, IObjectCreatedEvent
- pi_container = IProcessInstanceContainer(obj)
-
+ obj = event.object
+
for (ignored, cwf) in zapi.getUtilitiesFor(IContentWorkflowsManager):
# here we will lookup the configured processdefinitions
# for the newly created compoent. For every pd_name
@@ -47,14 +45,14 @@
for pd_name in cwf.getProcessDefinitionNamesForObject(obj):
- if pd_name in pi_container.keys():
- continue
- try:
- pi = createProcessInstance(cwf, pd_name)
- except KeyError:
- # No registered PD with that name..
- continue
- pi_container[pd_name] = pi
+ print "initializing content with: ", pd_name
+ # XXX try/except needed here
+ initializeStatefulProcessFor(obj, pd_name)
+
+ # XXX for now we cannot create more than one instance
+ # due to using a single string-attribute for storing
+ # the processdefinition-name in the content-component.
+ break
class ContentWorkflowsManager(Persistent, Contained):
Modified: Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/stateful/definition.py
===================================================================
--- Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/stateful/definition.py 2004-09-18 21:49:13 UTC (rev 27635)
+++ Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/stateful/definition.py 2004-09-18 21:54:13 UTC (rev 27636)
@@ -29,23 +29,32 @@
from zope.app.workflow.definition import ProcessDefinition
from zope.app.workflow.definition import ProcessDefinitionElementContainer
from zope.app.workflow.stateful.interfaces import IStatefulProcessDefinition
-from zope.app.workflow.stateful.interfaces import IState, ITransition, INITIAL
+from zope.app.workflow.stateful.interfaces import IState, IStateContained
+from zope.app.workflow.stateful.interfaces import ITransition, ITransitionContained, INITIAL
from zope.app.workflow.stateful.interfaces import IStatefulStatesContainer
from zope.app.workflow.stateful.interfaces import IStatefulTransitionsContainer
from zope.app.workflow.stateful.interfaces import MANUAL
-from zope.app.workflow.stateful.instance import StatefulProcessInstance
class State(Persistent, Contained):
"""State."""
- implements(IState)
+ implements(IState,IStateContained)
+ # see IState
+ targetInterface = None
+ def __init__(self, targetInterface=None):
+ self.targetInterface=targetInterface
+
+
+
class StatesContainer(ProcessDefinitionElementContainer):
"""Container that stores States."""
implements(IStatefulStatesContainer)
+
+
class StateNamesVocabulary(SimpleVocabulary):
"""Vocabulary providing the names of states in a local process definition.
"""
@@ -64,10 +73,11 @@
raise 'NoLocalProcessDefinition', 'No local process definition found.'
+
class Transition(Persistent, Contained):
"""Transition from one state to another."""
- implements(ITransition)
+ implements(ITransition, ITransitionContained)
# See ITransition
sourceState = None
@@ -91,11 +101,13 @@
return self.__parent__.getProcessDefinition()
+
class TransitionsContainer(ProcessDefinitionElementContainer):
"""Container that stores Transitions."""
implements(IStatefulTransitionsContainer)
+
class StatefulProcessDefinition(ProcessDefinition):
"""Stateful workflow process definition."""
@@ -103,11 +115,11 @@
def __init__(self):
super(StatefulProcessDefinition, self).__init__()
+ self.__targetInterface = None
self.__states = StatesContainer()
initial = State()
self.__states[self.getInitialStateName()] = initial
self.__transitions = TransitionsContainer()
- self.__schema = None
self._publishModified('transitions', self.__transitions)
self._publishModified('states', self.__states)
# See workflow.stateful.IStatefulProcessDefinition
@@ -121,17 +133,22 @@
notify(event)
modified(self)
- def getRelevantDataSchema(self):
- return self.__schema
+ def getTargetInterface(self):
+ return self.__targetInterface
- def setRelevantDataSchema(self, schema):
- self.__schema = schema
+ def setTargetInterface(self, i):
+ self.__targetInterface = i
+
+ # XXX register an Adapter for targetInterface
+ # that provides IStatefulProcessDefinition here
+
+
# See workflow.stateful.IStatefulProcessDefinition
- relevantDataSchema = property(getRelevantDataSchema,
- setRelevantDataSchema,
- None,
- "Schema for RelevantData.")
+ targetInterface = property(getTargetInterface,
+ setTargetInterface,
+ None,
+ "Interface for this Process.")
# See workflow.stateful.IStatefulProcessDefinition
states = property(lambda self: self.__states)
@@ -179,25 +196,7 @@
"""See workflow.stateful.IStatefulProcessDefinition"""
return self.transitions.keys()
- def createProcessInstance(self, definition_name):
- """See workflow.IProcessDefinition"""
- pi_obj = StatefulProcessInstance(definition_name)
- # TODO:
- # Process instances need to have a place, so they can look things
- # up. It's not clear to me (Jim) what place they should have.
-
- # The parent of the process instance should be the object it is
- # created for!!! This will cause all sorts of head-aches, but at this
- # stage we do not have the object around; it would need some API
- # changes to do that. (SR)
- pi_obj.__parent__ = self
-
-
- pi_obj.initialize()
- return pi_obj
-
-
def __getitem__(self, key):
"See Interface.Common.Mapping.IReadMapping"
Modified: Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/stateful/instance.py
===================================================================
--- Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/stateful/instance.py 2004-09-18 21:49:13 UTC (rev 27635)
+++ Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/stateful/instance.py 2004-09-18 21:54:13 UTC (rev 27636)
@@ -22,21 +22,21 @@
from zope.event import notify
from zope.app.workflow.interfaces import IProcessDefinition
from zope.app.workflow.stateful.interfaces import AUTOMATIC
+from zope.app.workflow.stateful.interfaces import ITransitionEvent
from zope.app.workflow.stateful.interfaces import IAfterTransitionEvent
from zope.app.workflow.stateful.interfaces import IBeforeTransitionEvent
-from zope.app.workflow.stateful.interfaces import IRelevantDataChangeEvent
-from zope.app.workflow.stateful.interfaces import IStatefulProcessInstance
-from zope.app.workflow.stateful.interfaces import ITransitionEvent
-from zope.app.workflow.stateful.interfaces import IBeforeRelevantDataChangeEvent
-from zope.app.workflow.stateful.interfaces import IAfterRelevantDataChangeEvent
+from zope.app.workflow.stateful.interfaces import IStatefulPIAdapter
+
from zope.app.servicenames import Utilities
from zope.app.traversing.api import getParent
-from zope.app.workflow.instance import ProcessInstance
+from zope.app.workflow.instance import PIAdapter
from zope.app.container.contained import Contained
from zope.security.interfaces import Unauthorized
from zope.interface import directlyProvides, implements
+from zope.interface import directlyProvidedBy
from zope.proxy import removeAllProxies
+from zope.security.proxy import removeSecurityProxy
from zope.schema import getFields
from zope.security.management import queryInteraction
from zope.security.checker import CheckerPublic, Checker
@@ -49,9 +49,8 @@
"""A simple implementation of the transition event."""
implements(ITransitionEvent)
- def __init__(self, object, process, transition):
- self.object = object
- self.process = process
+ def __init__(self, context, transition):
+ self.context = context
self.transition = transition
class BeforeTransitionEvent(TransitionEvent):
@@ -61,87 +60,11 @@
implements(IAfterTransitionEvent)
-class RelevantDataChangeEvent(object):
- """A simple implementation of the transition event."""
- implements(IRelevantDataChangeEvent)
- def __init__(self, process, schema, attributeName, oldValue, newValue):
- self.process = process
- self.schema = schema
- self.attributeName = attributeName
- self.oldValue = oldValue
- self.newValue = newValue
-class BeforeRelevantDataChangeEvent(RelevantDataChangeEvent):
- implements(IBeforeRelevantDataChangeEvent)
-class AfterRelevantDataChangeEvent(RelevantDataChangeEvent):
- implements(IAfterRelevantDataChangeEvent)
-class RelevantData(Persistent, Contained):
- """The relevant data object can store data that is important to the
- workflow and fires events when this data is changed.
-
- If you don't understand this code, don't worry, it is heavy lifting.
- """
-
- def __init__(self, schema=None, schemaPermissions=None):
- super(RelevantData, self).__init__()
- self.__schema = None
- # Add the new attributes, if there was a schema passed in
- if schema is not None:
- for name, field in getFields(schema).items():
- setattr(self, name, field.default)
- self.__schema = schema
- directlyProvides(self, schema)
-
- # Build up a Checker rules and store it for later
- self.__checker_getattr = {}
- self.__checker_setattr = {}
- for name in getFields(schema):
- get_perm, set_perm = schemaPermissions.get(name, (None, None))
- self.__checker_getattr[name] = get_perm or CheckerPublic
- self.__checker_setattr[name] = set_perm or CheckerPublic
-
- # Always permit our class's two public methods
- self.__checker_getattr['getChecker'] = CheckerPublic
- self.__checker_getattr['getSchema'] = CheckerPublic
-
-
- def __setattr__(self, key, value):
- # The '__schema' attribute has a sepcial function
- if key in ('_RelevantData__schema',
- '_RelevantData__checker_getattr',
- '_RelevantData__checker_setattr',
- 'getChecker', 'getSchema') or \
- key.startswith('_p_'):
- return super(RelevantData, self).__setattr__(key, value)
-
- is_schema_field = (self.__schema is not None and
- key in getFields(self.__schema).keys())
-
- if is_schema_field:
- process = self.__parent__
- # Send an Event before RelevantData changes
- oldvalue = getattr(self, key, None)
- notify(BeforeRelevantDataChangeEvent(
- process, self.__schema, key, oldvalue, value))
-
- super(RelevantData, self).__setattr__(key, value)
-
- if is_schema_field:
- # Send an Event after RelevantData has changed
- notify(AfterRelevantDataChangeEvent(
- process, self.__schema, key, oldvalue, value))
-
- def getChecker(self):
- return Checker(self.__checker_getattr, self.__checker_setattr)
-
- def getSchema(self):
- return self.__schema
-
-
class StateChangeInfo(object):
"""Immutable StateChangeInfo."""
@@ -154,112 +77,85 @@
new_state = property(lambda self: self.__new_state)
-class StatefulProcessInstance(ProcessInstance, Persistent):
- """Stateful Workflow ProcessInstance."""
- implements(IStatefulProcessInstance)
- def getData(self):
- if self._data is None:
- return None
- # Always give out the data attribute as proxied object.
- return Proxy(self._data, self._data.getChecker())
-
- data = property(getData)
+class StatefulPIAdapter(PIAdapter):
+ """Stateful Workflow ProcessInstance Adapter."""
+ implements(IStatefulPIAdapter)
+
def initialize(self):
- """See zope.app.workflow.interfaces.IStatefulProcessInstance"""
- pd = self.getProcessDefinition()
- clean_pd = removeAllProxies(pd)
- self._status = clean_pd.getInitialStateName()
+ """See zope.app.workflow.interface.IStatefulProcessInstance"""
+ clean_pd = removeAllProxies(self.processDefinition)
- # resolve schema class
- # This should really always return a schema
- schema = clean_pd.getRelevantDataSchema()
- if schema:
- # create relevant-data
- self._data = RelevantData(schema, clean_pd.schemaPermissions)
- else:
- self._data = None
- # setup permission on data
-
# check for Automatic Transitions
self._checkAndFireAuto(clean_pd)
+
+
def getOutgoingTransitions(self):
"""See zope.app.workflow.interfaces.IStatefulProcessInstance"""
- pd = self.getProcessDefinition()
- clean_pd = removeAllProxies(pd)
+ clean_pd = removeAllProxies(self.processDefinition)
return self._outgoingTransitions(clean_pd)
- def fireTransition(self, id):
+
+ # XXX API change here !!!
+ def fireTransition(self, trans_id, event=None):
"""See zope.app.workflow.interfaces.IStatefulProcessInstance"""
- pd = self.getProcessDefinition()
- clean_pd = removeAllProxies(pd)
- if not id in self._outgoingTransitions(clean_pd):
- raise KeyError, 'Invalid Transition Id: %s' % id
- transition = clean_pd.transitions[id]
+
+ clean_pd = removeAllProxies(self.processDefinition)
+ if not trans_id in self._outgoingTransitions(clean_pd, event):
+ raise KeyError, 'Invalid Transition Id: %s' % trans_id
+ transition = clean_pd.transitions[trans_id]
+
# Get the object whose status is being changed.
- obj = getParent(self)
+ obj = removeSecurityProxy(self.context)
# Send an event before the transition occurs.
- notify(BeforeTransitionEvent(obj, self, transition))
+ notify(BeforeTransitionEvent(obj, transition))
+ # XXX Jim suggests to send out ObjectStateChanging/Changed Events instead
+ # of Before/AfterTransition. Need to check the side effects (Phase2)
+
# change status
- self._status = transition.destinationState
+ # XXX change self.context to implement the new interface
+ # and remove the old state's interface
+ oldStateIF = clean_pd.states[transition.sourceState].targetInterface
+ newStateIF = clean_pd.states[transition.destinationState].targetInterface
+
+ directlyProvides(obj, directlyProvidedBy(obj) + newStateIF - oldStateIF)
+
# Send an event after the transition occured.
- notify(AfterTransitionEvent(obj, self, transition))
+ notify(AfterTransitionEvent(obj, transition))
# check for automatic transitions and fire them if necessary
self._checkAndFireAuto(clean_pd)
- def getProcessDefinition(self):
- """Get the ProcessDefinition object from WorkflowService."""
- return zapi.getUtility(IProcessDefinition, self.processDefinitionName)
- def _getContext(self):
- ctx = {}
+
+ def _getContext(self, context={}):
# data should be readonly for condition-evaluation
- ctx['data'] = self.data
- ctx['principal'] = None
+ context['principal'] = None
interaction = queryInteraction()
if interaction is not None:
principals = [p.principal for p in interaction.participations]
if principals:
# There can be more than one principal
assert len(principals) == 1
- ctx['principal'] = principals[0]
+ context['principal'] = principals[0]
- # TODO This needs to be discussed:
- # how can we know if this ProcessInstance is annotated
- # to a Content-Object and provide secure ***READONLY***
- # Access to it for evaluating Transition Conditions ???
+ content = removeSecurityProxy(self.context)
+ context['content'] = content
- #content = self.__parent__
+ return context
- # TODO: How can i make sure that nobody modifies content
- # while the condition scripts/conditions are evaluated ????
- # this hack only prevents from directly setting an attribute
- # using a setter-method directly is not protected :((
- #try:
- # checker = getChecker(content)
- # checker.set_permissions = {}
- #except TypeError:
- # # got object without Security Proxy
- # checker = selectChecker(content)
- # checker.set_permissions = {}
- # content = Proxy(content, checker)
- #ctx['content'] = content
-
- return ctx
-
-
def _extendContext(self, transition, ctx={}):
ctx['state_change'] = StateChangeInfo(transition)
return ctx
+
def _evaluateCondition(self, transition, contexts):
"""Evaluate a condition in context of relevant-data."""
if not transition.condition:
@@ -267,6 +163,7 @@
expr = Engine.compile(transition.condition)
return expr(Engine.getContext(contexts=contexts))
+
def _evaluateScript(self, transition, contexts):
"""Evaluate a script in context of relevant-data."""
script = transition.script
@@ -277,15 +174,20 @@
script = sm.resolve(script)
return script(contexts)
- def _outgoingTransitions(self, clean_pd):
+
+ def _outgoingTransitions(self, clean_pd, event=None):
ret = []
- contexts = self._getContext()
+ contexts = self._getContext({'event':event})
+ # XXX
+ context = removeSecurityProxy(self.context)
+ context_spec = directlyProvidedBy(context)
+
for name, trans in clean_pd.transitions.items():
- if self.status == trans.sourceState:
+ if context_spec.extends(clean_pd.states[trans.sourceState].targetInterface):
# check permissions
permission = trans.permission
- if not checkPermission(permission, self):
+ if not checkPermission(permission, context):
continue
ctx = self._extendContext(trans, contexts)
@@ -317,3 +219,26 @@
if trans.triggerMode == AUTOMATIC:
self.fireTransition(name)
return
+
+
+def initializeStatefulProcessFor(obj, pd_name):
+ """provide a component and a processdefinition_name and
+ to give that component behaviour.
+ """
+ # XXX For now we just add an attribute to the component
+ # to remember which processdefinition specifies the behaviour.
+ # This solution is not final and does not support multiple
+ # processes for the same component. A smarter solution using
+ # named Adapters providing (IProcessDefinition, [name])
+ # for (ISomeStatefulProcess, ISomeContentType)
+ obj.__processdefinition_name__ = pd_name
+
+ pd = zapi.getUtility(IProcessDefinition, pd_name)
+ clean_pd = removeAllProxies(pd)
+
+ # XXX Jim suggests to send out ObjectStateChanging/Changed Events here (Phase2)
+
+ # XXX Set Initial Interface to self.context here !!!
+ directlyProvides(obj, pd.states[pd.getInitialStateName()].targetInterface)
+
+ IStatefulPIAdapter(obj).initialize()
Modified: Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/stateful/interfaces.py
===================================================================
--- Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/stateful/interfaces.py 2004-09-18 21:49:13 UTC (rev 27635)
+++ Zope3/branches/jack-e_interfacebased_workflow/src/zope/app/workflow/stateful/interfaces.py 2004-09-18 21:54:13 UTC (rev 27636)
@@ -20,9 +20,13 @@
from zope.interface import Interface, Attribute
from zope.app.i18n import ZopeMessageIDFactory as _
+from zope.app.container.interfaces import IContained, IContainer
+from zope.app.container.constraints import ContainerTypesConstraint
+from zope.app.container.constraints import ItemTypePrecondition
+
from zope.app.workflow.interfaces import IWorkflowEvent
from zope.app.workflow.interfaces import IProcessDefinition
-from zope.app.workflow.interfaces import IProcessInstance
+from zope.app.workflow.interfaces import IProcessInstance, IPIAdapter
from zope.app.workflow.interfaces import IProcessDefinitionElementContainer
AUTOMATIC = u'Automatic'
@@ -54,41 +58,34 @@
objects that might change permissions when changing the status."""
-class IRelevantDataChangeEvent(IWorkflowEvent):
- """This event is fired, when the object's data changes and the data is
- considered 'relevant' to the workflow. The attributes of interest are
- usually defined by a so called Relevant Data Schema."""
+class IState(Interface):
+ """Interface for state of a stateful workflow process definition."""
+ # TODO: Should at least have a title, if not a value as well
- process = Attribute("""The process instance that is doing the
- transition. Note that this object really represents
- the workflow.""")
+ targetInterface = zope.schema.Choice(
+ title=_(u"State Interface"),
+ description=_(u"An Interface that is used to mark Instances "
+ u"representing that it is in this state."),
+ vocabulary="Interfaces",
+ default=None,
+ required=False)
- schema = Attribute("""The schema that defines the relevant data
- attributes.""")
- attributeName = Attribute("""Name of the attribute that is changed.""")
+class IStatefulStatesContainer(IProcessDefinitionElementContainer):
+ """Container that stores States."""
- oldValue = Attribute("""The old value of the attribute.""")
+ def __setitem__(name, object):
+ """Add a state"""
- newValue = Attribute("""The new value of the attribute.""")
+ __setitem__.precondition = ItemTypePrecondition(IState)
+
-class IBeforeRelevantDataChangeEvent(IRelevantDataChangeEvent):
- """This event is triggered before some of the workflow-relevant data is
- being changed."""
-
-
-class IAfterRelevantDataChangeEvent(IRelevantDataChangeEvent):
- """This event is triggered after some of the workflow-relevant data has
- been changed."""
-
-
-class IState(Interface):
+class IStateContained(IContained):
"""Interface for state of a stateful workflow process definition."""
- # TODO: Should at least have a title, if not a value as well
-class IStatefulStatesContainer(IProcessDefinitionElementContainer):
- """Container that stores States."""
+ __parent__ = zope.schema.Field(
+ constraint = ContainerTypesConstraint(IStatefulStatesContainer))
@@ -139,22 +136,33 @@
class IStatefulTransitionsContainer(IProcessDefinitionElementContainer):
"""Container that stores Transitions."""
+ def __setitem__(name, object):
+ """Add a transition"""
+ __setitem__.precondition = ItemTypePrecondition(ITransition)
+
+
+
+class ITransitionContained(IContained):
+ """Stateful workflow transition that lives in a StatefulTansitionsContainer."""
+
+ __parent__ = zope.schema.Field(
+ constraint = ContainerTypesConstraint(IStatefulTransitionsContainer))
+
+
+
+
class IStatefulProcessDefinition(IProcessDefinition):
"""Interface for stateful workflow process definition."""
- relevantDataSchema = zope.schema.Choice(
- title=_(u"Workflow-Relevant Data Schema"),
- description=_(u"Specifies the schema that characterizes the workflow "
- u"relevant data of a process instance, found in pd.data."),
+ targetInterface = zope.schema.Choice(
+ title=_(u"Process Interface"),
+ description=_(u"Interface that represents the Process and "
+ u"is extended by this Process's States."),
vocabulary="Interfaces",
default=None,
required=False)
- schemaPermissions = Attribute(u"A dictionary that maps set/get permissions"
- u"on the schema's fields. Entries looks as"
- u"follows: {fieldname:(set_perm, get_perm)}")
-
states = Attribute("State objects container.")
transitions = Attribute("Transition objects container.")
@@ -194,19 +202,17 @@
-class IStatefulProcessInstance(IProcessInstance):
- """Workflow process instance.
+class IStatefulPIAdapter(IPIAdapter):
+ """Workflow process instance Adapter.
- Represents the instance of a process defined by a
+ Handles Behaviour for a process defined by a
StatefulProcessDefinition.
"""
- data = Attribute("Relevant Data object.")
-
def initialize():
- """Initialize the ProcessInstance.
+ """Initialize the Process.
- set Initial State and create relevant Data.
+ set Initial State.
"""
def getOutgoingTransitions():
@@ -215,10 +221,11 @@
def fireTransition(id):
"""Fire a outgoing transitions."""
- def getProcessDefinition():
- """Get the process definition for this instance."""
+
+
+
class IContentProcessRegistry(Interface):
"""Content Type <-> Process Definitions Registry
More information about the Zope3-Checkins
mailing list