prefill signature client side
This commit is contained in:
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user