{site} and {site_name} everywhere; a state mail adds {self_link} (the
submitter's own page, when their record's state allows a move there),
{desk_link} and {link}, which picks by who the mail is to. gullberg's
{link} mails went out blank until now. Documented in docs/mail.md.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MznPTC31w5mSUK4XMehcpi
2.9 KiB
Portal writes every mail itself and hands it to gdo to deliver
(portal.mail.send); a reply comes back as a note beside the record
(portal.mail.received). A mail is a subject and a markdown body with
{placeholders}, sent as text with an HTML alternative. The kinds of
mail are the ones content declares on a state (see
State machines), the invite and its
rewording (invite: { group, mail }, mail.invite in site.yaml), the
note to a person named responsible for a question, and the answers to
a suggested change.
Variables
A placeholder is { letters, digits, _, - or . }. One that
names nothing renders as nothing; a brace that opens no name stays a
brace. The variables come in layers, so a template written for one
kind of mail reads the same in another.
On every mail
{site} |
the instance's public URL, no trailing slash |
{site_name} |
site.yaml's title |
On a mail about a record (every state mail)
{field} |
any field of the record, as the form sent it |
{chain} |
the record's id |
{email} |
the record's email field, trimmed |
{bucket}, {state} |
where it is and the state it just entered |
{notice.field} |
a field of the record this one belongs to, through its type: record field notice |
{self_link} |
the record's own page for the person it belongs to: a page offering a self-service move (self_transition) on the bucket from the state the record is in, with the ?chain=&email= that lets them act without signing in. Empty when there is no such page or no address |
{desk_link} |
the desk that moves the record: the page whose list over the bucket has transitions, else one that reads it. Empty when no page reads the bucket |
{link} |
where this reader acts: {self_link} in a mail to: submitter, {desk_link} in one to anyone else, and {site} when that link is empty |
{link} is the one to reach for. "Se meldingen og avtal befaring:
{link}" sent to the caretaker opens the desk the report is on; "Feilen
du meldte om er utbedret. {link}" sent to the tenant opens their own
case where they can still act on it, or the site when there is
nothing left for them to do.
On an invite and the note to someone responsible, beside every
field of the invite's form: {name}, {username}, {email},
{group} (invite), {questions} (responsible), {expires}, and
{link}, which here is the one-time sign-in link.
What the lint holds a mail to
A state mail that sends from a state no record reaches warns; one that
names a field no form recording into the bucket collects fails, since
it would go out with a blank where the field should be. The lint
knows every variable above, so {link} and the rest are never
flagged. to: submitter requires a required email on every form
filling the bucket, and to: "{notice.email}" requires the notice
field.