diff --git a/app/controllers/admin/mail_templates_controller.rb b/app/controllers/admin/mail_templates_controller.rb index bc7c948ff..3ed7f277d 100644 --- a/app/controllers/admin/mail_templates_controller.rb +++ b/app/controllers/admin/mail_templates_controller.rb @@ -12,7 +12,8 @@ class Admin::MailTemplatesController < AdminController def update mail_template = find_mail_template_by_slug(params[:id]) mail_template.update(update_params) - redirect_to admin_procedure_mail_templates_path + flash.notice = "Email mis à jour" + redirect_to edit_admin_procedure_mail_template_path(mail_template.procedure_id, params[:id]) end private diff --git a/app/controllers/new_administrateur/mail_templates_controller.rb b/app/controllers/new_administrateur/mail_templates_controller.rb new file mode 100644 index 000000000..7bc7dfba6 --- /dev/null +++ b/app/controllers/new_administrateur/mail_templates_controller.rb @@ -0,0 +1,35 @@ +module NewAdministrateur + class MailTemplatesController < AdministrateurController + include ActionView::Helpers::SanitizeHelper + + def preview + @procedure = procedure + @logo_url = procedure.logo.url + @service = procedure.service + + mail_template = find_mail_template_by_slug(params[:id]) + + render(html: sanitize(mail_template.body), layout: 'mailers/notification') + end + + private + + def procedure + @procedure = current_administrateur.procedures.find(params[:procedure_id]) + end + + def mail_templates + [ + @procedure.initiated_mail_template, + @procedure.received_mail_template, + @procedure.closed_mail_template, + @procedure.refused_mail_template, + @procedure.without_continuation_mail_template + ] + end + + def find_mail_template_by_slug(slug) + mail_templates.find { |template| template.class.const_get(:SLUG) == slug } + end + end +end diff --git a/app/javascript/new_design/state-button.js b/app/javascript/new_design/state-button.js index da47340fd..c30fa64a2 100644 --- a/app/javascript/new_design/state-button.js +++ b/app/javascript/new_design/state-button.js @@ -2,6 +2,7 @@ import { show, hide } from '@utils'; export function showMotivation(event, state) { event.preventDefault(); + motivationCancel(); show(document.querySelector(`.motivation.${state}`)); hide(document.querySelector('.dropdown-items')); } diff --git a/app/mailers/notification_mailer.rb b/app/mailers/notification_mailer.rb index 8b855662d..767a521b3 100644 --- a/app/mailers/notification_mailer.rb +++ b/app/mailers/notification_mailer.rb @@ -36,7 +36,14 @@ class NotificationMailer < ApplicationMailer create_commentaire_for_notification(dossier, subject, body) + if dossier.procedure.logo? + logo_filename = dossier.procedure.logo.filename + attachments.inline[logo_filename] = dossier.procedure.logo.read + @logo_url = attachments[logo_filename].url + end + @dossier = dossier + @service = dossier.procedure.service mail(subject: subject, to: email) do |format| # rubocop:disable Rails/OutputSafety diff --git a/app/models/procedure_presentation.rb b/app/models/procedure_presentation.rb index 89ee71721..91be561f2 100644 --- a/app/models/procedure_presentation.rb +++ b/app/models/procedure_presentation.rb @@ -18,7 +18,8 @@ class ProcedurePresentation < ApplicationRecord field_hash('Créé le', 'self', 'created_at'), field_hash('En construction le', 'self', 'en_construction_at'), field_hash('Mis à jour le', 'self', 'updated_at'), - field_hash('Demandeur', 'user', 'email') + field_hash('Demandeur', 'user', 'email'), + field_hash('Email instructeur', 'followers_gestionnaires', 'email') ] if procedure.for_individual @@ -92,7 +93,7 @@ class ProcedurePresentation < ApplicationRecord .where("champs.type_de_champ_id = #{column.to_i}") .order("champs.value #{order}") .pluck(:id) - when 'self', 'user', 'individual', 'etablissement' + when 'self', 'user', 'individual', 'etablissement', 'followers_gestionnaires' return (table == 'self' ? dossiers : dossiers.includes(table)) .order("#{self.class.sanitized_column(table, column)} #{order}") .pluck(:id) @@ -114,7 +115,7 @@ class ProcedurePresentation < ApplicationRecord dossiers .includes(relation) .where("champs.type_de_champ_id = ?", column.to_i) - .filter_ilike(:champ, :value, values) + .filter_ilike(relation, :value, values) when 'etablissement' if column == 'entreprise_date_creation' dates = values @@ -128,7 +129,7 @@ class ProcedurePresentation < ApplicationRecord .includes(table) .filter_ilike(table, column, values) end - when 'user', 'individual' + when 'user', 'individual', 'followers_gestionnaires' dossiers .includes(table) .filter_ilike(table, column, values) @@ -201,6 +202,8 @@ class ProcedurePresentation < ApplicationRecord dossier.send(column)&.strftime('%d/%m/%Y') when 'user', 'individual', 'etablissement' dossier.send(table)&.send(column) + when 'followers_gestionnaires' + dossier.send(table)&.map { |g| g.send(column) }&.join(', ') when 'type_de_champ' dossier.champs.find { |c| c.type_de_champ_id == column.to_i }.value when 'type_de_champ_private' @@ -230,8 +233,14 @@ class ProcedurePresentation < ApplicationRecord @column_whitelist[table] || [] end - def self.sanitized_column(table, column) - [(table == 'self' ? 'dossier' : table.to_s).pluralize, column] + def self.sanitized_column(association, column) + table = if association == 'self' + Dossier.table_name + else + Dossier.reflect_on_association(association).klass.table_name + end + + [table, column] .map { |name| ActiveRecord::Base.connection.quote_column_name(name) } .join('.') end diff --git a/app/views/admin/mail_templates/edit.html.haml b/app/views/admin/mail_templates/edit.html.haml index a00c116f6..c423fe418 100644 --- a/app/views/admin/mail_templates/edit.html.haml +++ b/app/views/admin/mail_templates/edit.html.haml @@ -15,6 +15,7 @@ .text-right = link_to "Annuler", admin_procedure_mail_templates_path(@procedure), class: "btn btn-default" = f.button :submit, 'Mettre à jour', class: "btn-success" + = link_to "Prévisualiser", preview_procedure_mail_template_path(@procedure, @mail_template.class.const_get(:SLUG)), class: "btn btn-primary", target: "_blank" .row .col-md-12 diff --git a/app/views/layouts/mailers/notification.html.haml b/app/views/layouts/mailers/notification.html.haml index c71be0360..b267a0fc5 100644 --- a/app/views/layouts/mailers/notification.html.haml +++ b/app/views/layouts/mailers/notification.html.haml @@ -1,7 +1,42 @@ +- if @logo_url.present? + - content_for :procedure_logo do + %table{ width: "100%", border: "0", cellspacing: "0", cellpadding: "0" } + %tr + %td{ align: "center" } + = image_tag @logo_url, height: "150", style: "display:block; max-height: 150px; max-width: 150px;" + - content_for :footer do + - if @dossier.present? + - messagerie_url = messagerie_dossier_url(@dossier) + - else + - messagerie_url = "#" %strong Merci de ne pas répondre à cet email. Pour vous adresser à votre administration, passez directement par votre = succeed '.' do - = link_to 'messagerie', messagerie_dossier_url(@dossier), target: '_blank', rel: 'noopener' + = link_to 'messagerie', messagerie_url, target: '_blank', rel: 'noopener' -= render template: 'layouts/mailers/layout' + - if @service.present? + %table{ width: "100%", border: "0", cellspacing: "0", cellpadding: "0", style: "cursor:auto;color:#55575d;font-family:Helvetica, Arial, sans-serif;font-size:11px;line-height:22px;text-align:left;" } + %tr + %td{ width: "50%", valign: "top" } + %p + %strong Cette démarche est gérée par : + %br + = @service.nom + %br + = @service.organisme + %br + = @service.adresse + %td{ width: "50%", valign: "top" } + %p + %strong Poser une question sur votre dossier : + %br + = link_to 'Par la messagerie', messagerie_url, target: '_blank', rel: 'noopener' + %br + Par téléphone : + = link_to @service.telephone, "tel:#{@service.telephone}" + %br + Horaires : #{ formatted_horaires(@service.horaires) } + + += render template: 'layouts/mailers/notifications_layout' diff --git a/app/views/layouts/mailers/notifications_layout.html.erb b/app/views/layouts/mailers/notifications_layout.html.erb new file mode 100644 index 000000000..7c9ea03f9 --- /dev/null +++ b/app/views/layouts/mailers/notifications_layout.html.erb @@ -0,0 +1,180 @@ + + + + + <%= yield(:title) %> + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+
+ + + + + + +
+
+

