[Zope3-checkins] CVS: Zope3/src/zope/app/workflow/stateful - testobject.py:1.1 testobject.pyc:1.1 testobject.zcml:1.1 configure.zcml:1.4 definition.py:1.6 instance.py:1.9 xmlexport_template.pt:1.2 xmlimportexport.py:1.6
Stephan Richter
srichter@cosmos.phy.tufts.edu
Thu, 31 Jul 2003 11:02:15 -0400
Update of /cvs-repository/Zope3/src/zope/app/workflow/stateful
In directory cvs.zope.org:/tmp/cvs-serv10755/src/zope/app/workflow/stateful
Modified Files:
configure.zcml definition.py instance.py xmlexport_template.pt
xmlimportexport.py
Added Files:
testobject.py testobject.pyc testobject.zcml
Log Message:
Implemented relevant data for Processes (read workflows). You can specify
a schema which contains the fields that should be available as relevant
data. Once the schema is set, you are able to specify "get" and "set"
permissions for each field. That;s it for the setup.
When you then create a Content Object that uses the process definition for
which you declared the schema, you will see that there is a form in the
'Workflows' tab of this content object that allows you to manipulate the
releavant data.
I also enhanced the XML Import/Export to support relevant data and their
permissions.
The easiest way to check all this functionality out is to uncomment the
include directives in browser/workflow/stateful/configure.zcml and
workflow/stateful/configure.zcml (both at the end of the file).
This completes my code and status review of the workflow package, which I
think is now ready for the beta.
=== Added File Zope3/src/zope/app/workflow/stateful/testobject.py ===
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Stateful Workflow Test Object
This represents a simple content object that can receive workflows.
$Id: testobject.py,v 1.1 2003/07/31 15:01:36 srichter Exp $
"""
from persistence import Persistent
from zope.interface import Interface, implements
from zope.schema import TextLine, Int
class ITestObject(Interface):
test = TextLine(
title=u"Test Attribute",
description=u"This is a test attribute.",
default=u"foo",
required=True)
class IWorkflowData(Interface):
title = TextLine(
title=u"Title",
required=True)
number = Int(
title=u"Number",
required=True)
class TestObject(Persistent):
implements(ITestObject)
test = u"foo"
=== Added File Zope3/src/zope/app/workflow/stateful/testobject.pyc ===
<Binary-ish file>
=== Added File Zope3/src/zope/app/workflow/stateful/testobject.zcml ===
<zopeConfigure
xmlns="http://namespaces.zope.org/zope">
<interface interface=".testobject.IWorkflowData"/>
<content class=".testobject.TestObject">
<implements
interface="zope.app.interfaces.annotation.IAttributeAnnotatable"/>
<implements interface=
"zope.app.interfaces.workflow.IProcessInstanceContainerAdaptable"/>
<factory
id="TestObject"
permission="zope.ManageContent"
title="Test Object"
description="Test Object" />
<require
permission="zope.View"
interface=".testobject.ITestObject"
set_schema=".testobject.ITestObject" />
</content>
</zopeConfigure>
=== Zope3/src/zope/app/workflow/stateful/configure.zcml 1.3 => 1.4 ===
--- Zope3/src/zope/app/workflow/stateful/configure.zcml:1.3 Tue Jul 29 20:00:25 2003
+++ Zope3/src/zope/app/workflow/stateful/configure.zcml Thu Jul 31 11:01:36 2003
@@ -73,7 +73,8 @@
/>
<require
permission="zope.workflow.ManageProcessDefinitions"
- interface="zope.app.interfaces.workflow.stateful.IStatefulTransitionsContainer"
+ interface=
+ "zope.app.interfaces.workflow.stateful.IStatefulTransitionsContainer"
/>
<implements
interface="zope.app.interfaces.annotation.IAttributeAnnotatable"
@@ -122,5 +123,8 @@
interface="zope.app.interfaces.workflow.stateful.IStatefulProcessDefinition"
factory=".xmlimportexport.XMLExportHandler"
/>
+
+<!-- Test Object for testing Stateful Workflows -->
+<!--include file="testobject.zcml"/-->
</zopeConfigure>
=== Zope3/src/zope/app/workflow/stateful/definition.py 1.5 => 1.6 ===
--- Zope3/src/zope/app/workflow/stateful/definition.py:1.5 Thu Jun 5 08:03:19 2003
+++ Zope3/src/zope/app/workflow/stateful/definition.py Thu Jul 31 11:01:36 2003
@@ -19,6 +19,7 @@
__metaclass__ = type
from persistence import Persistent
+from persistence.dict import PersistentDict
from zope.app.context import ContextWrapper
from zope.context import getWrapperContainer
@@ -27,7 +28,7 @@
from zope.app.interfaces.container import IReadContainer
from zope.app.interfaces.workflow.stateful import IStatefulProcessDefinition
-from zope.app.interfaces.workflow.stateful import IState, ITransition
+from zope.app.interfaces.workflow.stateful import IState, ITransition, INITIAL
from zope.app.interfaces.workflow.stateful import IStatefulStatesContainer
from zope.app.interfaces.workflow.stateful import IStatefulTransitionsContainer
@@ -39,19 +40,16 @@
class State(Persistent):
"""State."""
-
implements(IState)
-
class StatesContainer(ProcessDefinitionElementContainer):
- """Container that stores States.
- """
+ """Container that stores States."""
implements(IStatefulStatesContainer)
class Transition(Persistent):
- """Transition."""
+ """Transition from one state to another."""
implements(ITransition)
@@ -65,7 +63,6 @@
self.__permission = permission or None
self.__triggerMode = triggerMode
-
def getSourceState(self):
return self.__source
@@ -127,8 +124,7 @@
class TransitionsContainer(ProcessDefinitionElementContainer):
- """Container that stores Transitions.
- """
+ """Container that stores Transitions."""
implements(IStatefulTransitionsContainer)
@@ -144,79 +140,78 @@
self.__states.setObject(self.getInitialStateName(), initial)
self.__transitions = TransitionsContainer()
self.__schema = None
+ # See workflow.stateful.IStatefulProcessDefinition
+ self.schemaPermissions = PersistentDict()
_clear = clear = __init__
- ############################################################
- # Implementation methods for interface
- # zope.app.interfaces.workflow.stateful.IStatefulProcessDefinition
-
def getRelevantDataSchema(self):
return self.__schema
def setRelevantDataSchema(self, schema):
self.__schema = schema
+ # See workflow.stateful.IStatefulProcessDefinition
relevantDataSchema = property(getRelevantDataSchema,
setRelevantDataSchema,
None,
"Schema for RelevantData.")
-
-
+ # See workflow.stateful.IStatefulProcessDefinition
states = property(lambda self: self.__states)
+ # See workflow.stateful.IStatefulProcessDefinition
transitions = property(lambda self: self.__transitions)
def addState(self, name, state):
+ """See workflow.stateful.IStatefulProcessDefinition"""
if name in self.states:
raise KeyError, name
self.states.setObject(name, state)
def getState(self, name):
+ """See workflow.stateful.IStatefulProcessDefinition"""
return self.states[name]
getState = ContextMethod(getState)
def removeState(self, name):
+ """See workflow.stateful.IStatefulProcessDefinition"""
del self.states[name]
def getStateNames(self):
+ """See workflow.stateful.IStatefulProcessDefinition"""
return self.states.keys()
- # XXX This shouldn't be hardcoded
def getInitialStateName(self):
- return 'INITIAL'
+ """See workflow.stateful.IStatefulProcessDefinition"""
+ return INITIAL
def addTransition(self, name, transition):
+ """See workflow.stateful.IStatefulProcessDefinition"""
if name in self.transitions:
raise KeyError, name
self.transitions.setObject(name, transition)
def getTransition(self, name):
+ """See workflow.stateful.IStatefulProcessDefinition"""
return self.transitions[name]
getTransition = ContextMethod(getTransition)
def removeTransition(self, name):
+ """See workflow.stateful.IStatefulProcessDefinition"""
del self.transitions[name]
def getTransitionNames(self):
+ """See workflow.stateful.IStatefulProcessDefinition"""
return self.transitions.keys()
- # IProcessDefinition
-
def createProcessInstance(self, definition_name):
+ """See workflow.IProcessDefinition"""
pi_obj = StatefulProcessInstance(definition_name)
ContextWrapper(pi_obj, self).initialize()
return pi_obj
createProcessInstance = ContextMethod(createProcessInstance)
- #
- ############################################################
-
-
- ############################################################
- # Implementation methods for interface
- # zope.app.interfaces.container.IReadContainer
def __getitem__(self, key):
"See Interface.Common.Mapping.IReadMapping"
@@ -247,26 +242,25 @@
return self.get(key) is not None
- # Enumeration methods. We'll only expose Packages for now:
def __iter__(self):
+ """See zope.app.interfaces.container.IReadContainer"""
return iter(self.keys())
def keys(self):
+ """See zope.app.interfaces.container.IReadContainer"""
return ['states', 'transitions']
def values(self):
+ """See zope.app.interfaces.container.IReadContainer"""
return map(self.get, self.keys())
-
values = ContextMethod(values)
def items(self):
+ """See zope.app.interfaces.container.IReadContainer"""
return [(key, self.get(key)) for key in self.keys()]
-
items = ContextMethod(items)
def __len__(self):
+ """See zope.app.interfaces.container.IReadContainer"""
return 2
-
- #
- ############################################################
=== Zope3/src/zope/app/workflow/stateful/instance.py 1.8 => 1.9 ===
--- Zope3/src/zope/app/workflow/stateful/instance.py:1.8 Wed Jul 30 11:24:09 2003
+++ Zope3/src/zope/app/workflow/stateful/instance.py Thu Jul 31 11:01:36 2003
@@ -18,6 +18,7 @@
__metaclass__ = type
from persistence import Persistent
+from persistence.dict import PersistentDict
from zope.app.context import ContextWrapper
from zope.app.event import publish
@@ -39,7 +40,8 @@
from zope.proxy import removeAllProxies
from zope.schema import getFields
from zope.security.management import getSecurityManager
-from zope.security.checker import CheckerPublic
+from zope.security.checker import CheckerPublic, Checker
+from zope.security.proxy import Proxy
from zope.tales.engine import Engine
@@ -79,9 +81,12 @@
class RelevantData(Persistent):
"""The relevant data object can store data that is important to the
- workflow and fires events when this data is changed."""
+ workflow and fires events when this data is changed.
- def __init__(self, schema=None):
+ 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
@@ -91,9 +96,26 @@
self.__schema = schema
directlyProvides(self, schema)
+ # Build up a Checker rules and store it for later
+ self.__checker_getattr = PersistentDict()
+ self.__checker_setattr = PersistentDict()
+ 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 == '_RelevantData__schema':
+ 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 \
@@ -114,6 +136,13 @@
process, self.__schema, key, oldvalue, value))
__setattr__ = ContextMethod(__setattr__)
+ def getChecker(self):
+ return Checker(self.__checker_getattr.get,
+ self.__checker_setattr.get)
+
+ def getSchema(self):
+ return self.__schema
+
class StateChangeInfo:
"""Immutable StateChangeInfo."""
@@ -133,7 +162,9 @@
implements(IStatefulProcessInstance)
def getData(self):
- return ContextWrapper(self._data, self, name="data")
+ # Always give out the data attribute as proxied object.
+ data = Proxy(self._data, self._data.getChecker())
+ return ContextWrapper(data, self, name="data")
data = ContextProperty(getData)
@@ -147,12 +178,8 @@
# This should really always return a schema
schema = clean_pd.getRelevantDataSchema()
if schema:
- if isinstance(schema, (str, unicode)):
- sm = getServiceManager(self)
- schema = sm.resolve(schema)
-
# create relevant-data
- self._data = RelevantData(schema)
+ self._data = RelevantData(schema, clean_pd.schemaPermissions)
else:
self._data = None
# setup permission on data
=== Zope3/src/zope/app/workflow/stateful/xmlexport_template.pt 1.1 => 1.2 ===
--- Zope3/src/zope/app/workflow/stateful/xmlexport_template.pt:1.1 Thu May 8 13:27:19 2003
+++ Zope3/src/zope/app/workflow/stateful/xmlexport_template.pt Thu Jul 31 11:01:36 2003
@@ -6,7 +6,14 @@
<schema
name = ""
- tal:attributes="name wf/getRelevantDataSchema">
+ tal:attributes="name view/relevantDataSchema">
+ <permissions>
+ <permission for="title" type="set" id="zope.Public"
+ tal:repeat="perm view/getSchemaPermissions"
+ tal:attributes="for perm/fieldName;
+ type perm/type;
+ id perm/id"/>
+ </permissions>
</schema>
<states>
@@ -14,8 +21,9 @@
title = ""
name = ""
tal:repeat="state wf/getStateNames"
- tal:attributes="title python:view.getDublinCore(wf.getState(state)).Title();
- name state"></state>
+ tal:attributes="
+ title python:view.getDublinCore(wf.getState(state)).Title();
+ name state"></state>
</states>
<transitions>
@@ -30,14 +38,15 @@
title = ""
name = ""
tal:define="transObj python:wf.getTransition(trans)"
- tal:attributes="sourceState transObj/getSourceState;
- destinationState transObj/getDestinationState;
- condition transObj/getCondition;
- script transObj/getScript;
- permission python:view.getPermissionId(transObj.getPermission());
- triggerMode transObj/getTriggerMode;
- title python:view.getDublinCore(transObj).Title();
- name trans"></transition>
+ tal:attributes="
+ sourceState transObj/getSourceState;
+ destinationState transObj/getDestinationState;
+ condition transObj/getCondition;
+ script transObj/getScript;
+ permission python:view.getPermissionId(transObj.getPermission());
+ triggerMode transObj/getTriggerMode;
+ title python:view.getDublinCore(transObj).Title();
+ name trans"/>
</tal:block>
</transitions>
=== Zope3/src/zope/app/workflow/stateful/xmlimportexport.py 1.5 => 1.6 ===
--- Zope3/src/zope/app/workflow/stateful/xmlimportexport.py:1.5 Fri Jun 6 15:29:07 2003
+++ Zope3/src/zope/app/workflow/stateful/xmlimportexport.py Thu Jul 31 11:01:36 2003
@@ -15,25 +15,26 @@
$Id$
"""
-__metaclass__ = type
-
+from xml.sax import parse
+from xml.sax.handler import ContentHandler
-from zope.app.pagetemplate.viewpagetemplatefile \
- import ViewPageTemplateFile
+from zope.app.interfaces.dublincore import IZopeDublinCore
from zope.app.interfaces.workflow.stateful \
import IStatefulProcessDefinition
from zope.app.interfaces.workflow import IProcessDefinitionImportHandler
from zope.app.interfaces.workflow import IProcessDefinitionExportHandler
-from zope.component import getAdapter
-from zope.app.interfaces.dublincore import IZopeDublinCore
+from zope.app.pagetemplate.viewpagetemplatefile \
+ import ViewPageTemplateFile
+from zope.app.services.servicenames import Permissions
+from zope.app.workflow.stateful.definition import State, Transition
+from zope.component import getAdapter, getService
+from zope.configuration.name import resolve
+from zope.interface import implements
from zope.proxy import removeAllProxies
from zope.security.checker import CheckerPublic
+from zope.security.proxy import trustedRemoveSecurityProxy
-from xml.sax import parse
-from xml.sax.handler import ContentHandler
-
-from zope.app.workflow.stateful.definition import State, Transition
-from zope.interface import implements
+__metaclass__ = type
# basic implementation for a format-checker
@@ -57,6 +58,7 @@
def __init__(self, context, encoding='latin-1'):
self.context = context
self.encoding = encoding
+ self.perm_service = getService(self.context, Permissions)
def startElement(self, name, attrs):
handler = getattr(self, 'start' + name.title().replace('-', ''), None)
@@ -75,14 +77,34 @@
startStates = noop
startTransitions = noop
+ startPermissions = noop
def startWorkflow(self, attrs):
dc = getAdapter(self.context, IZopeDublinCore)
dc.title = attrs.get('title', u'')
def startSchema(self, attrs):
- name = attrs['name'].encode(self.encoding)
- self.context.setRelevantDataSchema(name)
+ name = attrs['name'].encode(self.encoding).strip()
+ if name:
+ self.context.relevantDataSchema = resolve(name)
+
+ def startPermission(self, attrs):
+ perms = trustedRemoveSecurityProxy(self.context.schemaPermissions)
+ fieldName = attrs.get('for')
+ type = attrs.get('type')
+ perm_id = attrs.get('id')
+ if perm_id == 'zope.Public':
+ perm = CheckerPublic
+ elif perm_id == '':
+ perm = None
+ else:
+ perm = self.perm_service.getPermission(perm_id)
+ if not fieldName in perms.keys():
+ perms[fieldName] = (CheckerPublic, CheckerPublic)
+ if type == u'get':
+ perms[fieldName] = (perm, perms[fieldName][1])
+ if type == u'set':
+ perms[fieldName] = (perms[fieldName][0], perm)
def startState(self, attrs):
encoding = self.encoding
@@ -162,3 +184,20 @@
return ''
return removeAllProxies(permission).getId()
+ def getSchemaPermissions(self):
+ info = []
+ perms = self.getDefinition().schemaPermissions
+ for field, (getPerm, setPerm) in perms.items():
+ info.append({'fieldName': field,
+ 'type': 'get',
+ 'id': self.getPermissionId(getPerm)})
+ info.append({'fieldName': field,
+ 'type': 'set',
+ 'id': self.getPermissionId(setPerm)})
+ return info
+
+ def relevantDataSchema(self):
+ schema = self.getDefinition().relevantDataSchema
+ if schema is None:
+ return 'None'
+ return schema.__module__ + '.' + schema.__name__