##// END OF EJS Templates
Search engine: issue custom fields can now be searched....
Jean-Philippe Lang -
r981:58610ec52af2
parent child
Show More
@@ -0,0 +1,9
1 class AddCustomFieldsSearchable < ActiveRecord::Migration
2 def self.up
3 add_column :custom_fields, :searchable, :boolean, :default => false
4 end
5
6 def self.down
7 remove_column :custom_fields, :searchable
8 end
9 end
@@ -43,6 +43,8 class CustomField < ActiveRecord::Base
43 43 def before_validation
44 44 # remove empty values
45 45 self.possible_values = self.possible_values.collect{|v| v unless v.empty?}.compact
46 # make sure these fields are not searchable
47 self.searchable = false if %w(int float date bool).include?(field_format)
46 48 end
47 49
48 50 def validate
@@ -7,21 +7,32 function toggle_custom_field_format() {
7 7 p_length = $("custom_field_min_length");
8 8 p_regexp = $("custom_field_regexp");
9 9 p_values = $("custom_field_possible_values");
10 p_searchable = $("custom_field_searchable");
10 11 switch (format.value) {
11 12 case "list":
12 13 Element.hide(p_length.parentNode);
13 14 Element.hide(p_regexp.parentNode);
15 Element.show(p_searchable.parentNode);
14 16 Element.show(p_values);
15 17 break;
16 18 case "date":
17 19 case "bool":
18 20 Element.hide(p_length.parentNode);
19 21 Element.hide(p_regexp.parentNode);
22 Element.hide(p_searchable.parentNode);
23 Element.hide(p_values);
24 break;
25 case "float":
26 case "int":
27 Element.show(p_length.parentNode);
28 Element.show(p_regexp.parentNode);
29 Element.hide(p_searchable.parentNode);
20 30 Element.hide(p_values);
21 31 break;
22 32 default:
23 33 Element.show(p_length.parentNode);
24 34 Element.show(p_regexp.parentNode);
35 Element.show(p_searchable.parentNode);
25 36 Element.hide(p_values);
26 37 break;
27 38 }
@@ -47,7 +58,6 function deleteValueField(e) {
47 58 //]]>
48 59 </script>
49 60
50 <!--[form:custom_field]-->
51 61 <div class="box">
52 62 <p><%= f.text_field :name, :required => true %></p>
53 63 <p><%= f.select :field_format, custom_field_formats_for_select, {}, :onchange => "toggle_custom_field_format();" %></p>
@@ -59,11 +69,8 function deleteValueField(e) {
59 69 <% (@custom_field.possible_values.to_a + [""]).each do |value| %>
60 70 <span><%= text_field_tag 'custom_field[possible_values][]', value, :size => 30 %> <%= image_to_function "delete.png", "deleteValueField(this);return false" %><br /></span>
61 71 <% end %>
62
63 72 </p>
64 73 </div>
65 <%= javascript_tag "toggle_custom_field_format();" %>
66 <!--[eoform:custom_field]-->
67 74
68 75 <div class="box">
69 76 <% case @custom_field.type.to_s
@@ -78,6 +85,7 when "IssueCustomField" %>
78 85 <p><%= f.check_box :is_required %></p>
79 86 <p><%= f.check_box :is_for_all %></p>
80 87 <p><%= f.check_box :is_filter %></p>
88 <p><%= f.check_box :searchable %></p>
81 89
82 90 <% when "UserCustomField" %>
83 91 <p><%= f.check_box :is_required %></p>
@@ -87,3 +95,4 when "IssueCustomField" %>
87 95
88 96 <% end %>
89 97 </div>
98 <%= javascript_tag "toggle_custom_field_format();" %>
@@ -549,3 +549,4 text_caracters_minimum: Must be at least %d characters long.
549 549 setting_bcc_recipients: Blind carbon copy recipients (bcc)
550 550 button_annotate: Annotate
551 551 label_issues_by: Issues by %s
552 field_searchable: Searchable
@@ -549,3 +549,4 text_caracters_minimum: Must be at least %d characters long.
549 549 setting_bcc_recipients: Blind carbon copy recipients (bcc)
550 550 button_annotate: Annotate
551 551 label_issues_by: Issues by %s
552 field_searchable: Searchable
@@ -549,3 +549,4 text_caracters_minimum: Muss mindestens %d Zeichen lang sein.
549 549 setting_bcc_recipients: Blind carbon copy recipients (bcc)
550 550 button_annotate: Annotate
551 551 label_issues_by: Issues by %s
552 field_searchable: Searchable
@@ -170,6 +170,7 field_redirect_existing_links: Redirect existing links
170 170 field_estimated_hours: Estimated time
171 171 field_column_names: Columns
172 172 field_time_zone: Time zone
173 field_searchable: Searchable
173 174
174 175 setting_app_title: Application title
175 176 setting_app_subtitle: Application subtitle
@@ -552,3 +552,4 setting_time_format: Formato de hora
552 552 setting_bcc_recipients: Blind carbon copy recipients (bcc)
553 553 button_annotate: Annotate
554 554 label_issues_by: Issues by %s
555 field_searchable: Searchable
@@ -170,6 +170,7 field_redirect_existing_links: Rediriger les liens existants
170 170 field_estimated_hours: Temps estimé
171 171 field_column_names: Colonnes
172 172 field_time_zone: Fuseau horaire
173 field_searchable: Utilisé pour les recherches
173 174
174 175 setting_app_title: Titre de l'application
175 176 setting_app_subtitle: Sous-titre de l'application
@@ -549,3 +549,4 text_caracters_minimum: Must be at least %d characters long.
549 549 setting_bcc_recipients: Blind carbon copy recipients (bcc)
550 550 button_annotate: Annotate
551 551 label_issues_by: Issues by %s
552 field_searchable: Searchable
@@ -549,3 +549,4 text_caracters_minimum: Must be at least %d characters long.
549 549 setting_bcc_recipients: Blind carbon copy recipients (bcc)
550 550 button_annotate: Annotate
551 551 label_issues_by: Issues by %s
552 field_searchable: Searchable
@@ -550,3 +550,4 text_caracters_minimum: Must be at least %d characters long.
550 550 setting_bcc_recipients: Blind carbon copy recipients (bcc)
551 551 button_annotate: Annotate
552 552 label_issues_by: Issues by %s
553 field_searchable: Searchable
@@ -549,3 +549,4 text_caracters_minimum: Must be at least %d characters long.
549 549 setting_bcc_recipients: Blind carbon copy recipients (bcc)
550 550 button_annotate: Annotate
551 551 label_issues_by: Issues by %s
552 field_searchable: Searchable
@@ -550,3 +550,4 text_caracters_minimum: Must be at least %d characters long.
550 550 setting_bcc_recipients: Blind carbon copy recipients (bcc)
551 551 button_annotate: Annotate
552 552 label_issues_by: Issues by %s
553 field_searchable: Searchable
@@ -549,3 +549,4 text_caracters_minimum: Musi być nie krótsze niż %d znaków.
549 549 setting_bcc_recipients: Odbiorcy kopii tajnej (kt/bcc)
550 550 button_annotate: Adnotuj
551 551 label_issues_by: Zagadnienia wprowadzone przez %s
552 field_searchable: Searchable
@@ -549,3 +549,4 text_caracters_minimum: Must be at least %d characters long.
549 549 setting_bcc_recipients: Blind carbon copy recipients (bcc)
550 550 button_annotate: Annotate
551 551 label_issues_by: Issues by %s
552 field_searchable: Searchable
@@ -549,3 +549,4 text_caracters_minimum: Must be at least %d characters long.
549 549 setting_bcc_recipients: Blind carbon copy recipients (bcc)
550 550 button_annotate: Annotate
551 551 label_issues_by: Issues by %s
552 field_searchable: Searchable
@@ -549,3 +549,4 text_caracters_minimum: Must be at least %d characters long.
549 549 setting_bcc_recipients: Blind carbon copy recipients (bcc)
550 550 button_annotate: Annotate
551 551 label_issues_by: Issues by %s
552 field_searchable: Searchable
@@ -549,3 +549,4 text_caracters_minimum: Must be at least %d characters long.
549 549 setting_bcc_recipients: Blind carbon copy recipients (bcc)
550 550 button_annotate: Annotate
551 551 label_issues_by: Issues by %s
552 field_searchable: Searchable
@@ -550,3 +550,4 text_caracters_minimum: Must be at least %d characters long.
550 550 setting_bcc_recipients: Blind carbon copy recipients (bcc)
551 551 button_annotate: Annotate
552 552 label_issues_by: Issues by %s
553 field_searchable: Searchable
@@ -550,3 +550,4 text_caracters_minimum: Must be at least %d characters long.
550 550 setting_bcc_recipients: Blind carbon copy recipients (bcc)
551 551 button_annotate: Annotate
552 552 label_issues_by: Issues by %s
553 field_searchable: Searchable
@@ -549,3 +549,4 default_activity_development: 開發
549 549 enumeration_issue_priorities: 項目重要性
550 550 enumeration_doc_categories: 文件分類
551 551 enumeration_activities: 活動 (time tracking)
552 field_searchable: Searchable
@@ -552,3 +552,4 text_caracters_minimum: Must be at least %d characters long.
552 552 setting_bcc_recipients: Blind carbon copy recipients (bcc)
553 553 button_annotate: Annotate
554 554 label_issues_by: Issues by %s
555 field_searchable: Searchable
@@ -11,16 +11,17 custom_fields_001:
11 11 is_required: false
12 12 field_format: list
13 13 custom_fields_002:
14 name: Build
14 name: Searchable field
15 15 min_length: 1
16 16 regexp: ""
17 17 is_for_all: true
18 18 type: IssueCustomField
19 max_length: 10
19 max_length: 100
20 20 possible_values: ""
21 21 id: 2
22 22 is_required: false
23 23 field_format: string
24 searchable: true
24 25 custom_fields_003:
25 26 name: Development status
26 27 min_length: 0
@@ -46,4 +46,11 custom_values_008:
46 46 custom_field_id: 1
47 47 customized_id: 3
48 48 id: 11
49 value: "MySQL" No newline at end of file
49 value: "MySQL"
50 custom_values_009:
51 customized_type: Issue
52 custom_field_id: 2
53 customized_id: 3
54 id: 12
55 value: "this is a stringforcustomfield search"
56 No newline at end of file
@@ -5,7 +5,7 require 'search_controller'
5 5 class SearchController; def rescue_action(e) raise e end; end
6 6
7 7 class SearchControllerTest < Test::Unit::TestCase
8 fixtures :projects, :issues
8 fixtures :projects, :issues, :custom_fields, :custom_values
9 9
10 10 def setup
11 11 @controller = SearchController.new
@@ -25,7 +25,9 class SearchControllerTest < Test::Unit::TestCase
25 25 assert assigns(:results).include?(Project.find(1))
26 26 end
27 27
28 def test_search_in_project
28 def test_search_without_searchable_custom_fields
29 CustomField.update_all "searchable = #{ActiveRecord::Base.connection.quoted_false}"
30
29 31 get :index, :id => 1
30 32 assert_response :success
31 33 assert_template 'index'
@@ -36,6 +38,15 class SearchControllerTest < Test::Unit::TestCase
36 38 assert_template 'index'
37 39 end
38 40
41 def test_search_with_searchable_custom_fields
42 get :index, :id => 1, :q => "stringforcustomfield"
43 assert_response :success
44 results = assigns(:results)
45 assert_not_nil results
46 assert_equal 1, results.size
47 assert results.include?(Issue.find(3))
48 end
49
39 50 def test_quick_jump_to_issue
40 51 # issue of a public project
41 52 get :index, :q => "3"
@@ -49,6 +49,9 module Redmine
49 49 raise 'No date column defined defined.'
50 50 end
51 51
52 # Should we search custom fields on this model ?
53 searchable_options[:search_custom_fields] = !reflect_on_association(:custom_values).nil?
54
52 55 send :include, Redmine::Acts::Searchable::InstanceMethods
53 56 end
54 57 end
@@ -67,11 +70,27 module Redmine
67 70 columns = searchable_options[:columns]
68 71 columns.slice!(1..-1) if options[:titles_only]
69 72
70 sql = ([ '(' + columns.collect {|column| "(LOWER(#{column}) LIKE ?)"}.join(' OR ') + ')' ] * tokens.size).join(options[:all_words] ? ' AND ' : ' OR ')
73 token_clauses = columns.collect {|column| "(LOWER(#{column}) LIKE ?)"}
74
75 if !options[:titles_only] && searchable_options[:search_custom_fields]
76 searchable_custom_field_ids = CustomField.find(:all,
77 :select => 'id',
78 :conditions => { :type => "#{self.name}CustomField",
79 :searchable => true }).collect(&:id)
80 if searchable_custom_field_ids.any?
81 custom_field_sql = "#{table_name}.id IN (SELECT customized_id FROM #{CustomValue.table_name}" +
82 " WHERE customized_type='#{self.name}' AND customized_id=#{table_name}.id AND LOWER(value) LIKE ?" +
83 " AND #{CustomValue.table_name}.custom_field_id IN (#{searchable_custom_field_ids.join(',')}))"
84 token_clauses << custom_field_sql
85 end
86 end
87
88 sql = ([token_clauses.join(' OR ')] * tokens.size).join(options[:all_words] ? ' AND ' : ' OR ')
89
71 90 if options[:offset]
72 91 sql = "(#{sql}) AND (#{searchable_options[:date_column]} " + (options[:before] ? '<' : '>') + "'#{connection.quoted_date(options[:offset])}')"
73 92 end
74 find_options[:conditions] = [sql, * (tokens * columns.size).sort]
93 find_options[:conditions] = [sql, * (tokens * token_clauses.size).sort]
75 94
76 95 results = with_scope(:find => {:conditions => ["#{searchable_options[:project_key]} = ?", project.id]}) do
77 96 find(:all, find_options)
General Comments 0
You need to be logged in to leave comments. Login now