base.rb 31 KB

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