Project

General

Profile

Feature #17720 » filter_after_updated_by_and_last_updated_by.patch

Marius BĂLTEANU, 2016-08-03 00:28

View differences:

app/models/issue_query.rb
124 124
    add_available_filter "priority_id",
125 125
      :type => :list, :values => IssuePriority.all.collect{|s| [s.name, s.id.to_s] }
126 126

  
127
    author_values = []
128
    author_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged?
129
    author_values += users.collect{|s| [s.name, s.id.to_s] }
127
    user_values = []
128
    user_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged?
129
    user_values += users.collect{|s| [s.name, s.id.to_s] }
130 130
    add_available_filter("author_id",
131
      :type => :list, :values => author_values
132
    ) unless author_values.empty?
131
      :type => :list, :values => user_values
132
    ) unless user_values.empty?
133 133

  
134 134
    assigned_to_values = []
135 135
    assigned_to_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged?
......
185 185
        :values => subprojects.collect{|s| [s.name, s.id.to_s] }
186 186
    end
187 187

  
188
    unless user_values.empty?
189
      add_available_filter("updated_by",
190
        :type => :list, :values => user_values
191
      )
192

  
193
      add_available_filter("last_updated_by",
194
        :type => :list, :values => user_values
195
      )
196
    end
197

  
188 198
    add_custom_fields_filters(issue_custom_fields)
189 199

  
190 200
    add_associations_custom_fields_filters :project, :author, :assigned_to, :fixed_version
......
474 484
    end
475 485
  end
476 486

  
487
  def sql_for_updated_by_field(field, operator, value)
488
    db_table = Journal.table_name
489

  
490
    "#{Issue.table_name}.id IN (SELECT #{db_table}.journalized_id FROM #{db_table} WHERE #{db_table}.journalized_type='Issue' AND #{db_table}.journalized_id = #{Issue.table_name}.id AND " +
491
      sql_for_field(field, operator, value, db_table, 'user_id') + ')'
492
  end
493

  
494
  def sql_for_last_updated_by_field(field, operator, value)
495
    db_table = Journal.table_name
496

  
497
    "#{Issue.table_name}.id IN (SELECT max(#{db_table}.journalized_id) FROM #{db_table} WHERE #{db_table}.journalized_type='Issue' AND #{db_table}.created_on = #{Issue.table_name}.updated_on AND #{db_table}.journalized_id = #{Issue.table_name}.id AND " +
498
      sql_for_field(field, operator, value, db_table, 'user_id') + ')'
499
  end
500

  
477 501
  def sql_for_relations(field, operator, value, options={})
478 502
    relation_options = IssueRelation::TYPES[field]
479 503
    return relation_options unless relation_options
app/models/query.rb
675 675
      operator = operator_for(field)
676 676

  
677 677
      # "me" value substitution
678
      if %w(assigned_to_id author_id user_id watcher_id).include?(field)
678
      if %w(assigned_to_id author_id user_id watcher_id updated_by last_updated_by).include?(field)
679 679
        if v.delete("me")
680 680
          if User.current.logged?
681 681
            v.push(User.current.id.to_s)
config/locales/en.yml
363 363
  field_total_estimated_hours: Total estimated time
364 364
  field_default_version: Default version
365 365
  field_remote_ip: IP address
366
  field_updated_by: Updated by
367
  field_last_updated_by: Last updated by
366 368

  
367 369
  setting_app_title: Application title
368 370
  setting_app_subtitle: Application subtitle
test/fixtures/issues.yml
1
--- 
2
issues_001: 
1
---
2
issues_001:
3 3
  created_on: <%= 3.days.ago.to_s(:db) %>
4 4
  project_id: 1
5
  updated_on: <%= 1.day.ago.to_s(:db) %>
5
  updated_on: <%= 1.days.ago.to_date.to_s(:db) %>
6 6
  priority_id: 4
7 7
  subject: Cannot print recipes
8 8
  id: 1
9
  fixed_version_id: 
9
  fixed_version_id:
10 10
  category_id: 1
11 11
  description: Unable to print recipes
12 12
  tracker_id: 1
13
  assigned_to_id: 
13
  assigned_to_id:
14 14
  author_id: 2
15 15
  status_id: 1
16 16
  start_date: <%= 1.day.ago.to_date.to_s(:db) %>
......
19 19
  lft: 1
20 20
  rgt: 2
21 21
  lock_version: 3
22
issues_002: 
22
issues_002:
23 23
  created_on: 2006-07-19 21:04:21 +02:00
24 24
  project_id: 1
25 25
  updated_on: 2006-07-19 21:09:50 +02:00
......
27 27
  subject: Add ingredients categories
28 28
  id: 2
29 29
  fixed_version_id: 2
30
  category_id: 
30
  category_id:
31 31
  description: Ingredients of the recipe should be classified by categories
