Envoyer les courriels transactionnels liés au compte utilisateur au format HTML avec Drupal 8

Une lettre

Nous disposons de plusieurs solutions pour envoyer automatiquement les mails émis par un projet Drupal 8 au format HTML. Sans pouvoir tous les citer, nous pouvons utiliser SwiftMailer, MimeMail pour envoyer des mails depuis le serveur lui-même,  ou encore Mailjet API, MailGun, etc. pour envoyer les emails depuis une plateforme tierce. En quelques clics, nous pouvons alors émettre les différents mails, qu'ils soient transactionnels (création de compte, création d'une commande, abonnement, etc.) ou métier (Newsletters, Journal d'activité, Ce que vous avez manqué, etc.), au format HTML. Il restera alors à implémenter un (ou plusieurs) template mail responsive qui sera correctement lu et interprété  sur la plupart des logiciels de messagerie. Et c'est sans doute la partie la plus conséquente.

Mais certaines de ces solutions peuvent postuler que le contenu de ces emails doit être considéré comme sain et sécurisé. Et pour ce faire ces solutions vérifient que le contenu des emails correspond à un markup vérifié, filtré et expurgée d'éventuelles injections XSS ou autres joyeusetés. Et dans le cas contraire, elles se chargent alors d'appliquer un filtre transformant alors toute balise HTML en texte, rendant inopérant votre formatage HTML. Ce qui est le cas notamment de SwifMailer.

Diantre.

Par exemple, il n'est pas rare de trouver ce petit traitement sur le contenu d'un email, avant tout formatage ou rendu.

/**
 * Massages the message body into the format expected for rendering.
 *
 * @param array $message
 *   The message.
 *
 * @return array
 *   The message array with the body formatted.
 */
public function massageMessageBody(array $message) {
  // Get default mail line endings and merge all lines in the e-mail body
  // separated by the mail line endings. Keep Markup objects and escape others
  // and then treat the result as safe markup.
  $line_endings = Settings::get('mail_line_endings', PHP_EOL);
  $message['body'] = Markup::create(implode($line_endings, array_map(function ($body) {
    // If $item is not marked safe then it will be escaped.
    return $body instanceof MarkupInterface ? $body : Html::escape($body);
  }, $message['body'])));
  return $message;
}

Tout simplement, si le contenu de l'email n'est pas une instance de Markup (et donc filtré et expurgé de chaines de caractères potentiellement dangereuses), alors on échappe toutes les balises HTML.

Heureusement, la plupart des contenus qui sont expédiés par email sont en général issues du contenu du site lui-même, contenu qui sera donc passé par les fourches caudines et intraitables de Drupal 8, et sera alors présenté au système de formatage des emails comme étant du markup vérifié et donc sain.

Il reste un cas un peu particulier : celui de tous les emails émis en fonction des différents événements liés à la vie d'un compte utilisateur (Création, Attente d'approbation, Suppression, Mail de bienvenue, etc.). Tous ces petits mails que nous pouvons configurer depuis l'interface de gestion des comptes de Drupal 8.

Gestion des emails utilisateur

Et parce que ces différents messages sont liés à la configuration du site Drupal 8, ils sont donc enregistrés comme une simple chaine de caractères.

Et quand ils se présentent au système de formatage d'envoi des emails, le contenu étant une simple chaine de caractères, donc non vérifiée, le formatage applique sa mesure de sécurité et échappe alors toutes les balises HTML que vous auriez pu utilisées. Alors faut-il devoir recourir ici à un expert Drupal 8 pour résoudre cet épineux problème ?

Heureusement nous avons une solution tout simple. Avant que la chaîne de caractères soit transmise au Plugin chargé du formatage du courriel (cf. L'envoi de mails sous toutes les coutures avec Drupal 8), il nous suffit d'intervenir en amont, pour transformer ces simple chaînes de caractères en instance de Markup.

Au moyen d'un petit module, implémentons hook_mail_alter() :

/**
 * Implements hook_mail_alter().
 */
function MYMODULE_mail_alter(&$message) {
  switch ($message['key']) {
    case 'page_mail':
    case 'page_copy':
    case 'cancel_confirm':
    case 'password_reset':
    case 'register_admin_created':
    case 'register_no_approval_required':
    case 'register_pending_approval':
    case 'register_pending_approval_admin':
    case 'status_activated':
    case 'status_blocked':
    case 'status_canceled':
      $message['headers']['Content-Type'] = 'text/html; charset=UTF-8; format=flowed; delsp=yes';
      foreach ($message['body'] as $key => $body) {
        $message['body'][$key] = new FormattableMarkup($body, []);
      }
      break;

  }
}

Et pour chacun des mails transactionnels liés à la vie d'un compte utilisateur, nous transformons les chaînes de caractères de leur contenu en une instance de FormattableMarkup. Notons au passage que nous pouvons aussi modifier l'en-tête du mail pour spécifier directement que le mail sera au format HTML, rendant inutile l'usage d'un module rien que pour cela. Mais il assez rare sur un projet Drupal 8 de n'avoir qu'à traiter ce type de mail.

Une solution toute simple, mais bien efficace, pour envoyer les mails de base d'un site Drupal 8 dans un format acceptable. Un petit hook que tout développeur Drupal 8 peut mettre dans son package de base. 

 

Ajouter un commentaire