Drupal 7 HTML E-Mail With PDF Attachments

RSS
by Ross

Are you having trouble getting attachments working on Drupal 7? You are not alone. Drupal pared back the core e-mail functionality quite a bit from 6 to 7. You can't even send html mail anymore without installing a module. This article will focus on getting attachments working - then getting pdf attachments working, but we will be using Mime Mail, so a side effect will be HTML enabled mail.

Most of the solutions for sending mail with an attachment in Drupal 7 don't seem to rely on Drupal modules. The most popular solution to this problem is to basically circumvent Drupal and create a new php class to send your mail. I wanted to use a more Drupal centric solution.

My solution involves the modules Mime Mail, Mail System, and Print, along with our own custom module that we will write that will send the email. To send an email with a pdf attachment, one must first be able to send an email with some kind of attachment at all. This is no small feat, as the documentation on this is severely lacking. However, attachment functionality is built into the Mime Mail module. Most of the confusion seems to arise from 2 things: the fact that the attachments are set via $message['params']['attachments'], rather than $message['attachment'], and that attachments are an array of arrays.

Another possible pitfall is that you must set up drupal_mail() to use mime mail as the mail system interface. This is where the mail system module comes into play. The module is pretty straightforward, and lets you set a different mail system per module. There is a straightforward configuration at admin/config/system/mailsystem where you create a new setting for your module, and select mime mail as the mail system.

To make sure that we can even send attachments, we test sending a .txt file that should exist on every site. This is the code that should be in your custom module to test for sending an attachment. I am using the case of a user submitting a form as the trigger to send the emails.

function _example_form_submit($form, &$form_state) {
  _example_form_save($form, $form_state);
  global $user;
  $accounts = array($user, user_load(XX)); //you can add accounts to this array to notify
  example_notify($accounts, $user); //could also pass $form_state or other variables
}

function example_notify($accounts, $from) {
  $params['from'] = $from;
  foreach ($accounts as $account) {
  $params['account'] = $account;
  // example_mail() will be called based on the first drupal_mail() parameter.
  drupal_mail('example', 'notice', $account->mail, user_preferred_language($account), $params, $from->mail);

  drupal_set_message('This form has been emailed to '. $account->name);
  }
}

function example_mail($key, &$message, $params) {
  $data['user'] = $params['from'];
  $account = $data['user']->name;

  $attachment = array(
  'filecontent' => file_get_contents(DRUPAL_ROOT . '/README.txt'),
  'filename' => 'test.txt',
  'filemime' => 'text/plain',
  );

  switch($key) {
  case 'notice':
  $langcode = $message['language']->language;
  $message['subject'] = 'example submission from' . $account;
  $message['body'][] ='

'. $account .' has submitted an example.

';
  $message['params']['attachments'][] = $attachment;
  break;
  }
}

Hooray! Attachments are now working! Not too bad once you gather all the pieces together.

Now, onto producing pdf attachments. We will use the pdf generation capabilities of the print module. Installing and configuring the module is beyond the scope of this blog post, but I can tell you that it is easier than you think it would be and the directions on the module page are pretty straightforward.

Now that we have this module installed and configured to show pdf versions of pages on the site, we need to tap into the print_pdf module in our custom code. We are going to access a hook of the print_pdf module called print_pdf_generate_path. This function is poorly named, since it's return value is not a path at all, but is instead the actual file data for the pdf being created. We pipe this into the filecontent of our attachment and we are good to go.

function _example_form_submit($form, &$form_state) {
  _example_form_save($form, $form_state);
  global $user;
  $accounts = array($user, user_load(XX)); //you can add accounts to this array to notify
  example_notify($accounts, $user); //could also pass $form_state or other variables
}

function example_notify($accounts, $from) {
  $params['from'] = $from;
  foreach ($accounts as $account) {
  $params['account'] = $account;
  // example_mail() will be called based on the first drupal_mail() parameter.
  drupal_mail('example', 'notice', $account->mail, user_preferred_language($account), $params, $from->mail);

  drupal_set_message('This form has been emailed to '. $account->name);
  }
}

function example_mail($key, &$message, $params) {
  $data['user'] = $params['from'];
  $account = $data['user']->name;

  module_load_include('inc', 'print_pdf', 'print_pdf.pages');
  $file_content = module_invoke('print_pdf', 'generate_path', 'example-url/');

  $attachment = array(
  'filecontent' => $file_content,
  'filename' => 'example-filename',
  'filemime' => 'application/pdf',
  );

  switch($key) {
  case 'notice':
  $langcode = $message['language']->language;
  $message['subject'] = 'example submission from '. $account;
  $message['body'][] ='

'. $account .' has submitted an example.

';
  $message['params']['attachments'][] = $attachment;
  break;
  }
}

It's downright simple once you see the example code, but believe me this took a lot of trial and error (since googling didn't seem to lead to much useful information) to get everything to meld together. Of particular note is the use of module_load_include in order to access functions of other modules that exist outside of the main .module file.

If you have any suggestions for improvement, or have questions, feel free to leave comments below. Portions of this code were adapted from other examples available, but I don't remember exactly from where. Mad props go out to you, Drupal community.