1 .. -*- coding: utf-8 -*- |
|
2 |
|
3 .. _Workflow: |
|
4 |
|
5 An Example: Workflow definition |
|
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 list 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` in ten minutes (see :ref:`BlogTenMinutes`). |
|
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 your application. 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, |
|
33 or by defining it in ``migration/postcreate.py``. |
|
34 This script is executed each time a new ``cubicweb-ctl db-init`` is done. |
|
35 We strongly recommand to create the workflow in ``migration/postcreate.py`` |
|
36 and we will now show you how. Read `Under the hood`_ to understand why. |
|
37 |
|
38 Update the schema |
|
39 ~~~~~~~~~~~~~~~~~ |
|
40 If we want a State for our BlogEntry, we have to define a relation |
|
41 ``in_state`` in the schema of BlogEntry. So we add |
|
42 the line ``in_state (...)``:: |
|
43 |
|
44 class BlogEntry(EntityType): |
|
45 title = String(maxsize=100, required=True) |
|
46 publish_date = Date(default='TODAY') |
|
47 text_format = String(meta=True, internationalizable=True, maxsize=50, |
|
48 default='text/rest', constraints=[format_constraint]) |
|
49 text = String(fulltextindexed=True) |
|
50 category = String(vocabulary=('important','business')) |
|
51 entry_of = SubjectRelation('Blog', cardinality='?*') |
|
52 in_state = SubjectRelation('State', cardinality='1*') |
|
53 |
|
54 As you updated the schema, you have to re-execute ``cubicweb-ctl db-init`` |
|
55 to initialize the database and migrate your existing entities. |
|
56 |
|
57 [WRITE ABOUT MIGRATION] |
|
58 |
|
59 Create states, transitions and group permissions |
|
60 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
61 |
|
62 The ``postcreate.py`` script is executed in a special environment, adding |
|
63 several `CubicWeb` primitives that can be used. |
|
64 They are all defined in the ``class ServerMigrationHelper``. |
|
65 We will only discuss the methods we use to create a workflow in this example. |
|
66 |
|
67 To define our workflow for BlogDemo, please add the following lines |
|
68 to ``migration/postcreate.py``:: |
|
69 |
|
70 _ = unicode |
|
71 |
|
72 moderators = add_entity('EGroup', name=u"moderators") |
|
73 |
|
74 This adds the `moderators` user group. |
|
75 |
|
76 :: |
|
77 |
|
78 submitted = add_state(_('submitted'), 'BlogEntry', initial=True) |
|
79 published = add_state(_('published'), 'BlogEntry') |
|
80 |
|
81 ``add_state`` expects as first argument the name of the state you want |
|
82 to create, then the entity type on which the state can be applied, |
|
83 and an optional argument to say if it is supposed to be the initial state |
|
84 of the entity type. |
|
85 |
|
86 :: |
|
87 |
|
88 add_transition(_('approve_blogentry'), 'BlogEntry', (submitted,), published, ('moderators', 'managers'),) |
|
89 |
|
90 |
|
91 ``add_transition`` expects |
|
92 |
|
93 * as the first argument the name of the |
|
94 transition, then the entity type on which the transition can be applied, |
|
95 * then the list of states on which the transition can be trigged, |
|
96 * the target state of the transition, |
|
97 * and the permissions |
|
98 (e.g. a list of user groups who can apply the transition; the user |
|
99 has to belong to at least one of the listed group to perform the action). |
|
100 |
|
101 :: |
|
102 |
|
103 checkpoint() |
|
104 |
|
105 .. note:: |
|
106 Do not forget to add the `_()` in front of all states and transitions names while creating |
|
107 a workflow so that they will be identified by the i18n catalog scripts. |
|
108 |
|
109 In addition to the user group condition, we could have added a RQL condition. |
|
110 In this case, the user can only perform the action if |
|
111 the two conditions are satisfied. |
|
112 |
|
113 If we use a RQL condition on a transition, we can use the following |
|
114 variables: |
|
115 |
|
116 * `%(eid)s`, object's eid |
|
117 * `%(ueid)s`, user executing the query eid |
|
118 * `%(seid)s`, the object's current state eid |
|
119 |
|
120 |
|
121 .. image:: images/lax-book.03-transitions-view.en.png |
|
122 |
|
123 You can notice that in the action box of a BlogEntry, the state |
|
124 is now listed as well as the possible transitions defined by the workflow. |
|
125 The transitions will only be displayed for users having the right permissions. |
|
126 In our example, the transition `approve_blogentry` will only be displayed |
|
127 for the users belonging to the group `moderators` or `managers`. |
|
128 |
|
129 |
|
130 Under the hood |
|
131 ~~~~~~~~~~~~~~ |
|
132 |
|
133 A workflow is a collection of entities of type ``State`` and of type ``Transition`` |
|
134 which are standard `CubicWeb` entity types. |
|
135 For instance, the following lines:: |
|
136 |
|
137 submitted = add_state(_('submitted'), 'BlogEntry', initial=True) |
|
138 published = add_state(_('published'), 'BlogEntry') |
|
139 |
|
140 will create two entities of type ``State``, one with name 'submitted', and the other |
|
141 with name 'published'. Whereas:: |
|
142 |
|
143 add_transition(_('approve_blogentry'), 'BlogEntry', (submitted,), published, ('moderators', 'managers'),) |
|
144 |
|
145 will create an entity of type ``Transition`` with name 'approve_blogentry' which will |
|
146 be linked to the ``State`` entities created before. |
|
147 As a consequence, we could use the administration interface to do these operations. |
|
148 But it is not recommanded because it will be uselessly complicated |
|
149 and will be only local to your instance. |
|
150 |
|
151 |
|
152 Indeed, if you create the states and transitions through the user interface, |
|
153 next time you initialize the database |
|
154 you will have to re-create all the entities. |
|
155 The user interface should only be a reference for you to view the states |
|
156 and transitions, but is not the appropriate interface to define your |
|
157 application workflow. |
|
158 |
|
159 |
|