Commit a6781df8 by Hung Dong Ngo

Initial commit

parents
= parent_status_filter
Description goes here
en:
parent_status: Parent status
field_parent_status: Parent status
# Plugin's routes
# See: http://guides.rubyonrails.org/routing.html
Redmine::Plugin.register :parent_status_filter do
name 'Parent Status Filter plugin'
author 'Dong'
description 'Add parent_status filter for issue and spent time'
version '0.0.1'
url 'http://example.com/path/to/plugin'
author_url 'http://example.com/about'
end
ActionDispatch::Callbacks.to_prepare do
require 'ps'
end
require 'ps/patches/issue_query_patch'
require 'ps/patches/issue_patch'
require 'ps/patches/query_patch'
require 'ps/patches/time_entry_query_patch'
module PS
module Patches
module IssuePatch
def self.prepended(base)
base.send(:prepend, InstanceMethods)
end
module InstanceMethods
def parent_status
return '' if self.parent.blank?
self.parent.status.try(:name)
end
end
end
end
end
unless Issue.included_modules.include?(PS::Patches::IssuePatch)
Issue.send(:prepend, PS::Patches::IssuePatch)
end
module PS
module Patches
module IssueQueryPatch
def self.prepended(base)
base.send(:prepend, InstanceMethods)
end
module InstanceMethods
def initialize(attributes=nil, *args)
super attributes
self.filters ||= { 'status_id' => {:operator => "o", :values => [""]} }
self.available_columns << QueryColumn.new(:parent_status, :caption => :parent_status, :sortable => "#{IssueStatus.table_name}.position", :groupable => true, :inline => true)
end
def initialize_available_filters
add_available_filter "parent_status",
:type => :list_parent_status, :values => lambda { issue_statuses_values }
add_available_filter "status_id",
:type => :list_status, :values => lambda { issue_statuses_values }
add_available_filter("project_id",
:type => :list, :values => lambda { project_values }
) if project.nil?
add_available_filter "tracker_id",
:type => :list, :values => trackers.collect{|s| [s.name, s.id.to_s] }
add_available_filter "priority_id",
:type => :list, :values => IssuePriority.all.collect{|s| [s.name, s.id.to_s] }
add_available_filter("author_id",
:type => :list, :values => lambda { author_values }
)
add_available_filter("assigned_to_id",
:type => :list_optional, :values => lambda { assigned_to_values }
)
add_available_filter("member_of_group",
:type => :list_optional, :values => lambda { Group.givable.visible.collect {|g| [g.name, g.id.to_s] } }
)
add_available_filter("assigned_to_role",
:type => :list_optional, :values => lambda { Role.givable.collect {|r| [r.name, r.id.to_s] } }
)
add_available_filter "fixed_version_id",
:type => :list_optional, :values => lambda { fixed_version_values }
add_available_filter "fixed_version.due_date",
:type => :date,
:name => l(:label_attribute_of_fixed_version, :name => l(:field_effective_date))
add_available_filter "fixed_version.status",
:type => :list,
:name => l(:label_attribute_of_fixed_version, :name => l(:field_status)),
:values => Version::VERSION_STATUSES.map{|s| [l("version_status_#{s}"), s] }
add_available_filter "category_id",
:type => :list_optional,
:values => lambda { project.issue_categories.collect{|s| [s.name, s.id.to_s] } } if project
add_available_filter "subject", :type => :text
add_available_filter "description", :type => :text
add_available_filter "created_on", :type => :date_past
add_available_filter "updated_on", :type => :date_past
add_available_filter "closed_on", :type => :date_past
add_available_filter "start_date", :type => :date
add_available_filter "due_date", :type => :date
add_available_filter "estimated_hours", :type => :float
add_available_filter "done_ratio", :type => :integer
if User.current.allowed_to?(:set_issues_private, nil, :global => true) ||
User.current.allowed_to?(:set_own_issues_private, nil, :global => true)
add_available_filter "is_private",
:type => :list,
:values => [[l(:general_text_yes), "1"], [l(:general_text_no), "0"]]
end
add_available_filter "attachment",
:type => :text, :name => l(:label_attachment)
if User.current.logged?
add_available_filter "watcher_id",
:type => :list, :values => [["<< #{l(:label_me)} >>", "me"]]
end
add_available_filter("updated_by",
:type => :list, :values => lambda { author_values }
)
add_available_filter("last_updated_by",
:type => :list, :values => lambda { author_values }
)
if project && !project.leaf?
add_available_filter "subproject_id",
:type => :list_subprojects,
:values => lambda { subproject_values }
end
add_custom_fields_filters(issue_custom_fields)
add_associations_custom_fields_filters :project, :author, :assigned_to, :fixed_version
IssueRelation::TYPES.each do |relation_type, options|
add_available_filter relation_type, :type => :relation, :label => options[:name], :values => lambda {all_projects_values}
end
add_available_filter "parent_id", :type => :tree, :label => :field_parent_issue
add_available_filter "child_id", :type => :tree, :label => :label_subtask_plural
add_available_filter "issue_id", :type => :integer, :label => :label_issue
Tracker.disabled_core_fields(trackers).each {|field|
delete_available_filter field
}
end
end
end
end
end
unless IssueQuery.included_modules.include?(PS::Patches::IssueQueryPatch)
IssueQuery.send(:prepend, PS::Patches::IssueQueryPatch)
end
module PS
module Patches
module QueryPatch
def self.prepended(base)
base.send(:prepend, InstanceMethods)
end
module InstanceMethods
def initialize(attributes=nil, *args)
super attributes
@is_for_all = project.nil?
self.operators_by_filter_type[:list_parent_status] = [ "o", "c", "*" ]
end
# Helper method to generate the WHERE sql for a +field+, +operator+ and a +value+
def sql_for_field(field, operator, value, db_table, db_field, is_custom_filter=false)
sql = ''
case operator
when "="
if value.any?
case type_for(field)
when :date, :date_past
sql = date_clause(db_table, db_field, parse_date(value.first), parse_date(value.first), is_custom_filter)
when :integer
int_values = value.first.to_s.scan(/[+-]?\d+/).map(&:to_i).join(",")
if int_values.present?
if is_custom_filter
sql = "(#{db_table}.#{db_field} <> '' AND CAST(CASE #{db_table}.#{db_field} WHEN '' THEN '0' ELSE #{db_table}.#{db_field} END AS decimal(30,3)) IN (#{int_values}))"
else
sql = "#{db_table}.#{db_field} IN (#{int_values})"
end
else
sql = "1=0"
end
when :float
if is_custom_filter
sql = "(#{db_table}.#{db_field} <> '' AND CAST(CASE #{db_table}.#{db_field} WHEN '' THEN '0' ELSE #{db_table}.#{db_field} END AS decimal(30,3)) BETWEEN #{value.first.to_f - 1e-5} AND #{value.first.to_f + 1e-5})"
else
sql = "#{db_table}.#{db_field} BETWEEN #{value.first.to_f - 1e-5} AND #{value.first.to_f + 1e-5}"
end
else
sql = queried_class.send(:sanitize_sql_for_conditions, ["#{db_table}.#{db_field} IN (?)", value])
end
else
# IN an empty set
sql = "1=0"
end
when "!"
if value.any?
sql = queried_class.send(:sanitize_sql_for_conditions, ["(#{db_table}.#{db_field} IS NULL OR #{db_table}.#{db_field} NOT IN (?))", value])
else
# NOT IN an empty set
sql = "1=1"
end
when "!*"
sql = "#{db_table}.#{db_field} IS NULL"
sql << " OR #{db_table}.#{db_field} = ''" if (is_custom_filter || [:text, :string].include?(type_for(field)))
when "*"
sql = if db_field.include? "parent_status"
"#{Issue.table_name}.parent_id IS NOT NULL"
else
sql = "#{db_table}.#{db_field} IS NOT NULL"
end
sql << " AND #{db_table}.#{db_field} <> ''" if is_custom_filter
when ">="
if [:date, :date_past].include?(type_for(field))
sql = date_clause(db_table, db_field, parse_date(value.first), nil, is_custom_filter)
else
if is_custom_filter
sql = "(#{db_table}.#{db_field} <> '' AND CAST(CASE #{db_table}.#{db_field} WHEN '' THEN '0' ELSE #{db_table}.#{db_field} END AS decimal(30,3)) >= #{value.first.to_f})"
else
sql = "#{db_table}.#{db_field} >= #{value.first.to_f}"
end
end
when "<="
if [:date, :date_past].include?(type_for(field))
sql = date_clause(db_table, db_field, nil, parse_date(value.first), is_custom_filter)
else
if is_custom_filter
sql = "(#{db_table}.#{db_field} <> '' AND CAST(CASE #{db_table}.#{db_field} WHEN '' THEN '0' ELSE #{db_table}.#{db_field} END AS decimal(30,3)) <= #{value.first.to_f})"
else
sql = "#{db_table}.#{db_field} <= #{value.first.to_f}"
end
end
when "><"
if [:date, :date_past].include?(type_for(field))
sql = date_clause(db_table, db_field, parse_date(value[0]), parse_date(value[1]), is_custom_filter)
else
if is_custom_filter
sql = "(#{db_table}.#{db_field} <> '' AND CAST(CASE #{db_table}.#{db_field} WHEN '' THEN '0' ELSE #{db_table}.#{db_field} END AS decimal(30,3)) BETWEEN #{value[0].to_f} AND #{value[1].to_f})"
else
sql = "#{db_table}.#{db_field} BETWEEN #{value[0].to_f} AND #{value[1].to_f}"
end
end
when "o"
sql = "#{queried_table_name}.status_id IN (SELECT id FROM #{IssueStatus.table_name} WHERE is_closed=#{self.class.connection.quoted_false})" if field == "status_id"
sql = joins_for_parent_status_statement(db_table, self.class.connection.quoted_false) if field.include? "parent_status"
when "c"
sql = "#{queried_table_name}.status_id IN (SELECT id FROM #{IssueStatus.table_name} WHERE is_closed=#{self.class.connection.quoted_true})" if field == "status_id"
sql = joins_for_parent_status_statement(db_table, self.class.connection.quoted_true) if field.include? "parent_status"
when "><t-"
# between today - n days and today
sql = relative_date_clause(db_table, db_field, - value.first.to_i, 0, is_custom_filter)
when ">t-"
# >= today - n days
sql = relative_date_clause(db_table, db_field, - value.first.to_i, nil, is_custom_filter)
when "<t-"
# <= today - n days
sql = relative_date_clause(db_table, db_field, nil, - value.first.to_i, is_custom_filter)
when "t-"
# = n days in past
sql = relative_date_clause(db_table, db_field, - value.first.to_i, - value.first.to_i, is_custom_filter)
when "><t+"
# between today and today + n days
sql = relative_date_clause(db_table, db_field, 0, value.first.to_i, is_custom_filter)
when ">t+"
# >= today + n days
sql = relative_date_clause(db_table, db_field, value.first.to_i, nil, is_custom_filter)
when "<t+"
# <= today + n days
sql = relative_date_clause(db_table, db_field, nil, value.first.to_i, is_custom_filter)
when "t+"
# = today + n days
sql = relative_date_clause(db_table, db_field, value.first.to_i, value.first.to_i, is_custom_filter)
when "t"
# = today
sql = relative_date_clause(db_table, db_field, 0, 0, is_custom_filter)
when "ld"
# = yesterday
sql = relative_date_clause(db_table, db_field, -1, -1, is_custom_filter)
when "w"
# = this week
first_day_of_week = l(:general_first_day_of_week).to_i
day_of_week = User.current.today.cwday
days_ago = (day_of_week >= first_day_of_week ? day_of_week - first_day_of_week : day_of_week + 7 - first_day_of_week)
sql = relative_date_clause(db_table, db_field, - days_ago, - days_ago + 6, is_custom_filter)
when "lw"
# = last week
first_day_of_week = l(:db_fieldgeneral_first_day_of_week).to_i
day_of_week = User.current.today.cwday
days_ago = (day_of_week >= first_day_of_week ? day_of_week - first_day_of_week : day_of_week + 7 - first_day_of_week)
sql = relative_date_clause(db_table, db_field, - days_ago - 7, - days_ago - 1, is_custom_filter)
when "l2w"
# = last 2 weeks
first_day_of_week = l(:general_first_day_of_week).to_i
day_of_week = User.current.today.cwday
days_ago = (day_of_week >= first_day_of_week ? day_of_week - first_day_of_week : day_of_week + 7 - first_day_of_week)
sql = relative_date_clause(db_table, db_field, - days_ago - 14, - days_ago - 1, is_custom_filter)
when "m"
# = this month
date = User.current.today
sql = date_clause(db_table, db_field, date.beginning_of_month, date.end_of_month, is_custom_filter)
when "lm"
# = last month
date = User.current.today.prev_month
sql = date_clause(db_table, db_field, date.beginning_of_month, date.end_of_month, is_custom_filter)
when "y"
# = this year
date = User.current.today
sql = date_clause(db_table, db_field, date.beginning_of_year, date.end_of_year, is_custom_filter)
when "~"
sql = sql_contains("#{db_table}.#{db_field}", value.first)
when "!~"
sql = sql_contains("#{db_table}.#{db_field}", value.first, false)
else
raise "Unknown query operator #{operator}"
end
return sql
end
private
def joins_for_parent_status_statement(db_table, is_closed)
sql = "#{Issue.table_name}.parent_id IN (SELECT id FROM #{Issue.table_name} as parent_issues WHERE parent_issues.status_id IN (SELECT id FROM #{IssueStatus.table_name} WHERE is_closed=#{is_closed}))"
return sql
end
end
end
end
end
unless Query.included_modules.include?(PS::Patches::QueryPatch)
Query.send(:prepend, PS::Patches::QueryPatch)
end
module PS
module Patches
module TimeEntryQueryPatch
def self.prepended(base)
base.send(:prepend, InstanceMethods)
end
module InstanceMethods
def initialize(attributes=nil, *args)
super attributes
self.filters ||= { 'spent_on' => {:operator => "*", :values => []} }
self.available_columns << QueryAssociationColumn.new(:issue, :parent_status, :caption => :parent_status, :sortable => "#{IssueStatus.table_name}.position")
end
def initialize_available_filters
add_available_filter "issue.parent_status",
:type => :list_parent_status,
:name => l("label_attribute_of_issue", :name => l(:parent_status))
add_available_filter "spent_on", :type => :date_past
add_available_filter("project_id",
:type => :list, :values => lambda { project_values }
) if project.nil?
if project && !project.leaf?
add_available_filter "subproject_id",
:type => :list_subprojects,
:values => lambda { subproject_values }
end
add_available_filter("issue_id", :type => :tree, :label => :label_issue)
add_available_filter("issue.tracker_id",
:type => :list,
:name => l("label_attribute_of_issue", :name => l(:field_tracker)),
:values => lambda { trackers.map {|t| [t.name, t.id.to_s]} })
add_available_filter("issue.status_id",
:type => :list,
:name => l("label_attribute_of_issue", :name => l(:field_status)),
:values => lambda { issue_statuses_values })
add_available_filter("issue.fixed_version_id",
:type => :list,
:name => l("label_attribute_of_issue", :name => l(:field_fixed_version)),
:values => lambda { fixed_version_values })
add_available_filter("user_id",
:type => :list_optional, :values => lambda { author_values }
)
activities = (project ? project.activities : TimeEntryActivity.shared)
add_available_filter("activity_id",
:type => :list, :values => activities.map {|a| [a.name, a.id.to_s]}
)
add_available_filter "comments", :type => :text
add_available_filter "hours", :type => :float
add_custom_fields_filters(TimeEntryCustomField)
add_associations_custom_fields_filters :project
add_custom_fields_filters(issue_custom_fields, :issue)
add_associations_custom_fields_filters :user
end
end
end
end
end
unless TimeEntryQuery.included_modules.include?(PS::Patches::TimeEntryQueryPatch)
TimeEntryQuery.send(:prepend, PS::Patches::TimeEntryQueryPatch)
end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment