Generate your Plone workflows by describing it in plain text with a DSL.
ftw.lawgiver generates Plone workflows based on a human readable specification written in a custom DSL.
Table of Contents
- How it works
- Action groups
- The workflow specification
- Generating the workflow
- Testing the workflow
Developing and maintaining complex Plone workflows is a time-consuming and cumbersome endeavor. Dozens of permissions need to be managed for different roles and different workflow states. Usually, this has to be done directly in the ZMI of Zope by selecting or unselecting thousands of checkboxes. This process has been shown to be very tedious and prone to errors. Furthermore, it is no simple task to document the workflow and the associated design decisions which led to the resulting configuration of permissions and roles. The extension or adaption of an existing workflow becomes very difficult, leading to workflows which are barely maintainable.
Another problem poses the communication between workflow integrator and customer. The security system of Zope is based on a role-based access control (RBAC) which is intrinsically complex due to its use of roles, permissions, and workflow states. Experience has shown that these security concepts can be hard to convey to customers.
ftw.lawgiver helps solving these problems by using a DSL to describe how a workflow should work. The lawgiver then generates the complete workflow definition (definition.xml) based on this specification. By separating this specification from the resulting workflow definition (which is in XML) the specification does not have to use permissions–handling the permissions is the job of the lawgiver.
Using the specification file the workflow can easily be regenerated at any time and will handle additional permissions automatically when regenerated. However, it is still the task of the developer to regenerate the definition.xml when more or other permissions have to be managed. He or she have to make sure that the workflow is properly installed with an upgrade step / reindexing security.
- Add ftw.lawgiver to your buildout configuration:
[instance] eggs += ftw.lawgiver
- Install the generic setup profile of ftw.lawgiver.
In the specification we use the concept of so called action groups for describing what a role is allowed to do. It basically groups together a bunch of semantically similar Plone / Zope permissions so that we only have to define the workflow based on these action groups and not on individual permissions.
For example there is an Access action group which contains permissions such as View and Access Contents Information.
The registration of a permission to an action group should be done in the package where the permission is defined. This allows to keep changes of the permissions and action group registrations together in branches, for reviews etc. ftw.lawgiver already assigns default Plone / Zope permissions to action groups.
The registration is done in ZCML. Here is an example lawgiver.zcml:
<configure xmlns="http://namespaces.zope.org/zope" xmlns:lawgiver="http://namespaces.zope.org/lawgiver" i18n_domain="my.package"> <include package="ftw.lawgiver" file="meta.zcml" /> <lawgiver:map_permissions action_group="add content" permissions="my.package: Add Foo, my.package: Add Bar" /> </configure>
If you define multiple permissions in the same
make sure to separate them by comma.
By putting the ZCML in a separate lawgiver.zcml file you can define lawgiver in your addon package without having to define a dependency to ftw.lawgiver by using zcml:condition while loading it in your default configure.zcml:
<configure xmlns="http://namespaces.zope.org/zope" xmlns:zcml="http://namespaces.zope.org/zcml" i18n_domain="my.package"> <include zcml:condition="installed ftw.lawgiver" file="lawgiver.zcml" /> </configure>
Maybe the permission to action group mapping does not work well for a specific workflow and you would like to change to mapping for this workflow only.
This can be easily achieved by also defining the workflow in the ZCML:
<configure xmlns="http://namespaces.zope.org/zope" xmlns:lawgiver="http://namespaces.zope.org/lawgiver" i18n_domain="my.package"> <include package="ftw.lawgiver" file="meta.zcml" /> <lawgiver:map_permissions action_group="add content" permissions="my.package: Add Foo, my.package: Add Bar" workflow="my_workflow" /> </configure>
The specification is written in a plain text file (specification.txt) in the same directory where the definition.xml is saved.
The states and transitions are defined in simple lists:
[My Custom Workflow] Description: A three state publication workflow Initial Status: Private Status Private: Status Pending: Status Published: Transitions: Publish (Private => Published) Submit for publication (Private => Pending) Reject (Pending => Private) Retract (Pending => Private) Publish (Pending => Published) Reject (Published => Private)
The asterisk (
*) in the state list indicates that this state is the initial
state. We are not using any internal ids for workflow states or
transitions. Instead, we use the same labels which the user will actually
see–the ids are automatically generated by ftw.lawgiver.
In Plone we have a given set of rather technical roles (e.g. Editor, Contributor, Reader) which may not apply for all use cases in real life. The customer may have own roles with different names. Since the existing roles are already well established in Plone it is usually not a good thing to add new roles to Plone. It is better to try to reuse the existing roles.
Because the customer has different labels for his roles we need to map customer roles to Plone roles:
Role mapping: editor-in-chief => Reviewer editor => Editor everyone => Anonymous
In our example we have only “normal” editors and an “editor-in-chief” who can review and publish the contents. We do not have to use the Contributor role since our editors can edit, add new content, and request a review for existing content. Therefore, it is not necessary to distinguish Editor and Contributor role.
Usually there are some general statements, for example that a user with adminstrator role can always edit the contents on any workflow state. Such statements should not be repeated for every state but defined once as a general statement.
General: An administrator can always view the content An administrator can always edit the content An administrator can always delete the content
These general statements apply for all states.
For each state we describe the actions a user with a certain role can do. We have the principle that any user / role is NOT allowed do anything by default, we have to explicitly list every action he will be allowed to perform.
Status Private: An editor can view this content. An editor can edit this content. An editor can delete this content. An editor can add new content. An editor can submit for publication. An editor-in-chief can view this content. An editor-in-chief can edit this content. An editor-in-chief can delete this content. An editor-in-chief can add new content. An editor-in-chief can publish this content. Status Pending: An editor can view this content. An editor can add new content. An editor can retract this content. An editor-in-chief can view this content. An editor-in-chief can edit this content. An editor-in-chief can delete this content. An editor-in-chief can add new content. An editor-in-chief can publish this content. An editor-in-chief can reject this content. Status Published: An editor can view this content. An editor can add new content. An editor can retract this content. An editor-in-chief can view this content. An editor-in-chief can add new content. An editor-in-chief can retract this content. Anyone can view this content.
Roles can be inherited from other roles, globally and for a single status:
[Role Inheritance Workflow] Initial Status: Foo Role mapping: editor => Editor editor-in-chief => Reviewer administrator => Site Administrator General: An administrator can always perform the same actions as an editor. An administrator can always perform the same actions as an editor-in-chief. Status Foo: An editor-in-chief can perform the same actions as an editor. An editor can view this content. An editor can edit this content. Status Bar: An editor can view this content. An editor-in-chief can view this content. An editor-in-chief can edit this content.
Worklists are automatically generated for you when you grant access to the worklist:
[A workflow] ... Status Pending: An editor-in-chief can access the worklist.
Those “can access the worklist” statements do not work in the “General” section, they need to be defined a “Status” section.
For each status with “can access the worklist” statements a worklist is generated, guarded with the role for which there is a statement.
All workflow directories in registered generic setup profiles are automatically scanned for workflow specifications. Just place a specification.txt in a workflow directory and ftw.lawgiver will discover it automatically.
- Specification: profiles/default/workflows/my_custom_workflow/specification.txt
- Workflow XML: profiles/default/workflows/my_custom_workflow/definition.txt
In this example it is assumed that profiles/default is a registered generic setup profile directory.
Sometimes the transition URLs need to point to another view. This can be
achieved by using the
transition-url option, where a string can be passed
which will then be substituted with the
transition id. Be sure to use a
%% for parts which should not be replaced when generating the workflow,
such as the
transition-url = %%(content_url)s/custom_wf_action?workflow_action=%(transition)s
For generating the workflow go to the lawgiver control panel (in the Plone control panel). There you can see a list of all workflows and by selecting one you can see the specification and other details, such as the action groups.
On this view you can generate the workflow (automatically saved in the definition.xml in the same directory as the specification.txt) and you can install the workflow / update the security.
It is important to detect when you have to rebuild your workflow. It is also important to dected permissions from third party addons which are not yet mapped to action groups.
By subclassing the
WorkflowTest it is easy to write a test for your
from ftw.lawgiver.tests.base import WorkflowTest from my.package.testing import MY_INTEGRATION_TESTING class TestMyWorkflow(WorkflowTest): # The workflow path may be a path relative to the this file or # an absolute path. workflow_path = '../profiles/default/workflows/my-workflow' # Use an integration testing layer. layer = MY_INTEGRATION_TESTING
What is tested?
- The test will fail when your workflow (
definition.xml) needs to be regenerated. This may be because new permissions should be managed.
- The test will faile when you install new addons which provide new permisisons. The permissions should be mapped to action groups or marked as unmanaged explicitly:
<configure xmlns="http://namespaces.zope.org/zope" xmlns:lawgiver="http://namespaces.zope.org/lawgiver" i18n_domain="ftw.lawgiver"> <include package="ftw.lawgiver" file="meta.zcml" /> <lawgiver:ignore workflow="my_workflow" permissions="ATContentTypes: Upload via url, ATContentTypes: View history" /> </configure>
collective.deletepermission solves a delete problem which occurs in certain situations by adding a new delete permission. See its readme for further details.
For beeing able to delete a content, the user should have the “delete” action
Delete portal content) on the content but also “add” (
on the parent content
- Initial implementation. [jone]