From c20ad5ca17d94a10cca530774827728015eea27d Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Thu, 22 Apr 2021 14:44:58 +0100 Subject: [PATCH 1/8] Remove the link between commentaire and user --- app/graphql/types/dossier_type.rb | 4 +-- app/models/champ.rb | 1 - app/models/commentaire.rb | 29 +++++++++++-------- app/services/commentaire_service.rb | 4 +-- ...422101149_add_expert_id_to_commentaires.rb | 5 ++++ db/schema.rb | 5 +++- spec/models/commentaire_spec.rb | 22 +++++++++----- 7 files changed, 45 insertions(+), 25 deletions(-) create mode 100644 db/migrate/20210422101149_add_expert_id_to_commentaires.rb diff --git a/app/graphql/types/dossier_type.rb b/app/graphql/types/dossier_type.rb index 9d611166d..7f340ffc3 100644 --- a/app/graphql/types/dossier_type.rb +++ b/app/graphql/types/dossier_type.rb @@ -80,10 +80,10 @@ module Types def messages(id: nil) if id.present? Loaders::Record - .for(Commentaire, where: { dossier: object }, includes: [:instructeur, :user], array: true) + .for(Commentaire, where: { dossier: object }, includes: [:instructeur, :expert], array: true) .load(ApplicationRecord.id_from_typed_id(id)) else - Loaders::Association.for(object.class, commentaires: [:instructeur, :user]).load(object) + Loaders::Association.for(object.class, commentaires: [:instructeur, :expert]).load(object) end end diff --git a/app/models/champ.rb b/app/models/champ.rb index fb35660ca..4b3fda82e 100644 --- a/app/models/champ.rb +++ b/app/models/champ.rb @@ -21,7 +21,6 @@ class Champ < ApplicationRecord belongs_to :dossier, -> { with_discarded }, inverse_of: false, touch: true, optional: false belongs_to :type_de_champ, inverse_of: :champ, optional: false belongs_to :parent, class_name: 'Champ', optional: true - has_many :commentaires has_one_attached :piece_justificative_file # We declare champ specific relationships (Champs::CarteChamp, Champs::SiretChamp and Champs::RepetitionChamp) diff --git a/app/models/commentaire.rb b/app/models/commentaire.rb index d55a5ce69..895bc2f3f 100644 --- a/app/models/commentaire.rb +++ b/app/models/commentaire.rb @@ -8,14 +8,15 @@ # created_at :datetime not null # updated_at :datetime not null # dossier_id :integer +# expert_id :bigint # instructeur_id :bigint -# user_id :bigint # class Commentaire < ApplicationRecord + self.ignored_columns = [:user_id] belongs_to :dossier, inverse_of: :commentaires, touch: true, optional: false - belongs_to :user, optional: true belongs_to :instructeur, optional: true + belongs_to :expert, optional: true validate :messagerie_available?, on: :create @@ -33,10 +34,10 @@ class Commentaire < ApplicationRecord after_create :notify def email - if user - user.email - elsif instructeur + if sent_by_instructeur? instructeur.email + elsif sent_by_expert? + expert.email else read_attribute(:email) end @@ -47,7 +48,7 @@ class Commentaire < ApplicationRecord end def redacted_email - if instructeur.present? + if sent_by_instructeur? if Flipper.enabled?(:hide_instructeur_email, dossier.procedure) "Instructeur n° #{instructeur.id}" else @@ -59,8 +60,15 @@ class Commentaire < ApplicationRecord end def sent_by_system? - [CONTACT_EMAIL, OLD_CONTACT_EMAIL].include?(email) && - user.nil? && instructeur.nil? + [CONTACT_EMAIL, OLD_CONTACT_EMAIL].include?(email) + end + + def sent_by_instructeur? + instructeur_id.present? + end + + def sent_by_expert? + expert_id.present? end def sent_by?(someone) @@ -76,15 +84,12 @@ class Commentaire < ApplicationRecord private def notify - dossier_user_email = dossier.user.email - invited_users_emails = dossier.invites.pluck(:email).to_a - # - If the email is the contact email, the commentaire is a copy # of an automated notification email we sent to a user, so do nothing. # - If a user or an invited user posted a commentaire, do nothing, # the notification system will properly # - Otherwise, a instructeur posted a commentaire, we need to notify the user - if !email.in?([CONTACT_EMAIL, dossier_user_email, *invited_users_emails]) + if sent_by_instructeur? || sent_by_expert? notify_user end end diff --git a/app/services/commentaire_service.rb b/app/services/commentaire_service.rb index f7611b290..7892c65f2 100644 --- a/app/services/commentaire_service.rb +++ b/app/services/commentaire_service.rb @@ -2,10 +2,10 @@ class CommentaireService class << self def build(sender, dossier, params) case sender - when User - params[:user] = sender when Instructeur params[:instructeur] = sender + when Expert + params[:expert] = sender end build_with_email(sender.email, dossier, params) diff --git a/db/migrate/20210422101149_add_expert_id_to_commentaires.rb b/db/migrate/20210422101149_add_expert_id_to_commentaires.rb new file mode 100644 index 000000000..78afbe946 --- /dev/null +++ b/db/migrate/20210422101149_add_expert_id_to_commentaires.rb @@ -0,0 +1,5 @@ +class AddExpertIdToCommentaires < ActiveRecord::Migration[6.1] + def change + add_belongs_to :commentaires, :expert, type: :bigint, foreign_key: true + end +end diff --git a/db/schema.rb b/db/schema.rb index c282337f9..fec39e667 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2021_04_16_160721) do +ActiveRecord::Schema.define(version: 2021_04_22_101149) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -175,7 +175,9 @@ ActiveRecord::Schema.define(version: 2021_04_16_160721) do t.datetime "updated_at", null: false t.bigint "user_id" t.bigint "instructeur_id" + t.bigint "expert_id" t.index ["dossier_id"], name: "index_commentaires_on_dossier_id" + t.index ["expert_id"], name: "index_commentaires_on_expert_id" t.index ["instructeur_id"], name: "index_commentaires_on_instructeur_id" t.index ["user_id"], name: "index_commentaires_on_user_id" end @@ -738,6 +740,7 @@ ActiveRecord::Schema.define(version: 2021_04_16_160721) do add_foreign_key "champs", "champs", column: "parent_id" add_foreign_key "closed_mails", "procedures" add_foreign_key "commentaires", "dossiers" + add_foreign_key "commentaires", "experts" add_foreign_key "dossier_operation_logs", "bill_signatures" add_foreign_key "dossier_operation_logs", "instructeurs" add_foreign_key "dossiers", "groupe_instructeurs" diff --git a/spec/models/commentaire_spec.rb b/spec/models/commentaire_spec.rb index c38a0f3d8..4e33eb5c9 100644 --- a/spec/models/commentaire_spec.rb +++ b/spec/models/commentaire_spec.rb @@ -63,7 +63,7 @@ describe Commentaire do end context 'with a commentaire created by a user' do - let(:commentaire) { build :commentaire, user: user } + let(:commentaire) { build :commentaire, email: user.email } let(:user) { build :user, email: 'some_user@exemple.fr' } it { is_expected.to eq 'some_user@exemple.fr' } @@ -73,26 +73,34 @@ describe Commentaire do describe "#notify" do let(:procedure) { create(:procedure) } let(:instructeur) { create(:instructeur) } + let(:expert) { create(:expert) } let(:assign_to) { create(:assign_to, instructeur: instructeur, procedure: procedure) } let(:user) { create(:user) } let(:dossier) { create(:dossier, :en_construction, procedure: procedure, user: user) } - let(:commentaire) { Commentaire.new(dossier: dossier, body: "Mon commentaire") } context "with a commentaire created by a instructeur" do + let(:commentaire) { CommentaireService.build(instructeur, dossier, body: "Mon commentaire") } + it "calls notify_user" do expect(commentaire).to receive(:notify_user) + commentaire.save + end + end - commentaire.email = instructeur.email + context "with a commentaire created by an expert" do + let(:commentaire) { CommentaireService.build(expert, dossier, body: "Mon commentaire") } + + it "calls notify_user" do + expect(commentaire).to receive(:notify_user) commentaire.save end end context "with a commentaire automatically created (notification)" do - it "does not call notify_user or notify_instructeurs" do - expect(commentaire).not_to receive(:notify_user) - expect(commentaire).not_to receive(:notify_instructeurs) + let(:commentaire) { CommentaireService.build_with_email(CONTACT_EMAIL, dossier, body: "Mon commentaire") } - commentaire.email = CONTACT_EMAIL + it "does not call notify_user" do + expect(commentaire).not_to receive(:notify_user) commentaire.save end end From 2a068fb9b0170675e959a03f122c5dbf6b825b24 Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Thu, 15 Apr 2021 16:24:03 +0100 Subject: [PATCH 2/8] Remove user feedback buttons --- app/assets/stylesheets/dossier_index.scss | 16 ------ app/controllers/stats_controller.rb | 38 ------------- app/controllers/users/feedbacks_controller.rb | 8 --- app/models/feedback.rb | 21 -------- app/models/user.rb | 1 - app/views/stats/index.html.haml | 13 ----- .../dossiers/_deleted_dossiers_list.html.haml | 11 ---- .../users/dossiers/_dossiers_list.html.haml | 11 ---- app/views/users/feedbacks/create.js.erb | 7 --- spec/controllers/stats_controller_spec.rb | 54 ------------------- spec/factories/feedback.rb | 18 ------- .../users/dossiers/index.html.haml_spec.rb | 13 ----- 12 files changed, 211 deletions(-) delete mode 100644 app/assets/stylesheets/dossier_index.scss delete mode 100644 app/controllers/users/feedbacks_controller.rb delete mode 100644 app/models/feedback.rb delete mode 100644 app/views/users/feedbacks/create.js.erb delete mode 100644 spec/factories/feedback.rb diff --git a/app/assets/stylesheets/dossier_index.scss b/app/assets/stylesheets/dossier_index.scss deleted file mode 100644 index e5a67e1ba..000000000 --- a/app/assets/stylesheets/dossier_index.scss +++ /dev/null @@ -1,16 +0,0 @@ -@import "colors"; -@import "constants"; - -#user-satisfaction { - text-align: center; - padding: 20px; - - .icon { - padding: 10px 5px; - margin: 10px 10px; - - &:hover { - cursor: pointer; - } - } -} diff --git a/app/controllers/stats_controller.rb b/app/controllers/stats_controller.rb index e8d5fa4de..751631dfb 100644 --- a/app/controllers/stats_controller.rb +++ b/app/controllers/stats_controller.rb @@ -17,8 +17,6 @@ class StatsController < ApplicationController stat.dossiers_deposes_entre_60_et_30_jours ) - @satisfaction_usagers = satisfaction_usagers - @contact_percentage = contact_percentage @dossiers_states_for_pie = { @@ -123,42 +121,6 @@ class StatsController < ApplicationController } end - def satisfaction_usagers - legend = { - Feedback.ratings.fetch(:unhappy) => "Mécontents", - Feedback.ratings.fetch(:neutral) => "Neutres", - Feedback.ratings.fetch(:happy) => "Satisfaits" - } - - number_of_weeks = 12 - totals = Feedback - .group_by_week(:created_at, last: number_of_weeks, current: false) - .count - - legend.keys.map do |rating| - data = Feedback - .where(rating: rating) - .group_by_week(:created_at, last: number_of_weeks, current: false) - .count - .map do |week, count| - total = totals[week] - # By default a week is displayed by the first day of the week – but we'd rather display the last day - label = week.next_week - - if total > 0 - [label, (count.to_f / total * 100).round(2)] - else - [label, 0] - end - end.to_h - - { - name: legend[rating], - data: data - } - end - end - def contact_percentage number_of_months = 13 diff --git a/app/controllers/users/feedbacks_controller.rb b/app/controllers/users/feedbacks_controller.rb deleted file mode 100644 index 6436025bb..000000000 --- a/app/controllers/users/feedbacks_controller.rb +++ /dev/null @@ -1,8 +0,0 @@ -module Users - class FeedbacksController < UserController - def create - current_user.feedbacks.create!(rating: params[:rating]) - flash.notice = "Merci de votre retour, si vous souhaitez nous en dire plus, n'hésitez pas à #{helpers.contact_link('nous contacter', type: Helpscout::FormAdapter::TYPE_AMELIORATION)}." - end - end -end diff --git a/app/models/feedback.rb b/app/models/feedback.rb deleted file mode 100644 index e0e96b281..000000000 --- a/app/models/feedback.rb +++ /dev/null @@ -1,21 +0,0 @@ -# == Schema Information -# -# Table name: feedbacks -# -# id :bigint not null, primary key -# rating :string not null -# created_at :datetime not null -# updated_at :datetime not null -# user_id :bigint -# -class Feedback < ApplicationRecord - belongs_to :user, optional: false - - enum rating: { - happy: 'happy', - neutral: 'neutral', - unhappy: 'unhappy' - } - - validates :rating, presence: true -end diff --git a/app/models/user.rb b/app/models/user.rb index d8a15af05..f9f6fa372 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -44,7 +44,6 @@ class User < ApplicationRecord has_many :dossiers, dependent: :destroy has_many :invites, dependent: :destroy has_many :dossiers_invites, through: :invites, source: :dossier - has_many :feedbacks, dependent: :destroy has_many :deleted_dossiers has_one :france_connect_information, dependent: :destroy belongs_to :instructeur, optional: true diff --git a/app/views/stats/index.html.haml b/app/views/stats/index.html.haml index 4b2032a7c..07792eafb 100644 --- a/app/views/stats/index.html.haml +++ b/app/views/stats/index.html.haml @@ -24,19 +24,6 @@ %span.big-number-card-detail #{number_with_delimiter(@dossiers_numbers[:last_30_days_count])} (#{@dossiers_numbers[:evolution]} %) sur les 30 derniers jours - .stat-card.stat-card-half.pull-left - %span.stat-card-title - Satisfaction usager - - .chart-container - .chart - = area_chart @satisfaction_usagers, - stacked: true, - suffix: ' %', - max: 100, - library: { plotOptions: { series: { marker: { enabled: true }}}}, - colors: ["#C31C25", "#F5962A", "#25B177"] - .stat-card.stat-card-half.pull-left %span.stat-card-title Pourcentage de contact utilisateur diff --git a/app/views/users/dossiers/_deleted_dossiers_list.html.haml b/app/views/users/dossiers/_deleted_dossiers_list.html.haml index 2b4efe2b3..4b1e039d2 100644 --- a/app/views/users/dossiers/_deleted_dossiers_list.html.haml +++ b/app/views/users/dossiers/_deleted_dossiers_list.html.haml @@ -23,17 +23,6 @@ = paginate(deleted_dossiers) - - if current_user.feedbacks.empty? || current_user.feedbacks.last.created_at < 1.month.ago - #user-satisfaction - %h3 Que pensez-vous de la facilité d'utilisation de ce service ? - .icons - = link_to feedback_path(rating: Feedback.ratings.fetch(:unhappy)), data: { remote: true, method: :post } do - %span.icon.frown - = link_to feedback_path(rating: Feedback.ratings.fetch(:neutral)), data: { remote: true, method: :post } do - %span.icon.meh - = link_to feedback_path(rating: Feedback.ratings.fetch(:happy)), data: { remote: true, method: :post } do - %span.icon.smile - - else .blank-tab %h2.empty-text Aucun dossier. diff --git a/app/views/users/dossiers/_dossiers_list.html.haml b/app/views/users/dossiers/_dossiers_list.html.haml index 35a90aae3..53ea0a2d8 100644 --- a/app/views/users/dossiers/_dossiers_list.html.haml +++ b/app/views/users/dossiers/_dossiers_list.html.haml @@ -31,17 +31,6 @@ = paginate(dossiers) - - if current_user.feedbacks.empty? || current_user.feedbacks.last.created_at < 1.month.ago - #user-satisfaction - %h3 Que pensez-vous de la facilité d'utilisation de ce service ? - .icons - = link_to feedback_path(rating: Feedback.ratings.fetch(:unhappy)), data: { remote: true, method: :post } do - %span.icon.frown - = link_to feedback_path(rating: Feedback.ratings.fetch(:neutral)), data: { remote: true, method: :post } do - %span.icon.meh - = link_to feedback_path(rating: Feedback.ratings.fetch(:happy)), data: { remote: true, method: :post } do - %span.icon.smile - - else .blank-tab %h2.empty-text Aucun dossier. diff --git a/app/views/users/feedbacks/create.js.erb b/app/views/users/feedbacks/create.js.erb deleted file mode 100644 index 3e7422464..000000000 --- a/app/views/users/feedbacks/create.js.erb +++ /dev/null @@ -1,7 +0,0 @@ -try { - window.scroll({ top: 0, left: 0, behavior: 'smooth' }); -} catch(e) { - window.scroll(0, 0); -} -<%= remove_element('#user-satisfaction') %> -<%= render_flash %> diff --git a/spec/controllers/stats_controller_spec.rb b/spec/controllers/stats_controller_spec.rb index 560ca44ed..ab80cf07e 100644 --- a/spec/controllers/stats_controller_spec.rb +++ b/spec/controllers/stats_controller_spec.rb @@ -185,60 +185,6 @@ describe StatsController, type: :controller do it { expect(subject).to eq(@expected_hash) } end - describe "#satisfaction_usagers" do - before do - # Test the stats on October 2018 – where the 1st, 8th, 15th, 22th and 29th are conveniently Mondays - # Current week: 1 negative feedback - Timecop.freeze(Time.zone.local(2018, 10, 22, 12, 00)) { create(:feedback, :unhappy) } - # Last week: 3 positive, 1 negative - Timecop.freeze(Time.zone.local(2018, 10, 21, 12, 00)) { create(:feedback, :unhappy) } - Timecop.freeze(Time.zone.local(2018, 10, 19, 12, 00)) { create(:feedback, :happy) } - Timecop.freeze(Time.zone.local(2018, 10, 17, 12, 00)) { create(:feedback, :happy) } - Timecop.freeze(Time.zone.local(2018, 10, 15, 12, 00)) { create(:feedback, :happy) } - # N-2 week: 2 positive, 2 negative - Timecop.freeze(Time.zone.local(2018, 10, 14, 12, 00)) { create(:feedback, :unhappy) } - Timecop.freeze(Time.zone.local(2018, 10, 12, 12, 00)) { create(:feedback, :happy) } - Timecop.freeze(Time.zone.local(2018, 10, 10, 12, 00)) { create(:feedback, :unhappy) } - Timecop.freeze(Time.zone.local(2018, 10, 8, 12, 00)) { create(:feedback, :happy) } - # N-3 week: 1 positive, 3 negative - Timecop.freeze(Time.zone.local(2018, 10, 1, 12, 00)) { create(:feedback, :unhappy) } - Timecop.freeze(Time.zone.local(2018, 10, 3, 12, 00)) { create(:feedback, :happy) } - Timecop.freeze(Time.zone.local(2018, 10, 5, 12, 00)) { create(:feedback, :unhappy) } - Timecop.freeze(Time.zone.local(2018, 10, 7, 12, 00)) { create(:feedback, :unhappy) } - end - - subject(:stats) do - Timecop.freeze(Time.zone.local(2018, 10, 28, 12, 00)) { - StatsController.new.send(:satisfaction_usagers) - } - end - - it 'returns one set of values for each kind of feedback' do - expect(stats.count).to eq 3 - expect(stats.map { |g| g[:name] }).to contain_exactly('Satisfaits', 'Neutres', 'Mécontents') - end - - it 'returns weekly ratios between a given feedback and all feedback' do - happy_data = stats.find { |g| g[:name] == 'Satisfaits' }[:data] - - expect(happy_data.values[-4]).to eq 0 - expect(happy_data.values[-3]).to eq 25.0 - expect(happy_data.values[-2]).to eq 50.0 - expect(happy_data.values[-1]).to eq 75.0 - - unhappy_data = stats.find { |g| g[:name] == 'Mécontents' }[:data] - expect(unhappy_data.values[-4]).to eq 0 - expect(unhappy_data.values[-3]).to eq 75.0 - expect(unhappy_data.values[-2]).to eq 50.0 - expect(unhappy_data.values[-1]).to eq 25.0 - end - - it 'excludes values still in the current week' do - unhappy_data = stats.find { |g| g[:name] == 'Mécontents' }[:data] - expect(unhappy_data.values).not_to include(100.0) - end - end - describe '#avis_usage' do let!(:dossier) { create(:dossier) } let!(:avis_with_dossier) { create(:avis) } diff --git a/spec/factories/feedback.rb b/spec/factories/feedback.rb deleted file mode 100644 index 51b522080..000000000 --- a/spec/factories/feedback.rb +++ /dev/null @@ -1,18 +0,0 @@ -FactoryBot.define do - factory :feedback do - rating { Feedback.ratings.fetch(:happy) } - association :user - - trait :happy do - rating { Feedback.ratings.fetch(:happy) } - end - - trait :neutral do - rating { Feedback.ratings.fetch(:neutral) } - end - - trait :unhappy do - rating { Feedback.ratings.fetch(:unhappy) } - end - end -end diff --git a/spec/views/users/dossiers/index.html.haml_spec.rb b/spec/views/users/dossiers/index.html.haml_spec.rb index e3d2514e5..a43e546a9 100644 --- a/spec/views/users/dossiers/index.html.haml_spec.rb +++ b/spec/views/users/dossiers/index.html.haml_spec.rb @@ -69,17 +69,4 @@ describe 'users/dossiers/index.html.haml', type: :view do expect(rendered).to have_selector('ul.tabs li.active', count: 1) end end - - context "quand le user n'a aucun feedback" do - it "affiche le formulaire de satisfaction" do - expect(rendered).to have_selector('#user-satisfaction', text: 'Que pensez-vous de la facilité d\'utilisation de ce service ?') - end - end - - context "quand le user a un feedback" do - let(:user) { create(:user, feedbacks: [build(:feedback)]) } - it "n'affiche pas le formulaire de satisfaction" do - expect(rendered).to_not have_selector('#user-satisfaction') - end - end end From 51d6faabeebd4f03635850dbbefeed78051a167a Mon Sep 17 00:00:00 2001 From: Pierre de La Morinerie Date: Thu, 22 Apr 2021 19:23:47 +0200 Subject: [PATCH 3/8] controllers: log invalid tokens in Users::SessionsController This is an attempt to understand why we have so many of these errors in production. --- app/controllers/users/sessions_controller.rb | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/app/controllers/users/sessions_controller.rb b/app/controllers/users/sessions_controller.rb index 5a15d5729..e6190313a 100644 --- a/app/controllers/users/sessions_controller.rb +++ b/app/controllers/users/sessions_controller.rb @@ -77,4 +77,22 @@ class Users::SessionsController < Devise::SessionsController redirect_to link_sent_path(email: instructeur.email) end end + + private + + def handle_unverified_request + log_invalid_authenticity_token_error + super + end + + def log_invalid_authenticity_token_error + Sentry.with_scope do |temp_scope| + tags = { + request_tokens: request_authenticity_tokens.compact.map { |t| t.gsub(/.....$/, '*****') }.join(', '), + session_token: session[:_csrf_token]&.gsub(/.....$/, '*****') + } + temp_scope.set_tags(tags) + Sentry.capture_message("ActionController::InvalidAuthenticityToken in Users::SessionsController") + end + end end From 275b71abaca14337e92029a6c460df545240cb04 Mon Sep 17 00:00:00 2001 From: simon lehericey Date: Fri, 16 Apr 2021 17:17:11 +0200 Subject: [PATCH 4/8] includes champs: :type_de_champ --- app/controllers/instructeurs/dossiers_controller.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/controllers/instructeurs/dossiers_controller.rb b/app/controllers/instructeurs/dossiers_controller.rb index 785382649..37b7e1d4c 100644 --- a/app/controllers/instructeurs/dossiers_controller.rb +++ b/app/controllers/instructeurs/dossiers_controller.rb @@ -233,7 +233,10 @@ module Instructeurs private def dossier - @dossier ||= current_instructeur.dossiers.find(params[:dossier_id]) + @dossier ||= current_instructeur + .dossiers + .includes(champs: :type_de_champ) + .find(params[:dossier_id]) end def generate_pdf_for_instructeur_export From 4a12e973bfde41239279ecc20b86266f94c86917 Mon Sep 17 00:00:00 2001 From: Pierre de La Morinerie Date: Thu, 22 Apr 2021 15:15:14 +0200 Subject: [PATCH 5/8] i18n: add unbreakable spaces to reset_link_sent locales --- config/locales/fr.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 7d61251a6..997db420c 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -58,10 +58,10 @@ fr: reset_link_sent: email_sent_html: "Nous vous avons envoyé un email à l’adresse %{email}." click_link_to_reset_password: "Cliquez sur le lien contenu dans l’email pour changer votre mot de passe." - no_mail: "Vous n’avez pas reçu l’email ?" + no_mail: "Vous n’avez pas reçu l’email ?" check_spams: "Vérifiez la boite Indésirables ou Spam de votre boite email." - check_account: "Avez-vous bien créé un compte %{application_name} avec l’adresse %{email} ? Si aucun compte n’existe avec cette adresse, vous ne recevrez pas de message." - check_france_connect_html: "Vous êtes-vous connecté avec France Connect par le passé ? Dans ce cas essayez à nouveau avec France Connect." + check_account: "Avez-vous bien créé un compte %{application_name} avec l’adresse %{email} ? Si aucun compte n’existe avec cette adresse, vous ne recevrez pas de message." + check_france_connect_html: "Vous êtes-vous connecté avec France Connect par le passé ? Dans ce cas essayez à nouveau avec France Connect." got_it: "Bien reçu !" open_your_mailbox: "Maintenant ouvrez votre boite email." title: "Lien de réinitialisation du mot de passe envoyé" From ac3bc38c7542144a4c004b4f3b314abf1fc53220 Mon Sep 17 00:00:00 2001 From: Pierre de La Morinerie Date: Tue, 27 Apr 2021 14:51:00 +0200 Subject: [PATCH 6/8] views: fix invalid locale name --- app/views/users/sessions/link_sent.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/users/sessions/link_sent.html.haml b/app/views/users/sessions/link_sent.html.haml index 52f463592..1b26f8f9c 100644 --- a/app/views/users/sessions/link_sent.html.haml +++ b/app/views/users/sessions/link_sent.html.haml @@ -11,7 +11,7 @@ %p Ouvrez votre boite email #{@email} puis cliquez sur le lien d’activation du message Connexion sécurisée à #{APPLICATION_NAME}. %p - = t('views.users.shared.email_can_take_a_while') + = t('views.users.shared.email_can_take_a_while_html') %section.link-sent-help %p From 0cb2162a6551d6a4aed7627701d1e6611d402dc7 Mon Sep 17 00:00:00 2001 From: Pierre de La Morinerie Date: Tue, 27 Apr 2021 13:55:12 +0200 Subject: [PATCH 7/8] db: fix out-to-date Procedure comments --- app/models/procedure.rb | 80 ++++++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/app/models/procedure.rb b/app/models/procedure.rb index 40c83c1e6..ea0b4e570 100644 --- a/app/models/procedure.rb +++ b/app/models/procedure.rb @@ -2,47 +2,47 @@ # # Table name: procedures # -# id :integer not null, primary key -# aasm_state :string default("brouillon") -# allow_expert_review :boolean default(TRUE), not null -# api_entreprise_token :string -# ask_birthday :boolean default(FALSE), not null -# auto_archive_on :date -# cadre_juridique :string -# cerfa_flag :boolean default(FALSE) -# cloned_from_library :boolean default(FALSE) -# closed_at :datetime -# declarative_with_state :string -# description :string -# direction :string -# duree_conservation_dossiers_dans_ds :integer -# duree_conservation_dossiers_hors_ds :integer -# durees_conservation_required :boolean default(TRUE) -# euro_flag :boolean default(FALSE) -# for_individual :boolean default(FALSE) +# id :integer not null, primary key +# aasm_state :string default("brouillon") +# allow_expert_review :boolean default(TRUE), not null +# api_entreprise_token :string +# ask_birthday :boolean default(FALSE), not null +# auto_archive_on :date +# cadre_juridique :string +# cerfa_flag :boolean default(FALSE) +# cloned_from_library :boolean default(FALSE) +# closed_at :datetime +# declarative_with_state :string +# description :string +# direction :string +# duree_conservation_dossiers_dans_ds :integer +# duree_conservation_dossiers_hors_ds :integer +# durees_conservation_required :boolean default(TRUE) +# euro_flag :boolean default(FALSE) # experts_require_administrateur_invitation :boolean default(FALSE) -# hidden_at :datetime -# juridique_required :boolean default(TRUE) -# libelle :string -# lien_demarche :string -# lien_notice :string -# lien_site_web :string -# monavis_embed :text -# organisation :string -# path :string not null -# published_at :datetime -# routing_criteria_name :text default("Votre ville") -# test_started_at :datetime -# unpublished_at :datetime -# web_hook_url :string -# whitelisted_at :datetime -# created_at :datetime not null -# updated_at :datetime not null -# canonical_procedure_id :bigint -# draft_revision_id :bigint -# parent_procedure_id :bigint -# published_revision_id :bigint -# service_id :bigint +# for_individual :boolean default(FALSE) +# hidden_at :datetime +# juridique_required :boolean default(TRUE) +# libelle :string +# lien_demarche :string +# lien_notice :string +# lien_site_web :string +# monavis_embed :text +# organisation :string +# path :string not null +# published_at :datetime +# routing_criteria_name :text default("Votre ville") +# test_started_at :datetime +# unpublished_at :datetime +# web_hook_url :string +# whitelisted_at :datetime +# created_at :datetime not null +# updated_at :datetime not null +# canonical_procedure_id :bigint +# draft_revision_id :bigint +# parent_procedure_id :bigint +# published_revision_id :bigint +# service_id :bigint # class Procedure < ApplicationRecord From d024b9ab9e076b008c15b60663cd9a0f3398e150 Mon Sep 17 00:00:00 2001 From: Pierre de La Morinerie Date: Tue, 27 Apr 2021 11:55:39 +0000 Subject: [PATCH 8/8] db: add uniqueness constraints Enforce uniqueness constraints at the database level, on: - Invites, - Procedures, - Individuals. These are the one less likely to have duplicates. A follow-up PR will contains similar migrations, but more likely to have existing duplicates that need to be removed. --- .../20210427120000_add_unique_index_to_invites.rb | 14 ++++++++++++++ ...0210427120001_add_unique_index_to_procedures.rb | 14 ++++++++++++++ ...210427120002_add_unique_index_to_individuals.rb | 14 ++++++++++++++ db/schema.rb | 6 ++++-- 4 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 db/migrate/20210427120000_add_unique_index_to_invites.rb create mode 100644 db/migrate/20210427120001_add_unique_index_to_procedures.rb create mode 100644 db/migrate/20210427120002_add_unique_index_to_individuals.rb diff --git a/db/migrate/20210427120000_add_unique_index_to_invites.rb b/db/migrate/20210427120000_add_unique_index_to_invites.rb new file mode 100644 index 000000000..cd41fb09d --- /dev/null +++ b/db/migrate/20210427120000_add_unique_index_to_invites.rb @@ -0,0 +1,14 @@ +class AddUniqueIndexToInvites < ActiveRecord::Migration[6.1] + include Database::MigrationHelpers + + disable_ddl_transaction! + + def up + delete_duplicates :invites, [:email, :dossier_id] + add_concurrent_index :invites, [:email, :dossier_id], unique: true + end + + def down + remove_index :invites, column: [:email, :dossier_id] + end +end diff --git a/db/migrate/20210427120001_add_unique_index_to_procedures.rb b/db/migrate/20210427120001_add_unique_index_to_procedures.rb new file mode 100644 index 000000000..2ed79ccfe --- /dev/null +++ b/db/migrate/20210427120001_add_unique_index_to_procedures.rb @@ -0,0 +1,14 @@ +class AddUniqueIndexToProcedures < ActiveRecord::Migration[6.1] + include Database::MigrationHelpers + + disable_ddl_transaction! + + def up + delete_duplicates :procedures, [:path, :closed_at, :hidden_at, :unpublished_at] + add_concurrent_index :procedures, [:path, :closed_at, :hidden_at, :unpublished_at], name: 'procedure_path_uniqueness', unique: true + end + + def down + remove_index :procedures, [:path, :closed_at, :hidden_at, :unpublished_at], name: 'procedure_path_uniqueness' + end +end diff --git a/db/migrate/20210427120002_add_unique_index_to_individuals.rb b/db/migrate/20210427120002_add_unique_index_to_individuals.rb new file mode 100644 index 000000000..39dac52ee --- /dev/null +++ b/db/migrate/20210427120002_add_unique_index_to_individuals.rb @@ -0,0 +1,14 @@ +class AddUniqueIndexToIndividuals < ActiveRecord::Migration[6.1] + include Database::MigrationHelpers + + disable_ddl_transaction! + + def up + delete_duplicates :individuals, [:dossier_id] + add_concurrent_index :individuals, [:dossier_id], unique: true + end + + def down + remove_index :individuals, [:dossier_id] + end +end diff --git a/db/schema.rb b/db/schema.rb index fec39e667..956e5a302 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2021_04_22_101149) do +ActiveRecord::Schema.define(version: 2021_04_27_120002) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -446,7 +446,7 @@ ActiveRecord::Schema.define(version: 2021_04_22_101149) do t.datetime "created_at" t.datetime "updated_at" t.date "birthdate" - t.index ["dossier_id"], name: "index_individuals_on_dossier_id" + t.index ["dossier_id"], name: "index_individuals_on_dossier_id", unique: true end create_table "initiated_mails", id: :serial, force: :cascade do |t| @@ -474,6 +474,7 @@ ActiveRecord::Schema.define(version: 2021_04_22_101149) do t.datetime "created_at" t.datetime "updated_at" t.text "message" + t.index ["email", "dossier_id"], name: "index_invites_on_email_and_dossier_id", unique: true end create_table "module_api_cartos", id: :serial, force: :cascade do |t| @@ -559,6 +560,7 @@ ActiveRecord::Schema.define(version: 2021_04_22_101149) do t.index ["draft_revision_id"], name: "index_procedures_on_draft_revision_id" t.index ["hidden_at"], name: "index_procedures_on_hidden_at" t.index ["parent_procedure_id"], name: "index_procedures_on_parent_procedure_id" + t.index ["path", "closed_at", "hidden_at", "unpublished_at"], name: "procedure_path_uniqueness", unique: true t.index ["path", "closed_at", "hidden_at"], name: "index_procedures_on_path_and_closed_at_and_hidden_at", unique: true t.index ["published_revision_id"], name: "index_procedures_on_published_revision_id" t.index ["service_id"], name: "index_procedures_on_service_id"