Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Ticket #2179 (closed enhancement: untested)

Opened 4 years ago

Last modified 11 months ago

[PATCH] Ability to add inline attachments (ie, inline images for html email)

Reported by: eastcoastcoder@gmail.com Assigned to: jamis@37signals.com
Priority: normal Milestone:
Component: ActionMailer Version: 0.14.3
Severity: normal Keywords: activesupport strings helpers validations needy
Cc: somekool@gmail.com, luben.manolov@gmail.com, chris+rubyonrails@qwirx.com

Description

ActionMailer currently lacks the ability to add inline attachments, such as inline images for use in html emails.

Attachments

actionmailer_inline_images.rb (2.1 kB) - added by mike@michaelhoughton.co.uk on 10/09/05 18:08:00.
tmail_content_id.rb (1.8 kB) - added by CodeNinja on 03/29/06 05:38:15.
Additional fix for the Content-ID... Include this along with the Patch.
actionmailer_inline_attachment.patch (1.6 kB) - added by chrisw on 04/07/08 23:41:33.
Patch to actionmailer to enable inline attachments, needed for HTML email with embedded images

Change History

09/13/05 16:40:09 changed by eastcoastcoder@gmail.com

The MIME format for inline attachments is similar to regular attachments, except:

1) Content-Disposition: inline, filename="[filename]" is used (s/attachment/inline).

2) Another header is required: Content-ID: <[uniqid]>

3) Other parts of the email can reference this resource by using the url cid:[uniqid]

See http://mailformat.dan.info/headers/mime.html for a good description

09/13/05 16:45:41 changed by eastcoastcoder@gmail.com

PROBLEMS

1) The biggest problem is in the part.rb file. There, # Also don't set filename and name when there is none (like in

# non-attachment parts) if content_disposition == "attachment"

it makes the domain assumption that only attachements can have filenames. Not only does this bind it to domain knowledge, it also results in repeated code there. A better implementation would be to just rely on squish to not set a filename if is not there. The only value I see in the current coupling to domain knowledge (attachments are different than other dispositions) is that there is currently no other way to not set a charset (unless it is an attachment, a nil charset is replaced with the default).

2) The other problem is that although the Part class can take a params[:header], it currently just ignores it. We need to be able to set arbitrary headers (specifically, a Content-ID).

Note that both these problems are not unique to this ticket but are general defects in the current ActiveMailer.

09/13/05 16:58:37 changed by eastcoastcoder@gmail.com

PROBLEMS

1) The biggest problem is in the part.rb file. There, # Also don't set filename and name when there is none (like in

# non-attachment parts) if content_disposition == "attachment"

it makes the domain assumption that only attachements can have filenames. Not only does this bind it to domain knowledge, it also results in repeated code there. A better implementation would be to just rely on squish to not set a filename if is not there. The only value I see in the current coupling to domain knowledge (attachments are different than other dispositions) is that there is currently no other way to not set a charset (unless it is an attachment, a nil charset is replaced with the default).

2) The other problem is that although the Part class can take a params[:header], it currently just ignores it. We need to be able to set arbitrary headers (specifically, a Content-ID).

Note that both these problems are not unique to this ticket but are general defects in the current ActiveMailer.

09/13/05 17:16:09 changed by eastcoastcoder@gmail.com

TODO - IMPLEMENTATION

1) Decouple part.rb from domain knowledge. Any part which says it has a filename or name should be believed!

2) Allow a Part to specifcy that it should have no charset, without it getting the default charset (how should this be implemented???).

3) Part should honor the :header param. For each header, part[key] = value .

4) Create an inline_attachment method (in part_container.rb), similar to attachment, except that it takes a cid, and sets the disposition to inline.

5) Create a cid generator, creating something along the lines of <filename.uniqid@hostname>. (Is there a privacy issue here?).

6) Ideally, the inline_attachment method could be used without a cid, automatically generating a cid and then returning it.

7) Last, the multipart/related MIME type will need to be set (More info???)

10/09/05 18:05:46 changed by mike@michaelhoughton.co.uk

I've implemented a small change to ActionMailer::Part#to_mail to support what (I think) is needed here. I haven't done the CID generator discussed in the implementation list.

The attached code defines a new #to_mail, and adds an inline_attachment method, such that this sort of thing works:

