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

TableRole
email_messagelog row of the sent mail
mail_log_itemone item per recipient
mail_attachmentattachments of the mail (document-centre reference)
cron_queuethe queued sending job
mail_template, mail_template_description, mail_channeltemplate 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 requiring document/document::list; preview: renders the template with the module variables (collectVars, the owner module's mailVars()). Owner modules: nav/taxaccount, finance/settlement, nav/ifa.
  • resolveChannel resolves the channel from the client.

Sending (mail/send.dispatch)

  1. Subject and body required (posted entities decoded).
  2. Recipients normalised (normalizeAddress); an invalid address is reported by name. Group recipients resolved with content filtering (resolveGroup). In paper mode no recipient is required.
  3. Mail kind: transactional or newsletter.
  4. Mode: queued by default (cancellable); immediate only with mail/send::immediate, otherwise queued with a log entry.
  5. Scheduled time: valid date, not in the past (1 minute tolerance).
  6. Generated attachments: the owner module's plans (generatePlans); if a plan was possible but none arrived, an error is returned.
  7. Attachments filtered by content scope (docIdsInScope), then dlbtmail->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's payload_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 needs canAction(send, list) and maySend(module).
  • Module-bound mail: canAction(<module>, sendmail); edit does not include it.
  • immediate and upload are separate keys; debug exists only in the catalogue and is not checked by the controller.
  • The keys are not classified in the seeds; in practice UNMAPPED.