From 5c981528f20d53ad441b61c70350cc53fe3e8b6f Mon Sep 17 00:00:00 2001 From: Jamis Buck Date: Fri, 1 Jul 2005 20:43:40 +0000 Subject: Better multipart support with implicit multipart/alternative and sorting of subparts [John Long] git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@1586 5ecf4fe2-1ee6-0310-87b1-e25e094e27de --- actionmailer/CHANGELOG | 2 + actionmailer/lib/action_mailer/base.rb | 49 +++++++++++++++++++++- .../implicitly_multipart_example.text.yaml.rhtml | 1 + actionmailer/test/mail_service_test.rb | 18 ++++++-- 4 files changed, 65 insertions(+), 5 deletions(-) create mode 100644 actionmailer/test/fixtures/test_mailer/implicitly_multipart_example.text.yaml.rhtml diff --git a/actionmailer/CHANGELOG b/actionmailer/CHANGELOG index 4338d4d3f6..c21a89e87c 100644 --- a/actionmailer/CHANGELOG +++ b/actionmailer/CHANGELOG @@ -1,5 +1,7 @@ *SVN* +* Better multipart support with implicit multipart/alternative and sorting of subparts [John Long] + * Allow for nested parts in multipart mails #1570 [Flurin Egger] * Normalize line endings in outgoing mail bodies to "\n" #1536 [John Long] diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 9a432faa32..21f4086008 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -106,7 +106,14 @@ module ActionMailer #:nodoc: # for unit and functional testing. # # * default_charset - The default charset used for the body and to encode the subject. Defaults to UTF-8. You can also - # pick a different charset from inside a method with @charset. + # pick a different charset from inside a method with @charset. + # * default_content_type - The default content type used for main part of the message. Defaults to "text/plain". You + # can also pick a different content type from inside a method with @content_type. + # * default_implicit_parts_order - When a message is built implicitly (i.e. multiple parts are assemble from templates + # which specify the content type in their filenames) this variable controls how the parts are ordered. Defaults to + # ["text/html", "text/enriched", "text/plain"]. Items that appear first in the array have higher priority in the mail client + # and appear last in the mime encoded message. You can also pick a different order from inside a method with + # @implicit_parts_order. class Base include ActionMailer::AdvAttrAccessor include ActionMailer::PartContainer @@ -144,8 +151,12 @@ module ActionMailer #:nodoc: @@default_content_type = "text/plain" cattr_accessor :default_content_type + @@default_implicit_parts_order = [ "text/html", "text/enriched", "text/plain" ] + cattr_accessor :default_implicit_parts_order + adv_attr_accessor :recipients, :subject, :body, :from, :sent_on, :headers, - :bcc, :cc, :charset, :content_type, :template + :bcc, :cc, :charset, :content_type, :implicit_parts_order, + :template attr_reader :mail @@ -163,6 +174,7 @@ module ActionMailer #:nodoc: @bcc = @cc = @from = @recipients = @sent_on = @subject = nil @charset = @@default_charset.dup @content_type = @@default_content_type.dup + @implicit_parts_order = @@default_implicit_parts_order.dup @template = method_name @parts = [] @headers = {} @@ -184,6 +196,10 @@ module ActionMailer #:nodoc: :disposition => "inline", :charset => charset, :body => render_message(File.basename(path).split(".")[0..-2].join('.'), @body)) end + unless @parts.empty? + @content_type = "multipart/alternative" + @parts = sort_parts(@parts, @implicit_parts_order) + end end # Then, if there were such templates, we check to see if we ought to @@ -235,6 +251,35 @@ module ActionMailer #:nodoc: ActionView::Base.new(template_path, assigns, self) end + def sort_parts(parts, order = []) + order = order.collect { |s| s.downcase } + + parts = parts.sort do |a, b| + a_ct = a.content_type.downcase + b_ct = b.content_type.downcase + + a_in = order.include? a_ct + b_in = order.include? b_ct + + s = case + when a_in && b_in + order.index(a_ct) <=> order.index(b_ct) + when a_in + -1 + when b_in + 1 + else + a_ct <=> b_ct + end + + # reverse the ordering because parts that come last are displayed + # first in mail clients + (s * -1) + end + + parts + end + def create_mail m = TMail::Mail.new diff --git a/actionmailer/test/fixtures/test_mailer/implicitly_multipart_example.text.yaml.rhtml b/actionmailer/test/fixtures/test_mailer/implicitly_multipart_example.text.yaml.rhtml new file mode 100644 index 0000000000..c14348c770 --- /dev/null +++ b/actionmailer/test/fixtures/test_mailer/implicitly_multipart_example.text.yaml.rhtml @@ -0,0 +1 @@ +yaml to: <%= @recipient %> \ No newline at end of file diff --git a/actionmailer/test/mail_service_test.rb b/actionmailer/test/mail_service_test.rb index 01b71801b8..85fe3ad69b 100755 --- a/actionmailer/test/mail_service_test.rb +++ b/actionmailer/test/mail_service_test.rb @@ -111,12 +111,13 @@ class TestMailer < ActionMailer::Base :body => "123456789" end - def implicitly_multipart_example(recipient) + def implicitly_multipart_example(recipient, order = nil) @recipients = recipient @subject = "multipart example" @from = "test@example.com" @sent_on = Time.local 2004, 12, 12 @body = { "recipient" => recipient } + @implicit_parts_order = order unless order.nil? end def html_mail(recipient) @@ -551,11 +552,22 @@ EOF def test_implicitly_multipart_messages mail = TestMailer.create_implicitly_multipart_example(@recipient) - assert_equal 2, mail.parts.length - assert_equal "text/html", mail.parts[0].content_type + assert_equal 3, mail.parts.length + assert_equal "multipart/alternative", mail.content_type + assert_equal "text/yaml", mail.parts[0].content_type assert_equal "utf-8", mail.parts[0].sub_header("content-type", "charset") assert_equal "text/plain", mail.parts[1].content_type assert_equal "utf-8", mail.parts[1].sub_header("content-type", "charset") + assert_equal "text/html", mail.parts[2].content_type + assert_equal "utf-8", mail.parts[2].sub_header("content-type", "charset") + end + + def test_implicitly_multipart_messages_with_custom_order + mail = TestMailer.create_implicitly_multipart_example(@recipient, ["text/yaml", "text/plain"]) + assert_equal 3, mail.parts.length + assert_equal "text/html", mail.parts[0].content_type + assert_equal "text/plain", mail.parts[1].content_type + assert_equal "text/yaml", mail.parts[2].content_type end def test_html_mail -- cgit v1.2.3