+

+
+
+
+ +
+
+ + +
+ + + + + + +
+ +
+ + + <%= yield(:procedure_logo) %> + + + + <% if content_for?(:footer) %> + + + + <% end %> + +
+
+ <%= yield %> +
+
+

+ — +

+

+ <%= yield(:footer) %> +

+
+
+ +
+
+ + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ demarches-simplifiees.fr est un service fourni par la DINSIC et incubé par beta.gouv.fr +
+
+
+

+

+
+
+
+ +
+
+ +
+ + diff --git a/app/views/notification_mailer/_signature.html.haml b/app/views/notification_mailer/_signature.html.haml new file mode 100644 index 000000000..132bfafa4 --- /dev/null +++ b/app/views/notification_mailer/_signature.html.haml @@ -0,0 +1,4 @@ +%p + Bonne journée, + +%p --nom du service-- diff --git a/app/views/notification_mailer/closed_mail.html.haml b/app/views/notification_mailer/closed_mail.html.haml index 080b1071a..dcaf366be 100644 --- a/app/views/notification_mailer/closed_mail.html.haml +++ b/app/views/notification_mailer/closed_mail.html.haml @@ -7,4 +7,4 @@ %p À tout moment, vous pouvez consulter votre dossier et les éventuels messages de l'administration à cette adresse : --lien dossier-- -= render partial: "layouts/mailers/signature" += render partial: "notification_mailer/signature" diff --git a/app/views/notification_mailer/closed_mail_with_attestation.html.haml b/app/views/notification_mailer/closed_mail_with_attestation.html.haml index d8b646f57..22dce3d5a 100644 --- a/app/views/notification_mailer/closed_mail_with_attestation.html.haml +++ b/app/views/notification_mailer/closed_mail_with_attestation.html.haml @@ -10,4 +10,4 @@ %p À tout moment, vous pouvez consulter votre dossier et les éventuels messages de l'administration à cette adresse : --lien dossier-- -= render partial: "layouts/mailers/signature" += render partial: "notification_mailer/signature" diff --git a/app/views/notification_mailer/initiated_mail.html.haml b/app/views/notification_mailer/initiated_mail.html.haml index 796695603..91007a340 100644 --- a/app/views/notification_mailer/initiated_mail.html.haml +++ b/app/views/notification_mailer/initiated_mail.html.haml @@ -7,4 +7,4 @@ %p À tout moment, vous pouvez consulter votre dossier et les éventuels messages de l'administration à cette adresse : --lien dossier-- -= render partial: "layouts/mailers/signature" += render partial: "notification_mailer/signature" diff --git a/app/views/notification_mailer/received_mail.html.haml b/app/views/notification_mailer/received_mail.html.haml index a774789c1..d006d6271 100644 --- a/app/views/notification_mailer/received_mail.html.haml +++ b/app/views/notification_mailer/received_mail.html.haml @@ -4,4 +4,4 @@ %p Votre administration vous confirme la bonne réception de votre dossier nº --numéro du dossier--. Celui-ci sera instruit dans le délai légal déclaré par votre interlocuteur. -= render partial: "layouts/mailers/signature" += render partial: "notification_mailer/signature" diff --git a/app/views/notification_mailer/refused_mail.html.haml b/app/views/notification_mailer/refused_mail.html.haml index 1ed19b296..f45054ca2 100644 --- a/app/views/notification_mailer/refused_mail.html.haml +++ b/app/views/notification_mailer/refused_mail.html.haml @@ -10,4 +10,4 @@ %p Pour en savoir plus sur le motif du refus, vous pouvez consulter votre dossier et les éventuels messages de l'administration à cette adresse : --lien dossier-- -= render partial: "layouts/mailers/signature" += render partial: "notification_mailer/signature" diff --git a/app/views/notification_mailer/without_continuation_mail.html.haml b/app/views/notification_mailer/without_continuation_mail.html.haml index 3033f61cc..c397c1adc 100644 --- a/app/views/notification_mailer/without_continuation_mail.html.haml +++ b/app/views/notification_mailer/without_continuation_mail.html.haml @@ -10,4 +10,4 @@ %p Pour en savoir plus sur les raisons de ce classement sans suite, vous pouvez consulter votre dossier et les éventuels messages de l'administration à cette adresse : --lien dossier-- -= render partial: "layouts/mailers/signature" += render partial: "notification_mailer/signature" diff --git a/config/routes.rb b/config/routes.rb index 409795dce..20cd28d8a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -378,6 +378,10 @@ Rails.application.routes.draw do patch :move end end + + resources :mail_templates, only: [] do + get 'preview', on: :member + end end resources :services, except: [:show] do diff --git a/spec/controllers/admin/mail_templates_controller_spec.rb b/spec/controllers/admin/mail_templates_controller_spec.rb index e9c35576f..d34ef2684 100644 --- a/spec/controllers/admin/mail_templates_controller_spec.rb +++ b/spec/controllers/admin/mail_templates_controller_spec.rb @@ -31,7 +31,7 @@ describe Admin::MailTemplatesController, type: :controller do } end - it { expect(response).to redirect_to admin_procedure_mail_templates_path(procedure) } + it { expect(response).to redirect_to edit_admin_procedure_mail_template_path(procedure, initiated_mail.class.const_get(:SLUG)) } context 'the mail template' do subject { procedure.reload; procedure.initiated_mail_template } diff --git a/spec/controllers/new_administrateur/mail_templates_controller_spec.rb b/spec/controllers/new_administrateur/mail_templates_controller_spec.rb new file mode 100644 index 000000000..2a8673f77 --- /dev/null +++ b/spec/controllers/new_administrateur/mail_templates_controller_spec.rb @@ -0,0 +1,21 @@ +describe NewAdministrateur::MailTemplatesController, type: :controller do + render_views + + let(:admin) { create(:administrateur) } + + describe '#preview' do + let(:procedure) { create(:procedure, :with_logo, :with_service, administrateur: admin) } + + before do + sign_in admin + get :preview, params: { id: "initiated_mail", procedure_id: procedure.id } + end + + it { expect(response).to have_http_status(:ok) } + + it { expect(response.body).to have_css("img[src*='#{procedure.logo.filename}']") } + + it { expect(response.body).to include(procedure.service.nom) } + it { expect(response.body).to include(procedure.service.telephone) } + end +end diff --git a/spec/factories/procedure.rb b/spec/factories/procedure.rb index e45f1c2aa..98488e884 100644 --- a/spec/factories/procedure.rb +++ b/spec/factories/procedure.rb @@ -41,6 +41,10 @@ FactoryBot.define do end end + trait :with_logo do + logo { Rack::Test::UploadedFile.new("./spec/fixtures/files/logo_test_procedure.png", 'application/pdf') } + end + trait :with_path do path { generate(:published_path) } end diff --git a/spec/mailers/previews/notification_mailer_preview.rb b/spec/mailers/previews/notification_mailer_preview.rb index af96ed00e..064d5471d 100644 --- a/spec/mailers/previews/notification_mailer_preview.rb +++ b/spec/mailers/previews/notification_mailer_preview.rb @@ -4,7 +4,8 @@ class NotificationMailerPreview < ActionMailer::Preview end def send_initiated_notification - NotificationMailer.send_initiated_notification(Dossier.last) + p = Procedure.where(id: Mails::InitiatedMail.where("body like ?", "% 'En construction le', "table" => 'self', "column" => 'en_construction_at' }, { "label" => 'Mis à jour le', "table" => 'self', "column" => 'updated_at' }, { "label" => 'Demandeur', "table" => 'user', "column" => 'email' }, + { "label" => 'Email instructeur', "table" => 'followers_gestionnaires', "column" => 'email' }, { "label" => 'SIREN', "table" => 'etablissement', "column" => 'entreprise_siren' }, { "label" => 'Forme juridique', "table" => 'etablissement', "column" => 'entreprise_forme_juridique' }, { "label" => 'Nom commercial', "table" => 'etablissement', "column" => 'entreprise_nom_commercial' }, @@ -196,6 +197,17 @@ describe ProcedurePresentation do it { is_expected.to eq('75008') } end + context 'for followers_gestionnaires table' do + let(:table) { 'followers_gestionnaires' } + let(:column) { 'email' } + + let(:dossier) { create(:dossier, procedure: procedure) } + let!(:follow1) { create(:follow, dossier: dossier, gestionnaire: create(:gestionnaire, email: 'user1@host')) } + let!(:follow2) { create(:follow, dossier: dossier, gestionnaire: create(:gestionnaire, email: 'user2@host')) } + + it { is_expected.to eq('user1@host, user2@host') } + end + context 'for type_de_champ table' do let(:table) { 'type_de_champ' } let(:column) { procedure.types_de_champ.first.id.to_s } @@ -631,6 +643,39 @@ describe ProcedurePresentation do end end end + + context 'for followers_gestionnaires table' do + let(:filter) { [{ 'table' => 'followers_gestionnaires', 'column' => 'email', 'value' => 'keepmail' }] } + + let!(:kept_dossier) { create(:dossier, procedure: procedure) } + let!(:discarded_dossier) { create(:dossier, procedure: procedure) } + + before do + create(:follow, dossier: kept_dossier, gestionnaire: create(:gestionnaire, email: 'me@keepmail.com')) + create(:follow, dossier: discarded_dossier, gestionnaire: create(:gestionnaire, email: 'me@discard.com')) + end + + it { is_expected.to contain_exactly(kept_dossier.id) } + + context 'with multiple search values' do + let(:filter) do + [ + { 'table' => 'followers_gestionnaires', 'column' => 'email', 'value' => 'keepmail' }, + { 'table' => 'followers_gestionnaires', 'column' => 'email', 'value' => 'beta.gouv.fr' } + ] + end + + let(:other_kept_dossier) { create(:dossier, procedure: procedure) } + + before do + create(:follow, dossier: other_kept_dossier, gestionnaire: create(:gestionnaire, email: 'bazinga@beta.gouv.fr')) + end + + it 'returns every dossier that matches any of the search criteria for a given column' do + is_expected.to contain_exactly(kept_dossier.id, other_kept_dossier.id) + end + end + end end describe '#eager_load_displayed_fields' do @@ -650,6 +695,7 @@ describe ProcedurePresentation do expect(displayed_dossier.association(:user)).not_to be_loaded expect(displayed_dossier.association(:individual)).not_to be_loaded expect(displayed_dossier.association(:etablissement)).not_to be_loaded + expect(displayed_dossier.association(:followers_gestionnaires)).not_to be_loaded end end @@ -665,6 +711,7 @@ describe ProcedurePresentation do expect(displayed_dossier.association(:user)).not_to be_loaded expect(displayed_dossier.association(:individual)).not_to be_loaded expect(displayed_dossier.association(:etablissement)).not_to be_loaded + expect(displayed_dossier.association(:followers_gestionnaires)).not_to be_loaded end end @@ -678,6 +725,7 @@ describe ProcedurePresentation do expect(displayed_dossier.association(:user)).to be_loaded expect(displayed_dossier.association(:individual)).not_to be_loaded expect(displayed_dossier.association(:etablissement)).not_to be_loaded + expect(displayed_dossier.association(:followers_gestionnaires)).not_to be_loaded end end @@ -691,6 +739,7 @@ describe ProcedurePresentation do expect(displayed_dossier.association(:user)).not_to be_loaded expect(displayed_dossier.association(:individual)).to be_loaded expect(displayed_dossier.association(:etablissement)).not_to be_loaded + expect(displayed_dossier.association(:followers_gestionnaires)).not_to be_loaded end end @@ -704,6 +753,21 @@ describe ProcedurePresentation do expect(displayed_dossier.association(:user)).not_to be_loaded expect(displayed_dossier.association(:individual)).not_to be_loaded expect(displayed_dossier.association(:etablissement)).to be_loaded + expect(displayed_dossier.association(:followers_gestionnaires)).not_to be_loaded + end + end + + context 'for followers_gestionnaires' do + let(:table) { 'followers_gestionnaires' } + let(:column) { 'email' } + + it 'preloads the followers_gestionnaires relation' do + expect(displayed_dossier.association(:champs)).not_to be_loaded + expect(displayed_dossier.association(:champs_private)).not_to be_loaded + expect(displayed_dossier.association(:user)).not_to be_loaded + expect(displayed_dossier.association(:individual)).not_to be_loaded + expect(displayed_dossier.association(:etablissement)).not_to be_loaded + expect(displayed_dossier.association(:followers_gestionnaires)).to be_loaded end end @@ -717,6 +781,7 @@ describe ProcedurePresentation do expect(displayed_dossier.association(:user)).not_to be_loaded expect(displayed_dossier.association(:individual)).not_to be_loaded expect(displayed_dossier.association(:etablissement)).not_to be_loaded + expect(displayed_dossier.association(:followers_gestionnaires)).not_to be_loaded end end end diff --git a/spec/views/admin/mail_templates/edit.html.haml_spec.rb b/spec/views/admin/mail_templates/edit.html.haml_spec.rb index f41f6087b..a381a79d9 100644 --- a/spec/views/admin/mail_templates/edit.html.haml_spec.rb +++ b/spec/views/admin/mail_templates/edit.html.haml_spec.rb @@ -10,6 +10,7 @@ describe 'admin/mail_templates/edit.html.haml', type: :view do allow(view).to receive(:admin_procedure_mail_templates_path).and_return("/toto") assign(:mail_template, mail_template) + assign(:procedure, procedure) end context "Champs are listed in the page" do