Defining a workflow
TODOA 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 generator | Description |
---|---|
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.