Project

General

Profile

Feature #3187 » view_own_issues.patch

Patch to add the view_own_issue permission - Gilles Pietri, 2009-05-20 14:25

View differences:

../redmine-0.8.3/app/models/query.rb 2009-05-05 14:28:08.000000000 +0200
16 16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17 17

  
18 18
class QueryColumn  
19
  attr_accessor :name, :sortable, :groupable, :default_order
20
  include Redmine::I18n
19
  attr_accessor :name, :sortable, :default_order
20
  include GLoc
21 21
  
22 22
  def initialize(name, options={})
23 23
    self.name = name
24 24
    self.sortable = options[:sortable]
25
    self.groupable = options[:groupable] || false
26 25
    self.default_order = options[:default_order]
27 26
  end
28 27
  
29 28
  def caption
29
    set_language_if_valid(User.current.language)
30 30
    l("field_#{name}")
31 31
  end
32
  
33
  # Returns true if the column is sortable, otherwise false
34
  def sortable?
35
    !sortable.nil?
36
  end
37 32
end
38 33

  
39 34
class QueryCustomFieldColumn < QueryColumn
40 35

  
41 36
  def initialize(custom_field)
42 37
    self.name = "cf_#{custom_field.id}".to_sym
43
    self.sortable = custom_field.order_statement || false
38
    self.sortable = false
44 39
    @cf = custom_field
45 40
  end
46 41
  
......
58 53
  belongs_to :user
59 54
  serialize :filters
60 55
  serialize :column_names
61
  serialize :sort_criteria, Array
62 56
  
63 57
  attr_protected :project_id, :user_id
64 58
  
......
71 65
                  "c"   => :label_closed_issues,
72 66
                  "!*"  => :label_none,
73 67
                  "*"   => :label_all,
74
                  ">="  => :label_greater_or_equal,
75
                  "<="  => :label_less_or_equal,
68
                  ">="   => '>=',
69
                  "<="   => '<=',
76 70
                  "<t+" => :label_in_less_than,
77 71
                  ">t+" => :label_in_more_than,
78 72
                  "t+"  => :label_in,
......
99 93
  cattr_reader :operators_by_filter_type
100 94

  
101 95
  @@available_columns = [
102
    QueryColumn.new(:project, :sortable => "#{Project.table_name}.name", :groupable => true),
103
    QueryColumn.new(:tracker, :sortable => "#{Tracker.table_name}.position", :groupable => true),
104
    QueryColumn.new(:status, :sortable => "#{IssueStatus.table_name}.position", :groupable => true),
105
    QueryColumn.new(:priority, :sortable => "#{Enumeration.table_name}.position", :default_order => 'desc', :groupable => true),
96
    QueryColumn.new(:project, :sortable => "#{Project.table_name}.name"),
97
    QueryColumn.new(:tracker, :sortable => "#{Tracker.table_name}.position"),
98
    QueryColumn.new(:status, :sortable => "#{IssueStatus.table_name}.position"),
99
    QueryColumn.new(:priority, :sortable => "#{Enumeration.table_name}.position", :default_order => 'desc'),
106 100
    QueryColumn.new(:subject, :sortable => "#{Issue.table_name}.subject"),
107 101
    QueryColumn.new(:author),
108
    QueryColumn.new(:assigned_to, :sortable => ["#{User.table_name}.lastname", "#{User.table_name}.firstname", "#{User.table_name}.id"], :groupable => true),
102
    QueryColumn.new(:assigned_to, :sortable => "#{User.table_name}.lastname"),
109 103
    QueryColumn.new(:updated_on, :sortable => "#{Issue.table_name}.updated_on", :default_order => 'desc'),
110
    QueryColumn.new(:category, :sortable => "#{IssueCategory.table_name}.name", :groupable => true),
111
    QueryColumn.new(:fixed_version, :sortable => ["#{Version.table_name}.effective_date", "#{Version.table_name}.name"], :default_order => 'desc', :groupable => true),
104
    QueryColumn.new(:category, :sortable => "#{IssueCategory.table_name}.name"),
105
    QueryColumn.new(:fixed_version, :sortable => "#{Version.table_name}.effective_date", :default_order => 'desc'),
112 106
    QueryColumn.new(:start_date, :sortable => "#{Issue.table_name}.start_date"),
113 107
    QueryColumn.new(:due_date, :sortable => "#{Issue.table_name}.due_date"),
114 108
    QueryColumn.new(:estimated_hours, :sortable => "#{Issue.table_name}.estimated_hours"),
115
    QueryColumn.new(:done_ratio, :sortable => "#{Issue.table_name}.done_ratio", :groupable => true),
109
    QueryColumn.new(:done_ratio, :sortable => "#{Issue.table_name}.done_ratio"),
116 110
    QueryColumn.new(:created_on, :sortable => "#{Issue.table_name}.created_on", :default_order => 'desc'),
117 111
  ]