32 32
  tracker_id: 2
33 33
  assigned_to_id: 3
34 34
  author_id: 2
35 35
  status_id: 2
36 36
  start_date: <%= 2.day.ago.to_date.to_s(:db) %>
37
  due_date: 
37
  due_date:
38 38
  root_id: 2
39 39
  lft: 1
40 40
  rgt: 2
41 41
  lock_version: 3
42 42
  done_ratio: 30
43
issues_003: 
43
issues_003:
44 44
  created_on: 2006-07-19 21:07:27 +02:00
45 45
  project_id: 1
46 46
  updated_on: 2006-07-19 21:07:27 +02:00
47 47
  priority_id: 4
48 48
  subject: Error 281 when updating a recipe
49 49
  id: 3
50
  fixed_version_id: 
51
  category_id: 
50
  fixed_version_id:
51
  category_id:
52 52
  description: Error 281 is encountered when saving a recipe
53 53
  tracker_id: 1
54 54
  assigned_to_id: 3
......
59 59
  root_id: 3
60 60
  lft: 1
61 61
  rgt: 2
62
issues_004: 
62
issues_004:
63 63
  created_on: <%= 5.days.ago.to_s(:db) %>
64 64
  project_id: 2
65 65
  updated_on: <%= 2.days.ago.to_s(:db) %>
66 66
  priority_id: 4
67 67
  subject: Issue on project 2
68 68
  id: 4
69
  fixed_version_id: 
70
  category_id: 
69
  fixed_version_id:
70
  category_id:
71 71
  description: Issue on project 2
72 72
  tracker_id: 1
73 73
  assigned_to_id: 2
......
76 76
  root_id: 4
77 77
  lft: 1
78 78
  rgt: 2
79
issues_005: 
79
issues_005:
80 80
  created_on: <%= 5.days.ago.to_s(:db) %>
81 81
  project_id: 3
82 82
  updated_on: <%= 2.days.ago.to_s(:db) %>
83 83
  priority_id: 4
84 84
  subject: Subproject issue
85 85
  id: 5
86
  fixed_version_id: 
87
  category_id: 
86
  fixed_version_id:
87
  category_id:
88 88
  description: This is an issue on a cookbook subproject
89 89
  tracker_id: 1
90
  assigned_to_id: 
90
  assigned_to_id:
91 91
  author_id: 2
92 92
  status_id: 1
93 93
  root_id: 5
94 94
  lft: 1
95 95
  rgt: 2
96
issues_006: 
96
issues_006:
97 97
  created_on: <%= 1.minute.ago.to_s(:db) %>
98 98
  project_id: 5
99
  updated_on: <%= 1.minute.ago.to_s(:db) %>
99
  updated_on: <%= 1.days.ago.to_date.to_s(:db) %>
100 100
  priority_id: 4
101 101
  subject: Issue of a private subproject
102 102
  id: 6
103
  fixed_version_id: 
104
  category_id: 
103
  fixed_version_id:
104
  category_id:
105 105
  description: This is an issue of a private subproject of cookbook
106 106
  tracker_id: 1
107
  assigned_to_id: 
107
  assigned_to_id:
108 108
  author_id: 2
109 109
  status_id: 1
110 110
  start_date: <%= Date.today.to_s(:db) %>
......
112 112
  root_id: 6
113 113
  lft: 1
114 114
  rgt: 2
115
issues_007: 
115
issues_007:
116 116
  created_on: <%= 10.days.ago.to_s(:db) %>
117 117
  project_id: 1
118 118
  updated_on: <%= 10.days.ago.to_s(:db) %>
119 119
  priority_id: 5
120 120
  subject: Issue due today
121 121
  id: 7
122
  fixed_version_id: 
123
  category_id: 
122
  fixed_version_id:
123
  category_id:
124 124
  description: This is an issue that is due today
125 125
  tracker_id: 1
126
  assigned_to_id: 
126
  assigned_to_id:
127 127
  author_id: 2
128 128
  status_id: 1
129 129
  start_date: <%= 10.days.ago.to_s(:db) %>
......
132 132
  root_id: 7
133 133
  lft: 1
134 134
  rgt: 2
135
issues_008: 
135
issues_008:
136 136
  created_on: <%= 10.days.ago.to_s(:db) %>
137 137
  project_id: 1
138 138
  updated_on: <%= 10.days.ago.to_s(:db) %>
139 139
  priority_id: 5
140 140
  subject: Closed issue
141 141
  id: 8
142
  fixed_version_id: 
143
  category_id: 
142
  fixed_version_id:
143
  category_id:
144 144
  description: This is a closed issue.
145 145
  tracker_id: 1
146
  assigned_to_id: 
146
  assigned_to_id:
147 147
  author_id: 2
148 148
  status_id: 5
149
  start_date: 
150
  due_date: 
