2019-07-10-01 (#4077)

2019-07-10-01
This commit is contained in:
Pierre de La Morinerie 2019-07-10 17:30:39 +02:00 committed by GitHub
commit 96766f0ad0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
52 changed files with 322 additions and 150 deletions

View file

@ -21,6 +21,14 @@
} }
} }
&.warning {
border-top: 8px solid $orange;
.card-title {
color: $orange;
}
}
&.feedback { &.feedback {
max-width: 600px; max-width: 600px;
margin: 30px auto; margin: 30px auto;

View file

@ -12,4 +12,8 @@
p { p {
margin: 16px auto; margin: 16px auto;
} }
.email-address {
font-weight: bold;
}
} }

View file

@ -265,7 +265,7 @@ class Admin::ProceduresController < AdminController
end end
def procedure_params def procedure_params
editable_params = [:libelle, :description, :organisation, :direction, :lien_site_web, :cadre_juridique, :deliberation, :notice, :expects_multiple_submissions, :web_hook_url, :euro_flag, :logo, :auto_archive_on] editable_params = [:libelle, :description, :organisation, :direction, :lien_site_web, :cadre_juridique, :deliberation, :notice, :web_hook_url, :euro_flag, :logo, :auto_archive_on]
if @procedure&.locked? if @procedure&.locked?
params.require(:procedure).permit(*editable_params) params.require(:procedure).permit(*editable_params)
else else

View file

@ -13,7 +13,7 @@ class FranceConnect::ParticulierController < ApplicationController
fetched_fci.tap(&:save) fetched_fci.tap(&:save)
if fci.user.nil? if fci.user.nil?
user = User.find_or_create_by(email: fci.email_france_connect) do |new_user| user = User.find_or_create_by!(email: fci.email_france_connect.downcase) do |new_user|
new_user.password = Devise.friendly_token[0, 20] new_user.password = Devise.friendly_token[0, 20]
new_user.confirmed_at = Time.zone.now new_user.confirmed_at = Time.zone.now
end end

View file

@ -138,7 +138,7 @@ module Gestionnaires
end end
def commentaire_params def commentaire_params
params.require(:commentaire).permit(:body, :file) params.require(:commentaire).permit(:body, :piece_jointe)
end end
end end
end end

View file

@ -166,7 +166,7 @@ module Gestionnaires
end end
def commentaire_params def commentaire_params
params.require(:commentaire).permit(:body, :file) params.require(:commentaire).permit(:body, :piece_jointe)
end end
def champs_private_params def champs_private_params

View file

@ -52,7 +52,7 @@ class SupportController < ApplicationController
email: email, email: email,
phone: params[:phone], phone: params[:phone],
text: params[:text], text: params[:text],
file: params[:file], file: params[:piece_jointe],
dossier_id: dossier&.id, dossier_id: dossier&.id,
browser: browser_name, browser: browser_name,
tags: tags tags: tags
@ -61,7 +61,7 @@ class SupportController < ApplicationController
def create_commentaire def create_commentaire
attributes = { attributes = {
file: params[:file], piece_jointe: params[:piece_jointe],
body: "[#{params[:subject]}]<br><br>#{params[:text]}" body: "[#{params[:subject]}]<br><br>#{params[:text]}"
} }
commentaire = CommentaireService.build(current_user, dossier, attributes) commentaire = CommentaireService.build(current_user, dossier, attributes)

View file

@ -354,7 +354,7 @@ module Users
end end
def commentaire_params def commentaire_params
params.require(:commentaire).permit(:body, :file) params.require(:commentaire).permit(:body, :piece_jointe)
end end
def passage_en_construction? def passage_en_construction?

View file

@ -12,8 +12,9 @@ module Users
def update_email def update_email
if @current_user.update(update_email_params) if @current_user.update(update_email_params)
flash.notice = t('devise.registrations.update_needs_confirmation') flash.notice = t('devise.registrations.update_needs_confirmation')
# to avoid leaking who has signed in
elsif @current_user.errors&.details&.dig(:email)&.any? { |e| e[:error] == :taken } elsif @current_user.errors&.details&.dig(:email)&.any? { |e| e[:error] == :taken }
UserMailer.account_already_taken(@current_user, requested_email).deliver_later
# avoid leaking information about whether an account with this email exists or not
flash.notice = t('devise.registrations.update_needs_confirmation') flash.notice = t('devise.registrations.update_needs_confirmation')
else else
flash.alert = @current_user.errors.full_messages flash.alert = @current_user.errors.full_messages
@ -27,5 +28,9 @@ module Users
def update_email_params def update_email_params
params.require(:user).permit(:email) params.require(:user).permit(:email)
end end
def requested_email
update_email_params[:email]
end
end end
end end

View file

