Project

General

Profile

Feature #26791 » 0001-Send-individual-emails-for-each-mail-recipient.patch

Holger Just, 2017-08-30 12:46

View differences:

app/models/email_address.rb
52 52
    # in that case, the user is just being created and
53 53
    # should not receive this email.
54 54
    if user.mails != [address]
55
      deliver_security_notification(user,
55
      deliver_security_notification(
56 56
        message: :mail_body_security_notification_add,
57 57
        field: :field_mail,
58 58
        value: address
......
63 63
  # send a security notification to user that an email has been changed (notified/not notified)
64 64
  def deliver_security_notification_update
65 65
    if saved_change_to_address?
66
      recipients = [user, address_before_last_save]
67 66
      options = {
67
        recipients: [address_before_last_save],
68 68
        message: :mail_body_security_notification_change_to,
69 69
        field: :field_mail,
70 70
        value: address
71 71
      }
72 72
    elsif saved_change_to_notify?
73
      recipients = [user, address]
74 73
      options = {
74
        recipients: [address],
75 75
        message: notify_before_last_save ? :mail_body_security_notification_notify_disabled : :mail_body_security_notification_notify_enabled,
76 76
        value: address
77 77
      }
78 78
    end
79
    deliver_security_notification(recipients, options)
79
    deliver_security_notification(options)
80 80
  end
81 81

  
82 82
  # send a security notification to user that an email address was deleted
83 83
  def deliver_security_notification_destroy
84
    deliver_security_notification([user, address],
84
    deliver_security_notification(
85
      recipients: [address],
85 86
      message: :mail_body_security_notification_remove,
86 87
      field: :field_mail,
87 88
      value: address
......
89 90
  end
90 91

  
91 92
  # generic method to send security notifications for email addresses
92
  def deliver_security_notification(recipients, options={})
93
    Mailer.security_notification(recipients,
93
  def deliver_security_notification(options={})
94
    Mailer.security_notification(user,
94 95
      options.merge(
95 96
        title: :label_my_account,
96 97
        url: {controller: 'my', action: 'account'}
app/models/mailer.rb
26 26
  include Redmine::I18n
27 27
  include Roadie::Rails::Automatic
28 28

  
29
  # This class wraps multiple generated `Mail::Message` objects and allows to
30
  # deliver them all at once. It is usually used to handle multiple mails for
31
  # different receivers created by a single mail event. The wrapped mails can
32
  # then be delivered in one go.
33
  #
34
  # The public interface of the class resembles a single mail message. You can
35
  # directly use any of the deliver_* methods to send the contained messages
36
  # now or later.
37
  class MultiMessage
38
    attr_reader :mails
39

  
40
    # @param mails [Array<Mail, Proc>] an Array of mails or Procs which create
41
    #   mail objects and allow to call a method on it.
42
    def initialize(action, *args)
43
      @action = action
44
      @args = args
45

  
46
      @mails = []
47
    end
48

  
49
    def for(users)
50
      Array.wrap(users).each do |user|
51
        @mails << ActionMailer::MessageDelivery.new(Mailer, @action, user, *@args)
52
      end
53
      self
54
    end
55

  
56
    def deliver_later(options = {})
57
      enqueue_delivery :deliver_now, options
58
    end
59

  
60
    def deliver_later!(options = {})
61
      enqueue_delivery :deliver_now!, options
62
    end
63

  
64
    def processed?
65
      @mails.any?(&:processed?)
66
    end
67

  
68
    # @return [Object] the delivery method of the first mail.
69
    #   Usually, this is the very same value for all mails and matches the
70
    #   default value of the Mailer class
71
    def delivery_method
72
      (@mails.first || ActionMailer::Base::NullMail.new).delivery_method
73
    end
74

  
75
    # @return [ActionMailer::Base] the delivery handler of the first mail. This
76
    #   is always the `Mailer` class.
77
    def delivery_handler
78
      (@mails.first || ActionMailer::Base::NullMail.new).delivery_handler
79
    end
80

  
81
    private
82

  
83
    def method_missing(method, *args, &block)
84
      if method =~ /\Adeliver([_!?]|\z)/
85
        @mails.each do |mail|
86
          mail.public_send(method, *args, &block)
87
        end
88
      else
89
        super
90
      end
91
    end
92

  
93
    def respond_to_missing(method, *args)
94
      method =~ /\Adeliver([_!?]|\z)/ || method == 'processed?' || super
95
    end
96

  
97
    # This method is slightly adapted from ActionMailer::MessageDelivery
98
    def enqueue_delivery(delivery_method, options = {})
99
      if processed?
100
        ::Kernel.raise "You've accessed the message before asking to " \
101
          "deliver it later, so you may have made local changes that would " \
102
          "be silently lost if we enqueued a job to deliver it. Why? Only " \
103
          "the mailer method *arguments* are passed with the delivery job! " \
104
          "Do not access the message in any way if you mean to deliver it " \
105
          "later. Workarounds: 1. don't touch the message before calling " \
106
          "#deliver_later, 2. only touch the message *within your mailer " \
107
          "method*, or 3. use a custom Active Job instead of #deliver_later."
108
      else
109
        args = 'Mailer', @action.to_s, delivery_method.to_s, *@args
110
        ::ActionMailer::DeliveryJob.set(options).perform_later(*args)
111
      end
112
    end
113
  end
114

  
115
  def process(action, *args)
116
    user = args.shift
117
    raise ArgumentError, "First argument has to be a user, was #{user.inspect}" unless user.is_a?(User)
118

  
119
    initial_user = User.current
120
    initial_language = ::I18n.locale
121
    begin
122
      User.current = user
123

  
124
      lang = find_language(user.language) if user.logged?
125
      lang ||= Setting.default_language
126
      set_language_if_valid(lang)
127

  
128
      super(action, *args)
129
    ensure
130
      User.current = initial_user
131
      ::I18n.locale = initial_language
132
    end
133
  end
134

  
29 135
  def self.default_url_options
30 136
    options = {:protocol => Setting.protocol}
31 137
    if Setting.host_name.to_s =~ /\A(https?\:\/\/)?(.+?)(\:(\d+))?(\/.+)?\z/i
......
39 144
    options
40 145
  end
41 146

  
42
  # Builds a mail for notifying to_users and cc_users about a new issue
43
  def issue_add(issue, to_users, cc_users)
147
  # Builds a mail for notifying the current user about a new issue
148
  #
149
  # Example:
150
  #   issue_add(issue) => Mail::Message object
151
  def issue_add(issue)
44 152
    redmine_headers 'Project' => issue.project.identifier,
45 153
                    'Issue-Id' => issue.id,
46 154
                    'Issue-Author' => issue.author.login
......
49 157
    references issue
50 158
    @author = issue.author
51 159
    @issue = issue
52
    @users = to_users + cc_users
160
    @users = [User.current]
53 161
    @issue_url = url_for(:controller => 'issues', :action => 'show', :id => issue)
54
    mail :to => to_users,
55
      :cc => cc_users,
162
    mail :to => User.current,
56 163
      :subject => "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] (#{issue.status.name}) #{issue.subject}"
57 164
  end
58 165

  
59 166
  # Notifies users about a new issue
167
  #
168
  # Example:
169
  #   Mailer.issue_add(journal).deliver => sends emails to the project's recipients
170
  def self.issue_add(issue)
171
    users = issue.notified_users | issue.notified_watchers
172
    MultiMessage.new(:issue_add, issue).for(users)
173
  end
174

  
175
  # Notifies users about a new issue
176
  #
177
  # Example:
178
  #   Mailer.deliver_issue_add(issue) => sends emails to the project's recipients
60 179
  def self.deliver_issue_add(issue)
61
    to = issue.notified_users
62
    cc = issue.notified_watchers - to
63
    issue.each_notification(to + cc) do |users|
64
      issue_add(issue, to & users, cc & users).deliver
65
    end
180
    issue_add(issue).deliver
66 181
  end
67 182

  
68
  # Builds a mail for notifying to_users and cc_users about an issue update
69
  def issue_edit(journal, to_users, cc_users)
183
  # Builds a mail for notifying the current user about an issue update
184
  #
185
  # Example:
186
  #   issue_edit(journal) => Mail::Message object
187
  def issue_edit(journal)
70 188
    issue = journal.journalized
71 189
    redmine_headers 'Project' => issue.project.identifier,
72 190
                    'Issue-Id' => issue.id,
......
79 197
    s << "(#{issue.status.name}) " if journal.new_value_for('status_id')
80 198
    s << issue.subject
81 199
    @issue = issue
82
    @users = to_users + cc_users
200
    @users = [User.current]
83 201
    @journal = journal
84
    @journal_details = journal.visible_details(@users.first)
202
    @journal_details = journal.visible_details
85 203
    @issue_url = url_for(:controller => 'issues', :action => 'show', :id => issue, :anchor => "change-#{journal.id}")
86
    mail :to => to_users,
87
      :cc => cc_users,
204

  
205
    mail :to => User.current,
88 206
      :subject => s
89 207
  end
90 208

  
209
  # Build a MultiMessage to notify users about an issue update
210
  #
211
  # Example:
212
  #   Mailer.issue_edit(journal).deliver => sends emails to the project's recipients
213
  def self.issue_edit(journal)
214
    users  = journal.notified_users
215
    users |= journal.notified_watchers
216
    users.select! do |user|
217
      journal.notes? || journal.visible_details(user).any?
218
    end
219
    MultiMessage.new(:issue_edit, journal).for(users)
220
  end
221

  
91 222
  # Notifies users about an issue update
223
  #
224
  # Example:
225
  #   Mailer.deliver_issue_edit(journal) => sends emails to the project's recipients
92 226
  def self.deliver_issue_edit(journal)
93
    issue = journal.journalized.reload
94
    to = journal.notified_users
95
    cc = journal.notified_watchers - to
96
    journal.each_notification(to + cc) do |users|
97
      issue.each_notification(users) do |users2|
98
        issue_edit(journal, to & users2, cc & users2).deliver
99
      end
100
    end
227
    issue_edit(journal).deliver
101 228
  end
102 229

  
103
  def reminder(user, issues, days)
104
    set_language_if_valid user.language
230
  # Builds a Mail::Message object used to send en email reminder to the current
231
  # user about their due issues.
232
  #
233
  # Example:
234
  #   reminder(issues, days) => Mail::Message object
235
  def reminder(issues, days)
105 236
    @issues = issues
106 237
    @days = days
107 238
    @issues_url = url_for(:controller => 'issues', :action => 'index',
108
                                :set_filter => 1, :assigned_to_id => user.id,
239
                                :set_filter => 1, :assigned_to_id => User.current.id,
109 240
                                :sort => 'due_date:asc')
110
    mail :to => user,
241
    mail :to => User.current,
111 242
      :subject => l(:mail_subject_reminder, :count => issues.size, :days => days)
112 243
  end
113 244

  
114
  # Builds a Mail::Message object used to email users belonging to the added document's project.
245
  # Builds a Mail::Message object used to email the given user about their due
246
  # issues
115 247
  #
116 248
  # Example:
117
  #   document_added(document) => Mail::Message object
118
  #   Mailer.document_added(document).deliver => sends an email to the document's project recipients
119
  def document_added(document)
249
  #   Mailer.reminder(user, issues, days, author).deliver => sends an email to the user
250
  def self.reminder(user, issues, days)
251
    MultiMessage.new(:reminder, issues, days).for(user)
252
  end
253

  
254
  # Builds a Mail::Message object used to email the current user that a document
255
  # was added.
256
  #
257
  # Example:
258
  #   document_added(document, author) => Mail::Message object
259
  def document_added(document, author)
120 260
    redmine_headers 'Project' => document.project.identifier
121
    @author = User.current
261
    @author = author
122 262
    @document = document
123 263
    @document_url = url_for(:controller => 'documents', :action => 'show', :id => document)
124
    mail :to => document.notified_users,
264
    mail :to => User.current,
125 265
      :subject => "[#{document.project.name}] #{l(:label_document_new)}: #{document.title}"
126 266
  end
127 267

  
128
  # Builds a Mail::Message object used to email recipients of a project when an attachements are added.
268
  # Build a MultiMessage to notify users about an added document.
269
  #
270
  # Example:
271
  #   Mailer.document_added(document).deliver => sends emails to the document's project recipients
272
  def self.document_added(document)
273
    users = document.notified_users
274
    MultiMessage.new(:document_added, document, User.current).for(users)
275
  end
276

  
277
  # Builds a Mail::Message object used to email the current user when
278
  # attachements are added.
129 279
  #
130 280
  # Example:
131 281
  #   attachments_added(attachments) => Mail::Message object
132
  #   Mailer.attachments_added(attachments).deliver => sends an email to the project's recipients
133 282
  def attachments_added(attachments)
134 283
    container = attachments.first.container
135 284
    added_to = ''
......
139 288
    when 'Project'
140 289
      added_to_url = url_for(:controller => 'files', :action => 'index', :project_id => container)
141 290
      added_to = "#{l(:label_project)}: #{container}"
142
      recipients = container.project.notified_users.select {|user| user.allowed_to?(:view_files, container.project)}
143 291
    when 'Version'
144 292
      added_to_url = url_for(:controller => 'files', :action => 'index', :project_id => container.project)
145 293
      added_to = "#{l(:label_version)}: #{container.name}"
146
      recipients = container.project.notified_users.select {|user| user.allowed_to?(:view_files, container.project)}
147 294
    when 'Document'
148 295
      added_to_url = url_for(:controller => 'documents', :action => 'show', :id => container.id)
149 296
      added_to = "#{l(:label_document)}: #{container.title}"
150
      recipients = container.notified_users
151 297
    end
152 298
    redmine_headers 'Project' => container.project.identifier
153 299
    @attachments = attachments
154 300
    @added_to = added_to
155 301
    @added_to_url = added_to_url
156
    mail :to => recipients,
302
    mail :to => User.current,
157 303
      :subject => "[#{container.project.name}] #{l(:label_attachment_new)}"
158 304
  end
159 305

  
160
  # Builds a Mail::Message object used to email recipients of a news' project when a news item is added.
306
  # Build a MultiMessage to notify users about an added attachment
307
  #
308
  # Example:
309
  #   Mailer.attachments_added(attachments).deliver => sends emails to the project's recipients
310
  def self.attachments_added(attachments)
311
    container = attachments.first.container
312
    case container.class.name
313
    when 'Project', 'Version'
314
      users = container.project.notified_users.select {|user| user.allowed_to?(:view_files, container.project)}
315
    when 'Document'
316
      users = container.notified_users
317
    end
318

  
319
    MultiMessage.new(:attachments_added, attachments).for(users)
320
  end
321

  
322
  # Builds a Mail::Message object used to email the current user when a news
323
  # item is added.
161 324
  #
162 325
  # Example:
163 326
  #   news_added(news) => Mail::Message object
164
  #   Mailer.news_added(news).deliver => sends an email to the news' project recipients
165 327
  def news_added(news)
166 328
    redmine_headers 'Project' => news.project.identifier
167 329
    @author = news.author
......
169 331
    references news
170 332
    @news = news
171 333
    @news_url = url_for(:controller => 'news', :action => 'show', :id => news)
172
    mail :to => news.notified_users,
173
      :cc => news.notified_watchers_for_added_news,
334
    mail :to => User.current,
174 335
      :subject => "[#{news.project.name}] #{l(:label_news)}: #{news.title}"
175 336
  end
176 337

  
177
  # Builds a Mail::Message object used to email recipients of a news' project when a news comment is added.
338
  # Build a MultiMessage to notify users about a new news item
339
  #
340
  # Example:
341
  #   Mailer.news_added(news).deliver => sends emails to the news' project recipients
342
  def self.news_added(news)
343
    users = news.notified_users | news.notified_watchers_for_added_news
344
    MultiMessage.new(:news_added, news).for(users)
345
  end
346

  
347
  # Builds a Mail::Message object used to email the current user when a news
348
  # comment is added.
178 349
  #
179 350
  # Example:
180 351
  #   news_comment_added(comment) => Mail::Message object
181
  #   Mailer.news_comment_added(comment) => sends an email to the news' project recipients
182 352
  def news_comment_added(comment)
183 353
    news = comment.commented
184 354
    redmine_headers 'Project' => news.project.identifier
......
188 358
    @news = news
189 359
    @comment = comment
190 360
    @news_url = url_for(:controller => 'news', :action => 'show', :id => news)
191
    mail :to => news.notified_users,
192
     :cc => news.notified_watchers,
361
    mail :to => User.current,
193 362
     :subject => "Re: [#{news.project.name}] #{l(:label_news)}: #{news.title}"
194 363
  end
195 364

  
196
  # Builds a Mail::Message object used to email the recipients of the specified message that was posted.
365
  # Build a MultiMessage to notify users about a new news comment
366
  #
367
  # Example:
368
  #   Mailer.news_comment_added(comment).deliver => sends emails to the news' project recipients
369
  def self.news_comment_added(comment)
370
    news = comment.commented
371
    users = news.notified_users | news.notified_watchers
372

  
373
    MultiMessage.new(:news_comment_added, comment).for(users)
374
  end
375

  
376
  # Builds a Mail::Message object used to email the current user that the
377
  # specified message was posted.
197 378
  #
198 379
  # Example:
199 380
  #   message_posted(message) => Mail::Message object
200
  #   Mailer.message_posted(message).deliver => sends an email to the recipients
201 381
  def message_posted(message)
202 382
    redmine_headers 'Project' => message.project.identifier,
203 383
                    'Topic-Id' => (message.parent_id || message.id)
204 384
    @author = message.author
205 385
    message_id message
206 386
    references message.root
207
    recipients = message.notified_users
208
    cc = ((message.root.notified_watchers + message.board.notified_watchers).uniq - recipients)
209 387
    @message = message
210 388
    @message_url = url_for(message.event_url)
211
    mail :to => recipients,
212
      :cc => cc,
389
    mail :to => User.current,
213 390
      :subject => "[#{message.board.project.name} - #{message.board.name} - msg#{message.root.id}] #{message.subject}"
214 391
  end
215 392

  
216
  # Builds a Mail::Message object used to email the recipients of a project of the specified wiki content was added.
393
  # Build a MultiMessage to notify users about a new forum message
394
  #
395
  # Example:
396
  #   Mailer.message_posted(message).deliver => sends emails to the recipients
397
  def self.message_posted(message)
398
    users  = message.notified_users
399
    users |= message.root.notified_watchers
400
    users |= message.board.notified_watchers
401

  
402
    MultiMessage.new(:message_posted, message).for(users)
403
  end
404

  
405
  # Builds a Mail::Message object used to email the current user that the
406
  # specified wiki content was added.
217 407
  #
218 408
  # Example:
219 409
  #   wiki_content_added(wiki_content) => Mail::Message object
220
  #   Mailer.wiki_content_added(wiki_content).deliver => sends an email to the project's recipients
221 410
  def wiki_content_added(wiki_content)
222 411
    redmine_headers 'Project' => wiki_content.project.identifier,
223 412
                    'Wiki-Page-Id' => wiki_content.page.id
224 413
    @author = wiki_content.author
225 414
    message_id wiki_content
226
    recipients = wiki_content.notified_users
227
    cc = wiki_content.page.wiki.notified_watchers - recipients
228 415
    @wiki_content = wiki_content
229 416
    @wiki_content_url = url_for(:controller => 'wiki', :action => 'show',
230 417
                                      :project_id => wiki_content.project,
231 418
                                      :id => wiki_content.page.title)
232
    mail :to => recipients,
233
      :cc => cc,
419
    mail :to => User.current,
234 420
      :subject => "[#{wiki_content.project.name}] #{l(:mail_subject_wiki_content_added, :id => wiki_content.page.pretty_title)}"
235 421
  end
236 422

  
237
  # Builds a Mail::Message object used to email the recipients of a project of the specified wiki content was updated.
423
  # Build a MultiMessage to notify users about added wiki content
424
  #
425
  # Example:
426
  #   Mailer.wiki_content_added(wiki_content).deliver => send emails to the project's recipients
427
  def self.wiki_content_added(wiki_content)
428
    users = wiki_content.notified_users | wiki_content.page.wiki.notified_watchers
429
    MultiMessage.new(:wiki_content_added, wiki_content).for(users)
430
  end
431

  
432
  # Builds a Mail::Message object used to email the current user about an update
433
  # of the specified wiki content.
238 434
  #
239 435
  # Example:
240 436
  #   wiki_content_updated(wiki_content) => Mail::Message object
241
  #   Mailer.wiki_content_updated(wiki_content).deliver => sends an email to the project's recipients
242 437
  def wiki_content_updated(wiki_content)
243 438
    redmine_headers 'Project' => wiki_content.project.identifier,
244 439
                    'Wiki-Page-Id' => wiki_content.page.id
245 440
    @author = wiki_content.author
246 441
    message_id wiki_content
247
    recipients = wiki_content.notified_users
248
    cc = wiki_content.page.wiki.notified_watchers + wiki_content.page.notified_watchers - recipients
249 442
    @wiki_content = wiki_content
250 443
    @wiki_content_url = url_for(:controller => 'wiki', :action => 'show',
251 444
                                      :project_id => wiki_content.project,
......
253 446
    @wiki_diff_url = url_for(:controller => 'wiki', :action => 'diff',
254 447
                                   :project_id => wiki_content.project, :id => wiki_content.page.title,
255 448
                                   :version => wiki_content.version)
256
    mail :to => recipients,
257
      :cc => cc,
449
    mail :to => User.current,
258 450
      :subject => "[#{wiki_content.project.name}] #{l(:mail_subject_wiki_content_updated, :id => wiki_content.page.pretty_title)}"
259 451
  end
260 452

  
261
  # Builds a Mail::Message object used to email the specified user their account information.
453
  # Build a MultiMessage to notify users about the update of the specified wiki content
262 454
  #
263 455
  # Example:
264
  #   account_information(user, password) => Mail::Message object
265
  #   Mailer.account_information(user, password).deliver => sends account information to the user
266
  def account_information(user, password)
267
    set_language_if_valid user.language
268
    @user = user
456
  #   Mailer.wiki_content_updated(wiki_content).deliver => sends an email to the project's recipients
457
  def self.wiki_content_updated(wiki_content)
458
    users  = wiki_content.notified_users
459
    users |= wiki_content.page.notified_watchers
460
    users |= wiki_content.page.wiki.notified_watchers
461

  
462
    MultiMessage.new(:wiki_content_updated, wiki_content).for(users)
463
  end
464

  
465
  # Builds a Mail::Message object used to email the current user their account information.
466
  #
467
  # Example:
468
  #   account_information(password) => Mail::Message object
469
  def account_information(password)
470
    @user = User.current
269 471
    @password = password
270 472
    @login_url = url_for(:controller => 'account', :action => 'login')
271
    mail :to => user.mail,
473
    mail :to => User.current.mail,
272 474
      :subject => l(:mail_subject_register, Setting.app_title)
273 475
  end
274 476

  
275
  # Builds a Mail::Message object used to email all active administrators of an account activation request.
477
  # Build a MultiMessage to mail a user their account information
478
  #
479
  # Example:
480
  #   Mailer.account_information(user, password).deliver => sends account information to the user
481
  def self.account_information(user, password)
482
    MultiMessage.new(:account_information, password).for(user)
483
  end
484

  
485
  # Builds a Mail::Message object used to email the current user about an account activation request.
276 486
  #
277 487
  # Example:
278 488
  #   account_activation_request(user) => Mail::Message object
279
  #   Mailer.account_activation_request(user).deliver => sends an email to all active administrators
280 489
  def account_activation_request(user)
281
    # Send the email to all active administrators
282
    recipients = User.active.where(:admin => true)
283 490
    @user = user
284 491
    @url = url_for(:controller => 'users', :action => 'index',
285 492
                         :status => User::STATUS_REGISTERED,
286 493
                         :sort_key => 'created_on', :sort_order => 'desc')
287
    mail :to => recipients,
494
    mail :to => User.current,
288 495
      :subject => l(:mail_subject_account_activation_request, Setting.app_title)
289 496
  end
290 497

  
291
  # Builds a Mail::Message object used to email the specified user that their account was activated by an administrator.
498
  # Build a MultiMessage to email all active administrators of an account activation request.
292 499
  #
293 500
  # Example:
294
  #   account_activated(user) => Mail::Message object
295
  #   Mailer.account_activated(user).deliver => sends an email to the registered user
296
  def account_activated(user)
297
    set_language_if_valid user.language
298
    @user = user
501
  #   Mailer.account_activation_request(user).deliver => sends an email to all active administrators
502
  def self.account_activation_request(user)
503
    # Send the email to all active administrators
504
    users = User.active.where(:admin => true)
505
    MultiMessage.new(:account_activation_request, user).for(users)
506
  end
507

  
508
  # Builds a Mail::Message object used to email the account of the current user
509
  # was activated by an administrator.
510
  #
511
  # Example:
512
  #   account_activated => Mail::Message object
513
  def account_activated
514
    @user = User.current
299 515
    @login_url = url_for(:controller => 'account', :action => 'login')
300
    mail :to => user.mail,
516
    mail :to => User.current.mail,
301 517
      :subject => l(:mail_subject_register, Setting.app_title)
302 518
  end
303 519

  
520
  # Build a MultiMessage to email the specified user that their account was
521
  # activated by an administrator.
522
  #
523
  # Example:
524
  #   Mailer.account_activated(user).deliver => sends an email to the registered user
525
  def self.account_activated(user)
526
    MultiMessage.new(:account_activated).for(user)
527
  end
528

  
529
  # Builds a Mail::Message object used to email the lost password token to the
530
  # token's user (or a different recipient).
531
  #
532
  # Example:
533
  #   lost_password(token) => Mail::Message object
304 534
  def lost_password(token, recipient=nil)
305
    set_language_if_valid(token.user.language)
306 535
    recipient ||= token.user.mail
307 536
    @token = token
308 537
    @url = url_for(:controller => 'account', :action => 'lost_password', :token => token.value)
......
310 539
      :subject => l(:mail_subject_lost_password, Setting.app_title)
311 540
  end
312 541

  
542
  # Build a MultiMessage to email the token's user (or a different recipient)
543
  # the lost password token for the token's user.
544
  #
545
  # Example:
546
  #   Mailer.lost_password(token).deliver => sends an email to the user
547
  def self.lost_password(token, recipient=nil)
548
    MultiMessage.new(:lost_password, token, recipient).for(token.user)
549
  end
550

  
313 551
  # Notifies user that his password was updated
314 552
  def self.password_updated(user)
315 553
    # Don't send a notification to the dummy email address when changing the password
......
324 562
    ).deliver
325 563
  end
326 564

  
565
  # Builds a Mail::Message object used to email the user activation link to the
566
  # token's user.
567
  #
568
  # Example:
569
  #   register(token) => Mail::Message object
327 570
  def register(token)
328
    set_language_if_valid(token.user.language)
329 571
    @token = token
330 572
    @url = url_for(:controller => 'account', :action => 'activate', :token => token.value)
331 573
    mail :to => token.user.mail,
332 574
      :subject => l(:mail_subject_register, Setting.app_title)
333 575
  end
334 576

  
335
  def security_notification(recipients, options={})
336
    redmine_headers 'Sender' => User.current.login
337
    @user = Array(recipients).detect{|r| r.is_a? User }
338
    set_language_if_valid(@user.try :language)
577
  # Build a MultiMessage to email the user activation link to the token's user.
578
  #
579
  # Example:
580
  #   Mailer.register(token).deliver => sends an email to the token's user
581
  def self.register(token)
582
    MultiMessage.new(:register, token).for(token.user)
583
  end
584

  
585
  # Build a Mail::Message object to email the current user and the additional
586
  # recipients given in options[:recipients] about a security related event.
587
  #
588
  # Example:
589
  #   security_notification(users,
590
  #     message: :mail_body_security_notification_add,
591
  #     field: :field_mail,
592
  #     value: address
593
  #   ) => Mail::Message object
594
  def security_notification(sender, options={})
595
    @sender = sender
596
    redmine_headers 'Sender' => sender.login
597
    @user = User.current
339 598
    @message = l(options[:message],
340 599
      field: (options[:field] && l(options[:field])),
341 600
      value: options[:value]
342 601
    )
343 602
    @title = options[:title] && l(options[:title])
344 603
    @url = options[:url] && (options[:url].is_a?(Hash) ? url_for(options[:url]) : options[:url])
345
    mail :to => recipients,
604
    mail :to => [User.current, *options[:recipients]].uniq,
346 605
      :subject => "[#{Setting.app_title}] #{l(:mail_subject_security_notification)}"
347 606
  end
348 607

  
349
  def settings_updated(recipients, changes)
350
    redmine_headers 'Sender' => User.current.login
608
  # Build a MultiMessage to email the given users about a security related event.
609
  #
610
  # You can specify additional recipients in options[:recipients]. These will be
611
  # added to all generated mails for all given users. Usually, you'll want to
612
  # give only a single user when setting the additional recipients.
613
  #
614
  # Example:
615
  #   Mailer.security_notification(users,
616
  #     message: :mail_body_security_notification_add,
617
  #     field: :field_mail,
618
  #     value: address
619
  #   ).deliver => sends a security notification to the given user(s)
620
  def self.security_notification(users, options={})
621
    sender = User.current
622
    MultiMessage.new(:security_notification, sender, options).for(users)
623
  end
624

  
625
  # Build a Mail::Message object to email the current user about an updated
626
  # setting.
627
  #
628
  # Example:
629
  #   settings_updated(sender, [:host_name]) => Mail::Message object
630
  def settings_updated(sender, changes)
631
    @sender = sender
632
    redmine_headers 'Sender' => sender.login
351 633
    @changes = changes
352 634
    @url = url_for(controller: 'settings', action: 'index')
353
    mail :to => recipients,
635
    mail :to => User.current,
354 636
      :subject => "[#{Setting.app_title}] #{l(:mail_subject_security_notification)}"
355 637
  end
356 638

  
639
  # Build a MultiMessage to email the given users about an update of a setting.
640
  #
641
  # Example:
642
  #   Mailer.settings_updated(users, [:host_name]).deliver => sends emails to the given user(s) about the update
643
  def self.settings_updated(users, changes)
644
    sender = User.current
645
    MultiMessage.new(:settings_updated, sender, changes).for(users)
646
  end
647

  
357 648
  # Notifies admins about settings changes
358 649
  def self.security_settings_updated(changes)
359 650
    return unless changes.present?
......
362 653
    settings_updated(users, changes).deliver
363 654
  end
364 655

  
365
  def test_email(user)
366
    set_language_if_valid(user.language)
656
  # Build a Mail::Message object with a test email for the current user
657
  #
658
  # Example:
659
  #   test_email => Mail::Message object
660
  def test_email
367 661
    @url = url_for(:controller => 'welcome')
368
    mail :to => user.mail,
662
    mail :to => User.current.mail,
369 663
      :subject => 'Redmine test'
370 664
  end
371 665

  
666
  # Build a MultiMessage to send a test email the given user
667
  #
668
  # Example:
669
  #   Mailer.test_email(user).deliver => send an email to the given user
670
  def self.test_email(user)
671
    MultiMessage.new(:test_email).for(user)
672
  end
673

  
372 674
  # Sends reminders to issue assignees
373 675
  # Available options:
374 676
  # * :days     => how many days in the future to remind about (defaults to 7)
......
478 780
      headers[:references] = @references_objects.collect {|o| "<#{self.class.references_for(o)}>"}.join(' ')
479 781
    end
480 782

  
481
    m = if block_given?
783
    if block_given?
482 784
      super headers, &block
483 785
    else
484 786
      super headers do |format|
......
486 788
        format.html unless Setting.plain_text_mail?
487 789
      end
488 790
    end
489
    set_language_if_valid @initial_language
490

  
491
    m
492
  end
493

  
494
  def initialize(*args)
495
    @initial_language = current_language
496
    set_language_if_valid Setting.default_language
497
    super
498 791
  end
499 792

  
500 793
  def self.deliver_mail(mail)
......
516 809
    if m = method.to_s.match(%r{^deliver_(.+)$})
517 810
      ActiveSupport::Deprecation.warn "Mailer.deliver_#{m[1]}(*args) is deprecated. Use Mailer.#{m[1]}(*args).deliver instead."
518 811
      send(m[1], *args).deliver
812
    elsif action_methods.include?(method.to_s)
813
      MultiMessage.new(method, *args).for(User.current)
519 814
    else
520 815
      super
521 816
    end
app/views/mailer/security_notification.html.erb
7 7
<%= content_tag :h1, @title -%>
8 8
<% end %></p>
9 9

  
10
<p><%= l(:field_user) %>: <strong><%= User.current.login %></strong><br/>
11
<%= l(:field_remote_ip) %>: <strong><%= User.current.remote_ip %></strong><br/>
12
<%= l(:label_date) %>: <strong><%= format_time Time.now, true, @user %></strong></p>
10
<p><%= l(:field_user) %>: <strong><%= @sender.login %></strong><br/>
11
<%= l(:field_remote_ip) %>: <strong><%= @sender.remote_ip %></strong><br/>
12
<%= l(:label_date) %>: <strong><%= format_time Time.now, true %></strong></p>
13 13

  
app/views/mailer/security_notification.text.erb
2 2

  
3 3
<%= @url || @title %>
4 4

  
5
<%= l(:field_user) %>: <%= User.current.login %>
6
<%= l(:field_remote_ip) %>: <%= User.current.remote_ip %>
7
<%= l(:label_date) %>: <%= format_time Time.now, true, @user %>
5
<%= l(:field_user) %>: <%= @sender.login %>
6
<%= l(:field_remote_ip) %>: <%= @sender.remote_ip %>
7
<%= l(:label_date) %>: <%= format_time Time.now, true %>
8 8

  
app/views/mailer/settings_updated.html.erb
8 8

  
9 9
<%= link_to @url, @url %>
10 10

  
11
<p><%= l(:field_user) %>: <strong><%= User.current.login %></strong><br/>
12
<%= l(:field_remote_ip) %>: <strong><%= User.current.remote_ip %></strong><br/>
11
<p><%= l(:field_user) %>: <strong><%= @sender.login %></strong><br/>
12
<%= l(:field_remote_ip) %>: <strong><%= @sender.remote_ip %></strong><br/>
13 13
<%= l(:label_date) %>: <strong><%= format_time Time.now, true %></strong></p>
14 14

  
app/views/mailer/settings_updated.text.erb
6 6

  
7 7
<%= @url %>
8 8

  
9
<%= l(:field_user) %>: <%= User.current.login %>
10
<%= l(:field_remote_ip) %>: <%= User.current.remote_ip %>
9
<%= l(:field_user) %>: <%= @sender.login %>
10
<%= l(:field_remote_ip) %>: <%= @sender.remote_ip %>
11 11
<%= l(:label_date) %>: <%= format_time Time.now, true %>
12 12

  
test/functional/documents_controller_test.rb
132 132
    assert_equal Enumeration.find(2), document.category
133 133
    assert_equal 1, document.attachments.size
134 134
    assert_equal 'testfile.txt', document.attachments.first.filename
135
    assert_equal 1, ActionMailer::Base.deliveries.size
135
    assert_equal 2, ActionMailer::Base.deliveries.size
136 136
  end
137 137

  
138 138
  def test_create_with_failure
test/functional/issues_controller_test.rb
3427 3427
      end
3428 3428
      assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
3429 3429

  
3430
      assert_equal 1, ActionMailer::Base.deliveries.size
3430
      assert_equal 2, ActionMailer::Base.deliveries.size
3431 3431
    end
3432 3432
  end
3433 3433

  
......
4982 4982
            
4983 4983
          }
4984 4984
        }
4985
      assert_equal 1, ActionMailer::Base.deliveries.size
4985
      assert_equal 2, ActionMailer::Base.deliveries.size
4986 4986
    end
4987 4987
  end
4988 4988

  
......
5457 5457
          :priority_id => 7,
5458 5458
          :assigned_to_id => '',
5459 5459
          :custom_field_values => {
5460
          '2' => ''}    
5460
          '2' => ''}
5461 5461
        }
5462 5462
      }
5463 5463
    assert_response 403
5464 5464
    assert_not_equal "Bulk should fail", Journal.last.notes
5465 5465
  end
5466 5466

  
5467
  def test_bullk_update_should_send_a_notification
5467
  def test_bulk_update_should_send_a_notification
5468 5468
    @request.session[:user_id] = 2
5469 5469
    ActionMailer::Base.deliveries.clear
5470 5470
    with_settings :notified_events => %w(issue_updated) do
......
5478 5478
          }
5479 5479
        }
