Problem/Motivation

When i send mail with symfony_mailer, no mail are displayed in dashboard.
Mails are sent successfully.

thank

CommentFileSizeAuthor
#13 3267796-10.patch3.96 KBtrickfun
#7 3267796-7.patch3.91 KBmiguel.leal
Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

  • 3267796-symfony-mailer Comparechanges, plain diff MR !12
  • 2.x Comparecompare
  • 8.x-1.x Comparecompare

Comments

trickfun created an issue. See original summary.

trickfun’s picture

Hi,
no feedback ?

is project alive?

sjerdo’s picture

Status: Active » Needs work

It seems like module symfony_mailer bypasses the Drupal mail system. As a result the implemented hook `hook_mail_alter` of the Mail Safety has no effect when using the Symfony mailer..

Sub-module symfony_mailer_bc stops sending the mail if $message['send'] is set to false: https://git.drupalcode.org/project/symfony_mailer/-/blob/1.x/modules/sym...
For symfony_mailer it seems like we need to implement hook_mailer_PHASE and throw a SkipMailException or use the email_skip_sending mail adjuster like Drupal::service('plugin.manager.email_adjuster')->createInstance('email_skip_sending', $config)->init($email);

trickfun’s picture

Hi sjerdo,
any news?

the module is unusable now.
do you have a problem resolution date?

thank you in advance

miguel.leal’s picture

Using sjerdo directions I think we can use an adjuster for a temporary solution.


namespace Drupal\YOUR_MODULE\Plugin\EmailAdjuster;

use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\mail_safety\Controller\MailSafetyController;
use Drupal\symfony_mailer\Address;
use Drupal\symfony_mailer\EmailInterface;
use Drupal\symfony_mailer\Processor\EmailAdjusterBase;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Defines an email adjuster for mail safety.
 *
 * @EmailAdjuster(
 *   id = "mail_safety_adjuster",
 *   label = @Translation("Mail Safety"),
 *   description = @Translation("Adds an adjuster to allow mail safety from logging symfony mails."),
 *   weight = 100000,
 * )
 */
class MailSafetyAdjuster extends EmailAdjusterBase implements ContainerFactoryPluginInterface {

  /**
   * Mail safety configuration.
   *
   * @var \Drupal\Core\Config\ImmutableConfig
   */
  protected $mailSafetyConfig;

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): self {
    $instance = new static($configuration, $plugin_id, $plugin_definition);
    $instance->mailSafetyConfig = $container->get('config.factory')->get('mail_safety.settings');

    return $instance;
  }

  /**
   * {@inheritdoc}
   */
  public function build(EmailInterface $email) {
    if (!$this->mailSafetyConfig->get('enabled')) {
      return;
    }

    $email->setReplyTo($email->getTo());

    if ($this->mailSafetyConfig->get('send_mail_to_default_mail')) {
      $addresses = array_map(function ($address) {
        return new Address($address, $address, 'en');
      }, explode(',', $this->mailSafetyConfig->get('default_mail_address')));

      $email->setTo(Address::convert($addresses));
    }
  }

  /**
   * {@inheritdoc}
   */
  public function postRender(EmailInterface $email) {
    if (!$this->mailSafetyConfig->get('enabled')) {
      return;
    }

    if ($this->mailSafetyConfig->get('send_mail_to_dashboard')) {
      /** @var \Drupal\symfony_mailer\AddressInterface[] $to */
      $to = $email->getReplyTo();
      /** @var \Drupal\symfony_mailer\AddressInterface $from */
      $from = $email->getSender();
      /** @var \Symfony\Component\Mime\Header\Headers $headers */
      $headers = $email->getHeaders();

      MailSafetyController::insert([
        'key' => 'symfony_mailer',
        'module' => 'YOUR_MODULE',
        'to' => current($to)->getEmail(),
        'from' => $from->getEmail(),
        'langcode' => $email->getLangcode(),
        'subject' => $email->getSubject(),
        'body' => [$email->getTextBody() ?? NULL],
        'headers' => $headers->toArray(),
        'send' => (bool) $this->mailSafetyConfig->get('send_mail_to_default_mail'),
      ]);
    }
    else {
      $email->setTransportDsn('null://null');
    }
  }

}

seogow’s picture

Here is a slightly updated Miguel's snippet, which works with Symfony mailer 1.x-dev. It may worth to just include it into 'mail_safety' when reviewed?

To use, you need to:

  • Have enabled the 'symfony_mailer' module.
  • Enable the 'mail_safety' module.
  • Create a custom module 'mail_safety_adjuster' and put the following snippet into 'src/plugin/EmailAdjuster'. Do not forget to create some useful mail_safety_adjuster.info.yml and enable the module.
  • Enable newly available 'Mail Safety' policy for all at '/admin/config/system/mailer/policy'.
namespace Drupal\mail_safety_adjuster\Plugin\EmailAdjuster;

use Drupal\Core\Mail\MailFormatHelper;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\mail_safety\Controller\MailSafetyController;
use Symfony\Component\Mime\Address;
use Drupal\symfony_mailer\Annotation\EmailAdjuster;
use Drupal\symfony_mailer\EmailInterface;
use Drupal\symfony_mailer\Processor\EmailAdjusterBase;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Defines an email adjuster for mail safety.
 *
 * @EmailAdjuster(
 *   id = "mail_safety_adjuster",
 *   label = @Translation("Mail Safety"),
 *   description = @Translation("Adds an adjuster to allow mail safety logging Symfony mails."),
 *   weight = 100000,
 * )
 */
class MailSafetyAdjuster extends EmailAdjusterBase implements ContainerFactoryPluginInterface {