149
  start_date:
150
  due_date:
151 151
  lock_version: 0
152 152
  root_id: 8
153 153
  lft: 1
154 154
  rgt: 2
155 155
  closed_on: <%= 3.days.ago.to_s(:db) %>
156
issues_009: 
156
issues_009:
157 157
  created_on: <%= 1.minute.ago.to_s(:db) %>
158 158
  project_id: 5
159 159
  updated_on: <%= 1.minute.ago.to_s(:db) %>
160 160
  priority_id: 5
161 161
  subject: Blocked Issue
162 162
  id: 9
163
  fixed_version_id: 
164
  category_id: 
163
  fixed_version_id:
164
  category_id:
165 165
  description: This is an issue that is blocked by issue #10
166 166
  tracker_id: 1
167
  assigned_to_id: 
167
  assigned_to_id:
168 168
  author_id: 2
169 169
  status_id: 1
170 170
  start_date: <%= Date.today.to_s(:db) %>
......
172 172
  root_id: 9
173 173
  lft: 1
174 174
  rgt: 2
175
issues_010: 
175
issues_010:
176 176
  created_on: <%= 1.minute.ago.to_s(:db) %>
177 177
  project_id: 5
178 178
  updated_on: <%= 1.minute.ago.to_s(:db) %>
179 179
  priority_id: 5
180 180
  subject: Issue Doing the Blocking
181 181
  id: 10
182
  fixed_version_id: 
183
  category_id: 
182
  fixed_version_id:
183
  category_id:
184 184
  description: This is an issue that blocks issue #9
185 185
  tracker_id: 1
186
  assigned_to_id: 
186
  assigned_to_id:
187 187
  author_id: 2
188 188
  status_id: 1
189 189
  start_date: <%= Date.today.to_s(:db) %>
......
191 191
  root_id: 10
192 192
  lft: 1
193 193
  rgt: 2
194
issues_011: 
194
issues_011:
195 195
  created_on: <%= 3.days.ago.to_s(:db) %>
196 196
  project_id: 1
197 197
  updated_on: <%= 1.day.ago.to_s(:db) %>
198 198
  priority_id: 5
199 199
  subject: Closed issue on a closed version
200 200
  id: 11
201
  fixed_version_id: 1 
201
  fixed_version_id: 1
202 202
  category_id: 1
203 203
  description:
204 204
  tracker_id: 1
205
  assigned_to_id: 
205
  assigned_to_id:
206 206
  author_id: 2
207 207
  status_id: 5
208 208
  start_date: <%= 1.day.ago.to_date.to_s(:db) %>
......
211 211
  lft: 1
212 212
  rgt: 2
213 213
  closed_on: <%= 1.day.ago.to_s(:db) %>
214
issues_012: 
214
issues_012:
215 215
  created_on: <%= 3.days.ago.to_s(:db) %>
216 216
  project_id: 1
217 217
  updated_on: <%= 1.day.ago.to_s(:db) %>
218 218
  priority_id: 5
219 219
  subject: Closed issue on a locked version
220 220
  id: 12
221
  fixed_version_id: 2 
221
  fixed_version_id: 2
222 222
  category_id: 1
223 223
  description:
224 224
  tracker_id: 1
225
  assigned_to_id: 
225
  assigned_to_id:
226 226
  author_id: 3
227 227
  status_id: 5
228 228
  start_date: <%= 1.day.ago.to_date.to_s(:db) %>
......
238 238
  priority_id: 4
239 239
  subject: Subproject issue two
240 240
  id: 13
241
  fixed_version_id: 
242
  category_id: 
241
  fixed_version_id:
242
  category_id:
243 243
  description: This is a second issue on a cookbook subproject
244 244
  tracker_id: 1
245
  assigned_to_id: 
245
  assigned_to_id:
246 246
  author_id: 2
247 247
  status_id: 1
248 248
  root_id: 13
......
252 252
  id: 14
253 253
  created_on: <%= 15.days.ago.to_s(:db) %>
254 254
  project_id: 3
255
  updated_on: <%= 15.days.ago.to_s(:db) %>
255
  updated_on: <%= 15.days.ago.to_date.to_s(:db) %>
256 256
  priority_id: 5
257 257
  subject: Private issue on public project
258
  fixed_version_id: 
259
  category_id: 
258
  fixed_version_id:
259
  category_id:
260 260
  description: This is a private issue
261 261
  tracker_id: 1
262
  assigned_to_id: 
262
  assigned_to_id:
263 263
  author_id: 2
264 264
  status_id: 1
265 265
  is_private: true
test/fixtures/journals.yml
1
--- 
1
---
2 2
journals_001:
3 3
  created_on: <%= 2.days.ago.to_date.to_s(:db) %>
4 4
  notes: "Journal notes"
......
6 6
  journalized_type: Issue
