diff --git a/app/assets/images/dsfr/artwork/background/ovoid.svg b/app/assets/images/dsfr/artwork/background/ovoid.svg
new file mode 100644
index 000000000..98d816a1f
--- /dev/null
+++ b/app/assets/images/dsfr/artwork/background/ovoid.svg
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/assets/images/dsfr/artwork/pictograms/system/technical-error.svg b/app/assets/images/dsfr/artwork/pictograms/system/technical-error.svg
new file mode 100644
index 000000000..3c90712e8
--- /dev/null
+++ b/app/assets/images/dsfr/artwork/pictograms/system/technical-error.svg
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index aa3c08696..7584990cb 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -419,6 +419,17 @@ class ApplicationController < ActionController::Base
prepend_view_path "app/custom_views"
end
+ def try_nav_bar_profile_from_referrer
+ # detect context from referer, simple (no detection when refreshing the page)
+ params = Rails.application.routes.recognize_path(request&.referer)
+
+ controller_class = "#{params[:controller].camelize}Controller".safe_constantize
+ return if controller_class.nil?
+
+ controller_instance = controller_class.new
+ controller_instance.try(:nav_bar_profile)
+ end
+
# Extract a value from params based on the "path"
#
# params: { dossiers: { champs_public_attributes: { 1234 => { value: "hello" } } } }
diff --git a/app/controllers/errors_controller.rb b/app/controllers/errors_controller.rb
new file mode 100644
index 000000000..be1b68565
--- /dev/null
+++ b/app/controllers/errors_controller.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+class ErrorsController < ApplicationController
+ def nav_bar_profile = try_nav_bar_profile_from_referrer
+
+ rescue_from Exception do
+ # catch any error, except errors triggered by middlewares outside controller (like warden middleware)
+ render file: Rails.public_path.join('500.html'), layout: false, status: :internal_server_error
+ end
+
+ def internal_server_error
+ # This dynamic template is rendered when a "normal" error occurs, (ie. a bug which is 99.99% of errors.)
+ # However if this action fails (error in the view or in a middlewares)
+ # the exceptions are rescued and a basic 100% static html file is rendererd instead.
+ render_error 500
+ end
+
+ def not_found = render_error 404
+ def unprocessable_entity = render_error 422
+
+ def show # generic page for others errors
+ @status = params[:status].to_i
+ @error_name = Rack::Utils::HTTP_STATUS_CODES[@status]
+
+ render_error @status
+ end
+
+ private
+
+ def render_error(status)
+ respond_to do |format|
+ format.html { render status: }
+ format.json { render status:, json: { status:, name: Rack::Utils::HTTP_STATUS_CODES[status] } }
+ end
+ end
+
+ # Intercept errors in before_action when fetching user or roles
+ # when db is unreachable so we can still display a nice 500 static page
+ def current_user
+ super
+ rescue
+ nil
+ end
+
+ def current_user_roles
+ super
+ rescue
+ nil
+ end
+end
diff --git a/app/controllers/release_notes_controller.rb b/app/controllers/release_notes_controller.rb
index 79776bcac..51a4430e0 100644
--- a/app/controllers/release_notes_controller.rb
+++ b/app/controllers/release_notes_controller.rb
@@ -21,16 +21,7 @@ class ReleaseNotesController < ApplicationController
render "scrollable_list" if params[:page].present?
end
- def nav_bar_profile
- # detect context from referer, simple (no detection when refreshing the page)
- params = Rails.application.routes.recognize_path(request&.referer)
-
- controller_class = "#{params[:controller].camelize}Controller".safe_constantize
- return if controller_class.nil?
-
- controller_instance = controller_class.new
- controller_instance.try(:nav_bar_profile)
- end
+ def nav_bar_profile = try_nav_bar_profile_from_referrer
private
diff --git a/app/views/errors/_artwork.html.haml b/app/views/errors/_artwork.html.haml
new file mode 100644
index 000000000..c7cd1c5bc
--- /dev/null
+++ b/app/views/errors/_artwork.html.haml
@@ -0,0 +1,9 @@
+.fr-col-12.fr-col-md-3.fr-col-offset-md-1.fr-px-6w.fr-px-md-0.fr-py-0
+ %svg.fr-responsive-img.fr-artwork{ xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", width: "160", height: "200", viewBox: "0 0 160 200" }
+ %use.fr-artwork-motif{ href: image_path("dsfr/artwork/background/ovoid.svg#artwork-motif") }
+
+ %use.fr-artwork-background{ href: image_path('dsfr/artwork/background/ovoid.svg#artwork-background') }
+ %g{ transform: "translate(40, 60)" }
+ %use.fr-artwork-decorative{ href: image_path('dsfr/artwork/pictograms/system/technical-error.svg#artwork-decorative') }
+ %use.fr-artwork-minor{ href: image_path('dsfr/artwork/pictograms/system/technical-error.svg#artwork-minor') }
+ %use.fr-artwork-major{ href: image_path('dsfr/artwork/pictograms/system/technical-error.svg#artwork-major') }
diff --git a/app/views/errors/internal_server_error.en.html.haml b/app/views/errors/internal_server_error.en.html.haml
new file mode 100644
index 000000000..5caa4933c
--- /dev/null
+++ b/app/views/errors/internal_server_error.en.html.haml
@@ -0,0 +1,19 @@
+%main#content{ role: "main" }
+ .fr-container
+ .fr-my-7w.fr-mt-md-12w.fr-mb-md-10w.fr-grid-row.fr-grid-row--gutters.fr-grid-row--middle.fr-grid-row--center
+ .fr-py-0.fr-col-12.fr-col-md-6
+ %h1 Unexpected Error
+ %p.fr-text--sm.fr-mb-3w Error 500
+ %p.fr-text--lead.fr-mb-3w
+ Sorry, an error has occurred. Our teams have been notified
+ to resolve the issue as quickly as possible.
+ %p.fr-text--sm.fr-mb-5w
+ Try refreshing the page or try again a little later.
+ %br
+ If you need immediate assistance, please contact us.
+
+ %ul.fr-btns-group.fr-btns-group--inline-md
+ %li
+ = link_to("Contact Us", contact_path, class: "fr-btn fr-btn--secondary")
+
+ = render partial: "artwork"
diff --git a/app/views/errors/internal_server_error.fr.html.haml b/app/views/errors/internal_server_error.fr.html.haml
new file mode 100644
index 000000000..e500608e0
--- /dev/null
+++ b/app/views/errors/internal_server_error.fr.html.haml
@@ -0,0 +1,19 @@
+%main#content{ role: "main" }
+ .fr-container
+ .fr-my-7w.fr-mt-md-12w.fr-mb-md-10w.fr-grid-row.fr-grid-row--gutters.fr-grid-row--middle.fr-grid-row--center
+ .fr-py-0.fr-col-12.fr-col-md-6
+ %h1 Erreur inattendue
+ %p.fr-text--sm.fr-mb-3w Erreur 500
+ %p.fr-text--lead.fr-mb-3w
+ Désolé, une erreur est survenue. Nos équipes ont été averties
+ pour résoudre le problème le plus rapidement possible.
+ %p.fr-text--sm.fr-mb-5w
+ Essayez de rafraîchir la page ou réessayez un peu plus tard.
+ %br
+ Si le problème persiste, merci de nous contacter.
+
+ %ul.fr-btns-group.fr-btns-group--inline-md
+ %li
+ = link_to("Contactez-nous", contact_path, class: "fr-btn fr-btn--secondary")
+
+ = render partial: "artwork"
diff --git a/app/views/errors/not_found.en.html.haml b/app/views/errors/not_found.en.html.haml
new file mode 100644
index 000000000..2472750b6
--- /dev/null
+++ b/app/views/errors/not_found.en.html.haml
@@ -0,0 +1,20 @@
+%main#content{ role: "main" }
+ .fr-container
+ .fr-my-7w.fr-mt-md-12w.fr-mb-md-10w.fr-grid-row.fr-grid-row--gutters.fr-grid-row--middle.fr-grid-row--center
+ .fr-py-0.fr-col-12.fr-col-md-6
+ %h1 Page not found
+ %p.fr-text--sm.fr-mb-3w Error 404
+ %p.fr-text--lead.fr-mb-3w The page you are looking for cannot be found. We apologize for the inconvenience.
+ %p.fr-text--sm.fr-mb-5w
+ If you typed the web address in the browser, check that it is correct. The page may no longer be available.
+ %br
+ In this case, to continue your visit you can check our homepage.
+ %br
+ Otherwise, contact us so we can direct you to the correct information.
+ %ul.fr-btns-group.fr-btns-group--inline-md
+ %li
+ = link_to("Homepage", root_path, class: "fr-btn")
+ %li
+ = link_to("Contact us", contact_path, class: "fr-btn fr-btn--secondary")
+
+ = render partial: "artwork"
diff --git a/app/views/errors/not_found.fr.html.haml b/app/views/errors/not_found.fr.html.haml
new file mode 100644
index 000000000..33b39584c
--- /dev/null
+++ b/app/views/errors/not_found.fr.html.haml
@@ -0,0 +1,22 @@
+%main#content{ role: "main" }
+ .fr-container
+ .fr-my-7w.fr-mt-md-12w.fr-mb-md-10w.fr-grid-row.fr-grid-row--gutters.fr-grid-row--middle.fr-grid-row--center
+ .fr-py-0.fr-col-12.fr-col-md-6
+ %h1 Page non trouvée
+ %p.fr-text--sm.fr-mb-3w Erreur 404
+ %p.fr-text--lead.fr-mb-3w La page que vous cherchez est introuvable. Excusez-nous pour la gène occasionnée.
+ %p.fr-text--sm.fr-mb-3w
+ Si vous avez tapé l’adresse web dans le navigateur, vérifiez qu’elle est correcte. La page n’est peut-être plus disponible.
+ %br
+ Dans ce cas, pour continuer votre visite vous pouvez consulter notre page d’accueil.
+ %br
+ Sinon contactez-nous pour que l’on puisse vous rediriger vers la bonne information.
+ %p.fr-text--sm.fr-mb-5w
+ Pour le laissez-passer A-38, relatif à l’enregistrement d'une galère, veuillez vous adresser à la capitainerie au port.
+ %ul.fr-btns-group.fr-btns-group--inline-md
+ %li
+ = link_to("Page d’accueil", root_path, class: "fr-btn")
+ %li
+ = link_to("Contactez-nous", contact_path, class: "fr-btn fr-btn--secondary")
+
+ = render partial: "artwork"
diff --git a/app/views/errors/show.en.html.haml b/app/views/errors/show.en.html.haml
new file mode 100644
index 000000000..93b97a353
--- /dev/null
+++ b/app/views/errors/show.en.html.haml
@@ -0,0 +1,19 @@
+%main#content{ role: "main" }
+ .fr-container
+ .fr-my-7w.fr-mt-md-12w.fr-mb-md-10w.fr-grid-row.fr-grid-row--gutters.fr-grid-row--middle.fr-grid-row--center
+ .fr-py-0.fr-col-12.fr-col-md-6
+ %h1= @error_name
+ %p.fr-text--sm.fr-mb-3w Error #{@status}
+ %p.fr-text--lead.fr-mb-3w An error prevents this page from loading.
+
+ - if @error_name.present? # valid error code
+ %p.fr-text--sm.fr-mb-5w
+ = link_to("What does that mean?", "https://developer.mozilla.org/en/docs/Web/HTTP/Status/#{@status}", **external_link_attributes)
+
+ %ul.fr-btns-group.fr-btns-group--inline-md
+ %li
+ = link_to("Homepage", root_path, class: "fr-btn")
+ %li
+ = link_to("Contact us", contact_path, class: "fr-btn fr-btn--secondary")
+
+ = render partial: "artwork"
diff --git a/app/views/errors/show.fr.html.haml b/app/views/errors/show.fr.html.haml
new file mode 100644
index 000000000..7e607420a
--- /dev/null
+++ b/app/views/errors/show.fr.html.haml
@@ -0,0 +1,19 @@
+%main#content{ role: "main" }
+ .fr-container
+ .fr-my-7w.fr-mt-md-12w.fr-mb-md-10w.fr-grid-row.fr-grid-row--gutters.fr-grid-row--middle.fr-grid-row--center
+ .fr-py-0.fr-col-12.fr-col-md-6
+ %h1= @error_name
+ %p.fr-text--sm.fr-mb-3w Erreur #{@status}
+ %p.fr-text--lead.fr-mb-3w Une erreur empêche le chargement de cette page.
+
+ - if @error_name.present? # valid error code
+ %p.fr-text--sm.fr-mb-5w
+ = link_to("Qu’est-ce que cela veut dire ?", "https://developer.mozilla.org/fr/docs/Web/HTTP/Status/#{@status}", **external_link_attributes)
+
+ %ul.fr-btns-group.fr-btns-group--inline-md
+ %li
+ = link_to("Page d’accueil", root_path, class: "fr-btn")
+ %li
+ = link_to("Contactez-nous", contact_path, class: "fr-btn fr-btn--secondary")
+
+ = render partial: "artwork"
diff --git a/app/views/errors/unprocessable_entity.en.html.haml b/app/views/errors/unprocessable_entity.en.html.haml
new file mode 100644
index 000000000..4acd9ba03
--- /dev/null
+++ b/app/views/errors/unprocessable_entity.en.html.haml
@@ -0,0 +1,24 @@
+%main#content{ role: "main" }
+ .fr-container
+ .fr-my-7w.fr-mt-md-12w.fr-mb-md-10w.fr-grid-row.fr-grid-row--gutters.fr-grid-row--middle.fr-grid-row--center
+ .fr-py-0.fr-col-12.fr-col-md-6
+ %h1 The requested action has been rejected
+ %p.fr-text--sm.fr-mb-3w Error 422
+ %p.fr-text--lead.fr-mb-3w We're sorry, but we can't process your request.
+ %p.fr-text--sm.fr-mb-5w
+ This may be due to a request that cannot be processed in its current state.
+ %br
+ Go back to
+ = link_to("the previous page", "javascript:window.location = document.referrer", class: "fr-link")
+ and then try again.
+ %br
+
+ If the problem persists, please don't hesitate to contact us for assistance.
+
+ %ul.fr-btns-group.fr-btns-group--inline-md
+ %li
+ = link_to 'Back', 'javascript:window.location = document.referrer', class: 'fr-btn'
+ %li
+ = link_to 'Contact us', contact_path, class: 'fr-btn fr-btn--secondary'
+
+ = render partial: "artwork"
diff --git a/app/views/errors/unprocessable_entity.fr.html.haml b/app/views/errors/unprocessable_entity.fr.html.haml
new file mode 100644
index 000000000..8653830a8
--- /dev/null
+++ b/app/views/errors/unprocessable_entity.fr.html.haml
@@ -0,0 +1,24 @@
+%main#content{ role: "main" }
+ .fr-container
+ .fr-my-7w.fr-mt-md-12w.fr-mb-md-10w.fr-grid-row.fr-grid-row--gutters.fr-grid-row--middle.fr-grid-row--center
+ .fr-py-0.fr-col-12.fr-col-md-6
+ %h1 L’action demandée a été rejetée
+ %p.fr-text--sm.fr-mb-3w Erreur 422
+ %p.fr-text--lead.fr-mb-3w Nous sommes désolés, mais nous ne pouvons pas traiter votre requête.
+ %p.fr-text--sm.fr-mb-5w
+ Cela peut être dû à une requête qui ne peut être traitée dans son état actuel.
+ %br
+ Revenez à
+ = link_to("la page précédente", "javascript:window.location = document.referrer", class: "fr-link")
+ puis réessayez.
+ %br
+
+ Si le problème persiste, n’hésitez pas à nous contacter pour obtenir de l’aide.
+
+ %ul.fr-btns-group.fr-btns-group--inline-md
+ %li
+ = link_to 'Retour', 'javascript:window.location = document.referrer', class: 'fr-btn'
+ %li
+ = link_to 'Contactez-nous', contact_path, class: 'fr-btn fr-btn--secondary'
+
+ = render partial: "artwork"
diff --git a/config/application.rb b/config/application.rb
index 2e79f8f01..638dfa89e 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -104,6 +104,8 @@ module TPS
config.active_record.encryption.primary_key = Rails.application.secrets.active_record_encryption.fetch(:primary_key)
config.active_record.encryption.key_derivation_salt = Rails.application.secrets.active_record_encryption.fetch(:key_derivation_salt)
+ config.exceptions_app = self.routes
+
# Copied from rgeo/activerecord-postgis-adapter
ActiveRecord::SchemaDumper.ignore_tables |= [
'geography_columns',
diff --git a/config/routes.rb b/config/routes.rb
index bc65dfb59..18317b7f5 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -698,6 +698,11 @@ Rails.application.routes.draw do
resources :release_notes, only: [:index]
+ get '/404', to: 'errors#not_found'
+ get '/422', to: 'errors#unprocessable_entity'
+ get '/500', to: 'errors#internal_server_error'
+ get '/:status', to: 'errors#show', constraints: { status: /[4-5][0-5]\d/ }
+
if Rails.env.test?
scope 'test/api_geo' do
get 'regions' => 'api_geo_test#regions'
diff --git a/public/404.html b/public/404.html
deleted file mode 100644
index 297300fe2..000000000
--- a/public/404.html
+++ /dev/null
@@ -1,67 +0,0 @@
-
-
-
- La page que vous cherchez n’existe pas (erreur 404)
-
-
-
-
-
-
-
-
-
-
La page que vous cherchez n’existe pas (erreur 404).
-
La page que vous cherchez a sans doute changé d’adresse, ou vous n’avez pas les droits nécessaires pour y accéder.
-
-
-
-
diff --git a/public/404_procedure_not_found.html b/public/404_procedure_not_found.html
deleted file mode 100644
index 93661f20b..000000000
--- a/public/404_procedure_not_found.html
+++ /dev/null
@@ -1,66 +0,0 @@
-
-
-
- The page you were looking for doesn't exist (404)
-
-
-
-
-
-
-
-
-
Cette démarche n'existe pas.
-
Merci de vérifier le lien que vous avez suivi et/ou de contacter votre administrateur.
-
-
Si vous êtes l'administrateur de l'application, merci de regarder les logs.
-
-
-
diff --git a/public/422.html b/public/422.html
deleted file mode 100644
index 899adfc29..000000000
--- a/public/422.html
+++ /dev/null
@@ -1,59 +0,0 @@
-
-
-
-
-
-
-
-
- Erreur 422 · demarches-simplifiees.fr
-
-
-
-
-
-
-
-
L’action demandée a été rejetée.
-
- Pas de panique, c’est probablement temporaire.
-
-
- Essayez de
- recharger la page précédente ,
- et tout devrait rentrer dans l’ordre.
-
-
-
-
-
diff --git a/public/500.html b/public/500.html
index 94047a0d5..778e9f022 100644
--- a/public/500.html
+++ b/public/500.html
@@ -1,53 +1,2157 @@
-
-
-
-
-
-
-
-
- Erreur 500 · demarches-simplifiees.fr
-
-
-
-
-
-