# Emails

Read this document only when the prompt requires creating or modifying application emails.

## How Email Is Handled

Application emails use two layers in `src/core/modules/Mail/Mail.php`:

- `sendMailTemplate()` resolves and renders an application mail template, wraps it in the shared layout, translates its subject, and delegates delivery.
- `sendMail()` is the low-level PHPMailer transport. It receives completed HTML and automatically creates `AltBody` by removing HTML and normalizing whitespace.
- `USER_MAIL_NOTIFICATION_TYPES` and `ADMIN_MAIL_NOTIFICATION_TYPES` in `Mail.php` are the source of truth for configurable email types.
- `sendMailTemplate()` accepts an optional notification key. If omitted, it uses the final template path segment. Unknown types remain mandatory.
- `sendAdminMailTemplate()` reuses the same template and accepts an optional admin notification key, defaulting to `admin-` plus the template name.
- The admin `order-paid` email reuses `src/pages/private/cart/mails/order-paid.mail.php`; `sendAdminMailTemplate()` adds `isAdminMail` to the template parameters so admin-only order metadata does not appear in player emails.

The shared header and footer live in:

```text
src/core/modules/Mail/templates/layout.mail.php
```

Page-specific templates contain only the email content. The helper wraps that content in the shared layout before sending it.

## Template Location

Keep each template with the page responsible for the behavior:

```text
src/pages/{public|private|admin}/{page}/mails/{name}.mail.php
```

Templates are referenced by a restricted identifier rather than a filesystem path:

```text
{public|private|admin}/{page}/{name}
```

The resolver accepts only lowercase letters, numbers, and hyphens in the page and template names. It cannot load arbitrary paths or templates outside page `mails` directories.

## Parameters And Translation

Pass all template data through the `$params` argument. Its keys become local variables inside the template. Use escaped output for values that can contain account, order, or request data.

Templates receive a `$translate` callable. Use it instead of `__()` so content is translated with the recipient account's preferred web language, including callbacks and other headless requests. The subject is translated by the helper in the same language.

Pass the recipient account as the final argument to `sendMailTemplate()`. If its preferred language is unavailable, the helper falls back to the current application language.

## Creating An Email

1. Create `{name}.mail.php` under the responsible page's `mails` directory.
2. Render content only; do not add the document wrapper, logo, header, or footer.
3. Use `$translate()` for visible strings and escape dynamic values with `htmlspecialchars()`.
4. Add translation entries for new strings in the supported language files.
5. Load `src/core/modules/Mail/Mail.php` with `require_once` if the current flow has not loaded it.
6. Call `sendMailTemplate()` with the restricted identifier, recipient, untranslated subject key, params, and recipient account.
7. Handle its result consistently with the calling flow: `true` means sent; any other value is an error message or failure.
8. For retried events such as provider callbacks, send only on the first successful state transition to avoid duplicate email.

Example:

```php
require_once CORE.'modules/Mail/Mail.php';

$successOrMessage = sendMailTemplate(
  'private/example/status-changed',
  $account['Email'],
  $account['Username'],
  'Status changed',
  [
    'account' => $account,
    'status' => $status,
  ],
  $account
);
```

Corresponding template:

```php
<p><?php echo htmlspecialchars($translate('Hello,').' '.$account['Username'], ENT_QUOTES, 'UTF-8'); ?></p>
<p><?php echo htmlspecialchars($translate('Your status is:').' '.$status, ENT_QUOTES, 'UTF-8'); ?></p>
```

## Operational Rules

- Email delivery is synchronous; there is no queue or retry mechanism.
- A delivery failure must not reverse an already completed domain operation such as a payment.
- Do not place shared header or footer markup in page templates.
- Do not pass user-controlled template identifiers.
- Use `sendMail()` directly only when the caller intentionally supplies complete HTML, such as an administrative custom-email tool.
- Notification preferences are stored in CMS table `cms_notifications` and missing rows mean enabled.
- The authenticated `notifications` page shows user and admin types in separate blocks; only GM accounts can see the admin block.
- MFA confirmation messages live under `src/pages/private/mfa/mails/` and are sent after successful enable/remove operations. They use the recipient account as the language context and must not be sent for failed or invalid OTP attempts.
- Admin order-paid metadata comes from `cms_orders` plus the matching `cms_payments` row. `StoreModel::getTransactionByTrxIDByTrxID($order['txn_id'])` supplies the payment method, true `net_money`, payer email, and transaction data.
- Admin order links use `orders-management?OrderID={orderId}&Username={accountName}`. The route accepts `OrderID` and `Username` query parameters.
- For email investigations, skip bundled PHPMailer files, theme templates, unrelated page mail templates, and provider internals unless the requested email or provider behavior names them.
