The Workflow system introduced in Wagtail 2.9 allows users to create tasks, which represent stages of moderation.
Wagtail provides one built in task type: ``GroupApprovalTask``, which allows any user in specific groups to approve or reject moderation.
However, it is possible to add your task types in code. Instances of your custom task can then be created in the ``Tasks`` section of the Wagtail Admin.
Task models
~~~~~~~~~~~
All custom tasks must be models inheriting from ``wagtailcore.Task``. In this set of examples, we'll set up a task which can be approved by only one specific user.
..code-block:: python
# <project>/models.py
from wagtail.core.models import Task
class UserApprovalTask(Task):
pass
Subclassed Tasks follow the same approach as Pages: they are concrete models, with the specific subclass instance accessible by calling ``Task.specific()``.
You can now add any custom fields. To make these editable in the admin, they must also be added as panels.
For example:
..code-block:: python
# <project>/models.py
from django.conf import settings
from django.db import models
from wagtail.admin.edit_handlers import FieldPanel
from wagtail.core.models import Task
class UserApprovalTask(Task):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=False)
panels = Task.panels + [FieldPanel('user')]
Any fields that shouldn't be edited after task creation - for example, anything that would fundamentally change the meaning of the task in any history logs -
can be added to ``exlude_on_edit``. For example:
..code-block:: python
# <project>/models.py
from django.conf import settings
from django.db import models
from wagtail.admin.edit_handlers import FieldPanel
from wagtail.core.models import Task
class UserApprovalTask(Task):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=False)
panels = Task.panels + [FieldPanel('user')]
# prevent editing of ``user`` after the task is created
exclude_on_edit = {'user'}
Custom TaskState models
~~~~~~~~~~~~~~~~~~~~~~~
You might also need to store custom state information for the task. Normally, this is done on an instance of ``TaskState``, which is created when a page starts
the task. However, this can also be subclassed equivalently to ``Task``:
..code-block:: python
# <project>/models.py
from wagtail.core.models import TaskState
class UserApprovalTaskState(TaskState):
pass
Your custom task must then be instructed to generate an instance of your custom task state on start instead of a plain ``TaskState`` instance:
..code-block:: python
# <project>/models.py
from django.conf import settings
from django.db import models
from wagtail.admin.edit_handlers import FieldPanel
from wagtail.core.models import Task, TaskState
class UserApprovalTaskState(TaskState):
pass
class UserApprovalTask(Task):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=False)
panels = Task.panels + [FieldPanel('user')]
# prevent editing of ``user`` after the task is created
exclude_on_edit = {'user'}
Customising behaviour
~~~~~~~~~~~~~~~~~~~~~
Both ``Task`` and ``TaskState`` have a number of methods which can be overridden to implement custom behaviour. Here are some of the most useful:
This performs the actions specified in ``Task.get_actions(page, user)``: it is passed an action name, eg ``approve``, and the relevant task state. By default,
it calls ``approve`` and ``reject`` methods on the task state when the corresponding action names are passed through.
For example, let's say we wanted to add an additional option: cancelling the entire workflow:
This returns a QuerySet of ``TaskStates`` (or subclasses) the given user can moderate - this is currently used to select pages to display on the user's dashboard.