aboutsummaryrefslogtreecommitdiffstats
path: root/actionmailer/lib/action_mailer/base.rb
diff options
context:
space:
mode:
Diffstat (limited to 'actionmailer/lib/action_mailer/base.rb')
-rw-r--r--actionmailer/lib/action_mailer/base.rb219
1 files changed, 110 insertions, 109 deletions
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb
index dfa1b6444b..25b8e4874e 100644
--- a/actionmailer/lib/action_mailer/base.rb
+++ b/actionmailer/lib/action_mailer/base.rb
@@ -1,9 +1,10 @@
require 'active_support/core_ext/class'
+require 'mail'
+require 'action_mailer/tmail_compat'
module ActionMailer #:nodoc:
# Action Mailer allows you to send email from your application using a mailer model and views.
#
- #
# = Mailer Models
#
# To use Action Mailer, you need to create a mailer model.
@@ -22,7 +23,8 @@ module ActionMailer #:nodoc:
# bcc ["bcc@example.com", "Order Watcher <watcher@example.com>"]
# from "system@example.com"
# subject "New account information"
- # body :account => recipient
+ #
+ # @account = recipient
# end
# end
#
@@ -42,13 +44,6 @@ module ActionMailer #:nodoc:
# address. Setting this is useful when you want delivery notifications sent to a different address than
# the one in <tt>from</tt>.
#
- # The <tt>body</tt> method has special behavior. It takes a hash which generates an instance variable
- # named after each key in the hash containing the value that that key points to.
- #
- # So, for example, <tt>body :account => recipient</tt> would result
- # in an instance variable <tt>@account</tt> with the value of <tt>recipient</tt> being accessible in the
- # view.
- #
#
# = Mailer views
#
@@ -68,7 +63,12 @@ module ActionMailer #:nodoc:
# You can even use Action Pack helpers in these views. For example:
#
# You got a new note!
- # <%= truncate(note.body, 25) %>
+ # <%= truncate(@note.body, 25) %>
+ #
+ # If you need to access the subject, from or the recipients in the view, you can do that through mailer object:
+ #
+ # You got a new note from <%= mailer.from %>!
+ # <%= truncate(@note.body, 25) %>
#
#
# = Generating URLs
@@ -150,7 +150,7 @@ module ActionMailer #:nodoc:
#
# part "text/plain" do |p|
# p.body = render_message("signup-as-plain", :account => recipient)
- # p.transfer_encoding = "base64"
+ # p.content_transfer_encoding = "base64"
# end
# end
# end
@@ -250,29 +250,23 @@ module ActionMailer #:nodoc:
# <tt>["text/html", "text/enriched", "text/plain"]</tt>. 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 AdvAttrAccessor, PartContainer, Quoting, Utils
+ class Base < AbstractController::Base
+ include Quoting
+ extend AdvAttrAccessor
- include AbstractController::RenderingController
+ include AbstractController::Logger
+ include AbstractController::Rendering
include AbstractController::LocalizedCache
include AbstractController::Layouts
-
include AbstractController::Helpers
- helper MailHelper
- if Object.const_defined?(:ActionController)
- include ActionController::UrlWriter
- end
+ helper ActionMailer::MailHelper
+ include ActionController::UrlWriter
include ActionMailer::DeprecatedBody
private_class_method :new #:nodoc:
- class_inheritable_accessor :view_paths
- self.view_paths = []
-
- cattr_accessor :logger
-
@@raise_delivery_errors = true
cattr_accessor :raise_delivery_errors
@@ -291,10 +285,16 @@ module ActionMailer #:nodoc:
@@default_mime_version = "1.0"
cattr_accessor :default_mime_version
- @@default_implicit_parts_order = [ "text/html", "text/enriched", "text/plain" ]
+ # This specifies the order that the parts of a multipart email will be. Usually you put
+ # text/plain at the top so someone without a MIME capable email reader can read the plain
+ # text of your email first.
+ #
+ # Any content type that is not listed here will be inserted in the order you add them to
+ # the email after the content types you list here.
+ @@default_implicit_parts_order = [ "text/plain", "text/enriched", "text/html" ]
cattr_accessor :default_implicit_parts_order
- @@protected_instance_variables = []
+ @@protected_instance_variables = %w(@parts @mail)
cattr_reader :protected_instance_variables
# Specify the BCC addresses for the message
@@ -344,24 +344,13 @@ module ActionMailer #:nodoc:
# have multiple mailer methods share the same template.
adv_attr_accessor :template
- # The mail and action_name instances referenced by this mailer.
- attr_reader :mail, :action_name
-
- # Where the response body is stored.
- attr_internal :response_body
-
# Override the mailer name, which defaults to an inflected version of the
# mailer's class name. If you want to use a template in a non-standard
# location, you can use this to specify that location.
- attr_writer :mailer_name
+ adv_attr_accessor :mailer_name
- def mailer_name(value = nil)
- if value
- @mailer_name = value
- else
- @mailer_name || self.class.mailer_name
- end
- end
+ # Expose the internal mail
+ attr_reader :mail
# Alias controller_path to mailer_name so render :partial in views work.
alias :controller_path :mailer_name
@@ -376,6 +365,7 @@ module ActionMailer #:nodoc:
def mailer_name
@mailer_name ||= name.underscore
end
+ alias :controller_path :mailer_name
def delivery_method=(method_name)
@delivery_method = ActionMailer::DeliveryMethod.lookup_method(method_name)
@@ -411,8 +401,7 @@ module ActionMailer #:nodoc:
# end
def receive(raw_email)
logger.info "Received mail:\n #{raw_email}" unless logger.nil?
- mail = TMail::Mail.parse(raw_email)
- mail.base64_decode
+ mail = Mail.new(raw_email)
new.receive(mail)
end
@@ -447,21 +436,47 @@ module ActionMailer #:nodoc:
superclass_delegating_reader :delivery_method
self.delivery_method = :smtp
+ # 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
+ if custom_headers = params.delete(:headers)
+ ActiveSupport::Deprecation.warn('Passing custom headers with :headers => {} is deprecated. ' <<
+ 'Please just pass in custom headers directly.', caller[0,10])
+ params.merge!(custom_headers)
+ end
+ part = Mail::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)
+ super # Run deprecation hooks
+
+ params = { :content_type => params } if String === params
+ params = { :content_disposition => "attachment",
+ :content_transfer_encoding => "base64" }.merge(params)
+
+ part(params, &block)
+ end
+
# Instantiate a new mailer object. If +method_name+ is not +nil+, the mailer
# will be initialized according to the named method. If not, the mailer will
# remain uninitialized (useful when you only need to invoke the "receive"
# method, for instance).
- def initialize(method_name=nil, *parameters) #:nodoc:
- @_response_body = nil
+ def initialize(method_name=nil, *args)
super()
- create!(method_name, *parameters) if method_name
+ process(method_name, *args) if method_name
end
- # Initialize the mailer via the given +method_name+. The body will be
- # rendered and a new TMail::Mail object created.
- def create!(method_name, *parameters) #:nodoc:
+ # Process the mailer via the given +method_name+. The body will be
+ # rendered and a new Mail object created.
+ def process(method_name, *args)
initialize_defaults(method_name)
- __send__(method_name, *parameters)
+ super
# Create e-mail parts
create_parts
@@ -470,11 +485,11 @@ module ActionMailer #:nodoc:
@subject ||= I18n.t(:subject, :scope => [:actionmailer, mailer_name, method_name],
:default => method_name.humanize)
- # build the mail object itself
- @mail = create_mail
+ # Build the mail object itself
+ create_mail
end
- # Delivers a TMail::Mail object. By default, it delivers the cached mail
+ # Delivers a Mail object. By default, it delivers the cached mail
# object (from the <tt>create!</tt> method). If no cached mail object exists, and
# no alternate has been given as the parameter, this will fail.
def deliver!(mail = @mail)
@@ -485,7 +500,7 @@ module ActionMailer #:nodoc:
logger.debug "\n#{mail.encoded}"
end
- ActiveSupport::Notifications.instrument(:deliver_mail, :mail => @mail) do
+ ActiveSupport::Notifications.instrument(:deliver_mail, :mail => mail) do
begin
self.delivery_method.perform_delivery(mail) if perform_deliveries
rescue Exception => e # Net::SMTP errors or sendmail pipe errors
@@ -501,15 +516,14 @@ module ActionMailer #:nodoc:
# Set up the default values for the various instance variables of this
# mailer. Subclasses may override this method to provide different
# defaults.
- def initialize_defaults(method_name)
+ def initialize_defaults(method_name) #:nodoc:
@charset ||= @@default_charset.dup
@content_type ||= @@default_content_type.dup
@implicit_parts_order ||= @@default_implicit_parts_order.dup
@mime_version ||= @@default_mime_version.dup if @@default_mime_version
- @mailer_name ||= self.class.mailer_name
+ @mailer_name ||= self.class.mailer_name.dup
@template ||= method_name
- @action_name = @template
@parts ||= []
@headers ||= {}
@@ -518,29 +532,18 @@ module ActionMailer #:nodoc:
super # Run deprecation hooks
end
- def create_parts
+ def create_parts #:nodoc:
super # Run deprecation hooks
if String === response_body
- @parts.unshift Part.new(
- :content_type => "text/plain",
- :disposition => "inline",
- :charset => charset,
- :body => response_body
- )
+ @parts.unshift create_inline_part(response_body)
else
- self.class.template_root.find_all(@template, {}, mailer_name).each do |template|
- @parts << Part.new(
- :content_type => template.mime_type ? template.mime_type.to_s : "text/plain",
- :disposition => "inline",
- :charset => charset,
- :body => render_to_body(:_template => template)
- )
+ self.class.template_root.find_all(@template, {}, @mailer_name).each do |template|
+ @parts << create_inline_part(render_to_body(:_template => template), template.mime_type)
end
if @parts.size > 1
@content_type = "multipart/alternative" if @content_type !~ /^multipart/
- @parts = sort_parts(@parts, @implicit_parts_order)
end
# If this is a multipart e-mail add the mime_version if it is not
@@ -549,37 +552,19 @@ module ActionMailer #:nodoc:
end
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
+ def create_inline_part(body, mime_type=nil) #:nodoc:
+ ct = mime_type || "text/plain"
+ main_type, sub_type = split_content_type(ct.to_s)
- parts
+ Mail::Part.new(
+ :content_type => [main_type, sub_type, {:charset => charset}],
+ :content_disposition => "inline",
+ :body => body
+ )
end
- def create_mail
- m = TMail::Mail.new
+ def create_mail #:nodoc:
+ m = Mail.new
m.subject, = quote_any_if_necessary(charset, subject)
m.to, m.from = quote_any_address_if_necessary(charset, recipients, from)
@@ -592,27 +577,43 @@ module ActionMailer #:nodoc:
headers.each { |k, v| m[k] = v }
real_content_type, ctype_attrs = parse_content_type
-
- if @parts.empty?
- m.set_content_type(real_content_type, nil, ctype_attrs)
- m.body = normalize_new_lines(body)
- elsif @parts.size == 1 && @parts.first.parts.empty?
- m.set_content_type(real_content_type, nil, ctype_attrs)
- m.body = normalize_new_lines(@parts.first.body)
+ main_type, sub_type = split_content_type(real_content_type)
+
+ if @parts.size == 1 && @parts.first.parts.empty?
+ m.content_type([main_type, sub_type, ctype_attrs])
+ m.body = @parts.first.body.encoded
else
@parts.each do |p|
- part = (TMail::Mail === p ? p : p.to_mail(self))
- m.parts << part
+ m.add_part(p)
end
+ m.body.set_sort_order(@implicit_parts_order)
+ m.body.sort_parts!
+
if real_content_type =~ /multipart/
ctype_attrs.delete "charset"
- m.set_content_type(real_content_type, nil, ctype_attrs)
+ m.content_type([main_type, sub_type, ctype_attrs])
end
end
+ m.content_transfer_encoding = '8bit' unless m.body.only_us_ascii?
+
@mail = m
end
+
+ def split_content_type(ct) #:nodoc:
+ ct.to_s.split("/")
+ end
+
+ def parse_content_type(defaults=nil) #:nodoc:
+ if @content_type.blank?
+ [ nil, {} ]
+ else
+ ctype, *attrs = @content_type.split(/;\s*/)
+ attrs = attrs.inject({}) { |h,s| k,v = s.split(/\=/, 2); h[k] = v; h }
+ [ctype, {"charset" => @charset}.merge(attrs)]
+ end
+ end
end
end