I'm wondering how to attach a file to an email sent using drupal_mail using htmlmail and mailmime? My use case is attaching an invoice PDF generated as a string by the Print module using the print_pdf_generate_path() function. I have this working in mimemail module but I'd like to try this with htmlmail/mailmime. My mimemail code looks like:

  module_load_include('inc', 'print_pdf', 'print_pdf.pages');  // require print_pdf.pages.inc

  // Prepare attachment.
  $path = 'node/' . $details['invoice']->nid;
  $attachments[] = array(   // attach PDF
    'filecontent' => print_pdf_generate_path($path),  // call the print_pdf function to generate PDF content string
    'filemime' => 'application/pdf',
    'filename' => 'Invoice.pdf',
    'filepath' => NULL,
  );

  // Send email.
  $params = array(
    'context' => array(
      'subject' => 'Invoice',
      'body' => 'Your invoice is attached.',
    ),
    'plaintext' => FALSE,
    'attachments' => $attachments,
  );
  drupal_mail('mimemail', 'invoice', $details['email'], language_default(), $params);

Could you please post some example code showing how to achieve the same thing using htmlmail?

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

lmeurs’s picture

+1

B-Prod’s picture

Status: Active » Needs review

Here is a function that allows to send attachments. Please note that in my case, the module it belongs to has htmlmail and mailmime as dependencies. If you do not intend to do as well, you will need to add some extra tests.

Note also that the patch 1 from #1518322: addAttachment method: error in the $location variable name is welcome to avoid error messages.

/**
 * Send an email using HTMLMailSystem class.
 * 
 * The mail can support attachments.
 * This function relies on HtmlMail and Mime Mail modules.
 * 
 * Attachements are added side the $params variable and foamatted as follow:
 * - filename: The file name.
 * - mime: The mime type.
 * - content: The content of the file.
 * - encoding: (optional) The type of encoding to use. Defaults to base64.
 * - charset: (optional) The character set of attachment's content. Default to ''.
 * - language: (optional) The attachment language. Default to ''. For more information
 *   on possible options, take a look at http://tools.ietf.org/html/rfc3282
 * 
 * @see mailmime::addAttachment()
 * @see Mail_mime::addAttachment()
 * 
 * @todo allow all parameters from Mail_mime::addAttachment() method. For that,
 * a complete reading of http://tools.ietf.org/html/rfc2557#section-4 could be a
 * starting point.
 * @todo handle drupal internal files.
 */
function mymodule_send_mail($module, $key, $to, $language = NULL, $params = array(), $from = NULL) {
  if (! isset($language)) {
    $language = $GLOBALS['language'];
  }
  
  $mail_system_settings = array($module => 'HTMLMailSystem');
  mailsystem_set($mail_system_settings);
  $message = drupal_mail($module, $key, $to, $language, $params, $from, empty($params['attachments']));
  
  // Do not process further if the mail was send or if a module disabled the action. 
  if ((isset($message['result']) &&  FALSE === $message['result']) || empty($message['send'])) {
    return $message;
  }
  
  // Here we are sure to have file attachments.
  $mime = &$message['MailMIME'];
  
  foreach ($params['attachments'] as $attachment) {
    $attachment += array(
      'encoding' => 'base64',
      'disposition' => 'attachment',
      'charset' => '',
      'language' => '',
    );
    
    $mime->addAttachment(
      $attachment['content'],
      $attachment['mime'],
      $attachment['filename'],
      FALSE,
      $attachment['encoding'],
      $attachment['disposition'],
      $attachment['charset'],
      $attachment['language']);
  }//end foreach  
  
  $message['body'] = $mime->get();
  $system = drupal_mail_system($module, NULL);
  $message['result'] = $system->mail($message);
  
  // Log errors.
  if (!$message['result']) {
    watchdog('mail', 'Error sending e-mail (from %from to %to).', array('%from' => $message['from'], '%to' => $message['to']), WATCHDOG_ERROR);
    drupal_set_message(t('Unable to send e-mail. Contact the site administrator if the problem persists.'), 'error');
  }
  
  mailsystem_clear($mail_system_settings);
  return $message;
}

Here is a way to call this function :

$params = array(
  'attachments' => array(
    array(
      'filename' => 'myfile.pdf',
      'mime' => 'application/pdf',
      'content' => file_get_contents('myfile_path'),
    ),
  ),
);
mymodule_send_mail('mymodule', 'mymodule_mail_key', 'to@test.com', NULL, $params);

@pillarsdotnet: I would be pleased to help adding such functionality inside HtmlMail module, but before I would like to know if it is a desired feature or not.

pillarsdotnet’s picture

@B-Prod -- Patches are always welcome.

B-Prod’s picture

Actually I did not provide a patch because it is a workaround to answer tanc's question.
If you really want to add such feature, I would be glad to help, but it seems to me that it is something related to HTML mail module rather than maill MIME.

Since you also are a maintainer of HTML mail module, please let me know if you want to add this feature. If so, I will create a regular patch in a separate issue.

