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! |