5480 5480
      assert_response 302
5481
      assert_equal 2, ActionMailer::Base.deliveries.size
5481
      assert_equal 5, ActionMailer::Base.deliveries.size
5482 5482
    end
5483 5483
  end
5484 5484

  
test/functional/issues_custom_fields_visibility_test.rb
282 282
        assert_response 302
283 283
      end
284 284
    end
285
    assert_equal users_to_test.values.uniq.size, ActionMailer::Base.deliveries.size
285

  
286
    assert_equal users_to_test.keys.size, ActionMailer::Base.deliveries.size
286 287
    # tests that each user receives 1 email with the custom fields he is allowed to see only
287 288
    users_to_test.each do |user, fields|
288 289
      mails = ActionMailer::Base.deliveries.select {|m| m.bcc.include? user.mail}
......
319 320
        }
320 321
      assert_response 302
321 322
    end
322
    assert_equal users_to_test.values.uniq.size, ActionMailer::Base.deliveries.size
323
    assert_equal users_to_test.keys.size, ActionMailer::Base.deliveries.size
323 324
    # tests that each user receives 1 email with the custom fields he is allowed to see only
324 325
    users_to_test.each do |user, fields|
325 326
      mails = ActionMailer::Base.deliveries.select {|m| m.bcc.include? user.mail}