Moreover, if you need some help for maintaining the HTML mail module, I would be happy to contribute as a co-maintainer.

pillarsdotnet’s picture

@#4

Please submit patch and I'll see about adding you as co-maintainer. Help is always welcome; I'm swamped with work right now.

bluetegu’s picture

Hi B-Prod,

#2 was a great help for me. I did remove all lines related to mailsystem settings (i.e. set and clear), cause I think it messed up my mailsystem settings for other modules configured through the mailsystem admin form. I simply configured the settings manually instead. I didn't investigate this, so my issue may be related to something else altogether, but I thought its worth reporting.

Thanks,
Ron

B-Prod’s picture

Title: Request example code - how to programatically attach file to email? » Allow file attachments to email using Mail MIME
Project: Mail MIME » HTML Mail
Component: Documentation » Code
Category: support » feature
FileSize
12.96 KB
6.68 KB

@pillarsdotnet: sorry for the delay, I don't know why this issue was lost from my dashboard... Co-maintaining is not necessary at all, the patch below affects HTML mail module.

This patch allows users that have enabled Mail MIME module to send attachments. Some comments in the project page would be welcome if this feature is accepted. A starting point may be in the HTMLMailSystem::handleAttachments() method comments:

  /**
   * Add file attachments to email using Mail MIME module.
   *
   * Attachements are added side the $params variable and foamatted as follow:
   * - filename: The file name.
   * - file: The path to the local file or its content.
   * - if_file: (optional) Whether the "file" key contains the path to the file or
   *   the content of the file. Default to TRUE.
   * - mime: (optional) The mime type.
   * - encoding: (optional) The type of encoding to use. Defaults to base64.
   * - charset: (optional) The character set of attachment's content. Default to ''.
   * - language: (optional) The attachment language. Default to ''. For more information
   *   on possible options, take a look at http://tools.ietf.org/html/rfc3282
   *
   * @see mailmime::addAttachment()
   * @see Mail_mime::addAttachment()
   */

The HTML Mail test form has also be updated, to allow users to test file attachments. I created 2 inputs, one for internal path, other for external file. The first file is used as this (file URI), the second is uploaded in the Drupal temporary directory and its content is used, not its URI. This allows to tests the "is_file" feature and also to remove the temporary file immediately.

I attached a blank PDF file to this comment, because when putting this file in the patch, the PDF file is corrupted when applying it (patch command does not accept binary files). The "blank.pdf" file goes under HTML Mail module root directory.

semiaddict’s picture

Thank you B-Prod.
This worked great for me.

B-Prod’s picture

Status: Needs review » Reviewed & tested by the community

Set as RTBC from comment #8 above.

@pillarsdotnet: is this new feature going to be ported in a future release?

webankit’s picture

#8 It works:-)
If there is an image it gets embedded rather than being an attachment

yogeshchaugule8’s picture

The #7 patch worked for me when I'm returning the filepath in $attachments['file'] not when returning the file data in it..

Anybody’s picture

stewart.adam’s picture

Issue summary: View changes

Note that although this patch works, Mail MIME is currently broken on PHP 5.4 and above due to its dependency on outdated PEAR libraries.
Edit: it would appear that this issue restores compatibility for now #1564790: E_STRICT compatibility

salvis’s picture

@stewart.adam: I'm a bit confused by #13 vs. #2293273: PDF attachments truncated in GMail web interface.

Edit: it would appear that this issue restores compatibility for now #1564790: E_STRICT compatibility

So, please review and test it, so we can commit it.

lmeurs’s picture

@B-Prod: Thanks a million, works like a charm!

lmeurs’s picture

Minor problem... When opening the test form after the patch has been applied, I received the following warning:

Notice: Undefined index: internal_file in htmlmail_test_form() (line 287 of ...\modules\_mail\htmlmail\htmlmail.admin.inc).

htmlmail_test_form() tries to load the default test values using variable_get(). When default test values have been stored previously, the $defaults array does not contain the internal_file element yet. This element is used later on to give a value to the form field, that's why the warning appeared.

Suggestion

On form submit internal_file's value is being checked and set in case it has not been set before. This checking should probably be done after loading the defaults and before building the form.

zeroduh’s picture

Well, this is my 2 cents. For my setup, after applying the patch, I adjust some of the patch code, because my attachments array are returning slightly diferent (using Drupal 7.31, Rules 7.x-2.7, HtmlMail 7.x-2.65+10-dev and MailMime 7.x-2.18+2-dev ). I think the file was not loaded, for me was something like this:

  [0] => [
    "filepath" => "pathOfTheFile.jpg",
  ];

