##// END OF EJS Templates
Adds a rake task to send reminders. An email is sent to each user with a list of the issues due in the next days, if any....
Jean-Philippe Lang -
r1445:0c8105277070
parent child
Show More
@@ -0,0 +1,9
1 <p><%= l(:mail_body_reminder, @issues.size, @days) %></p>
2
3 <ul>
4 <% @issues.each do |issue| -%>
5 <li><%=h "#{issue.project} - #{issue.tracker} ##{issue.id}: #{issue.subject}" %></li>
6 <% end -%>
7 </ul>
8
9 <p><%= link_to l(:label_issue_view_all), @issues_url %></p>
@@ -0,0 +1,7
1 <%= l(:mail_body_reminder, @issues.size, @days) %>:
2
3 <% @issues.each do |issue| -%>
4 * <%= "#{issue.project} - #{issue.tracker} ##{issue.id}: #{issue.subject}" %>
5 <% end -%>
6
7 <%= @issues_url %>
@@ -0,0 +1,39
1 # redMine - project management software
2 # Copyright (C) 2008 Jean-Philippe Lang
3 #
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
18 desc <<-END_DESC
19 Send reminders about issues due in the next days.
20
21 Available options:
22 * days => number of days to remind about (defaults to 7)
23 * tracker => id of tracker (defaults to all trackers)
24 * project => id or identifier of project (defaults to all projects)
25
26 Example:
27 rake redmine:send_reminders days=7 RAILS_ENV="production"
28 END_DESC
29
30 namespace :redmine do
31 task :send_reminders => :environment do
32 options = {}
33 options[:days] = ENV['days'].to_i if ENV['days']
34 options[:project] = ENV['project'] if ENV['project']
35 options[:tracker] = ENV['tracker'].to_i if ENV['tracker']
36
37 Mailer.reminders(options)
38 end
39 end
@@ -51,6 +51,15 class Mailer < ActionMailer::Base
51 51 :issue_url => url_for(:controller => 'issues', :action => 'show', :id => issue)
52 52 end
53 53
54 def reminder(user, issues, days)
55 set_language_if_valid user.language
56 recipients user.mail
57 subject l(:mail_subject_reminder, issues.size)
58 body :issues => issues,
59 :days => days,
60 :issues_url => url_for(:controller => 'issues', :action => 'index', :set_filter => 1, :assigned_to_id => user.id, :sort_key => 'issues.due_date', :sort_order => 'asc')
61 end
62
54 63 def document_added(document)
55 64 redmine_headers 'Project' => document.project.identifier
56 65 recipients document.project.recipients
@@ -145,6 +154,28 class Mailer < ActionMailer::Base
145 154 super
146 155 end
147 156
157 # Sends reminders to issue assignees
158 # Available options:
159 # * :days => how many days in the future to remind about (defaults to 7)
160 # * :tracker => id of tracker for filtering issues (defaults to all trackers)
161 # * :project => id or identifier of project to process (defaults to all projects)
162 def self.reminders(options={})
163 days = options[:days] || 7
164 project = options[:project] ? Project.find(options[:project]) : nil
165 tracker = options[:tracker] ? Tracker.find(options[:tracker]) : nil
166
167 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]
168 s << "#{Issue.table_name}.project_id = #{project.id}" if project
169 s << "#{Issue.table_name}.tracker_id = #{tracker.id}" if tracker
170
171 issues_by_assignee = Issue.find(:all, :include => [:status, :assigned_to, :project, :tracker],
172 :conditions => s.conditions
173 ).group_by(&:assigned_to)
174 issues_by_assignee.each do |assignee, issues|
175 deliver_reminder(assignee, issues, days) unless assignee.nil?
176 end
177 end
178
148 179 private
149 180 def initialize_defaults(method_name)
150 181 super
@@ -619,3 +619,5 error_scm_annotate: "Обектът не съществува или не мож
619 619 label_planning: Планиране
620 620 text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
621 621 label_and_its_subprojects: %s and its subprojects
622 mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
623 mail_subject_reminder: "%d issue(s) due in the next days"
@@ -624,3 +624,5 error_scm_annotate: "Položka neexistuje nebo nemůže být komentována."
624 624 label_planning: Plánování
625 625 text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
626 626 label_and_its_subprojects: %s and its subprojects
627 mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
628 mail_subject_reminder: "%d issue(s) due in the next days"
@@ -621,3 +621,5 error_scm_annotate: "The entry does not exist or can not be annotated."
621 621 label_planning: Planlægning
622 622 text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
623 623 label_and_its_subprojects: %s and its subprojects
624 mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
625 mail_subject_reminder: "%d issue(s) due in the next days"
@@ -620,3 +620,5 enumeration_doc_categories: Dokumentenkategorien
620 620 enumeration_activities: Aktivitäten (Zeiterfassung)
621 621 text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
622 622 label_and_its_subprojects: %s and its subprojects
623 mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
624 mail_subject_reminder: "%d issue(s) due in the next days"
@@ -91,6 +91,8 mail_body_account_information_external: You can use your "%s" account to log in.
91 91 mail_body_account_information: Your account information
92 92 mail_subject_account_activation_request: %s account activation request
93 93 mail_body_account_activation_request: 'A new user (%s) has registered. His account is pending your approval:'
94 mail_subject_reminder: "%d issue(s) due in the next days"
95 mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
94 96
95 97 gui_validation_error: 1 error
96 98 gui_validation_error_plural: %d errors
@@ -622,3 +622,5 error_scm_annotate: "No existe la entrada o no ha podido ser anotada"
622 622 label_planning: Planificación
623 623 text_subprojects_destroy_warning: 'Sus subprojectos: %s también se eliminarán'
624 624 label_and_its_subprojects: %s and its subprojects
625 mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
626 mail_subject_reminder: "%d issue(s) due in the next days"
@@ -619,3 +619,5 error_scm_annotate: "Merkintää ei ole tai siihen ei voi lisätä selityksiä."
619 619 label_planning: Suunnittelu
620 620 text_subprojects_destroy_warning: 'Tämän alaprojekti(t): %s tullaan myös poistamaan.'
621 621 label_and_its_subprojects: %s and its subprojects
622 mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
623 mail_subject_reminder: "%d issue(s) due in the next days"
@@ -91,6 +91,8 mail_body_account_information_external: Vous pouvez utiliser votre compte "%s" p
91 91 mail_body_account_information: Paramètres de connexion de votre compte
92 92 mail_subject_account_activation_request: "Demande d'activation d'un compte %s"
93 93 mail_body_account_activation_request: "Un nouvel utilisateur (%s) s'est inscrit. Son compte nécessite votre approbation:"
94 mail_subject_reminder: "%d demande(s) arrivent à échéance"
95 mail_body_reminder: "%d demande(s) qui vous sont assignées arrivent à échéance dans les %d prochains jours:"
94 96
95 97 gui_validation_error: 1 erreur
96 98 gui_validation_error_plural: %d erreurs
@@ -619,3 +619,5 error_scm_annotate: "הכניסה לא קיימת או שלא ניתן לתאר
619 619 label_planning: תכנון
620 620 text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
621 621 label_and_its_subprojects: %s and its subprojects
622 mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
623 mail_subject_reminder: "%d issue(s) due in the next days"
@@ -620,3 +620,5 default_activity_development: Fejlesztés
620 620 enumeration_issue_priorities: Feladat prioritások
621 621 enumeration_doc_categories: Dokumentum kategóriák
622 622 enumeration_activities: Tevékenységek (idő rögzítés)
623 mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
624 mail_subject_reminder: "%d issue(s) due in the next days"
@@ -619,3 +619,5 error_scm_annotate: "The entry does not exist or can not be annotated."
619 619 label_planning: Planning
620 620 text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
621 621 label_and_its_subprojects: %s and its subprojects
622 mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
623 mail_subject_reminder: "%d issue(s) due in the next days"
@@ -620,3 +620,5 error_scm_annotate: "エントリが存在しない、もしくはアノテー
620 620 label_planning: 計画
621 621 text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
622 622 label_and_its_subprojects: %s and its subprojects
623 mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
624 mail_subject_reminder: "%d issue(s) due in the next days"
@@ -619,3 +619,5 error_scm_annotate: "The entry does not exist or can not be annotated."
619 619 label_planning: Planning
620 620 text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
621 621 label_and_its_subprojects: %s and its subprojects
622 mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
623 mail_subject_reminder: "%d issue(s) due in the next days"
@@ -621,3 +621,5 label_planning: Planavimas
621 621 text_subprojects_destroy_warning: 'Šis(ie) subprojektas(ai): %s taip pat bus ištrintas(i).'
622 622 label_and_its_subprojects: %s projektas ir jo subprojektai
623 623
624 mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
625 mail_subject_reminder: "%d issue(s) due in the next days"
@@ -620,3 +620,5 error_scm_annotate: "The entry does not exist or can not be annotated."
620 620 label_planning: Planning
621 621 text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
622 622 label_and_its_subprojects: %s and its subprojects
623 mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
624 mail_subject_reminder: "%d issue(s) due in the next days"
@@ -620,3 +620,5 default_activity_development: Utvikling
620 620 enumeration_issue_priorities: Sakssprioriteringer
621 621 enumeration_doc_categories: Dokument-kategorier
622 622 enumeration_activities: Aktiviteter (tidssporing)
623 mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
624 mail_subject_reminder: "%d issue(s) due in the next days"
@@ -619,3 +619,5 error_scm_annotate: "Wpis nie istnieje lub nie można do niego dodawać adnotacj
619 619 label_planning: Planning
620 620 text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
621 621 label_and_its_subprojects: %s and its subprojects
622 mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
623 mail_subject_reminder: "%d issue(s) due in the next days"
@@ -619,3 +619,5 label_planning: Planejamento
619 619 text_subprojects_destroy_warning: 'Seu(s) subprojeto(s): %s também serão excluídos.'
620 620 label_age: Age
621 621 label_and_its_subprojects: %s and its subprojects
622 mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
623 mail_subject_reminder: "%d issue(s) due in the next days"
@@ -619,3 +619,5 error_scm_annotate: "The entry does not exist or can not be annotated."
619 619 label_planning: Planning
620 620 text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
621 621 label_and_its_subprojects: %s and its subprojects
622 mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
623 mail_subject_reminder: "%d issue(s) due in the next days"
@@ -619,3 +619,5 error_scm_annotate: "The entry does not exist or can not be annotated."
619 619 label_planning: Planning
620 620 text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
621 621 label_and_its_subprojects: %s and its subprojects
622 mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
623 mail_subject_reminder: "%d issue(s) due in the next days"
@@ -623,3 +623,5 error_scm_annotate: "Данные отсутствуют или не могут
623 623 label_planning: Планирование
624 624 text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
625 625 label_and_its_subprojects: %s and its subprojects
626 mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
627 mail_subject_reminder: "%d issue(s) due in the next days"
@@ -620,3 +620,5 error_scm_annotate: "The entry does not exist or can not be annotated."
620 620 label_planning: Planning
621 621 text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
622 622 label_and_its_subprojects: %s and its subprojects
623 mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
624 mail_subject_reminder: "%d issue(s) due in the next days"
@@ -620,3 +620,5 error_scm_annotate: "The entry does not exist or can not be annotated."
620 620 label_planning: Planning
621 621 text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
622 622 label_and_its_subprojects: %s and its subprojects
623 mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
624 mail_subject_reminder: "%d issue(s) due in the next days"
@@ -622,3 +622,5 enumeration_issue_priorities: ความสำคัญของปัญห
622 622 enumeration_doc_categories: ประเภทเอกสาร
623 623 enumeration_activities: กิจกรรม (ใช้ในการติดตามเวลา)
624 624 label_and_its_subprojects: %s and its subprojects
625 mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
626 mail_subject_reminder: "%d issue(s) due in the next days"
@@ -621,3 +621,5 error_scm_annotate: "The entry does not exist or can not be annotated."
621 621 label_planning: Planning
622 622 text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
623 623 label_and_its_subprojects: %s and its subprojects
624 mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
625 mail_subject_reminder: "%d issue(s) due in the next days"
@@ -620,3 +620,5 default_activity_development: 開發
620 620 enumeration_issue_priorities: 項目優先權
621 621 enumeration_doc_categories: 文件分類
622 622 enumeration_activities: 活動 (時間追蹤)
623 mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
624 mail_subject_reminder: "%d issue(s) due in the next days"
@@ -619,4 +619,6 default_activity_development: 开发
619 619
620 620 enumeration_issue_priorities: 问题优先级
621 621 enumeration_doc_categories: 文档类别
622 enumeration_activities: 活动(时间跟踪) No newline at end of file
622 enumeration_activities: 活动(时间跟踪)mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
623 mail_subject_reminder: "%d issue(s) due in the next days"
624 mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
@@ -42,7 +42,7 issues_003:
42 42 category_id:
43 43 description: Error 281 is encountered when saving a recipe
44 44 tracker_id: 1
45 assigned_to_id:
45 assigned_to_id: 3
46 46 author_id: 2
47 47 status_id: 1
48 48 start_date: <%= 1.day.from_now.to_date.to_s(:db) %>
@@ -116,4 +116,13 class MailerTest < Test::Unit::TestCase
116 116 assert Mailer.deliver_register(token)
117 117 end
118 118 end
119
120 def test_reminders
121 ActionMailer::Base.deliveries.clear
122 Mailer.reminders(:days => 42)
123 assert_equal 1, ActionMailer::Base.deliveries.size
124 mail = ActionMailer::Base.deliveries.last
125 assert mail.bcc.include?('dlopper@somenet.foo')
126 assert mail.body.include?('Bug #3: Error 281 when updating a recipe')
127 end
119 128 end
General Comments 0
You need to be logged in to leave comments. Login now