##// END OF EJS Templates
Adding missing setter for Query#available_columns...
Eric Davis -
r3571:657aa624a4e3
parent child
Show More
@@ -1,582 +1,590
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2008 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class QueryColumn
19 19 attr_accessor :name, :sortable, :groupable, :default_order
20 20 include Redmine::I18n
21 21
22 22 def initialize(name, options={})
23 23 self.name = name
24 24 self.sortable = options[:sortable]
25 25 self.groupable = options[:groupable] || false
26 26 if groupable == true
27 27 self.groupable = name.to_s
28 28 end
29 29 self.default_order = options[:default_order]
30 30 @caption_key = options[:caption] || "field_#{name}"
31 31 end
32 32
33 33 def caption
34 34 l(@caption_key)
35 35 end
36 36
37 37 # Returns true if the column is sortable, otherwise false
38 38 def sortable?
39 39 !sortable.nil?
40 40 end
41 41
42 42 def value(issue)
43 43 issue.send name
44 44 end
45 45 end
46 46
47 47 class QueryCustomFieldColumn < QueryColumn
48 48
49 49 def initialize(custom_field)
50 50 self.name = "cf_#{custom_field.id}".to_sym
51 51 self.sortable = custom_field.order_statement || false
52 52 if %w(list date bool int).include?(custom_field.field_format)
53 53 self.groupable = custom_field.order_statement
54 54 end
55 55 self.groupable ||= false
56 56 @cf = custom_field
57 57 end
58 58
59 59 def caption
60 60 @cf.name
61 61 end
62 62
63 63 def custom_field
64 64 @cf
65 65 end
66 66
67 67 def value(issue)
68 68 cv = issue.custom_values.detect {|v| v.custom_field_id == @cf.id}
69 69 cv && @cf.cast_value(cv.value)
70 70 end
71 71 end
72 72
73 73 class Query < ActiveRecord::Base
74 74 class StatementInvalid < ::ActiveRecord::StatementInvalid
75 75 end
76 76
77 77 belongs_to :project
78 78 belongs_to :user
79 79 serialize :filters
80 80 serialize :column_names
81 81 serialize :sort_criteria, Array
82 82
83 83 attr_protected :project_id, :user_id
84 84
85 85 validates_presence_of :name, :on => :save
86 86 validates_length_of :name, :maximum => 255
87 87
88 88 @@operators = { "=" => :label_equals,
89 89 "!" => :label_not_equals,
90 90 "o" => :label_open_issues,
91 91 "c" => :label_closed_issues,
92 92 "!*" => :label_none,
93 93 "*" => :label_all,
94 94 ">=" => :label_greater_or_equal,
95 95 "<=" => :label_less_or_equal,
96 96 "<t+" => :label_in_less_than,
97 97 ">t+" => :label_in_more_than,
98 98 "t+" => :label_in,
99 99 "t" => :label_today,
100 100 "w" => :label_this_week,
101 101 ">t-" => :label_less_than_ago,
102 102 "<t-" => :label_more_than_ago,
103 103 "t-" => :label_ago,
104 104 "~" => :label_contains,
105 105 "!~" => :label_not_contains }
106 106
107 107 cattr_reader :operators
108 108
109 109 @@operators_by_filter_type = { :list => [ "=", "!" ],
110 110 :list_status => [ "o", "=", "!", "c", "*" ],
111 111 :list_optional => [ "=", "!", "!*", "*" ],
112 112 :list_subprojects => [ "*", "!*", "=" ],
113 113 :date => [ "<t+", ">t+", "t+", "t", "w", ">t-", "<t-", "t-" ],
114 114 :date_past => [ ">t-", "<t-", "t-", "t", "w" ],
115 115 :string => [ "=", "~", "!", "!~" ],
116 116 :text => [ "~", "!~" ],
117 117 :integer => [ "=", ">=", "<=", "!*", "*" ] }
118 118
119 119 cattr_reader :operators_by_filter_type
120 120
121 121 @@available_columns = [
122 122 QueryColumn.new(:project, :sortable => "#{Project.table_name}.name", :groupable => true),
123 123 QueryColumn.new(:tracker, :sortable => "#{Tracker.table_name}.position", :groupable => true),
124 124 QueryColumn.new(:parent, :sortable => ["#{Issue.table_name}.root_id", "#{Issue.table_name}.lft ASC"], :default_order => 'desc', :caption => :field_parent_issue),
125 125 QueryColumn.new(:status, :sortable => "#{IssueStatus.table_name}.position", :groupable => true),
126 126 QueryColumn.new(:priority, :sortable => "#{IssuePriority.table_name}.position", :default_order => 'desc', :groupable => true),
127 127 QueryColumn.new(:subject, :sortable => "#{Issue.table_name}.subject"),
128 128 QueryColumn.new(:author),
129 129 QueryColumn.new(:assigned_to, :sortable => ["#{User.table_name}.lastname", "#{User.table_name}.firstname", "#{User.table_name}.id"], :groupable => true),
130 130 QueryColumn.new(:updated_on, :sortable => "#{Issue.table_name}.updated_on", :default_order => 'desc'),
131 131 QueryColumn.new(:category, :sortable => "#{IssueCategory.table_name}.name", :groupable => true),
132 132 QueryColumn.new(:fixed_version, :sortable => ["#{Version.table_name}.effective_date", "#{Version.table_name}.name"], :default_order => 'desc', :groupable => true),
133 133 QueryColumn.new(:start_date, :sortable => "#{Issue.table_name}.start_date"),
134 134 QueryColumn.new(:due_date, :sortable => "#{Issue.table_name}.due_date"),
135 135 QueryColumn.new(:estimated_hours, :sortable => "#{Issue.table_name}.estimated_hours"),
136 136 QueryColumn.new(:done_ratio, :sortable => "#{Issue.table_name}.done_ratio", :groupable => true),
137 137 QueryColumn.new(:created_on, :sortable => "#{Issue.table_name}.created_on", :default_order => 'desc'),
138 138 ]
139 139 cattr_reader :available_columns
140 140
141 141 def initialize(attributes = nil)
142 142 super attributes
143 143 self.filters ||= { 'status_id' => {:operator => "o", :values => [""]} }
144 144 end
145 145
146 146 def after_initialize
147 147 # Store the fact that project is nil (used in #editable_by?)
148 148 @is_for_all = project.nil?
149 149 end
150 150
151 151 def validate
152 152 filters.each_key do |field|
153 153 errors.add label_for(field), :blank unless
154 154 # filter requires one or more values
155 155 (values_for(field) and !values_for(field).first.blank?) or
156 156 # filter doesn't require any value
157 157 ["o", "c", "!*", "*", "t", "w"].include? operator_for(field)
158 158 end if filters
159 159 end
160 160
161 161 def editable_by?(user)
162 162 return false unless user
163 163 # Admin can edit them all and regular users can edit their private queries
164 164 return true if user.admin? || (!is_public && self.user_id == user.id)
165 165 # Members can not edit public queries that are for all project (only admin is allowed to)
166 166 is_public && !@is_for_all && user.allowed_to?(:manage_public_queries, project)
167 167 end
168 168
169 169 def available_filters
170 170 return @available_filters if @available_filters
171 171
172 172 trackers = project.nil? ? Tracker.find(:all, :order => 'position') : project.rolled_up_trackers
173 173
174 174 @available_filters = { "status_id" => { :type => :list_status, :order => 1, :values => IssueStatus.find(:all, :order => 'position').collect{|s| [s.name, s.id.to_s] } },
175 175 "tracker_id" => { :type => :list, :order => 2, :values => trackers.collect{|s| [s.name, s.id.to_s] } },
176 176 "priority_id" => { :type => :list, :order => 3, :values => IssuePriority.all.collect{|s| [s.name, s.id.to_s] } },
177 177 "subject" => { :type => :text, :order => 8 },
178 178 "created_on" => { :type => :date_past, :order => 9 },
179 179 "updated_on" => { :type => :date_past, :order => 10 },
180 180 "start_date" => { :type => :date, :order => 11 },
181 181 "due_date" => { :type => :date, :order => 12 },
182 182 "estimated_hours" => { :type => :integer, :order => 13 },
183 183 "done_ratio" => { :type => :integer, :order => 14 }}
184 184
185 185 user_values = []
186 186 user_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged?
187 187 if project
188 188 user_values += project.users.sort.collect{|s| [s.name, s.id.to_s] }
189 189 else
190 190 project_ids = User.current.projects.collect(&:id)
191 191 if project_ids.any?
192 192 # members of the user's projects
193 193 user_values += User.active.find(:all, :conditions => ["#{User.table_name}.id IN (SELECT DISTINCT user_id FROM members WHERE project_id IN (?))", project_ids]).sort.collect{|s| [s.name, s.id.to_s] }
194 194 end
195 195 end
196 196 @available_filters["assigned_to_id"] = { :type => :list_optional, :order => 4, :values => user_values } unless user_values.empty?
197 197 @available_filters["author_id"] = { :type => :list, :order => 5, :values => user_values } unless user_values.empty?
198 198
199 199 if User.current.logged?
200 200 @available_filters["watcher_id"] = { :type => :list, :order => 15, :values => [["<< #{l(:label_me)} >>", "me"]] }
201 201 end
202 202
203 203 if project
204 204 # project specific filters
205 205 unless @project.issue_categories.empty?
206 206 @available_filters["category_id"] = { :type => :list_optional, :order => 6, :values => @project.issue_categories.collect{|s| [s.name, s.id.to_s] } }
207 207 end
208 208 unless @project.shared_versions.empty?
209 209 @available_filters["fixed_version_id"] = { :type => :list_optional, :order => 7, :values => @project.shared_versions.sort.collect{|s| ["#{s.project.name} - #{s.name}", s.id.to_s] } }
210 210 end
211 211 unless @project.descendants.active.empty?
212 212 @available_filters["subproject_id"] = { :type => :list_subprojects, :order => 13, :values => @project.descendants.visible.collect{|s| [s.name, s.id.to_s] } }
213 213 end
214 214 add_custom_fields_filters(@project.all_issue_custom_fields)
215 215 else
216 216 # global filters for cross project issue list
217 217 system_shared_versions = Version.visible.find_all_by_sharing('system')
218 218 unless system_shared_versions.empty?
219 219 @available_filters["fixed_version_id"] = { :type => :list_optional, :order => 7, :values => system_shared_versions.sort.collect{|s| ["#{s.project.name} - #{s.name}", s.id.to_s] } }
220 220 end
221 221 add_custom_fields_filters(IssueCustomField.find(:all, :conditions => {:is_filter => true, :is_for_all => true}))
222 222 end
223 223 @available_filters
224 224 end
225 225
226 226 def add_filter(field, operator, values)
227 227 # values must be an array
228 228 return unless values and values.is_a? Array # and !values.first.empty?
229 229 # check if field is defined as an available filter
230 230 if available_filters.has_key? field
231 231 filter_options = available_filters[field]
232 232 # check if operator is allowed for that filter
233 233 #if @@operators_by_filter_type[filter_options[:type]].include? operator
234 234 # allowed_values = values & ([""] + (filter_options[:values] || []).collect {|val| val[1]})
235 235 # filters[field] = {:operator => operator, :values => allowed_values } if (allowed_values.first and !allowed_values.first.empty?) or ["o", "c", "!*", "*", "t"].include? operator
236 236 #end
237 237 filters[field] = {:operator => operator, :values => values }
238 238 end
239 239 end
240 240
241 241 def add_short_filter(field, expression)
242 242 return unless expression
243 243 parms = expression.scan(/^(o|c|!\*|!|\*)?(.*)$/).first
244 244 add_filter field, (parms[0] || "="), [parms[1] || ""]
245 245 end
246 246
247 247 # Add multiple filters using +add_filter+
248 248 def add_filters(fields, operators, values)
249 249 fields.each do |field|
250 250 add_filter(field, operators[field], values[field])
251 251 end
252 252 end
253 253
254 254 def has_filter?(field)
255 255 filters and filters[field]
256 256 end
257 257
258 258 def operator_for(field)
259 259 has_filter?(field) ? filters[field][:operator] : nil
260 260 end
261 261
262 262 def values_for(field)
263 263 has_filter?(field) ? filters[field][:values] : nil
264 264 end
265 265
266 266 def label_for(field)
267 267 label = available_filters[field][:name] if available_filters.has_key?(field)
268 268 label ||= field.gsub(/\_id$/, "")
269 269 end
270 270
271 271 def available_columns
272 272 return @available_columns if @available_columns
273 273 @available_columns = Query.available_columns
274 274 @available_columns += (project ?
275 275 project.all_issue_custom_fields :
276 276 IssueCustomField.find(:all)
277 277 ).collect {|cf| QueryCustomFieldColumn.new(cf) }
278 278 end
279 279
280 def self.available_columns=(v)
281 self.available_columns = (v)
282 end
283
284 def self.add_available_column(column)
285 self.available_columns << (column) if column.is_a?(QueryColumn)
286 end
287
280 288 # Returns an array of columns that can be used to group the results
281 289 def groupable_columns
282 290 available_columns.select {|c| c.groupable}
283 291 end
284 292
285 293 # Returns a Hash of columns and the key for sorting
286 294 def sortable_columns
287 295 {'id' => "#{Issue.table_name}.id"}.merge(available_columns.inject({}) {|h, column|
288 296 h[column.name.to_s] = column.sortable
289 297 h
290 298 })
291 299 end
292 300
293 301 def columns
294 302 if has_default_columns?
295 303 available_columns.select do |c|
296 304 # Adds the project column by default for cross-project lists
297 305 Setting.issue_list_default_columns.include?(c.name.to_s) || (c.name == :project && project.nil?)
298 306 end
299 307 else
300 308 # preserve the column_names order
301 309 column_names.collect {|name| available_columns.find {|col| col.name == name}}.compact
302 310 end
303 311 end
304 312
305 313 def column_names=(names)
306 314 if names
307 315 names = names.select {|n| n.is_a?(Symbol) || !n.blank? }
308 316 names = names.collect {|n| n.is_a?(Symbol) ? n : n.to_sym }
309 317 # Set column_names to nil if default columns
310 318 if names.map(&:to_s) == Setting.issue_list_default_columns
311 319 names = nil
312 320 end
313 321 end
314 322 write_attribute(:column_names, names)
315 323 end
316 324
317 325 def has_column?(column)
318 326 column_names && column_names.include?(column.name)
319 327 end
320 328
321 329 def has_default_columns?
322 330 column_names.nil? || column_names.empty?
323 331 end
324 332
325 333 def sort_criteria=(arg)
326 334 c = []
327 335 if arg.is_a?(Hash)
328 336 arg = arg.keys.sort.collect {|k| arg[k]}
329 337 end
330 338 c = arg.select {|k,o| !k.to_s.blank?}.slice(0,3).collect {|k,o| [k.to_s, o == 'desc' ? o : 'asc']}
331 339 write_attribute(:sort_criteria, c)
332 340 end
333 341
334 342 def sort_criteria
335 343 read_attribute(:sort_criteria) || []
336 344 end
337 345
338 346 def sort_criteria_key(arg)
339 347 sort_criteria && sort_criteria[arg] && sort_criteria[arg].first
340 348 end
341 349
342 350 def sort_criteria_order(arg)
343 351 sort_criteria && sort_criteria[arg] && sort_criteria[arg].last
344 352 end
345 353
346 354 # Returns the SQL sort order that should be prepended for grouping
347 355 def group_by_sort_order
348 356 if grouped? && (column = group_by_column)
349 357 column.sortable.is_a?(Array) ?
350 358 column.sortable.collect {|s| "#{s} #{column.default_order}"}.join(',') :
351 359 "#{column.sortable} #{column.default_order}"
352 360 end
353 361 end
354 362
355 363 # Returns true if the query is a grouped query
356 364 def grouped?
357 365 !group_by.blank?
358 366 end
359 367
360 368 def group_by_column
361 369 groupable_columns.detect {|c| c.name.to_s == group_by}
362 370 end
363 371
364 372 def group_by_statement
365 373 group_by_column.groupable
366 374 end
367 375
368 376 def project_statement
369 377 project_clauses = []
370 378 if project && !@project.descendants.active.empty?
371 379 ids = [project.id]
372 380 if has_filter?("subproject_id")
373 381 case operator_for("subproject_id")
374 382 when '='
375 383 # include the selected subprojects
376 384 ids += values_for("subproject_id").each(&:to_i)
377 385 when '!*'
378 386 # main project only
379 387 else
380 388 # all subprojects
381 389 ids += project.descendants.collect(&:id)
382 390 end
383 391 elsif Setting.display_subprojects_issues?
384 392 ids += project.descendants.collect(&:id)
385 393 end
386 394 project_clauses << "#{Project.table_name}.id IN (%s)" % ids.join(',')
387 395 elsif project
388 396 project_clauses << "#{Project.table_name}.id = %d" % project.id
389 397 end
390 398 project_clauses << Project.allowed_to_condition(User.current, :view_issues)
391 399 project_clauses.join(' AND ')
392 400 end
393 401
394 402 def statement
395 403 # filters clauses
396 404 filters_clauses = []
397 405 filters.each_key do |field|
398 406 next if field == "subproject_id"
399 407 v = values_for(field).clone
400 408 next unless v and !v.empty?
401 409 operator = operator_for(field)
402 410
403 411 # "me" value subsitution
404 412 if %w(assigned_to_id author_id watcher_id).include?(field)
405 413 v.push(User.current.logged? ? User.current.id.to_s : "0") if v.delete("me")
406 414 end
407 415
408 416 sql = ''
409 417 if field =~ /^cf_(\d+)$/
410 418 # custom field
411 419 db_table = CustomValue.table_name
412 420 db_field = 'value'
413 421 is_custom_filter = true
414 422 sql << "#{Issue.table_name}.id 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=#{$1} WHERE "
415 423 sql << sql_for_field(field, operator, v, db_table, db_field, true) + ')'
416 424 elsif field == 'watcher_id'
417 425 db_table = Watcher.table_name
418 426 db_field = 'user_id'
419 427 sql << "#{Issue.table_name}.id #{ operator == '=' ? 'IN' : 'NOT IN' } (SELECT #{db_table}.watchable_id FROM #{db_table} WHERE #{db_table}.watchable_type='Issue' AND "
420 428 sql << sql_for_field(field, '=', v, db_table, db_field) + ')'
421 429 else
422 430 # regular field
423 431 db_table = Issue.table_name
424 432 db_field = field
425 433 sql << '(' + sql_for_field(field, operator, v, db_table, db_field) + ')'
426 434 end
427 435 filters_clauses << sql
428 436
429 437 end if filters and valid?
430 438
431 439 (filters_clauses << project_statement).join(' AND ')
432 440 end
433 441
434 442 # Returns the issue count
435 443 def issue_count
436 444 Issue.count(:include => [:status, :project], :conditions => statement)
437 445 rescue ::ActiveRecord::StatementInvalid => e
438 446 raise StatementInvalid.new(e.message)
439 447 end
440 448
441 449 # Returns the issue count by group or nil if query is not grouped
442 450 def issue_count_by_group
443 451 r = nil
444 452 if grouped?
445 453 begin
446 454 # Rails will raise an (unexpected) RecordNotFound if there's only a nil group value
447 455 r = Issue.count(:group => group_by_statement, :include => [:status, :project], :conditions => statement)
448 456 rescue ActiveRecord::RecordNotFound
449 457 r = {nil => issue_count}
450 458 end
451 459 c = group_by_column
452 460 if c.is_a?(QueryCustomFieldColumn)
453 461 r = r.keys.inject({}) {|h, k| h[c.custom_field.cast_value(k)] = r[k]; h}
454 462 end
455 463 end
456 464 r
457 465 rescue ::ActiveRecord::StatementInvalid => e
458 466 raise StatementInvalid.new(e.message)
459 467 end
460 468
461 469 # Returns the issues
462 470 # Valid options are :order, :offset, :limit, :include, :conditions
463 471 def issues(options={})
464 472 order_option = [group_by_sort_order, options[:order]].reject {|s| s.blank?}.join(',')
465 473 order_option = nil if order_option.blank?
466 474
467 475 Issue.find :all, :include => ([:status, :project] + (options[:include] || [])).uniq,
468 476 :conditions => Query.merge_conditions(statement, options[:conditions]),
469 477 :order => order_option,
470 478 :limit => options[:limit],
471 479 :offset => options[:offset]
472 480 rescue ::ActiveRecord::StatementInvalid => e
473 481 raise StatementInvalid.new(e.message)
474 482 end
475 483
476 484 # Returns the journals
477 485 # Valid options are :order, :offset, :limit
478 486 def journals(options={})
479 487 Journal.find :all, :include => [:details, :user, {:issue => [:project, :author, :tracker, :status]}],
480 488 :conditions => statement,
481 489 :order => options[:order],
482 490 :limit => options[:limit],
483 491 :offset => options[:offset]
484 492 rescue ::ActiveRecord::StatementInvalid => e
485 493 raise StatementInvalid.new(e.message)
486 494 end
487 495
488 496 # Returns the versions
489 497 # Valid options are :conditions
490 498 def versions(options={})
491 499 Version.find :all, :include => :project,
492 500 :conditions => Query.merge_conditions(project_statement, options[:conditions])
493 501 rescue ::ActiveRecord::StatementInvalid => e
494 502 raise StatementInvalid.new(e.message)
495 503 end
496 504
497 505 private
498 506
499 507 # Helper method to generate the WHERE sql for a +field+, +operator+ and a +value+
500 508 def sql_for_field(field, operator, value, db_table, db_field, is_custom_filter=false)
501 509 sql = ''
502 510 case operator
503 511 when "="
504 512 sql = "#{db_table}.#{db_field} IN (" + value.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + ")"
505 513 when "!"
506 514 sql = "(#{db_table}.#{db_field} IS NULL OR #{db_table}.#{db_field} NOT IN (" + value.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + "))"
507 515 when "!*"
508 516 sql = "#{db_table}.#{db_field} IS NULL"
509 517 sql << " OR #{db_table}.#{db_field} = ''" if is_custom_filter
510 518 when "*"
511 519 sql = "#{db_table}.#{db_field} IS NOT NULL"
512 520 sql << " AND #{db_table}.#{db_field} <> ''" if is_custom_filter
513 521 when ">="
514 522 sql = "#{db_table}.#{db_field} >= #{value.first.to_i}"
515 523 when "<="
516 524 sql = "#{db_table}.#{db_field} <= #{value.first.to_i}"
517 525 when "o"
518 526 sql = "#{IssueStatus.table_name}.is_closed=#{connection.quoted_false}" if field == "status_id"
519 527 when "c"
520 528 sql = "#{IssueStatus.table_name}.is_closed=#{connection.quoted_true}" if field == "status_id"
521 529 when ">t-"
522 530 sql = date_range_clause(db_table, db_field, - value.first.to_i, 0)
523 531 when "<t-"
524 532 sql = date_range_clause(db_table, db_field, nil, - value.first.to_i)
525 533 when "t-"
526 534 sql = date_range_clause(db_table, db_field, - value.first.to_i, - value.first.to_i)
527 535 when ">t+"
528 536 sql = date_range_clause(db_table, db_field, value.first.to_i, nil)
529 537 when "<t+"
530 538 sql = date_range_clause(db_table, db_field, 0, value.first.to_i)
531 539 when "t+"
532 540 sql = date_range_clause(db_table, db_field, value.first.to_i, value.first.to_i)
533 541 when "t"
534 542 sql = date_range_clause(db_table, db_field, 0, 0)
535 543 when "w"
536 544 from = l(:general_first_day_of_week) == '7' ?
537 545 # week starts on sunday
538 546 ((Date.today.cwday == 7) ? Time.now.at_beginning_of_day : Time.now.at_beginning_of_week - 1.day) :
539 547 # week starts on monday (Rails default)
540 548 Time.now.at_beginning_of_week
541 549 sql = "#{db_table}.#{db_field} BETWEEN '%s' AND '%s'" % [connection.quoted_date(from), connection.quoted_date(from + 7.days)]
542 550 when "~"
543 551 sql = "LOWER(#{db_table}.#{db_field}) LIKE '%#{connection.quote_string(value.first.to_s.downcase)}%'"
544 552 when "!~"
545 553 sql = "LOWER(#{db_table}.#{db_field}) NOT LIKE '%#{connection.quote_string(value.first.to_s.downcase)}%'"
546 554 end
547 555
548 556 return sql
549 557 end
550 558
551 559 def add_custom_fields_filters(custom_fields)
552 560 @available_filters ||= {}
553 561
554 562 custom_fields.select(&:is_filter?).each do |field|
555 563 case field.field_format
556 564 when "text"
557 565 options = { :type => :text, :order => 20 }
558 566 when "list"
559 567 options = { :type => :list_optional, :values => field.possible_values, :order => 20}
560 568 when "date"
561 569 options = { :type => :date, :order => 20 }
562 570 when "bool"
563 571 options = { :type => :list, :values => [[l(:general_text_yes), "1"], [l(:general_text_no), "0"]], :order => 20 }
564 572 else
565 573 options = { :type => :string, :order => 20 }
566 574 end
567 575 @available_filters["cf_#{field.id}"] = options.merge({ :name => field.name })
568 576 end
569 577 end
570 578
571 579 # Returns a SQL clause for a date or datetime field.
572 580 def date_range_clause(table, field, from, to)
573 581 s = []
574 582 if from
575 583 s << ("#{table}.#{field} > '%s'" % [connection.quoted_date((Date.yesterday + from).to_time.end_of_day)])
576 584 end
577 585 if to
578 586 s << ("#{table}.#{field} <= '%s'" % [connection.quoted_date((Date.today + to).to_time.end_of_day)])
579 587 end
580 588 s.join(' AND ')
581 589 end
582 590 end
General Comments 0
You need to be logged in to leave comments. Login now