Patch by Mizuki ISHIKAWA. git-svn-id: https://svn.redmine.org/redmine/trunk@22177 e93f8b46-1217-0410-a6f0-8f06a7374b81tags/5.1.0
@@ -26,15 +26,15 @@ module QueriesHelper | |||
ungrouped = [] | |||
grouped = {label_string: [], label_date: [], label_time_tracking: [], label_attachment: []} | |||
query.available_filters.map do |field, field_options| | |||
if field_options[:type] == :relation | |||
if field =~ /^(.+)\./ | |||
# association filters | |||
group = "field_#{$1}".to_sym | |||
elsif field_options[:type] == :relation | |||
group = :label_relations | |||
elsif field_options[:type] == :tree | |||
group = query.is_a?(IssueQuery) ? :label_relations : nil | |||
elsif /^cf_\d+\./.match?(field) | |||
group = (field_options[:through] || field_options[:field]).try(:name) | |||
elsif field =~ /^(.+)\./ | |||
# association filters | |||
group = "field_#{$1}".to_sym | |||
elsif %w(member_of_group assigned_to_role).include?(field) | |||
group = :field_assigned_to | |||
elsif field_options[:type] == :date_past || field_options[:type] == :date | |||
@@ -256,7 +256,7 @@ module QueriesHelper | |||
link_to value, issue_path(item) | |||
when :subject | |||
link_to value, issue_path(item) | |||
when :parent | |||
when :parent, :'issue.parent' | |||
value ? (value.visible? ? link_to_issue(value, :subject => false) : "##{value.id}") : '' | |||
when :description | |||
item.description? ? content_tag('div', textilizable(item, :description), :class => "wiki") : '' |
@@ -31,6 +31,7 @@ class TimeEntryQuery < Query | |||
QueryColumn.new(:activity, :sortable => "#{TimeEntryActivity.table_name}.position", :groupable => true), | |||
QueryColumn.new(:issue, :sortable => "#{Issue.table_name}.id", :groupable => true), | |||
QueryAssociationColumn.new(:issue, :tracker, :caption => :field_tracker, :sortable => "#{Tracker.table_name}.position"), | |||
QueryAssociationColumn.new(:issue, :parent, :caption => :field_parent_issue, :sortable => ["#{Issue.table_name}.root_id", "#{Issue.table_name}.lft ASC"], :default_order => 'desc'), | |||
QueryAssociationColumn.new(:issue, :status, :caption => :field_status, :sortable => "#{IssueStatus.table_name}.position"), | |||
QueryAssociationColumn.new(:issue, :category, :caption => :field_category, :sortable => "#{IssueCategory.table_name}.name"), | |||
QueryAssociationColumn.new(:issue, :fixed_version, :caption => :field_fixed_version, :sortable => Version.fields_for_order_statement), | |||
@@ -61,6 +62,10 @@ class TimeEntryQuery < Query | |||
: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.parent_id", | |||
:type => :tree, | |||
:name => l("label_attribute_of_issue", :name => l(:field_parent_issue))) | |||
add_available_filter( | |||
"issue.status_id", | |||
:type => :list, | |||
@@ -205,6 +210,30 @@ class TimeEntryQuery < Query | |||
end | |||
end | |||
def sql_for_issue_parent_id_field(field, operator, value) | |||
case operator | |||
when "=" | |||
# accepts a comma separated list of ids | |||
parent_ids = value.first.to_s.scan(/\d+/).map(&:to_i).uniq | |||
issue_ids = Issue.where(:parent_id => parent_ids).pluck(:id) | |||
if issue_ids.present? | |||
"#{TimeEntry.table_name}.issue_id IN (#{issue_ids.join(',')})" | |||
else | |||
"1=0" | |||
end | |||
when "~" | |||
root_id, lft, rgt = Issue.where(:id => value.first.to_i).pick(:root_id, :lft, :rgt) | |||
issue_ids = Issue.where("#{Issue.table_name}.root_id = ? AND #{Issue.table_name}.lft > ? AND #{Issue.table_name}.rgt < ?", root_id, lft, rgt).pluck(:id) if root_id && lft && rgt | |||
if issue_ids.present? | |||
"#{TimeEntry.table_name}.issue_id IN (#{issue_ids.join(',')})" | |||
else | |||
"1=0" | |||
end | |||
else | |||
sql_for_field("parent_id", operator, value, Issue.table_name, "parent_id") | |||
end | |||
end | |||
def sql_for_activity_id_field(field, operator, value) | |||
ids = value.map(&:to_i).join(',') | |||
table_name = Enumeration.table_name |
@@ -1387,6 +1387,52 @@ class TimelogControllerTest < Redmine::ControllerTest | |||
assert_select 'td.issue-category', :text => 'Printing' | |||
end | |||
def test_index_with_issue_parent_filter | |||
issue1 = Issue.generate!(project_id: 'ecookbook', parent_id: 2) | |||
entry1 = TimeEntry.generate!(issue: issue1, hours: 2.5) | |||
issue2 = Issue.generate!(project_id: 'ecookbook', parent_id: 5) | |||
entry2 = TimeEntry.generate!(issue: issue2, hours: 5.0) | |||
get :index, params: { | |||
project_id: 'ecookbook', | |||
f: ['issue.parent_id'], | |||
op: {'issue.parent_id' => '='}, | |||
v: {'issue.parent_id' => ['2,5']} | |||
} | |||
assert_response :success | |||
assert_equal [entry1.id, entry2.id].sort, css_select('input[name="ids[]"]').map {|e| e.attr(:value).to_i}.sort | |||
end | |||
def test_index_with_issue_parent_column | |||
issue = Issue.generate!(project_id: 'ecookbook', parent_id: 2) | |||
entry = TimeEntry.generate!(issue: issue, hours: 2.5) | |||
get :index, params: { | |||
project_id: 'ecookbook', | |||
c: %w(project spent_on issue comments hours issue.parent) | |||
} | |||
assert_response :success | |||
assert_select 'td.issue-parent', text: "#{issue.parent.tracker} ##{issue.parent.id}" | |||
end | |||
def test_index_with_issue_parent_sort | |||
issue1 = Issue.generate!(project_id: 'ecookbook', parent_id: 2) | |||
entry1 = TimeEntry.generate!(issue: issue1, hours: 2.5) | |||
issue2 = Issue.generate!(project_id: 'ecookbook', parent_id: 5) | |||
entry2 = TimeEntry.generate!(issue: issue2, hours: 5.0) | |||
get :index, :params => { | |||
:c => ["hours", 'issue.parent'], | |||
:sort => 'issue.parent' | |||
} | |||
assert_response :success | |||
# Make sure that values are properly sorted | |||
values = css_select("td.issue-parent").map(&:text).reject(&:blank?) | |||
assert_equal ["#{issue1.parent.tracker} ##{issue1.parent.id}", "#{issue2.parent.tracker} ##{issue2.parent.id}"].sort, values.sort | |||
end | |||
def test_index_with_issue_fixed_version_column | |||
issue = Issue.find(1) | |||
issue.fixed_version = Version.find(3) |
@@ -504,6 +504,32 @@ class QueryTest < ActiveSupport::TestCase | |||
find_issues_with_query(query) | |||
end | |||
def test_time_entry_operator_is_on_issue_parent_id_should_accept_comma_separated_values | |||
issue1 = Issue.generate!(project_id: 'ecookbook', parent_id: 2) | |||
entry1 = TimeEntry.generate!(issue: issue1) | |||
issue2 = Issue.generate!(project_id: 'ecookbook', parent_id: 5) | |||
entry2 = TimeEntry.generate!(issue: issue2) | |||
query = TimeEntryQuery.new(:name => '_') | |||
query.add_filter("issue.parent_id", '=', ['2,5']) | |||
entries = TimeEntry.where(query.statement).to_a | |||
assert_equal 2, entries.size | |||
assert_equal [entry1.id, entry2.id].sort, entries.map(&:id).sort | |||
end | |||
def test_time_entry_contains_operator_is_on_issue_parent_id | |||
issue1 = Issue.generate!(project_id: 'ecookbook', parent_id: 2) | |||
entry1 = TimeEntry.generate!(issue: issue1) | |||
issue2 = Issue.generate!(project_id: 'ecookbook', parent_id: issue1.id) | |||
entry2 = TimeEntry.generate!(issue: issue2) | |||
query = TimeEntryQuery.new(:name => '_') | |||
query.add_filter("issue.parent_id", '~', ['2']) | |||
entries = TimeEntry.where(query.statement).to_a | |||
assert_equal 2, entries.size | |||
assert_equal [entry1.id, entry2.id].sort, entries.map(&:id).sort | |||
end | |||
def test_date_filter_should_not_accept_non_date_values | |||
query = IssueQuery.new(:name => '_') | |||
query.add_filter('created_on', '=', ['a']) |