This page describes a prototype Workflow Plugin for Confluence. After reading it you should be able to create a workflow description and use it to manage a set of pages in Confluence.
The purposes of the Confluence Workflow Plugin Prototype are:
The feature that this does not provide is the ability of different users to see different versions of a page. This is a problem for approval workflows, where we want an edit to remain invisible to 'ordinary' users until it has been approved.
I've also written up some ideas for a minimal Approval Workflow.
You will need Java and Groovy development skills to implement this plugin. This is currently provided 'as-is' without Atlassian technical support, but you can search for or post questions relating to it in the Developer Forums. Alternatively, the Atlassian partner Saikore now offers paid support.
This section describes the concepts used in building the Workflow Plugin.
This is the entity whose life cycle is managed by the workflow plugin. In this implementation a client is a Confluence page. The client is responsible for remembering which workflow it is taking part in, remembering its workflow state, and changing this state when told to by the workflow system. A client may (and should) have other state information which is not visible to the workflow system, for instance the contents of a Confluence page are not managed by the workflow system at all.
This is the set of data which defines a workflow. A workflow type is assembled from collections of States, Operations, Triggers and Actions.
At any time a Workflow Client is in one (and only one) State. This state determines which Operations are available to be performed on the client.
An Operation may be requested by the user on a Workflow Client. An Operation itself doesn't change any state, either in the workflow system or in the Workflow Client, but simply sends a signal to the Workflow Type that this Operation has been requested on that particular Workflow Client. It is just a description meaningful to a user, associated with a code meaningful to the Workflow Type, together with security rules to determine when the Operation can be performed. The signals sent to the Workflow Type may cause one or more Triggers to fire. Whether an Operation is available on a particular Client depends on the State of the client and the group membership of the current user. In addition to Operations defined in a particular Workflow Type, all Workflow Types recognize page edit and page view operations.
A Trigger listens for Operations, and either fires or does not fire, depending on the Operation, its internal state (if any - many simple triggers are stateless) and its implementation. When a Trigger fires it tells the set of Actions it contains to execute.
Examples of Triggers are:
An Action is a piece of code which is executed in response to the firing of a Trigger.
Some Actions interact with the Workflow System:
Others interact with Confluence:
Others could interact with the contents of the page itself:
From you Confluence install directory, go to plugins/workflow or acccess from the Confluence source under src/etc/plugins/workflow. Build the plugin into a JAR file.
Decide what groups will be involved in the workflow, create them and assign appropriate users to them. Grant suitable permissions to the space.
You need to create an instance of a class which implements com.atlassian.confluence.extra.workflow.WorkflowType, and register it by passing it to WorkflowManager.registerType().
One way to do this on a test basis is to put your workflow type in a {script} macro. The script macro can be downloaded from here. You'll need to visit the page after restarting the server.
The example below uses a Groovy script - you could just as well use Beanshell, Jython or JRuby.
1 2{script:groovy} import com.atlassian.confluence.extra.workflow.*; import com.atlassian.confluence.core.ContentPermission; State requested = new State("test", "In Progress", "In Progress"); State readyToReview = new State("test", "Ready for review", "Ready for review"); State accepted = new State("test", "Accepted", "Accepted"); State rejected = new State("test", "Rejected", "Rejected"); def states = [DEV:requested, readyToReview, accepted, rejected]; def ops = [ new DefaultOperation([DEV:requested, rejected], [DEV:"writer"], "completed", "Submit for Review"), new DefaultOperation([DEV:readyToReview],[DEV:"reviewer"], "accept", "Accept"), new DefaultOperation([DEV:readyToReview],[DEV:"reviewer"], "reject", "Reject"), ]; def groups = [DEV:"writer", "reviewer", "confluence-administrator"]; def triggers = [ new SingleEventTrigger("init", [DEV: new StateChangeAction(requested), new RestrictAccessToGroupAction(new ContentPermission(ContentPermission.EDIT_PERMISSION,"writer")), new RestrictAccessToGroupAction(new ContentPermission(ContentPermission.VIEW_PERMISSION,"writer")), ] ), new SingleEventTrigger( "completed", [DEV: new StateChangeAction(readyToReview), new RestrictAccessToGroupAction(new ContentPermission(ContentPermission.EDIT_PERMISSION,"reviewer")), new RestrictAccessToGroupAction(new ContentPermission(ContentPermission.VIEW_PERMISSION,"reviewer")), ] ), new SingleEventTrigger( "accept", [DEV: new StateChangeAction(accepted), new RestrictAccessToGroupAction(new ContentPermission(ContentPermission.EDIT_PERMISSION,"empty-group")), new RestrictAccessToGroupAction(new ContentPermission(ContentPermission.VIEW_PERMISSION,"confluence-users")) ] ), new SingleEventTrigger( "reject", [DEV: new StateChangeAction(rejected), new RestrictAccessToGroupAction(new ContentPermission(ContentPermission.EDIT_PERMISSION,"writer")), new RestrictAccessToGroupAction(new ContentPermission(ContentPermission.VIEW_PERMISSION,"writer")) ] ), new SingleEventTrigger( PageEditOperation.OPERATION_NAME, [DEV: new StateChangeAction(requested), new RestrictAccessToGroupAction(new ContentPermission(ContentPermission.EDIT_PERMISSION,"writer")), new RestrictAccessToGroupAction(new ContentPermission(ContentPermission.VIEW_PERMISSION,"writer")) ] ), ]; WorkflowManager.registerType(new DefaultWorkflowType("test2","Page Review 2",states,ops,triggers,groups)); {script}
Put a {workflowtype:yourWorkflowTypeName} macro after your script, so you can see that it is properly creating the WorkflowType.
To make a page take part in the workflow you have just created, add the {workflow:workflowTypeName} macro to the page and hit Update.
You'll get a workflow box with the option 'Start Workflow'. Select this and the page will refresh. The workflow box will now indicate that the page is in the starting state for that workflow type.
You can use the {workflowTasks} macro to display a list of all workflow pages which are descendants of the current page. Any task which the viewing user can perform an action on will be starred.
Rate this page: