]> source.dussan.org Git - redmine.git/commitdiff
Send emails asynchronously (#26791).
authorJean-Philippe Lang <jp_lang@yahoo.fr>
Wed, 10 Oct 2018 17:13:09 +0000 (17:13 +0000)
committerJean-Philippe Lang <jp_lang@yahoo.fr>
Wed, 10 Oct 2018 17:13:09 +0000 (17:13 +0000)
Custom async_* delivery methods are removed in favor of ActiveJob (Async by default).

git-svn-id: http://svn.redmine.org/redmine/trunk@17588 e93f8b46-1217-0410-a6f0-8f06a7374b81

37 files changed:
app/controllers/account_controller.rb
app/controllers/admin_controller.rb
app/controllers/documents_controller.rb
app/controllers/files_controller.rb
app/controllers/my_controller.rb
app/controllers/users_controller.rb
app/models/comment.rb
app/models/document.rb
app/models/email_address.rb
app/models/issue.rb
app/models/journal.rb
app/models/mail_handler.rb
app/models/mailer.rb
app/models/message.rb
app/models/news.rb
app/models/setting.rb
app/models/user.rb
app/models/wiki_content.rb
app/views/mailer/_issue.html.erb
app/views/mailer/_issue.text.erb
app/views/mailer/account_activation_request.html.erb
app/views/mailer/account_activation_request.text.erb
app/views/mailer/issue_add.html.erb
app/views/mailer/issue_add.text.erb
app/views/mailer/issue_edit.html.erb
app/views/mailer/issue_edit.text.erb
app/views/mailer/security_notification.html.erb
app/views/mailer/security_notification.text.erb
app/views/mailer/settings_updated.html.erb
app/views/mailer/settings_updated.text.erb
config/initializers/10-patches.rb
lib/redmine/configuration.rb
lib/redmine/info.rb
lib/tasks/email.rake
test/unit/mail_handler_test.rb
test/unit/mailer_localisation_test.rb [new file with mode: 0644]
test/unit/mailer_test.rb

index 5070295d2a72d973642c8151820d497dc006bc1e..7bb6447613cf3a2aff52df920904080eb9569d7a 100644 (file)
@@ -87,7 +87,7 @@ class AccountController < ApplicationController
           @user.must_change_passwd = false
           if @user.save
             @token.destroy
-            Mailer.password_updated(@user, { remote_ip: request.remote_ip })
+            Mailer.deliver_password_updated(@user, User.current)
             flash[:notice] = l(:notice_account_password_updated)
             redirect_to signin_path
             return
@@ -119,7 +119,7 @@ class AccountController < ApplicationController
         if token.save
           # Don't use the param to send the email
           recipent = user.mails.detect {|e| email.casecmp(e) == 0} || user.mail
-          Mailer.lost_password(token, recipent).deliver
+          Mailer.deliver_lost_password(user, token, recipent)
           flash[:notice] = l(:notice_account_lost_email_sent)
           redirect_to signin_path
           return
@@ -313,7 +313,7 @@ class AccountController < ApplicationController
   def register_by_email_activation(user, &block)
     token = Token.new(:user => user, :action => "register")
     if user.save and token.save
-      Mailer.register(token).deliver
+      Mailer.deliver_register(user, token)
       flash[:notice] = l(:notice_account_register_done, :email => ERB::Util.h(user.mail))
       redirect_to signin_path
     else
@@ -343,7 +343,7 @@ class AccountController < ApplicationController
   def register_manually_by_administrator(user, &block)
     if user.save
       # Sends an email to the administrators
-      Mailer.account_activation_request(user).deliver
+      Mailer.deliver_account_activation_request(user)
       account_pending(user)
     else
       yield if block_given?
index dfc73c5abe038069ba7c06fbc3909b31e90e536e..885c3a169f6455ad1dd37931a1125f3f4e381439 100644 (file)
@@ -60,16 +60,12 @@ class AdminController < ApplicationController
   end
 
   def test_email
-    raise_delivery_errors = ActionMailer::Base.raise_delivery_errors
-    # Force ActionMailer to raise delivery errors so we can catch it
-    ActionMailer::Base.raise_delivery_errors = true
     begin
-      @test = Mailer.test_email(User.current).deliver
+      Mailer.deliver_test_email(User.current)
       flash[:notice] = l(:notice_email_sent, ERB::Util.h(User.current.mail))
     rescue Exception => e
       flash[:error] = l(:notice_email_error, ERB::Util.h(Redmine::CodesetUtil.replace_invalid_utf8(e.message.dup)))
     end
-    ActionMailer::Base.raise_delivery_errors = raise_delivery_errors
     redirect_to settings_path(:tab => 'notifications')
   end
 
index dbf0e88dc7277019460d463a987feaaa7fd88087..00b6dd9f1076f26b6ee0aa6af9f32c743d3b61d4 100644 (file)
@@ -88,7 +88,7 @@ class DocumentsController < ApplicationController
     render_attachment_warning_if_needed(@document)
 
     if attachments.present? && attachments[:files].present? && Setting.notified_events.include?('document_added')
-      Mailer.attachments_added(attachments[:files]).deliver
+      Mailer.deliver_attachments_added(attachments[:files])
     end
     redirect_to document_path(@document)
   end
index 7c596657c86f6219aa37676a8d52f1383e17da71..6aa55f8a67cb4eaf3dc3d9f448ed10633d7e67de 100644 (file)
@@ -55,7 +55,7 @@ class FilesController < ApplicationController
 
     if attachments[:files].present?
       if Setting.notified_events.include?('file_added')
-        Mailer.attachments_added(attachments[:files]).deliver
+        Mailer.deliver_attachments_added(attachments[:files])
       end
       respond_to do |format|
         format.html {
index 0a1d40349e6dc7cb2db42506e9685feafee42a06..d2cc0c53c78296573572f0b3a9ad3a8b25694929 100644 (file)
@@ -96,7 +96,7 @@ class MyController < ApplicationController
         if @user.save
           # The session token was destroyed by the password change, generate a new one
           session[:tk] = @user.generate_session_token
-          Mailer.password_updated(@user)
+          Mailer.deliver_password_updated(@user, User.current)
           flash[:notice] = l(:notice_account_password_updated)
           redirect_to my_account_path
         end
index aed4cd83fa3f4b8af77257b82637132dae99bcd3..65dd9a47016496a37589d84233b452ad16a5bb08 100644 (file)
@@ -101,7 +101,7 @@ class UsersController < ApplicationController
     @user.pref.safe_attributes = params[:pref]
 
     if @user.save
-      Mailer.account_information(@user, @user.password).deliver if params[:send_information]
+      Mailer.deliver_account_information(@user, @user.password) if params[:send_information]
 
       respond_to do |format|
         format.html {
@@ -146,9 +146,9 @@ class UsersController < ApplicationController
       @user.pref.save
 
       if was_activated
-        Mailer.account_activated(@user).deliver
+        Mailer.deliver_account_activated(@user)
       elsif @user.active? && params[:send_information] && @user != User.current
-        Mailer.account_information(@user, @user.password).deliver
+        Mailer.deliver_account_information(@user, @user.password)
       end
 
       respond_to do |format|
index 3b204412351e2b76d65f96d2eebffcdd9198cb22..f2833ac6c5af27c9942d2b7125c1ef8e2e93df82 100644 (file)
@@ -22,7 +22,7 @@ class Comment < ActiveRecord::Base
 
   validates_presence_of :commented, :author, :content
 
-  after_create :send_notification
+  after_create_commit :send_notification
 
   safe_attributes 'comments'
 
@@ -37,9 +37,9 @@ class Comment < ActiveRecord::Base
   private
 
   def send_notification
-    mailer_method = "#{commented.class.name.underscore}_comment_added"
-    if Setting.notified_events.include?(mailer_method)
-      Mailer.send(mailer_method, self).deliver
+    event = "#{commented.class.name.underscore}_comment_added"
+    if Setting.notified_events.include?(event)
+      Mailer.public_send("deliver_#{event}", self)
     end
   end
 end
index 8e4b155174629e139178b5b932fc4155012def1b..ccf750fc68d97aa4ee68d3be7992ea338110333e 100644 (file)
@@ -32,7 +32,7 @@ class Document < ActiveRecord::Base
   validates_presence_of :project, :title, :category
   validates_length_of :title, :maximum => 255
 
-  after_create :send_notification
+  after_create_commit :send_notification
 
   scope :visible, lambda {|*args|
     joins(:project).
@@ -68,7 +68,7 @@ class Document < ActiveRecord::Base
 
   def send_notification
     if Setting.notified_events.include?('document_added')
-      Mailer.document_added(self).deliver
+      Mailer.deliver_document_added(self, User.current)
     end
   end
 end
index ffd5d6b4f07143f7e0ab0aac24cecddc8f820b14..3bcd929d69bd641a057a5bf801dd5c200833289d 100644 (file)
@@ -20,9 +20,12 @@ class EmailAddress < ActiveRecord::Base
 
   belongs_to :user
 
-  after_create :deliver_security_notification_create
-  after_update :destroy_tokens, :deliver_security_notification_update
-  after_destroy :destroy_tokens, :deliver_security_notification_destroy
+  after_update :destroy_tokens
+  after_destroy :destroy_tokens
+
+  after_create_commit :deliver_security_notification_create
+  after_update_commit :deliver_security_notification_update
+  after_destroy_commit :deliver_security_notification_destroy
 
   validates_presence_of :address
   validates_format_of :address, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, :allow_blank => true
@@ -91,12 +94,13 @@ class EmailAddress < ActiveRecord::Base
 
   # generic method to send security notifications for email addresses
   def deliver_security_notification(options={})
-    Mailer.security_notification(user,
+    Mailer.deliver_security_notification(user,
+      User.current,
       options.merge(
         title: :label_my_account,
         url: {controller: 'my', action: 'account'}
       )
-    ).deliver
+    )
   end
 
   # Delete all outstanding password reset tokens on email change.
index d38a4f5ea114c43e5daa19bd7c91000974c4def4..b20da8d91c7c3a116e583732933b57932e9bd32d 100644 (file)
@@ -114,7 +114,7 @@ class Issue < ActiveRecord::Base
   # Should be after_create but would be called before previous after_save callbacks
   after_save :after_create_from_copy
   after_destroy :update_parent_attributes
-  after_create :send_notification
+  after_create_commit :send_notification
 
   # Returns a SQL conditions string used to find all issues visible by the specified user
   def self.visible_condition(user, options={})
index cc96967f51807d8a4950634de07bd38d1fac36da..ce3f9d0b3229527eb9887ad3d8d1e3b8d47c4a7b 100644 (file)
@@ -42,7 +42,7 @@ class Journal < ActiveRecord::Base
                                             " (#{JournalDetail.table_name}.prop_key = 'status_id' OR #{Journal.table_name}.notes <> '')").distinct
 
   before_create :split_private_notes
-  after_commit :send_notification, :on => :create
+  after_create_commit :send_notification
 
   scope :visible, lambda {|*args|
     user = args.shift || User.current
index ecc131051c1a64ee2cd7f68f19920c8b6dc76475..bd0d81ba9aac1642efbd1401b268a0b0af93ef3d 100755 (executable)
@@ -130,7 +130,7 @@ class MailHandler < ActionMailer::Base
           end
           add_user_to_group(handler_options[:default_group])
           unless handler_options[:no_account_notice]
-            ::Mailer.account_information(@user, @user.password).deliver
+            ::Mailer.deliver_account_information(@user, @user.password)
           end
         else
           if logger
index 8f4262950fe01ace730eaa341a18b565576d9925..50b6ca744b5d7f7468a61ca07f09f7c816e06947 100644 (file)
@@ -26,94 +26,12 @@ class Mailer < ActionMailer::Base
   include Redmine::I18n
   include Roadie::Rails::Automatic
 
-  # This class wraps multiple generated `Mail::Message` objects and allows to
-  # deliver them all at once. It is usually used to handle multiple mails for
-  # different receivers created by a single mail event. The wrapped mails can
-  # then be delivered in one go.
-  #
-  # The public interface of the class resembles a single mail message. You can
-  # directly use any of the deliver_* methods to send the contained messages
-  # now or later.
-  class MultiMessage
-    attr_reader :mails
-
-    # @param mails [Array<Mail, Proc>] an Array of mails or Procs which create
-    #   mail objects and allow to call a method on it.
-    def initialize(action, *args)
-      @action = action
-      @args = args
-
-      @mails = []
-    end
-
-    def for(users)
-      Array.wrap(users).each do |user|
-        @mails << ActionMailer::MessageDelivery.new(Mailer, @action, user, *@args)
-      end
-      self
-    end
-
-    def deliver_later(options = {})
-      enqueue_delivery :deliver_now, options
-    end
-
-    def deliver_later!(options = {})
-      enqueue_delivery :deliver_now!, options
-    end
-
-    def processed?
-      @mails.any?(&:processed?)
-    end
-
-    # @return [Object] the delivery method of the first mail.
-    #   Usually, this is the very same value for all mails and matches the
-    #   default value of the Mailer class
-    def delivery_method
-      (@mails.first || ActionMailer::Base::NullMail.new).delivery_method
-    end
-
-    # @return [ActionMailer::Base] the delivery handler of the first mail. This
-    #   is always the `Mailer` class.
-    def delivery_handler
-      (@mails.first || ActionMailer::Base::NullMail.new).delivery_handler
-    end
-
-    private
-
-    def method_missing(method, *args, &block)
-      if method =~ /\Adeliver([_!?]|\z)/
-        @mails.each do |mail|
-          mail.public_send(method, *args, &block)
-        end
-      else
-        super
-      end
-    end
-
-    def respond_to_missing(method, *args)
-      method =~ /\Adeliver([_!?]|\z)/ || method == 'processed?' || super
-    end
-
-    # This method is slightly adapted from ActionMailer::MessageDelivery
-    def enqueue_delivery(delivery_method, options = {})
-      if processed?
-        ::Kernel.raise "You've accessed the message before asking to " \
-          "deliver it later, so you may have made local changes that would " \
-          "be silently lost if we enqueued a job to deliver it. Why? Only " \
-          "the mailer method *arguments* are passed with the delivery job! " \
-          "Do not access the message in any way if you mean to deliver it " \
-          "later. Workarounds: 1. don't touch the message before calling " \
-          "#deliver_later, 2. only touch the message *within your mailer " \
-          "method*, or 3. use a custom Active Job instead of #deliver_later."
-      else
-        args = 'Mailer', @action.to_s, delivery_method.to_s, *@args
-        ::ActionMailer::DeliveryJob.set(options).perform_later(*args)
-      end
-    end
-  end
-
+  # Overrides ActionMailer::Base#process in order to set the recipient as the current user
+  # and his language as the default locale. 
+  # The first argument of all actions of this Mailer must be a User (the recipient),
+  # otherwise an ArgumentError is raised.
   def process(action, *args)
-    user = args.shift
+    user = args.first
     raise ArgumentError, "First argument has to be a user, was #{user.inspect}" unless user.is_a?(User)
 
     initial_user = User.current
@@ -132,7 +50,8 @@ class Mailer < ActionMailer::Base
     end
   end
 
-
+  # Default URL options for generating URLs in emails based on host_name and protocol
+  # defined in application settings.
   def self.default_url_options
     options = {:protocol => Setting.protocol}
     if Setting.host_name.to_s =~ /\A(https?\:\/\/)?(.+?)(\:(\d+))?(\/.+)?\z/i
@@ -146,11 +65,8 @@ class Mailer < ActionMailer::Base
     options
   end
 
-  # Builds a mail for notifying the current user about a new issue
-  #
-  # Example:
-  #   issue_add(issue) => Mail::Message object
-  def issue_add(issue)
+  # Builds a mail for notifying user about a new issue
+  def issue_add(user, issue)
     redmine_headers 'Project' => issue.project.identifier,
                     'Issue-Id' => issue.id,
                     'Issue-Author' => issue.author.login
@@ -159,34 +75,25 @@ class Mailer < ActionMailer::Base
     references issue
     @author = issue.author
     @issue = issue
-    @users = [User.current]
+    @user = user
     @issue_url = url_for(:controller => 'issues', :action => 'show', :id => issue)
-    mail :to => User.current,
+    mail :to => user,
       :subject => "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] (#{issue.status.name}) #{issue.subject}"
   end
 
-  # Notifies users about a new issue
-  #
-  # Example:
-  #   Mailer.issue_add(journal).deliver => sends emails to the project's recipients
-  def self.issue_add(issue)
-    users = issue.notified_users | issue.notified_watchers
-    MultiMessage.new(:issue_add, issue).for(users)
-  end
-
-  # Notifies users about a new issue
+  # Notifies users about a new issue.
   #
   # Example:
-  #   Mailer.deliver_issue_add(issue) => sends emails to the project's recipients
+  #   Mailer.deliver_issue_add(issue)
   def self.deliver_issue_add(issue)
-    issue_add(issue).deliver
+    users = issue.notified_users | issue.notified_watchers
+    users.each do |user|
+      issue_add(user, issue).deliver
+    end
   end
 
-  # Builds a mail for notifying the current user about an issue update
-  #
-  # Example:
-  #   issue_edit(journal) => Mail::Message object
-  def issue_edit(journal)
+  # Builds a mail for notifying user about an issue update
+  def issue_edit(user, journal)
     issue = journal.journalized
     redmine_headers 'Project' => issue.project.identifier,
                     'Issue-Id' => issue.id,
@@ -199,89 +106,52 @@ class Mailer < ActionMailer::Base
     s << "(#{issue.status.name}) " if journal.new_value_for('status_id')
     s << issue.subject
     @issue = issue
-    @users = [User.current]
+    @user = user
     @journal = journal
     @journal_details = journal.visible_details
     @issue_url = url_for(:controller => 'issues', :action => 'show', :id => issue, :anchor => "change-#{journal.id}")
 
-    mail :to => User.current,
+    mail :to => user,
       :subject => s
   end
 
-  # Build a MultiMessage to notify users about an issue update
+  # Notifies users about an issue update.
   #
   # Example:
-  #   Mailer.issue_edit(journal).deliver => sends emails to the project's recipients
-  def self.issue_edit(journal)
-    users  = journal.notified_users
-    users |= journal.notified_watchers
+  #   Mailer.deliver_issue_edit(journal)
+  def self.deliver_issue_edit(journal)
+    users  = journal.notified_users | journal.notified_watchers
     users.select! do |user|
       journal.notes? || journal.visible_details(user).any?
     end
-    MultiMessage.new(:issue_edit, journal).for(users)
-  end
-
-  # Notifies users about an issue update
-  #
-  # Example:
-  #   Mailer.deliver_issue_edit(journal) => sends emails to the project's recipients
-  def self.deliver_issue_edit(journal)
-    issue_edit(journal).deliver
-  end
-
-  # Builds a Mail::Message object used to send en email reminder to the current
-  # user about their due issues.
-  #
-  # Example:
-  #   reminder(issues, days) => Mail::Message object
-  def reminder(issues, days)
-    @issues = issues
-    @days = days
-    @issues_url = url_for(:controller => 'issues', :action => 'index',
-                                :set_filter => 1, :assigned_to_id => User.current.id,
-                                :sort => 'due_date:asc')
-    mail :to => User.current,
-      :subject => l(:mail_subject_reminder, :count => issues.size, :days => days)
-  end
-
-  # Builds a Mail::Message object used to email the given user about their due
-  # issues
-  #
-  # Example:
-  #   Mailer.reminder(user, issues, days, author).deliver => sends an email to the user
-  def self.reminder(user, issues, days)
-    MultiMessage.new(:reminder, issues, days).for(user)
+    users.each do |user|
+      issue_edit(user, journal).deliver
+    end
   end
 
-  # Builds a Mail::Message object used to email the current user that a document
-  # was added.
-  #
-  # Example:
-  #   document_added(document, author) => Mail::Message object
-  def document_added(document, author)
+  # Builds a mail to user about a new document.
+  def document_added(user, document, author)
     redmine_headers 'Project' => document.project.identifier
     @author = author
     @document = document
     @document_url = url_for(:controller => 'documents', :action => 'show', :id => document)
-    mail :to => User.current,
+    mail :to => user,
       :subject => "[#{document.project.name}] #{l(:label_document_new)}: #{document.title}"
   end
 
-  # Build a MultiMessage to notify users about an added document.
+  # Notifies users that document was created by author
   #
   # Example:
-  #   Mailer.document_added(document).deliver => sends emails to the document's project recipients
-  def self.document_added(document)
+  #   Mailer.deliver_document_added(document, author)
+  def self.deliver_document_added(document, author)
     users = document.notified_users
-    MultiMessage.new(:document_added, document, User.current).for(users)
+    users.each do |user|
+      document_added(user, document, author).deliver_later
+    end
   end
 
-  # Builds a Mail::Message object used to email the current user when
-  # attachements are added.
-  #
-  # Example:
-  #   attachments_added(attachments) => Mail::Message object
-  def attachments_added(attachments)
+  # Builds a mail to user about new attachements.
+  def attachments_added(user, attachments)
     container = attachments.first.container
     added_to = ''
     added_to_url = ''
@@ -301,15 +171,15 @@ class Mailer < ActionMailer::Base
     @attachments = attachments
     @added_to = added_to
     @added_to_url = added_to_url
-    mail :to => User.current,
+    mail :to => user,
       :subject => "[#{container.project.name}] #{l(:label_attachment_new)}"
   end
 
-  # Build a MultiMessage to notify users about an added attachment
+  # Notifies users about new attachments
   #
   # Example:
-  #   Mailer.attachments_added(attachments).deliver => sends emails to the project's recipients
-  def self.attachments_added(attachments)
+  #   Mailer.deliver_attachments_added(attachments)
+  def self.deliver_attachments_added(attachments)
     container = attachments.first.container
     case container.class.name
     when 'Project', 'Version'
@@ -318,40 +188,36 @@ class Mailer < ActionMailer::Base
       users = container.notified_users
     end
 
-    MultiMessage.new(:attachments_added, attachments).for(users)
+    users.each do |user|
+      attachments_added(user, attachments).deliver_later
+    end
   end
 
-  # Builds a Mail::Message object used to email the current user when a news
-  # item is added.
-  #
-  # Example:
-  #   news_added(news) => Mail::Message object
-  def news_added(news)
+  # Builds a mail to user about a new news.
+  def news_added(user, news)
     redmine_headers 'Project' => news.project.identifier
     @author = news.author
     message_id news
     references news
     @news = news
     @news_url = url_for(:controller => 'news', :action => 'show', :id => news)
-    mail :to => User.current,
+    mail :to => user,
       :subject => "[#{news.project.name}] #{l(:label_news)}: #{news.title}"
   end
 
-  # Build a MultiMessage to notify users about a new news item
+  # Notifies users about new news
   #
   # Example:
-  #   Mailer.news_added(news).deliver => sends emails to the news' project recipients
-  def self.news_added(news)
+  #   Mailer.deliver_news_added(news)
+  def self.deliver_news_added(news)
     users = news.notified_users | news.notified_watchers_for_added_news
-    MultiMessage.new(:news_added, news).for(users)
+    users.each do |user|
+      news_added(user, news).deliver_later
+    end
   end
 
-  # Builds a Mail::Message object used to email the current user when a news
-  # comment is added.
-  #
-  # Example:
-  #   news_comment_added(comment) => Mail::Message object
-  def news_comment_added(comment)
+  # Builds a mail to user about a new news comment.
+  def news_comment_added(user, comment)
     news = comment.commented
     redmine_headers 'Project' => news.project.identifier
     @author = comment.author
@@ -360,27 +226,24 @@ class Mailer < ActionMailer::Base
     @news = news
     @comment = comment
     @news_url = url_for(:controller => 'news', :action => 'show', :id => news)
-    mail :to => User.current,
+    mail :to => user,
      :subject => "Re: [#{news.project.name}] #{l(:label_news)}: #{news.title}"
   end
 
-  # Build a MultiMessage to notify users about a new news comment
+  # Notifies users about a new comment on a news
   #
   # Example:
-  #   Mailer.news_comment_added(comment).deliver => sends emails to the news' project recipients
-  def self.news_comment_added(comment)
+  #   Mailer.deliver_news_comment_added(comment)
+  def self.deliver_news_comment_added(comment)
     news = comment.commented
     users = news.notified_users | news.notified_watchers
-
-    MultiMessage.new(:news_comment_added, comment).for(users)
+    users.each do |user|
+      news_comment_added(user, comment).deliver_later
+    end
   end
 
-  # Builds a Mail::Message object used to email the current user that the
-  # specified message was posted.
-  #
-  # Example:
-  #   message_posted(message) => Mail::Message object
-  def message_posted(message)
+  # Builds a mail to user about a new message.
+  def message_posted(user, message)
     redmine_headers 'Project' => message.project.identifier,
                     'Topic-Id' => (message.parent_id || message.id)
     @author = message.author
@@ -388,28 +251,26 @@ class Mailer < ActionMailer::Base
     references message.root
     @message = message
     @message_url = url_for(message.event_url)
-    mail :to => User.current,
+    mail :to => user,
       :subject => "[#{message.board.project.name} - #{message.board.name} - msg#{message.root.id}] #{message.subject}"
   end
 
-  # Build a MultiMessage to notify users about a new forum message
+  # Notifies users about a new forum message.
   #
   # Example:
-  #   Mailer.message_posted(message).deliver => sends emails to the recipients
-  def self.message_posted(message)
+  #   Mailer.deliver_message_posted(message)
+  def self.deliver_message_posted(message)
     users  = message.notified_users
     users |= message.root.notified_watchers
     users |= message.board.notified_watchers
 
-    MultiMessage.new(:message_posted, message).for(users)
+    users.each do |user|
+      message_posted(user, message).deliver_later
+    end
   end
 
-  # Builds a Mail::Message object used to email the current user that the
-  # specified wiki content was added.
-  #
-  # Example:
-  #   wiki_content_added(wiki_content) => Mail::Message object
-  def wiki_content_added(wiki_content)
+  # Builds a mail to user about a new wiki content.
+  def wiki_content_added(user, wiki_content)
     redmine_headers 'Project' => wiki_content.project.identifier,
                     'Wiki-Page-Id' => wiki_content.page.id
     @author = wiki_content.author
@@ -418,25 +279,23 @@ class Mailer < ActionMailer::Base
     @wiki_content_url = url_for(:controller => 'wiki', :action => 'show',
                                       :project_id => wiki_content.project,
                                       :id => wiki_content.page.title)
-    mail :to => User.current,
+    mail :to => user,
       :subject => "[#{wiki_content.project.name}] #{l(:mail_subject_wiki_content_added, :id => wiki_content.page.pretty_title)}"
   end
 
-  # Build a MultiMessage to notify users about added wiki content
+  # Notifies users about a new wiki content (wiki page added).
   #
   # Example:
-  #   Mailer.wiki_content_added(wiki_content).deliver => send emails to the project's recipients
-  def self.wiki_content_added(wiki_content)
+  #   Mailer.deliver_wiki_content_added(wiki_content)
+  def self.deliver_wiki_content_added(wiki_content)
     users = wiki_content.notified_users | wiki_content.page.wiki.notified_watchers
-    MultiMessage.new(:wiki_content_added, wiki_content).for(users)
+    users.each do |user|
+      wiki_content_added(user, wiki_content).deliver_later
+    end
   end
 
-  # Builds a Mail::Message object used to email the current user about an update
-  # of the specified wiki content.
-  #
-  # Example:
-  #   wiki_content_updated(wiki_content) => Mail::Message object
-  def wiki_content_updated(wiki_content)
+  # Builds a mail to user about an update of the specified wiki content.
+  def wiki_content_updated(user, wiki_content)
     redmine_headers 'Project' => wiki_content.project.identifier,
                     'Wiki-Page-Id' => wiki_content.page.id
     @author = wiki_content.author
@@ -448,154 +307,141 @@ class Mailer < ActionMailer::Base
     @wiki_diff_url = url_for(:controller => 'wiki', :action => 'diff',
                                    :project_id => wiki_content.project, :id => wiki_content.page.title,
                                    :version => wiki_content.version)
-    mail :to => User.current,
+    mail :to => user,
       :subject => "[#{wiki_content.project.name}] #{l(:mail_subject_wiki_content_updated, :id => wiki_content.page.pretty_title)}"
   end
 
-  # Build a MultiMessage to notify users about the update of the specified wiki content
+  # Notifies users about the update of the specified wiki content
   #
   # Example:
-  #   Mailer.wiki_content_updated(wiki_content).deliver => sends an email to the project's recipients
-  def self.wiki_content_updated(wiki_content)
+  #   Mailer.deliver_wiki_content_updated(wiki_content)
+  def self.deliver_wiki_content_updated(wiki_content)
     users  = wiki_content.notified_users
     users |= wiki_content.page.notified_watchers
     users |= wiki_content.page.wiki.notified_watchers
 
-    MultiMessage.new(:wiki_content_updated, wiki_content).for(users)
+    users.each do |user|
+      wiki_content_updated(user, wiki_content).deliver_later
+    end
   end
 
-  # Builds a Mail::Message object used to email the current user their account information.
-  #
-  # Example:
-  #   account_information(password) => Mail::Message object
-  def account_information(password)
-    @user = User.current
+  # Builds a mail to user about his account information.
+  def account_information(user, password)
+    @user = user
     @password = password
     @login_url = url_for(:controller => 'account', :action => 'login')
-    mail :to => User.current.mail,
+    mail :to => user.mail,
       :subject => l(:mail_subject_register, Setting.app_title)
   end
 
-  # Build a MultiMessage to mail a user their account information
-  #
-  # Example:
-  #   Mailer.account_information(user, password).deliver => sends account information to the user
-  def self.account_information(user, password)
-    MultiMessage.new(:account_information, password).for(user)
+  # Notifies user about his account information.
+  def self.deliver_account_information(user, password)
+    account_information(user, password).deliver_later
   end
 
-  # Builds a Mail::Message object used to email the current user about an account activation request.
-  #
-  # Example:
-  #   account_activation_request(user) => Mail::Message object
-  def account_activation_request(user)
-    @user = user
+  # Builds a mail to user about an account activation request.
+  def account_activation_request(user, new_user)
+    @new_user = new_user
     @url = url_for(:controller => 'users', :action => 'index',
                          :status => User::STATUS_REGISTERED,
                          :sort_key => 'created_on', :sort_order => 'desc')
-    mail :to => User.current,
+    mail :to => user,
       :subject => l(:mail_subject_account_activation_request, Setting.app_title)
   end
 
-  # Build a MultiMessage to email all active administrators of an account activation request.
+  # Notifies admin users that an account activation request needs
+  # their approval.
   #
-  # Example:
-  #   Mailer.account_activation_request(user).deliver => sends an email to all active administrators
-  def self.account_activation_request(user)
+  # Exemple:
+  #   Mailer.deliver_account_activation_request(new_user)
+  def self.deliver_account_activation_request(new_user)
     # Send the email to all active administrators
     users = User.active.where(:admin => true)
-    MultiMessage.new(:account_activation_request, user).for(users)
+    users.each do |user|
+      account_activation_request(user, new_user).deliver_later
+    end
   end
 
-  # Builds a Mail::Message object used to email the account of the current user
-  # was activated by an administrator.
-  #
-  # Example:
-  #   account_activated => Mail::Message object
-  def account_activated
-    @user = User.current
+  # Builds a mail to notify user that his account was activated.
+  def account_activated(user)
+    @user = user
     @login_url = url_for(:controller => 'account', :action => 'login')
-    mail :to => User.current.mail,
+    mail :to => user.mail,
       :subject => l(:mail_subject_register, Setting.app_title)
   end
 
-  # Build a MultiMessage to email the specified user that their account was
-  # activated by an administrator.
+  # Notifies user that his account was activated.
   #
-  # Example:
-  #   Mailer.account_activated(user).deliver => sends an email to the registered user
-  def self.account_activated(user)
-    MultiMessage.new(:account_activated).for(user)
+  # Exemple:
+  #   Mailer.deliver_account_activated(user)
+  def self.deliver_account_activated(user)
+    account_activated(user).deliver_later
   end
 
-  # Builds a Mail::Message object used to email the lost password token to the
-  # token's user (or a different recipient).
-  #
-  # Example:
-  #   lost_password(token) => Mail::Message object
-  def lost_password(token, recipient=nil)
-    recipient ||= token.user.mail
+  # Builds a mail with the password recovery link.
+  def lost_password(user, token, recipient=nil)
+    recipient ||= user.mail
     @token = token
     @url = url_for(:controller => 'account', :action => 'lost_password', :token => token.value)
     mail :to => recipient,
       :subject => l(:mail_subject_lost_password, Setting.app_title)
   end
 
-  # Build a MultiMessage to email the token's user (or a different recipient)
-  # the lost password token for the token's user.
+  # Sends an email to user with a password recovery link.
+  # The email will be sent to the email address specifiedby recipient if provided.
   #
-  # Example:
-  #   Mailer.lost_password(token).deliver => sends an email to the user
-  def self.lost_password(token, recipient=nil)
-    MultiMessage.new(:lost_password, token, recipient).for(token.user)
+  # Exemple:
+  #   Mailer.deliver_account_activated(user, token)
+  #   Mailer.deliver_account_activated(user, token, 'foo@example.net')
+  def self.deliver_lost_password(user, token, recipient=nil)
+    lost_password(user, token, recipient).deliver_later
   end
 
-  # Notifies user that his password was updated
-  def self.password_updated(user, options={})
+  # Notifies user that his password was updated by sender.
+  #
+  # Exemple:
+  #   Mailer.deliver_password_updated(user, sender)
+  def self.deliver_password_updated(user, sender)
     # Don't send a notification to the dummy email address when changing the password
     # of the default admin account which is required after the first login
     # TODO: maybe not the best way to handle this
     return if user.admin? && user.login == 'admin' && user.mail == 'admin@example.net'
 
-    security_notification(user,
+    deliver_security_notification(user,
+      sender,
       message: :mail_body_password_updated,
       title: :button_change_password,
-      remote_ip: options[:remote_ip],
-      originator: user,
       url: {controller: 'my', action: 'password'}
-    ).deliver
+    )
   end
 
-  # Builds a Mail::Message object used to email the user activation link to the
-  # token's user.
-  #
-  # Example:
-  #   register(token) => Mail::Message object
-  def register(token)
+  # Builds a mail to user with his account activation link.
+  def register(user, token)
     @token = token
     @url = url_for(:controller => 'account', :action => 'activate', :token => token.value)
-    mail :to => token.user.mail,
+    mail :to => user.mail,
       :subject => l(:mail_subject_register, Setting.app_title)
   end
 
-  # Build a MultiMessage to email the user activation link to the token's user.
+  # Sends an mail to user with his account activation link.
   #
-  # Example:
-  #   Mailer.register(token).deliver => sends an email to the token's user
-  def self.register(token)
-    MultiMessage.new(:register, token).for(token.user)
+  # Exemple:
+  #   Mailer.deliver_register(user, token)
+  def self.deliver_register(user, token)
+    register(user, token).deliver_later
   end
 
-  # Build a Mail::Message object to email the current user and the additional
-  # recipients given in options[:recipients] about a security related event.
+  # Build a mail to user and the additional recipients given in
+  # options[:recipients] about a security related event made by sender.
   #
   # Example:
-  #   security_notification(users,
+  #   security_notification(user,
+  #     sender,
   #     message: :mail_body_security_notification_add,
   #     field: :field_mail,
   #     value: address
   #   ) => Mail::Message object
-  def security_notification(sender, options={})
+  def security_notification(user, sender, options={})
     @sender = sender
     redmine_headers 'Sender' => sender.login
     @message = l(options[:message],
@@ -603,79 +449,100 @@ class Mailer < ActionMailer::Base
       value: options[:value]
     )
     @title = options[:title] && l(options[:title])
-    @originator = options[:originator] || sender
-    @remote_ip = options[:remote_ip] || @originator.remote_ip
+    @remote_ip = options[:remote_ip] || @sender.remote_ip
     @url = options[:url] && (options[:url].is_a?(Hash) ? url_for(options[:url]) : options[:url])
-    redmine_headers 'Sender' => @originator.login
     redmine_headers 'Url' => @url
-    mail :to => [User.current, *options[:recipients]].uniq,
+    mail :to => [user, *options[:recipients]].uniq,
       :subject => "[#{Setting.app_title}] #{l(:mail_subject_security_notification)}"
   end
 
-  # Build a MultiMessage to email the given users about a security related event.
+  # Notifies the given users about a security related event made by sender.
   #
   # You can specify additional recipients in options[:recipients]. These will be
   # added to all generated mails for all given users. Usually, you'll want to
   # give only a single user when setting the additional recipients.
   #
   # Example:
-  #   Mailer.security_notification(users,
+  #   Mailer.deliver_security_notification(users,
+  #     sender,
   #     message: :mail_body_security_notification_add,
   #     field: :field_mail,
   #     value: address
-  #   ).deliver => sends a security notification to the given user(s)
-  def self.security_notification(users, options={})
-    sender = User.current
-    MultiMessage.new(:security_notification, sender, options).for(users)
+  #   )
+  def self.deliver_security_notification(users, sender, options={})
+    # Symbols cannot be serialized:
+    # ActiveJob::SerializationError: Unsupported argument type: Symbol
+    options = options.transform_values {|v| v.is_a?(Symbol) ? v.to_s : v }
+    # sender's remote_ip would be lost on serialization/deserialization
+    # we have to pass it with options
+    options[:remote_ip] ||= sender.remote_ip
+
+    Array.wrap(users).each do |user|
+      security_notification(user, sender, options).deliver_later
+    end
   end
 
-  # Build a Mail::Message object to email the current user about an updated
-  # setting.
-  #
-  # Example:
-  #   settings_updated(sender, [:host_name]) => Mail::Message object
-  def settings_updated(sender, changes)
+  # Build a mail to user about application settings changes made by sender.
+  def settings_updated(user, sender, changes, options={})
     @sender = sender
     redmine_headers 'Sender' => sender.login
     @changes = changes
+    @remote_ip = options[:remote_ip] || @sender.remote_ip
     @url = url_for(controller: 'settings', action: 'index')
-    mail :to => User.current,
+    mail :to => user,
       :subject => "[#{Setting.app_title}] #{l(:mail_subject_security_notification)}"
   end
 
-  # Build a MultiMessage to email the given users about an update of a setting.
+  # Notifies admins about application settings changes made by sender, where
+  # changes is an array of settings names.
   #
-  # Example:
-  #   Mailer.settings_updated(users, [:host_name]).deliver => sends emails to the given user(s) about the update
-  def self.settings_updated(users, changes)
-    sender = User.current
-    MultiMessage.new(:settings_updated, sender, changes).for(users)
-  end
-  
-  # Notifies admins about settings changes
-  def self.security_settings_updated(changes)
+  # Exemple:
+  #   Mailer.deliver_settings_updated(sender, [:login_required, :self_registration])
+  def self.deliver_settings_updated(sender, changes, options={})
     return unless changes.present?
 
+    # Symbols cannot be serialized:
+    # ActiveJob::SerializationError: Unsupported argument type: Symbol
+    changes = changes.map(&:to_s)
+    # sender's remote_ip would be lost on serialization/deserialization
+    # we have to pass it with options
+    options[:remote_ip] ||= sender.remote_ip
+
     users = User.active.where(admin: true).to_a
-    settings_updated(users, changes).deliver
+    users.each do |user|
+      settings_updated(user, sender, changes, options).deliver_later
+    end
   end
 
-  # Build a Mail::Message object with a test email for the current user
-  #
-  # Example:
-  #   test_email => Mail::Message object
+  # Build a test email to user.
   def test_email(user)
     @url = url_for(:controller => 'welcome')
-    mail :to => user.mail,
+    mail :to => user,
       :subject => 'Redmine test'
   end
 
-  # Build a MultiMessage to send a test email the given user
+  # Send a test email to user. Will raise error that may occur during delivery.
   #
-  # Example:
-  #   Mailer.test_email(user).deliver => send an email to the given user
-  def self.test_email(user)
-    MultiMessage.new(:test_email, user).for(user)
+  # Exemple:
+  #   Mailer.deliver_test_email(user)
+  def self.deliver_test_email(user)
+    raise_delivery_errors_was = self.raise_delivery_errors
+    self.raise_delivery_errors = true
+    # Email must be delivered synchronously so we can catch errors
+    test_email(user).deliver_now
+  ensure
+    self.raise_delivery_errors = raise_delivery_errors_was
+  end
+
+  # Builds a reminder mail to user about issues that are due in the next days.
+  def reminder(user, issues, days)
+    @issues = issues
+    @days = days
+    @issues_url = url_for(:controller => 'issues', :action => 'index',
+                                :set_filter => 1, :assigned_to_id => user.id,
+                                :sort => 'due_date:asc')
+    mail :to => user,
+      :subject => l(:mail_subject_reminder, :count => issues.size, :days => days)
   end
 
   # Sends reminders to issue assignees
@@ -731,17 +598,19 @@ class Mailer < ActionMailer::Base
     ActionMailer::Base.perform_deliveries = was_enabled
   end
 
-  # Sends emails synchronously in the given block
+  # Execute the given block with inline sending of emails if the default Async
+  # queue is used for the mailer. See the Rails guide:
+  # Using the asynchronous queue from a Rake task will generally not work because
+  # Rake will likely end, causing the in-process thread pool to be deleted, before
+  # any/all of the .deliver_later emails are processed
   def self.with_synched_deliveries(&block)
-    saved_method = ActionMailer::Base.delivery_method
-    if m = saved_method.to_s.match(%r{^async_(.+)$})
-      synched_method = m[1]
-      ActionMailer::Base.delivery_method = synched_method.to_sym
-      ActionMailer::Base.send "#{synched_method}_settings=", ActionMailer::Base.send("async_#{synched_method}_settings")
+    adapter = ActionMailer::DeliveryJob.queue_adapter
+    if adapter.is_a?(ActiveJob::QueueAdapters::AsyncAdapter)
+      ActionMailer::DeliveryJob.queue_adapter = ActiveJob::QueueAdapters::InlineAdapter.new
     end
     yield
   ensure
-    ActionMailer::Base.delivery_method = saved_method
+    ActionMailer::DeliveryJob.queue_adapter = adapter
   end
 
   def mail(headers={}, &block)
@@ -816,8 +685,6 @@ class Mailer < ActionMailer::Base
     if m = method.to_s.match(%r{^deliver_(.+)$})
       ActiveSupport::Deprecation.warn "Mailer.deliver_#{m[1]}(*args) is deprecated. Use Mailer.#{m[1]}(*args).deliver instead."
       send(m[1], *args).deliver
-    elsif action_methods.include?(method.to_s)
-      MultiMessage.new(method, *args).for(User.current)
     else
       super
     end
index 7580952896a31c3c9fffad3d94288be87960e8a6..83e8cd2c02bdea29e58c55fc0ff8e607280bd67d 100644 (file)
@@ -45,7 +45,7 @@ class Message < ActiveRecord::Base
   after_create :add_author_as_watcher, :reset_counters!
   after_update :update_messages_board
   after_destroy :reset_counters!
-  after_create :send_notification
+  after_create_commit :send_notification
 
   scope :visible, lambda {|*args|
     joins(:board => :project).
@@ -114,7 +114,7 @@ class Message < ActiveRecord::Base
 
   def send_notification
     if Setting.notified_events.include?('message_posted')
-      Mailer.message_posted(self).deliver
+      Mailer.deliver_message_posted(self)
     end
   end
 end
index 8243bc3015cdcf49af6bd688a8011a52ed6d265a..90df77e3262033bbcc27956da1155091dd431ed6 100644 (file)
@@ -35,7 +35,7 @@ class News < ActiveRecord::Base
   acts_as_watchable
 
   after_create :add_author_as_watcher
-  after_create :send_notification
+  after_create_commit :send_notification
 
   scope :visible, lambda {|*args|
     joins(:project).
@@ -91,7 +91,7 @@ class News < ActiveRecord::Base
 
   def send_notification
     if Setting.notified_events.include?('news_added')
-      Mailer.news_added(self).deliver
+      Mailer.deliver_news_added(self)
     end
   end
 end
index fa04f1c1af8b0d1a84ce054b01ec98ab6398a4b2..e59c48c86c802e811eb1d5bd9104e341032dd0e2 100644 (file)
@@ -135,7 +135,7 @@ class Setting < ActiveRecord::Base
       end
     end
     if changes.any?
-      Mailer.security_settings_updated(changes)
+      Mailer.deliver_settings_updated(User.current, changes)
     end
     nil
   end
index c5061225c102a3ad0afe863f1c16106ff18f3433..0f82a680681fcd54eb3d17e8c2df525c485888c5 100644 (file)
@@ -920,7 +920,7 @@ class User < Principal
 
     if deliver
       users = User.active.where(admin: true).to_a
-      Mailer.security_notification(users, options).deliver
+      Mailer.deliver_security_notification(users, User.current, options)
     end
   end
 end
index 3360b71cac7de5366cfc6d520274ced1513ac37e..32e0c8450817b6470202bc8b5d62ed23aa1c435e 100644 (file)
@@ -26,7 +26,8 @@ class WikiContent < ActiveRecord::Base
   validates_length_of :comments, :maximum => 1024, :allow_nil => true
 
   after_save :create_version
-  after_save :send_notification
+  after_create_commit :send_notification_create
+  after_update_commit :send_notification_update
 
   scope :without_text, lambda {select(:id, :page_id, :version, :updated_on)}
 
@@ -82,16 +83,17 @@ class WikiContent < ActiveRecord::Base
     versions << WikiContentVersion.new(attributes.except("id"))
   end
 
-  def send_notification
-    # new_record? returns false in after_save callbacks
-    if saved_change_to_id?
-      if Setting.notified_events.include?('wiki_content_added')
-        Mailer.wiki_content_added(self).deliver
-      end
-    elsif saved_change_to_text?
-      if Setting.notified_events.include?('wiki_content_updated')
-        Mailer.wiki_content_updated(self).deliver
-      end
+  # Notifies users that a wiki page was created
+  def send_notification_create
+    if Setting.notified_events.include?('wiki_content_added')
+      Mailer.deliver_wiki_content_added(self)
+    end
+  end
+
+  # Notifies users that a wiki page was updated
+  def send_notification_update
+    if Setting.notified_events.include?('wiki_content_updated') && saved_change_to_text?
+      Mailer.deliver_wiki_content_updated(self)
     end
   end
 
index 9461d849060fceef94119319c1f669a58c2b8fe4..58287c658749236a62258619c01b862348064daa 100644 (file)
@@ -1,6 +1,6 @@
 <h1><%= link_to("#{issue.tracker.name} ##{issue.id}: #{issue.subject}", issue_url) %></h1>
 
-<%= render_email_issue_attributes(issue, users.first, true) %>
+<%= render_email_issue_attributes(issue, user, true) %>
 
 <%= textilizable(issue, :description, :only_path => false) %>
 
index 0034c441ff16f0d04628954914f23d75e455a2e8..819aebad6c0fcacfb9ffdb05347c5ef2b4bc8932 100644 (file)
@@ -1,7 +1,7 @@
 <%= "#{issue.tracker.name} ##{issue.id}: #{issue.subject}" %>
 <%= issue_url %>
 
-<%= render_email_issue_attributes(issue, users.first) %>
+<%= render_email_issue_attributes(issue, user) %>
 ----------------------------------------
 <%= issue.description %>
 
index b19cf32198ccea23547534d7978da461313aafa0..5f95f8231ce3d824a74a5a334edf255c2b683aec 100644 (file)
@@ -1,2 +1,2 @@
-<p><%= l(:mail_body_account_activation_request, h(@user.login)) %></p>
+<p><%= l(:mail_body_account_activation_request, h(@new_user.login)) %></p>
 <p><%= link_to @url, @url %></p>
index f431e22d3645933f2ac1f744027b2e2ec8125e0b..b35016b0304161aa182047582c9270bac4738634 100644 (file)
@@ -1,2 +1,2 @@
-<%= l(:mail_body_account_activation_request, @user.login) %>
+<%= l(:mail_body_account_activation_request, @new_user.login) %>
 <%= @url %>
index 14292be96525e3a96dbe59ed880897a9f733157f..7bb6115b59ccc187219e0d701a81e18819fbe8c1 100644 (file)
@@ -1,3 +1,3 @@
 <%= l(:text_issue_added, :id => link_to("##{@issue.id}", @issue_url), :author => h(@issue.author)).html_safe %>
 <hr />
-<%= render :partial => 'issue', :formats => [:html], :locals => { :issue => @issue, :users => @users, :issue_url => @issue_url } %>
+<%= render :partial => 'issue', :formats => [:html], :locals => { :issue => @issue, :user => @user, :issue_url => @issue_url } %>
index 6e3b427259c8414cfc1a70542cf08a44c9843183..b38111d54f25f884a31259a86d1721eb6a45bb4a 100644 (file)
@@ -1,4 +1,4 @@
 <%= l(:text_issue_added, :id => "##{@issue.id}", :author => @issue.author) %>
 
 ----------------------------------------
-<%= render :partial => 'issue', :formats => [:text], :locals => { :issue => @issue, :users => @users, :issue_url => @issue_url } %>
+<%= render :partial => 'issue', :formats => [:text], :locals => { :issue => @issue, :user => @user, :issue_url => @issue_url } %>
index b7fc18ed6123a9d3a7ac2501d55050e6d0287655..b5bdd88eed4d86b59ce81bb3a8f037df1ef9423c 100644 (file)
@@ -12,4 +12,4 @@
 
 <%= textilizable(@journal, :notes, :only_path => false) %>
 <hr />
-<%= render :partial => 'issue', :formats => [:html], :locals => { :issue => @issue, :users => @users, :issue_url => @issue_url } %>
+<%= render :partial => 'issue', :formats => [:html], :locals => { :issue => @issue, :user => @user, :issue_url => @issue_url } %>
index 173d2c4fe73796288f3550f8b34ed0eed29f777c..3542c1789622fc1380eaeb0c6ffad421a92476a5 100644 (file)
@@ -9,4 +9,4 @@
 
 <% end -%>
 ----------------------------------------
-<%= render :partial => 'issue', :formats => [:text], :locals => { :issue => @issue, :users => @users, :issue_url => @issue_url } %>
+<%= render :partial => 'issue', :formats => [:text], :locals => { :issue => @issue, :user => @user, :issue_url => @issue_url } %>
index 84394d1abc568f45fe2cb4db02d684f6e32016e3..762bbf09245be88c07ea6703e88a4c0cad1374ce 100644 (file)
@@ -7,6 +7,6 @@
 <%= content_tag :h1, @title -%>
 <% end %></p>
 
-<p><%= l(:field_user) %>: <strong><%= @originator.login %></strong><br/>
+<p><%= l(:field_user) %>: <strong><%= @sender.login %></strong><br/>
 <%= l(:field_remote_ip) %>: <strong><%= @remote_ip %></strong><br/>
 <%= l(:label_date) %>: <strong><%= format_time Time.now, true %></strong></p>
index 7ff3f3d57cd0f3e59775e36e346100f84cc6ab08..e5c143e940d6836685c8f0c48d516532affa86d3 100644 (file)
@@ -2,6 +2,6 @@
 
 <%= @url || @title %>
 
-<%= l(:field_user) %>: <%= @originator.login %>
+<%= l(:field_user) %>: <%= @sender.login %>
 <%= l(:field_remote_ip) %>: <%= @remote_ip %>
 <%= l(:label_date) %>: <%= format_time Time.now, true %>
index c50eb9ea1803f216b213bf06d3e3850adcfb50e6..999719ff7466dffda43f27b1e0261bdb6538c20c 100644 (file)
@@ -9,6 +9,6 @@
 <%= link_to @url, @url %>
 
 <p><%= l(:field_user) %>: <strong><%= @sender.login %></strong><br/>
-<%= l(:field_remote_ip) %>: <strong><%= @sender.remote_ip %></strong><br/>
+<%= l(:field_remote_ip) %>: <strong><%= @remote_ip %></strong><br/>
 <%= l(:label_date) %>: <strong><%= format_time Time.now, true %></strong></p>
 
index 9b0be909d49ddd43b237d92b20b8500d2c99f16a..8987fd0cb5b381f37b80ae0e6b58afea66966aac 100644 (file)
@@ -7,6 +7,6 @@
 <%= @url %>
 
 <%= l(:field_user) %>: <%= @sender.login %>
-<%= l(:field_remote_ip) %>: <%= @sender.remote_ip %>
+<%= l(:field_remote_ip) %>: <%= @remote_ip %>
 <%= l(:label_date) %>: <%= format_time Time.now, true %>
 
index b5698b43629c7d67588e0f178f3d22895357b665..8d73eba2c54abaaa50e6c1ec05cb7ea75c5a7769 100644 (file)
@@ -106,35 +106,18 @@ end
 require 'mail'
 
 module DeliveryMethods
-  class AsyncSMTP < ::Mail::SMTP
-    def deliver!(*args)
-      Thread.start do
-        super *args
-      end
-    end
-  end
-
-  class AsyncSendmail < ::Mail::Sendmail
-    def deliver!(*args)
-      Thread.start do
-        super *args
-      end
-    end
-  end
-
   class TmpFile
     def initialize(*args); end
 
     def deliver!(mail)
       dest_dir = File.join(Rails.root, 'tmp', 'emails')
       Dir.mkdir(dest_dir) unless File.directory?(dest_dir)
-      File.open(File.join(dest_dir, mail.message_id.gsub(/[<>]/, '') + '.eml'), 'wb') {|f| f.write(mail.encoded) }
+      filename = "#{Time.now.to_i}_#{mail.message_id.gsub(/[<>]/, '')}.eml"
+      File.open(File.join(dest_dir, filename), 'wb') {|f| f.write(mail.encoded) }
     end
   end
 end
 
-ActionMailer::Base.add_delivery_method :async_smtp, DeliveryMethods::AsyncSMTP
-ActionMailer::Base.add_delivery_method :async_sendmail, DeliveryMethods::AsyncSendmail
 ActionMailer::Base.add_delivery_method :tmp_file, DeliveryMethods::TmpFile
 
 # Changes how sent emails are logged
@@ -155,16 +138,6 @@ module ActionMailer
   end
 end
 
-# #deliver is deprecated in Rails 4.2
-# Prevents massive deprecation warnings
-module ActionMailer
-  class MessageDelivery < Delegator
-    def deliver
-      deliver_now
-    end
-  end
-end
-
 module ActionController
   module MimeResponds
     class Collector
index c9a9b6b8c2ad50ef5fe5cbd3ffc3908ae5b2fa5e..6ca65304cf9f3c6fc7f434d29a0a5a974daec700 100644 (file)
@@ -53,6 +53,13 @@ module Redmine
         if @config['email_delivery']
           ActionMailer::Base.perform_deliveries = true
           @config['email_delivery'].each do |k, v|
+            # Comprehensive error message for those who used async_smtp and async_sendmail
+            # delivery methods that are removed in Redmine 4.0.
+            if k == 'delivery_method' && v.to_s =~ /\Aasync_(.+)/
+              $stderr.puts "Redmine now uses ActiveJob to send emails asynchronously and the :#{v} delivery method is no longer available.\n" +
+                "Please update your config/configuration.yml to use :#$1 delivery method instead."
+              exit 1
+            end
             v.symbolize_keys! if v.respond_to?(:symbolize_keys!)
             ActionMailer::Base.send("#{k}=", v)
           end
index f7926a7aa6fdd21f85fe4708de1836a658a90fb5..62452e277c11b67814a6553fa2c3751610766fab 100644 (file)
@@ -13,7 +13,9 @@ module Redmine
           ["Ruby version", "#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]"],
           ["Rails version", Rails::VERSION::STRING],
           ["Environment", Rails.env],
-          ["Database adapter", ActiveRecord::Base.connection.adapter_name]
+          ["Database adapter", ActiveRecord::Base.connection.adapter_name],
+          ["Mailer queue", ActionMailer::DeliveryJob.queue_adapter.class.name],
+          ["Mailer delivery", ActionMailer::Base.delivery_method]
         ].map {|info| "  %-30s %s" % info}.join("\n") + "\n"
 
         s << "SCM:\n"
index 374232f650f546a27e16acfc7385bc517621607b..25d7d667b6387e9d8741dd1bfbbdf81359f98579 100644 (file)
@@ -161,11 +161,8 @@ END_DESC
       user = User.find_by_login(args[:login])
       abort l(:notice_email_error, "User #{args[:login]} not found") unless user && user.logged?
 
-      ActionMailer::Base.raise_delivery_errors = true
       begin
-        Mailer.with_synched_deliveries do
-          Mailer.test_email(user).deliver
-        end
+        Mailer.deliver_test_email(user)
         puts l(:notice_email_sent, user.mail)
       rescue Exception => e
         abort l(:notice_email_error, e.message)
index 8aa2cdbe8cfb34e5c83d7f93853fbf03ff6702d9..51219d65e25b0a043ee879ed36faf4d98ea819a4 100644 (file)
@@ -34,6 +34,7 @@ class MailHandlerTest < ActiveSupport::TestCase
   def setup
     ActionMailer::Base.deliveries.clear
     Setting.notified_events = Redmine::Notifiable.all.collect(&:name)
+    User.current = nil
   end
 
   def teardown
diff --git a/test/unit/mailer_localisation_test.rb b/test/unit/mailer_localisation_test.rb
new file mode 100644 (file)
index 0000000..f0cffc5
--- /dev/null
@@ -0,0 +1,141 @@
+# encoding: utf-8
+#
+# Redmine - project management software
+# Copyright (C) 2006-2017  Jean-Philippe Lang
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+require File.expand_path('../../test_helper', __FILE__)
+
+class MailerLocalisationTest < ActiveSupport::TestCase
+  include Redmine::I18n
+  include Rails::Dom::Testing::Assertions
+  fixtures :projects, :enabled_modules, :issues, :users, :email_addresses, :user_preferences, :members,
+           :member_roles, :roles, :documents, :attachments, :news,
+           :tokens, :journals, :journal_details, :changesets,
+           :trackers, :projects_trackers,
+           :issue_statuses, :enumerations, :messages, :boards, :repositories,
+           :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions,
+           :versions,
+           :comments
+
+  def setup
+    ActionMailer::Base.deliveries.clear
+    Setting.plain_text_mail = '0'
+    Setting.default_language = 'en'
+    User.current = nil
+  end
+
+  # test mailer methods for each language
+  def test_issue_add
+    issue = Issue.find(1)
+    with_each_user_language do |user|
+      assert Mailer.issue_add(user, issue).deliver_now
+    end
+  end
+
+  def test_issue_edit
+    journal = Journal.find(1)
+    with_each_user_language do |user|
+      assert Mailer.issue_edit(user, journal).deliver_now
+    end
+  end
+
+  def test_document_added
+    document = Document.find(1)
+    author = User.find(2)
+    with_each_user_language do |user|
+      assert Mailer.document_added(user, document, author).deliver_now
+    end
+  end
+
+  def test_attachments_added
+    attachements = [ Attachment.find_by_container_type('Document') ]
+    with_each_user_language do |user|
+      assert Mailer.attachments_added(user, attachements).deliver_now
+    end
+  end
+
+  def test_news_added
+    news = News.first
+    with_each_user_language do |user|
+      assert Mailer.news_added(user, news).deliver_now
+    end
+  end
+
+  def test_news_comment_added
+    comment = Comment.find(2)
+    with_each_user_language do |user|
+      assert Mailer.news_comment_added(user, comment).deliver_now
+    end
+  end
+
+  def test_message_posted
+    message = Message.first
+    with_each_user_language do |user|
+      assert Mailer.message_posted(user, message).deliver_now
+    end
+  end
+
+  def test_wiki_content_added
+    content = WikiContent.find(1)
+    with_each_user_language do |user|
+      assert Mailer.wiki_content_added(user, content).deliver_now
+    end
+  end
+
+  def test_wiki_content_updated
+    content = WikiContent.find(1)
+    with_each_user_language do |user|
+      assert Mailer.wiki_content_updated(user, content).deliver_now
+    end
+  end
+
+  def test_account_information
+    with_each_user_language do |user|
+      assert Mailer.account_information(user, 'pAsswORd').deliver_now
+    end
+  end
+
+  def test_lost_password
+    token = Token.find(2)
+    with_each_user_language do |user|
+      assert Mailer.lost_password(user, token).deliver_now
+    end
+  end
+
+  def test_register
+    token = Token.find(1)
+    with_each_user_language do |user|
+      assert Mailer.register(user, token).deliver_now
+    end
+  end
+
+  def test_test_email
+    with_each_user_language do |user|
+      assert Mailer.test_email(user).deliver_now
+    end
+  end
+
+  private
+
+  def with_each_user_language(&block)
+    user = User.find(2)
+    valid_languages.each do |lang|
+      user.update_attribute :language, lang
+      yield user
+    end
+  end
+end
index 5d2ac0d314aa084c9cc74c0ad4b5763d82eed5ae..a5f6c04ff0e032b42c8d592890124e88e651c5db 100644 (file)
@@ -122,7 +122,7 @@ class MailerTest < ActiveSupport::TestCase
 
   def test_generated_links_with_port_and_prefix
     with_settings :host_name => '10.0.0.1:81/redmine', :protocol => 'http' do
-      Mailer.test_email(User.find(1)).deliver
+      Mailer.test_email(User.find(1)).deliver_now
       mail = last_email
       assert_not_nil mail
       assert_include 'http://10.0.0.1:81/redmine', mail_body(mail)
@@ -131,7 +131,7 @@ class MailerTest < ActiveSupport::TestCase
 
   def test_generated_links_with_port
     with_settings :host_name => '10.0.0.1:81', :protocol => 'http' do
-      Mailer.test_email(User.find(1)).deliver
+      Mailer.test_email(User.find(1)).deliver_now
       mail = last_email
       assert_not_nil mail
       assert_include 'http://10.0.0.1:81', mail_body(mail)
@@ -239,7 +239,7 @@ class MailerTest < ActiveSupport::TestCase
 
   def test_from_header
     with_settings :mail_from => 'redmine@example.net' do
-      Mailer.test_email(User.find(1)).deliver
+      Mailer.deliver_test_email(User.find(1))
     end
     mail = last_email
     assert_equal 'redmine@example.net', mail.from_addrs.first
@@ -247,7 +247,7 @@ class MailerTest < ActiveSupport::TestCase
 
   def test_from_header_with_phrase
     with_settings :mail_from => 'Redmine app <redmine@example.net>' do
-      Mailer.test_email(User.find(1)).deliver
+      Mailer.deliver_test_email(User.find(1))
     end
     mail = last_email
     assert_equal 'redmine@example.net', mail.from_addrs.first
@@ -263,7 +263,7 @@ class MailerTest < ActiveSupport::TestCase
     user.pref.no_self_notified = false
     user.pref.save
     User.current = user
-    Mailer.news_added(news.reload).deliver
+    Mailer.deliver_news_added(news.reload)
     assert_equal 1, last_email.bcc.size
 
     # nobody to notify
@@ -271,7 +271,7 @@ class MailerTest < ActiveSupport::TestCase
     user.pref.save
     User.current = user
     ActionMailer::Base.deliveries.clear
-    Mailer.news_added(news.reload).deliver
+    Mailer.deliver_news_added(news.reload)
     assert ActionMailer::Base.deliveries.empty?
   end
 
@@ -300,7 +300,7 @@ class MailerTest < ActiveSupport::TestCase
 
   def test_message_posted_message_id
     message = Message.find(1)
-    Mailer.message_posted(message).deliver
+    Mailer.deliver_message_posted(message)
     mail = last_email
     assert_match /^redmine\.message-1\.\d+\.[a-f0-9]+@example\.net/, mail.message_id
     assert_include "redmine.message-1.20070512151532@example.net", mail.references
@@ -314,7 +314,7 @@ class MailerTest < ActiveSupport::TestCase
 
   def test_reply_posted_message_id
     message = Message.find(3)
-    Mailer.message_posted(message).deliver
+    Mailer.deliver_message_posted(message)
     mail = last_email
     assert_match /^redmine\.message-3\.\d+\.[a-f0-9]+@example\.net/, mail.message_id
     assert_include "redmine.message-1.20070512151532@example.net", mail.references
@@ -329,22 +329,23 @@ class MailerTest < ActiveSupport::TestCase
   test "#issue_add should notify project members" do
     issue = Issue.find(1)
     assert Mailer.deliver_issue_add(issue)
-    assert last_email.bcc.include?('dlopper@somenet.foo')
+    assert_include 'dlopper@somenet.foo', recipients
   end
 
   def test_issue_add_should_send_mail_to_all_user_email_address
     EmailAddress.create!(:user_id => 3, :address => 'otheremail@somenet.foo')
     issue = Issue.find(1)
     assert Mailer.deliver_issue_add(issue)
-    assert last_email.bcc.include?('dlopper@somenet.foo')
-    assert last_email.bcc.include?('otheremail@somenet.foo')
+
+    assert mail = ActionMailer::Base.deliveries.find {|m| m.bcc.include?('dlopper@somenet.foo')}
+    assert mail.bcc.include?('otheremail@somenet.foo')
   end
 
   test "#issue_add should not notify project members that are not allow to view the issue" do
     issue = Issue.find(1)
     Role.find(2).remove_permission!(:view_issues)
     assert Mailer.deliver_issue_add(issue)
-    assert !last_email.bcc.include?('dlopper@somenet.foo')
+    assert_not_include 'dlopper@somenet.foo', recipients
   end
 
   test "#issue_add should notify issue watchers" do
@@ -358,7 +359,7 @@ class MailerTest < ActiveSupport::TestCase
 
     Watcher.create!(:watchable => issue, :user => user)
     assert Mailer.deliver_issue_add(issue)
-    assert last_email.bcc.include?(user.mail)
+    assert_include user.mail, recipients
   end
 
   test "#issue_add should not notify watchers not allowed to view the issue" do
@@ -367,7 +368,7 @@ class MailerTest < ActiveSupport::TestCase
     Watcher.create!(:watchable => issue, :user => user)
     Role.non_member.remove_permission!(:view_issues)
     assert Mailer.deliver_issue_add(issue)
-    assert !last_email.bcc.include?(user.mail)
+    assert_not_include user.mail, recipients
   end
 
   def test_issue_add_should_include_enabled_fields
@@ -391,21 +392,6 @@ class MailerTest < ActiveSupport::TestCase
     end
   end
 
-  # test mailer methods for each language
-  def test_issue_add
-    issue = Issue.find(1)
-    with_each_language_as_default do
-      assert Mailer.deliver_issue_add(issue)
-    end
-  end
-
-  def test_issue_edit
-    journal = Journal.find(1)
-    with_each_language_as_default do
-      assert Mailer.deliver_issue_edit(journal)
-    end
-  end
-
   def test_issue_edit_should_send_private_notes_to_users_with_permission_only
     journal = Journal.find(1)
     journal.private_notes = true
@@ -415,13 +401,14 @@ class MailerTest < ActiveSupport::TestCase
     assert_difference 'ActionMailer::Base.deliveries.size', 2 do
       Mailer.deliver_issue_edit(journal)
     end
-    assert_equal %w(dlopper@somenet.foo jsmith@somenet.foo), ActionMailer::Base.deliveries.last(2).flat_map(&:bcc).sort
+    assert_equal %w(dlopper@somenet.foo jsmith@somenet.foo), recipients
+    ActionMailer::Base.deliveries.clear
 
     Role.find(2).remove_permission! :view_private_notes
     assert_difference 'ActionMailer::Base.deliveries.size', 1 do
       Mailer.deliver_issue_edit(journal)
     end
-    assert_equal %w(jsmith@somenet.foo), ActionMailer::Base.deliveries.last.bcc.sort
+    assert_equal %w(jsmith@somenet.foo), recipients
   end
 
   def test_issue_edit_should_send_private_notes_to_watchers_with_permission_only
@@ -432,11 +419,12 @@ class MailerTest < ActiveSupport::TestCase
 
     Role.non_member.add_permission! :view_private_notes
     Mailer.deliver_issue_edit(journal)
-    assert_include 'someone@foo.bar', ActionMailer::Base.deliveries.last.bcc.sort
+    assert_include 'someone@foo.bar', recipients
+    ActionMailer::Base.deliveries.clear
 
     Role.non_member.remove_permission! :view_private_notes
     Mailer.deliver_issue_edit(journal)
-    assert_not_include 'someone@foo.bar', ActionMailer::Base.deliveries.last.bcc.sort
+    assert_not_include 'someone@foo.bar', recipients
   end
 
   def test_issue_edit_should_mark_private_notes
@@ -461,29 +449,15 @@ class MailerTest < ActiveSupport::TestCase
     ActionMailer::Base.deliveries.clear
 
     Mailer.deliver_issue_edit(journal)
-    last_email.bcc.each do |email|
+    recipients.each do |email|
       user = User.find_by_mail(email)
       assert private_issue.visible?(user), "Issue was not visible to #{user}"
     end
   end
 
-  def test_document_added
-    document = Document.find(1)
-    with_each_language_as_default do
-      assert Mailer.document_added(document).deliver
-    end
-  end
-
-  def test_attachments_added
-    attachements = [ Attachment.find_by_container_type('Document') ]
-    with_each_language_as_default do
-      assert Mailer.attachments_added(attachements).deliver
-    end
-  end
-
   def test_version_file_added
     attachements = [ Attachment.find_by_container_type('Version') ]
-    assert Mailer.attachments_added(attachements).deliver
+    assert Mailer.deliver_attachments_added(attachements)
     assert_not_nil last_email.bcc
     assert last_email.bcc.any?
     assert_select_email do
@@ -493,7 +467,7 @@ class MailerTest < ActiveSupport::TestCase
 
   def test_project_file_added
     attachements = [ Attachment.find_by_container_type('Project') ]
-    assert Mailer.attachments_added(attachements).deliver
+    assert Mailer.deliver_attachments_added(attachements)
     assert_not_nil last_email.bcc
     assert last_email.bcc.any?
     assert_select_email do
@@ -501,107 +475,46 @@ class MailerTest < ActiveSupport::TestCase
     end
   end
 
-  def test_news_added
-    news = News.first
-    with_each_language_as_default do
-      assert Mailer.news_added(news).deliver
-    end
-  end
-
   def test_news_added_should_notify_project_news_watchers
     user1 = User.generate!
     user2 = User.generate!
     news = News.find(1)
     news.project.enabled_module('news').add_watcher(user1)
 
-    Mailer.news_added(news).deliver
-    assert_include user1.mail, last_email.bcc
-    assert_not_include user2.mail, last_email.bcc
-  end
-
-  def test_news_comment_added
-    comment = Comment.find(2)
-    with_each_language_as_default do
-      assert Mailer.news_comment_added(comment).deliver
-    end
-  end
-
-  def test_message_posted
-    message = Message.first
-    recipients = ([message.root] + message.root.children).collect {|m| m.author.mail if m.author}
-    recipients = recipients.compact.uniq
-    with_each_language_as_default do
-      assert Mailer.message_posted(message).deliver
-    end
+    Mailer.deliver_news_added(news)
+    assert_include user1.mail, recipients
+    assert_not_include user2.mail, recipients
   end
 
   def test_wiki_content_added
     content = WikiContent.find(1)
-    with_each_language_as_default do
-      assert_difference 'ActionMailer::Base.deliveries.size', 2 do
-        assert Mailer.wiki_content_added(content).deliver
-        assert_select_email do
-          assert_select 'a[href=?]',
-            'http://localhost:3000/projects/ecookbook/wiki/CookBook_documentation',
-            :text => 'CookBook documentation'
-        end
+    assert_difference 'ActionMailer::Base.deliveries.size', 2 do
+      assert Mailer.deliver_wiki_content_added(content)
+      assert_select_email do
+        assert_select 'a[href=?]',
+          'http://localhost:3000/projects/ecookbook/wiki/CookBook_documentation',
+          :text => 'CookBook documentation'
       end
     end
   end
 
   def test_wiki_content_updated
     content = WikiContent.find(1)
-    with_each_language_as_default do
-      assert_difference 'ActionMailer::Base.deliveries.size', 2 do
-        assert Mailer.wiki_content_updated(content).deliver
-        assert_select_email do
-          assert_select 'a[href=?]',
-            'http://localhost:3000/projects/ecookbook/wiki/CookBook_documentation',
-            :text => 'CookBook documentation'
-        end
-      end
-    end
-  end
-
-  def test_account_information
-    user = User.find(2)
-    valid_languages.each do |lang|
-      user.update_attribute :language, lang.to_s
-      user.reload
-      assert Mailer.account_information(user, 'pAsswORd').deliver
-    end
-  end
-
-  def test_lost_password
-    token = Token.find(2)
-    valid_languages.each do |lang|
-      token.user.update_attribute :language, lang.to_s
-      token.reload
-      assert Mailer.lost_password(token).deliver
+    assert Mailer.deliver_wiki_content_updated(content)
+    assert_select_email do
+      assert_select 'a[href=?]',
+        'http://localhost:3000/projects/ecookbook/wiki/CookBook_documentation',
+        :text => 'CookBook documentation'
     end
   end
 
   def test_register
     token = Token.find(1)
-    valid_languages.each do |lang|
-      token.user.update_attribute :language, lang.to_s
-      token.reload
-      ActionMailer::Base.deliveries.clear
-      assert Mailer.register(token).deliver
-      mail = last_email
-      assert_select_email do
-        assert_select "a[href=?]",
-                      "http://localhost:3000/account/activate?token=#{token.value}",
-                      :text => "http://localhost:3000/account/activate?token=#{token.value}"
-      end
-    end
-  end
-
-  def test_test
-    user = User.find(1)
-    valid_languages.each do |lang|
-      user.update_attribute :language, lang.to_s
-      assert Mailer.test_email(user).deliver
+    assert Mailer.deliver_register(token.user, token)
+    assert_select_email do
+      assert_select "a[href=?]",
+                    "http://localhost:3000/account/activate?token=#{token.value}",
+                    :text => "http://localhost:3000/account/activate?token=#{token.value}"
     end
   end
 
@@ -674,7 +587,7 @@ class MailerTest < ActiveSupport::TestCase
 
       Mailer.reminders(:days => 7)
       assert_equal 2, ActionMailer::Base.deliveries.size
-      assert_equal %w(dlopper@somenet.foo jsmith@somenet.foo), ActionMailer::Base.deliveries.map(&:bcc).flatten.sort
+      assert_equal %w(dlopper@somenet.foo jsmith@somenet.foo), recipients
       ActionMailer::Base.deliveries.each do |mail|
         assert_mail_body_match 'Assigned to group', mail
       end
@@ -690,9 +603,7 @@ class MailerTest < ActiveSupport::TestCase
 
       Mailer.reminders(:days => 42, :version => 'acme')
       assert_equal 1, ActionMailer::Base.deliveries.size
-
-      mail = last_email
-      assert mail.bcc.include?('dlopper@somenet.foo')
+      assert_include 'dlopper@somenet.foo', recipients
     end
   end
 
@@ -709,8 +620,8 @@ class MailerTest < ActiveSupport::TestCase
 
       Mailer.reminders(:days => 42)
       assert_equal 1, ActionMailer::Base.deliveries.size
+      assert_include 'dlopper@somenet.foo', recipients
       mail = last_email
-      assert mail.bcc.include?('dlopper@somenet.foo')
       assert_mail_body_no_match 'Issue dlopper should not see', mail
     end
   end
@@ -718,10 +629,12 @@ class MailerTest < ActiveSupport::TestCase
   def test_security_notification
     set_language_if_valid User.find(1).language
     with_settings :emails_footer => "footer without link" do
-      User.current.remote_ip = '192.168.1.1'
-      assert Mailer.security_notification(User.find(1), message: :notice_account_password_updated).deliver
+      sender = User.find(2)
+      sender.remote_ip = '192.168.1.1'
+      assert Mailer.deliver_security_notification(User.find(1), sender, message: :notice_account_password_updated)
       mail = last_email
       assert_not_nil mail
+      assert_mail_body_match sender.login, mail
       assert_mail_body_match '192.168.1.1', mail
       assert_mail_body_match I18n.t(:notice_account_password_updated), mail
       assert_select_email do
@@ -731,30 +644,25 @@ class MailerTest < ActiveSupport::TestCase
     end
   end
 
-  def test_security_notification_with_overridden_originator_and_remote_ip
+  def test_security_notification_with_overridden_remote_ip
     set_language_if_valid User.find(1).language
     with_settings :emails_footer => "footer without link" do
-      User.current.remote_ip = '192.168.1.1'
-      assert Mailer.security_notification(User.find(1), message: :notice_account_password_updated, originator: User.find(2), remote_ip: '10.0.0.42').deliver
+      sender = User.find(2)
+      sender.remote_ip = '192.168.1.1'
+      assert Mailer.deliver_security_notification(User.find(1), sender, message: :notice_account_password_updated, remote_ip: '10.0.0.42')
       mail = last_email
       assert_not_nil mail
-      assert_mail_body_match User.find(2).login, mail
       assert_mail_body_match '10.0.0.42', mail
-      assert_mail_body_match I18n.t(:notice_account_password_updated), mail
-      assert_select_email do
-        assert_select "h1", false
-        assert_select "a", false
-      end
     end
   end
 
   def test_security_notification_should_include_title
     set_language_if_valid User.find(2).language
     with_settings :emails_footer => "footer without link" do
-      assert Mailer.security_notification(User.find(2),
+      assert Mailer.deliver_security_notification(User.find(2), User.find(2),
         message: :notice_account_password_updated,
         title: :label_my_account
-      ).deliver
+      )
       assert_select_email do
         assert_select "a", false
         assert_select "h1", :text => I18n.t(:label_my_account)
@@ -765,11 +673,11 @@ class MailerTest < ActiveSupport::TestCase
   def test_security_notification_should_include_link
     set_language_if_valid User.find(3).language
     with_settings :emails_footer => "footer without link" do
-      assert Mailer.security_notification(User.find(3),
+      assert Mailer.deliver_security_notification(User.find(3), User.find(3),
       message: :notice_account_password_updated,
       title: :label_my_account,
       url: {controller: 'my', action: 'account'}
-      ).deliver
+      )
       assert_select_email do
         assert_select "h1", false
         assert_select 'a[href=?]', 'http://localhost:3000/my/account', :text => I18n.t(:label_my_account)
@@ -782,8 +690,9 @@ class MailerTest < ActiveSupport::TestCase
     set_language_if_valid 'it'
     # Send an email to a french user
     user = User.find(1)
-    user.language = 'fr'
-    Mailer.account_activated(user).deliver
+    user.update_attribute :language, 'fr'
+    
+    Mailer.deliver_account_activated(user)
     mail = last_email
     assert_mail_body_match 'Votre compte', mail
 
@@ -792,7 +701,7 @@ class MailerTest < ActiveSupport::TestCase
 
   def test_with_deliveries_off
     Mailer.with_deliveries false do
-      Mailer.test_email(User.find(1)).deliver
+      Mailer.test_email(User.find(1)).deliver_now
     end
     assert ActionMailer::Base.deliveries.empty?
     # should restore perform_deliveries
@@ -808,7 +717,7 @@ class MailerTest < ActiveSupport::TestCase
   def test_layout_should_include_the_emails_header
     with_settings :emails_header => "*Header content*" do
       with_settings :plain_text_mail => 0 do
-        assert Mailer.test_email(User.find(1)).deliver
+        assert Mailer.test_email(User.find(1)).deliver_now
         assert_select_email do
           assert_select ".header" do
             assert_select "strong", :text => "Header content"
@@ -816,7 +725,7 @@ class MailerTest < ActiveSupport::TestCase
         end
       end
       with_settings :plain_text_mail => 1 do
-        assert Mailer.test_email(User.find(1)).deliver
+        assert Mailer.test_email(User.find(1)).deliver_now
         mail = last_email
         assert_not_nil mail
         assert_include "*Header content*", mail.body.decoded
@@ -826,7 +735,7 @@ class MailerTest < ActiveSupport::TestCase
 
   def test_layout_should_not_include_empty_emails_header
     with_settings :emails_header => "", :plain_text_mail => 0 do
-      assert Mailer.test_email(User.find(1)).deliver
+      assert Mailer.test_email(User.find(1)).deliver_now
       assert_select_email do
         assert_select ".header", false
       end
@@ -836,7 +745,7 @@ class MailerTest < ActiveSupport::TestCase
   def test_layout_should_include_the_emails_footer
     with_settings :emails_footer => "*Footer content*" do
       with_settings :plain_text_mail => 0 do
-        assert Mailer.test_email(User.find(1)).deliver
+        assert Mailer.test_email(User.find(1)).deliver_now
         assert_select_email do
           assert_select ".footer" do
             assert_select "strong", :text => "Footer content"
@@ -844,7 +753,7 @@ class MailerTest < ActiveSupport::TestCase
         end
       end
       with_settings :plain_text_mail => 1 do
-        assert Mailer.test_email(User.find(1)).deliver
+        assert Mailer.test_email(User.find(1)).deliver_now
         mail = last_email
         assert_not_nil mail
         assert_include "\n-- \n", mail.body.decoded
@@ -856,13 +765,13 @@ class MailerTest < ActiveSupport::TestCase
   def test_layout_should_not_include_empty_emails_footer
     with_settings :emails_footer => "" do
       with_settings :plain_text_mail => 0 do
-        assert Mailer.test_email(User.find(1)).deliver
+        assert Mailer.test_email(User.find(1)).deliver_now
         assert_select_email do
           assert_select ".footer", false
         end
       end
       with_settings :plain_text_mail => 1 do
-        assert Mailer.test_email(User.find(1)).deliver
+        assert Mailer.test_email(User.find(1)).deliver_now
         mail = last_email
         assert_not_nil mail
         assert_not_include "\n-- \n", mail.body.decoded
@@ -902,16 +811,14 @@ class MailerTest < ActiveSupport::TestCase
   end
 
   def test_with_synched_deliveries_should_yield_with_synced_deliveries
-    ActionMailer::Base.delivery_method = :async_smtp
-    ActionMailer::Base.async_smtp_settings = {:foo => 'bar'}
+    ActionMailer::DeliveryJob.queue_adapter = ActiveJob::QueueAdapters::AsyncAdapter.new
 
     Mailer.with_synched_deliveries do
-      assert_equal :smtp, ActionMailer::Base.delivery_method
-      assert_equal({:foo => 'bar'}, ActionMailer::Base.smtp_settings)
+      assert_kind_of ActiveJob::QueueAdapters::InlineAdapter, ActionMailer::DeliveryJob.queue_adapter
     end
-    assert_equal :async_smtp, ActionMailer::Base.delivery_method
+    assert_kind_of ActiveJob::QueueAdapters::AsyncAdapter, ActionMailer::DeliveryJob.queue_adapter
   ensure
-    ActionMailer::Base.delivery_method = :test
+    ActionMailer::DeliveryJob.queue_adapter = ActiveJob::QueueAdapters::InlineAdapter.new
   end
 
   def test_email_addresses_should_keep_addresses
@@ -940,6 +847,11 @@ class MailerTest < ActiveSupport::TestCase
 
   private
 
+  # Returns an array of email addresses to which emails were sent
+  def recipients
+    ActionMailer::Base.deliveries.map(&:bcc).flatten.sort
+  end
+
   def last_email
     mail = ActionMailer::Base.deliveries.last
     assert_not_nil mail
@@ -953,12 +865,4 @@ class MailerTest < ActiveSupport::TestCase
   def html_part
     last_email.parts.detect {|part| part.content_type.include?('text/html')}
   end
-
-  def with_each_language_as_default(&block)
-    valid_languages.each do |lang|
-      with_settings :default_language => lang.to_s do
-        yield lang
-      end
-    end
-  end
 end