@@ -305,6 +305,8 class Query < ActiveRecord::Base | |||||
305 | add_custom_fields_filters(IssueCustomField.find(:all, :conditions => {:is_filter => true, :is_for_all => true})) |
|
305 | add_custom_fields_filters(IssueCustomField.find(:all, :conditions => {:is_filter => true, :is_for_all => true})) | |
306 | end |
|
306 | end | |
307 |
|
307 | |||
|
308 | add_associations_custom_fields_filters :project, :author, :assigned_to, :fixed_version | |||
|
309 | ||||
308 | if User.current.allowed_to?(:set_issues_private, nil, :global => true) || |
|
310 | if User.current.allowed_to?(:set_issues_private, nil, :global => true) || | |
309 | User.current.allowed_to?(:set_own_issues_private, nil, :global => true) |
|
311 | User.current.allowed_to?(:set_own_issues_private, nil, :global => true) | |
310 | @available_filters["is_private"] = { :type => :list, :order => 15, :values => [[l(:general_text_yes), "1"], [l(:general_text_no), "0"]] } |
|
312 | @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 | |||||
572 | end |
|
574 | end | |
573 | end |
|
575 | end | |
574 |
|
576 | |||
575 |
if field =~ / |
|
577 | if field =~ /cf_(\d+)$/ | |
576 | # custom field |
|
578 | # custom field | |
577 | filters_clauses << sql_for_custom_field(field, operator, v, $1) |
|
579 | filters_clauses << sql_for_custom_field(field, operator, v, $1) | |
578 | elsif respond_to?("sql_for_#{field}_field") |
|
580 | elsif respond_to?("sql_for_#{field}_field") | |
@@ -733,7 +735,8 class Query < ActiveRecord::Base | |||||
733 | db_table = CustomValue.table_name |
|
735 | db_table = CustomValue.table_name | |
734 | db_field = 'value' |
|
736 | db_field = 'value' | |
735 | filter = @available_filters[field] |
|
737 | filter = @available_filters[field] | |
736 | if filter && filter[:format] == 'user' |
|
738 | return nil unless filter | |
|
739 | if filter[:format] == 'user' | |||
737 | if value.delete('me') |
|
740 | if value.delete('me') | |
738 | value.push User.current.id.to_s |
|
741 | value.push User.current.id.to_s | |
739 | end |
|
742 | end | |
@@ -744,7 +747,15 class Query < ActiveRecord::Base | |||||
744 | operator = '=' |
|
747 | operator = '=' | |
745 | not_in = 'NOT' |
|
748 | not_in = 'NOT' | |
746 | end |
|
749 | end | |
747 | "#{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 " + |
|
750 | customized_key = "id" | |
|
751 | customized_class = Issue | |||
|
752 | if field =~ /^(.+)\.cf_/ | |||
|
753 | assoc = $1 | |||
|
754 | customized_key = "#{assoc}_id" | |||
|
755 | customized_class = Issue.reflect_on_association(assoc.to_sym).klass.base_class rescue nil | |||
|
756 | raise "Unknown Issue association #{assoc}" unless customized_class | |||
|
757 | end | |||
|
758 | "#{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 " + | |||
748 | sql_for_field(field, operator, value, db_table, db_field, true) + ')' |
|
759 | sql_for_field(field, operator, value, db_table, db_field, true) + ')' | |
749 | end |
|
760 | end | |
750 |
|
761 | |||
@@ -853,7 +864,8 class Query < ActiveRecord::Base | |||||
853 | return sql |
|
864 | return sql | |
854 | end |
|
865 | end | |
855 |
|
866 | |||
856 | def add_custom_fields_filters(custom_fields) |
|
867 | def add_custom_fields_filters(custom_fields, assoc=nil) | |
|
868 | return unless custom_fields.present? | |||
857 | @available_filters ||= {} |
|
869 | @available_filters ||= {} | |
858 |
|
870 | |||
859 | custom_fields.select(&:is_filter?).each do |field| |
|
871 | custom_fields.select(&:is_filter?).each do |field| | |
@@ -880,7 +892,25 class Query < ActiveRecord::Base | |||||
880 | else |
|
892 | else | |
881 | options = { :type => :string, :order => 20 } |
|
893 | options = { :type => :string, :order => 20 } | |
882 | end |
|
894 | end | |
883 | @available_filters["cf_#{field.id}"] = options.merge({ :name => field.name, :format => field.field_format }) |
|
895 | filter_id = "cf_#{field.id}" | |
|
896 | filter_name = field.name | |||
|
897 | if assoc.present? | |||
|
898 | filter_id = "#{assoc}.#{filter_id}" | |||
|
899 | filter_name = l("label_attribute_of_#{assoc}", :name => filter_name) | |||
|
900 | end | |||
|
901 | @available_filters[filter_id] = options.merge({ :name => filter_name, :format => field.field_format }) | |||
|
902 | end | |||
|
903 | end | |||
|
904 | ||||
|
905 | def add_associations_custom_fields_filters(*associations) | |||
|
906 | fields_by_class = CustomField.where(:is_filter => true).group_by(&:class) | |||
|
907 | associations.each do |assoc| | |||
|
908 | association_klass = Issue.reflect_on_association(assoc).klass | |||
|
909 | fields_by_class.each do |field_class, fields| | |||
|
910 | if field_class.customized_class <= association_klass | |||
|
911 | add_custom_fields_filters(fields, assoc) | |||
|
912 | end | |||
|
913 | end | |||
884 | end |
|
914 | end | |
885 | end |
|
915 | end | |
886 |
|
916 |
@@ -55,11 +55,21 when "IssueCustomField" %> | |||||
55 | <p><%= f.check_box :is_required %></p> |
|
55 | <p><%= f.check_box :is_required %></p> | |
56 | <p><%= f.check_box :visible %></p> |
|
56 | <p><%= f.check_box :visible %></p> | |
57 | <p><%= f.check_box :editable %></p> |
|
57 | <p><%= f.check_box :editable %></p> | |
|
58 | <p><%= f.check_box :is_filter %></p> | |||
58 |
|
59 | |||
59 | <% when "ProjectCustomField" %> |
|
60 | <% when "ProjectCustomField" %> | |
60 | <p><%= f.check_box :is_required %></p> |
|
61 | <p><%= f.check_box :is_required %></p> | |
61 | <p><%= f.check_box :visible %></p> |
|
62 | <p><%= f.check_box :visible %></p> | |
62 | <p><%= f.check_box :searchable %></p> |
|
63 | <p><%= f.check_box :searchable %></p> | |
|
64 | <p><%= f.check_box :is_filter %></p> | |||
|
65 | ||||
|
66 | <% when "VersionCustomField" %> | |||
|
67 | <p><%= f.check_box :is_required %></p> | |||
|
68 | <p><%= f.check_box :is_filter %></p> | |||
|
69 | ||||
|
70 | <% when "GroupCustomField" %> | |||
|
71 | <p><%= f.check_box :is_required %></p> | |||
|
72 | <p><%= f.check_box :is_filter %></p> | |||
63 |
|
73 | |||
64 | <% when "TimeEntryCustomField" %> |
|
74 | <% when "TimeEntryCustomField" %> | |
65 | <p><%= f.check_box :is_required %></p> |
|
75 | <p><%= f.check_box :is_required %></p> |
@@ -866,6 +866,10 en: | |||||
866 | label_fields_permissions: Fields permissions |
|
866 | label_fields_permissions: Fields permissions | |
867 | label_readonly: Read-only |
|
867 | label_readonly: Read-only | |
868 | label_required: Required |
|
868 | label_required: Required | |
|
869 | label_attribute_of_project: "Project's %{name}" | |||
|
870 | label_attribute_of_author: "Author's %{name}" | |||
|
871 | label_attribute_of_assigned_to: "Assignee's %{name}" | |||
|
872 | label_attribute_of_fixed_version: "Target version's %{name}" | |||
869 |
|
873 | |||
870 | button_login: Login |
|
874 | button_login: Login | |
871 | button_submit: Submit |
|
875 | button_submit: Submit |
@@ -841,6 +841,10 fr: | |||||
841 | label_fields_permissions: Permissions sur les champs |
|
841 | label_fields_permissions: Permissions sur les champs | |
842 | label_readonly: Lecture |
|
842 | label_readonly: Lecture | |
843 | label_required: Obligatoire |
|
843 | label_required: Obligatoire | |
|
844 | label_attribute_of_project: "%{name} du projet" | |||
|
845 | label_attribute_of_author: "%{name} de l'auteur" | |||
|
846 | label_attribute_of_assigned_to: "%{name} de l'assignΓ©" | |||
|
847 | label_attribute_of_fixed_version: "%{name} de la version cible" | |||
844 |
|
848 | |||
845 | button_login: Connexion |
|
849 | button_login: Connexion | |
846 | button_submit: Soumettre |
|
850 | button_submit: Soumettre |
@@ -152,10 +152,11 function buildFilterRow(field, operator, values) { | |||||
152 | var option = $('<option>'); |
|
152 | var option = $('<option>'); | |
153 | if ($.isArray(filterValue)) { |
|
153 | if ($.isArray(filterValue)) { | |
154 | option.val(filterValue[1]).html(filterValue[0]); |
|
154 | option.val(filterValue[1]).html(filterValue[0]); | |
|
155 | if (values.indexOf(filterValue[1]) > -1) {option.attr('selected', true)}; | |||
155 | } else { |
|
156 | } else { | |
156 | option.val(filterValue).html(filterValue); |
|
157 | option.val(filterValue).html(filterValue); | |
|
158 | if (values.indexOf(filterValue) > -1) {option.attr('selected', true)}; | |||
157 | } |
|
159 | } | |
158 | if (values.indexOf(filterValues[i][1]) > -1) {option.attr('selected', true)}; |
|
|||
159 | select.append(option); |
|
160 | select.append(option); | |
160 | } |
|
161 | } | |
161 | break; |
|
162 | break; |
@@ -339,7 +339,7 fieldset#date-range p { margin: 2px 0 2px 0; } | |||||
339 | fieldset#filters table { border-collapse: collapse; } |
|
339 | fieldset#filters table { border-collapse: collapse; } | |
340 | fieldset#filters table td { padding: 0; vertical-align: middle; } |
|
340 | fieldset#filters table td { padding: 0; vertical-align: middle; } | |
341 | fieldset#filters tr.filter { height: 2.1em; } |
|
341 | fieldset#filters tr.filter { height: 2.1em; } | |
342 |
fieldset#filters td.field { width:2 |
|
342 | fieldset#filters td.field { width:250px; } | |
343 | fieldset#filters td.operator { width:170px; } |
|
343 | fieldset#filters td.operator { width:170px; } | |
344 | fieldset#filters td.values { white-space:nowrap; } |
|
344 | fieldset#filters td.values { white-space:nowrap; } | |
345 | fieldset#filters td.values select {min-width:130px;} |
|
345 | fieldset#filters td.values select {min-width:130px;} |
@@ -230,6 +230,22 class IssuesControllerTest < ActionController::TestCase | |||||
230 | assert_equal({}, query.filters) |
|
230 | assert_equal({}, query.filters) | |
231 | end |
|
231 | end | |
232 |
|
232 | |||
|
233 | def test_index_with_project_custom_field_filter | |||
|
234 | field = ProjectCustomField.create!(:name => 'Client', :is_filter => true, :field_format => 'string') | |||
|
235 | CustomValue.create!(:custom_field => field, :customized => Project.find(3), :value => 'Foo') | |||
|
236 | CustomValue.create!(:custom_field => field, :customized => Project.find(5), :value => 'Foo') | |||
|
237 | filter_name = "project.cf_#{field.id}" | |||
|
238 | @request.session[:user_id] = 1 | |||
|
239 | ||||
|
240 | get :index, :set_filter => 1, | |||
|
241 | :f => [filter_name], | |||
|
242 | :op => {filter_name => '='}, | |||
|
243 | :v => {filter_name => ['Foo']} | |||
|
244 | assert_response :success | |||
|
245 | assert_template 'index' | |||
|
246 | assert_equal [3, 5], assigns(:issues).map(&:project_id).uniq.sort | |||
|
247 | end | |||
|
248 | ||||
233 | def test_index_with_query |
|
249 | def test_index_with_query | |
234 | get :index, :project_id => 1, :query_id => 5 |
|
250 | get :index, :project_id => 1, :query_id => 5 | |
235 | assert_response :success |
|
251 | assert_response :success |
@@ -577,6 +577,51 class QueryTest < ActiveSupport::TestCase | |||||
577 | User.current = nil |
|
577 | User.current = nil | |
578 | end |
|
578 | end | |
579 |
|
579 | |||
|
580 | def test_filter_on_project_custom_field | |||
|
581 | field = ProjectCustomField.create!(:name => 'Client', :is_filter => true, :field_format => 'string') | |||
|
582 | CustomValue.create!(:custom_field => field, :customized => Project.find(3), :value => 'Foo') | |||
|
583 | CustomValue.create!(:custom_field => field, :customized => Project.find(5), :value => 'Foo') | |||
|
584 | ||||
|
585 | query = Query.new(:name => '_') | |||
|
586 | filter_name = "project.cf_#{field.id}" | |||
|
587 | assert_include filter_name, query.available_filters.keys | |||
|
588 | query.filters = {filter_name => {:operator => '=', :values => ['Foo']}} | |||
|
589 | assert_equal [3, 5], find_issues_with_query(query).map(&:project_id).uniq.sort | |||
|
590 | end | |||
|
591 | ||||
|
592 | def test_filter_on_author_custom_field | |||
|
593 | field = UserCustomField.create!(:name => 'Client', :is_filter => true, :field_format => 'string') | |||
|
594 | CustomValue.create!(:custom_field => field, :customized => User.find(3), :value => 'Foo') | |||
|
595 | ||||
|
596 | query = Query.new(:name => '_') | |||
|
597 | filter_name = "author.cf_#{field.id}" | |||
|
598 | assert_include filter_name, query.available_filters.keys | |||
|
599 | query.filters = {filter_name => {:operator => '=', :values => ['Foo']}} | |||
|
600 | assert_equal [3], find_issues_with_query(query).map(&:author_id).uniq.sort | |||
|
601 | end | |||
|
602 | ||||
|
603 | def test_filter_on_assigned_to_custom_field | |||
|
604 | field = UserCustomField.create!(:name => 'Client', :is_filter => true, :field_format => 'string') | |||
|
605 | CustomValue.create!(:custom_field => field, :customized => User.find(3), :value => 'Foo') | |||
|
606 | ||||
|
607 | query = Query.new(:name => '_') | |||
|
608 | filter_name = "assigned_to.cf_#{field.id}" | |||
|
609 | assert_include filter_name, query.available_filters.keys | |||
|
610 | query.filters = {filter_name => {:operator => '=', :values => ['Foo']}} | |||
|
611 | assert_equal [3], find_issues_with_query(query).map(&:assigned_to_id).uniq.sort | |||
|
612 | end | |||
|
613 | ||||
|
614 | def test_filter_on_fixed_version_custom_field | |||
|
615 | field = VersionCustomField.create!(:name => 'Client', :is_filter => true, :field_format => 'string') | |||
|
616 | CustomValue.create!(:custom_field => field, :customized => Version.find(2), :value => 'Foo') | |||
|
617 | ||||
|
618 | query = Query.new(:name => '_') | |||
|
619 | filter_name = "fixed_version.cf_#{field.id}" | |||
|
620 | assert_include filter_name, query.available_filters.keys | |||
|
621 | query.filters = {filter_name => {:operator => '=', :values => ['Foo']}} | |||
|
622 | assert_equal [2], find_issues_with_query(query).map(&:fixed_version_id).uniq.sort | |||
|
623 | end | |||
|
624 | ||||
580 | def test_statement_should_be_nil_with_no_filters |
|
625 | def test_statement_should_be_nil_with_no_filters | |
581 | q = Query.new(:name => '_') |
|
626 | q = Query.new(:name => '_') | |
582 | q.filters = {} |
|
627 | q.filters = {} |
General Comments 0
You need to be logged in to leave comments.
Login now