🔒

Templates System

Diagnostic.ly · Password required
Wrong password. Try again.

Templates System

Technical reference for the templates system — covers template sets, triggers, database schema, communication workflow, language support, and admin interfaces.

Templates System

Technical Reference — app.diagnostic.ly

Overview

The templates system is responsible for all outbound communications on the platform — emails and SMS messages sent to assignees (patients), account admins, observers, managers, and system users. Every automated communication is driven by a template stored in the database, rendered with dynamic content, and delivered via an asynchronous email queue.

The system is multi-tenant: template sets are created and managed exclusively by global admins, and they support multiple languages through a copy-and-translate workflow. Accounts do not author or manage templates — they are simply assigned a messaging set by a global admin, and all communications sent on their behalf are driven by that set.


Core Concepts

Template Set messaging

A template set is the top-level container. It holds the branding and sender configuration for a group of templates — the “from” name and email address, logo, website URL, provisioning URL, support phone number, and email signature. Every template in the system belongs to exactly one template set.

Template sets are created and managed exclusively by global admins. Accounts cannot create template sets or individual templates. Instead, a global admin assigns a messaging set to an account by setting accounts.messaging_id. From that point forward, all communications sent on behalf of that account are resolved from the assigned set.

Template sets can be:

  • Global (account_id = 0) — available to all accounts as a shared baseline
  • Account-specific (account_id = N) — created by a global admin and scoped to one account

When an event triggers a communication, the platform resolves which template set to use by looking up the account’s messaging_id.

Trigger messaging_type

A trigger (called a “messaging type”) is a named event in the platform workflow that causes a communication to be sent. Examples:

  • Assignee - Kit Shipped
  • Account Admin - Order Placed
  • Authentication - 2FA Authentication Message
  • Assignee - Test Results Available Notification

Each trigger has a stable integer id. Application code references triggers exclusively by this numeric ID — the message_purpose string is for human display only.

Template message_templates

A template is the actual content of a communication. It belongs to a template set and is linked to a specific trigger. Each trigger can have at most one template per combination of:

  • Template set (messagingID)
  • Media type (templateType: 1 = Email, 2 = SMS)
  • Language (language_id)

A single trigger may have up to two templates per language in any given template set (one email, one SMS).


Database Schema

messaging — Template Sets

ColumnTypeDescription
idintPrimary key
messaging_set_namevarcharDisplay name
namevarcharWebsite/brand name
email_from_name_for_purchasersvarcharFrom name for account admin emails
email_from_address_for_purchsersvarcharFrom address for account admin emails
email_from_name_for_assigneesvarcharFrom name for assignee emails
email_from_address_for_assigneesvarcharFrom address for assignee emails
websiteURLvarcharPatient-facing portal URL
provisionURLvarcharProvisioning/admin portal URL
email_signaturevarcharAppended to all outbound emails
logoURLvarcharLogo file path (relative to assets/logo/)
faviconURLvarcharFavicon file path
supportPhonevarcharSupport phone shown in templates
account_idint0 = global; otherwise FK to accounts.id
allow_accounts_to_selecttinyintWhether accounts can self-assign this set

messaging_type — Triggers

ColumnTypeDescription
idintPrimary key; referenced by application code as messageTypeID
message_purposevarcharHuman-readable trigger name
sortorderintDisplay sort order
language_idintFK → languages.id
message_template_groupings_idintFK → message_template_groupings.id (audience category)
communication_idintFK → communication_types.id (workflow phase)
created_at / updated_attimestampAudit timestamps

message_templates — Template Content

ColumnTypeDescription
templateIDintPrimary key
messagingIDintFK → messaging.id (which template set)
messageTypeIDintFK → messaging_type.id (which trigger)
templateTypetinyint1 = Email, 2 = SMS
language_idintFK → languages.id
subjecttextEmail subject line (empty for SMS)
bodylongtextHTML email body or SMS text content
startDatedateScheduled start date (for time-delayed sends)
delayDurationInDaysintDays to wait before sending
numberOfRemindersintHow many reminder sends to schedule
message_template_grouping_idintFK → message_template_groupings.id; used for special template types (Test Results Layouts, Action Pages)
message_template_bundlevarcharComma-separated bundle IDs (Test Results Layouts only)
permit_pdf_downloadtinyintAllow PDF download of results
permit_share_emailtinyintAllow email sharing of results
action_page_namevarcharAction Pages only — display name
url_slugvarcharAction Pages only — URL path
user_typevarcharAction Pages only — user type context
relevant_portalvarcharAction Pages only — portal context
redirect_urlvarcharAction Pages only — post-action redirect
action_pages_typevarcharAction Pages only — defaults to "Interstitial"
custom_messagetextOptional custom message override
descriptiontextHuman description (Test Results Layouts)
account_idintSet by global admins when a template is scoped to a specific account’s messaging set; accounts do not write this field
newly_added_templatetinyintFlag; highlights recently added templates in the UI

