diff app/models/mailer.rb @ 1464:261b3d9a4903 redmine-2.4

Update to Redmine 2.4 branch rev 12663
author Chris Cannam
date Tue, 14 Jan 2014 14:37:42 +0000
parents 433d4f72a19b
children 51364c0cd58f e248c7af89ec
line wrap: on
line diff
--- a/app/models/mailer.rb	Fri Jun 14 09:05:06 2013 +0100
+++ b/app/models/mailer.rb	Tue Jan 14 14:37:42 2014 +0000
@@ -1,5 +1,5 @@
 # Redmine - project management software
-# Copyright (C) 2006-2012  Jean-Philippe Lang
+# Copyright (C) 2006-2013  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
@@ -27,34 +27,35 @@
     { :host => Setting.host_name, :protocol => Setting.protocol }
   end
 
-  # Builds a Mail::Message object used to email recipients of the added issue.
-  #
-  # Example:
-  #   issue_add(issue) => Mail::Message object
-  #   Mailer.issue_add(issue).deliver => sends an email to issue recipients
-  def issue_add(issue)
+  # Builds a mail for notifying to_users and cc_users about a new issue
+  def issue_add(issue, to_users, cc_users)
     redmine_headers 'Project' => issue.project.identifier,
                     'Issue-Id' => issue.id,
                     'Issue-Author' => issue.author.login
     redmine_headers 'Issue-Assignee' => issue.assigned_to.login if issue.assigned_to
     message_id issue
+    references issue
     @author = issue.author
     @issue = issue
+    @users = to_users + cc_users
     @issue_url = url_for(:controller => 'issues', :action => 'show', :id => issue)
-    recipients = issue.recipients
-    cc = issue.watcher_recipients - recipients
-    mail :to => recipients,
-      :cc => cc,
+    mail :to => to_users.map(&:mail),
+      :cc => cc_users.map(&:mail),
       :subject => "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] (#{issue.status.name}) #{issue.subject}"
   end
 
-  # Builds a Mail::Message object used to email recipients of the edited issue.
-  #
-  # Example:
-  #   issue_edit(journal) => Mail::Message object
-  #   Mailer.issue_edit(journal).deliver => sends an email to issue recipients
-  def issue_edit(journal)
-    issue = journal.journalized.reload
+  # Notifies users about a new issue
+  def self.deliver_issue_add(issue)
+    to = issue.notified_users
+    cc = issue.notified_watchers - to
+    issue.each_notification(to + cc) do |users|
+      Mailer.issue_add(issue, to & users, cc & users).deliver
+    end
+  end
+
+  # Builds a mail for notifying to_users and cc_users about an issue update
+  def issue_edit(journal, to_users, cc_users)
+    issue = journal.journalized
     redmine_headers 'Project' => issue.project.identifier,
                     'Issue-Id' => issue.id,
                     'Issue-Author' => issue.author.login
@@ -62,20 +63,31 @@
     message_id journal
     references issue
     @author = journal.user
-    recipients = journal.recipients
-    # Watchers in cc
-    cc = journal.watcher_recipients - recipients
     s = "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] "
     s << "(#{issue.status.name}) " if journal.new_value_for('status_id')
     s << issue.subject
     @issue = issue
+    @users = to_users + cc_users
     @journal = journal
+    @journal_details = journal.visible_details(@users.first)
     @issue_url = url_for(:controller => 'issues', :action => 'show', :id => issue, :anchor => "change-#{journal.id}")
-    mail :to => recipients,
-      :cc => cc,
+    mail :to => to_users.map(&:mail),
+      :cc => cc_users.map(&:mail),
       :subject => s
   end
 
+  # Notifies users about an issue update
+  def self.deliver_issue_edit(journal)
+    issue = journal.journalized.reload
+    to = journal.notified_users
+    cc = journal.notified_watchers
+    journal.each_notification(to + cc) do |users|
+      issue.each_notification(users) do |users2|
+        Mailer.issue_edit(journal, to & users2, cc & users2).deliver
+      end
+    end
+  end
+
   def reminder(user, issues, days)
     set_language_if_valid user.language
     @issues = issues
@@ -142,6 +154,7 @@
     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 => news.recipients,
@@ -158,6 +171,7 @@
     redmine_headers 'Project' => news.project.identifier
     @author = comment.author
     message_id comment
+    references news
     @news = news
     @comment = comment
     @news_url = url_for(:controller => 'news', :action => 'show', :id => news)
@@ -176,7 +190,7 @@
                     'Topic-Id' => (message.parent_id || message.id)
     @author = message.author
     message_id message
-    references message.parent unless message.parent.nil?
+    references message.root
     recipients = message.recipients
     cc = ((message.root.watcher_recipients + message.board.watcher_recipients).uniq - recipients)
     @message = message
@@ -252,7 +266,7 @@
   #   Mailer.account_activation_request(user).deliver => sends an email to all active administrators
   def account_activation_request(user)
     # Send the email to all active administrators
