From 0495a96cae36139754c89661084438afa98c960f Mon Sep 17 00:00:00 2001 From: ulferts Date: Fri, 30 Aug 2024 09:50:25 +0200 Subject: [PATCH 01/18] restructure i18n spec - use Time.use_zone instead of Time.zone --- spec/lib/redmine/i18n_spec.rb | 64 ++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/spec/lib/redmine/i18n_spec.rb b/spec/lib/redmine/i18n_spec.rb index 34585e021e7f..e51c7d5b9f8a 100644 --- a/spec/lib/redmine/i18n_spec.rb +++ b/spec/lib/redmine/i18n_spec.rb @@ -35,43 +35,45 @@ module OpenProject let(:format) { "%d/%m/%Y" } let(:user) { build_stubbed(:user) } - after do - Time.zone = nil - end - - describe "with user time zone" do - before do - login_as user - allow(user).to receive(:time_zone).and_return(ActiveSupport::TimeZone["Athens"]) - end + describe "#format_time_as_date" do + describe "with user time zone" do + before do + login_as user + allow(user).to receive(:time_zone).and_return(ActiveSupport::TimeZone["Athens"]) + end - it "returns a date in the user timezone for a utc timestamp" do - Time.zone = "UTC" - time = Time.zone.local(2013, 6, 30, 23, 59) - expect(format_time_as_date(time, format)).to eq "01/07/2013" - end + it "returns a date in the user timezone for a utc timestamp" do + Time.use_zone("UTC") do + time = Time.zone.local(2013, 6, 30, 23, 59) + expect(format_time_as_date(time, format)).to eq "01/07/2013" + end + end - it "returns a date in the user timezone for a non-utc timestamp" do - Time.zone = "Berlin" - time = Time.zone.local(2013, 6, 30, 23, 59) - expect(format_time_as_date(time, format)).to eq "01/07/2013" + it "returns a date in the user timezone for a non-utc timestamp" do + Time.use_zone("Berlin") do + time = Time.zone.local(2013, 6, 30, 23, 59) + expect(format_time_as_date(time, format)).to eq "01/07/2013" + end + end end - end - describe "without user time zone" do - before { allow(User.current).to receive(:time_zone).and_return(nil) } + describe "without user time zone" do + before { allow(User.current).to receive(:time_zone).and_return(nil) } - it "returns a date in the local system timezone for a utc timestamp" do - Time.zone = "UTC" - time = Time.zone.local(2013, 6, 30, 23, 59) - allow(time).to receive(:localtime).and_return(ActiveSupport::TimeZone["Athens"].local(2013, 7, 1, 1, 59)) - expect(format_time_as_date(time, format)).to eq "01/07/2013" - end + it "returns a date in the local system timezone for a utc timestamp" do + Time.use_zone("UTC") do + time = Time.zone.local(2013, 6, 30, 23, 59) + allow(time).to receive(:localtime).and_return(ActiveSupport::TimeZone["Athens"].local(2013, 7, 1, 1, 59)) + expect(format_time_as_date(time, format)).to eq "01/07/2013" + end + end - it "returns a date in the original timezone for a non-utc timestamp" do - Time.zone = "Berlin" - time = Time.zone.local(2013, 6, 30, 23, 59) - expect(format_time_as_date(time, format)).to eq "30/06/2013" + it "returns a date in the original timezone for a non-utc timestamp" do + Time.use_zone("Berlin") do + time = Time.zone.local(2013, 6, 30, 23, 59) + expect(format_time_as_date(time, format)).to eq "30/06/2013" + end + end end end From b8409906927ef8bd2efc0315df091af4c5b1c0ac Mon Sep 17 00:00:00 2001 From: ulferts Date: Fri, 30 Aug 2024 10:17:48 +0200 Subject: [PATCH 02/18] introduce a helper method for rendering the user's time zone --- lib_static/redmine/i18n.rb | 4 ++ .../side_panel/details_component.html.erb | 2 +- .../meeting/app/forms/meeting/start_time.rb | 4 +- spec/lib/redmine/i18n_spec.rb | 42 +++++++++++++++++++ 4 files changed, 50 insertions(+), 2 deletions(-) diff --git a/lib_static/redmine/i18n.rb b/lib_static/redmine/i18n.rb index c54f18d32d91..6604ba31fc67 100644 --- a/lib_static/redmine/i18n.rb +++ b/lib_static/redmine/i18n.rb @@ -147,6 +147,10 @@ def format_time(time, include_date = true) (Setting.time_format.blank? ? ::I18n.l(local, format: :time) : local.strftime(Setting.time_format)) end + def format_time_zone + (User.current.time_zone || Time.zone).to_s[/\((.*?)\)/m, 1] + end + def day_name(day) ::I18n.t("date.day_names")[day % 7] end diff --git a/modules/meeting/app/components/meetings/side_panel/details_component.html.erb b/modules/meeting/app/components/meetings/side_panel/details_component.html.erb index a49740fe66e1..b7425f44e1a1 100644 --- a/modules/meeting/app/components/meetings/side_panel/details_component.html.erb +++ b/modules/meeting/app/components/meetings/side_panel/details_component.html.erb @@ -36,7 +36,7 @@ time.with_column(ml: 2) do render(Primer::Beta::Text.new(color: :subtle, font_size: :small)) do - (User.current.time_zone || Time.zone).to_s[/\((.*?)\)/m, 1] + format_time_zone end end end diff --git a/modules/meeting/app/forms/meeting/start_time.rb b/modules/meeting/app/forms/meeting/start_time.rb index d077e40230f0..15fbf9e9649b 100644 --- a/modules/meeting/app/forms/meeting/start_time.rb +++ b/modules/meeting/app/forms/meeting/start_time.rb @@ -27,6 +27,8 @@ #++ class Meeting::StartTime < ApplicationForm + include Redmine::I18n + form do |meeting_form| meeting_form.text_field( name: :start_time_hour, @@ -36,7 +38,7 @@ class Meeting::StartTime < ApplicationForm label: Meeting.human_attribute_name(:start_time), leading_visual: { icon: :clock }, required: true, - caption: Time.zone.to_s[/\((.*?)\)/m, 1] + caption: format_time_zone ) end diff --git a/spec/lib/redmine/i18n_spec.rb b/spec/lib/redmine/i18n_spec.rb index e51c7d5b9f8a..384a08475f36 100644 --- a/spec/lib/redmine/i18n_spec.rb +++ b/spec/lib/redmine/i18n_spec.rb @@ -374,6 +374,48 @@ module OpenProject end end + describe "#format_time_zone" do + current_user { build_stubbed(:user, preferences: { time_zone: user_time_zone }) } + let(:user_time_zone) { "" } + + context "with the current user having set a time zone" do + let(:user_time_zone) { "Kathmandu" } + + it "renders the time zone of the user" do + # No DST in this time zone so the expectation should be constant. + expect(format_time_zone).to eql "GMT+05:45" + end + end + + context "without the current user having a time zone and no default one configured" do + let(:user_time_zone) { "" } + + it "renders the UTC time zone" do + expect(format_time_zone).to eql "GMT+00:00" + end + end + + context "without the current user having a time zone but with a default one configured", + with_settings: { user_default_timezone: "Kathmandu" } do + let(:user_time_zone) { "" } + + it "renders the default time zone" do + # No DST in this time zone so the expectation should be constant. + expect(format_time_zone).to eql "GMT+05:45" + end + end + + context "without the current user having a time zone and also a default one configured", + with_settings: { user_default_timezone: "Atlanta" } do + let(:user_time_zone) { "Kathmandu" } + + it "renders the default time zone" do + # No DST in this time zone so the expectation should be constant. + expect(format_time_zone).to eql "GMT+05:45" + end + end + end + describe "day names" do valid_languages.each do |lang| context "for locale #{lang}" do From 82521317f89c6aa222a02f91daad9b483584084b Mon Sep 17 00:00:00 2001 From: ulferts Date: Fri, 30 Aug 2024 10:27:03 +0200 Subject: [PATCH 03/18] remove width limitation to reliably show selected option --- app/views/admin/settings/users_settings/show.html.erb | 1 - 1 file changed, 1 deletion(-) diff --git a/app/views/admin/settings/users_settings/show.html.erb b/app/views/admin/settings/users_settings/show.html.erb index 0f67f8b37049..b4171880d12e 100644 --- a/app/views/admin/settings/users_settings/show.html.erb +++ b/app/views/admin/settings/users_settings/show.html.erb @@ -48,7 +48,6 @@ See COPYRIGHT and LICENSE files for more details. <%= render Settings::TimeZoneSettingComponent.new( "user_default_timezone", - container_class: "-slim", title: I18n.t("tooltip_user_default_timezone") ) %> From 6298b8762de46e6d7ec707a4b20ac08746608540 Mon Sep 17 00:00:00 2001 From: ulferts Date: Fri, 30 Aug 2024 12:24:31 +0200 Subject: [PATCH 04/18] use UTC time zone as user pref if non is explicitly configured The system did this anyway, but now it becomes explicit --- .../settings/time_zone_setting_component.rb | 2 +- app/models/anonymous_user.rb | 2 +- app/models/user.rb | 2 +- app/models/user_preference.rb | 2 +- app/views/users/_preferences.html.erb | 2 ++ lib_static/redmine/i18n.rb | 9 +++---- spec/lib/redmine/i18n_spec.rb | 24 ++++++++++++------- spec/models/user_preference_spec.rb | 22 +++++++++++++++++ 8 files changed, 47 insertions(+), 18 deletions(-) diff --git a/app/components/settings/time_zone_setting_component.rb b/app/components/settings/time_zone_setting_component.rb index 674ad6a53ce1..772c8e35b012 100644 --- a/app/components/settings/time_zone_setting_component.rb +++ b/app/components/settings/time_zone_setting_component.rb @@ -30,7 +30,7 @@ module Settings ## - # A text field to enter numeric values. + # A select field to select a time zone from. class TimeZoneSettingComponent < ::ApplicationComponent options :form, :title options container_class: "-wide" diff --git a/app/models/anonymous_user.rb b/app/models/anonymous_user.rb index 27e4514d6563..e73178ebddf3 100644 --- a/app/models/anonymous_user.rb +++ b/app/models/anonymous_user.rb @@ -49,7 +49,7 @@ def name(*_args); I18n.t(:label_user_anonymous) end def mail; nil end - def time_zone; nil end + def time_zone; ActiveSupport::TimeZone["Etc/UTC"] end def rss_key; nil end diff --git a/app/models/user.rb b/app/models/user.rb index 23e85c02b3bf..7d6bc21a5f73 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -398,7 +398,7 @@ def log_failed_login end def log_successful_login - update_attribute(:last_login_on, Time.now) + update_attribute(:last_login_on, Time.current) end def pref diff --git a/app/models/user_preference.rb b/app/models/user_preference.rb index a2f49ede552a..769ff84773ff 100644 --- a/app/models/user_preference.rb +++ b/app/models/user_preference.rb @@ -132,7 +132,7 @@ def high_contrast_theme? end def time_zone - super.presence || Setting.user_default_timezone.presence + super.presence || Setting.user_default_timezone.presence || "Etc/UTC" end def daily_reminders diff --git a/app/views/users/_preferences.html.erb b/app/views/users/_preferences.html.erb index 44a513b0527e..45399a774315 100644 --- a/app/views/users/_preferences.html.erb +++ b/app/views/users/_preferences.html.erb @@ -30,8 +30,10 @@ See COPYRIGHT and LICENSE files for more details. <%= render Settings::TimeZoneSettingComponent.new( "time_zone", form: pref_fields, + include_blank: false, container_class: (defined? input_size) ? "-#{input_size}" : "-wide" ) + %>
<%= pref_fields.select :theme, theme_options_for_select, container_class: '-middle' %> diff --git a/lib_static/redmine/i18n.rb b/lib_static/redmine/i18n.rb index 6604ba31fc67..5c107025668c 100644 --- a/lib_static/redmine/i18n.rb +++ b/lib_static/redmine/i18n.rb @@ -138,17 +138,14 @@ def format_time(time, include_date = true) time = time.to_time if time.is_a?(String) zone = User.current.time_zone - local = if zone - time.in_time_zone(zone) - else - (time.utc? ? time.to_time.localtime : time) - end + local = time.in_time_zone(zone) + (include_date ? "#{format_date(local)} " : "") + (Setting.time_format.blank? ? ::I18n.l(local, format: :time) : local.strftime(Setting.time_format)) end def format_time_zone - (User.current.time_zone || Time.zone).to_s[/\((.*?)\)/m, 1] + User.current.time_zone.to_s[/\((.*?)\)/m, 1] end def day_name(day) diff --git a/spec/lib/redmine/i18n_spec.rb b/spec/lib/redmine/i18n_spec.rb index 384a08475f36..e2dabf1727b5 100644 --- a/spec/lib/redmine/i18n_spec.rb +++ b/spec/lib/redmine/i18n_spec.rb @@ -273,7 +273,10 @@ module OpenProject time_format: "%H %M", date_format: "%d %m %Y" } do - let!(:now) { Time.parse("2011-02-20 15:45:22") } + let(:user_time_zone) { "" } + let(:now) { Time.zone.parse("2011-02-20 15:45:22") } + + current_user { build_stubbed(:user, preferences: { time_zone: user_time_zone }) } it "with date and hours" do expect(format_time(now)) @@ -285,14 +288,19 @@ module OpenProject .to eql now.strftime("%H %M") end - it "with a utc to date and hours" do - expect(format_time(now.utc)) - .to eql now.localtime.strftime("%d %m %Y %H %M") - end + context "with another time zone configured for the user" do + # Kathmandu has a +05:45 offset + let(:user_time_zone) { "Kathmandu" } - it "with a utce to only hours" do - expect(format_time(now.utc, false)) - .to eql now.localtime.strftime("%H %M") + it "renders correctly for data and hours" do + expect(format_time(now)) + .to eql "20 02 2011 21 30" + end + + it "renders correctly for only hours" do + expect(format_time(now, false)) + .to eql "21 30" + end end context "with a different format defined", with_settings: { diff --git a/spec/models/user_preference_spec.rb b/spec/models/user_preference_spec.rb index b0cd3fdff46d..ee7c3286a1bb 100644 --- a/spec/models/user_preference_spec.rb +++ b/spec/models/user_preference_spec.rb @@ -222,4 +222,26 @@ expect(subject[:auto_hide_popups]).to eql(value_auto_hide_popups) end end + + describe "#time_zone" do + context "with a time zone set and a default configured", with_settings: { user_default_timezone: "America/Los_Angeles" } do + let(:settings) { { "time_zone" => "Africa/Algiers" } } + + it "returns the time zone set" do + expect(preference.time_zone).to eql "Africa/Algiers" + end + end + + context "with no time zone configured but a default", with_settings: { user_default_timezone: "America/Los_Angeles" } do + it "returns the default time zone" do + expect(preference.time_zone).to eql "America/Los_Angeles" + end + end + + context "with neiter a time zone configured nor a default one", with_settings: { user_default_timezone: "" } do + it "returns UTC" do + expect(preference.time_zone).to eql "Etc/UTC" + end + end + end end From b27f164486e29d0cd7645d0c3d8ea70ed913fd32 Mon Sep 17 00:00:00 2001 From: ulferts Date: Fri, 30 Aug 2024 14:31:06 +0200 Subject: [PATCH 05/18] factor DST into the calculation of the formatted time zone offset --- lib_static/redmine/i18n.rb | 10 ++++++-- .../side_panel/details_component.html.erb | 2 +- .../app/controllers/meetings_controller.rb | 9 +------- .../meeting/app/forms/meeting/start_time.rb | 2 +- spec/lib/redmine/i18n_spec.rb | 23 +++++++++---------- 5 files changed, 22 insertions(+), 24 deletions(-) diff --git a/lib_static/redmine/i18n.rb b/lib_static/redmine/i18n.rb index 5c107025668c..534884af36e1 100644 --- a/lib_static/redmine/i18n.rb +++ b/lib_static/redmine/i18n.rb @@ -144,8 +144,14 @@ def format_time(time, include_date = true) (Setting.time_format.blank? ? ::I18n.l(local, format: :time) : local.strftime(Setting.time_format)) end - def format_time_zone - User.current.time_zone.to_s[/\((.*?)\)/m, 1] + # Returns the offset to UTC (with utc prepended) currently active + # in the current users time zone. DST is factored in so the offset can + # shift over the course of the year + def formatted_time_zone_offset + # Doing User.current.time_zone and format that will not take heed of DST as it has no notion + # of a current time. + # https://github.com/rails/rails/issues/7297 + "UTC#{User.current.time_zone.now.formatted_offset}" end def day_name(day) diff --git a/modules/meeting/app/components/meetings/side_panel/details_component.html.erb b/modules/meeting/app/components/meetings/side_panel/details_component.html.erb index b7425f44e1a1..2431e0f8f0c7 100644 --- a/modules/meeting/app/components/meetings/side_panel/details_component.html.erb +++ b/modules/meeting/app/components/meetings/side_panel/details_component.html.erb @@ -36,7 +36,7 @@ time.with_column(ml: 2) do render(Primer::Beta::Text.new(color: :subtle, font_size: :small)) do - format_time_zone + formatted_time_zone_offset end end end diff --git a/modules/meeting/app/controllers/meetings_controller.rb b/modules/meeting/app/controllers/meetings_controller.rb index f7cadcbf7eba..44fcb157e7e4 100644 --- a/modules/meeting/app/controllers/meetings_controller.rb +++ b/modules/meeting/app/controllers/meetings_controller.rb @@ -290,14 +290,7 @@ def load_meetings(query) end def set_time_zone(&) - zone = User.current.time_zone - if zone.nil? - localzone = Time.current.utc_offset - localzone -= 3600 if Time.current.dst? - zone = ::ActiveSupport::TimeZone[localzone] - end - - Time.use_zone(zone, &) + Time.use_zone(User.current.time_zone, &) end def build_meeting diff --git a/modules/meeting/app/forms/meeting/start_time.rb b/modules/meeting/app/forms/meeting/start_time.rb index 15fbf9e9649b..36b43092839e 100644 --- a/modules/meeting/app/forms/meeting/start_time.rb +++ b/modules/meeting/app/forms/meeting/start_time.rb @@ -38,7 +38,7 @@ class Meeting::StartTime < ApplicationForm label: Meeting.human_attribute_name(:start_time), leading_visual: { icon: :clock }, required: true, - caption: format_time_zone + caption: formatted_time_zone_offset ) end diff --git a/spec/lib/redmine/i18n_spec.rb b/spec/lib/redmine/i18n_spec.rb index e2dabf1727b5..3c4ceaf42561 100644 --- a/spec/lib/redmine/i18n_spec.rb +++ b/spec/lib/redmine/i18n_spec.rb @@ -382,16 +382,17 @@ module OpenProject end end - describe "#format_time_zone" do + describe "#formatted_time_zone_offset" do current_user { build_stubbed(:user, preferences: { time_zone: user_time_zone }) } let(:user_time_zone) { "" } + let(:berlin_gmt) { ActiveSupport::TimeZone["Europe/Berlin"].now.utc_offset == 7200 ? "UTC+02:00" : "UTC+01:00" } + context "with the current user having set a time zone" do - let(:user_time_zone) { "Kathmandu" } + let(:user_time_zone) { "Europe/Berlin" } it "renders the time zone of the user" do - # No DST in this time zone so the expectation should be constant. - expect(format_time_zone).to eql "GMT+05:45" + expect(formatted_time_zone_offset).to eql berlin_gmt end end @@ -399,27 +400,25 @@ module OpenProject let(:user_time_zone) { "" } it "renders the UTC time zone" do - expect(format_time_zone).to eql "GMT+00:00" + expect(formatted_time_zone_offset).to eql "UTC+00:00" end end context "without the current user having a time zone but with a default one configured", - with_settings: { user_default_timezone: "Kathmandu" } do + with_settings: { user_default_timezone: "Europe/Berlin" } do let(:user_time_zone) { "" } it "renders the default time zone" do - # No DST in this time zone so the expectation should be constant. - expect(format_time_zone).to eql "GMT+05:45" + expect(formatted_time_zone_offset).to eql berlin_gmt end end context "without the current user having a time zone and also a default one configured", - with_settings: { user_default_timezone: "Atlanta" } do - let(:user_time_zone) { "Kathmandu" } + with_settings: { user_default_timezone: "America/Atlanta" } do + let(:user_time_zone) { "Europe/Berlin" } it "renders the default time zone" do - # No DST in this time zone so the expectation should be constant. - expect(format_time_zone).to eql "GMT+05:45" + expect(formatted_time_zone_offset).to eql berlin_gmt end end end From 1373a185452517e3ca6382636aa745c068d35277 Mon Sep 17 00:00:00 2001 From: ulferts Date: Fri, 30 Aug 2024 14:49:28 +0200 Subject: [PATCH 06/18] remove local time reference from format_time_as_date With the server now always running in UTC time, using the local time makes no sense any more --- lib_static/redmine/i18n.rb | 6 +----- spec/lib/redmine/i18n_spec.rb | 38 +++++++++++++---------------------- 2 files changed, 15 insertions(+), 29 deletions(-) diff --git a/lib_static/redmine/i18n.rb b/lib_static/redmine/i18n.rb index 534884af36e1..481c9d13527a 100644 --- a/lib_static/redmine/i18n.rb +++ b/lib_static/redmine/i18n.rb @@ -120,11 +120,7 @@ def format_time_as_date(time, format = nil) return nil unless time zone = User.current.time_zone - local_date = (if zone - time.in_time_zone(zone) - else - time.utc? ? time.localtime : time - end).to_date + local_date = time.in_time_zone(zone).to_date if format local_date.strftime(format) diff --git a/spec/lib/redmine/i18n_spec.rb b/spec/lib/redmine/i18n_spec.rb index 3c4ceaf42561..c122a363bf54 100644 --- a/spec/lib/redmine/i18n_spec.rb +++ b/spec/lib/redmine/i18n_spec.rb @@ -36,43 +36,33 @@ module OpenProject let(:user) { build_stubbed(:user) } describe "#format_time_as_date" do + current_user { build_stubbed(:user, preferences: { time_zone: user_time_zone }) } + describe "with user time zone" do - before do - login_as user - allow(user).to receive(:time_zone).and_return(ActiveSupport::TimeZone["Athens"]) - end + let(:user_time_zone) { "Europe/Athens" } it "returns a date in the user timezone for a utc timestamp" do - Time.use_zone("UTC") do - time = Time.zone.local(2013, 6, 30, 23, 59) - expect(format_time_as_date(time, format)).to eq "01/07/2013" - end + time = ActiveSupport::TimeZone["UTC"].local(2013, 6, 30, 23, 59) + expect(format_time_as_date(time, format)).to eq "01/07/2013" end it "returns a date in the user timezone for a non-utc timestamp" do - Time.use_zone("Berlin") do - time = Time.zone.local(2013, 6, 30, 23, 59) - expect(format_time_as_date(time, format)).to eq "01/07/2013" - end + time = ActiveSupport::TimeZone["Berlin"].local(2013, 6, 30, 23, 59) + expect(format_time_as_date(time, format)).to eq "01/07/2013" end end describe "without user time zone" do - before { allow(User.current).to receive(:time_zone).and_return(nil) } + let(:user_time_zone) { "" } - it "returns a date in the local system timezone for a utc timestamp" do - Time.use_zone("UTC") do - time = Time.zone.local(2013, 6, 30, 23, 59) - allow(time).to receive(:localtime).and_return(ActiveSupport::TimeZone["Athens"].local(2013, 7, 1, 1, 59)) - expect(format_time_as_date(time, format)).to eq "01/07/2013" - end + it "returns a date in the utc timezone for a utc timestamp" do + time = ActiveSupport::TimeZone["UTC"].local(2013, 6, 30, 23, 59) + expect(format_time_as_date(time, format)).to eq "30/06/2013" end - it "returns a date in the original timezone for a non-utc timestamp" do - Time.use_zone("Berlin") do - time = Time.zone.local(2013, 6, 30, 23, 59) - expect(format_time_as_date(time, format)).to eq "30/06/2013" - end + it "returns a date in the utc timezone for a non-utc timestamp" do + time = ActiveSupport::TimeZone["Berlin"].local(2013, 6, 30, 23, 59) + expect(format_time_as_date(time, format)).to eq "30/06/2013" end end end From ebc6cdfd645ca88ced3fbe352689a7470a422354 Mon Sep 17 00:00:00 2001 From: ulferts Date: Fri, 30 Aug 2024 15:41:23 +0200 Subject: [PATCH 07/18] remove around_action altering the server`s time zone for this request --- lib_static/redmine/i18n.rb | 4 ++-- .../side_panel/details_form_component.rb | 4 ++-- .../app/controllers/meetings_controller.rb | 5 ----- modules/meeting/app/models/meeting.rb | 7 ++++--- .../meeting/app/views/meetings/_form.html.erb | 4 ++-- .../meeting/app/views/meetings/show.html.erb | 2 +- spec/lib/redmine/i18n_spec.rb | 18 ++++++++++++++---- 7 files changed, 25 insertions(+), 19 deletions(-) diff --git a/lib_static/redmine/i18n.rb b/lib_static/redmine/i18n.rb index 481c9d13527a..8dff1872b47d 100644 --- a/lib_static/redmine/i18n.rb +++ b/lib_static/redmine/i18n.rb @@ -129,7 +129,7 @@ def format_time_as_date(time, format = nil) end end - def format_time(time, include_date = true) + def format_time(time, include_date = true, format: Setting.time_format) return nil unless time time = time.to_time if time.is_a?(String) @@ -137,7 +137,7 @@ def format_time(time, include_date = true) local = time.in_time_zone(zone) (include_date ? "#{format_date(local)} " : "") + - (Setting.time_format.blank? ? ::I18n.l(local, format: :time) : local.strftime(Setting.time_format)) + (format.blank? ? ::I18n.l(local, format: :time) : local.strftime(format)) end # Returns the offset to UTC (with utc prepended) currently active diff --git a/modules/meeting/app/components/meetings/side_panel/details_form_component.rb b/modules/meeting/app/components/meetings/side_panel/details_form_component.rb index e290cdbe3e72..bbd26b4c5cf1 100644 --- a/modules/meeting/app/components/meetings/side_panel/details_form_component.rb +++ b/modules/meeting/app/components/meetings/side_panel/details_form_component.rb @@ -45,11 +45,11 @@ def render? private def start_date_initial_value - @meeting.start_time&.strftime("%Y-%m-%d") + format_time_as_date(@meeting.start_time, "%Y-%m-%d") end def start_time_initial_value - @meeting.start_time&.strftime("%H:%M") + format_time(@meeting.start_time, false, format: "%H:%M") end end end diff --git a/modules/meeting/app/controllers/meetings_controller.rb b/modules/meeting/app/controllers/meetings_controller.rb index 44fcb157e7e4..762142ceeb9c 100644 --- a/modules/meeting/app/controllers/meetings_controller.rb +++ b/modules/meeting/app/controllers/meetings_controller.rb @@ -27,7 +27,6 @@ #++ class MeetingsController < ApplicationController - around_action :set_time_zone before_action :load_and_authorize_in_optional_project, only: %i[index new show create history] before_action :verify_activities_module_activated, only: %i[history] before_action :determine_date_range, only: %i[history] @@ -289,10 +288,6 @@ def load_meetings(query) .paginate(page: page_param, per_page: per_page_param) end - def set_time_zone(&) - Time.use_zone(User.current.time_zone, &) - end - def build_meeting @meeting = Meeting.new @meeting.project = @project diff --git a/modules/meeting/app/models/meeting.rb b/modules/meeting/app/models/meeting.rb index 58b362589eb6..47e0633c671a 100644 --- a/modules/meeting/app/models/meeting.rb +++ b/modules/meeting/app/models/meeting.rb @@ -253,14 +253,15 @@ def allowed_participants def set_initial_values # set defaults - write_attribute(:start_time, Date.tomorrow + 10.hours) if start_time.nil? + # Start date is set to tomorrow at 10 AM (Current users local time) + write_attribute(:start_time, User.current.time_zone.now.at_midnight + 34.hours) if start_time.nil? self.duration ||= 1 update_derived_fields end def update_derived_fields - @start_date = start_time.to_date.iso8601 - @start_time_hour = start_time.strftime("%H:%M") + @start_date = format_time_as_date(start_time, "%Y-%m-%d") + @start_time_hour = format_time(start_time, false, format: "%H:%M") end private diff --git a/modules/meeting/app/views/meetings/_form.html.erb b/modules/meeting/app/views/meetings/_form.html.erb index af48e62e620b..abf981a7d722 100644 --- a/modules/meeting/app/views/meetings/_form.html.erb +++ b/modules/meeting/app/views/meetings/_form.html.erb @@ -160,7 +160,7 @@ See COPYRIGHT and LICENSE files for more details. <%= Meeting.human_attribute_name(:start_time) %> <%= f.text_field :start_time_hour, @@ -169,7 +169,7 @@ See COPYRIGHT and LICENSE files for more details. type: 'time', no_label: true, step: 5.minutes, - suffix: Time.zone.to_s, + suffix: formatted_time_zone_offset, container_class: '-xslim' %>
diff --git a/modules/meeting/app/views/meetings/show.html.erb b/modules/meeting/app/views/meetings/show.html.erb index 15b97a0bd21d..2028a375d135 100644 --- a/modules/meeting/app/views/meetings/show.html.erb +++ b/modules/meeting/app/views/meetings/show.html.erb @@ -101,7 +101,7 @@ See COPYRIGHT and LICENSE files for more details.