test/functional/messages_controller_test.rb
136 136
    assert_equal 2, message.author_id
137 137
    assert_equal 1, message.board_id
138 138

  
139
    mail = ActionMailer::Base.deliveries.last
140
    assert_not_nil mail
141
    assert_equal "[#{message.board.project.name} - #{message.board.name} - msg#{message.root.id}] Test created message", mail.subject
142
    assert_mail_body_match 'Message body', mail
139
    mails = ActionMailer::Base.deliveries
140
    assert_not_empty mails
141
    mails.each do |mail|
142
      assert_equal "[#{message.board.project.name} - #{message.board.name} - msg#{message.root.id}] Test created message", mail.subject
143
      assert_mail_body_match 'Message body', mail
144
    end
145

  
143 146
    # author
144
    assert mail.bcc.include?('jsmith@somenet.foo')
147
    assert_equal ['jsmith@somenet.foo'], mails[0].bcc
145 148
    # project member
146
    assert mail.bcc.include?('dlopper@somenet.foo')
149
    assert_equal ['dlopper@somenet.foo'], mails[1].bcc
147 150
  end
148 151

  
149 152
  def test_get_edit
test/functional/news_controller_test.rb
127 127
    assert_equal 'This is the description', news.description
128 128
    assert_equal User.find(2), news.author
