|
1 .. -*- coding: utf-8 -*- |
|
2 |
|
3 .. _Workflow: |
|
4 |
|
5 Defining a Workflow |
|
6 =================== |
|
7 |
|
8 General |
|
9 ------- |
|
10 |
|
11 A workflow describes how certain entities have to evolve between different |
|
12 states. Hence we have a set of states, and a "transition graph", i.e. a set of |
|
13 possible transitions from one state to another state. |
|
14 |
|
15 We will define a simple workflow for a blog, with only the following two states: |
|
16 `submitted` and `published`. You may want to take a look at :ref:`TutosBase` if |
|
17 you want to quickly setup an instance running a blog. |
|
18 |
|
19 Setting 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 Creating states, transitions and group permissions |
|
54 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
55 |
|
56 The :mod:`postcreate` script is executed in a special environment, |
|
57 adding several |cubicweb| primitives that can be used. |
|
58 |
|
59 They are all defined in the :class:`ServerMigrationHelper` class. |
|
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 |
|
119 transitions names while creating a workflow so that they will be |
|
120 identified by the i18n catalog scripts. |
|
121 |
|
122 In addition to the user groups (one of which the user needs to belong |
|
123 to), we could have added a RQL condition. In this case, the user can |
|
124 only perform the action if the two conditions are satisfied. |
|
125 |
|
126 If we use an RQL condition on a transition, we can use the following variables: |
|
127 |
|
128 * `X`, the entity on which we may pass the transition |
|
129 * `U`, the user executing that may pass the transition |
|
130 |
|
131 |
|
132 .. image:: ../../images/03-transitions-view_en.png |
|
133 |
|
134 You can notice that in the action box of a BlogEntry, the state is now |
|
135 listed as well as the possible transitions for the current state |
|
136 defined by the workflow. |
|
137 |
|
138 The transitions will only be displayed for users having the right permissions. |
|
139 In our example, the transition `approve_blogentry` will only be displayed |
|
140 for the users belonging to the group `moderators` or `managers`. |
|
141 |
|
142 |
|
143 Two bits of warning |
|
144 ~~~~~~~~~~~~~~~~~~~ |
|
145 |
|
146 We could perfectly use the administration interface to do these |
|
147 operations. It is a convenient thing to do at times (when doing |
|
148 development, to quick-check things). But it is not recommended beyond |
|
149 that because it is a bit complicated to do it right and it will be |
|
150 only local to your instance (or, said a bit differently, such a |
|
151 workflow only exists in an instance database). Furthermore, you cannot |
|
152 write unit tests against deployed instances, and experience shows it |
|
153 is mandatory to have tests for any mildly complicated workflow |
|
154 setup. |
|
155 |
|
156 Indeed, if you create the states and transitions through the user |
|
157 interface, next time you initialize the database you will have to |
|
158 re-create all the workflow entities. The user interface should only be |
|
159 a reference for you to view the states and transitions, but is not the |
|
160 appropriate interface to define your application workflow. |