]> source.dussan.org Git - redmine.git/commitdiff
Adds a rake task to send reminders. An email is sent to each user with a list of...
authorJean-Philippe Lang <jp_lang@yahoo.fr>
Sun, 25 May 2008 17:31:50 +0000 (17:31 +0000)
committerJean-Philippe Lang <jp_lang@yahoo.fr>
Sun, 25 May 2008 17:31:50 +0000 (17:31 +0000)
See @rake -D redmine:send_reminders@ for options.

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

33 files changed:
app/models/mailer.rb
app/views/mailer/reminder.text.html.rhtml [new file with mode: 0644]
app/views/mailer/reminder.text.plain.rhtml [new file with mode: 0644]
lang/bg.yml
lang/cs.yml
lang/da.yml
lang/de.yml
lang/en.yml
lang/es.yml
lang/fi.yml
lang/fr.yml
lang/he.yml
lang/hu.yml
lang/it.yml
lang/ja.yml
lang/ko.yml
lang/lt.yml
lang/nl.yml
lang/no.yml
lang/pl.yml
lang/pt-br.yml
lang/pt.yml
lang/ro.yml
lang/ru.yml
lang/sr.yml
lang/sv.yml
lang/th.yml
lang/uk.yml
lang/zh-tw.yml
lang/zh.yml
lib/tasks/reminder.rake [new file with mode: 0644]
test/fixtures/issues.yml
test/unit/mailer_test.rb

index 6fc879a15d9cf4bcdacdcd466434608a511b5f5e..aae374268f93b8125e2a5b7a263aede80134a05d 100644 (file)
@@ -51,6 +51,15 @@ class Mailer < ActionMailer::Base
          :issue_url => url_for(:controller => 'issues', :action => 'show', :id => issue)
   end
   
+  def reminder(user, issues, days)
+    set_language_if_valid user.language
+    recipients user.mail
+    subject l(:mail_subject_reminder, issues.size)
+    body :issues => issues,
+         :days => days,
+         :issues_url => url_for(:controller => 'issues', :action => 'index', :set_filter => 1, :assigned_to_id => user.id, :sort_key => 'issues.due_date', :sort_order => 'asc')
+  end
+  
   def document_added(document)
     redmine_headers 'Project' => document.project.identifier
     recipients document.project.recipients
@@ -144,6 +153,28 @@ class Mailer < ActionMailer::Base
                     (bcc.nil? || bcc.empty?)
     super
   end
+  
+  # Sends reminders to issue assignees
+  # Available options:
+  # * :days     => how many days in the future to remind about (defaults to 7)
+  # * :tracker  => id of tracker for filtering issues (defaults to all trackers)
+  # * :project  => id or identifier of project to process (defaults to all projects)
+  def self.reminders(options={})
+    days = options[:days] || 7
+    project = options[:project] ? Project.find(options[:project]) : nil
+    tracker = options[:tracker] ? Tracker.find(options[:tracker]) : nil
+    
+    s = ARCondition.new ["#{IssueStatus.table_name}.is_closed = ? AND #{Issue.table_name}.due_date <= ? AND #{Issue.table_name}.assigned_to_id IS NOT NULL", false, days.day.from_now.to_date]
+    s << "#{Issue.table_name}.project_id = #{project.id}" if project
+    s << "#{Issue.table_name}.tracker_id = #{tracker.id}" if tracker
+    
+    issues_by_assignee = Issue.find(:all, :include => [:status, :assigned_to, :project, :tracker],
+                                          :conditions => s.conditions
+                                    ).group_by(&:assigned_to)
+    issues_by_assignee.each do |assignee, issues|
+      deliver_reminder(assignee, issues, days) unless assignee.nil?
+    end
+  end
 
   private
   def initialize_defaults(method_name)
