|
1 .. -*- coding: utf-8 -*- |
|
2 |
|
3 .. _Workflow: |
|
4 |
|
5 Define a Workflow |
|
6 ================= |
|
7 |
|
8 General |
|
9 ------- |
|
10 |
|
11 A workflow describes how certain entities have to evolve between |
|
12 different states. Hence we have a set of states, and a "transition graph", |
|
13 i.e. a set of possible transitions from one state to another state. |
|
14 |
|
15 We will define a simple workflow for a blog, with only the following |
|
16 two states: `submitted` and `published`. So first, we create a simple |
|
17 *CubicWeb* instance in ten minutes (see :ref:`BlogFiveMinutes`). |
|
18 |
|
19 Set-up a workflow |
|
20 ----------------- |
|
21 |
|
22 We want to create a workflow to control the quality of the BlogEntry |
|
23 submitted on the instance. When a BlogEntry is created by a user |
|
24 its state should be `submitted`. To be visible to all, it has to |
|
25 be in the state `published`. To move it from `submitted` to `published`, |
|
26 we need a transition that we can call `approve_blogentry`. |
|
27 |
|
28 A BlogEntry state should not be modifiable by every user. |
|
29 So we have to define a group of users, `moderators`, and |
|
30 this group will have appropriate permissions to publish a BlogEntry. |
|
31 |
|
32 There are two ways to create a workflow: from the user interface, or |
|
33 by defining it in ``migration/postcreate.py``. This script is executed |
|
34 each time a new ``cubicweb-ctl db-init`` is done. We strongly |
|
35 recommend to create the workflow in ``migration/postcreate.py`` and we |
|
36 will now show you how. Read `Two bits of warning`_ to understand why. |
|
37 |
|
38 The state of an entity is managed by the `in_state` attribute which |
|
39 can be added to your entity schema by inheriting from |
|
40 `cubicweb.schema.WorkflowableEntityType`. |
|
41 |
|
42 |
|
43 About our example of BlogEntry, we must have: |
|
44 |
|
45 .. sourcecode:: python |
|
46 |
|
47 from cubicweb.schema import WorkflowableEntityType |
|
48 |
|
49 class BlogEntry(WorkflowableEntityType): |
|
50 ... |
|
51 |
|
52 |
|
53 Create states, transitions and group permissions |
|
54 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
55 |
|
56 The ``postcreate.py`` script is executed in a special environment, adding |
|
57 several *CubicWeb* primitives that can be used. |
|
58 |
|
59 They are all defined in the ``class ServerMigrationHelper``. |
|
60 We will only discuss the methods we use to create a workflow in this example. |
|
61 |
|
62 A workflow is a collection of entities of type ``State`` and of type |
|
63 ``Transition`` which are standard *CubicWeb* entity types. |
|
64 |
|
65 To define a workflow for BlogDemo, please add the following lines |
|
66 to ``migration/postcreate.py``: |
|
67 |
|
68 .. sourcecode:: python |
|
69 |
|
70 _ = unicode |
|
71 |
|
72 moderators = add_entity('CWGroup', name=u"moderators") |
|
73 |
|
74 This adds the `moderators` user group. |
|
75 |
|
76 .. sourcecode:: python |
|
77 |
|
78 wf = add_workflow(u'blog publication workflow', 'BlogEntry') |
|
79 |
|
80 At first, instanciate a new workflow object with a gentle description |
|
81 and the concerned entity types (this one can be a tuple for multiple |
|
82 value). |
|
83 |
|
84 .. sourcecode:: python |
|
85 |
|
86 submitted = wf.add_state(_('submitted'), initial=True) |
|
87 published = wf.add_state(_('published')) |
|
88 |
|
89 This will create two entities of type ``State``, one with name |
|
90 'submitted', and the other with name 'published'. |
|
91 |
|
92 ``add_state`` expects as first argument the name of the state you want |
|
93 to create and an optional argument to say if it is supposed to be the |
|
94 initial state of the entity type. |
|
95 |
|
96 .. sourcecode:: python |
|
97 |
|
98 wf.add_transition(_('approve_blogentry'), (submitted,), published, ('moderators', 'managers'),) |
|
99 |
|
100 This will create an entity of type ``Transition`` with name |
|
101 `approve_blogentry` which will be linked to the ``State`` entities |
|
102 created before. |
|
103 |
|
104 ``add_transition`` expects |
|
105 |
|
106 * as the first argument: the name of the transition |
|
107 * then the list of states on which the transition can be triggered, |
|
108 * the target state of the transition, |
|
109 * and the permissions |
|
110 (e.g. a list of user groups who can apply the transition; the user |
|
111 has to belong to at least one of the listed group to perform the action). |
|
112 |
|
113 .. sourcecode:: python |
|
114 |
|
115 checkpoint() |
|
116 |
|
117 .. note:: |
|
118 Do not forget to add the `_()` in front of all states and transitions names while creating |
|
119 a workflow so that they will be identified by the i18n catalog scripts. |
|
120 |
|
121 In addition to the user groups (one of which the user needs to belong |
|
122 to), we could have added a RQL condition. In this case, the user can |
|
123 only perform the action if the two conditions are satisfied. |
|
124 |
|
125 If we use an RQL condition on a transition, we can use the following variables: |
|
126 |
|
127 * `X`, the entity on which we may pass the transition |
|
128 * `U`, the user executing that may pass the transition |
|
129 |
|
130 |
|
131 .. image:: ../../images/03-transitions-view_en.png |
|
132 |
|
133 You can notice that in the action box of a BlogEntry, the state is now |
|
134 listed as well as the possible transitions for the current state |
|
135 defined by the workflow. |
|
136 |
|
137 The transitions will only be displayed for users having the right permissions. |
|
138 In our example, the transition `approve_blogentry` will only be displayed |
|
139 for the users belonging to the group `moderators` or `managers`. |
|
140 |
|
141 |
|
142 Two bits of warning |
|
143 ~~~~~~~~~~~~~~~~~~~ |
|
144 |
|
145 We could perfectly use the administration interface to do these |
|
146 operations. It is a convenient thing to do at times (when doing |
|
147 development, to quick-check things). But it is not recommended beyond |
|
148 that because it is a bit complicated to do it right and it will be |
|
149 only local to your instance (or, said a bit differently, such a |
|
150 workflow only exists in an instance database). Furthermore, you cannot |
|
151 write unit tests against deployed instances, and experience shows it |
|
152 is mandatory to have tests for any mildly complicated workflow |
|
153 setup. |
|
154 |
|
155 Indeed, if you create the states and transitions through the user |
|
156 interface, next time you initialize the database you will have to |
|
157 re-create all the workflow entities. The user interface should only be |
|
158 a reference for you to view the states and transitions, but is not the |
|
159 appropriate interface to define your application workflow. |