Index: openacs-4/packages/workflow/www/doc/developer-guide.adp =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/www/doc/developer-guide.adp,v diff -u -N -r1.1 -r1.2 --- openacs-4/packages/workflow/www/doc/developer-guide.adp 20 Aug 2015 18:49:02 -0000 1.1 +++ openacs-4/packages/workflow/www/doc/developer-guide.adp 26 Aug 2015 18:03:35 -0000 1.2 @@ -2,20 +2,25 @@ {/doc/workflow {Workflow}} {Package Developer's Guide to Workflow} Package Developer's Guide to Workflow - - -

Package Developer's Guide to Workflow

Workflow Documentation : Package Developer's Guide +

Package Developer's Guide to Workflow

+Workflow Documentation + : Package Developer's Guide

By Lars Pind -

Introduction

The workflow package manages a collaborative process surrounding -some object.

Examples of the object could be a bug in a bug tracker, a ticket +

+

Introduction

+

The workflow package manages a collaborative process surrounding +some object.

+

Examples of the object could be a bug in a bug tracker, a ticket in a ticket tracker, a content item in a content management system, a user contribution in a moderated forum/comments/whatever application, a user's request for particpation in a -course/event/whatever.

For example, when a new bug is submitted, someone's assigned to +course/event/whatever.

+

For example, when a new bug is submitted, someone's assigned to fix it, and whoever submitted it is assigned to verify the fix and close the bug. Once the bug's fixed, the submitter will get notified, and the bug will wait in the 'resolved' state until the submitter has verified and then closed the bug.

+ In order to make use of workflow in your own application, here are the things you need to consider:
    @@ -26,12 +31,17 @@ application will need.
  1. Write the code to set up the initial process, and to clone that process for each package instance.
  2. Integrate workflow support into your application's API.
  3. Integrate workflow support into your application's user interface.
  4. Integrate workflow into your application's queries
  5. -

Let's first look at some concepts before getting into the + +

Let's first look at some concepts before getting into the technicalities of how you actually do this. For a working example of building workflow-based applications, we recommend you take a -look at bug-tracker.

Workflow Concepts

What's in a workflow

In its broadest, most conceptual sense, a workflow is defined as +look at bug-tracker.

+

Workflow Concepts

+

What's in a workflow

+

In its broadest, most conceptual sense, a workflow is defined as (and this does get a little hairy, so feel free to skip if you just -want to start developing your applicaton):

+

Finite State Machines (FSMs)

+

As mentioned, workflow is designed to support workflows based on any model, however the only model currently implemented is the -finite state machine.

The workflow API is designed so that whenver you're using +finite state machine.

+

The workflow API is designed so that whenver you're using features that are specific to finite state machines, the procedure name will include the letters "fsm" in the name, as in workflow::case::fsm::get. That way you'll know when you're hard-wiring a dependency on the -particular workflow model.

It's considered good practice to only use non-fsm API calls, but +particular workflow model.

+

It's considered good practice to only use non-fsm API calls, but in practice, it can be hard to create good user experiences -without. So feel free.

+without. So feel free.

+
Notation:

[Action] (State) {Role}

(State1) -> [Action1] -> (State2) -> [Action2] -> (State3)

-

Cases

So much for defining the workflow. When you start "running" your +

+

Cases

+

So much for defining the workflow. When you start "running" your workflow, that's called a workflow case, or simply a case. A case is concerned with a particular object, it's always in a particular state, and it has specific people or groups assigned to -its different roles.

In-flow and out-of-flow

When defining actions, we differentiate between in-flow and +its different roles.

+

In-flow and out-of-flow

+

When defining actions, we differentiate between in-flow and out-of-flow. In-flow refers to the normal idealized flow of the workflow, out-of-flow are the rest. Concretely what it means is that if you're assigned to an in-flow action, we'll bug you about @@ -78,55 +97,71 @@ don't do that with out-of-flow actions. So we'll send a notification that says "Please come back and resolve this bug", whereas we'll not notify everybody who are allowed to comment -saying "Please come back and comment on this bug".

For bug-tracker, the normal flow (in-flow) is this:

(Open) -> [Resolve] -> (Resolved) -> [Close] --> (Closed)

Other actions not in the normal flow are [Edit] and [Comment], +saying "Please come back and comment on this bug".

+

For bug-tracker, the normal flow (in-flow) is this:

+
(Open) -> [Resolve] -> (Resolved) -> [Close] +-> (Closed)
+

Other actions not in the normal flow are [Edit] and [Comment], which are always enabled, but never change the state. And [Reopen] which throw you back to the (Open) state. And finally [Resolved] is in-flow when in the (Open) state, but out-of-flow when in the (Resolved) state, meaning that you can re-resolve a bug if you need -to, but you're not required to.

In-flow and out-of-flow depends on the action, the state, and +to, but you're not required to.

+

In-flow and out-of-flow depends on the action, the state, and the user's role in the case. For example, it might be that users in the {Submitter} role are allowed to resolve their own bugs, but the [Resolve] action will still only be considered in-flow to people in -the {Assignee} or {Resolver} role.

The Six Steps Conceptually

The recommended way a workflow is linked to an application is +the {Assignee} or {Resolver} role.

+

The Six Steps Conceptually

+

The recommended way a workflow is linked to an application is this: As part of developing your application, you define your default workflow, which will be used as a template for customization by the users of your applications. This workflow will be installed using the APM after-install callback, and will be -linked to your application's package-key.

Then when a new instance of your application is created, your +linked to your application's package-key.

+

Then when a new instance of your application is created, your default workflow will be cloned, and the clone linked to the new instance, so that your users can customize the workflow for each instance of your application individually. The default copy installed along with your package is never actually used except for cloning when creating a new instance. This means that your users can customize this deafult workflow, and the modified version will -serve as the boilerplate for all new package instances.

In order to integrate workflow with your application, you'll +serve as the boilerplate for all new package instances.

+

In order to integrate workflow with your application, you'll want to implement one or more of the callback service contracts. These can do things like determine default assignees based on certain data in your application, get information about your application's object for use when sending out notifications, or perform side-effects, such as actually changing the publication -state of a content item when you execute the [Publish] action.

When integrating the workflow with your application's user +state of a content item when you execute the [Publish] action.

+

When integrating the workflow with your application's user experience, what workflow will give you is essentially the list of actions that the given user can perform on the given object at the given time. In bug-tracker, for example, bug-tracker takes care of displaying the form displaying and editing a bug, while workflow takes care of displaying the buttons that say [Comment], [Edit], [Resolve], [Reopen], [Close], etc., along the bottom of the form. Workflow also has a place to store which form elements should -be opened for editing depending on the action being executed.

Your application should typically have an API for creating a new +be opened for editing depending on the action being executed.

+

Your application should typically have an API for creating a new object, editing an object, etc. This application object API will need to be workflow-aware, so when a new object is created, a new workflow case will be started as well. And when the object's edited, that should generally only happen through a workflow -action, so that needs to be taken into account as well.

The final step is integrating workflow into your application's +action, so that needs to be taken into account as well.

+

The final step is integrating workflow into your application's queries when you want to filter an object listing/count based on the workflow state. This is the only place where you'll -directly be dependent on the workflow data model.

Defining Your Process (FSM)

The current version of workflow only supports workflows based on +directly be dependent on the workflow data model.

+

Defining Your Process (FSM)

+

The current version of workflow only supports workflows based on a finite state machine (FSM). Support for other models, such as petri nets and directed graphs are possible, but not currently -designed or implemented.

An FSM-based workflow consists of a set of states, actions, and -roles.

You define a new workflow like this:

+designed or implemented.

+

An FSM-based workflow consists of a set of states, actions, and +roles.

+

You define a new workflow like this:

+
 set spec {
     workflow-short-name {
         ...
@@ -152,19 +187,25 @@
 }
 
 set workflow_id [workflow::fsm::new_from_spec -spec $spec]
-

All the items (workflow, roles, states, actions) have a +

+

All the items (workflow, roles, states, actions) have a short-name, which should be lowercase and use underbar (_) instead of spaces. These are mainly used to refer to the items in other -parts of the spec.

The workflow short name can be used to refer to the workflow in +parts of the spec.

+

The workflow short name can be used to refer to the workflow in your application, which is useful if you have several different workflows. The bug-tracker, for example, could have a workflow for -bugs and another one for patches.

Finally, you can also refer states, roles, and actions in your +bugs and another one for patches.

+

Finally, you can also refer states, roles, and actions in your application using short names, although this creates a dependency in your application on a particular form of the workflow, and there's currently no mechanism for ensuring that your workflow contains the states, roles, and actions you'd refer to. This is on -the todo-list.

Workflow

These are the attributes you can specify for the workflow -itself:

+the todo-list.

+

Workflow

+

These are the attributes you can specify for the workflow +itself:

+
@@ -190,12 +231,16 @@ -
Workflow Attributes
AttributeDescription
actionsDenotes the section of the spec that defines the workflow's actions.
+ +
Internationalization Note:

When we make workflow internationalized for OpenACS 5.0, pretty names will contain message keys in the form "#message-key#". More about this in the package developer's guide to internationalization.

-

Roles

Attributes for roles:

+ +

Roles

+

Attributes for roles:

+
@@ -204,7 +249,10 @@ -
Role Attributes
AttributeDescription
callbacksCallbacks that define how assignment of this role to users is done.

States

A few typical examples of states:

+
+

States

+

A few typical examples of states:

+ @@ -216,8 +264,10 @@ -
Examples of States
ApplicationStates
Simple Approval(Requested), (Approved), and (Rejected)

These are the state attributes in the workflow -specification:

+
+

These are the state attributes in the workflow +specification:

+ @@ -229,27 +279,36 @@ It's currently up to your application to do incorporate this into your application. -
State Attributes
AttributeDescription

Actions

Actions are what the workflow allows you to do to your -object.

+ +

Actions

+

Actions are what the workflow allows you to do to your +object.

+
Terminology:
Enabled
The action is allowed to be executed in the workflow's current state.
Allowed
The given user is allowed to execute the action given his current relationship to the workflow case and the object.
Assigned
The same as allowed, but the action is in-flow for this user.
Available
The action is both enabled and allowed for this user.
-

Some actions will always be enabled. In bug-tracker, for +

+

Some actions will always be enabled. In bug-tracker, for example, we have [Comment] and [Edit] actions, which are always allowed, regardless of whether the bug is (Open), (Resolved), or -(Closed).

Other actions, however, will only be enabled in certain +(Closed).

+

Other actions, however, will only be enabled in certain states. In bug-tracker, for example, the [Close] action is only -available in the (Resolved) state.

Another distinction is that some actions change the state, +available in the (Resolved) state.

+

Another distinction is that some actions change the state, and others do not. [Comment] and [Edit], for example, do not. [Resolve], [Close], and [Reopen] do. For an FSM, when an action changes the state, you simply specify what the new state should -be.

There's a special action called the initial action. This +be.

+

There's a special action called the initial action. This is implicitly executed when a new case is started for this workflow, and must always specify the "new_state" attribute to -define which state new cases start out in.

Attributes for actions:

+define which state new cases start out in.

+

Attributes for actions:

+
@@ -290,8 +349,11 @@ -
Action Attributes
AttributeDescription
callbacksSide-effect callbacks which are executed when this action is executed.

Putting A Workflow Together

When you put this all together, here's a real live example of -what defining a workflow could look like:

+
+

Putting A Workflow Together

+

When you put this all together, here's a real live example of +what defining a workflow could look like:

+
 ad_proc -private bug_tracker::bug::workflow_create {} {
     Create the 'bug' workflow for bug-tracker
 } {
@@ -407,13 +469,18 @@
     
     return $workflow_id
 }
-

Defining Callbacks

There are a number of different types of callbacks, each of +

+

Defining Callbacks

+

There are a number of different types of callbacks, each of which applies to different workflow items (workflows, roles, -states, actions). They're all defined as service contracts.

In order to make use of them, your application will need to +states, actions). They're all defined as service contracts.

+

In order to make use of them, your application will need to implement these service contracts, and register the implementation with the relevant workflow item through the 'callbacks' attribute -in the spec above.

Here are the types of callbacks defined, how they're used, and -the workflow items they apply to.

+in the spec above.

+

Here are the types of callbacks defined, how they're used, and +the workflow items they apply to.

+
@@ -466,9 +533,11 @@ -
Service contracts
Service ContractApplies ToDescription

All the service contracts have 3 operations each. The first two + +

All the service contracts have 3 operations each. The first two are the same for all service contracts, and they really just act -like static variables:

+like static variables:

+
@@ -481,8 +550,10 @@ in the user interface to let the workflow designer pick which implementation to use. -
Common service contract operations
OperationInputOutputDescription

The third operation is the one that does the real work. Here are -the inputs and outputs:

+
+

The third operation is the one that does the real work. Here are +the inputs and outputs:

+ @@ -507,18 +578,25 @@ -
Specific service contract operations
ContractOperationInputOutput
Workflow.NotificationInfoGetNotificationInfocase_id:integer object_id:integerinfo:string,multiple

For the most up-to-date information about the service contracts, + +

For the most up-to-date information about the service contracts, your safest bet is to refer to the user-visible pages of the acs-service-contract package itself, which will let you view your -currently installed contracts and implementations.

Log Entry Data and Log Entry Titles

One noteworthy thing that side-effects can be used for, is to +currently installed contracts and implementations.

+

Log Entry Data and Log Entry Titles

+

One noteworthy thing that side-effects can be used for, is to record information about a log entry for use later in displaying a more detailed log entry title, or can be used to e.g. tie a workflow log entry to a particular content repository revision, -etc.

Using workflow::case::add_log_data, +etc.

+

Using workflow::case::add_log_data, you can add arbitrary key/value pairs to a log entry. These can the be retrieved later using workflow::case::get_log_data_by_key, -and workflow::case::get_log_data.

Installing and Instantiating (APM Tcl Callbacks)

Here are the workflow-related operations that you'll typically -want your application to do from the APM Tcl Callbacks:

+and workflow::case::get_log_data.

+

Installing and Instantiating (APM Tcl Callbacks)

+

Here are the workflow-related operations that you'll typically +want your application to do from the APM Tcl Callbacks:

+
after-install
before-uninstall
    @@ -529,14 +607,19 @@ API)
