diff --git a/app/components/groupe_gestionnaire/card/administrateurs_component.rb b/app/components/groupe_gestionnaire/card/administrateurs_component.rb new file mode 100644 index 000000000..73b398fcf --- /dev/null +++ b/app/components/groupe_gestionnaire/card/administrateurs_component.rb @@ -0,0 +1,5 @@ +class GroupeGestionnaire::Card::AdministrateursComponent < ApplicationComponent + def initialize(groupe_gestionnaire:) + @groupe_gestionnaire = groupe_gestionnaire + end +end diff --git a/app/components/groupe_gestionnaire/card/administrateurs_component/administrateurs_component.fr.yml b/app/components/groupe_gestionnaire/card/administrateurs_component/administrateurs_component.fr.yml new file mode 100644 index 000000000..123fa3c98 --- /dev/null +++ b/app/components/groupe_gestionnaire/card/administrateurs_component/administrateurs_component.fr.yml @@ -0,0 +1,5 @@ +--- +fr: + title: + one: Administrateur + other: Administrateurs diff --git a/app/components/groupe_gestionnaire/card/administrateurs_component/administrateurs_component.html.haml b/app/components/groupe_gestionnaire/card/administrateurs_component/administrateurs_component.html.haml new file mode 100644 index 000000000..34864bea5 --- /dev/null +++ b/app/components/groupe_gestionnaire/card/administrateurs_component/administrateurs_component.html.haml @@ -0,0 +1,12 @@ +.fr-col-6.fr-col-md-4.fr-col-lg-3 + = link_to gestionnaire_groupe_gestionnaire_administrateurs_path(@groupe_gestionnaire), id: 'administrateurs', class: 'fr-tile fr-enlarge-link' do + .fr-tile__body.flex.column.align-center.justify-between + %div + %span.icon.accept + %p.fr-tile-status-accept Validé + %div + .line-count.fr-my-1w + %p.fr-tag= @groupe_gestionnaire.administrateurs.size + %h3.fr-h6 + = t('.title', count: @groupe_gestionnaire.administrateurs.size) + %p.fr-btn.fr-btn--tertiary= t('views.shared.actions.edit') diff --git a/app/components/groupe_gestionnaire/groupe_gestionnaire_administrateurs/administrateur_component.rb b/app/components/groupe_gestionnaire/groupe_gestionnaire_administrateurs/administrateur_component.rb new file mode 100644 index 000000000..88cb19a0f --- /dev/null +++ b/app/components/groupe_gestionnaire/groupe_gestionnaire_administrateurs/administrateur_component.rb @@ -0,0 +1,42 @@ +class GroupeGestionnaire::GroupeGestionnaireAdministrateurs::AdministrateurComponent < ApplicationComponent + include ApplicationHelper + + def initialize(groupe_gestionnaire:, administrateur:) + @groupe_gestionnaire = groupe_gestionnaire + @administrateur = administrateur + end + + def email + if @administrateur == current_gestionnaire + "#{@administrateur.email} (C’est vous !)" + else + @administrateur.email + end + end + + def created_at + try_format_datetime(@administrateur.created_at) + end + + def registration_state + @administrateur.registration_state + end + + def remove_button + if is_there_at_least_another_active_admin? + button_to 'Retirer', + gestionnaire_groupe_gestionnaire_administrateur_path(@groupe_gestionnaire, @administrateur), + method: :delete, + class: 'button', + form: { data: { turbo: true, turbo_confirm: "Retirer « #{@administrateur.email} » des administrateurs de « #{@groupe_gestionnaire.name} » ?" } } + end + end + + def is_there_at_least_another_active_admin? + if @administrateur.active? + @groupe_gestionnaire.administrateurs.count(&:active?) > 1 + else + @groupe_gestionnaire.administrateurs.count(&:active?) >= 1 + end + end +end diff --git a/app/components/groupe_gestionnaire/groupe_gestionnaire_administrateurs/administrateur_component/administrateur_component.html.haml b/app/components/groupe_gestionnaire/groupe_gestionnaire_administrateurs/administrateur_component/administrateur_component.html.haml new file mode 100644 index 000000000..9678adb08 --- /dev/null +++ b/app/components/groupe_gestionnaire/groupe_gestionnaire_administrateurs/administrateur_component/administrateur_component.html.haml @@ -0,0 +1,5 @@ +%tr{ id: dom_id(@administrateur) } + %td= email + %td= created_at + %td= registration_state + %td= remove_button diff --git a/app/controllers/gestionnaires/groupe_gestionnaire_administrateurs_controller.rb b/app/controllers/gestionnaires/groupe_gestionnaire_administrateurs_controller.rb new file mode 100644 index 000000000..f45f7220b --- /dev/null +++ b/app/controllers/gestionnaires/groupe_gestionnaire_administrateurs_controller.rb @@ -0,0 +1,17 @@ +module Gestionnaires + class GroupeGestionnaireAdministrateursController < GestionnaireController + before_action :retrieve_groupe_gestionnaire, except: [:new] + + def index + end + + def create + administrateurs, flash[:alert], flash[:notice] = @groupe_gestionnaire.add_administrateurs(emails: [params.require(:administrateur)[:email]], current_user: current_gestionnaire) + @administrateur = administrateurs[0] + end + + def destroy + @administrateur, flash[:alert], flash[:notice] = @groupe_gestionnaire.remove_administrateur(params[:id], current_gestionnaire) + end + end +end diff --git a/app/controllers/gestionnaires/groupe_gestionnaire_gestionnaires_controller.rb b/app/controllers/gestionnaires/groupe_gestionnaire_gestionnaires_controller.rb index 7934ba026..d19366c35 100644 --- a/app/controllers/gestionnaires/groupe_gestionnaire_gestionnaires_controller.rb +++ b/app/controllers/gestionnaires/groupe_gestionnaire_gestionnaires_controller.rb @@ -11,7 +11,7 @@ module Gestionnaires end def destroy - @gestionnaire, flash[:alert], flash[:notice] = @groupe_gestionnaire.remove(params[:id], current_gestionnaire) + @gestionnaire, flash[:alert], flash[:notice] = @groupe_gestionnaire.remove_gestionnaire(params[:id], current_gestionnaire) end end end diff --git a/app/controllers/manager/groupe_gestionnaires_controller.rb b/app/controllers/manager/groupe_gestionnaires_controller.rb index 7de374dde..3f41d93c5 100644 --- a/app/controllers/manager/groupe_gestionnaires_controller.rb +++ b/app/controllers/manager/groupe_gestionnaires_controller.rb @@ -7,7 +7,7 @@ module Manager end def remove_gestionnaire - _gestionnaire, flash[:alert], flash[:notice] = groupe_gestionnaire.remove(gestionnaire_id, current_super_admin) + _gestionnaire, flash[:alert], flash[:notice] = groupe_gestionnaire.remove_gestionnaire(gestionnaire_id, current_super_admin) redirect_to manager_groupe_gestionnaire_path(groupe_gestionnaire) end diff --git a/app/mailers/groupe_gestionnaire_mailer.rb b/app/mailers/groupe_gestionnaire_mailer.rb index 6717d6da4..ef1dcac23 100644 --- a/app/mailers/groupe_gestionnaire_mailer.rb +++ b/app/mailers/groupe_gestionnaire_mailer.rb @@ -1,12 +1,12 @@ class GroupeGestionnaireMailer < ApplicationMailer layout 'mailers/layout' - def notify_removed_gestionnaire(groupe_gestionnaire, removed_gestionnaire, current_super_admin_email) + def notify_removed_gestionnaire(groupe_gestionnaire, removed_gestionnaire_email, current_super_admin_email) @groupe_gestionnaire = groupe_gestionnaire @current_super_admin_email = current_super_admin_email subject = "Vous avez été retiré(e) du groupe gestionnaire \"#{groupe_gestionnaire.name}\"" - mail(to: removed_gestionnaire.email, subject: subject) + mail(to: removed_gestionnaire_email, subject: subject) end def notify_added_gestionnaires(groupe_gestionnaire, added_gestionnaires, current_super_admin_email) @@ -19,6 +19,24 @@ class GroupeGestionnaireMailer < ApplicationMailer mail(bcc: added_gestionnaire_emails, subject: subject) end + def notify_removed_administrateur(groupe_gestionnaire, removed_administrateur_email, current_super_admin_email) + @groupe_gestionnaire = groupe_gestionnaire + @current_super_admin_email = current_super_admin_email + subject = "Vous avez été retiré(e) du groupe gestionnaire \"#{groupe_gestionnaire.name}\"" + + mail(to: removed_administrateur_email, subject: subject) + end + + def notify_added_administrateurs(groupe_gestionnaire, added_administrateurs, current_super_admin_email) + added_administrateur_emails = added_administrateurs.map(&:email) + @groupe_gestionnaire = groupe_gestionnaire + @current_super_admin_email = current_super_admin_email + + subject = "Vous avez été ajouté(e) en tant qu'administrateur du groupe gestionnaire \"#{groupe_gestionnaire.name}\"" + + mail(bcc: added_administrateur_emails, subject: subject) + end + def self.critical_email?(action_name) false end diff --git a/app/models/administrateur.rb b/app/models/administrateur.rb index e0af22bbc..ab8c30aed 100644 --- a/app/models/administrateur.rb +++ b/app/models/administrateur.rb @@ -1,4 +1,5 @@ class Administrateur < ApplicationRecord + include UserFindByConcern UNUSED_ADMIN_THRESHOLD = ENV.fetch('UNUSED_ADMIN_THRESHOLD') { 6 }.to_i.months has_and_belongs_to_many :instructeurs @@ -27,10 +28,6 @@ class Administrateur < ApplicationRecord .merge(APIToken.where(last_v2_authenticated_at: nil).or(APIToken.where(last_v2_authenticated_at: ..UNUSED_ADMIN_THRESHOLD.ago))) end - def self.by_email(email) - Administrateur.find_by(users: { email: email }) - end - def email user&.email end diff --git a/app/models/concerns/user_find_by_concern.rb b/app/models/concerns/user_find_by_concern.rb new file mode 100644 index 000000000..cb52c3b4e --- /dev/null +++ b/app/models/concerns/user_find_by_concern.rb @@ -0,0 +1,27 @@ +# Request a watermark on files attached to a `Champs::TitreIdentiteChamp`. +# +# We're using a class extension here, but we could as well have a periodic +# job that watermarks relevant attachments. +module UserFindByConcern + extend ActiveSupport::Concern + + included do + def self.by_email(email) + find_by(users: { email: email }) + end + + def self.find_all_by_identifier(ids: [], emails: []) + find_all_by_identifier_with_emails(ids:, emails:).first + end + + def self.find_all_by_identifier_with_emails(ids: [], emails: []) + valid_emails, invalid_emails = emails.partition { URI::MailTo::EMAIL_REGEXP.match?(_1) } + + [ + where(id: ids).or(where(users: { email: valid_emails })).distinct(:id), + valid_emails, + invalid_emails + ] + end + end +end diff --git a/app/models/gestionnaire.rb b/app/models/gestionnaire.rb index ed08f4ef1..6fa109540 100644 --- a/app/models/gestionnaire.rb +++ b/app/models/gestionnaire.rb @@ -1,4 +1,5 @@ class Gestionnaire < ApplicationRecord + include UserFindByConcern has_and_belongs_to_many :groupe_gestionnaires belongs_to :user @@ -7,10 +8,6 @@ class Gestionnaire < ApplicationRecord default_scope { eager_load(:user) } - def self.by_email(email) - find_by(users: { email: email }) - end - def email user&.email end @@ -19,20 +16,6 @@ class Gestionnaire < ApplicationRecord user&.active? end - def self.find_all_by_identifier(ids: [], emails: []) - find_all_by_identifier_with_emails(ids:, emails:).first - end - - def self.find_all_by_identifier_with_emails(ids: [], emails: []) - valid_emails, invalid_emails = emails.partition { URI::MailTo::EMAIL_REGEXP.match?(_1) } - - [ - where(id: ids).or(where(users: { email: valid_emails })).distinct(:id), - valid_emails, - invalid_emails - ] - end - def can_be_deleted? groupe_gestionnaires.roots.each do |rt| return false unless rt.gestionnaires.size > 1 diff --git a/app/models/groupe_gestionnaire.rb b/app/models/groupe_gestionnaire.rb index fe7f3e9a0..57c8d40e5 100644 --- a/app/models/groupe_gestionnaire.rb +++ b/app/models/groupe_gestionnaire.rb @@ -4,28 +4,28 @@ class GroupeGestionnaire < ApplicationRecord has_ancestry - def add(gestionnaire) + def add_gestionnaire(gestionnaire) return if gestionnaire.nil? return if in?(gestionnaire.groupe_gestionnaires) gestionnaires << gestionnaire end - def remove(gestionnaire_id, current_user) + def remove_gestionnaire(gestionnaire_id, current_user) if !self.is_root? || self.gestionnaires.one? alert = "Suppression impossible : il doit y avoir au moins un gestionnaire dans le groupe racine" else gestionnaire = Gestionnaire.find(gestionnaire_id) - if gestionnaire.nil? || !in?(gestionnaire.groupe_gestionnaires) || !gestionnaire.groupe_gestionnaires.destroy(self) - alert = "Le gestionnaire « #{gestionnaire.email} » n’est pas dans le groupe." + if !in?(gestionnaire.groupe_gestionnaires) || !gestionnaire.groupe_gestionnaires.destroy(self) + alert = "Le gestionnaire « #{gestionnaire.email} » n’est pas dans le groupe gestionnaire." else if gestionnaire.groupe_gestionnaires.empty? gestionnaire.destroy end - notice = "Le gestionnaire « #{gestionnaire.email} » a été retiré du groupe." + notice = "Le gestionnaire « #{gestionnaire.email} » a été retiré du groupe gestionnaire." GroupeGestionnaireMailer - .notify_removed_gestionnaire(self, gestionnaire, current_user.email) + .notify_removed_gestionnaire(self, gestionnaire.email, current_user.email) .deliver_later end end @@ -51,7 +51,7 @@ class GroupeGestionnaire < ApplicationRecord # We dont't want to assign a user to an groupe_gestionnaire if they are already assigned to it gestionnaires_duplicate = gestionnaires_to_add & gestionnaires gestionnaires_to_add -= gestionnaires - gestionnaires_to_add.each { add(_1) } + gestionnaires_to_add.each { add_gestionnaire(_1) } if invalid_emails.present? alert = I18n.t('activerecord.wrong_address', @@ -75,6 +75,83 @@ class GroupeGestionnaire < ApplicationRecord [gestionnaires_to_add, alert, notice] end + def add_administrateur(administrateur) + return if administrateur.nil? + return if id == administrateur.groupe_gestionnaire_id + + administrateurs << administrateur + end + + def remove_administrateur(administrateur_id, current_user) + administrateur = Administrateur.find(administrateur_id) + + if id != administrateur.groupe_gestionnaire_id + alert = "L'administrateur « #{administrateur.email} » n’est pas dans le groupe gestionnaire." + else + administrateur.destroy + notice = "L'administrateur « #{administrateur.email} » a été retiré du groupe gestionnaire." + GroupeGestionnaireMailer + .notify_removed_administrateur(self, administrateur.email, current_user.email) + .deliver_later + end + [administrateur, alert, notice] + end + + def add_administrateurs(ids: [], emails: [], current_user: nil) + emails = emails.to_json + emails = JSON.parse(emails).map { EmailSanitizableConcern::EmailSanitizer.sanitize(_1) } + + administrateurs_to_add, valid_emails, invalid_emails = Administrateur.find_all_by_identifier_with_emails(ids:, emails:) + not_found_emails = valid_emails - administrateurs_to_add.map(&:email) + + # Send invitations to users without account + if not_found_emails.present? + administrateurs_to_add += not_found_emails.map do |email| + user = User.create_or_promote_to_administrateur(email, SecureRandom.hex) + user.invite_administrateur!(self) + user.administrateur + end + end + administrateurs_already_in_groupe_gestionnaire = [] + # We dont't want to assign a user to an groupe_gestionnaire if they are already assigned to it + administrateurs_duplicate = administrateurs_to_add & administrateurs + administrateurs_to_add -= administrateurs + administrateurs_to_add.each do |administrateur| + if !current_user.is_a?(SuperAdmin) && administrateur.groupe_gestionnaire_id && ((administrateur.groupe_gestionnaire.ancestor_ids + [administrateur.groupe_gestionnaire_id]) & current_user.groupe_gestionnaire_ids).empty? + administrateurs_already_in_groupe_gestionnaire << administrateur + next + end + add_administrateur(administrateur) + end + + if administrateurs_already_in_groupe_gestionnaire.present? + alert = I18n.t('activerecord.errors.administrateurs_already_in_groupe_gestionnaire', + count: administrateurs_already_in_groupe_gestionnaire.size, + emails: administrateurs_already_in_groupe_gestionnaire) + end + + if invalid_emails.present? + alert = I18n.t('activerecord.wrong_address', + count: invalid_emails.size, + emails: invalid_emails) + end + if administrateurs_duplicate.present? + alert = I18n.t('activerecord.errors.duplicate_email', + count: invalid_emails.size, + emails: administrateurs_duplicate.map(&:email)) + end + + if administrateurs_to_add.present? + notice = "Les administrateurs ont bien été affectés au groupe gestionnaire" + + GroupeGestionnaireMailer + .notify_added_administrateurs(self, administrateurs_to_add, current_user.email) + .deliver_later + end + + [administrateurs_to_add, alert, notice] + end + def can_be_deleted?(current_user) (gestionnaires.empty? || (gestionnaires == [current_user])) && administrateurs.empty? && children.empty? end diff --git a/app/models/instructeur.rb b/app/models/instructeur.rb index a856c203a..1833b48cc 100644 --- a/app/models/instructeur.rb +++ b/app/models/instructeur.rb @@ -1,4 +1,5 @@ class Instructeur < ApplicationRecord + include UserFindByConcern has_and_belongs_to_many :administrateurs has_many :assign_to, dependent: :destroy @@ -36,24 +37,6 @@ class Instructeur < ApplicationRecord default_scope { eager_load(:user) } - def self.by_email(email) - find_by(users: { email: email }) - end - - def self.find_all_by_identifier(ids: [], emails: []) - find_all_by_identifier_with_emails(ids:, emails:).first - end - - def self.find_all_by_identifier_with_emails(ids: [], emails: []) - valid_emails, invalid_emails = emails.partition { URI::MailTo::EMAIL_REGEXP.match?(_1) } - - [ - where(id: ids).or(where(users: { email: valid_emails })).distinct(:id), - valid_emails, - invalid_emails - ] - end - def email user.email end diff --git a/app/views/gestionnaires/groupe_gestionnaire_administrateurs/_add_admin_form.html.haml b/app/views/gestionnaires/groupe_gestionnaire_administrateurs/_add_admin_form.html.haml new file mode 100644 index 000000000..6852a13e5 --- /dev/null +++ b/app/views/gestionnaires/groupe_gestionnaire_administrateurs/_add_admin_form.html.haml @@ -0,0 +1,13 @@ += form_for groupe_gestionnaire.administrateurs.new(user: User.new), + url: { controller: 'groupe_gestionnaire_administrateurs' }, + html: { id: "new_administrateur" }, + data: { turbo: true, turbo_force: :server } do |f| + .fr-input-group + = f.label :email, class: "fr-label" do + Ajouter un administrateur + %span.fr-hint-text + = "Renseignez l’email d’un administrateur pour lui permettre de gérer le groupe « #{groupe_gestionnaire.name} ». Exemple : marie.dupont@exemple.fr" + + = f.email_field :email, required: true, class: "fr-input", autofocus: true + + = f.submit 'Ajouter comme administrateur', class: 'fr-btn' diff --git a/app/views/gestionnaires/groupe_gestionnaire_administrateurs/create.turbo_stream.haml b/app/views/gestionnaires/groupe_gestionnaire_administrateurs/create.turbo_stream.haml new file mode 100644 index 000000000..1adda3a89 --- /dev/null +++ b/app/views/gestionnaires/groupe_gestionnaire_administrateurs/create.turbo_stream.haml @@ -0,0 +1,5 @@ +- if @administrateur.present? + = turbo_stream.update 'administrateurs' do + = render GroupeGestionnaire::GroupeGestionnaireAdministrateurs::AdministrateurComponent.with_collection(@groupe_gestionnaire.administrateurs.order('users.email'), groupe_gestionnaire: @groupe_gestionnaire) + = turbo_stream.replace "new_administrateur", partial: 'add_admin_form', locals: { groupe_gestionnaire: @groupe_gestionnaire } + = turbo_stream.focus 'administrateur_email' diff --git a/app/views/gestionnaires/groupe_gestionnaire_administrateurs/destroy.turbo_stream.haml b/app/views/gestionnaires/groupe_gestionnaire_administrateurs/destroy.turbo_stream.haml new file mode 100644 index 000000000..b72211119 --- /dev/null +++ b/app/views/gestionnaires/groupe_gestionnaire_administrateurs/destroy.turbo_stream.haml @@ -0,0 +1,6 @@ += turbo_stream.update 'administrateurs' do + = render GroupeGestionnaire::GroupeGestionnaireAdministrateurs::AdministrateurComponent.with_collection(@groupe_gestionnaire.administrateurs.order('users.email'), groupe_gestionnaire: @groupe_gestionnaire) +- if @groupe_gestionnaire.administrateurs.one? + = turbo_stream.focus 'administrateur_email' +- else + = turbo_stream.focus_all '#administrateurs tr:first-child input[type="submit"]' diff --git a/app/views/gestionnaires/groupe_gestionnaire_administrateurs/index.html.haml b/app/views/gestionnaires/groupe_gestionnaire_administrateurs/index.html.haml new file mode 100644 index 000000000..f2796c906 --- /dev/null +++ b/app/views/gestionnaires/groupe_gestionnaire_administrateurs/index.html.haml @@ -0,0 +1,20 @@ += render partial: 'gestionnaires/breadcrumbs', + locals: { steps: [['Groupes gestionnaire', gestionnaire_groupe_gestionnaires_path], + ["#{@groupe_gestionnaire.name.truncate_words(10)}", gestionnaire_groupe_gestionnaire_path(@groupe_gestionnaire)], + ['Administrateurs']], preview: false } + +.container + %h1 Gérer les administrateurs de « #{@groupe_gestionnaire.name} » + + %table.table + %thead + %tr + %th= 'Adresse email' + %th= 'Enregistré le' + %th= 'État' + %th + %tbody#administrateurs + = render(GroupeGestionnaire::GroupeGestionnaireAdministrateurs::AdministrateurComponent.with_collection(@groupe_gestionnaire.administrateurs.order('users.email'), groupe_gestionnaire: @groupe_gestionnaire)) + + .fr-mt-4w + = render 'add_admin_form', groupe_gestionnaire: @groupe_gestionnaire diff --git a/app/views/gestionnaires/groupe_gestionnaires/show.html.haml b/app/views/gestionnaires/groupe_gestionnaires/show.html.haml index 124d12dd8..88b26e3bf 100644 --- a/app/views/gestionnaires/groupe_gestionnaires/show.html.haml +++ b/app/views/gestionnaires/groupe_gestionnaires/show.html.haml @@ -18,4 +18,5 @@ %a{ href: gestionnaire_groupe_gestionnaire_path(@groupe_gestionnaire.groupe_gestionnaire) }= @groupe_gestionnaire.groupe_gestionnaire.name .fr-grid-row.fr-grid-row--gutters.fr-mb-5w = render GroupeGestionnaire::Card::GestionnairesComponent.new(groupe_gestionnaire: @groupe_gestionnaire) + = render GroupeGestionnaire::Card::AdministrateursComponent.new(groupe_gestionnaire: @groupe_gestionnaire) = render GroupeGestionnaire::Card::ChildrenComponent.new(groupe_gestionnaire: @groupe_gestionnaire) diff --git a/app/views/groupe_gestionnaire_mailer/notify_added_administrateurs.html.haml b/app/views/groupe_gestionnaire_mailer/notify_added_administrateurs.html.haml new file mode 100644 index 000000000..94f82e361 --- /dev/null +++ b/app/views/groupe_gestionnaire_mailer/notify_added_administrateurs.html.haml @@ -0,0 +1,6 @@ +%p= t(:hello, scope: [:views, :shared, :greetings]) + +%p + = t(".email_body", groupe_gestionnaire_name: @groupe_gestionnaire.name, email: @current_super_admin_email, application_name: APPLICATION_NAME) + += render partial: "layouts/mailers/signature" diff --git a/app/views/groupe_gestionnaire_mailer/notify_removed_administrateur.html.haml b/app/views/groupe_gestionnaire_mailer/notify_removed_administrateur.html.haml new file mode 100644 index 000000000..94f82e361 --- /dev/null +++ b/app/views/groupe_gestionnaire_mailer/notify_removed_administrateur.html.haml @@ -0,0 +1,6 @@ +%p= t(:hello, scope: [:views, :shared, :greetings]) + +%p + = t(".email_body", groupe_gestionnaire_name: @groupe_gestionnaire.name, email: @current_super_admin_email, application_name: APPLICATION_NAME) + += render partial: "layouts/mailers/signature" diff --git a/config/locales/models/groupe_gestionnaire/fr.yml b/config/locales/models/groupe_gestionnaire/fr.yml index cd0300184..211b5bb2e 100644 --- a/config/locales/models/groupe_gestionnaire/fr.yml +++ b/config/locales/models/groupe_gestionnaire/fr.yml @@ -11,6 +11,9 @@ fr: duplicate_email: one: "%{emails} est déjà gestionnaire de ce groupe" other: "%{emails} sont déjà gestionnaires de ce groupe" + administrateurs_already_in_groupe_gestionnaire: + one: Cet administrateur est déjà dans un groupe gestionnaire dont vous n'avez pas la gestion. + other: Ces administrateurs sont déjà dans un groupe gestionnaire dont vous n'avez pas la gestion. wrong_address: one: "%{emails} n’est pas une adresse email valide" other: "%{emails} ne sont pas des adresses emails valides" diff --git a/config/locales/views/groupe_gestionnaire_mailer/notify_added_administrateurs/en.yml b/config/locales/views/groupe_gestionnaire_mailer/notify_added_administrateurs/en.yml new file mode 100644 index 000000000..7731e844d --- /dev/null +++ b/config/locales/views/groupe_gestionnaire_mailer/notify_added_administrateurs/en.yml @@ -0,0 +1,4 @@ +en: + groupe_gestionnaire_mailer: + notify_added_administrateurs: + email_body: "You were assigned as administrateur on the admins group %{groupe_gestionnaire_name} on %{application_name} by « %{email} »" diff --git a/config/locales/views/groupe_gestionnaire_mailer/notify_added_administrateurs/fr.yml b/config/locales/views/groupe_gestionnaire_mailer/notify_added_administrateurs/fr.yml new file mode 100644 index 000000000..227073a70 --- /dev/null +++ b/config/locales/views/groupe_gestionnaire_mailer/notify_added_administrateurs/fr.yml @@ -0,0 +1,4 @@ +fr: + groupe_gestionnaire_mailer: + notify_added_administrateurs: + email_body: "Vous venez d’être nommé administrateur du groupe gestionnaire %{groupe_gestionnaire_name} sur %{application_name} par « %{email} »." diff --git a/config/locales/views/groupe_gestionnaire_mailer/notify_removed_administrateur/en.yml b/config/locales/views/groupe_gestionnaire_mailer/notify_removed_administrateur/en.yml new file mode 100644 index 000000000..f30cf2787 --- /dev/null +++ b/config/locales/views/groupe_gestionnaire_mailer/notify_removed_administrateur/en.yml @@ -0,0 +1,4 @@ +en: + groupe_gestionnaire_mailer: + notify_removed_administrateur: + email_body: "You were removed from the admins group %{groupe_gestionnaire_name} on %{application_name} by « %{email} »" diff --git a/config/locales/views/groupe_gestionnaire_mailer/notify_removed_administrateur/fr.yml b/config/locales/views/groupe_gestionnaire_mailer/notify_removed_administrateur/fr.yml new file mode 100644 index 000000000..32eeda1cb --- /dev/null +++ b/config/locales/views/groupe_gestionnaire_mailer/notify_removed_administrateur/fr.yml @@ -0,0 +1,4 @@ +fr: + groupe_gestionnaire_mailer: + notify_removed_administrateur: + email_body: "Vous venez d’être supprimé(e) du groupe gestionnaire %{groupe_gestionnaire_name} sur %{application_name} par « %{email} »." diff --git a/config/locales/views/groupe_gestionnaire_mailer/notify_removed_gestionnaire/fr.yml b/config/locales/views/groupe_gestionnaire_mailer/notify_removed_gestionnaire/fr.yml index 927149552..4b8154bd9 100644 --- a/config/locales/views/groupe_gestionnaire_mailer/notify_removed_gestionnaire/fr.yml +++ b/config/locales/views/groupe_gestionnaire_mailer/notify_removed_gestionnaire/fr.yml @@ -1,4 +1,4 @@ fr: groupe_gestionnaire_mailer: notify_removed_gestionnaire: - email_body: "Vous venez d’être supprimé(e) du groupe %{groupe_gestionnaire_name} sur %{application_name} par « %{email} »." + email_body: "Vous venez d’être supprimé(e) du groupe gestionnaire %{groupe_gestionnaire_name} sur %{application_name} par « %{email} »." diff --git a/config/routes.rb b/config/routes.rb index a63ce70f1..82e55eec0 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -506,6 +506,7 @@ Rails.application.routes.draw do scope module: 'gestionnaires', as: 'gestionnaire' do resources :groupe_gestionnaires, path: 'groupes', only: [:index, :show, :create, :edit, :update, :destroy] do resources :gestionnaires, controller: 'groupe_gestionnaire_gestionnaires', only: [:index, :create, :destroy] + resources :administrateurs, controller: 'groupe_gestionnaire_administrateurs', only: [:index, :create, :destroy] resources :children, controller: 'groupe_gestionnaire_children', only: [:index, :create, :destroy] end end diff --git a/spec/controllers/gestionnaires/groupe_gestionnaire_administrateurs_controller_spec.rb b/spec/controllers/gestionnaires/groupe_gestionnaire_administrateurs_controller_spec.rb new file mode 100644 index 000000000..55f034f65 --- /dev/null +++ b/spec/controllers/gestionnaires/groupe_gestionnaire_administrateurs_controller_spec.rb @@ -0,0 +1,49 @@ +describe Gestionnaires::GroupeGestionnaireAdministrateursController, type: :controller do + let(:gestionnaire) { create(:gestionnaire).tap { _1.user.update(last_sign_in_at: Time.zone.now) } } + let(:groupe_gestionnaire) { create(:groupe_gestionnaire, gestionnaires: [gestionnaire]) } + + before { sign_in gestionnaire.user } + + describe '#create' do + before do + post :create, + params: { + groupe_gestionnaire_id: groupe_gestionnaire.id, + administrateur: { email: new_administrateur_email } + }, + format: :turbo_stream + end + + context 'of a new administrateur' do + let(:new_administrateur_email) { 'new_administrateur@mail.com' } + + it { expect(groupe_gestionnaire.reload.administrateurs.map(&:email)).to include(new_administrateur_email) } + it { expect(flash.notice).to eq("Les administrateurs ont bien été affectés au groupe gestionnaire") } + end + end + + describe '#destroy' do + let(:gestionnaire) { create(:gestionnaire) } + let(:new_administrateur) { create(:administrateur) } + + before do + groupe_gestionnaire.administrateurs << new_administrateur + end + + def remove_administrateur(administrateur) + delete :destroy, + params: { + groupe_gestionnaire_id: groupe_gestionnaire.id, + id: administrateur.id + }, + format: :turbo_stream + end + + context 'when there are many administrateurs' do + before { remove_administrateur(new_administrateur) } + + it { expect(groupe_gestionnaire.reload.administrateurs.count).to eq(0) } + it { expect(flash.notice).to eq("L'administrateur « #{new_administrateur.email} » a été retiré du groupe gestionnaire.") } + end + end +end diff --git a/spec/controllers/gestionnaires/groupe_gestionnaire_gestionnaires_controller_spec.rb b/spec/controllers/gestionnaires/groupe_gestionnaire_gestionnaires_controller_spec.rb index f85a35fd5..bad6298e9 100644 --- a/spec/controllers/gestionnaires/groupe_gestionnaire_gestionnaires_controller_spec.rb +++ b/spec/controllers/gestionnaires/groupe_gestionnaire_gestionnaires_controller_spec.rb @@ -44,7 +44,7 @@ describe Gestionnaires::GroupeGestionnaireGestionnairesController, type: :contro it { expect(groupe_gestionnaire.gestionnaires).to include(gestionnaire) } it { expect(groupe_gestionnaire.reload.gestionnaires.count).to eq(1) } - it { expect(flash.notice).to eq("Le gestionnaire « #{new_gestionnaire.email} » a été retiré du groupe.") } + it { expect(flash.notice).to eq("Le gestionnaire « #{new_gestionnaire.email} » a été retiré du groupe gestionnaire.") } end context 'when there is only one gestionnaire' do diff --git a/spec/mailers/groupe_gestionnaire_mailer_spec.rb b/spec/mailers/groupe_gestionnaire_mailer_spec.rb index 1ce585f54..5fae5ce86 100644 --- a/spec/mailers/groupe_gestionnaire_mailer_spec.rb +++ b/spec/mailers/groupe_gestionnaire_mailer_spec.rb @@ -6,9 +6,9 @@ RSpec.describe GroupeGestionnaireMailer, type: :mailer do let(:current_super_admin_email) { 'toto@email.com' } - subject { described_class.notify_removed_gestionnaire(groupe_gestionnaire, gestionnaire_to_remove, current_super_admin_email) } + subject { described_class.notify_removed_gestionnaire(groupe_gestionnaire, gestionnaire_to_remove.email, current_super_admin_email) } - it { expect(subject.body).to include('Vous venez d’être supprimé(e) du groupe') } + it { expect(subject.body).to include('Vous venez d’être supprimé(e) du groupe gestionnaire') } it { expect(subject.to).to match_array(['int3@g']) } end @@ -21,9 +21,37 @@ RSpec.describe GroupeGestionnaireMailer, type: :mailer do subject { described_class.notify_added_gestionnaires(groupe_gestionnaire, gestionnaires_to_add, current_super_admin_email) } - before { gestionnaires_to_add.each { groupe_gestionnaire.add(_1) } } + before { gestionnaires_to_add.each { groupe_gestionnaire.add_gestionnaire(_1) } } it { expect(subject.body).to include('Vous venez d’être nommé gestionnaire du groupe gestionnaire') } it { expect(subject.bcc).to match_array(['int3@g', 'int4@g']) } end + + describe '#notify_removed_administrateur' do + let(:groupe_gestionnaire) { create(:groupe_gestionnaire) } + + let(:administrateur_to_remove) { create(:administrateur, email: 'int3@g') } + + let(:current_super_admin_email) { 'toto@email.com' } + + subject { described_class.notify_removed_administrateur(groupe_gestionnaire, administrateur_to_remove.email, current_super_admin_email) } + + it { expect(subject.body).to include('Vous venez d’être supprimé(e) du groupe gestionnaire') } + it { expect(subject.to).to match_array(['int3@g']) } + end + + describe '#notify_added_administrateurs' do + let(:groupe_gestionnaire) { create(:groupe_gestionnaire) } + + let(:administrateurs_to_add) { [create(:administrateur, email: 'int3@g'), create(:administrateur, email: 'int4@g')] } + + let(:current_super_admin_email) { 'toto@email.com' } + + subject { described_class.notify_added_administrateurs(groupe_gestionnaire, administrateurs_to_add, current_super_admin_email) } + + before { administrateurs_to_add.each { groupe_gestionnaire.add_administrateur(_1) } } + + it { expect(subject.body).to include('Vous venez d’être nommé administrateur du groupe gestionnaire') } + it { expect(subject.bcc).to match_array(['int3@g', 'int4@g']) } + end end diff --git a/spec/mailers/previews/groupe_gestionnaire_mailer_preview.rb b/spec/mailers/previews/groupe_gestionnaire_mailer_preview.rb index 53ce5f929..a5f6d5137 100644 --- a/spec/mailers/previews/groupe_gestionnaire_mailer_preview.rb +++ b/spec/mailers/previews/groupe_gestionnaire_mailer_preview.rb @@ -1,18 +1,32 @@ class GroupeGestionnaireMailerPreview < ActionMailer::Preview def notify_removed_gestionnaire - groupe_gestionnaire = GroupeGestionnaire.new(name: 'un groupe d\'admin') + groupe_gestionnaire = GroupeGestionnaire.new(name: 'un groupe gestionnaire') current_super_admin_email = 'admin@dgfip.com' gestionnaire = Gestionnaire.new(user: user) - GroupeGestionnaireMailer.notify_removed_gestionnaire(groupe_gestionnaire, gestionnaire, current_super_admin_email) + GroupeGestionnaireMailer.notify_removed_gestionnaire(groupe_gestionnaire, gestionnaire.email, current_super_admin_email) end def notify_added_gestionnaires - groupe_gestionnaire = GroupeGestionnaire.new(name: 'un groupe d\'admin') + groupe_gestionnaire = GroupeGestionnaire.new(name: 'un groupe gestionnaire') current_super_admin_email = 'admin@dgfip.com' gestionnaires = [Gestionnaire.new(user: user)] GroupeGestionnaireMailer.notify_added_gestionnaires(groupe_gestionnaire, gestionnaires, current_super_admin_email) end + def notify_removed_administrateur + groupe_gestionnaire = GroupeGestionnaire.new(name: 'un groupe gestionnaire') + current_super_admin_email = 'admin@dgfip.com' + administrateur = Administrateur.new(user: user) + GroupeGestionnaireMailer.notify_removed_administrateur(groupe_gestionnaire, administrateur.email, current_super_admin_email) + end + + def notify_added_administrateurs + groupe_gestionnaire = GroupeGestionnaire.new(name: 'un groupe gestionnaire') + current_super_admin_email = 'admin@dgfip.com' + administrateurs = [Administrateur.new(user: user)] + GroupeGestionnaireMailer.notify_added_administrateurs(groupe_gestionnaire, administrateurs, current_super_admin_email) + end + private def user diff --git a/spec/models/groupe_gestionnaire_spec.rb b/spec/models/groupe_gestionnaire_spec.rb index 365773d63..d3f29b7c4 100644 --- a/spec/models/groupe_gestionnaire_spec.rb +++ b/spec/models/groupe_gestionnaire_spec.rb @@ -4,11 +4,11 @@ describe GroupeGestionnaire, type: :model do it { is_expected.to have_and_belong_to_many(:gestionnaires) } end - describe "#add" do + describe "#add_gestionnaire" do let(:groupe_gestionnaire) { create(:groupe_gestionnaire) } let(:gestionnaire) { create(:gestionnaire) } - subject { groupe_gestionnaire.add(gestionnaire) } + subject { groupe_gestionnaire.add_gestionnaire(gestionnaire) } it 'adds the gestionnaire to the groupe gestionnaire' do subject @@ -37,25 +37,59 @@ describe GroupeGestionnaire, type: :model do end end - describe "#remove" do + describe "#add_administrateur" do + let(:groupe_gestionnaire) { create(:groupe_gestionnaire) } let(:gestionnaire) { create(:gestionnaire) } - let(:gestionnaire_to_remove) { create(:gestionnaire) } - let(:groupe_gestionnaire) { create(:groupe_gestionnaire, gestionnaires: [gestionnaire, gestionnaire_to_remove]) } + let(:administrateur) { create(:administrateur) } - it 'removes the gestionnaire by id' do - expect(groupe_gestionnaire.reload.gestionnaires.size).to eq(2) - groupe_gestionnaire.remove(gestionnaire_to_remove.id, gestionnaire) - expect(groupe_gestionnaire.reload.gestionnaires).not_to include(gestionnaire_to_remove) - expect(groupe_gestionnaire.reload.gestionnaires.size).to eq(1) + subject { groupe_gestionnaire.add_administrateur(administrateur) } + + it 'adds the administrateur to the groupe gestionnaire' do + subject + expect(groupe_gestionnaire.reload.administrateurs).to include(administrateur) + end + end + + describe "#add_administrateurs" do + let(:gestionnaire) { create(:gestionnaire) } + let(:groupe_gestionnaire) { create(:groupe_gestionnaire, gestionnaires: [gestionnaire]) } + let(:administrateur) { create(:administrateur) } + + it 'adds the administrateur by id' do + groupe_gestionnaire.add_administrateurs(ids: [administrateur.id], current_user: gestionnaire) + expect(groupe_gestionnaire.reload.administrateurs).to include(administrateur) end - it 'does not remove the gestionnaire if last' do - expect(groupe_gestionnaire.reload.gestionnaires.size).to eq(2) - groupe_gestionnaire.remove(gestionnaire.id, gestionnaire) - expect(groupe_gestionnaire.reload.gestionnaires.size).to eq(1) - groupe_gestionnaire.remove(gestionnaire_to_remove.id, gestionnaire) - expect(groupe_gestionnaire.reload.gestionnaires).to include(gestionnaire_to_remove) - expect(groupe_gestionnaire.reload.gestionnaires.size).to eq(1) + it 'adds the existing administrateur by email' do + groupe_gestionnaire.add_administrateurs(emails: [administrateur.email], current_user: gestionnaire) + expect(groupe_gestionnaire.reload.administrateurs).to include(administrateur) + end + + context "when administrateurs_already_in_groupe_gestionnaire" do + let(:other_groupe_gestionnaire) { create(:groupe_gestionnaire) } + let(:administrateur) { create(:administrateur, groupe_gestionnaire_id: other_groupe_gestionnaire.id) } + it 'does not add the existing administrateur by email' do + groupe_gestionnaire.add_administrateurs(emails: [administrateur.email], current_user: gestionnaire) + expect(groupe_gestionnaire.reload.administrateurs).not_to include(administrateur) + end + end + + it 'adds the new administrateur by email' do + groupe_gestionnaire.add_administrateurs(emails: ['new_administrateur@ds.fr'], current_user: gestionnaire) + expect(groupe_gestionnaire.reload.administrateurs.last.email).to eq('new_administrateur@ds.fr') + end + end + + describe "#remove_administrateur" do + let(:gestionnaire) { create(:gestionnaire) } + let(:groupe_gestionnaire) { create(:groupe_gestionnaire, gestionnaires: [gestionnaire]) } + let!(:administrateur) { create(:administrateur, groupe_gestionnaire_id: groupe_gestionnaire.id) } + + it 'removes the administrateur by id' do + expect(groupe_gestionnaire.reload.administrateurs.size).to eq(1) + groupe_gestionnaire.remove_administrateur(administrateur.id, gestionnaire) + expect(groupe_gestionnaire.reload.administrateurs).not_to include(administrateur) + expect(groupe_gestionnaire.reload.administrateurs.size).to eq(0) end end end