From 7467d04f5388bfc10bb9b64e3507c94535fb6d75 Mon Sep 17 00:00:00 2001 From: Marius BALTEANU Date: Mon, 2 Apr 2018 19:50:02 +0000 Subject: [PATCH] Setting to restrict adding/editing/deleting log times after a preconfigured number of days diff --git a/app/models/time_entry.rb b/app/models/time_entry.rb index 130f398f4..e9474ce35 100644 --- a/app/models/time_entry.rb +++ b/app/models/time_entry.rb @@ -159,6 +159,7 @@ class TimeEntry < ActiveRecord::Base errors.add :activity_id, :inclusion if activity_id_changed? && project && !project.activities.include?(activity) if spent_on_changed? && user errors.add :base, I18n.t(:error_spent_on_future_date) if !Setting.timelog_accept_future_dates? && (spent_on > user.today) + errors.add :base, I18n.t(:error_spent_on_old_date, :days => Setting.timelog_lock_days_older_than.to_i) if date_is_locked_for_user? end end @@ -186,7 +187,7 @@ class TimeEntry < ActiveRecord::Base # Returns true if the time entry can be edited by usr, otherwise false def editable_by?(usr) - visible?(usr) && ( + visible?(usr) && !date_is_locked_for_user? && ( (usr == user && usr.allowed_to?(:edit_own_time_entries, project)) || usr.allowed_to?(:edit_time_entries, project) ) end @@ -231,4 +232,13 @@ class TimeEntry < ActiveRecord::Base 0.0 end end + + # Returns true if time entry is logged on a locked date and the user doesn't have the permission + # to manage time entries on locked dates + def date_is_locked_for_user? + if user && spent_on + number_of_days = Setting.timelog_lock_days_older_than.to_i + ((user.today - spent_on).to_i > number_of_days) && !User.current.allowed_to?(:manage_time_entries_on_locked_dates, self.project) + end + end end diff --git a/app/views/settings/_timelog.html.erb b/app/views/settings/_timelog.html.erb index 01d42384b..5fcf4d34c 100644 --- a/app/views/settings/_timelog.html.erb +++ b/app/views/settings/_timelog.html.erb @@ -9,6 +9,9 @@

<%= setting_check_box :timelog_accept_0_hours %>

<%= setting_check_box :timelog_accept_future_dates %>

+ +

<%= setting_text_field :timelog_lock_days_older_than, :size => 6 %> <%= l(:label_day_plural) %>

