Commit ab958d0a authored by Adam Shepherd's avatar Adam Shepherd
Browse files

Issue #3262946 by AdamPS, ekes: Add support for more headers on legacy emails

parent b6760827
Loading
Loading
Loading
Loading
+126 −15
Changes for modules/symfony_mailer_bc/src/MailManagerReplacement.php: 126 added lines, 15 removed lines.
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@

namespace Drupal\symfony_mailer_bc;

use Drupal\Component\Render\MarkupInterface;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
@@ -10,6 +11,7 @@ use Drupal\Core\Mail\MailManager;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\StringTranslation\TranslationInterface;
use Drupal\symfony_mailer\EmailFactory;
use Drupal\symfony_mailer\EmailInterface;
use Drupal\symfony_mailer\MailerHelperInterface;

/**
@@ -17,6 +19,19 @@ use Drupal\symfony_mailer\MailerHelperInterface;
 */
class MailManagerReplacement extends MailManager {

  /**
   * List of headers for conversion to/from array.
   *
   * @var array
   */
  protected const HEADERS = [
    'From' => 'from',
    'Reply-To' => 'reply-to',
    'To' => 'to',
    'Cc' => 'cc',
    'Bcc' => 'bcc',
  ];

  /**
   * The email factory.
   *
@@ -64,29 +79,125 @@ class MailManagerReplacement extends MailManager {
   * {@inheritdoc}
   */
  public function mail($module, $key, $to, $langcode, $params = [], $reply = NULL, $send = TRUE) {
    // Call alter hook.
    $context = ['module' => $module, 'to' => $to, 'reply' => $reply, 'entity' => NULL];
    $this->moduleHandler->alter(['mailer_bc', "mailer_bc_$module"], $key, $params, $context);
    $message = [
      'module' => $module,
      'key' => $key,
      'to' => $to,
      'langcode' => $langcode,
      'params' => $params,
      'reply-to' => $reply,
      'send' => $send,
      'subject' => '',
      'body' => [],
    ];
    $entity = NULL;

    // Call alter hooks.
    $this->moduleHandler->alter(['mailer_bc', "mailer_bc_$module"], $message, $entity);

    if ($entity = $context['entity']) {
      $email = $this->emailFactory->newEntityEmail($entity, $key);
    if ($entity) {
      $email = $this->emailFactory->newEntityEmail($entity, $message['key']);
    }
    else {
      $email = $this->emailFactory->newModuleEmail($module, $key);
      $email = $this->emailFactory->newModuleEmail($message['module'], $message['key']);
    }

    $email->setTo(...$this->mailerHelper->parseAddress($context['to']))
      ->setLangcode($langcode)
      ->setParams($params);
    if ($context['reply']) {
      $email->setReplyTo(...$this->mailerHelper->parseAddress($context['reply']));
    $this->emailFromArray($email, $message);
    if ($message['send']) {
      $result = $email->send();
    }

    if ($send) {
      $result = $email->send();
    $message = $this->emailToArray($email);
    return $message + ['result' => $result ?? NULL, 'send' => $message['send']];
  }

  /**
   * Fills an Email from a message array.
   *
   * @param \Drupal\symfony_mailer\EmailInterface $email
   *   The email to fill.
   * @param array $message
   *   The array to fill from.
   * @param array $original
   *   (Optional) The original message array.
   */
  public function emailFromArray(EmailInterface $email, array $message, array $original = []) {
    $email->setLangcode($message['langcode'])
      ->setParams($message['params'])
      ->setSubject($message['subject']);

    // Address headers.
    $headers = $email->getHeaders();
    foreach (self::HEADERS as $name => $key) {
      // If the header hasn't change then no need to parse it.
      $encoded = $message['headers'][$name] ?? $message[$key] ?? NULL;
      $encoded_original = $original['headers'][$name] ?? $original[$key] ?? NULL;
      if (isset($encoded) && ($encoded != $encoded_original)) {
        $addresses = $this->mailerHelper->parseAddress($encoded);
        if ($header = $headers->get($name)) {
          $header->setAddresses($addresses);
        }
        else {
          $headers->addMailboxListHeader($name, $addresses);
        }
      }
    }

    // Body.
    if (is_array($message['body'])) {
      foreach ($message['body'] as $part) {
        if ($part instanceof MarkupInterface) {
          $content = ['#markup' => $part];
        }
        else {
          $content = [
            '#type' => 'processed_text',
            '#text' => $part,
          ];
        }
        $email->appendBody($content);
      }
    }
    else {
      $email->setHtmlBody($message['body']);
    }
  }

  /**
   * Gets a message array for an Email.
   *
   * @param \Drupal\symfony_mailer\EmailInterface $email
   *   The email to convert.
   *
   * @return array
   *   Message array.
   */
  public function emailToArray(EmailInterface $email) {
    $module = $email->getType();
    $key = $email->getSubType();
    $message = [
      'id' => $module . '_' . $key,
      'module' => $module,
      'key' => $key,
      'langcode' => $email->getLangcode(),
      'params' => $email->getParams(),
      'send' => TRUE,
      'subject' => $email->getSubject(),
      'body' => $email->isRendered() ? $email->getHtmlBody() : $email->getBody(),
    ];

    // Address headers.
    $headers = $email->getHeaders();
    foreach (self::HEADERS as $name => $key) {
      if ($headers->has($name)) {
        $message['headers'][$name] = $headers->get($name)->getBodyAsString();
      }
      if ($key) {
        $message[$key] = $message['headers'][$name] ?? NULL;
      }
    // Set the 'send' element for Webform module.
    return ['result' => $result, 'send' => $send];
    }

    return $message;
  }

}
+42 −43
Changes for modules/symfony_mailer_bc/src/Plugin/EmailBuilder/LegacyEmailBuilder.php: 42 added lines, 43 removed lines.
Original line number Diff line number Diff line
@@ -2,69 +2,68 @@

namespace Drupal\symfony_mailer_bc\Plugin\EmailBuilder;

use Drupal\Component\Render\MarkupInterface;
use Drupal\Core\Mail\MailManagerInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\symfony_mailer\Processor\EmailProcessorBase;
use Drupal\symfony_mailer\EmailInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Defines the Legacy Email Builder plug-in that calls hook_mail().
 */
class LegacyEmailBuilder extends EmailProcessorBase {
class LegacyEmailBuilder extends EmailProcessorBase implements ContainerFactoryPluginInterface {

  /**
   * {@inheritdoc}
   * Mail manager service.
   *
   * @var \Drupal\Core\Mail\MailManagerInterface
   */
  public function preRender(EmailInterface $email) {
    $message = $this->getMessage($email);
    $email->setSubject($message['subject']);
  protected $mailManager;

    foreach ($message['body'] as $part) {
      if ($part instanceof MarkupInterface) {
        $content = ['#markup' => $part];
      }
      else {
        $content = [
          '#type' => 'processed_text',
          '#text' => $part,
        ];
  /**
   * @param array $configuration
   *   A configuration array containing information about the plugin instance.
   * @param string $plugin_id
   *   The plugin ID for the plugin instance.
   * @param mixed $plugin_definition
   *   The plugin implementation definition.
   * @param \Drupal\Core\Mail\MailManagerInterface $mail_manager
   *   Mail manager service.
 */
  public function __construct(array $configuration, $plugin_id, $plugin_definition, MailManagerInterface $mail_manager) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->mailManager = $mail_manager;
  }

      $email->appendBody($content);
    }
  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->get('plugin.manager.mail')
    );
  }

  /**
   * Gets a message array by calling hook_mail().
   *
   * @param \Drupal\symfony_mailer\EmailInterface $email
   *   The email to build.
   *
   * @return array
   *   Message array.
   * {@inheritdoc}
   */
  protected function getMessage(EmailInterface $email) {
    $module = $email->getType();
    $key = $email->getSubType();
    $message = [
      'id' => $module . '_' . $key,
      'module' => $module,
      'key' => $key,
      'to' => $email->getTo()[0],
      'reply-to' => $email->getReplyTo()[0] ?? NULL,
      'langcode' => $email->getLangcode(),
      'params' => $email->getParams(),
      'send' => TRUE,
      'subject' => '',
      'body' => [],
      'headers' => [],
    ];
  public function preBuild(EmailInterface $email) {
    $message = $this->mailManager->emailToArray($email);
    $original = $message;

    // Call hook_mail() on this module.
    if (function_exists($function = $module . '_mail')) {
      $function($key, $message, $email->getParams());
    if (function_exists($function = $email->getType() . '_mail')) {
      $function($email->getSubType(), $message, $email->getParams());
    }

    if (isset($message['send']) && !$message['send']) {
      throw new SkipMailException('Send aborted by hook_mail()');
    }

    return $message;
    $this->mailManager->emailFromArray($email, $message, $original);
  }

}
+10 −22
Changes for modules/symfony_mailer_bc/symfony_mailer_bc.api.php: 10 added lines, 22 removed lines.
Original line number Diff line number Diff line
@@ -11,36 +11,24 @@
 * The parameters supplied are from the old mail manager interface. The altered
 * values will be passed to the email factory.
 *
 * @param string $key
 *   The email sub-type, known as 'key' on the old interface.
 * @param array $params
 *   The email parameters.
 * @param array $context
 *   Array with entries:
 *   - module: The email type, known as 'module' on the old interface.
 *   - to: The email 'to' address.
 *   - reply: The email 'reply-to' address.
 *   - entity: The associated config entity, always null on the old interface.
 * @param array $message
 *   A message array, as described in hook_mail_alter().
 * @param ?\Drupal\Core\Config\Entity\ConfigEntityInterface $entity
 *   Entity. @see \Drupal\symfony_mailer\EmailInterface::getEntity()
 *
 * @see \Drupal\Core\Mail\MailManagerInterface::mail()
 * @see \Drupal\symfony_mailer\EmailFactory
 */
function hook_mailer_bc_alter(string &$key, array &$params, array &$context) {
function hook_mailer_bc_alter(array &$message, ?ConfigEntityInterface $entity) {
}

/**
 * Alters back-compatibility creation of an email from a specific module.
 *
 * @param string $key
 *   The email sub-type, known as 'key' on the old interface.
 * @param array $params
 *   The email parameters.
 * @param array $context
 *   Array with entries:
 *   - module: The email type, known as 'module' on the old interface.
 *   - to: The email 'to' address.
 *   - reply: The email 'reply-to' address.
 *   - entity: The associated config entity, always null on the old interface.
 * @param array $message
 *   A message array, as described in hook_mail_alter().
 * @param ?\Drupal\Core\Config\Entity\ConfigEntityInterface $entity
 *   Entity. @see \Drupal\symfony_mailer\EmailInterface::getEntity()
 */
function hook_mailer_bc_MODULE_alter(string &$key, array &$params, array &$context) {
function hook_mailer_bc_MODULE_alter(array &$message, ?ConfigEntityInterface $entity) {
}
+18 −19
Changes for modules/symfony_mailer_bc/symfony_mailer_bc.module: 18 added lines, 19 removed lines.
Original line number Diff line number Diff line
@@ -91,60 +91,59 @@ function symfony_mailer_bc_form_alter(&$form, FormStateInterface $form_state, $f
/**
 * Implements hook_mailer_bc_MODULE_alter() for contact.
 */
function symfony_mailer_bc_mailer_bc_contact_alter(string &$key, array &$params, array &$context) {
  if (isset($params['contact_form'])) {
function symfony_mailer_bc_mailer_bc_contact_alter(array &$message, ?ConfigEntityInterface &$entity) {
  if (isset($message['params']['contact_form'])) {
    // Set the associated config entity.
    $context['entity'] = $params['contact_form'];
    $entity = $message['params']['contact_form'];
  }

  // This creates two separate email types, so no longer need the key to
  // contain page_ or user_.
  $key = substr($key, 5);
  $message['key'] = substr($message['key'], 5);
}

/**
 * Implements hook_mailer_bc_MODULE_alter() for simplenews.
 */
function symfony_mailer_bc_mailer_bc_simplenews_alter(string &$key, array &$params, array &$context) {
  if ($key == 'node' || $key == 'test') {
function symfony_mailer_bc_mailer_bc_simplenews_alter(array &$message, ?ConfigEntityInterface &$entity) {
  if ($message['key'] == 'node' || $message['key'] == 'test') {
    // Set the associated config entity.
    $context['entity'] = $params['simplenews_mail']->getNewsletter();
    $entity = $message['params']['simplenews_mail']->getNewsletter();

    // Remove the reply-to address because we use policy from this module
    // instead.
    $context['reply'] = '';
    $message['reply'] = '';

    // Use a parameter for 'test' rather than the key. We don't want test
    // email to use a different policy, template, and so on.
    $params['test'] = ($key == 'test');
    $key = 'node';

    $message['params']['test'] = ($message['key'] == 'test');
    $message['key'] = 'node';
  }
  elseif ($key == 'subscribe_combined') {
    $key = 'subscribe';
  elseif ($message['key'] == 'subscribe_combined') {
    $message['key'] = 'subscribe';
  }
}

/**
 * Implements hook_mailer_bc_MODULE_alter() for system.
 */
function symfony_mailer_bc_mailer_bc_system_alter(string &$key, array &$params, array &$context) {
function symfony_mailer_bc_mailer_bc_system_alter(array &$message, ?ConfigEntityInterface &$entity) {
  // Simplify the params.
  $params = $params['context'];
  $message['params'] = $message['params']['context'];
}

/**
 * Implements hook_mailer_bc_MODULE_alter() for user.
 */
function symfony_mailer_bc_mailer_bc_user_alter(string &$key, array &$params, array &$context) {
function symfony_mailer_bc_mailer_bc_user_alter(array &$message, ?ConfigEntityInterface &$entity) {
  // Use the entity type as the parameter key.
  // @see \Drupal\symfony_mailer\EmailInterface::setParam()
  $params = ['user' => $params['account']];
  $message['params'] = ['user' => $message['params']['account']];
}

/**
 * Implements hook_mailer_bc_MODULE_alter() for user_registrationpassword.
 */
function symfony_mailer_bc_mailer_bc_user_registrationpassword_alter(string &$key, array &$params, array &$context) {
  symfony_mailer_bc_mailer_bc_user_alter($key, $params, $context);
function symfony_mailer_bc_mailer_bc_user_registrationpassword_alter(array &$message, ?ConfigEntityInterface &$entity) {
  symfony_mailer_bc_mailer_bc_user_alter($message, $entity);
}
+1 −1
Changes for src/BaseEmailTrait.php: 1 added line, 1 removed line.
Original line number Diff line number Diff line
@@ -140,7 +140,7 @@ trait BaseEmailTrait {
  }

  public function getHtmlBody(): ?string {
    $this->valid('postRender');
    $this->valid('postRender', 'postSend');
    return $this->inner->getHtmlBody();
  }

Loading