diff --git a/app/controllers/admin/procedures_controller.rb b/app/controllers/admin/procedures_controller.rb index 47a86c99c..cd6259131 100644 --- a/app/controllers/admin/procedures_controller.rb +++ b/app/controllers/admin/procedures_controller.rb @@ -79,7 +79,7 @@ class Admin::ProceduresController < AdminController end def transfer - admin = Administrateur.find_by(email: params[:email_admin].downcase) + admin = Administrateur.by_email(params[:email_admin].downcase) if admin.nil? render '/admin/procedures/transfer', formats: 'js', status: 404 diff --git a/app/controllers/manager/administrateurs_controller.rb b/app/controllers/manager/administrateurs_controller.rb index f976f3cf6..10dec19d5 100644 --- a/app/controllers/manager/administrateurs_controller.rb +++ b/app/controllers/manager/administrateurs_controller.rb @@ -22,11 +22,7 @@ module Manager def delete administrateur = Administrateur.find(params[:id]) - if !administrateur.can_be_deleted? - fail "Impossible de supprimer cet administrateur car il a des dossiers ou des procédures" - end - administrateur.dossiers.each(&:delete_and_keep_track) - administrateur.destroy + administrateur.delete_and_transfer_services logger.info("L'administrateur #{administrateur.id} est supprimé par #{current_administration.id}") flash[:notice] = "L'administrateur #{administrateur.id} est supprimé" diff --git a/app/controllers/manager/demandes_controller.rb b/app/controllers/manager/demandes_controller.rb index ffdac506c..fc9aa6fb2 100644 --- a/app/controllers/manager/demandes_controller.rb +++ b/app/controllers/manager/demandes_controller.rb @@ -48,9 +48,9 @@ module Manager end def pending_demandes - already_approved_emails = Administrateur - .where(email: demandes.map { |d| d[:email] }) - .pluck(:email) + already_approved_emails = Administrateur.eager(:user) + .where(users: { email: demandes.map { |d| d[:email] } }) + .map(&:email) demandes.reject { |demande| already_approved_emails.include?(demande[:email]) } end diff --git a/app/controllers/manager/instructeurs_controller.rb b/app/controllers/manager/instructeurs_controller.rb index 82b1e11eb..ed2107c45 100644 --- a/app/controllers/manager/instructeurs_controller.rb +++ b/app/controllers/manager/instructeurs_controller.rb @@ -6,5 +6,19 @@ module Manager flash[:notice] = "Instructeur réinvité." redirect_to manager_instructeur_path(instructeur) end + + def delete + instructeur = Instructeur.find(params[:id]) + + if !instructeur.can_be_deleted? + fail "Impossible de supprimer cet instructeur car il est administrateur ou il est le seul instructeur sur une démarche" + end + instructeur.destroy! + + logger.info("L'instructeur #{instructeur.id} est supprimé par #{current_administration.id}") + flash[:notice] = "L'instructeur #{instructeur.id} est supprimé" + + redirect_to manager_instructeurs_path + end end end diff --git a/app/controllers/manager/procedures_controller.rb b/app/controllers/manager/procedures_controller.rb index 3e1026f78..bd20a3061 100644 --- a/app/controllers/manager/procedures_controller.rb +++ b/app/controllers/manager/procedures_controller.rb @@ -29,7 +29,7 @@ module Manager end def add_administrateur - administrateur = Administrateur.find_by(email: params[:email]) + administrateur = Administrateur.by_email(params[:email]) if administrateur procedure.administrateurs << administrateur flash[:notice] = "L'administrateur \"#{params[:email]}\" est ajouté à la démarche." diff --git a/app/controllers/manager/users_controller.rb b/app/controllers/manager/users_controller.rb index 9bdbb0449..d9e33fcb9 100644 --- a/app/controllers/manager/users_controller.rb +++ b/app/controllers/manager/users_controller.rb @@ -37,7 +37,7 @@ module Manager def delete user = User.find(params[:id]) if !user.can_be_deleted? - fail "Impossible de supprimer cet utilisateur car il a des dossiers en instruction" + fail "Impossible de supprimer cet utilisateur. Il a des dossiers en instruction ou il est administrateur." end user.delete_and_keep_track_dossiers(current_administration) diff --git a/app/controllers/new_administrateur/procedure_administrateurs_controller.rb b/app/controllers/new_administrateur/procedure_administrateurs_controller.rb index f01a83f97..a35b980a6 100644 --- a/app/controllers/new_administrateur/procedure_administrateurs_controller.rb +++ b/app/controllers/new_administrateur/procedure_administrateurs_controller.rb @@ -9,7 +9,7 @@ module NewAdministrateur email = params.require(:administrateur)[:email]&.strip&.downcase # Find the admin - administrateur = Administrateur.find_by(email: email) + administrateur = Administrateur.by_email(email) if administrateur.nil? flash.alert = "L’administrateur « #{email} » n’existe pas. Invitez-le à demander un compte administrateur à l’addresse #{new_demande_url}." return diff --git a/app/controllers/users/passwords_controller.rb b/app/controllers/users/passwords_controller.rb index 97708654f..47d1c75e0 100644 --- a/app/controllers/users/passwords_controller.rb +++ b/app/controllers/users/passwords_controller.rb @@ -11,7 +11,7 @@ class Users::PasswordsController < Devise::PasswordsController def create # Check the credentials associated to the mail to generate a correct reset link email = params[:user][:email] - if Administrateur.find_by(email: email) + if Administrateur.by_email(email) @devise_mapping = Devise.mappings[:administrateur] params[:administrateur] = params[:user] # uncomment to check password complexity for Instructeur @@ -56,7 +56,7 @@ class Users::PasswordsController < Devise::PasswordsController def try_to_authenticate_administrateur if user_signed_in? - administrateur = Administrateur.find_by(email: current_user.email) + administrateur = Administrateur.by_email(current_user.email) if administrateur sign_in(administrateur.user) diff --git a/app/javascript/new_design/select2.js b/app/javascript/new_design/select2.js index ca55d1dd9..1b3cf05a5 100644 --- a/app/javascript/new_design/select2.js +++ b/app/javascript/new_design/select2.js @@ -52,6 +52,7 @@ const baseOptions = { const baseAjaxOptions = { delay: 250, + timeout: 10 * 1000, // 10 sec cache: true, data({ term: nom }) { return { diff --git a/app/javascript/shared/remote-poller.js b/app/javascript/shared/remote-poller.js index a04607212..6aca55745 100644 --- a/app/javascript/shared/remote-poller.js +++ b/app/javascript/shared/remote-poller.js @@ -86,5 +86,5 @@ class RemotePoller { } } -const attachementPoller = new RemotePoller({ interval: 2000, maxChecks: 5 }); -const exportPoller = new RemotePoller({ interval: 4000, maxChecks: 10 }); +const attachementPoller = new RemotePoller({ interval: 3000, maxChecks: 5 }); +const exportPoller = new RemotePoller({ interval: 6000, maxChecks: 10 }); diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb index b444548f4..3504f1a7a 100644 --- a/app/mailers/application_mailer.rb +++ b/app/mailers/application_mailer.rb @@ -3,11 +3,6 @@ class ApplicationMailer < ActionMailer::Base default from: "demarches-simplifiees.fr <#{CONTACT_EMAIL}>" layout 'mailer' - # Don’t retry to send a message if the server rejects the recipient address - rescue_from Net::SMTPSyntaxError do |_error| - message.perform_deliveries = false - end - # Attach the procedure logo to the email (if any). # Returns the attachment url. def attach_logo(procedure) diff --git a/app/mailers/devise_user_mailer.rb b/app/mailers/devise_user_mailer.rb index de07cc1b7..a19c04768 100644 --- a/app/mailers/devise_user_mailer.rb +++ b/app/mailers/devise_user_mailer.rb @@ -4,11 +4,6 @@ class DeviseUserMailer < Devise::Mailer include Devise::Controllers::UrlHelpers # Optional. eg. `confirmation_url` layout 'mailers/layout' - # Don’t retry to send a message if the server rejects the recipient address - rescue_from Net::SMTPSyntaxError do |_error| - message.perform_deliveries = false - end - def template_paths ['devise_mailer'] end diff --git a/app/models/administrateur.rb b/app/models/administrateur.rb index 4d4b84d4a..a353901f5 100644 --- a/app/models/administrateur.rb +++ b/app/models/administrateur.rb @@ -1,21 +1,27 @@ class Administrateur < ApplicationRecord - self.ignored_columns = ['features', 'encrypted_password', 'reset_password_token', 'reset_password_sent_at', 'remember_created_at', 'sign_in_count', 'current_sign_in_at', 'last_sign_in_at', 'current_sign_in_ip', 'last_sign_in_ip', 'failed_attempts', 'unlock_token', 'locked_at'] - include EmailSanitizableConcern + self.ignored_columns = ['email', 'features', 'encrypted_password', 'reset_password_token', 'reset_password_sent_at', 'remember_created_at', 'sign_in_count', 'current_sign_in_at', 'last_sign_in_at', 'current_sign_in_ip', 'last_sign_in_ip', 'failed_attempts', 'unlock_token', 'locked_at'] include ActiveRecord::SecureToken has_and_belongs_to_many :instructeurs has_many :administrateurs_procedures has_many :procedures, through: :administrateurs_procedures has_many :services - has_many :dossiers, -> { state_not_brouillon }, through: :procedures has_one :user, dependent: :nullify - before_validation -> { sanitize_email(:email) } + default_scope { eager_load(:user) } scope :inactive, -> { joins(:user).where(users: { last_sign_in_at: nil }) } scope :with_publiees_ou_closes, -> { joins(:procedures).where(procedures: { aasm_state: [:publiee, :close, :depubliee] }) } + def self.by_email(email) + Administrateur.find_by(users: { email: email }) + end + + def email + user.email + end + # validate :password_complexity, if: Proc.new { |a| Devise.password_length.include?(a.password.try(:size)) } def password_complexity @@ -68,6 +74,18 @@ class Administrateur < ApplicationRecord end def can_be_deleted? - dossiers.state_instruction_commencee.none? && procedures.all? { |p| p.administrateurs.count > 1 } + procedures.all? { |p| p.administrateurs.count > 1 } + end + + def delete_and_transfer_services + if !can_be_deleted? + fail "Impossible de supprimer cet administrateur car il a des démarches où il est le seul administrateur" + end + + procedures.each do |procedure| + next_administrateur = procedure.administrateurs.where.not(id: self.id).first + procedure.service.update(administrateur: next_administrateur) + end + destroy end end diff --git a/app/models/dossier.rb b/app/models/dossier.rb index 29ea1344b..8a9b41352 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -381,7 +381,7 @@ class Dossier < ApplicationRecord update(hidden_at: deleted_dossier.deleted_at) if en_construction? - administration_emails = followers_instructeurs.present? ? followers_instructeurs.map(&:email) : procedure.administrateurs.pluck(:email) + administration_emails = followers_instructeurs.present? ? followers_instructeurs.map(&:email) : procedure.administrateurs.map(&:email) administration_emails.each do |email| DossierMailer.notify_deletion_to_administration(deleted_dossier, email).deliver_later end diff --git a/app/models/instructeur.rb b/app/models/instructeur.rb index 4c142b870..97b2b0a49 100644 --- a/app/models/instructeur.rb +++ b/app/models/instructeur.rb @@ -17,9 +17,9 @@ class Instructeur < ApplicationRecord has_many :previously_followed_dossiers, -> { distinct }, through: :previous_follows, source: :dossier has_many :avis has_many :dossiers_from_avis, through: :avis, source: :dossier - has_many :trusted_device_tokens + has_many :trusted_device_tokens, dependent: :destroy - has_one :user + has_one :user, dependent: :nullify default_scope { eager_load(:user) } @@ -176,6 +176,10 @@ class Instructeur < ApplicationRecord trusted_device_token&.token_young? end + def can_be_deleted? + user.administrateur.nil? && procedures.all? { |p| p.defaut_groupe_instructeur.instructeurs.count > 1 } + end + private def annotations_hash(demande, annotations_privees, avis, messagerie) diff --git a/app/models/user.rb b/app/models/user.rb index 4838da20d..1191236c4 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -82,7 +82,7 @@ class User < ApplicationRecord user = User.create_or_promote_to_instructeur(email, password) if user.valid? && user.administrateur_id.nil? - user.create_administrateur!(email: email) + user.create_administrateur! end user @@ -97,7 +97,7 @@ class User < ApplicationRecord end def can_be_deleted? - dossiers.state_instruction_commencee.empty? + administrateur.nil? && instructeur.nil? && dossiers.state_instruction_commencee.empty? end def delete_and_keep_track_dossiers(administration) @@ -105,12 +105,11 @@ class User < ApplicationRecord raise "Cannot delete this user because instruction has started for some dossiers" end - if can_be_deleted? - dossiers.each do |dossier| - dossier.delete_and_keep_track(administration) - end - destroy + dossiers.each do |dossier| + dossier.delete_and_keep_track(administration) end + dossiers.with_hidden.destroy_all + destroy! end private diff --git a/app/services/administrateur_usage_statistics_service.rb b/app/services/administrateur_usage_statistics_service.rb index 1cb9d5b57..16d220efb 100644 --- a/app/services/administrateur_usage_statistics_service.rb +++ b/app/services/administrateur_usage_statistics_service.rb @@ -119,7 +119,7 @@ class AdministrateurUsageStatisticsService end def nb_instructeurs_by_administrateur_id - @nb_instructeurs_by_administrateur_id ||= with_default(0, Administrateur.joins(:instructeurs).group(:administrateur_id).count) + @nb_instructeurs_by_administrateur_id ||= with_default(0, Administrateur.joins(:instructeurs).group('administrateurs.id').count) end def nb_dossiers_by_administrateur_id_and_procedure_id_and_synthetic_state diff --git a/app/views/admin/procedures/new_from_existing.html.haml b/app/views/admin/procedures/new_from_existing.html.haml index 49b7b424a..e171bd860 100644 --- a/app/views/admin/procedures/new_from_existing.html.haml +++ b/app/views/admin/procedures/new_from_existing.html.haml @@ -56,4 +56,4 @@ %td.flex = link_to('Consulter', apercu_procedure_path(id: procedure.id), target: "_blank", rel: "noopener", class: 'button small') = link_to('Cloner', admin_procedure_clone_path(procedure.id, from_new_from_existing: true), 'data-method' => :put, class: 'button small primary') - = link_to('Contacter', "mailto:#{procedure.administrateurs.pluck(:email) * ","}", class: 'button small') + = link_to('Contacter', "mailto:#{procedure.administrateurs.map(&:email) * ","}", class: 'button small') diff --git a/app/views/administration_mailer/invite_admin.html.haml b/app/views/administration_mailer/invite_admin.html.haml index b048e8b30..ee56b73a0 100644 --- a/app/views/administration_mailer/invite_admin.html.haml +++ b/app/views/administration_mailer/invite_admin.html.haml @@ -12,11 +12,11 @@ - if @reset_password_token.present? %p %b - Pour l’activer, cliquez sur le lien suivant : + Pour l’activer, cliquez sur le lien suivant : = link_to(admin_activate_url(token: @reset_password_token), admin_activate_url(token: @reset_password_token)) - else %p - Pour vous connecter, cliquez sur le lien suivant : + Pour vous connecter, cliquez sur le lien suivant : = link_to(new_user_session_url, new_user_session_url) %p diff --git a/app/views/devise_mailer/confirmation_instructions.html.haml b/app/views/devise_mailer/confirmation_instructions.html.haml index a63b05978..08ed08de9 100644 --- a/app/views/devise_mailer/confirmation_instructions.html.haml +++ b/app/views/devise_mailer/confirmation_instructions.html.haml @@ -6,7 +6,7 @@ Bonjour, %p - Pour activer votre compte sur demarches-simplifiees.fr, veuillez cliquer sur le lien suivant : + Pour activer votre compte sur demarches-simplifiees.fr, veuillez cliquer sur le lien suivant : = link_to(confirmation_url(@user, confirmation_token: @token), confirmation_url(@user, confirmation_token: @token)) - else @@ -16,7 +16,7 @@ Bonjour, %p - Pour confirmer votre changement d'adresse email, veuillez cliquer sur le lien suivant : + Pour confirmer votre changement d'adresse email, veuillez cliquer sur le lien suivant : = link_to(confirmation_url(@user, confirmation_token: @token), confirmation_url(@user, confirmation_token: @token)) = render partial: "layouts/mailers/signature" diff --git a/app/views/instructeurs/procedures/download_export.js.erb b/app/views/instructeurs/procedures/download_export.js.erb index fe8b67bb9..d25101ffc 100644 --- a/app/views/instructeurs/procedures/download_export.js.erb +++ b/app/views/instructeurs/procedures/download_export.js.erb @@ -7,4 +7,4 @@ <% end %> <% end %> -<%= render_flash(timeout: 7000) %> +<%= render_flash(timeout: 20000) %> diff --git a/app/views/manager/administrateurs/show.html.erb b/app/views/manager/administrateurs/show.html.erb index 73019d37f..b33304288 100644 --- a/app/views/manager/administrateurs/show.html.erb +++ b/app/views/manager/administrateurs/show.html.erb @@ -36,7 +36,7 @@ as well as a link to its edit page. <% if page.resource.invitation_expired? %> <%= link_to "renvoyer l'invitation", reinvite_manager_administrateur_path(page.resource), method: :post, class: "button" %> <% end %> - <%= button_to "supprimer", delete_manager_administrateur_path(page.resource), method: :delete, disabled: !page.resource.can_be_deleted?, class: "button", data: { confirm: "Confirmez-vous la suppression de l'administrateur ?" }, title: page.resource.can_be_deleted? ? "Supprimer" : "Cet administrateur a des dossiers ou des procédures dont il est le seul admin et ne peut être supprimé" %> + <%= button_to "supprimer", delete_manager_administrateur_path(page.resource), method: :delete, disabled: !page.resource.can_be_deleted?, class: "button", data: { confirm: "Confirmez-vous la suppression de l'administrateur ?" }, title: page.resource.can_be_deleted? ? "Supprimer" : "Cet administrateur a des démarches dont il est le seul admin et ne peut être supprimé" %> diff --git a/app/views/manager/instructeurs/show.html.erb b/app/views/manager/instructeurs/show.html.erb index e423414a9..983e171e9 100644 --- a/app/views/manager/instructeurs/show.html.erb +++ b/app/views/manager/instructeurs/show.html.erb @@ -34,7 +34,10 @@ as well as a link to its edit page.