Tokens¶
Orchestra registers an orchestra
token type so any text rendered
in the context of a running process can carry tokens that resolve against it.
The first consumer is the message interaction body, where an
author can greet a link holder by name, echo a reference number, or reflect a
value the process carries. The same tokens are available to anything else that
renders process text, such as notifications or audit messages.
The tokens are a capability of the engine, so they live in orchestra_engine
and resolve wherever it is installed. They go through Drupal's standard token
service, so global tokens ([site:name], [current-date:medium], and so on)
work in the same text alongside the orchestra ones.
Available tokens¶
Following Drupal's convention ([node:type] is the machine name,
[node:type-name] the human one), the bare token is the machine value and the
-label variant is the human-readable one.
| Token | Resolves to |
|---|---|
[orchestra:var:NAME] |
A process variable visible to the current branch, by name |
[orchestra:instance-id] |
The process instance id |
[orchestra:label] |
What the lists call the instance: its label, else its workflow and id |
[orchestra:workflow] |
The workflow machine name |
[orchestra:workflow-label] |
The workflow label |
[orchestra:state] |
The engine state machine name (running, completed, canceled, failed) |
[orchestra:state-label] |
The human-readable engine state (Running, Completed, …) |
[orchestra:status] |
The workflow-authored status machine name the current node references, when set |
[orchestra:status-label] |
The workflow-authored status as its human-readable term label, when set |
[orchestra:correlation-key] |
The business reference key correlating the run, when set |
[orchestra:created] |
The date the instance started |
[orchestra:token-id] |
The current branch token id |
[orchestra:node] |
The machine name of the node the current branch sits on |
[orchestra:node-label] |
The label of that node |
[orchestra:node-value:NAME] |
A named text set in that node's Values (see Node values) |
[orchestra:deadline] |
The timer deadline on the current branch, when one is set |
Variables and branch scoping¶
[orchestra:var:NAME] resolves a process variable as the current
branch sees it. The engine layers a token's lineage over the instance-wide
variables, so a branch-local value shadows the instance-wide one, and a name set
only instance-wide resolves the same on every branch. This is the same view the
flow conditions and the dispatcher use.
Internal bookkeeping variables (those with a double-underscore prefix) are not part of that view, so they never resolve as tokens.
Nested values¶
When a variable holds a structured value (an array), a dot-path reaches into
it: [orchestra:var:validation.comment] resolves the comment key of the
validation variable, and deeper paths (a.b.c) work the same way. A variable
of exactly the dotted name is read first, the way flow conditions read it, so
[orchestra:var:submission.city] reads a webform answer stored on its own as
well as one inside a submission map. A path that does not resolve to a scalar
leaves the token to clear.
Chaining into a referenced entity¶
When a variable holds an entity id, name the target entity type and chain that
type's own tokens onto it:
[orchestra:var:NAME:ENTITY_TYPE:CHAIN]. For example
[orchestra:var:validator_uid:user:display-name] loads the user whose id is in
validator_uid and renders its [user:display-name], and
[orchestra:var:node_id:node:title] renders a referenced node's title. The
entity type is explicit in the token, never guessed; an id that does not load or
a value that is not a scalar leaves the token to clear.
Like core's own entity tokens, chaining does not apply an entity or field view-access check: it resolves the field regardless of who the rendered text is later shown to (that is the point, for example when a message shown to the poster names the validator). Because the chain type and property are fixed by the workflow author (who holds administer orchestra), only a field the author chose is ever surfaced. Still, treat it as a disclosure: only chain fields that are safe to show to everyone the message can reach. Do not chain a sensitive field (a private profile field, an unpublished entity's data) into a message whose audience should not see it.
The identifier is not the author's, though: it is whatever the variable holds by the time the token renders, and a variable can be written by a webform submission, an interaction payload or an API signal. So chaining off a variable an untrusted party fills lets whoever filled it name any entity of that type and read the chosen field of it back out of the message. Where the id can come from outside the workflow, chain only a field that is safe for that party to read, or write the id into a variable of its own from a step that checks it first. The same applies to the entity-target action, whose help text carries the same caution for the same reason.
Node values¶
A node that sends notifications (a Notify node, and every node that parks, whose
task, arrival and reminder notifications go out while the branch stands on it)
has a Values section: named texts, each a machine name mapped to text the
author writes. [orchestra:node-value:NAME] reads one from the node the current
branch stands on, so a single template serves every node and each node says its
own words: a template whose subject is [orchestra:node-value:title] reads
"Request [orchestra:var:reference] approved" on one node and "Request
[orchestra:var:reference] rejected" on another.
The tokens inside a value are replaced too, with the same data as the text that
reads it: the instance, the branch, [site:*], and [user:*] when the message
goes to one person. What a process variable brings into a value stays data, as
everywhere else: token syntax a link holder typed into a variable is shown,
never evaluated. A value does not read another value, so two values cannot read
each other in a loop.
A name the node does not set reads as nothing, even in a renderer that keeps
the tokens it cannot answer. That is what lets one template serve nodes that set
a title and nodes that set none. The value is read when the text is rendered,
from the workflow version the instance is pinned to, so a
reminder sent days later reads the variables as they are then. A reminder's
timer stands on its task's node, so it reads the task's values.
The instance-scoped tokens (workflow, state, status, correlation key, created) resolve from the instance alone. The branch-scoped tokens (the current node, its values, the deadline) resolve only when a branch is in context, since a "current node" is a property of a branch, not the whole instance. The node's label is read from the workflow version the instance is pinned to, so it matches the run even after the live workflow is edited.
[orchestra:state] and [orchestra:status] are distinct. The state is the
engine lifecycle (running, completed, canceled, failed), set by the
engine. The status is the workflow-authored value the current node
references, a tenant-scoped vocabulary term such as "Under review" or "Approved"
that speaks to the business, and is empty when the workflow authors none.
Date formats¶
The date tokens accept any Drupal date format by chaining it on, exactly as core date tokens do:
[orchestra:created:medium][orchestra:created:long][orchestra:created:custom:Y-m-d][orchestra:deadline:short]
The bare [orchestra:created] uses the medium format.
Discovering tokens¶
When the contrib Token module is
installed, the message interaction form shows a token browser listing the
orchestra group and every global token, so an author can insert one without
memorizing the names. The replacement itself does not need that module: it uses
the core token service, which is always present.
Safety¶
A variable value is inserted as data, not markup: token replacement escapes each value, so a value an author cannot control (a variable the link holder supplied, say) cannot inject markup into the page. In the message interaction the result is then rendered through the restricted HTML format, falling back to admin-HTML filtering when that format is not installed, so the author's own formatting is kept while unsafe markup is stripped.
Using the tokens in your own code¶
To render text with these tokens from a module, pass the instance under the
orchestra data key and, for the branch-scoped tokens, the branch token under
orchestra_token:
$text = \Drupal::token()->replace($authored_text, [
'orchestra' => $instance,
'orchestra_token' => $token,
], ['clear' => TRUE]);
Pass a \Drupal\Core\Render\BubbleableMetadata as the fourth argument and
apply it to your render array so the cache metadata the tokens carry is not
lost: a dependency on the instance, its workflow and the branch, plus whatever
the text reads beyond them - the process variables, the status terms a status
label comes from - so a cached render is rebuilt when the value it showed
changes. Omit orchestra_token when there is no branch in context; the
branch-scoped tokens then resolve to nothing and the instance-scoped ones still
work.