Build email before cancelling the send

Created on 25 April 2023, almost 2 years ago
Updated 1 May 2023, almost 2 years ago

Problem/Motivation

When setting the $send parameter to FALSE in the MailManagerReplacement::mail() it will bail before building the email.

Doing this the result isn't as useful compared to after it's build (my particular use case is I want a preview of the mail as close to what the user will actually receive).

Proposed resolution

Move the cancel mail check to right before the mail is actually send.

Feature request
Status

Closed: won't fix

Version

1.0

Component

Code

Created by

🇩🇰Denmark Birk

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @Birk
  • 🇩🇰Denmark Birk

    Included a proof of concept patch that works in my particular situation.

    It's a quick fix, and I've not looked into any side effects of the patch.
    Maybe someone with more experience in the symfony_mailer module could point out any obvious flaws.

  • 🇬🇧United Kingdom adamps

    my particular use case is I want a preview of the mail as close to what the user will actually receive

    True this could be useful, however it seems like a fairly unusual requirement. And sending the email is slower with the patch, which is bad for the majority case. If $send is FALSE, the Drupal Core MailManager will execute format() but not mail() which has an fairly undefined result, but probably not very close to the final email.

    Here's something you could try. Instead of setting $send to FALSE, enable the NullTransport, which you can do using a hook.

    Maybe someone with more experience in the symfony_mailer module could point out any obvious flaws.

    It looks like it would only work for LegacyEmailBuilder and not for e.g. UserEmailBuilder.

  • 🇬🇧United Kingdom adamps

    Or even simpler: throw a SkipMailException late in the PHASE_POST_RENDER phase.

  • 🇩🇰Denmark Birk

    I don't think the patch would have any performance impact for sending mails (it would though for not sending mails with $send FALSE).

    I hadn't thought about using the NullTransporter, thank you for the idea, we should be able to implement a non intrusive module with that approach.

    It might be an unusual requirement, but it's a feature we have provided clients, to give them peace of mind before sending out emails.

  • Status changed to Closed: won't fix almost 2 years ago
  • 🇬🇧United Kingdom adamps

    I don't think the patch would have any performance impact for sending mails (it would though for not sending mails with $send FALSE).

    I agree, and still I don't want to make skip sending slower as it could be a common case for some sites.

    I propose the way this module should work is:

    1. SkipMailException cancels immediately that it is thrown for performance
    2. NullTransport builds the email then doesn't send it for inspection
    3. For MailManagerReplacement::mail($send = FALSE) we choose performance, and sites that want inspection can easily alter with a hook

    Hopefully this gives you what your client needs without causing any disadvantage for other sites.

  • 🇩🇰Denmark Birk

    I'll share a quick overview of our implementation in case it has value for anyone ending in this issue.

    We ended up implementing a skipsend in the $params argument, and then having a PHASE_POST_RENDER hook (I think this could be any phase hook) set the the transport DSN to a

    NullTransport</mail>.
    The hook is as follows:
    
    <?php
    function hook_mailer_post_render($email) {
      if ($legacy_message = $email->getParam('legacy_message')) {
        if (!empty($legacy_message['params']['skipsend'])) {
          $email->setTransportDsn('null://default');
        }
      }
    }
    ?>
    
    This assumes there's a <code>NullTransport

    transporter named null for it to work. And in our preview controller we're calling the mail with the extra parameter but with $send TRUE (so MailManagerReplacement::mail() and LegacyEmailBuilder::buil() wont end the process to early) :

    // ...
    $params['__skipsend__'] = TRUE;
    $result = $mailManager->mail($module, $key, $to, $langcode, $params);
    // ...
    

    Thank you for your input on the issue, and all your work on the module!

Production build 0.71.5 2024