Index: app/models/mailer.rb =================================================================== --- app/models/mailer.rb (revision 16172) +++ app/models/mailer.rb (working copy) @@ -376,6 +376,7 @@ # * :project => id or identifier of project to process (defaults to all projects) # * :users => array of user/group ids who should be reminded # * :version => name of target version for filtering issues (defaults to none) + # * :recipients => array of recipient types (available values are :assignee and :watcher, default to :assignee) def self.reminders(options={}) days = options[:days] || 7 project = options[:project] ? Project.find(options[:project]) : nil @@ -385,9 +386,11 @@ raise ActiveRecord::RecordNotFound.new("Couldn't find Version with named #{options[:version]}") end user_ids = options[:users] + recipients = options[:recipients] + recipients = [:assignee] if recipients.blank? - scope = Issue.open.where("#{Issue.table_name}.assigned_to_id IS NOT NULL" + - " AND #{Project.table_name}.status = #{Project::STATUS_ACTIVE}" + + scope = Issue.open.where( + "#{Project.table_name}.status = #{Project::STATUS_ACTIVE}" + " AND #{Issue.table_name}.due_date <= ?", days.day.from_now.to_date ) scope = scope.where(:assigned_to_id => user_ids) if user_ids.present? @@ -394,20 +397,32 @@ scope = scope.where(:project_id => project.id) if project scope = scope.where(:fixed_version_id => target_version_id) if target_version_id.present? scope = scope.where(:tracker_id => tracker.id) if tracker - issues_by_assignee = scope.includes(:status, :assigned_to, :project, :tracker). - group_by(&:assigned_to) - issues_by_assignee.keys.each do |assignee| - if assignee.is_a?(Group) - assignee.users.each do |user| - issues_by_assignee[user] ||= [] - issues_by_assignee[user] += issues_by_assignee[assignee] + issues = scope.includes(:status, :assigned_to, :project, :tracker) + issues_by_recipient = {} + + if recipients.include?(:assignee) + issues_by_recipient = + issues.where('assigned_to_id IS NOT NULL').group_by(&:assigned_to) + issues_by_recipient.keys.each do |assignee| + if assignee.is_a?(Group) + assignee.users.each do |user| + issues_by_recipient[user] ||= [] + issues_by_recipient[user] += issues_by_recipient[assignee] + end end end end - - issues_by_assignee.each do |assignee, issues| - reminder(assignee, issues, days).deliver if assignee.is_a?(User) && assignee.active? + if recipients.include?(:watcher) + issues.each do |issue| + issue.notified_watchers.each do |watcher| + issues_by_recipient[watcher] ||= [] + issues_by_recipient[watcher] |= [issue] + end + end end + issues_by_recipient.each do |recipient, issues| + reminder(recipient, issues, days).deliver if recipient.is_a?(User) && recipient.active? + end end # Activates/desactivates email deliveries during +block+ Index: lib/tasks/reminder.rake =================================================================== --- lib/tasks/reminder.rake (revision 16172) +++ lib/tasks/reminder.rake (working copy) @@ -24,6 +24,8 @@ * project => id or identifier of project (defaults to all projects) * users => comma separated list of user/group ids who should be reminded * version => name of target version for filtering issues (defaults to none) + * recipients => comma separated list of recipient types + (default to 'assignee', available values are 'assignee' and 'watcher') Example: rake redmine:send_reminders days=7 users="1,23, 56" RAILS_ENV="production" @@ -36,7 +38,10 @@ options[:project] = ENV['project'] if ENV['project'] options[:tracker] = ENV['tracker'].to_i if ENV['tracker'] options[:users] = (ENV['users'] || '').split(',').each(&:strip!) - options[:version] = ENV['version'] if ENV['version'] + options[:version] = ENV['version'] if ENV['version'] + options[:recipients] = + ENV['recipients'].to_s.downcase.split(',').map(&:strip).map(&:to_sym) & + [:assignee, :watcher] Mailer.with_synched_deliveries do Mailer.reminders(options) Index: test/unit/mailer_test.rb =================================================================== --- test/unit/mailer_test.rb (revision 16172) +++ test/unit/mailer_test.rb (working copy) @@ -663,6 +663,33 @@ end end + def test_reminders_with_recipient_option + with_settings :default_language => 'en' do + # assigned to dlopper, watched by jsmith + issues(:issues_003).add_watcher(users(:users_002)) + # assigned to nobody, watched by jsmith + Issue.generate!(:assigned_to => nil, :due_date => 5.days.from_now, :subject => 'Assigned to nobody').add_watcher(users(:users_002)) + ActionMailer::Base.deliveries.clear + + Mailer.reminders(:days => 42, :recipients => [:assignee]) + assert_equal 1, ActionMailer::Base.deliveries.size + assert last_email.bcc.include?('dlopper@somenet.foo') + ActionMailer::Base.deliveries.clear + + Mailer.reminders(:days => 42, :recipients => [:watcher]) + assert_equal 1, ActionMailer::Base.deliveries.size + mail = last_email + assert mail.bcc.include?('jsmith@somenet.foo') + assert_mail_body_match 'Bug #3: Error 281 when updating a recipe', mail + assert_mail_body_match 'Assigned to nobody', mail + ActionMailer::Base.deliveries.clear + + Mailer.reminders(:days => 42, :recipients => [:assignee, :watcher]) + assert_equal 2, ActionMailer::Base.deliveries.size + assert_equal %w(dlopper@somenet.foo jsmith@somenet.foo), ActionMailer::Base.deliveries.map(&:bcc).flatten.sort + end + end + def test_security_notification set_language_if_valid User.find(1).language with_settings :emails_footer => "footer without link" do