Action tasks¶
The optional Orchestra Action submodule (orchestra_action) adds an
action task: an automated step whose work is a Drupal Action plugin. It is
Orchestra's answer to Maestro's "batch function" task (a step that runs a
named, automated operation rather than waiting for a human), built on Drupal's
own action system instead of a bare function name.
Why an Action plugin, not a callable string¶
Maestro's batch task stores a PHP function name in configuration and calls it. Orchestra deliberately does not offer an arbitrary-callable field: a workflow is config (exportable, importable, shippable as a recipe), so a literal callable would turn "edit a workflow" into "run arbitrary PHP." A Drupal Action plugin is the safe, idiomatic equivalent: discoverable, configurable and access-aware, with its body in a plugin class. For logic that is purely about process flow, a custom task type remains the cleaner path, since it receives the token and instance directly.
The action task¶
A node of type action runs the configured action and then advances, with no
parking and no inbox. Three kinds of action fit:
- an entity action (one that names an entity type, like Publish or Delete) runs on a resolved target entity;
- a system action (one with no entity type, like Send email) runs as-is;
- a context-aware action (see below) is additionally handed the running process, so it can read and write process variables.
flowchart LR
s([Start]) --> a["Notify owner<br/>type: action<br/>action: Send email"]
a --> g{outcome?}
g -->|success| d([Done])
g -->|failure| r["Escalate<br/>type: action"]
Node config (in the Complete modeler or in YAML):
n_notify:
type: action
config:
action_id: action_send_email_action
action_settings:
recipient: '[node:author:mail]'
subject: 'Your article is in review'
message: 'A reviewer will look at it shortly.'
payload_variable: notified
completion_scope: instance
action_id: the action plugin to run.action_settings: the action's own configuration, when it has any, typed by the action's own schema (action.configuration.<action id>), so Config Translation offers its texts, such as an email's subject and message. The node editor writes it for you; see Configuring the action.payload_variable/completion_scope: see Success and failure.target_variable: see Choosing the entity.
Choosing the entity¶
An entity action needs something to act on, and the action task takes it from
the process variable named by target_variable. The entity type is taken from
the action, so the variable chooses only which entity of that type. The action
then runs on whichever one it names, with full privilege and no further
check, because an automated step has no actor to check against: point this
only at a variable the process itself computes, never at one an untrusted party
can set, a webform field or an interaction payload, or they choose the entity a
privileged action runs on.
To act on the content the run is attached to, use the content_action
task instead, which orchestra_content_action ships. It is the same step with
the run's attachment as its source, so nothing outside the model chooses what it
acts on; an attachment_key picks which attached entity when the run carries
several, and the empty key is the primary subject.
A system action takes no entity and ignores both. If an entity action cannot resolve one, the step fails (see below).
Success and failure¶
The step is automated, so it runs and the token advances. With a
payload_variable configured, the task catches a failing action and sets the
variable to failure (and to success otherwise), so an outgoing flow
condition can branch to an error path, the equivalent of Maestro's task
completion status. Without a payload variable, a failure propagates and fails
the process (the engine's default), so a misconfiguration is never swallowed.
Configuring the action¶
When the chosen action has its own settings (a configurable action such as Send email), its settings form is embedded in the node editor and follows the action selected right now: pick a different action and its own settings replace the previous one's, which start from that action's defaults. A non-configurable action (Publish, Unpublish, Delete) needs none.
BPMN.io
In the diagram editor a node's properties are plain text fields, so the
action's settings show as the raw JSON string and are best edited in the
accessible form modeler (orchestra_cm), which embeds
the action's own form; action_id and the simple keys work in either.
Context-aware actions¶
A plain Action plugin's execute() receives only the entity, so it cannot reach
the process. An action that needs the process (to read or write a variable, or
to know which instance it runs in) implements
Drupal\orchestra_engine\OrchestraContextAwareInterface:
public function setProcessContext(ProcessInstanceInterface $instance, TokenInterface $token): void;
The action task injects the instance and token before running such an action,
giving it the same reach a Maestro batch function has from its process and queue
IDs. This interface lives in the kernel, so it is the general seam for any
foreign plugin Orchestra later runs as automated work; an action that needs no
context simply does not implement it.
Acting on something else¶
The two tasks differ in one thing only: where the entity comes from. Everything
else, from instantiating the action to the success and failure contract, lives
in ActionTaskBase (orchestra_action), which is @api and exists to be
extended. orchestra_content_action is what that looks like in practice.
A module with its own idea of what a step should act on ships a task type of its own, extending the base and answering two questions:
final class MyActionTask extends ActionTaskBase {
protected function buildTargetElements(FormStateInterface $form_state): array {
// The settings that say what to act on, merged in after the action select.
}
protected function resolveEntity(string $entity_type_id, ProcessInstanceInterface $instance, TokenInterface $token): ?EntityInterface {
// The entity of that type, or NULL: the step then reports that it needed
// one and could not resolve it.
}
}
Add the settings the target needs to defaultConfiguration() and they are
carried through the form, the modeler round-trip and storage with the rest; the
base folds every key it declares back on submit.
Relationship to the moderation task¶
The moderation_transition task is a
special case of an action task: setting an entity's moderation state is one
specific automated operation. The action task is the generic form: any
action, on the entity a variable names, with success/failure routing; its
content_action sibling is the same step on the run's own attachment.