Feature #17720 » filter_after_updated_by_and_last_updated_by_v2.patch
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? |
... | ... | |
194 | 194 |
:values => subprojects.collect{|s| [s.name, s.id.to_s] } |
195 | 195 |
end |
196 | 196 | |
197 |
unless user_values.empty? |
|
198 |
add_available_filter("updated_by", |
|
199 |
:type => :list, :values => user_values |
|
200 |
) |
|
201 | ||
202 |
add_available_filter("last_updated_by", |
|
203 |
:type => :list, :values => user_values |
|
204 |
) |
|
205 |
end |
|
206 | ||
197 | 207 |
add_custom_fields_filters(issue_custom_fields) |
198 | 208 | |
199 | 209 |
add_associations_custom_fields_filters :project, :author, :assigned_to, :fixed_version |
... | ... | |
504 | 514 |
end |
505 | 515 |
end |
506 | 516 | |
517 |
def sql_for_updated_by_field(field, operator, value) |
|
518 |
db_table = Journal.table_name |
|
519 | ||
520 |
"#{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 " + |
|
521 |
sql_for_field(field, operator, value, db_table, 'user_id') + ')' |
|
522 |
end |
|
523 | ||
524 |
def sql_for_last_updated_by_field(field, operator, value) |
|
525 |
db_table = Journal.table_name |
|
526 | ||
527 |
"#{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 " + |
|
528 |
sql_for_field(field, operator, value, db_table, 'user_id') + ')' |
|
529 |
end |
|
530 | ||
507 | 531 |
def sql_for_relations(field, operator, value, options={}) |
508 | 532 |
relation_options = IssueRelation::TYPES[field] |
509 | 533 |
return relation_options unless relation_options |
app/models/query.rb | ||
---|---|---|
695 | 695 |
operator = operator_for(field) |
696 | 696 | |
697 | 697 |
# "me" value substitution |
698 |
if %w(assigned_to_id author_id user_id watcher_id).include?(field) |
|
698 |
if %w(assigned_to_id author_id user_id watcher_id updated_by last_updated_by).include?(field)
|
|
699 | 699 |
if v.delete("me") |
700 | 700 |
if User.current.logged? |
701 | 701 |
v.push(User.current.id.to_s) |
config/locales/en.yml | ||
---|---|---|
365 | 365 |
field_default_version: Default version |
366 | 366 |
field_remote_ip: IP address |
367 | 367 |
field_textarea_font: Font used for text areas |
368 |
field_updated_by: Updated by |
|
369 |
field_last_updated_by: Last updated by |
|
368 | 370 | |
369 | 371 |
setting_app_title: Application title |
370 | 372 |
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 | ||
---|---|---|
2702 | 2702 |
end |
2703 | 2703 | |
2704 | 2704 |
def test_last_journal_id_without_journals_should_return_nil |
2705 |
assert_nil Issue.find(3).last_journal_id
|
|
2705 |
assert_nil Issue.find(4).last_journal_id
|
|
2706 | 2706 |
end |
2707 | 2707 | |
2708 | 2708 |
def test_journals_after_should_return_journals_with_greater_id |
test/unit/query_test.rb | ||
---|---|---|
691 | 691 |
assert !result.include?(i3) |
692 | 692 |
end |
693 | 693 | |
694 |
def test_filter_updated_by |
|
695 |
query = IssueQuery.new(:name => '_') |
|
696 |
filter_name = "updated_by" |
|
697 |
assert_include filter_name, query.available_filters.keys |
|
698 | ||
699 |
query.filters = {filter_name => {:operator => '=', :values => [2]}} |
|
700 |
assert_equal [1,2,14], find_issues_with_query(query).map(&:id).uniq.sort |
|
701 | ||
702 |
query.filters = {filter_name => {:operator => '=', :values => [4]}} |
|
703 |
assert_equal [], find_issues_with_query(query).map(&:id).uniq.sort |
|
704 | ||
705 |
query.filters = {filter_name => {:operator => '!', :values => [3]}, 'project_id' => {:operator => '=', :values =>[3]}} |
|
706 |
assert_equal [14], find_issues_with_query(query).map(&:id).uniq.sort |
|
707 |
end |
|
708 | ||
709 |
def test_filter_updated_by_me |
|
710 |
User.current = User.find(1) |
|
711 | ||
712 |
query = IssueQuery.new(:name => '_', :filters => { "updated_by" => {:operator => '=', :values => ['me']}}) |
|
713 |
assert_equal [1,6], find_issues_with_query(query).map(&:id).uniq.sort |
|
714 |
end |
|
715 | ||
716 |
def test_filter_last_updated_by |
|
717 |
query = IssueQuery.new(:name => '_') |
|
718 |
filter_name = "last_updated_by" |
|
719 |
assert_include filter_name, query.available_filters.keys |
|
720 | ||
721 |
query.filters = {filter_name => {:operator => '=', :values => [2]}} |
|
722 |
assert_equal [1,2,14], find_issues_with_query(query).map(&:id).uniq.sort |
|
723 | ||
724 |
query.filters = {filter_name => {:operator => '!', :values => [1,2]}} |
|
725 |
assert_equal [3], find_issues_with_query(query).map(&:id).uniq.sort |
|
726 |
end |
|
727 | ||
694 | 728 |
def test_user_custom_field_filtered_on_me |
695 | 729 |
User.current = User.find(2) |
696 | 730 |
cf = IssueCustomField.create!(:field_format => 'user', :is_for_all => true, :is_filter => true, :name => 'User custom field', :tracker_ids => [1]) |