diff --git a/app/views/mailer/reminder.text.html.rhtml b/app/views/mailer/reminder.text.html.rhtml
new file mode 100644 (file)
index 0000000..1e33fbe
--- /dev/null
@@ -0,0 +1,9 @@
+<p><%= l(:mail_body_reminder, @issues.size, @days) %></p>
+
+<ul>
+<% @issues.each do |issue| -%>
+    <li><%=h "#{issue.project} - #{issue.tracker} ##{issue.id}: #{issue.subject}" %></li>
+<% end -%>
+</ul>
+
+<p><%= link_to l(:label_issue_view_all), @issues_url %></p>
diff --git a/app/views/mailer/reminder.text.plain.rhtml b/app/views/mailer/reminder.text.plain.rhtml
new file mode 100644 (file)
index 0000000..7e6a2e5
--- /dev/null
@@ -0,0 +1,7 @@
+<%= l(:mail_body_reminder, @issues.size, @days) %>:
+
+<% @issues.each do |issue| -%>
+* <%= "#{issue.project} - #{issue.tracker} ##{issue.id}: #{issue.subject}" %>
+<% end -%>
+
+<%= @issues_url %>
index 224177d94e3f71902922d3fd26d079f3404c447c..10198bce9df7358e660f5afc7ce9a0ceabfa1e50 100644 (file)
@@ -619,3 +619,5 @@ error_scm_annotate: "Обектът не съществува или не мож
 label_planning: Планиране
 text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
 label_and_its_subprojects: %s and its subprojects
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
index 2126553fc6cf5159ed4c2a0958c106d5facccfa2..f90068b3b05177cf29f2247c24aa58c13cd23c1f 100644 (file)
@@ -624,3 +624,5 @@ error_scm_annotate: "Položka neexistuje nebo nemůže být komentována."
 label_planning: Plánování
 text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
 label_and_its_subprojects: %s and its subprojects
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
index 3b5ca07d3ff9d5ad68e5486104422a79a684abf5..ca4ddeb468fa3220fdd42bb19817c919a56405e5 100644 (file)
@@ -621,3 +621,5 @@ error_scm_annotate: "The entry does not exist or can not be annotated."
 label_planning: Planlægning
 text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
 label_and_its_subprojects: %s and its subprojects
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
index 273bad27741f21746476a336b4e10e503d5a1b9d..fa33e1c7422c56e134bef7a80623ede17fe14e41 100644 (file)
@@ -620,3 +620,5 @@ enumeration_doc_categories: Dokumentenkategorien
 enumeration_activities: Aktivitäten (Zeiterfassung)
 text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
 label_and_its_subprojects: %s and its subprojects
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
index 892a63a1b1c006ab565341d996809ff1369bd506..f9e07d211aa86092377c1dae8a1fa00cffec91ad 100644 (file)
@@ -91,6 +91,8 @@ mail_body_account_information_external: You can use your "%s" account to log in.
 mail_body_account_information: Your account information
 mail_subject_account_activation_request: %s account activation request
 mail_body_account_activation_request: 'A new user (%s) has registered. His account is pending your approval:'
+mail_subject_reminder: "%d issue(s) due in the next days"
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
 
 gui_validation_error: 1 error
 gui_validation_error_plural: %d errors
index 26009d716b9e5c24ebe1a19a49a555ac49b13175..3d8c0a931f88c6ab483a9188fad8225ed07b9a59 100644 (file)
@@ -622,3 +622,5 @@ error_scm_annotate: "No existe la entrada o no ha podido ser anotada"
 label_planning: Planificación
 text_subprojects_destroy_warning: 'Sus subprojectos: %s también se eliminarán' 
 label_and_its_subprojects: %s and its subprojects
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
index 908ef1283381f0a8619ab3a59f04ee6531d13271..60428bb4976568341988c08e8dc63dfd7b559cc1 100644 (file)
@@ -619,3 +619,5 @@ error_scm_annotate: "Merkintää ei ole tai siihen ei voi lisätä selityksiä."
 label_planning: Suunnittelu
 text_subprojects_destroy_warning: 'Tämän alaprojekti(t): %s tullaan myös poistamaan.'
 label_and_its_subprojects: %s and its subprojects
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
index 8827edd5e06651c44ad327c2f3027707aa668ea8..8b002bbf6c602de0db2910a78d57579a641e27c5 100644 (file)
@@ -91,6 +91,8 @@ mail_body_account_information_external: Vous pouvez utiliser votre compte "%s" p
 mail_body_account_information: Paramètres de connexion de votre compte
 mail_subject_account_activation_request: "Demande d'activation d'un compte %s"
 mail_body_account_activation_request: "Un nouvel utilisateur (%s) s'est inscrit. Son compte nécessite votre approbation:"
+mail_subject_reminder: "%d demande(s) arrivent à échéance"
+mail_body_reminder: "%d demande(s) qui vous sont assignées arrivent à échéance dans les %d prochains jours:"
 
 gui_validation_error: 1 erreur
 gui_validation_error_plural: %d erreurs
