Merge from docusealco/wip
This commit is contained in:
@@ -15,6 +15,7 @@ class AccountConfigsController < ApplicationController
|
|||||||
AccountConfig::ESIGNING_PREFERENCE_KEY,
|
AccountConfig::ESIGNING_PREFERENCE_KEY,
|
||||||
AccountConfig::FORM_WITH_CONFETTI_KEY,
|
AccountConfig::FORM_WITH_CONFETTI_KEY,
|
||||||
AccountConfig::DOWNLOAD_LINKS_AUTH_KEY,
|
AccountConfig::DOWNLOAD_LINKS_AUTH_KEY,
|
||||||
|
AccountConfig::DOWNLOAD_LINKS_EXPIRE_KEY,
|
||||||
AccountConfig::FORCE_SSO_AUTH_KEY,
|
AccountConfig::FORCE_SSO_AUTH_KEY,
|
||||||
AccountConfig::FLATTEN_RESULT_PDF_KEY,
|
AccountConfig::FLATTEN_RESULT_PDF_KEY,
|
||||||
AccountConfig::ENFORCE_SIGNING_ORDER_KEY,
|
AccountConfig::ENFORCE_SIGNING_ORDER_KEY,
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ module Api
|
|||||||
def show
|
def show
|
||||||
blob_uuid, purp, exp = ApplicationRecord.signed_id_verifier.verified(params[:signed_uuid])
|
blob_uuid, purp, exp = ApplicationRecord.signed_id_verifier.verified(params[:signed_uuid])
|
||||||
|
|
||||||
if blob_uuid.blank? || (purp.present? && purp != 'blob') || (exp && exp < Time.current.to_i)
|
if blob_uuid.blank? || purp != 'blob'
|
||||||
Rollbar.error('Blob not found') if defined?(Rollbar)
|
Rollbar.error('Blob not found') if defined?(Rollbar)
|
||||||
|
|
||||||
return head :not_found
|
return head :not_found
|
||||||
@@ -24,8 +24,9 @@ module Api
|
|||||||
attachment = blob.attachments.take
|
attachment = blob.attachments.take
|
||||||
|
|
||||||
@record = attachment.record
|
@record = attachment.record
|
||||||
|
@record = @record.record if @record.is_a?(ActiveStorage::Attachment)
|
||||||
|
|
||||||
authorization_check!(attachment) if exp.blank?
|
authorization_check!(attachment, @record, exp)
|
||||||
|
|
||||||
if request.headers['Range'].present?
|
if request.headers['Range'].present?
|
||||||
send_blob_byte_range_data blob, request.headers['Range']
|
send_blob_byte_range_data blob, request.headers['Range']
|
||||||
@@ -41,14 +42,19 @@ module Api
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def authorization_check!(attachment)
|
def authorization_check!(attachment, record, exp)
|
||||||
is_authorized = attachment.name.in?(%w[logo preview_images]) ||
|
return if attachment.name == 'logo'
|
||||||
(current_user && attachment.record.account.id == current_user.account_id) ||
|
return if exp.to_i >= Time.current.to_i
|
||||||
(current_user && !Docuseal.multitenant? && current_user.role == 'superadmin') ||
|
|
||||||
!attachment.record.account.account_configs
|
|
||||||
.find_or_initialize_by(key: AccountConfig::DOWNLOAD_LINKS_AUTH_KEY).value
|
|
||||||
|
|
||||||
return if is_authorized
|
return if current_user && current_ability.can?(:read, record)
|
||||||
|
|
||||||
|
configs = record.account.account_configs.where(key: [AccountConfig::DOWNLOAD_LINKS_AUTH_KEY,
|
||||||
|
AccountConfig::DOWNLOAD_LINKS_EXPIRE_KEY])
|
||||||
|
|
||||||
|
require_auth = configs.any? { |c| c.key == AccountConfig::DOWNLOAD_LINKS_AUTH_KEY && c.value }
|
||||||
|
require_ttl = configs.none? { |c| c.key == AccountConfig::DOWNLOAD_LINKS_EXPIRE_KEY && c.value == false }
|
||||||
|
|
||||||
|
return if !require_ttl && !require_auth
|
||||||
|
|
||||||
Rollbar.error('Blob aunauthorized') if defined?(Rollbar)
|
Rollbar.error('Blob aunauthorized') if defined?(Rollbar)
|
||||||
|
|
||||||
|
|||||||
@@ -18,12 +18,14 @@ module Api
|
|||||||
field: :completed_at
|
field: :completed_at
|
||||||
)
|
)
|
||||||
|
|
||||||
|
expires_at = Accounts.link_expires_at(current_account)
|
||||||
|
|
||||||
render json: {
|
render json: {
|
||||||
data: submitters.map do |s|
|
data: submitters.map do |s|
|
||||||
{
|
{
|
||||||
event_type: 'form.completed',
|
event_type: 'form.completed',
|
||||||
timestamp: s.completed_at,
|
timestamp: s.completed_at,
|
||||||
data: Submitters::SerializeForWebhook.call(s)
|
data: Submitters::SerializeForWebhook.call(s, expires_at:)
|
||||||
}
|
}
|
||||||
end,
|
end,
|
||||||
pagination: {
|
pagination: {
|
||||||
|
|||||||
@@ -34,10 +34,12 @@ module Api
|
|||||||
associations: [:blob]
|
associations: [:blob]
|
||||||
).call
|
).call
|
||||||
|
|
||||||
|
expires_at = Accounts.link_expires_at(current_account)
|
||||||
|
|
||||||
render json: {
|
render json: {
|
||||||
id: @submission.id,
|
id: @submission.id,
|
||||||
documents: documents.map do |attachment|
|
documents: documents.map do |attachment|
|
||||||
{ name: attachment.filename.base, url: ActiveStorage::Blob.proxy_url(attachment.blob) }
|
{ name: attachment.filename.base, url: ActiveStorage::Blob.proxy_url(attachment.blob, expires_at:) }
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -14,16 +14,19 @@ module Api
|
|||||||
:created_by_user, :submission_events,
|
:created_by_user, :submission_events,
|
||||||
template: :folder,
|
template: :folder,
|
||||||
submitters: { documents_attachments: :blob, attachments_attachments: :blob },
|
submitters: { documents_attachments: :blob, attachments_attachments: :blob },
|
||||||
audit_trail_attachment: :blob
|
audit_trail_attachment: :blob,
|
||||||
|
combined_document_attachment: :blob
|
||||||
),
|
),
|
||||||
field: :completed_at)
|
field: :completed_at)
|
||||||
|
|
||||||
|
expires_at = Accounts.link_expires_at(current_account)
|
||||||
|
|
||||||
render json: {
|
render json: {
|
||||||
data: submissions.map do |s|
|
data: submissions.map do |s|
|
||||||
{
|
{
|
||||||
event_type: 'submission.completed',
|
event_type: 'submission.completed',
|
||||||
timestamp: s.completed_at,
|
timestamp: s.completed_at,
|
||||||
data: Submissions::SerializeForApi.call(s, s.submitters)
|
data: Submissions::SerializeForApi.call(s, s.submitters, expires_at:)
|
||||||
}
|
}
|
||||||
end,
|
end,
|
||||||
pagination: {
|
pagination: {
|
||||||
|
|||||||
@@ -18,10 +18,12 @@ module Api
|
|||||||
combined_document_attachment: :blob,
|
combined_document_attachment: :blob,
|
||||||
audit_trail_attachment: :blob))
|
audit_trail_attachment: :blob))
|
||||||
|
|
||||||
|
expires_at = Accounts.link_expires_at(current_account)
|
||||||
|
|
||||||
render json: {
|
render json: {
|
||||||
data: submissions.map do |s|
|
data: submissions.map do |s|
|
||||||
Submissions::SerializeForApi.call(s, s.submitters, params,
|
Submissions::SerializeForApi.call(s, s.submitters, params,
|
||||||
with_events: false, with_documents: false, with_values: false)
|
with_events: false, with_documents: false, with_values: false, expires_at:)
|
||||||
end,
|
end,
|
||||||
pagination: {
|
pagination: {
|
||||||
count: submissions.size,
|
count: submissions.size,
|
||||||
|
|||||||
@@ -14,9 +14,11 @@ module Api
|
|||||||
documents_attachments: :blob, attachments_attachments: :blob)
|
documents_attachments: :blob, attachments_attachments: :blob)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
expires_at = Accounts.link_expires_at(current_account)
|
||||||
|
|
||||||
render json: {
|
render json: {
|
||||||
data: submitters.map do |s|
|
data: submitters.map do |s|
|
||||||
Submitters::SerializeForApi.call(s, with_template: true, with_events: true, params:)
|
Submitters::SerializeForApi.call(s, with_template: true, with_events: true, params:, expires_at:)
|
||||||
end,
|
end,
|
||||||
pagination: {
|
pagination: {
|
||||||
count: submitters.size,
|
count: submitters.size,
|
||||||
|
|||||||
@@ -26,13 +26,15 @@ module Api
|
|||||||
original_template: @template,
|
original_template: @template,
|
||||||
documents: params[:documents])
|
documents: params[:documents])
|
||||||
|
|
||||||
|
Templates.maybe_assign_access(cloned_template)
|
||||||
|
|
||||||
cloned_template.save!
|
cloned_template.save!
|
||||||
|
|
||||||
WebhookUrls.enqueue_events(cloned_template, 'template.created')
|
WebhookUrls.enqueue_events(cloned_template, 'template.created')
|
||||||
|
|
||||||
SearchEntries.enqueue_reindex(cloned_template)
|
SearchEntries.enqueue_reindex(cloned_template)
|
||||||
|
|
||||||
render json: Templates::SerializeForApi.call(cloned_template, schema_documents)
|
render json: Templates::SerializeForApi.call(cloned_template, schema_documents:)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -24,13 +24,14 @@ module Api
|
|||||||
name: :preview_images)
|
name: :preview_images)
|
||||||
.preload(:blob)
|
.preload(:blob)
|
||||||
|
|
||||||
|
expires_at = Accounts.link_expires_at(current_account)
|
||||||
|
|
||||||
render json: {
|
render json: {
|
||||||
data: templates.map do |t|
|
data: templates.map do |t|
|
||||||
Templates::SerializeForApi.call(
|
Templates::SerializeForApi.call(t,
|
||||||
t,
|
schema_documents: schema_documents.select { |e| e.record_id == t.id },
|
||||||
schema_documents.select { |e| e.record_id == t.id },
|
preview_image_attachments:,
|
||||||
preview_image_attachments
|
expires_at:)
|
||||||
)
|
|
||||||
end,
|
end,
|
||||||
pagination: {
|
pagination: {
|
||||||
count: templates.size,
|
count: templates.size,
|
||||||
|
|||||||
@@ -10,11 +10,13 @@ class SubmissionsExportController < ApplicationController
|
|||||||
attachments_attachments: :blob })
|
attachments_attachments: :blob })
|
||||||
.order(id: :asc)
|
.order(id: :asc)
|
||||||
|
|
||||||
|
expires_at = Accounts.link_expires_at(current_account)
|
||||||
|
|
||||||
if params[:format] == 'csv'
|
if params[:format] == 'csv'
|
||||||
send_data Submissions::GenerateExportFiles.call(submissions, format: params[:format]),
|
send_data Submissions::GenerateExportFiles.call(submissions, format: params[:format], expires_at:),
|
||||||
filename: "#{@template.name}.csv"
|
filename: "#{@template.name}.csv"
|
||||||
elsif params[:format] == 'xlsx'
|
elsif params[:format] == 'xlsx'
|
||||||
send_data Submissions::GenerateExportFiles.call(submissions, format: params[:format]),
|
send_data Submissions::GenerateExportFiles.call(submissions, format: params[:format], expires_at:),
|
||||||
filename: "#{@template.name}.xlsx"
|
filename: "#{@template.name}.xlsx"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ class TemplatesCloneAndReplaceController < ApplicationController
|
|||||||
|
|
||||||
documents = Templates::ReplaceAttachments.call(cloned_template, params, extract_fields: true)
|
documents = Templates::ReplaceAttachments.call(cloned_template, params, extract_fields: true)
|
||||||
|
|
||||||
|
Templates.maybe_assign_access(cloned_template)
|
||||||
|
|
||||||
cloned_template.save!
|
cloned_template.save!
|
||||||
|
|
||||||
Templates::CloneAttachments.call(template: cloned_template, original_template: @template,
|
Templates::CloneAttachments.call(template: cloned_template, original_template: @template,
|
||||||
|
|||||||
@@ -69,6 +69,8 @@ class TemplatesController < ApplicationController
|
|||||||
@template.account = current_account
|
@template.account = current_account
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Templates.maybe_assign_access(@template)
|
||||||
|
|
||||||
if @template.save
|
if @template.save
|
||||||
Templates::CloneAttachments.call(template: @template, original_template: @base_template) if @base_template
|
Templates::CloneAttachments.call(template: @template, original_template: @base_template) if @base_template
|
||||||
|
|
||||||
|
|||||||
@@ -46,6 +46,8 @@ class TemplatesUploadsController < ApplicationController
|
|||||||
template.folder = TemplateFolders.find_or_create_by_name(current_user, params[:folder_name])
|
template.folder = TemplateFolders.find_or_create_by_name(current_user, params[:folder_name])
|
||||||
template.name = File.basename((url_params || params)[:files].first.original_filename, '.*')
|
template.name = File.basename((url_params || params)[:files].first.original_filename, '.*')
|
||||||
|
|
||||||
|
Templates.maybe_assign_access(template)
|
||||||
|
|
||||||
template.save!
|
template.save!
|
||||||
|
|
||||||
template
|
template
|
||||||
|
|||||||
@@ -60,7 +60,15 @@ export default {
|
|||||||
return ['UL', 'I', 'EM', 'B', 'STRONG', 'P']
|
return ['UL', 'I', 'EM', 'B', 'STRONG', 'P']
|
||||||
},
|
},
|
||||||
dom () {
|
dom () {
|
||||||
const text = this.string.replace(/(?<!\(\s*)(https?:\/\/[^\s)]+)(?!\s*\))/g, (url) => `[${url}](${url})`)
|
const linkParts = this.string.split(/(https?:\/\/[^\s)]+)/g)
|
||||||
|
|
||||||
|
const text = linkParts.map((part, index) => {
|
||||||
|
if (part.match(/^https?:\/\//) && !linkParts[index - 1]?.match(/\(\s*$/) && !linkParts[index + 1]?.match(/^\s*\)/)) {
|
||||||
|
return `[${part}](${part})`
|
||||||
|
} else {
|
||||||
|
return part
|
||||||
|
}
|
||||||
|
}).join('')
|
||||||
|
|
||||||
return new DOMParser().parseFromString(snarkdown(text.replace(/\n/g, '<br>')), 'text/html')
|
return new DOMParser().parseFromString(snarkdown(text.replace(/\n/g, '<br>')), 'text/html')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1265,24 +1265,31 @@ export default {
|
|||||||
if (!field.areas.length) {
|
if (!field.areas.length) {
|
||||||
this.template.fields.splice(this.template.fields.indexOf(field), 1)
|
this.template.fields.splice(this.template.fields.indexOf(field), 1)
|
||||||
|
|
||||||
this.template.fields.forEach((f) => {
|
this.removeFieldConditions(field)
|
||||||
(f.conditions || []).forEach((c) => {
|
}
|
||||||
|
|
||||||
|
this.save()
|
||||||
|
},
|
||||||
|
removeFieldConditions (field) {
|
||||||
|
this.template.fields.forEach((f) => {
|
||||||
|
if (f.conditions) {
|
||||||
|
f.conditions.forEach((c) => {
|
||||||
if (c.field_uuid === field.uuid) {
|
if (c.field_uuid === field.uuid) {
|
||||||
f.conditions.splice(f.conditions.indexOf(c), 1)
|
f.conditions.splice(f.conditions.indexOf(c), 1)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
}
|
||||||
|
})
|
||||||
|
|
||||||
this.template.schema.forEach((item) => {
|
this.template.schema.forEach((item) => {
|
||||||
(item.conditions || []).forEach((c) => {
|
if (item.conditions) {
|
||||||
|
item.conditions.forEach((c) => {
|
||||||
if (c.field_uuid === field.uuid) {
|
if (c.field_uuid === field.uuid) {
|
||||||
item.conditions.splice(item.conditions.indexOf(c), 1)
|
item.conditions.splice(item.conditions.indexOf(c), 1)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
this.save()
|
|
||||||
},
|
},
|
||||||
pasteField () {
|
pasteField () {
|
||||||
const field = this.template.fields.find((f) => f.areas?.includes(this.copiedArea))
|
const field = this.template.fields.find((f) => f.areas?.includes(this.copiedArea))
|
||||||
@@ -1715,8 +1722,15 @@ export default {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
this.template.fields =
|
this.template.fields = this.template.fields.reduce((acc, f) => {
|
||||||
this.template.fields.filter((f) => !removedFieldUuids.includes(f.uuid) || f.areas?.length)
|
if (removedFieldUuids.includes(f.uuid) && !f.areas?.length) {
|
||||||
|
this.removeFieldConditions(f)
|
||||||
|
} else {
|
||||||
|
acc.push(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc
|
||||||
|
}, [])
|
||||||
|
|
||||||
this.save()
|
this.save()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ class AccountConfig < ApplicationRecord
|
|||||||
FORM_PREFILL_SIGNATURE_KEY = 'form_prefill_signature'
|
FORM_PREFILL_SIGNATURE_KEY = 'form_prefill_signature'
|
||||||
ESIGNING_PREFERENCE_KEY = 'esigning_preference'
|
ESIGNING_PREFERENCE_KEY = 'esigning_preference'
|
||||||
DOWNLOAD_LINKS_AUTH_KEY = 'download_links_auth'
|
DOWNLOAD_LINKS_AUTH_KEY = 'download_links_auth'
|
||||||
|
DOWNLOAD_LINKS_EXPIRE_KEY = 'download_links_expire'
|
||||||
FORCE_SSO_AUTH_KEY = 'force_sso_auth'
|
FORCE_SSO_AUTH_KEY = 'force_sso_auth'
|
||||||
FLATTEN_RESULT_PDF_KEY = 'flatten_result_pdf'
|
FLATTEN_RESULT_PDF_KEY = 'flatten_result_pdf'
|
||||||
WITH_SIGNATURE_ID = 'with_signature_id'
|
WITH_SIGNATURE_ID = 'with_signature_id'
|
||||||
|
|||||||
@@ -114,16 +114,16 @@ class Submission < ApplicationRecord
|
|||||||
@fields_uuid_index ||= (template_fields || template.fields).index_by { |f| f['uuid'] }
|
@fields_uuid_index ||= (template_fields || template.fields).index_by { |f| f['uuid'] }
|
||||||
end
|
end
|
||||||
|
|
||||||
def audit_trail_url
|
def audit_trail_url(expires_at: nil)
|
||||||
return if audit_trail.blank?
|
return if audit_trail.blank?
|
||||||
|
|
||||||
ActiveStorage::Blob.proxy_url(audit_trail.blob)
|
ActiveStorage::Blob.proxy_url(audit_trail.blob, expires_at:)
|
||||||
end
|
end
|
||||||
alias audit_log_url audit_trail_url
|
alias audit_log_url audit_trail_url
|
||||||
|
|
||||||
def combined_document_url
|
def combined_document_url(expires_at: nil)
|
||||||
return if combined_document.blank?
|
return if combined_document.blank?
|
||||||
|
|
||||||
ActiveStorage::Blob.proxy_url(combined_document.blob)
|
ActiveStorage::Blob.proxy_url(combined_document.blob, expires_at:)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -130,6 +130,18 @@
|
|||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
<% account_config = AccountConfig.find_or_initialize_by(account: current_account, key: AccountConfig::DOWNLOAD_LINKS_EXPIRE_KEY) %>
|
||||||
|
<% if can?(:manage, account_config) %>
|
||||||
|
<%= form_for account_config, url: account_configs_path, method: :post do |f| %>
|
||||||
|
<%= f.hidden_field :key %>
|
||||||
|
<div class="flex items-center justify-between py-2.5">
|
||||||
|
<span>
|
||||||
|
<%= t('expirable_file_download_links') %>
|
||||||
|
</span>
|
||||||
|
<%= f.check_box :value, class: 'toggle', checked: account_config.value != false, onchange: 'this.form.requestSubmit()' %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
<% account_config = AccountConfig.find_or_initialize_by(account: current_account, key: AccountConfig::DOWNLOAD_LINKS_AUTH_KEY) %>
|
<% account_config = AccountConfig.find_or_initialize_by(account: current_account, key: AccountConfig::DOWNLOAD_LINKS_AUTH_KEY) %>
|
||||||
<% if can?(:manage, account_config) %>
|
<% if can?(:manage, account_config) %>
|
||||||
<%= form_for account_config, url: account_configs_path, method: :post do |f| %>
|
<%= form_for account_config, url: account_configs_path, method: :post do |f| %>
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ en: &en
|
|||||||
select: Select
|
select: Select
|
||||||
party: Party
|
party: Party
|
||||||
edit_order: Edit Order
|
edit_order: Edit Order
|
||||||
|
expirable_file_download_links: Expirable file download links
|
||||||
invite_form_fields: Invite form fields
|
invite_form_fields: Invite form fields
|
||||||
default_parties: Default parties
|
default_parties: Default parties
|
||||||
authenticate_embedded_form_preview_with_token: Authenticate embedded form preview with token
|
authenticate_embedded_form_preview_with_token: Authenticate embedded form preview with token
|
||||||
@@ -36,6 +37,7 @@ en: &en
|
|||||||
bcc_recipients: BCC recipients
|
bcc_recipients: BCC recipients
|
||||||
resend_pending: Re-send pending
|
resend_pending: Re-send pending
|
||||||
always_enforce_signing_order: Always enforce the signing order
|
always_enforce_signing_order: Always enforce the signing order
|
||||||
|
create_templates_with_private_access_by_default: Create templates with private access by default
|
||||||
ensure_unique_recipients: Ensure unique recipients
|
ensure_unique_recipients: Ensure unique recipients
|
||||||
edit_per_party: Edit per party
|
edit_per_party: Edit per party
|
||||||
reply_to: Reply to
|
reply_to: Reply to
|
||||||
@@ -900,6 +902,8 @@ en: &en
|
|||||||
range_without_total: "%{from}-%{to} events"
|
range_without_total: "%{from}-%{to} events"
|
||||||
|
|
||||||
es: &es
|
es: &es
|
||||||
|
expirable_file_download_links: Enlaces de descarga de archivos con vencimiento
|
||||||
|
create_templates_with_private_access_by_default: Crear plantillas con acceso privado por defecto
|
||||||
party: Parte
|
party: Parte
|
||||||
edit_order: Edita Pedido
|
edit_order: Edita Pedido
|
||||||
select: Seleccionar
|
select: Seleccionar
|
||||||
@@ -1780,6 +1784,8 @@ es: &es
|
|||||||
range_without_total: "%{from}-%{to} eventos"
|
range_without_total: "%{from}-%{to} eventos"
|
||||||
|
|
||||||
it: &it
|
it: &it
|
||||||
|
expirable_file_download_links: Link di download di file con scadenza
|
||||||
|
create_templates_with_private_access_by_default: Crea modelli con accesso privato per impostazione predefinita
|
||||||
party: Parte
|
party: Parte
|
||||||
edit_order: Modifica Ordine
|
edit_order: Modifica Ordine
|
||||||
select: Seleziona
|
select: Seleziona
|
||||||
@@ -2660,6 +2666,8 @@ it: &it
|
|||||||
range_without_total: "%{from}-%{to} eventi"
|
range_without_total: "%{from}-%{to} eventi"
|
||||||
|
|
||||||
fr: &fr
|
fr: &fr
|
||||||
|
expirable_file_download_links: Liens de téléchargement de fichiers expirables
|
||||||
|
create_templates_with_private_access_by_default: Créer des modèles avec un accès privé par défaut
|
||||||
party: Partie
|
party: Partie
|
||||||
edit_order: Modifier la commande
|
edit_order: Modifier la commande
|
||||||
select: Sélectionner
|
select: Sélectionner
|
||||||
@@ -3543,6 +3551,8 @@ fr: &fr
|
|||||||
range_without_total: "%{from} à %{to} événements"
|
range_without_total: "%{from} à %{to} événements"
|
||||||
|
|
||||||
pt: &pt
|
pt: &pt
|
||||||
|
expirable_file_download_links: Links de download de arquivos com expiração
|
||||||
|
create_templates_with_private_access_by_default: Criar modelos com acesso privado por padrão
|
||||||
party: Parte
|
party: Parte
|
||||||
edit_order: Edita Pedido
|
edit_order: Edita Pedido
|
||||||
select: Selecionar
|
select: Selecionar
|
||||||
@@ -4424,6 +4434,8 @@ pt: &pt
|
|||||||
range_without_total: "%{from}-%{to} eventos"
|
range_without_total: "%{from}-%{to} eventos"
|
||||||
|
|
||||||
de: &de
|
de: &de
|
||||||
|
expirable_file_download_links: Ablaufbare Datei-Download-Links
|
||||||
|
create_templates_with_private_access_by_default: Vorlagen standardmäßig mit privatem Zugriff erstellen
|
||||||
party: Partei
|
party: Partei
|
||||||
edit_order: Bestellung bearbeiten
|
edit_order: Bestellung bearbeiten
|
||||||
select: Auswählen
|
select: Auswählen
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class PopulateExpireLinkConfigs < ActiveRecord::Migration[8.0]
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
class MigrationAccount < ActiveRecord::Base
|
||||||
|
self.table_name = 'accounts'
|
||||||
|
end
|
||||||
|
|
||||||
|
class MigrationAccountConfig < ActiveRecord::Base
|
||||||
|
self.table_name = 'account_configs'
|
||||||
|
|
||||||
|
serialize :value, coder: JSON
|
||||||
|
end
|
||||||
|
|
||||||
|
def up
|
||||||
|
MigrationAccount.find_each do |account|
|
||||||
|
config = MigrationAccountConfig.find_or_initialize_by(key: 'download_links_expire', account_id: account.id)
|
||||||
|
|
||||||
|
next if config.persisted?
|
||||||
|
|
||||||
|
config.value = false
|
||||||
|
config.save!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
+1
-1
@@ -10,7 +10,7 @@
|
|||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema[8.0].define(version: 2025_07_18_121133) do
|
ActiveRecord::Schema[8.0].define(version: 2025_08_31_125322) do
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "btree_gin"
|
enable_extension "btree_gin"
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module Accounts
|
module Accounts
|
||||||
|
LINK_EXPIRES_AT = 40.minutes
|
||||||
|
|
||||||
module_function
|
module_function
|
||||||
|
|
||||||
def create_duplicate(account)
|
def create_duplicate(account)
|
||||||
@@ -185,4 +187,11 @@ module Accounts
|
|||||||
rescue TZInfo::InvalidTimezoneIdentifier
|
rescue TZInfo::InvalidTimezoneIdentifier
|
||||||
'UTC'
|
'UTC'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def link_expires_at(account)
|
||||||
|
return if AccountConfig.find_or_initialize_by(account: account,
|
||||||
|
key: AccountConfig::DOWNLOAD_LINKS_EXPIRE_KEY).value == false
|
||||||
|
|
||||||
|
LINK_EXPIRES_AT.from_now
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -102,6 +102,7 @@ module ReplaceEmailVariables
|
|||||||
return unless submitter
|
return unless submitter
|
||||||
|
|
||||||
value = submitter.try(field_name)
|
value = submitter.try(field_name)
|
||||||
|
expires_at = nil
|
||||||
|
|
||||||
if value_name
|
if value_name
|
||||||
field = (submission.template_fields || submission.template.fields).find { |e| e['name'] == value_name }
|
field = (submission.template_fields || submission.template.fields).find { |e| e['name'] == value_name }
|
||||||
@@ -114,7 +115,11 @@ module ReplaceEmailVariables
|
|||||||
|
|
||||||
attachment = submitter.attachments.find { |e| e.uuid == attachment_uuid }
|
attachment = submitter.attachments.find { |e| e.uuid == attachment_uuid }
|
||||||
|
|
||||||
ActiveStorage::Blob.proxy_url(attachment.blob) if attachment
|
if attachment
|
||||||
|
expires_at ||= Accounts.link_expires_at(Account.new(id: submission.account_id))
|
||||||
|
|
||||||
|
ActiveStorage::Blob.proxy_url(attachment.blob, expires_at:)
|
||||||
|
end
|
||||||
else
|
else
|
||||||
value[field&.dig('uuid')]
|
value[field&.dig('uuid')]
|
||||||
end
|
end
|
||||||
|
|||||||
+1
-1
@@ -239,7 +239,7 @@ module Submissions
|
|||||||
|
|
||||||
item['conditions'].each_with_object([]) do |condition, acc|
|
item['conditions'].each_with_object([]) do |condition, acc|
|
||||||
result =
|
result =
|
||||||
if fields_index[condition['field_uuid']]['submitter_uuid'] == include_submitter_uuid
|
if fields_index.dig(condition['field_uuid'], 'submitter_uuid') == include_submitter_uuid
|
||||||
submitter_conditions_acc << condition if submitter_conditions_acc
|
submitter_conditions_acc << condition if submitter_conditions_acc
|
||||||
|
|
||||||
true
|
true
|
||||||
|
|||||||
@@ -391,8 +391,8 @@ module Submissions
|
|||||||
composer.formatted_text_box(
|
composer.formatted_text_box(
|
||||||
Array.wrap(value).map do |uuid|
|
Array.wrap(value).map do |uuid|
|
||||||
attachment = submitter.attachments.find { |a| a.uuid == uuid }
|
attachment = submitter.attachments.find { |a| a.uuid == uuid }
|
||||||
link =
|
|
||||||
ActiveStorage::Blob.proxy_url(attachment.blob)
|
link = r.submissions_preview_url(submission.slug, **Docuseal.default_url_options)
|
||||||
|
|
||||||
{ link:, text: "#{attachment.filename}\n", style: :link }
|
{ link:, text: "#{attachment.filename}\n", style: :link }
|
||||||
end,
|
end,
|
||||||
@@ -489,6 +489,10 @@ module Submissions
|
|||||||
padding: [5, 0, 0, 8],
|
padding: [5, 0, 0, 8],
|
||||||
position: :float, text_align: :left)
|
position: :float, text_align: :left)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def r
|
||||||
|
Rails.application.routes.url_helpers
|
||||||
|
end
|
||||||
# rubocop:enable Metrics
|
# rubocop:enable Metrics
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ module Submissions
|
|||||||
|
|
||||||
module_function
|
module_function
|
||||||
|
|
||||||
def call(submissions, format: :csv)
|
def call(submissions, format: :csv, expires_at: nil)
|
||||||
rows = build_table_rows(submissions)
|
rows = build_table_rows(submissions, expires_at:)
|
||||||
|
|
||||||
if format.to_sym == :csv
|
if format.to_sym == :csv
|
||||||
rows_to_csv(rows)
|
rows_to_csv(rows)
|
||||||
@@ -57,7 +57,7 @@ module Submissions
|
|||||||
headers.map { |key| row.find { |e| e[:name] == key }&.dig(:value) }
|
headers.map { |key| row.find { |e| e[:name] == key }&.dig(:value) }
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_table_rows(submissions)
|
def build_table_rows(submissions, expires_at: nil)
|
||||||
submissions.preload(submitters: [attachments_attachments: :blob, documents_attachments: :blob])
|
submissions.preload(submitters: [attachments_attachments: :blob, documents_attachments: :blob])
|
||||||
.find_each.map do |submission|
|
.find_each.map do |submission|
|
||||||
submission_data = []
|
submission_data = []
|
||||||
@@ -69,7 +69,7 @@ module Submissions
|
|||||||
|
|
||||||
submission_data += build_submission_data(submitter, submitter_name, submitters_count)
|
submission_data += build_submission_data(submitter, submitter_name, submitters_count)
|
||||||
|
|
||||||
submission_data += submitter_formatted_fields(submitter).map do |field|
|
submission_data += submitter_formatted_fields(submitter, expires_at:).map do |field|
|
||||||
{
|
{
|
||||||
name: column_name(field[:name], submitter_name, submitters_count),
|
name: column_name(field[:name], submitter_name, submitters_count),
|
||||||
value: field[:value]
|
value: field[:value]
|
||||||
@@ -81,7 +81,7 @@ module Submissions
|
|||||||
submission_data += submitter.documents.map.with_index(1) do |attachment, index|
|
submission_data += submitter.documents.map.with_index(1) do |attachment, index|
|
||||||
{
|
{
|
||||||
name: "#{I18n.t('document')} #{index}",
|
name: "#{I18n.t('document')} #{index}",
|
||||||
value: ActiveStorage::Blob.proxy_url(attachment.blob)
|
value: ActiveStorage::Blob.proxy_url(attachment.blob, expires_at:)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -123,7 +123,7 @@ module Submissions
|
|||||||
submitters_count > 1 ? "#{submitter_name} - #{name}" : name
|
submitters_count > 1 ? "#{submitter_name} - #{name}" : name
|
||||||
end
|
end
|
||||||
|
|
||||||
def submitter_formatted_fields(submitter)
|
def submitter_formatted_fields(submitter, expires_at: nil)
|
||||||
fields = submitter.submission.template_fields || submitter.submission.template.fields
|
fields = submitter.submission.template_fields || submitter.submission.template.fields
|
||||||
|
|
||||||
template_fields = fields.select { |f| f['submitter_uuid'] == submitter.uuid }
|
template_fields = fields.select { |f| f['submitter_uuid'] == submitter.uuid }
|
||||||
@@ -142,11 +142,13 @@ module Submissions
|
|||||||
value =
|
value =
|
||||||
if template_field_type.in?(%w[image signature])
|
if template_field_type.in?(%w[image signature])
|
||||||
attachment = attachments_index[submitter_value]
|
attachment = attachments_index[submitter_value]
|
||||||
ActiveStorage::Blob.proxy_url(attachment.blob) if attachment
|
|
||||||
|
ActiveStorage::Blob.proxy_url(attachment.blob, expires_at:) if attachment
|
||||||
elsif template_field_type == 'file'
|
elsif template_field_type == 'file'
|
||||||
Array.wrap(submitter_value).compact_blank.filter_map do |e|
|
Array.wrap(submitter_value).compact_blank.filter_map do |e|
|
||||||
attachment = attachments_index[e]
|
attachment = attachments_index[e]
|
||||||
ActiveStorage::Blob.proxy_url(attachment.blob) if attachment
|
|
||||||
|
ActiveStorage::Blob.proxy_url(attachment.blob, expires_at:) if attachment
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
submitter_value
|
submitter_value
|
||||||
|
|||||||
@@ -201,13 +201,15 @@ module Submissions
|
|||||||
|
|
||||||
attachments_data_cache = {}
|
attachments_data_cache = {}
|
||||||
|
|
||||||
return pdfs_index if submitter.submission.template_fields.blank?
|
submission = submitter.submission
|
||||||
|
|
||||||
with_headings = find_last_submitter(submitter.submission, submitter:).blank? if with_headings.nil?
|
return pdfs_index if submission.template_fields.blank?
|
||||||
|
|
||||||
|
with_headings = find_last_submitter(submission, submitter:).blank? if with_headings.nil?
|
||||||
|
|
||||||
locale = submitter.metadata.fetch('lang', account.locale)
|
locale = submitter.metadata.fetch('lang', account.locale)
|
||||||
|
|
||||||
submitter.submission.template_fields.each do |field|
|
submission.template_fields.each do |field|
|
||||||
next if field['type'] == 'heading' && !with_headings
|
next if field['type'] == 'heading' && !with_headings
|
||||||
next if field['submitter_uuid'] != submitter.uuid && field['type'] != 'heading'
|
next if field['submitter_uuid'] != submitter.uuid && field['type'] != 'heading'
|
||||||
|
|
||||||
@@ -476,7 +478,7 @@ module Submissions
|
|||||||
height_diff - (height_diff.zero? ? diff : 0)
|
height_diff - (height_diff.zero? ? diff : 0)
|
||||||
],
|
],
|
||||||
A: { Type: :Action, S: :URI,
|
A: { Type: :Action, S: :URI,
|
||||||
URI: ActiveStorage::Blob.proxy_url(attachment.blob) }
|
URI: r.submissions_preview_url(submission.slug, **Docuseal.default_url_options) }
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -747,11 +749,15 @@ module Submissions
|
|||||||
def maybe_rotate_pdf(pdf)
|
def maybe_rotate_pdf(pdf)
|
||||||
return pdf if pdf.pages.size > MAX_PAGE_ROTATE
|
return pdf if pdf.pages.size > MAX_PAGE_ROTATE
|
||||||
|
|
||||||
|
is_pages_rotated = pdf.pages.root[:Rotate].present? && pdf.pages.root[:Rotate] != 0
|
||||||
|
|
||||||
|
pdf.pages.root[:Rotate] = 0 if is_pages_rotated
|
||||||
|
|
||||||
is_rotated = pdf.pages.filter_map do |page|
|
is_rotated = pdf.pages.filter_map do |page|
|
||||||
page.rotate(0, flatten: true) if page[:Rotate] != 0
|
page.rotate(0, flatten: true) if page[:Rotate] != 0
|
||||||
end.present?
|
end.present?
|
||||||
|
|
||||||
return pdf unless is_rotated
|
return pdf if !is_rotated && !is_pages_rotated
|
||||||
|
|
||||||
io = StringIO.new
|
io = StringIO.new
|
||||||
|
|
||||||
@@ -873,7 +879,7 @@ module Submissions
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def h
|
def r
|
||||||
Rails.application.routes.url_helpers
|
Rails.application.routes.url_helpers
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ module Submissions
|
|||||||
module SerializeForApi
|
module SerializeForApi
|
||||||
SERIALIZE_PARAMS = {
|
SERIALIZE_PARAMS = {
|
||||||
only: %i[id name slug source submitters_order expire_at created_at updated_at archived_at],
|
only: %i[id name slug source submitters_order expire_at created_at updated_at archived_at],
|
||||||
methods: %i[audit_log_url combined_document_url],
|
|
||||||
include: {
|
include: {
|
||||||
submitters: { only: %i[id] },
|
submitters: { only: %i[id] },
|
||||||
template: { only: %i[id name external_id created_at updated_at],
|
template: { only: %i[id name external_id created_at updated_at],
|
||||||
@@ -15,11 +14,13 @@ module Submissions
|
|||||||
|
|
||||||
module_function
|
module_function
|
||||||
|
|
||||||
def call(submission, submitters = nil, params = {}, with_events: true, with_documents: true, with_values: true)
|
def call(submission, submitters = nil, params = {}, with_events: true, with_documents: true, with_values: true,
|
||||||
|
expires_at: Accounts.link_expires_at(Account.new(id: submission.account_id)))
|
||||||
submitters ||= submission.submitters.preload(documents_attachments: :blob, attachments_attachments: :blob)
|
submitters ||= submission.submitters.preload(documents_attachments: :blob, attachments_attachments: :blob)
|
||||||
|
|
||||||
serialized_submitters = submitters.map do |submitter|
|
serialized_submitters = submitters.map do |submitter|
|
||||||
Submitters::SerializeForApi.call(submitter, with_documents:, with_events: false, with_values:, params:)
|
Submitters::SerializeForApi.call(submitter, with_documents:, with_events: false, with_values:, params:,
|
||||||
|
expires_at:)
|
||||||
end
|
end
|
||||||
|
|
||||||
json = submission.as_json(SERIALIZE_PARAMS)
|
json = submission.as_json(SERIALIZE_PARAMS)
|
||||||
@@ -30,8 +31,6 @@ module Submissions
|
|||||||
json['submission_events'] = Submitters::SerializeForApi.serialize_events(submission.submission_events)
|
json['submission_events'] = Submitters::SerializeForApi.serialize_events(submission.submission_events)
|
||||||
end
|
end
|
||||||
|
|
||||||
json['combined_document_url'] ||= maybe_build_combined_url(submitters, submission, params)
|
|
||||||
|
|
||||||
if submitters.all?(&:completed_at?)
|
if submitters.all?(&:completed_at?)
|
||||||
last_submitter = submitters.max_by(&:completed_at)
|
last_submitter = submitters.max_by(&:completed_at)
|
||||||
|
|
||||||
@@ -39,10 +38,16 @@ module Submissions
|
|||||||
json['documents'] = serialized_submitters.find { |e| e['id'] == last_submitter.id }['documents']
|
json['documents'] = serialized_submitters.find { |e| e['id'] == last_submitter.id }['documents']
|
||||||
end
|
end
|
||||||
|
|
||||||
|
json['audit_log_url'] = submission.audit_log_url(expires_at:)
|
||||||
|
json['combined_document_url'] = submission.combined_document_url(expires_at:)
|
||||||
|
json['combined_document_url'] ||= maybe_build_combined_url(submitters, submission, params, expires_at:)
|
||||||
|
|
||||||
json['status'] = 'completed'
|
json['status'] = 'completed'
|
||||||
json['completed_at'] = last_submitter.completed_at.as_json
|
json['completed_at'] = last_submitter.completed_at.as_json
|
||||||
else
|
else
|
||||||
json['documents'] = [] if with_documents
|
json['documents'] = [] if with_documents
|
||||||
|
json['audit_log_url'] = nil
|
||||||
|
json['combined_document_url'] = nil
|
||||||
json['status'] = build_status(submission, submitters)
|
json['status'] = build_status(submission, submitters)
|
||||||
json['completed_at'] = nil
|
json['completed_at'] = nil
|
||||||
end
|
end
|
||||||
@@ -60,7 +65,7 @@ module Submissions
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def maybe_build_combined_url(submitters, submission, params)
|
def maybe_build_combined_url(submitters, submission, params, expires_at: nil)
|
||||||
return unless submitters.all?(&:completed_at?)
|
return unless submitters.all?(&:completed_at?)
|
||||||
|
|
||||||
attachment = submission.combined_document_attachment
|
attachment = submission.combined_document_attachment
|
||||||
@@ -71,7 +76,7 @@ module Submissions
|
|||||||
attachment = Submissions::GenerateCombinedAttachment.call(submitter)
|
attachment = Submissions::GenerateCombinedAttachment.call(submitter)
|
||||||
end
|
end
|
||||||
|
|
||||||
ActiveStorage::Blob.proxy_url(attachment.blob) if attachment
|
ActiveStorage::Blob.proxy_url(attachment.blob, expires_at:) if attachment
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ module Submitters
|
|||||||
module_function
|
module_function
|
||||||
|
|
||||||
def call(submitter, with_template: false, with_events: false, with_documents: true, with_urls: false,
|
def call(submitter, with_template: false, with_events: false, with_documents: true, with_urls: false,
|
||||||
with_values: true, params: {})
|
with_values: true, params: {}, expires_at: Accounts.link_expires_at(Account.new(id: submitter.account_id)))
|
||||||
ActiveRecord::Associations::Preloader.new(
|
ActiveRecord::Associations::Preloader.new(
|
||||||
records: [submitter],
|
records: [submitter],
|
||||||
associations: if with_documents
|
associations: if with_documents
|
||||||
@@ -24,15 +24,19 @@ module Submitters
|
|||||||
additional_attrs = {}
|
additional_attrs = {}
|
||||||
|
|
||||||
if params[:include].to_s.include?('fields')
|
if params[:include].to_s.include?('fields')
|
||||||
additional_attrs['fields'] = SerializeForWebhook.build_fields_array(submitter)
|
additional_attrs['fields'] = SerializeForWebhook.build_fields_array(submitter, expires_at:)
|
||||||
end
|
end
|
||||||
|
|
||||||
if with_template
|
if with_template
|
||||||
additional_attrs['template'] = submitter.submission.template.as_json(only: %i[id name created_at updated_at])
|
additional_attrs['template'] = submitter.submission.template.as_json(only: %i[id name created_at updated_at])
|
||||||
end
|
end
|
||||||
|
|
||||||
additional_attrs['values'] = SerializeForWebhook.build_values_array(submitter) if with_values
|
additional_attrs['values'] = SerializeForWebhook.build_values_array(submitter, expires_at:) if with_values
|
||||||
additional_attrs['documents'] = SerializeForWebhook.build_documents_array(submitter) if with_documents
|
|
||||||
|
if with_documents
|
||||||
|
additional_attrs['documents'] = SerializeForWebhook.build_documents_array(submitter, expires_at:)
|
||||||
|
end
|
||||||
|
|
||||||
additional_attrs['preferences'] = submitter.preferences.except('default_values')
|
additional_attrs['preferences'] = submitter.preferences.except('default_values')
|
||||||
additional_attrs['submission_events'] = serialize_events(submitter.submission_events) if with_events
|
additional_attrs['submission_events'] = serialize_events(submitter.submission_events) if with_events
|
||||||
|
|
||||||
|
|||||||
@@ -10,13 +10,13 @@ module Submitters
|
|||||||
|
|
||||||
module_function
|
module_function
|
||||||
|
|
||||||
def call(submitter)
|
def call(submitter, expires_at: Accounts.link_expires_at(Account.new(id: submitter.account_id)))
|
||||||
ActiveRecord::Associations::Preloader.new(
|
ActiveRecord::Associations::Preloader.new(
|
||||||
records: [submitter], associations: [documents_attachments: :blob, attachments_attachments: :blob]
|
records: [submitter], associations: [documents_attachments: :blob, attachments_attachments: :blob]
|
||||||
).call
|
).call
|
||||||
|
|
||||||
values = build_values_array(submitter)
|
values = build_values_array(submitter, expires_at:)
|
||||||
documents = build_documents_array(submitter)
|
documents = build_documents_array(submitter, expires_at:)
|
||||||
|
|
||||||
submission = submitter.submission
|
submission = submitter.submission
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ module Submitters
|
|||||||
'preferences' => submitter.preferences.except('default_values'),
|
'preferences' => submitter.preferences.except('default_values'),
|
||||||
'values' => values,
|
'values' => values,
|
||||||
'documents' => documents,
|
'documents' => documents,
|
||||||
'audit_log_url' => submitter.submission.audit_log_url,
|
'audit_log_url' => submitter.submission.audit_log_url(expires_at:),
|
||||||
'submission_url' => r.submissions_preview_url(submission.slug, **Docuseal.default_url_options),
|
'submission_url' => r.submissions_preview_url(submission.slug, **Docuseal.default_url_options),
|
||||||
'template' => submission.template.as_json(
|
'template' => submission.template.as_json(
|
||||||
only: %i[id name external_id created_at updated_at],
|
only: %i[id name external_id created_at updated_at],
|
||||||
@@ -40,15 +40,15 @@ module Submitters
|
|||||||
),
|
),
|
||||||
'submission' => {
|
'submission' => {
|
||||||
'id' => submission.id,
|
'id' => submission.id,
|
||||||
'audit_log_url' => submission.audit_log_url,
|
'audit_log_url' => submission.audit_log_url(expires_at:),
|
||||||
'combined_document_url' => submission.combined_document_url,
|
'combined_document_url' => submission.combined_document_url(expires_at:),
|
||||||
'status' => build_submission_status(submission),
|
'status' => build_submission_status(submission),
|
||||||
'url' => r.submissions_preview_url(submission.slug, **Docuseal.default_url_options),
|
'url' => r.submissions_preview_url(submission.slug, **Docuseal.default_url_options),
|
||||||
'created_at' => submission.created_at.as_json
|
'created_at' => submission.created_at.as_json
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_values_array(submitter)
|
def build_values_array(submitter, expires_at: nil)
|
||||||
fields = submitter.submission.template_fields.presence || submitter.submission&.template&.fields || []
|
fields = submitter.submission.template_fields.presence || submitter.submission&.template&.fields || []
|
||||||
attachments_index = submitter.attachments.index_by(&:uuid)
|
attachments_index = submitter.attachments.index_by(&:uuid)
|
||||||
submitter_field_counters = Hash.new { 0 }
|
submitter_field_counters = Hash.new { 0 }
|
||||||
@@ -64,13 +64,13 @@ module Submitters
|
|||||||
|
|
||||||
next if !submitter.values.key?(field['uuid']) && !submitter.completed_at?
|
next if !submitter.values.key?(field['uuid']) && !submitter.completed_at?
|
||||||
|
|
||||||
value = fetch_field_value(field, submitter.values[field['uuid']], attachments_index)
|
value = fetch_field_value(field, submitter.values[field['uuid']], attachments_index, expires_at:)
|
||||||
|
|
||||||
{ 'field' => field_name, 'value' => value }
|
{ 'field' => field_name, 'value' => value }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_fields_array(submitter)
|
def build_fields_array(submitter, expires_at: nil)
|
||||||
fields = submitter.submission.template_fields.presence || submitter.submission&.template&.fields || []
|
fields = submitter.submission.template_fields.presence || submitter.submission&.template&.fields || []
|
||||||
attachments_index = submitter.attachments.index_by(&:uuid)
|
attachments_index = submitter.attachments.index_by(&:uuid)
|
||||||
submitter_field_counters = Hash.new { 0 }
|
submitter_field_counters = Hash.new { 0 }
|
||||||
@@ -86,7 +86,7 @@ module Submitters
|
|||||||
|
|
||||||
next if !submitter.values.key?(field['uuid']) && !submitter.completed_at?
|
next if !submitter.values.key?(field['uuid']) && !submitter.completed_at?
|
||||||
|
|
||||||
value = fetch_field_value(field, submitter.values[field['uuid']], attachments_index)
|
value = fetch_field_value(field, submitter.values[field['uuid']], attachments_index, expires_at:)
|
||||||
|
|
||||||
{ 'name' => field_name, 'uuid' => field['uuid'], 'value' => value, 'readonly' => field['readonly'] == true }
|
{ 'name' => field_name, 'uuid' => field['uuid'], 'value' => value, 'readonly' => field['readonly'] == true }
|
||||||
end
|
end
|
||||||
@@ -104,26 +104,26 @@ module Submitters
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_documents_array(submitter)
|
def build_documents_array(submitter, expires_at: nil)
|
||||||
submitter.documents.map do |attachment|
|
submitter.documents.map do |attachment|
|
||||||
{ 'name' => attachment.filename.base, 'url' => rails_storage_proxy_url(attachment) }
|
{ 'name' => attachment.filename.base, 'url' => rails_storage_proxy_url(attachment, expires_at:) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_field_value(field, value, attachments_index)
|
def fetch_field_value(field, value, attachments_index, expires_at: nil)
|
||||||
if field['type'].in?(%w[image signature initials stamp payment])
|
if field['type'].in?(%w[image signature initials stamp payment])
|
||||||
rails_storage_proxy_url(attachments_index[value])
|
rails_storage_proxy_url(attachments_index[value], expires_at:)
|
||||||
elsif field['type'] == 'file'
|
elsif field['type'] == 'file'
|
||||||
Array.wrap(value).compact_blank.filter_map { |e| rails_storage_proxy_url(attachments_index[e]) }
|
Array.wrap(value).compact_blank.filter_map { |e| rails_storage_proxy_url(attachments_index[e], expires_at:) }
|
||||||
else
|
else
|
||||||
value
|
value
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def rails_storage_proxy_url(attachment)
|
def rails_storage_proxy_url(attachment, expires_at: nil)
|
||||||
return if attachment.blank?
|
return if attachment.blank?
|
||||||
|
|
||||||
ActiveStorage::Blob.proxy_url(attachment.blob)
|
ActiveStorage::Blob.proxy_url(attachment.blob, expires_at:)
|
||||||
end
|
end
|
||||||
|
|
||||||
def r
|
def r
|
||||||
|
|||||||
@@ -37,6 +37,10 @@ module Templates
|
|||||||
hash
|
hash
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def maybe_assign_access(_template)
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
def search(current_user, templates, keyword)
|
def search(current_user, templates, keyword)
|
||||||
if Docuseal.fulltext_search?
|
if Docuseal.fulltext_search?
|
||||||
fulltext_search(current_user, templates, keyword)
|
fulltext_search(current_user, templates, keyword)
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ module Templates
|
|||||||
|
|
||||||
module_function
|
module_function
|
||||||
|
|
||||||
def call(template, schema_documents = template.schema_documents.preload(:blob), preview_image_attachments = nil)
|
def call(template, schema_documents: template.schema_documents.preload(:blob), preview_image_attachments: nil,
|
||||||
|
expires_at: Accounts.link_expires_at(Account.new(id: template.account_id)))
|
||||||
json = template.as_json(SERIALIZE_PARAMS)
|
json = template.as_json(SERIALIZE_PARAMS)
|
||||||
|
|
||||||
preview_image_attachments ||=
|
preview_image_attachments ||=
|
||||||
@@ -40,8 +41,8 @@ module Templates
|
|||||||
{
|
{
|
||||||
'id' => attachment.id,
|
'id' => attachment.id,
|
||||||
'uuid' => attachment.uuid,
|
'uuid' => attachment.uuid,
|
||||||
'url' => ActiveStorage::Blob.proxy_url(attachment.blob),
|
'url' => ActiveStorage::Blob.proxy_url(attachment.blob, expires_at:),
|
||||||
'preview_image_url' => first_page_blob && ActiveStorage::Blob.proxy_url(first_page_blob),
|
'preview_image_url' => first_page_blob && ActiveStorage::Blob.proxy_url(first_page_blob, expires_at:),
|
||||||
'filename' => attachment.filename
|
'filename' => attachment.filename
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -8,6 +8,10 @@ describe 'Templates API' do
|
|||||||
let(:folder) { create(:template_folder, account:) }
|
let(:folder) { create(:template_folder, account:) }
|
||||||
let(:template_preferences) { { 'request_email_subject' => 'Subject text', 'request_email_body' => 'Body Text' } }
|
let(:template_preferences) { { 'request_email_subject' => 'Subject text', 'request_email_body' => 'Body Text' } }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(Accounts).to receive(:link_expires_at).and_return(Accounts::LINK_EXPIRES_AT)
|
||||||
|
end
|
||||||
|
|
||||||
describe 'GET /api/templates' do
|
describe 'GET /api/templates' do
|
||||||
it 'returns a list of templates' do
|
it 'returns a list of templates' do
|
||||||
templates = [
|
templates = [
|
||||||
@@ -211,8 +215,8 @@ describe 'Templates API' do
|
|||||||
{
|
{
|
||||||
id: template.documents.first.id,
|
id: template.documents.first.id,
|
||||||
uuid: template.documents.first.uuid,
|
uuid: template.documents.first.uuid,
|
||||||
url: ActiveStorage::Blob.proxy_url(attachment.blob),
|
url: ActiveStorage::Blob.proxy_url(attachment.blob, expires_at: Accounts::LINK_EXPIRES_AT),
|
||||||
preview_image_url: ActiveStorage::Blob.proxy_url(first_page_blob),
|
preview_image_url: ActiveStorage::Blob.proxy_url(first_page_blob, expires_at: Accounts::LINK_EXPIRES_AT),
|
||||||
filename: 'sample-document.pdf'
|
filename: 'sample-document.pdf'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -235,7 +239,7 @@ describe 'Templates API' do
|
|||||||
folder_name: folder.name,
|
folder_name: folder.name,
|
||||||
source: 'native',
|
source: 'native',
|
||||||
external_id: template.external_id,
|
external_id: template.external_id,
|
||||||
application_key: template.external_id # Backward compatibility
|
application_key: template.external_id
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user