Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

9449 ETQ instructeur ou admin, je peux apposer sur une attestation un tampon dédié à un groupe instructeur #9507

Merged
merged 13 commits into from
Sep 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions app/components/attachment/edit_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ def poll_url
if champ.present?
auto_attach_url
else
attachment_path(user_can_edit: true, view_as: @view_as, auto_attach_url: @auto_attach_url)
attachment_path(user_can_edit: true, view_as: @view_as, auto_attach_url: @auto_attach_url, direct_upload: @direct_upload)
end
end

Expand Down Expand Up @@ -204,12 +204,14 @@ def accept_content_type
end

def allowed_formats
return nil unless champ&.titre_identite?

@allowed_formats ||= begin
content_type_validator.options[:in].filter_map do |content_type|
formats = content_type_validator.options[:in].filter_map do |content_type|
MiniMime.lookup_by_content_type(content_type)&.extension
end.uniq.sort_by { EXTENSIONS_ORDER.index(_1) || 999 }

# When too many formats are allowed, consider instead manually indicating
# above the input a more comprehensive of formats allowed, like "any image", or a simplified list.
formats.size > 5 ? [] : formats
end
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
%p.fr-text--sm.fr-text-mention--grey.fr-mb-1w
- if max_file_size.present?
= t('.max_file_size', max_file_size: number_to_human_size(max_file_size))
- if allowed_formats
- if allowed_formats.present?
= t('.allowed_formats', formats: allowed_formats.join(', '))


Expand Down
4 changes: 4 additions & 0 deletions app/controllers/administrateurs/administrateur_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ def administrateur_as_manager?
end

def alert_for_missing_siret_service
return if flash[:alert].present?

procedures = missing_siret_services
if procedures.any?
errors = []
Expand All @@ -61,6 +63,8 @@ def missing_siret_services
end

def alert_for_missing_service
return if flash[:alert].present?

procedures = missing_service
if procedures.any?
errors = []
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module Administrateurs
class AttestationTemplatesController < AdministrateurController
include UninterlacePngConcern

before_action :retrieve_procedure

def show
Expand Down Expand Up @@ -63,23 +65,14 @@ def activated_attestation_params
signature_file = params['attestation_template'].delete('signature')

if logo_file.present?
@activated_attestation_params[:logo] = uninterlaced_png(logo_file)
@activated_attestation_params[:logo] = uninterlace_png(logo_file)
end
if signature_file.present?
@activated_attestation_params[:signature] = uninterlaced_png(signature_file)
@activated_attestation_params[:signature] = uninterlace_png(signature_file)
end
end

@activated_attestation_params
end

def uninterlaced_png(uploaded_file)
if uploaded_file&.content_type == 'image/png'
chunky_img = ChunkyPNG::Image.from_io(uploaded_file.to_io)
chunky_img.save(uploaded_file.tempfile.to_path, interlace: false)
uploaded_file.tempfile.reopen(uploaded_file.tempfile.to_path, 'rb')
end
uploaded_file
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ module Administrateurs
class GroupeInstructeursController < AdministrateurController
include ActiveSupport::NumberHelper
include Logic
include UninterlacePngConcern
include GroupeInstructeursSignatureConcern

before_action :ensure_not_super_admin!, only: [:add_instructeur]

Expand Down Expand Up @@ -389,6 +391,10 @@ def groupe_instructeur_params
params.require(:groupe_instructeur).permit(:label)
end

def signature_params
params.require(:groupe_instructeur).permit(:signature)
end

def paginated_groupe_instructeurs
groupes = if params[:q].present?
query = ActiveRecord::Base.sanitize_sql_like(params[:q])
Expand Down
1 change: 1 addition & 0 deletions app/controllers/attachments_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ def show
@attachment = @blob.attachments.find(params[:id])

@user_can_edit = cast_bool(params[:user_can_edit])
@direct_upload = cast_bool(params[:direct_upload])
@view_as = params[:view_as]&.to_sym
@auto_attach_url = params[:auto_attach_url]

Expand Down
63 changes: 63 additions & 0 deletions app/controllers/concerns/groupe_instructeurs_signature_concern.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
module GroupeInstructeursSignatureConcern
extend ActiveSupport::Concern

included do
def add_signature
@procedure = procedure
@groupe_instructeur = groupe_instructeur
@instructeurs = paginated_instructeurs

signature_file = params[:groupe_instructeur][:signature]

if params[:groupe_instructeur].nil? || signature_file.blank?
if respond_to?(:available_instructeur_emails)
@available_instructeur_emails = available_instructeur_emails
end

flash[:alert] = "Aucun fichier joint pour le tampon de l'attestation"
render :show
else
signature = uninterlace_png(signature_file)

if @groupe_instructeur.signature.attach(signature)
handle_redirect :success
else
handle_redirect :alert
end
end
end

def preview_attestation
attestation_template = procedure.attestation_template || procedure.build_attestation_template
@attestation = attestation_template.render_attributes_for({ groupe_instructeur: groupe_instructeur })

