Email Templates

Every system email, the magic login link, the group invitation, etc. is rendered from a template file you can customize the design of. Copy it to your child theme, edit the copy, and your changes survive every plugin update. The mechanics are the same as the shortcode template system: the plugin ships working templates, and your theme can override any of them.

Which emails are templatable

Email HTML template Text template
Magic login link emails/magic_link_html.php emails/magic_link_text.php
Group invitation emails/group_invite_html.php emails/group_invite_text.php

Each email is sent as multipart/alternative: an HTML body and a plain text body. Override either or both. A missing template isn't an error; the system falls back to a built-in plain message.

The Sushi LMS notification emails (enrollment confirmation, course completed, quiz passed/failed, certificate earned) are also template files, under emails/sushi-lms/. They use their own simpler layout and a $data array of values rather than the shared shell described below; their own shell partial is covered in its own section.

The override workflow

  1. In your (child) theme, create a torii/emails/ directory.
  2. In the plugin, find the template under templates/emails/. Copy the file into your theme directory, keeping the same name and sub-path.
  3. Edit your copy. The system checks your theme first and prefers your copy over its own.

You can also use the Templates screen in the admin area (System category) to copy all templates into your child theme in one click. The copy never overwrites existing files, so re-running it after a plugin update picks up newly added templates while leaving your edits alone.

Placeholders

Templates contain {{placeholder}} tokens that get replaced with the real values when the email is sent. The sender escapes each value for the destination before substituting it, HTML-escaped for the HTML template, plain for the text variant, so a URL or name with special characters comes through intact.

Replacement happens after the template has rendered, not during. The tokens survive PHP rendering and are swapped for real values in the finished output. That's why a placeholder works anywhere in the template, including inside the body markup the shell echoes and in the shell itself, if you add one there.

Template Placeholders
magic_link_html / magic_link_text {{magic_link}}, {{expires_in}}
group_invite_html / group_invite_text {{accept_url}}, {{expires_in}}, {{group_name}}, {{inviter_name}}, {{team_name}}

Keep every placeholder somewhere in your edited template. If you remove one, the recipient sees nothing where the value belongs, and no error tells you it went missing.

The shared HTML shell

The two HTML email templates don't each carry a full page of markup. They hand three pieces of content to a shared shell, emails/partials/html-shell.php, which renders the complete email: outer background, the 600px card, the colored header band, the content area, and the footer note.

The shell receives a $data array with three keys:

  • heading, the header band text (e.g. "Your Magic Login Link"). Escaped by the shell.
  • body, the HTML for the content area, including the action button and the placeholder-bearing copy. Emitted as-is; the calling template is responsible for its markup.
  • footer_note, the small grey footer line (e.g. "If you did not request this link..."). Escaped by the shell.

That's the whole standardized pattern: an email template is nothing but that array plus an include of the shell. The magic link template, trimmed to its bones, looks like this:

$data = [
    'heading'     => 'Your Magic Login Link',
    'footer_note' => 'If you did not request this link, you can safely ignore this email.',
    'body'        => '<p>You requested a magic login link. Click the button below to log in:</p>
        <!-- action button table, expiry note, fallback link, with {{magic_link}} and {{expires_in}} inside -->',
];

include torii_email_class::elf_get_template_path( 'partials/html-shell' );

Everything else, the outer page, the 600px card, the colored header band, the footer, is the shell's business.

This is where the shell pays off: copy partials/html-shell.php to your theme and restyle it once, and every email built on the shell gets your branding, colors, fonts, layout, without touching any individual email template. A theme override of an individual email template still wins for that email's content; the shell only shapes the frame around it.

When editing a shell override, keep echoing the three $data values ($data['heading'], $data['body'], $data['footer_note']). Drop them and the email arrives with its content missing. The plugin's shipped shell is the best starting point for an override. Copy it and restyle rather than writing from zero.

Note: the partials/ directory isn't counted as a customizable email template on the Templates admin screen, but the copy operation does ship it to your theme, because the parent templates need it.

The Sushi LMS Template

The five Sushi LMS notification emails follow the same idea with their own smaller shell template, emails/sushi-lms/partials/shell.php. Each notification template prepares a few variables and includes the shell, which renders the greeting, the body, an optional action button, and a site-name footer.

The shell expects these in scope from the including template:

  • $display_name, the recipient's escaped display name
  • $body, the notification's unique HTML, already escaped
  • $button, an optional action button as an array: url, label, and color (hex). Empty or missing means no button.
  • $site_name and $site_url, the escaped site identity for the footer
  • $namespace, the translation namespace

The override story is the same as the main shell: copy sushi-lms/partials/shell.php to your theme's torii/emails/sushi-lms/partials/ directory, restyle it once, and all five notification emails follow. Keep echoing the variables above, or the notification loses its content.

Plain Text Versions

The plain text templates (*_text.php) are small: a greeting, the critical link on its own line, the expiry note, the ignore note. Email clients that prefer text, and most spam filters inspecting the multipart structure, see this version. When you customize the HTML, give the text variant a matching pass: same link, same expiry wording, no markup.

Fallbacks

If a template can't be found, plugin file missing and no theme copy, the email still sends using a built-in plain fallback body. A template problem will never silently stop an email.

Hooks

  • wpal/torii/emails/template-paths filters the array of search paths the loader checks (theme first, then plugin). Add your own locations, e.g. from a companion plugin.
  • wpal/torii/email/magic_link/subject filters the magic link subject line.
  • wpal/torii/email/magic_link/body_html and body_text filter the rendered bodies before sending.
  • wpal/torii/email/group_invite/subject, body_html, body_text do the same for group invitations.

The subject and body filters receive the user or invite context. Site-specific customization, different subjects per membership level, tracking parameters on links, belongs in a small filter callback rather than a template edit when the data involved isn't a placeholder.