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

  1. 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.
  2. 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; TenantScopedRepositoryBase holds it and StatusRepository says only which entity type it is for.
  3. The entity, in the engine. Plus the application key 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.
  4. 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.
  5. The choice, in the model editor. A select beside the tenant scope, offering the applications of the tenant being edited.
  6. The filter, on the task lists. Exposed on My tasks, ahead of the Workflow filter.
  7. 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.

Merge request reports

Loading
Loading