Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@
shadow-base
">

<% available_timezones_for_select = ActiveSupport::TimeZone.all.map(&:name) %>
<li class="h-8 flex items-center hover:bg-gray-25 rounded">
<%= autosubmit_select_tag(
"solidus_timezone",
options_for_select(available_timezones_for_select, selected: Time.zone.name),
icon: 'time-zone-line',
) %>
</li>

<% if (available_locales = Spree.i18n_available_locales).any? %>
<li class="h-8 flex items-center hover:bg-gray-25 rounded">
<%= autosubmit_select_tag(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@
<div class="py-1.5">
<%= f.text_field(:password_confirmation) %>
</div>
<% if @user.respond_to?(:timezone) %>
<div class="py-1.5">
<%= f.select :timezone, ActiveSupport::TimeZone.all.map { |t| [t.name, t.to_s] }, include_blank: t("spree.none") %>
</div>
<% end %>
<div class="py-1.5">
<%= f.checkbox_row(:spree_role_ids, options: role_options, row_title: "Roles", layout: :subsection) %>
</div>
Expand Down
1 change: 1 addition & 0 deletions admin/app/controllers/solidus_admin/base_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class BaseController < ApplicationController
include SolidusAdmin::ControllerHelpers::Theme
include SolidusAdmin::ComponentsHelper
include SolidusAdmin::AuthenticationAdapters::Backend if defined?(Spree::Backend)
include Spree::Core::ControllerHelpers::Timezone

layout :set_layout

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def index
end
end

context "successful request" do
context "authorized request" do
before do
user = create(:admin_user, email: "admin@example.com")
allow_any_instance_of(SolidusAdmin::BaseController).to receive(:spree_current_user).and_return(user)
Expand All @@ -43,6 +43,12 @@ def index
get :index
expect(response.code).to eq "200"
end

it "sets timezone by param" do
get :index, params: {solidus_timezone: "Hawaii"}
expect(session).to have_key(:solidus_timezone)
expect(session[:solidus_timezone]).to eq("Hawaii")
end
end

describe "layout rendering" do
Expand Down
1 change: 1 addition & 0 deletions admin/spec/features/users_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
let(:sign_in_date) { DateTime.now }

before do
allow_any_instance_of(Spree.user_class).to receive(:try).with(:timezone) { nil }
allow_any_instance_of(Spree.user_class).to receive(:try).with(:email).and_call_original
allow_any_instance_of(Spree.user_class).to receive(:try).with(:last_sign_in_at).and_return(sign_in_date)
end
Expand Down
2 changes: 2 additions & 0 deletions backend/app/controllers/spree/admin/base_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
module Spree
module Admin
class BaseController < Spree::BaseController
include Spree::Core::ControllerHelpers::Timezone

helper "spree/admin/navigation"
layout "spree/layouts/admin"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<%= button_tag class: 'btn fa fa-chevron-circle-left', id: 'admin-nav-toggle', type: :button do %>
<span class="text"><%= t('spree.minimize_menu') %></span>
<% end %>
<%= render partial: 'spree/admin/shared/timezone_selection' %>
<%= render partial: 'spree/admin/shared/locale_selection' %>
<%= render partial: 'spree/admin/shared/theme_selection' %>
<% if lookup_context.exists?('spree/admin/shared/_navigation_footer') %>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
data-legacy-label="<%= 'spree.navigation.switch_to_legacy'.then { t(_1, default: t(_1, locale: :en)) } %>"
data-admin-label="<%= 'spree.navigation.switch_to_solidus_admin'.then { t(_1, default: t(_1, locale: :en)) } %>"
></span>
<span><input type="checkbox" id="solidus-admin-switch" class="solidus-admin--nav--switch"/></span>
<span><input type="checkbox" id="solidus-admin-switch" class="solidus-admin--nav--switch"></span>
</label>
</li>
</ul>
Expand All @@ -49,6 +49,7 @@
</summary>

<ul>
<%= render 'spree/admin/shared/timezone_selection_solidus_admin' %>
<%= render 'spree/admin/shared/locale_selection_solidus_admin' %>
<%= render 'spree/admin/shared/theme_selection_solidus_admin' %>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<% available_timezones_for_select = ActiveSupport::TimeZone.all.map(&:name) %>

<%= form_tag(url_for, method: :get, style: "width: 100%;") do %>
<label class="admin-navbar-selection admin-timezone-selection">
<i class="fa fa-globe fa-fw" title="<%= I18n.t('spree.choose_dashboard_locale') %>"></i>
<select name="solidus_timezone" class="custom-select fullwidth" onchange="this.form.requestSubmit()">
<%= options_for_select(available_timezones_for_select, selected: Time.zone.name) %>
</select>
</label>
<% end %>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<% available_timezones_for_select = ActiveSupport::TimeZone.all.map(&:name) %>

<li>
<%= form_tag(url_for, method: :get, style: "width: 100%;") do %>
<label>
<svg aria-hidden="true"><use xlink:href="<%= image_path('spree/backend/themes/solidus_admin/remixicon.symbol.svg') %>#ri-time-zone-line"></use></svg>
<select name="solidus_timezone" onchange="this.form.requestSubmit()">
<%= options_for_select(available_timezones_for_select, selected: Time.zone.name) %>
</select>
<svg aria-hidden="true"><use xlink:href="<%= image_path('spree/backend/themes/solidus_admin/remixicon.symbol.svg') %>#ri-expand-up-down-line"></use></svg>
</label>
<% end %>
</li>
9 changes: 9 additions & 0 deletions backend/app/views/spree/admin/users/_form.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -76,5 +76,14 @@
<% end %>
<% end %>
<% end %>

<% if can?(:update, @user) && @user.respond_to?(:timezone) %>
<%= f.field_container :timezone do %>
<%= f.label :timezone %>
<%= f.collection_select :timezone, ActiveSupport::TimeZone.all, :name, :to_s,
{include_blank: t("spree.none")},
class: "select2 fullwidth" %>
<% end %>
<% end %>
</div>
</div>
15 changes: 15 additions & 0 deletions backend/spec/controllers/spree/admin/base_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,19 @@ def index
end
end
end

context "authorized request" do
stub_authorization!

it "allows access" do
get :index
expect(response.body).to eq("test")
end

it "sets timezone by param" do
get :index, params: {solidus_timezone: "Hawaii"}
expect(session).to have_key(:solidus_timezone)
expect(session[:solidus_timezone]).to eq("Hawaii")
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ module Admin
let(:stock_item) { variant.stock_items.first }
let!(:user) { create :user }

before { expect(controller).to receive(:spree_current_user).and_return(user) }
before { expect(controller).to receive(:spree_current_user).twice.and_return(user) }
before { request.env["HTTP_REFERER"] = "product_admin_page" }

subject do
Expand Down
55 changes: 55 additions & 0 deletions core/app/helpers/spree/core/controller_helpers/timezone.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# frozen_string_literal: true

module Spree
module Core
module ControllerHelpers
module Timezone
extend ActiveSupport::Concern

included do
around_action :set_timezone
end

private

# Sets the timezone for the current request.
#
# Uses the most preferred timezone or falls back to the server default.
#
# It respects the server's configured timezone from +config/application.rb+.
#
def set_timezone(&action)
timezone = if timezone_change_needed?
resolved_timezone || Time.zone.name
else
session[:solidus_timezone]
end
session[:solidus_timezone] = timezone
Time.use_zone(timezone, &action)
end

# Checks if we need to change the timezone or not.
def timezone_change_needed?
params[:solidus_timezone].present? || session[:solidus_timezone].blank?
end

# Returns the first valid timezone from the priority chain, or nil.
#
# The priority order is:
#
# * the passed parameter: +params[:solidus_timezone]+
# * the user's timezone preference
#
def resolved_timezone
candidates = [params[:solidus_timezone], timezone_from_user].compact
candidates.detect { |tz| ActiveSupport::TimeZone[tz].present? }
end

# Try to get the timezone from user settings.
def timezone_from_user
spree_current_user.try(:timezone).presence
end
end
end
end
end
2 changes: 1 addition & 1 deletion core/lib/spree/permitted_attributes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ module PermittedAttributes
# by changing a user with higher priveleges' email to one a lower-priveleged
# admin owns. Creating a user with an email is handled separate at the
# controller level.
@@user_attributes = [:password, :password_confirmation, customer_metadata: {}]
@@user_attributes = [:password, :password_confirmation, :timezone, customer_metadata: {}]

@@variant_attributes = [
:name, :presentation, :cost_price, :lock_version,
Expand Down
101 changes: 101 additions & 0 deletions core/spec/helpers/controller_helpers/timezone_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# frozen_string_literal: true

require "rails_helper"

RSpec.describe Spree::Core::ControllerHelpers::Timezone, type: :controller do
controller(ActionController::Base) do
include Spree::Core::ControllerHelpers::Timezone

def index
render plain: Time.zone.name
end

private

attr_reader :spree_current_user
end

let(:original_timezone) { Time.zone.name }

describe "#set_timezone" do
context "with params[:solidus_timezone]" do
it "sets the timezone from the param" do
get :index, params: {solidus_timezone: "Hawaii"}
expect(response.body).to eq("Hawaii")
end

it "stores the timezone in the session" do
get :index, params: {solidus_timezone: "Hawaii"}
expect(session[:solidus_timezone]).to eq("Hawaii")
end

it "takes priority over session" do
get :index, params: {solidus_timezone: "Hawaii"}, session: {solidus_timezone: "Tokyo"}
expect(response.body).to eq("Hawaii")
end
end

context "with session[:solidus_timezone]" do
it "uses the timezone from the session" do
get :index, session: {solidus_timezone: "Tokyo"}
expect(response.body).to eq("Tokyo")
end
end

context "with spree_current_user timezone" do
let(:user) { double("User", timezone: "Berlin") }

before do
controller.instance_variable_set(:@spree_current_user, user)
end

it "uses the user's timezone" do
get :index
expect(response.body).to eq("Berlin")
end

context "when user does not respond to timezone" do
let(:user) { double("User") }

it "falls back to the server default" do
get :index
expect(response.body).to eq(original_timezone)
end
end

context "when user's timezone is blank" do
let(:user) { double("User", timezone: "") }

it "falls back to the server default" do
get :index
expect(response.body).to eq(original_timezone)
end
end
end

context "with an invalid timezone" do
it "falls back to the server default" do
get :index, params: {solidus_timezone: "Nonexistent/Zone"}
expect(response.body).to eq(original_timezone)
end
end

context "with no timezone set anywhere" do
it "uses the server default timezone" do
get :index
expect(response.body).to eq(original_timezone)
end

it "stores the server default in session" do
get :index
expect(session[:solidus_timezone]).to eq(original_timezone)
end
end

it "restores the original timezone after the request" do
original = Time.zone.name
get :index, params: {solidus_timezone: "Hawaii"}
expect(Time.zone.name).to eq(original)
end
end
end
Loading