Merge pull request #3777 from betagouv/dev

2019-04-11-01
This commit is contained in:
Mathieu Magnin 2019-04-11 11:20:29 +02:00 committed by GitHub
commit 283269adcd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 386 additions and 17 deletions

View file

@ -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

View file

@ -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

View file

@ -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'));
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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'

View file

@ -0,0 +1,180 @@
<?xml version="1.0" encoding="UTF-8"?>
<!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
<head>
<title><%= yield(:title) %></title>
<!--[if !mso]>
<!-- -->
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!--<![endif]-->
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style type="text/css"> #outlook a { padding: 0; } .ReadMsgBody { width: 100%; } .ExternalClass { width: 100%; } .ExternalClass * { line-height:100%; } body { margin: 0; padding: 0; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; } table, td { border-collapse:collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; } img { border: 0; height: auto; line-height: 100%; outline: none; text-decoration: none; -ms-interpolation-mode: bicubic; } p { display: block; margin: 13px 0; }</style>
<!--[if !mso]>
<!-->
<style type="text/css"> @media only screen and (max-width:480px) { @-ms-viewport { width:320px; } @viewport { width:320px; } }</style>
<!--<![endif]-->
<!--[if mso]>
<xml>
<o:OfficeDocumentSettings>
<o:AllowPNG/>
<o:PixelsPerInch>96</o:PixelsPerInch>
</o:OfficeDocumentSettings>
</xml>
<![endif]-->
<!--[if lte mso 11]>
<style type="text/css"> .outlook-group-fix { width:100% !important; }</style>
<![endif]-->
<style type="text/css"> @media only screen and (min-width:480px) { .mj-column-per-66 { width:66.66666666666666%!important; }.mj-column-per-33 { width:33.33333333333333%!important; }.mj-column-per-100 { width:100%!important; } }</style>
</head>
<body style="background: #F4F4F4;">
<div style="background-color:#F4F4F4;">
<!--[if mso | IE]>
<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="600" align="center" style="width:600px;">
<tr>
<td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;">
<![endif]-->
<div style="margin:0px auto;max-width:600px;">
<table role="presentation" cellpadding="0" cellspacing="0" style="font-size:0px;width:100%;" align="center" border="0">
<tbody>
<tr>
<td style="text-align:center;vertical-align:top;direction:ltr;font-size:0px;padding:20px 0px 20px 0px;">
<div class="mj-column-per-33 outlook-group-fix" style="vertical-align:top;display:inline-block;direction:ltr;font-size:13px;text-align:left;width:100%;">
<table role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
<tbody>
<tr>
<td style="word-wrap:break-word;font-size:0px;padding:0px 25px 0px 0px;padding-top:0px;padding-bottom:0px;" align="right">
<div class="" style="cursor:auto;color:#55575d;font-family:Helvetica, Arial, sans-serif;font-size:11px;line-height:22px;text-align:right;">
<p style="margin: 10px 0;">
</p>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!--[if mso | IE]>
</td>
</tr>
</table>
<![endif]-->
</td>
</tr>
</tbody>
</table>
</div>
<!--[if mso | IE]>
</td>
</tr>
</table>
<![endif]-->
<!--[if mso | IE]>
<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="600" align="center" style="width:600px;">
<tr>
<td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;">
<![endif]-->
<div style="margin:0px auto;max-width:600px;background:#ffffff;">
<table role="presentation" cellpadding="0" cellspacing="0" style="font-size:0px;width:100%;background:#ffffff;" align="center" border="0">
<tbody>
<tr>
<td style="text-align:center;vertical-align:top;direction:ltr;font-size:0px;padding:20px 0px 20px 0px;">
<!--[if mso | IE]>
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
<tr>
<td style="vertical-align:top;width:600px;">
<![endif]-->
<div class="mj-column-per-100 outlook-group-fix" style="vertical-align:top;display:inline-block;direction:ltr;font-size:13px;text-align:left;width:100%;">
<table role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
<tbody>
<%= yield(:procedure_logo) %>
<tr>
<td style="word-wrap:break-word;font-size:0px;padding:0px 25px 0px 25px;padding-top:0px;padding-bottom:0px;" align="left">
<div class="" style="cursor:auto;color:#55575d;font-family:Helvetica, Arial, sans-serif;font-size:13px;line-height:22px;text-align:left;">
<%= yield %>
</div>
</td>
</tr>
<% if content_for?(:footer) %>
<tr>
<td style="word-wrap:break-word;font-size:0px;padding:0px 25px 0px 25px;padding-top:0px;padding-bottom:0px;" align="left">
<p style="font-size:13px;text-align:left;">
</p>
<p style="cursor:auto;color:#55575d;font-family:Helvetica, Arial, sans-serif;font-size:12px;font-weight:bold;line-height:22px;text-align:left;">
<%= yield(:footer) %>
</p>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
<!--[if mso | IE]>
</td>
</tr>
</table>
<![endif]-->
</td>
</tr>
</tbody>
</table>
</div>
<!--[if mso | IE]>
</td>
</tr>
</table>
<![endif]-->
<!--[if mso | IE]>
<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="600" align="center" style="width:600px;">
<tr>
<td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;">
<![endif]-->
<div style="margin:0px auto;max-width:600px;">
<table role="presentation" cellpadding="0" cellspacing="0" style="font-size:0px;width:100%;" align="center" border="0">
<tbody>
<tr>
<td style="text-align:center;vertical-align:top;direction:ltr;font-size:0px;padding:20px 0px 20px 0px;">
<!--[if mso | IE]>
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
<tr>
<td style="vertical-align:top;width:600px;">
<![endif]-->
<div class="mj-column-per-100 outlook-group-fix" style="vertical-align:top;display:inline-block;direction:ltr;font-size:13px;text-align:left;width:100%;">
<table role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
<tbody>
<tr>
<td style="word-wrap:break-word;font-size:0px;padding:0px 20px 0px 20px;padding-top:0px;padding-bottom:0px;" align="center">
<div class="" style="cursor:auto;color:#55575d;font-family:Helvetica, Arial, sans-serif;font-size:11px;line-height:22px;text-align:center;">
demarches-simplifiees.fr est un service fourni par la DINSIC et incubé par beta.gouv.fr
</div>
</td>
</tr>
<tr>
<td style="word-wrap:break-word;font-size:0px;padding:0px 20px 0px 20px;padding-top:0px;padding-bottom:0px;" align="center">
<div class="" style="cursor:auto;color:#55575d;font-family:Helvetica, Arial, sans-serif;font-size:11px;line-height:22px;text-align:center;">
<p style="margin: 10px 0;">
</p>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!--[if mso | IE]>
</td>
</tr>
</table>
<![endif]-->
</td>
</tr>
</tbody>
</table>
</div>
<!--[if mso | IE]>
</td>
</tr>
</table>
<in![endif]-->
</div>
</body>
</html>

View file

@ -0,0 +1,4 @@
%p
Bonne journée,
%p --nom du service--

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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

View file

@ -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 }

View file

@ -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

View file

@ -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

View file

@ -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 ?", "%<img%").pluck(:procedure_id).uniq).order("RANDOM()").first
NotificationMailer.send_initiated_notification(p.dossiers.last)
end
def send_closed_notification

View file

@ -61,6 +61,7 @@ describe ProcedurePresentation do
{ "label" => '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

View file

@ -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