7 7
  user_id: 1
8 8
  journalized_id: 1
9
journals_002: 
9
journals_002:
10 10
  created_on: <%= 1.days.ago.to_date.to_s(:db) %>
11 11
  notes: "Some notes with Redmine links: #2, r2."
12 12
  id: 2
......
14 14
  user_id: 2
15 15
  journalized_id: 1
16 16
journals_003:
17
  created_on: <%= 1.days.ago.to_date.to_s(:db) %>
17
  created_on: 2006-07-19 21:09:50 +02:00
18 18
  notes: "A comment with inline image: !picture.jpg! and a reference to #1 and r2."
19 19
  id: 3
20 20
  journalized_type: Issue
......
29 29
  journalized_id: 6
30 30
journals_005:
31 31
  id: 5
32
  created_on: <%= 1.days.ago.to_date.to_s(:db) %>
32
  created_on: <%= 15.days.ago.to_date.to_s(:db) %>
33 33
  notes: "A comment on a private issue."
34 34
  user_id: 2
35 35
  journalized_type: Issue
36 36
  journalized_id: 14
37
journals_006:
38
  id: 6
39
  created_on: 2006-07-19 21:07:27 +02:00
40
  notes: "New updates"
41
  user_id: 3
42
  journalized_type: Issue
43
  journalized_id: 3
test/unit/issue_test.rb
738 738
    user = User.find(2)
739 739
    group = Group.generate!
740 740
    group.users << user
741
 
741

  
742 742
    issue = Issue.generate!(:author_id => 1, :assigned_to => group)
743 743
    assert_include 4, issue.new_statuses_allowed_to(user).map(&:id)
744 744
  end
......
2635 2635
  end
2636 2636

  
2637 2637
  def test_last_journal_id_without_journals_should_return_nil
2638
    assert_nil Issue.find(3).last_journal_id
2638
    assert_nil Issue.find(4).last_journal_id
2639 2639
  end
2640 2640

  
2641 2641
  def test_journals_after_should_return_journals_with_greater_id
test/unit/query_test.rb
683 683
    assert !result.include?(i3)
684 684
  end
685 685

  
686
  def test_filter_updated_by
687
    query = IssueQuery.new(:name => '_')
688
    filter_name = "updated_by"
689
    assert_include filter_name, query.available_filters.keys
690

  
691
    query.filters = {filter_name => {:operator => '=', :values => [2]}}
692
    assert_equal [1,2,14], find_issues_with_query(query).map(&:id).uniq.sort
693

  
694
    query.filters = {filter_name => {:operator => '=', :values => [4]}}
695
    assert_equal [], find_issues_with_query(query).map(&:id).uniq.sort
696

  
697
    query.filters = {filter_name => {:operator => '!', :values => [3]}, 'project_id' => {:operator => '=', :values =>[3]}}
698
    assert_equal [14], find_issues_with_query(query).map(&:id).uniq.sort
699
  end
700

  
701
  def test_filter_updated_by_me
702
    User.current = User.find(1)
703

  
704
    query = IssueQuery.new(:name => '_', :filters => { "updated_by" => {:operator => '=', :values => ['me']}})
705
    assert_equal [1,6], find_issues_with_query(query).map(&:id).uniq.sort
706
  end
707

  
708
  def test_filter_last_updated_by
709
    query = IssueQuery.new(:name => '_')
710
    filter_name = "last_updated_by"
711
    assert_include filter_name, query.available_filters.keys
712

  
713
    query.filters = {filter_name => {:operator => '=', :values => [2]}}
714
    assert_equal [1,2,14], find_issues_with_query(query).map(&:id).uniq.sort
715

  
716
    query.filters = {filter_name => {:operator => '!', :values => [1,2]}}
717
    assert_equal [3], find_issues_with_query(query).map(&:id).uniq.sort
718
  end
719

  
686 720
  def test_user_custom_field_filtered_on_me
687 721
    User.current = User.find(2)
688 722
    cf = IssueCustomField.create!(:field_format => 'user', :is_for_all => true, :is_filter => true, :name => 'User custom field', :tracker_ids => [1])
......
943 977
  def test_filter_on_parent
944 978
    Issue.delete_all
945 979
    parent = Issue.generate_with_descendants!
946
    
980

  
947 981

  
948 982
    query = IssueQuery.new(:name => '_')
949 983
    query.filters = {"parent_id" => {:operator => '=', :values => [parent.id.to_s]}}
......
973 1007
    parent = Issue.generate_with_descendants!
974 1008
    child, leaf = parent.children.sort_by(&:id)
975 1009
    grandchild = child.children.first
976
    
1010

  
977 1011

  
978 1012
    query = IssueQuery.new(:name => '_')
979 1013
    query.filters = {"child_id" => {:operator => '=', :values => [grandchild.id.to_s]}}
(1-1/8)