after-instantiate
before-uninstantiate
-

To see what this could look like in practice, check out -packages/bug-tracker/tcl/install-procs.tcl.

Integrating With Your Application's API

Newer applications will define a namespace for each of the +

+

To see what this could look like in practice, check out +packages/bug-tracker/tcl/install-procs.tcl.

+

Integrating With Your Application's API

+

Newer applications will define a namespace for each of the objects it defines, which will contain procs like "get", "new", -"delete", etc., to manipulate these objects.

Given such a setup, here are the procs that you want to +"delete", etc., to manipulate these objects.

+

Given such a setup, here are the procs that you want to integrate workflow into for your workflow-integrated objects. For a real-life example, see packages/bug-tracker/tcl/bug-procs.tcl and search for -"workflow::".

+"workflow::".

+
get
In addition to your application's object data, you'll want to call workflow::case::get_id to get the case_id for your object, and then either workflow::case::get @@ -563,47 +646,66 @@ to execute the action, including state changes, side-effects, and notifications.

-

Integrating With Your Application's User Interface

Usually, you'll want one page that lists all the objects in your +

+

Integrating With Your Application's User Interface

+

Usually, you'll want one page that lists all the objects in your package instance, and another that lets the user view/edit one object, called the object form page. This section is about the object form page, the next section is about the object listing -page.