<%= Meeting.human_attribute_name(:start_time) %>: <%= format_date @meeting.start_time %> <%= format_time @meeting.start_time, false %> - - <%= format_time @meeting.end_time, false %> <%= Time.zone %>

+ - <%= format_time @meeting.end_time, false %> <%= formatted_time_zone_offset %>

diff --git a/spec/lib/redmine/i18n_spec.rb b/spec/lib/redmine/i18n_spec.rb index c122a363bf54..df49b76b19d4 100644 --- a/spec/lib/redmine/i18n_spec.rb +++ b/spec/lib/redmine/i18n_spec.rb @@ -41,12 +41,12 @@ module OpenProject describe "with user time zone" do let(:user_time_zone) { "Europe/Athens" } - it "returns a date in the user timezone for a utc timestamp" do + it "returns a date string in the user timezone for a utc timestamp" do time = ActiveSupport::TimeZone["UTC"].local(2013, 6, 30, 23, 59) expect(format_time_as_date(time, format)).to eq "01/07/2013" end - it "returns a date in the user timezone for a non-utc timestamp" do + it "returns a date string in the user timezone for a non-utc timestamp" do time = ActiveSupport::TimeZone["Berlin"].local(2013, 6, 30, 23, 59) expect(format_time_as_date(time, format)).to eq "01/07/2013" end @@ -55,12 +55,12 @@ module OpenProject describe "without user time zone" do let(:user_time_zone) { "" } - it "returns a date in the utc timezone for a utc timestamp" do + it "returns a date string in the utc timezone for a utc timestamp" do time = ActiveSupport::TimeZone["UTC"].local(2013, 6, 30, 23, 59) expect(format_time_as_date(time, format)).to eq "30/06/2013" end - it "returns a date in the utc timezone for a non-utc timestamp" do + it "returns a date string in the utc timezone for a non-utc timestamp" do time = ActiveSupport::TimeZone["Berlin"].local(2013, 6, 30, 23, 59) expect(format_time_as_date(time, format)).to eq "30/06/2013" end @@ -278,6 +278,11 @@ module OpenProject .to eql now.strftime("%H %M") end + it "renders correctly for only hours and when providing a custom format" do + expect(format_time(now, false, format: "%H:%M")) + .to eql now.strftime("%H:%M") + end + context "with another time zone configured for the user" do # Kathmandu has a +05:45 offset let(:user_time_zone) { "Kathmandu" } @@ -291,6 +296,11 @@ module OpenProject expect(format_time(now, false)) .to eql "21 30" end + + it "renders correctly for only hours and when providing a custom format" do + expect(format_time(now, false, format: "%H:%M")) + .to eql "21:30" + end end context "with a different format defined", with_settings: { From 8b004a1ee43f48ec906ed0508daf64dafd051622 Mon Sep 17 00:00:00 2001 From: ulferts Date: Fri, 30 Aug 2024 16:18:56 +0200 Subject: [PATCH 08/18] use named parameter --- app/models/exports/concerns/csv.rb | 2 +- app/models/work_package/pdf_export/page.rb | 2 +- lib_static/redmine/i18n.rb | 4 +-- .../lib/open_project/bim/bcf_xml/exporter.rb | 2 +- .../app/components/boards/row_component.rb | 2 +- .../app/components/meetings/row_component.rb | 2 +- .../side_panel/details_component.html.erb | 2 +- .../side_panel/details_form_component.rb | 4 +-- .../forms/meeting_agenda_item/meeting_form.rb | 2 +- .../activities/meeting_activity_provider.rb | 4 +-- .../models/activities/meeting_event_mapper.rb | 4 +-- modules/meeting/app/models/meeting.rb | 4 +-- .../meeting/app/models/meeting/journalized.rb | 2 +- .../icalendar_notification.html.erb | 2 +- .../icalendar_notification.text.erb | 2 +- .../app/views/meeting_mailer/invited.html.erb | 4 +-- .../app/views/meeting_mailer/invited.text.erb | 2 +- .../views/meeting_mailer/rescheduled.html.erb | 8 +++--- .../views/meeting_mailer/rescheduled.text.erb | 4 +-- .../meeting/app/views/meetings/show.html.erb | 4 +-- .../spec/mailers/meeting_mailer_spec.rb | 4 +-- .../concerns/spreadsheet_builder.rb | 2 +- spec/lib/redmine/i18n_spec.rb | 26 +++++++++---------- .../work_package_list_to_pdf_gantt_spec.rb | 2 +- .../work_package_list_to_pdf_spec.rb | 2 +- .../pdf_export/work_package_to_pdf_spec.rb | 2 +- 26 files changed, 50 insertions(+), 50 deletions(-) diff --git a/app/models/exports/concerns/csv.rb b/app/models/exports/concerns/csv.rb index cc1c7b59e542..627421612153 100644 --- a/app/models/exports/concerns/csv.rb +++ b/app/models/exports/concerns/csv.rb @@ -84,7 +84,7 @@ def format_csv(record, attribute) def csv_export_filename sane_filename( "#{Setting.app_title} #{title} \ - #{format_time_as_date(Time.zone.now, '%Y-%m-%d')}.csv" + #{format_time_as_date(Time.zone.now, format: '%Y-%m-%d')}.csv" ) end end diff --git a/app/models/work_package/pdf_export/page.rb b/app/models/work_package/pdf_export/page.rb index ff5f1eea866b..571efcfaf76f 100644 --- a/app/models/work_package/pdf_export/page.rb +++ b/app/models/work_package/pdf_export/page.rb @@ -127,7 +127,7 @@ def footer_page_nr end def footer_date - format_time(Time.zone.now, true) + format_time(Time.zone.now) end def total_page_nr_text diff --git a/lib_static/redmine/i18n.rb b/lib_static/redmine/i18n.rb index 8dff1872b47d..4d2426f4d59a 100644 --- a/lib_static/redmine/i18n.rb +++ b/lib_static/redmine/i18n.rb @@ -116,7 +116,7 @@ def link_regex # Format the time to a date in the user time zone if one is set. # If none is set and the time is in utc time zone (meaning it came from active record), format the date in the system timezone # otherwise just use the date in the time zone attached to the time. - def format_time_as_date(time, format = nil) + def format_time_as_date(time, format: nil) return nil unless time zone = User.current.time_zone @@ -129,7 +129,7 @@ def format_time_as_date(time, format = nil) end end - def format_time(time, include_date = true, format: Setting.time_format) + def format_time(time, include_date: true, format: Setting.time_format) return nil unless time time = time.to_time if time.is_a?(String) diff --git a/modules/bim/lib/open_project/bim/bcf_xml/exporter.rb b/modules/bim/lib/open_project/bim/bcf_xml/exporter.rb index b3dbb206a22d..dcb27d4b8d64 100644 --- a/modules/bim/lib/open_project/bim/bcf_xml/exporter.rb +++ b/modules/bim/lib/open_project/bim/bcf_xml/exporter.rb @@ -43,7 +43,7 @@ def bcf_filename sane_filename( "#{Setting.app_title} #{I18n.t(:label_work_package_plural)} \ - #{format_time_as_date(Time.now, '%Y-%m-%d')}.bcf" + #{format_time_as_date(Time.current, format: '%Y-%m-%d')}.bcf" ) end diff --git a/modules/boards/app/components/boards/row_component.rb b/modules/boards/app/components/boards/row_component.rb index e774c1fa3f38..7323c5140470 100644 --- a/modules/boards/app/components/boards/row_component.rb +++ b/modules/boards/app/components/boards/row_component.rb @@ -39,7 +39,7 @@ def name end def created_at - safe_join([helpers.format_date(model.created_at), helpers.format_time(model.created_at, false)], " ") + safe_join([helpers.format_date(model.created_at), helpers.format_time(model.created_at, include_date: false)], " ") end def type diff --git a/modules/meeting/app/components/meetings/row_component.rb b/modules/meeting/app/components/meetings/row_component.rb index 89b578bed95e..b945f6ddf157 100644 --- a/modules/meeting/app/components/meetings/row_component.rb +++ b/modules/meeting/app/components/meetings/row_component.rb @@ -47,7 +47,7 @@ def type end def start_time - safe_join([helpers.format_date(model.start_time), helpers.format_time(model.start_time, false)], " ") + safe_join([helpers.format_date(model.start_time), helpers.format_time(model.start_time, include_date: false)], " ") end def duration diff --git a/modules/meeting/app/components/meetings/side_panel/details_component.html.erb b/modules/meeting/app/components/meetings/side_panel/details_component.html.erb index 2431e0f8f0c7..1c55629c1ce8 100644 --- a/modules/meeting/app/components/meetings/side_panel/details_component.html.erb +++ b/modules/meeting/app/components/meetings/side_panel/details_component.html.erb @@ -30,7 +30,7 @@ flex_layout(align_items: :center) do |time| time.with_column do render(Primer::Beta::Text.new) do - "#{format_time(@meeting.start_time, false)} - #{format_time(@meeting.end_time, false)}" + "#{format_time(@meeting.start_time, include_date: false)} - #{format_time(@meeting.end_time, include_date:false)}" end end diff --git a/modules/meeting/app/components/meetings/side_panel/details_form_component.rb b/modules/meeting/app/components/meetings/side_panel/details_form_component.rb index bbd26b4c5cf1..fb0bdde00cdb 100644 --- a/modules/meeting/app/components/meetings/side_panel/details_form_component.rb +++ b/modules/meeting/app/components/meetings/side_panel/details_form_component.rb @@ -45,11 +45,11 @@ def render? private def start_date_initial_value - format_time_as_date(@meeting.start_time, "%Y-%m-%d") + format_time_as_date(@meeting.start_time, format: "%Y-%m-%d") end def start_time_initial_value - format_time(@meeting.start_time, false, format: "%H:%M") + format_time(@meeting.start_time, include_date: false, format: "%H:%M") end end end diff --git a/modules/meeting/app/forms/meeting_agenda_item/meeting_form.rb b/modules/meeting/app/forms/meeting_agenda_item/meeting_form.rb index 976f4ca5580c..cd44d1fedb88 100644 --- a/modules/meeting/app/forms/meeting_agenda_item/meeting_form.rb +++ b/modules/meeting/app/forms/meeting_agenda_item/meeting_form.rb @@ -52,7 +52,7 @@ class MeetingAgendaItem::MeetingForm < ApplicationForm label: "#{meeting.project.name}: " \ "#{meeting.title} " \ "#{format_date(meeting.start_time)} " \ - "#{format_time(meeting.start_time, false)}", + "#{format_time(meeting.start_time, include_date: false)}", value: meeting.id ) end diff --git a/modules/meeting/app/models/activities/meeting_activity_provider.rb b/modules/meeting/app/models/activities/meeting_activity_provider.rb index 802eb0739452..01003beee95f 100644 --- a/modules/meeting/app/models/activities/meeting_activity_provider.rb +++ b/modules/meeting/app/models/activities/meeting_activity_provider.rb @@ -109,8 +109,8 @@ def event_title(event) end_time = start_time + event["meeting_duration"].to_f.hours fstart_with = format_date start_time - fstart_without = format_time start_time, false - fend_without = format_time end_time, false + fstart_without = format_time start_time, include_date: false + fend_without = format_time end_time, include_date: false "#{I18n.t(:label_meeting)}: #{event['meeting_title']} (#{fstart_with} #{fstart_without}-#{fend_without})" else diff --git a/modules/meeting/app/models/activities/meeting_event_mapper.rb b/modules/meeting/app/models/activities/meeting_event_mapper.rb index a02cb4c6ad31..281839a5390a 100644 --- a/modules/meeting/app/models/activities/meeting_event_mapper.rb +++ b/modules/meeting/app/models/activities/meeting_event_mapper.rb @@ -204,8 +204,8 @@ def event_title(_journal, data) end_time = start_time + data[:meeting_duration].to_f.hours fstart_with = format_date start_time - fstart_without = format_time start_time, false - fend_without = format_time end_time, false + fstart_without = format_time start_time, include_date: false + fend_without = format_time end_time, include_date: false "#{I18n.t(:label_meeting)}: #{data[:meeting_title]} (#{fstart_with} #{fstart_without}-#{fend_without})" end diff --git a/modules/meeting/app/models/meeting.rb b/modules/meeting/app/models/meeting.rb index 47e0633c671a..27697a97fc57 100644 --- a/modules/meeting/app/models/meeting.rb +++ b/modules/meeting/app/models/meeting.rb @@ -260,8 +260,8 @@ def set_initial_values end def update_derived_fields - @start_date = format_time_as_date(start_time, "%Y-%m-%d") - @start_time_hour = format_time(start_time, false, format: "%H:%M") + @start_date = format_time_as_date(start_time, format: "%Y-%m-%d") + @start_time_hour = format_time(start_time, include_date: false, format: "%H:%M") end private diff --git a/modules/meeting/app/models/meeting/journalized.rb b/modules/meeting/app/models/meeting/journalized.rb index cd49c4211119..b26ffc7b5cb0 100644 --- a/modules/meeting/app/models/meeting/journalized.rb +++ b/modules/meeting/app/models/meeting/journalized.rb @@ -35,7 +35,7 @@ module Meeting::Journalized acts_as_event title: Proc.new { |o| "#{I18n.t(:label_meeting)}: #{o.title} \ #{format_date o.start_time} \ - #{format_time o.start_time, false}-#{format_time o.end_time, false})" + #{format_time o.start_time, include_date: false}-#{format_time o.end_time, include_date: false})" }, url: Proc.new { |o| { controller: "/meetings", action: "show", id: o } }, author: Proc.new(&:user), diff --git a/modules/meeting/app/views/meeting_mailer/icalendar_notification.html.erb b/modules/meeting/app/views/meeting_mailer/icalendar_notification.html.erb index 1aea9386abca..9e194f38d65e 100644 --- a/modules/meeting/app/views/meeting_mailer/icalendar_notification.html.erb +++ b/modules/meeting/app/views/meeting_mailer/icalendar_notification.html.erb @@ -4,7 +4,7 @@

