Skip to content

Commit

Permalink
1st commit
Browse files Browse the repository at this point in the history
  • Loading branch information
preciousplum authored and test committed Jun 10, 2018
1 parent 6c55e7e commit d0c2075
Show file tree
Hide file tree
Showing 13 changed files with 285 additions and 2 deletions.
3 changes: 2 additions & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
Expand Down Expand Up @@ -198,4 +199,4 @@
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
limitations under the License.
31 changes: 30 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,31 @@
# custom_users_as_assignees
# Expand Custom Users as Assignees plugin
Redmine plugin for adding assignee functionality includes default query and reminder to custom users

This plugin can realize multiple assignees on issues for review tasks by multiple people use case and so on.
* send email notifications to users specified in the **Custom field** of the **User** format **when they are added or removed.** on issues.
* send reminder to users specified in the **Custom field** of the **User** format
* issues that users specified in the **Custom field** of the **User** format are queried as assignees such as **"My page" > "Issues assigned to me"**
*) Use "just Assignee" field instead to proceed query by the original scope of "assignee".

## Installation

1. To install the plugin

`git clone` or copy an unarchived plugin(archived file is [here](https://github.com/hidakatsuya/redmine_default_custom_query/releases)) to `plugins/custom_users_as_assignees` on your Redmine path.

cd {RAILS_ROOT}/plugins
$ git clone https://github.com/preciousplum/custom_users_as_assignees

2. Restart Redmine.

Now you should be able to see the plugin in **Administration > Plugins**.
*) Migration is **not** required.

## Compatibility
This plugin version is compatible with Redmine 3.3.0 and later in principle.
However, this plugin has been tested on Redmine 3.4.5 only currently.

## Special Thanks
This plugin was developed based on notify_custom_users plugin.
https://github.com/Restream/notify_custom_users

3 changes: 3 additions & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# English strings go here for Rails i18n
en:
field_just_assigned_to: just Assignee
2 changes: 2 additions & 0 deletions config/locales/ja.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ja:
field_just_assigned_to: 「担当者」のみ
2 changes: 2 additions & 0 deletions config/locales/zh-TW.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
zh:
field_just_assigned_to: 正「指派给」
2 changes: 2 additions & 0 deletions config/locales/zh.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
zh:
field_just_assigned_to: 只「指派给」
2 changes: 2 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Plugin's routes
# See: http://guides.rubyonrails.org/routing.html
33 changes: 33 additions & 0 deletions init.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
require 'redmine'

ActionDispatch::Callbacks.to_prepare do
require_dependency 'project'
require_dependency 'principal'
require_dependency 'user'
require_dependency 'issue'
require_dependency 'issue_query'
require_dependency 'mailer'

unless Issue.included_modules.include? CustomUsersAsAssignees::IssuePatch
Issue.send :include, CustomUsersAsAssignees::IssuePatch
end
unless User.included_modules.include? CustomUsersAsAssignees::UserPatch
User.send :include, CustomUsersAsAssignees::UserPatch
end
unless User.included_modules.include? CustomUsersAsAssignees::IssueQueryPatch
IssueQuery.send :include, CustomUsersAsAssignees::IssueQueryPatch
end
unless User.included_modules.include? CustomUsersAsAssignees::MailerPatch
Mailer.send :include, CustomUsersAsAssignees::MailerPatch
end

end

Redmine::Plugin.register :custom_users_as_assignees do
name 'Expand Custom Users as Assignees plugin'
author 'preciousplum'
description 'Redmine plugin for adding assignee functionality includes default query and reminder to custom users'
version '0.0.1'
url 'https://github.com/preciousplum/custom_users_as_assignees'
author_url 'https://github.com/preciousplum/'
end
61 changes: 61 additions & 0 deletions lib/custom_users_as_assignees/issue_patch.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
module CustomUsersAsAssignees
module IssuePatch

def self.included(base)
base.send :include, InstanceMethods
base.send :has_many, :customized, :class_name => 'CustomValue', :foreign_key => 'customized_id'
base.class_eval do
alias_method_chain :notified_users, :custom_users
end
end

module InstanceMethods
# find users selected in custom fields with type 'user'
def custom_users
custom_user_values = custom_field_values.select do |v|
v.custom_field.field_format == "user"
end
custom_user_ids = custom_user_values.map(&:value).flatten
custom_user_ids.reject! { |id| id.blank? }
User.find(custom_user_ids)
end

# added or removed users selected in custom fields with type 'user'
def custom_users_added_or_removed
if last_journal_id && (journal = journals.find(last_journal_id))
custom_user_added_ids = []
custom_user_removed_ids = []
journal.details.each do |det|
if det.property == 'cf'
custom_field_id = det.prop_key
if CustomField.find_by_id(custom_field_id).field_format == 'user'
custom_user_added_ids << det.value if det.value.present?
custom_user_removed_ids << det.old_value if det.old_value.present?
end
end
end
custom_user_added_ids.uniq!
custom_user_removed_ids.uniq!
custom_user_changed_ids = (custom_user_added_ids + custom_user_removed_ids).uniq
User.find(custom_user_changed_ids)
else
[]
end
end

# add 'custom users' to notified_users
def notified_users_with_custom_users
notified = notified_users_without_custom_users

custom_users_current = custom_users
custom_users_changed = custom_users_added_or_removed

notified_custom_users = (custom_users_current + custom_users_changed).select do |u|
u.active? && u.notify_custom_user?(self, custom_users_current, custom_users_changed) && visible?(u)
end
notified += notified_custom_users
notified.uniq
end
end
end
end
62 changes: 62 additions & 0 deletions lib/custom_users_as_assignees/issue_query_patch.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
module CustomUsersAsAssignees
module IssueQueryPatch
def self.included(base)
base.send :include, InstanceMethods
base.class_eval do
alias_method_chain :initialize_available_filters, :extra_filters
end
end

module InstanceMethods
def initialize_available_filters_with_extra_filters
return @initialize_available_filters if @initialize_available_filters
initialize_available_filters_without_extra_filters

add_available_filter("just_assigned_to_id",
:type => :list_optional, :values => lambda { assigned_to_values }
)

@initialize_available_filters

end

# call the original procedure for assigned_to_id_field
def sql_for_just_assigned_to_id_field(field, operator, value)
# "me" value substitution
if "just_assigned_to_id".include?(field)
if value.delete("me")
if User.current.logged?
value.push(User.current.id.to_s)
else
value.push("0")
end
end
end
'(' + sql_for_field("assigned_to_id", operator, value, Issue.table_name, "assigned_to_id") + ')'
end

# overwrite the procedure for assigned_to_id_field to query all custom users field
def sql_for_assigned_to_id_field(field, operator, value)
targets = value;
value.each do |target|
begin
targets += User.find(target).group_ids.map(&:to_s)
targets.uniq!
rescue
end
end

int_values = targets.to_s.scan(/[+-]?\d+/).map(&:to_i).join(",")
if int_values.present?
subquery = "#{Issue.table_name}.id #{ operator == '=' ? 'IN' : 'NOT IN' } "
subquery += "(SELECT #{Issue.table_name}.id FROM issues" +
" LEFT OUTER JOIN custom_values ON custom_values.customized_id = issues.id AND custom_values.customized_type = 'Issue'" +
" LEFT OUTER JOIN custom_fields ON custom_fields.id = custom_values.custom_field_id" +
" WHERE issues.assigned_to_id IN (#{int_values})" +
" OR (custom_fields.field_format = 'user' AND custom_values.value IN (#{int_values}) ) )"
end
end

end # InstanceMethods
end
end
68 changes: 68 additions & 0 deletions lib/custom_users_as_assignees/mailer_patch.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
module CustomUsersAsAssignees
module MailerPatch
def self.included(base)
base.extend(ClassMethods)

base.class_eval do
class << self
alias_method_chain :reminders, :custom_users
end
end
end
end

module ClassMethods
def reminders_with_custom_users(options={})
days = options[:days] || 7
project = options[:project] ? Project.find(options[:project]) : nil
tracker = options[:tracker] ? Tracker.find(options[:tracker]) : nil
target_version_id = options[:version] ? Version.named(options[:version]).pluck(:id) : nil
if options[:version] && target_version_id.blank?
raise ActiveRecord::RecordNotFound.new("Couldn't find Version with named #{options[:version]}")
end
user_ids = options[:users]

scope = Issue.open.where("#{Issue.table_name}.assigned_to_id IS NOT NULL" +
" AND #{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?
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 = scope.includes(:status, :assigned_to, :project, :tracker, :customized, customized: :custom_field)
issues_by_assignee = issues.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]
end
end
end

issues.each do |issue|
assigneeid = issue["assigned_to_id"]
issue.customized.each do |customizeduser|
if customizeduser.custom_field["field_format"] == "user" && customizeduser.value != ""
userid = customizeduser.value.to_i
if userid != assigneeid
user = User.find(userid)
issues_by_assignee[user] ||= []
issues_by_assignee[user].push(issue)
issues_by_assignee[user].uniq!
end
end
end
end

issues_by_assignee.each do |assignee, issues|
if assignee.is_a?(User) && assignee.active? && issues.present?
visible_issues = issues.select {|i| i.visible?(assignee)}
reminder(assignee, visible_issues, days).deliver if visible_issues.present?
end
end
end
end
end
16 changes: 16 additions & 0 deletions lib/custom_users_as_assignees/user_patch.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
module CustomUsersAsAssignees
module UserPatch
def self.included(base)
base.send :include, InstanceMethods
end

module InstanceMethods
def notify_custom_user?(object, custom_users_current, custom_users_changed)
if %w(selected only_my_events only_assigned).include?(mail_notification)
object.is_a?(Issue) &&
(custom_users_changed.include?(self) || (custom_users_changed == [] && custom_users_current.include?(self)))
end
end
end
end
end
2 changes: 2 additions & 0 deletions test/test_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Load the Redmine helper
require File.expand_path(File.dirname(__FILE__) + '/../../../test/test_helper')

0 comments on commit d0c2075

Please sign in to comment.