f78b415c51
field_name is called by actionview-7.0.4.2/lib/action_view/helpers/tags/base.rb who pass 3 arguments. We need to be compliant in order to make it valid. We reuse the same method signature with nil default values as those params are not being used.
246 lines
6.7 KiB
Ruby
246 lines
6.7 KiB
Ruby
# Display a widget for uploading, editing and deleting a file attachment
|
|
class Attachment::EditComponent < ApplicationComponent
|
|
attr_reader :champ
|
|
attr_reader :attachment
|
|
attr_reader :user_can_destroy
|
|
alias user_can_destroy? user_can_destroy
|
|
attr_reader :user_can_replace
|
|
alias user_can_replace? user_can_replace
|
|
attr_reader :as_multiple
|
|
alias as_multiple? as_multiple
|
|
|
|
EXTENSIONS_ORDER = ['jpeg', 'png', 'pdf', 'zip'].freeze
|
|
|
|
def initialize(champ: nil, auto_attach_url: nil, attached_file:, direct_upload: true, index: 0, as_multiple: false, view_as: :link, user_can_destroy: true, user_can_replace: false, **kwargs)
|
|
@as_multiple = as_multiple
|
|
@attached_file = attached_file
|
|
@auto_attach_url = auto_attach_url
|
|
@champ = champ
|
|
@direct_upload = direct_upload
|
|
@index = index
|
|
@view_as = view_as
|
|
@user_can_destroy = user_can_destroy
|
|
@user_can_replace = user_can_replace
|
|
|
|
# attachment passed by kwarg because we don't want a default (nil) value.
|
|
@attachment = if kwargs.key?(:attachment)
|
|
kwargs.delete(:attachment)
|
|
elsif attached_file.respond_to?(:attachment)
|
|
attached_file.attachment
|
|
else
|
|
fail ArgumentError, "You must pass an `attachment` kwarg when not using as single attachment like in #{attached_file.name}. Set it to nil for a new attachment."
|
|
end
|
|
|
|
# When parent form has nested attributes, pass the form builder object_name
|
|
# to correctly infer the input attribute name.
|
|
@form_object_name = kwargs.delete(:form_object_name)
|
|
|
|
verify_initialization!(kwargs)
|
|
end
|
|
|
|
def first?
|
|
@index.zero?
|
|
end
|
|
|
|
def max_file_size
|
|
return if file_size_validator.nil?
|
|
|
|
file_size_validator.options[:less_than]
|
|
end
|
|
|
|
def attachment_id
|
|
@attachment_id ||= (attachment&.id || SecureRandom.uuid)
|
|
end
|
|
|
|
def attachment_path(**args)
|
|
helpers.attachment_path attachment.id, args.merge(signed_id: attachment.blob.signed_id)
|
|
end
|
|
|
|
def destroy_attachment_path
|
|
attachment_path(champ_id: champ&.id)
|
|
end
|
|
|
|
def attachment_input_class
|
|
"attachment-input-#{attachment_id}"
|
|
end
|
|
|
|
def file_field_options
|
|
track_issue_with_missing_validators if missing_validators?
|
|
{
|
|
class: class_names("fr-upload attachment-input": true, "#{attachment_input_class}": true, "hidden": persisted?, "fr-mt-2w": user_can_replace?),
|
|
direct_upload: @direct_upload,
|
|
id: input_id,
|
|
aria: { describedby: champ&.describedby_id },
|
|
data: {
|
|
auto_attach_url:,
|
|
turbo_force: true
|
|
}.merge(has_file_size_validator? ? { max_file_size: } : {})
|
|
.merge(user_can_replace? ? { replace_attachment_target: "input" } : {})
|
|
}.merge(has_content_type_validator? ? { accept: accept_content_type } : {})
|
|
end
|
|
|
|
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)
|
|
end
|
|
end
|
|
|
|
def poll_context
|
|
return :dossier if champ.present?
|
|
|
|
nil
|
|
end
|
|
|
|
def field_name(object_name = nil, method_name = nil, *method_names, multiple: false, index: nil)
|
|
helpers.field_name(@form_object_name || ActiveModel::Naming.param_key(@attached_file.record), attribute_name)
|
|
end
|
|
|
|
def attribute_name
|
|
@attached_file.name
|
|
end
|
|
|
|
def remove_button_options
|
|
{
|
|
role: 'button',
|
|
data: { turbo: "true", turbo_method: :delete }
|
|
}
|
|
end
|
|
|
|
def replace_button_options
|
|
{
|
|
type: 'button',
|
|
data: {
|
|
action: "click->replace-attachment#open",
|
|
auto_attach_url: auto_attach_url
|
|
}.compact
|
|
}
|
|
end
|
|
|
|
def replace_controller_attributes
|
|
return {} if !persisted? || !user_can_replace? || as_multiple?
|
|
|
|
{
|
|
"data-controller": 'replace-attachment'
|
|
}
|
|
end
|
|
|
|
def retry_button_options
|
|
{
|
|
type: 'button',
|
|
class: 'fr-btn fr-btn--sm fr-btn--tertiary fr-mt-1w attachment-upload-error-retry',
|
|
data: { input_target: ".#{attachment_input_class}", action: 'autosave#onClickRetryButton' }
|
|
}
|
|
end
|
|
|
|
def persisted?
|
|
!!attachment&.persisted?
|
|
end
|
|
|
|
def downloadable?
|
|
return false unless @view_as == :download
|
|
|
|
viewable?
|
|
end
|
|
|
|
def viewable?
|
|
return false if attachment.virus_scanner_error?
|
|
return false if attachment.watermark_pending?
|
|
|
|
true
|
|
end
|
|
|
|
def error?
|
|
attachment.virus_scanner_error?
|
|
end
|
|
|
|
def error_message
|
|
case
|
|
when attachment.virus_scanner.infected?
|
|
t(".errors.virus_infected")
|
|
when attachment.virus_scanner.corrupt?
|
|
t(".errors.corrupted_file")
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def input_id
|
|
if champ.present?
|
|
# There is always a single input by champ, its id must match the label "for" attribute.
|
|
return champ.input_id
|
|
end
|
|
|
|
helpers.field_id(@form_object_name || @attached_file.record, attribute_name)
|
|
end
|
|
|
|
def auto_attach_url
|
|
return @auto_attach_url if @auto_attach_url.present?
|
|
|
|
params = { replace_attachment_id: @attachment.id } if user_can_replace? && @attachment.present?
|
|
|
|
return helpers.auto_attach_url(@champ, params) if @champ.present?
|
|
|
|
nil
|
|
end
|
|
|
|
def file_size_validator
|
|
@attached_file.record
|
|
._validators[attribute_name.to_sym]
|
|
.find { |validator| validator.class == ActiveStorageValidations::SizeValidator }
|
|
end
|
|
|
|
def content_type_validator
|
|
@attached_file.record
|
|
._validators[attribute_name.to_sym]
|
|
.find { |validator| validator.class == ActiveStorageValidations::ContentTypeValidator }
|
|
end
|
|
|
|
def accept_content_type
|
|
list = content_type_validator.options[:in].dup
|
|
list << ".acidcsa" if list.include?("application/octet-stream")
|
|
list.join(', ')
|
|
end
|
|
|
|
def allowed_formats
|
|
return nil unless champ&.titre_identite?
|
|
|
|
@allowed_formats ||= begin
|
|
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 }
|
|
end
|
|
end
|
|
|
|
def has_content_type_validator?
|
|
!content_type_validator.nil?
|
|
end
|
|
|
|
def has_file_size_validator?
|
|
!file_size_validator.nil?
|
|
end
|
|
|
|
def missing_validators?
|
|
return true if !has_file_size_validator?
|
|
return true if !has_content_type_validator?
|
|
return false
|
|
end
|
|
|
|
def verify_initialization!(kwargs)
|
|
fail ArgumentError, "Unknown kwarg #{kwargs.keys.join(', ')}" unless kwargs.empty?
|
|
|
|
fail ArgumentError, "Invalid view_as:#{@view_as}, must be :download or :link" if [:download, :link].exclude?(@view_as)
|
|
end
|
|
|
|
def track_issue_with_missing_validators
|
|
Sentry.capture_message(
|
|
"Strange case of missing validator",
|
|
extra: {
|
|
champ: champ,
|
|
field_name: field_name,
|
|
attachment_id: attachment_id
|
|
}
|
|
)
|
|
end
|
|
end
|