129 129
    assert_equal Project.find(1), news.project
130
    assert_equal 1, ActionMailer::Base.deliveries.size
130
    assert_equal 2, ActionMailer::Base.deliveries.size
131 131
  end
132 132

  
133 133
  def test_post_create_with_attachment
test/unit/changeset_test.rb
46 46
    fixed = Issue.find(1)
47 47
    assert fixed.closed?
48 48
    assert_equal 90, fixed.done_ratio
49
    assert_equal 1, ActionMailer::Base.deliveries.size
49
    assert_equal 2, ActionMailer::Base.deliveries.size
50 50
  end
51 51

  
52 52
  def test_ref_keywords
test/unit/comment_test.rb
36 36
    Watcher.create!(:watchable => @news, :user => @jsmith)
37 37

  
38 38
    with_settings :notified_events => %w(news_comment_added) do
39
      assert_difference 'ActionMailer::Base.deliveries.size' do
39
      assert_difference 'ActionMailer::Base.deliveries.size', 2 do
40 40
        Comment.create!(:commented => @news, :author => @jsmith, :comments => "my comment")
41 41
      end
42 42
    end
test/unit/document_test.rb
41 41
      doc = Document.new(:project => Project.find(1), :title => 'New document', :category => Enumeration.find_by_name('User documentation'))
