Repository navigation
feat(mail): authorable transactional email templates (TIGER-267) - #359
Merged
Merged
Conversation
Add a DB-override tier to Tiger_Mail's file-based template engine so owners
and agents can author transactional-email subject + HTML body (with {{var}}
placeholders) without a code change. File = base, DB row = override, last
wins — the same live-override pattern as Config/Translation, scoped by org
('' = global default, else per-tenant).
Engine (library):
- migration 0054: email_template store, plain UNIQUE(org_id, template_key)
(key is reused, not freed — store() revives soft-deleted rows; a generated
active-only column is rejected by MySQL/MariaDB err 1901 anyway).
- Tiger_Model_EmailTemplate: resolve (org wins over global), byKey, store
(upsert-with-revive), reset (soft-delete → file default renders again).
- Tiger_Mail: a registered-template registry (registerTemplate/
registeredTemplates), forOrg(), {{var}} substitution, template() prefers an
authored row over the .phtml and falls back fail-safe, plus renderTemplate()
for preview (incl. unsaved drafts).
Admin module (modules/email):
- /email/admin: DataTables list (Default vs Customized) + an editor with a
variable palette and a live iframe preview.
- every /api method _isAllowed-guarded; admin-gated ACL deny-by-default; full
en i18n; house primitives only (TigerModal, no browser dialogs).
- the 5 core emails (reset/otp/verify/register-verify/backup) registered with
their real variables.
Tests: unit substitution (2) + integration behavioral against real MySQL (5,
16 assertions) — migration applies, org-override-wins, reset, revive-without-
dup-key, override-vs-file-fallback, preview.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASauLLscjqdsNqBNsx2Typ
Picks up Tiger_Model_EmailTemplate (@api) and the new `email` module — the CI `check` job's build-capabilities --check gate. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ASauLLscjqdsNqBNsx2Typ
Bump Tiger_Version::VERSION to 1.39.0 and add the CHANGELOG entry for the authorable transactional email templates (TIGER-267), so the release tag v1.39.0 matches the version-check gate. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ASauLLscjqdsNqBNsx2Typ
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds a DB-override tier to
Tiger_Mail's existing file-based template engine so owners/agents can author transactional-email subject + HTML body (with{{var}}placeholders) from an admin screen — no code change, no deploy. File = base, DB row = override, last wins: the same live-override pattern asConfig/Translation, scoped byorg_id(''= global default, else per-tenant).Engine (
library/)migrations/0054—email_templatestore. PlainUNIQUE(org_id, template_key)(not active-only): an override's key is deliberately reused —store()revives soft-deleted rows — and a generated active-only column is rejected by MySQL/MariaDB (err 1901) anyway. Rationale is in the migration docblock.Tiger_Model_EmailTemplate—resolve()(org row wins over global, the config cascade),byKey(),store()(upsert-with-revive),reset()(soft-delete → the file default renders again).Tiger_Mail— a registered-template registry (registerTemplate/registeredTemplates),forOrg(),{{var}}substitution,template()now prefers an authored row over the.phtmland falls back fail-safe to the file on any error, plusrenderTemplate()for preview (including unsaved drafts).Admin module (
modules/email)/email/admin— a DataTables list (Default vs Customized) and an editor with a clickable variable palette + live iframe preview./apimethod_isAllowed-guarded; admin-gated ACL, deny-by-default; fulleni18n; house primitives only (TigerModal — no browser dialogs); no inline<script>/<style>.reset,otp,verify,register-verify,backup) are registered with their real variables so the screen isn't empty.Tests
Other locales (es/pt/hi/de/fr) are a follow-up; only
enships here.🤖 Generated with Claude Code
https://claude.ai/code/session_01ASauLLscjqdsNqBNsx2Typ