Skip to content

Commit

Permalink
Merge pull request #11795 from rioug/11720-enterprise-user-consent-to…
Browse files Browse the repository at this point in the history
…-ToS-changes

Add banner to allow user to accept ToS changes
  • Loading branch information
drummer83 authored Dec 22, 2023
2 parents 5f7760c + 1bb4a66 commit 72c824a
Show file tree
Hide file tree
Showing 20 changed files with 303 additions and 7 deletions.
1 change: 1 addition & 0 deletions app/controllers/spree/admin/base_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class BaseController < ApplicationController
helper 'admin/injection'
helper 'admin/orders'
helper 'admin/enterprises'
helper 'admin/terms_of_service'
helper 'enterprise_fees'
helper 'angular_form'

Expand Down
25 changes: 25 additions & 0 deletions app/helpers/admin/terms_of_service_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# frozen_string_literal: true

module Admin
module TermsOfServiceHelper
def tos_need_accepting?
return false unless spree_user_signed_in?

return false if Spree::Config.enterprises_require_tos == false

return false if TermsOfServiceFile.current.nil?

!accepted_tos?
end

private

def accepted_tos?
file_uploaded_at = TermsOfServiceFile.updated_at

current_spree_user.terms_of_service_accepted_at.present? &&
current_spree_user.terms_of_service_accepted_at > file_uploaded_at &&
current_spree_user.terms_of_service_accepted_at < DateTime.now
end
end
end
9 changes: 9 additions & 0 deletions app/reflexes/user_reflex.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# frozen_string_literal: true

class UserReflex < ApplicationReflex
def accept_terms_of_services
current_user.update(terms_of_service_accepted_at: DateTime.now)

morph "#banner-container", ""
end
end
8 changes: 8 additions & 0 deletions app/views/admin/_terms_of_service_banner.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#banner-container
.terms-of-service-banner.form-actions
.column-left
%p= t("admin.terms_of_service_have_been_updated_html", tos_link: link_to(t("admin.terms_of_service"), TermsOfServiceFile.current_url, target: "_blank"))
.column-right
%button{ data: { reflex: "click->user#accept_terms_of_services" } }
= t("admin.accept_terms_of_service")

2 changes: 2 additions & 0 deletions app/views/spree/layouts/_admin_body.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@
%span= yield :sidebar_title
= yield :sidebar

= render "admin/terms_of_service_banner" if tos_need_accepting?

%script
= raw "Spree.api_key = \"#{spree_current_user.try(:spree_api_key).to_s}\";"

Expand Down
1 change: 1 addition & 0 deletions app/webpacker/css/admin/all.scss
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@
@import "side_menu";
@import "tables";
@import "tag_rules";
@import "terms_of_service_banner";
@import "terms_of_service_files";
@import "validation";
@import "variant_overrides";
Expand Down
4 changes: 4 additions & 0 deletions app/webpacker/css/admin/globals/variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -149,3 +149,7 @@ $border-radius: 3px !default;

$font-weight-bold: 600 !default;
$font-weight-normal: 400 !default;

// z-index
//--------------------------------------------------------------
$tos-banner-z-index: 102;
14 changes: 8 additions & 6 deletions app/webpacker/css/admin/products_v3.scss
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@
}

// Form actions floats over other controls when active
.form-actions {
position: absolute;
top: -1em;
left: 0;
right: 0;
z-index: 1; // Ensure tom-select and .disabled-section are covered
#products-form {
.form-actions {
position: absolute;
top: -1em;
left: 0;
right: 0;
z-index: 1; // Ensure tom-select and .disabled-section are covered
}
}

// Hopefully these rules will be moved to component(s).
Expand Down
27 changes: 27 additions & 0 deletions app/webpacker/css/admin/terms_of_service_banner.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#banner-container {
position: fixed;
bottom: 0px;
left: 0;
width: 100%;
z-index: $tos-banner-z-index;

