add download combined pdf button

pull/289/head
Pete Matsyburka 1 year ago
parent fc60df7974
commit 9866e5468c

@ -16,13 +16,16 @@ class SubmissionsDebugController < ApplicationController
render 'submit_form/show'
end
f.pdf do
result =
if params[:audit]
Submissions::GenerateAuditTrail.call(@submitter.submission)
elsif params[:combined]
Submissions::GenerateCombinedAttachment.call(@submitter)
else
Submissions::GenerateResultAttachments.call(@submitter)
end
send_data ActiveStorage::Attachment.where(name: params[:audit] ? :audit_trail : :documents).last.download,
send_data Array.wrap(result).first.download,
filename: 'debug.pdf',
disposition: 'inline',
type: 'application/pdf'

@ -33,8 +33,18 @@ class SubmissionsDownloadController < ApplicationController
return head :not_found
end
if params[:combined]
url = build_combined_url(submitter)
if url
render json: [url]
else
head :not_found
end
else
render json: build_urls(last_submitter)
end
end
private
@ -47,4 +57,14 @@ class SubmissionsDownloadController < ApplicationController
ActiveStorage::Blob.proxy_url(attachment.blob, expires_at: FILES_TTL.from_now.to_i)
end
end
def build_combined_url(submitter)
return if submitter.submission.submitters.exists?(completed_at: nil)
return if submitter.submission.submitters.order(:completed_at).last != submitter
attachment = submitter.submission.combined_document_attachment
attachment ||= Submissions::GenerateCombinedAttachment.call(submitter)
ActiveStorage::Blob.proxy_url(attachment.blob, expires_at: FILES_TTL.from_now.to_i)
end
end

@ -52,6 +52,7 @@ class Submission < ApplicationRecord
attribute :slug, :string, default: -> { SecureRandom.base58(14) }
has_one_attached :audit_trail
has_one_attached :combined_document
has_many :template_schema_documents,
->(e) { where(uuid: (e.template_schema.presence || e.template.schema).pluck('attachment_uuid')) },

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" class="<%= local_assigns[:class] %>" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M6 9l6 6l6 -6" />
</svg>

After

Width:  |  Height:  |  Size: 312 B

@ -14,7 +14,9 @@
</a>
<% end %>
<% if last_submitter = @submission.submitters.to_a.select(&:completed_at?).max_by(&:completed_at) %>
<download-button data-src="<%= submitter_download_index_path(last_submitter.slug, { sig: params[:sig] }.compact) %>" class="base-button">
<% is_all_completed = @submission.submitters.to_a.all?(&:completed_at?) %>
<div class="join relative">
<download-button data-src="<%= submitter_download_index_path(last_submitter.slug, { sig: params[:sig] }.compact) %>" class="base-button <%= '!rounded-r-none !pr-2' if is_all_completed %>">
<span class="flex items-center justify-center space-x-2" data-target="download-button.defaultButton">
<%= svg_icon('download', class: 'w-6 h-6') %>
<span class="hidden md:inline">Download</span>
@ -24,6 +26,30 @@
<span class="hidden md:inline">Downloading</span>
</span>
</download-button>
<% if is_all_completed %>
<div class="dropdown dropdown-end">
<label tabindex="0" class="base-button !rounded-l-none !pl-1 !pr-2 !border-l-neutral-500">
<span class="text-sm align-text-top">
<%= svg_icon('chevron_down', class: 'w-6 h-6 flex-shrink-0 stroke-2') %>
</span>
</label>
<ul tabindex="0" class="z-10 dropdown-content p-2 mt-2 shadow menu text-base bg-base-100 rounded-box text-right">
<li>
<download-button data-src="<%= submitter_download_index_path(last_submitter.slug, { sig: params[:sig], combined: true }.compact) %>" class="flex items-center">
<span class="flex items-center justify-center space-x-2" data-target="download-button.defaultButton">
<%= svg_icon('download', class: 'w-6 h-6 flex-shrink-0') %>
<span class="whitespace-nowrap">Download combined PDF</span>
</span>
<span class="flex items-center justify-center space-x-2 hidden" data-target="download-button.loadingButton">
<%= svg_icon('loader', class: 'w-6 h-6 animate-spin') %>
<span>Downloading</span>
</span>
</download-button>
</li>
</ul>
</div>
<% end %>
</div>
<% elsif @submission.submitters.to_a.size == 1 %>
<%= render 'shared/clipboard_copy', text: start_form_url(slug: @submission.template.slug), class: 'base-button', icon_class: 'w-6 h-6 text-white', copy_title: 'Copy Share Link', copied_title: 'Copied to Clipboard' %>
<% end %>
@ -77,7 +103,7 @@
<% submitter_field_counters = Hash.new { 0 } %>
<% (@submission.template_submitters || @submission.template.submitters).each_with_index do |item, index| %>
<% submitter = @submission.submitters.find { |e| e.uuid == item['uuid'] } %>
<div class="sticky -top-1 bg-base-100 pt-1 -mt-1 z-10">
<div class="sticky -top-1 bg-base-100 pt-1 -mt-1">
<div class="border border-base-300 rounded-md px-2 py-1 mb-1">
<div class="flex items-center space-x-1">
<span class="mx-1 w-3 h-3 rounded-full <%= colors[index] %>"></span>

