Custom fields must be marked as "Used as filter" to show up in the filters list. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10164 e93f8b46-1217-0410-a6f0-8f06a7374b81tags/2.1.0
@@ -305,6 +305,8 @@ class Query < ActiveRecord::Base | |||
add_custom_fields_filters(IssueCustomField.find(:all, :conditions => {:is_filter => true, :is_for_all => true})) | |||
end | |||
add_associations_custom_fields_filters :project, :author, :assigned_to, :fixed_version | |||
if User.current.allowed_to?(:set_issues_private, nil, :global => true) || | |||
User.current.allowed_to?(:set_own_issues_private, nil, :global => true) | |||
@available_filters["is_private"] = { :type => :list, :order => 15, :values => [[l(:general_text_yes), "1"], [l(:general_text_no), "0"]] } | |||
@@ -572,7 +574,7 @@ class Query < ActiveRecord::Base | |||
end | |||
end | |||
if field =~ /^cf_(\d+)$/ | |||
if field =~ /cf_(\d+)$/ | |||
# custom field | |||
filters_clauses << sql_for_custom_field(field, operator, v, $1) | |||
elsif respond_to?("sql_for_#{field}_field") | |||
@@ -733,7 +735,8 @@ class Query < ActiveRecord::Base | |||
db_table = CustomValue.table_name | |||
db_field = 'value' | |||
filter = @available_filters[field] | |||
if filter && filter[:format] == 'user' | |||
return nil unless filter | |||
if filter[:format] == 'user' | |||
if value.delete('me') | |||
value.push User.current.id.to_s | |||
end | |||
@@ -744,7 +747,15 @@ class Query < ActiveRecord::Base | |||
operator = '=' | |||
not_in = 'NOT' | |||
end | |||
"#{Issue.table_name}.id #{not_in} IN (SELECT #{Issue.table_name}.id FROM #{Issue.table_name} LEFT OUTER JOIN #{db_table} ON #{db_table}.customized_type='Issue' AND #{db_table}.customized_id=#{Issue.table_name}.id AND #{db_table}.custom_field_id=#{custom_field_id} WHERE " + | |||
customized_key = "id" | |||
customized_class = Issue | |||
if field =~ /^(.+)\.cf_/ | |||
assoc = $1 | |||
customized_key = "#{assoc}_id" | |||
customized_class = Issue.reflect_on_association(assoc.to_sym).klass.base_class rescue nil | |||
raise "Unknown Issue association #{assoc}" unless customized_class | |||
end | |||
"#{Issue.table_name}.#{customized_key} #{not_in} IN (SELECT #{customized_class.table_name}.id FROM #{customized_class.table_name} LEFT OUTER JOIN #{db_table} ON #{db_table}.customized_type='#{customized_class}' AND #{db_table}.customized_id=#{customized_class.table_name}.id AND #{db_table}.custom_field_id=#{custom_field_id} WHERE " + | |||
sql_for_field(field, operator, value, db_table, db_field, true) + ')' | |||
end | |||
@@ -853,7 +864,8 @@ class Query < ActiveRecord::Base | |||
return sql | |||
end | |||
def add_custom_fields_filters(custom_fields) | |||
def add_custom_fields_filters(custom_fields, assoc=nil) | |||
return unless custom_fields.present? | |||
@available_filters ||= {} | |||
custom_fields.select(&:is_filter?).each do |field| | |||
@@ -880,7 +892,25 @@ class Query < ActiveRecord::Base | |||
else | |||
options = { :type => :string, :order => 20 } | |||
end | |||
@available_filters["cf_#{field.id}"] = options.merge({ :name => field.name, :format => field.field_format }) | |||
filter_id = "cf_#{field.id}" | |||
filter_name = field.name | |||
if assoc.present? | |||
filter_id = "#{assoc}.#{filter_id}" | |||
filter_name = l("label_attribute_of_#{assoc}", :name => filter_name) | |||
end | |||
@available_filters[filter_id] = options.merge({ :name => filter_name, :format => field.field_format }) | |||
end | |||
end | |||
def add_associations_custom_fields_filters(*associations) | |||
fields_by_class = CustomField.where(:is_filter => true).group_by(&:class) | |||
associations.each do |assoc| | |||
association_klass = Issue.reflect_on_association(assoc).klass | |||
fields_by_class.each do |field_class, fields| | |||
if field_class.customized_class <= association_klass | |||
add_custom_fields_filters(fields, assoc) | |||
end | |||
end | |||
end | |||
end | |||
@@ -55,11 +55,21 @@ when "IssueCustomField" %> | |||
<p><%= f.check_box :is_required %></p> | |||
<p><%= f.check_box :visible %></p> | |||
<p><%= f.check_box :editable %></p> | |||
<p><%= f.check_box :is_filter %></p> | |||
<% when "ProjectCustomField" %> | |||
<p><%= f.check_box :is_required %></p> | |||
<p><%= f.check_box :visible %></p> | |||
<p><%= f.check_box :searchable %></p> | |||
<p><%= f.check_box :is_filter %></p> | |||
<% when "VersionCustomField" %> | |||
<p><%= f.check_box :is_required %></p> | |||
<p><%= f.check_box :is_filter %></p> | |||
<% when "GroupCustomField" %> | |||
<p><%= f.check_box :is_required %></p> | |||
<p><%= f.check_box :is_filter %></p> | |||
<% when "TimeEntryCustomField" %> | |||
<p><%= f.check_box :is_required %></p> |
@@ -866,6 +866,10 @@ en: | |||
label_fields_permissions: Fields permissions | |||
label_readonly: Read-only | |||
label_required: Required | |||
label_attribute_of_project: "Project's %{name}" | |||
label_attribute_of_author: "Author's %{name}" | |||
label_attribute_of_assigned_to: "Assignee's %{name}" | |||
label_attribute_of_fixed_version: "Target version's %{name}" | |||
button_login: Login | |||
button_submit: Submit |
@@ -841,6 +841,10 @@ fr: | |||
label_fields_permissions: Permissions sur les champs | |||
label_readonly: Lecture | |||
label_required: Obligatoire | |||
label_attribute_of_project: "%{name} du projet" | |||
label_attribute_of_author: "%{name} de l'auteur" | |||
label_attribute_of_assigned_to: "%{name} de l'assigné" | |||
label_attribute_of_fixed_version: "%{name} de la version cible" | |||
button_login: Connexion | |||
button_submit: Soumettre |
@@ -152,10 +152,11 @@ function buildFilterRow(field, operator, values) { | |||
var option = $('<option>'); | |||
if ($.isArray(filterValue)) { | |||
option.val(filterValue[1]).html(filterValue[0]); | |||
if (values.indexOf(filterValue[1]) > -1) {option.attr('selected', true)}; | |||
} else { | |||
option.val(filterValue).html(filterValue); | |||
if (values.indexOf(filterValue) > -1) {option.attr('selected', true)}; | |||
} | |||
if (values.indexOf(filterValues[i][1]) > -1) {option.attr('selected', true)}; | |||
select.append(option); | |||
} | |||
break; |
@@ -339,7 +339,7 @@ fieldset#date-range p { margin: 2px 0 2px 0; } | |||
fieldset#filters table { border-collapse: collapse; } | |||
fieldset#filters table td { padding: 0; vertical-align: middle; } | |||
fieldset#filters tr.filter { height: 2.1em; } | |||
fieldset#filters td.field { width:200px; } | |||
fieldset#filters td.field { width:250px; } | |||
fieldset#filters td.operator { width:170px; } | |||
fieldset#filters td.values { white-space:nowrap; } | |||
fieldset#filters td.values select {min-width:130px;} |
@@ -230,6 +230,22 @@ class IssuesControllerTest < ActionController::TestCase | |||
assert_equal({}, query.filters) | |||
end | |||
def test_index_with_project_custom_field_filter | |||
field = ProjectCustomField.create!(:name => 'Client', :is_filter => true, :field_format => 'string') | |||
CustomValue.create!(:custom_field => field, :customized => Project.find(3), :value => 'Foo') | |||
CustomValue.create!(:custom_field => field, :customized => Project.find(5), :value => 'Foo') | |||
filter_name = "project.cf_#{field.id}" | |||
@request.session[:user_id] = 1 | |||
get :index, :set_filter => 1, | |||
:f => [filter_name], | |||
:op => {filter_name => '='}, | |||
:v => {filter_name => ['Foo']} | |||
assert_response :success | |||
assert_template 'index' | |||
assert_equal [3, 5], assigns(:issues).map(&:project_id).uniq.sort | |||
end | |||
def test_index_with_query | |||
get :index, :project_id => 1, :query_id => 5 | |||
assert_response :success |
@@ -577,6 +577,51 @@ class QueryTest < ActiveSupport::TestCase | |||
User.current = nil | |||
end | |||
def test_filter_on_project_custom_field | |||
field = ProjectCustomField.create!(:name => 'Client', :is_filter => true, :field_format => 'string') | |||
CustomValue.create!(:custom_field => field, :customized => Project.find(3), :value => 'Foo') | |||
CustomValue.create!(:custom_field => field, :customized => Project.find(5), :value => 'Foo') | |||
query = Query.new(:name => '_') | |||
filter_name = "project.cf_#{field.id}" | |||
assert_include filter_name, query.available_filters.keys | |||
query.filters = {filter_name => {:operator => '=', :values => ['Foo']}} | |||
assert_equal [3, 5], find_issues_with_query(query).map(&:project_id).uniq.sort | |||
end | |||
def test_filter_on_author_custom_field | |||
field = UserCustomField.create!(:name => 'Client', :is_filter => true, :field_format => 'string') | |||
CustomValue.create!(:custom_field => field, :customized => User.find(3), :value => 'Foo') | |||
query = Query.new(:name => '_') | |||
filter_name = "author.cf_#{field.id}" | |||
assert_include filter_name, query.available_filters.keys | |||
query.filters = {filter_name => {:operator => '=', :values => ['Foo']}} | |||
assert_equal [3], find_issues_with_query(query).map(&:author_id).uniq.sort | |||
end | |||
def test_filter_on_assigned_to_custom_field | |||
field = UserCustomField.create!(:name => 'Client', :is_filter => true, :field_format => 'string') | |||
CustomValue.create!(:custom_field => field, :customized => User.find(3), :value => 'Foo') | |||
query = Query.new(:name => '_') | |||
filter_name = "assigned_to.cf_#{field.id}" | |||
assert_include filter_name, query.available_filters.keys | |||
query.filters = {filter_name => {:operator => '=', :values => ['Foo']}} | |||
assert_equal [3], find_issues_with_query(query).map(&:assigned_to_id).uniq.sort | |||
end | |||
def test_filter_on_fixed_version_custom_field | |||
field = VersionCustomField.create!(:name => 'Client', :is_filter => true, :field_format => 'string') | |||
CustomValue.create!(:custom_field => field, :customized => Version.find(2), :value => 'Foo') | |||
query = Query.new(:name => '_') | |||
filter_name = "fixed_version.cf_#{field.id}" | |||
assert_include filter_name, query.available_filters.keys | |||
query.filters = {filter_name => {:operator => '=', :values => ['Foo']}} | |||
assert_equal [2], find_issues_with_query(query).map(&:fixed_version_id).uniq.sort | |||
end | |||
def test_statement_should_be_nil_with_no_filters | |||
q = Query.new(:name => '_') | |||
q.filters = {} |