diff --git a/admin_manual/configuration_server/email_configuration.rst b/admin_manual/configuration_server/email_configuration.rst index ad3a7bd426d..91c216aa0ba 100644 --- a/admin_manual/configuration_server/email_configuration.rst +++ b/admin_manual/configuration_server/email_configuration.rst @@ -110,6 +110,13 @@ settings and look the same in all the different email clients out there. Modifying the look of emails beyond the theming app capabilities """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.. warning:: Nextcloud 36 redesigned the email template: every email is now a single card, + with dark mode and right-to-left support. A custom class overriding the HTML fragments of the + previous design keeps working, but its parts will look out of place next to the new ones. + Review it against the new ``EMailTemplate`` before upgrading. Calendar emails also no longer + pass the ``meeting_*_html`` values to the template, they pass ``meeting_*_previous`` for + changed values instead. + You can overwrite templates by writing a class that implements the template interface (or extends it to not need to copy over everything). Easiest way is to then put this class into an app and load it so you do not need to patch it on every update. diff --git a/developer_manual/digging_deeper/email.rst b/developer_manual/digging_deeper/email.rst index be39fd13cde..e422b4e075e 100644 --- a/developer_manual/digging_deeper/email.rst +++ b/developer_manual/digging_deeper/email.rst @@ -37,6 +37,118 @@ The mailer is hidden behind the ``\OCP\Mail\IMailer`` interface that can be :ref } } +Email templates +--------------- + +Most emails should not build their own HTML. ``IMailer::createEMailTemplate()`` returns an +``\OCP\Mail\IEMailTemplate`` that renders a themed HTML email and a matching plain text version, +with dark mode support for the mail clients that have it. Attach it to a message with +``IMessage::useTemplate()``: + +.. code-block:: php + + $template = $this->mailer->createEMailTemplate('myapp.ProjectShared', [ + 'project' => $projectName, + ]); + $template->setLanguage($l->getLanguageCode()); + $template->setSubject($l->t('%s shared a project with you', [$sharerName])); + $template->addHeader(); + $template->addHeading($l->t('%s shared a project with you', [$sharerName])); + $template->addBodyText($l->t('You can now see and edit the project.')); + $template->addBodyButton($l->t('Open project'), $projectUrl); + $template->addFooter(); + + $message = $this->mailer->createMessage(); + $message->setTo([$recipientEmail]); + $message->useTemplate($template); + $this->mailer->send($message); + +The first argument of ``createEMailTemplate()`` identifies the email (``.``), and +the second one passes data to custom templates (see `Modifying the look of emails +`_ +in the administration manual). + +Write the email in the recipient's language: get an ``IL10N`` for their language from +``\OCP\L10N\IFactory`` and pass its code to ``setLanguage()``. The template then sets the right +``lang`` and ``dir`` attributes, mirrors the layout for right-to-left languages like Arabic or +Hebrew, and writes the default footer in that language. Call it before ``addFooter()``. + +Building blocks +^^^^^^^^^^^^^^^ + +Since Nextcloud 36, the template has blocks to show who an email is about, a highlighted note, a +card with details, and a row of buttons. Unlike ``addBodyText()`` with a separate plain text, these +blocks never take HTML: every value is escaped by the template, URLs included, and each block +writes its own plain text version. + +.. figure:: ../images/email-template-blocks.png + :alt: An email with a sender, a heading, a details card with a calendar badge, a description note, a warning note and three buttons + :width: 500px + + A sender, a details card, two notes and a row of buttons. + +``addBodySender()`` + The person the email is sent for (sharer, organizer...), with an initials circle, their name + and an optional second line such as their email address. Add it before the heading. + +``addBodyNote()`` + A highlighted box. Use ``IEMailTemplate::NOTE_NEUTRAL`` (the default) for text written by a + user, like a share note or an event description, with the label shown above it. Use + ``NOTE_INFO``, ``NOTE_WARNING`` or ``NOTE_ERROR`` for messages from the server, with the label + as a bold title. Line breaks are kept. + +``addBodyDetails()`` + A card describing an item: title, subtitle, an initials circle or a calendar badge, and + labelled rows. See below. + +``addBodyButtons()`` + Any number of buttons, the first one is the primary action, with an optional question above + them like "Will you attend?". + +The details card is built with ``\OCP\Mail\EMailDetails``. Each row holds one or more parts, each +rendered on its own line: plain text, a link, or muted secondary text. + +.. code-block:: php + + use OCP\Mail\EMailDetails; + use OCP\Mail\IEMailTemplate; + + $template->addHeader(); + $template->addBodySender($organizerName, $organizerEmail); + $template->addHeading($l->t('%s invited you to an event', [$organizerName])); + + $details = (new EMailDetails($eventTitle)) + ->setSubtitle($l->l('date', $start, ['width' => 'full'])) + ->setDateBadge($l->l('date', $start, ['width' => '~MMM']), $l->l('date', $start, ['width' => '~d'])); + $details->addRow($l->t('Where')) + ->text($location) + ->link($l->t('Join in Nextcloud Talk'), $talkUrl); + $details->addRow($l->t('Attendees')) + ->text($firstAttendee) + ->muted($l->n('and %n other', 'and %n others', $remaining)); + $template->addBodyDetails($details); + + $template->addBodyNote($description, $l->t('Description')); + $template->addBodyButtons([ + ['text' => $l->t('Accept'), 'url' => $acceptUrl], + ['text' => $l->t('Decline'), 'url' => $declineUrl], + ], $l->t('Will you attend?')); + $template->addFooter(); + +Use ``setInitials($name)`` instead of ``setDateBadge()`` to show an initials circle, for example +for a conversation or a team. The badge strings are passed as is, so localize them yourself. + +A short note about a shared item goes before its details card. Longer content, like an event +description, goes after it. + +Footer +^^^^^^ + +``addFooter()`` without a text adds the default footer: the instance name and slogan, then +"This is an automatically sent email, please do not reply.". When the email has a sender block, +the second line becomes "This email was sent from on behalf of ." instead. +Pass a text to ``addFooter()`` to replace the default footer entirely. + Inline attachments ------------------ diff --git a/developer_manual/images/email-template-blocks.png b/developer_manual/images/email-template-blocks.png new file mode 100644 index 00000000000..f74ae6c396f Binary files /dev/null and b/developer_manual/images/email-template-blocks.png differ diff --git a/developer_manual/release_notes/new.rst b/developer_manual/release_notes/new.rst index e4fbbcf6f09..27ff4b43c06 100644 --- a/developer_manual/release_notes/new.rst +++ b/developer_manual/release_notes/new.rst @@ -11,6 +11,20 @@ This pages covers new features of the platform. Every feature should just have a brief description. Details have to be documented on a dedicated, persistent page. After branch-off the contents below will be cleared. +Email template blocks +--------------------- + +The email template (``\OCP\Mail\IEMailTemplate``) was redesigned with dark mode and +right-to-left support, and has new building blocks so emails no longer need hand-built HTML: + +- ``addBodySender()`` shows who the email is about, with an initials circle. +- ``addBodyNote()`` shows a highlighted note from a user, or an info, warning or error message. +- ``addBodyDetails()`` shows a card with a title and labelled rows, built with ``\OCP\Mail\EMailDetails``. +- ``addBodyButtons()`` shows any number of buttons. +- ``setLanguage()`` sets the language of the email, which also mirrors the layout for right-to-left languages. + +All values are escaped by the template. See :ref:`email` for details. + New ``\OCP\Files\IUserFolder`` API ----------------------------------