42 42
      assert doc.save
43 43
    end
44
    assert_equal 1, ActionMailer::Base.deliveries.size
44
    assert_equal 2, ActionMailer::Base.deliveries.size
45 45
  end
46 46

  
47 47
  def test_create_with_default_category
test/unit/issue_test.rb
2430 2430
                      :subject => 'test_create', :estimated_hours => '1:30')
2431 2431
    with_settings :notified_events => %w(issue_added) do
2432 2432
      assert issue.save
2433
      assert_equal 1, ActionMailer::Base.deliveries.size
2433
      assert_equal 2, ActionMailer::Base.deliveries.size
2434 2434
    end
2435 2435
  end
2436 2436

  
......
2442 2442
                      :subject => 'test_create', :estimated_hours => '1:30')
2443 2443
    with_settings :notified_events => %w(issue_added issue_updated) do
2444 2444
      assert issue.save
2445
      assert_equal 1, ActionMailer::Base.deliveries.size
2445
      assert_equal 2, ActionMailer::Base.deliveries.size
2446 2446
    end
2447 2447
  end
2448 2448

  
......
2482 2482
    issue.subject = 'Subjet update'
2483 2483
    with_settings :notified_events => %w(issue_updated) do
2484 2484
      assert issue.save
2485
      assert_equal 1, ActionMailer::Base.deliveries.size