  /**
   * Mail safety configuration.
   *
   * @var \Drupal\Core\Config\ImmutableConfig
   */
  protected $mailSafetyConfig;

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): self {
    $instance = new static($configuration, $plugin_id, $plugin_definition);
    $instance->mailSafetyConfig = $container->get('config.factory')->get('mail_safety.settings');

    return $instance;
  }

  /**
   * {@inheritdoc}
   */
  public function postRender(EmailInterface $email) {
    if (!$this->mailSafetyConfig->get('enabled')) {
      return;
    }

    $to = current($email->getTo())->getAddress();
    if (!$this->mailSafetyConfig->get('send_mail_to_default_mail') || !$this->mailSafetyConfig->get('default_mail_address')) {
      $email->setTransportDsn('null://null');
    } else {
      $address = $this->mailSafetyConfig->get('default_mail_address');
      $email->setTo(new Address($address, $address));
    }

    if ($this->mailSafetyConfig->get('send_mail_to_dashboard')) {
      $headers = $email->getHeaders();
      MailSafetyController::insert([
        'key' => 'symfony_mailer',
        'module' => 'mail_safety_adjuster',
        'to' => $to,
        'from' => current($email->getFrom())->getAddress(),
        'langcode' => $email->getLangcode(),
        'subject' => $email->getSubject(),
        'body' => [$email->getTextBody() ?? MailFormatHelper::htmlToText($email->getHtmlBody())],
        'headers' => $headers->toArray(),
        'send' => FALSE,
      ]);

    }
  }

}
miguel.leal’s picture

StatusFileSize
new3.91 KB

seogow, i appreciate your update, but on my side (at least for 1.0 alpha), i can't interact and set a new address during the postRender, so I'm still using a build method. And also the getFrom() is always empty for me.

Anyway, not sure if this will be used like that when mail safety became ready with symfony mailer, however, this workaround can be easier to use as a patch.

In the end as already mentioned, this adjuster 'Mail Safety' can to be enabled on the symfony mailer policy for *All* (/admin/config/system/mailer).

trickfun’s picture

Patch doesn't work.
Dashboard is always empty.

Thank you

trickfun’s picture

Any news for this patch?
Thank you

trickfun’s picture

StatusFileSize
new50.82 KB

I fix the patch.
Some wrong if conditions preventing save messages into mail safety table.

trickfun’s picture

trickfun’s picture

StatusFileSize
new52.73 KB

this is the patch for 8.2 version

trickfun’s picture

StatusFileSize
new3.96 KB

Sorry, the patch is wrong.
this is the right, i hope

flyke’s picture

I can conform this works on 8.x-1.2
- Apply patch
- Clear caches (drush cr)
- Visit Settings > System > Mailer (/admin/config/system/mailer)
- Edit *All* (/admin/config/system/mailer/policy/_)
- From the dropdown, select 'Mail Safety' and add it

agogo’s picture

Thanks for the patch! Also works with 2.0. (#13)

A small note is that the patch forces all mails to not be sent when not using a/the default mail address by setting setTransportDsn as null. I had to make a copy of the patch and remove that part (+ the setReplyTo setting that is there for some reason).

geek-merlin’s picture

Thanks for the patch, it is a valuable POC. Also for #15.

The adjuster approach has the downside though, that i must add an adjuster.

What i want as a site owner is sth like a simple config override setting to set in all dev environments and when i see that setting, i have no fear that a customer gets an ouch mail (which is the promise of this module).

Maybe leveraging hook_mailer_alter helps here.

geek-merlin’s picture

Feedback from AdamPS (Maintainer of Drupal Symfony Mailer) in the related issue:

The patch on #3267796: No mail with symfony_mailer seems like the wrong approach - as you say it requires adding an adjuster. Presumably the module previously used hook_mail_alter() and it should now use the new hooks and throw a SkipSending.

msuthars’s picture

I'm using the following snippet:

/**
 * Implements hook_mailer_build().
 */
function YourModule_mailer_build(\Drupal\symfony_mailer\EmailInterface $email) {
  $config = \Drupal::config('mail_safety.settings');

  if ($config->get('send_mail_to_dashboard')) {
    try {
      $params = $email->getParams();
      $message = [
        'key' => $params['legacy_message']['key'],
        'module' => $params['legacy_message']['module'],
        'to' => $params['legacy_message']['to'],
        'from' => $params['legacy_message']['module'],
        'langcode' => $params['legacy_message']['langcode'],
        'subject' => $params['legacy_message']['params']['subject'] ?? NULL,
        'body' => [$params['legacy_message']['params']['body'] ?? MailFormatHelper::htmlToText($params['legacy_message']['params']['body']->__toString())],
        'headers' => $email->getHeaders()->toArray(),
        'send' => FALSE,
      ];
     \Drupal\mail_safety\Controller\MailSafetyController::insert($message);
    }
    catch (\Exception $e) {
      \Drupal::logger('YourModuleLog')->error($e->getMessage());
    }
  }

  if ($config->get('enabled') && $config->get('send_mail_to_default_mail')) {
    $email->setTo($config->get('default_mail_address'));
  }

  if ($config->get('enabled') && !$config->get('send_mail_to_default_mail')) {
    $email->addProcessor(function ($email) {
      throw new \Drupal\symfony_mailer\Exception\SkipMailException('Email sending skipped by mail_safety.');
    }, \Drupal\symfony_mailer\EmailInterface::PHASE_BUILD, 0);
  }
}
drunxfish’s picture

Version: 8.x-1.2 » 2.x-dev

drunxfish’s picture

Status: Needs work » Needs review

Implemented solution, everything is in order, however needs a review!