-    recipients = User.active.find(:all, :conditions => {:admin => true}).collect { |u| u.mail }.compact
+    recipients = User.active.where(:admin => true).all.collect { |u| u.mail }.compact
     @user = user
     @url = url_for(:controller => 'users', :action => 'index',
                          :status => User::STATUS_REGISTERED,
@@ -297,31 +311,6 @@
       :subject => 'Redmine test'
   end
 
-  # Overrides default deliver! method to prevent from sending an email
-  # with no recipient, cc or bcc
-  def deliver!(mail = @mail)
-    set_language_if_valid @initial_language
-    return false if (recipients.nil? || recipients.empty?) &&
-                    (cc.nil? || cc.empty?) &&
-                    (bcc.nil? || bcc.empty?)
-
-
-    # Log errors when raise_delivery_errors is set to false, Rails does not
-    raise_errors = self.class.raise_delivery_errors
-    self.class.raise_delivery_errors = true
-    begin
-      return super(mail)
-    rescue Exception => e
-      if raise_errors
-        raise e
-      elsif mylogger
-        mylogger.error "The following error occured while sending email notification: \"#{e.message}\". Check your configuration in config/configuration.yml."
-      end
-    ensure
-      self.class.raise_delivery_errors = raise_errors
-    end
-  end
-
   # Sends reminders to issue assignees
   # Available options:
   # * :days     => how many days in the future to remind about (defaults to 7)
@@ -379,7 +368,7 @@
     ActionMailer::Base.delivery_method = saved_method
   end
 
-  def mail(headers={})
+  def mail(headers={}, &block)
     headers.merge! 'X-Mailer' => 'Redmine',
             'X-Redmine-Host' => Setting.host_name,
             'X-Redmine-Site' => Setting.app_title,
@@ -389,8 +378,9 @@
             'List-Id' => "<#{Setting.mail_from.to_s.gsub('@', '.')}>"
 
     # Removes the author from the recipients and cc
-    # if he doesn't want to receive notifications about what he does
-    if @author && @author.logged? && @author.pref[:no_self_notified]
+    # if the author does not want to receive notifications
+    # about what the author do
+    if @author && @author.logged? && @author.pref.no_self_notified
       headers[:to].delete(@author.mail) if headers[:to].is_a?(Array)
       headers[:cc].delete(@author.mail) if headers[:cc].is_a?(Array)
     end
@@ -410,15 +400,20 @@
       headers[:message_id] = "<#{self.class.message_id_for(@message_id_object)}>"
     end
     if @references_objects
-      headers[:references] = @references_objects.collect {|o| "<#{self.class.message_id_for(o)}>"}.join(' ')
+      headers[:references] = @references_objects.collect {|o| "<#{self.class.references_for(o)}>"}.join(' ')
     end
 
-    super headers do |format|
-      format.text
-      format.html unless Setting.plain_text_mail?
+    m = if block_given?
+      super headers, &block
+    else
+      super headers do |format|
+        format.text
+        format.html unless Setting.plain_text_mail?
+      end
     end
+    set_language_if_valid @initial_language
 
-    set_language_if_valid @initial_language
+    m
   end
 
   def initialize(*args)
@@ -426,10 +421,20 @@
     set_language_if_valid Setting.default_language
     super
   end
-  
+
   def self.deliver_mail(mail)
     return false if mail.to.blank? && mail.cc.blank? && mail.bcc.blank?
-    super
+    begin
+      # Log errors when raise_delivery_errors is set to false, Rails does not
+      mail.raise_delivery_errors = true
+      super
+    rescue Exception => e
+      if ActionMailer::Base.raise_delivery_errors
+        raise e
+      else
+        Rails.logger.error "Email delivery error: #{e.message}"
+      end
+    end
   end
 
   def self.method_missing(method, *args, &block)
@@ -448,15 +453,30 @@
     h.each { |k,v| headers["X-Redmine-#{k}"] = v.to_s }
   end
 
-  # Returns a predictable Message-Id for the given object
-  def self.message_id_for(object)
-    # id + timestamp should reduce the odds of a collision
-    # as far as we don't send multiple emails for the same object
+  def self.token_for(object, rand=true)
     timestamp = object.send(object.respond_to?(:created_on) ? :created_on : :updated_on)
-    hash = "redmine.#{object.class.name.demodulize.underscore}-#{object.id}.#{timestamp.strftime("%Y%m%d%H%M%S")}"
+    hash = [
+      "redmine",
+      "#{object.class.name.demodulize.underscore}-#{object.id}",
+      timestamp.strftime("%Y%m%d%H%M%S")
+    ]
+    if rand
+      hash << Redmine::Utils.random_hex(8)
+    end
     host = Setting.mail_from.to_s.gsub(%r{^.*@}, '')
     host = "#{::Socket.gethostname}.redmine" if host.empty?
-    "#{hash}@#{host}"
+    "#{hash.join('.')}@#{host}"
+  end
+
+  # Returns a Message-Id for the given object
+  def self.message_id_for(object)
+    token_for(object, true)
+  end
+
+  # Returns a uniq token for a given object referenced by all notifications
+  # related to this object
+  def self.references_for(object)
+    token_for(object, false)
   end
 
   def message_id(object)