For a real-life example, look at +page.

+

For a real-life example, look at packages/bug-tracker/www/bug.tcl. You may want to have -that handy while reading through this.

We're hoping to make some streamlining of both ad_form and +that handy while reading through this.

+

We're hoping to make some streamlining of both ad_form and workflow to make this form page integration even easier at some -point. But no promises.

Use workflow::case::get_id -to get the case_id.

If you want buttons along the bottom of the form like +point. But no promises.

+

Use workflow::case::get_id +to get the case_id.

+

If you want buttons along the bottom of the form like bug-tracker, use the new 'action' feature of the form builder. What you do is pass a list of possible actions to the form builder as a list-of-lists with { label value }. These will be displayed as -buttons at the bottom of the form.

When one of these buttons are clicked, the form will be in +buttons at the bottom of the form.

+

When one of these buttons are clicked, the form will be in edit-mode, and you can use form get_action to get the -value of the action chosen.

So up top, you'll want to ask the form if an action is in +value of the action chosen.

+

So up top, you'll want to ask the form if an action is in progress, and which one it is, by saying set action_id [form get_action form-id]. If no action is in progress this -will return the empty string.

Then you should check that this action is currently available to -this user by saying workflow::case::action::available_p.

To get the currently available actions so you can offer them to +will return the empty string.