Supporting Tables

TablePurpose
message_template_groupingsAudience/category labels for triggers (Admins, Assignees, Observers, etc.)
communication_typesWorkflow phase labels (Ecommerce, Shipment, Test Results, etc.)
languagesSupported languages; id=1 is English (default)
replacement_variablesDynamic placeholders available for use in template bodies
email_queueOutbound email queue; templates are sent asynchronously through this table
communication_settingsSandbox mode flag and sandbox redirect address

Template Categorization

Audience Groupings message_template_groupings

Every trigger is assigned to an audience grouping. These groupings appear as the “Category” filter on the admin template management page.

IDNameDescription
1All Template TypesMeta-filter that shows all triggers
2EcommerceOrder and purchase notifications
3AdminsGlobal admin system notifications
4ManagersShipping and workflow manager notifications
5AccountsAccount admin notifications
6Assignees/Patients - NotificationsOperational assignee communications
7Assignees/Patients - ResultsTest result communications to assignees
8ObserversObserver notifications
9AuthenticationLogin, 2FA, password reset
10Test Results LayoutsDefines result page rendering (not email/SMS)
11Employees/GroupsEmployee/group account notifications
12Providers/Medical StaffClinical staff notifications
13GeneralMiscellaneous platform communications
14Support EntityCustomer care and support templates
15Shipment InsertsPhysical shipment insert templates
16Action PagesInterstitial pages in the test workflow (not email/SMS)
17Direct MailDirect mail / physical letter templates
18Case ManagementCase management workflow communications

Workflow Phases communication_types

Each trigger is also classified by its position in the platform workflow. This is a higher-level grouping used to organize triggers by lifecycle stage.

IDNameTriggers it covers
1Ecommerce TransactionsOrder confirmations
2Assignee Portal RegistrationNew user onboarding, login credentials, self-registration
3Shipment CommunicationsKit shipped, arrived, delays, re-shipments, lab return
4General CommunicationsEducation reminders, observer notifications, auth, support
5Test ResultsResults available, partial results, specimen rejected, canceled
6Direct MailHand-to-patient, use-in-clinic, return labels, observer feedback

Triggers that have no communication_id are uncategorized.


Trigger-to-Communication Workflow

Resolution Flow

When a platform event occurs, the following steps happen to send a communication:

1. Application code identifies the trigger
   └─ Hardcodes the messaging_type.id (e.g. messageTypeID = 92)

2. Look up the recipient's account → get messaging_id
   └─ SELECT messaging_id FROM accounts WHERE id = ?

3. Resolve the template
   └─ getMessageTemplateByMessageIdandType(messagingID, messageTypeID, languageId)
   └─ Queries: message_templates WHERE messagingID=? AND messageTypeID=? AND language_id=?

4. Populate replacement variables in subject and body
   └─ Substitutes dynamic placeholders with runtime values

5. Queue the email
   └─ CheckCommunicationSuppression::sendEmailCM()
        → EmailQueue::addGroupMembershipUsersDataEmailQueue()
   └─ Adds a row to email_queue with: to address, from, subject, body, templateId, messagingID

6. Cron: process_email_queue.php
   └─ Reads unprocessed rows from email_queue
   └─ Sends via PHPMailer over SMTP
   └─ Logs delivery result

Missing Template Handling

If step 3 finds no template, the platform calls SendEmailMessageTemplate::sendMessageTemplateNotFoundEmail(), which sends an alert email to the customer support address. The alert includes:

  • The message_purpose name of the missing trigger
  • The account name and user name
  • The messageTypeID that was looked up
  • The environment (development / staging / production)
This is the primary observable signal when a trigger has no template configured for a given account.

Communication Suppression

Before queuing an email, CheckCommunicationSuppression::sendEmailCM() checks whether the recipient user has suppression active:

  • users.communication_types — sets allowed communication channel (1 = Email & SMS, 2 = Email only, 3 = SMS only)
  • users.suppress_communication_group_id — suppresses an entire message_template_groupings category

If suppression is active for the relevant grouping, the email is not queued.

Sandbox Mode

communication_settings contains an is_sandbox_enabled flag. When enabled, all outbound emails are redirected to the sandbox_email address rather than the actual recipient. This is used in development and staging environments.


Language Support