So, I've adde the file load functions from a uri path. If you are having the same problem, just replace the function with this:

  /**
   * Add file attachments to email using Mail MIME module.
   *
   * Attachements are added side the $params variable and foamatted as follow:
   * - filename: The file name.
   * - file: The path to the local file or its content.
   * - if_file: (optional) Whether the "file" key contains the path to the file or
   *   the content of the file. Default to TRUE.
   * - mime: (optional) The mime type.
   * - encoding: (optional) The type of encoding to use. Defaults to base64.
   * - charset: (optional) The character set of attachment's content. Default to ''.
   * - language: (optional) The attachment language. Default to ''. For more information
   *   on possible options, take a look at http://tools.ietf.org/html/rfc3282
   *
   * @see mailmime::addAttachment()
   * @see Mail_mime::addAttachment()
   */
  protected function handleAttachments(array &$message) {
    if (!empty($message['params']['attachments'])) {
      $watchdog_args = array('@id' => $message['id'], '@to' => $message['to']);

      if (isset($message['MailMIME'])) {
        foreach ($message['params']['attachments'] as $attachment) {

          // Set the file
          if(isset($attachment['filepath'])) {
            $attachment['file'] = $attachment['filepath'];
          }

          // Load the file object
          $fileQuery = file_load_multiple([], ['uri' => $attachment['file']]);
          if($fileQuery && !empty($fileQuery)) {
            $attachment = (array) end($fileQuery);

            // Set the relative file path
            $fileUri = explode('://', $attachment['uri']);
            if(isset($fileUri[0])) {
              $attachment['file'] = variable_get('file_'.$fileUri[0].'_path').'/'.$fileUri[1];
            }
          }

          // Add default values
          $attachment += array(
            'is_file' =>TRUE,
            'encoding' => 'base64',
            'disposition' => 'attachment',
            'charset' => '',
            'language' => '',
          );

          $watchdog_args['!debug'] = highlight_string('<?php ' . var_export($attachment, TRUE), TRUE);

          if (empty($attachment['filename']) || empty($attachment['file'])) {
            watchdog('htmlmail', 'Some required data are missing in file attachment for message @id whose recipient is @to.<hr />!debug', $watchdog_args, WATCHDOG_ERROR);
            continue;
          }

          if ($attachment['is_file'] && !file_exists($attachment['file'])) {
            watchdog('htmlmail', 'Some required data are missing in file attachment for message @id whose recipient is @to.<hr />!debug', $watchdog_args, WATCHDOG_ERROR);
            continue;
          }

          if (empty($attachment['mime'])) {
            $attachment['mime'] = file_get_mimetype($attachment['filename']);
          }

          $message['MailMIME']->addAttachment(
            $attachment['file'],
            $attachment['mime'],
            $attachment['filename'],
            $attachment['is_file'],
            $attachment['encoding'],
            $attachment['disposition'],
            $attachment['charset'],
            $attachment['language']);
        }//end foreach
      }
      else {
        $watchdog_args['!url'] = l(t('Mail MIME module'), 'http://drupal.org/project/mailmime');
        watchdog('htmlmail', '!url is required to handle file attachments. There hase been ignored from the message @id to recipient @to.', $watchdog_args);
      }
    }
  }

Hope that helps!

Alan D.’s picture

Status: Reviewed & tested by the community » Needs review

Based off zeroduh comment, this probably needs a review.

Using #1341772: File attachments patch, I successfully sent emails with attachments using MailMime.

Code was as simple as:


  $attachments = array();
  if (!empty($form_state['values']['attachements'])) {
    foreach ($form_state['values']['attachements'] as $fid) {
      if ($file = (array) file_load($fid)) {
        // Copied from WebForm module, it didn't send untl filepath was updated.
        $file['filepath'] = $file['uri'];
        $attachments[$file['uri']] = $file;
      }
    }
  }
  if ($attachments) {
    $params['attachments'] = $attachments;
  }
  drupal_mail('module', 'key', $to, $langcode, $params, $form);

Hope this helps others :)

Status: Needs review » Needs work

The last submitted patch, 7: htmlmail-attach-file-to-email-1428802-7.patch, failed testing.

salvis’s picture

Thank you for your feedback, Alan D. — it's great if you got it to work, but I wish we could clean up the current mess and make it work for everyone.

If #1341772: File attachments works for you, how about RTBC'ing it?

Also still pending:
#1564790: E_STRICT compatibility
#2390001: Strict Warnings

Here in this thread we have only one patch that does not apply. Did you use it? How did you get it to apply? Do you have an updated patch? What exactly do you mean that needs review?

Where does your snippet go?

Alan D.’s picture

This thread was Needs review as per @zeroduh comment, I didn't personally attempt to see if this one worked.

"Did you use it?", manually applied from memory. That patch was the first I tried and it worked, but this thread looked like it was more comprehensive so I didn't want to rtbtc the other one....

salvis’s picture

I'm in a bad spot here — I volunteered to co-maintain HTML Mail and Mail MIME to try to keep them afloat in the absence of the maintainer, but I never have used nor will ever use either of them, so I must rely on those who actively use them to tell me exactly what works and how...

lmeurs’s picture

Status: Needs work » Closed (duplicate)
Related issues: +#1341772: File attachments

Few years ago I used the patch from this issue to send attachments. Thanks Alan D. for linking to #1341772: File attachments, today I successfully tested HTML Mail 7.x-2.71 with Mail MIME, without the patch from this issue.

I set the status to duplicate. Thanks all!