aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actionmailer/CHANGELOG2
-rw-r--r--actionmailer/lib/action_mailer/base.rb21
-rw-r--r--actionmailer/lib/action_mailer/part.rb57
-rw-r--r--actionmailer/lib/action_mailer/part_container.rb25
-rwxr-xr-xactionmailer/test/mail_service_test.rb27
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