render 'administrateurs/attestation_templates/show', formats: [:pdf]
end

private

def handle_redirect(status)
redirect, preview = if self.class.module_parent_name == "Administrateurs"
[
:admin_procedure_groupe_instructeur_path,
:preview_attestation_admin_procedure_groupe_instructeur_path
]
else
[
:instructeur_groupe_path,
:preview_attestation_instructeur_groupe_path
]
end

redirect_path = method(redirect).call(@procedure, @groupe_instructeur)
preview_path = method(preview).call(@procedure, @groupe_instructeur)

case status
when :success
redirect_to redirect_path, notice: "Le tampon de l’attestation a bien été ajouté. #{helpers.link_to("Prévisualiser l’attestation", preview_path)}"
when :alert
redirect_to redirect_path, alert: "Une erreur a empêché l’ajout du tampon. Réessayez dans quelques instants."
end
end
end
end
19 changes: 19 additions & 0 deletions app/controllers/concerns/uninterlace_png_concern.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
module UninterlacePngConcern
extend ActiveSupport::Concern

private

def uninterlace_png(uploaded_file)
if uploaded_file&.content_type == 'image/png' && interlaced?(uploaded_file.tempfile.to_path)
chunky_img = ChunkyPNG::Image.from_io(uploaded_file.to_io)
chunky_img.save(uploaded_file.tempfile.to_path, interlace: false)
uploaded_file.tempfile.reopen(uploaded_file.tempfile.to_path, 'rb')
end
uploaded_file
end

def interlaced?(png_path)
png = MiniMagick::Image.open(png_path)
png.data["interlace"] != "None"
end
end
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
module Instructeurs
class GroupeInstructeursController < InstructeurController
include UninterlacePngConcern
include GroupeInstructeursSignatureConcern

ITEMS_PER_PAGE = 25

def index
Expand Down
33 changes: 26 additions & 7 deletions app/models/attestation_template.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,16 +60,27 @@ def signature_url
end

def render_attributes_for(params = {})
dossier = params.fetch(:dossier, false)

