This release is a pre-release and may not be stable for production use.
Multi-Workflow Support for Plone
collective.multiworkflow
The backend package for Multi-Workflow Support for Plone — a Plone 6.2 add-on that assigns additional workflows to content types through behaviors, and carries them through the catalog, the REST API, and Volto. See also the frontend package @plone-collective/volto-multiworkflow.
Features
When a content type provides a participating behavior, this package appends one or more additional workflows to that type's workflow chain. The type's configured chain is never replaced, and content that does not provide the behavior is left completely untouched.
-
Behavior-driven chains — a behavior marker interface extends
IAdditionalWorkflowsand declares the workflow ids it contributes with one ZCML directive. A workflow chain adapter, registered for that marker, appends them to the type's existing chain.<configure xmlns:plone="http://namespaces.plone.org/plone"> <plone:additionalworkflows marker=".interfaces.IFoundationMember" workflows="foundation_member_workflow" /> </configure>
workflowsis a whitespace-separated list, appended in the order given. Amarkerthat does not extendIAdditionalWorkflowsraisesConfigurationErrorwhile the ZCML is read, rather than being ignored at runtime. The directive is available as soon as this package is installed —Products.CMFPloneauto-includes add-onmeta.zcml— but a package that wants to be explicit can<include package="collective.multiworkflow" file="meta.zcml" />first. For contributions ZCML cannot express, thecontributes()factory the directive wraps stays public. -
Deterministic composition — the base chain order is preserved, contributed workflows are appended in interface resolution order, and duplicates are removed. A contributed workflow id that does not exist in
portal_workflowis logged and skipped rather than breaking chain lookup. -
review_stateis never affected — each additional workflow uses its ownstate_variable, so Plone's publication workflow behaves exactly as before. -
Workflow-aware helpers —
get_state,get_states,transition, andtransitionsread and drive per-workflow state. Their defaults reproduce today'splone.apibehavior exactly.conflicting_permissionsaudits a chain for workflows claiming the same permission. -
REST API — on participating content, the
@workflowendpoint gains achainkey with one entry per workflow (workflow_id,title,state,state_variable,transitions,history), and its top-leveltransitionslist is narrowed to the primary workflow, so a client written before this package cannot present an unrelated workflow's transitions as publication actions.@historymerges every chain workflow's transitions into the single time-sorted stream it already returns, tagging every entry with aworkflow_id(nullfor versioning entries). Both are registered on the marker interface, so content that provides no participating behavior is served core's payload untouched — with nochainkey at all, which is what a client should test for. -
Catalog support — the installation profile adds one
workflow_statesKeywordIndexdescribing an object's whole chain, so a site gains no further indexes as behaviors contribute more workflows. Values read<workflow-id>|<state-id>and are in chain order; build them withformat_state()and read them back withparse_state(). Parsedreview_statecollection queries are rewritten onto it, so collections written before the add-on keep working. -
Import-safe —
plone.exportimportrestores workflow state by assigningworkflow_historydirectly, which fires no transition and so leaves an additional workflow's catalog entry stale.collective.multiworkflow.exportimportpatches the importer to reindex what it changed. The patch is generic, names no index of this package's own, and is written to be moved upstream. -
Fully typed — every public API is annotated,
mypyruns over the package in CI, and a PEP 561py.typedmarker ships in the wheel, so consumers type check against this package rather than around it.
Requirements for additional workflows
An additional workflow may manage permissions. DCWorkflowDefinition rewrites only the permissions listed in its own permissions, and executing a transition re-applies the mappings of the transitioning workflow alone, so workflows sharing a chain compose as long as their permission sets do not overlap.
- Declare
workflow_statesas thestate_variable; neverreview_state. Using the same name as the catalog index is what makesWorkflowTool._reindexWorkflowVariableskeep that index fresh on every transition, with no subscriber involved. Sharing the name across workflows is safe, because DCWorkflow keys its status records by workflow id rather than by variable name. A workflow keeping a bespoke variable still works: an event handler reindexes for it, but only when nothing else will. - Keep transition ids unique across every workflow in a type's chain.
- Keep managed permissions disjoint across the chain. Where two workflows claim the same permission, the mapping is left as whichever transitioned last wrote it, and stays that way until the other transitions or
portal_workflow.updateRoleMappings()runs.conflicting_permissions(obj)reports the overlap for a given object.
Not in scope
- Placeful workflows (
CMFPlacefulWorkflow) integration. - Sub-workflows — a transition in one workflow triggering another. The chain model is concurrent, not hierarchical.
- Reconciling workflows that manage the same permission — the package reports the conflict rather than arbitrating it.
- Any change to
review_statesemantics.
Trying the example
The package ships an example — a foundation_member behavior and a foundation_member_workflow that tracks a membership lifecycle — but installing collective.multiworkflow deliberately does not add a behavior to your site.
The example lives in the collective.multiworkflow.demo subpackage, which the root configure.zcml never includes, so neither the behavior nor its demo profile exists in a plain installation.
To see it running, load that package's ZCML explicitly and apply its profile.
This repository's instance.yaml already does the first part for local development:
zcml_package_includes: 'collective.multiworkflow,collective.multiworkflow.demo'
then install the collective.multiworkflow.demo:demo profile. make create-site does both.
Documentation
Full documentation is published at collective.github.io/collective-multiworkflow, and its source lives in docs/ at the repository root.
The pages closest to this package are the Python API reference, the ZCML directive, the catalog index, and the REST API, whose request and response examples are generated by this package's own test suite, in tests/docs/.
Installation
Install collective.multiworkflow with uv.
uv add collective.multiworkflow
Create the Plone site.
make create-site
Contribute
Prerequisites ✅
- An operating system that runs all the requirements mentioned.
- uv
- Make
- Git
- Docker (optional)
Installation 🔧
-
Clone this repository.
git clone git@github.com:collective/collective-multiworkflow.git cd collective-multiworkflow/backend
-
Install this code base.
make install
Add features using plonecli or bobtemplates.plone
This package provides markers as strings (<!-- extra stuff goes here -->) that are compatible with plonecli and bobtemplates.plone.
These markers act as hooks to add all kinds of features through subtemplates, including behaviors, control panels, upgrade steps, or other subtemplates from bobtemplates.plone.
plonecli is a command line client for bobtemplates.plone, adding autocompletion and other features.
To add a feature as a subtemplate to your package, use the following command pattern.
make add <template_name>
For example, you can add a content type to your package with the following command.
make add content_type
You can add a behavior with the following command.
make add behavior
You can check the list of available subtemplates in the [`bobtemplates.plone` `README.md` file](https://github.com/plone/bobtemplates.plone/?tab=readme-ov-file#provided-subtemplates).
See also the documentation of [Mockup and Patternslib](https://6.docs.plone.org/classic-ui/mockup.html) for how to build the UI toolkit for Classic UI.
License
The project is licensed under GPLv2.
Credits and acknowledgements 🙏
Generated using Cookieplone (2.0.0b3) and cookieplone-templates (61a8f90) on 2026-08-07 16:01:41.322560. A special thanks to all contributors and supporters!
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file collective_multiworkflow-1.0.0a1.tar.gz.
File metadata
- Download URL: collective_multiworkflow-1.0.0a1.tar.gz
- Upload date:
- Size: 238.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.12.4 {"installer":{"name":"uv","version":"0.12.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
47e465e71710377b39fd22a54f18f68136a183b2512cb76288ab2f5d98015936
|
|
| MD5 |
5c51c89743847963dfbc45c35191d048
|
|
| BLAKE2b-256 |
fed6368c7e856c686b6e97cecd7e590f9a3f590e2a9557f3ac2124426420d5ca
|
File details
Details for the file collective_multiworkflow-1.0.0a1-py3-none-any.whl.
File metadata
- Download URL: collective_multiworkflow-1.0.0a1-py3-none-any.whl
- Upload date:
- Size: 226.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.12.4 {"installer":{"name":"uv","version":"0.12.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b7eb91d1fcd11e3a168d3026f06402ac05d45cf6e25e44192a121169ac7e86e0
|
|
| MD5 |
dc2b156b19a7f8d426467720b19cb8bf
|
|
| BLAKE2b-256 |
2f3925323afa38f6697011e993cc95a43875d7f2910ebffef9521963bcbaba38
|