5

I am trying to use the MailManager class in Drupal 8 to send HTML mail. I got it working, but it isn't parsing the HTML markup. I think something is wrong with the headers of the email.

What I have is a custom module (Custom_forward) that contains a form and that calls Mailmanager::mail() in its submitForm() method, which contains the following code.

        // Email body
        $header = $sender . ' thought you would like to see this page from the website.';
        $personal_message = $form_state->getValues()['message_body'];
        $entity_url_value = '<a href="' . $entity_url->toString() . '">Go to article</a>';
        $message = $header . '<br>' . $personal_message . '<br>' . $entity_url_value;

        // Email
        $langcode = 'en';
        $mailManager = \Drupal::service('plugin.manager.mail');
        $to = $form_state->getValues()['recipient'];

        $params['title'] = 'Forward';
        $params['message'] = $message;
        $result = $mailManager->mail('custom_forward', 'forward', $to, $langcode, $params, NULL, true);

        if ($result['result'] !== true) {
            drupal_set_message(t('There was a problem sending your message and it was not sent.'), 'error');
            \Drupal::logger('mail-log')->error($message);
        }
        else {
            drupal_set_message(t('Your message has been sent.'));
        }

According to Drupal you also need the hook_mail method in mytheme.theme to be able to send message, you can see this method here:

function custom_forward_mail($key, &$message, $params) {
  $message['body'] = [];
  $message['subject'] = [];
  $message['headers'] = array(
    'content-type' => 'text/html',
    'charset' => 'UTF-8',
    'format' => 'flowed',
    'delsp' => 'yes',
    'from' => \Drupal::config('system.site')->get('mail')
  );

  switch ($key) {
    case 'forward':
      $message['subject'] = $params['title'];
      $message['body'][] = '<html><body>' . $params['message'] . '</body></html>';
      break;
  }
}

As you can see, I am setting the content type to text/html and that should tell the mail system to parse the content as an HTML, but this is not working for some reasons.

This is a testing email. All the HTML markup is not shown, but also not working.

Bassem thought you would like to see this page from the website. personal message Go to article [1] [1] http://myhost/node/81
3
  • 1
    Why would you want to write custom code for this? Just use Mime Mail.
    – colan
    Jan 23, 2018 at 19:44
  • I ended up using mail system and swift mailer modules. Custom code is for doing a customized behaviour ( forwarding an article from one user to another ) Jan 24, 2018 at 13:16
  • 1
    Ah, okay. I chose Mime Mail because it didn't require an external library, unlike Swift Mailer.
    – colan
    Jan 24, 2018 at 19:39

2 Answers 2

12

Without any module it is not possible to send HTML emails. This is because the default plugin the MailManager class (which is a plugin manager) uses to effectively send emails contains the following code. (See PhpMail.php.)

public function format(array $message) {

    // Join the body array into one string.
    $message['body'] = implode("\n\n", $message['body']);

    // Convert any HTML to plain-text.
    $message['body'] = MailFormatHelper::htmlToText($message['body']);

    // Wrap the mail body for sending.
    $message['body'] = MailFormatHelper::wrapMail($message['body']);
    return $message;
  }

The comment clearly states: Convert any HTML to plain-text.

If you look at MailFormatHelper::htmlToText(), you will see the code that is converting the HTML markup in plain text, in particular the following that changes any <a> tag as you saw in your test email.

  // Replace inline <a> tags with the text of link and a footnote.
  // 'See <a href="https://www.drupal.org">the Drupal site</a>' becomes
  // 'See the Drupal site [1]' with the URL included as a footnote.
  static::htmlToMailUrls(NULL, TRUE);
  $pattern = '@(<a[^>]+?href="([^"]*)"[^>]*?>(.+?)</a>)@i';
  $string = preg_replace_callback($pattern, 'static::htmlToMailUrls', $string);
  $urls = static::htmlToMailUrls();
  $footnotes = '';
  if (count($urls)) {
    $footnotes .= "\n";
    for ($i = 0, $max = count($urls); $i < $max; $i++) {
      $footnotes .= '[' . ($i + 1) . '] ' . $urls[$i] . "\n";
    }
  }

You need a module that defines a new mail plugin using a different implementation of the format() method and make the MailManager class use it. As said in the documentation for MailManager::getInstance(), the class uses the configuration in system.mail.interface. (The array returned from \Drupal::config('system.mail.interface').) In your example, an array value for custom_forward_formard (e.g. 'custom_forward_forward => 'custom_forward_mail', where custom_forward_mail is the plugin ID for the class implemented by your module) would do the trick.

1

The default mail module would not let you send html mail, but there is a hack for that. before assigning html variable to body please use PHP function htmlentities, it prevent html tags being removed from message. and you can get the html mail.

$message['body'][] =  htmlentities($msg); 

this is for quick hack.. without altering any core codes.

2
  • This sends it to my spam folder, so would not recommend! Dec 30, 2020 at 22:20
  • All this does is send a mangled plain text email, where for example '<' is changed to '&lt;' and single quotes are changed to '&#039;' etc.
    – anonymous
    Jan 28, 2021 at 18:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.