Group workflows with an Application entity, and filter the task lists by it
Implements #3624939.
A site that has run Orchestra for a while has dozens of workflows, and nothing says which of them are one piece of work. A person with tasks across a dozen of them cannot narrow their list to the one they are on.
An application is that grouping: the orchestra_application config
entity, carrying a label, which a workflow names by machine name. A
workflow belongs to at most one, and belonging to none is the ordinary
state of a site that has defined none.
Named once rather than typed on each workflow, which is what lets it be renamed in one place, translated once, and offered back as a filter. Tenant-scoped exactly like a status: one with no tenant is offered in every tenant, one scoped to a tenant is offered only there.
What the branch does, one commit per step
- The four delete confirmations each join their own counts. Four
identical lines in four modules; a fifth form would have written the
fifth copy. Moved onto
EntityCountLabelsTrait, which exists for exactly this. - The tenant-scoped vocabulary rule is written once. "Available in
tenant T = shared terms + terms scoped to T" was the status
vocabulary's rule and is now the application vocabulary's too;
TenantScopedRepositoryBaseholds it andStatusRepositorysays only which entity type it is for. - The entity, in the engine. Plus the
applicationkey on the workflow, its config dependency, the repository, the usage counter and the deletion guard. The engine stays headless: no list, no form, no route here. - The screens, in
orchestra_ui. List, add/edit/delete, the menu item under Configuration > Workflow > Orchestra, and the Edit local task that gives the Translate tab a tab bar to appear in. - The choice, in the model editor. A select beside the tenant scope, offering the applications of the tenant being edited.
- The filter, on the task lists. Exposed on My tasks, ahead of the Workflow filter.
- The docs.
Two things worth reading
Deleting an application workflows belong to is refused. Left to
config, that deletion does not remove a name: it repairs its dependents
by saving every one of those workflows with the reference stripped,
which edits the workflows and leaves nothing recording that the grouping
existed. WorkflowApplicationTest fails against the unfixed code.
The filter names no column of its own. An instance stores the workflow it runs, not an application, so the handler resolves the chosen applications to their workflows and asks the question the instance table can answer. Resolving rather than storing means no write path to keep in step and no run answering for what its workflow said when it started; the workflow list is small, configuration and already cached.
An application with no workflow in it has to answer nothing rather than
everything - an empty set is not "no condition" - and excluding one has
to exclude nothing. ApplicationFilterTest covers both, and fails
against the version that returns early on an empty set.
Gates
phpcs (Drupal, DrupalPractice) clean bar the 9 pre-existing
orchestra_bpmn_io/js findings, phpstan level 5 clean,
check-translations.php exit 0 with French for every new string,
cspell clean on the diff.