Easy Email templating¶
With Orchestra Easy Email (orchestra_easy_email) every notification is an
Easy Email email. This page shows
how to word and lay out those emails with as few templates as possible: one
layout for the whole site, one Easy Email template for most notifications, and
the words that change from step to step set on the step itself.
For how a notification is triggered and who receives it, see Notifications; for the channels and when an email leaves, see Notification delivery.
Three layers¶
An Orchestra email is put together from three places, each owning one thing:
| Layer | Where it lives | What it owns |
|---|---|---|
| Layout | a Twig file in the theme (easy-email-body-html.html.twig) |
the look shared by every email: header, footer, colors, and the zones an Orchestra email fills in |
| Template | an Easy Email template (configuration) | the subject, and the body of one kind of email |
| Values | the Values of a workflow node | the words of one step: its title, its text, its link |
The layout wraps the template's body ({{ body }}), and both read the node's
values: the template through tokens ([orchestra:node-value:title]), the layout
through a Twig variable ({{ node_values.title }}). Put each thing in the
lowest layer that owns it and nothing is written twice.
flowchart LR
N[Notify or task node<br/>Values: title, body, link] -->|tokens| T[Easy Email template<br/>subject + body]
N -->|node_values| L[Theme layout<br/>easy-email-body-html.html.twig]
T -->|"{{ body }}"| L
L --> M[Email]
Which template an email uses¶
Every notification Orchestra sends carries a type: a machine name (lowercase letters, digits and underscores) that says what kind of message it is. The type is not the template; it is what the template is looked up by.
Where the type comes from¶
| The email is sent by | Its type | Where the type is set |
|---|---|---|
| a Notify node | the node's Notification type, generic unless changed |
the Notify node's own settings in the modeler, beside its Recipients |
| Notify on arrival, on a node that parks | the feature's Notification type, interaction_link when left empty |
the node's Notify on arrival section in the modeler |
| a task being assigned or reassigned | task_assigned |
fixed by Orchestra Inbox, not configurable |
| an escalation timer reminding a task | task_reminder |
fixed by Orchestra Inbox, not configurable |
| a task being canceled | task_canceled |
fixed by Orchestra Inbox, not configurable |
| your own code | whatever it passes | the $type argument of OrchestraNotification::notify() |
So the types you choose are those of your Notify nodes and arrival
notifications. Giving two Notify nodes the same type sends them through the same
template; giving one a type of its own, request_rejected say, lets it have a
template of its own, orchestra_request_rejected. With node values, most nodes
can keep generic and still say different things.
How the type finds a template¶
The channel turns the type into a list of template ids, from the most specific to the most general, and sends the email with the first one that exists in Structure > Email templates. The ids combine the type with the instance's tenant (its tenant's machine name, when the instance belongs to one), its workflow (the workflow's machine name) and the workflow's application (its machine name, when the workflow belongs to one):
orchestra_<tenant>_<workflow>_<type>orchestra_<tenant>_<application>_<type>orchestra_<tenant>_<type>orchestra_<workflow>_<type>orchestra_<application>_<type>orchestra_<type>orchestra_generic, the catch-all
Tenant, application and workflow ids share one name space: give them distinct machine names, or a template named after one also reaches the other.
A level whose part is missing (no tenant, no application) is skipped. For
example, an instance of the leave_request workflow, in the hr application
and the city_hall tenant, reaching a Notify node whose type is
request_rejected tries:
orchestra_city_hall_leave_request_request_rejectedorchestra_city_hall_hr_request_rejectedorchestra_city_hall_request_rejectedorchestra_leave_request_request_rejectedorchestra_hr_request_rejectedorchestra_request_rejectedorchestra_generic
So a template can be made for one workflow, for every workflow of one application, for one tenant, for one type anywhere, or for everything, just by the id it is given, and the most specific one wins.
A process variable named orchestra_notification_template_suffix adds a variant
at every level: with the suffix urgent, orchestra_request_rejected_urgent is
tried before orchestra_request_rejected, and so on for each of the ids above.
The variable is read as it stands when the step sends the notification (a
notification sent later, from cron, keeps the value of that moment), so a step
that sets it switches the templates of every notification after it.
When none of the ids exists, not even orchestra_generic, the email is not sent
and the Orchestra log names the ids that were tried.
The shipped templates¶
| Template | Used by |
|---|---|
orchestra_generic |
every type that has no template of its own, Notify nodes left at generic included |
orchestra_task_assigned |
task_assigned |
orchestra_task_reminder |
task_reminder |
orchestra_task_canceled |
task_canceled |
orchestra_interaction_link |
interaction_link, the arrival notifications left at the default type |
The fewer types you use, the fewer templates you keep: a Notify node left at
generic needs no template of its own, its words come from its values.
Tokens¶
An Easy Email template, and the text of a node value, can use:
| Token | What it reads |
|---|---|
[orchestra:*] |
the instance and the step: label, workflow-label, node-label, status-label, correlation-key, created, … (see Tokens) |
[orchestra:var:NAME] |
a process variable, with dot paths and entity chains ([orchestra:var:validator_uid:user:display-name]) |
[orchestra:node-value:NAME] |
a value the node sets (see below) |
[orchestra-notification:label], […:link], […:message] |
what the notification itself carries, which depends on who sent it (below) |
[user:*] |
the recipient, when the email goes to one person |
[site:*] |
the site |
What each notification carries under [orchestra-notification:*]:
| Sent by | label |
link |
message |
|---|---|---|---|
| Notify node | – | – | – |
task_assigned |
the task's label | the inbox | – |
task_reminder |
the task's label | the inbox | the timer's message |
task_canceled |
the task's label | – | why it was canceled |
arrival (interaction_link) |
the step's label | the step itself | – |
[user:*] reads the person the email is addressed to, so it resolves on any
email sent to one person: an email with no Cc, and each blind copy, which goes
out on its own. An email shared between a To and a Cc has several readers and
leaves [user:*] empty.
What a process variable brings in is data: a link holder who typed
[site:mail] into a form field sees [site:mail] in the email, never the
site's address. Easy Email renders the text again after Orchestra, and this
holds through every pass.
Easy Email renders every email in the site's default language, whatever the recipient's own language. On a multilingual site, values and templates are translated through configuration translation, and the email is still sent in the default language.
Node values¶
A Notify node, and every node that parks (task, wait, interaction), has a Values section: a list of names, each mapped to a text that may contain tokens.
| Name | Text |
|---|---|
subject |
Request [orchestra:var:reference] approved |
title |
Your request is approved |
body |
[orchestra:var:validator_uid:user:display-name] approved it on [orchestra:var:approved_on]. |
link |
[orchestra-notification:link] |
The rules:
- A name is a machine name: lowercase letters, digits and underscores, unique on the node.
[orchestra:node-value:NAME]reads the value of the node the step stands on. A reminder reads its task's node, since its timer stands there.- A name the node does not set reads as nothing, so one template serves nodes that set a title and nodes that set none.
- The tokens inside a value are replaced with the same data as the template: the instance, the step, the notification, the recipient.
- A value is plain text. Its markup is escaped, so write the markup in the template or the layout and keep only words in the value.
- A value does not read another value.
- A value is read when the email is rendered, from the workflow version the instance runs, so a reminder sent days later reads the variables as they are then.
Use the same names on every node and the templates and the layout stay generic. A convention that covers most emails:
| Name | Used for |
|---|---|
subject |
the subject line |
title |
the heading |
intro |
a first sentence, before the body |
body |
the main text |
details |
a secondary block, shown only when set |
link |
the address of the call to action |
link_label |
the words of the call to action |
A value that is only a token¶
A value whose text is a single token reads as empty when that token does, which makes a block of the layout depend on the data rather than on the node:
| Name | Text |
|---|---|
reason |
[orchestra:var:rejection_reason] |
{% if node_values.reason %}
<div class="reason"><strong>{{ 'Reason'|t }}</strong> {{ node_values.reason }}</div>
{% endif %}
The block shows only when the variable holds something. Keep the label in the
layout: a value Reason: [orchestra:var:rejection_reason] is never empty, and
its block would always show.
Layout files¶
Easy Email renders each body through a theme template. A theme overrides it by
placing a file of the same name in its templates/ directory, then clearing
the cache.
| File | Lays out |
|---|---|
easy-email-body-html.html.twig |
the HTML body of every email of the site |
easy-email-body-html--TEMPLATE.html.twig |
the HTML body of one Easy Email template |
easy-email-body-plain.html.twig |
the plain-text body of every email |
easy-email-body-plain--TEMPLATE.html.twig |
the plain-text body of one template |
TEMPLATE is the template id with its underscores turned into hyphens: the
template orchestra_task_reminder is laid out by
easy-email-body-html--orchestra-task-reminder.html.twig, a per-tenant
orchestra_city_hall_task_assigned by
easy-email-body-html--orchestra-city-hall-task-assigned.html.twig. There is no
file for "every Orchestra email": the site-wide file lays them out, and puts
their own zones in {% if orchestra %}.
The layout gets:
| Variable | What it holds |
|---|---|
body |
the template's body, its tokens already replaced |
easy_email |
the Easy Email entity |
orchestra |
Orchestra emails only: the Orchestra tokens, by name |
node_values |
Orchestra emails only: the node's values, by name |
orchestra reads [orchestra:*] with an underscore for each hyphen:
orchestra.label, orchestra.workflow_label, orchestra.node_label,
orchestra.status_label, orchestra.correlation_key. It does not read process
variables: a layout that needs one reads a value the node relays. node_values
reads the node's values: node_values.title is [orchestra:node-value:title].
Both are plain text Twig escapes, both read as empty for a name nobody sets, and
both work with Twig's own fallbacks: {{ node_values.title ?: orchestra.node_label }}
shows the node's title, or the step's label where the node sets none, which no
token can do. Each name is rendered the first time the layout reads it, so a
layout pays only for what it shows.
The file is read from the theme that renders the email, which is not always the front theme: an email leaves before the page, after it or from cron. Which theme that is, and where it is set, depends on the HTML mailer (see step 2).
Step by step¶
This walks through the most generic setup: one layout for the site, one Easy Email template for most notifications, and the words on the nodes. Each step says where to click, what to write, and how to check it.
Step 1: use the Easy Email channel¶
- Make sure the site sends HTML email. Easy Email needs an HTML mailer, which
it does not install itself: Mailer Plus (the
symfony_mailerproject, with its Mailer override module so that Easy Email's mail goes through it), Drupal Symfony Mailer Lite, or Mime Mail. Drupal's own mailer sends plain text, and the HTML would arrive flattened. - Enable Orchestra Easy Email (
orchestra_easy_email). It brings Easy Email and installs the four Orchestra templates. - Uninstall Orchestra Mail (
orchestra_mail) if it is enabled: the two channels send independently, so with both every recipient gets two emails. The status report warns while both are on.
Check: Structure > Email templates (/admin/structure/email-templates/templates)
lists orchestra_generic, orchestra_task_assigned, orchestra_task_reminder,
orchestra_task_canceled and orchestra_interaction_link.
Step 2: know which theme renders the emails¶
The layout lives in a theme, so it matters which theme an email is rendered in. An email leaves at different moments, before the page, after it, or from cron, and by default it is rendered in whichever theme is active then: the front theme, the administration theme when an operator acts in the back office, the default theme from cron. Which setting decides depends on the HTML mailer:
- Mailer Plus (
symfony_mailer, with its Mailer override and Mailer policy modules): the Theme adjuster of a mailer policy decides, at Configuration > System > Mailer Plus > Policy (/admin/config/system/mailer/policy). The policy that applies to every email,_, ships with Active with fallback: the active theme, except the administration theme, which falls back to the default theme. So with that setting, put the layout in the default (front) theme, or pick one theme outright in the adjuster. - Drupal Symfony Mailer Lite or Mime Mail: either is selected through Mail System, whose Theme to render the emails setting decides (Configuration > System > Mail System); set it to the theme that holds the layout rather than "Current".
Step 3: write the site layout¶
In that theme, create templates/easy-email-body-html.html.twig (the folder may
hold sub-folders; Drupal finds the file by name):
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" class="email">
{% block header %}
<tr><td class="logo">
<img src="public://logo.png" height="64" alt="{{ 'My site'|t }}">
</td></tr>
<tr><td class="header">{{ 'My site'|t }}</td></tr>
{% endblock %}
{% if orchestra %}
{% block orchestra_heading %}
<tr><td class="step">{{ orchestra.workflow_label }}: {{ orchestra.label }}</td></tr>
<tr><td class="title"><h1>{{ node_values.title ?: orchestra.node_label }}</h1></td></tr>
{% if node_values.intro %}
<tr><td class="intro"><p>{{ node_values.intro }}</p></td></tr>
{% endif %}
{% endblock %}
{% endif %}
{% block content %}
<tr><td class="content">{{ body }}</td></tr>
{% endblock %}
{% block action %}
{% if node_values.details %}
<tr><td class="details">{{ node_values.details }}</td></tr>
{% endif %}
{% if node_values.link %}
<tr><td class="action">
<a class="button" href="{{ node_values.link }}">{{ node_values.link_label ?: 'Open'|t }}</a>
</td></tr>
{% endif %}
{% endblock %}
{% block footer %}
<tr><td class="footer">{{ 'Sent by My site.'|t }}</td></tr>
{% endblock %}
</table>
Line by line:
- The header and footer are outside
{% if orchestra %}: every email of the site gets them, Orchestra's and other modules' alike. - The logo is a file of the site (
public://logo.png, the one uploaded in the theme's settings), written by its ownpublic://address so that Mailer Plus's Inline images adjuster embeds it in the email: add that adjuster to the mailer policy. To have the logo on every email of the site, put it in Mailer Plus's wrapper instead, not in both. See Logo and images, which also covers linking the image rather than embedding it. {% if orchestra %}holds what only an Orchestra email has: here its workflow and instance, and a heading.node_values.title ?: orchestra.node_labelshows the node'stitlevalue, or the step's own label when the node sets none, so every Orchestra email has a heading.{{ body }}is the Easy Email template's body, its tokens already replaced.- The
intro,detailsandlinkzones show only when the node sets them. - Each zone is a
{% block %}, so a template that needs another layout extends this one and replaces only the blocks that differ (step 8). - Email clients ignore most CSS: keep styles inline (
style="...") in a real layout; the classes here only name the zones.
Then create templates/easy-email-body-plain.html.twig, the same for the
plain-text alternative:
{% if orchestra %}{{ node_values.title ?: orchestra.node_label }}
{% endif %}{% if node_values.intro %}{{ node_values.intro }}
{% endif %}{{ body }}
{% if node_values.details %}
{{ node_values.details }}{% endif %}{% if node_values.link %}
{{ node_values.link_label ?: 'Open'|t }}: {{ node_values.link }}{% endif %}
Clear the cache (drush cr) so Drupal finds the new files.
Step 4: reduce the template to tokens¶
Open Structure > Email templates, then Edit on orchestra_generic, and
under Content:
- Subject:
[orchestra:node-value:subject]. - HTML body:
<p>[orchestra:node-value:body]</p>, in a text format that keeps the markup (Full HTML). - Generate plain text body from HTML body: leave it off, and set Plain
text body to
[orchestra:node-value:body]; or turn it on and let Easy Email derive the plain text from the HTML. - Inbox preview, the line mail clients show after the subject:
[orchestra:node-value:intro], or leave it empty. - Save.
Replacement patterns, on the same form, lists Easy Email's own and the site-wide tokens only; the Orchestra ones are in the Tokens table above, and in the token browser of a node's Values section. The template now holds no words of its own: everything it says comes from the node.
Step 5: set the words on the nodes¶
Open the workflow in the Complete modeler and, for each Notify node:
- Leave Notification type at
generic, so the node usesorchestra_generic. - Open its Values section and click Add value for each name:
| Name | Text |
|---|---|
subject |
[orchestra:label]: approved |
title |
Your request is approved |
intro |
Good news, [user:display-name]. |
body |
[orchestra:var:validator_uid:user:display-name] approved it. |
link |
[orchestra:var:request_url] |
link_label |
See the request |
The token browser below the list helps find tokens (with the Token module). 3. Save the workflow. If it is versioned, publish a version: an instance reads the values of the version it runs, so the new words reach the instances started on the new version.
A second Notify node, on the "rejected" branch, sets its own subject,
title and body, and a details value with
[orchestra:var:rejection_reason]. Both send the same template through the
same layout.
Step 6: try it¶
- To keep a copy of what is sent, edit the template and tick Save emails of
this type by default under Email storage. Sent emails then appear in
Reports > Email log (
/admin/reports/email). - Run an instance through the Notify node.
- Check the email: subject from
subject, heading fromtitle(or the step's label), the zones the node sets, nothing for the ones it does not. - Nothing arrived? The Orchestra log names the template ids it tried; see Troubleshooting.
Turn the email storage option off afterwards if the log should not keep every email.
Step 7: the task and arrival emails¶
task_assigned, task_reminder, task_canceled and the arrival email keep
their own templates, because they carry what a Notify node does not: the task's
label, the inbox or step link, the reminder's message, why a task was canceled.
They already go through the site layout. To make them generic too:
- Reduce each one's body to the node's words, as in step 4: for
orchestra_task_assigned, HTML body<p>[orchestra:node-value:body]</p>. Keep a subject that always resolves,[orchestra-notification:label], since a task node may set nosubject. - On each task (or interaction) node, set
titleandbodyin its values. - To show the layout's button on task emails, add a value
linkwith the text[orchestra-notification:link]: it relays the inbox or step link the notification carries, which the layout cannot read otherwise.
A reminder reads the same node's values as the assignment, since its timer
stands on the task's node. To word it differently, use
[orchestra-notification:message] (the timer's message) in
orchestra_task_reminder, or lay that template out on its own (below).
Step 8: one template laid out on its own¶
To give one template a different layout, create a file named after it, its
underscores turned into hyphens, templates/easy-email-body-html--orchestra-task-reminder.html.twig
for orchestra_task_reminder, and have it extend the site layout, replacing
only the blocks that differ:
{% extends 'easy-email-body-html.html.twig' %}
{% block orchestra_heading %}
<tr><td class="badge">{{ 'Reminder'|t }}</td></tr>
{{ parent() }}
{% endblock %}
extends names the site layout by its file name, and Drupal finds it in the
theme. The reminder keeps the header, the content, the button and the footer of
every other email, and adds a badge above the heading; {{ parent() }} keeps
what the site layout had in that block. Clear the cache after adding the file.
See Sharing parts between layouts for the other ways to reuse a layout.
Keep templates working when a node sets nothing¶
A template cannot fall back: a subject read only from a value is empty on a node
that sets none. Either set that value on every node that uses the template, or
write the subject from a token that always resolves ([orchestra:label],
[orchestra-notification:label]), or both:
[orchestra:label] [orchestra:node-value:subject]. The layout can fall back
(?:), the template cannot.
Logo and images¶
A mailbox cannot open a path of the site. An image in an email has to travel inside the email (embedded), or be fetched from a full address when the email is opened, which many mail clients do only once the reader allows images. Embedding is the recommended way: the logo shows at once, everywhere.
Use a small PNG or JPEG, not SVG: Gmail and Outlook do not show it.
Embedded (recommended, with Mailer Plus)¶
- Put the logo in the site's files, for example the one uploaded in the
theme's settings:
public://logo.png. - In the mailer policy that applies to every email,
_, at Configuration > System > Mailer Plus > Policy, add the Inline images adjuster. - In the layout, write the image with the file's own address,
public://…, not a web address:
<img src="public://logo.png" height="64" alt="{{ 'My site'|t }}">
When the email is sent, Inline images reads that file from the site's files,
attaches it to the email, and points the src to the attachment (cid:). The
email then carries the logo (here 20 KB), so keep the image small.
It works in Mailer Plus's wrapper, email-wrap.html.twig, for a logo on every
email of the site, as well as in Easy Email's layout,
easy-email-body-html.html.twig, for the Orchestra emails only: Inline images
runs after the email is wrapped. Put the logo in one of them, not both, or it
shows twice.
The public:// address is what keeps it simple. A web address in the src
(/sites/…/logo.png, or a file of the theme) makes Inline images download the
image from the site's own address while sending, which needs the server to reach
its public address, and from cron the site's URL; a local Docker setup usually
cannot. public:// is read from disk, and needs neither. A public:// address
only works where Inline images processes the email: without that adjuster, the
src reaches the mail client as it is, and the image does not show.
Fetched¶
To link the image instead, give it a full address.
- In Easy Email's layout,
file_url()gives the file's address on the site, and the URL to absolute adjuster of the policy completes it:
<img src="{{ file_url('public://logo.png') }}" height="64" alt="{{ 'My site'|t }}">
file_url(directory ~ '/images/email-logo.png') points to a file of the
theme that renders the email instead.
- In Mailer Plus's wrapper, URL to absolute does not help: it runs before the
email is wrapped, so an address written there stays relative and the image
does not show. Give the wrapper a full address from the theme:
// THEME.theme
function THEME_preprocess_email_wrap(array &$variables) {
$logo = theme_get_setting('logo.path', 'THEME');
if ($logo) {
$variables['logo_url'] = \Drupal::service('file_url_generator')->generateAbsoluteString($logo);
}
}
{% if logo_url %}<img src="{{ logo_url }}" height="64" alt="{{ 'My site'|t }}">{% endif %}
A full address uses the address of the request that sends the email. An email
sent from cron has no request to take it from: give the cron command the site's
URL (drush --uri=https://www.example.com cron), or the image points to
http://default.
With another HTML mailer¶
With Mime Mail, local images are embedded (cid:) unless its Link images
only setting is on, in which case they are linked by their full address. With
Symfony Mailer Lite, nothing rewrites addresses: write the full address in the
layout, or compute it in a preprocess function as above. Embedding the image as
data: text in the src is no way out: Gmail and Outlook do not show such
images.
With Mailer Plus: the wrapper and the stylesheet¶
Mailer Plus adds two things of its own that combine with the layout above:
- Wrap and convert (on in the
_policy) wraps the body of every email the site sends, Easy Email or not, in its templateemail-wrap.html.twig, and writes the plain-text version from the HTML. A theme overrides that file like any other. Put what every email of the site shares, the logo (see above), the header, the footer, inemail-wrap.html.twig, and keepeasy-email-body-html.html.twigfor what is Easy Email's or Orchestra's: theorchestraandnode_valueszones. Do not put the header in both, or it shows twice. - Inline CSS copies the theme's
emaillibrary intostyleattributes, since mail clients ignore most stylesheets. Declare it in the theme'sTHEME.libraries.yml:
email:
css:
theme:
css/email.css: {}
Then the layouts can use classes (class="header", class="button") and
style them in css/email.css, instead of style attributes on every tag.
Sharing parts between layouts¶
The layout files are ordinary theme templates, rendered by Drupal's Twig, so
everything Twig offers to reuse markup works in them, and the Orchestra
variables (orchestra, node_values, body) reach every template they pull
in.
Extend a layout and replace blocks. A layout made of {% block %} zones is a
base the others extend. A per-template file starts with
{% extends 'easy-email-body-html.html.twig' %} and redefines only its blocks;
{{ parent() }} inside a block keeps the base's content. A file can only extend
another, it cannot add markup outside its blocks.
Include a partial. Put a piece used by several layouts in a file of its own,
anywhere under the theme's templates/ directory, and include it through the
theme's namespace:
{# THEME/templates/email/signature.html.twig #}
<p class="signature">{{ 'The @site team'|t({'@site': 'My site'}) }}</p>
{% block footer %}
<tr><td class="footer">{% include '@THEME/email/signature.html.twig' %}</td></tr>
{% endblock %}
@THEME is the theme's machine name. The partial sees the layout's variables;
to hand it only some, write
{% include '@THEME/email/signature.html.twig' with { name: orchestra.label } only %}.
Embed a partial with blocks of its own. {% embed %} includes a file and
replaces blocks inside it, for a component used with different contents, such as
a call-to-action box:
{# THEME/templates/email/action-box.html.twig #}
<div class="action-box">
{% block action_text %}{% endblock %}
<a class="button" href="{{ url }}">{{ label }}</a>
</div>
{% if node_values.link %}
{% embed '@THEME/email/action-box.html.twig' with { url: node_values.link, label: node_values.link_label ?: 'Open'|t } %}
{% block action_text %}<p>{{ node_values.intro }}</p>{% endblock %}
{% endembed %}
{% endif %}
Branch inside one layout. When the difference is small, a condition in the
site layout is simpler than a file: {% if easy_email.bundle == 'orchestra_task_reminder' %}
tests the template the email uses, {% if orchestra %} whether it is an
Orchestra email at all.
All of it is read from the theme that renders the email: keep the base layout, the files that extend it and the partials in the same theme (see step 2).
Complex cases¶
Several brands on one site¶
Give each tenant its own templates and layouts. The template lookup tries
orchestra_<tenant>_<type> first, so orchestra_city_hall_generic serves the
city_hall tenant and every other tenant keeps orchestra_generic. Each
template gets its own layout file,
easy-email-body-html--orchestra-city-hall-generic.html.twig, which can
{% include %} a shared part of the site layout and change only the header and
colors.
Variants of one step¶
A process variable orchestra_notification_template_suffix switches template
for the rest of the instance: set it to urgent and every notification first
tries orchestra_<type>_urgent. A step that needs different words for the same
template is better served by a value that is only a token (see above), or by
two Notify nodes on two branches.
A shared child process¶
When a child process is shared by several parents, its nodes are shared, and so
are their values: every parent sends the child's words. Write them so they read
well for every parent, with tokens for what differs ([orchestra:label], a
variable the parent hands the child). A parent that needs other words can set
the orchestra_notification_template_suffix variable: an embedded child runs in
the parent's instance and sees it as it is, a child run as an instance of its
own needs it in its input mapping. The child's notifications then switch to the
templates of that suffix.
Optional blocks¶
A layout block that depends on data reads a value that is only a token (see A value that is only a token): the block shows when the data is there and disappears when it is not, without a template per case.
Links and buttons¶
Escaping keeps a value from breaking the markup, not from pointing anywhere: a
value placed in an href links wherever its text says. Build a link from an
address the site makes, [orchestra-notification:link] or a URL your own step
stores, never from a variable a link holder filled in.
Troubleshooting¶
| Symptom | Look at |
|---|---|
| No email at all | the Orchestra log: "no Easy Email template for the … notification" names the ids tried. Check the node's type and the template ids. |
| The layout is not applied | the theme that renders the email (Mailer Plus's policy Theme, or Mail System's Theme to render the emails), the file name (hyphens, not underscores), and clear the cache. |
| The header shows twice | with Mailer Plus, a header in both email-wrap.html.twig and the Easy Email layout. |
| The logo does not show | embedded: the policy must have Inline images, and the src must be the file's public:// address; linked: an address written in email-wrap.html.twig is not made absolute, and from cron the site's URL must be given to the cron command. See Logo and images. |
| A value is empty | the name on the node and in the template must match exactly; the email must be about a step (an email sent for the whole instance has no node to read values from); a token in the value may resolve to nothing. |
| Tags show as text | a value is plain text: put the markup in the template or the layout. |
[user:*] is empty |
the email has several readers (To and Cc), or the recipient is an address with no account. |
| The wrong language | Easy Email sends in the site's default language. |