diff options
-rw-r--r-- | actionmailer/CHANGELOG | 2 | ||||
-rw-r--r-- | actionmailer/lib/action_mailer/base.rb | 21 | ||||
-rw-r--r-- | actionmailer/lib/action_mailer/part.rb | 57 | ||||
-rw-r--r-- | actionmailer/lib/action_mailer/part_container.rb | 25 | ||||
-rwxr-xr-x | actionmailer/test/mail_service_test.rb | 27 |
5 files changed, 99 insertions, 33 deletions
diff --git a/actionmailer/CHANGELOG b/actionmailer/CHANGELOG index 93995081f0..4338d4d3f6 100644 --- a/actionmailer/CHANGELOG +++ b/actionmailer/CHANGELOG @@ -1,5 +1,7 @@ *SVN* +* Allow for nested parts in multipart mails #1570 [Flurin Egger] + * Normalize line endings in outgoing mail bodies to "\n" #1536 [John Long] * Allow template to be explicitly specified #1448 [tuxie@dekadance.se] diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 919d92670f..9a432faa32 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -1,5 +1,6 @@ require 'action_mailer/adv_attr_accessor' require 'action_mailer/part' +require 'action_mailer/part_container' require 'tmail/net' module ActionMailer #:nodoc: @@ -108,6 +109,7 @@ module ActionMailer #:nodoc: # pick a different charset from inside a method with <tt>@charset</tt>. class Base include ActionMailer::AdvAttrAccessor + include ActionMailer::PartContainer private_class_method :new #:nodoc: @@ -220,25 +222,6 @@ module ActionMailer #:nodoc: return @mail end - # Add a part to a multipart message, with the given content-type. The - # part itself is yielded to the block, so that other properties (charset, - # body, headers, etc.) can be set on it. - def part(params) - params = {:content_type => params} if String === params - part = Part.new(params) - yield part if block_given? - @parts << part - end - - # Add an attachment to a multipart message. This is simply a part with the - # content-disposition set to "attachment". - def attachment(params, &block) - params = { :content_type => params } if String === params - params = { :disposition => "attachment", - :transfer_encoding => "base64" }.merge(params) - part(params, &block) - end - private def render_message(method_name, body) initialize_template_class(body).render_file(method_name) diff --git a/actionmailer/lib/action_mailer/part.rb b/actionmailer/lib/action_mailer/part.rb index cb3a3dc053..b47961a744 100644 --- a/actionmailer/lib/action_mailer/part.rb +++ b/actionmailer/lib/action_mailer/part.rb @@ -1,9 +1,11 @@ require 'action_mailer/adv_attr_accessor' +require 'action_mailer/part_container' module ActionMailer class Part #:nodoc: include ActionMailer::AdvAttrAccessor + include ActionMailer::PartContainer adv_attr_accessor :content_type, :content_disposition, :charset, :body adv_attr_accessor :filename, :transfer_encoding, :headers @@ -16,27 +18,54 @@ module ActionMailer @filename = params[:filename] @transfer_encoding = params[:transfer_encoding] || "quoted-printable" @headers = params[:headers] || {} + @parts = [] end def to_mail(defaults) part = TMail::Mail.new - part.set_content_type(content_type || defaults.content_type, nil, - "charset" => (content_disposition == "attachment" ? - nil : (charset || defaults.charset)), - "name" => filename) - part.set_content_disposition(content_disposition, - "filename" => filename) - - part.content_transfer_encoding = transfer_encoding || "quoted-printable" - case (transfer_encoding || "").downcase - when "base64" then - part.body = TMail::Base64.folding_encode(body) - when "quoted-printable" - part.body = [body].pack("M*") + + if @parts.empty? + part.content_transfer_encoding = transfer_encoding || "quoted-printable" + case (transfer_encoding || "").downcase + when "base64" then + part.body = TMail::Base64.folding_encode(body) + when "quoted-printable" + part.body = [body].pack("M*") + else + part.body = body + end + + # Always set the content_type after setting the body and or parts! + # Also don't set filename and name when there is none (like in + # non-attachment parts) + if content_disposition == "attachment" + part.set_content_type(content_type || defaults.content_type, nil, + "charset" => nil, + "name" => filename) + part.set_content_disposition(content_disposition, "filename" => filename) else + part.set_content_type(content_type || defaults.content_type, nil, + "charset" => (charset || defaults.charset)) + part.set_content_disposition(content_disposition) + end + else + if String === body + part = TMail::Mail.new part.body = body + part.set_content_type content_type, nil, { "charset" => charset } + part.set_content_disposition "inline" + m.parts << part + end + + @parts.each do |p| + prt = (TMail::Mail === p ? p : p.to_mail(defaults)) + part.parts << prt + end + + part.set_content_type(content_type, nil, { "charset" => charset }) if content_type =~ /multipart/ end - + + part end end diff --git a/actionmailer/lib/action_mailer/part_container.rb b/actionmailer/lib/action_mailer/part_container.rb new file mode 100644 index 0000000000..57ad2f5157 --- /dev/null +++ b/actionmailer/lib/action_mailer/part_container.rb @@ -0,0 +1,25 @@ +module ActionMailer + module PartContainer + attr_reader :parts + + # Add a part to a multipart message, with the given content-type. The + # part itself is yielded to the block, so that other properties (charset, + # body, headers, etc.) can be set on it. + def part(params) + params = {:content_type => params} if String === params + part = Part.new(params) + yield part if block_given? + @parts << part + end + + # Add an attachment to a multipart message. This is simply a part with the + # content-disposition set to "attachment". + def attachment(params, &block) + params = { :content_type => params } if String === params + params = { :disposition => "attachment", + :transfer_encoding => "base64" }.merge(params) + part(params, &block) + end + + end +end diff --git a/actionmailer/test/mail_service_test.rb b/actionmailer/test/mail_service_test.rb index f5402c6617..01b71801b8 100755 --- a/actionmailer/test/mail_service_test.rb +++ b/actionmailer/test/mail_service_test.rb @@ -145,6 +145,20 @@ class TestMailer < ActionMailer::Base "line #5\n\nline#6\r\n\r\nline #7" end + def nested_multipart(recipient) + recipients recipient + subject "nested multipart" + from "test@example.com" + content_type "multipart/mixed" + part :content_type => "multipart/alternative", :content_disposition => "inline" do |p| + p.part :content_type => "text/plain", :body => "test text\nline #2" + p.part :content_type => "text/html", :body => "<b>test</b> HTML<br/>\nline #2" + end + attachment :content_type => "application/octet-stream",:filename => "test.txt", :body => "test abcdefghijklmnopqstuvwxyz" + + end + + class <<self attr_accessor :received_body end @@ -179,6 +193,19 @@ class ActionMailerTest < Test::Unit::TestCase @recipient = 'test@localhost' end + def test_nested_parts + created = nil + assert_nothing_raised { created = TestMailer.create_nested_multipart(@recipient)} + assert_equal 2,created.parts.size + assert_equal 2,created.parts.first.parts.size + + assert_equal "multipart/mixed", created.content_type + assert_equal "multipart/alternative", created.parts.first.content_type + assert_equal "text/plain", created.parts.first.parts.first.content_type + assert_equal "text/html", created.parts.first.parts[1].content_type + assert_equal "application/octet-stream", created.parts[1].content_type + end + def test_signed_up expected = new_mail expected.to = @recipient |