CodamAIDocs
Topicdone

How the right template is found

One default per email and language, plus a tenant's own wording: the search from the tenant to the default, database before what ships, and what happens with a broken template.

Variants
tenant + languagetenant + language codedefault + languagedefault + language codedefault in the fallback languageapplication and flow as placeholdersbroken template → skipped

What this is about

Every email has exactly one default per language. On top of that, a tenant can have its own wording, which applies to it instead of the default. CIAS has to pick exactly one template for every email. It searches from the specific to the general: first the tenant’s wording, then the default, each time first in the exact language.

Where the templates come from

CIAS ships templates for every email in de and en. On start, CIAS copies them into the database, each as the default of its email and language. So whoever maintains the wording finds every email in the database, not only the ones already changed.

When an installation starts
  1. 1
    CIAS
    reads the shipped templates: its own and those other modules register with a PackagedMailKeys bean
  2. 2
    CIAS→Database
    creates every missing default, marked as shipped
  3. 3
    CIAS→Database
    brings shipped defaults nobody changed up to the text of the new version
    Result: Changed defaults stay as they are. A new shipped text is reported once in the log.

Whether a template is still the shipped one or already changed is shown by the field origin (PACKAGED or EDITED). If several instances start at the same time, the database decides who creates a row. A failure here does not stop the start.

Where an email’s features come from

Featurefor registration emailsExample
Tenantthe tenant the person joins. Missing if none is known yetnordbau
Languagethe language of the request, German if none is givende-AT

The staircase

Which template does CIAS take for VERIFY_EMAIL, tenant nordbau, language de-AT?
  1. CIAS
    Tenant + language
    nordbau + de-AT: this customer's wording, exactly in this language
    ↳ no next
  2. CIAS
    Tenant + language code
    nordbau + de: this customer's wording in the language without a country
    ↳ no next
  3. CIAS
    Default + language
    de-AT: the default
    ↳ no next
  4. CIAS
    Default + language code
    de: the default without a country
    ↳ no next
  5. CIAS
    Default in the fallback language
    the fallback language, German unless configured
    ↳ no error: no template
  6. Template found, email is generated

Steps whose feature is missing are skipped. If an email has no tenant, the search starts at the default. If the language is already just a language code like de, the language code steps are skipped.

Two sources per step

At every step CIAS asks two sources, in this order:

  1. the database: the defaults and the tenants’ wording, see Edit templates,
  2. the class path: the files shipped with the application.

Because the defaults are copied into the database on start, the database almost always answers already. The class path steps in if a row is missing or cannot be generated, or if the installation runs without a database for templates. The shipped files are here, one file for the subject and one for the text per language:

cias/mail/{KEY}/{language}.subject.ftl               default
cias/mail/tenant/{tenant}/{KEY}/{language}.…         tenant

The text is called {language}.body.ftl for plain text or {language}.body.html.ftl for HTML. If both exist, plain text wins.

Application and flow: placeholders instead of templates of their own

A registration knows which product a person came from (application, for example auditoor) and which flow it runs through (flow, for example SELF_SERVICE or TENANT_ADMIN). Neither is a feature of a template. Both are available in every email as the placeholders ${application} and ${flow}, empty if the sender has no value. A template can branch on them:

<#if flow == "TENANT_ADMIN">
You were invited by your administrator.
<#else>
You registered yourself.
</#if>

The variants

Where the search ends

When: Nordbau has its own welcome email in de-AT.

Result: this template, only for Nordbau

When: Nordbau has its own welcome email in de, the person chose de-AT.

Result: Nordbau's wording in de. The customer's own wording beats a default in the more exact language

When: No wording of the tenant's own, the person chose en.

Result: the English default

When: The person chose de-AT, there is only de.

Result: the default in de

When: The person chose fr, there is no French template.

The fallback language applies. It is German, configurable with codamai.cias.notification.fallback-language.

Result: the default in the fallback language

When: The welcome email should sound different for auditoor.

Instead of a template of its own, the default branches on ${application}.

Result: one template for all products, with different text

When: A template that was found cannot be generated, for example an older row with a typo in the template language.

CIAS writes a warning to the log, skips the template and keeps searching: first in the shipped file of the same step, then on the next step. New broken templates cannot be saved in the first place, see Edit templates.

Result: next source or next step

Only if the default in the fallback language is missing or broken too does generating the email fail. That is a delivery error, not a business one.

Pitfalls

Next

Sources in the code and the knowledge base
  • CIAS/cias-notification – FreeMarkerMailRenderer (render: application and flow in the model, candidates), MailTemplateKey (email, tenant, language), MailRequest
  • CIAS/cias-notification – CompositeMailTemplateSource (database before class path per candidate), DbMailTemplateSource, ClasspathMailTemplateSource (paths cias/mail/…)
  • CIAS/cias-notification – MailTemplateSeeder, ClasspathPackagedTemplates (KEYS, LANGUAGES), PackagedMailKeys
  • CIAS/cias-notification – CiasNotificationConfiguration (codamai.cias.notification.fallback-language)
  • CIAS/cias-registration – RegistrationService.sendMail (tenantKey, application, flow, locale)
  • CIAS/cias-notification/docs/adr – ADR-019
Search