.terms-of-service-banner {
padding: 18px;
text-align: center;
font-size: 120%;
color: white;
font-weight: 600;
margin-top: 0;
background-color: rgba($color-notice, 0.8);
display: flex;

.column-left {
width: 70%;
}

.column-right {
width: 30%;
text-align: center;
}
}
}
2 changes: 2 additions & 0 deletions app/webpacker/css/admin_v3/all.scss
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,5 @@
@import "app/components/confirm_modal_component/confirm_modal_component";
@import "app/components/vertical_ellipsis_menu/component"; // admin_v3 and only V3
@import "app/webpacker/css/admin/trix.scss";

@import "terms_of_service_banner"; // admin_v3
4 changes: 4 additions & 0 deletions app/webpacker/css/admin_v3/globals/variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -176,3 +176,7 @@ $font-weight-normal: 400 !default;
$btn-relaxed-height: 40px !default;
$btn-regular-height: 32px !default;
$btn-condensed-height: 26px !default;

// z-index
//--------------------------------------------------------------
$tos-banner-z-index: 102;
25 changes: 25 additions & 0 deletions app/webpacker/css/admin_v3/terms_of_service_banner.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#banner-container {
position: fixed;
bottom: 0px;
left: 0;
width: 100%;
z-index: $tos-banner-z-index;
padding: 0 1.5%;

.terms-of-service-banner {
display: flex;

.column-left {
width: 70%;
font-size: 1rem;
font-weight: bold;
padding: 0.75em 1em;
}

.column-right {
width: 30%;
padding: 0.5em 1em;
text-align: right;
}
}
}
3 changes: 3 additions & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,9 @@ en:

available_units: "Available Units"

terms_of_service_have_been_updated_html: "Open Food Network's Terms of Service have been updated: %{tos_link}"
terms_of_service: Read Terms of Service
accept_terms_of_service: Accept Terms of Service
shopfront_settings:
embedded_shopfront_settings: "Embedded Shopfront Settings"
enable_embedded_shopfronts: "Enable Embedded Shopfronts"
Expand Down
1 change: 0 additions & 1 deletion config/routes/spree.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@

resources :users


constraints FeatureToggleConstraint.new(:admin_style_v3, negate: true) do
# Show old bulk products screen
resources :products, :index do
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
class AddTermsOfServiceAcceptedAtToSpreeUsers < ActiveRecord::Migration[7.0]
def up
add_column :spree_users, :terms_of_service_accepted_at, :datetime

if Spree::Config.enterprises_require_tos == true
# There isn't really a way to know which user have access to admin pages, so we update
# everyone. It's technically wrong to say shoppers have accepted ToS, but they will be
# required to accept the terms if they sign up for an enterprise.
Spree::User.update_all(terms_of_service_accepted_at: Time.zone.now)
end
end

def down
remove_column :spree_users, :terms_of_service_accepted_at
end
end
1 change: 1 addition & 0 deletions db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -932,6 +932,7 @@
t.boolean "show_api_key_view", default: false, null: false
t.string "provider"
t.string "uid"
t.datetime "terms_of_service_accepted_at"
t.index ["confirmation_token"], name: "index_spree_users_on_confirmation_token", unique: true
t.index ["email"], name: "email_idx_unique", unique: true
t.index ["persistence_token"], name: "index_users_on_persistence_token"
Expand Down
1 change: 1 addition & 0 deletions spec/factories/user_factory.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

confirmation_sent_at { '1970-01-01 00:00:00' }
confirmed_at { '1970-01-01 00:00:01' }
terms_of_service_accepted_at { 1.hour.ago }

before(:create) do |user, evaluator|
if evaluator.confirmation_sent_at
Expand Down
23 changes: 23 additions & 0 deletions spec/reflexes/user_reflex_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# frozen_string_literal: true

require "reflex_helper"

describe UserReflex, type: :reflex do
let(:current_user) { create(:user) }
let(:context) { { url: spree.admin_dashboard_url, connection: { current_user: } } }

describe "#accept_terms_of_services" do
subject(:reflex) { build_reflex(method_name: :accept_terms_of_services, **context) }

it "updates terms_of_service_accepted_at" do
expect {
reflex.run(:accept_terms_of_services)
current_user.reload
}.to change{ current_user.terms_of_service_accepted_at }
end

