Project

General

Profile

Feature #13244 » 0001-project-setting-to-deny-time-logging-on-closed-issue.patch

Jens Krämer, 2024-12-09 09:48

View differences:

app/controllers/context_menus_controller.rb
33 33

  
34 34
    @can = {
35 35
      :edit => @issues.all?(&:attributes_editable?),
36
      :log_time => (@project && User.current.allowed_to?(:log_time, @project)),
36
      :log_time => @issue&.time_loggable?,
37 37
      :copy => User.current.allowed_to?(:copy_issues, @projects) && Issue.allowed_target_projects.any?,
38 38
      :add_watchers => User.current.allowed_to?(:add_issue_watchers, @projects),
39 39
      :delete => @issues.all?(&:deletable?),
app/models/issue.rb
188 188
    end
189 189
  end
190 190

  
191
  # Returns true if user or current user is allowed to log time on the issue
192
  def time_loggable?(user=User.current)
193
    user.allowed_to?(:log_time, project) && (project.timelog_on_closed_issues? || !closed?)
194
  end
195

  
191 196
  # Returns true if user or current user is allowed to edit or add notes to the issue
192 197
  def editable?(user=User.current)
193 198
    attributes_editable?(user) || notes_addable?(user)
app/models/project.rb
834 834
    'parent_id',
835 835
    'default_version_id',
836 836
    'default_issue_query_id',
837
    'default_assigned_to_id')
837
    'default_assigned_to_id',
838
    'timelog_on_closed_issues')
