Notifications¶
Orchestra reaches people at points you define while modeling a workflow. You decide where to notify, who to notify, and what to send; a delivery channel decides how it is sent and in which medium. This page covers the modeling side. For the channels (email, Easy Email, SMS, a custom subscriber) and the event they consume, see Notification delivery.
Notifications never ride the audit event: the audit log is a
separate concern (OrchestraAuditableEvent), so recording history and reaching
people stay apart.
From the modeler's view¶
Defining notifications is part of building the workflow, not an afterthought. At each point where the workflow should reach someone, you decide three things in the model:
- Where to notify: on a task assignment, when a branch arrives at an interactive wait (notify on arrival), or at an explicit Notify node you place on a branch.
- Who to notify: an audience, the same plugin type a task uses to say who it reaches, from named users to "the user held in this variable" to a fixed external email address.
- What to send: a notification type (a template id). The wording lives in the channel, so the model stays channel-neutral and every recipient is rendered in their own language. A node may also set named texts of its own, its Values, for a shared template to read (see One template for many notifications).
Everything below is how the where and who are expressed while modeling; the what and its rendering belong to delivery.
How a notification flows¶
flowchart LR
subgraph M["chosen while modeling"]
P1[task assigned<br/>or reassigned]
P2[branch parks on<br/>an interactive wait]
P3[Notify node<br/>on a branch]
end
P1 --> D["the point's dispatcher<br/>resolves the audience"]
P2 --> D
P3 --> D
D --> EV["OrchestraNotificationEvent<br/>type + params + recipients"]
EV --> CH{delivery channel}
CH --> MAIL[orchestra_mail]
CH --> EE[Easy Email]
CH --> SUB[custom subscriber]
CH --> SMS[SMS / chat / push]
The where is a dispatch point, the who resolves to Recipient objects,
and the what is the type a channel templates on. The model never names a
channel, so enabling or swapping one (the right half of the diagram) changes
delivery without touching the workflow. That half is
Notification delivery.
The when is a notification profile: a Notify node and an interaction's arrival notification each name one, or follow their workflow, their tenant and the site. By default a notification goes once the page has been sent, and an interaction link, the only way its recipient can act, before it and as an incident when it cannot be sent. See When a notification is sent.
Where: the notification points¶
Three points can notify. The code that resolves the audience and dispatches at a point is its dispatcher, and each one lives in the module that owns the thing being notified about, so a point needs nothing installed beyond that module:
| Provided by | Notification point | Type |
|---|---|---|
orchestra_inbox |
a task is assigned / reassigned (WorkItemEvent), times out (TaskTimedOutEvent), or is canceled (WorkItemEvent) |
task_assigned, task_reminder, task_canceled |
orchestra_interaction |
a branch parks on a bearer-interaction node with a "Notify on arrival" audience (TokenParkedEvent) |
interaction_link |
orchestra_engine (the Notify node) |
a token traverses a Notify node | the node's configured type (default generic) |
Each resolves its audience to recipients (audience plugins, shared through the
kernel AudienceResolver, so "Users by variable" resolves the original
poster) and dispatches; none knows how the message is sent.
Who gets notified¶
Audiences are Audience plugins, the same plugin type a task uses to say who it
reaches (a staffing audience additionally implements AssignmentInterface):
| Audience | Notifies by default | Resolves to |
|---|---|---|
| Users | yes | the named users |
| Users by variable | yes | the user(s) named in a process variable, by user ID, username or email address |
| Users by variable, including users who have not logged in yet | yes | as Users by variable, plus the address of a username with no account yet that will be provisioned at first login |
| Roles | no (opt in) | the members of the roles |
| Roles by variable | no (opt in) | the members of the role(s) in a variable |
| Groups | no (opt in) | the active members of the named groups (the optional orchestra_group submodule) |
| Groups by variable | no (opt in) | the active members of the group(s) whose ID is in a process variable (the optional orchestra_group submodule) |
| Email address | yes | a fixed email address, with no Drupal account |
| Email by variable | yes | the email address(es) held in a process variable |
The last two are notify-only audiences: they resolve to an account-less recipient (an external address), so they never appear in the task assignment editor and cannot staff a task; they exist for the Notify node and on-arrival notifications.
For tasks, the per-assignment Notify this audience flag decides who is
mailed, so pooling a task to a broad role never mails its members by surprise; a
reassignment always notifies the new assignee. When a task is canceled (a
threshold or quorum join, or a join timeout, fired on another branch, the
instance was
canceled or failed), the people told about it are told it is gone and why:
whoever took it from its pool or was reassigned it, otherwise the audience its
assignment mail went to, so a pool nobody mailed is not mailed then either, nor
one named user whose assignment does not notify them. A task canceled because
its holder was blocked or deleted tells nobody. Whoever is
covering for a resolved recipient is added to the
audience, and that recipient drops out only if their own delegations
unanimously ask to be left alone, the one case where an audience narrows rather
than widens. For a Notify node, every
recipient row is notified (you placed the node deliberately). The authenticated
and anonymous pseudo-roles match nobody, so "notify all authenticated users"
never mass-mails the site.
The Notify node (visible, conditional)¶
A Notify node is a workflow node, so it shows on the chart and the flow routes to it conditionally: send the "approved" branch through a Notify node to mail people only then. It renders nothing itself, it resolves its Recipients (the audience-row editor, the same widget tasks use) and dispatches the neutral event with its notification type, carrying the process context, then advances immediately. The message layout lives in the channel (a mail template, or an Easy Email template); the words that change from one node to the next can live on the node, in its Values.
Each recipient row also has a Delivery setting, To (default), Cc or
Bcc. It keeps the full audience-plugin range (users, roles, by-variable,
custom rules) and only tags which mail header the resolved people land in, so
you can say "poster in To, validator in Cc" without giving up the powerful
audience mechanism. It is a hint the channel interprets: the mail channel maps
it to headers (see Notification delivery),
and a channel with no such notion (SMS, chat) ignores it and reaches everyone.
To address the person who completed a task (a validator), set Store
completer in variable on the task node; the completer's uid is written there
on completion (and recorded as the audit actor), so a later Notify node names
them with "Users by variable". The submission-validation example closes with a
Notify node that mails both the original poster and the validator once the
request is processed.
One template for many notifications¶
Most notifications share one layout, a title, a body and a link, and differ only in their words. Rather than copying the template once per notification, give each node that sends one its own Values, and let one template read them.
A value is a machine name mapped to text, and the text may contain tokens:
| Name | Text |
|---|---|
title |
Your request [orchestra:var:reference] was approved |
body |
It will be processed within [orchestra:var:delay] days. |
link |
[orchestra:var:review_url] |
The template reads them as [orchestra:node-value:title],
[orchestra:node-value:body] and so on, and reads only the names it needs. A
name the node does not set reads as nothing, so one template serves a node with
a title and a node without. The values belong to the step, not to the instance:
nothing is written to the process variables, and a value set for one
notification is never there for the next. See Node values
for how they are rendered.
The Values section is offered on a Notify node and on every node that parks, whose task, arrival and reminder notifications are sent while the branch stands on it. With Easy Email, one theme layout can also leave out a block whose value is empty: see Easy Email templating.
Where a notification is rendered¶
A process is started in one place and advanced in another: someone submits on a public site, an operator acts from a back office, cron and queue workers run with no request at all. Anything a notification is built from would otherwise resolve against whichever request happens to be running, so links point at the wrong place and the site name, the mail settings and the theme are the wrong ones.
Orchestra therefore names two places, and they are not the same:
- The workspace, where people do the work. A task notification links to the inbox, and the inbox is the same place for everyone, whoever caused the task. A parked interaction step is work too, so its link goes there as well.
- The origin, where the process was started. A Notify node is author-composed content, normally addressed to whoever started the run, so it is rendered where they were. Each instance records its origin when it starts, since by the time the message goes out the run may be advancing anywhere; a subprocess belongs where its parent does. A run started with nothing to record, by cron or the command line, falls back to where its tenant receives people.
Orchestra itself knows nothing about sites or domains. The origin is an opaque
string and the shipped implementation of orchestra_engine.notification_context
changes nothing at all, so a site with a single front end pays nothing for this.
Install Orchestra Domain on a
Domain Access site to give both places meaning. Each tenant says which domains
it lives on, on its own Domains form beside its retention settings: its
primary domain, any secondary domains it is also reached through, and a
workspace domain where its operators work.
Which domain a run belongs to is the run's own origin, recorded when it started, so a tenant reached through several sites needs no rule about them. Only a run that recorded no origin needs one, and the primary domain is the answer: a run started by cron or the command line is rendered there, which is why it is the one the form requires. A tenant naming neither a primary nor a workspace inherits the default tenant's, which is how tenants sharing a front door or a back office name it once between them. That inheritance is for rendering only: which requests reach a tenant is never inherited, or two tenants would answer for one domain. Deleting a domain clears it from every tenant that named it, whichever of the three it was named as, so a tenant is never left pointing at a machine name a later domain could take over.
Outbound only
This is for output built outside the request it belongs to. Never wrap the rendering of a page being served: on a Domain site the negotiated domain also drives routing and language negotiation for that page.