it "removes banner from the page" do
expect(reflex.run(:accept_terms_of_services)).to morph("#banner-container").with("")
end
end
end
88 changes: 88 additions & 0 deletions spec/requests/spree/admin/overview_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# frozen_string_literal: true

require "spec_helper"

describe "/admin", type: :request do
let(:enterprise) { create(:supplier_enterprise, name: "Feedme") }
let(:enterprise_user) { create(:user, enterprise_limit: 1) }

before do
enterprise_user.enterprise_roles.build(enterprise:).save
sign_in enterprise_user
end

describe "GET /admin" do
before do
mocked_tos = double(TermsOfServiceFile, updated_at: 2.hours.ago)
allow(TermsOfServiceFile).to receive(:current).and_return(mocked_tos)
# Mock current_url so we don't have to set up a complicated TermsOfServiceFile mock
# with attachement
allow(TermsOfServiceFile).to receive(:current_url).and_return("tmp/tos.pdf")
end

it "loads the dashboard page" do
get "/admin"

expect(response).to render_template("spree/admin/overview/single_enterprise_dashboard")
end

# The banner will show on all admin page, we are just testing it here
describe "terms of service updated banner" do
context "when terms of service has been updated" do
before { Spree::Config.enterprises_require_tos = true }

it "shows accept new ToS banner" do
enterprise_user.update(terms_of_service_accepted_at: nil)

get "/admin"

expect(response.body).to include("Terms of Service have been updated")
end

context "when user has accepted new terms of service" do
it "doesn't show accept new ToS banner" do
enterprise_user.update(terms_of_service_accepted_at: 1.hour.ago)

get "/admin"

expect(response.body).to_not include("Terms of Service have been updated")
end
end

# Shouldn't be possible
context "when user has accepted new terms of service in the future" do
it "shows accept new ToS banner" do
enterprise_user.update(terms_of_service_accepted_at: 1.hour.from_now)

get "/admin"

expect(response.body).to include("Terms of Service have been updated")
end
end

context "when no ToS has been uploaded" do
it "doesn't show accept new ToS banner" do
allow(TermsOfServiceFile).to receive(:current).and_return(nil)

get "/admin"

expect(response.body).to_not include("Terms of Service have been updated")
end
end

context "when enterprises don't need to accept ToS" do
before do
Spree::Config.enterprises_require_tos = false
enterprise_user.update(terms_of_service_accepted_at: nil)
end

it "doesn't show accept new ToS banner" do
get "/admin"

expect(response.body).to_not include("Terms of Service have been updated")
end
end
end
end
end
end
55 changes: 55 additions & 0 deletions spec/system/admin/tos_banner_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# frozen_string_literal: true

require 'system_helper'

describe 'Terms of Service banner' do
include AuthenticationHelper

let(:admin_user) { create(:admin_user, terms_of_service_accepted_at: nil) }
let(:test_file) { "Terms-of-service.pdf" }
let(:pdf_upload) do
Rack::Test::UploadedFile.new(Rails.public_path.join(test_file), "application/pdf")
end

before do
Spree::Config.enterprises_require_tos = true
TermsOfServiceFile.create!(attachment: pdf_upload)
login_as admin_user
end

context "when not accepted" do
it "shows banner" do
visit '/admin'

expect(page).to have_content("Terms of Service have been updated")

# Click on the accept button
expect do
click_button "Accept Terms of Service"
admin_user.reload
end.to change { admin_user.terms_of_service_accepted_at }
expect(page).to_not have_content("Terms of Service have been updated")

# Check the banner doesn't show again once ToS has been accepted
page.refresh
expect(page).to_not have_content("Terms of Service have been updated")
end
end

context "when updating Terms of Service" do
it "shows the banner" do
# ToS has been accepted
admin_user.update!(terms_of_service_accepted_at: 2.days.ago)

# Upload new ToS
visit admin_terms_of_service_files_path
attach_file "Attachment", Rails.public_path.join(test_file)
click_button "Create Terms of service file"

# check it has been uploaded
expect(page).to have_link "Terms of Service"

expect(page).to have_content("Terms of Service have been updated")
end
end
end

0 comments on commit 72c824a

Please sign in to comment.