Workflow emails

An email in your workflow is defined as a JS Object with the keys described below. They are processed by WorkflowInstance sendEmail(), workflow Notifications and the service std:workflow_emails:send_email.

Using emails

There needs to be at least 1 to recipient or 1 toExternal external recipient for an email to send.

The function version of the keys described below is useful when passing emails as configuration to workflow components.

If you’re not using a workflow you can still send emails defined like below through the std:workflow_emails:send_email service. You won’t have the WorkflowInstance M available and M.getActionableBy() will replaced for entity lookup. Most of the helpers for writing email text won’t work without WorkflowInstance. template key will have to a Template object. Example:

O.service("std:workflow_emails:send_email", {
    template: P.template("invoice"),
    to: "client",
    view: {
        charge: quote
    }
}, standaloneEntities);

Notifications

Notifications are pre-defined ‘notification’ emails, as a lookup of name to email definition. The template key can be left out, as it will default to templates in the notification/ directory with the same name as the notification.

Example notification definitions:

EgWorkflow.notifications({
    check_rejection: {
        to: "submitter"
    },
    approved: {
        to: "submitter"
    },
    rejected: {
        to: "submitter",
        cc: "reviewer"
    }
});

When the workflow enters a state (or passes through a dispatch state), the notification with the same name as the state is automatically sent. view key will have to be a function as notifications are created when the plugin is loaded.

Recipient

can be entity names with the usual suffixes like “_list” or the name is resolved using M.getActionableBy(), numeric id of SecurityPricipal, SecurityPricipal object and Ref / StoreObject representing a SecurityPricipal object.

Entity names are adjusted for efficiency and to prevent exceptions for you. You’re allowed to lookup the entities yourself e.g.

    to: M.entities.supervisor_list

Internally SecurityPricipal object(s) are yielded using O.user().

External recipient

is a JS Object with at least the email, nameFirst and name properties. Note that external recipients don’t get de-duplicated or respect the except key.

key template

Template object, or name of template within consuming plugin.

The view is passed has the additional properties of the WorkflowInstance M and the SecurityPrincipal toUser that the email is being sent to.

key view

View for rendering template.

If view is a function, the function is called to generate the view with WorkflowInstance M as the only argument.

emailSubject can be set here, or preferably use the emailSubject() template function.

key to

Recipient or list of recipients.

If to is a function, the function is called to generate the recipient(s) with WorkflowInstance M as the only argument.

key cc

CC recipient or list of recipients.

If cc is a function, the function is called to generate the recipient(s) with WorkflowInstance M as the only argument.

key except

Recipient or list of recipients that shouldn’t have email sent to them.

If except is a function, the function is called to generate the recipient(s) with WorkflowInstance M as the only argument.

key toExternal

External recipient or list of external recipients.

If toExternal is a function, the function is called to generate the external recipient(s) with WorkflowInstance M as the only argument.

key ccExternal

CC external recipient or list of external recipients.

If ccExternal is a function, the function is called to generate the external recipient(s) with WorkflowInstance M as the only argument.

Workflow template functions

A number of globalTemplateFunction() are available to use in HSVT email templates to perform common workflow text interpolations.

The following examples will assume the notification definition is set up as follows

{
    to: ["user_list"],
    view(M) {
        return {
            aList: ["one", "two", "three"],
            value: M.entities.user_maybe ? M.entities.user.title : "No user"
        };
    }
}

M:text(text)

text should be a single string. It interpolates text surrounded by \@s by fetching the title of the first object that appears in the entity list. If an entity could not be found, a ‘?’ will be put in the entity’s place.

Supports paragraphs with line breaks in the string. Removes all other whitespace.

If you need to concatenate multiple strings and still want to use this function, you can pass one use of concat() into this function instead of a string.

Usage:

M:text("Interpolates @user@! Also interpolates NAMEing, e.g. NAME(example:routing:committee).")

M:if-entity(entityName)

If the entity entityName is defined for this workflow instance, render the anonymous block. Otherwise render the else block.

Usage:

<p> "Has user? " M:if-entity("user") { "true" } else { "false" } </p>

M:button(title url)

Renders a button in the email. The arguments title and url are optional. If undefined, the button will render with M.title and point to M.url. title may include interpolations marked with \@ or NAME().

Usage:

M:button()
M:button("Other title")
M:button("Other url" "/abc")

M:list(arg1 arg2 …)

Takes an array variable or multiple string arguments and returns them separated by commas.

<p> "List (args): " M:entity-list("Big" "bad" "wolf") </p>
<p> "List (array): " M:entity-list(aList) </p>

Output:
List (args): Big, bad, wolf
List (array): one, two, three

M:entity-list(entityName)

entityName is a string of an entity. Like JS’s join(), this concatenates a list of entity titles by separating items with commas, without a trailing comma.

Usage:

<p> "List (entities): " M:entity-list("user") </p>

Output: User title 1, User title 2, User title 3.

M:first-name(entity)

entity can either be the string name of an entity or an entity. If the entity’s title is of type O.T_TEXT_PERSON_NAME, this will return the first name of the entity. If the title is not a person name type, then the full title will be returned. If you pass in an object, and the object is undefined then no text will be rendered.

Usage:

<p> "First name: " M:first-name("user") </p>
<p> "Alternatively: " M:first-name(M.entities.user_maybe) </p>

M:last-name(entity)

entity can either be the string name of an entity or an entity. If the entity’s title is of type O.T_TEXT_PERSON_NAME, this will return the last name of the entity. If the title is not a person name type, then the full title will be returned. If you pass in an object, and the object is undefined then no text will be rendered.

<p> "Last name: " M:last-name("user") </p>
<p> "Alternatively: " M:last-name(M.entities.user_maybe) </p>

M:switch-role(user)

user is a SecurityPrincipal. The template renders the first named block identified by the name of a role user has in the workflow instance. If user doesn’t have any of the roles, render the anonymous block.

If the user is a group, the user is identified as having the role of the block that is identified by the matching group code.

Usage:

M:switch-role(toUser) {
    <p>"Optional generic fallback text."</p>
} organisationHead {
    <p>"Text specific to the role 'organisationHead'."</p>
} example:group:administrators {
    <p>"Text specific to administrators."</p>
}