<%= t(:text_notificiation_invited) %>

    -
  • <%=t :label_meeting_date_time %>: <%= format_time_as_date @meeting.start_time %> <%= format_time @meeting.start_time, false %>-<%= format_time @meeting.end_time, false %> <%= @formatted_timezone %>
  • +
  • <%=t :label_meeting_date_time %>: <%= format_time_as_date @meeting.start_time %> <%= format_time @meeting.start_time, include_date: false %>-<%= format_time @meeting.end_time, include_date: false %> <%= @formatted_timezone %>
  • <%=Meeting.human_attribute_name(:location) %>: <%= @meeting.location %>
  • <%=Meeting.human_attribute_name(:participants_invited) %>: <%= @meeting.participants.invited.sort.join("; ") %>
  • <%=Meeting.human_attribute_name(:participants_attended) %>: <%= @meeting.participants.attended.sort.join("; ") %>
  • diff --git a/modules/meeting/app/views/meeting_mailer/icalendar_notification.text.erb b/modules/meeting/app/views/meeting_mailer/icalendar_notification.text.erb index 89b8ed4fef39..6ccc85941ff8 100644 --- a/modules/meeting/app/views/meeting_mailer/icalendar_notification.text.erb +++ b/modules/meeting/app/views/meeting_mailer/icalendar_notification.text.erb @@ -3,7 +3,7 @@ <%= t(:text_notificiation_invited) %> -<%=t :label_meeting_date_time %>: <%= format_time_as_date @meeting.start_time %> <%= format_time @meeting.start_time, false %>-<%= format_time @meeting.end_time, false %> <%= @formatted_timezone %> +<%=t :label_meeting_date_time %>: <%= format_time_as_date @meeting.start_time %> <%= format_time @meeting.start_time, include_date: false %>-<%= format_time @meeting.end_time, include_date: false %> <%= @formatted_timezone %> <%=Meeting.human_attribute_name(:location) %>: <%= @meeting.location %> <%=Meeting.human_attribute_name(:participants_invited) %>: <%= @meeting.participants.invited.sort.join("; ") %> <%=Meeting.human_attribute_name(:participants_attended) %>: <%= @meeting.participants.attended.sort.join("; ") %> diff --git a/modules/meeting/app/views/meeting_mailer/invited.html.erb b/modules/meeting/app/views/meeting_mailer/invited.html.erb index a44634c827eb..2bc6b23b1447 100644 --- a/modules/meeting/app/views/meeting_mailer/invited.html.erb +++ b/modules/meeting/app/views/meeting_mailer/invited.html.erb @@ -45,9 +45,9 @@ See COPYRIGHT and LICENSE files for more details. <%= I18n.t(:label_meeting_date_time) %> - <%= format_time_as_date @meeting.start_time %> <%= format_time @meeting.start_time, false %> + <%= format_time_as_date @meeting.start_time %> <%= format_time @meeting.start_time, include_date: false %> - - <%= format_time @meeting.end_time, false %> <%= Time.zone %> + <%= format_time @meeting.end_time, include_date: false %> <%= Time.zone %> <% if @meeting.location.present? %> diff --git a/modules/meeting/app/views/meeting_mailer/invited.text.erb b/modules/meeting/app/views/meeting_mailer/invited.text.erb index 3c4160486f58..3471877f970e 100644 --- a/modules/meeting/app/views/meeting_mailer/invited.text.erb +++ b/modules/meeting/app/views/meeting_mailer/invited.text.erb @@ -32,7 +32,7 @@ See COPYRIGHT and LICENSE files for more details. <%= @meeting.project.name %>: <%= @meeting.title %> (<%= meeting_url(@meeting) %>) <%= @meeting.author %> -<%=t :label_meeting_date_time %>: <%= format_time_as_date @meeting.start_time %> <%= format_time @meeting.start_time, false %>-<%= format_time @meeting.end_time, false %> <%= Time.zone %> +<%=t :label_meeting_date_time %>: <%= format_time_as_date @meeting.start_time %> <%= format_time @meeting.start_time, include_date: false %>-<%= format_time @meeting.end_time, include_date: false %> <%= Time.zone %> <%= Meeting.human_attribute_name(:location) %>: <%= @meeting.location %> <%= Meeting.human_attribute_name(:participants_invited) %>: <%= @meeting.participants.invited.sort.join("; ") %> <%= Meeting.human_attribute_name(:participants_attended) %>: <%= @meeting.participants.attended.sort.join("; ") %> diff --git a/modules/meeting/app/views/meeting_mailer/rescheduled.html.erb b/modules/meeting/app/views/meeting_mailer/rescheduled.html.erb index 29744c72316d..456e625769c4 100644 --- a/modules/meeting/app/views/meeting_mailer/rescheduled.html.erb +++ b/modules/meeting/app/views/meeting_mailer/rescheduled.html.erb @@ -46,9 +46,9 @@ See COPYRIGHT and LICENSE files for more details. - <%= format_time_as_date @changes[:old_start] %> <%= format_time @changes[:old_start], false %> + <%= format_time_as_date @changes[:old_start] %> <%= format_time @changes[:old_start], include_date: false %> - - <%= format_time (@changes[:old_start] + @changes[:old_duration].hours), false %> <%= Time.zone %> + <%= format_time (@changes[:old_start] + @changes[:old_duration].hours), include_date: false %> <%= formatted_time_zone_offset %> @@ -57,9 +57,9 @@ See COPYRIGHT and LICENSE files for more details. <%= t('meeting.email.rescheduled.new_date_time') %> - <%= format_time_as_date @changes[:new_start] %> <%= format_time @changes[:new_start], false %> + <%= format_time_as_date @changes[:new_start] %> <%= format_time @changes[:new_start], include_date: false %> - - <%= format_time (@changes[:new_start] + @changes[:new_duration].hours), false %> <%= Time.zone %> + <%= format_time (@changes[:new_start] + @changes[:new_duration].hours), include_date: false %> <%= formatted_time_zone_offset %> <% if @meeting.location.present? %> diff --git a/modules/meeting/app/views/meeting_mailer/rescheduled.text.erb b/modules/meeting/app/views/meeting_mailer/rescheduled.text.erb index dc4e8250bee8..911558e4a846 100644 --- a/modules/meeting/app/views/meeting_mailer/rescheduled.text.erb +++ b/modules/meeting/app/views/meeting_mailer/rescheduled.text.erb @@ -35,7 +35,7 @@ See COPYRIGHT and LICENSE files for more details. title: @meeting.title) %> <%= t('meeting.email.rescheduled.old_date_time') %>: -<%= format_time_as_date @changes[:old_start] %> <%= format_time @changes[:old_start], false %> - <%= format_time (@changes[:old_start] + @changes[:old_duration]), false %> <%= Time.zone %> +<%= format_time_as_date @changes[:old_start] %> <%= format_time @changes[:old_start], include_date: false %> - <%= format_time (@changes[:old_start] + @changes[:old_duration]), include_date: false %> <%= Time.zone %> <%= t('meeting.email.rescheduled.new_date_time') %>: -<%= format_time_as_date @changes[:new_start] %> <%= format_time @changes[:new_start], false %> - <%= format_time (@changes[:new_start] + @changes[:new_duration]), false %> <%= Time.zone %> +<%= format_time_as_date @changes[:new_start] %> <%= format_time @changes[:new_start], include_date: false %> - <%= format_time (@changes[:new_start] + @changes[:new_duration]), include_date: false %> <%= Time.zone %> diff --git a/modules/meeting/app/views/meetings/show.html.erb b/modules/meeting/app/views/meetings/show.html.erb index 2028a375d135..ecb297e3b64e 100644 --- a/modules/meeting/app/views/meetings/show.html.erb +++ b/modules/meeting/app/views/meetings/show.html.erb @@ -100,8 +100,8 @@ See COPYRIGHT and LICENSE files for more details.