118 112
  cattr_reader :available_columns
......
120 114
  def initialize(attributes = nil)
121 115
    super attributes
122 116
    self.filters ||= { 'status_id' => {:operator => "o", :values => [""]} }
117
    set_language_if_valid(User.current.language)
123 118
  end
124 119
  
125 120
  def after_initialize
......
129 124
  
130 125
  def validate
131 126
    filters.each_key do |field|
132
      errors.add label_for(field), :blank unless 
127
      errors.add label_for(field), :activerecord_error_blank unless 
133 128
          # filter requires one or more values
134 129
          (values_for(field) and !values_for(field).first.blank?) or 
135 130
          # filter doesn't require any value
......
171 166
    end
172 167
    @available_filters["assigned_to_id"] = { :type => :list_optional, :order => 4, :values => user_values } unless user_values.empty?
173 168
    @available_filters["author_id"] = { :type => :list, :order => 5, :values => user_values } unless user_values.empty?
174
    
175
    if User.current.logged?
176
      @available_filters["watcher_id"] = { :type => :list, :order => 15, :values => [["<< #{l(:label_me)} >>", "me"]] }
177
    end
178 169
  
179 170
    if project
180 171
      # project specific filters
......
184 175
      unless @project.versions.empty?
185 176
        @available_filters["fixed_version_id"] = { :type => :list_optional, :order => 7, :values => @project.versions.sort.collect{|s| [s.name, s.id.to_s] } }
186 177
      end
187
      unless @project.descendants.active.empty?
188
        @available_filters["subproject_id"] = { :type => :list_subprojects, :order => 13, :values => @project.descendants.visible.collect{|s| [s.name, s.id.to_s] } }
178
      unless @project.active_children.empty?
179
        @available_filters["subproject_id"] = { :type => :list_subprojects, :order => 13, :values => @project.active_children.collect{|s| [s.name, s.id.to_s] } }
189 180
      end
190 181
      add_custom_fields_filters(@project.all_issue_custom_fields)
191 182
    else
......
238 229
    @available_columns = Query.available_columns
239 230
    @available_columns += (project ? 
240 231
                            project.all_issue_custom_fields :
241
                            IssueCustomField.find(:all)
232
                            IssueCustomField.find(:all, :conditions => {:is_for_all => true})
242 233
                           ).collect {|cf| QueryCustomFieldColumn.new(cf) }      
243 234
  end
244 235
  
245
  # Returns an array of columns that can be used to group the results
246
  def groupable_columns
247
    available_columns.select {|c| c.groupable}
248
  end
249
  
250 236
  def columns
251 237
    if has_default_columns?
252 238
      available_columns.select do |c|
......
273 259
    column_names.nil? || column_names.empty?
274 260
  end
275 261
  
276
  def sort_criteria=(arg)
277
    c = []
278
    if arg.is_a?(Hash)
279
      arg = arg.keys.sort.collect {|k| arg[k]}
280
    end
281
    c = arg.select {|k,o| !k.to_s.blank?}.slice(0,3).collect {|k,o| [k.to_s, o == 'desc' ? o : 'asc']}
282
    write_attribute(:sort_criteria, c)
283
  end
284
  
285
  def sort_criteria
286
    read_attribute(:sort_criteria) || []
287
  end
288
  
289
  def sort_criteria_key(arg)
290
    sort_criteria && sort_criteria[arg] && sort_criteria[arg].first
291
  end
292
  
293
  def sort_criteria_order(arg)
294
    sort_criteria && sort_criteria[arg] && sort_criteria[arg].last
295
  end
296
  
297
  # Returns the SQL sort order that should be prepended for grouping
298
  def group_by_sort_order
299
    if grouped? && (column = group_by_column)
300
      column.sortable.is_a?(Array) ?
301
        column.sortable.collect {|s| "#{s} #{column.default_order}"}.join(',') :
