views:

2181

answers:

2

Hello,

I'm using the following code to send emails in rails:

class InvoiceMailer < ActionMailer::Base

  def invoice(invoice)
    from          CONFIG[:email]
    recipients    invoice.email
    subject       "Bevestiging Inschrijving #{invoice.course.name}"
    content_type  "multipart/alternative"

    part "text/html" do |p|
      p.body = render_message 'invoice_html', :invoice => invoice
    end

    part "text/plain" do |p|
      p.body = render_message 'invoice_plain', :invoice => invoice
    end

    pdf = Prawn::Document.new(:page_size => 'A4')
    PDFRenderer.render_invoice(pdf, invoice)
    attachment :content_type => "application/pdf", :body => pdf.render, :filename => "factuur.pdf"

    invoice.course.course_files.each do |file|
      attachment :content_type => file.content_type, :body => File.read(file.full_path), :filename => file.filename
    end
  end

end

It seems fine to me, and the emails also show up like they should in the Gmail web-interface. In Mail (the Apple program), however, I get just 1 attachment (where there should be 2) and there is no text. I just can't seem to figure out what's causing it.

I copied the email from the logs:


Sent mail to [email protected]

From: [email protected]
To: [email protected]
Subject: Bevestiging Inschrijving Authentiek Spreken
Mime-Version: 1.0
Content-Type: multipart/alternative; boundary=mimepart_4a5b035ea0d4_769515bbca0ce9b412a


--mimepart_4a5b035ea0d4_769515bbca0ce9b412a
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: Quoted-printable
Content-Disposition: inline



  
  
  
    

Dear sir

= --mimepart_4a5b035ea0d4_769515bbca0ce9b412a Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: Quoted-printable Content-Disposition: inline Dear sir * Foo= --mimepart_4a5b035ea0d4_769515bbca0ce9b412a Content-Type: application/pdf; name=factuur.pdf Content-Transfer-Encoding: Base64 Content-Disposition: attachment; filename=factuur.pdf JVBERi0xLjMK/////woxIDAgb2JqCjw8IC9DcmVhdG9yIChQcmF3bikKL1By b2R1Y2VyIChQcmF3bikKPj4KZW5kb2JqCjIgMCBvYmoKPDwgL0NvdW50IDEK ... ... ... MCBuIAp0cmFpbGVyCjw8IC9JbmZvIDEgMCBSCi9TaXplIDExCi9Sb290IDMg MCBSCj4+CnN0YXJ0eHJlZgo4Nzc1CiUlRU9GCg== --mimepart_4a5b035ea0d4_769515bbca0ce9b412a Content-Type: application/pdf; name=Spelregels.pdf Content-Transfer-Encoding: Base64 Content-Disposition: attachment; filename=Spelregels.pdf JVBERi0xLjQNJeLjz9MNCjYgMCBvYmoNPDwvTGluZWFyaXplZCAxL0wgMjEx NjYvTyA4L0UgMTY5NTIvTiAxL1QgMjEwMDAvSCBbIDg3NiAxOTJdPj4NZW5k ... ... ... MDIwNzQ4IDAwMDAwIG4NCnRyYWlsZXINCjw8L1NpemUgNj4+DQpzdGFydHhy ZWYNCjExNg0KJSVFT0YNCg== --mimepart_4a5b035ea0d4_769515bbca0ce9b412a--
+7  A: 

I suspect the issue is you're defining the overall email as multipart/alternative, suggesting each part is just an alternate view of the same message.

I use something like the following to send mixed html/plain emails with attachments, and it seems to work OK.

class InvoiceMailer < ActionMailer::Base

  def invoice(invoice)
    from          CONFIG[:email]
    recipients    invoice.email
    subject       "Bevestiging Inschrijving #{invoice.course.name}"
    content_type  "multipart/mixed"

    part(:content_type => "multipart/alternative") do |p|
      part "text/html" do |p|
        p.body = render_message 'invoice_html', :invoice => invoice
      end

      part "text/plain" do |p|
        p.body = render_message 'invoice_plain', :invoice => invoice
      end
    end

    pdf = Prawn::Document.new(:page_size => 'A4')
    PDFRenderer.render_invoice(pdf, invoice)
    attachment :content_type => "application/pdf", :body => pdf.render, :filename => "factuur.pdf"

    invoice.course.course_files.each do |file|
      attachment :content_type => file.content_type, :body => File.read(file.full_path), :filename => file.filename
    end
  end

end
James Healy
The idea is right, but this code doesn't work *as is*. See Dan's answer to make it actually work.
DR
+5  A: 

A nod to James on this, as it helped me get our mailer working right.

A slight refinement to this: First, we use the block arguments within the blocks to add parts (I had problems when I didn't).

Also, if you want to use layouts, you have to use #render directly. Here's an example of both principles at work. As shown above, you need to make sure you keep the html part last.

  def message_with_attachment_and_layout( options )
    from options[:from]
    recipients options[:to]
    subject options[:subject]
    content_type    "multipart/mixed"
    part :content_type => 'multipart/alternative' do |copy|
      copy.part :content_type => 'text/plain' do |plain|
        plain.body = render( :file => "#{options[:render]}.text.plain", 
          :layout => 'email', :body => options )
      end
      copy.part :content_type => 'text/html' do |html|
        html.body = render( :file => "#{options[:render]}.text.html", 
          :layout => 'email', :body => options )
      end
    end
    attachment :content_type => "application/pdf", 
      :filename => options[:attachment][:filename],
      :body => File.read( options[:attachment][:path] + '.pdf' )
  end

This example uses an options hash to create a generic multipart message with both attachments and layout, which you would use like this:

TestMailer.deliver_message_with_attachment_and_layout( 
  :from => '[email protected]', :to => '[email protected]', 
  :subject => 'test', :render => 'test', 
  :attachment => { :filename => 'A Nice PDF', 
    :path => 'path/to/some/nice/pdf' } )

(We don't actually do this: it's nicer to have each mailer fill in a lot of these details for you, but I thought it would make it easier to understand the code.)

Hope that helps. Best of luck.

Regards, Dan

Dan Yoder