- <%= Meeting.human_attribute_name(:start_time) %>: <%= format_date @meeting.start_time %> <%= format_time @meeting.start_time, false %> - - <%= format_time @meeting.end_time, false %> <%= formatted_time_zone_offset %>

+ <%= Meeting.human_attribute_name(:start_time) %>: <%= format_date @meeting.start_time %> <%= format_time @meeting.start_time, include_date: false %> + - <%= format_time @meeting.end_time, include_date: false %> <%= formatted_time_zone_offset %>

diff --git a/modules/meeting/spec/mailers/meeting_mailer_spec.rb b/modules/meeting/spec/mailers/meeting_mailer_spec.rb index 8d6eb3d32281..65c741168004 100644 --- a/modules/meeting/spec/mailers/meeting_mailer_spec.rb +++ b/modules/meeting/spec/mailers/meeting_mailer_spec.rb @@ -253,8 +253,8 @@ def check_meeting_mail_content(body) expect(body).to include(meeting.project.name) expect(body).to include(meeting.title) expect(body).to include(i18n.format_date(meeting.start_date)) - expect(body).to include(i18n.format_time(meeting.start_time, false)) - expect(body).to include(i18n.format_time(meeting.end_time, false)) + expect(body).to include(i18n.format_time(meeting.start_time, include_date: false)) + expect(body).to include(i18n.format_time(meeting.end_time, include_date: false)) expect(body).to include(meeting.participants[0].name) expect(body).to include(meeting.participants[1].name) end diff --git a/modules/xls_export/app/models/xls_export/concerns/spreadsheet_builder.rb b/modules/xls_export/app/models/xls_export/concerns/spreadsheet_builder.rb index 60780f76ddb1..1c2cc22ccb07 100644 --- a/modules/xls_export/app/models/xls_export/concerns/spreadsheet_builder.rb +++ b/modules/xls_export/app/models/xls_export/concerns/spreadsheet_builder.rb @@ -79,7 +79,7 @@ def headers def xls_export_filename sane_filename( "#{Setting.app_title} #{spreadsheet_title} \ - #{format_time_as_date(Time.zone.now, '%Y-%m-%d')}.xls" + #{format_time_as_date(Time.zone.now, format: '%Y-%m-%d')}.xls" ) end end diff --git a/spec/lib/redmine/i18n_spec.rb b/spec/lib/redmine/i18n_spec.rb index df49b76b19d4..9512c0234ba7 100644 --- a/spec/lib/redmine/i18n_spec.rb +++ b/spec/lib/redmine/i18n_spec.rb @@ -43,12 +43,12 @@ module OpenProject it "returns a date string in the user timezone for a utc timestamp" do time = ActiveSupport::TimeZone["UTC"].local(2013, 6, 30, 23, 59) - expect(format_time_as_date(time, format)).to eq "01/07/2013" + expect(format_time_as_date(time, format:)).to eq "01/07/2013" end it "returns a date string in the user timezone for a non-utc timestamp" do time = ActiveSupport::TimeZone["Berlin"].local(2013, 6, 30, 23, 59) - expect(format_time_as_date(time, format)).to eq "01/07/2013" + expect(format_time_as_date(time, format:)).to eq "01/07/2013" end end @@ -57,12 +57,12 @@ module OpenProject it "returns a date string in the utc timezone for a utc timestamp" do time = ActiveSupport::TimeZone["UTC"].local(2013, 6, 30, 23, 59) - expect(format_time_as_date(time, format)).to eq "30/06/2013" + expect(format_time_as_date(time, format:)).to eq "30/06/2013" end it "returns a date string in the utc timezone for a non-utc timestamp" do time = ActiveSupport::TimeZone["Berlin"].local(2013, 6, 30, 23, 59) - expect(format_time_as_date(time, format)).to eq "30/06/2013" + expect(format_time_as_date(time, format:)).to eq "30/06/2013" end end end @@ -274,12 +274,12 @@ module OpenProject end it "with only hours" do - expect(format_time(now, false)) + expect(format_time(now, include_date: false)) .to eql now.strftime("%H %M") end it "renders correctly for only hours and when providing a custom format" do - expect(format_time(now, false, format: "%H:%M")) + expect(format_time(now, include_date: false, format: "%H:%M")) .to eql now.strftime("%H:%M") end @@ -293,12 +293,12 @@ module OpenProject end it "renders correctly for only hours" do - expect(format_time(now, false)) + expect(format_time(now, include_date: false)) .to eql "21 30" end it "renders correctly for only hours and when providing a custom format" do - expect(format_time(now, false, format: "%H:%M")) + expect(format_time(now, include_date: false, format: "%H:%M")) .to eql "21:30" end end @@ -313,7 +313,7 @@ module OpenProject end it "renders only hours" do - expect(format_time(now, false)) + expect(format_time(now, include_date: false)) .to eql "15:45" end end @@ -328,7 +328,7 @@ module OpenProject end it "falls back to default for only hours" do - expect(format_time(now, false)) + expect(format_time(now, include_date: false)) .to eql "03:45 PM" end @@ -343,7 +343,7 @@ module OpenProject it "raises no error for only hours" do described_class.with_locale lang do - expect { format_time(now, false) } + expect { format_time(now, include_date: false) } .not_to raise_error end end @@ -361,7 +361,7 @@ module OpenProject end it "falls back to default for only hours" do - expect(format_time(now, false)) + expect(format_time(now, include_date: false)) .to eql "03:45 PM" end end @@ -376,7 +376,7 @@ module OpenProject end it "falls back to default for only hours" do - expect(format_time(now, false)) + expect(format_time(now, include_date: false)) .to eql "15:45" end end diff --git a/spec/models/work_packages/pdf_export/work_package_list_to_pdf_gantt_spec.rb b/spec/models/work_packages/pdf_export/work_package_list_to_pdf_gantt_spec.rb index a3574502c794..054584134b1d 100644 --- a/spec/models/work_packages/pdf_export/work_package_list_to_pdf_gantt_spec.rb +++ b/spec/models/work_packages/pdf_export/work_package_list_to_pdf_gantt_spec.rb @@ -78,7 +78,7 @@ def respond_to?(*) end end let(:export_time) { DateTime.new(2024, 4, 22, 12, 37) } - let(:export_time_formatted) { format_time(export_time, true) } + let(:export_time_formatted) { format_time(export_time, include_date: true) } let(:export) do login_as(user) work_packages diff --git a/spec/models/work_packages/pdf_export/work_package_list_to_pdf_spec.rb b/spec/models/work_packages/pdf_export/work_package_list_to_pdf_spec.rb index 9b8443df9876..6bcce108994b 100644 --- a/spec/models/work_packages/pdf_export/work_package_list_to_pdf_spec.rb +++ b/spec/models/work_packages/pdf_export/work_package_list_to_pdf_spec.rb @@ -62,7 +62,7 @@ member_with_permissions: { project => %w[view_work_packages export_work_packages] }) end let(:export_time) { DateTime.new(2023, 6, 30, 23, 59) } - let(:export_time_formatted) { format_time(export_time, true) } + let(:export_time_formatted) { format_time(export_time, include_date: true) } let(:work_package_parent) do create(:work_package, project:, diff --git a/spec/models/work_packages/pdf_export/work_package_to_pdf_spec.rb b/spec/models/work_packages/pdf_export/work_package_to_pdf_spec.rb index d73b936f1282..34dc9cf41558 100644 --- a/spec/models/work_packages/pdf_export/work_package_to_pdf_spec.rb +++ b/spec/models/work_packages/pdf_export/work_package_to_pdf_spec.rb @@ -91,7 +91,7 @@ let(:category) { create(:category, project:, name: "Demo") } let(:version) { create(:version, project:) } let(:export_time) { DateTime.new(2023, 6, 30, 23, 59) } - let(:export_time_formatted) { format_time(export_time, true) } + let(:export_time_formatted) { format_time(export_time, include_date: true) } let(:image_path) { Rails.root.join("spec/fixtures/files/image.png") } let(:priority) { create(:priority_normal) } let(:image_attachment) { Attachment.new author: user, file: File.open(image_path) } From 35ca717a989a826bca95e2e638d097a3a3ca299c Mon Sep 17 00:00:00 2001 From: ulferts Date: Fri, 30 Aug 2024 16:29:27 +0200 Subject: [PATCH 09/18] include fallback to user_default_timezone and utc into all system users --- app/models/anonymous_user.rb | 2 +- app/models/deleted_user.rb | 2 +- app/models/system_user.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/models/anonymous_user.rb b/app/models/anonymous_user.rb index e73178ebddf3..e3627be53b54 100644 --- a/app/models/anonymous_user.rb +++ b/app/models/anonymous_user.rb @@ -49,7 +49,7 @@ def name(*_args); I18n.t(:label_user_anonymous) end def mail; nil end - def time_zone; ActiveSupport::TimeZone["Etc/UTC"] end + def time_zone; ActiveSupport::TimeZone[Setting.user_default_timezone.presence || "Etc/UTC"] end def rss_key; nil end diff --git a/app/models/deleted_user.rb b/app/models/deleted_user.rb index ae65cfb4af70..b59a6f122cb3 100644 --- a/app/models/deleted_user.rb +++ b/app/models/deleted_user.rb @@ -17,7 +17,7 @@ def builtin? = true def admin = false def name(*_args) = I18n.t("user.deleted") def mail = nil - def time_zone = nil + def time_zone; ActiveSupport::TimeZone[Setting.user_default_timezone.presence || "Etc/UTC"] end def rss_key = nil def destroy = false end diff --git a/app/models/system_user.rb b/app/models/system_user.rb index 1a3195dacc4b..4250e8fdb235 100644 --- a/app/models/system_user.rb +++ b/app/models/system_user.rb @@ -47,7 +47,7 @@ def name(*_args); "System" end def mail; nil end - def time_zone; nil end + def time_zone; ActiveSupport::TimeZone[Setting.user_default_timezone.presence || "Etc/UTC"] end def rss_key; nil end From 48377fc73e5974add9dc3336fed3bb0ef0e47063 Mon Sep 17 00:00:00 2001 From: ulferts Date: Fri, 30 Aug 2024 17:28:40 +0200 Subject: [PATCH 10/18] fix showing a message in case no own time zone is configured --- app/models/user.rb | 2 +- app/models/user_preference.rb | 4 ++++ .../app/controllers/meetings_controller.rb | 4 ++-- spec/models/user_preference_spec.rb | 22 +++++++++++++++++++ 4 files changed, 29 insertions(+), 3 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index 7d6bc21a5f73..96f11b04dd3a 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -688,6 +688,6 @@ def log_failed_login_timestamp end def self.default_admin_account_changed? - !User.active.find_by_login("admin").try(:current_password).try(:matches_plaintext?, "admin") # rubocop:disable Rails/DynamicFindBy + !User.active.find_by_login("admin").try(:current_password).try(:matches_plaintext?, "admin") end end diff --git a/app/models/user_preference.rb b/app/models/user_preference.rb index 769ff84773ff..220768c6a2df 100644 --- a/app/models/user_preference.rb +++ b/app/models/user_preference.rb @@ -135,6 +135,10 @@ def time_zone super.presence || Setting.user_default_timezone.presence || "Etc/UTC" end + def time_zone? + settings["time_zone"].present? + end + def daily_reminders super.presence || { enabled: true, times: ["08:00:00+00:00"] }.with_indifferent_access end diff --git a/modules/meeting/app/controllers/meetings_controller.rb b/modules/meeting/app/controllers/meetings_controller.rb index 762142ceeb9c..8b4885b68e8a 100644 --- a/modules/meeting/app/controllers/meetings_controller.rb +++ b/modules/meeting/app/controllers/meetings_controller.rb @@ -96,8 +96,8 @@ def create # rubocop:disable Metrics/AbcSize if call.success? text = I18n.t(:notice_successful_create) - if User.current.time_zone.nil? - link = I18n.t(:notice_timezone_missing, zone: Time.zone) + unless User.current.pref.time_zone? + link = I18n.t(:notice_timezone_missing, zone: formatted_time_zone_offset) text += " #{view_context.link_to(link, { controller: '/my', action: :settings, anchor: 'pref_time_zone' }, class: 'link_to_profile')}" end diff --git a/spec/models/user_preference_spec.rb b/spec/models/user_preference_spec.rb index ee7c3286a1bb..b482481ae070 100644 --- a/spec/models/user_preference_spec.rb +++ b/spec/models/user_preference_spec.rb @@ -244,4 +244,26 @@ end end end + + describe "#time_zone?" do + context "with a time zone set and a default configured", with_settings: { user_default_timezone: "America/Los_Angeles" } do + let(:settings) { { "time_zone" => "Africa/Algiers" } } + + it "is true" do + expect(preference).to be_time_zone + end + end + + context "with no time zone configured but a default", with_settings: { user_default_timezone: "America/Los_Angeles" } do + it "is false" do + expect(preference).not_to be_time_zone + end + end + + context "with neiter a time zone configured nor a default one", with_settings: { user_default_timezone: "" } do + it "is false" do + expect(preference).not_to be_time_zone + end + end + end end From 1d7fd3c2ec238e9b03072ef4673aea7435ca309f Mon Sep 17 00:00:00 2001 From: ulferts Date: Mon, 2 Sep 2024 18:11:41 +0200 Subject: [PATCH 11/18] nil memoized tz variable on reload --- app/models/user.rb | 6 ++++ modules/meeting/app/mailers/meeting_mailer.rb | 7 ---- .../icalendar_notification.html.erb | 2 +- .../icalendar_notification.text.erb | 2 +- .../app/views/meeting_mailer/invited.html.erb | 2 +- .../app/views/meeting_mailer/invited.text.erb | 2 +- .../views/meeting_mailer/rescheduled.html.erb | 4 +-- .../views/meeting_mailer/rescheduled.text.erb | 4 +-- .../spec/mailers/meeting_mailer_spec.rb | 35 ++++++++++--------- ...r_preference_representer_rendering_spec.rb | 4 +-- spec/models/deleted_user_spec.rb | 2 +- spec/models/users/default_timezone_spec.rb | 4 +-- 12 files changed, 37 insertions(+), 37 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index 96f11b04dd3a..c88ad5908233 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -409,6 +409,12 @@ def time_zone @time_zone ||= (pref.time_zone.blank? ? nil : ActiveSupport::TimeZone[pref.time_zone]) end + def reload(*) + @time_zone = nil + + super + end + def wants_comments_in_reverse_order? pref.comments_in_reverse_order? end diff --git a/modules/meeting/app/mailers/meeting_mailer.rb b/modules/meeting/app/mailers/meeting_mailer.rb index 817962f41c3d..fa026b3eb582 100644 --- a/modules/meeting/app/mailers/meeting_mailer.rb +++ b/modules/meeting/app/mailers/meeting_mailer.rb @@ -63,8 +63,6 @@ def icalendar_notification(meeting, user, _actor, **) set_headers @meeting with_attached_ics(meeting, user) do - timezone = Time.zone || Time.zone_default - @formatted_timezone = format_timezone_offset timezone, @meeting.start_time subject = "[#{@meeting.project.name}] #{@meeting.title}" mail(to: user, subject:) end @@ -95,9 +93,4 @@ def set_headers(meeting) headers["Content-Type"] = 'text/calendar; charset=utf-8; method="PUBLISH"; name="meeting.ics"' headers["Content-Transfer-Encoding"] = "8bit" end - - def format_timezone_offset(timezone, time) - offset = ::ActiveSupport::TimeZone.seconds_to_utc_offset time.utc_offest_for_timezone(timezone), true - "(GMT#{offset}) #{timezone.name}" - end end diff --git a/modules/meeting/app/views/meeting_mailer/icalendar_notification.html.erb b/modules/meeting/app/views/meeting_mailer/icalendar_notification.html.erb index 9e194f38d65e..4c2129253fb3 100644 --- a/modules/meeting/app/views/meeting_mailer/icalendar_notification.html.erb +++ b/modules/meeting/app/views/meeting_mailer/icalendar_notification.html.erb @@ -4,7 +4,7 @@

<%= t(:text_notificiation_invited) %>

    -
  • <%=t :label_meeting_date_time %>: <%= format_time_as_date @meeting.start_time %> <%= format_time @meeting.start_time, include_date: false %>-<%= format_time @meeting.end_time, include_date: false %> <%= @formatted_timezone %>
  • +
  • <%=t :label_meeting_date_time %>: <%= format_time_as_date @meeting.start_time %> <%= format_time @meeting.start_time, include_date: false %>-<%= format_time @meeting.end_time, include_date: false %> (<%= formatted_time_zone_offset %>)
  • <%=Meeting.human_attribute_name(:location) %>: <%= @meeting.location %>
  • <%=Meeting.human_attribute_name(:participants_invited) %>: <%= @meeting.participants.invited.sort.join("; ") %>
  • <%=Meeting.human_attribute_name(:participants_attended) %>: <%= @meeting.participants.attended.sort.join("; ") %>
  • diff --git a/modules/meeting/app/views/meeting_mailer/icalendar_notification.text.erb b/modules/meeting/app/views/meeting_mailer/icalendar_notification.text.erb index 6ccc85941ff8..077cc974d317 100644 --- a/modules/meeting/app/views/meeting_mailer/icalendar_notification.text.erb +++ b/modules/meeting/app/views/meeting_mailer/icalendar_notification.text.erb @@ -3,7 +3,7 @@ <%= t(:text_notificiation_invited) %> -<%=t :label_meeting_date_time %>: <%= format_time_as_date @meeting.start_time %> <%= format_time @meeting.start_time, include_date: false %>-<%= format_time @meeting.end_time, include_date: false %> <%= @formatted_timezone %> +<%=t :label_meeting_date_time %>: <%= format_time_as_date @meeting.start_time %> <%= format_time @meeting.start_time, include_date: false %>-<%= format_time @meeting.end_time, include_date: false %> (<%= formatted_time_zone_offset %>) <%=Meeting.human_attribute_name(:location) %>: <%= @meeting.location %> <%=Meeting.human_attribute_name(:participants_invited) %>: <%= @meeting.participants.invited.sort.join("; ") %> <%=Meeting.human_attribute_name(:participants_attended) %>: <%= @meeting.participants.attended.sort.join("; ") %> diff --git a/modules/meeting/app/views/meeting_mailer/invited.html.erb b/modules/meeting/app/views/meeting_mailer/invited.html.erb index 2bc6b23b1447..499075a3b736 100644 --- a/modules/meeting/app/views/meeting_mailer/invited.html.erb +++ b/modules/meeting/app/views/meeting_mailer/invited.html.erb @@ -47,7 +47,7 @@ See COPYRIGHT and LICENSE files for more details. <%= format_time_as_date @meeting.start_time %> <%= format_time @meeting.start_time, include_date: false %> - - <%= format_time @meeting.end_time, include_date: false %> <%= Time.zone %> + <%= format_time @meeting.end_time, include_date: false %> (<%= formatted_time_zone_offset %>) <% if @meeting.location.present? %> diff --git a/modules/meeting/app/views/meeting_mailer/invited.text.erb b/modules/meeting/app/views/meeting_mailer/invited.text.erb index 3471877f970e..36f7ce7116a3 100644 --- a/modules/meeting/app/views/meeting_mailer/invited.text.erb +++ b/modules/meeting/app/views/meeting_mailer/invited.text.erb @@ -32,7 +32,7 @@ See COPYRIGHT and LICENSE files for more details. <%= @meeting.project.name %>: <%= @meeting.title %> (<%= meeting_url(@meeting) %>) <%= @meeting.author %> -<%=t :label_meeting_date_time %>: <%= format_time_as_date @meeting.start_time %> <%= format_time @meeting.start_time, include_date: false %>-<%= format_time @meeting.end_time, include_date: false %> <%= Time.zone %> +<%=t :label_meeting_date_time %>: <%= format_time_as_date @meeting.start_time %> <%= format_time @meeting.start_time, include_date: false %>-<%= format_time @meeting.end_time, include_date: false %> (<%= formatted_time_zone_offset %>) <%= Meeting.human_attribute_name(:location) %>: <%= @meeting.location %> <%= Meeting.human_attribute_name(:participants_invited) %>: <%= @meeting.participants.invited.sort.join("; ") %> <%= Meeting.human_attribute_name(:participants_attended) %>: <%= @meeting.participants.attended.sort.join("; ") %> diff --git a/modules/meeting/app/views/meeting_mailer/rescheduled.html.erb b/modules/meeting/app/views/meeting_mailer/rescheduled.html.erb index 456e625769c4..b0cd462ebbb5 100644 --- a/modules/meeting/app/views/meeting_mailer/rescheduled.html.erb +++ b/modules/meeting/app/views/meeting_mailer/rescheduled.html.erb @@ -48,7 +48,7 @@ See COPYRIGHT and LICENSE files for more details. <%= format_time_as_date @changes[:old_start] %> <%= format_time @changes[:old_start], include_date: false %> - - <%= format_time (@changes[:old_start] + @changes[:old_duration].hours), include_date: false %> <%= formatted_time_zone_offset %> + <%= format_time (@changes[:old_start] + @changes[:old_duration].hours), include_date: false %> (<%= formatted_time_zone_offset %>) @@ -59,7 +59,7 @@ See COPYRIGHT and LICENSE files for more details. <%= format_time_as_date @changes[:new_start] %> <%= format_time @changes[:new_start], include_date: false %> - - <%= format_time (@changes[:new_start] + @changes[:new_duration].hours), include_date: false %> <%= formatted_time_zone_offset %> + <%= format_time (@changes[:new_start] + @changes[:new_duration].hours), include_date: false %> (<%= formatted_time_zone_offset %>) <% if @meeting.location.present? %> diff --git a/modules/meeting/app/views/meeting_mailer/rescheduled.text.erb b/modules/meeting/app/views/meeting_mailer/rescheduled.text.erb index 911558e4a846..788101237038 100644 --- a/modules/meeting/app/views/meeting_mailer/rescheduled.text.erb +++ b/modules/meeting/app/views/meeting_mailer/rescheduled.text.erb @@ -35,7 +35,7 @@ See COPYRIGHT and LICENSE files for more details. title: @meeting.title) %> <%= t('meeting.email.rescheduled.old_date_time') %>: -<%= format_time_as_date @changes[:old_start] %> <%= format_time @changes[:old_start], include_date: false %> - <%= format_time (@changes[:old_start] + @changes[:old_duration]), include_date: false %> <%= Time.zone %> +<%= format_time_as_date @changes[:old_start] %> <%= format_time @changes[:old_start], include_date: false %> - <%= format_time (@changes[:old_start] + @changes[:old_duration]), include_date: false %> (<%= formatted_time_zone_offset %>} <%= t('meeting.email.rescheduled.new_date_time') %>: -<%= format_time_as_date @changes[:new_start] %> <%= format_time @changes[:new_start], include_date: false %> - <%= format_time (@changes[:new_start] + @changes[:new_duration]), include_date: false %> <%= Time.zone %> +<%= format_time_as_date @changes[:new_start] %> <%= format_time @changes[:new_start], include_date: false %> - <%= format_time (@changes[:new_start] + @changes[:new_duration]), include_date: false %> (<%= formatted_time_zone_offset %>} diff --git a/modules/meeting/spec/mailers/meeting_mailer_spec.rb b/modules/meeting/spec/mailers/meeting_mailer_spec.rb index 65c741168004..b5fc84a3747a 100644 --- a/modules/meeting/spec/mailers/meeting_mailer_spec.rb +++ b/modules/meeting/spec/mailers/meeting_mailer_spec.rb @@ -47,6 +47,8 @@ let(:meeting_agenda) do create(:meeting_agenda, meeting:) end + let(:tokyo_offset) { "UTC#{ActiveSupport::TimeZone['Asia/Tokyo'].now.formatted_offset}" } + let(:berlin_offset) { "UTC#{ActiveSupport::TimeZone['Europe/Berlin'].now.formatted_offset}" } before do User.current = author @@ -88,11 +90,9 @@ context "with a recipient with another time zone" do let!(:preference) { watcher1.pref.update(time_zone: "Asia/Tokyo") } - it "renders the mail with the correcet locale" do - expect(mail.text_part.body).to include("Tokyo") - expect(mail.text_part.body).to include("GMT+09:00") - expect(mail.html_part.body).to include("Tokyo") - expect(mail.html_part.body).to include("GMT+09:00") + it "renders the mail with the correct locale" do + expect(mail.text_part.body).to include(tokyo_offset) + expect(mail.html_part.body).to include(tokyo_offset) expect(mail.to).to contain_exactly(watcher1.mail) end @@ -111,8 +111,8 @@ it "renders the mail with the correct locale" do expect(mail.html_part.body).to include("11/09/2021 11:00 PM") - expect(mail.html_part.body).to include("12:00 AM (GMT+01:00) Europe/Berlin") - expect(mail.text_part.body).to include("11/09/2021 11:00 PM-12:00 AM (GMT+01:00) Europe/Berlin") + expect(mail.html_part.body).to include("12:00 AM (#{berlin_offset})") + expect(mail.text_part.body).to include("11/09/2021 11:00 PM-12:00 AM (#{berlin_offset})") expect(mail.to).to contain_exactly(author.mail) end @@ -124,9 +124,9 @@ it "renders the mail with the correct locale" do expect(mail.html_part.body).to include("11/10/2021 07:00 AM") - expect(mail.html_part.body).to include("08:00 AM (GMT+09:00) Asia/Tokyo") + expect(mail.html_part.body).to include("08:00 AM (#{tokyo_offset})") - expect(mail.text_part.body).to include("11/10/2021 07:00 AM-08:00 AM (GMT+09:00) Asia/Tokyo") + expect(mail.text_part.body).to include("11/10/2021 07:00 AM-08:00 AM (#{tokyo_offset})") expect(mail.to).to contain_exactly(watcher1.mail) end @@ -160,7 +160,7 @@ expect(body).to include(meeting.project.name) expect(body).to include(meeting.title) expect(body).to include(meeting.location) - expect(body).to include("01/19/2021 11:00 AM-12:00 PM (GMT+01:00) Europe/Berlin") + expect(body).to include("01/19/2021 11:00 AM-12:00 PM (#{berlin_offset})") expect(body).to include(meeting.participants[0].name) expect(body).to include(meeting.participants[1].name) end @@ -174,7 +174,7 @@ expect(body).to include(meeting.title) expect(body).to include(meeting.location) expect(body).to include("01/19/2021 11:00 AM") - expect(body).to include("12:00 PM (GMT+01:00) Europe/Berlin") + expect(body).to include("12:00 PM (#{berlin_offset})") expect(body).to include(meeting.participants[0].name) expect(body).to include(meeting.participants[1].name) end @@ -207,9 +207,9 @@ let(:mail) { described_class.icalendar_notification(meeting, watcher1, author) } it "renders the mail with the correct locale" do - expect(mail.text_part.body).to include("01/19/2021 07:00 PM-08:00 PM (GMT+09:00) Asia/Tokyo") + expect(mail.text_part.body).to include("01/19/2021 07:00 PM-08:00 PM (#{tokyo_offset})") expect(mail.html_part.body).to include("01/19/2021 07:00 PM") - expect(mail.html_part.body).to include("08:00 PM (GMT+09:00) Asia/Tokyo") + expect(mail.html_part.body).to include("08:00 PM (#{tokyo_offset})") expect(mail.to).to contain_exactly(watcher1.mail) end @@ -227,9 +227,9 @@ let(:mail) { described_class.icalendar_notification(meeting, author, author) } it "renders the mail with the correct locale" do - expect(mail.text_part.body).to include("11/09/2021 11:00 PM-12:00 AM (GMT+01:00) Europe/Berlin") + expect(mail.text_part.body).to include("11/09/2021 11:00 PM-12:00 AM (#{berlin_offset})") expect(mail.html_part.body).to include("11/09/2021 11:00 PM") - expect(mail.html_part.body).to include("12:00 AM (GMT+01:00) Europe/Berlin") + expect(mail.html_part.body).to include("12:00 AM (#{berlin_offset})") expect(mail.to).to contain_exactly(author.mail) end @@ -240,8 +240,8 @@ let!(:preference) { watcher1.pref.update(time_zone: "Asia/Tokyo") } it "renders the mail with the correct locale" do - expect(mail.text_part.body).to include("11/10/2021 07:00 AM-08:00 AM (GMT+09:00) Asia/Tokyo") - expect(mail.html_part.body).to include("11/10/2021 07:00 AM-08:00 AM (GMT+09:00) Asia/Tokyo") + expect(mail.text_part.body).to include("11/10/2021 07:00 AM-08:00 AM (#{tokyo_offset})") + expect(mail.html_part.body).to include("11/10/2021 07:00 AM-08:00 AM (#{tokyo_offset})") expect(mail.to).to contain_exactly(watcher1.mail) end @@ -255,6 +255,7 @@ def check_meeting_mail_content(body) expect(body).to include(i18n.format_date(meeting.start_date)) expect(body).to include(i18n.format_time(meeting.start_time, include_date: false)) expect(body).to include(i18n.format_time(meeting.end_time, include_date: false)) + expect(body).to include(i18n.formatted_time_zone_offset) expect(body).to include(meeting.participants[0].name) expect(body).to include(meeting.participants[1].name) end diff --git a/spec/lib/api/v3/user_preferences/user_preference_representer_rendering_spec.rb b/spec/lib/api/v3/user_preferences/user_preference_representer_rendering_spec.rb index 8aa1e05291ea..0c23e3199c3e 100644 --- a/spec/lib/api/v3/user_preferences/user_preference_representer_rendering_spec.rb +++ b/spec/lib/api/v3/user_preferences/user_preference_representer_rendering_spec.rb @@ -62,8 +62,8 @@ context "without a timezone set" do let(:preference) { build(:user_preference, time_zone: "") } - it "shows the timeZone as nil" do - expect(subject).to be_json_eql(nil.to_json).at_path("timeZone") + it "shows the timeZone as utc" do + expect(subject).to be_json_eql("Etc/UTC".to_json).at_path("timeZone") end end diff --git a/spec/models/deleted_user_spec.rb b/spec/models/deleted_user_spec.rb index 03193804152d..5b477505d99d 100644 --- a/spec/models/deleted_user_spec.rb +++ b/spec/models/deleted_user_spec.rb @@ -48,7 +48,7 @@ end describe "#time_zone" do - it { expect(user.time_zone).to be_nil } + it { expect(user.time_zone).to eql ActiveSupport::TimeZone["Etc/UTC"] } end describe "#rss_key" do diff --git a/spec/models/users/default_timezone_spec.rb b/spec/models/users/default_timezone_spec.rb index e0a83088ce78..f0f2b6e5cf61 100644 --- a/spec/models/users/default_timezone_spec.rb +++ b/spec/models/users/default_timezone_spec.rb @@ -32,8 +32,8 @@ let(:user) { create(:user) } context "with no system default set" do - it "is not set" do - expect(user.pref.time_zone).to be_nil + it "is still set to Etc/UTC as that will be calculated with internally" do + expect(user.pref.time_zone).to eq "Etc/UTC" end end From a5714f7c4807573587315a3f2242c59d5c640fd0 Mon Sep 17 00:00:00 2001 From: ulferts Date: Fri, 6 Sep 2024 09:29:25 +0200 Subject: [PATCH 12/18] adapt specs to use actual UTC time zone --- modules/meeting/spec/features/meetings_copy_spec.rb | 4 ++-- modules/meeting/spec/features/meetings_new_spec.rb | 2 +- .../structured_meetings/mobile_structure_meeting_spec.rb | 2 +- .../structured_meetings/structured_meeting_crud_spec.rb | 2 +- .../structured_meeting_participant_spec.rb | 2 +- .../spec/features/structured_meetings/turbo_links_spec.rb | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/meeting/spec/features/meetings_copy_spec.rb b/modules/meeting/spec/features/meetings_copy_spec.rb index 9218095fda73..3a81be1d576a 100644 --- a/modules/meeting/spec/features/meetings_copy_spec.rb +++ b/modules/meeting/spec/features/meetings_copy_spec.rb @@ -34,7 +34,7 @@ shared_let(:user) do create(:user, member_with_permissions: { project => permissions }).tap do |u| - u.pref[:time_zone] = "UTC" + u.pref[:time_zone] = "Etc/UTC" u.save! end @@ -66,7 +66,7 @@ start_of_meeting = start_time.strftime(twelve_hour_format) end_of_meeting = (start_time + meeting.duration.hours).strftime(twelve_hour_format) - "Start time: #{date} #{start_of_meeting} - #{end_of_meeting} (GMT+00:00) UTC" + "Start time: #{date} #{start_of_meeting} - #{end_of_meeting} UTC+00:00" end before do diff --git a/modules/meeting/spec/features/meetings_new_spec.rb b/modules/meeting/spec/features/meetings_new_spec.rb index 29dcf6148c42..fa7bc94c48fb 100644 --- a/modules/meeting/spec/features/meetings_new_spec.rb +++ b/modules/meeting/spec/features/meetings_new_spec.rb @@ -33,7 +33,7 @@ RSpec.describe "Meetings new", :js, with_cuprite: false do shared_let(:project) { create(:project, enabled_module_names: %w[meetings]) } shared_let(:admin) { create(:admin) } - let(:time_zone) { "utc" } + let(:time_zone) { "Etc/UTC" } let(:user) do create(:user, lastname: "First", diff --git a/modules/meeting/spec/features/structured_meetings/mobile_structure_meeting_spec.rb b/modules/meeting/spec/features/structured_meetings/mobile_structure_meeting_spec.rb index 249744c00efb..eefba3dd0817 100644 --- a/modules/meeting/spec/features/structured_meetings/mobile_structure_meeting_spec.rb +++ b/modules/meeting/spec/features/structured_meetings/mobile_structure_meeting_spec.rb @@ -41,7 +41,7 @@ lastname: "First", member_with_permissions: { project => %i[view_meetings create_meetings edit_meetings delete_meetings manage_agendas close_meeting_agendas view_work_packages] }).tap do |u| - u.pref[:time_zone] = "utc" + u.pref[:time_zone] = "Etc/UTC" u.save! end diff --git a/modules/meeting/spec/features/structured_meetings/structured_meeting_crud_spec.rb b/modules/meeting/spec/features/structured_meetings/structured_meeting_crud_spec.rb index a2800ec121ba..c8f92c517370 100644 --- a/modules/meeting/spec/features/structured_meetings/structured_meeting_crud_spec.rb +++ b/modules/meeting/spec/features/structured_meetings/structured_meeting_crud_spec.rb @@ -42,7 +42,7 @@ lastname: "First", member_with_permissions: { project => %i[view_meetings create_meetings edit_meetings delete_meetings manage_agendas view_work_packages] }).tap do |u| - u.pref[:time_zone] = "utc" + u.pref[:time_zone] = "Etc/UTC" u.save! end diff --git a/modules/meeting/spec/features/structured_meetings/structured_meeting_participant_spec.rb b/modules/meeting/spec/features/structured_meetings/structured_meeting_participant_spec.rb index f2a2816d897e..e89f4494ef90 100644 --- a/modules/meeting/spec/features/structured_meetings/structured_meeting_participant_spec.rb +++ b/modules/meeting/spec/features/structured_meetings/structured_meeting_participant_spec.rb @@ -41,7 +41,7 @@ lastname: "First", member_with_permissions: { project => %i[view_meetings create_meetings edit_meetings delete_meetings manage_agendas close_meeting_agendas view_work_packages] }).tap do |u| - u.pref[:time_zone] = "utc" + u.pref[:time_zone] = "Etc/UTC" u.save! end diff --git a/modules/meeting/spec/features/structured_meetings/turbo_links_spec.rb b/modules/meeting/spec/features/structured_meetings/turbo_links_spec.rb index 1cb1af730276..7eb01c1d3c9f 100644 --- a/modules/meeting/spec/features/structured_meetings/turbo_links_spec.rb +++ b/modules/meeting/spec/features/structured_meetings/turbo_links_spec.rb @@ -42,7 +42,7 @@ lastname: "First", member_with_permissions: { project => %i[view_meetings create_meetings edit_meetings delete_meetings manage_agendas view_work_packages] }).tap do |u| - u.pref[:time_zone] = "utc" + u.pref[:time_zone] = "Etc/UTC" u.save! end From 4c61ce1bfccf92a3bcbe9010966b7cdc750bde69 Mon Sep 17 00:00:00 2001 From: ulferts Date: Fri, 6 Sep 2024 10:14:40 +0200 Subject: [PATCH 13/18] increase time_zone robustness --- app/models/user.rb | 2 +- spec/models/user_spec.rb | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/app/models/user.rb b/app/models/user.rb index c88ad5908233..4563f1086b92 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -406,7 +406,7 @@ def pref end def time_zone - @time_zone ||= (pref.time_zone.blank? ? nil : ActiveSupport::TimeZone[pref.time_zone]) + @time_zone ||= ActiveSupport::TimeZone[pref.time_zone] || ActiveSupport::TimeZone["Etc/UTC"] end def reload(*) diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 2cb717f4e12c..66d7db19d217 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -823,6 +823,39 @@ def build_user_double_with_expired_password(is_expired) end end + describe "#time_zone" do + let(:user) { build(:user, preferences:) } + + context "with an existing time zone in the prefs" do + let(:preferences) { { "time_zone" => "Europe/Athens" } } + + it "returns the matching ActiveSupport::TimeZone" do + expect(user.time_zone) + .to eql ActiveSupport::TimeZone["Europe/Athens"] + end + end + + context "with an invalid time zone" do + # Would need to be Etc/UTC or UTC to be valid + let(:preferences) { { "time_zone" => "utc" } } + + it "returns the utc ActiveSupport::TimeZone" do + expect(user.time_zone) + .to eql ActiveSupport::TimeZone["Etc/UTC"] + end + end + + context "without a time zone" do + # Would need to be Etc/UTC or UTC to be valid + let(:preferences) { {} } + + it "returns the utc ActiveSupport::TimeZone" do + expect(user.time_zone) + .to eql ActiveSupport::TimeZone["Etc/UTC"] + end + end + end + describe "#find_by_mail" do let!(:user1) { create(:user, mail: "foo+test@example.org") } let!(:user2) { create(:user, mail: "foo@example.org") } From 8de035d16ca6d020d50a87d88097b58d8438f109 Mon Sep 17 00:00:00 2001 From: Jens Ulferts Date: Mon, 23 Sep 2024 18:06:05 +0200 Subject: [PATCH 14/18] Document format_time function Co-authored-by: Christophe Bliard --- lib_static/redmine/i18n.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib_static/redmine/i18n.rb b/lib_static/redmine/i18n.rb index 4d2426f4d59a..d3dbd020cde7 100644 --- a/lib_static/redmine/i18n.rb +++ b/lib_static/redmine/i18n.rb @@ -129,6 +129,17 @@ def format_time_as_date(time, format: nil) end end + # Formats the given time as a time string according to the user's time zone + # and optional specified format. + # + # @param time [Time, String] The time to format. Can be a Time object or a + # String. + # @param include_date [Boolean] Whether to include the date in the formatted + # output. Defaults to true. + # @param format [String] The strftime format to use for the time. Defaults + # to the format in `Setting.time_format`. + # @return [String, nil] The formatted time string, or nil if the time is not + # provided. def format_time(time, include_date: true, format: Setting.time_format) return nil unless time From b2e6d67d19a5355cc2caed4fef5621330b3bbc38 Mon Sep 17 00:00:00 2001 From: ulferts Date: Mon, 23 Sep 2024 18:08:23 +0200 Subject: [PATCH 15/18] remove unnecessary empty line --- app/views/users/_preferences.html.erb | 1 - 1 file changed, 1 deletion(-) diff --git a/app/views/users/_preferences.html.erb b/app/views/users/_preferences.html.erb index 45399a774315..37d8fe87686e 100644 --- a/app/views/users/_preferences.html.erb +++ b/app/views/users/_preferences.html.erb @@ -33,7 +33,6 @@ See COPYRIGHT and LICENSE files for more details. include_blank: false, container_class: (defined? input_size) ? "-#{input_size}" : "-wide" ) - %>
    <%= pref_fields.select :theme, theme_options_for_select, container_class: '-middle' %> From db1e9f3e4753c041a1b239cecc0c2f8ca828da3b Mon Sep 17 00:00:00 2001 From: ulferts Date: Mon, 23 Sep 2024 18:11:47 +0200 Subject: [PATCH 16/18] document format_time_as_date function --- lib_static/redmine/i18n.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib_static/redmine/i18n.rb b/lib_static/redmine/i18n.rb index d3dbd020cde7..f4f41ca36e7e 100644 --- a/lib_static/redmine/i18n.rb +++ b/lib_static/redmine/i18n.rb @@ -113,9 +113,13 @@ def link_regex /(\[(.+?)\]\((.+?)\))/ end - # Format the time to a date in the user time zone if one is set. - # If none is set and the time is in utc time zone (meaning it came from active record), format the date in the system timezone - # otherwise just use the date in the time zone attached to the time. + # Formats the given time as a date string according to the user's time zone and + # optional specified format. + # + # @param time [Time, String] The time to format. Can be a Time object or a String. + # @param format [String, nil] The strftime format to use for the date. If nil, the default + # date format from `Setting.date_format` is used. + # @return [String, nil] The formatted date string, or nil if the time is not provided. def format_time_as_date(time, format: nil) return nil unless time From ff45d0d0a229e27425abc5eec98278839d85b95f Mon Sep 17 00:00:00 2001 From: ulferts Date: Mon, 23 Sep 2024 18:25:44 +0200 Subject: [PATCH 17/18] attempt to remove string to time conversion --- lib_static/redmine/i18n.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib_static/redmine/i18n.rb b/lib_static/redmine/i18n.rb index f4f41ca36e7e..ef945f544883 100644 --- a/lib_static/redmine/i18n.rb +++ b/lib_static/redmine/i18n.rb @@ -116,7 +116,7 @@ def link_regex # Formats the given time as a date string according to the user's time zone and # optional specified format. # - # @param time [Time, String] The time to format. Can be a Time object or a String. + # @param time [Time] The time to format. # @param format [String, nil] The strftime format to use for the date. If nil, the default # date format from `Setting.date_format` is used. # @return [String, nil] The formatted date string, or nil if the time is not provided. @@ -136,8 +136,7 @@ def format_time_as_date(time, format: nil) # Formats the given time as a time string according to the user's time zone # and optional specified format. # - # @param time [Time, String] The time to format. Can be a Time object or a - # String. + # @param time [Time] The time to format. # @param include_date [Boolean] Whether to include the date in the formatted # output. Defaults to true. # @param format [String] The strftime format to use for the time. Defaults @@ -147,7 +146,6 @@ def format_time_as_date(time, format: nil) def format_time(time, include_date: true, format: Setting.time_format) return nil unless time - time = time.to_time if time.is_a?(String) zone = User.current.time_zone local = time.in_time_zone(zone) From cd615f5108034852b1473eb775e55574951163bb Mon Sep 17 00:00:00 2001 From: ulferts Date: Tue, 24 Sep 2024 14:01:07 +0200 Subject: [PATCH 18/18] move shared function user code into module --- app/models/anonymous_user.rb | 36 +++++++++----------- app/models/deleted_user.rb | 18 ++-------- app/models/system_user.rb | 40 ++++++++++++---------- app/models/user.rb | 40 ++-------------------- app/models/users/function_user.rb | 56 +++++++++++++++++++++++++++++++ 5 files changed, 98 insertions(+), 92 deletions(-) create mode 100644 app/models/users/function_user.rb diff --git a/app/models/anonymous_user.rb b/app/models/anonymous_user.rb index e3627be53b54..b55057d0e928 100644 --- a/app/models/anonymous_user.rb +++ b/app/models/anonymous_user.rb @@ -27,31 +27,25 @@ #++ class AnonymousUser < User - validate :validate_unique_anonymous_user, on: :create - - # There should be only one AnonymousUser in the database - def validate_unique_anonymous_user - errors.add :base, "An anonymous user already exists." if AnonymousUser.any? - end - - def available_custom_fields - [] - end - - # Overrides a few properties - def logged?; false end - - def builtin?; true end - - def admin; false end + include Users::FunctionUser def name(*_args); I18n.t(:label_user_anonymous) end - def mail; nil end + def self.first + anonymous_user = super - def time_zone; ActiveSupport::TimeZone[Setting.user_default_timezone.presence || "Etc/UTC"] end + if anonymous_user.nil? + (anonymous_user = new.tap do |u| + u.lastname = "Anonymous" + u.login = "" + u.firstname = "" + u.mail = "" + u.status = User.statuses[:active] + end).save - def rss_key; nil end + raise "Unable to create the anonymous user." if anonymous_user.new_record? + end - def destroy; false end + anonymous_user + end end diff --git a/app/models/deleted_user.rb b/app/models/deleted_user.rb index b59a6f122cb3..ad89f12e71e0 100644 --- a/app/models/deleted_user.rb +++ b/app/models/deleted_user.rb @@ -1,23 +1,9 @@ class DeletedUser < User - validate :validate_unique_deleted_user, on: :create - - # There should be only one DeletedUser in the database - def validate_unique_deleted_user - errors.add :base, "A DeletedUser already exists." if DeletedUser.any? - end - def self.first super || create(type: to_s, status: statuses[:locked]) end - # Overrides a few properties - def available_custom_fields = [] - def logged? = false - def builtin? = true - def admin = false + include Users::FunctionUser + def name(*_args) = I18n.t("user.deleted") - def mail = nil - def time_zone; ActiveSupport::TimeZone[Setting.user_default_timezone.presence || "Etc/UTC"] end - def rss_key = nil - def destroy = false end diff --git a/app/models/system_user.rb b/app/models/system_user.rb index 4250e8fdb235..a840ef477bbd 100644 --- a/app/models/system_user.rb +++ b/app/models/system_user.rb @@ -31,31 +31,35 @@ # class SystemUser < User - validate :validate_unique_system_user, on: :create - - # There should be only one SystemUser in the database - def validate_unique_system_user - errors.add :base, "A SystemUser already exists." if SystemUser.any? - end - - # Overrides a few properties - def logged?; false end - - def builtin?; true end + include Users::FunctionUser def name(*_args); "System" end - def mail; nil end + def run_given + User.execute_as(self) do + yield self + end + end - def time_zone; ActiveSupport::TimeZone[Setting.user_default_timezone.presence || "Etc/UTC"] end + def self.first + system_user = super - def rss_key; nil end + if system_user.nil? + system_user = new( + firstname: "", + lastname: "System", + login: "", + mail: "", + admin: true, + status: User.statuses[:active], + first_login: false + ) - def destroy; false end + system_user.save - def run_given - User.execute_as(self) do - yield self + raise "Unable to create the system user." unless system_user.persisted? end + + system_user end end diff --git a/app/models/user.rb b/app/models/user.rb index 4563f1086b92..d1ac0dc4f27f 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -543,46 +543,12 @@ def missing_authentication_method? # Returns the anonymous user. If the anonymous user does not exist, it is created. There can be only # one anonymous user per database. - def self.anonymous # rubocop:disable Metrics/AbcSize - RequestStore[:anonymous_user] ||= - begin - anonymous_user = AnonymousUser.first - - if anonymous_user.nil? - (anonymous_user = AnonymousUser.new.tap do |u| - u.lastname = "Anonymous" - u.login = "" - u.firstname = "" - u.mail = "" - u.status = User.statuses[:active] - end).save - - raise "Unable to create the anonymous user." if anonymous_user.new_record? - end - anonymous_user - end + def self.anonymous + RequestStore[:anonymous_user] ||= AnonymousUser.first end def self.system - system_user = SystemUser.first - - if system_user.nil? - system_user = SystemUser.new( - firstname: "", - lastname: "System", - login: "", - mail: "", - admin: true, - status: User.statuses[:active], - first_login: false - ) - - system_user.save(validate: false) - - raise "Unable to create the automatic migration user." unless system_user.persisted? - end - - system_user + SystemUser.first end protected diff --git a/app/models/users/function_user.rb b/app/models/users/function_user.rb new file mode 100644 index 000000000000..fe8309243f53 --- /dev/null +++ b/app/models/users/function_user.rb @@ -0,0 +1,56 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +module Users::FunctionUser + extend ActiveSupport::Concern + + included do + validate :validate_unique_function_user, on: :create + + # There should be only one such user in the database + def validate_unique_function_user + errors.add :base, "A #{self.class.name} already exists." if self.class.any? + end + + def available_custom_fields = [] + + def logged? = false + + def builtin? = true + + def name(*_args); raise NotImplementedError end + + def mail = nil + + def time_zone; ActiveSupport::TimeZone[Setting.user_default_timezone.presence || "Etc/UTC"] end + + def rss_key = nil + + def destroy = false + end +end