├── .gitignore ├── Gemfile ├── LICENSE ├── README.md ├── app ├── models │ └── reminding_mailer.rb └── views │ ├── reminding_mailer │ ├── remind_user_issue_estimates.html.erb │ ├── remind_user_issue_estimates.text.erb │ ├── remind_user_issue_statuses.html.erb │ ├── remind_user_issue_statuses.text.erb │ ├── remind_user_issue_trackers.html.erb │ ├── remind_user_issue_trackers.text.erb │ ├── remind_user_past_due_issues.html.erb │ ├── remind_user_past_due_issues.text.erb │ ├── reminder_issue_email.html.erb │ ├── reminder_issue_email.text.erb │ ├── reminder_status_email.html.erb │ └── reminder_status_email.text.erb │ └── settings │ └── _reminder_settings.html.erb ├── config ├── locales │ └── en.yml ├── routes.rb └── schedule.rb ├── init.rb ├── lib ├── redmine_update_reminder │ └── patches │ │ └── user_patch.rb └── tasks │ └── reminder.rake └── test └── test_helper.rb /.gitignore: -------------------------------------------------------------------------------- 1 | *.rbc 2 | *.sassc 3 | .sass-cache 4 | capybara-*.html 5 | .rspec 6 | /.bundle 7 | /vendor/bundle 8 | /log/* 9 | /tmp/* 10 | /db/*.sqlite3 11 | /public/system/* 12 | /coverage/ 13 | /spec/tmp/* 14 | **.orig 15 | rerun.txt 16 | pickle-email-*.html 17 | *~ -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gem 'whenever',">=0.8.4" -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Hisham Malik 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Redmine Update Reminder: 2 | ======================== 3 | 4 | This plugin sends reminder email to assigned users if an issue is not updated within specified duration. 5 | 6 | Functionality: 7 | ============== 8 | 9 | All the mails are cc'ed to a single Group (generally scrum master/HR) along with the assigned user for notifying that a given task needs updation. 10 | The duration for each kind of tracker can be set in settings. 11 | Duration takes input in days, and can be decimal point numbers (for instance .5 for 12 hours). 12 | Emails Headers and Footers are configurable for personalization. 13 | 14 | Installation: 15 | ============= 16 | 17 | The plugin is available for download from 18 | `github.com:arkhitech/redmine_update_reminder` 19 | 20 | Go to redmine's plugins directory and `wheneverize` the downloaded redmine_update_reminder plugin directory. 21 | Open config directory and edit schedule.rb 22 | 23 | 24 | for example: 25 | 26 | set :environment, "production" 27 | every 1.day do 28 | rake "redmine_update_reminder:send_user_reminders" 29 | end 30 | 31 | This will check for all issues that have not been updated in the specified duration by the user and send them email. 32 | 33 | Check whenever gems documentation for detailed description of its working. 34 | 35 | There are two rake tasks. These can be run without scheduling using this command 36 | 37 | RAILS_ENV=production rake redmine_update_reminder:send_issue_reminders 38 | 39 | RAILS_ENV=production rake redmine_update_reminder:send_user_reminders 40 | -------------------------------------------------------------------------------- /app/models/reminding_mailer.rb: -------------------------------------------------------------------------------- 1 | class RemindingMailer < ActionMailer::Base 2 | layout 'mailer' 3 | default from: Setting.mail_from 4 | helper :issues 5 | include Redmine::Utils::DateCalculation 6 | 7 | def self.default_url_options 8 | ::Mailer.default_url_options 9 | end 10 | 11 | def cc_group_email_addresses 12 | @cc_group_email_addresses ||= begin 13 | cc_group = Setting.plugin_redmine_update_reminder['cc'] 14 | if cc_group.present? 15 | Group.includes(:users).find(cc_group).users.map(&:mail) 16 | else 17 | [] 18 | end 19 | 20 | end 21 | end 22 | private :cc_group_email_addresses 23 | 24 | def cc_role_email_addresses(user) 25 | cc_role_ids = Setting.plugin_redmine_update_reminder['cc_roles'] 26 | if cc_role_ids.present? 27 | User.active.preload(:email_address).joins(members: :member_roles).where("#{Member.table_name}.project_id" => user.projects.pluck(:id)). 28 | where("#{MemberRole.table_name}.role_id IN (?)", cc_role_ids).map(&:email_address).map(&:address) 29 | else 30 | [] 31 | end 32 | end 33 | private :cc_role_email_addresses 34 | 35 | def cc_email_addresses(user) 36 | return [] if non_working_week_days.include?(Date.today.cwday) 37 | cc_email_addresses = cc_group_email_addresses 38 | cc_email_addresses += cc_role_email_addresses(user) 39 | cc_email_addresses.uniq 40 | end 41 | private :cc_email_addresses 42 | 43 | def reminder_issue_email(user, issue, updated_since) 44 | @user = user 45 | @issue = issue 46 | @updated_since = updated_since 47 | 48 | mail(to: @user.mail, subject: @issue.subject, cc: cc_email_addresses(user)) 49 | end 50 | 51 | def reminder_status_email(user, issue, updated_since) 52 | @user = user 53 | @issue = issue 54 | @updated_since = updated_since 55 | 56 | mail(to: user.mail, subject: @issue.subject, cc: cc_email_addresses(user)) 57 | end 58 | 59 | def remind_user_issue_trackers(user, issues_with_updated_since) 60 | @user = user 61 | @issues_with_updated_since = issues_with_updated_since 62 | subject = I18n.t('update_reminder.issue_tracker_update_required', 63 | user_name: user.name, issue_count: @issues_with_updated_since.count) 64 | 65 | mail(to: user.mail, subject: subject, cc: cc_email_addresses(user)) 66 | end 67 | 68 | def remind_user_issue_statuses(user, issues_with_updated_since) 69 | @user = user 70 | @issues_with_updated_since = issues_with_updated_since 71 | 72 | subject = I18n.t('update_reminder.issue_status_update_required', 73 | user_name: user.name, issue_count: @issues_with_updated_since.count) 74 | mail(to: user.mail, subject: subject, cc: cc_email_addresses(user)) 75 | end 76 | 77 | def remind_user_past_due_issues(user, issues) 78 | @user = user 79 | @issues = issues 80 | 81 | subject = I18n.t('update_reminder.past_due_issue_update_required', 82 | user_name: user.name, issue_count: @issues.count) 83 | mail(to: user.mail, subject: subject, cc: cc_email_addresses(user)) 84 | end 85 | 86 | def remind_user_issue_estimates(user, issues_with_updated_since) 87 | @user = user 88 | @issues_with_updated_since = issues_with_updated_since 89 | 90 | subject = I18n.t('update_reminder.issue_estimate_required', 91 | user_name: user.name, issue_count: @issues_with_updated_since.count) 92 | mail(to: user.mail, subject: subject, cc: cc_email_addresses(user)) 93 | end 94 | 95 | end -------------------------------------------------------------------------------- /app/views/reminding_mailer/remind_user_issue_estimates.html.erb: -------------------------------------------------------------------------------- 1 | 2 | <%=@user.name%>, 3 |

4 | <%= Setting.plugin_redmine_update_reminder['header'] %> 5 | 6 | 15 | 16 | Immediate attention is required. 17 |

18 | 19 | <%= Setting.plugin_redmine_update_reminder['footer'] %> 20 | -------------------------------------------------------------------------------- /app/views/reminding_mailer/remind_user_issue_estimates.text.erb: -------------------------------------------------------------------------------- 1 | <%=@user.name%>, 2 | 3 | <%= Setting.plugin_redmine_update_reminder['header'] %> 4 | 5 | <% @issues_with_updated_since.each_with_index do |issue_with_updated_since, i| -%> 6 | <%issue, updated_since = issue_with_updated_since%> 7 | <%= i+1%>. <%= link_to_issue(issue, project: true, only_path: false) %> 8 | is missing estimates for *<%=time_ago_in_words(updated_since)%>*. 9 | <% end -%> 10 | 11 | 12 | Immediate attention is required. 13 | 14 | <%= Setting.plugin_redmine_update_reminder['footer'] %> -------------------------------------------------------------------------------- /app/views/reminding_mailer/remind_user_issue_statuses.html.erb: -------------------------------------------------------------------------------- 1 | 2 | <%=@user.name%>, 3 |

4 | <%= Setting.plugin_redmine_update_reminder['header'] %> 5 | 6 | 15 | 16 | Immediate attention is required. 17 |

18 | 19 | <%= Setting.plugin_redmine_update_reminder['footer'] %> 20 | -------------------------------------------------------------------------------- /app/views/reminding_mailer/remind_user_issue_statuses.text.erb: -------------------------------------------------------------------------------- 1 | <%=@user.name%>, 2 | 3 | <%= Setting.plugin_redmine_update_reminder['header'] %> 4 | 5 | <% @issues_with_updated_since.each_with_index do |issue_with_updated_since, i| -%> 6 | <%issue, updated_since = issue_with_updated_since%> 7 | <%= i+1%>. <%= link_to_issue(issue, project: true, only_path: false) %> 8 | has been in *<%=issue.status.name%>* status for *<%=time_ago_in_words(updated_since)%>*. 9 | <% end -%> 10 | 11 | 12 | Immediate attention is required. 13 | 14 | <%= Setting.plugin_redmine_update_reminder['footer'] %> -------------------------------------------------------------------------------- /app/views/reminding_mailer/remind_user_issue_trackers.html.erb: -------------------------------------------------------------------------------- 1 | <%=@user.name%>, 2 |

3 | <%= Setting.plugin_redmine_update_reminder['header'] %> 4 | 5 | 14 | 15 | Immediate attention is required. 16 | 17 |
18 | <%= Setting.plugin_redmine_update_reminder['footer'] %> -------------------------------------------------------------------------------- /app/views/reminding_mailer/remind_user_issue_trackers.text.erb: -------------------------------------------------------------------------------- 1 | <%=@user.name%>, 2 | 3 | <%= Setting.plugin_redmine_update_reminder['header'] %> 4 | 5 | <% @issues_with_updated_since.each_with_index do |issue_with_updated_since, i| -%> 6 | <%issue, updated_since = issue_with_updated_since%> 7 | <% i+1%>. <%= link_to_issue(issue, project: true, only_path: false) %> 8 | has not been updated for *<%=time_ago_in_words(updated_since)%>*. 9 | <% end -%> 10 | 11 | 12 | Immediate attention is required. 13 | 14 |
15 | <%= Setting.plugin_redmine_update_reminder['footer'] %> -------------------------------------------------------------------------------- /app/views/reminding_mailer/remind_user_past_due_issues.html.erb: -------------------------------------------------------------------------------- 1 | <%=@user.name%>, 2 |

3 | <%= Setting.plugin_redmine_update_reminder['header'] %> 4 | 5 | 14 | 15 | Immediate attention is required. 16 | 17 |
18 | <%= Setting.plugin_redmine_update_reminder['footer'] %> -------------------------------------------------------------------------------- /app/views/reminding_mailer/remind_user_past_due_issues.text.erb: -------------------------------------------------------------------------------- 1 | <%=@user.name%>, 2 | 3 | <%= Setting.plugin_redmine_update_reminder['header'] %> 4 | 5 | <% @issues.each_with_index do |issue, i| -%> 6 | <% i+1%>. <%= link_to_issue(issue, project: true, only_path: false) %> 7 | has been past due for *<%=time_ago_in_words(issue.due_date)%>* 8 | and last update was provided *<%=time_ago_in_words(issue.updated_on)%>* ago. 9 | 10 | <% end -%> 11 | 12 | 13 | Immediate attention is required. 14 | 15 |
16 | <%= Setting.plugin_redmine_update_reminder['footer'] %> -------------------------------------------------------------------------------- /app/views/reminding_mailer/reminder_issue_email.html.erb: -------------------------------------------------------------------------------- 1 | 2 | <%= Redmine::WikiFormatting.to_html(Setting.text_formatting, 3 | Setting.plugin_redmine_update_reminder['header']).html_safe %> 4 | 5 | 6 | <%= @user.name %>, 7 |

8 | <%= link_to("#{h(@issue.tracker)} ##{@issue.id}", 9 | {controller: 'issues', action: 'show', id: @issue, only_path: false}) %> 10 | has not been updated for <%=time_ago_in_words(@updated_since)%>. Immediate attention is required. 11 |

12 | 13 | 14 | <%= Redmine::WikiFormatting.to_html(Setting.text_formatting, 15 | Setting.plugin_redmine_update_reminder['footer']).html_safe %> 16 | 17 |
18 | 19 | <%= Setting.plugin_redmine_update_reminder['footer'] %> 20 | -------------------------------------------------------------------------------- /app/views/reminding_mailer/reminder_issue_email.text.erb: -------------------------------------------------------------------------------- 1 | <%= Setting.plugin_redmine_update_reminder['header'] %> 2 | 3 | <%=@user.name%>, 4 | 5 | <%="#{h(@issue.tracker)} ##{@issue.id}"%>, 6 | <%= url_for({controller: 'issues', action: 'show', id: @issue}) %> 7 | has not been updated for *<%=time_ago_in_words(@updated_since)%>*. 8 | 9 | Immediate attention is required. 10 | 11 | <%= Setting.plugin_redmine_update_reminder['footer'] %> -------------------------------------------------------------------------------- /app/views/reminding_mailer/reminder_status_email.html.erb: -------------------------------------------------------------------------------- 1 | 2 | <%= Redmine::WikiFormatting.to_html(Setting.text_formatting, 3 | Setting.plugin_redmine_update_reminder['header']).html_safe %> 4 | 5 | 6 | <%= @user.name %>, 7 |

8 | <%= link_to("#{h(@issue.tracker)} ##{@issue.id}", 9 | {controller: 'issues', action: 'show', id: @issue, only_path: false}) %> 10 | has been in <%=@issue.status.name%> status for <%=time_ago_in_words(@updated_since)%>. Attention is required at the earliest. 11 |

12 | 13 | 14 | <%= Redmine::WikiFormatting.to_html(Setting.text_formatting, 15 | Setting.plugin_redmine_update_reminder['footer']).html_safe %> 16 | 17 |
18 | 19 | <%= Setting.plugin_redmine_update_reminder['footer'] %> 20 | -------------------------------------------------------------------------------- /app/views/reminding_mailer/reminder_status_email.text.erb: -------------------------------------------------------------------------------- 1 | <%= Setting.plugin_redmine_update_reminder['header'] %> 2 | 3 | <%=@user.name%>, 4 | 5 | <%="#{h(@issue.tracker)} ##{@issue.id}"%>, 6 | <%= url_for({controller: 'issues', action: 'show', id: @issue}) %> has been in 7 | *<%=@issue.status.name%>* status for *<%=time_ago_in_words(@updated_since)%>*. 8 | 9 | Attention is required at the earliest. 10 | 11 | <%= Setting.plugin_redmine_update_reminder['footer'] %> -------------------------------------------------------------------------------- /app/views/settings/_reminder_settings.html.erb: -------------------------------------------------------------------------------- 1 | 2 |

3 | 4 | <%= select_tag('settings[remind_group]', 5 | options_for_select(Group.all.collect{|g| [g.name, g.id]}, @settings['remind_group'])) %> 6 |

7 |

8 | 9 | <%= select_tag('settings[cc]', 10 | options_for_select(Group.all.collect{|g| [g.name, g.id]}, @settings['cc'])) %> 11 |

12 |

13 | 14 | <%= select_tag('settings[cc_roles]', 15 | options_for_select(Role.all.collect{|r| [r.name, r.id]}, @settings['cc_roles']), multiple: true) %> 16 |

17 | 18 |

19 | 20 | <%= text_area_tag 'settings[header]', @settings['header'] %> 21 |

22 | 23 |

24 | 25 | <%= text_area_tag 'settings[footer]', @settings['footer'] %> 26 |

27 | 28 | 29 |

30 | 33 |

34 |
35 | 36 | 37 | 38 | 39 | <%IssueStatus.where(is_closed: false).each do |issue_status|%> 40 | 41 | <%end%> 42 | 43 | <%Tracker.all.each do |tracker| -%> 44 | 45 | 46 | 50 | <%IssueStatus.where(is_closed: false).each do |issue_status| -%> 51 | 55 | <%end%> 56 | 57 | <%end%> 58 |
TrackerGeneral<%=issue_status.name%>
<%=tracker.name%> 47 | <%= text_field_tag "settings[#{tracker.id}_update_duration]", 48 | @settings["#{tracker.id}_update_duration"], size: 3 %> 49 | 52 | <%= text_field_tag "settings[#{tracker.id}-status-#{issue_status.id}-update]", 53 | @settings["#{tracker.id}-status-#{issue_status.id}-update"], size: 3 %> 54 |
59 |
60 | 61 |

62 | 65 |

66 |
67 | 68 | 69 | 70 | <%IssueStatus.where(is_closed: false).each do |issue_status|%> 71 | 72 | <%end%> 73 | 74 | <%Tracker.all.each do |tracker| -%> 75 | 76 | 77 | <%IssueStatus.where(is_closed: false).each do |issue_status| -%> 78 | 82 | <%end%> 83 | 84 | <%end%> 85 |
Tracker<%=issue_status.name%>
<%=tracker.name%> 79 | <%= text_field_tag "settings[#{tracker.id}-status-#{issue_status.id}-estimate]", 80 | @settings["#{tracker.id}-status-#{issue_status.id}-estimate"], size: 3 %> 81 |
86 |
87 | -------------------------------------------------------------------------------- /config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # English strings go here for Rails i18n 2 | en: 3 | update_reminder: 4 | issue_tracker_update_required: '%{user_name} - Tracker - Missing update on %{issue_count} issues!' 5 | issue_status_update_required: '%{user_name} - Missing status update on %{issue_count} issues!' 6 | past_due_issue_update_required: '%{user_name} - Past Due - Missing update on %{issue_count} issues!' 7 | issue_estimate_required: '%{user_name} - Missing estimates on %{issue_count} issues!' 8 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | # Plugin's routes 2 | # See: http://guides.rubyonrails.org/routing.html 3 | -------------------------------------------------------------------------------- /config/schedule.rb: -------------------------------------------------------------------------------- 1 | # Use this file to easily define all of your cron jobs. 2 | # 3 | # It's helpful, but not entirely necessary to understand cron before proceeding. 4 | # http://en.wikipedia.org/wiki/Cron 5 | 6 | # Example: 7 | # 8 | # set :output, "/path/to/my/cron_log.log" 9 | # 10 | # every 2.hours do 11 | # command "/usr/bin/some_great_command" 12 | # runner "MyModel.some_method" 13 | # rake "some:great:rake:task" 14 | # end 15 | # 16 | #set :environment, "production" 17 | #every 15.minutes do 18 | #rake "trackers" 19 | #end 20 | # every 4.days do 21 | # runner "AnotherModel.prune_old_records" 22 | # end 23 | 24 | # Learn more: http://github.com/javan/whenever 25 | -------------------------------------------------------------------------------- /init.rb: -------------------------------------------------------------------------------- 1 | Rails.configuration.to_prepare do 2 | require_dependency 'user' 3 | User.send(:include, RedmineUpdateReminder::Patches::UserPatch) 4 | end 5 | 6 | Redmine::Plugin.register :redmine_update_reminder do 7 | name 'Redmine Update Reminder' 8 | author 'Arkhitech' 9 | url 'https://github.com/arkhitech/redmine_update_reminder' 10 | author_url 'http://www.arkhitech.com' 11 | description 'This is a plugin for Redmine which sends a reminder email to the assignee working on a task, whose status is not updated with-in allowed duration' 12 | version '1.1' 13 | settings(default: { 14 | 'header' => 'Missing Required Task Update', 15 | 'footer' => 'powered by arkhitech.com', 16 | }, partial: 'settings/reminder_settings') 17 | end 18 | -------------------------------------------------------------------------------- /lib/redmine_update_reminder/patches/user_patch.rb: -------------------------------------------------------------------------------- 1 | module RedmineUpdateReminder 2 | module Patches 3 | module UserPatch 4 | def self.included(base) 5 | 6 | base.class_eval do 7 | unloadable 8 | has_many :members, inverse_of: :user 9 | end 10 | end 11 | 12 | end 13 | end 14 | end -------------------------------------------------------------------------------- /lib/tasks/reminder.rake: -------------------------------------------------------------------------------- 1 | namespace :redmine_update_reminder do 2 | require 'redmine/utils' 3 | include Redmine::Utils::DateCalculation 4 | 5 | def send_user_issue_estimates_reminders(issue_status_ids, user, mailed_issue_ids) 6 | issues_with_updated_since = [] 7 | trackers = Tracker.all 8 | trackers.each do |tracker| 9 | issue_status_ids.each do |issue_status_id| 10 | estimate_update = Setting.plugin_redmine_update_reminder["#{tracker.id}-status-#{issue_status_id}-estimate"].to_f 11 | 12 | if estimate_update > 0 13 | oldest_estimated_since = estimate_update.days.ago 14 | 15 | issues = Issue.where(tracker_id: tracker.id, assigned_to_id: user.id, status_id: issue_status_id). 16 | where('estimated_hours IS NULL OR estimated_hours <= 0'). 17 | where.not(id: mailed_issue_ids.to_a) 18 | 19 | issues.each do |issue| 20 | if issue.updated_on < oldest_estimated_since 21 | issues_with_updated_since << [issue, issue.updated_on] 22 | end 23 | end 24 | end 25 | end 26 | end 27 | RemindingMailer.remind_user_issue_estimates(user, 28 | issues_with_updated_since).deliver_now if issues_with_updated_since.count > 0 29 | end 30 | 31 | def send_user_past_due_issues_reminders(issue_status_ids, user, mailed_issue_ids) 32 | issues = Issue.where(assigned_to_id: user.id, 33 | status_id: issue_status_ids).where('due_date < ?', Date.today). 34 | where.not(id: mailed_issue_ids.to_a) 35 | 36 | RemindingMailer.remind_user_past_due_issues(user, issues).deliver_now if issues.exists? 37 | end 38 | def send_user_tracker_reminders(issue_status_ids, user, mailed_issue_ids) 39 | trackers = Tracker.all 40 | issues_with_updated_since = [] 41 | trackers.each do |tracker| 42 | update_duration = Setting.plugin_redmine_update_reminder["#{tracker.id}_update_duration"].to_f 43 | if update_duration > 0 44 | 45 | updated_since = update_duration.days.ago 46 | issues = Issue.where(tracker_id: tracker.id, assigned_to_id: user.id, status_id: issue_status_ids). 47 | where('updated_on < ?', updated_since).where.not(id: mailed_issue_ids.to_a) 48 | 49 | issues.find_each do |issue| 50 | issues_with_updated_since << [issue, issue.updated_on] 51 | end 52 | end 53 | end 54 | RemindingMailer.remind_user_issue_trackers(user, 55 | issues_with_updated_since).deliver_now if issues_with_updated_since.count > 0 56 | end 57 | 58 | def send_user_status_reminders(issue_status_ids, user, mailed_issue_ids) 59 | issues_with_updated_since = [] 60 | trackers = Tracker.all 61 | trackers.each do |tracker| 62 | issue_status_ids.each do |issue_status_id| 63 | update_duration = Setting.plugin_redmine_update_reminder["#{tracker.id}-status-#{issue_status_id}-update"].to_f 64 | if update_duration > 0 65 | 66 | oldest_status_date = update_duration.days.ago 67 | issues = Issue.where(tracker_id: tracker.id, assigned_to_id: user.id, status_id: issue_status_id). 68 | where.not(id: mailed_issue_ids.to_a) 69 | 70 | issues.find_each do |issue| 71 | issue.journals.each do |journal| 72 | journal.visible_details do |detail| 73 | if detail[:prop_key] == 'status_id' && 74 | detail[:new_value] == issue_status_id && 75 | oldest_status_date > journal.created_on 76 | issues_with_updated_since << [issue, journal.created_on] 77 | break 78 | end 79 | end 80 | end 81 | end 82 | end 83 | end 84 | end 85 | RemindingMailer.remind_user_issue_statuses(user, 86 | issues_with_updated_since).deliver_now if issues_with_updated_since.count > 0 87 | end 88 | 89 | def send_issue_status_reminders(issue_status_ids, user_ids, mailed_issue_ids) 90 | Tracker.all.each do |tracker| 91 | issue_status_ids.each do |issue_status_id| 92 | update_duration = Setting.plugin_redmine_update_reminder["#{tracker.id}-status-#{issue_status_id}-update"].to_f 93 | if update_duration > 0 94 | 95 | oldest_status_date = update_duration.days.ago 96 | issues = Issue.where(tracker_id: tracker.id, assigned_to_id: user_ids, 97 | status_id: issue_status_id).where.not(id: mailed_issue_ids.to_a) 98 | 99 | issues.find_each do |issue| 100 | issue.journals.each do |journal| 101 | journal.visible_details do |detail| 102 | if detail[:prop_key] == 'status_id' && 103 | detail[:new_value] == issue_status_id && 104 | oldest_status_date > journal.created_on 105 | RemindingMailer.reminder_status_email(issue.assigned_to, issue, 106 | journal.created_on).deliver_now 107 | mailed_issue_ids << issue.id 108 | break 109 | end 110 | end 111 | end 112 | end 113 | end 114 | end 115 | end 116 | end 117 | 118 | def send_issue_tracker_reminders(issue_status_ids, user_ids, mailed_issue_ids) 119 | trackers = Tracker.all 120 | 121 | trackers.each do |tracker| 122 | update_duration = Setting.plugin_redmine_update_reminder["#{tracker.id}_update_duration"].to_f 123 | if update_duration > 0 124 | 125 | updated_since = update_duration.days.ago 126 | issues = Issue.where(tracker_id: tracker.id, assigned_to_id: user_ids, 127 | status_id: issue_status_ids).where('updated_on < ?', updated_since). 128 | where.not(id: mailed_issue_ids.to_a) 129 | 130 | issues.find_each do |issue| 131 | RemindingMailer.reminder_issue_email(issue.assigned_to, issue, issue.updated_on).deliver_now 132 | mailed_issue_ids << issue.id 133 | end 134 | end 135 | end 136 | 137 | end 138 | 139 | task send_user_reminders: :environment do 140 | open_issue_status_ids = IssueStatus.where(is_closed: false).pluck('id') 141 | remind_group = Setting.plugin_redmine_update_reminder['remind_group'] 142 | users = Group.includes(:users).find(remind_group).users 143 | 144 | users.find_each do |user| 145 | mailed_issue_ids = Set.new 146 | send_user_tracker_reminders(open_issue_status_ids, user, mailed_issue_ids) 147 | send_user_status_reminders(open_issue_status_ids, user, mailed_issue_ids) 148 | send_user_past_due_issues_reminders(open_issue_status_ids, user, mailed_issue_ids) 149 | send_user_issue_estimates_reminders(open_issue_status_ids, user, mailed_issue_ids) 150 | end 151 | end 152 | 153 | task send_issue_reminders: :environment do 154 | open_issue_status_ids = IssueStatus.where(is_closed: false).pluck('id') 155 | 156 | remind_group = Setting.plugin_redmine_update_reminder['remind_group'] 157 | user_ids = Group.includes(:users).find(remind_group).user_ids 158 | 159 | mailed_issue_ids = Set.new 160 | 161 | send_issue_tracker_reminders(open_issue_status_ids, user_ids, mailed_issue_ids) 162 | send_issue_status_reminders(open_issue_status_ids, user_ids, mailed_issue_ids) 163 | end 164 | end 165 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | # Load the Redmine helper 2 | require File.expand_path(File.dirname(__FILE__) + '/../../../test/test_helper') 3 | --------------------------------------------------------------------------------