diff options
author | Jean-Philippe Lang <jp_lang@yahoo.fr> | 2012-09-29 12:57:38 +0000 |
---|---|---|
committer | Jean-Philippe Lang <jp_lang@yahoo.fr> | 2012-09-29 12:57:38 +0000 |
commit | 1b6da80e16dd0cb200588422dfc5a655cb3b94f0 (patch) | |
tree | 5a4bedc3760b89680f96e989fa606fcba92e6250 /app/models/query.rb | |
parent | 69b8931e92e37feceb7353d5baa6be6a51492bf0 (diff) | |
download | redmine-1b6da80e16dd0cb200588422dfc5a655cb3b94f0.tar.gz redmine-1b6da80e16dd0cb200588422dfc5a655cb3b94f0.zip |
Makes related issues available for display and filtering on the issue list (#3239, #3265).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10513 e93f8b46-1217-0410-a6f0-8f06a7374b81
Diffstat (limited to 'app/models/query.rb')
-rw-r--r-- | app/models/query.rb | 73 |
1 files changed, 65 insertions, 8 deletions
diff --git a/app/models/query.rb b/app/models/query.rb index ddf4d87d9..92a568119 100644 --- a/app/models/query.rb +++ b/app/models/query.rb @@ -113,7 +113,9 @@ class Query < ActiveRecord::Base "<t-" => :label_more_than_ago, "t-" => :label_ago, "~" => :label_contains, - "!~" => :label_not_contains } + "!~" => :label_not_contains, + "=p" => :label_any_issues_in_project, + "=!p" => :label_any_issues_not_in_project} cattr_reader :operators @@ -126,7 +128,8 @@ class Query < ActiveRecord::Base :string => [ "=", "~", "!", "!~", "!*", "*" ], :text => [ "~", "!~", "!*", "*" ], :integer => [ "=", ">=", "<=", "><", "!*", "*" ], - :float => [ "=", ">=", "<=", "><", "!*", "*" ] } + :float => [ "=", ">=", "<=", "><", "!*", "*" ], + :relation => ["=", "=p", "=!p", "!*", "*"]} cattr_reader :operators_by_filter_type @@ -147,6 +150,7 @@ class Query < ActiveRecord::Base QueryColumn.new(:estimated_hours, :sortable => "#{Issue.table_name}.estimated_hours"), QueryColumn.new(:done_ratio, :sortable => "#{Issue.table_name}.done_ratio", :groupable => true), QueryColumn.new(:created_on, :sortable => "#{Issue.table_name}.created_on", :default_order => 'desc'), + QueryColumn.new(:relations, :caption => :label_related_issues) ] cattr_reader :available_columns @@ -233,6 +237,10 @@ class Query < ActiveRecord::Base "estimated_hours" => { :type => :float, :order => 13 }, "done_ratio" => { :type => :integer, :order => 14 }} + IssueRelation::TYPES.each do |relation_type, options| + @available_filters[relation_type] = {:type => :relation, :order => @available_filters.size + 100, :label => options[:name]} + end + principals = [] if project principals += project.principals.sort @@ -244,7 +252,6 @@ class Query < ActiveRecord::Base end end else - all_projects = Project.visible.all if all_projects.any? # members of visible projects principals += Principal.member_of(all_projects) @@ -254,10 +261,7 @@ class Query < ActiveRecord::Base if User.current.logged? && User.current.memberships.any? project_values << ["<< #{l(:label_my_projects).downcase} >>", "mine"] end - Project.project_tree(all_projects) do |p, level| - prefix = (level > 0 ? ('--' * level + ' ') : '') - project_values << ["#{prefix}#{p.name}", p.id.to_s] - end + project_values += all_projects_values @available_filters["project_id"] = { :type => :list, :order => 1, :values => project_values} unless project_values.empty? end end @@ -317,7 +321,7 @@ class Query < ActiveRecord::Base } @available_filters.each do |field, options| - options[:name] ||= l("field_#{field}".gsub(/_id$/, '')) + options[:name] ||= l(options[:label] || "field_#{field}".gsub(/_id$/, '')) end @available_filters @@ -332,6 +336,21 @@ class Query < ActiveRecord::Base json end + def all_projects + @all_projects ||= Project.visible.all + end + + def all_projects_values + return @all_projects_values if @all_projects_values + + values = [] + Project.project_tree(all_projects) do |p, level| + prefix = (level > 0 ? ('--' * level + ' ') : '') + values << ["#{prefix}#{p.name}", p.id.to_s] + end + @all_projects_values = values + end + def add_filter(field, operator, values) # values must be an array return unless values.nil? || values.is_a?(Array) @@ -635,6 +654,9 @@ class Query < ActiveRecord::Base if has_column?(:spent_hours) Issue.load_visible_spent_hours(issues) end + if has_column?(:relations) + Issue.load_visible_relations(issues) + end issues rescue ::ActiveRecord::StatementInvalid => e raise StatementInvalid.new(e.message) @@ -729,6 +751,41 @@ class Query < ActiveRecord::Base "#{Issue.table_name}.is_private #{op} (#{va})" end + def sql_for_relations(field, operator, value, options={}) + relation_options = IssueRelation::TYPES[field] + return relation_options unless relation_options + + relation_type = field + join_column, target_join_column = "issue_from_id", "issue_to_id" + if relation_options[:reverse] || options[:reverse] + relation_type = relation_options[:reverse] || relation_type + join_column, target_join_column = target_join_column, join_column + end + + sql = case operator + when "*", "!*" + op = (operator == "*" ? 'IN' : 'NOT IN') + "#{Issue.table_name}.id #{op} (SELECT DISTINCT #{IssueRelation.table_name}.#{join_column} FROM #{IssueRelation.table_name} WHERE #{IssueRelation.table_name}.relation_type = '#{connection.quote_string(relation_type)}')" + when "=", "!" + op = (operator == "=" ? 'IN' : 'NOT IN') + "#{Issue.table_name}.id #{op} (SELECT DISTINCT #{IssueRelation.table_name}.#{join_column} FROM #{IssueRelation.table_name} WHERE #{IssueRelation.table_name}.relation_type = '#{connection.quote_string(relation_type)}' AND #{IssueRelation.table_name}.#{target_join_column} = #{value.first.to_i})" + when "=p", "=!p" + op = (operator == "=p" ? '=' : '<>') + "#{Issue.table_name}.id IN (SELECT DISTINCT #{IssueRelation.table_name}.#{join_column} FROM #{IssueRelation.table_name}, #{Issue.table_name} relissues WHERE #{IssueRelation.table_name}.relation_type = '#{connection.quote_string(relation_type)}' AND #{IssueRelation.table_name}.#{target_join_column} = relissues.id AND relissues.project_id #{op} #{value.first.to_i})" + end + + if relation_options[:sym] == field && !options[:reverse] + sqls = [sql, sql_for_relations(field, operator, value, :reverse => true)] + sqls.join(["!", "!*"].include?(operator) ? " AND " : " OR ") + else + sql + end + end + + IssueRelation::TYPES.keys.each do |relation_type| + alias_method "sql_for_#{relation_type}_field".to_sym, :sql_for_relations + end + private def sql_for_custom_field(field, operator, value, custom_field_id) |