302
        "#{column.sortable} #{column.default_order}"
303
    end
304
  end
305
  
306
  # Returns true if the query is a grouped query
307
  def grouped?
308
    !group_by.blank?
309
  end
310
  
311
  def group_by_column
312
    groupable_columns.detect {|c| c.name.to_s == group_by}
313
  end
314
  
315 262
  def project_statement
316 263
    project_clauses = []
317
    if project && !@project.descendants.active.empty?
264
    if project && !@project.active_children.empty?
318 265
      ids = [project.id]
319 266
      if has_filter?("subproject_id")
320 267
        case operator_for("subproject_id")
......
325 272
          # main project only
326 273
        else
327 274
          # all subprojects
328
          ids += project.descendants.collect(&:id)
275
          ids += project.child_ids
329 276
        end
330 277
      elsif Setting.display_subprojects_issues?
331
        ids += project.descendants.collect(&:id)
278
        ids += project.child_ids
332 279
      end
333 280
      project_clauses << "#{Project.table_name}.id IN (%s)" % ids.join(',')
334 281
    elsif project
335 282
      project_clauses << "#{Project.table_name}.id = %d" % project.id
336 283
    end
337
    project_clauses <<  Project.allowed_to_condition(User.current, :view_issues)
338 284
    project_clauses.join(' AND ')
339 285
  end
340 286

  
......
345 291
      next if field == "subproject_id"
346 292
      v = values_for(field).clone
347 293
      next unless v and !v.empty?
348
      operator = operator_for(field)
349
      
350
      # "me" value subsitution
351
      if %w(assigned_to_id author_id watcher_id).include?(field)
352
        v.push(User.current.logged? ? User.current.id.to_s : "0") if v.delete("me")
353
      end
354
      
294
            
355 295
      sql = ''
296
      is_custom_filter = false
356 297
      if field =~ /^cf_(\d+)$/
357 298
        # custom field
358 299
        db_table = CustomValue.table_name
359 300
        db_field = 'value'
360 301
        is_custom_filter = true
361 302
        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 "
362
        sql << sql_for_field(field, operator, v, db_table, db_field, true) + ')'
363
      elsif field == 'watcher_id'
364
        db_table = Watcher.table_name
365
        db_field = 'user_id'
366
        sql << "#{Issue.table_name}.id #{ operator == '=' ? 'IN' : 'NOT IN' } (SELECT #{db_table}.watchable_id FROM #{db_table} WHERE #{db_table}.watchable_type='Issue' AND "
367
        sql << sql_for_field(field, '=', v, db_table, db_field) + ')'
368 303
      else
369 304
        # regular field
370 305
        db_table = Issue.table_name
371 306
        db_field = field
372
        sql << '(' + sql_for_field(field, operator, v, db_table, db_field) + ')'
307
        sql << '('
373 308
      end
374
      filters_clauses << sql
375 309
      
310
      # "me" value subsitution
311
      if %w(assigned_to_id author_id).include?(field)
312
        v.push(User.current.logged? ? User.current.id.to_s : "0") if v.delete("me")
313
      end
314
      
315
      sql = sql + sql_for_field(field, v, db_table, db_field, is_custom_filter)
316
      
317
      sql << ')'
318
      filters_clauses << sql
376 319
    end if filters and valid?
377 320
    
321
    permissions = '('
322
    permissions << Project.allowed_to_condition(User.current, :view_issues)
323
    permissions << ' OR ('
324
    permissions << Project.allowed_to_condition(User.current, :view_own_issues)
325
    permissions << ' AND '
326
    permissions << "#{Issue.table_name}.author_id = #{User.current.id.to_s}"
327
    permissions << '))'
328

  
329
    filters_clauses << permissions
378 330
    (filters_clauses << project_statement).join(' AND ')
379 331
  end
380 332
  
381 333
  private
382 334
  
383
  # Helper method to generate the WHERE sql for a +field+, +operator+ and a +value+
384
  def sql_for_field(field, operator, value, db_table, db_field, is_custom_filter=false)
335
  # Helper method to generate the WHERE sql for a +field+ with a +value+
336
  def sql_for_field(field, value, db_table, db_field, is_custom_filter)
385 337
    sql = ''
386
    case operator
338
    case operator_for field
387 339
    when "="
388 340
      sql = "#{db_table}.#{db_field} IN (" + value.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + ")"
389 341
    when "!"
(1-1/3)