How to Programmatically Send E-Mail Using HTML and Text Content-Type from Drupal 7

HTML Email

Let's use a scenario where we want to send HTML email each time a new node article is added.

By default, Drupal MailsSystemInterface sends out email in plain text format. Say, we want to send out HTML email only on certain outgoing mail, but not all. To do this, the first thing we need to do is to  have our custom implementation of the MailSystemInterface which we can override later.

1. Implements Drupal MailSystemInterface

/**
* Implements MailSystemInterface
*/
class CustomModuleMailSystem implements MailSystemInterface {
  /** 
   * Concatenate and wrap the e-mail body for plain-text mails
   * @param $message
   *  A message array, as described in hook_mail_alter().
   *
   *  @return
   *    The formatted $message
   */  
  public function format(array $message) {
    $message['body'] = implode("\n\n", $message['body']);
    return $message;
  }
 
  /** 
   * Send an e-mail message, using Drupal variables and default settings.
   * @see drupal_mail*()
   * @param $message
   *  A message array, as described in hook_mail_alter()
   *  @return
   *    TRUE if the mail was successfully accepted, otherwise FALSE
   */  
  public function mail(array $message) {
    $mimeheaders = array();
    foreach($message['headers'] as $name => $value) {
      $mimeheaders[] = $name . ': ' . mime_header_encode($value);
    }   
    $line_endings = variable_get('mail_line_endings', MAIL_LINE_ENDINGS);
    return mail(
      $message['to'],
      mime_header_encode($message['subject']),
      preg_replace('@\r?\n@', $line_endings, $message['body']),
      join("\n", $mimeheaders)
    );
  }
}

 

2. Create a wrapper function calling drupal_mail()

The drupal_mail() function expects the following arguments: $module, $key, $to, $language, $params, $from, and $send. 

/**
 * Wrapper for drupal_mail.
 */
function custom_module_mail_send( $key, $node) {
  $node_wrapper = entity_metadata_wrapper('node', $node);
  $site_mail = variable_get('site_mail', 'info@almostdone.me');

  $module = 'custom_module';
  $key = $key;
  $to = $node_wrapper->author->mail->value(); // Send email to author;
  $from = $site_mail;
  $language = isset($values['lang']) ? $values['lang'] : language_default();
  $params = array(
    'subject' => 'Article Added',
    'body' => theme('custom_module_email', array())
  );

  $send = TRUE;
  $mail = drupal_mail($module, $key, $to, $language, $params, $from, $send);
  if($mail['result']) {
    return TRUE;
  } else {
    $error_msg = 'Failed to send the email';
    watchdog('custom_module', $error_msg, array(), WATCHDOG_ALERT);
    return false;
  }
}

 

3. Implements hook_mail().

hook_mail() prepares the message before it get sent. It is invoked when drupal_mail() is called.

/**
 * Implements hook_mail()
 * @param $key
 * @param $message
 * @param $params
 */
function custom_module_mail($key, &$message, $params) {
  switch($key) {
  //We will give our mail a key named 'custom_module_email'
    case 'custom_module_email':
      $message['subject'] = isset($params['subject']) ? $params['subject'] : t('Node Saved');
      $message['body'][]= isset($params['body']) ? $params['body'] : NULL;
      if(isset($params['headers']) && is_array($params['headers'])) {
        $message['headers'] += $params['headers'];
      }
      break;
  }
}

4. Implement hook that executes custom_module_mail_send().

Based on our scenario, we wanted to send the email when a new article is saved. So the hook we will implement is hook_node_insert().

/**
 * Implements hook_node_insert().
 */
function custom_module_node_insert($node) {
  if ($node->type === 'article') {
    $key = 'custom_module_email';
    custom_module_mail_send($node, $key);
  }
}

 

5. Create the HTML template for our HTML email

/**
 * Implements hook_theme()
 */
function custom_module_theme($existing, $type, $theme, $path) {
  if($type == 'module') {
    return array(
      'custom_module_email' => array(
          'variables' => array(),  
          'template' => 'custom_module_email', //no need for the file extension here. Therefore, we will have a file in theme folder (see below), that will be named custom_module_email.tpl.php
          'path' => drupal_get_path('module', 'custom_module'). '/theme',
        ),  
      );  
  }
}

 

Now, let's write that template file, that will use the theme variables passed from theme() function. We will first create a folder named theme and inside that, we will create the file custom_module_email.tpl.php.

<?php 
/**                            
 * @file: custom_module_email.tpl.php
 * Contains HTML template of email response when a new article is added
 */
?>
<html>
<body>
<h2><?php print $node->title; ?></h2>
<span>Custom HTML message</span>
</html>

6. Implement hook_mail_alter()

 

By default, MailSystemInterface will send the email as text format. So we are using this hook to override that.

/**
 * Implements hook_mail_alter()
 */
function custom_module_mail_alter(&$message) {
  // Override the message header only on the outgoing message we want to target. 
  if($message['id'] == 'custom_module_custom_module_email') {
    $headers = array(
      'MIME-Version' => '1.0',
      'Content-Type' => 'text/html; charset=UTF-8; format=flowed',
      'Content-Transfer-Encoding' => '8bit',
      'X-Mailer' => 'Drupal',
    );
  }else{
    $headers = array(
      'MIME-Version' => '1.0',
      'Content-Type' => 'text/plain; charset=UTF-8; format=flowed; delsp=yes',
      'Content-Transfer-Encoding' => '8bit',
      'X-Mailer' => 'Drupal',
    );
  }
 
  //Overwrite header message header with Content-Type accommodating for html format
  foreach($headers as $key => $value) {
    $message['headers'][$key] = $value;
  }
}

 

7. Update mail_system configuration

Implement this on custom_module.install file.

<?php
function custom_module_enable() {
  mailsystem_set(array('custom_module' =>'CustomModuleMailSystem'));
}
 
function custom_module_disable() { 
  mailsystem_clear(array('custom_module' => 'CustomModuleMailSystem'));
}