Placeful workflow

by Plone Documentation Team last modified Dec 06, 2009 10:10 PM
Contributors: Mikko Ohtamma, Martin Aspeli, Kamon Ayeva, Israel Saeta Pérez
b-org uses CMFPlacefulWorkflow, which ships with Plone 2.5, to manage the workflow of content objects inside a project.

Placeful workflows are based on the concept of policies. You can think of a policy as a mapping of workflows to types, in the same way as you could control from the portal_workflow tool. Policies are created, normally by copying an existing policy (possibly the default, global policy), and then applied to a context. In Plone, this can be done using the policy option in the state menu.

Placeful workflows are used in b-org Projects. Inside a project, project members should have elevated view and modify permissions over content. This is achieved using the following technique:

  • A new role TeamMember is made available within any Project.
  • A custom workflow, borg_project_default_workflow is a customisation of the default Plone workflow that has a simplified set of states and actions, and is aware of the TeamMember role.
  • A placeful workflow policy sets the default workflow, as well as the workflow for folders, to this one.
  • When a Project is created, this placeful workflow policy is enabled for the project.

The custom workflow is defined using GenericSetup, in profiles/default/workflows/borg_project_default_workflow/definition.xml. You can of course install your own workflow if necessary. The workflow policy is set up in the importVarious setup step, in setuphandlers.py:

from Products.CMFCore.utils import getToolByName
from config import LOCALROLES_PLUGIN_NAME, PLACEFUL_WORKFLOW_POLICY

...

def addProjectPlacefulWorkflowPolicy(portal, out):
"""Add the placeful workflow policy used by project spaces.
"""
placeful_workflow = getToolByName(portal, 'portal_placeful_workflow')
if PLACEFUL_WORKFLOW_POLICY not in placeful_workflow.objectIds():
placeful_workflow.manage_addWorkflowPolicy(PLACEFUL_WORKFLOW_POLICY,
duplicate_id='portal_workflow')
policy = placeful_workflow.getWorkflowPolicyById(PLACEFUL_WORKFLOW_POLICY)
policy.setTitle('[borg] Project content workflows')
policy.setDefaultChain(('borg_project_default_workflow',))
policy.setChainForPortalTypes(('Folder', 'Large Plone Folder',),
('borg_project_default_workflow',))

Again, you could add a different policy if you needed different settings.

Finally, we apply the policy when a project is created. We will see how this is set up when events are covered in the next section, but the relevant code is in events/project.py:

from zope.interface import implements
from zope.component import getUtility

from Products.CMFCore.utils import getToolByName

from Products.borg.config import PLACEFUL_WORKFLOW_POLICY
from Products.borg.interfaces import ILocalWorkflowSelection

class DefaultLocalWorkflowSelection(object):
"""Select the default local workflow policy.

Local adapters or overrides may supercede this.
"""

implements(ILocalWorkflowSelection)

workflowPolicy = PLACEFUL_WORKFLOW_POLICY


def addLocalProjectWorkflow(ob, event):
"""Apply the local workflow for project spaces when a project is added.
"""

# Add the TeamMember role if necessary
if 'TeamMember' not in ob.validRoles():
# Note: API sucks :-(
ob.manage_defined_roles(submit='Add Role',
REQUEST={'role': 'TeamMember'})


# Find out which workflow to use - this is looked up as a utility so
# that other components can override it.
workflowSelection = getUtility(ILocalWorkflowSelection, context=ob)

# Set the placeful (local) workflow
placeful_workflow = getToolByName(ob, 'portal_placeful_workflow')
ob.manage_addProduct['CMFPlacefulWorkflow'].manage_addWorkflowPolicyConfig()
config = placeful_workflow.getWorkflowPolicyConfig(ob)
config.setPolicyBelow(policy=workflowSelection.workflowPolicy)

Here, the local role is added to the newly created project instance (it is not made global so as not to pollute the global roles list), and the policy is associated with the contents of the (folderish) project object.

Note that we do not hard-code the name of the workflow policy! Instead, we ask a utility called ILocalWorkflowSelection. This could be overridden using a local utility, but the global one references the policy created above, as defined in DefaultLocalWorkflowSelection. This utility is registered in events/configure.zcml as follows:

  <utility provides="..interfaces.ILocalWorkflowSelection"
factory=".project.DefaultLocalWorkflowSelection" />