Customize
Record workflows
Create workflow

Defining a workflow

TODO

A workflow is defined inside the invenio.cfg file

The overall definition is as follows:

# invenio.cfg
from invenio_i18n import lazy_gettext as _
from shared.workflows import WorkflowNamePermissions, WorkflowNameRequests
 
from oarepo_workflows import Workflow
 
# if using communities (default in most repositories).
COMMUNITIES_ROLES = [
    dict(name="curator", title=_("Curator"), description=_("Curator of the community")),
    # ... other community roles that are used inside any of the workflows are here
]
 
WORKFLOWS={
  'workflow-name': Workflow(
    label = _('Workflow label'),
    permission_policy_cls = WorkflowNamePermissions,
    request_policy_cls = WorkflowNameRequests,
  )
}

The WorkflowNamePermissions and WorkflowNameRequests are classes that define the permissions and requests for the workflow. These classes can be either defined inside the invenio.cfg file or in a shared file, such as shown above.

Permissions

The permission class looks like this:

# shared code or invenio.cfg
from oarepo_workflows import WorkflowPermissionPolicy
 
class WorkflowNamePermissions(WorkflowPermissionPolicy):
 
    # can_create can not use InState as no record exists yet
    can_create = [
        CommunityMembers(),
        SystemProcess(),
    ]
 
    # it is safe to specify AnyUser here, as inside invenio code the can_read
    # is checked as well
    can_search = [AnyUser(), SystemProcess()]
 
    can_read = [
        RecordOwners(),
        CommunityRole("curator"),
        IfInState("approving", CommunityRole("approver")),
        IfInState("approved", CommunityRole("approver"), CommunityRole("publisher")),
        IfInState("published",
            IfRestricted(
                "visibility",
                then_=[CommunityMembers()],
                else_=[AnyUser()],
            )
        ),
        SystemProcess()
    ]
 
    can_update = [
        IfInState("draft", RecordOwners(), CommunityRole("curator")),
        SystemProcess()
    ]
 
    can_delete = [
        IfInState("draft", RecordOwners(), CommunityRole("curator")),
        # published record can not be deleted directly by anyone else than system
        SystemProcess()
    ]

TODO: list possible permission generators that can be included in the policy

Requests

A request configuration consists of:

  • permission generator - who can request the action (the creator of the request)
  • permission generator - who can approve the action (the recipient of the request)
  • state transitions:
    • submitted: the state to move the record when the request is submitted
    • accepted: the state to move the record when the request is approved
    • declined: the state to move the record when the request is rejected

States can be omitted if the record should not change the state when the request is submitted/accepted/declined. An example of such a request is a request to access embargoed files - the state of the record will not change, but the approval of the request will allow the user to access the files.

If the recipient of the request is not defined or evaluates to an empty set, the request will be automatically approved.

Note: in the current invenio implementation, only the first recipient will be used.

# shared code or invenio.cfg
from oarepo_workflows import (
    WorkflowRequestPolicy,
    IfInState,
    WorkflowRequest,
    WorkflowTransitions,
    AutoApprove,
    WorkflowRequestEscalation,
)
from oarepo_runtime.services.permissions import UserWithRole, RecordOwners
from oarepo_requests.services.permissions import IfRequestedBy
from oarepo_communities.services.permissions import (
    CommunityRole,
    DefaultCommunityRole,
    CommunityMembers,
    DefaultCommunityMembers
)
 
 
class WorkflowNameRequests(WorkflowRequestPolicy):
    """Specific requests here"""
    delete_request = WorkflowRequest(
        requesters = [
            IfInState("published", [RecordOwners(), CommunityRole("curator")]),
        ],
        recipients = [
            IfRequestedBy(
                CommunityRole("curator"),
                then_=[AutoApprove()],
                else_=[CommunityRole("curator")],
            )
        ],
        transitions = WorkflowTransitions(
            submitted = 'deleting',
            accepted = 'deleted',
            declined = 'draft'
        )
        escalations = [
            WorklfowEscalation(
                after=timedelta(days=14),
                recipients=[UserWithRole("administrator")]
            )
        ]
    )

Requesters

The requesters are the users who can create the request. The requesters can be defined as a list of permission generators. The request will be created if the set of needs created by the permission generators has non-empty intersection with the set of needs provided by the user.

The permission generators will be evaluated with context containing:

  • request_type
  • record

Recipients

The recipients are the users who can approve the request. The recipients can be defined as a list of permission generators that must support entity_reference method. In the current implementation only the first recipient will be used and warning will be logged if more than one recipient is defined.

The permission generators will be evaluated with context containing:

  • request_type
  • record

Currently supported permission generators that can be used in recipients section are:

Permission generatorDescription
CommunityMembers()all members of the community
CommunityRole(role_name: str)the user must have the role in the community
RecordOwners()the user must be the owner of the record
IfInState(state_name: str, *permission_generators)If the record is in given state, use the specified permission generators
IfRequestedBy(generator, if_true, if_false)If the request is created by the user with the specified permission, use the specified permission generators
SystemProcess()the system process can approve the request and no one else
Administrator()repository administrator

All these permissions generators apart from IfRequestedBy should be imported from oarepo_workflows module. Please do not use generic invenio permission generators as they are not supported.