inline_attachment :content_type => "image/jpeg", 
  :body => File.read("#{RAILS_ROOT}/public/images/bg.jpg"),
  :filename=>"bg.jpg",
  :cid => '<cid.bg@michaelhoughton.co.uk>'

10/09/05 18:08:00 changed by mike@michaelhoughton.co.uk

  • attachment actionmailer_inline_images.rb added.

11/22/05 22:13:05 changed by Mathieu

  • cc set to somekool@gmail.com.

that will be very very interesting.

12/01/05 12:51:28 changed by matt@prognostikos.com

  • version changed from 0.13.1 to 0.14.3.
  • summary changed from Ability to add inline attachments (ie, inline images for html email) to [PATCH] Ability to add inline attachments (ie, inline images for html email).

The attached patch is working well for me as a plugin with 0.14.3...

01/30/06 08:00:31 changed by rordev.1.sts@xoxy.net

I found this didn't work correctly, the content-id header is dropped from the images by tmail. I fixed it by modifying tmail (details here, but perhaps there is a better way.

Stephen Sykes

03/29/06 05:38:15 changed by CodeNinja

  • attachment tmail_content_id.rb added.

Additional fix for the Content-ID... Include this along with the Patch.

04/04/06 21:42:51 changed by rordev1.1.sts@xoxy.net

Rails 1.1 has some changes. If you have upgraded to 1.1 you need to change this patch to remove the to_mail definition from it - just leave the inline_attachment def.

The tmail fix is still needed.

See http://www.stephensykes.com/blog.html

04/13/06 14:14:31 changed by anonymous

  • cc changed from somekool@gmail.com to somekool@gmail.com, luben.manolov@gmail.com.

06/30/06 07:03:04 changed by max@maxidoors.ru

  • keywords set to activesupport strings helpers validations.

So, what's with the patch? This is really important feature, why not to include it?

07/27/06 03:26:07 changed by anonymous

  • summary changed from hi-world cup to [PATCH] Ability to add inline attachments (ie, inline images for html email).

09/02/06 19:36:50 changed by david

  • owner changed from David to jamis@37signals.com.

02/21/07 12:38:25 changed by josh

  • keywords changed from activesupport strings helpers validations to activesupport strings helpers validations needy.
  • status changed from new to closed.
  • resolution set to untested.

You need to write some units to go along with your code. Another suggestion would be to use 'svn diff' to create a diff file for your patch so the core team can easily patch it in. If you want it to go in, you have to make it as easy as possible for them.

04/07/08 23:39:52 changed by chrisw

  • cc changed from somekool@gmail.com, luben.manolov@gmail.com to somekool@gmail.com, luben.manolov@gmail.com, chris+rubyonrails@qwirx.com.

Hi all,

For reference for anyone who actually wants this to work (tests or no tests), here's what I had to do to get an image embedded in an HTML email in Thunderbird:

Apply the newly-attached actionmailer_inline_attachment.patch to vendor/rails/actionmailer. Note that changes to to_mail do appear to be necessary, contrary to what rordev1.1.sts@xoxy.net wrote above, as otherwise you can't get "disposition: inline" with a filename or remove the content-type

Write your mailer like this:

class MyMailer < ActionMailer::Base
  def initialize_defaults(method_name)
    super(method_name)
    @content_type = 'multipart/related; type=text/html'
  end

  def email(user)
    @recipients = [user.email]
    @subject    = "Hello"
    @from       = "Me <me@example.com>"
    @sent_on    = Time.now()
    @headers    = {}
    
    TMail::HeaderField::FNAME_TO_CLASS.delete 'content-id'
    
    part :content_type => "text/html",
      :body => render_message('email.text.html.rhtml', @body)

    inline_attachment :content_type => "image/jpeg", 
      :body => File.read("#{RAILS_ROOT}/public/images/logo.jpg"),
      :filename => 'logo.jpg',
      :cid => '<logo@example.com>'
  end
end

And then reference the CID in your HTML body part (email.text.html.rhtml):

<img src="cid:logo@example.com">

04/07/08 23:41:33 changed by chrisw

  • attachment actionmailer_inline_attachment.patch added.

Patch to actionmailer to enable inline attachments, needed for HTML email with embedded images