From 0c78056a69cc3bee7fb1cd3261046995db55bfdf Mon Sep 17 00:00:00 2001 From: Jean-Philippe Lang Date: Wed, 10 Oct 2018 17:13:09 +0000 Subject: [PATCH] Send emails asynchronously (#26791). 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 --- app/controllers/account_controller.rb | 8 +- app/controllers/admin_controller.rb | 6 +- app/controllers/documents_controller.rb | 2 +- app/controllers/files_controller.rb | 2 +- app/controllers/my_controller.rb | 2 +- app/controllers/users_controller.rb | 6 +- app/models/comment.rb | 8 +- app/models/document.rb | 4 +- app/models/email_address.rb | 14 +- app/models/issue.rb | 2 +- app/models/journal.rb | 2 +- app/models/mail_handler.rb | 2 +- app/models/mailer.rb | 569 +++++++----------- app/models/message.rb | 4 +- app/models/news.rb | 4 +- app/models/setting.rb | 2 +- app/models/user.rb | 2 +- app/models/wiki_content.rb | 24 +- app/views/mailer/_issue.html.erb | 2 +- app/views/mailer/_issue.text.erb | 2 +- .../account_activation_request.html.erb | 2 +- .../account_activation_request.text.erb | 2 +- app/views/mailer/issue_add.html.erb | 2 +- app/views/mailer/issue_add.text.erb | 2 +- app/views/mailer/issue_edit.html.erb | 2 +- app/views/mailer/issue_edit.text.erb | 2 +- .../mailer/security_notification.html.erb | 2 +- .../mailer/security_notification.text.erb | 2 +- app/views/mailer/settings_updated.html.erb | 2 +- app/views/mailer/settings_updated.text.erb | 2 +- config/initializers/10-patches.rb | 31 +- lib/redmine/configuration.rb | 7 + lib/redmine/info.rb | 4 +- lib/tasks/email.rake | 5 +- test/unit/mail_handler_test.rb | 1 + test/unit/mailer_localisation_test.rb | 141 +++++ test/unit/mailer_test.rb | 252 +++----- 37 files changed, 511 insertions(+), 617 deletions(-) create mode 100644 test/unit/mailer_localisation_test.rb diff --git a/app/controllers/account_controller.rb b/app/controllers/account_controller.rb index 5070295d2..7bb644761 100644 --- a/app/controllers/account_controller.rb +++ b/app/controllers/account_controller.rb @@ -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? diff --git a/app/controllers/admin_controller.rb b/app/controllers/admin_controller.rb index dfc73c5ab..885c3a169 100644 --- a/app/controllers/admin_controller.rb +++ b/app/controllers/admin_controller.rb @@ -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 diff --git a/app/controllers/documents_controller.rb b/app/controllers/documents_controller.rb index dbf0e88dc..00b6dd9f1 100644 --- a/app/controllers/documents_controller.rb +++ b/app/controllers/documents_controller.rb @@ -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 diff --git a/app/controllers/files_controller.rb b/app/controllers/files_controller.rb index 7c596657c..6aa55f8a6 100644 --- a/app/controllers/files_controller.rb +++ b/app/controllers/files_controller.rb @@ -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 { diff --git a/app/controllers/my_controller.rb b/app/controllers/my_controller.rb index 0a1d40349..d2cc0c53c 100644 --- a/app/controllers/my_controller.rb +++ b/app/controllers/my_controller.rb @@ -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 diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index aed4cd83f..65dd9a470 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -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| diff --git a/app/models/comment.rb b/app/models/comment.rb index 3b2044123..f2833ac6c 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -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 diff --git a/app/models/document.rb b/app/models/document.rb index 8e4b15517..ccf750fc6 100644 --- a/app/models/document.rb +++ b/app/models/document.rb @@ -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 diff --git a/app/models/email_address.rb b/app/models/email_address.rb index ffd5d6b4f..3bcd929d6 100644 --- a/app/models/email_address.rb +++ b/app/models/email_address.rb @@ -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. diff --git a/app/models/issue.rb b/app/models/issue.rb index d38a4f5ea..b20da8d91 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -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={}) diff --git a/app/models/journal.rb b/app/models/journal.rb index cc96967f5..ce3f9d0b3 100644 --- a/app/models/journal.rb +++ b/app/models/journal.rb @@ -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 diff --git a/app/models/mail_handler.rb b/app/models/mail_handler.rb index ecc131051..bd0d81ba9 100755 --- a/app/models/mail_handler.rb +++ b/app/models/mail_handler.rb @@ -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 diff --git a/app/models/mailer.rb b/app/models/mailer.rb index 8f4262950..50b6ca744 100644 --- a/app/models/mailer.rb +++ b/app/models/mailer.rb @@ -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] 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 diff --git a/app/models/message.rb b/app/models/message.rb index 758095289..83e8cd2c0 100644 --- a/app/models/message.rb +++ b/app/models/message.rb @@ -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 diff --git a/app/models/news.rb b/app/models/news.rb index 8243bc301..90df77e32 100644 --- a/app/models/news.rb +++ b/app/models/news.rb @@ -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 diff --git a/app/models/setting.rb b/app/models/setting.rb index fa04f1c1a..e59c48c86 100644 --- a/app/models/setting.rb +++ b/app/models/setting.rb @@ -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 diff --git a/app/models/user.rb b/app/models/user.rb index c5061225c..0f82a6806 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -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 diff --git a/app/models/wiki_content.rb b/app/models/wiki_content.rb index 3360b71ca..32e0c8450 100644 --- a/app/models/wiki_content.rb +++ b/app/models/wiki_content.rb @@ -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 diff --git a/app/views/mailer/_issue.html.erb b/app/views/mailer/_issue.html.erb index 9461d8490..58287c658 100644 --- a/app/views/mailer/_issue.html.erb +++ b/app/views/mailer/_issue.html.erb @@ -1,6 +1,6 @@

<%= link_to("#{issue.tracker.name} ##{issue.id}: #{issue.subject}", issue_url) %>

-<%= render_email_issue_attributes(issue, users.first, true) %> +<%= render_email_issue_attributes(issue, user, true) %> <%= textilizable(issue, :description, :only_path => false) %> diff --git a/app/views/mailer/_issue.text.erb b/app/views/mailer/_issue.text.erb index 0034c441f..819aebad6 100644 --- a/app/views/mailer/_issue.text.erb +++ b/app/views/mailer/_issue.text.erb @@ -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 %> diff --git a/app/views/mailer/account_activation_request.html.erb b/app/views/mailer/account_activation_request.html.erb index b19cf3219..5f95f8231 100644 --- a/app/views/mailer/account_activation_request.html.erb +++ b/app/views/mailer/account_activation_request.html.erb @@ -1,2 +1,2 @@ -

<%= l(:mail_body_account_activation_request, h(@user.login)) %>

+

<%= l(:mail_body_account_activation_request, h(@new_user.login)) %>

<%= link_to @url, @url %>

diff --git a/app/views/mailer/account_activation_request.text.erb b/app/views/mailer/account_activation_request.text.erb index f431e22d3..b35016b03 100644 --- a/app/views/mailer/account_activation_request.text.erb +++ b/app/views/mailer/account_activation_request.text.erb @@ -1,2 +1,2 @@ -<%= l(:mail_body_account_activation_request, @user.login) %> +<%= l(:mail_body_account_activation_request, @new_user.login) %> <%= @url %> diff --git a/app/views/mailer/issue_add.html.erb b/app/views/mailer/issue_add.html.erb index 14292be96..7bb6115b5 100644 --- a/app/views/mailer/issue_add.html.erb +++ b/app/views/mailer/issue_add.html.erb @@ -1,3 +1,3 @@ <%= l(:text_issue_added, :id => link_to("##{@issue.id}", @issue_url), :author => h(@issue.author)).html_safe %>
-<%= 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 } %> diff --git a/app/views/mailer/issue_add.text.erb b/app/views/mailer/issue_add.text.erb index 6e3b42725..b38111d54 100644 --- a/app/views/mailer/issue_add.text.erb +++ b/app/views/mailer/issue_add.text.erb @@ -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 } %> diff --git a/app/views/mailer/issue_edit.html.erb b/app/views/mailer/issue_edit.html.erb index b7fc18ed6..b5bdd88ee 100644 --- a/app/views/mailer/issue_edit.html.erb +++ b/app/views/mailer/issue_edit.html.erb @@ -12,4 +12,4 @@ <%= textilizable(@journal, :notes, :only_path => false) %>
-<%= 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 } %> diff --git a/app/views/mailer/issue_edit.text.erb b/app/views/mailer/issue_edit.text.erb index 173d2c4fe..3542c1789 100644 --- a/app/views/mailer/issue_edit.text.erb +++ b/app/views/mailer/issue_edit.text.erb @@ -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 } %> diff --git a/app/views/mailer/security_notification.html.erb b/app/views/mailer/security_notification.html.erb index 84394d1ab..762bbf092 100644 --- a/app/views/mailer/security_notification.html.erb +++ b/app/views/mailer/security_notification.html.erb @@ -7,6 +7,6 @@ <%= content_tag :h1, @title -%> <% end %>

