Email and notifications
Settings
SMTP is defined under AACP → System → System Settings → Email (permission: system.settings.manage):
| Setting | Default |
|---|---|
mail.enabled |
off (no email is sent without configuration) |
mail.smtp_host / mail.smtp_port |
empty / 587 |
mail.smtp_encryption |
tls |
mail.smtp_user / mail.smtp_password |
empty (the password is stored encrypted) |
mail.from_email / mail.from_name |
empty |
Next to the settings there is a button to send a test email.
Sending: with the queue
CpMailerService::sendHtml() puts the email into the sending queue; the HTTP request does not wait for the SMTP server. The real delivery is done in the background (the Messenger worker). The test email and worker handlers send immediately (sendNow).
$log = $mailer->sendHtml('user@example.com', 'Subject', '<p>Hello</p>', 'Hello');
Before using the service, check with canSend() whether the system is ready to send (if mail.enabled is off, configuration is missing).
Log and resending
Every outgoing email attempt is written to the cp_mail_logs table (recipient, subject, status, error). A failed email can be resent (resend) from the email log in AACP. Logs are cleaned after mail.log_retention_days (90 days).
Email templates
The text of the emails the system sends is not fixed in code; it is edited from the AACP → System → Email templates screen. Templates are:
- Separate per language: each template can have a subject, an HTML body and a plain-text counterpart for each language.
- Can be switched on and off.
- Resettable: when a template's row is deleted, the version sent again goes back to the default that comes with the code (the translation keys stay as a fallback forever).
Emails the core sends: account verification, welcome, awaiting administrator approval, approved, identity change request and its result, and two notification envelopes.
Placeholders
ICU placeholders ({name}) and [type:property] tokens ([site:name], [user:mail]) can be used in the body. Each template definition says which placeholders the sending code passes; the screen lists them.
Email HTML is not site HTML: it needs tables and inline styles, which are exactly what the rich text sanitizer removes. The ability to edit templates is already at the same level as the authority to change the SMTP server (system.settings.manage), so no sanitizing is done here.
A module adds its own templates
To introduce a module's emails to AACP, MailTemplateProviderInterface is implemented; because it is tagged, the core does not have to know the module's name:
final class ShopMailTemplates implements MailTemplateProviderInterface
{
public function mailTemplates(): array
{
return [new MailTemplateDefinition(key: 'shop.order_paid', labelKey: 'shop.mail.order_paid', /* default translation keys, placeholders */)];
}
}
Notifications
A notification is the system for telling a user about an event (a comment arrived, content was approved). The single entry point is NotificationDispatcher::dispatch():
$dispatcher->dispatch('blog.comment.reply', $recipient, ['title' => $post->getTitle()], actor: $author);
Rules:
- The in-app notification is written inside the request, because the badge needs to update immediately.
- Email is always queued; sending a notification does not wait for SMTP.
Notification types
Each type carries an event key (eventKey), a label key and an owning module. The core introduces types; modules add their own with contributions.yaml. In their account settings the user sees which types to switch on/off.
Channels
| Channel | Behaviour |
|---|---|
| In-app | Written to the user's notification box |
| Email (immediate) | From the queue when the event happens |
| Email (digest) | Collected as a daily or weekly digest |
Digest emails are produced by the NotificationDigestTask task. A new channel (e.g. SMS, Slack) can be added through the channel interface (NotificationChannelInterface); the class implementing the interface is tagged in services.yaml.
User preferences
NotificationPreferenceResolver decides which channels run from the event × user preference matrix. The user can switch notification emails on and off (notif_mail_enabled) and chooses the mode: immediate, daily or weekly.
| Setting | Default |
|---|---|
notification.enabled |
on |
notification.digest.enabled |
on |
Account area
Notification preferences are added to the account page as an account profile extension (NotificationPreferenceExtension). Other modules can also add their own sections to the account page with the same pattern (AccountProfileExtensionInterface).