Patch #32196 » 0001-Allow-import-time-entries-for-other-users.patch
app/helpers/imports_helper.rb | ||
---|---|---|
33 | 33 |
tags << options_for_select(import.columns_options, import.mapping[field]) |
34 | 34 |
if values = options[:values] |
35 | 35 |
tags << content_tag('option', '--', :disabled => true) |
36 |
tags << options_for_select(values.map {|text, value| [text, "value:#{value}"]}, import.mapping[field]) |
|
36 |
tags << options_for_select(values.map {|text, value| [text, "value:#{value}"]}, import.mapping[field] || options[:default_value])
|
|
37 | 37 |
end |
38 | 38 |
tags |
39 | 39 |
end |
app/models/time_entry_import.rb | ||
---|---|---|
43 | 43 |
project.activities |
44 | 44 |
end |
45 | 45 | |
46 |
def allowed_target_users |
|
47 |
users = [] |
|
48 |
if project |
|
49 |
users = project.members.active.preload(:user) |
|
50 |
users = users.map(&:user).select{ |u| u.allowed_to?(:log_time, project) } |
|
51 |
end |
|
52 |
users << User.current if User.current.logged? && !users.include?(User.current) |
|
53 |
users |
|
54 |
end |
|
55 | ||
46 | 56 |
def project |
47 | 57 |
project_id = mapping['project_id'].to_i |
48 | 58 |
allowed_target_projects.find_by_id(project_id) || allowed_target_projects.first |
... | ... | |
55 | 65 |
end |
56 | 66 |
end |
57 | 67 | |
68 |
def user_value |
|
69 |
if mapping['user_id'].to_s =~ /\Avalue:(\d+)\z/ |
|
70 |
$1.to_i |
|
71 |
end |
|
72 |
end |
|
73 | ||
58 | 74 |
private |
59 | 75 | |
60 | 76 |
def build_object(row, item) |
61 | 77 |
object = TimeEntry.new |
62 |
object.user = user
|
|
78 |
object.author = user
|
|
63 | 79 | |
64 | 80 |
activity_id = nil |
65 | 81 |
if activity |
... | ... | |
68 | 84 |
activity_id = allowed_target_activities.named(activity_name).first.try(:id) |
69 | 85 |
end |
70 | 86 | |
87 |
user_id = nil |
|
88 |
if User.current.allowed_to?(:log_time_for_other_users, project) |
|
89 |
user_id = user_value || row_value(row, 'user_id') |
|
90 |
else |
|
91 |
user_id = user.id |
|
92 |
end |
|
93 | ||
71 | 94 |
attributes = { |
72 | 95 |
:project_id => project.id, |
73 | 96 |
:activity_id => activity_id, |
97 |
:user_id => user_id, |
|
74 | 98 | |
75 | 99 |
:issue_id => row_value(row, 'issue_id'), |
76 | 100 |
:spent_on => row_date(row, 'spent_on'), |
app/views/imports/_time_entries_fields_mapping.html.erb | ||
---|---|---|
10 | 10 |
:values => @import.allowed_target_activities.sorted.map {|t| [t.name, t.id]} %> |
11 | 11 |
</p> |
12 | 12 | |
13 | ||
14 | 13 |
<div class="splitcontent"> |
15 | 14 |
<div class="splitcontentleft"> |
15 |
<% if User.current.allowed_to?(:log_time_for_other_users, @import.project) %> |
|
16 |
<p> |
|
17 |
<label for="import_mapping_user_id"><%= l(:field_user) %></label> |
|
18 |
<%= mapping_select_tag @import, 'user_id', :required => true, |
|
19 |
:values => @import.allowed_target_users.map {|u| [u.name, u.id]}, :default_value => "value:#{User.current.id}" %> |
|
20 |
</p> |
|
21 |
<% end %> |
|
16 | 22 |
<p> |
17 | 23 |
<label for="import_mapping_issue_id"><%= l(:field_issue) %></label> |
18 | 24 |
<%= mapping_select_tag @import, 'issue_id' %> |
app/views/imports/_time_entries_saved_objects.html.erb | ||
---|---|---|
2 | 2 |
<thead> |
3 | 3 |
<tr> |
4 | 4 |
<th><%= t(:field_project) %></th> |
5 |
<th><%= t(:field_user) %></th> |
|
5 | 6 |
<th><%= t(:field_activity) %></th> |
6 | 7 |
<th><%= t(:field_issue) %></th> |
7 | 8 |
<th><%= t(:field_spent_on) %></th> |
... | ... | |
13 | 14 |
<% saved_objects.each do |time_entry| %> |
14 | 15 |
<tr> |
15 | 16 |
<td><%= link_to_project(time_entry.project, :jump => 'time_entries') if time_entry.project %></td> |
17 |
<td><%= link_to_user time_entry.user %></td> |
|
16 | 18 |
<td><%= time_entry.activity.name if time_entry.activity %></td> |
17 | 19 |
<td><%= link_to_issue time_entry.issue if time_entry.issue %></td> |
18 | 20 |
<td><%= format_date(time_entry.spent_on) %></td> |
test/fixtures/files/import_time_entries.csv | ||
---|---|---|
1 |
row;issue_id;date;hours;comment;activity;overtime |
|
2 |
1;;2020-01-01;1;Some Design;Design;yes |
|
3 |
2;;2020-01-02;2;Some Development;Development;yes |
|
4 |
3;1;2020-01-03;3;Some QA;QA;no |
|
5 |
4;2;2020-01-04;4;Some Inactivity;Inactive Activity;no |
|
1 |
row;issue_id;date;hours;comment;activity;overtime;user_id |
|
2 |
1;;2020-01-01;1;Some Design;Design;yes;2 |
|
3 |
2;;2020-01-02;2;Some Development;Development;yes;2 |
|
4 |
3;1;2020-01-03;3;Some QA;QA;no;3 |
|
5 |
4;2;2020-01-04;4;Some Inactivity;Inactive Activity;no;2 |
test/functional/imports_controller_test.rb | ||
---|---|---|
187 | 187 |
assert_equal '0', mapping['subject'] |
188 | 188 |
end |
189 | 189 | |
190 |
def test_get_mapping_time_entry |
|
191 |
Role.find(1).add_permission! :log_time_for_other_users |
|
192 |
import = generate_time_entry_import |
|
193 |
import.settings = {'separator' => ";", 'wrapper' => '"', 'encoding' => "ISO-8859-1"} |
|
194 |
import.save! |
|
195 | ||
196 |
get :mapping, :params => { |
|
197 |
:id => import.to_param |
|
198 |
} |
|
199 | ||
200 |
assert_response :success |
|
201 | ||
202 |
# 'user_id' field should be available because User#2 has both |
|
203 |
# 'import_time_entries' and 'log_time_for_other_users' permissions |
|
204 |
assert_select 'select[name=?]', 'import_settings[mapping][user_id]' do |
|
205 |
# Current user should be the default value |
|
206 |
assert_select 'option[value="value:2"][selected]', :text => User.find(2).name |
|
207 |
assert_select 'option[value="value:3"]', :text => User.find(3).name |
|
208 |
end |
|
209 |
end |
|
210 | ||
211 |
def test_get_mapping_time_entry_for_user_without_log_time_for_other_users_permission |
|
212 |
import = generate_time_entry_import |
|
213 |
import.settings = {'separator' => ";", 'wrapper' => '"', 'encoding' => "ISO-8859-1"} |
|
214 |
import.save! |
|
215 | ||
216 |
get :mapping, :params => { |
|
217 |
:id => import.to_param |
|
218 |
} |
|
219 | ||
220 |
assert_response :success |
|
221 | ||
222 |
assert_select 'select[name=?]', 'import_settings[mapping][user_id]', 0 |
|
223 |
end |
|
224 | ||
190 | 225 |
def test_get_run |
191 | 226 |
import = generate_import_with_mapping |
192 | 227 |
test/object_helpers.rb | ||
---|---|---|
259 | 259 |
import.save! |
260 | 260 |
import |
261 | 261 |
end |
262 | ||
263 |
def generate_time_entry_import(fixture_name='import_time_entries.csv') |
|
264 |
import = TimeEntryImport.new |
|
265 |
import.user_id = 2 |
|
266 |
import.file = uploaded_test_file(fixture_name, 'text/csv') |
|
267 |
import.save! |
|
268 |
import |
|
269 |
end |
|
262 | 270 |
end |
263 | 271 | |
264 | 272 |
module TrackerObjectHelpers |
test/unit/time_entry_import_test.rb | ||
---|---|---|
36 | 36 | |
37 | 37 |
def setup |
38 | 38 |
set_language_if_valid 'en' |
39 |
User.current = nil |
|
39 | 40 |
end |
40 | 41 | |
41 | 42 |
def test_authorized |
... | ... | |
125 | 126 |
assert_equal '0', fourth.custom_field_value(overtime_cf) |
126 | 127 |
end |
127 | 128 | |
129 |
def test_maps_user_id_for_user_with_permissions |
|
130 |
User.current = User.find(1) |
|
131 |
import = generate_import_with_mapping |
|
132 |
first, second, third, fourth = new_records(TimeEntry, 4) { import.run } |
|
133 | ||
134 |
assert_equal 2, first.user_id |
|
135 |
assert_equal 2, second.user_id |
|
136 |
assert_equal 3, third.user_id |
|
137 |
assert_equal 2, fourth.user_id |
|
138 |
end |
|
139 | ||
140 |
def test_maps_user_to_column_value |
|
141 |
User.current = User.find(1) |
|
142 |
import = generate_import_with_mapping |
|
143 |
import.mapping.merge!('user_id' => 'value:1') |
|
144 |
import.save! |
|
145 |
first, second, third, fourth = new_records(TimeEntry, 4) { import.run } |
|
146 | ||
147 |
assert_equal 1, first.user_id |
|
148 |
assert_equal 1, second.user_id |
|
149 |
assert_equal 1, third.user_id |
|
150 |
assert_equal 1, fourth.user_id |
|
151 |
end |
|
152 | ||
153 |
def test_maps_user_id_for_user_without_permissions |
|
154 |
# User 2 doesn't have log_time_for_other_users permission |
|
155 |
User.current = User.find(2) |
|
156 |
import = generate_import_with_mapping |
|
157 |
first, second, third, fourth = new_records(TimeEntry, 4) { import.run } |
|
158 | ||
159 |
assert_equal 2, first.user_id |
|
160 |
assert_equal 2, second.user_id |
|
161 |
# user_id value from CSV should be ignored |
|
162 |
assert_equal 2, third.user_id |
|
163 |
assert_equal 2, fourth.user_id |
|
164 |
end |
|
165 | ||
128 | 166 |
protected |
129 | 167 | |
130 | 168 |
def generate_import(fixture_name='import_time_entries.csv') |
... | ... | |
146 | 184 |
'issue_id' => '1', |
147 | 185 |
'spent_on' => '2', |
148 | 186 |
'hours' => '3', |
149 |
'comments' => '4' |
|
187 |
'comments' => '4', |
|
188 |
'user_id' => '7' |
|
150 | 189 |
} |
151 | 190 |
} |
152 | 191 |
import.save! |