-

<%= l(:field_user) %>: <%= @originator.login %>
+

<%= l(:field_user) %>: <%= @sender.login %>
<%= l(:field_remote_ip) %>: <%= @remote_ip %>
<%= l(:label_date) %>: <%= format_time Time.now, true %>

diff --git a/app/views/mailer/security_notification.text.erb b/app/views/mailer/security_notification.text.erb index 7ff3f3d57..e5c143e94 100644 --- a/app/views/mailer/security_notification.text.erb +++ b/app/views/mailer/security_notification.text.erb @@ -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 %> diff --git a/app/views/mailer/settings_updated.html.erb b/app/views/mailer/settings_updated.html.erb index c50eb9ea1..999719ff7 100644 --- a/app/views/mailer/settings_updated.html.erb +++ b/app/views/mailer/settings_updated.html.erb @@ -9,6 +9,6 @@ <%= link_to @url, @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 %>

diff --git a/app/views/mailer/settings_updated.text.erb b/app/views/mailer/settings_updated.text.erb index 9b0be909d..8987fd0cb 100644 --- a/app/views/mailer/settings_updated.text.erb +++ b/app/views/mailer/settings_updated.text.erb @@ -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 %> diff --git a/config/initializers/10-patches.rb b/config/initializers/10-patches.rb index b5698b436..8d73eba2c 100644 --- a/config/initializers/10-patches.rb +++ b/config/initializers/10-patches.rb @@ -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 diff --git a/lib/redmine/configuration.rb b/lib/redmine/configuration.rb index c9a9b6b8c..6ca65304c 100644 --- a/lib/redmine/configuration.rb +++ b/lib/redmine/configuration.rb @@ -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 diff --git a/lib/redmine/info.rb b/lib/redmine/info.rb index f7926a7aa..62452e277 100644 --- a/lib/redmine/info.rb +++ b/lib/redmine/info.rb @@ -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" diff --git a/lib/tasks/email.rake b/lib/tasks/email.rake index 374232f65..25d7d667b 100644 --- a/lib/tasks/email.rake +++ b/lib/tasks/email.rake @@ -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) diff --git a/test/unit/mail_handler_test.rb b/test/unit/mail_handler_test.rb index 8aa2cdbe8..51219d65e 100644 --- a/test/unit/mail_handler_test.rb +++ b/test/unit/mail_handler_test.rb @@ -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 index 000000000..f0cffc5b9 --- /dev/null +++ b/test/unit/mailer_localisation_test.rb @@ -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 diff --git a/test/unit/mailer_test.rb b/test/unit/mailer_test.rb index 5d2ac0d31..a5f6c04ff 100644 --- a/test/unit/mailer_test.rb +++ b/test/unit/mailer_test.rb @@ -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 ' 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 -- 2.39.5