@ -12,7 +12,6 @@ module Submissions
'Helvetica'
end
SIGN_REASON = 'Signed with DocuSeal.co'
VERIFIED_TEXT = 'Verified'
UNVERIFIED_TEXT = 'Unverified'
@ -31,9 +30,35 @@ module Submissions
# rubocop:disable Metrics
def call(submission)
document = build_audit_trail(submission)
account = submission.account
pkcs = Accounts.load_signing_pkcs(account)
tsa_url = Accounts.load_timeserver_url(account)
io = StringIO.new
document.trailer.info[:Creator] = "#{Docuseal.product_name} (#{Docuseal::PRODUCT_URL})"
sign_params = {
reason: sign_reason,
**Submissions::GenerateResultAttachments.build_signing_params(pkcs, tsa_url)
}
document.sign(io, **sign_params)
ActiveStorage::Attachment.create!(
blob: ActiveStorage::Blob.create_and_upload!(
io: StringIO.new(io.string), filename: "Audit Log - #{submission.template.name}.pdf"
),
name: 'audit_trail',
record: submission
)
end
def build_audit_trail(submission)
account = submission.account
verify_url = Rails.application.routes.url_helpers.settings_esign_url(**Docuseal.default_url_options)
page_size =
if TimeUtils.timezone_abbr(account.timezone, Time.current.beginning_of_year).in?(US_TIMEZONES)
@ -299,24 +324,11 @@ module Submissions
composer.table(events_data, cell_style: { padding: [0, 0, 12, 0], border: { width: 0 } }) if events_data.present?
io = StringIO.new
composer.document.trailer.info[:Creator] = "#{Docuseal.product_name} (#{Docuseal::PRODUCT_URL})"
sign_params = {
reason: SIGN_REASON,
**Submissions::GenerateResultAttachments.build_signing_params(pkcs, tsa_url)
}
composer.document.sign(io, **sign_params)
composer.document
end
ActiveStorage::Attachment.create!(
blob: ActiveStorage::Blob.create_and_upload!(
io: StringIO.new(io.string), filename: "Audit Log - #{submission.template.name}.pdf"
),
name: 'audit_trail',
record: submission
)
def sign_reason
'Signed with DocuSeal.co'
end
def add_logo(column, _submission = nil)