2485
      assert_equal 2, ActionMailer::Base.deliveries.size
2486 2486
      ActionMailer::Base.deliveries.clear
2487 2487

  
2488 2488
      stale.init_journal(User.find(1))
test/unit/journal_observer_test.rb
36 36
    with_settings :notified_events => %w(issue_updated) do
37 37
      assert journal.save
38 38
    end
39
    assert_equal 1, ActionMailer::Base.deliveries.size
39
    assert_equal 2, ActionMailer::Base.deliveries.size
40 40
  end
41 41

  
42 42
  def test_create_should_not_send_email_notification_with_notify_set_to_false
......
71 71
    with_settings :notified_events => %w(issue_note_added) do
72 72
      assert journal.save
73 73
    end
74
    assert_equal 1, ActionMailer::Base.deliveries.size
74
    assert_equal 2, ActionMailer::Base.deliveries.size
75 75
  end
76 76

  
77 77
  def test_create_should_not_send_email_notification_without_issue_note_added
......
95 95
    with_settings :notified_events => %w(issue_status_updated) do
96 96
      assert issue.save
97 97
    end
98
    assert_equal 1, ActionMailer::Base.deliveries.size
98
    assert_equal 2, ActionMailer::Base.deliveries.size
99 99
  end
100 100

  
101 101
  def test_create_should_not_send_email_notification_without_issue_status_updated