+

Then you should check that this action is currently available to +this user by saying workflow::case::action::available_p.

+

To get the currently available actions so you can offer them to the user, use workflow::case::get_available_actions which will return the action_id's, then workflow::action::get -to get the details about each of the actions.

If you're using ad_form, and you want only one +to get the details about each of the actions.

+

If you're using ad_form, and you want only one assignee per role, and you want assignment integrated with your form, use workflow::case::role::add_assignee_widgets to add the widgets. It'll do an ad_form -extend, so they'll -appear at the point at which you call this proc.

To set the editable fields as defined for the action, do -this:

+appear at the point at which you call this proc.

+

To set the editable fields as defined for the action, do +this:

+
 if { ![empty_string_p $action_id] } {
     foreach field [workflow::action::get_element -action_id $action_id -element edit_fields] { 
         element set_properties bug $field -mode edit 
     }
 }
-

Similarly, on submit, you'll want to set the value of the -editable fields.

To populate values of the assignee widgets, use +

+

Similarly, on submit, you'll want to set the value of the +editable fields.

+

To populate values of the assignee widgets, use workflow::case::role::set_assignee_values in your -on_request block.

To add the case log to the comment field, use workflow::case::get_activity_html +on_request block.

+

To add the case log to the comment field, use workflow::case::get_activity_html and feed it to the before_html property of a -textarea.

Integrating With Your Application's Queries

Here's an example of how the bug-tracker integrates with -workflow for nformation about the current state of bugs.

+textarea.

+

Integrating With Your Application's Queries

+

Here's an example of how the bug-tracker integrates with +workflow for nformation about the current state of bugs.

+
 select b.bug_id,
        ...
 
@@ -635,8 +737,14 @@
 and    st.state_id = cfsm.current_state
 and    b.project_id = :package_id
 order  by $order_by_clause
-

Note the outer join to get the assignee(s). The joins to get -information about the current state is straight-forward.

Good Luck!

That's all I think you'll need to know to start developing -workflow-enabled applications.

Let me know how it goes, or of something's missing, by posting -on the OpenACS Forums.


lars\@pinds.com
- +
+

Note the outer join to get the assignee(s). The joins to get +information about the current state is straight-forward.

+

Good Luck!

+

That's all I think you'll need to know to start developing +workflow-enabled applications.

+

Let me know how it goes, or of something's missing, by posting +on the OpenACS Forums.

+
+ +
lars\@pinds.com