@ -0,0 +1,62 @@
# frozen_string_literal: true
module Submissions
module GenerateCombinedAttachment
module_function
def call(submitter)
pdf = build_combined_pdf(submitter)
submission = submitter.submission
account = submission.account
pkcs = Accounts.load_signing_pkcs(account)
tsa_url = Accounts.load_timeserver_url(account)
io = StringIO.new
pdf.trailer.info[:Creator] = "#{Docuseal.product_name} (#{Docuseal::PRODUCT_URL})"
sign_params = {
reason: sign_reason,
**Submissions::GenerateResultAttachments.build_signing_params(pkcs, tsa_url)
}
pdf.sign(io, **sign_params)
ActiveStorage::Attachment.create!(
blob: ActiveStorage::Blob.create_and_upload!(
io: StringIO.new(io.string), filename: "#{submission.template.name}.pdf"
),
name: 'combined_document',
record: submission
)
end
def build_combined_pdf(submitter)
pdfs_index = Submissions::GenerateResultAttachments.generate_pdfs(submitter)
audit_trail = Submissions::GenerateAuditTrail.build_audit_trail(submitter.submission)
audit_trail.dispatch_message(:complete_objects)
result = HexaPDF::Document.new
submitter.submission.template_schema.each do |item|
pdf = pdfs_index[item['attachment_uuid']]
pdf.dispatch_message(:complete_objects)
pdf.pages.each { |page| result.pages << result.import(page) }
end
audit_trail.pages.each { |page| result.pages << result.import(page) }
result
end
def sign_reason
'Signed with DocuSeal.co'
end
end
end

@ -39,17 +39,58 @@ module Submissions
# rubocop:disable Metrics
def call(submitter)
cell_layouter = HexaPDF::Layout::TextLayouter.new(text_valign: :center, text_align: :center)
pdfs_index = generate_pdfs(submitter)
template = submitter.submission.template
account = submitter.account
pkcs = Accounts.load_signing_pkcs(account)
tsa_url = Accounts.load_timeserver_url(account)
image_pdfs = []
original_documents = template.documents.preload(:blob)
result_attachments =
submitter.submission.template_schema.map do |item|
pdf = pdfs_index[item['attachment_uuid']]
attachment = build_pdf_attachment(pdf:, submitter:, pkcs:, tsa_url:,
uuid: item['attachment_uuid'],
name: item['name'])
image_pdfs << pdf if original_documents.find { |a| a.uuid == item['attachment_uuid'] }.image?
attachment
end
return result_attachments.map { |e| e.tap(&:save!) } if image_pdfs.size < 2
images_pdf =
image_pdfs.each_with_object(HexaPDF::Document.new) do |pdf, doc|
pdf.pages.each { |page| doc.pages << doc.import(page) }
end
images_pdf_attachment =
build_pdf_attachment(
pdf: images_pdf,
submitter:,
tsa_url:,
pkcs:,
uuid: images_pdf_uuid(original_documents.select(&:image?)),
name: template.name
)
(result_attachments + [images_pdf_attachment]).map { |e| e.tap(&:save!) }
end
def generate_pdfs(submitter)
cell_layouter = HexaPDF::Layout::TextLayouter.new(text_valign: :center, text_align: :center)
is_flatten =
submitter.account.account_configs
.find_or_initialize_by(key: AccountConfig::FLATTEN_RESULT_PDF_KEY).value != false
account = submitter.account
pkcs = Accounts.load_signing_pkcs(account)
tsa_url = Accounts.load_timeserver_url(account)
attachments_data_cache = {}
pdfs_index = build_pdfs_index(submitter, flatten: is_flatten)
@ -259,40 +300,7 @@ module Submissions
end
end
image_pdfs = []
original_documents = template.documents.preload(:blob)
result_attachments =
submitter.submission.template_schema.map do |item|
pdf = pdfs_index[item['attachment_uuid']]
attachment = build_pdf_attachment(pdf:, submitter:, pkcs:, tsa_url:,
uuid: item['attachment_uuid'],
name: item['name'])
image_pdfs << pdf if original_documents.find { |a| a.uuid == item['attachment_uuid'] }.image?
attachment
end
return result_attachments.map { |e| e.tap(&:save!) } if image_pdfs.size < 2
images_pdf =
image_pdfs.each_with_object(HexaPDF::Document.new) do |pdf, doc|
pdf.pages.each { |page| doc.pages << doc.import(page) }
end
images_pdf_attachment =
build_pdf_attachment(
pdf: images_pdf,
submitter:,
tsa_url:,
pkcs:,
uuid: images_pdf_uuid(original_documents.select(&:image?)),
name: template.name
)
(result_attachments + [images_pdf_attachment]).map { |e| e.tap(&:save!) }
pdfs_index
end
def build_pdf_attachment(pdf:, submitter:, pkcs:, tsa_url:, uuid:, name:)

Loading…
Cancel
Save