Every template record has a language_id foreign key. A template set can have the same trigger covered in multiple languages — one template row per language. Language 1 (English) is the default and must be configured first before translations can be added.

The languages table defines available languages. A companion language_status table tracks which languages are active or selected for a given context.

Copy & Translate Workflow

The admin interface provides a Copy & Translate feature (ajaxTranslateMessageTemplate.php) that:

  1. Copies all English (language_id=1) templates for a given template set and category to the selected target language
  2. Auto-translates each template’s subject and body using a translation service
  3. Creates new message_templates rows with the target language_id
  4. Newly created translations are flagged with newly_added_template=1, which highlights them in the template list for review

The Copy Missing variant does the same but only for triggers that don’t yet have a template in the target language, filling gaps without overwriting existing translations.

The workflow runs asynchronously, reporting progress as it copies and translates each template (step 1: copying, step 2: translating N of M, step 3: complete).


Special Template Types

Test Results Layouts (grouping_id = 10)

These templates do not send emails or SMS. Instead they define the visual layout of the test results page presented to the assignee. Key fields:

  • description — Human label for the layout
  • message_template_bundle — Comma-separated IDs of product bundles this layout applies to
  • permit_pdf_download — Whether assignees can download a PDF of their results
  • permit_share_email — Whether assignees can share results by email

Multiple layouts can exist in a template set, each associated with different bundles.

Action Pages (grouping_id = 16)

Action pages are interstitial screens shown to users at specific points in the test workflow. They are not emails — they are rendered as full web pages. Key fields:

  • action_page_name — Display name in the admin UI
  • url_slug — URL path fragment appended to the provisioning URL
  • action_pages_type — Currently always "Interstitial"
  • user_type — Which user type sees this page
  • relevant_portal — Which portal context this belongs to
  • redirect_url — Where the user is sent after the action page

Action pages stored in the Global Library can be copied to specific accounts via the “Copy to Account” function.


Replacement Variables

Template bodies support dynamic placeholders — tokens that are substituted with real values at send time. These are stored in the replacement_variables table, scoped by type and use_type (1 = Templates).

The editor displays available variables for the template type (email vs SMS) via getReplacementVariablesShortcuts($type, 1). Variables are inserted into the subject or body and resolved by the application before the email is queued.


Admin Interfaces

Global Admin admin/

FilePurpose
templates.phpList all template sets; filter by library (global or account-specific); add new sets
global_messagingtemplate.phpView all templates within a selected set, filtered by category and language; add, delete, copy & translate
edittemplate.phpCreate or edit a single template (subject, body, media type, delay, reminder schedule)
global_sitedetail.phpEdit the metadata of a template set (from addresses, URLs, logo, etc.)
media_manager.phpManage image/media assets used in template bodies
ajaxTranslateMessageTemplate.phpAJAX backend for the copy-and-translate workflow
check_missing_templates.phpUtility to identify triggers with no configured template in a given set

Account Admin account/

Accounts have read-only visibility into the messaging set assigned to them by a global admin. They cannot create, edit, or delete templates or template sets.
FilePurpose
messagingtemplate.phpView the templates in the account’s assigned messaging set

Key Models

FileResponsibility
app/Models/MessagingTemplates.phpAll CRUD operations on message_templates; bulk copy; replacement variable lookup; reminder email logic
app/Models/MessagingTypes.phpQuery messaging_type records; look up trigger types by language and template set; check “in use” status
app/Models/Messaging.phpCRUD for messaging (template sets); get sets by account
app/Helpers/CheckCommunicationSuppression.phpChecks suppression rules before queuing; calls EmailQueue to enqueue
app/Helpers/SendEmailMessageTemplate.phpSends the “template not found” error alert to customer support
app/Models/EmailQueue.phpInserts rows into the email_queue table

Adding a New Trigger

When a new platform event needs to send a communication, the steps are:

  1. Insert a row into messaging_type with the message_purpose label, an appropriate message_template_groupings_id, and communication_id.

  2. Note the new id — this becomes the messageTypeID constant used in application code.

  3. Author the template via the admin UI: navigate to a template set → Add Template → select the new trigger from the message purpose dropdown → write subject and body.

  4. Reference the ID in application code wherever the event fires:

    $messageTypeID = 92; // "Assignee - Kit Shipped"
    $msgTemplates = $mtObj->getMessageTemplateByMessageIdandType(
        $siteData['id'], $messageTypeID, $selected_language_id
    );
    
  5. Repeat for all affected template sets — the trigger will show as “Missing” in any set that hasn’t had a template authored for it.

  6. Add translations as needed using Copy & Translate in each language-enabled set.