Loading core/lib/Drupal/Core/Mailer/EventSubscriber/OriginatorSubscriber.php 0 → 100644 +151 −0 Original line number Diff line number Diff line <?php declare(strict_types=1); namespace Drupal\Core\Mailer\EventSubscriber; use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Language\LanguageManagerInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Mailer\Event\MessageEvent; use Symfony\Component\Mime\Address; use Symfony\Component\Mime\Email; /** * Message subscriber which sets the from and sender headers. */ class OriginatorSubscriber implements EventSubscriberInterface { public function __construct( protected readonly ConfigFactoryInterface $configFactory, protected readonly LanguageManagerInterface $languageManager, ) { } /** * Sets the default from header and a sender header if necessary. * * @param \Symfony\Component\Mailer\Event\MessageEvent $event * The message event. */ public function onMessage(MessageEvent $event): void { $message = $event->getMessage(); if ($message instanceof Email) { $this->setDefaultFrom($message); $this->setDefaultSender($message); $this->removeRedundantSender($message); } } /** * Sets the default from address. * * @param \Symfony\Component\Mime\Email $message * The email message. */ protected function setDefaultFrom(Email $message): void { $from = $message->getFrom(); if (count($from) === 0) { $langcode = $message->getHeaders()->get('Content-Language')?->getBodyAsString(); $siteAddress = $this->getSiteAddress($langcode); $message->from($siteAddress); } } /** * Sets the default sender address. * * @param \Symfony\Component\Mime\Email $message * The email message. */ protected function setDefaultSender(Email $message): void { if (!$message->getSender()) { $langcode = $message->getHeaders()->get('Content-Language')?->getBodyAsString(); $siteAddress = $this->getSiteAddress($langcode); $message->sender($siteAddress); } } /** * Removes the Sender address if it is redundant. * * Rules according to RFC 5322 section 3.6.2 (Originator Fields): * * If the from field contains more than one mailbox specification in the * mailbox-list, then the sender field, containing the field name * "Sender" and a single mailbox specification, MUST appear in the * message. * * The "Sender:" field specifies the mailbox of the agent responsible * for the actual transmission of the message. * * If the originator of the message can be indicated by a single mailbox * and the author and transmitter are identical, the "Sender:" field * SHOULD NOT be used. * * @param \Symfony\Component\Mime\Email $message * The email message. * * @see https://www.rfc-editor.org/rfc/rfc5322.html#section-3.6.2 */ protected function removeRedundantSender(Email $message): void { $from = $message->getFrom(); $sender = $message->getSender(); $senderRedundant = count($from) === 1 && $sender !== NULL && $from[0]->getAddress() === $sender->getAddress(); if ($senderRedundant) { $message->getHeaders()->remove('Sender'); } } /** * Returns the site email address. * * @param string|null $langcode * The language code from the email. */ protected function getSiteAddress(?string $langcode): Address { return $this->executeInEnvironment($langcode, function () { $config = $this->configFactory->get('system.site'); return new Address($config->get('mail'), $config->get('name')); }); } /** * Invokes the given callback after switching the config language. * * @param string|null $langcode * The language code. * @param callable(): T $callback * Callback to execute inside substituted environment. * * @return T * Returns the result returned by the callback. * * @template T * * @todo Replace with execution environment once that is available. * @see: https://www.drupal.org/i/3536307 */ protected function executeInEnvironment(?string $langcode, callable $callback): mixed { $originalLanguage = $this->languageManager->getConfigOverrideLanguage(); if ($langcode && ($language = $this->languageManager->getLanguage($langcode))) { $this->languageManager->setConfigOverrideLanguage($language); } try { return $callback(); } finally { $this->languageManager->setConfigOverrideLanguage($originalLanguage); } } /** * {@inheritdoc} */ public static function getSubscribedEvents(): array { return [ // Should be the last one to allow header changes by other listeners. MessageEvent::class => ['onMessage', -255], ]; } } core/modules/mailer/mailer.services.yml +2 −0 Original line number Diff line number Diff line Loading @@ -49,3 +49,5 @@ services: Symfony\Component\Mailer\Mailer: autowire: true Symfony\Component\Mailer\MailerInterface: '@Symfony\Component\Mailer\Mailer' Drupal\Core\Mailer\EventSubscriber\OriginatorSubscriber: autowire: true core/modules/mailer/tests/modules/mailer_event_subscriber_test/mailer_event_subscriber_test.info.yml 0 → 100644 +5 −0 Original line number Diff line number Diff line name: 'Mailer event subscriber test' type: module description: 'Support module for mailer event subscriber testing.' package: Testing version: VERSION core/modules/mailer/tests/modules/mailer_event_subscriber_test/mailer_event_subscriber_test.services.yml 0 → 100644 +6 −0 Original line number Diff line number Diff line services: mailer_event_subscriber_test.message_event_test_subscriber: class: Drupal\mailer_event_subscriber_test\EventSubscriber\MessageEventTestSubscriber arguments: ['@state'] tags: - { name: 'event_subscriber' } core/modules/mailer/tests/modules/mailer_event_subscriber_test/src/EventSubscriber/MessageEventTestSubscriber.php 0 → 100644 +84 −0 Original line number Diff line number Diff line <?php declare(strict_types=1); namespace Drupal\mailer_event_subscriber_test\EventSubscriber; use Drupal\Core\State\StateInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Mailer\Event\MessageEvent; use Symfony\Component\Mime\Address; use Symfony\Component\Mime\Email; /** * A message event test subscriber. */ class MessageEventTestSubscriber implements EventSubscriberInterface { /** * Constructs a new message event test subscriber. * * @param \Drupal\Core\State\StateInterface $state * The state service. */ public function __construct( protected StateInterface $state, ) { } /** * Sets a custom from header. * * @param \Symfony\Component\Mailer\Event\MessageEvent $event * The message event. */ public function setCustomFrom(MessageEvent $event): void { $customFrom = $this->state->get('mailer_event_subscriber_test.set_custom_from'); $message = $event->getMessage(); if (!empty($customFrom) && $message instanceof Email) { $message->from(Address::create($customFrom)); } } /** * Sets a custom message sender header. * * @param \Symfony\Component\Mailer\Event\MessageEvent $event * The message event. */ public function setCustomMessageSender(MessageEvent $event): void { $customFrom = $this->state->get('mailer_event_subscriber_test.set_custom_message_sender'); $message = $event->getMessage(); if (!empty($customFrom) && $message instanceof Email) { $message->sender(Address::create($customFrom)); } } /** * Sets a custom envelope sender. * * @param \Symfony\Component\Mailer\Event\MessageEvent $event * The message event. */ public function setCustomEnvelopeSender(MessageEvent $event): void { $customEnvelopeSender = $this->state->get('mailer_event_subscriber_test.set_custom_envelope_sender'); if (!empty($customEnvelopeSender)) { $envelope = $event->getEnvelope(); $envelope->setSender(Address::create($customEnvelopeSender)); } } /** * {@inheritdoc} */ public static function getSubscribedEvents(): array { return [ MessageEvent::class => [ ['setCustomFrom', 0], ['setCustomMessageSender', 0], ['setCustomEnvelopeSender', 0], ], ]; } } Loading
core/lib/Drupal/Core/Mailer/EventSubscriber/OriginatorSubscriber.php 0 → 100644 +151 −0 Original line number Diff line number Diff line <?php declare(strict_types=1); namespace Drupal\Core\Mailer\EventSubscriber; use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Language\LanguageManagerInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Mailer\Event\MessageEvent; use Symfony\Component\Mime\Address; use Symfony\Component\Mime\Email; /** * Message subscriber which sets the from and sender headers. */ class OriginatorSubscriber implements EventSubscriberInterface { public function __construct( protected readonly ConfigFactoryInterface $configFactory, protected readonly LanguageManagerInterface $languageManager, ) { } /** * Sets the default from header and a sender header if necessary. * * @param \Symfony\Component\Mailer\Event\MessageEvent $event * The message event. */ public function onMessage(MessageEvent $event): void { $message = $event->getMessage(); if ($message instanceof Email) { $this->setDefaultFrom($message); $this->setDefaultSender($message); $this->removeRedundantSender($message); } } /** * Sets the default from address. * * @param \Symfony\Component\Mime\Email $message * The email message. */ protected function setDefaultFrom(Email $message): void { $from = $message->getFrom(); if (count($from) === 0) { $langcode = $message->getHeaders()->get('Content-Language')?->getBodyAsString(); $siteAddress = $this->getSiteAddress($langcode); $message->from($siteAddress); } } /** * Sets the default sender address. * * @param \Symfony\Component\Mime\Email $message * The email message. */ protected function setDefaultSender(Email $message): void { if (!$message->getSender()) { $langcode = $message->getHeaders()->get('Content-Language')?->getBodyAsString(); $siteAddress = $this->getSiteAddress($langcode); $message->sender($siteAddress); } } /** * Removes the Sender address if it is redundant. * * Rules according to RFC 5322 section 3.6.2 (Originator Fields): * * If the from field contains more than one mailbox specification in the * mailbox-list, then the sender field, containing the field name * "Sender" and a single mailbox specification, MUST appear in the * message. * * The "Sender:" field specifies the mailbox of the agent responsible * for the actual transmission of the message. * * If the originator of the message can be indicated by a single mailbox * and the author and transmitter are identical, the "Sender:" field * SHOULD NOT be used. * * @param \Symfony\Component\Mime\Email $message * The email message. * * @see https://www.rfc-editor.org/rfc/rfc5322.html#section-3.6.2 */ protected function removeRedundantSender(Email $message): void { $from = $message->getFrom(); $sender = $message->getSender(); $senderRedundant = count($from) === 1 && $sender !== NULL && $from[0]->getAddress() === $sender->getAddress(); if ($senderRedundant) { $message->getHeaders()->remove('Sender'); } } /** * Returns the site email address. * * @param string|null $langcode * The language code from the email. */ protected function getSiteAddress(?string $langcode): Address { return $this->executeInEnvironment($langcode, function () { $config = $this->configFactory->get('system.site'); return new Address($config->get('mail'), $config->get('name')); }); } /** * Invokes the given callback after switching the config language. * * @param string|null $langcode * The language code. * @param callable(): T $callback * Callback to execute inside substituted environment. * * @return T * Returns the result returned by the callback. * * @template T * * @todo Replace with execution environment once that is available. * @see: https://www.drupal.org/i/3536307 */ protected function executeInEnvironment(?string $langcode, callable $callback): mixed { $originalLanguage = $this->languageManager->getConfigOverrideLanguage(); if ($langcode && ($language = $this->languageManager->getLanguage($langcode))) { $this->languageManager->setConfigOverrideLanguage($language); } try { return $callback(); } finally { $this->languageManager->setConfigOverrideLanguage($originalLanguage); } } /** * {@inheritdoc} */ public static function getSubscribedEvents(): array { return [ // Should be the last one to allow header changes by other listeners. MessageEvent::class => ['onMessage', -255], ]; } }
core/modules/mailer/mailer.services.yml +2 −0 Original line number Diff line number Diff line Loading @@ -49,3 +49,5 @@ services: Symfony\Component\Mailer\Mailer: autowire: true Symfony\Component\Mailer\MailerInterface: '@Symfony\Component\Mailer\Mailer' Drupal\Core\Mailer\EventSubscriber\OriginatorSubscriber: autowire: true
core/modules/mailer/tests/modules/mailer_event_subscriber_test/mailer_event_subscriber_test.info.yml 0 → 100644 +5 −0 Original line number Diff line number Diff line name: 'Mailer event subscriber test' type: module description: 'Support module for mailer event subscriber testing.' package: Testing version: VERSION
core/modules/mailer/tests/modules/mailer_event_subscriber_test/mailer_event_subscriber_test.services.yml 0 → 100644 +6 −0 Original line number Diff line number Diff line services: mailer_event_subscriber_test.message_event_test_subscriber: class: Drupal\mailer_event_subscriber_test\EventSubscriber\MessageEventTestSubscriber arguments: ['@state'] tags: - { name: 'event_subscriber' }
core/modules/mailer/tests/modules/mailer_event_subscriber_test/src/EventSubscriber/MessageEventTestSubscriber.php 0 → 100644 +84 −0 Original line number Diff line number Diff line <?php declare(strict_types=1); namespace Drupal\mailer_event_subscriber_test\EventSubscriber; use Drupal\Core\State\StateInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Mailer\Event\MessageEvent; use Symfony\Component\Mime\Address; use Symfony\Component\Mime\Email; /** * A message event test subscriber. */ class MessageEventTestSubscriber implements EventSubscriberInterface { /** * Constructs a new message event test subscriber. * * @param \Drupal\Core\State\StateInterface $state * The state service. */ public function __construct( protected StateInterface $state, ) { } /** * Sets a custom from header. * * @param \Symfony\Component\Mailer\Event\MessageEvent $event * The message event. */ public function setCustomFrom(MessageEvent $event): void { $customFrom = $this->state->get('mailer_event_subscriber_test.set_custom_from'); $message = $event->getMessage(); if (!empty($customFrom) && $message instanceof Email) { $message->from(Address::create($customFrom)); } } /** * Sets a custom message sender header. * * @param \Symfony\Component\Mailer\Event\MessageEvent $event * The message event. */ public function setCustomMessageSender(MessageEvent $event): void { $customFrom = $this->state->get('mailer_event_subscriber_test.set_custom_message_sender'); $message = $event->getMessage(); if (!empty($customFrom) && $message instanceof Email) { $message->sender(Address::create($customFrom)); } } /** * Sets a custom envelope sender. * * @param \Symfony\Component\Mailer\Event\MessageEvent $event * The message event. */ public function setCustomEnvelopeSender(MessageEvent $event): void { $customEnvelopeSender = $this->state->get('mailer_event_subscriber_test.set_custom_envelope_sender'); if (!empty($customEnvelopeSender)) { $envelope = $event->getEnvelope(); $envelope->setSender(Address::create($customEnvelopeSender)); } } /** * {@inheritdoc} */ public static function getSubscribedEvents(): array { return [ MessageEvent::class => [ ['setCustomFrom', 0], ['setCustomMessageSender', 0], ['setCustomEnvelopeSender', 0], ], ]; } }