index 71bc5e5b3a6547ea533243340d58c524dcb2c469..f972ef62a64167542ae64e2dc6089959f80822a2 100644 (file)
@@ -619,3 +619,5 @@ error_scm_annotate: "הכניסה לא קיימת או שלא ניתן לתאר
 label_planning: תכנון
 text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
 label_and_its_subprojects: %s and its subprojects
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
index c3feaf16d62636e41cd80ac11ade387bb7d5691d..f6d312ce0f0e1983cb92c519095eab6e67d7202b 100644 (file)
@@ -620,3 +620,5 @@ default_activity_development: Fejlesztés
 enumeration_issue_priorities: Feladat prioritások
 enumeration_doc_categories: Dokumentum kategóriák
 enumeration_activities: Tevékenységek (idő rögzítés)
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
index 1771e1bdaa248a6a63516c5f5432705ef7d8fc25..a8646644aeb3395363b31d20dae2978a7c363328 100644 (file)
@@ -619,3 +619,5 @@ error_scm_annotate: "The entry does not exist or can not be annotated."
 label_planning: Planning
 text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
 label_and_its_subprojects: %s and its subprojects
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
index 84f27a2eb29a234ca49e8c76dedc3fb481f86b0f..d119cf55e0801eef2cd88d128b62dbdea9073d05 100644 (file)
@@ -620,3 +620,5 @@ error_scm_annotate: "エントリが存在しない、もしくはアノテー
 label_planning: 計画
 text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
 label_and_its_subprojects: %s and its subprojects
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
index ee94c4025b39a0d24f135bb92a213c4c9d0aa3ba..98dde9e0634846352300cbfb59c30fe6f81f6734 100644 (file)
@@ -619,3 +619,5 @@ error_scm_annotate: "The entry does not exist or can not be annotated."
 label_planning: Planning
 text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
 label_and_its_subprojects: %s and its subprojects
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
index bc2faaa00916e14780c020d24a5d388f30afce46..6b791d87b250085d8695226652ec2a28e8aa2a17 100644 (file)
@@ -621,3 +621,5 @@ label_planning: Planavimas
 text_subprojects_destroy_warning: 'Šis(ie) subprojektas(ai): %s taip pat bus ištrintas(i).'
 label_and_its_subprojects: %s projektas ir jo subprojektai
 
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
index 01755137afabd1fc7b265e732282d45c82103b2e..c36eaecd572611e3d8f53e57d691af6183cae8d2 100644 (file)
@@ -620,3 +620,5 @@ error_scm_annotate: "The entry does not exist or can not be annotated."
 label_planning: Planning
 text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
 label_and_its_subprojects: %s and its subprojects
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
index 13fcd24f51bab2d3f584d25ad372a90653abbd2f..5e788dc502df01f6e06fc31ac9e717e266ac78ff 100644 (file)
@@ -620,3 +620,5 @@ default_activity_development: Utvikling
 enumeration_issue_priorities: Sakssprioriteringer
 enumeration_doc_categories: Dokument-kategorier
 enumeration_activities: Aktiviteter (tidssporing)
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
index c83173ca2416a2bd2136ba9546a8b35357e01d41..4476c2160227c95a2983a9a5144dd161796ad75d 100644 (file)
@@ -619,3 +619,5 @@ error_scm_annotate: "Wpis nie istnieje lub nie można do niego dodawać adnotacj
 label_planning: Planning
 text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
 label_and_its_subprojects: %s and its subprojects
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
index 9714df2ca7d439a37b1045627b5b6a6e1d8a7249..9b3d231516190f168e1078fd6daf752f617513f0 100644 (file)
@@ -619,3 +619,5 @@ label_planning: Planejamento
 text_subprojects_destroy_warning: 'Seu(s) subprojeto(s): %s também serão excluídos.'\r
 label_age: Age\r
 label_and_its_subprojects: %s and its subprojects\r
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"\r
+mail_subject_reminder: "%d issue(s) due in the next days"\r
index 9d0586775ef0bd8d86698be3153ffd66fb76f437..c56b76a0b7b351153d070a6455e1aa2a81b9612c 100644 (file)
@@ -619,3 +619,5 @@ error_scm_annotate: "The entry does not exist or can not be annotated."
 label_planning: Planning
 text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
 label_and_its_subprojects: %s and its subprojects
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
index 01127f5c517ef63498c1215bc7e555e2a37b436d..30464569b3d1342efe213820802d86d650bd9428 100644 (file)
@@ -619,3 +619,5 @@ error_scm_annotate: "The entry does not exist or can not be annotated."
 label_planning: Planning
 text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
 label_and_its_subprojects: %s and its subprojects
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
index 4f0a4bb7b6350a187f572e853fa6c735985d5b05..f4fd920375eac349d0aaf441c92a55f2642b4430 100644 (file)
@@ -623,3 +623,5 @@ error_scm_annotate: "Данные отсутствуют или не могут
 label_planning: Планирование
 text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
 label_and_its_subprojects: %s and its subprojects
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
index 263bd11872d21fb13211a235480d9a545183c459..53e4f9f2c2235e2704f18cbf440264b737fe2dd4 100644 (file)
@@ -620,3 +620,5 @@ error_scm_annotate: "The entry does not exist or can not be annotated."
 label_planning: Planning
 text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
 label_and_its_subprojects: %s and its subprojects
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
index 5b0c57acf36b729820773576d00f5ad809eb4688..248f3dcba0215a06e49320cf2f6ff1fae2a62c02 100644 (file)
@@ -620,3 +620,5 @@ error_scm_annotate: "The entry does not exist or can not be annotated."
 label_planning: Planning
 text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
 label_and_its_subprojects: %s and its subprojects
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
index 643ee01c665d005b031d896aac834351259a8334..444316aa156b24b0042fc1f9a263bb9ad5e7c808 100644 (file)
@@ -622,3 +622,5 @@ enumeration_issue_priorities: ความสำคัญของปัญห
 enumeration_doc_categories: ประเภทเอกสาร
 enumeration_activities: กิจกรรม (ใช้ในการติดตามเวลา)
 label_and_its_subprojects: %s and its subprojects
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
index e61fc5b383960dc058ed4537c37c7f77110d5abc..3cc60ef48377117b09bed8e21b6fb4b63fb9e05a 100644 (file)
@@ -621,3 +621,5 @@ error_scm_annotate: "The entry does not exist or can not be annotated."
 label_planning: Planning
 text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
 label_and_its_subprojects: %s and its subprojects
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
index 1bdc7d715c2ff4e6c55da8dd41aefb8ab2b2c87b..3926113b2b935f359620c044cb0bc8a7c03d5563 100644 (file)
@@ -620,3 +620,5 @@ default_activity_development: 開發
 enumeration_issue_priorities: 項目優先權
 enumeration_doc_categories: 文件分類
 enumeration_activities: 活動 (時間追蹤)
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
index ac5eb456d2a9870d2af772e0bf6de5d7b069b0ac..ffd7cd811562716321a6d374e575aad63c884655 100644 (file)
@@ -619,4 +619,6 @@ default_activity_development: 开发
 
 enumeration_issue_priorities: 问题优先级
 enumeration_doc_categories: 文档类别
