diff options
Diffstat (limited to 'app')
30 files changed, 278 insertions, 409 deletions
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<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 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 @@ <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) %> 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 @@ -<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> 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 %> <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 } %> 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) %> <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 } %> 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 %></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> 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 %> <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> 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 %> |