+
diff --git a/config/locales/en.yml b/config/locales/en.yml index 89fd151c4..26fca2025 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -233,6 +233,7 @@ en: error_can_not_delete_auth_source: "This authentication mode is in use and cannot be deleted." error_spent_on_future_date: "Cannot log time on a future date" error_not_allowed_to_log_time_for_other_users: "You are not allowed to log time for other users" + error_spent_on_old_date: "Cannot log time on a date older than %{days} days" mail_subject_lost_password: "Your %{value} password" mail_body_lost_password: 'To change your password, click on the following link:' @@ -494,6 +495,7 @@ en: setting_timelog_accept_future_dates: Accept time logs on future dates setting_show_status_changes_in_mail_subject: Show status changes in issue mail notifications subject setting_project_list_defaults: Projects list defaults + setting_timelog_lock_days_older_than: Lock (add/edit/delete time logs) dates older than permission_add_project: Create project permission_add_subprojects: Create subprojects diff --git a/config/settings.yml b/config/settings.yml index 7c0912232..d3727af73 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -337,3 +337,6 @@ timelog_accept_future_dates: default: 1 show_status_changes_in_mail_subject: default: 1 +timelog_lock_days_older_than: + format: int + default: 9999 diff --git a/lib/redmine.rb b/lib/redmine.rb index beaebaec8..a8733e825 100644 --- a/lib/redmine.rb +++ b/lib/redmine.rb @@ -133,6 +133,8 @@ Redmine::AccessControl.map do |map| map.permission :manage_project_activities, {:projects => :settings, :project_enumerations => [:update, :destroy]}, :require => :member map.permission :log_time_for_other_users, :require => :member map.permission :import_time_entries, {} + map.permission :manage_time_entries_on_locked_dates, {}, :require => :loggedin + map.permission :manage_project_activities, {:project_enumerations => [:update, :destroy]}, :require => :member end map.project_module :news do |map| diff --git a/test/fixtures/roles.yml b/test/fixtures/roles.yml index 13433874e..1c24c57cf 100644 --- a/test/fixtures/roles.yml +++ b/test/fixtures/roles.yml @@ -37,6 +37,7 @@ roles_001: - :edit_time_entries - :delete_time_entries - :import_time_entries + - :manage_time_entries_on_locked_dates - :view_news - :manage_news - :comment_news @@ -95,6 +96,7 @@ roles_002: - :log_time - :view_time_entries - :edit_own_time_entries + - :manage_time_entries_on_locked_dates - :view_news - :manage_news - :comment_news @@ -141,6 +143,7 @@ roles_003: - :view_calendar - :log_time - :view_time_entries + - :manage_time_entries_on_locked_dates - :view_news - :manage_news - :comment_news @@ -179,6 +182,7 @@ roles_004: - :view_calendar - :log_time - :view_time_entries + - :manage_time_entries_on_locked_dates - :view_news - :comment_news - :view_documents @@ -206,6 +210,7 @@ roles_005: - :view_gantt - :view_calendar - :view_time_entries + - :manage_time_entries_on_locked_dates - :view_news - :view_documents - :view_wiki_pages diff --git a/test/unit/time_entry_test.rb b/test/unit/time_entry_test.rb index 355a1d8fc..4fa3f3d96 100644 --- a/test/unit/time_entry_test.rb +++ b/test/unit/time_entry_test.rb @@ -127,6 +127,7 @@ class TimeEntryTest < ActiveSupport::TestCase def test_should_accept_future_dates entry = TimeEntry.generate + entry.spent_on = Date.tomorrow entry.spent_on = User.current.today + 1 assert entry.save @@ -135,6 +136,7 @@ class TimeEntryTest < ActiveSupport::TestCase def test_should_not_accept_future_dates_if_disabled with_settings :timelog_accept_future_dates => '0' do entry = TimeEntry.generate + entry.spent_on = Date.tomorrow entry.spent_on = User.current.today + 1 assert !entry.save @@ -142,6 +144,51 @@ class TimeEntryTest < ActiveSupport::TestCase end end + def test_should_allow_time_entries_on_locked_dates_for_users_with_permission + User.current = User.find(2) + + with_settings :timelog_lock_days_older_than => '5' do + entry = TimeEntry.generate(:project => Project.find(1)) + entry.spent_on = Date.today - 6 + + assert entry.save + end + end + + def test_should_not_allow_time_entries_on_locked_dates_for_users_without_permission + User.current = User.find(2) + Role.find(1).remove_permission!(:manage_time_entries_on_locked_dates) + + with_settings :timelog_lock_days_older_than => '5' do + entry = TimeEntry.generate(:project => Project.find(1)) + entry.spent_on = Date.today - 6 + + assert !entry.save + assert entry.errors[:base].present? + end + end + + def test_editable_by_should_return_true_for_time_entry_on_locked_date_and_user_with_permission + user = User.find(2) + User.current = user + time_entry = TimeEntry.find(1) + + with_settings :timelog_lock_days_older_than => '5' do + assert time_entry.editable_by?(user) + end + end + + def test_editable_by_should_return_false_for_time_entry_on_locked_date_and_user_without_permission + user = User.find(2) + User.current = user + Role.find(1).remove_permission!(:manage_time_entries_on_locked_dates) + time_entry = TimeEntry.find(1) + + with_settings :timelog_lock_days_older_than => '5' do + assert !time_entry.editable_by?(user) + end + end + def test_spent_on_with_blank c = TimeEntry.new c.spent_on = '' -- 2.39.3 (Apple Git-146)