838 839

  
839 840
  safe_attributes(
840 841
    'is_public',
app/models/time_entry.rb
182 182
    if spent_on && spent_on_changed? && user
183 183
      errors.add :base, I18n.t(:error_spent_on_future_date) if !Setting.timelog_accept_future_dates? && (spent_on > user.today)
184 184
    end
185
    if project && !project.timelog_on_closed_issues? && issue&.closed? && issue&.was_closed?
186
      errors.add :base, I18n.t(:error_timelog_on_closed_issue)
187
    end
185 188
  end
186 189

  
187 190
  def hours=(h)
app/views/issues/_action_menu.html.erb
3 3
            :onclick => 'showAndScrollTo("update", "issue_notes"); return false;',
4 4
            :class => 'icon icon-edit ', :accesskey => accesskey(:edit) if @issue.editable? %>
5 5
<%= link_to sprite_icon('time-add', l(:button_log_time)), new_issue_time_entry_path(@issue),
6
            :class => 'icon icon-time-add ' if User.current.allowed_to?(:log_time, @project) %>
6
            :class => 'icon icon-time-add ' if @issue.time_loggable? %>
7 7
<%= watcher_link(@issue, User.current) %>
8 8
<%= link_to sprite_icon('copy', l(:button_copy)), project_copy_issue_path(@project, @issue),
9 9
            :class => 'icon icon-copy ' if User.current.allowed_to?(:copy_issues, @project) && Issue.allowed_target_projects.any? %>
app/views/issues/_edit.html.erb
9 9
        </div>
10 10
        </fieldset>
11 11
    <% end %>
12
    <% if User.current.allowed_to?(:log_time, @issue.project) %>
12
    <% if @issue.time_loggable? %>
13 13
        <fieldset class="tabular" id="log_time"><legend><%= l(:button_log_time) %></legend>
14 14
        <%= labelled_fields_for :time_entry, @time_entry do |time_entry| %>
15 15
        <div class="splitcontent">
app/views/projects/settings/_issues.html.erb
48 48
  <% end %>
49 49
  </div>
50 50

  
51
  <% if @project.module_enabled?('time_tracking') %>
52
  <div class="box tabular">
53
    <p>
54
      <%= f.check_box :timelog_on_closed_issues %><em class="info"><%=l(:text_timelog_on_closed_issues)%></em>
55
    </p>
56
  </div>
57
  <% end %>
58

  
51 59
  <p><%= submit_tag l(:button_save) %></p>
52 60
<% end %>
config/locales/de.yml
1462 1462
    zero: "%{filename}"
1463 1463
    one: "%{filename} and 1 file"
1464 1464
    other: "%{filename} and %{count} files"
1465
  field_timelog_on_closed_issues: Zeiterfassung an geschlossenen Aufgaben
1466
  text_timelog_on_closed_issues: Deaktivieren um die Zeiterfassung an geschlossenen Aufgaben zu verhindern.
1467
  error_timelog_on_closed_issue: Zeiterfassung an geschlossenen Aufgaben ist nicht möglich.
config/locales/en.yml
1424 1424
  text_user_destroy_confirmation: "Are you sure you want to delete this user and remove all references to them? This cannot be undone. Often, locking a user instead of deleting them is the better solution. To confirm, please enter their login (%{login}) below."
1425 1425
  text_project_destroy_enter_identifier: "To confirm, please enter the project's identifier (%{identifier}) below."
1426 1426
  field_name_or_email_or_login: Name, email or login
1427
  field_timelog_on_closed_issues: Time tracking on closed issues
1428
  text_timelog_on_closed_issues: Uncheck to prevent users from logging time on closed issues.
1429
  error_timelog_on_closed_issue: Cannot log time on a closed issue
db/migrate/20241113033816_add_timelog_on_closed_issues_flag_to_projects.rb
1
class AddTimelogOnClosedIssuesFlagToProjects < ActiveRecord::Migration[5.2]
2
  def change
3
    add_column :projects, :timelog_on_closed_issues, :bool, default: true, null: false
4
  end
5
end
test/functional/issues_controller_test.rb
5912 5912
    assert_select 'input[name=?]', 'time_entry[hours]', 0
5913 5913
  end
5914 5914

  
5915
  def test_get_edit_should_not_display_the_time_entry_form_on_closed_issue
5916
    @request.session[:user_id] = 2
5917
    issue = Issue.find(1)
5918
    issue.project.update_attribute(:timelog_on_closed_issues, false)
5919
    issue.update :status => IssueStatus.find(5)
5920
    get(:edit, :params => {:id => 1})
5921
    assert_select 'input[name=?]', 'time_entry[hours]', 0
5922
  end
5923

  
5915 5924
  def test_get_edit_with_params
5916 5925
    @request.session[:user_id] = 2
5917 5926
    get(
......
6365 6374
    assert mail.subject.include?("(#{IssueStatus.find(2).name})")
6366 6375
  end
6367 6376

  
6377
  def test_update_should_accept_time_entry_when_closing_issue
6378
    issue = Issue.find(1)
6379
    issue.project.update_attribute(:timelog_on_closed_issues, false)
6380
    assert_equal 1, issue.status_id
6381
    @request.session[:user_id] = 2
6382

  
6383
    assert_difference('TimeEntry.count', 1) do
6384
      put(
6385
        :update,
6386
        :params => {
6387
          :id => 1,
6388
          :issue => {
6389
            :status_id => 5,
6390
          },
6391
          :time_entry => {
6392
            :hours => '2',
6393
            :comments => '',
6394
            :activity_id => TimeEntryActivity.first
6395
          }
6396
        }
6397
      )
6398
    end
6399

  
6400
    assert_redirected_to :action => 'show', :id => '1'
6401
    issue.reload
6402
    assert issue.closed?
6403
  end
6404

  
6405
  def test_update_should_not_accept_time_entry_on_closed_issue
6406
    issue = Issue.find(1)
6407
    issue.project.update_attribute(:timelog_on_closed_issues, false)
6408
    issue.update :status => IssueStatus.find(5)
6409
    @request.session[:user_id] = 2
6410

  
6411
    assert_no_difference('TimeEntry.count') do
6412
      put(
6413
        :update,
6414
        :params => {
6415
          :id => 1,
6416
          :issue => {
6417
          },
6418
          :time_entry => {
6419
            :hours => '2',
6420
            :comments => '',
6421
            :activity_id => TimeEntryActivity.first
6422
          }
6423
        }
6424
      )
6425
    end
6426
    assert_response :success
6427
  end
6428

  
6368 6429
  def test_put_update_with_note_only
6369 6430
    notes = 'Note added by IssuesControllerTest#test_update_with_note_only'
6370 6431

  
test/unit/time_entry_test.rb
185 185
    end
186 186
  end
187 187

  
188
  def test_should_not_accept_closed_issue
189
    project = Project.find(1)
190
    project.update_attribute(:timelog_on_closed_issues, false)
191
    entry = TimeEntry.generate project: project
192
    issue = project.issues.to_a.detect(&:closed?)
193
    entry.issue = issue
194
    assert !entry.save
195
    assert entry.errors[:base].present?
196
    assert_equal 'Cannot log time on a closed issue', entry.errors[:base].first
197
  end
198

  
188 199
  def test_should_require_spent_on
189 200
    with_settings :timelog_accept_future_dates => '0' do
190 201
      entry = TimeEntry.find(1)
(10-10/11)