@ -22,7 +22,6 @@ class TypesDeChampEditor extends Component {
type_champ: 'text', type_champ: 'text',
types_de_champ: [], types_de_champ: [],
private: props.isAnnotation, private: props.isAnnotation,
drop_down_list_value: '--Premier élément du menu--\n',
libelle: `${ libelle: `${
props.isAnnotation ? 'Nouvelle annotation' : 'Nouveau champ' props.isAnnotation ? 'Nouvelle annotation' : 'Nouveau champ'
} ${props.typeDeChampsTypes[0][0]}` } ${props.typeDeChampsTypes[0][0]}`

View file

@ -115,6 +115,18 @@ function updateTypeDeChamp(
{ typeDeChamp, field, value }, { typeDeChamp, field, value },
done done
) { ) {
if (field == 'type_champ' && !typeDeChamp.drop_down_list_value) {
switch (value) {
case 'linked_drop_down_list':
typeDeChamp.drop_down_list_value =
'--Fromage--\nbleu de sassenage\npicodon\n--Dessert--\néclair\ntarte aux pommes\n';
break;
case 'drop_down_list':
case 'multiple_drop_down_list':
typeDeChamp.drop_down_list_value = '--Premier élément du menu--\n';
}
}
typeDeChamp[field] = value; typeDeChamp[field] = value;
getUpdateHandler(typeDeChamp, state)(done); getUpdateHandler(typeDeChamp, state)(done);

View file

@ -1,10 +1,5 @@
module BizDev module BizDev
BIZ_DEV_MAPPING = { BIZ_DEV_MAPPING = {
8 =>
{
full_name: "Camille Garrigue",
pipedrive_id: 3189424
},
9 => 9 =>
{ {
full_name: "Philippe Vrignaud", full_name: "Philippe Vrignaud",
@ -14,11 +9,6 @@ module BizDev
{ {
full_name: "Benjamin Doberset", full_name: "Benjamin Doberset",
pipedrive_id: 4223834 pipedrive_id: 4223834
},
11 =>
{
full_name: "Rédouane Bouchane",
pipedrive_id: 4438645
} }
} }

View file

@ -8,4 +8,12 @@ class UserMailer < ApplicationMailer
mail(to: user.email, subject: @subject) mail(to: user.email, subject: @subject)
end end
def account_already_taken(user, requested_email)
@user = user
@requested_email = requested_email
@subject = "Changement dadresse email"
mail(to: requested_email, subject: @subject)
end
end end

View file

@ -5,9 +5,10 @@ class Commentaire < ApplicationRecord
belongs_to :gestionnaire belongs_to :gestionnaire
mount_uploader :file, CommentaireFileUploader mount_uploader :file, CommentaireFileUploader
validates :file, file_size: { maximum: 20.megabytes, message: "La taille du fichier doit être inférieure à 20 Mo" }
validate :is_virus_free?
validate :messagerie_available?, on: :create validate :messagerie_available?, on: :create
has_one_attached :piece_jointe
validates :body, presence: { message: "Votre message ne peut être vide" } validates :body, presence: { message: "Votre message ne peut être vide" }
default_scope { order(created_at: :asc) } default_scope { order(created_at: :asc) }
@ -47,10 +48,15 @@ class Commentaire < ApplicationRecord
end end
def file_url def file_url
if Flipflop.remote_storage? if piece_jointe.attached?
if piece_jointe.virus_scanner.safe?
Rails.application.routes.url_helpers.url_for(piece_jointe)
end
elsif Flipflop.remote_storage?
RemoteDownloader.new(file.path).url RemoteDownloader.new(file.path).url
else elsif file&.url
file.url # FIXME: this is horrible but used only in dev and will be removed after migration
File.join(LOCAL_DOWNLOAD_URL, file.url)
end end
end end
@ -74,12 +80,6 @@ class Commentaire < ApplicationRecord
DossierMailer.notify_new_answer(dossier).deliver_later DossierMailer.notify_new_answer(dossier).deliver_later
end end
def is_virus_free?
if file.present? && file_changed? && !ClamavService.safe_file?(file.path)
errors.add(:file, "Virus détecté dans le fichier joint, merci de changer de fichier")
end
end
def messagerie_available? def messagerie_available?
return if sent_by_system? return if sent_by_system?
if dossier.present? && !dossier.messagerie_available? if dossier.present? && !dossier.messagerie_available?

View file

@ -1,6 +1,8 @@
class Dossier < ApplicationRecord class Dossier < ApplicationRecord
include DossierFilteringConcern include DossierFilteringConcern
self.ignored_columns = [:expects_multiple_submissions]
enum state: { enum state: {
brouillon: 'brouillon', brouillon: 'brouillon',
en_construction: 'en_construction', en_construction: 'en_construction',
@ -115,7 +117,7 @@ class Dossier < ApplicationRecord
scope :nearing_end_of_retention, -> (duration = '1 month') { joins(:procedure).where("en_instruction_at + (duree_conservation_dossiers_dans_ds * interval '1 month') - now() < interval ?", duration) } scope :nearing_end_of_retention, -> (duration = '1 month') { joins(:procedure).where("en_instruction_at + (duree_conservation_dossiers_dans_ds * interval '1 month') - now() < interval ?", duration) }
scope :since, -> (since) { where('dossiers.en_construction_at >= ?', since) } scope :since, -> (since) { where('dossiers.en_construction_at >= ?', since) }
scope :for_api, -> { scope :for_api, -> {
includes(commentaires: [], includes(commentaires: { piece_jointe_attachment: :blob },
champs: [ champs: [
:geo_areas, :geo_areas,
:etablissement, :etablissement,

View file

@ -13,6 +13,11 @@ class CommentaireService
def build_with_email(email, dossier, params) def build_with_email(email, dossier, params)
attributes = params.merge(email: email, dossier: dossier) attributes = params.merge(email: email, dossier: dossier)
# For some reason ActiveStorage trows an error in tests if we passe an empty string here.
# I suspect it could be resolved in rails 6 by using explicit `attach()`
if attributes[:piece_jointe].blank?
attributes.delete(:piece_jointe)
end
Commentaire.new(attributes) Commentaire.new(attributes)
end end
end end

View file

@ -14,7 +14,7 @@ class LocalDownloader
end end
def url def url
@url ||= File.join(TPS::Application::URL, 'downloads', random_folder_name, "#{@filename_suffix}.#{@extension}") @url ||= File.join(LOCAL_DOWNLOAD_URL, 'downloads', random_folder_name, "#{@filename_suffix}.#{@extension}")
end end
protected protected

View file

@ -127,15 +127,6 @@
.col-md-6 .col-md-6
%h4 Options avancées %h4 Options avancées
= f.label :expects_multiple_submissions do
= f.check_box :expects_multiple_submissions
Ajuster pour le dépôt récurrent de dossiers
%p.help-block
%i.fa.fa-info-circle
Si cette démarche est conçue pour quune même personne y dépose régulièrement plusieurs
dossiers, linterface est ajustée pour rendre plus facile la création de plusieurs dossiers
à la suite.
- if Flipflop.web_hook? - if Flipflop.web_hook?
%label{ for: :web_hook_url } Lien de rappel HTTP (webhook) %label{ for: :web_hook_url } Lien de rappel HTTP (webhook)
= f.text_field :web_hook_url, class: 'form-control', placeholder: 'https://callback.exemple.fr/' = f.text_field :web_hook_url, class: 'form-control', placeholder: 'https://callback.exemple.fr/'

View file

@ -15,4 +15,4 @@
Nous restons à votre disposition si vous avez besoin daccompagnement. Nous restons à votre disposition si vous avez besoin daccompagnement.
%p %p
= render partial: "layouts/mailers/bizdev_signature", locals: { author_name: "Camille Garrigue" } = render partial: "layouts/mailers/bizdev_signature", locals: { author_name: "Benjamin Doberset" }

View file

@ -11,7 +11,7 @@
%h4 En construction %h4 En construction
Vous permettez à l'usager de modifier ses réponses au formulaire Vous permettez à l'usager de modifier ses réponses au formulaire
%li %li
= link_to passer_en_instruction_gestionnaire_dossier_path(dossier.procedure, dossier), method: :post, data: { remote: true, confirm: "Confirmer vous le passage en instruction de ce dossier ?" } do = link_to passer_en_instruction_gestionnaire_dossier_path(dossier.procedure, dossier), method: :post, data: { remote: true, confirm: "Confirmez-vous le passage en instruction de ce dossier ?" } do
%span.icon.in-progress %span.icon.in-progress
.dropdown-description .dropdown-description
%h4 Passer en instruction %h4 Passer en instruction
@ -19,7 +19,7 @@
- if dossier.en_instruction? - if dossier.en_instruction?
%li %li
= link_to repasser_en_construction_gestionnaire_dossier_path(dossier.procedure, dossier), method: :post, data: { remote:true, confirm: "Confirmer vous le passage en construction de ce dossier ?" } do = link_to repasser_en_construction_gestionnaire_dossier_path(dossier.procedure, dossier), method: :post, data: { remote:true, confirm: "Confirmez-vous le passage en construction de ce dossier ?" } do
%span.icon.edit %span.icon.edit
.dropdown-description .dropdown-description
%h4 Repasser en construction %h4 Repasser en construction

View file

@ -61,7 +61,7 @@
.messagerie .messagerie
%ul.messages-list %ul.messages-list
- @dossier.commentaires.each do |commentaire| - @dossier.commentaires.with_attached_piece_jointe.each do |commentaire|
%li %li
= render partial: "shared/dossiers/messages/message", locals: { commentaire: commentaire, connected_user: current_gestionnaire, messagerie_seen_at: nil } = render partial: "shared/dossiers/messages/message", locals: { commentaire: commentaire, connected_user: current_gestionnaire, messagerie_seen_at: nil }

View file

@ -26,11 +26,11 @@
= link_to admin_procedures_path, class: "menu-item menu-link" do = link_to admin_procedures_path, class: "menu-item menu-link" do
= image_tag "icons/switch-profile.svg" = image_tag "icons/switch-profile.svg"
Passer en administrateur Passer en administrateur
%li
= link_to profil_path, class: "menu-item menu-link" do
= image_tag "icons/switch-profile.svg"
Voir mon profil
%li
= link_to profil_path, class: "menu-item menu-link" do
= image_tag "icons/switch-profile.svg"
Voir mon profil
%li %li
= link_to destroy_user_session_path, method: :delete, class: "menu-item menu-link" do = link_to destroy_user_session_path, method: :delete, class: "menu-item menu-link" do
= image_tag "icons/sign-out.svg" = image_tag "icons/sign-out.svg"

View file

@ -157,6 +157,11 @@
Titre de la carte mise en avant Titre de la carte mise en avant
%p Et voici le contenu de la carte %p Et voici le contenu de la carte
.card.warning
.card-title
Titre de la carte davertissement
%p Et voici le contenu de la carte
.card.feedback .card.feedback
.card-title .card-title
Titre de la carte pour demander un avis Titre de la carte pour demander un avis

View file

@ -1,6 +1,6 @@
.messagerie.container .messagerie.container
%ul.messages-list %ul.messages-list
- dossier.commentaires.each do |commentaire| - dossier.commentaires.with_attached_piece_jointe.each do |commentaire|
%li.message{ class: commentaire_is_from_me_class(commentaire, connected_user) } %li.message{ class: commentaire_is_from_me_class(commentaire, connected_user) }
= render partial: "shared/dossiers/messages/message", locals: { commentaire: commentaire, connected_user: connected_user, messagerie_seen_at: messagerie_seen_at } = render partial: "shared/dossiers/messages/message", locals: { commentaire: commentaire, connected_user: connected_user, messagerie_seen_at: messagerie_seen_at }

View file

@ -2,8 +2,8 @@
= f.text_area :body, rows: 5, placeholder: 'Répondre ici', required: true, class: 'message-textarea' = f.text_area :body, rows: 5, placeholder: 'Répondre ici', required: true, class: 'message-textarea'
.flex.justify-between.wrap .flex.justify-between.wrap
%div %div
= f.file_field :file, id: :file, accept: commentaire.file.accept_extension_list = f.file_field :piece_jointe, id: 'piece_jointe', direct_upload: true
%label{ for: :file } %label{ for: :piece_jointe }
.notice .notice
(taille max : 20 Mo) (taille max : 20 Mo)

View file

@ -10,7 +10,10 @@
= commentaire_date(commentaire) = commentaire_date(commentaire)
.rich-text= sanitize(simple_format(commentaire.body)) .rich-text= sanitize(simple_format(commentaire.body))
- if commentaire.file.present? - if commentaire.piece_jointe.attached?
.attachment-link
= render partial: "shared/attachment/show", locals: { attachment: commentaire.piece_jointe.attachment }
- elsif commentaire.file.present?
.attachment-link .attachment-link
= link_to commentaire.file_url, class: "button", target: "_blank", rel: "noopener", title: "Télécharger" do = link_to commentaire.file_url, class: "button", target: "_blank", rel: "noopener", title: "Télécharger" do
%span.icon.attachment %span.icon.attachment

View file

@ -76,7 +76,7 @@
Une capture décran peut nous aider à identifier plus facilement lendroit à améliorer. Une capture décran peut nous aider à identifier plus facilement lendroit à améliorer.
.notice.hidden{ data: { 'contact-type-only': Helpscout::FormAdapter::TYPE_AUTRE } } .notice.hidden{ data: { 'contact-type-only': Helpscout::FormAdapter::TYPE_AUTRE } }
Une capture décran peut nous aider à identifier plus facilement le problème. Une capture décran peut nous aider à identifier plus facilement le problème.
= file_field_tag :file = file_field_tag :piece_jointe
= hidden_field_tag :tags, @tags&.join(',') = hidden_field_tag :tags, @tags&.join(',')

View file

@ -0,0 +1,20 @@
- content_for(:title, @subject)
%p
Bonjour,
%p
Lutilisateur « #{@user.email} » a demandé le changement de son adresse vers « #{@requested_email} ».
%p
Malheureusement, votre compte « #{@requested_email} » existe déjà. Nous ne pouvons pas fusionner automatiquement vos comptes.
%p
%strong Nous ne pouvons donc pas effectuer le changement dadresse email.
%p
Si vous n'êtes pas à lorigine de cette demande, vous pouvez ignorer ce message. Et si vous avez besoin dassistance, nhésitez pas à nous contacter à
= succeed '.' do
= mail_to CONTACT_EMAIL
= render partial: "layouts/mailers/signature"

View file

@ -1,5 +1,5 @@
- has_delete_action = dossier.can_be_deleted_by_user? - has_delete_action = dossier.can_be_deleted_by_user?
- has_new_dossier_action = dossier.procedure.expects_multiple_submissions? && dossier.procedure.accepts_new_dossiers? - has_new_dossier_action = dossier.procedure.accepts_new_dossiers?
- has_actions = has_delete_action || has_new_dossier_action - has_actions = has_delete_action || has_new_dossier_action

View file

@ -20,4 +20,6 @@
et et
%b échanger avec un instructeur. %b échanger avec un instructeur.
= link_to 'Accéder à votre dossier', dossier_path(@dossier), class: 'button large primary' .flex.column.align-center
= link_to 'Accéder à votre dossier', dossier_path(@dossier), class: 'button large primary'
= link_to 'Déposer un autre dossier', procedure_lien(@dossier.procedure)

View file

@ -7,12 +7,16 @@
.card .card
.card-title Coordonnées .card-title Coordonnées
%p Votre email est actuellement #{current_user.email} %p
Votre email est actuellement
%span.email-address= current_user.email
- if current_user.unconfirmed_email.present? - if current_user.unconfirmed_email.present?
%p .card.warning
Un email a été envoyé à #{current_user.unconfirmed_email}. .card-title
%br Changement en attente :
Merci de vérifier vos emails et de cliquer sur le lien dactivation pour finaliser la validation de votre nouvelle adresse. %span.email-address= current_user.unconfirmed_email
%p
Pour finaliser votre changement dadresse, vérifiez vos emails et cliquez sur le lien de confirmation.
= form_for @current_user, url: update_email_path, method: :patch, html: { class: 'form' } do |f| = form_for @current_user, url: update_email_path, method: :patch, html: { class: 'form' } do |f|
= f.email_field :email, value: nil, placeholder: 'Nouvelle adresse email', required: true = f.email_field :email, value: nil, placeholder: 'Nouvelle adresse email', required: true

View file

@ -31,8 +31,6 @@ module TPS
config.assets.paths << Rails.root.join('app', 'assets', 'fonts') config.assets.paths << Rails.root.join('app', 'assets', 'fonts')
config.assets.precompile += ['.woff'] config.assets.precompile += ['.woff']
URL = ENV['APP_HOST'] || "http://localhost:3000/"
config.active_job.queue_adapter = :delayed_job config.active_job.queue_adapter = :delayed_job
config.action_view.sanitized_allowed_tags = ActionView::Base.sanitized_allowed_tags + ['u'] config.action_view.sanitized_allowed_tags = ActionView::Base.sanitized_allowed_tags + ['u']

View file

@ -28,3 +28,6 @@ FAQ_ADMIN_URL = "https://faq.demarches-simplifiees.fr/collection/1-administrateu
COMMENT_TROUVER_MA_DEMARCHE_URL = [FAQ_URL, "article", "59-comment-trouver-ma-demarche"].join("/") COMMENT_TROUVER_MA_DEMARCHE_URL = [FAQ_URL, "article", "59-comment-trouver-ma-demarche"].join("/")
STATUS_PAGE_URL = "https://status.demarches-simplifiees.fr" STATUS_PAGE_URL = "https://status.demarches-simplifiees.fr"
MATOMO_IFRAME_URL = "https://stats.data.gouv.fr/index.php?module=CoreAdminHome&action=optOut&language=fr&&fontColor=333333&fontSize=16px&fontFamily=Muli" MATOMO_IFRAME_URL = "https://stats.data.gouv.fr/index.php?module=CoreAdminHome&action=optOut&language=fr&&fontColor=333333&fontSize=16px&fontFamily=Muli"
# FIXME: This is only used in dev in couple of places and should be removed after PJ migration
LOCAL_DOWNLOAD_URL = "http://#{ENV.fetch('APP_HOST', 'localhost:3000')}"

View file

@ -42,7 +42,7 @@ fr:
signed_up_but_inactive: "Vous êtes bien enregistré. Vous ne pouvez cependant pas vous connecter car votre compte n'est pas encore activé." signed_up_but_inactive: "Vous êtes bien enregistré. Vous ne pouvez cependant pas vous connecter car votre compte n'est pas encore activé."
signed_up_but_locked: "Vous êtes bien enregistré. Vous ne pouvez cependant pas vous connecter car votre compte est verrouillé." signed_up_but_locked: "Vous êtes bien enregistré. Vous ne pouvez cependant pas vous connecter car votre compte est verrouillé."
signed_up_but_unconfirmed: "Nous vous avons envoyé un email contenant un lien d'activation. Ouvrez ce lien pour activer votre compte." signed_up_but_unconfirmed: "Nous vous avons envoyé un email contenant un lien d'activation. Ouvrez ce lien pour activer votre compte."
update_needs_confirmation: "Votre compte a bien été mis à jour mais nous devons vérifier votre nouvelle adresse email. Merci de vérifier vos emails et de cliquer sur le lien dactivation pour finaliser la validation de votre nouvelle adresse." update_needs_confirmation: "Vous devez confirmer votre nouvelle adresse email. Vérifiez vos emails, et cliquez sur le lien de confirmation pour confirmer votre changement dadresse."
updated: "Votre compte a été modifié avec succès." updated: "Votre compte a été modifié avec succès."
sessions: sessions:
signed_in: "Connecté." signed_in: "Connecté."

View file

@ -0,0 +1,37 @@
namespace :'2019_05_29_migrate_commentaire_pj' do
task run: :environment do
commentaires = Commentaire.where
.not(file: nil)
.left_joins(:piece_jointe_attachment)
.where('active_storage_attachments.id IS NULL')
.order(:created_at)
limit = ENV['LIMIT']
if limit
commentaires.limit!(limit.to_i)
end
progress = ProgressReport.new(commentaires.count)
commentaires.find_each do |commentaire|
if commentaire.file.present?
uri = URI.parse(URI.escape(commentaire.file_url))
response = Typhoeus.get(uri)
if response.success?
filename = commentaire.file.filename || commentaire.file_identifier
updated_at = commentaire.updated_at
dossier_updated_at = commentaire.dossier.updated_at
commentaire.piece_jointe.attach(
io: StringIO.new(response.body),
filename: filename,
content_type: commentaire.file.content_type,
metadata: { virus_scan_result: ActiveStorage::VirusScanner::SAFE }
)
commentaire.update_column(:updated_at, updated_at)
commentaire.dossier.update_column(:updated_at, dossier_updated_at)
end
end
progress.inc
end
progress.finish
end
end

View file

@ -71,11 +71,30 @@ describe FranceConnect::ParticulierController, type: :controller do
end end
context 'when france_connect_particulier_id does not have an associate user' do context 'when france_connect_particulier_id does not have an associate user' do
it { is_expected.to redirect_to(root_path) } context 'when the email address is not used yet' do
it { expect { subject }.to change(User, :count).by(1) }
it { is_expected.to redirect_to(root_path) }
end
it do context 'when the email address is already used' do
subject let!(:user) { create(:user, email: email, france_connect_information: nil) }
expect(User.find_by(email: email)).not_to be_nil
it 'associates the france_connect infos with the existing user' do
expect { subject }.not_to change(User, :count)
expect(user.reload.loged_in_with_france_connect).to eq(User.loged_in_with_france_connects.fetch(:particulier))
expect(subject).to redirect_to(root_path)
end
end
context 'when a differently cased email address is already used' do
let(:email) { 'TEST@test.com' }
let!(:user) { create(:user, email: email.downcase, france_connect_information: nil) }
it 'associates the france_connect infos with the existing user' do
expect { subject }.not_to change(User, :count)
expect(user.reload.loged_in_with_france_connect).to eq(User.loged_in_with_france_connects.fetch(:particulier))
expect(subject).to redirect_to(root_path)
end
end end
end end
end end

View file

@ -92,7 +92,7 @@ describe Gestionnaires::AvisController, type: :controller do
let(:file) { nil } let(:file) { nil }
let(:scan_result) { true } let(:scan_result) { true }
subject { post :create_commentaire, params: { id: avis_without_answer.id, commentaire: { body: 'commentaire body', file: file } } } subject { post :create_commentaire, params: { id: avis_without_answer.id, commentaire: { body: 'commentaire body', piece_jointe: file } } }
before do before do
allow(ClamavService).to receive(:safe_file?).and_return(scan_result) allow(ClamavService).to receive(:safe_file?).and_return(scan_result)
@ -110,16 +110,10 @@ describe Gestionnaires::AvisController, type: :controller do
it do it do
subject subject
expect(Commentaire.last.file.path).to include("piece_justificative_0.pdf") expect(Commentaire.last.piece_jointe.filename).to eq("piece_justificative_0.pdf")
end end
it { expect { subject }.to change(Commentaire, :count).by(1) } it { expect { subject }.to change(Commentaire, :count).by(1) }
context "and a virus" do
let(:scan_result) { false }
it { expect { subject }.not_to change(Commentaire, :count) }
end
end end
end end

View file

@ -350,15 +350,15 @@ describe Gestionnaires::DossiersController, type: :controller do
expect(flash.notice).to be_present expect(flash.notice).to be_present
end end
context "when the commentaire creation fails" do context "when the commentaire created with virus file" do
let(:scan_result) { false } let(:scan_result) { false }
it "renders the messagerie page with the invalid commentaire" do it "creates a commentaire (shows message that file have a virus)" do
expect { subject }.not_to change(Commentaire, :count) expect { subject }.to change(Commentaire, :count).by(1)
expect(gestionnaire.followed_dossiers).to include(dossier)
expect(response).to render_template :messagerie expect(response).to redirect_to(messagerie_gestionnaire_dossier_path(dossier.procedure, dossier))
expect(flash.alert).to be_present expect(flash.notice).to be_present
expect(assigns(:commentaire).body).to eq("avant\napres")
end end
end end
end end

View file

@ -806,7 +806,7 @@ describe Users::DossiersController, type: :controller do
id: dossier.id, id: dossier.id,
commentaire: { commentaire: {
body: body, body: body,
file: file piece_jointe: file
} }
} }
} }
@ -822,18 +822,6 @@ describe Users::DossiersController, type: :controller do
expect(response).to redirect_to(messagerie_dossier_path(dossier)) expect(response).to redirect_to(messagerie_dossier_path(dossier))
expect(flash.notice).to be_present expect(flash.notice).to be_present
end end
context "when the commentaire creation fails" do
let(:scan_result) { false }
it "renders the messagerie page with the invalid commentaire" do
expect { subject }.not_to change(Commentaire, :count)
expect(response).to render_template :messagerie
expect(flash.alert).to be_present
expect(assigns(:commentaire).body).to eq("avant\napres")
end
end
end end
describe '#ask_deletion' do describe '#ask_deletion' do

View file

@ -1,6 +1,8 @@
require 'spec_helper' require 'spec_helper'
describe Users::ProfilController, type: :controller do describe Users::ProfilController, type: :controller do
include ActiveJob::TestHelper
let(:user) { create(:user) } let(:user) { create(:user) }
before { sign_in(user) } before { sign_in(user) }
@ -34,13 +36,17 @@ describe Users::ProfilController, type: :controller do
end end
context 'when the mail is already taken' do context 'when the mail is already taken' do
let!(:user2) { create(:user) } let(:existing_user) { create(:user) }
before do before do
patch :update_email, params: { user: { email: user2.email } } perform_enqueued_jobs do
patch :update_email, params: { user: { email: existing_user.email } }
end
user.reload user.reload
end end
it { expect(user.unconfirmed_email).to be_nil }
it { expect(ActionMailer::Base.deliveries.last.to).to eq([existing_user.email]) }
it { expect(response).to redirect_to(profil_path) } it { expect(response).to redirect_to(profil_path) }
it { expect(flash.notice).to eq(I18n.t('devise.registrations.update_needs_confirmation')) } it { expect(flash.notice).to eq(I18n.t('devise.registrations.update_needs_confirmation')) }
end end

View file

@ -41,12 +41,12 @@ FactoryBot.define do
factory :champ_date, class: 'Champs::DateChamp' do factory :champ_date, class: 'Champs::DateChamp' do
type_de_champ { create(:type_de_champ_date) } type_de_champ { create(:type_de_champ_date) }
value { 1.day.ago.iso8601 } value { '2019-07-10' }
end end
factory :champ_datetime, class: 'Champs::DatetimeChamp' do factory :champ_datetime, class: 'Champs::DatetimeChamp' do
type_de_champ { create(:type_de_champ_datetime) } type_de_champ { create(:type_de_champ_datetime) }
value { 1.day.ago.iso8601 } value { '15/09/1962 15:35' }
end end
factory :champ_number, class: 'Champs::NumberChamp' do factory :champ_number, class: 'Champs::NumberChamp' do
@ -96,17 +96,17 @@ FactoryBot.define do
factory :champ_drop_down_list, class: 'Champs::DropDownListChamp' do factory :champ_drop_down_list, class: 'Champs::DropDownListChamp' do
type_de_champ { create(:type_de_champ_drop_down_list) } type_de_champ { create(:type_de_champ_drop_down_list) }
value { '' } value { 'choix 1' }
end end
factory :champ_multiple_drop_down_list, class: 'Champs::MultipleDropDownListChamp' do factory :champ_multiple_drop_down_list, class: 'Champs::MultipleDropDownListChamp' do
type_de_champ { create(:type_de_champ_multiple_drop_down_list) } type_de_champ { create(:type_de_champ_multiple_drop_down_list) }
value { '' } value { '["choix 1", "choix 2"]' }
end end
factory :champ_linked_drop_down_list, class: 'Champs::LinkedDropDownListChamp' do factory :champ_linked_drop_down_list, class: 'Champs::LinkedDropDownListChamp' do
type_de_champ { create(:type_de_champ_linked_drop_down_list) } type_de_champ { create(:type_de_champ_linked_drop_down_list) }
value { '{}' } value { '["categorie 1", "choix 1"]' }
end end
factory :champ_pays, class: 'Champs::PaysChamp' do factory :champ_pays, class: 'Champs::PaysChamp' do
@ -116,12 +116,12 @@ FactoryBot.define do
factory :champ_regions, class: 'Champs::RegionChamp' do factory :champ_regions, class: 'Champs::RegionChamp' do
type_de_champ { create(:type_de_champ_regions) } type_de_champ { create(:type_de_champ_regions) }
value { '' } value { 'Guadeloupe' }
end end
factory :champ_departements, class: 'Champs::DepartementChamp' do factory :champ_departements, class: 'Champs::DepartementChamp' do
type_de_champ { create(:type_de_champ_departements) } type_de_champ { create(:type_de_champ_departements) }
value { '' } value { '971 - Guadeloupe' }
end end
factory :champ_engagement, class: 'Champs::EngagementChamp' do factory :champ_engagement, class: 'Champs::EngagementChamp' do
@ -136,7 +136,7 @@ FactoryBot.define do
factory :champ_explication, class: 'Champs::ExplicationChamp' do factory :champ_explication, class: 'Champs::ExplicationChamp' do
type_de_champ { create(:type_de_champ_explication) } type_de_champ { create(:type_de_champ_explication) }
value { 'une explication' } value { '' }
end end
factory :champ_dossier_link, class: 'Champs::DossierLinkChamp' do factory :champ_dossier_link, class: 'Champs::DossierLinkChamp' do
@ -164,5 +164,15 @@ FactoryBot.define do
factory :champ_repetition, class: 'Champs::RepetitionChamp' do factory :champ_repetition, class: 'Champs::RepetitionChamp' do
type_de_champ { create(:type_de_champ_repetition) } type_de_champ { create(:type_de_champ_repetition) }
after(:build) do |champ_repetition, _evaluator|
type_de_champ_text = create(:type_de_champ_text, order_place: 0, parent: champ_repetition.type_de_champ, libelle: 'Nom')
type_de_champ_number = create(:type_de_champ_number, order_place: 1, parent: champ_repetition.type_de_champ, libelle: 'Age')
create(:champ_text, row: 0, type_de_champ: type_de_champ_text, parent: champ_repetition)
create(:champ_number, row: 0, type_de_champ: type_de_champ_number, parent: champ_repetition)
create(:champ_text, row: 1, type_de_champ: type_de_champ_text, parent: champ_repetition)
create(:champ_number, row: 1, type_de_champ: type_de_champ_number, parent: champ_repetition)
end
end end
end end

View file

@ -7,5 +7,9 @@ FactoryBot.define do
commentaire.dossier = create :dossier, :en_construction commentaire.dossier = create :dossier, :en_construction
end end
end end
trait :with_file do
file { Rack::Test::UploadedFile.new("./spec/fixtures/files/logo_test_procedure.png", 'application/pdf') }
end
end end
end end

View file

@ -19,14 +19,15 @@ feature 'Changing an email' do
click_button 'Changer mon adresse' click_button 'Changer mon adresse'
end end
user.reload expect(page).to have_content(I18n.t('devise.registrations.update_needs_confirmation'))
expect(user.email).to eq(old_email) expect(page).to have_content(old_email)
expect(user.unconfirmed_email).to eq(new_email) expect(page).to have_content(new_email)
click_confirmation_link_for(new_email) click_confirmation_link_for(new_email)
user.reload expect(page).to have_content(I18n.t('devise.confirmations.confirmed'))
expect(user.email).to eq(new_email) expect(page).not_to have_content(old_email)
expect(user.unconfirmed_email).to be_nil expect(page).to have_content(new_email)
expect(user.reload.email).to eq(new_email)
end end
end end

View file

@ -0,0 +1,42 @@
describe '2019_05_29_migrate_commentaire_pj.rake' do
let(:rake_task) { Rake::Task['2019_05_29_migrate_commentaire_pj:run'] }
let!(:commentaires) do
create(:commentaire)
create(:commentaire, :with_file)
create(:commentaire, :with_file)
end
before do
Commentaire.all.each do |commentaire|
if commentaire.file.present?
stub_request(:get, commentaire.file_url)
.to_return(status: 200, body: File.read(commentaire.file.path))
end
end
end
after do
ENV['LIMIT'] = nil
rake_task.reenable
end
it 'should migrate pj' do
comment_updated_at = Commentaire.last.updated_at
dossier_updated_at = Commentaire.last.dossier.updated_at
expect(Commentaire.all.map(&:piece_jointe).map(&:attached?)).to eq([false, false, false])
rake_task.invoke
expect(Commentaire.where(file: nil).count).to eq(1)
expect(Commentaire.all.map(&:piece_jointe).map(&:attached?)).to eq([false, true, true])
expect(Commentaire.last.updated_at).to eq(comment_updated_at)
expect(Commentaire.last.dossier.updated_at).to eq(dossier_updated_at)
end
it 'should migrate pj within limit' do
expect(Commentaire.all.map(&:piece_jointe).map(&:attached?)).to eq([false, false, false])
ENV['LIMIT'] = '1'
rake_task.invoke
expect(Commentaire.where(file: nil).count).to eq(1)
expect(Commentaire.all.map(&:piece_jointe).map(&:attached?)).to eq([false, true, false])
end
end

View file

@ -3,6 +3,10 @@ class UserMailerPreview < ActionMailer::Preview
UserMailer.new_account_warning(user) UserMailer.new_account_warning(user)
end end
def account_already_taken
UserMailer.account_already_taken(user, 'dircab@territoires.gouv.fr')
end
private private
def user def user

View file

@ -0,0 +1,21 @@
require "rails_helper"
RSpec.describe UserMailer, type: :mailer do
let(:user) { build(:user) }
describe '.new_account_warning' do
subject { described_class.new_account_warning(user) }
it { expect(subject.to).to eq([user.email]) }
it { expect(subject.body).to include(user.email) }
end
describe '.account_already_taken' do
let(:requested_email) { 'new@exemple.fr' }
subject { described_class.account_already_taken(user, requested_email) }
it { expect(subject.to).to eq([requested_email]) }
it { expect(subject.body).to include(requested_email) }
end
end

View file

@ -395,7 +395,7 @@ describe Champ do
describe "repetition" do describe "repetition" do
let(:dossier) { create(:dossier) } let(:dossier) { create(:dossier) }
let(:champ) { create(:champ_repetition, dossier: dossier) } let(:champ) { Champs::RepetitionChamp.create(dossier: dossier) }
let(:champ_text) { create(:champ_text, row: 0) } let(:champ_text) { create(:champ_text, row: 0) }
let(:champ_integer_number) { create(:champ_integer_number, row: 0) } let(:champ_integer_number) { create(:champ_integer_number, row: 0) }
let(:champ_text_attrs) { attributes_for(:champ_text, row: 1) } let(:champ_text_attrs) { attributes_for(:champ_text, row: 1) }

View file

@ -1,4 +1,4 @@
require 'spec_helper' require 'rails_helper'
describe Dossier do describe Dossier do
include ActiveJob::TestHelper include ActiveJob::TestHelper
@ -31,7 +31,7 @@ describe Dossier do
let(:procedure) { create(:procedure, duree_conservation_dossiers_dans_ds: 6) } let(:procedure) { create(:procedure, duree_conservation_dossiers_dans_ds: 6) }
let!(:young_dossier) { create(:dossier, procedure: procedure) } let!(:young_dossier) { create(:dossier, procedure: procedure) }
let!(:expiring_dossier) { create(:dossier, :en_instruction, en_instruction_at: 170.days.ago, procedure: procedure) } let!(:expiring_dossier) { create(:dossier, :en_instruction, en_instruction_at: 170.days.ago, procedure: procedure) }
let!(:just_expired_dossier) { create(:dossier, :en_instruction, en_instruction_at: (6.months + 1.hour + 1.second).ago, procedure: procedure) } let!(:just_expired_dossier) { create(:dossier, :en_instruction, en_instruction_at: (6.months + 1.hour + 10.seconds).ago, procedure: procedure) }
let!(:long_expired_dossier) { create(:dossier, :en_instruction, en_instruction_at: 1.year.ago, procedure: procedure) } let!(:long_expired_dossier) { create(:dossier, :en_instruction, en_instruction_at: 1.year.ago, procedure: procedure) }
context 'with default delay to end of retention' do context 'with default delay to end of retention' do
@ -420,7 +420,6 @@ describe Dossier do
it "send an email when the dossier is created for the very first time" do it "send an email when the dossier is created for the very first time" do
dossier = nil dossier = nil
ActiveJob::Base.queue_adapter = :test
expect do expect do
perform_enqueued_jobs do perform_enqueued_jobs do
dossier = Dossier.create(procedure: procedure, state: Dossier.states.fetch(:brouillon), user: user) dossier = Dossier.create(procedure: procedure, state: Dossier.states.fetch(:brouillon), user: user)
@ -887,7 +886,7 @@ describe Dossier do
describe "#check_mandatory_champs" do describe "#check_mandatory_champs" do
let(:procedure) { create(:procedure, :with_type_de_champ) } let(:procedure) { create(:procedure, :with_type_de_champ) }
let(:dossier) { create(:dossier, :with_all_champs, procedure: procedure) } let(:dossier) { create(:dossier, procedure: procedure) }
it 'no mandatory champs' do it 'no mandatory champs' do
expect(dossier.check_mandatory_champs).to be_empty expect(dossier.check_mandatory_champs).to be_empty
@ -946,7 +945,11 @@ describe Dossier do
end end
context "when no champs" do context "when no champs" do
let(:champ_with_error) { dossier.champs.first } let(:champ_with_error) do
repetition_champ = dossier.champs.first
text_champ = repetition_champ.rows.first.first
text_champ
end
it 'should have errors' do it 'should have errors' do
errors = dossier.check_mandatory_champs errors = dossier.check_mandatory_champs

View file

@ -41,7 +41,7 @@ describe DossierSerializer do
expect(subject[3][:value]).to eq(42) expect(subject[3][:value]).to eq(42)
expect(subject[4][:value]).to eq(42.1) expect(subject[4][:value]).to eq(42.1)
expect(subject[5][:value]).to eq({ primary: nil, secondary: nil }) expect(subject[5][:value]).to eq({ primary: 'categorie 1', secondary: 'choix 1' })
} }
end end
end end

View file

@ -1,24 +1,21 @@
require 'spec_helper' require 'spec_helper'
describe CommentaireService do describe CommentaireService do
include ActiveJob::TestHelper
describe '.create' do describe '.create' do
let(:dossier) { create :dossier, :en_construction } let(:dossier) { create :dossier, :en_construction }
let(:sender) { dossier.user } let(:sender) { dossier.user }
let(:body) { 'Contenu du message.' } let(:body) { 'Contenu du message.' }
let(:file) { nil } let(:file) { nil }
let(:scan_result) { true }
subject(:commentaire) { CommentaireService.build(sender, dossier, { body: body, file: file }) } subject(:commentaire) { CommentaireService.build(sender, dossier, { body: body, piece_jointe: file }) }
before do
allow(ClamavService).to receive(:safe_file?).and_return(scan_result)
end
it 'creates a new valid commentaire' do it 'creates a new valid commentaire' do
expect(commentaire.email).to eq sender.email expect(commentaire.email).to eq sender.email
expect(commentaire.dossier).to eq dossier expect(commentaire.dossier).to eq dossier
expect(commentaire.body).to eq 'Contenu du message.' expect(commentaire.body).to eq 'Contenu du message.'
expect(commentaire.file).to be_blank expect(commentaire.piece_jointe.attached?).to be_falsey
expect(commentaire).to be_valid expect(commentaire).to be_valid
end end
@ -34,14 +31,15 @@ describe CommentaireService do
context 'when it has a file' do context 'when it has a file' do
let(:file) { Rack::Test::UploadedFile.new("./spec/fixtures/files/piece_justificative_0.pdf", 'application/pdf') } let(:file) { Rack::Test::UploadedFile.new("./spec/fixtures/files/piece_justificative_0.pdf", 'application/pdf') }
it 'saves the attached file' do before do
expect(commentaire.file).to be_present expect(ClamavService).to receive(:safe_file?).and_return(true)
expect(commentaire).to be_valid
end end
context 'and a virus' do it 'saves the attached file' do
let(:scan_result) { false } perform_enqueued_jobs do
it { expect(commentaire).not_to be_valid } commentaire.save
expect(commentaire.piece_jointe.attached?).to be_truthy
end
end end
end end
end end

View file

@ -157,15 +157,6 @@ describe ProcedureExportV2Service do
context 'with repetitions' do context 'with repetitions' do
let!(:dossier) { create(:dossier, :en_instruction, :with_all_champs, :for_individual, procedure: procedure) } let!(:dossier) { create(:dossier, :en_instruction, :with_all_champs, :for_individual, procedure: procedure) }
let(:champ_repetition) { dossier.champs.find { |champ| champ.type_champ == 'repetition' } } let(:champ_repetition) { dossier.champs.find { |champ| champ.type_champ == 'repetition' } }
let(:type_de_champ_text) { create(:type_de_champ_text, order_place: 0, parent: champ_repetition.type_de_champ) }
let(:type_de_champ_number) { create(:type_de_champ_number, order_place: 1, parent: champ_repetition.type_de_champ) }
before do
create(:champ_text, row: 0, type_de_champ: type_de_champ_text, parent: champ_repetition)
create(:champ_number, row: 0, type_de_champ: type_de_champ_number, parent: champ_repetition)
create(:champ_text, row: 1, type_de_champ: type_de_champ_text, parent: champ_repetition)
create(:champ_number, row: 1, type_de_champ: type_de_champ_number, parent: champ_repetition)
end
it 'should have sheets' do it 'should have sheets' do
expect(subject.sheets.map(&:name)).to eq(['Dossiers', 'Etablissements', 'Avis', champ_repetition.libelle]) expect(subject.sheets.map(&:name)).to eq(['Dossiers', 'Etablissements', 'Avis', champ_repetition.libelle])
@ -175,8 +166,8 @@ describe ProcedureExportV2Service do
expect(repetition_sheet.headers).to eq([ expect(repetition_sheet.headers).to eq([
"Dossier ID", "Dossier ID",
"Ligne", "Ligne",
type_de_champ_text.libelle, "Nom",
type_de_champ_number.libelle "Age"
]) ])
end end

View file

@ -1,5 +1,5 @@
describe 'users/dossiers/dossier_actions.html.haml', type: :view do describe 'users/dossiers/dossier_actions.html.haml', type: :view do
let(:procedure) { create(:procedure, :published, expects_multiple_submissions: true) } let(:procedure) { create(:procedure, :published) }
let(:dossier) { create(:dossier, :en_construction, procedure: procedure) } let(:dossier) { create(:dossier, :en_construction, procedure: procedure) }
subject { render 'users/dossiers/dossier_actions.html.haml', dossier: dossier } subject { render 'users/dossiers/dossier_actions.html.haml', dossier: dossier }
@ -12,18 +12,13 @@ describe 'users/dossiers/dossier_actions.html.haml', type: :view do
it { is_expected.not_to have_link('Supprimer le dossier') } it { is_expected.not_to have_link('Supprimer le dossier') }
end end
context 'when the procedure doesnt expect multiple submissions' do
let(:procedure) { create(:procedure, :published, expects_multiple_submissions: false) }
it { is_expected.not_to have_link('Commencer un autre dossier') }
end
context 'when the procedure is closed' do context 'when the procedure is closed' do
let(:procedure) { create(:procedure, :archived, expects_multiple_submissions: true) } let(:procedure) { create(:procedure, :archived) }
it { is_expected.not_to have_link('Commencer un autre dossier') } it { is_expected.not_to have_link('Commencer un autre dossier') }
end end
context 'when there are no actions to display' do context 'when there are no actions to display' do
let(:procedure) { create(:procedure, :published, expects_multiple_submissions: false) } let(:procedure) { create(:procedure, :archived) }
let(:dossier) { create(:dossier, :accepte, procedure: procedure) } let(:dossier) { create(:dossier, :accepte, procedure: procedure) }
it 'doesnt render the menu at all' do it 'doesnt render the menu at all' do