##// END OF EJS Templates
remove trailing white-spaces from query model source....
Toshi MARUYAMA -
r5702:22e80f04ae5b
parent child
Show More
@@ -5,20 +5,20
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 class QueryColumn
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]
@@ -29,20 +29,20 class QueryColumn
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
46 46 def css_classes
47 47 name
48 48 end
@@ -59,20 +59,20 class QueryCustomFieldColumn < QueryColumn
59 59 self.groupable ||= false
60 60 @cf = custom_field
61 61 end
62
62
63 63 def caption
64 64 @cf.name
65 65 end
66
66
67 67 def custom_field
68 68 @cf
69 69 end
70
70
71 71 def value(issue)
72 72 cv = issue.custom_values.detect {|v| v.custom_field_id == @cf.id}
73 73 cv && @cf.cast_value(cv.value)
74 74 end
75
75
76 76 def css_classes
77 77 @css_classes ||= "#{name} #{@cf.field_format}"
78 78 end
@@ -81,19 +81,19 end
81 81 class Query < ActiveRecord::Base
82 82 class StatementInvalid < ::ActiveRecord::StatementInvalid
83 83 end
84
84
85 85 belongs_to :project
86 86 belongs_to :user
87 87 serialize :filters
88 88 serialize :column_names
89 89 serialize :sort_criteria, Array
90
90
91 91 attr_protected :project_id, :user_id
92
92
93 93 validates_presence_of :name, :on => :save
94 94 validates_length_of :name, :maximum => 255
95
96 @@operators = { "=" => :label_equals,
95
96 @@operators = { "=" => :label_equals,
97 97 "!" => :label_not_equals,
98 98 "o" => :label_open_issues,
99 99 "c" => :label_closed_issues,
@@ -113,7 +113,7 class Query < ActiveRecord::Base
113 113 "!~" => :label_not_contains }
114 114
115 115 cattr_reader :operators
116
116
117 117 @@operators_by_filter_type = { :list => [ "=", "!" ],
118 118 :list_status => [ "o", "=", "!", "c", "*" ],
119 119 :list_optional => [ "=", "!", "!*", "*" ],
@@ -145,27 +145,27 class Query < ActiveRecord::Base
145 145 QueryColumn.new(:created_on, :sortable => "#{Issue.table_name}.created_on", :default_order => 'desc'),
146 146 ]
147 147 cattr_reader :available_columns
148
148
149 149 def initialize(attributes = nil)
150 150 super attributes
151 151 self.filters ||= { 'status_id' => {:operator => "o", :values => [""]} }
152 152 end
153
153
154 154 def after_initialize
155 155 # Store the fact that project is nil (used in #editable_by?)
156 156 @is_for_all = project.nil?
157 157 end
158
158
159 159 def validate
160 160 filters.each_key do |field|
161 errors.add label_for(field), :blank unless
161 errors.add label_for(field), :blank unless
162 162 # filter requires one or more values
163 (values_for(field) and !values_for(field).first.blank?) or
163 (values_for(field) and !values_for(field).first.blank?) or
164 164 # filter doesn't require any value
165 165 ["o", "c", "!*", "*", "t", "w"].include? operator_for(field)
166 166 end if filters
167 167 end
168
168
169 169 def editable_by?(user)
170 170 return false unless user
171 171 # Admin can edit them all and regular users can edit their private queries
@@ -173,23 +173,23 class Query < ActiveRecord::Base
173 173 # Members can not edit public queries that are for all project (only admin is allowed to)
174 174 is_public && !@is_for_all && user.allowed_to?(:manage_public_queries, project)
175 175 end
176
176
177 177 def available_filters
178 178 return @available_filters if @available_filters
179
179
180 180 trackers = project.nil? ? Tracker.find(:all, :order => 'position') : project.rolled_up_trackers
181
182 @available_filters = { "status_id" => { :type => :list_status, :order => 1, :values => IssueStatus.find(:all, :order => 'position').collect{|s| [s.name, s.id.to_s] } },
183 "tracker_id" => { :type => :list, :order => 2, :values => trackers.collect{|s| [s.name, s.id.to_s] } },
181
182 @available_filters = { "status_id" => { :type => :list_status, :order => 1, :values => IssueStatus.find(:all, :order => 'position').collect{|s| [s.name, s.id.to_s] } },
183 "tracker_id" => { :type => :list, :order => 2, :values => trackers.collect{|s| [s.name, s.id.to_s] } },
184 184 "priority_id" => { :type => :list, :order => 3, :values => IssuePriority.all.collect{|s| [s.name, s.id.to_s] } },
185 "subject" => { :type => :text, :order => 8 },
186 "created_on" => { :type => :date_past, :order => 9 },
185 "subject" => { :type => :text, :order => 8 },
186 "created_on" => { :type => :date_past, :order => 9 },
187 187 "updated_on" => { :type => :date_past, :order => 10 },
188 188 "start_date" => { :type => :date, :order => 11 },
189 189 "due_date" => { :type => :date, :order => 12 },
190 190 "estimated_hours" => { :type => :integer, :order => 13 },
191 191 "done_ratio" => { :type => :integer, :order => 14 }}
192
192
193 193 user_values = []
194 194 user_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged?
195 195 if project
@@ -199,7 +199,7 class Query < ActiveRecord::Base
199 199 if all_projects.any?
200 200 # members of visible projects
201 201 user_values += User.active.find(:all, :conditions => ["#{User.table_name}.id IN (SELECT DISTINCT user_id FROM members WHERE project_id IN (?))", all_projects.collect(&:id)]).sort.collect{|s| [s.name, s.id.to_s] }
202
202
203 203 # project filter
204 204 project_values = []
205 205 Project.project_tree(all_projects) do |p, level|
@@ -217,11 +217,11 class Query < ActiveRecord::Base
217 217
218 218 role_values = Role.givable.collect {|r| [r.name, r.id.to_s] }
219 219 @available_filters["assigned_to_role"] = { :type => :list_optional, :order => 7, :values => role_values } unless role_values.empty?
220
220
221 221 if User.current.logged?
222 222 @available_filters["watcher_id"] = { :type => :list, :order => 15, :values => [["<< #{l(:label_me)} >>", "me"]] }
223 223 end
224
224
225 225 if project
226 226 # project specific filters
227 227 categories = @project.issue_categories.all
@@ -249,7 +249,7 class Query < ActiveRecord::Base
249 249 end
250 250 @available_filters
251 251 end
252
252
253 253 def add_filter(field, operator, values)
254 254 # values must be an array
255 255 return unless values and values.is_a? Array # and !values.first.empty?
@@ -264,7 +264,7 class Query < ActiveRecord::Base
264 264 filters[field] = {:operator => operator, :values => values }
265 265 end
266 266 end
267
267
268 268 def add_short_filter(field, expression)
269 269 return unless expression
270 270 parms = expression.scan(/^(o|c|!\*|!|\*)?(.*)$/).first
@@ -279,19 +279,19 class Query < ActiveRecord::Base
279 279 end
280 280 end
281 281 end
282
282
283 283 def has_filter?(field)
284 284 filters and filters[field]
285 285 end
286
286
287 287 def operator_for(field)
288 288 has_filter?(field) ? filters[field][:operator] : nil
289 289 end
290
290
291 291 def values_for(field)
292 292 has_filter?(field) ? filters[field][:values] : nil
293 293 end
294
294
295 295 def label_for(field)
296 296 label = available_filters[field][:name] if available_filters.has_key?(field)
297 297 label ||= field.gsub(/\_id$/, "")
@@ -303,17 +303,17 class Query < ActiveRecord::Base
303 303 @available_columns += (project ?
304 304 project.all_issue_custom_fields :
305 305 IssueCustomField.find(:all)
306 ).collect {|cf| QueryCustomFieldColumn.new(cf) }
306 ).collect {|cf| QueryCustomFieldColumn.new(cf) }
307 307 end
308 308
309 309 def self.available_columns=(v)
310 310 self.available_columns = (v)
311 311 end
312
312
313 313 def self.add_available_column(column)
314 314 self.available_columns << (column) if column.is_a?(QueryColumn)
315 315 end
316
316
317 317 # Returns an array of columns that can be used to group the results
318 318 def groupable_columns
319 319 available_columns.select {|c| c.groupable}
@@ -326,7 +326,7 class Query < ActiveRecord::Base
326 326 h
327 327 })
328 328 end
329
329
330 330 def columns
331 331 if has_default_columns?
332 332 available_columns.select do |c|
@@ -338,7 +338,7 class Query < ActiveRecord::Base
338 338 column_names.collect {|name| available_columns.find {|col| col.name == name}}.compact
339 339 end
340 340 end
341
341
342 342 def column_names=(names)
343 343 if names
344 344 names = names.select {|n| n.is_a?(Symbol) || !n.blank? }
@@ -350,15 +350,15 class Query < ActiveRecord::Base
350 350 end
351 351 write_attribute(:column_names, names)
352 352 end
353
353
354 354 def has_column?(column)
355 355 column_names && column_names.include?(column.name)
356 356 end
357
357
358 358 def has_default_columns?
359 359 column_names.nil? || column_names.empty?
360 360 end
361
361
362 362 def sort_criteria=(arg)
363 363 c = []
364 364 if arg.is_a?(Hash)
@@ -367,19 +367,19 class Query < ActiveRecord::Base
367 367 c = arg.select {|k,o| !k.to_s.blank?}.slice(0,3).collect {|k,o| [k.to_s, o == 'desc' ? o : 'asc']}
368 368 write_attribute(:sort_criteria, c)
369 369 end
370
370
371 371 def sort_criteria
372 372 read_attribute(:sort_criteria) || []
373 373 end
374
374
375 375 def sort_criteria_key(arg)
376 376 sort_criteria && sort_criteria[arg] && sort_criteria[arg].first
377 377 end
378
378
379 379 def sort_criteria_order(arg)
380 380 sort_criteria && sort_criteria[arg] && sort_criteria[arg].last
381 381 end
382
382
383 383 # Returns the SQL sort order that should be prepended for grouping
384 384 def group_by_sort_order
385 385 if grouped? && (column = group_by_column)
@@ -388,20 +388,20 class Query < ActiveRecord::Base
388 388 "#{column.sortable} #{column.default_order}"
389 389 end
390 390 end
391
391
392 392 # Returns true if the query is a grouped query
393 393 def grouped?
394 394 !group_by_column.nil?
395 395 end
396
396
397 397 def group_by_column
398 398 groupable_columns.detect {|c| c.groupable && c.name.to_s == group_by}
399 399 end
400
400
401 401 def group_by_statement
402 402 group_by_column.try(:groupable)
403 403 end
404
404
405 405 def project_statement
406 406 project_clauses = []
407 407 if project && !@project.descendants.active.empty?
@@ -435,12 +435,12 class Query < ActiveRecord::Base
435 435 v = values_for(field).clone
436 436 next unless v and !v.empty?
437 437 operator = operator_for(field)
438
438
439 439 # "me" value subsitution
440 440 if %w(assigned_to_id author_id watcher_id).include?(field)
441 441 v.push(User.current.logged? ? User.current.id.to_s : "0") if v.delete("me")
442 442 end
443
443
444 444 sql = ''
445 445 if field =~ /^cf_(\d+)$/
446 446 # custom field
@@ -472,7 +472,7 class Query < ActiveRecord::Base
472 472 end
473 473 user_ids.flatten.uniq.compact
474 474 }.sort.collect(&:to_s)
475
475
476 476 sql << '(' + sql_for_field("assigned_to_id", operator, members_of_groups, Issue.table_name, "assigned_to_id", false) + ')'
477 477
478 478 elsif field == "assigned_to_role" # named field
@@ -486,14 +486,14 class Query < ActiveRecord::Base
486 486 roles = Role.givable.find_all_by_id(v)
487 487 end
488 488 roles ||= []
489
489
490 490 members_of_roles = roles.inject([]) {|user_ids, role|
491 491 if role && role.members
492 492 user_ids << role.members.collect(&:user_id)
493 493 end
494 494 user_ids.flatten.uniq.compact
495 495 }.sort.collect(&:to_s)
496
496
497 497 sql << '(' + sql_for_field("assigned_to_id", operator, members_of_roles, Issue.table_name, "assigned_to_id", false) + ')'
498 498 else
499 499 # regular field
@@ -502,22 +502,22 class Query < ActiveRecord::Base
502 502 sql << '(' + sql_for_field(field, operator, v, db_table, db_field) + ')'
503 503 end
504 504 filters_clauses << sql
505
505
506 506 end if filters and valid?
507
507
508 508 filters_clauses << project_statement
509 509 filters_clauses.reject!(&:blank?)
510
510
511 511 filters_clauses.any? ? filters_clauses.join(' AND ') : nil
512 512 end
513
513
514 514 # Returns the issue count
515 515 def issue_count
516 516 Issue.count(:include => [:status, :project], :conditions => statement)
517 517 rescue ::ActiveRecord::StatementInvalid => e
518 518 raise StatementInvalid.new(e.message)
519 519 end
520
520
521 521 # Returns the issue count by group or nil if query is not grouped
522 522 def issue_count_by_group
523 523 r = nil
@@ -537,13 +537,13 class Query < ActiveRecord::Base
537 537 rescue ::ActiveRecord::StatementInvalid => e
538 538 raise StatementInvalid.new(e.message)
539 539 end
540
540
541 541 # Returns the issues
542 542 # Valid options are :order, :offset, :limit, :include, :conditions
543 543 def issues(options={})
544 544 order_option = [group_by_sort_order, options[:order]].reject {|s| s.blank?}.join(',')
545 545 order_option = nil if order_option.blank?
546
546
547 547 Issue.visible.find :all, :include => ([:status, :project] + (options[:include] || [])).uniq,
548 548 :conditions => Query.merge_conditions(statement, options[:conditions]),
549 549 :order => order_option,
@@ -564,7 +564,7 class Query < ActiveRecord::Base
564 564 rescue ::ActiveRecord::StatementInvalid => e
565 565 raise StatementInvalid.new(e.message)
566 566 end
567
567
568 568 # Returns the versions
569 569 # Valid options are :conditions
570 570 def versions(options={})
@@ -573,9 +573,9 class Query < ActiveRecord::Base
573 573 rescue ::ActiveRecord::StatementInvalid => e
574 574 raise StatementInvalid.new(e.message)
575 575 end
576
576
577 577 private
578
578
579 579 # Helper method to generate the WHERE sql for a +field+, +operator+ and a +value+
580 580 def sql_for_field(field, operator, value, db_table, db_field, is_custom_filter=false)
581 581 sql = ''
@@ -632,13 +632,13 class Query < ActiveRecord::Base
632 632 when "!~"
633 633 sql = "LOWER(#{db_table}.#{db_field}) NOT LIKE '%#{connection.quote_string(value.first.to_s.downcase)}%'"
634 634 end
635
635
636 636 return sql
637 637 end
638
638
639 639 def add_custom_fields_filters(custom_fields)
640 640 @available_filters ||= {}
641
641
642 642 custom_fields.select(&:is_filter?).each do |field|
643 643 case field.field_format
644 644 when "text"
@@ -658,7 +658,7 class Query < ActiveRecord::Base
658 658 @available_filters["cf_#{field.id}"] = options.merge({ :name => field.name })
659 659 end
660 660 end
661
661
662 662 # Returns a SQL clause for a date or datetime field.
663 663 def date_range_clause(table, field, from, to)
664 664 s = []
General Comments 0
You need to be logged in to leave comments. Login now