-enumeration_activities: 活动(时间跟踪)
\ No newline at end of file
+enumeration_activities: 活动(时间跟踪)mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
diff --git a/lib/tasks/reminder.rake b/lib/tasks/reminder.rake
new file mode 100644 (file)
index 0000000..73844fb
--- /dev/null
@@ -0,0 +1,39 @@
+# redMine - project management software
+# Copyright (C) 2008  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.
+
+desc <<-END_DESC
+Send reminders about issues due in the next days.
+
+Available options:
+  * days     => number of days to remind about (defaults to 7)
+  * tracker  => id of tracker (defaults to all trackers)
+  * project  => id or identifier of project (defaults to all projects)
+
+Example:
+  rake redmine:send_reminders days=7 RAILS_ENV="production"
+END_DESC
+
+namespace :redmine do
+  task :send_reminders => :environment do
+    options = {}
+    options[:days] = ENV['days'].to_i if ENV['days']
+    options[:project] = ENV['project'] if ENV['project']
+    options[:tracker] = ENV['tracker'].to_i if ENV['tracker']
+    
+    Mailer.reminders(options)
+  end
+end
index c037624aef01dd7fb193227ee51ee12d111f9a12..9d3287c6ffa7eb9540b4e231b3a3a601e016cde8 100644 (file)
@@ -42,7 +42,7 @@ issues_003:
   category_id: \r
   description: Error 281 is encountered when saving a recipe\r
   tracker_id: 1\r
-  assigned_to_id: \r
+  assigned_to_id: 3\r
   author_id: 2\r
   status_id: 1\r
   start_date: <%= 1.day.from_now.to_date.to_s(:db) %>\r
index 64648b94c783d61426e46c6f71b3d736704e19a7..8cf6c1423d092c43eaf5064424865bb182ec75fb 100644 (file)
@@ -116,4 +116,13 @@ class MailerTest < Test::Unit::TestCase
       assert Mailer.deliver_register(token)
     end
   end
+  
+  def test_reminders
+    ActionMailer::Base.deliveries.clear
+    Mailer.reminders(:days => 42)
+    assert_equal 1, ActionMailer::Base.deliveries.size
+    mail = ActionMailer::Base.deliveries.last
+    assert mail.bcc.include?('dlopper@somenet.foo')
+    assert mail.body.include?('Bug #3: Error 281 when updating a recipe')
+  end
 end