##// END OF EJS Templates
Adds a X-Redmine-Sender header to email notifications (#5643)....
Jean-Philippe Lang -
r8665:967f42aa98f5
parent child
Show More
@@ -1,472 +1,477
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class Mailer < ActionMailer::Base
19 19 layout 'mailer'
20 20 helper :application
21 21 helper :issues
22 22 helper :custom_fields
23 23
24 24 include ActionController::UrlWriter
25 25 include Redmine::I18n
26 26
27 27 def self.default_url_options
28 28 h = Setting.host_name
29 29 h = h.to_s.gsub(%r{\/.*$}, '') unless Redmine::Utils.relative_url_root.blank?
30 30 { :host => h, :protocol => Setting.protocol }
31 31 end
32 32
33 33 # Builds a tmail object used to email recipients of the added issue.
34 34 #
35 35 # Example:
36 36 # issue_add(issue) => tmail object
37 37 # Mailer.deliver_issue_add(issue) => sends an email to issue recipients
38 38 def issue_add(issue)
39 39 redmine_headers 'Project' => issue.project.identifier,
40 40 'Issue-Id' => issue.id,
41 41 'Issue-Author' => issue.author.login
42 42 redmine_headers 'Issue-Assignee' => issue.assigned_to.login if issue.assigned_to
43 43 message_id issue
44 @author = issue.author
44 45 recipients issue.recipients
45 46 cc(issue.watcher_recipients - @recipients)
46 47 subject "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] (#{issue.status.name}) #{issue.subject}"
47 48 body :issue => issue,
48 49 :issue_url => url_for(:controller => 'issues', :action => 'show', :id => issue)
49 50 render_multipart('issue_add', body)
50 51 end
51 52
52 53 # Builds a tmail object used to email recipients of the edited issue.
53 54 #
54 55 # Example:
55 56 # issue_edit(journal) => tmail object
56 57 # Mailer.deliver_issue_edit(journal) => sends an email to issue recipients
57 58 def issue_edit(journal)
58 59 issue = journal.journalized.reload
59 60 redmine_headers 'Project' => issue.project.identifier,
60 61 'Issue-Id' => issue.id,
61 62 'Issue-Author' => issue.author.login
62 63 redmine_headers 'Issue-Assignee' => issue.assigned_to.login if issue.assigned_to
63 64 message_id journal
64 65 references issue
65 66 @author = journal.user
66 67 recipients issue.recipients
67 68 # Watchers in cc
68 69 cc(issue.watcher_recipients - @recipients)
69 70 s = "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] "
70 71 s << "(#{issue.status.name}) " if journal.new_value_for('status_id')
71 72 s << issue.subject
72 73 subject s
73 74 body :issue => issue,
74 75 :journal => journal,
75 76 :issue_url => url_for(:controller => 'issues', :action => 'show', :id => issue, :anchor => "change-#{journal.id}")
76 77
77 78 render_multipart('issue_edit', body)
78 79 end
79 80
80 81 def reminder(user, issues, days)
81 82 set_language_if_valid user.language
82 83 recipients user.mail
83 84 subject l(:mail_subject_reminder, :count => issues.size, :days => days)
84 85 body :issues => issues,
85 86 :days => days,
86 87 :issues_url => url_for(:controller => 'issues', :action => 'index',
87 88 :set_filter => 1, :assigned_to_id => user.id,
88 89 :sort => 'due_date:asc')
89 90 render_multipart('reminder', body)
90 91 end
91 92
92 93 # Builds a tmail object used to email users belonging to the added document's project.
93 94 #
94 95 # Example:
95 96 # document_added(document) => tmail object
96 97 # Mailer.deliver_document_added(document) => sends an email to the document's project recipients
97 98 def document_added(document)
98 99 redmine_headers 'Project' => document.project.identifier
99 100 recipients document.recipients
100 101 subject "[#{document.project.name}] #{l(:label_document_new)}: #{document.title}"
101 102 body :document => document,
102 103 :document_url => url_for(:controller => 'documents', :action => 'show', :id => document)
103 104 render_multipart('document_added', body)
104 105 end
105 106
106 107 # Builds a tmail object used to email recipients of a project when an attachements are added.
107 108 #
108 109 # Example:
109 110 # attachments_added(attachments) => tmail object
110 111 # Mailer.deliver_attachments_added(attachments) => sends an email to the project's recipients
111 112 def attachments_added(attachments)
112 113 container = attachments.first.container
113 114 added_to = ''
114 115 added_to_url = ''
115 116 case container.class.name
116 117 when 'Project'
117 118 added_to_url = url_for(:controller => 'files', :action => 'index', :project_id => container)
118 119 added_to = "#{l(:label_project)}: #{container}"
119 120 recipients container.project.notified_users.select {|user| user.allowed_to?(:view_files, container.project)}.collect {|u| u.mail}
120 121 when 'Version'
121 122 added_to_url = url_for(:controller => 'files', :action => 'index', :project_id => container.project)
122 123 added_to = "#{l(:label_version)}: #{container.name}"
123 124 recipients container.project.notified_users.select {|user| user.allowed_to?(:view_files, container.project)}.collect {|u| u.mail}
124 125 when 'Document'
125 126 added_to_url = url_for(:controller => 'documents', :action => 'show', :id => container.id)
126 127 added_to = "#{l(:label_document)}: #{container.title}"
127 128 recipients container.recipients
128 129 end
129 130 redmine_headers 'Project' => container.project.identifier
130 131 subject "[#{container.project.name}] #{l(:label_attachment_new)}"
131 132 body :attachments => attachments,
132 133 :added_to => added_to,
133 134 :added_to_url => added_to_url
134 135 render_multipart('attachments_added', body)
135 136 end
136 137
137 138 # Builds a tmail object used to email recipients of a news' project when a news item is added.
138 139 #
139 140 # Example:
140 141 # news_added(news) => tmail object
141 142 # Mailer.deliver_news_added(news) => sends an email to the news' project recipients
142 143 def news_added(news)
143 144 redmine_headers 'Project' => news.project.identifier
144 145 message_id news
145 146 recipients news.recipients
146 147 subject "[#{news.project.name}] #{l(:label_news)}: #{news.title}"
147 148 body :news => news,
148 149 :news_url => url_for(:controller => 'news', :action => 'show', :id => news)
149 150 render_multipart('news_added', body)
150 151 end
151 152
152 153 # Builds a tmail object used to email recipients of a news' project when a news comment is added.
153 154 #
154 155 # Example:
155 156 # news_comment_added(comment) => tmail object
156 157 # Mailer.news_comment_added(comment) => sends an email to the news' project recipients
157 158 def news_comment_added(comment)
158 159 news = comment.commented
159 160 redmine_headers 'Project' => news.project.identifier
160 161 message_id comment
161 162 recipients news.recipients
162 163 cc news.watcher_recipients
163 164 subject "Re: [#{news.project.name}] #{l(:label_news)}: #{news.title}"
164 165 body :news => news,
165 166 :comment => comment,
166 167 :news_url => url_for(:controller => 'news', :action => 'show', :id => news)
167 168 render_multipart('news_comment_added', body)
168 169 end
169 170
170 171 # Builds a tmail object used to email the recipients of the specified message that was posted.
171 172 #
172 173 # Example:
173 174 # message_posted(message) => tmail object
174 175 # Mailer.deliver_message_posted(message) => sends an email to the recipients
175 176 def message_posted(message)
176 177 redmine_headers 'Project' => message.project.identifier,
177 178 'Topic-Id' => (message.parent_id || message.id)
178 179 message_id message
179 180 references message.parent unless message.parent.nil?
180 181 recipients(message.recipients)
181 182 cc((message.root.watcher_recipients + message.board.watcher_recipients).uniq - @recipients)
182 183 subject "[#{message.board.project.name} - #{message.board.name} - msg#{message.root.id}] #{message.subject}"
183 184 body :message => message,
184 185 :message_url => url_for(message.event_url)
185 186 render_multipart('message_posted', body)
186 187 end
187 188
188 189 # Builds a tmail object used to email the recipients of a project of the specified wiki content was added.
189 190 #
190 191 # Example:
191 192 # wiki_content_added(wiki_content) => tmail object
192 193 # Mailer.deliver_wiki_content_added(wiki_content) => sends an email to the project's recipients
193 194 def wiki_content_added(wiki_content)
194 195 redmine_headers 'Project' => wiki_content.project.identifier,
195 196 'Wiki-Page-Id' => wiki_content.page.id
196 197 message_id wiki_content
197 198 recipients wiki_content.recipients
198 199 cc(wiki_content.page.wiki.watcher_recipients - recipients)
199 200 subject "[#{wiki_content.project.name}] #{l(:mail_subject_wiki_content_added, :id => wiki_content.page.pretty_title)}"
200 201 body :wiki_content => wiki_content,
201 202 :wiki_content_url => url_for(:controller => 'wiki', :action => 'show',
202 203 :project_id => wiki_content.project,
203 204 :id => wiki_content.page.title)
204 205 render_multipart('wiki_content_added', body)
205 206 end
206 207
207 208 # Builds a tmail object used to email the recipients of a project of the specified wiki content was updated.
208 209 #
209 210 # Example:
210 211 # wiki_content_updated(wiki_content) => tmail object
211 212 # Mailer.deliver_wiki_content_updated(wiki_content) => sends an email to the project's recipients
212 213 def wiki_content_updated(wiki_content)
213 214 redmine_headers 'Project' => wiki_content.project.identifier,
214 215 'Wiki-Page-Id' => wiki_content.page.id
215 216 message_id wiki_content
216 217 recipients wiki_content.recipients
217 218 cc(wiki_content.page.wiki.watcher_recipients + wiki_content.page.watcher_recipients - recipients)
218 219 subject "[#{wiki_content.project.name}] #{l(:mail_subject_wiki_content_updated, :id => wiki_content.page.pretty_title)}"
219 220 body :wiki_content => wiki_content,
220 221 :wiki_content_url => url_for(:controller => 'wiki', :action => 'show',
221 222 :project_id => wiki_content.project,
222 223 :id => wiki_content.page.title),
223 224 :wiki_diff_url => url_for(:controller => 'wiki', :action => 'diff',
224 225 :project_id => wiki_content.project, :id => wiki_content.page.title,
225 226 :version => wiki_content.version)
226 227 render_multipart('wiki_content_updated', body)
227 228 end
228 229
229 230 # Builds a tmail object used to email the specified user their account information.
230 231 #
231 232 # Example:
232 233 # account_information(user, password) => tmail object
233 234 # Mailer.deliver_account_information(user, password) => sends account information to the user
234 235 def account_information(user, password)
235 236 set_language_if_valid user.language
236 237 recipients user.mail
237 238 subject l(:mail_subject_register, Setting.app_title)
238 239 body :user => user,
239 240 :password => password,
240 241 :login_url => url_for(:controller => 'account', :action => 'login')
241 242 render_multipart('account_information', body)
242 243 end
243 244
244 245 # Builds a tmail object used to email all active administrators of an account activation request.
245 246 #
246 247 # Example:
247 248 # account_activation_request(user) => tmail object
248 249 # Mailer.deliver_account_activation_request(user)=> sends an email to all active administrators
249 250 def account_activation_request(user)
250 251 # Send the email to all active administrators
251 252 recipients User.active.find(:all, :conditions => {:admin => true}).collect { |u| u.mail }.compact
252 253 subject l(:mail_subject_account_activation_request, Setting.app_title)
253 254 body :user => user,
254 255 :url => url_for(:controller => 'users', :action => 'index',
255 256 :status => User::STATUS_REGISTERED,
256 257 :sort_key => 'created_on', :sort_order => 'desc')
257 258 render_multipart('account_activation_request', body)
258 259 end
259 260
260 261 # Builds a tmail object used to email the specified user that their account was activated by an administrator.
261 262 #
262 263 # Example:
263 264 # account_activated(user) => tmail object
264 265 # Mailer.deliver_account_activated(user) => sends an email to the registered user
265 266 def account_activated(user)
266 267 set_language_if_valid user.language
267 268 recipients user.mail
268 269 subject l(:mail_subject_register, Setting.app_title)
269 270 body :user => user,
270 271 :login_url => url_for(:controller => 'account', :action => 'login')
271 272 render_multipart('account_activated', body)
272 273 end
273 274
274 275 def lost_password(token)
275 276 set_language_if_valid(token.user.language)
276 277 recipients token.user.mail
277 278 subject l(:mail_subject_lost_password, Setting.app_title)
278 279 body :token => token,
279 280 :url => url_for(:controller => 'account', :action => 'lost_password', :token => token.value)
280 281 render_multipart('lost_password', body)
281 282 end
282 283
283 284 def register(token)
284 285 set_language_if_valid(token.user.language)
285 286 recipients token.user.mail
286 287 subject l(:mail_subject_register, Setting.app_title)
287 288 body :token => token,
288 289 :url => url_for(:controller => 'account', :action => 'activate', :token => token.value)
289 290 render_multipart('register', body)
290 291 end
291 292
292 293 def test(user)
293 294 set_language_if_valid(user.language)
294 295 recipients user.mail
295 296 subject 'Redmine test'
296 297 body :url => url_for(:controller => 'welcome')
297 298 render_multipart('test', body)
298 299 end
299 300
300 301 # Overrides default deliver! method to prevent from sending an email
301 302 # with no recipient, cc or bcc
302 303 def deliver!(mail = @mail)
303 304 set_language_if_valid @initial_language
304 305 return false if (recipients.nil? || recipients.empty?) &&
305 306 (cc.nil? || cc.empty?) &&
306 307 (bcc.nil? || bcc.empty?)
307 308
308 309 # Set Message-Id and References
309 310 if @message_id_object
310 311 mail.message_id = self.class.message_id_for(@message_id_object)
311 312 end
312 313 if @references_objects
313 314 mail.references = @references_objects.collect {|o| self.class.message_id_for(o)}
314 315 end
315 316
316 317 # Log errors when raise_delivery_errors is set to false, Rails does not
317 318 raise_errors = self.class.raise_delivery_errors
318 319 self.class.raise_delivery_errors = true
319 320 begin
320 321 return super(mail)
321 322 rescue Exception => e
322 323 if raise_errors
323 324 raise e
324 325 elsif mylogger
325 326 mylogger.error "The following error occured while sending email notification: \"#{e.message}\". Check your configuration in config/configuration.yml."
326 327 end
327 328 ensure
328 329 self.class.raise_delivery_errors = raise_errors
329 330 end
330 331 end
331 332
332 333 # Sends reminders to issue assignees
333 334 # Available options:
334 335 # * :days => how many days in the future to remind about (defaults to 7)
335 336 # * :tracker => id of tracker for filtering issues (defaults to all trackers)
336 337 # * :project => id or identifier of project to process (defaults to all projects)
337 338 # * :users => array of user ids who should be reminded
338 339 def self.reminders(options={})
339 340 days = options[:days] || 7
340 341 project = options[:project] ? Project.find(options[:project]) : nil
341 342 tracker = options[:tracker] ? Tracker.find(options[:tracker]) : nil
342 343 user_ids = options[:users]
343 344
344 345 scope = Issue.open.scoped(:conditions => ["#{Issue.table_name}.assigned_to_id IS NOT NULL" +
345 346 " AND #{Project.table_name}.status = #{Project::STATUS_ACTIVE}" +
346 347 " AND #{Issue.table_name}.due_date <= ?", days.day.from_now.to_date]
347 348 )
348 349 scope = scope.scoped(:conditions => {:assigned_to_id => user_ids}) if user_ids.present?
349 350 scope = scope.scoped(:conditions => {:project_id => project.id}) if project
350 351 scope = scope.scoped(:conditions => {:tracker_id => tracker.id}) if tracker
351 352
352 353 issues_by_assignee = scope.all(:include => [:status, :assigned_to, :project, :tracker]).group_by(&:assigned_to)
353 354 issues_by_assignee.each do |assignee, issues|
354 355 deliver_reminder(assignee, issues, days) if assignee && assignee.active?
355 356 end
356 357 end
357 358
358 359 # Activates/desactivates email deliveries during +block+
359 360 def self.with_deliveries(enabled = true, &block)
360 361 was_enabled = ActionMailer::Base.perform_deliveries
361 362 ActionMailer::Base.perform_deliveries = !!enabled
362 363 yield
363 364 ensure
364 365 ActionMailer::Base.perform_deliveries = was_enabled
365 366 end
366 367
367 368 private
368 369 def initialize_defaults(method_name)
369 370 super
370 371 @initial_language = current_language
371 372 set_language_if_valid Setting.default_language
372 373 from Setting.mail_from
373 374
374 375 # Common headers
375 376 headers 'X-Mailer' => 'Redmine',
376 377 'X-Redmine-Host' => Setting.host_name,
377 378 'X-Redmine-Site' => Setting.app_title,
378 379 'X-Auto-Response-Suppress' => 'OOF',
379 380 'Auto-Submitted' => 'auto-generated'
380 381 end
381 382
382 383 # Appends a Redmine header field (name is prepended with 'X-Redmine-')
383 384 def redmine_headers(h)
384 385 h.each { |k,v| headers["X-Redmine-#{k}"] = v.to_s }
385 386 end
386 387
387 388 # Overrides the create_mail method
388 389 def create_mail
389 390 # Removes the current user from the recipients and cc
390 391 # if he doesn't want to receive notifications about what he does
391 392 @author ||= User.current
392 393 if @author.pref[:no_self_notified]
393 394 recipients.delete(@author.mail) if recipients
394 395 cc.delete(@author.mail) if cc
395 396 end
396 397
398 if @author.logged?
399 redmine_headers 'Sender' => @author.login
400 end
401
397 402 notified_users = [recipients, cc].flatten.compact.uniq
398 403 # Rails would log recipients only, not cc and bcc
399 404 mylogger.info "Sending email notification to: #{notified_users.join(', ')}" if mylogger
400 405
401 406 # Blind carbon copy recipients
402 407 if Setting.bcc_recipients?
403 408 bcc(notified_users)
404 409 recipients []
405 410 cc []
406 411 end
407 412 super
408 413 end
409 414
410 415 # Rails 2.3 has problems rendering implicit multipart messages with
411 416 # layouts so this method will wrap an multipart messages with
412 417 # explicit parts.
413 418 #
414 419 # https://rails.lighthouseapp.com/projects/8994/tickets/2338-actionmailer-mailer-views-and-content-type
415 420 # https://rails.lighthouseapp.com/projects/8994/tickets/1799-actionmailer-doesnt-set-template_format-when-rendering-layouts
416 421
417 422 def render_multipart(method_name, body)
418 423 if Setting.plain_text_mail?
419 424 content_type "text/plain"
420 425 body render(:file => "#{method_name}.text.erb",
421 426 :body => body,
422 427 :layout => 'mailer.text.erb')
423 428 else
424 429 content_type "multipart/alternative"
425 430 part :content_type => "text/plain",
426 431 :body => render(:file => "#{method_name}.text.erb",
427 432 :body => body, :layout => 'mailer.text.erb')
428 433 part :content_type => "text/html",
429 434 :body => render_message("#{method_name}.html.erb", body)
430 435 end
431 436 end
432 437
433 438 # Makes partial rendering work with Rails 1.2 (retro-compatibility)
434 439 def self.controller_path
435 440 ''
436 441 end unless respond_to?('controller_path')
437 442
438 443 # Returns a predictable Message-Id for the given object
439 444 def self.message_id_for(object)
440 445 # id + timestamp should reduce the odds of a collision
441 446 # as far as we don't send multiple emails for the same object
442 447 timestamp = object.send(object.respond_to?(:created_on) ? :created_on : :updated_on)
443 448 hash = "redmine.#{object.class.name.demodulize.underscore}-#{object.id}.#{timestamp.strftime("%Y%m%d%H%M%S")}"
444 449 host = Setting.mail_from.to_s.gsub(%r{^.*@}, '')
445 450 host = "#{::Socket.gethostname}.redmine" if host.empty?
446 451 "<#{hash}@#{host}>"
447 452 end
448 453
449 454 private
450 455
451 456 def message_id(object)
452 457 @message_id_object = object
453 458 end
454 459
455 460 def references(object)
456 461 @references_objects ||= []
457 462 @references_objects << object
458 463 end
459 464
460 465 def mylogger
461 466 Rails.logger
462 467 end
463 468 end
464 469
465 470 # Patch TMail so that message_id is not overwritten
466 471 module TMail
467 472 class Mail
468 473 def add_message_id( fqdn = nil )
469 474 self.message_id ||= ::TMail::new_message_id(fqdn)
470 475 end
471 476 end
472 477 end
@@ -1,510 +1,518
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../../test_helper', __FILE__)
19 19
20 20 class MailerTest < ActiveSupport::TestCase
21 21 include Redmine::I18n
22 22 include ActionController::Assertions::SelectorAssertions
23 23 fixtures :projects, :enabled_modules, :issues, :users, :members,
24 24 :member_roles, :roles, :documents, :attachments, :news,
25 25 :tokens, :journals, :journal_details, :changesets, :trackers,
26 26 :issue_statuses, :enumerations, :messages, :boards, :repositories,
27 27 :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions,
28 28 :versions,
29 29 :comments
30 30
31 31 def setup
32 32 ActionMailer::Base.deliveries.clear
33 33 Setting.host_name = 'mydomain.foo'
34 34 Setting.protocol = 'http'
35 35 Setting.plain_text_mail = '0'
36 36 end
37 37
38 38 def test_generated_links_in_emails
39 39 Setting.host_name = 'mydomain.foo'
40 40 Setting.protocol = 'https'
41 41
42 42 journal = Journal.find(2)
43 43 assert Mailer.deliver_issue_edit(journal)
44 44
45 45 mail = ActionMailer::Base.deliveries.last
46 46 assert_kind_of TMail::Mail, mail
47 47
48 48 assert_select_email do
49 49 # link to the main ticket
50 50 assert_select "a[href=?]",
51 51 "https://mydomain.foo/issues/1#change-2",
52 52 :text => "Bug #1: Can't print recipes"
53 53 # link to a referenced ticket
54 54 assert_select "a[href=?][title=?]",
55 55 "https://mydomain.foo/issues/2",
56 56 "Add ingredients categories (Assigned)",
57 57 :text => "#2"
58 58 # link to a changeset
59 59 assert_select "a[href=?][title=?]",
60 60 "https://mydomain.foo/projects/ecookbook/repository/revisions/2",
61 61 "This commit fixes #1, #2 and references #1 &amp; #3",
62 62 :text => "r2"
63 63 end
64 64 end
65 65
66 66 def test_generated_links_with_prefix
67 67 relative_url_root = Redmine::Utils.relative_url_root
68 68 Setting.host_name = 'mydomain.foo/rdm'
69 69 Setting.protocol = 'http'
70 70 Redmine::Utils.relative_url_root = '/rdm'
71 71
72 72 journal = Journal.find(2)
73 73 assert Mailer.deliver_issue_edit(journal)
74 74
75 75 mail = ActionMailer::Base.deliveries.last
76 76 assert_kind_of TMail::Mail, mail
77 77
78 78 assert_select_email do
79 79 # link to the main ticket
80 80 assert_select "a[href=?]",
81 81 "http://mydomain.foo/rdm/issues/1#change-2",
82 82 :text => "Bug #1: Can't print recipes"
83 83 # link to a referenced ticket
84 84 assert_select "a[href=?][title=?]",
85 85 "http://mydomain.foo/rdm/issues/2",
86 86 "Add ingredients categories (Assigned)",
87 87 :text => "#2"
88 88 # link to a changeset
89 89 assert_select "a[href=?][title=?]",
90 90 "http://mydomain.foo/rdm/projects/ecookbook/repository/revisions/2",
91 91 "This commit fixes #1, #2 and references #1 &amp; #3",
92 92 :text => "r2"
93 93 end
94 94 ensure
95 95 # restore it
96 96 Redmine::Utils.relative_url_root = relative_url_root
97 97 end
98 98
99 99 def test_generated_links_with_prefix_and_no_relative_url_root
100 100 relative_url_root = Redmine::Utils.relative_url_root
101 101 Setting.host_name = 'mydomain.foo/rdm'
102 102 Setting.protocol = 'http'
103 103 Redmine::Utils.relative_url_root = nil
104 104
105 105 journal = Journal.find(2)
106 106 assert Mailer.deliver_issue_edit(journal)
107 107
108 108 mail = ActionMailer::Base.deliveries.last
109 109 assert_kind_of TMail::Mail, mail
110 110
111 111 assert_select_email do
112 112 # link to the main ticket
113 113 assert_select "a[href=?]",
114 114 "http://mydomain.foo/rdm/issues/1#change-2",
115 115 :text => "Bug #1: Can't print recipes"
116 116 # link to a referenced ticket
117 117 assert_select "a[href=?][title=?]",
118 118 "http://mydomain.foo/rdm/issues/2",
119 119 "Add ingredients categories (Assigned)",
120 120 :text => "#2"
121 121 # link to a changeset
122 122 assert_select "a[href=?][title=?]",
123 123 "http://mydomain.foo/rdm/projects/ecookbook/repository/revisions/2",
124 124 "This commit fixes #1, #2 and references #1 &amp; #3",
125 125 :text => "r2"
126 126 end
127 127 ensure
128 128 # restore it
129 129 Redmine::Utils.relative_url_root = relative_url_root
130 130 end
131 131
132 132 def test_email_headers
133 133 issue = Issue.find(1)
134 134 Mailer.deliver_issue_add(issue)
135 135 mail = ActionMailer::Base.deliveries.last
136 136 assert_not_nil mail
137 137 assert_equal 'OOF', mail.header_string('X-Auto-Response-Suppress')
138 138 assert_equal 'auto-generated', mail.header_string('Auto-Submitted')
139 139 end
140 140
141 def test_email_headers_should_include_sender
142 issue = Issue.find(1)
143 Mailer.deliver_issue_add(issue)
144 mail = ActionMailer::Base.deliveries.last
145 assert_not_nil mail
146 assert_equal issue.author.login, mail.header_string('X-Redmine-Sender')
147 end
148
141 149 def test_plain_text_mail
142 150 Setting.plain_text_mail = 1
143 151 journal = Journal.find(2)
144 152 Mailer.deliver_issue_edit(journal)
145 153 mail = ActionMailer::Base.deliveries.last
146 154 assert_equal "text/plain", mail.content_type
147 155 assert_equal 0, mail.parts.size
148 156 assert !mail.encoded.include?('href')
149 157 end
150 158
151 159 def test_html_mail
152 160 Setting.plain_text_mail = 0
153 161 journal = Journal.find(2)
154 162 Mailer.deliver_issue_edit(journal)
155 163 mail = ActionMailer::Base.deliveries.last
156 164 assert_equal 2, mail.parts.size
157 165 assert mail.encoded.include?('href')
158 166 end
159 167
160 168 def test_from_header
161 169 with_settings :mail_from => 'redmine@example.net' do
162 170 Mailer.deliver_test(User.find(1))
163 171 end
164 172 mail = ActionMailer::Base.deliveries.last
165 173 assert_not_nil mail
166 174 assert_equal 'redmine@example.net', mail.from_addrs.first.address
167 175 end
168 176
169 177 def test_from_header_with_phrase
170 178 with_settings :mail_from => 'Redmine app <redmine@example.net>' do
171 179 Mailer.deliver_test(User.find(1))
172 180 end
173 181 mail = ActionMailer::Base.deliveries.last
174 182 assert_not_nil mail
175 183 assert_equal 'redmine@example.net', mail.from_addrs.first.address
176 184 assert_equal 'Redmine app', mail.from_addrs.first.name
177 185 end
178 186
179 187 def test_should_not_send_email_without_recipient
180 188 news = News.find(:first)
181 189 user = news.author
182 190 # Remove members except news author
183 191 news.project.memberships.each {|m| m.destroy unless m.user == user}
184 192
185 193 user.pref[:no_self_notified] = false
186 194 user.pref.save
187 195 User.current = user
188 196 Mailer.deliver_news_added(news.reload)
189 197 assert_equal 1, last_email.bcc.size
190 198
191 199 # nobody to notify
192 200 user.pref[:no_self_notified] = true
193 201 user.pref.save
194 202 User.current = user
195 203 ActionMailer::Base.deliveries.clear
196 204 Mailer.deliver_news_added(news.reload)
197 205 assert ActionMailer::Base.deliveries.empty?
198 206 end
199 207
200 208 def test_issue_add_message_id
201 209 issue = Issue.find(1)
202 210 Mailer.deliver_issue_add(issue)
203 211 mail = ActionMailer::Base.deliveries.last
204 212 assert_not_nil mail
205 213 assert_equal Mailer.message_id_for(issue), mail.message_id
206 214 assert_nil mail.references
207 215 end
208 216
209 217 def test_issue_edit_message_id
210 218 journal = Journal.find(1)
211 219 Mailer.deliver_issue_edit(journal)
212 220 mail = ActionMailer::Base.deliveries.last
213 221 assert_not_nil mail
214 222 assert_equal Mailer.message_id_for(journal), mail.message_id
215 223 assert_equal Mailer.message_id_for(journal.issue), mail.references.first.to_s
216 224 assert_select_email do
217 225 # link to the update
218 226 assert_select "a[href=?]",
219 227 "http://mydomain.foo/issues/#{journal.journalized_id}#change-#{journal.id}"
220 228 end
221 229 end
222 230
223 231 def test_message_posted_message_id
224 232 message = Message.find(1)
225 233 Mailer.deliver_message_posted(message)
226 234 mail = ActionMailer::Base.deliveries.last
227 235 assert_not_nil mail
228 236 assert_equal Mailer.message_id_for(message), mail.message_id
229 237 assert_nil mail.references
230 238 assert_select_email do
231 239 # link to the message
232 240 assert_select "a[href=?]",
233 241 "http://mydomain.foo/boards/#{message.board.id}/topics/#{message.id}",
234 242 :text => message.subject
235 243 end
236 244 end
237 245
238 246 def test_reply_posted_message_id
239 247 message = Message.find(3)
240 248 Mailer.deliver_message_posted(message)
241 249 mail = ActionMailer::Base.deliveries.last
242 250 assert_not_nil mail
243 251 assert_equal Mailer.message_id_for(message), mail.message_id
244 252 assert_equal Mailer.message_id_for(message.parent), mail.references.first.to_s
245 253 assert_select_email do
246 254 # link to the reply
247 255 assert_select "a[href=?]",
248 256 "http://mydomain.foo/boards/#{message.board.id}/topics/#{message.root.id}?r=#{message.id}#message-#{message.id}",
249 257 :text => message.subject
250 258 end
251 259 end
252 260
253 261 context("#issue_add") do
254 262 setup do
255 263 ActionMailer::Base.deliveries.clear
256 264 Setting.bcc_recipients = '1'
257 265 @issue = Issue.find(1)
258 266 end
259 267
260 268 should "notify project members" do
261 269 assert Mailer.deliver_issue_add(@issue)
262 270 assert last_email.bcc.include?('dlopper@somenet.foo')
263 271 end
264 272
265 273 should "not notify project members that are not allow to view the issue" do
266 274 Role.find(2).remove_permission!(:view_issues)
267 275 assert Mailer.deliver_issue_add(@issue)
268 276 assert !last_email.bcc.include?('dlopper@somenet.foo')
269 277 end
270 278
271 279 should "notify issue watchers" do
272 280 user = User.find(9)
273 281 # minimal email notification options
274 282 user.pref[:no_self_notified] = '1'
275 283 user.pref.save
276 284 user.mail_notification = false
277 285 user.save
278 286
279 287 Watcher.create!(:watchable => @issue, :user => user)
280 288 assert Mailer.deliver_issue_add(@issue)
281 289 assert last_email.bcc.include?(user.mail)
282 290 end
283 291
284 292 should "not notify watchers not allowed to view the issue" do
285 293 user = User.find(9)
286 294 Watcher.create!(:watchable => @issue, :user => user)
287 295 Role.non_member.remove_permission!(:view_issues)
288 296 assert Mailer.deliver_issue_add(@issue)
289 297 assert !last_email.bcc.include?(user.mail)
290 298 end
291 299 end
292 300
293 301 # test mailer methods for each language
294 302 def test_issue_add
295 303 issue = Issue.find(1)
296 304 valid_languages.each do |lang|
297 305 Setting.default_language = lang.to_s
298 306 assert Mailer.deliver_issue_add(issue)
299 307 end
300 308 end
301 309
302 310 def test_issue_edit
303 311 journal = Journal.find(1)
304 312 valid_languages.each do |lang|
305 313 Setting.default_language = lang.to_s
306 314 assert Mailer.deliver_issue_edit(journal)
307 315 end
308 316 end
309 317
310 318 def test_document_added
311 319 document = Document.find(1)
312 320 valid_languages.each do |lang|
313 321 Setting.default_language = lang.to_s
314 322 assert Mailer.deliver_document_added(document)
315 323 end
316 324 end
317 325
318 326 def test_attachments_added
319 327 attachements = [ Attachment.find_by_container_type('Document') ]
320 328 valid_languages.each do |lang|
321 329 Setting.default_language = lang.to_s
322 330 assert Mailer.deliver_attachments_added(attachements)
323 331 end
324 332 end
325 333
326 334 def test_version_file_added
327 335 attachements = [ Attachment.find_by_container_type('Version') ]
328 336 assert Mailer.deliver_attachments_added(attachements)
329 337 assert_not_nil last_email.bcc
330 338 assert last_email.bcc.any?
331 339 assert_select_email do
332 340 assert_select "a[href=?]", "http://mydomain.foo/projects/ecookbook/files"
333 341 end
334 342 end
335 343
336 344 def test_project_file_added
337 345 attachements = [ Attachment.find_by_container_type('Project') ]
338 346 assert Mailer.deliver_attachments_added(attachements)
339 347 assert_not_nil last_email.bcc
340 348 assert last_email.bcc.any?
341 349 assert_select_email do
342 350 assert_select "a[href=?]", "http://mydomain.foo/projects/ecookbook/files"
343 351 end
344 352 end
345 353
346 354 def test_news_added
347 355 news = News.find(:first)
348 356 valid_languages.each do |lang|
349 357 Setting.default_language = lang.to_s
350 358 assert Mailer.deliver_news_added(news)
351 359 end
352 360 end
353 361
354 362 def test_news_comment_added
355 363 comment = Comment.find(2)
356 364 valid_languages.each do |lang|
357 365 Setting.default_language = lang.to_s
358 366 assert Mailer.deliver_news_comment_added(comment)
359 367 end
360 368 end
361 369
362 370 def test_message_posted
363 371 message = Message.find(:first)
364 372 recipients = ([message.root] + message.root.children).collect {|m| m.author.mail if m.author}
365 373 recipients = recipients.compact.uniq
366 374 valid_languages.each do |lang|
367 375 Setting.default_language = lang.to_s
368 376 assert Mailer.deliver_message_posted(message)
369 377 end
370 378 end
371 379
372 380 def test_wiki_content_added
373 381 content = WikiContent.find(:first)
374 382 valid_languages.each do |lang|
375 383 Setting.default_language = lang.to_s
376 384 assert_difference 'ActionMailer::Base.deliveries.size' do
377 385 assert Mailer.deliver_wiki_content_added(content)
378 386 end
379 387 end
380 388 end
381 389
382 390 def test_wiki_content_updated
383 391 content = WikiContent.find(:first)
384 392 valid_languages.each do |lang|
385 393 Setting.default_language = lang.to_s
386 394 assert_difference 'ActionMailer::Base.deliveries.size' do
387 395 assert Mailer.deliver_wiki_content_updated(content)
388 396 end
389 397 end
390 398 end
391 399
392 400 def test_account_information
393 401 user = User.find(2)
394 402 valid_languages.each do |lang|
395 403 user.update_attribute :language, lang.to_s
396 404 user.reload
397 405 assert Mailer.deliver_account_information(user, 'pAsswORd')
398 406 end
399 407 end
400 408
401 409 def test_lost_password
402 410 token = Token.find(2)
403 411 valid_languages.each do |lang|
404 412 token.user.update_attribute :language, lang.to_s
405 413 token.reload
406 414 assert Mailer.deliver_lost_password(token)
407 415 end
408 416 end
409 417
410 418 def test_register
411 419 token = Token.find(1)
412 420 Setting.host_name = 'redmine.foo'
413 421 Setting.protocol = 'https'
414 422
415 423 valid_languages.each do |lang|
416 424 token.user.update_attribute :language, lang.to_s
417 425 token.reload
418 426 ActionMailer::Base.deliveries.clear
419 427 assert Mailer.deliver_register(token)
420 428 mail = ActionMailer::Base.deliveries.last
421 429 assert mail.body.include?("https://redmine.foo/account/activate?token=#{token.value}")
422 430 end
423 431 end
424 432
425 433 def test_test
426 434 user = User.find(1)
427 435 valid_languages.each do |lang|
428 436 user.update_attribute :language, lang.to_s
429 437 assert Mailer.deliver_test(user)
430 438 end
431 439 end
432 440
433 441 def test_reminders
434 442 Mailer.reminders(:days => 42)
435 443 assert_equal 1, ActionMailer::Base.deliveries.size
436 444 mail = ActionMailer::Base.deliveries.last
437 445 assert mail.bcc.include?('dlopper@somenet.foo')
438 446 assert mail.body.include?('Bug #3: Error 281 when updating a recipe')
439 447 assert_equal '1 issue(s) due in the next 42 days', mail.subject
440 448 end
441 449
442 450 def test_reminders_should_not_include_closed_issues
443 451 Issue.generate!(:project_id => 1, :tracker_id => 1, :status_id => 5, :subject => 'Closed issue', :assigned_to_id => 3, :due_date => 5.days.from_now)
444 452 ActionMailer::Base.deliveries.clear
445 453
446 454 Mailer.reminders(:days => 42)
447 455 assert_equal 1, ActionMailer::Base.deliveries.size
448 456 mail = ActionMailer::Base.deliveries.last
449 457 assert mail.bcc.include?('dlopper@somenet.foo')
450 458 assert !mail.body.include?('Closed issue')
451 459 end
452 460
453 461 def test_reminders_for_users
454 462 Mailer.reminders(:days => 42, :users => ['5'])
455 463 assert_equal 0, ActionMailer::Base.deliveries.size # No mail for dlopper
456 464 Mailer.reminders(:days => 42, :users => ['3'])
457 465 assert_equal 1, ActionMailer::Base.deliveries.size # No mail for dlopper
458 466 mail = ActionMailer::Base.deliveries.last
459 467 assert mail.bcc.include?('dlopper@somenet.foo')
460 468 assert mail.body.include?('Bug #3: Error 281 when updating a recipe')
461 469 end
462 470
463 471 def last_email
464 472 mail = ActionMailer::Base.deliveries.last
465 473 assert_not_nil mail
466 474 mail
467 475 end
468 476
469 477 def test_mailer_should_not_change_locale
470 478 Setting.default_language = 'en'
471 479 # Set current language to italian
472 480 set_language_if_valid 'it'
473 481 # Send an email to a french user
474 482 user = User.find(1)
475 483 user.language = 'fr'
476 484 Mailer.deliver_account_activated(user)
477 485 mail = ActionMailer::Base.deliveries.last
478 486 assert mail.body.include?('Votre compte')
479 487
480 488 assert_equal :it, current_language
481 489 end
482 490
483 491 def test_with_deliveries_off
484 492 Mailer.with_deliveries false do
485 493 Mailer.deliver_test(User.find(1))
486 494 end
487 495 assert ActionMailer::Base.deliveries.empty?
488 496 # should restore perform_deliveries
489 497 assert ActionMailer::Base.perform_deliveries
490 498 end
491 499
492 500 def test_tmail_to_header_field_should_not_include_blank_lines
493 501 mail = TMail::Mail.new
494 502 mail.to = ["a.user@example.com", "v.user2@example.com", "e.smith@example.com", "info@example.com", "v.pupkin@example.com",
495 503 "b.user@example.com", "w.user2@example.com", "f.smith@example.com", "info2@example.com", "w.pupkin@example.com"]
496 504
497 505 assert !mail.encoded.strip.split("\r\n").detect(&:blank?), "#{mail.encoded} malformed"
498 506 end
499 507
500 508 def test_layout_should_include_the_emails_header
501 509 with_settings :emails_header => "*Header content*" do
502 510 assert Mailer.deliver_test(User.find(1))
503 511 assert_select_email do
504 512 assert_select ".header" do
505 513 assert_select "strong", :text => "Header content"
506 514 end
507 515 end
508 516 end
509 517 end
510 518 end
General Comments 0
You need to be logged in to leave comments. Login now