actionmailer 3.2.9 → 6.1.7.6
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionmailer might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/CHANGELOG.md +110 -17
- data/MIT-LICENSE +1 -2
- data/README.rdoc +39 -61
- data/lib/action_mailer/base.rb +599 -317
- data/lib/action_mailer/collector.rb +9 -7
- data/lib/action_mailer/delivery_job.rb +45 -0
- data/lib/action_mailer/delivery_methods.rb +33 -37
- data/lib/action_mailer/gem_version.rb +17 -0
- data/lib/action_mailer/inline_preview_interceptor.rb +57 -0
- data/lib/action_mailer/log_subscriber.rb +25 -8
- data/lib/action_mailer/mail_delivery_job.rb +43 -0
- data/lib/action_mailer/mail_helper.rb +29 -13
- data/lib/action_mailer/mail_with_error_handling.rb +10 -0
- data/lib/action_mailer/message_delivery.rb +178 -0
- data/lib/action_mailer/parameterized.rb +172 -0
- data/lib/action_mailer/preview.rb +142 -0
- data/lib/action_mailer/railtie.rb +48 -4
- data/lib/action_mailer/rescuable.rb +29 -0
- data/lib/action_mailer/test_case.rb +53 -14
- data/lib/action_mailer/test_helper.rb +114 -13
- data/lib/action_mailer/version.rb +8 -7
- data/lib/action_mailer.rb +37 -15
- data/lib/rails/generators/mailer/USAGE +6 -8
- data/lib/rails/generators/mailer/mailer_generator.rb +26 -4
- data/lib/rails/generators/mailer/templates/application_mailer.rb.tt +6 -0
- data/lib/rails/generators/mailer/templates/{mailer.rb → mailer.rb.tt} +3 -4
- metadata +105 -36
data/lib/action_mailer/base.rb
CHANGED
@@ -1,36 +1,41 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
|
9
|
-
require
|
10
|
-
|
11
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "action_mailer/mail_with_error_handling"
|
4
|
+
require "action_mailer/collector"
|
5
|
+
require "active_support/core_ext/string/inflections"
|
6
|
+
require "active_support/core_ext/hash/except"
|
7
|
+
require "active_support/core_ext/module/anonymous"
|
8
|
+
|
9
|
+
require "action_mailer/log_subscriber"
|
10
|
+
require "action_mailer/rescuable"
|
11
|
+
|
12
|
+
module ActionMailer
|
12
13
|
# Action Mailer allows you to send email from your application using a mailer model and views.
|
13
14
|
#
|
14
15
|
# = Mailer Models
|
15
16
|
#
|
16
17
|
# To use Action Mailer, you need to create a mailer model.
|
17
18
|
#
|
18
|
-
# $ rails generate mailer Notifier
|
19
|
+
# $ bin/rails generate mailer Notifier
|
19
20
|
#
|
20
|
-
# The generated model inherits from <tt>
|
21
|
-
#
|
22
|
-
#
|
21
|
+
# The generated model inherits from <tt>ApplicationMailer</tt> which in turn
|
22
|
+
# inherits from <tt>ActionMailer::Base</tt>. A mailer model defines methods
|
23
|
+
# used to generate an email message. In these methods, you can set up variables to be used in
|
24
|
+
# the mailer views, options on the mail itself such as the <tt>:from</tt> address, and attachments.
|
23
25
|
#
|
24
|
-
#
|
26
|
+
# class ApplicationMailer < ActionMailer::Base
|
27
|
+
# default from: 'from@example.com'
|
28
|
+
# layout 'mailer'
|
29
|
+
# end
|
25
30
|
#
|
26
|
-
# class
|
27
|
-
# default :
|
28
|
-
#
|
31
|
+
# class NotifierMailer < ApplicationMailer
|
32
|
+
# default from: 'no-reply@example.com',
|
33
|
+
# return_path: 'system@example.com'
|
29
34
|
#
|
30
35
|
# def welcome(recipient)
|
31
36
|
# @account = recipient
|
32
|
-
# mail(:
|
33
|
-
# :
|
37
|
+
# mail(to: recipient.email_address_with_name,
|
38
|
+
# bcc: ["bcc@example.com", "Order Watcher <watcher@example.com>"])
|
34
39
|
# end
|
35
40
|
# end
|
36
41
|
#
|
@@ -43,41 +48,38 @@ module ActionMailer #:nodoc:
|
|
43
48
|
# in the same manner as <tt>attachments[]=</tt>
|
44
49
|
#
|
45
50
|
# * <tt>headers[]=</tt> - Allows you to specify any header field in your email such
|
46
|
-
# as <tt>headers['X-No-Spam'] = 'True'</tt>. Note
|
47
|
-
#
|
48
|
-
# can appear multiple times. If you want to change a field that can appear multiple times,
|
49
|
-
# you need to set it to nil first so that Mail knows you are replacing it and not adding
|
50
|
-
# another field of the same name.
|
51
|
+
# as <tt>headers['X-No-Spam'] = 'True'</tt>. Note that declaring a header multiple times
|
52
|
+
# will add many fields of the same name. Read #headers doc for more information.
|
51
53
|
#
|
52
54
|
# * <tt>headers(hash)</tt> - Allows you to specify multiple headers in your email such
|
53
55
|
# as <tt>headers({'X-No-Spam' => 'True', 'In-Reply-To' => '1234@message.id'})</tt>
|
54
56
|
#
|
55
57
|
# * <tt>mail</tt> - Allows you to specify email to be sent.
|
56
58
|
#
|
57
|
-
# The hash passed to the mail method allows you to specify any header that a Mail::Message
|
58
|
-
# will accept (any valid
|
59
|
+
# The hash passed to the mail method allows you to specify any header that a <tt>Mail::Message</tt>
|
60
|
+
# will accept (any valid email header including optional fields).
|
59
61
|
#
|
60
|
-
# The mail method, if not passed a block, will inspect your views and send all the views with
|
62
|
+
# The +mail+ method, if not passed a block, will inspect your views and send all the views with
|
61
63
|
# the same name as the method, so the above action would send the +welcome.text.erb+ view
|
62
|
-
# file as well as the +welcome.
|
64
|
+
# file as well as the +welcome.html.erb+ view file in a +multipart/alternative+ email.
|
63
65
|
#
|
64
66
|
# If you want to explicitly render only certain templates, pass a block:
|
65
67
|
#
|
66
|
-
# mail(:
|
68
|
+
# mail(to: user.email) do |format|
|
67
69
|
# format.text
|
68
70
|
# format.html
|
69
71
|
# end
|
70
72
|
#
|
71
73
|
# The block syntax is also useful in providing information specific to a part:
|
72
74
|
#
|
73
|
-
# mail(:
|
74
|
-
# format.text(:
|
75
|
+
# mail(to: user.email) do |format|
|
76
|
+
# format.text(content_transfer_encoding: "base64")
|
75
77
|
# format.html
|
76
78
|
# end
|
77
79
|
#
|
78
80
|
# Or even to render a special view:
|
79
81
|
#
|
80
|
-
# mail(:
|
82
|
+
# mail(to: user.email) do |format|
|
81
83
|
# format.text
|
82
84
|
# format.html { render "some_other_template" }
|
83
85
|
# end
|
@@ -87,26 +89,27 @@ module ActionMailer #:nodoc:
|
|
87
89
|
# Like Action Controller, each mailer class has a corresponding view directory in which each
|
88
90
|
# method of the class looks for a template with its name.
|
89
91
|
#
|
90
|
-
# To define a template to be used with a
|
92
|
+
# To define a template to be used with a mailer, create an <tt>.erb</tt> file with the same
|
91
93
|
# name as the method in your mailer model. For example, in the mailer defined above, the template at
|
92
|
-
# <tt>app/views/
|
94
|
+
# <tt>app/views/notifier_mailer/welcome.text.erb</tt> would be used to generate the email.
|
93
95
|
#
|
94
|
-
# Variables defined in the model are accessible as instance variables in
|
96
|
+
# Variables defined in the methods of your mailer model are accessible as instance variables in their
|
97
|
+
# corresponding view.
|
95
98
|
#
|
96
99
|
# Emails by default are sent in plain text, so a sample view for our model example might look like this:
|
97
100
|
#
|
98
101
|
# Hi <%= @account.name %>,
|
99
102
|
# Thanks for joining our service! Please check back often.
|
100
103
|
#
|
101
|
-
# You can even use Action
|
104
|
+
# You can even use Action View helpers in these views. For example:
|
102
105
|
#
|
103
106
|
# You got a new note!
|
104
|
-
# <%= truncate(@note.body, :
|
107
|
+
# <%= truncate(@note.body, length: 25) %>
|
105
108
|
#
|
106
109
|
# If you need to access the subject, from or the recipients in the view, you can do that through message object:
|
107
110
|
#
|
108
111
|
# You got a new note from <%= message.from %>!
|
109
|
-
# <%= truncate(@note.body, :
|
112
|
+
# <%= truncate(@note.body, length: 25) %>
|
110
113
|
#
|
111
114
|
#
|
112
115
|
# = Generating URLs
|
@@ -117,48 +120,62 @@ module ActionMailer #:nodoc:
|
|
117
120
|
#
|
118
121
|
# When using <tt>url_for</tt> you'll need to provide the <tt>:host</tt>, <tt>:controller</tt>, and <tt>:action</tt>:
|
119
122
|
#
|
120
|
-
# <%= url_for(:
|
123
|
+
# <%= url_for(host: "example.com", controller: "welcome", action: "greeting") %>
|
121
124
|
#
|
122
125
|
# When using named routes you only need to supply the <tt>:host</tt>:
|
123
126
|
#
|
124
|
-
# <%= users_url(:
|
127
|
+
# <%= users_url(host: "example.com") %>
|
125
128
|
#
|
126
|
-
# You should use the <tt>named_route_url</tt> style (which generates absolute URLs) and avoid using the
|
127
|
-
# <tt>named_route_path</tt> style (which generates relative URLs), since clients reading the mail will
|
129
|
+
# You should use the <tt>named_route_url</tt> style (which generates absolute URLs) and avoid using the
|
130
|
+
# <tt>named_route_path</tt> style (which generates relative URLs), since clients reading the mail will
|
128
131
|
# have no concept of a current URL from which to determine a relative path.
|
129
132
|
#
|
130
133
|
# It is also possible to set a default host that will be used in all mailers by setting the <tt>:host</tt>
|
131
134
|
# option as a configuration option in <tt>config/application.rb</tt>:
|
132
135
|
#
|
133
|
-
# config.action_mailer.default_url_options = { :
|
136
|
+
# config.action_mailer.default_url_options = { host: "example.com" }
|
134
137
|
#
|
135
|
-
#
|
136
|
-
#
|
137
|
-
#
|
138
|
-
# <tt
|
138
|
+
# You can also define a <tt>default_url_options</tt> method on individual mailers to override these
|
139
|
+
# default settings per-mailer.
|
140
|
+
#
|
141
|
+
# By default when <tt>config.force_ssl</tt> is +true+, URLs generated for hosts will use the HTTPS protocol.
|
139
142
|
#
|
140
143
|
# = Sending mail
|
141
144
|
#
|
142
|
-
# Once a mailer action and template are defined, you can deliver your message or
|
143
|
-
# for
|
145
|
+
# Once a mailer action and template are defined, you can deliver your message or defer its creation and
|
146
|
+
# delivery for later:
|
147
|
+
#
|
148
|
+
# NotifierMailer.welcome(User.first).deliver_now # sends the email
|
149
|
+
# mail = NotifierMailer.welcome(User.first) # => an ActionMailer::MessageDelivery object
|
150
|
+
# mail.deliver_now # generates and sends the email now
|
151
|
+
#
|
152
|
+
# The <tt>ActionMailer::MessageDelivery</tt> class is a wrapper around a delegate that will call
|
153
|
+
# your method to generate the mail. If you want direct access to the delegator, or <tt>Mail::Message</tt>,
|
154
|
+
# you can call the <tt>message</tt> method on the <tt>ActionMailer::MessageDelivery</tt> object.
|
155
|
+
#
|
156
|
+
# NotifierMailer.welcome(User.first).message # => a Mail::Message object
|
144
157
|
#
|
145
|
-
#
|
146
|
-
#
|
147
|
-
#
|
158
|
+
# Action Mailer is nicely integrated with Active Job so you can generate and send emails in the background
|
159
|
+
# (example: outside of the request-response cycle, so the user doesn't have to wait on it):
|
160
|
+
#
|
161
|
+
# NotifierMailer.welcome(User.first).deliver_later # enqueue the email sending to Active Job
|
162
|
+
#
|
163
|
+
# Note that <tt>deliver_later</tt> will execute your method from the background job.
|
148
164
|
#
|
149
165
|
# You never instantiate your mailer class. Rather, you just call the method you defined on the class itself.
|
166
|
+
# All instance methods are expected to return a message object to be sent.
|
150
167
|
#
|
151
168
|
# = Multipart Emails
|
152
169
|
#
|
153
|
-
# Multipart messages can also be used implicitly because Action Mailer will automatically detect and use
|
154
|
-
# multipart templates, where each template is named after the name of the action, followed by the content
|
155
|
-
# type. Each such detected template will be added as a separate part
|
170
|
+
# Multipart messages can also be used implicitly because Action Mailer will automatically detect and use
|
171
|
+
# multipart templates, where each template is named after the name of the action, followed by the content
|
172
|
+
# type. Each such detected template will be added to the message, as a separate part.
|
156
173
|
#
|
157
174
|
# For example, if the following templates exist:
|
158
175
|
# * signup_notification.text.erb
|
159
|
-
# * signup_notification.
|
160
|
-
# * signup_notification.
|
161
|
-
# * signup_notification.
|
176
|
+
# * signup_notification.html.erb
|
177
|
+
# * signup_notification.xml.builder
|
178
|
+
# * signup_notification.yml.erb
|
162
179
|
#
|
163
180
|
# Each would be rendered and added as a separate part to the message, with the corresponding content
|
164
181
|
# type. The content type for the entire message is automatically set to <tt>multipart/alternative</tt>,
|
@@ -173,28 +190,51 @@ module ActionMailer #:nodoc:
|
|
173
190
|
#
|
174
191
|
# Sending attachment in emails is easy:
|
175
192
|
#
|
176
|
-
# class
|
193
|
+
# class NotifierMailer < ApplicationMailer
|
177
194
|
# def welcome(recipient)
|
178
195
|
# attachments['free_book.pdf'] = File.read('path/to/file.pdf')
|
179
|
-
# mail(:
|
196
|
+
# mail(to: recipient, subject: "New account information")
|
180
197
|
# end
|
181
198
|
# end
|
182
199
|
#
|
183
|
-
# Which will (if it had both a <tt>welcome.text.erb</tt> and <tt>welcome.
|
200
|
+
# Which will (if it had both a <tt>welcome.text.erb</tt> and <tt>welcome.html.erb</tt>
|
184
201
|
# template in the view directory), send a complete <tt>multipart/mixed</tt> email with two parts,
|
185
202
|
# the first part being a <tt>multipart/alternative</tt> with the text and HTML email parts inside,
|
186
203
|
# and the second being a <tt>application/pdf</tt> with a Base64 encoded copy of the file.pdf book
|
187
204
|
# with the filename +free_book.pdf+.
|
188
205
|
#
|
206
|
+
# If you need to send attachments with no content, you need to create an empty view for it,
|
207
|
+
# or add an empty body parameter like this:
|
208
|
+
#
|
209
|
+
# class NotifierMailer < ApplicationMailer
|
210
|
+
# def welcome(recipient)
|
211
|
+
# attachments['free_book.pdf'] = File.read('path/to/file.pdf')
|
212
|
+
# mail(to: recipient, subject: "New account information", body: "")
|
213
|
+
# end
|
214
|
+
# end
|
215
|
+
#
|
216
|
+
# You can also send attachments with html template, in this case you need to add body, attachments,
|
217
|
+
# and custom content type like this:
|
218
|
+
#
|
219
|
+
# class NotifierMailer < ApplicationMailer
|
220
|
+
# def welcome(recipient)
|
221
|
+
# attachments["free_book.pdf"] = File.read("path/to/file.pdf")
|
222
|
+
# mail(to: recipient,
|
223
|
+
# subject: "New account information",
|
224
|
+
# content_type: "text/html",
|
225
|
+
# body: "<html><body>Hello there</body></html>")
|
226
|
+
# end
|
227
|
+
# end
|
228
|
+
#
|
189
229
|
# = Inline Attachments
|
190
230
|
#
|
191
231
|
# You can also specify that a file should be displayed inline with other HTML. This is useful
|
192
232
|
# if you want to display a corporate logo or a photo.
|
193
233
|
#
|
194
|
-
# class
|
234
|
+
# class NotifierMailer < ApplicationMailer
|
195
235
|
# def welcome(recipient)
|
196
236
|
# attachments.inline['photo.png'] = File.read('path/to/photo.png')
|
197
|
-
# mail(:
|
237
|
+
# mail(to: recipient, subject: "Here is what we look like")
|
198
238
|
# end
|
199
239
|
# end
|
200
240
|
#
|
@@ -210,7 +250,7 @@ module ActionMailer #:nodoc:
|
|
210
250
|
#
|
211
251
|
# <h1>Please Don't Cringe</h1>
|
212
252
|
#
|
213
|
-
# <%= image_tag attachments['photo.png'].url, :
|
253
|
+
# <%= image_tag attachments['photo.png'].url, alt: 'Our Photo', class: 'photo' -%>
|
214
254
|
#
|
215
255
|
# = Observing and Intercepting Mails
|
216
256
|
#
|
@@ -223,24 +263,24 @@ module ActionMailer #:nodoc:
|
|
223
263
|
# An interceptor class must implement the <tt>:delivering_email(message)</tt> method which will be
|
224
264
|
# called before the email is sent, allowing you to make modifications to the email before it hits
|
225
265
|
# the delivery agents. Your class should make any needed modifications directly to the passed
|
226
|
-
# in Mail::Message instance.
|
266
|
+
# in <tt>Mail::Message</tt> instance.
|
227
267
|
#
|
228
268
|
# = Default Hash
|
229
269
|
#
|
230
270
|
# Action Mailer provides some intelligent defaults for your emails, these are usually specified in a
|
231
271
|
# default method inside the class definition:
|
232
272
|
#
|
233
|
-
# class
|
234
|
-
# default :
|
273
|
+
# class NotifierMailer < ApplicationMailer
|
274
|
+
# default sender: 'system@example.com'
|
235
275
|
# end
|
236
276
|
#
|
237
277
|
# You can pass in any header value that a <tt>Mail::Message</tt> accepts. Out of the box,
|
238
278
|
# <tt>ActionMailer::Base</tt> sets the following:
|
239
279
|
#
|
240
|
-
# * <tt
|
241
|
-
# * <tt
|
242
|
-
# * <tt
|
243
|
-
# * <tt
|
280
|
+
# * <tt>mime_version: "1.0"</tt>
|
281
|
+
# * <tt>charset: "UTF-8"</tt>
|
282
|
+
# * <tt>content_type: "text/plain"</tt>
|
283
|
+
# * <tt>parts_order: [ "text/plain", "text/enriched", "text/html" ]</tt>
|
244
284
|
#
|
245
285
|
# <tt>parts_order</tt> and <tt>charset</tt> are not actually valid <tt>Mail::Message</tt> header fields,
|
246
286
|
# but Action Mailer translates them appropriately and sets the correct values.
|
@@ -248,38 +288,107 @@ module ActionMailer #:nodoc:
|
|
248
288
|
# As you can pass in any header, you need to either quote the header as a string, or pass it in as
|
249
289
|
# an underscored symbol, so the following will work:
|
250
290
|
#
|
251
|
-
# class
|
291
|
+
# class NotifierMailer < ApplicationMailer
|
252
292
|
# default 'Content-Transfer-Encoding' => '7bit',
|
253
|
-
# :
|
293
|
+
# content_description: 'This is a description'
|
254
294
|
# end
|
255
295
|
#
|
256
|
-
# Finally, Action Mailer also supports passing <tt>Proc</tt> objects into the default hash,
|
257
|
-
# can define methods that evaluate as the message is being generated:
|
296
|
+
# Finally, Action Mailer also supports passing <tt>Proc</tt> and <tt>Lambda</tt> objects into the default hash,
|
297
|
+
# so you can define methods that evaluate as the message is being generated:
|
258
298
|
#
|
259
|
-
# class
|
260
|
-
# default 'X-Special-Header' => Proc.new { my_method }
|
299
|
+
# class NotifierMailer < ApplicationMailer
|
300
|
+
# default 'X-Special-Header' => Proc.new { my_method }, to: -> { @inviter.email_address }
|
261
301
|
#
|
262
302
|
# private
|
263
|
-
#
|
264
303
|
# def my_method
|
265
304
|
# 'some complex call'
|
266
305
|
# end
|
267
306
|
# end
|
268
307
|
#
|
269
|
-
# Note that the proc is evaluated right at the start of the mail message generation, so if you
|
270
|
-
# set something in the
|
271
|
-
# mailer method, it will get
|
308
|
+
# Note that the proc/lambda is evaluated right at the start of the mail message generation, so if you
|
309
|
+
# set something in the default hash using a proc, and then set the same thing inside of your
|
310
|
+
# mailer method, it will get overwritten by the mailer method.
|
311
|
+
#
|
312
|
+
# It is also possible to set these default options that will be used in all mailers through
|
313
|
+
# the <tt>default_options=</tt> configuration in <tt>config/application.rb</tt>:
|
314
|
+
#
|
315
|
+
# config.action_mailer.default_options = { from: "no-reply@example.org" }
|
316
|
+
#
|
317
|
+
# = Callbacks
|
318
|
+
#
|
319
|
+
# You can specify callbacks using <tt>before_action</tt> and <tt>after_action</tt> for configuring your messages.
|
320
|
+
# This may be useful, for example, when you want to add default inline attachments for all
|
321
|
+
# messages sent out by a certain mailer class:
|
322
|
+
#
|
323
|
+
# class NotifierMailer < ApplicationMailer
|
324
|
+
# before_action :add_inline_attachment!
|
325
|
+
#
|
326
|
+
# def welcome
|
327
|
+
# mail
|
328
|
+
# end
|
329
|
+
#
|
330
|
+
# private
|
331
|
+
# def add_inline_attachment!
|
332
|
+
# attachments.inline["footer.jpg"] = File.read('/path/to/filename.jpg')
|
333
|
+
# end
|
334
|
+
# end
|
335
|
+
#
|
336
|
+
# Callbacks in Action Mailer are implemented using
|
337
|
+
# <tt>AbstractController::Callbacks</tt>, so you can define and configure
|
338
|
+
# callbacks in the same manner that you would use callbacks in classes that
|
339
|
+
# inherit from <tt>ActionController::Base</tt>.
|
340
|
+
#
|
341
|
+
# Note that unless you have a specific reason to do so, you should prefer
|
342
|
+
# using <tt>before_action</tt> rather than <tt>after_action</tt> in your
|
343
|
+
# Action Mailer classes so that headers are parsed properly.
|
344
|
+
#
|
345
|
+
# = Previewing emails
|
346
|
+
#
|
347
|
+
# You can preview your email templates visually by adding a mailer preview file to the
|
348
|
+
# <tt>ActionMailer::Base.preview_path</tt>. Since most emails do something interesting
|
349
|
+
# with database data, you'll need to write some scenarios to load messages with fake data:
|
350
|
+
#
|
351
|
+
# class NotifierMailerPreview < ActionMailer::Preview
|
352
|
+
# def welcome
|
353
|
+
# NotifierMailer.welcome(User.first)
|
354
|
+
# end
|
355
|
+
# end
|
356
|
+
#
|
357
|
+
# Methods must return a <tt>Mail::Message</tt> object which can be generated by calling the mailer
|
358
|
+
# method without the additional <tt>deliver_now</tt> / <tt>deliver_later</tt>. The location of the
|
359
|
+
# mailer previews directory can be configured using the <tt>preview_path</tt> option which has a default
|
360
|
+
# of <tt>test/mailers/previews</tt>:
|
361
|
+
#
|
362
|
+
# config.action_mailer.preview_path = "#{Rails.root}/lib/mailer_previews"
|
363
|
+
#
|
364
|
+
# An overview of all previews is accessible at <tt>http://localhost:3000/rails/mailers</tt>
|
365
|
+
# on a running development server instance.
|
366
|
+
#
|
367
|
+
# Previews can also be intercepted in a similar manner as deliveries can be by registering
|
368
|
+
# a preview interceptor that has a <tt>previewing_email</tt> method:
|
369
|
+
#
|
370
|
+
# class CssInlineStyler
|
371
|
+
# def self.previewing_email(message)
|
372
|
+
# # inline CSS styles
|
373
|
+
# end
|
374
|
+
# end
|
375
|
+
#
|
376
|
+
# config.action_mailer.preview_interceptors :css_inline_styler
|
377
|
+
#
|
378
|
+
# Note that interceptors need to be registered both with <tt>register_interceptor</tt>
|
379
|
+
# and <tt>register_preview_interceptor</tt> if they should operate on both sending and
|
380
|
+
# previewing emails.
|
272
381
|
#
|
273
382
|
# = Configuration options
|
274
383
|
#
|
275
384
|
# These options are specified on the class level, like
|
276
385
|
# <tt>ActionMailer::Base.raise_delivery_errors = true</tt>
|
277
386
|
#
|
278
|
-
# * <tt>
|
387
|
+
# * <tt>default_options</tt> - You can pass this in at a class level as well as within the class itself as
|
279
388
|
# per the above section.
|
280
389
|
#
|
281
390
|
# * <tt>logger</tt> - the logger is used for generating information on the mailing run if available.
|
282
|
-
# Can be set to nil for no logging. Compatible with both Ruby's own Logger and Log4r loggers.
|
391
|
+
# Can be set to +nil+ for no logging. Compatible with both Ruby's own +Logger+ and Log4r loggers.
|
283
392
|
#
|
284
393
|
# * <tt>smtp_settings</tt> - Allows detailed configuration for <tt>:smtp</tt> delivery method:
|
285
394
|
# * <tt>:address</tt> - Allows you to use a remote mail server. Just change it from its default
|
@@ -290,19 +399,20 @@ module ActionMailer #:nodoc:
|
|
290
399
|
# * <tt>:password</tt> - If your mail server requires authentication, set the password in this setting.
|
291
400
|
# * <tt>:authentication</tt> - If your mail server requires authentication, you need to specify the
|
292
401
|
# authentication type here.
|
293
|
-
# This is a symbol and one of <tt>:plain</tt> (will send the password
|
294
|
-
# send password Base64 encoded) or <tt>:cram_md5</tt> (combines a Challenge/Response mechanism to exchange
|
402
|
+
# This is a symbol and one of <tt>:plain</tt> (will send the password Base64 encoded), <tt>:login</tt> (will
|
403
|
+
# send the password Base64 encoded) or <tt>:cram_md5</tt> (combines a Challenge/Response mechanism to exchange
|
295
404
|
# information and a cryptographic Message Digest 5 algorithm to hash important information)
|
296
|
-
# * <tt>:enable_starttls_auto</tt> -
|
297
|
-
#
|
405
|
+
# * <tt>:enable_starttls_auto</tt> - Detects if STARTTLS is enabled in your SMTP server and starts
|
406
|
+
# to use it. Defaults to <tt>true</tt>.
|
298
407
|
# * <tt>:openssl_verify_mode</tt> - When using TLS, you can set how OpenSSL checks the certificate. This is
|
299
408
|
# really useful if you need to validate a self-signed and/or a wildcard certificate. You can use the name
|
300
|
-
# of an OpenSSL verify constant ('none'
|
301
|
-
#
|
409
|
+
# of an OpenSSL verify constant (<tt>'none'</tt> or <tt>'peer'</tt>) or directly the constant
|
410
|
+
# (<tt>OpenSSL::SSL::VERIFY_NONE</tt> or <tt>OpenSSL::SSL::VERIFY_PEER</tt>).
|
411
|
+
# * <tt>:ssl/:tls</tt> Enables the SMTP connection to use SMTP/TLS (SMTPS: SMTP over direct TLS connection)
|
302
412
|
#
|
303
413
|
# * <tt>sendmail_settings</tt> - Allows you to override options for the <tt>:sendmail</tt> delivery method.
|
304
414
|
# * <tt>:location</tt> - The location of the sendmail executable. Defaults to <tt>/usr/sbin/sendmail</tt>.
|
305
|
-
# * <tt>:arguments</tt> - The command line arguments. Defaults to <tt>-i
|
415
|
+
# * <tt>:arguments</tt> - The command line arguments. Defaults to <tt>-i</tt> with <tt>-f sender@address</tt>
|
306
416
|
# added automatically before the message is sent.
|
307
417
|
#
|
308
418
|
# * <tt>file_settings</tt> - Allows you to override options for the <tt>:file</tt> delivery method.
|
@@ -313,39 +423,46 @@ module ActionMailer #:nodoc:
|
|
313
423
|
#
|
314
424
|
# * <tt>delivery_method</tt> - Defines a delivery method. Possible values are <tt>:smtp</tt> (default),
|
315
425
|
# <tt>:sendmail</tt>, <tt>:test</tt>, and <tt>:file</tt>. Or you may provide a custom delivery method
|
316
|
-
# object
|
426
|
+
# object e.g. +MyOwnDeliveryMethodClass+. See the Mail gem documentation on the interface you need to
|
317
427
|
# implement for a custom delivery agent.
|
318
428
|
#
|
319
429
|
# * <tt>perform_deliveries</tt> - Determines whether emails are actually sent from Action Mailer when you
|
320
|
-
# call <tt>.deliver</tt> on an
|
430
|
+
# call <tt>.deliver</tt> on an email message or on an Action Mailer method. This is on by default but can
|
321
431
|
# be turned off to aid in functional testing.
|
322
432
|
#
|
323
433
|
# * <tt>deliveries</tt> - Keeps an array of all the emails sent out through the Action Mailer with
|
324
434
|
# <tt>delivery_method :test</tt>. Most useful for unit and functional testing.
|
325
435
|
#
|
436
|
+
# * <tt>deliver_later_queue_name</tt> - The name of the queue used with <tt>deliver_later</tt>. Defaults to +mailers+.
|
326
437
|
class Base < AbstractController::Base
|
327
438
|
include DeliveryMethods
|
439
|
+
include Rescuable
|
440
|
+
include Parameterized
|
441
|
+
include Previews
|
442
|
+
|
328
443
|
abstract!
|
329
444
|
|
330
|
-
include AbstractController::Logger
|
331
445
|
include AbstractController::Rendering
|
332
|
-
|
446
|
+
|
447
|
+
include AbstractController::Logger
|
333
448
|
include AbstractController::Helpers
|
334
449
|
include AbstractController::Translation
|
335
450
|
include AbstractController::AssetPaths
|
451
|
+
include AbstractController::Callbacks
|
452
|
+
include AbstractController::Caching
|
336
453
|
|
337
|
-
|
454
|
+
include ActionView::Layouts
|
338
455
|
|
339
|
-
|
456
|
+
PROTECTED_IVARS = AbstractController::Rendering::DEFAULT_PROTECTED_INSTANCE_VARIABLES + [:@_action_has_layout]
|
340
457
|
|
341
|
-
|
458
|
+
helper ActionMailer::MailHelper
|
342
459
|
|
343
|
-
class_attribute :
|
344
|
-
|
345
|
-
:
|
346
|
-
:
|
347
|
-
:
|
348
|
-
:
|
460
|
+
class_attribute :delivery_job, default: ::ActionMailer::DeliveryJob
|
461
|
+
class_attribute :default_params, default: {
|
462
|
+
mime_version: "1.0",
|
463
|
+
charset: "UTF-8",
|
464
|
+
content_type: "text/plain",
|
465
|
+
parts_order: [ "text/plain", "text/enriched", "text/html" ]
|
349
466
|
}.freeze
|
350
467
|
|
351
468
|
class << self
|
@@ -354,140 +471,210 @@ module ActionMailer #:nodoc:
|
|
354
471
|
observers.flatten.compact.each { |observer| register_observer(observer) }
|
355
472
|
end
|
356
473
|
|
474
|
+
# Unregister one or more previously registered Observers.
|
475
|
+
def unregister_observers(*observers)
|
476
|
+
observers.flatten.compact.each { |observer| unregister_observer(observer) }
|
477
|
+
end
|
478
|
+
|
357
479
|
# Register one or more Interceptors which will be called before mail is sent.
|
358
480
|
def register_interceptors(*interceptors)
|
359
481
|
interceptors.flatten.compact.each { |interceptor| register_interceptor(interceptor) }
|
360
482
|
end
|
361
483
|
|
484
|
+
# Unregister one or more previously registered Interceptors.
|
485
|
+
def unregister_interceptors(*interceptors)
|
486
|
+
interceptors.flatten.compact.each { |interceptor| unregister_interceptor(interceptor) }
|
487
|
+
end
|
488
|
+
|
362
489
|
# Register an Observer which will be notified when mail is delivered.
|
363
|
-
# Either a class or
|
364
|
-
# it will be
|
490
|
+
# Either a class, string or symbol can be passed in as the Observer.
|
491
|
+
# If a string or symbol is passed in it will be camelized and constantized.
|
365
492
|
def register_observer(observer)
|
366
|
-
|
367
|
-
|
493
|
+
Mail.register_observer(observer_class_for(observer))
|
494
|
+
end
|
495
|
+
|
496
|
+
# Unregister a previously registered Observer.
|
497
|
+
# Either a class, string or symbol can be passed in as the Observer.
|
498
|
+
# If a string or symbol is passed in it will be camelized and constantized.
|
499
|
+
def unregister_observer(observer)
|
500
|
+
Mail.unregister_observer(observer_class_for(observer))
|
368
501
|
end
|
369
502
|
|
370
503
|
# Register an Interceptor which will be called before mail is sent.
|
371
|
-
# Either a class or
|
372
|
-
# it will be
|
504
|
+
# Either a class, string or symbol can be passed in as the Interceptor.
|
505
|
+
# If a string or symbol is passed in it will be camelized and constantized.
|
373
506
|
def register_interceptor(interceptor)
|
374
|
-
|
375
|
-
|
507
|
+
Mail.register_interceptor(observer_class_for(interceptor))
|
508
|
+
end
|
509
|
+
|
510
|
+
# Unregister a previously registered Interceptor.
|
511
|
+
# Either a class, string or symbol can be passed in as the Interceptor.
|
512
|
+
# If a string or symbol is passed in it will be camelized and constantized.
|
513
|
+
def unregister_interceptor(interceptor)
|
514
|
+
Mail.unregister_interceptor(observer_class_for(interceptor))
|
515
|
+
end
|
516
|
+
|
517
|
+
def observer_class_for(value) # :nodoc:
|
518
|
+
case value
|
519
|
+
when String, Symbol
|
520
|
+
value.to_s.camelize.constantize
|
521
|
+
else
|
522
|
+
value
|
523
|
+
end
|
376
524
|
end
|
525
|
+
private :observer_class_for
|
377
526
|
|
527
|
+
# Returns the name of the current mailer. This method is also being used as a path for a view lookup.
|
528
|
+
# If this is an anonymous mailer, this method will return +anonymous+ instead.
|
378
529
|
def mailer_name
|
379
530
|
@mailer_name ||= anonymous? ? "anonymous" : name.underscore
|
380
531
|
end
|
532
|
+
# Allows to set the name of current mailer.
|
381
533
|
attr_writer :mailer_name
|
382
534
|
alias :controller_path :mailer_name
|
383
535
|
|
536
|
+
# Sets the defaults through app configuration:
|
537
|
+
#
|
538
|
+
# config.action_mailer.default(from: "no-reply@example.org")
|
539
|
+
#
|
540
|
+
# Aliased by ::default_options=
|
384
541
|
def default(value = nil)
|
385
542
|
self.default_params = default_params.merge(value).freeze if value
|
386
543
|
default_params
|
387
544
|
end
|
388
|
-
|
389
|
-
# Receives a raw email, parses it into an email object, decodes it,
|
390
|
-
# instantiates a new mailer, and passes the email object to the mailer
|
391
|
-
# object's +receive+ method. If you want your mailer to be able to
|
392
|
-
# process incoming messages, you'll need to implement a +receive+
|
393
|
-
# method that accepts the raw email string as a parameter:
|
545
|
+
# Allows to set defaults through app configuration:
|
394
546
|
#
|
395
|
-
#
|
396
|
-
|
397
|
-
# ...
|
398
|
-
# end
|
399
|
-
# end
|
400
|
-
def receive(raw_mail)
|
401
|
-
ActiveSupport::Notifications.instrument("receive.action_mailer") do |payload|
|
402
|
-
mail = Mail.new(raw_mail)
|
403
|
-
set_payload_for_mail(payload, mail)
|
404
|
-
new.receive(mail)
|
405
|
-
end
|
406
|
-
end
|
547
|
+
# config.action_mailer.default_options = { from: "no-reply@example.org" }
|
548
|
+
alias :default_options= :default
|
407
549
|
|
408
|
-
# Wraps an email delivery inside of
|
409
|
-
#
|
410
|
-
#
|
411
|
-
#
|
550
|
+
# Wraps an email delivery inside of <tt>ActiveSupport::Notifications</tt> instrumentation.
|
551
|
+
#
|
552
|
+
# This method is actually called by the <tt>Mail::Message</tt> object itself
|
553
|
+
# through a callback when you call <tt>:deliver</tt> on the <tt>Mail::Message</tt>,
|
554
|
+
# calling +deliver_mail+ directly and passing a <tt>Mail::Message</tt> will do
|
555
|
+
# nothing except tell the logger you sent the email.
|
412
556
|
def deliver_mail(mail) #:nodoc:
|
413
557
|
ActiveSupport::Notifications.instrument("deliver.action_mailer") do |payload|
|
414
|
-
|
558
|
+
set_payload_for_mail(payload, mail)
|
415
559
|
yield # Let Mail do the delivery actions
|
416
560
|
end
|
417
561
|
end
|
418
562
|
|
419
|
-
|
420
|
-
|
563
|
+
# Returns an email in the format "Name <email@example.com>".
|
564
|
+
def email_address_with_name(address, name)
|
565
|
+
Mail::Address.new.tap do |builder|
|
566
|
+
builder.address = address
|
567
|
+
builder.display_name = name
|
568
|
+
end.to_s
|
421
569
|
end
|
422
570
|
|
423
|
-
|
571
|
+
private
|
572
|
+
def set_payload_for_mail(payload, mail)
|
573
|
+
payload[:mail] = mail.encoded
|
574
|
+
payload[:mailer] = name
|
575
|
+
payload[:message_id] = mail.message_id
|
576
|
+
payload[:subject] = mail.subject
|
577
|
+
payload[:to] = mail.to
|
578
|
+
payload[:from] = mail.from
|
579
|
+
payload[:bcc] = mail.bcc if mail.bcc.present?
|
580
|
+
payload[:cc] = mail.cc if mail.cc.present?
|
581
|
+
payload[:date] = mail.date
|
582
|
+
payload[:perform_deliveries] = mail.perform_deliveries
|
583
|
+
end
|
424
584
|
|
425
|
-
def
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
payload[:bcc] = mail.bcc if mail.bcc.present?
|
432
|
-
payload[:cc] = mail.cc if mail.cc.present?
|
433
|
-
payload[:date] = mail.date
|
434
|
-
payload[:mail] = mail.encoded
|
585
|
+
def method_missing(method_name, *args)
|
586
|
+
if action_methods.include?(method_name.to_s)
|
587
|
+
MessageDelivery.new(self, method_name, *args)
|
588
|
+
else
|
589
|
+
super
|
590
|
+
end
|
435
591
|
end
|
592
|
+
ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
|
436
593
|
|
437
|
-
def
|
438
|
-
|
439
|
-
new(method, *args).message
|
594
|
+
def respond_to_missing?(method, include_all = false)
|
595
|
+
action_methods.include?(method.to_s) || super
|
440
596
|
end
|
441
597
|
end
|
442
598
|
|
443
599
|
attr_internal :message
|
444
600
|
|
445
|
-
|
446
|
-
# will be initialized according to the named method. If not, the mailer will
|
447
|
-
# remain uninitialized (useful when you only need to invoke the "receive"
|
448
|
-
# method, for instance).
|
449
|
-
def initialize(method_name=nil, *args)
|
601
|
+
def initialize
|
450
602
|
super()
|
603
|
+
@_mail_was_called = false
|
451
604
|
@_message = Mail.new
|
452
|
-
process(method_name, *args) if method_name
|
453
605
|
end
|
454
606
|
|
455
|
-
def process(*args) #:nodoc:
|
456
|
-
|
607
|
+
def process(method_name, *args) #:nodoc:
|
608
|
+
payload = {
|
609
|
+
mailer: self.class.name,
|
610
|
+
action: method_name,
|
611
|
+
args: args
|
612
|
+
}
|
457
613
|
|
458
|
-
|
459
|
-
|
460
|
-
@_message = NullMail.new
|
614
|
+
ActiveSupport::Notifications.instrument("process.action_mailer", payload) do
|
615
|
+
super
|
616
|
+
@_message = NullMail.new unless @_mail_was_called
|
461
617
|
end
|
462
618
|
end
|
463
619
|
|
464
620
|
class NullMail #:nodoc:
|
465
|
-
def body;
|
621
|
+
def body; "" end
|
622
|
+
def header; {} end
|
623
|
+
|
624
|
+
def respond_to?(string, include_all = false)
|
625
|
+
true
|
626
|
+
end
|
466
627
|
|
467
628
|
def method_missing(*args)
|
468
629
|
nil
|
469
630
|
end
|
470
631
|
end
|
471
632
|
|
633
|
+
# Returns the name of the mailer object.
|
472
634
|
def mailer_name
|
473
635
|
self.class.mailer_name
|
474
636
|
end
|
475
637
|
|
476
|
-
#
|
477
|
-
|
638
|
+
# Returns an email in the format "Name <email@example.com>".
|
639
|
+
def email_address_with_name(address, name)
|
640
|
+
self.class.email_address_with_name(address, name)
|
641
|
+
end
|
642
|
+
|
643
|
+
# Allows you to pass random and unusual headers to the new <tt>Mail::Message</tt>
|
644
|
+
# object which will add them to itself.
|
478
645
|
#
|
479
646
|
# headers['X-Special-Domain-Specific-Header'] = "SecretValue"
|
480
647
|
#
|
481
|
-
# You can also pass a hash into headers of header field names and values,
|
482
|
-
# will then be set on the Mail::Message object:
|
648
|
+
# You can also pass a hash into headers of header field names and values,
|
649
|
+
# which will then be set on the <tt>Mail::Message</tt> object:
|
483
650
|
#
|
484
651
|
# headers 'X-Special-Domain-Specific-Header' => "SecretValue",
|
485
652
|
# 'In-Reply-To' => incoming.message_id
|
486
653
|
#
|
487
|
-
# The resulting Mail::Message will have the following in
|
654
|
+
# The resulting <tt>Mail::Message</tt> will have the following in its header:
|
488
655
|
#
|
489
656
|
# X-Special-Domain-Specific-Header: SecretValue
|
490
|
-
|
657
|
+
#
|
658
|
+
# Note about replacing already defined headers:
|
659
|
+
#
|
660
|
+
# * +subject+
|
661
|
+
# * +sender+
|
662
|
+
# * +from+
|
663
|
+
# * +to+
|
664
|
+
# * +cc+
|
665
|
+
# * +bcc+
|
666
|
+
# * +reply-to+
|
667
|
+
# * +orig-date+
|
668
|
+
# * +message-id+
|
669
|
+
# * +references+
|
670
|
+
#
|
671
|
+
# Fields can only appear once in email headers while other fields such as
|
672
|
+
# <tt>X-Anything</tt> can appear multiple times.
|
673
|
+
#
|
674
|
+
# If you want to replace any header which already exists, first set it to
|
675
|
+
# +nil+ in order to reset the value otherwise another field will be added
|
676
|
+
# for the same header.
|
677
|
+
def headers(args = nil)
|
491
678
|
if args
|
492
679
|
@_message.headers(args)
|
493
680
|
else
|
@@ -499,23 +686,23 @@ module ActionMailer #:nodoc:
|
|
499
686
|
#
|
500
687
|
# mail.attachments['filename.jpg'] = File.read('/path/to/filename.jpg')
|
501
688
|
#
|
502
|
-
# If you do this, then Mail will take the file name and work out the mime type
|
503
|
-
# set the Content-Type, Content-Disposition, Content-Transfer-Encoding
|
504
|
-
#
|
689
|
+
# If you do this, then Mail will take the file name and work out the mime type.
|
690
|
+
# It will also set the Content-Type, Content-Disposition, Content-Transfer-Encoding
|
691
|
+
# and encode the contents of the attachment in Base64.
|
505
692
|
#
|
506
693
|
# You can also specify overrides if you want by passing a hash instead of a string:
|
507
694
|
#
|
508
|
-
# mail.attachments['filename.jpg'] = {:
|
509
|
-
# :
|
695
|
+
# mail.attachments['filename.jpg'] = {mime_type: 'application/gzip',
|
696
|
+
# content: File.read('/path/to/filename.jpg')}
|
510
697
|
#
|
511
|
-
# If you want to use
|
512
|
-
#
|
513
|
-
#
|
698
|
+
# If you want to use encoding other than Base64 then you will need to pass encoding
|
699
|
+
# type along with the pre-encoded content as Mail doesn't know how to decode the
|
700
|
+
# data:
|
514
701
|
#
|
515
702
|
# file_content = SpecialEncode(File.read('/path/to/filename.jpg'))
|
516
|
-
# mail.attachments['filename.jpg'] = {:
|
517
|
-
# :
|
518
|
-
# :
|
703
|
+
# mail.attachments['filename.jpg'] = {mime_type: 'application/gzip',
|
704
|
+
# encoding: 'SpecialEncoding',
|
705
|
+
# content: file_content }
|
519
706
|
#
|
520
707
|
# You can also search for specific attachments:
|
521
708
|
#
|
@@ -526,224 +713,319 @@ module ActionMailer #:nodoc:
|
|
526
713
|
# mail.attachments[0] # => Mail::Part (first attachment)
|
527
714
|
#
|
528
715
|
def attachments
|
529
|
-
@
|
716
|
+
if @_mail_was_called
|
717
|
+
LateAttachmentsProxy.new(@_message.attachments)
|
718
|
+
else
|
719
|
+
@_message.attachments
|
720
|
+
end
|
721
|
+
end
|
722
|
+
|
723
|
+
class LateAttachmentsProxy < SimpleDelegator
|
724
|
+
def inline; self end
|
725
|
+
def []=(_name, _content); _raise_error end
|
726
|
+
|
727
|
+
private
|
728
|
+
def _raise_error
|
729
|
+
raise RuntimeError, "Can't add attachments after `mail` was called.\n" \
|
730
|
+
"Make sure to use `attachments[]=` before calling `mail`."
|
731
|
+
end
|
530
732
|
end
|
531
733
|
|
532
734
|
# The main method that creates the message and renders the email templates. There are
|
533
735
|
# two ways to call this method, with a block, or without a block.
|
534
736
|
#
|
535
|
-
#
|
536
|
-
# in an email message, these are:
|
737
|
+
# It accepts a headers hash. This hash allows you to specify
|
738
|
+
# the most used headers in an email message, these are:
|
537
739
|
#
|
538
|
-
# *
|
539
|
-
# ask the Rails I18n class for a translated
|
740
|
+
# * +:subject+ - The subject of the message, if this is omitted, Action Mailer will
|
741
|
+
# ask the Rails I18n class for a translated +:subject+ in the scope of
|
540
742
|
# <tt>[mailer_scope, action_name]</tt> or if this is missing, will translate the
|
541
|
-
# humanized version of the
|
542
|
-
# *
|
743
|
+
# humanized version of the +action_name+
|
744
|
+
# * +:to+ - Who the message is destined for, can be a string of addresses, or an array
|
543
745
|
# of addresses.
|
544
|
-
# *
|
545
|
-
# *
|
746
|
+
# * +:from+ - Who the message is from
|
747
|
+
# * +:cc+ - Who you would like to Carbon-Copy on this email, can be a string of addresses,
|
546
748
|
# or an array of addresses.
|
547
|
-
# *
|
749
|
+
# * +:bcc+ - Who you would like to Blind-Carbon-Copy on this email, can be a string of
|
548
750
|
# addresses, or an array of addresses.
|
549
|
-
# *
|
550
|
-
# *
|
751
|
+
# * +:reply_to+ - Who to set the Reply-To header of the email to.
|
752
|
+
# * +:date+ - The date to say the email was sent on.
|
551
753
|
#
|
552
|
-
# You can set default values for any of the above headers (except
|
553
|
-
# class method:
|
754
|
+
# You can set default values for any of the above headers (except +:date+)
|
755
|
+
# by using the ::default class method:
|
554
756
|
#
|
555
757
|
# class Notifier < ActionMailer::Base
|
556
|
-
#
|
557
|
-
#
|
558
|
-
#
|
758
|
+
# default from: 'no-reply@test.lindsaar.net',
|
759
|
+
# bcc: 'email_logger@test.lindsaar.net',
|
760
|
+
# reply_to: 'bounces@test.lindsaar.net'
|
559
761
|
# end
|
560
762
|
#
|
561
763
|
# If you need other headers not listed above, you can either pass them in
|
562
764
|
# as part of the headers hash or use the <tt>headers['name'] = value</tt>
|
563
765
|
# method.
|
564
766
|
#
|
565
|
-
# When a
|
566
|
-
# address for the Mail message. Setting this is useful
|
567
|
-
# sent to a different address than the
|
568
|
-
#
|
569
|
-
# field for the 'envelope
|
767
|
+
# When a +:return_path+ is specified as header, that value will be used as
|
768
|
+
# the 'envelope from' address for the Mail message. Setting this is useful
|
769
|
+
# when you want delivery notifications sent to a different address than the
|
770
|
+
# one in +:from+. Mail will actually use the +:return_path+ in preference
|
771
|
+
# to the +:sender+ in preference to the +:from+ field for the 'envelope
|
772
|
+
# from' value.
|
570
773
|
#
|
571
|
-
# If you do not pass a block to the +mail+ method, it will find all
|
572
|
-
# view paths using by default the mailer name and the
|
573
|
-
# called from, it will then create parts for
|
574
|
-
# making educated guesses on correct
|
575
|
-
#
|
774
|
+
# If you do not pass a block to the +mail+ method, it will find all
|
775
|
+
# templates in the view paths using by default the mailer name and the
|
776
|
+
# method name that it is being called from, it will then create parts for
|
777
|
+
# each of these templates intelligently, making educated guesses on correct
|
778
|
+
# content type and sequence, and return a fully prepared <tt>Mail::Message</tt>
|
779
|
+
# ready to call <tt>:deliver</tt> on to send.
|
576
780
|
#
|
577
781
|
# For example:
|
578
782
|
#
|
579
783
|
# class Notifier < ActionMailer::Base
|
580
|
-
# default :
|
784
|
+
# default from: 'no-reply@test.lindsaar.net'
|
581
785
|
#
|
582
786
|
# def welcome
|
583
|
-
# mail(:
|
787
|
+
# mail(to: 'mikel@test.lindsaar.net')
|
584
788
|
# end
|
585
789
|
# end
|
586
790
|
#
|
587
|
-
# Will look for all templates at "app/views/notifier" with name "welcome".
|
588
|
-
#
|
791
|
+
# Will look for all templates at "app/views/notifier" with name "welcome".
|
792
|
+
# If no welcome template exists, it will raise an ActionView::MissingTemplate error.
|
589
793
|
#
|
590
|
-
#
|
794
|
+
# However, those can be customized:
|
795
|
+
#
|
796
|
+
# mail(template_path: 'notifications', template_name: 'another')
|
591
797
|
#
|
592
798
|
# And now it will look for all templates at "app/views/notifications" with name "another".
|
593
799
|
#
|
594
800
|
# If you do pass a block, you can render specific templates of your choice:
|
595
801
|
#
|
596
|
-
# mail(:
|
802
|
+
# mail(to: 'mikel@test.lindsaar.net') do |format|
|
597
803
|
# format.text
|
598
804
|
# format.html
|
599
805
|
# end
|
600
806
|
#
|
601
|
-
# You can even render text directly without using a template:
|
807
|
+
# You can even render plain text directly without using a template:
|
602
808
|
#
|
603
|
-
# mail(:
|
604
|
-
# format.text { render :
|
605
|
-
# format.html { render :
|
809
|
+
# mail(to: 'mikel@test.lindsaar.net') do |format|
|
810
|
+
# format.text { render plain: "Hello Mikel!" }
|
811
|
+
# format.html { render html: "<h1>Hello Mikel!</h1>".html_safe }
|
606
812
|
# end
|
607
813
|
#
|
608
|
-
# Which will render a
|
609
|
-
#
|
814
|
+
# Which will render a +multipart/alternative+ email with +text/plain+ and
|
815
|
+
# +text/html+ parts.
|
610
816
|
#
|
611
817
|
# The block syntax also allows you to customize the part headers if desired:
|
612
818
|
#
|
613
|
-
# mail(:
|
614
|
-
# format.text(:
|
819
|
+
# mail(to: 'mikel@test.lindsaar.net') do |format|
|
820
|
+
# format.text(content_transfer_encoding: "base64")
|
615
821
|
# format.html
|
616
822
|
# end
|
617
823
|
#
|
618
|
-
def mail(headers={}, &block)
|
619
|
-
|
620
|
-
# Should be removed when old API is removed
|
621
|
-
@mail_was_called = true
|
622
|
-
m = @_message
|
824
|
+
def mail(headers = {}, &block)
|
825
|
+
return message if @_mail_was_called && headers.blank? && !block
|
623
826
|
|
624
|
-
# At the beginning, do not consider class default for
|
827
|
+
# At the beginning, do not consider class default for content_type
|
625
828
|
content_type = headers[:content_type]
|
626
|
-
parts_order = headers[:parts_order]
|
627
|
-
|
628
|
-
# Call all the procs (if any)
|
629
|
-
default_values = self.class.default.merge(self.class.default) do |k,v|
|
630
|
-
v.respond_to?(:call) ? v.bind(self).call : v
|
631
|
-
end
|
632
829
|
|
633
|
-
|
634
|
-
headers = headers.reverse_merge(default_values)
|
635
|
-
headers[:subject] ||= default_i18n_subject
|
830
|
+
headers = apply_defaults(headers)
|
636
831
|
|
637
832
|
# Apply charset at the beginning so all fields are properly quoted
|
638
|
-
|
833
|
+
message.charset = charset = headers[:charset]
|
639
834
|
|
640
835
|
# Set configure delivery behavior
|
641
|
-
wrap_delivery_behavior!(headers
|
836
|
+
wrap_delivery_behavior!(headers[:delivery_method], headers[:delivery_method_options])
|
642
837
|
|
643
|
-
|
644
|
-
assignable = headers.except(:parts_order, :content_type, :body, :template_name, :template_path)
|
645
|
-
assignable.each { |k, v| m[k] = v }
|
838
|
+
assign_headers_to_message(message, headers)
|
646
839
|
|
647
840
|
# Render the templates and blocks
|
648
|
-
responses
|
649
|
-
|
841
|
+
responses = collect_responses(headers, &block)
|
842
|
+
@_mail_was_called = true
|
650
843
|
|
651
|
-
|
652
|
-
|
653
|
-
m.charset = charset
|
844
|
+
create_parts_from_responses(message, responses)
|
845
|
+
wrap_inline_attachments(message)
|
654
846
|
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
847
|
+
# Set up content type, reapply charset and handle parts order
|
848
|
+
message.content_type = set_content_type(message, content_type, headers[:content_type])
|
849
|
+
message.charset = charset
|
850
|
+
|
851
|
+
if message.multipart?
|
852
|
+
message.body.set_sort_order(headers[:parts_order])
|
853
|
+
message.body.sort_parts!
|
659
854
|
end
|
660
855
|
|
661
|
-
|
856
|
+
message
|
662
857
|
end
|
663
858
|
|
664
|
-
|
859
|
+
private
|
860
|
+
# Used by #mail to set the content type of the message.
|
861
|
+
#
|
862
|
+
# It will use the given +user_content_type+, or multipart if the mail
|
863
|
+
# message has any attachments. If the attachments are inline, the content
|
864
|
+
# type will be "multipart/related", otherwise "multipart/mixed".
|
865
|
+
#
|
866
|
+
# If there is no content type passed in via headers, and there are no
|
867
|
+
# attachments, or the message is multipart, then the default content type is
|
868
|
+
# used.
|
869
|
+
def set_content_type(m, user_content_type, class_default) # :doc:
|
870
|
+
params = m.content_type_parameters || {}
|
871
|
+
case
|
872
|
+
when user_content_type.present?
|
873
|
+
user_content_type
|
874
|
+
when m.has_attachments?
|
875
|
+
if m.attachments.all?(&:inline?)
|
876
|
+
["multipart", "related", params]
|
877
|
+
else
|
878
|
+
["multipart", "mixed", params]
|
879
|
+
end
|
880
|
+
when m.multipart?
|
881
|
+
["multipart", "alternative", params]
|
882
|
+
else
|
883
|
+
m.content_type || class_default
|
884
|
+
end
|
885
|
+
end
|
886
|
+
|
887
|
+
# Translates the +subject+ using Rails I18n class under <tt>[mailer_scope, action_name]</tt> scope.
|
888
|
+
# If it does not find a translation for the +subject+ under the specified scope it will default to a
|
889
|
+
# humanized version of the <tt>action_name</tt>.
|
890
|
+
# If the subject has interpolations, you can pass them through the +interpolations+ parameter.
|
891
|
+
def default_i18n_subject(interpolations = {}) # :doc:
|
892
|
+
mailer_scope = self.class.mailer_name.tr("/", ".")
|
893
|
+
I18n.t(:subject, **interpolations.merge(scope: [mailer_scope, action_name], default: action_name.humanize))
|
894
|
+
end
|
895
|
+
|
896
|
+
# Emails do not support relative path links.
|
897
|
+
def self.supports_path? # :doc:
|
898
|
+
false
|
899
|
+
end
|
900
|
+
|
901
|
+
def apply_defaults(headers)
|
902
|
+
default_values = self.class.default.transform_values do |value|
|
903
|
+
compute_default(value)
|
904
|
+
end
|
905
|
+
|
906
|
+
headers_with_defaults = headers.reverse_merge(default_values)
|
907
|
+
headers_with_defaults[:subject] ||= default_i18n_subject
|
908
|
+
headers_with_defaults
|
909
|
+
end
|
910
|
+
|
911
|
+
def compute_default(value)
|
912
|
+
return value unless value.is_a?(Proc)
|
665
913
|
|
666
|
-
|
667
|
-
|
668
|
-
case
|
669
|
-
when user_content_type.present?
|
670
|
-
user_content_type
|
671
|
-
when m.has_attachments?
|
672
|
-
if m.attachments.detect { |a| a.inline? }
|
673
|
-
["multipart", "related", params]
|
914
|
+
if value.arity == 1
|
915
|
+
instance_exec(self, &value)
|
674
916
|
else
|
675
|
-
|
917
|
+
instance_exec(&value)
|
676
918
|
end
|
677
|
-
when m.multipart?
|
678
|
-
["multipart", "alternative", params]
|
679
|
-
else
|
680
|
-
m.content_type || class_default
|
681
919
|
end
|
682
|
-
end
|
683
920
|
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
I18n.t(:subject, :scope => [mailer_scope, action_name], :default => action_name.humanize)
|
690
|
-
end
|
921
|
+
def assign_headers_to_message(message, headers)
|
922
|
+
assignable = headers.except(:parts_order, :content_type, :body, :template_name,
|
923
|
+
:template_path, :delivery_method, :delivery_method_options)
|
924
|
+
assignable.each { |k, v| message[k] = v }
|
925
|
+
end
|
691
926
|
|
692
|
-
|
693
|
-
|
927
|
+
def collect_responses(headers, &block)
|
928
|
+
if block_given?
|
929
|
+
collect_responses_from_block(headers, &block)
|
930
|
+
elsif headers[:body]
|
931
|
+
collect_responses_from_text(headers)
|
932
|
+
else
|
933
|
+
collect_responses_from_templates(headers)
|
934
|
+
end
|
935
|
+
end
|
694
936
|
|
695
|
-
|
696
|
-
|
937
|
+
def collect_responses_from_block(headers)
|
938
|
+
templates_name = headers[:template_name] || action_name
|
939
|
+
collector = ActionMailer::Collector.new(lookup_context) { render(templates_name) }
|
697
940
|
yield(collector)
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
:
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
templates_name = headers.delete(:template_name) || action_name
|
941
|
+
collector.responses
|
942
|
+
end
|
943
|
+
|
944
|
+
def collect_responses_from_text(headers)
|
945
|
+
[{
|
946
|
+
body: headers.delete(:body),
|
947
|
+
content_type: headers[:content_type] || "text/plain"
|
948
|
+
}]
|
949
|
+
end
|
708
950
|
|
709
|
-
|
710
|
-
|
951
|
+
def collect_responses_from_templates(headers)
|
952
|
+
templates_path = headers[:template_path] || self.class.mailer_name
|
953
|
+
templates_name = headers[:template_name] || action_name
|
711
954
|
|
712
|
-
|
713
|
-
|
714
|
-
|
955
|
+
each_template(Array(templates_path), templates_name).map do |template|
|
956
|
+
format = template.format || self.formats.first
|
957
|
+
{
|
958
|
+
body: render(template: template, formats: [format]),
|
959
|
+
content_type: Mime[format].to_s
|
715
960
|
}
|
716
961
|
end
|
717
962
|
end
|
718
963
|
|
719
|
-
|
720
|
-
|
964
|
+
def each_template(paths, name, &block)
|
965
|
+
templates = lookup_context.find_all(name, paths)
|
966
|
+
if templates.empty?
|
967
|
+
raise ActionView::MissingTemplate.new(paths, name, paths, false, "mailer")
|
968
|
+
else
|
969
|
+
templates.uniq(&:format).each(&block)
|
970
|
+
end
|
971
|
+
end
|
721
972
|
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
973
|
+
def wrap_inline_attachments(message)
|
974
|
+
# If we have both types of attachment, wrap all the inline attachments
|
975
|
+
# in multipart/related, but not the actual attachments
|
976
|
+
if message.attachments.detect(&:inline?) && message.attachments.detect { |a| !a.inline? }
|
977
|
+
related = Mail::Part.new
|
978
|
+
related.content_type = "multipart/related"
|
979
|
+
mixed = [ related ]
|
726
980
|
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
981
|
+
message.parts.each do |p|
|
982
|
+
if p.attachment? && !p.inline?
|
983
|
+
mixed << p
|
984
|
+
else
|
985
|
+
related.add_part(p)
|
986
|
+
end
|
987
|
+
end
|
988
|
+
|
989
|
+
message.parts.clear
|
990
|
+
mixed.each { |c| message.add_part(c) }
|
991
|
+
end
|
737
992
|
end
|
738
|
-
end
|
739
993
|
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
994
|
+
def create_parts_from_responses(m, responses)
|
995
|
+
if responses.size == 1 && !m.has_attachments?
|
996
|
+
responses[0].each { |k, v| m[k] = v }
|
997
|
+
elsif responses.size > 1 && m.has_attachments?
|
998
|
+
container = Mail::Part.new
|
999
|
+
container.content_type = "multipart/alternative"
|
1000
|
+
responses.each { |r| insert_part(container, r, m.charset) }
|
1001
|
+
m.add_part(container)
|
1002
|
+
else
|
1003
|
+
responses.each { |r| insert_part(m, r, m.charset) }
|
1004
|
+
end
|
1005
|
+
end
|
1006
|
+
|
1007
|
+
def insert_part(container, response, charset)
|
1008
|
+
response[:charset] ||= charset
|
1009
|
+
part = Mail::Part.new(response)
|
1010
|
+
container.add_part(part)
|
1011
|
+
end
|
745
1012
|
|
746
|
-
|
1013
|
+
# This and #instrument_name is for caching instrument
|
1014
|
+
def instrument_payload(key)
|
1015
|
+
{
|
1016
|
+
mailer: mailer_name,
|
1017
|
+
key: key
|
1018
|
+
}
|
1019
|
+
end
|
1020
|
+
|
1021
|
+
def instrument_name
|
1022
|
+
"action_mailer"
|
1023
|
+
end
|
1024
|
+
|
1025
|
+
def _protected_ivars
|
1026
|
+
PROTECTED_IVARS
|
1027
|
+
end
|
1028
|
+
|
1029
|
+
ActiveSupport.run_load_hooks(:action_mailer, self)
|
747
1030
|
end
|
748
1031
|
end
|
749
|
-
|