##// END OF EJS Templates
Ability to save "sort order" in custom queries (#2899)....
Jean-Philippe Lang -
r2504:c7c8dc71f2fc
parent child
Show More
@@ -0,0 +1,9
1 class AddQueriesSortCriteria < ActiveRecord::Migration
2 def self.up
3 add_column :queries, :sort_criteria, :text
4 end
5
6 def self.down
7 remove_column :queries, :sort_criteria
8 end
9 end
@@ -45,7 +45,7 class IssuesController < ApplicationController
45
45
46 def index
46 def index
47 retrieve_query
47 retrieve_query
48 sort_init 'id', 'desc'
48 sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria)
49 sort_update({'id' => "#{Issue.table_name}.id"}.merge(@query.columns.inject({}) {|h, c| h[c.name.to_s] = c.sortable; h}))
49 sort_update({'id' => "#{Issue.table_name}.id"}.merge(@query.columns.inject({}) {|h, c| h[c.name.to_s] = c.sortable; h}))
50
50
51 if @query.valid?
51 if @query.valid?
@@ -471,6 +471,7 private
471 @query = Query.find(params[:query_id], :conditions => cond)
471 @query = Query.find(params[:query_id], :conditions => cond)
472 @query.project = @project
472 @query.project = @project
473 session[:query] = {:id => @query.id, :project_id => @query.project_id}
473 session[:query] = {:id => @query.id, :project_id => @query.project_id}
474 sort_clear
474 else
475 else
475 if params[:set_filter] || session[:query].nil? || session[:query][:project_id] != (@project ? @project.id : nil)
476 if params[:set_filter] || session[:query].nil? || session[:query][:project_id] != (@project ? @project.id : nil)
476 # Give it a name, required to be valid
477 # Give it a name, required to be valid
@@ -69,6 +69,11 module SortHelper
69 normalize!
69 normalize!
70 end
70 end
71
71
72 def criteria=(arg)
73 @criteria = arg
74 normalize!
75 end
76
72 def to_param
77 def to_param
73 @criteria.collect {|k,o| k + (o ? '' : ':desc')}.join(',')
78 @criteria.collect {|k,o| k + (o ? '' : ':desc')}.join(',')
74 end
79 end
@@ -102,24 +107,42 module SortHelper
102 @criteria.first && @criteria.first.last
107 @criteria.first && @criteria.first.last
103 end
108 end
104
109
110 def empty?
111 @criteria.empty?
112 end
113
105 private
114 private
106
115
107 def normalize!
116 def normalize!
108 @criteria = @criteria.collect {|s| [s.first, (s.last == false || s.last == 'desc') ? false : true]}
117 @criteria ||= []
118 @criteria = @criteria.collect {|s| s = s.to_a; [s.first, (s.last == false || s.last == 'desc') ? false : true]}
109 @criteria = @criteria.select {|k,o| @available_criteria.has_key?(k)} if @available_criteria
119 @criteria = @criteria.select {|k,o| @available_criteria.has_key?(k)} if @available_criteria
110 @criteria.slice!(3)
120 @criteria.slice!(3)
111 self
121 self
112 end
122 end
113 end
123 end
124
125 def sort_name
126 controller_name + '_' + action_name + '_sort'
127 end
114
128
115 # Initializes the default sort column (default_key) and sort order
129 # Initializes the default sort.
116 # (default_order).
130 # Examples:
117 #
131 #
118 # - default_key is a column attribute name.
132 # sort_init 'name'
119 # - default_order is 'asc' or 'desc'.
133 # sort_init 'id', 'desc'
134 # sort_init ['name', ['id', 'desc']]
135 # sort_init [['name', 'desc'], ['id', 'desc']]
120 #
136 #
121 def sort_init(default_key, default_order='asc')
137 def sort_init(*args)
122 @sort_default = "#{default_key}:#{default_order}"
138 case args.size
139 when 1
140 @sort_default = args.first.is_a?(Array) ? args.first : [[args.first]]
141 when 2
142 @sort_default = [[args.first, args.last]]
143 else
144 raise ArgumentError
145 end
123 end
146 end
124
147
125 # Updates the sort state. Call this in the controller prior to calling
148 # Updates the sort state. Call this in the controller prior to calling
@@ -127,13 +150,18 module SortHelper
127 # - criteria can be either an array or a hash of allowed keys
150 # - criteria can be either an array or a hash of allowed keys
128 #
151 #
129 def sort_update(criteria)
152 def sort_update(criteria)
130 sort_name = controller_name + '_' + action_name + '_sort'
131
132 @sort_criteria = SortCriteria.new
153 @sort_criteria = SortCriteria.new
133 @sort_criteria.available_criteria = criteria
154 @sort_criteria.available_criteria = criteria
134 @sort_criteria.from_param(params[:sort] || session[sort_name] || @sort_default)
155 @sort_criteria.from_param(params[:sort] || session[sort_name])
156 @sort_criteria.criteria = @sort_default if @sort_criteria.empty?
135 session[sort_name] = @sort_criteria.to_param
157 session[sort_name] = @sort_criteria.to_param
136 end
158 end
159
160 # Clears the sort criteria session data
161 #
162 def sort_clear
163 session[sort_name] = nil
164 end
137
165
138 # Returns an SQL sort clause corresponding to the current sort state.
166 # Returns an SQL sort clause corresponding to the current sort state.
139 # Use this to sort the controller's table items collection.
167 # Use this to sort the controller's table items collection.
@@ -188,13 +216,6 module SortHelper
188 #
216 #
189 # <%= sort_header_tag('id', :title => 'Sort by contact ID', :width => 40) %>
217 # <%= sort_header_tag('id', :title => 'Sort by contact ID', :width => 40) %>
190 #
218 #
191 # Renders:
192 #
193 # <th title="Sort by contact ID" width="40">
194 # <a href="/contact/list?sort_order=desc&amp;sort_key=id">Id</a>
195 # &nbsp;&nbsp;<img alt="Sort_asc" src="/images/sort_asc.png" />
196 # </th>
197 #
198 def sort_header_tag(column, options = {})
219 def sort_header_tag(column, options = {})
199 caption = options.delete(:caption) || column.to_s.humanize
220 caption = options.delete(:caption) || column.to_s.humanize
200 default_order = options.delete(:default_order) || 'asc'
221 default_order = options.delete(:default_order) || 'asc'
@@ -28,6 +28,11 class QueryColumn
28 def caption
28 def caption
29 l("field_#{name}")
29 l("field_#{name}")
30 end
30 end
31
32 # Returns true if the column is sortable, otherwise false
33 def sortable?
34 !sortable.nil?
35 end
31 end
36 end
32
37
33 class QueryCustomFieldColumn < QueryColumn
38 class QueryCustomFieldColumn < QueryColumn
@@ -52,6 +57,7 class Query < ActiveRecord::Base
52 belongs_to :user
57 belongs_to :user
53 serialize :filters
58 serialize :filters
54 serialize :column_names
59 serialize :column_names
60 serialize :sort_criteria, Array
55
61
56 attr_protected :project_id, :user_id
62 attr_protected :project_id, :user_id
57
63
@@ -261,6 +267,27 class Query < ActiveRecord::Base
261 column_names.nil? || column_names.empty?
267 column_names.nil? || column_names.empty?
262 end
268 end
263
269
270 def sort_criteria=(arg)
271 c = []
272 if arg.is_a?(Hash)
273 arg = arg.keys.sort.collect {|k| arg[k]}
274 end
275 c = arg.select {|k,o| !k.to_s.blank?}.slice(0,3).collect {|k,o| [k.to_s, o == 'desc' ? o : 'asc']}
276 write_attribute(:sort_criteria, c)
277 end
278
279 def sort_criteria
280 read_attribute(:sort_criteria) || []
281 end
282
283 def sort_criteria_key(arg)
284 sort_criteria && sort_criteria[arg] && sort_criteria[arg].first
285 end
286
287 def sort_criteria_order(arg)
288 sort_criteria && sort_criteria[arg] && sort_criteria[arg].last
289 end
290
264 def project_statement
291 def project_statement
265 project_clauses = []
292 project_clauses = []
266 if project && !@project.descendants.active.empty?
293 if project && !@project.descendants.active.empty?
@@ -25,5 +25,14
25 <%= render :partial => 'queries/filters', :locals => {:query => query}%>
25 <%= render :partial => 'queries/filters', :locals => {:query => query}%>
26 </fieldset>
26 </fieldset>
27
27
28 <fieldset><legend><%= l(:label_sort) %></legend>
29 <% 3.times do |i| %>
30 <%= i+1 %>: <%= select_tag("query[sort_criteria][#{i}][]",
31 options_for_select([[]] + query.available_columns.select(&:sortable?).collect {|column| [column.caption, column.name.to_s]}, @query.sort_criteria_key(i))) %>
32 <%= select_tag("query[sort_criteria][#{i}][]",
33 options_for_select([[], [l(:label_ascending), 'asc'], [l(:label_descending), 'desc']], @query.sort_criteria_order(i))) %><br />
34 <% end %>
35 </fieldset>
36
28 <%= render :partial => 'queries/columns', :locals => {:query => query}%>
37 <%= render :partial => 'queries/columns', :locals => {:query => query}%>
29 </div>
38 </div>
@@ -778,3 +778,6 bg:
778 field_identity_url: OpenID URL
778 field_identity_url: OpenID URL
779 label_login_with_open_id_option: or login with OpenID
779 label_login_with_open_id_option: or login with OpenID
780 field_content: Content
780 field_content: Content
781 label_descending: Descending
782 label_sort: Sort
783 label_ascending: Ascending
@@ -779,3 +779,6 ca:
779 field_identity_url: OpenID URL
779 field_identity_url: OpenID URL
780 label_login_with_open_id_option: or login with OpenID
780 label_login_with_open_id_option: or login with OpenID
781 field_content: Content
781 field_content: Content
782 label_descending: Descending
783 label_sort: Sort
784 label_ascending: Ascending
@@ -783,3 +783,6 cs:
783 field_identity_url: OpenID URL
783 field_identity_url: OpenID URL
784 label_login_with_open_id_option: or login with OpenID
784 label_login_with_open_id_option: or login with OpenID
785 field_content: Content
785 field_content: Content
786 label_descending: Descending
787 label_sort: Sort
788 label_ascending: Ascending
@@ -811,3 +811,6 da:
811 setting_per_page_options: Objects per page options
811 setting_per_page_options: Objects per page options
812 mail_body_reminder: "{{count}} issue(s) that are assigned to you are due in the next {{days}} days:"
812 mail_body_reminder: "{{count}} issue(s) that are assigned to you are due in the next {{days}} days:"
813 field_content: Content
813 field_content: Content
814 label_descending: Descending
815 label_sort: Sort
816 label_ascending: Ascending
@@ -810,3 +810,6 de:
810 field_identity_url: OpenID URL
810 field_identity_url: OpenID URL
811 label_login_with_open_id_option: or login with OpenID
811 label_login_with_open_id_option: or login with OpenID
812 field_content: Content
812 field_content: Content
813 label_descending: Descending
814 label_sort: Sort
815 label_ascending: Ascending
@@ -662,6 +662,9 en:
662 label_issue_watchers: Watchers
662 label_issue_watchers: Watchers
663 label_example: Example
663 label_example: Example
664 label_display: Display
664 label_display: Display
665 label_sort: Sort
666 label_ascending: Ascending
667 label_descending: Descending
665
668
666 button_login: Login
669 button_login: Login
667 button_submit: Submit
670 button_submit: Submit
@@ -831,3 +831,6 es:
831 field_identity_url: OpenID URL
831 field_identity_url: OpenID URL
832 label_login_with_open_id_option: or login with OpenID
832 label_login_with_open_id_option: or login with OpenID
833 field_content: Content
833 field_content: Content
834 label_descending: Descending
835 label_sort: Sort
836 label_ascending: Ascending
@@ -821,3 +821,6 fi:
821 field_identity_url: OpenID URL
821 field_identity_url: OpenID URL
822 label_login_with_open_id_option: or login with OpenID
822 label_login_with_open_id_option: or login with OpenID
823 field_content: Content
823 field_content: Content
824 label_descending: Descending
825 label_sort: Sort
826 label_ascending: Ascending
@@ -694,6 +694,9 fr:
694 label_issue_watchers: Observateurs
694 label_issue_watchers: Observateurs
695 label_example: Exemple
695 label_example: Exemple
696 label_display: Affichage
696 label_display: Affichage
697 label_sort: Tri
698 label_ascending: Croissant
699 label_descending: Décroissant
697
700
698 button_login: Connexion
701 button_login: Connexion
699 button_submit: Soumettre
702 button_submit: Soumettre
@@ -810,3 +810,6 gl:
810 field_identity_url: OpenID URL
810 field_identity_url: OpenID URL
811 label_login_with_open_id_option: or login with OpenID
811 label_login_with_open_id_option: or login with OpenID
812 field_content: Content
812 field_content: Content
813 label_descending: Descending
814 label_sort: Sort
815 label_ascending: Ascending
@@ -793,3 +793,6 he:
793 field_identity_url: OpenID URL
793 field_identity_url: OpenID URL
794 label_login_with_open_id_option: or login with OpenID
794 label_login_with_open_id_option: or login with OpenID
795 field_content: Content
795 field_content: Content
796 label_descending: Descending
797 label_sort: Sort
798 label_ascending: Ascending
@@ -816,3 +816,6
816 field_identity_url: OpenID URL
816 field_identity_url: OpenID URL
817 label_login_with_open_id_option: bejelentkezés OpenID használatával
817 label_login_with_open_id_option: bejelentkezés OpenID használatával
818 field_content: Content
818 field_content: Content
819 label_descending: Descending
820 label_sort: Sort
821 label_ascending: Ascending
@@ -796,3 +796,6 it:
796 field_identity_url: OpenID URL
796 field_identity_url: OpenID URL
797 label_login_with_open_id_option: or login with OpenID
797 label_login_with_open_id_option: or login with OpenID
798 field_content: Content
798 field_content: Content
799 label_descending: Descending
800 label_sort: Sort
801 label_ascending: Ascending
@@ -809,3 +809,6 ja:
809 field_identity_url: OpenID URL
809 field_identity_url: OpenID URL
810 label_login_with_open_id_option: or login with OpenID
810 label_login_with_open_id_option: or login with OpenID
811 field_content: Content
811 field_content: Content
812 label_descending: Descending
813 label_sort: Sort
814 label_ascending: Ascending
@@ -840,3 +840,6 ko:
840 field_identity_url: OpenID URL
840 field_identity_url: OpenID URL
841 label_login_with_open_id_option: or login with OpenID
841 label_login_with_open_id_option: or login with OpenID
842 field_content: Content
842 field_content: Content
843 label_descending: Descending
844 label_sort: Sort
845 label_ascending: Ascending
@@ -821,3 +821,6 lt:
821 field_identity_url: OpenID URL
821 field_identity_url: OpenID URL
822 label_login_with_open_id_option: or login with OpenID
822 label_login_with_open_id_option: or login with OpenID
823 field_content: Content
823 field_content: Content
824 label_descending: Descending
825 label_sort: Sort
826 label_ascending: Ascending
@@ -766,3 +766,6 nl:
766 field_identity_url: OpenID URL
766 field_identity_url: OpenID URL
767 label_login_with_open_id_option: or login with OpenID
767 label_login_with_open_id_option: or login with OpenID
768 field_content: Content
768 field_content: Content
769 label_descending: Descending
770 label_sort: Sort
771 label_ascending: Ascending
@@ -783,3 +783,6
783 field_identity_url: OpenID URL
783 field_identity_url: OpenID URL
784 label_login_with_open_id_option: or login with OpenID
784 label_login_with_open_id_option: or login with OpenID
785 field_content: Content
785 field_content: Content
786 label_descending: Descending
787 label_sort: Sort
788 label_ascending: Ascending
@@ -814,3 +814,6 pl:
814 field_identity_url: OpenID URL
814 field_identity_url: OpenID URL
815 label_login_with_open_id_option: or login with OpenID
815 label_login_with_open_id_option: or login with OpenID
816 field_content: Content
816 field_content: Content
817 label_descending: Descending
818 label_sort: Sort
819 label_ascending: Ascending
@@ -816,3 +816,6 pt-BR:
816 field_identity_url: OpenID URL
816 field_identity_url: OpenID URL
817 label_login_with_open_id_option: ou use o OpenID
817 label_login_with_open_id_option: ou use o OpenID
818 field_content: Content
818 field_content: Content
819 label_descending: Descending
820 label_sort: Sort
821 label_ascending: Ascending
@@ -802,3 +802,6 pt:
802 field_identity_url: OpenID URL
802 field_identity_url: OpenID URL
803 label_login_with_open_id_option: or login with OpenID
803 label_login_with_open_id_option: or login with OpenID
804 field_content: Content
804 field_content: Content
805 label_descending: Descending
806 label_sort: Sort
807 label_ascending: Ascending
@@ -823,3 +823,6 ro:
823 field_identity_url: OpenID URL
823 field_identity_url: OpenID URL
824 label_login_with_open_id_option: or login with OpenID
824 label_login_with_open_id_option: or login with OpenID
825 field_content: Content
825 field_content: Content
826 label_descending: Descending
827 label_sort: Sort
828 label_ascending: Ascending
@@ -909,3 +909,6 ru:
909 field_identity_url: OpenID URL
909 field_identity_url: OpenID URL
910 label_login_with_open_id_option: or login with OpenID
910 label_login_with_open_id_option: or login with OpenID
911 field_content: Content
911 field_content: Content
912 label_descending: Descending
913 label_sort: Sort
914 label_ascending: Ascending
@@ -782,3 +782,6 sk:
782 field_identity_url: OpenID URL
782 field_identity_url: OpenID URL
783 label_login_with_open_id_option: or login with OpenID
783 label_login_with_open_id_option: or login with OpenID
784 field_content: Content
784 field_content: Content
785 label_descending: Descending
786 label_sort: Sort
787 label_ascending: Ascending
@@ -780,3 +780,6 sl:
780 field_identity_url: OpenID URL
780 field_identity_url: OpenID URL
781 label_login_with_open_id_option: or login with OpenID
781 label_login_with_open_id_option: or login with OpenID
782 field_content: Content
782 field_content: Content
783 label_descending: Descending
784 label_sort: Sort
785 label_ascending: Ascending
@@ -804,3 +804,6
804 field_identity_url: OpenID URL
804 field_identity_url: OpenID URL
805 label_login_with_open_id_option: or login with OpenID
805 label_login_with_open_id_option: or login with OpenID
806 field_content: Content
806 field_content: Content
807 label_descending: Descending
808 label_sort: Sort
809 label_ascending: Ascending
@@ -838,3 +838,6 sv:
838 enumeration_doc_categories: Dokumentkategorier
838 enumeration_doc_categories: Dokumentkategorier
839 enumeration_activities: Aktiviteter (tidsuppföljning)
839 enumeration_activities: Aktiviteter (tidsuppföljning)
840 field_content: Content
840 field_content: Content
841 label_descending: Descending
842 label_sort: Sort
843 label_ascending: Ascending
@@ -781,3 +781,6 th:
781 field_identity_url: OpenID URL
781 field_identity_url: OpenID URL
782 label_login_with_open_id_option: or login with OpenID
782 label_login_with_open_id_option: or login with OpenID
783 field_content: Content
783 field_content: Content
784 label_descending: Descending
785 label_sort: Sort
786 label_ascending: Ascending
@@ -817,3 +817,6 tr:
817 field_identity_url: OpenID URL
817 field_identity_url: OpenID URL
818 label_login_with_open_id_option: or login with OpenID
818 label_login_with_open_id_option: or login with OpenID
819 field_content: Content
819 field_content: Content
820 label_descending: Descending
821 label_sort: Sort
822 label_ascending: Ascending
@@ -780,3 +780,6 uk:
780 field_identity_url: OpenID URL
780 field_identity_url: OpenID URL
781 label_login_with_open_id_option: or login with OpenID
781 label_login_with_open_id_option: or login with OpenID
782 field_content: Content
782 field_content: Content
783 label_descending: Descending
784 label_sort: Sort
785 label_ascending: Ascending
@@ -850,3 +850,6 vi:
850 field_identity_url: OpenID URL
850 field_identity_url: OpenID URL
851 label_login_with_open_id_option: or login with OpenID
851 label_login_with_open_id_option: or login with OpenID
852 field_content: Content
852 field_content: Content
853 label_descending: Descending
854 label_sort: Sort
855 label_ascending: Ascending
@@ -888,3 +888,6
888 enumeration_doc_categories: 文件分類
888 enumeration_doc_categories: 文件分類
889 enumeration_activities: 活動 (時間追蹤)
889 enumeration_activities: 活動 (時間追蹤)
890 field_content: Content
890 field_content: Content
891 label_descending: Descending
892 label_sort: Sort
893 label_ascending: Ascending
@@ -813,3 +813,6 zh:
813 enumeration_doc_categories: 文档类别
813 enumeration_doc_categories: 文档类别
814 enumeration_activities: 活动(时间跟踪)
814 enumeration_activities: 活动(时间跟踪)
815 field_content: Content
815 field_content: Content
816 label_descending: Descending
817 label_sort: Sort
818 label_ascending: Ascending
@@ -67,3 +67,23 queries_004:
67
67
68 user_id: 2
68 user_id: 2
69 column_names:
69 column_names:
70 queries_005:
71 id: 5
72 project_id:
73 is_public: true
74 name: Open issues by priority and tracker
75 filters: |
76 status_id:
77 :values:
78 - "1"
79 :operator: o
80
81 user_id: 1
82 column_names:
83 sort_criteria: |
84 ---
85 - - priority
86 - desc
87 - - tracker
88 - asc
89 No newline at end of file
@@ -111,6 +111,22 class QueriesControllerTest < Test::Unit::TestCase
111 assert q.valid?
111 assert q.valid?
112 end
112 end
113
113
114 def test_new_with_sort
115 @request.session[:user_id] = 1
116 post :new,
117 :confirm => '1',
118 :default_columns => '1',
119 :operators => {"status_id" => "o"},
120 :values => {"status_id" => ["1"]},
121 :query => {:name => "test_new_with_sort",
122 :is_public => "1",
123 :sort_criteria => {"0" => ["due_date", "desc"], "1" => ["tracker", ""]}}
124
125 query = Query.find_by_name("test_new_with_sort")
126 assert_not_nil query
127 assert_equal [['due_date', 'desc'], ['tracker', 'asc']], query.sort_criteria
128 end
129
114 def test_get_edit_global_public_query
130 def test_get_edit_global_public_query
115 @request.session[:user_id] = 1
131 @request.session[:user_id] = 1
116 get :edit, :id => 4
132 get :edit, :id => 4
@@ -202,6 +218,19 class QueriesControllerTest < Test::Unit::TestCase
202 :disabled => 'disabled' }
218 :disabled => 'disabled' }
203 end
219 end
204
220
221 def test_get_edit_sort_criteria
222 @request.session[:user_id] = 1
223 get :edit, :id => 5
224 assert_response :success
225 assert_template 'edit'
226 assert_tag :tag => 'select', :attributes => { :name => 'query[sort_criteria][0][]' },
227 :child => { :tag => 'option', :attributes => { :value => 'priority',
228 :selected => 'selected' } }
229 assert_tag :tag => 'select', :attributes => { :name => 'query[sort_criteria][0][]' },
230 :child => { :tag => 'option', :attributes => { :value => 'desc',
231 :selected => 'selected' } }
232 end
233
205 def test_destroy
234 def test_destroy
206 @request.session[:user_id] = 2
235 @request.session[:user_id] = 2
207 post :destroy, :id => 1
236 post :destroy, :id => 1
@@ -195,6 +195,31 class QueryTest < Test::Unit::TestCase
195 assert q.has_column?(c)
195 assert q.has_column?(c)
196 end
196 end
197
197
198 def test_default_sort
199 q = Query.new
200 assert_equal [], q.sort_criteria
201 end
202
203 def test_set_sort_criteria_with_hash
204 q = Query.new
205 q.sort_criteria = {'0' => ['priority', 'desc'], '2' => ['tracker']}
206 assert_equal [['priority', 'desc'], ['tracker', 'asc']], q.sort_criteria
207 end
208
209 def test_set_sort_criteria_with_array
210 q = Query.new
211 q.sort_criteria = [['priority', 'desc'], 'tracker']
212 assert_equal [['priority', 'desc'], ['tracker', 'asc']], q.sort_criteria
213 end
214
215 def test_create_query_with_sort
216 q = Query.new(:name => 'Sorted')
217 q.sort_criteria = [['priority', 'desc'], 'tracker']
218 assert q.save
219 q.reload
220 assert_equal [['priority', 'desc'], ['tracker', 'asc']], q.sort_criteria
221 end
222
198 def test_sort_by_string_custom_field_asc
223 def test_sort_by_string_custom_field_asc
199 q = Query.new
224 q = Query.new
200 c = q.available_columns.find {|col| col.is_a?(QueryCustomFieldColumn) && col.custom_field.field_format == 'string' }
225 c = q.available_columns.find {|col| col.is_a?(QueryCustomFieldColumn) && col.custom_field.field_format == 'string' }
General Comments 0
You need to be logged in to leave comments. Login now