{
attributes = {
created_at: Time.zone.now,
title: dossier ? replace_tags(title, dossier) : params.fetch(:title, title),
body: dossier ? replace_tags(body, dossier) : params.fetch(:body, body),
footer: params.fetch(:footer, footer),
logo: params.fetch(:logo, logo.attached? ? logo : nil),
signature: params.fetch(:signature, signature.attached? ? signature : nil)
logo: params.fetch(:logo, logo.attached? ? logo : nil)
}

dossier = params[:dossier]

if dossier.present?
attributes.merge({
title: replace_tags(title, dossier),
body: replace_tags(body, dossier),
signature: signature_to_render(dossier.groupe_instructeur)
})
else
attributes.merge({
title: params.fetch(:title, title),
body: params.fetch(:body, body),
signature: signature_to_render(params[:groupe_instructeur])
})
end
end

def logo_checksum
Expand All @@ -90,6 +101,14 @@ def signature_filename

private

def signature_to_render(groupe_instructeur)
if groupe_instructeur&.signature&.attached?
groupe_instructeur.signature
else
signature
end
end

def used_tags
used_tags_for(title) + used_tags_for(body)
end
Expand Down
5 changes: 5 additions & 0 deletions app/models/groupe_instructeur.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ class GroupeInstructeur < ApplicationRecord
has_one :defaut_procedure, -> { with_discarded }, class_name: 'Procedure', foreign_key: :defaut_groupe_instructeur_id, dependent: :nullify, inverse_of: :defaut_groupe_instructeur
has_one :contact_information

has_one_attached :signature

SIGNATURE_MAX_SIZE = 1.megabytes
validates :signature, content_type: ['image/png', 'image/jpg', 'image/jpeg'], size: { less_than: SIGNATURE_MAX_SIZE }

validates :label, presence: true, allow_nil: false
validates :label, uniqueness: { scope: :procedure }
validates :closed, acceptance: { accept: [false] }, if: -> { (self == procedure.defaut_groupe_instructeur) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,22 @@
= tag[:description]

%h3.header-subsection Logo de l'attestation
%p.fr-text--sm.fr-text-mention--grey.fr-mb-0
Dimensions conseillées : au minimum 500px de largeur ou de hauteur.
= render Attachment::EditComponent.new(attached_file: @attestation_template.logo, direct_upload: false)

%p.notice
Formats acceptés : JPG / JPEG / PNG.
%br
Dimensions conseillées : au minimum 500 px de largeur ou de hauteur, poids maximum : 0,5 Mo.

%h3.header-subsection Tampon de l'attestation
%h3.header-subsection.fr-mt-5w Tampon de l'attestation
%p.fr-text--sm.fr-text-mention--grey.fr-mb-0
Dimensions conseillées : au minimum 500px de largeur ou de hauteur.
= render Attachment::EditComponent.new(attached_file: @attestation_template.signature, direct_upload: false)

%p.notice
Formats acceptés : JPG / JPEG / PNG.
%br
Dimensions conseillées : au minimum 500 px de largeur ou de hauteur, poids maximum : 0,5 Mo.

= render Dsfr::InputComponent.new(form: f, attribute: :footer, input_type: :text_field, opts: { maxlength: 190, size: nil }, required: false)
- if @attestation_template.procedure.routing_enabled?
%p.fr-text--sm.fr-text-mention--grey
À noter : chaque groupe instructeur peut apposer son propre tampon à la place de celui-ci.


.fr-mt-4w
= render Dsfr::InputComponent.new(form: f, attribute: :footer, input_type: :text_field, opts: { maxlength: 190, size: nil }, required: false)

3 changes: 3 additions & 0 deletions app/views/administrateurs/groupe_instructeurs/show.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,6 @@
= render partial: 'administrateurs/groupe_instructeurs/contact_information',
locals: { procedure: @procedure,
groupe_instructeur: @groupe_instructeur }

= render partial: "shared/groupe_instructeurs/signature_form", locals: { groupe_instructeur: @groupe_instructeur,
preview_path: preview_attestation_admin_procedure_groupe_instructeur_path(@groupe_instructeur.procedure, @groupe_instructeur) }
2 changes: 1 addition & 1 deletion app/views/attachments/show.turbo_stream.haml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
= turbo_stream.replace dom_id(@attachment, :edit) do
- if @user_can_edit
= render Attachment::EditComponent.new(attachment: @attachment, attached_file: @attachment.record.public_send(@attachment.name), auto_attach_url: @auto_attach_url, view_as: @view_as)
= render Attachment::EditComponent.new(attachment: @attachment, attached_file: @attachment.record.public_send(@attachment.name), auto_attach_url: @auto_attach_url, view_as: @view_as, direct_upload: @direct_upload)
- else
= render Attachment::ShowComponent.new(attachment: @attachment)
3 changes: 3 additions & 0 deletions app/views/instructeurs/groupe_instructeurs/show.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,6 @@
%p= service.telephone
- if service.horaires.present?
%p= service.horaires

= render partial: "shared/groupe_instructeurs/signature_form", locals: { groupe_instructeur: @groupe_instructeur,
preview_path: preview_attestation_instructeur_groupe_path(@groupe_instructeur.procedure, @groupe_instructeur) }
19 changes: 19 additions & 0 deletions app/views/shared/groupe_instructeurs/_signature_form.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.card.mt-2
= render NestedForms::FormOwnerComponent.new
= form_with url: { action: :add_signature }, method: :post, html: { multipart: true } do |f|
.card-title Tampon de l'attestation

%p.fr-text--sm.fr-text-mention--grey
Vous pouvez apposer sur l’attestation un tampon (ou signature) dédié à ce groupe d’instructeurs.
Si vous n’en fournissez pas, celui de la démarche sera utilisé, le cas échéant.

.fr-upload-group.fr-mb-4w
%p.fr-text--sm.fr-text-mention--grey.fr-mb-1w
Dimensions conseillées : au minimum 500px de largeur ou de hauteur.
= render Attachment::EditComponent.new(attached_file: groupe_instructeur.signature, direct_upload: false)

.fr-btns-group.fr-btns-group--inline
= f.submit 'Ajouter le tampon', class: 'fr-btn'

- if @groupe_instructeur.signature.persisted?
= link_to("Prévisualiser", preview_path, class: "fr-btn fr-btn--secondary", **external_link_attributes)
4 changes: 4 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,8 @@
member do
post 'add_instructeur'
delete 'remove_instructeur'
post 'add_signature'
get 'preview_attestation'
end
end

Expand Down Expand Up @@ -532,6 +534,8 @@
delete 'remove_instructeur'
get 'reaffecter_dossiers'
post 'reaffecter'
post 'add_signature'
get 'preview_attestation'
end

collection do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
expect(assigns(:attestation)).to include(attestation_params)
expect(assigns(:attestation)[:created_at]).to eq(Time.zone.now)
expect(assigns(:attestation)[:logo]).to eq(nil)
expect(assigns(:attestation)[:signature]).to eq(nil)
expect(assigns(:attestation)[:signature]).not_to be_attached
end
it_behaves_like 'rendering a PDF successfully'
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -843,4 +843,22 @@ def remove_instructeur(instructeur)
expect(procedure4.reload.routing_enabled).to be_truthy
end
end

describe '#add_signature' do
let(:signature) { fixture_file_upload('spec/fixtures/files/black.png', 'image/png') }

before {
post :add_signature,
params: {
procedure_id: procedure.id,
id: gi_1_1.id,
groupe_instructeur: {
signature: signature
}
}
}

it { expect(response).to redirect_to(admin_procedure_groupe_instructeur_path(procedure, gi_1_1)) }
it { expect(gi_1_1.signature).to be_attached }
end
end
Loading