0001-Allow-import-time-entries-for-other-users.patch

Marius BALTEANU, 2019-11-01 23:03

Download (9.72 KB)

View differences:

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