diff --git a/app/controllers/gestionnaires/avis_controller.rb b/app/controllers/gestionnaires/avis_controller.rb index 7b7fede16..e6b1e3e87 100644 --- a/app/controllers/gestionnaires/avis_controller.rb +++ b/app/controllers/gestionnaires/avis_controller.rb @@ -134,7 +134,7 @@ module Gestionnaires end def avis_params - params.require(:avis).permit(:answer) + params.require(:avis).permit(:answer, :piece_justificative_file) end def commentaire_params diff --git a/app/models/avis.rb b/app/models/avis.rb index e755f946b..9a085d11b 100644 --- a/app/models/avis.rb +++ b/app/models/avis.rb @@ -1,10 +1,13 @@ class Avis < ApplicationRecord include EmailSanitizableConcern + include VirusScanConcern belongs_to :dossier, touch: true belongs_to :gestionnaire belongs_to :claimant, class_name: 'Gestionnaire' + has_one_attached :piece_justificative_file + validates :email, format: { with: Devise.email_regexp, message: "n'est pas valide" }, allow_nil: true validates :claimant, presence: true @@ -19,6 +22,9 @@ class Avis < ApplicationRecord scope :by_latest, -> { order(updated_at: :desc) } scope :updated_since?, -> (date) { where('avis.updated_at > ?', date) } + after_commit :create_avis_virus_scan + after_initialize { add_virus_scan_on(self.piece_justificative_file) } + # The form allows subtmitting avis requests to several emails at once, # hence this virtual attribute. attr_accessor :emails @@ -48,4 +54,8 @@ class Avis < ApplicationRecord self.email = nil end end + + def create_avis_virus_scan + create_virus_scan(self.piece_justificative_file) + end end diff --git a/app/models/concerns/virus_scan_concern.rb b/app/models/concerns/virus_scan_concern.rb new file mode 100644 index 000000000..c7bfd7013 --- /dev/null +++ b/app/models/concerns/virus_scan_concern.rb @@ -0,0 +1,21 @@ +module VirusScanConcern + extend ActiveSupport::Concern + + attr_reader :attachment_attribute + + def add_virus_scan_on(piece_justificative) + @attachment_attribute = piece_justificative + end + + def virus_scan + VirusScan.find_by(blob_key: self.attachment_attribute.blob.key) + end + + def create_virus_scan(piece_justificative) + if piece_justificative&.attachment&.blob.present? + VirusScan.find_or_create_by!(blob_key: piece_justificative.blob.key) do |virus_scan| + virus_scan.status = VirusScan.statuses.fetch(:pending) + end + end + end +end diff --git a/app/views/gestionnaires/avis/instruction.html.haml b/app/views/gestionnaires/avis/instruction.html.haml index 12c5f0537..3d81ac7b5 100644 --- a/app/views/gestionnaires/avis/instruction.html.haml +++ b/app/views/gestionnaires/avis/instruction.html.haml @@ -13,6 +13,8 @@ = form_for @avis, url: gestionnaire_avis_path(@avis), html: { class: 'form' } do |f| = f.text_area :answer, rows: 3, placeholder: 'Votre avis', required: true + = render partial: "shared/piece_jointe/pj_upload_field", locals: { pj: @avis.piece_justificative_file, object: @avis, form: f } + .flex.justify-between.align-baseline %p.confidentiel.flex - if @avis.confidentiel? diff --git a/app/views/gestionnaires/shared/avis/_list.html.haml b/app/views/gestionnaires/shared/avis/_list.html.haml index 3de3b5040..23acc5a0b 100644 --- a/app/views/gestionnaires/shared/avis/_list.html.haml +++ b/app/views/gestionnaires/shared/avis/_list.html.haml @@ -28,4 +28,5 @@ Réponse donnée le #{l(avis.updated_at, format: '%d/%m/%y à %H:%M')} - else %span.waiting En attente de réponse + = render partial: 'shared/piece_jointe/pj_link', locals: { object: avis, pj: avis.piece_justificative_file } %p= avis.answer diff --git a/app/views/shared/piece_jointe/_pj_link.html.haml b/app/views/shared/piece_jointe/_pj_link.html.haml new file mode 100644 index 000000000..656d1dd4a --- /dev/null +++ b/app/views/shared/piece_jointe/_pj_link.html.haml @@ -0,0 +1,20 @@ +- if pj.attached? + .pj-link + - if object.virus_scan.blank? || object.virus_scan.safe? + = link_to url_for(pj), target: '_blank', title: "Télécharger la pièce jointe" do + %span.icon.attachment + = pj.filename.to_s + - if object.virus_scan.blank? + (ce fichier n’a pas été analysé par notre antivirus, téléchargez-le avec précaution) + + - else + = pj.filename.to_s + - if object.virus_scan.pending? + (analyse antivirus en cours + = link_to "rafraichir", request.path + ) + - elsif object.virus_scan.infected? + - if user_can_upload + (virus détecté, merci d’envoyer un autre fichier) + - else + (virus détecté, le téléchargement de ce fichier est bloqué) diff --git a/app/views/shared/piece_jointe/_pj_upload_field.haml b/app/views/shared/piece_jointe/_pj_upload_field.haml new file mode 100644 index 000000000..1c2e95dce --- /dev/null +++ b/app/views/shared/piece_jointe/_pj_upload_field.haml @@ -0,0 +1,12 @@ +.piece-justificative + - if pj.attached? + .piece-justificative-actions{ id: "piece_justificative_#{object.id}" } + .piece-justificative-action + = render partial: "shared/piece_jointe/pj_link", locals: { object: object, pj: object.piece_justificative_file, user_can_upload: true } + .piece-justificative-action + = button_tag 'Remplacer', type: 'button', class: 'button small', data: { 'toggle-target': "#champs_#{object.id}" } + + = form.file_field :piece_justificative_file, + id: "champs_#{object.id}", + class: "piece-justificative-input #{'hidden' if pj.attached?}", + direct_upload: true diff --git a/spec/controllers/gestionnaires/avis_controller_spec.rb b/spec/controllers/gestionnaires/avis_controller_spec.rb index 35f9d5159..662937feb 100644 --- a/spec/controllers/gestionnaires/avis_controller_spec.rb +++ b/spec/controllers/gestionnaires/avis_controller_spec.rb @@ -53,14 +53,30 @@ describe Gestionnaires::AvisController, type: :controller do end describe '#update' do - before do - patch :update, params: { id: avis_without_answer.id, avis: { answer: 'answer' } } - avis_without_answer.reload - end + describe 'without attachment' do + before do + patch :update, params: { id: avis_without_answer.id, avis: { answer: 'answer' } } + avis_without_answer.reload + end - it { expect(response).to redirect_to(instruction_gestionnaire_avis_path(avis_without_answer)) } - it { expect(avis_without_answer.answer).to eq('answer') } - it { expect(flash.notice).to eq('Votre réponse est enregistrée.') } + it { expect(response).to redirect_to(instruction_gestionnaire_avis_path(avis_without_answer)) } + it { expect(avis_without_answer.answer).to eq('answer') } + it { expect(avis_without_answer.piece_justificative_file).to_not be_attached } + it { expect(flash.notice).to eq('Votre réponse est enregistrée.') } + end + describe 'with attachment' do + let(:file) { Rack::Test::UploadedFile.new("./spec/fixtures/files/piece_justificative_0.pdf", 'application/pdf') } + + before do + post :update, params: { id: avis_without_answer.id, avis: { answer: 'answer', piece_justificative_file: file } } + avis_without_answer.reload + end + + it { expect(response).to redirect_to(instruction_gestionnaire_avis_path(avis_without_answer)) } + it { expect(avis_without_answer.answer).to eq('answer') } + it { expect(avis_without_answer.piece_justificative_file).to be_attached } + it { expect(flash.notice).to eq('Votre réponse est enregistrée.') } + end end describe '#create_commentaire' do