prefill signature client side

pull/629/head
Pete Matsyburka 3 weeks ago
parent f8cb7ffdab
commit 11b0b16ca7

@ -31,6 +31,7 @@ safeRegisterElement('submission-form', class extends HTMLElement {
isDemo: this.dataset.isDemo === 'true', isDemo: this.dataset.isDemo === 'true',
attribution: this.dataset.attribution !== 'false', attribution: this.dataset.attribution !== 'false',
scrollPadding: this.dataset.scrollPadding || '-80px', scrollPadding: this.dataset.scrollPadding || '-80px',
signatureText: this.dataset.signatureText,
language: this.dataset.language, language: this.dataset.language,
dryRun: this.dataset.dryRun === 'true', dryRun: this.dataset.dryRun === 'true',
expand: ['true', 'false'].includes(this.dataset.expand) ? this.dataset.expand === 'true' : null, expand: ['true', 'false'].includes(this.dataset.expand) ? this.dataset.expand === 'true' : null,

@ -402,6 +402,8 @@
:remember-signature="rememberSignature" :remember-signature="rememberSignature"
:attachments-index="attachmentsIndex" :attachments-index="attachmentsIndex"
:require-signing-reason="requireSigningReason" :require-signing-reason="requireSigningReason"
:signature-text="signatureText"
:signature-src="signatureSrc"
:button-text="submitButtonText" :button-text="submitButtonText"
:dry-run="dryRun" :dry-run="dryRun"
:with-disclosure="withDisclosure" :with-disclosure="withDisclosure"
@ -832,6 +834,16 @@ export default {
required: false, required: false,
default: '' default: ''
}, },
signatureText: {
type: String,
required: false,
default: ''
},
signatureSrc: {
type: String,
required: false,
default: ''
},
previousSignatureValue: { previousSignatureValue: {
type: String, type: String,
required: false, required: false,

@ -408,6 +408,16 @@ export default {
required: false, required: false,
default: '' default: ''
}, },
signatureText: {
type: String,
required: false,
default: ''
},
signatureSrc: {
type: String,
required: false,
default: ''
},
modelValue: { modelValue: {
type: String, type: String,
required: false, required: false,
@ -422,7 +432,7 @@ export default {
isOtherReason: false, isOtherReason: false,
isUsePreviousValue: true, isUsePreviousValue: true,
isTouchAttachment: false, isTouchAttachment: false,
isTextSignature: this.field.preferences?.format === 'typed' || this.field.preferences?.format === 'typed_or_upload', isTextSignature: !this.signatureSrc && (!!this.signatureText || this.field.preferences?.format === 'typed' || this.field.preferences?.format === 'typed_or_upload'),
uploadImageInputKey: Math.random().toString() uploadImageInputKey: Math.random().toString()
} }
}, },
@ -482,7 +492,9 @@ export default {
if (entry.isIntersecting) { if (entry.isIntersecting) {
this.setCanvasSize() this.setCanvasSize()
if (this.isTextSignature) { if (this.signatureSrc) {
this.$nextTick(() => this.drawSignatureSrc())
} else if (this.isTextSignature) {
this.$nextTick(() => { this.$nextTick(() => {
if (this.$refs.textInput) { if (this.$refs.textInput) {
this.initTypedSignature() this.initTypedSignature()
@ -686,7 +698,9 @@ export default {
} }
}, },
async initTypedSignature () { async initTypedSignature () {
if (this.submitter.name) { if (this.signatureText) {
this.$refs.textInput.value = this.signatureText
} else if (this.submitter.name) {
this.$refs.textInput.value = this.submitter.name this.$refs.textInput.value = this.submitter.name
} }
@ -696,6 +710,48 @@ export default {
this.updateWrittenSignature({ target: this.$refs.textInput }) this.updateWrittenSignature({ target: this.$refs.textInput })
} }
}, },
drawSignatureSrc () {
const canvas = this.$refs.canvas
if (!canvas) return
const img = new Image()
img.crossOrigin = 'anonymous'
img.onload = () => {
const context = canvas.getContext('2d')
const aspectRatio = img.width / img.height
const canvasWidth = canvas.width / scale
const canvasHeight = canvas.height / scale
let targetWidth = canvasWidth
let targetHeight = canvasHeight
if (canvasWidth / canvasHeight > aspectRatio) {
targetWidth = canvasHeight * aspectRatio
} else {
targetHeight = canvasWidth / aspectRatio
}
const x = (canvasWidth - targetWidth) / 2
const y = (canvasHeight - targetHeight) / 2
context.clearRect(0, 0, canvasWidth, canvasHeight)
context.drawImage(img, x, y, targetWidth, targetHeight)
this.isSignatureStarted = true
this.$emit('start')
}
img.onerror = () => {
console.error(`Failed to load signature image from ${this.signatureSrc}. The remote server must send an Access-Control-Allow-Origin header to allow CORS access.`)
}
img.src = this.signatureSrc
},
drawImage (event) { drawImage (event) {
this.remove() this.remove()
this.clear() this.clear()

@ -2,4 +2,4 @@
<% data_fields = Submissions.filtered_conditions_fields(submitter).to_json %> <% data_fields = Submissions.filtered_conditions_fields(submitter).to_json %>
<% invite_submitters = (submitter.submission.template_submitters || submitter.submission.template.submitters).select { |s| s['invite_by_uuid'] == submitter.uuid && submitter.submission.submitters.none? { |e| e.uuid == s['uuid'] } }.to_json %> <% invite_submitters = (submitter.submission.template_submitters || submitter.submission.template.submitters).select { |s| s['invite_by_uuid'] == submitter.uuid && submitter.submission.submitters.none? { |e| e.uuid == s['uuid'] } }.to_json %>
<% optional_invite_submitters = (submitter.submission.template_submitters || submitter.submission.template.submitters).select { |s| s['optional_invite_by_uuid'] == submitter.uuid && submitter.submission.submitters.none? { |e| e.uuid == s['uuid'] } }.to_json %> <% optional_invite_submitters = (submitter.submission.template_submitters || submitter.submission.template.submitters).select { |s| s['optional_invite_by_uuid'] == submitter.uuid && submitter.submission.submitters.none? { |e| e.uuid == s['uuid'] } }.to_json %>
<submission-form data-is-demo="<%= Docuseal.demo? %>" data-schema="<%= schema.to_json %>" data-reuse-signature="<%= configs[:reuse_signature] %>" data-require-signing-reason="<%= configs[:require_signing_reason] %>" data-with-signature-id="<%= configs[:with_signature_id] %>" data-with-field-labels="<%= configs[:with_field_labels] %>" data-with-confetti="<%= configs[:with_confetti] %>" data-completed-redirect-url="<%= submitter.preferences['completed_redirect_url'].presence || submitter.submission.template&.preferences&.dig('completed_redirect_url') %>" data-completed-message="<%= (configs[:completed_message]&.compact_blank.presence || submitter.submission.template&.preferences&.dig('completed_message') || {}).to_json %>" data-completed-button="<%= configs[:completed_button].to_json %>" data-go-to-last="<%= submitter.preferences.key?('go_to_last') ? submitter.preferences['go_to_last'] : submitter.opened_at? %>" data-submitter="<%= submitter.to_json(only: %i[uuid slug name phone email]) %>" data-can-send-email="<%= Accounts.can_send_emails?(submitter.submission.account) %>" data-optional-invite-submitters="<%= optional_invite_submitters %>" data-invite-submitters="<%= invite_submitters %>" data-attachments="<%= data_attachments %>" data-fields="<%= data_fields %>" data-values="<%= submitter.values.to_json %>" data-with-typed-signature="<%= configs[:with_typed_signature] %>" data-previous-signature-value="<%= local_assigns[:signature_attachment]&.uuid %>" data-remember-signature="<%= configs[:prefill_signature] %>" data-dry-run="<%= local_assigns[:dry_run] %>" data-expand="<%= local_assigns[:expand] %>" data-scroll-padding="<%= local_assigns[:scroll_padding] %>" data-language="<%= I18n.locale.to_s.split('-').first %>"></submission-form> <submission-form data-is-demo="<%= Docuseal.demo? %>" data-schema="<%= schema.to_json %>" data-reuse-signature="<%= configs[:reuse_signature] %>" data-require-signing-reason="<%= configs[:require_signing_reason] %>" data-with-signature-id="<%= configs[:with_signature_id] %>" data-with-field-labels="<%= configs[:with_field_labels] %>" data-with-confetti="<%= configs[:with_confetti] %>" data-completed-redirect-url="<%= submitter.preferences['completed_redirect_url'].presence || submitter.submission.template&.preferences&.dig('completed_redirect_url') %>" data-completed-message="<%= (configs[:completed_message]&.compact_blank.presence || submitter.submission.template&.preferences&.dig('completed_message') || {}).to_json %>" data-completed-button="<%= configs[:completed_button].to_json %>" data-go-to-last="<%= submitter.preferences.key?('go_to_last') ? submitter.preferences['go_to_last'] : submitter.opened_at? %>" data-submitter="<%= submitter.to_json(only: %i[uuid slug name phone email]) %>" data-can-send-email="<%= Accounts.can_send_emails?(submitter.submission.account) %>" data-optional-invite-submitters="<%= optional_invite_submitters %>" data-invite-submitters="<%= invite_submitters %>" data-attachments="<%= data_attachments %>" data-fields="<%= data_fields %>" data-values="<%= submitter.values.to_json %>" data-with-typed-signature="<%= configs[:with_typed_signature] %>" data-signature-text="<%= params[:signature] %>" data-previous-signature-value="<%= local_assigns[:signature_attachment]&.uuid %>" data-remember-signature="<%= configs[:prefill_signature] %>" data-dry-run="<%= local_assigns[:dry_run] %>" data-expand="<%= local_assigns[:expand] %>" data-scroll-padding="<%= local_assigns[:scroll_padding] %>" data-language="<%= I18n.locale.to_s.split('-').first %>"></submission-form>

@ -9,29 +9,13 @@ module Submitters
def call(submitter, params, cookies = nil, attachments = []) def call(submitter, params, cookies = nil, attachments = [])
attachments = attachments.select { |e| e.record_id == submitter.id && e.record_type == 'Submitter' } attachments = attachments.select { |e| e.record_id == submitter.id && e.record_type == 'Submitter' }
if (value = params[:signature_src].presence || params[:signature].presence) if params[:signed_signature_uuids].present?
find_or_create_signature_from_value(submitter, value, attachments)
elsif params[:signed_signature_uuids].present?
find_storage_signature(submitter, params[:signed_signature_uuids], attachments) find_storage_signature(submitter, params[:signed_signature_uuids], attachments)
elsif cookies elsif cookies
find_session_signature(submitter, cookies, attachments) find_session_signature(submitter, cookies, attachments)
end end
end end
def find_or_create_signature_from_value(submitter, value, attachments)
_, attachment = Submitters::NormalizeValues.normalize_attachment_value(value,
{ 'type' => 'signature' },
submitter.account,
attachments,
for_submitter: submitter)
attachment.record ||= submitter
attachment.save!
attachment
end
def sign_signature_uuid(uuid) def sign_signature_uuid(uuid)
ApplicationRecord.signed_id_verifier.generate(uuid, purpose: SIGNED_UUID_PURPPOSE) ApplicationRecord.signed_id_verifier.generate(uuid, purpose: SIGNED_UUID_PURPPOSE)
end end

Loading…
Cancel
Save