Task System

The task system lets editors assign work items (“tasks”) to other users or groups. A task is a piece of content stored in a per-user task container. Each user gets their own container, automatically created when their member area is set up. Tasks carry an initiator, one or more responsibles, an action, a due date, optional rich text, and an optional relation to a related content object.

The most common case – asking a colleague to review a specific page – is exposed through the dedicated @task-review REST endpoint, which is a thin shortcut around creating a task.

Overview

The system is built from two content types and a three-state workflow:

  • TaskContainer – a singleton folder living inside each user’s home folder (id tasks). It is created automatically when a member area is created and on first login. It is excluded from navigation and cannot be moved or copied.

  • Task – the actual work item, created inside a TaskContainer. Its title is derived from the action and the title of the related content (for example review - My Page).

A task always has:

  • an initiator (defaults to the current user),

  • one or more responsibles (users and/or groups),

  • an action (chosen from a configurable vocabulary),

  • a due date (defaults to 14 days from creation),

  • optional rich text,

  • an optional related content relation.

When a task is created or modified, the responsible principals are granted the Contributor role locally on the task, so they can see and work on it.

Actions

The set of available actions is configured in the registry record wcs.backend.tasksystem.actions. It is a list of Title:value strings. The shipped default is:

Information:info
Review:review
Aufgabe:task

The part before the colon is the human-readable title shown in the form; the part after the colon is the stored value. The first entry is used as the default action for a newly created task. To add or rename actions, edit this registry record.

Task Review Workflow

Tasks use the task_workflow. It has three states and is German-labelled:

State (review_state)

Title

Meaning

task_workflow--STATUS--offen

Offen

Open, not started yet

task_workflow--STATUS--in-arbeit

In Arbeit

In progress

task_workflow--STATUS--erledigt

Erledigt

Done

New tasks start in Offen. The available transitions are:

  • Offen → In Arbeit (mit arbeit beginnen)

  • Offen → Erledigt (erledigen)

  • In Arbeit → Erledigt (erledigen)

  • In Arbeit → Offen (wieder eröffnen)

  • Erledigt → Offen (wieder eröffnen)

        ┌──────────────────────────────┐
        │                              │
        ▼          mit arbeit          │ wieder
     ┌──────┐      beginnen        ┌────────┐  eröffnen
     │ Offen│ ───────────────────▶ │In Arbeit│
     └──────┘                      └────────┘
        │   \                          │
        │    \  erledigen              │ erledigen
        │     \                        ▼
        │      \                  ┌─────────┐
        │       └────────────────▶│ Erledigt│
        │            wieder       └─────────┘
        └────────────────────────────┘
                  eröffnen

All transitions are user-triggered and guarded by the Contributor, Manager, and Owner roles – the responsibles of a task (who receive Contributor locally) can therefore move it through the workflow.

Due Date Indexing

The due_date field is exposed to the catalog through a due_date indexer, so tasks can be searched and sorted by their deadline.

REST API

POST @task-review

Creates a review task for the current context. This is a convenience wrapper around posting a new Task into the current user’s task container. The endpoint is available on any folderish content.

It takes the responsible(s) from the request body and fills in the rest automatically:

  • initiator is set to the current user,

  • related is set to the context the endpoint was called on,

  • @type is set to Task,

  • action is set to review.

The responsible property is required; omitting it returns a 400 Bad Request. Any additional task fields (for example text or due_date) may be passed in the body and are forwarded to the created task.

The created task is stored in the calling user’s task container, with the context page as its related content.

POST /Plone/topics/my-page/@task-review HTTP/1.1
Host: localhost:8080
Accept: application/json
Content-Type: application/json

{
    "responsible": ["user:editor1"],
    "text": "<p>Please review the updated figures.</p>"
}

The response is the standard serialization of the newly created Task object.

Consuming @task-review

JavaScript:

async function requestReview(pageUrl, responsibles, message) {
    const response = await fetch(`${pageUrl}/@task-review`, {
        method: 'POST',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json'
        },
        credentials: 'include',
        body: JSON.stringify({
            responsible: responsibles,
            text: message
        })
    });
    if (!response.ok) {
        throw new Error(`Review request failed: ${response.status}`);
    }
    return response.json();
}

// Usage
const task = await requestReview(
    'http://localhost:8080/Plone/topics/my-page',
    ['user:editor1', 'group:editors'],
    '<p>Please review.</p>'
);
console.log(task['@id']);

Python:

import requests

response = requests.post(
    'http://localhost:8080/Plone/topics/my-page/@task-review',
    auth=('initiator', 'password'),
    headers={
        'Accept': 'application/json',
        'Content-Type': 'application/json',
    },
    json={
        'responsible': ['user:editor1'],
        'text': '<p>Please review.</p>',
    },
)
response.raise_for_status()
print(response.json()['@id'])

The responsible values use the standard Plone principal token format: user:<userid> for a single user and group:<groupid> for a group.

Working with Tasks Directly

Tasks are normal content objects, so they can be listed, read, and transitioned through the regular plone.restapi endpoints. List the tasks in a user’s container with a GET on the container, read a single task with a GET on it, and move it through the workflow with the standard @workflow transition endpoints using the transition ids from the table above.

POST /Plone/Members/editor1/tasks/my-task/@workflow/task_workflow--TRANSITION--erledigen--offen_erledigt HTTP/1.1
Host: localhost:8080
Accept: application/json