##// END OF EJS Templates
Fixed: TMail 1.2.7 malforms To header when number of recipients more then 8 (#8751)....
Jean-Philippe Lang -
r7743:e39d7a0ca8d9
parent child
Show More
@@ -1,107 +1,114
1 1
2 2 require 'active_record'
3 3
4 4 module ActiveRecord
5 5 class Base
6 6 include Redmine::I18n
7 7
8 8 # Translate attribute names for validation errors display
9 9 def self.human_attribute_name(attr)
10 10 l("field_#{attr.to_s.gsub(/_id$/, '')}", :default => attr)
11 11 end
12 12 end
13 13 end
14 14
15 15 module ActiveRecord
16 16 class Errors
17 17 def full_messages(options = {})
18 18 full_messages = []
19 19
20 20 @errors.each_key do |attr|
21 21 @errors[attr].each do |message|
22 22 next unless message
23 23
24 24 if attr == "base"
25 25 full_messages << message
26 26 elsif attr == "custom_values"
27 27 # Replace the generic "custom values is invalid"
28 28 # with the errors on custom values
29 29 @base.custom_values.each do |value|
30 30 value.errors.each do |attr, msg|
31 31 full_messages << value.custom_field.name + ' ' + msg
32 32 end
33 33 end
34 34 else
35 35 attr_name = @base.class.human_attribute_name(attr)
36 36 full_messages << attr_name + ' ' + message.to_s
37 37 end
38 38 end
39 39 end
40 40 full_messages
41 41 end
42 42 end
43 43 end
44 44
45 45 module ActionView
46 46 module Helpers
47 47 module DateHelper
48 48 # distance_of_time_in_words breaks when difference is greater than 30 years
49 49 def distance_of_date_in_words(from_date, to_date = 0, options = {})
50 50 from_date = from_date.to_date if from_date.respond_to?(:to_date)
51 51 to_date = to_date.to_date if to_date.respond_to?(:to_date)
52 52 distance_in_days = (to_date - from_date).abs
53 53
54 54 I18n.with_options :locale => options[:locale], :scope => :'datetime.distance_in_words' do |locale|
55 55 case distance_in_days
56 56 when 0..60 then locale.t :x_days, :count => distance_in_days.round
57 57 when 61..720 then locale.t :about_x_months, :count => (distance_in_days / 30).round
58 58 else locale.t :over_x_years, :count => (distance_in_days / 365).floor
59 59 end
60 60 end
61 61 end
62 62 end
63 63 end
64 64 end
65 65
66 66 ActionView::Base.field_error_proc = Proc.new{ |html_tag, instance| "#{html_tag}" }
67 67
68 68 module AsynchronousMailer
69 69 # Adds :async_smtp and :async_sendmail delivery methods
70 70 # to perform email deliveries asynchronously
71 71 %w(smtp sendmail).each do |type|
72 72 define_method("perform_delivery_async_#{type}") do |mail|
73 73 Thread.start do
74 74 send "perform_delivery_#{type}", mail
75 75 end
76 76 end
77 77 end
78 78
79 79 # Adds a delivery method that writes emails in tmp/emails for testing purpose
80 80 def perform_delivery_tmp_file(mail)
81 81 dest_dir = File.join(Rails.root, 'tmp', 'emails')
82 82 Dir.mkdir(dest_dir) unless File.directory?(dest_dir)
83 83 File.open(File.join(dest_dir, mail.message_id.gsub(/[<>]/, '') + '.eml'), 'wb') {|f| f.write(mail.encoded) }
84 84 end
85 85 end
86 86
87 87 ActionMailer::Base.send :include, AsynchronousMailer
88 88
89 # TMail::Unquoter.convert_to_with_fallback_on_iso_8859_1 introduced in TMail 1.2.7
90 # triggers a test failure in test_add_issue_with_japanese_keywords(MailHandlerTest)
91 89 module TMail
90 # TMail::Unquoter.convert_to_with_fallback_on_iso_8859_1 introduced in TMail 1.2.7
91 # triggers a test failure in test_add_issue_with_japanese_keywords(MailHandlerTest)
92 92 class Unquoter
93 93 class << self
94 94 alias_method :convert_to, :convert_to_without_fallback_on_iso_8859_1
95 95 end
96 96 end
97
98 # Patch for TMail 1.2.7. See http://www.redmine.org/issues/8751
99 class Encoder
100 def puts_meta(str)
101 add_text str
102 end
103 end
97 104 end
98 105
99 106 module ActionController
100 107 module MimeResponds
101 108 class Responder
102 109 def api(&block)
103 110 any(:xml, :json, &block)
104 111 end
105 112 end
106 113 end
107 114 end
@@ -1,489 +1,497
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",
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",
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",
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 141 def test_plain_text_mail
142 142 Setting.plain_text_mail = 1
143 143 journal = Journal.find(2)
144 144 Mailer.deliver_issue_edit(journal)
145 145 mail = ActionMailer::Base.deliveries.last
146 146 assert_equal "text/plain", mail.content_type
147 147 assert_equal 0, mail.parts.size
148 148 assert !mail.encoded.include?('href')
149 149 end
150 150
151 151 def test_html_mail
152 152 Setting.plain_text_mail = 0
153 153 journal = Journal.find(2)
154 154 Mailer.deliver_issue_edit(journal)
155 155 mail = ActionMailer::Base.deliveries.last
156 156 assert_equal 2, mail.parts.size
157 157 assert mail.encoded.include?('href')
158 158 end
159 159
160 160 def test_from_header
161 161 with_settings :mail_from => 'redmine@example.net' do
162 162 Mailer.deliver_test(User.find(1))
163 163 end
164 164 mail = ActionMailer::Base.deliveries.last
165 165 assert_not_nil mail
166 166 assert_equal 'redmine@example.net', mail.from_addrs.first.address
167 167 end
168 168
169 169 def test_from_header_with_phrase
170 170 with_settings :mail_from => 'Redmine app <redmine@example.net>' do
171 171 Mailer.deliver_test(User.find(1))
172 172 end
173 173 mail = ActionMailer::Base.deliveries.last
174 174 assert_not_nil mail
175 175 assert_equal 'redmine@example.net', mail.from_addrs.first.address
176 176 assert_equal 'Redmine app', mail.from_addrs.first.name
177 177 end
178 178
179 179 def test_should_not_send_email_without_recipient
180 180 news = News.find(:first)
181 181 user = news.author
182 182 # Remove members except news author
183 183 news.project.memberships.each {|m| m.destroy unless m.user == user}
184 184
185 185 user.pref[:no_self_notified] = false
186 186 user.pref.save
187 187 User.current = user
188 188 Mailer.deliver_news_added(news.reload)
189 189 assert_equal 1, last_email.bcc.size
190 190
191 191 # nobody to notify
192 192 user.pref[:no_self_notified] = true
193 193 user.pref.save
194 194 User.current = user
195 195 ActionMailer::Base.deliveries.clear
196 196 Mailer.deliver_news_added(news.reload)
197 197 assert ActionMailer::Base.deliveries.empty?
198 198 end
199 199
200 200 def test_issue_add_message_id
201 201 issue = Issue.find(1)
202 202 Mailer.deliver_issue_add(issue)
203 203 mail = ActionMailer::Base.deliveries.last
204 204 assert_not_nil mail
205 205 assert_equal Mailer.message_id_for(issue), mail.message_id
206 206 assert_nil mail.references
207 207 end
208 208
209 209 def test_issue_edit_message_id
210 210 journal = Journal.find(1)
211 211 Mailer.deliver_issue_edit(journal)
212 212 mail = ActionMailer::Base.deliveries.last
213 213 assert_not_nil mail
214 214 assert_equal Mailer.message_id_for(journal), mail.message_id
215 215 assert_equal Mailer.message_id_for(journal.issue), mail.references.first.to_s
216 216 end
217 217
218 218 def test_message_posted_message_id
219 219 message = Message.find(1)
220 220 Mailer.deliver_message_posted(message)
221 221 mail = ActionMailer::Base.deliveries.last
222 222 assert_not_nil mail
223 223 assert_equal Mailer.message_id_for(message), mail.message_id
224 224 assert_nil mail.references
225 225 assert_select_email do
226 226 # link to the message
227 227 assert_select "a[href=?]",
228 228 "http://mydomain.foo/boards/#{message.board.id}/topics/#{message.id}",
229 229 :text => message.subject
230 230 end
231 231 end
232 232
233 233 def test_reply_posted_message_id
234 234 message = Message.find(3)
235 235 Mailer.deliver_message_posted(message)
236 236 mail = ActionMailer::Base.deliveries.last
237 237 assert_not_nil mail
238 238 assert_equal Mailer.message_id_for(message), mail.message_id
239 239 assert_equal Mailer.message_id_for(message.parent), mail.references.first.to_s
240 240 assert_select_email do
241 241 # link to the reply
242 242 assert_select "a[href=?]",
243 243 "http://mydomain.foo/boards/#{message.board.id}/topics/#{message.root.id}?r=#{message.id}#message-#{message.id}",
244 244 :text => message.subject
245 245 end
246 246 end
247 247
248 248 context("#issue_add") do
249 249 setup do
250 250 ActionMailer::Base.deliveries.clear
251 251 Setting.bcc_recipients = '1'
252 252 @issue = Issue.find(1)
253 253 end
254 254
255 255 should "notify project members" do
256 256 assert Mailer.deliver_issue_add(@issue)
257 257 assert last_email.bcc.include?('dlopper@somenet.foo')
258 258 end
259 259
260 260 should "not notify project members that are not allow to view the issue" do
261 261 Role.find(2).remove_permission!(:view_issues)
262 262 assert Mailer.deliver_issue_add(@issue)
263 263 assert !last_email.bcc.include?('dlopper@somenet.foo')
264 264 end
265 265
266 266 should "notify issue watchers" do
267 267 user = User.find(9)
268 268 # minimal email notification options
269 269 user.pref[:no_self_notified] = '1'
270 270 user.pref.save
271 271 user.mail_notification = false
272 272 user.save
273 273
274 274 Watcher.create!(:watchable => @issue, :user => user)
275 275 assert Mailer.deliver_issue_add(@issue)
276 276 assert last_email.bcc.include?(user.mail)
277 277 end
278 278
279 279 should "not notify watchers not allowed to view the issue" do
280 280 user = User.find(9)
281 281 Watcher.create!(:watchable => @issue, :user => user)
282 282 Role.non_member.remove_permission!(:view_issues)
283 283 assert Mailer.deliver_issue_add(@issue)
284 284 assert !last_email.bcc.include?(user.mail)
285 285 end
286 286 end
287 287
288 288 # test mailer methods for each language
289 289 def test_issue_add
290 290 issue = Issue.find(1)
291 291 valid_languages.each do |lang|
292 292 Setting.default_language = lang.to_s
293 293 assert Mailer.deliver_issue_add(issue)
294 294 end
295 295 end
296 296
297 297 def test_issue_edit
298 298 journal = Journal.find(1)
299 299 valid_languages.each do |lang|
300 300 Setting.default_language = lang.to_s
301 301 assert Mailer.deliver_issue_edit(journal)
302 302 end
303 303 end
304 304
305 305 def test_document_added
306 306 document = Document.find(1)
307 307 valid_languages.each do |lang|
308 308 Setting.default_language = lang.to_s
309 309 assert Mailer.deliver_document_added(document)
310 310 end
311 311 end
312 312
313 313 def test_attachments_added
314 314 attachements = [ Attachment.find_by_container_type('Document') ]
315 315 valid_languages.each do |lang|
316 316 Setting.default_language = lang.to_s
317 317 assert Mailer.deliver_attachments_added(attachements)
318 318 end
319 319 end
320 320
321 321 def test_version_file_added
322 322 attachements = [ Attachment.find_by_container_type('Version') ]
323 323 assert Mailer.deliver_attachments_added(attachements)
324 324 assert_not_nil last_email.bcc
325 325 assert last_email.bcc.any?
326 326 assert_select_email do
327 327 assert_select "a[href=?]", "http://mydomain.foo/projects/ecookbook/files"
328 328 end
329 329 end
330 330
331 331 def test_project_file_added
332 332 attachements = [ Attachment.find_by_container_type('Project') ]
333 333 assert Mailer.deliver_attachments_added(attachements)
334 334 assert_not_nil last_email.bcc
335 335 assert last_email.bcc.any?
336 336 assert_select_email do
337 337 assert_select "a[href=?]", "http://mydomain.foo/projects/ecookbook/files"
338 338 end
339 339 end
340 340
341 341 def test_news_added
342 342 news = News.find(:first)
343 343 valid_languages.each do |lang|
344 344 Setting.default_language = lang.to_s
345 345 assert Mailer.deliver_news_added(news)
346 346 end
347 347 end
348 348
349 349 def test_news_comment_added
350 350 comment = Comment.find(2)
351 351 valid_languages.each do |lang|
352 352 Setting.default_language = lang.to_s
353 353 assert Mailer.deliver_news_comment_added(comment)
354 354 end
355 355 end
356 356
357 357 def test_message_posted
358 358 message = Message.find(:first)
359 359 recipients = ([message.root] + message.root.children).collect {|m| m.author.mail if m.author}
360 360 recipients = recipients.compact.uniq
361 361 valid_languages.each do |lang|
362 362 Setting.default_language = lang.to_s
363 363 assert Mailer.deliver_message_posted(message)
364 364 end
365 365 end
366 366
367 367 def test_wiki_content_added
368 368 content = WikiContent.find(:first)
369 369 valid_languages.each do |lang|
370 370 Setting.default_language = lang.to_s
371 371 assert_difference 'ActionMailer::Base.deliveries.size' do
372 372 assert Mailer.deliver_wiki_content_added(content)
373 373 end
374 374 end
375 375 end
376 376
377 377 def test_wiki_content_updated
378 378 content = WikiContent.find(:first)
379 379 valid_languages.each do |lang|
380 380 Setting.default_language = lang.to_s
381 381 assert_difference 'ActionMailer::Base.deliveries.size' do
382 382 assert Mailer.deliver_wiki_content_updated(content)
383 383 end
384 384 end
385 385 end
386 386
387 387 def test_account_information
388 388 user = User.find(2)
389 389 valid_languages.each do |lang|
390 390 user.update_attribute :language, lang.to_s
391 391 user.reload
392 392 assert Mailer.deliver_account_information(user, 'pAsswORd')
393 393 end
394 394 end
395 395
396 396 def test_lost_password
397 397 token = Token.find(2)
398 398 valid_languages.each do |lang|
399 399 token.user.update_attribute :language, lang.to_s
400 400 token.reload
401 401 assert Mailer.deliver_lost_password(token)
402 402 end
403 403 end
404 404
405 405 def test_register
406 406 token = Token.find(1)
407 407 Setting.host_name = 'redmine.foo'
408 408 Setting.protocol = 'https'
409 409
410 410 valid_languages.each do |lang|
411 411 token.user.update_attribute :language, lang.to_s
412 412 token.reload
413 413 ActionMailer::Base.deliveries.clear
414 414 assert Mailer.deliver_register(token)
415 415 mail = ActionMailer::Base.deliveries.last
416 416 assert mail.body.include?("https://redmine.foo/account/activate?token=#{token.value}")
417 417 end
418 418 end
419 419
420 420 def test_test
421 421 user = User.find(1)
422 422 valid_languages.each do |lang|
423 423 user.update_attribute :language, lang.to_s
424 424 assert Mailer.deliver_test(user)
425 425 end
426 426 end
427 427
428 428 def test_reminders
429 429 Mailer.reminders(:days => 42)
430 430 assert_equal 1, ActionMailer::Base.deliveries.size
431 431 mail = ActionMailer::Base.deliveries.last
432 432 assert mail.bcc.include?('dlopper@somenet.foo')
433 433 assert mail.body.include?('Bug #3: Error 281 when updating a recipe')
434 434 assert_equal '1 issue(s) due in the next 42 days', mail.subject
435 435 end
436 436
437 437 def test_reminders_for_users
438 438 Mailer.reminders(:days => 42, :users => ['5'])
439 439 assert_equal 0, ActionMailer::Base.deliveries.size # No mail for dlopper
440 440 Mailer.reminders(:days => 42, :users => ['3'])
441 441 assert_equal 1, ActionMailer::Base.deliveries.size # No mail for dlopper
442 442 mail = ActionMailer::Base.deliveries.last
443 443 assert mail.bcc.include?('dlopper@somenet.foo')
444 444 assert mail.body.include?('Bug #3: Error 281 when updating a recipe')
445 445 end
446 446
447 447 def last_email
448 448 mail = ActionMailer::Base.deliveries.last
449 449 assert_not_nil mail
450 450 mail
451 451 end
452 452
453 453 def test_mailer_should_not_change_locale
454 454 Setting.default_language = 'en'
455 455 # Set current language to italian
456 456 set_language_if_valid 'it'
457 457 # Send an email to a french user
458 458 user = User.find(1)
459 459 user.language = 'fr'
460 460 Mailer.deliver_account_activated(user)
461 461 mail = ActionMailer::Base.deliveries.last
462 462 assert mail.body.include?('Votre compte')
463 463
464 464 assert_equal :it, current_language
465 465 end
466 466
467 467 def test_with_deliveries_off
468 468 Mailer.with_deliveries false do
469 469 Mailer.deliver_test(User.find(1))
470 470 end
471 471 assert ActionMailer::Base.deliveries.empty?
472 472 # should restore perform_deliveries
473 473 assert ActionMailer::Base.perform_deliveries
474 474 end
475
476 def test_tmail_to_header_field_should_not_include_blank_lines
477 mail = TMail::Mail.new
478 mail.to = ["a.user@example.com", "v.user2@example.com", "e.smith@example.com", "info@example.com", "v.pupkin@example.com",
479 "b.user@example.com", "w.user2@example.com", "f.smith@example.com", "info2@example.com", "w.pupkin@example.com"]
480
481 assert !mail.encoded.strip.split("\r\n").detect(&:blank?), "#{mail.encoded} malformed"
482 end
475 483
476 484 context "layout" do
477 485 should "include the emails_header" do
478 486 with_settings(:emails_header => "*Header content*") do
479 487 assert Mailer.deliver_test(User.find(1))
480 488
481 489 assert_select_email do
482 490 assert_select ".header" do
483 491 assert_select "strong", :text => "Header content"
484 492 end
485 493 end
486 494 end
487 495 end
488 496 end
489 497 end
General Comments 0
You need to be logged in to leave comments. Login now