Merge pull request #10040 from colinux/cross-domain-component
Prépare la bannière informant du changement de nom de domaine, avec redirection automatique le cas échéant
This commit is contained in:
commit
17f9992722
24 changed files with 218 additions and 16 deletions
1
.env.test
Normal file
1
.env.test
Normal file
|
@ -0,0 +1 @@
|
|||
APP_HOST="test.host" # must match host defined in spec/rails_helper.rb
|
|
@ -10,7 +10,6 @@ ol.fr-ol-content--override {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// with Marianne font, weight of font is less bolder, so bold it up
|
||||
.button.primary {
|
||||
font-weight: bold;
|
||||
|
@ -20,10 +19,17 @@ trix-editor.fr-input {
|
|||
max-height: none;
|
||||
}
|
||||
|
||||
.fr-label + .fr-ds-combobox { // same as .fr-label + .fr-input
|
||||
margin-top: 0.5rem;
|
||||
.fr-header {
|
||||
.fr-notice {
|
||||
// get back link underlined in notices, because they are usually hidden in headers
|
||||
--underline-img: linear-gradient(0deg, currentColor, currentColor);
|
||||
}
|
||||
}
|
||||
|
||||
.fr-label + .fr-ds-combobox {
|
||||
// same as .fr-label + .fr-input
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.fr-ds-combobox {
|
||||
.fr-menu {
|
||||
|
|
|
@ -2,11 +2,18 @@
|
|||
class Dsfr::NoticeComponent < ApplicationComponent
|
||||
renders_one :title
|
||||
|
||||
def initialize(closable: false)
|
||||
attr_reader :data_attributes
|
||||
|
||||
def initialize(closable: false, data_attributes: {})
|
||||
@closable = closable
|
||||
@data_attributes = data_attributes
|
||||
end
|
||||
|
||||
def closable?
|
||||
!!@closable
|
||||
end
|
||||
|
||||
def notice_data_attributes
|
||||
{ "data-dsfr-header-target": "notice" }.merge(data_attributes)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
.fr-notice.fr-notice--info{ "data-dsfr-header-target": "notice" }
|
||||
.fr-notice.fr-notice--info{ **notice_data_attributes }
|
||||
.fr-container
|
||||
.fr-notice__body
|
||||
%p.fr-notice__title
|
||||
|
|
38
app/components/switch_domain_banner_component.rb
Normal file
38
app/components/switch_domain_banner_component.rb
Normal file
|
@ -0,0 +1,38 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class SwitchDomainBannerComponent < ApplicationComponent
|
||||
attr_reader :user
|
||||
|
||||
def initialize(user:)
|
||||
@user = user
|
||||
end
|
||||
|
||||
def render?
|
||||
return false unless helpers.switch_domain_enabled?(request)
|
||||
return false if user&.preferred_domain_demarches_gouv_fr? && requested_from_new_domain?
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def auto_switch?
|
||||
helpers.auto_switch_domain?(request, user.present?)
|
||||
end
|
||||
|
||||
def manual_switch?
|
||||
helpers.app_host_legacy?(request) && user.present?
|
||||
end
|
||||
|
||||
def new_host_url
|
||||
helpers.url_for(url_options)
|
||||
end
|
||||
|
||||
def requested_from_new_domain?
|
||||
Current.host == ApplicationHelper::APP_HOST
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def url_options
|
||||
request.params.except(:switch_domain).merge(host: ApplicationHelper::APP_HOST)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
en:
|
||||
message_new_domain: "demarches-simplifiees.fr is now called"
|
||||
follow_link: Follow this link to connect to the new address.
|
||||
detected_error: We have detected a network error in your access. Contact your IT department to authorize connection to demarches.gouv.fr.
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
fr:
|
||||
message_new_domain: "demarches-simplifiees.fr s’appelle maintenant"
|
||||
follow_link: Suivez ce lien pour vous connecter à la nouvelle adresse.
|
||||
detected_error: Nous avons détecté une erreur réseau pour vous y accéder. Contactez votre service informatique pour autoriser la connexion sur demarches.gouv.fr.
|
|
@ -0,0 +1,29 @@
|
|||
- if auto_switch?
|
||||
:javascript
|
||||
const hintUrl = "http://#{ApplicationHelper::APP_HOST}/favicon.ico"
|
||||
|
||||
fetch(hintUrl, { mode: 'cors' }).then(() => {
|
||||
window.location = '#{new_host_url}';
|
||||
}).catch((e) => {
|
||||
const error = new Error("Connection test on new host failed: " + e);
|
||||
const event = new CustomEvent("sentry:capture-exception", { detail: error });
|
||||
setTimeout(() => dispatchEvent(event), 100); // listener is not immediately enabled
|
||||
})
|
||||
|
||||
|
||||
= render Dsfr::NoticeComponent.new(closable: true, data_attributes: { "data-switch-domain-notice" => true }) do |c|
|
||||
- c.with_title do
|
||||
= t(".message_new_domain")
|
||||
= "#{helpers.link_to APPLICATION_NAME, new_host_url}."
|
||||
|
||||
- if manual_switch?
|
||||
= t(".follow_link")
|
||||
- elsif auto_switch? || true
|
||||
= t(".detected_error")
|
||||
|
||||
- if user && !user.preferred_domain_demarches_gouv_fr? && requested_from_new_domain?
|
||||
= form_tag(helpers.preferred_domain_path, method: :patch, remote: true, name: "update-preferred-domain")
|
||||
:javascript
|
||||
document.addEventListener('noticeClosed', function(e) {
|
||||
document.forms['update-preferred-domain'].submit();
|
||||
});
|
|
@ -63,6 +63,12 @@ module Users
|
|||
redirect_to profil_path
|
||||
end
|
||||
|
||||
def preferred_domain
|
||||
current_user.update_preferred_domain(request.host_with_port)
|
||||
|
||||
head :no_content
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def find_transfers
|
||||
|
|
|
@ -39,7 +39,9 @@ class Users::RegistrationsController < Devise::RegistrationsController
|
|||
return redirect_to after_inactive_sign_up_path_for(existing_user)
|
||||
end
|
||||
|
||||
super
|
||||
super do
|
||||
resource.update_preferred_domain(Current.host) if resource.valid?
|
||||
end
|
||||
end
|
||||
|
||||
# GET /resource/edit
|
||||
|
|
|
@ -13,6 +13,7 @@ class Users::SessionsController < Devise::SessionsController
|
|||
|
||||
if user&.valid_password?(params[:user][:password])
|
||||
user.update(loged_in_with_france_connect: nil)
|
||||
user.update_preferred_domain(Current.host)
|
||||
end
|
||||
|
||||
super
|
||||
|
|
|
@ -9,5 +9,8 @@ export class DSFRHeaderController extends ApplicationController {
|
|||
this.noticeTarget.parentNode?.removeChild(this.noticeTarget);
|
||||
|
||||
this.element.classList.remove('fr-header__with-notice-info');
|
||||
|
||||
const event = new CustomEvent('noticeClosed', { bubbles: true });
|
||||
this.element.dispatchEvent(event);
|
||||
}
|
||||
}
|
||||
|
|
18
app/models/concerns/domain_migratable_concern.rb
Normal file
18
app/models/concerns/domain_migratable_concern.rb
Normal file
|
@ -0,0 +1,18 @@
|
|||
module DomainMigratableConcern
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
enum preferred_domain: { demarches_gouv_fr: 0, demarches_simplifiees_fr: 1 }, _prefix: true
|
||||
|
||||
validates :preferred_domain, inclusion: { in: User.preferred_domains.keys, allow_nil: true }
|
||||
|
||||
def update_preferred_domain(host)
|
||||
case host
|
||||
when ApplicationHelper::APP_HOST
|
||||
preferred_domain_demarches_gouv_fr!
|
||||
when ApplicationHelper::APP_HOST_LEGACY
|
||||
preferred_domain_demarches_simplifiees_fr!
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,4 +1,5 @@
|
|||
class User < ApplicationRecord
|
||||
include DomainMigratableConcern
|
||||
include EmailSanitizableConcern
|
||||
include PasswordComplexityConcern
|
||||
|
||||
|
|
|
@ -70,6 +70,8 @@
|
|||
- if is_expert_context
|
||||
= render partial: 'layouts/search_dossiers_form'
|
||||
|
||||
= render SwitchDomainBannerComponent.new(user: current_user)
|
||||
|
||||
#modal-header__menu.fr-header__menu.fr-modal{ "aria-labelledby": "navbar-burger-button" }
|
||||
.fr-container
|
||||
%button.fr-btn--close.fr-btn{ "aria-controls" => "modal-header__menu", title: t('close_modal', scope: [:layouts, :header]) }= t('close_modal', scope: [:layouts, :header])
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
- if auto_switch_domain?(request, user_signed_in?)
|
||||
:javascript
|
||||
const hintUrl = "#{image_url(FAVICONS_SRC["16px"])}"
|
||||
fetch(hintUrl)
|
||||
.then(function(){
|
||||
window.location = window.location.href.replace("#{ApplicationHelper::APP_HOST_LEGACY}", "#{ApplicationHelper::APP_HOST}")
|
||||
})
|
|
@ -36,7 +36,6 @@
|
|||
|
||||
= yield(:invisible_captcha_styles)
|
||||
= render partial: 'layouts/setup_theme'
|
||||
= render partial: 'layouts/switch_domain_banner'
|
||||
|
||||
%body{ { id: content_for(:page_id), class: browser.platform.ios? ? 'ios' : nil, data: { controller: 'turbo number-input' } }.compact }
|
||||
= render partial: 'layouts/skiplinks'
|
||||
|
|
|
@ -386,6 +386,7 @@ Rails.application.routes.draw do
|
|||
post 'accept_merge' => 'profil#accept_merge'
|
||||
post 'refuse_merge' => 'profil#refuse_merge'
|
||||
delete 'france_connect_information' => 'profil#destroy_fci'
|
||||
patch 'preferred_domain', to: 'profil#preferred_domain'
|
||||
get 'fermeture/:path', to: 'commencer#closing_details', as: :closing_details
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddPreferredDomainToUsers < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
add_column :users, :preferred_domain, :integer, default: nil
|
||||
end
|
||||
end
|
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema[7.0].define(version: 2024_03_18_134256) do
|
||||
ActiveRecord::Schema[7.0].define(version: 2024_03_18_152314) do
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "pgcrypto"
|
||||
enable_extension "plpgsql"
|
||||
|
@ -1136,6 +1136,7 @@ ActiveRecord::Schema[7.0].define(version: 2024_03_18_134256) do
|
|||
t.string "locale"
|
||||
t.datetime "locked_at", precision: nil
|
||||
t.string "loged_in_with_france_connect", default: "false"
|
||||
t.integer "preferred_domain"
|
||||
t.datetime "remember_created_at", precision: nil
|
||||
t.bigint "requested_merge_into_id"
|
||||
t.datetime "reset_password_sent_at", precision: nil
|
||||
|
|
|
@ -3,7 +3,7 @@ if defined?(HamlLint)
|
|||
class Linter::ApplicationNameLinter < Linter
|
||||
include LinterRegistry
|
||||
|
||||
FORBIDDEN = 'demarches-simplifiees.fr'
|
||||
FORBIDDEN = 'demarches.gouv.fr'
|
||||
REPLACEMENT = "APPLICATION_NAME"
|
||||
MSG = 'Hardcoding %s is forbidden, use %s instead'
|
||||
|
||||
|
|
59
spec/components/switch_domain_banner_component_spec.rb
Normal file
59
spec/components/switch_domain_banner_component_spec.rb
Normal file
|
@ -0,0 +1,59 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "rails_helper"
|
||||
|
||||
RSpec.describe SwitchDomainBannerComponent, type: :component do
|
||||
let(:app_host_legacy) { "demarches-simplifiees.fr" }
|
||||
let(:app_host) { "demarches.gouv.fr" }
|
||||
|
||||
let(:user) { create(:user) }
|
||||
let(:request_host) { app_host_legacy }
|
||||
let(:path) { "/" }
|
||||
|
||||
before do
|
||||
allow(Current).to receive(:host).and_return(app_host)
|
||||
stub_const("ApplicationHelper::APP_HOST_LEGACY", app_host_legacy)
|
||||
stub_const("ApplicationHelper::APP_HOST", app_host)
|
||||
|
||||
Flipper.enable(:switch_domain)
|
||||
end
|
||||
|
||||
after do
|
||||
Flipper.disable(:switch_domain)
|
||||
end
|
||||
|
||||
subject(:rendered) do
|
||||
with_request_url path, host: request_host, format: nil do
|
||||
render_inline(described_class.new(user: user))
|
||||
end
|
||||
end
|
||||
|
||||
context "when request is already on APP_HOST" do
|
||||
let(:request_host) { app_host }
|
||||
|
||||
it "notify about names change" do
|
||||
expect(rendered.to_html).to include("demarches-simplifiees.fr")
|
||||
expect(rendered.to_html).to include(app_host)
|
||||
expect(rendered.to_html).not_to include("window.location")
|
||||
expect(rendered.to_html).not_to include("Suivez ce lien")
|
||||
end
|
||||
|
||||
context "when user has already set preferred domain" do
|
||||
let(:user) { create(:user, preferred_domain: :demarches_gouv_fr) }
|
||||
|
||||
it "does not render the banner" do
|
||||
expect(rendered.to_html).to be_empty
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "URL generation" do
|
||||
let(:path) { "/admin/procedures" }
|
||||
|
||||
it "generate an url to the new domain" do
|
||||
expect(rendered.to_html).to have_link(APPLICATION_NAME, href: "http://demarches.gouv.fr/admin/procedures")
|
||||
expect(rendered.to_html).not_to include("window.location")
|
||||
expect(rendered.to_html).to include("Suivez ce lien")
|
||||
end
|
||||
end
|
||||
end
|
|
@ -42,6 +42,10 @@ describe Users::RegistrationsController, type: :controller do
|
|||
post :create, params: { user: user }
|
||||
end
|
||||
|
||||
before do
|
||||
allow(Current).to receive(:host).and_return(ENV.fetch("APP_HOST"))
|
||||
end
|
||||
|
||||
context 'when user is correct' do
|
||||
it 'sends confirmation instruction' do
|
||||
message = double()
|
||||
|
@ -49,6 +53,8 @@ describe Users::RegistrationsController, type: :controller do
|
|||
expect(message).to receive(:deliver_later)
|
||||
|
||||
subject
|
||||
|
||||
expect(User.last.preferred_domain_demarches_gouv_fr?).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -65,6 +65,18 @@ describe Users::SessionsController, type: :controller do
|
|||
expect(flash.alert).to eq("Adresse électronique ou mot de passe incorrect.")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user has not yet a preferred domain' do
|
||||
before do
|
||||
allow(Current).to receive(:host).and_return(ENV.fetch("APP_HOST"))
|
||||
end
|
||||
|
||||
it 'update preferred domain' do
|
||||
subject
|
||||
|
||||
expect(user.reload.preferred_domain_demarches_gouv_fr?).to be_truthy
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the credentials are wrong' do
|
||||
|
|
Loading…
Add table
Reference in a new issue