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