Sending mail – system description
The two modes of the central sending form, the validation order, queued, immediate and scheduled sending, attachment upload, background jobs and permission keys.
Overview
The sending form works in two modes: module-bound (from a row action, with module and object id) and free (Mail → Send mail). Every send goes through the central Dlbtmail::dispatch(); there is no per-module sending. Table names carry the installation's own prefix in the database.
Data model
| Table | Role |
|---|---|
email_message | log row of the sent mail |
mail_log_item | one item per recipient |
mail_attachment | attachments of the mail (document-centre reference) |
cron_queue | the queued sending job |
mail_template, mail_template_description, mail_channel | template and channel (read) |
Form and search helpers
- The template list comes from
templatesFor: in module mode only the module's templates, in free mode only free templates. The template's client scope is checked (error_template_scope). search: recipient search (users, persons, companies);docsearch: document-centre search with content filtering, also requiringdocument/document::list;preview: renders the template with the module variables (collectVars, the owner module'smailVars()). Owner modules:nav/taxaccount,finance/settlement,nav/ifa.resolveChannelresolves the channel from the client.
Sending (mail/send.dispatch)
- Subject and body required (posted entities decoded).
- Recipients normalised (
normalizeAddress); an invalid address is reported by name. Group recipients resolved with content filtering (resolveGroup). In paper mode no recipient is required. - Mail kind:
transactionalornewsletter. - Mode: queued by default (cancellable);
immediateonly withmail/send::immediate, otherwise queued with a log entry. - Scheduled time: valid date, not in the past (1 minute tolerance).
- Generated attachments: the owner module's plans (
generatePlans); if a plan was possible but none arrived, an error is returned. - Attachments filtered by content scope (
docIdsInScope), thendlbtmail->dispatch(). The library handles the gmail, ms365, smtp and mail providers, the block list and the unsubscribe link.
Attachment upload
mail/send.upload stores the file in the client's document centre through the shared Dlbtdocstore::addDocument() path with an object type. An empty POST yields a post_max_size error; a missing storage rule is reported. Permission: canAction(upload); the client must be inside the content scope (error_upload_client).
Background jobs
mail_message: one-off job created by queuing; sends from the log row'spayload_json(Dlbtmail::runQueued). A cancelled row is not sent; retries follow the channel's attempt count and retry interval.mail_sweep: global, every five minutes, self-sustaining (catalog/controller/mail/batch::processSweepForCronqueue); restarts mails with a lost queue entry or stuck ones (releaseStuck) from the log (dueQueuedIds).
Permissions
- Free mail:
maySend('')=canAction(mail/send, send); opening the form needscanAction(send, list)andmaySend(module). - Module-bound mail:
canAction(<module>, sendmail);editdoes not include it. immediateanduploadare separate keys;debugexists only in the catalogue and is not checked by the controller.- The keys are not classified in the seeds; in practice
UNMAPPED.