......
132 132
    with_settings :notified_events => %w(issue_assigned_to_updated) do
133 133
      assert issue.save
134 134
    end
135
    assert_equal 1, ActionMailer::Base.deliveries.size
135
    assert_equal 2, ActionMailer::Base.deliveries.size
136 136
  end
137 137

  
138 138
  def test_create_should_not_send_email_notification_without_issue_assignee_updated
......
157 157
    with_settings :notified_events => %w(issue_priority_updated) do
158 158
      assert issue.save
159 159
    end
160
    assert_equal 1, ActionMailer::Base.deliveries.size
160
    assert_equal 2, ActionMailer::Base.deliveries.size
161 161
  end
162 162

  
163 163
  def test_create_should_not_send_email_notification_without_issue_priority_updated
test/unit/journal_test.rb
50 50
    journal = issue.init_journal(user, issue)
51 51

  
52 52
    assert journal.save
53
    assert_equal 1, ActionMailer::Base.deliveries.size
53
    assert_equal 2, ActionMailer::Base.deliveries.size
54 54
  end
55 55

  
56 56
  def test_should_not_save_journal_with_blank_notes_and_no_details
test/unit/mail_handler_test.rb
433 433
      )
434 434
    end
435 435

  
436
    # only 1 email for the new issue notification
