The D7 version of MIME Mail doesn't yet work with attachments. While this is probably a known problem, I figured I'd open an issue to keep track of progress in correcting this behavior.

The obvious source of this problem is that the mimemail_html_body() function loops through the $attachments parameter and checks each attachment for $a->filename. In Drupal 7, files no longer have a filename, instead they have $file->uri. Common URIs would be public://myfile.png, private://myfile.png, or s3://myfile.png.

Reading through the source of MIME Mail it looks like there are also assumptions that if a file URL contains "://" it is an absolute HTTP path. This assumption is now completely inaccurate since all file paths are specified as URIs, which all contain "://".

Comments

quicksketch’s picture

Status: Active » Needs review
StatusFileSize
new1.41 KB

This patch seems to do the trick. It makes the obvious change of changing $a->filepath to $a->uri, and tidies up the URL logic a bit to work with local or remote files.

sgabe’s picture

+++ mimemail.inc	19 Feb 2011 01:59:38 -0000
@@ -123,12 +123,13 @@
-        $file = drupal_realpath($url);
+        $file = file_create_url($url);

We operate on the local file system to embed images, so we need to use drupal_realpath() here.

quicksketch’s picture

We operate on the local file system to embed images, so we need to use drupal_realpath() here.

Ah, in which case we should probably keep using drupal_realpath() but use file_create_url() if it fails. drupal_realpath() will return FALSE in the event that the file is not stored locally, since entries in the "file" table are no longer guaranteed to be either local or writable (say if you're referencing a file on flickr with the flickr:// stream wrapper). file_create_url() will work for all stream wrappers.

sgabe’s picture

StatusFileSize
new2.03 KB

I'm attaching a revised patch.

guillaumev’s picture

Hi,

Maybe I'm missing something, but I don't understand this patch, which actually breaks on my system. When I look at the code in mimemail.rules.inc, the attachments are sent as:

array(
  'filepath' => '...',
  'filemime' => '...'
);

So, I don't understand why you would replace $a->filepath by $a->uri, which actually does not exist in $a... But again, I might be missing something...

guillaumev’s picture

StatusFileSize
new2.18 KB

Here is the patch I would propose, which also fixes a few notification errors...

quicksketch’s picture

So, I don't understand why you would replace $a->filepath by $a->uri, which actually does not exist in $a

If you use file_load() in Drupal 7 or look in the managed_file database table, you'll see that "filepath" no longer exists. This is because Drupal now uses stream wrappers like public://example_directory/example.png instead of a path like sites/default/files/example_directory/example.png. This makes it so that you can move your file directory around wherever you like, makes it so Drupal core can simultaneously support private and public files (as well as other storage mechanisms), and a lot of other benefits. Unfortunately it's also confusing to users who have never used stream wrappers before (they're a feature of PHP, not specifically Drupal).

mimemail.rules.inc, the attachments are sent as:

I'm guessing the Rules integration hasn't been updated yet either, so this problem needs to be corrected in more places than just the ones the patch current fixes.

pmcgilvray’s picture

#6 fixed this issue for me. Thanks guillaumev!

Isostar’s picture

Can someone post an example of the code for sending an attachment with mailsystem/mimemail.
There are also other people struggling with the same issue: http://drupal.org/node/1214098

I tried following code, with and without patch. The mail is sent, but without attach. The file object is loaded correctly.

function f2m_mail($key, &$message, $params) {
  $feedback=$params['feedback'];
  $node = $params['node'];
  $fid = $node->field_file['nl'][0]['fid'];
  $file = file_load($fid); //TODO moet file id inkomen
  switch($key) {
    case 'feedback':
      $message['subject']= $params['subject'];
      $message['body'][]= $feedback;
      $message['headers']['BCC'] = "***@***.com";
      $message['attachments'][] = array(
//      'filepath' => file_uri_target($file->uri), 
        'uri' => $file->uri,
        'filename' => $file->filename,
        'filemime' => $file->filemime,
      );
      break;
   }
}

Thanks in advance!

oadaeh’s picture

Category: task » bug
Priority: Major » Normal
StatusFileSize
new1.88 KB

In having to figure this out (again! -- because there's no documentation (again!)) I see the points both @quicksketch and @guillaumev are making.

In using new Drupal 7 functions that change the way things are done, you need to update your variables and change the way you write your code.

In using old/existing PHP functionality, you don't /need/ to change anything, but can -- if you know you how/what to change. Really, we should all be moving toward the way things are done w/D7, but it's unnecessary to break existing code, just because we can.

@guillaumev's patch takes into account both methods.

In changing the way Mime Mail operates w/o documenting it, no one knows what is going on, and we're all just stabbing in the dark guessing until we figure it out or give up, which I think is part of @guillaumev's frustration, and certainly, my frustration, especially after having gone through this once before.

I've attached a patch that updates @guillaumev's patch with the current state of Mime Mail. I also changed the variable assignment order so that they match the function parameter order. I think everything else is as it is in the three previous patch submissions.

jschrab’s picture

(Nevermind - comment posted in error)

sgabe’s picture

Title: D7 MIME Mail does not support attachments » Initial support of attachments
Status: Needs review » Fixed
StatusFileSize
new2.06 KB

I have committed the following patch for a start. Let's continue in separate issues.

ibes’s picture

Status: Fixed » Active

Don't work for me,

I try to use mimemail via mail_alter like:

  function mymodule_mail_alter(&$message) {
    $message['subject'] = "changed!";
    $message['params']['attachments']['filecontent'] = 'My attachment.'; // that's like I want it to work
    $message['params']['attachments']['filename'] = 'attachment.txt';
    $message['params']['attachments']['filemime'] = 'text/plain';
   // tried this:  $message['params']['attachments']['uri'] = 'sites/default/files/file.txt';
   // also tried this:  $message['params']['attachments']['filepath'] = 'sites/default/files/file.txt';
} 

it works just fine to alter other params like the subject.

get the PHP notice:
Notice: Undefined property: stdClass::$uri in mimemail_html_body() (Zeile 359 von /var/www/vhosts/xxx/httpdocs/sites/all/modules/mimemail/mimemail.inc).

I want to send an attachment built from a files source code (i will generate a PDF and send it per mail).

==

in file mimemail.inc on line 356, function mimemail_html_body, there is:
$content = isset($a->filecontent) ? $a->content : NULL;

guess it must be:
$content = isset($a->filecontent) ? $a->filecontent : NULL;

==

in file mimemail.inc on line 144, function _mimemail_file, there is:

if (isset($file) && @is_file($file)) {

I compared it with the version for drupal 6:

if (isset($file) && (@is_file($file) || $content)) {

I guess the Drupal7 version kills the support for "filecontent" and will lead to the notice (see above)

Sorry for my bad style - first bug report...

sgabe’s picture

Priority: Normal » Critical
Issue tags: +D7 stable release blocker

Lets mark this as D7 release blocker.

sgabe’s picture

Yes, the port of #932962: Allow attachments to be added by contents was wrong for D7. The fix is committed. Thanks for pointing this out!

sgabe’s picture

#1290836: Better documentation for attachment needed is marked as a duplicate of this, we need to update the documentation according to the changes.

ibes’s picture

Get a file attachment out of a string seems to work, but I get a PHP notice:

Notice: Undefined property: stdClass::$uri in mimemail_html_body() (Zeile 359 von /var/www/httpdocs/sites/all/modules/mimemail/mimemail.inc).

this is line 359:

 _mimemail_file($a->uri, $content, $name, $type, 'attachment');

yeah - as there is no "uri" (because it makes no sense to set uri AND content) - so that's the reason for the notice.
there has to be something, to prove whether there is a uri

what about do it this way: (mimemail.inc - line 353)

  if (is_array($attachments) && !empty($attachments)) {
    foreach ($attachments as $a) {
      $a = (object) $a;
+   $uri = isset($a->uri) ? $a->uri : NULL;
      $content = isset($a->filecontent) ? $a->filecontent : NULL;
      $name = isset($a->filename) ? $a->filename : 'attachment.dat';
      $type = isset($a->filemime) ? $a->filemime : 'application/octet-stream';
 -    _mimemail_file($a->uri, $content, $name, $type, 'attachment');
+    _mimemail_file($uri, $content, $name, $type, 'attachment');
      $parts = array_merge($parts, _mimemail_file());
    }
  }

Also if I try to attach an existing file the module wants to have "uri" and not the older "filepath" argument.

Why was the name changed from "filepath" to "uri" - guess that makes more trouble by upgrade from 6.x to 7.x and I don't see a great improvement in this.

My code:

function hook_mail_alter(&$message) {
    $message['params']['attachments'] = array (
        array(
          'filecontent' => 'content',
          'filename' => 'content.txt',
          'filemime' => 'text/plain',
        ))
}

two arrays, because there may be more attachments - second array to get the values of the attachment.

To send a existing file you must use 'uri' with an relative(!) path like the readme.txt says - that works for me:

Images with absolute URL will be available as remote content. To embed images
into emails you have to use a relative URL or an internal path.
For example:
instead of http://www.mysite.com/sites/default/files/mypicture.jpg
use /home/www/public_html/drupal/sites/default/files/mypicture.jpg
or /sites/default/files/mypicture.jpg

Code, that works for me:

function hook_mail_alter(&$message) {
    $message['params']['attachments'] = array (
        array(
          'uri' => 'themes/bartik/logo.png',
          'filename' => 'image.png',
          'filemime' => 'image/png',
        ))
}
ibes’s picture

StatusFileSize
new797 bytes

a patch for the issues in #17

ibes’s picture

Status: Active » Needs review
sgabe’s picture

Status: Needs review » Needs work

We need to update the documentation according to the modifications. Furthermore I think maybe we should preserve filepath in the array and just add support for uri.

ibes’s picture

I am not sure if I got you right:
So "filepath" should be best practise for the path to the file and "uri" should also be supported?

Like: $url = isset($a->filepath) ? $a->filepath : (isset($a->uri) : $a->uri : NULL);

Where are pieces of documentation:
- in the readme file
- inline documentation
- elsewhere?

I guess we should check if the existing naming still fits and I guess it would make things much easier to have more info about the attachment array. For example, that there are two array: an array of attachment array and the array of each attachment. This information is worth 1 - 2 days :)

What modules should work together with mimemail?

- Simplenews
- Webform
- Rules

ibes’s picture

Patch #18 works without errors and notices in

- webform
- hook_mail_alter
- drupal_mail

I did not found a rules integration (see other issue) and no way to test it with an attachment in simplenews.

Works without adding a mimetype, filename.
Works with uri (not the old filepath), filecontent

Path can be
- absolute server path
- relative to drupal root

URLs to external pages (http://) seems to do not work - as the readme also says that I guess this is like designed

Readme file is quite good and still up to date, but would specify this part:

You can use the following optional parameters to build the e-mail:
'plain':
Boolean, whether to send messages in plaintext-only (optional, default is FALSE).
'plaintext':
Plaintext portion of a multipart e-mail (optional).
'attachments':
Array of arrays with the path, name and MIME type of the file (optional).
'headers':
A keyed array with headers (optional).

won't solve this now, but this could be a nice start:

$params['attachments'] = array (
array(
'uri' => '/sites/files/attachment.txt', //or filecontent to generate attachment from filecontent
//'filecontent' => $content, // or uri to attach existing file
'filename' => 'attachment.txt', //optional
'filemime' => 'text/plain', //optional
)
)

for more attachments attend another array in the attachments array

this is the existing inline documetation (file: mimemail.module - function mimemail_prepare_message):

- 'attachments':
* An array of arrays which describe one or more attachments.
* Existing files can be added by path, dinamically-generated files
* can be added by content. The internal array consists of two parts:
* - 'filepath':
* Relative Drupal path to an existing file (filecontent is NULL).
* - 'filecontent':
* The actual content of the file (filepath is NULL).
* - 'filename':
* The filename of the file.
* - 'filemime':
* The MIME type of the file.
* The array of arrays looks something like this:
* Array
* (
* [0] => Array
* (
* [filepath] => '/sites/default/files/attachment.txt'
* [filecontent] => 'My attachment.'
* [filename] => 'attachment.txt'
* [filemime] => 'text/plain'
* )
* )

"filepath" have to be updated, if "uri" is the new wording for it - can include this in my patch.
I don't know what is the new standard. I guess in some places in the mimemail.module it is already "uri" and I haven't seen "filepath" beside the documentation

sgabe’s picture

Status: Needs work » Needs review
StatusFileSize
new1.73 KB

So "filepath" should be best practise for the path to the file and "uri" should also be supported?

Yeah, I think that's enough. In this case the documentation is good as it is. The rest of the issues here were covered by earlier commits.

ibes’s picture

tested with a similar testset as in #18 + rules

works nice. I think we have it!

sgabe’s picture

@ibes: Did you try to send attachment via Rules with tokens? I believe if you have a file field set for the entity, there is a token for the file path which should work in the rule, but I didn't test it yet.

ibes’s picture

no, haven't tested that.

Only by defining attachments in the Rules UI.
I wondered if there is a way to let users (optionally) also specify the filename of the attachment.
Should not be that much extra code, but looks better - but this is another issue. Should I open another or extend an existing issue?

I will try to do that token to attachment thing!

sgabe’s picture

I mean defining attachment in the Rules UI but with tokens. For example this [node:field_file-filefield-filemime]:[node:field_file-filefield-filepath] works in D6 to set an attachment when a node (which has a file field) is viewed but I didn't try this in D7 yet.

ibes’s picture

test to get an attachment by using tokens of a file field (cck)

Wasn't able to test it because I wasn't able to get any content by use the field token.
I don't know why the token gives me no content - but with a token output I planned to test it like this:

Here is how I wanted to do my test:

- activate module token
- create an node type with file
- set an file token in "manage displays" -> "tokens"
- set an rule to send html mail on creating or updating that node type
- put in the file token in attachments

I guess something like this should be put in the attachment input field:
[node:field-datei:file:mime]:[node:field-datei:file:url]

ibes’s picture

got attachment running with tokens if:
- token display is set to url
- mimetype is defined manually and only the filepath is set by token
attachment is sent - but filename is "attachment.dat" - that is not quite nice

Wasn't able to get the mimetype via token.

Tried [node:field-datei:file:mime]:[node:field-datei] (the token display is set on url)
as rules or mimemail didn't know the first token it makes "[node" to mimetype and "field-datei:file:mime] to filepath (as there is a ":" after "node")

sgabe’s picture

According to the File interface this should work as expected, the file object has a filename field.

ibes’s picture

the mimemail rules action has no input option for a file name - only filepath and mimetype...

butler360’s picture

Subscribing. Trying to send attachment as a Rule fired after Webform submission. Works perfectly so far, except I, too, get attachment.dat instead of myfile.pdf.

ibes’s picture

@butler360 in #32:

You can't specific a file name via rules, so mimemail use its default "attachment.dat".
I don't like this either - i will open a new issue.
This is nothing that will crash the hole thing, but should be done.

ibes’s picture

What is left to review in this issue?

- the last patch works
- It is possible to send attachments via rules. There are about two issues with mimemail and rules but it is possible to send attachments with it.
- Also Webform is tested
- mail_alter is tested.

Anything else to test or other issues on this?

butler360’s picture

I don't understand the point, then. Are you saying it's intentional that every attachment is sent as attachment.dat?

sgabe’s picture

@butler360: In case of attachments sent via Rules, that's a bug, see #1305830: Set default filename and mimetype to enforce auto detection.

ibes’s picture

@butler360: what do you think about this change - see: #1305824: Leave MIME type and use only path to specify an attachment

@sgabe: What do you think - are we ready to close this issue? As there are some issues on the rules integration mimemail don't seem to be ready for RC, but by fixing this issue I think it will work for most projects that don't use mimemail via rules + attachments.
So would be good to have this patch in the dev version.

sgabe’s picture

Status: Needs review » Fixed

Committed. Thanks everyone.

Status: Fixed » Closed (fixed)
Issue tags: -D7 stable release blocker

Automatically closed -- issue fixed for 2 weeks with no activity.