Feature #13244 » 0001-project-setting-to-deny-time-logging-on-closed-issue.patch
| 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) |