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