PageRenderTime 33ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/actionmailer/lib/action_mailer/base.rb

https://github.com/ghar/rails
Ruby | 736 lines | 204 code | 48 blank | 484 comment | 19 complexity | bd24e0df55a2b42cb3dedd19b21f17f6 MD5 | raw file
  1. require 'mail'
  2. require 'action_mailer/collector'
  3. require 'active_support/core_ext/array/wrap'
  4. require 'active_support/core_ext/object/blank'
  5. require 'active_support/core_ext/proc'
  6. require 'active_support/core_ext/string/inflections'
  7. require 'active_support/core_ext/hash/except'
  8. require 'action_mailer/log_subscriber'
  9. module ActionMailer #:nodoc:
  10. # Action Mailer allows you to send email from your application using a mailer model and views.
  11. #
  12. # = Mailer Models
  13. #
  14. # To use Action Mailer, you need to create a mailer model.
  15. #
  16. # $ rails generate mailer Notifier
  17. #
  18. # The generated model inherits from <tt>ActionMailer::Base</tt>. Emails are defined by creating methods
  19. # within the model which are then used to set variables to be used in the mail template, to
  20. # change options on the mail, or to add attachments.
  21. #
  22. # Examples:
  23. #
  24. # class Notifier < ActionMailer::Base
  25. # default :from => 'no-reply@example.com',
  26. # :return_path => 'system@example.com'
  27. #
  28. # def welcome(recipient)
  29. # @account = recipient
  30. # mail(:to => recipient.email_address_with_name,
  31. # :bcc => ["bcc@example.com", "Order Watcher <watcher@example.com>"])
  32. # end
  33. # end
  34. #
  35. # Within the mailer method, you have access to the following methods:
  36. #
  37. # * <tt>attachments[]=</tt> - Allows you to add attachments to your email in an intuitive
  38. # manner; <tt>attachments['filename.png'] = File.read('path/to/filename.png')</tt>
  39. #
  40. # * <tt>attachments.inline[]=</tt> - Allows you to add an inline attachment to your email
  41. # in the same manner as <tt>attachments[]=</tt>
  42. #
  43. # * <tt>headers[]=</tt> - Allows you to specify any header field in your email such
  44. # as <tt>headers['X-No-Spam'] = 'True'</tt>. Note, while most fields like <tt>To:</tt>
  45. # <tt>From:</tt> can only appear once in an email header, other fields like <tt>X-Anything</tt>
  46. # can appear multiple times. If you want to change a field that can appear multiple times,
  47. # you need to set it to nil first so that Mail knows you are replacing it and not adding
  48. # another field of the same name.
  49. #
  50. # * <tt>headers(hash)</tt> - Allows you to specify multiple headers in your email such
  51. # as <tt>headers({'X-No-Spam' => 'True', 'In-Reply-To' => '1234@message.id'})</tt>
  52. #
  53. # * <tt>mail</tt> - Allows you to specify email to be sent.
  54. #
  55. # The hash passed to the mail method allows you to specify any header that a Mail::Message
  56. # will accept (any valid Email header including optional fields).
  57. #
  58. # The mail method, if not passed a block, will inspect your views and send all the views with
  59. # the same name as the method, so the above action would send the +welcome.text.erb+ view
  60. # file as well as the +welcome.text.html.erb+ view file in a +multipart/alternative+ email.
  61. #
  62. # If you want to explicitly render only certain templates, pass a block:
  63. #
  64. # mail(:to => user.email) do |format|
  65. # format.text
  66. # format.html
  67. # end
  68. #
  69. # The block syntax is also useful in providing information specific to a part:
  70. #
  71. # mail(:to => user.email) do |format|
  72. # format.text(:content_transfer_encoding => "base64")
  73. # format.html
  74. # end
  75. #
  76. # Or even to render a special view:
  77. #
  78. # mail(:to => user.email) do |format|
  79. # format.text
  80. # format.html { render "some_other_template" }
  81. # end
  82. #
  83. # = Mailer views
  84. #
  85. # Like Action Controller, each mailer class has a corresponding view directory in which each
  86. # method of the class looks for a template with its name.
  87. #
  88. # To define a template to be used with a mailing, create an <tt>.erb</tt> file with the same
  89. # name as the method in your mailer model. For example, in the mailer defined above, the template at
  90. # <tt>app/views/notifier/welcome.text.erb</tt> would be used to generate the email.
  91. #
  92. # Variables defined in the model are accessible as instance variables in the view.
  93. #
  94. # Emails by default are sent in plain text, so a sample view for our model example might look like this:
  95. #
  96. # Hi <%= @account.name %>,
  97. # Thanks for joining our service! Please check back often.
  98. #
  99. # You can even use Action Pack helpers in these views. For example:
  100. #
  101. # You got a new note!
  102. # <%= truncate(@note.body, 25) %>
  103. #
  104. # If you need to access the subject, from or the recipients in the view, you can do that through message object:
  105. #
  106. # You got a new note from <%= message.from %>!
  107. # <%= truncate(@note.body, 25) %>
  108. #
  109. #
  110. # = Generating URLs
  111. #
  112. # URLs can be generated in mailer views using <tt>url_for</tt> or named routes. Unlike controllers from
  113. # Action Pack, the mailer instance doesn't have any context about the incoming request, so you'll need
  114. # to provide all of the details needed to generate a URL.
  115. #
  116. # When using <tt>url_for</tt> you'll need to provide the <tt>:host</tt>, <tt>:controller</tt>, and <tt>:action</tt>:
  117. #
  118. # <%= url_for(:host => "example.com", :controller => "welcome", :action => "greeting") %>
  119. #
  120. # When using named routes you only need to supply the <tt>:host</tt>:
  121. #
  122. # <%= users_url(:host => "example.com") %>
  123. #
  124. # You should use the <tt>named_route_url</tt> style (which generates absolute URLs) and avoid using the
  125. # <tt>named_route_path</tt> style (which generates relative URLs), since clients reading the mail will
  126. # have no concept of a current URL from which to determine a relative path.
  127. #
  128. # It is also possible to set a default host that will be used in all mailers by setting the <tt>:host</tt>
  129. # option as a configuration option in <tt>config/application.rb</tt>:
  130. #
  131. # config.action_mailer.default_url_options = { :host => "example.com" }
  132. #
  133. # When you decide to set a default <tt>:host</tt> for your mailers, then you need to make sure to use the
  134. # <tt>:only_path => false</tt> option when using <tt>url_for</tt>. Since the <tt>url_for</tt> view helper
  135. # will generate relative URLs by default when a <tt>:host</tt> option isn't explicitly provided, passing
  136. # <tt>:only_path => false</tt> will ensure that absolute URLs are generated.
  137. #
  138. # = Sending mail
  139. #
  140. # Once a mailer action and template are defined, you can deliver your message or create it and save it
  141. # for delivery later:
  142. #
  143. # Notifier.welcome(david).deliver # sends the email
  144. # mail = Notifier.welcome(david) # => a Mail::Message object
  145. # mail.deliver # sends the email
  146. #
  147. # You never instantiate your mailer class. Rather, you just call the method you defined on the class itself.
  148. #
  149. # = Multipart Emails
  150. #
  151. # Multipart messages can also be used implicitly because Action Mailer will automatically detect and use
  152. # multipart templates, where each template is named after the name of the action, followed by the content
  153. # type. Each such detected template will be added as a separate part to the message.
  154. #
  155. # For example, if the following templates exist:
  156. # * signup_notification.text.erb
  157. # * signup_notification.text.html.erb
  158. # * signup_notification.text.xml.builder
  159. # * signup_notification.text.yaml.erb
  160. #
  161. # Each would be rendered and added as a separate part to the message, with the corresponding content
  162. # type. The content type for the entire message is automatically set to <tt>multipart/alternative</tt>,
  163. # which indicates that the email contains multiple different representations of the same email
  164. # body. The same instance variables defined in the action are passed to all email templates.
  165. #
  166. # Implicit template rendering is not performed if any attachments or parts have been added to the email.
  167. # This means that you'll have to manually add each part to the email and set the content type of the email
  168. # to <tt>multipart/alternative</tt>.
  169. #
  170. # = Attachments
  171. #
  172. # Sending attachment in emails is easy:
  173. #
  174. # class ApplicationMailer < ActionMailer::Base
  175. # def welcome(recipient)
  176. # attachments['free_book.pdf'] = File.read('path/to/file.pdf')
  177. # mail(:to => recipient, :subject => "New account information")
  178. # end
  179. # end
  180. #
  181. # Which will (if it had both a <tt>welcome.text.erb</tt> and <tt>welcome.text.html.erb</tt>
  182. # template in the view directory), send a complete <tt>multipart/mixed</tt> email with two parts,
  183. # the first part being a <tt>multipart/alternative</tt> with the text and HTML email parts inside,
  184. # and the second being a <tt>application/pdf</tt> with a Base64 encoded copy of the file.pdf book
  185. # with the filename +free_book.pdf+.
  186. #
  187. # = Inline Attachments
  188. #
  189. # You can also specify that a file should be displayed inline with other HTML. This is useful
  190. # if you want to display a corporate logo or a photo.
  191. #
  192. # class ApplicationMailer < ActionMailer::Base
  193. # def welcome(recipient)
  194. # attachments.inline['photo.png'] = File.read('path/to/photo.png')
  195. # mail(:to => recipient, :subject => "Here is what we look like")
  196. # end
  197. # end
  198. #
  199. # And then to reference the image in the view, you create a <tt>welcome.html.erb</tt> file and
  200. # make a call to +image_tag+ passing in the attachment you want to display and then call
  201. # +url+ on the attachment to get the relative content id path for the image source:
  202. #
  203. # <h1>Please Don't Cringe</h1>
  204. #
  205. # <%= image_tag attachments['photo.png'].url -%>
  206. #
  207. # As we are using Action View's +image_tag+ method, you can pass in any other options you want:
  208. #
  209. # <h1>Please Don't Cringe</h1>
  210. #
  211. # <%= image_tag attachments['photo.png'].url, :alt => 'Our Photo', :class => 'photo' -%>
  212. #
  213. # = Observing and Intercepting Mails
  214. #
  215. # Action Mailer provides hooks into the Mail observer and interceptor methods. These allow you to
  216. # register classes that are called during the mail delivery life cycle.
  217. #
  218. # An observer class must implement the <tt>:delivered_email(message)</tt> method which will be
  219. # called once for every email sent after the email has been sent.
  220. #
  221. # An interceptor class must implement the <tt>:delivering_email(message)</tt> method which will be
  222. # called before the email is sent, allowing you to make modifications to the email before it hits
  223. # the delivery agents. Your class should make any needed modifications directly to the passed
  224. # in Mail::Message instance.
  225. #
  226. # = Default Hash
  227. #
  228. # Action Mailer provides some intelligent defaults for your emails, these are usually specified in a
  229. # default method inside the class definition:
  230. #
  231. # class Notifier < ActionMailer::Base
  232. # default :sender => 'system@example.com'
  233. # end
  234. #
  235. # You can pass in any header value that a <tt>Mail::Message</tt> accepts. Out of the box,
  236. # <tt>ActionMailer::Base</tt> sets the following:
  237. #
  238. # * <tt>:mime_version => "1.0"</tt>
  239. # * <tt>:charset => "UTF-8",</tt>
  240. # * <tt>:content_type => "text/plain",</tt>
  241. # * <tt>:parts_order => [ "text/plain", "text/enriched", "text/html" ]</tt>
  242. #
  243. # <tt>parts_order</tt> and <tt>charset</tt> are not actually valid <tt>Mail::Message</tt> header fields,
  244. # but Action Mailer translates them appropriately and sets the correct values.
  245. #
  246. # As you can pass in any header, you need to either quote the header as a string, or pass it in as
  247. # an underscored symbol, so the following will work:
  248. #
  249. # class Notifier < ActionMailer::Base
  250. # default 'Content-Transfer-Encoding' => '7bit',
  251. # :content_description => 'This is a description'
  252. # end
  253. #
  254. # Finally, Action Mailer also supports passing <tt>Proc</tt> objects into the default hash, so you
  255. # can define methods that evaluate as the message is being generated:
  256. #
  257. # class Notifier < ActionMailer::Base
  258. # default 'X-Special-Header' => Proc.new { my_method }
  259. #
  260. # private
  261. #
  262. # def my_method
  263. # 'some complex call'
  264. # end
  265. # end
  266. #
  267. # Note that the proc is evaluated right at the start of the mail message generation, so if you
  268. # set something in the defaults using a proc, and then set the same thing inside of your
  269. # mailer method, it will get over written by the mailer method.
  270. #
  271. # = Configuration options
  272. #
  273. # These options are specified on the class level, like
  274. # <tt>ActionMailer::Base.raise_delivery_errors = true</tt>
  275. #
  276. # * <tt>default</tt> - You can pass this in at a class level as well as within the class itself as
  277. # per the above section.
  278. #
  279. # * <tt>logger</tt> - the logger is used for generating information on the mailing run if available.
  280. # Can be set to nil for no logging. Compatible with both Ruby's own Logger and Log4r loggers.
  281. #
  282. # * <tt>smtp_settings</tt> - Allows detailed configuration for <tt>:smtp</tt> delivery method:
  283. # * <tt>:address</tt> - Allows you to use a remote mail server. Just change it from its default
  284. # "localhost" setting.
  285. # * <tt>:port</tt> - On the off chance that your mail server doesn't run on port 25, you can change it.
  286. # * <tt>:domain</tt> - If you need to specify a HELO domain, you can do it here.
  287. # * <tt>:user_name</tt> - If your mail server requires authentication, set the username in this setting.
  288. # * <tt>:password</tt> - If your mail server requires authentication, set the password in this setting.
  289. # * <tt>:authentication</tt> - If your mail server requires authentication, you need to specify the
  290. # authentication type here.
  291. # This is a symbol and one of <tt>:plain</tt> (will send the password in the clear), <tt>:login</tt> (will
  292. # send password Base64 encoded) or <tt>:cram_md5</tt> (combines a Challenge/Response mechanism to exchange
  293. # information and a cryptographic Message Digest 5 algorithm to hash important information)
  294. # * <tt>:enable_starttls_auto</tt> - When set to true, detects if STARTTLS is enabled in your SMTP server
  295. # and starts to use it.
  296. # * <tt>:openssl_verify_mode</tt> - When using TLS, you can set how OpenSSL checks the certificate. This is
  297. # really useful if you need to validate a self-signed and/or a wildcard certificate. You can use the name
  298. # of an OpenSSL verify constant ('none', 'peer', 'client_once','fail_if_no_peer_cert') or directly the
  299. # constant (OpenSSL::SSL::VERIFY_NONE, OpenSSL::SSL::VERIFY_PEER,...).
  300. #
  301. # * <tt>sendmail_settings</tt> - Allows you to override options for the <tt>:sendmail</tt> delivery method.
  302. # * <tt>:location</tt> - The location of the sendmail executable. Defaults to <tt>/usr/sbin/sendmail</tt>.
  303. # * <tt>:arguments</tt> - The command line arguments. Defaults to <tt>-i -t</tt> with <tt>-f sender@address</tt>
  304. # added automatically before the message is sent.
  305. #
  306. # * <tt>file_settings</tt> - Allows you to override options for the <tt>:file</tt> delivery method.
  307. # * <tt>:location</tt> - The directory into which emails will be written. Defaults to the application
  308. # <tt>tmp/mails</tt>.
  309. #
  310. # * <tt>raise_delivery_errors</tt> - Whether or not errors should be raised if the email fails to be delivered.
  311. #
  312. # * <tt>delivery_method</tt> - Defines a delivery method. Possible values are <tt>:smtp</tt> (default),
  313. # <tt>:sendmail</tt>, <tt>:test</tt>, and <tt>:file</tt>. Or you may provide a custom delivery method
  314. # object eg. MyOwnDeliveryMethodClass.new. See the Mail gem documentation on the interface you need to
  315. # implement for a custom delivery agent.
  316. #
  317. # * <tt>perform_deliveries</tt> - Determines whether emails are actually sent from Action Mailer when you
  318. # call <tt>.deliver</tt> on an mail message or on an Action Mailer method. This is on by default but can
  319. # be turned off to aid in functional testing.
  320. #
  321. # * <tt>deliveries</tt> - Keeps an array of all the emails sent out through the Action Mailer with
  322. # <tt>delivery_method :test</tt>. Most useful for unit and functional testing.
  323. #
  324. class Base < AbstractController::Base
  325. include DeliveryMethods
  326. abstract!
  327. include AbstractController::Logger
  328. include AbstractController::Rendering
  329. include AbstractController::Layouts
  330. include AbstractController::Helpers
  331. include AbstractController::Translation
  332. include AbstractController::AssetPaths
  333. self.protected_instance_variables = %w(@_action_has_layout)
  334. helper ActionMailer::MailHelper
  335. private_class_method :new #:nodoc:
  336. class_attribute :default_params
  337. self.default_params = {
  338. :mime_version => "1.0",
  339. :charset => "UTF-8",
  340. :content_type => "text/plain",
  341. :parts_order => [ "text/plain", "text/enriched", "text/html" ]
  342. }.freeze
  343. class << self
  344. # Register one or more Observers which will be notified when mail is delivered.
  345. def register_observers(*observers)
  346. observers.flatten.compact.each { |observer| register_observer(observer) }
  347. end
  348. # Register one or more Interceptors which will be called before mail is sent.
  349. def register_interceptors(*interceptors)
  350. interceptors.flatten.compact.each { |interceptor| register_interceptor(interceptor) }
  351. end
  352. # Register an Observer which will be notified when mail is delivered.
  353. # Either a class or a string can be passed in as the Observer. If a string is passed in
  354. # it will be <tt>constantize</tt>d.
  355. def register_observer(observer)
  356. delivery_observer = (observer.is_a?(String) ? observer.constantize : observer)
  357. Mail.register_observer(delivery_observer)
  358. end
  359. # Register an Interceptor which will be called before mail is sent.
  360. # Either a class or a string can be passed in as the Interceptor. If a string is passed in
  361. # it will be <tt>constantize</tt>d.
  362. def register_interceptor(interceptor)
  363. delivery_interceptor = (interceptor.is_a?(String) ? interceptor.constantize : interceptor)
  364. Mail.register_interceptor(delivery_interceptor)
  365. end
  366. def mailer_name
  367. @mailer_name ||= name.underscore
  368. end
  369. attr_writer :mailer_name
  370. alias :controller_path :mailer_name
  371. def default(value = nil)
  372. self.default_params = default_params.merge(value).freeze if value
  373. default_params
  374. end
  375. # Receives a raw email, parses it into an email object, decodes it,
  376. # instantiates a new mailer, and passes the email object to the mailer
  377. # object's +receive+ method. If you want your mailer to be able to
  378. # process incoming messages, you'll need to implement a +receive+
  379. # method that accepts the raw email string as a parameter:
  380. #
  381. # class MyMailer < ActionMailer::Base
  382. # def receive(mail)
  383. # ...
  384. # end
  385. # end
  386. def receive(raw_mail)
  387. ActiveSupport::Notifications.instrument("receive.action_mailer") do |payload|
  388. mail = Mail.new(raw_mail)
  389. set_payload_for_mail(payload, mail)
  390. new.receive(mail)
  391. end
  392. end
  393. # Wraps an email delivery inside of Active Support Notifications instrumentation. This
  394. # method is actually called by the <tt>Mail::Message</tt> object itself through a callback
  395. # when you call <tt>:deliver</tt> on the Mail::Message, calling +deliver_mail+ directly
  396. # and passing a Mail::Message will do nothing except tell the logger you sent the email.
  397. def deliver_mail(mail) #:nodoc:
  398. ActiveSupport::Notifications.instrument("deliver.action_mailer") do |payload|
  399. self.set_payload_for_mail(payload, mail)
  400. yield # Let Mail do the delivery actions
  401. end
  402. end
  403. def respond_to?(method, include_private = false) #:nodoc:
  404. super || action_methods.include?(method.to_s)
  405. end
  406. protected
  407. def set_payload_for_mail(payload, mail) #:nodoc:
  408. payload[:mailer] = name
  409. payload[:message_id] = mail.message_id
  410. payload[:subject] = mail.subject
  411. payload[:to] = mail.to
  412. payload[:from] = mail.from
  413. payload[:bcc] = mail.bcc if mail.bcc.present?
  414. payload[:cc] = mail.cc if mail.cc.present?
  415. payload[:date] = mail.date
  416. payload[:mail] = mail.encoded
  417. end
  418. def method_missing(method, *args) #:nodoc:
  419. return super unless respond_to?(method)
  420. new(method, *args).message
  421. end
  422. end
  423. attr_internal :message
  424. # Instantiate a new mailer object. If +method_name+ is not +nil+, the mailer
  425. # will be initialized according to the named method. If not, the mailer will
  426. # remain uninitialized (useful when you only need to invoke the "receive"
  427. # method, for instance).
  428. def initialize(method_name=nil, *args)
  429. super()
  430. @_message = Mail.new
  431. process(method_name, *args) if method_name
  432. end
  433. def process(*args) #:nodoc:
  434. lookup_context.skip_default_locale!
  435. super
  436. end
  437. def mailer_name
  438. self.class.mailer_name
  439. end
  440. # Allows you to pass random and unusual headers to the new +Mail::Message+ object
  441. # which will add them to itself.
  442. #
  443. # headers['X-Special-Domain-Specific-Header'] = "SecretValue"
  444. #
  445. # You can also pass a hash into headers of header field names and values, which
  446. # will then be set on the Mail::Message object:
  447. #
  448. # headers 'X-Special-Domain-Specific-Header' => "SecretValue",
  449. # 'In-Reply-To' => incoming.message_id
  450. #
  451. # The resulting Mail::Message will have the following in it's header:
  452. #
  453. # X-Special-Domain-Specific-Header: SecretValue
  454. def headers(args=nil)
  455. if args
  456. @_message.headers(args)
  457. else
  458. @_message
  459. end
  460. end
  461. # Allows you to add attachments to an email, like so:
  462. #
  463. # mail.attachments['filename.jpg'] = File.read('/path/to/filename.jpg')
  464. #
  465. # If you do this, then Mail will take the file name and work out the mime type
  466. # set the Content-Type, Content-Disposition, Content-Transfer-Encoding and
  467. # base64 encode the contents of the attachment all for you.
  468. #
  469. # You can also specify overrides if you want by passing a hash instead of a string:
  470. #
  471. # mail.attachments['filename.jpg'] = {:mime_type => 'application/x-gzip',
  472. # :content => File.read('/path/to/filename.jpg')}
  473. #
  474. # If you want to use a different encoding than Base64, you can pass an encoding in,
  475. # but then it is up to you to pass in the content pre-encoded, and don't expect
  476. # Mail to know how to decode this data:
  477. #
  478. # file_content = SpecialEncode(File.read('/path/to/filename.jpg'))
  479. # mail.attachments['filename.jpg'] = {:mime_type => 'application/x-gzip',
  480. # :encoding => 'SpecialEncoding',
  481. # :content => file_content }
  482. #
  483. # You can also search for specific attachments:
  484. #
  485. # # By Filename
  486. # mail.attachments['filename.jpg'] # => Mail::Part object or nil
  487. #
  488. # # or by index
  489. # mail.attachments[0] # => Mail::Part (first attachment)
  490. #
  491. def attachments
  492. @_message.attachments
  493. end
  494. # The main method that creates the message and renders the email templates. There are
  495. # two ways to call this method, with a block, or without a block.
  496. #
  497. # Both methods accept a headers hash. This hash allows you to specify the most used headers
  498. # in an email message, these are:
  499. #
  500. # * <tt>:subject</tt> - The subject of the message, if this is omitted, Action Mailer will
  501. # ask the Rails I18n class for a translated <tt>:subject</tt> in the scope of
  502. # <tt>[:actionmailer, mailer_scope, action_name]</tt> or if this is missing, will translate the
  503. # humanized version of the <tt>action_name</tt>
  504. # * <tt>:to</tt> - Who the message is destined for, can be a string of addresses, or an array
  505. # of addresses.
  506. # * <tt>:from</tt> - Who the message is from
  507. # * <tt>:cc</tt> - Who you would like to Carbon-Copy on this email, can be a string of addresses,
  508. # or an array of addresses.
  509. # * <tt>:bcc</tt> - Who you would like to Blind-Carbon-Copy on this email, can be a string of
  510. # addresses, or an array of addresses.
  511. # * <tt>:reply_to</tt> - Who to set the Reply-To header of the email to.
  512. # * <tt>:date</tt> - The date to say the email was sent on.
  513. #
  514. # You can set default values for any of the above headers (except :date) by using the <tt>default</tt>
  515. # class method:
  516. #
  517. # class Notifier < ActionMailer::Base
  518. # self.default :from => 'no-reply@test.lindsaar.net',
  519. # :bcc => 'email_logger@test.lindsaar.net',
  520. # :reply_to => 'bounces@test.lindsaar.net'
  521. # end
  522. #
  523. # If you need other headers not listed above, you can either pass them in
  524. # as part of the headers hash or use the <tt>headers['name'] = value</tt>
  525. # method.
  526. #
  527. # When a <tt>:return_path</tt> is specified as header, that value will be used as the 'envelope from'
  528. # address for the Mail message. Setting this is useful when you want delivery notifications
  529. # sent to a different address than the one in <tt>:from</tt>. Mail will actually use the
  530. # <tt>:return_path</tt> in preference to the <tt>:sender</tt> in preference to the <tt>:from</tt>
  531. # field for the 'envelope from' value.
  532. #
  533. # If you do not pass a block to the +mail+ method, it will find all templates in the
  534. # view paths using by default the mailer name and the method name that it is being
  535. # called from, it will then create parts for each of these templates intelligently,
  536. # making educated guesses on correct content type and sequence, and return a fully
  537. # prepared Mail::Message ready to call <tt>:deliver</tt> on to send.
  538. #
  539. # For example:
  540. #
  541. # class Notifier < ActionMailer::Base
  542. # default :from => 'no-reply@test.lindsaar.net',
  543. #
  544. # def welcome
  545. # mail(:to => 'mikel@test.lindsaar.net')
  546. # end
  547. # end
  548. #
  549. # Will look for all templates at "app/views/notifier" with name "welcome". However, those
  550. # can be customized:
  551. #
  552. # mail(:template_path => 'notifications', :template_name => 'another')
  553. #
  554. # And now it will look for all templates at "app/views/notifications" with name "another".
  555. #
  556. # If you do pass a block, you can render specific templates of your choice:
  557. #
  558. # mail(:to => 'mikel@test.lindsaar.net') do |format|
  559. # format.text
  560. # format.html
  561. # end
  562. #
  563. # You can even render text directly without using a template:
  564. #
  565. # mail(:to => 'mikel@test.lindsaar.net') do |format|
  566. # format.text { render :text => "Hello Mikel!" }
  567. # format.html { render :text => "<h1>Hello Mikel!</h1>" }
  568. # end
  569. #
  570. # Which will render a <tt>multipart/alternative</tt> email with <tt>text/plain</tt> and
  571. # <tt>text/html</tt> parts.
  572. #
  573. # The block syntax also allows you to customize the part headers if desired:
  574. #
  575. # mail(:to => 'mikel@test.lindsaar.net') do |format|
  576. # format.text(:content_transfer_encoding => "base64")
  577. # format.html
  578. # end
  579. #
  580. def mail(headers={}, &block)
  581. # Guard flag to prevent both the old and the new API from firing
  582. # Should be removed when old API is removed
  583. @mail_was_called = true
  584. m = @_message
  585. # At the beginning, do not consider class default for parts order neither content_type
  586. content_type = headers[:content_type]
  587. parts_order = headers[:parts_order]
  588. # Call all the procs (if any)
  589. default_values = self.class.default.merge(self.class.default) do |k,v|
  590. v.respond_to?(:call) ? v.bind(self).call : v
  591. end
  592. # Handle defaults
  593. headers = headers.reverse_merge(default_values)
  594. headers[:subject] ||= default_i18n_subject
  595. # Apply charset at the beginning so all fields are properly quoted
  596. m.charset = charset = headers[:charset]
  597. # Set configure delivery behavior
  598. wrap_delivery_behavior!(headers.delete(:delivery_method))
  599. # Assign all headers except parts_order, content_type and body
  600. assignable = headers.except(:parts_order, :content_type, :body, :template_name, :template_path)
  601. assignable.each { |k, v| m[k] = v }
  602. # Render the templates and blocks
  603. responses, explicit_order = collect_responses_and_parts_order(headers, &block)
  604. create_parts_from_responses(m, responses)
  605. # Setup content type, reapply charset and handle parts order
  606. m.content_type = set_content_type(m, content_type, headers[:content_type])
  607. m.charset = charset
  608. if m.multipart?
  609. parts_order ||= explicit_order || headers[:parts_order]
  610. m.body.set_sort_order(parts_order)
  611. m.body.sort_parts!
  612. end
  613. m
  614. end
  615. protected
  616. def set_content_type(m, user_content_type, class_default)
  617. params = m.content_type_parameters || {}
  618. case
  619. when user_content_type.present?
  620. user_content_type
  621. when m.has_attachments?
  622. if m.attachments.detect { |a| a.inline? }
  623. ["multipart", "related", params]
  624. else
  625. ["multipart", "mixed", params]
  626. end
  627. when m.multipart?
  628. ["multipart", "alternative", params]
  629. else
  630. m.content_type || class_default
  631. end
  632. end
  633. # Translates the +subject+ using Rails I18n class under <tt>[:actionmailer, mailer_scope, action_name]</tt> scope.
  634. # If it does not find a translation for the +subject+ under the specified scope it will default to a
  635. # humanized version of the <tt>action_name</tt>.
  636. def default_i18n_subject #:nodoc:
  637. mailer_scope = self.class.mailer_name.gsub('/', '.')
  638. I18n.t(:subject, :scope => [mailer_scope, action_name], :default => action_name.humanize)
  639. end
  640. def collect_responses_and_parts_order(headers) #:nodoc:
  641. responses, parts_order = [], nil
  642. if block_given?
  643. collector = ActionMailer::Collector.new(lookup_context) { render(action_name) }
  644. yield(collector)
  645. parts_order = collector.responses.map { |r| r[:content_type] }
  646. responses = collector.responses
  647. elsif headers[:body]
  648. responses << {
  649. :body => headers.delete(:body),
  650. :content_type => self.class.default[:content_type] || "text/plain"
  651. }
  652. else
  653. templates_path = headers.delete(:template_path) || self.class.mailer_name
  654. templates_name = headers.delete(:template_name) || action_name
  655. each_template(templates_path, templates_name) do |template|
  656. self.formats = template.formats
  657. responses << {
  658. :body => render(:template => template),
  659. :content_type => template.mime_type.to_s
  660. }
  661. end
  662. end
  663. [responses, parts_order]
  664. end
  665. def each_template(paths, name, &block) #:nodoc:
  666. templates = lookup_context.find_all(name, Array.wrap(paths))
  667. templates.uniq_by { |t| t.formats }.each(&block)
  668. end
  669. def create_parts_from_responses(m, responses) #:nodoc:
  670. if responses.size == 1 && !m.has_attachments?
  671. responses[0].each { |k,v| m[k] = v }
  672. elsif responses.size > 1 && m.has_attachments?
  673. container = Mail::Part.new
  674. container.content_type = "multipart/alternative"
  675. responses.each { |r| insert_part(container, r, m.charset) }
  676. m.add_part(container)
  677. else
  678. responses.each { |r| insert_part(m, r, m.charset) }
  679. end
  680. end
  681. def insert_part(container, response, charset) #:nodoc:
  682. response[:charset] ||= charset
  683. part = Mail::Part.new(response)
  684. container.add_part(part)
  685. end
  686. ActiveSupport.run_load_hooks(:action_mailer, self)
  687. end
  688. end