142 lines
3.6 KiB
Ruby
142 lines
3.6 KiB
Ruby
# Display a widget for uploading, editing and deleting a file attachment
|
|
class Attachment::EditComponent < ApplicationComponent
|
|
attr_reader :template, :form, :attachment
|
|
|
|
delegate :persisted?, to: :attachment, allow_nil: true
|
|
|
|
def initialize(form:, attached_file:, user_can_destroy: false, direct_upload: true, id: nil, index: 0, **kwargs)
|
|
@form = form
|
|
@attached_file = attached_file
|
|
|
|
@attachment = if kwargs.key?(:attachment)
|
|
kwargs[: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
|
|
|
|
@user_can_destroy = user_can_destroy
|
|
@direct_upload = direct_upload
|
|
@id = id
|
|
@index = index
|
|
end
|
|
|
|
def max_file_size
|
|
file_size_validator.options[:less_than]
|
|
end
|
|
|
|
def user_can_destroy?
|
|
@user_can_destroy
|
|
end
|
|
|
|
def attachment_path
|
|
helpers.attachment_path attachment.id, { signed_id: attachment.blob.signed_id }
|
|
end
|
|
|
|
def attachment_id
|
|
@attachment_id ||= (attachment&.id || SecureRandom.uuid)
|
|
end
|
|
|
|
def attachment_input_class
|
|
"attachment-input-#{attachment_id}"
|
|
end
|
|
|
|
def champ
|
|
@form.object.is_a?(Champ) ? @form.object : nil
|
|
end
|
|
|
|
def file_field_options
|
|
track_issue_with_missing_validators if missing_validators?
|
|
{
|
|
class: "attachment-input #{attachment_input_class} #{'hidden' if persisted?}",
|
|
direct_upload: @direct_upload,
|
|
id: input_id(@id),
|
|
aria: { describedby: champ&.describedby_id },
|
|
data: {
|
|
auto_attach_url:
|
|
}.merge(has_file_size_validator? ? { max_file_size: } : {})
|
|
}.merge(has_content_type_validator? ? { accept: accept_content_type } : {})
|
|
end
|
|
|
|
def auto_attach_url
|
|
helpers.auto_attach_url(form.object)
|
|
end
|
|
|
|
def input_id(given_id)
|
|
return given_id if given_id.present?
|
|
|
|
if champ.present?
|
|
# Single or first attachment input must match label "for" attribute. Others must remain unique.
|
|
return champ.input_id if @index.zero?
|
|
return "#{champ.input_id}_#{attachment_id}"
|
|
end
|
|
|
|
file_field_name
|
|
end
|
|
|
|
def file_field_name
|
|
@attached_file.name
|
|
end
|
|
|
|
def remove_button_options
|
|
{
|
|
role: 'button',
|
|
class: 'button small danger',
|
|
data: { turbo_method: :delete }
|
|
}
|
|
end
|
|
|
|
def retry_button_options
|
|
{
|
|
type: 'button',
|
|
class: 'button attachment-error-retry',
|
|
data: { input_target: ".#{attachment_input_class}", action: 'autosave#onClickRetryButton' }
|
|
}
|
|
end
|
|
|
|
def file_size_validator
|
|
@attached_file.record
|
|
._validators[file_field_name.to_sym]
|
|
.find { |validator| validator.class == ActiveStorageValidations::SizeValidator }
|
|
end
|
|
|
|
def content_type_validator
|
|
@attached_file.record
|
|
._validators[file_field_name.to_sym]
|
|
.find { |validator| validator.class == ActiveStorageValidations::ContentTypeValidator }
|
|
end
|
|
|
|
def accept_content_type
|
|
list = content_type_validator.options[:in]
|
|
if list.include?("application/octet-stream")
|
|
list.push(".acidcsa")
|
|
end
|
|
list.join(', ')
|
|
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 track_issue_with_missing_validators
|
|
Sentry.capture_message(
|
|
"Strange case of missing validator",
|
|
extra: {
|
|
champ: champ,
|
|
file_field_name: file_field_name,
|
|
attachment_id: attachment_id
|
|
}
|
|
)
|
|
end
|
|
end
|