437
    assert_equal 1, ActionMailer::Base.deliveries.size
438
    email = ActionMailer::Base.deliveries.first
439
    assert_include 'Ticket by unknown user', email.subject
436
    # only 2 emails for the new issue notification
437
    assert_equal 2, ActionMailer::Base.deliveries.size
438
    ActionMailer::Base.deliveries.each do |email|
439
      assert_include 'Ticket by unknown user', email.subject
440
    end
440 441
  end
441 442

  
442 443
  def test_created_user_should_have_mail_notification_to_none_with_no_notification_option
......
861 862
  def test_update_issue_should_send_email_notification
862 863
    journal = submit_email('ticket_reply.eml')
863 864
    assert journal.is_a?(Journal)
864
    assert_equal 1, ActionMailer::Base.deliveries.size
865
    assert_equal 3, ActionMailer::Base.deliveries.size
865 866
  end
866 867

  
867 868
  def test_update_issue_should_not_set_defaults
test/unit/mailer_test.rb
412 412
    journal.save!
413 413

  
414 414
    Role.find(2).add_permission! :view_private_notes
415
    Mailer.deliver_issue_edit(journal)
416
    assert_equal %w(dlopper@somenet.foo jsmith@somenet.foo), ActionMailer::Base.deliveries.last.bcc.sort
415
    assert_difference 'ActionMailer::Base.deliveries.size', 2 do
416
      Mailer.deliver_issue_edit(journal)
417
    end
418
    assert_equal %w(dlopper@somenet.foo jsmith@somenet.foo), ActionMailer::Base.deliveries.last(2).flat_map(&:bcc).sort
417 419

  
418 420
    Role.find(2).remove_permission! :view_private_notes
419
    Mailer.deliver_issue_edit(journal)
421
    assert_difference 'ActionMailer::Base.deliveries.size', 1 do
422
      Mailer.deliver_issue_edit(journal)
423
    end
420 424
    assert_equal %w(jsmith@somenet.foo), ActionMailer::Base.deliveries.last.bcc.sort
421 425
  end
422 426

  
......
534 538
  def test_wiki_content_added
535 539
    content = WikiContent.find(1)
536 540
    with_each_language_as_default do
537
      assert_difference 'ActionMailer::Base.deliveries.size' do
541
      assert_difference 'ActionMailer::Base.deliveries.size', 2 do
538 542
        assert Mailer.wiki_content_added(content).deliver
539 543
        assert_select_email do
540 544
          assert_select 'a[href=?]',
......
548 552
  def test_wiki_content_updated
549 553
    content = WikiContent.find(1)
550 554
    with_each_language_as_default do
551
      assert_difference 'ActionMailer::Base.deliveries.size' do
555
      assert_difference 'ActionMailer::Base.deliveries.size', 2 do
552 556
        assert Mailer.wiki_content_updated(content).deliver
553 557
        assert_select_email do
554 558
          assert_select 'a[href=?]',
test/unit/news_test.rb
34 34
    with_settings :notified_events => %w(news_added) do
35 35
      assert news.save
36 36
    end
37
    assert_equal 1, ActionMailer::Base.deliveries.size
37
    assert_equal 2, ActionMailer::Base.deliveries.size
38 38
  end
39 39

  
40 40
  def test_should_include_news_for_projects_with_news_enabled
test/unit/repository_test.rb
262 262
    assert_equal User.find_by_login('dlopper'), journal.user
263 263
    assert_equal 'Applied in changeset r2.', journal.notes
264 264

  
265
    # 2 email notifications
266
    assert_equal 2, ActionMailer::Base.deliveries.size
267
    mail = ActionMailer::Base.deliveries.first
268
    assert_not_nil mail
269
    assert mail.subject.starts_with?(
270
        "[#{fixed_issue.project.name} - #{fixed_issue.tracker.name} ##{fixed_issue.id}]")
271
    assert_mail_body_match(
272
        "Status changed from #{old_status} to #{fixed_issue.status}", mail)
265
    # 5 email notifications, 2 for #1, 3 for #2
266
    assert_equal 5, ActionMailer::Base.deliveries.size
267
    ActionMailer::Base.deliveries.first(2).each do |mail|
268
      assert_not_nil mail
269
      assert mail.subject.starts_with?(
270
          "[#{fixed_issue.project.name} - #{fixed_issue.tracker.name} ##{fixed_issue.id}]")
271
      assert_mail_body_match(
272
          "Status changed from #{old_status} to #{fixed_issue.status}", mail)
273
    end
273 274

  
274 275
    # ignoring commits referencing an issue of another project
275 276
    assert_equal [], Issue.find(4).changesets
test/unit/wiki_content_test.rb
53 53
      assert page.save
54 54
    end
55 55

  
56
    assert_equal 1, ActionMailer::Base.deliveries.size
57
    assert_include 'wiki page has been added', mail_body(ActionMailer::Base.deliveries.last)
56
    assert_equal 2, ActionMailer::Base.deliveries.size
57
    ActionMailer::Base.deliveries.each do |mail|
58
      assert_include 'wiki page has been added', mail_body(mail)
59
    end
58 60
  end
59 61

  
60 62
  def test_update_should_be_versioned
......
100 102
      assert content.save
101 103
    end
102 104

  
103
    assert_equal 1, ActionMailer::Base.deliveries.size
104
    assert_include 'wiki page has been updated', mail_body(ActionMailer::Base.deliveries.last)
105
    assert_equal 2, ActionMailer::Base.deliveries.size
106
    ActionMailer::Base.deliveries.each do |mail|
107
      assert_include 'wiki page has been updated', mail_body(mail)
108
    end
105 109
  end
106 110

  
107 111
  def test_fetch_history
(2-2/4)