Decides which stored templates become override files, and what those files are called.
Message templates are moving from phoenix_kit_email_templates to files a
host owns in its own repository. plan/2 answers the two questions that
migration turns on — which rows carry an edit worth keeping, and what
filename preserves the behaviour that row had — as data, so
mix phoenix_kit_emails.templates.export is left with nothing to do but
write and report.
Which rows
| Row | Planned | Why |
|---|---|---|
| System template, edited | yes | the edit is the thing worth keeping |
| System template, untouched | no | byte-identical to what this package ships; core supplies it now, translated |
Operator-authored (is_system: false) | no | newsletter layouts, authored at runtime — they keep a table and an editor |
A template whose name this package does not ship counts as edited: nothing is known about what it should look like, and a file too many is recoverable where a dropped edit is not.
Which filename
The name becomes a directory and each part a file. The subtle part is the
locale: a stored field is a language map, and Template.get_translation/3
falls back through it, so one locale's content is what every recipient
without their own translation actually receives. That locale must be written
without a code in the filename, because a locale-less file is what core's
resolution falls through to. Writing it as subject.en.txt would silently
drop the operator's customization for every non-English recipient — the exact
opposite of the point of exporting it.
Summary
Functions
Whether template still matches what this package ships under its name.
The locale every recipient falls through to: "en" when the map has it —
the key Template.get_translation/3 itself defaults to — otherwise the
lowest-sorting one, so the choice is deterministic rather than whatever the
map happens to yield first.
The {path, content} pairs one template exports to.
Plans an export of templates against shipped (this package's own
defaults, as default_system_templates/0 returns them).
Writes planned {path, content} pairs, returning {path, outcome} for each.
Types
@type plan() :: %{ edited: [PhoenixKit.Modules.Emails.Template.t()], untouched: [PhoenixKit.Modules.Emails.Template.t()], authored: [PhoenixKit.Modules.Emails.Template.t()], files: [{Path.t(), String.t()}] }
What an export would do, without having done any of it.
Functions
@spec edited?(PhoenixKit.Modules.Emails.Template.t(), %{optional(String.t()) => map()}) :: boolean()
Whether template still matches what this package ships under its name.
Compares the three content fields only — a slug, a usage count or a timestamp differing does not make a template customized.
The locale every recipient falls through to: "en" when the map has it —
the key Template.get_translation/3 itself defaults to — otherwise the
lowest-sorting one, so the choice is deterministic rather than whatever the
map happens to yield first.
@spec files_for(PhoenixKit.Modules.Emails.Template.t(), Path.t()) :: [ {Path.t(), String.t()} ]
The {path, content} pairs one template exports to.
Empty and non-string values are skipped: a part a template does not supply is absent, not an empty file.
@spec plan([PhoenixKit.Modules.Emails.Template.t()], [map()], keyword()) :: plan()
Plans an export of templates against shipped (this package's own
defaults, as default_system_templates/0 returns them).
Options: :out, the target directory (default priv/phoenix_kit_templates).
@spec write_files( [{Path.t(), String.t()}], keyword() ) :: [{Path.t(), :written | :would_write | :skipped}]
Writes planned {path, content} pairs, returning {path, outcome} for each.
Outcomes are :written, :would_write (under dry_run: true) and
:skipped — a path that already exists is refused unless force: true.
That refusal is the point: a re-run, or an export onto a host that has
already hand-written an override, must never silently replace a file a human
wrote. Nothing here deletes.