mirror of
https://github.com/docusealco/docuseal
synced 2026-11-04 06:42:32 -05:00
refactor prefill
This commit is contained in:
1 parent
439f95d87e
commit
2d6f4c521e
13 files changed
+247
-128
No files matched your search
@@ -31,24 +31,22 @@ class SubmitFormController < ApplicationController
|
||||
|
||||
Submissions.preload_with_pages(submission)
|
||||
|
||||
Submitters::MaybeUpdateDefaultValues.call(@submitter, current_user)
|
||||
|
||||
@attachments_index = build_attachments_index(submission)
|
||||
|
||||
if current_user && current_user.email == @submitter.email
|
||||
@current_user_data = current_user.as_json(only: %i[first_name last_name], methods: %i[full_name])
|
||||
|
||||
initials = UserConfigs.load_initials(current_user)
|
||||
|
||||
@prefill_initials = Submitters.build_prefill_attachment_data(@submitter, initials) if initials
|
||||
end
|
||||
|
||||
return unless @form_configs[:prefill_signature]
|
||||
|
||||
if (user_signature = UserConfigs.load_signature(current_user))
|
||||
@signature_attachment = ActiveStorage::Attachment.find_or_create_by!(
|
||||
blob_id: user_signature.blob_id,
|
||||
name: 'attachments',
|
||||
record: @submitter
|
||||
)
|
||||
end
|
||||
signature = UserConfigs.load_signature(current_user) ||
|
||||
Submitters::FindRememberedSignature.call(@submitter, params, cookies)
|
||||
|
||||
@signature_attachment ||=
|
||||
Submitters::MaybeAssignDefaultBrowserSignature.call(@submitter, params, cookies, @attachments_index.values)
|
||||
|
||||
@attachments_index[@signature_attachment.uuid] = @signature_attachment if @signature_attachment
|
||||
@prefill_signature = Submitters.build_prefill_attachment_data(@submitter, signature) if signature
|
||||
end
|
||||
|
||||
def update
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class SubmitFormPrefillAttachmentsController < ActionController::API
|
||||
include ActiveStorage::SetCurrent
|
||||
|
||||
def create
|
||||
submitter = Submitter.find_by!(slug: params[:submit_form_slug])
|
||||
|
||||
unless can_upload?(submitter)
|
||||
return render json: { error: I18n.t('form_has_been_archived') }, status: :unprocessable_content
|
||||
end
|
||||
|
||||
uuid, slug = ApplicationRecord.signed_id_verifier.verify(params[:prefill_token], purpose: :prefill_attachment)
|
||||
|
||||
raise ActiveRecord::RecordNotFound if slug != submitter.slug
|
||||
|
||||
source_attachment = ActiveStorage::Attachment.find_by!(uuid:)
|
||||
|
||||
attachment = submitter.attachments_attachments.find_or_create_by!(blob_id: source_attachment.blob_id)
|
||||
|
||||
render json: attachment.as_json(only: %i[uuid created_at], methods: %i[url filename content_type])
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def can_upload?(submitter)
|
||||
!submitter.declined_at? &&
|
||||
!submitter.completed_at? &&
|
||||
!submitter.submission.archived_at? &&
|
||||
!submitter.submission.expired? &&
|
||||
!submitter.submission.template&.archived_at?
|
||||
end
|
||||
end
|
||||
@@ -32,7 +32,9 @@ safeRegisterElement('submission-form', class extends HTMLElement {
|
||||
optionalInviteSubmitters: JSON.parse(this.dataset.optionalInviteSubmitters),
|
||||
schema: JSON.parse(this.dataset.schema),
|
||||
canSendEmail: this.dataset.canSendEmail === 'true',
|
||||
previousSignatureValue: this.dataset.previousSignatureValue,
|
||||
prefillSignature: this.dataset.prefillSignature ? JSON.parse(this.dataset.prefillSignature) : null,
|
||||
prefillInitials: this.dataset.prefillInitials ? JSON.parse(this.dataset.prefillInitials) : null,
|
||||
currentUser: this.dataset.currentUser ? JSON.parse(this.dataset.currentUser) : null,
|
||||
goToLast: this.dataset.goToLast === 'true',
|
||||
isDemo: this.dataset.isDemo === 'true',
|
||||
attribution: this.dataset.attribution !== 'false',
|
||||
|
||||
@@ -491,8 +491,8 @@
|
||||
:reason="values[currentField.preferences?.reason_field_uuid]"
|
||||
:field="currentField"
|
||||
:values="values"
|
||||
:previous-value="previousSignatureValueFor(currentField) || previousSignatureValue"
|
||||
:touch-attachment-uuid="previousSignatureValue"
|
||||
:previous-value="previousSignatureValueFor(currentField)"
|
||||
:prefill-data="prefillSignature"
|
||||
:with-typed-signature="withTypedSignature"
|
||||
:remember-signature="rememberSignature"
|
||||
:attachments-index="attachmentsIndex"
|
||||
@@ -507,7 +507,6 @@
|
||||
:show-field-names="showFieldNames"
|
||||
:native-platform="nativePlatform"
|
||||
@update:reason="values[currentField.preferences?.reason_field_uuid] = $event"
|
||||
@touch-attachment="attachmentsIndex[previousSignatureValue] ? attachmentsIndex[previousSignatureValue].created_at = new Date() : null"
|
||||
@attached="attachments.push($event)"
|
||||
@start="scrollIntoField(currentField)"
|
||||
@minimize="minimizeForm"
|
||||
@@ -521,6 +520,7 @@
|
||||
:dry-run="dryRun"
|
||||
:submitter="submitter"
|
||||
:previous-value="previousInitialsValue"
|
||||
:prefill-data="prefillInitials"
|
||||
:attachments-index="attachmentsIndex"
|
||||
:show-field-names="showFieldNames"
|
||||
:submitter-slug="submitterSlug"
|
||||
@@ -981,10 +981,20 @@ export default {
|
||||
required: false,
|
||||
default: ''
|
||||
},
|
||||
previousSignatureValue: {
|
||||
type: String,
|
||||
prefillSignature: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: ''
|
||||
default: null
|
||||
},
|
||||
prefillInitials: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: null
|
||||
},
|
||||
currentUser: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: null
|
||||
},
|
||||
allowToSkip: {
|
||||
type: Boolean,
|
||||
@@ -1346,6 +1356,30 @@ export default {
|
||||
|
||||
screen?.orientation?.addEventListener('change', this.onOrientationChange)
|
||||
|
||||
if (this.currentUser) {
|
||||
this.fields.forEach((field) => {
|
||||
if (field.readonly || field.type !== 'text') {
|
||||
return
|
||||
}
|
||||
|
||||
const fieldName = (field.name || '').toLowerCase()
|
||||
|
||||
let value
|
||||
|
||||
if (fieldName === 'full name' || fieldName === 'legal name') {
|
||||
value = this.currentUser.full_name
|
||||
} else if (fieldName === 'first name') {
|
||||
value = this.currentUser.first_name
|
||||
} else if (fieldName === 'last name') {
|
||||
value = this.currentUser.last_name
|
||||
}
|
||||
|
||||
if (value) {
|
||||
this.values[field.uuid] ??= value
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
this.fields.forEach((field) => {
|
||||
if (field.default_value && !field.readonly) {
|
||||
this.values[field.uuid] ||= field.default_value
|
||||
|
||||
@@ -87,7 +87,7 @@
|
||||
</button>
|
||||
</span>
|
||||
<button
|
||||
v-if="modelValue || computedPreviousValue"
|
||||
v-if="modelValue || computedPreviousValue || computedPrefillData"
|
||||
type="button"
|
||||
class="btn font-medium btn-outline btn-sm clear-canvas-button"
|
||||
@click="remove"
|
||||
@@ -139,8 +139,8 @@
|
||||
:name="`values[${field.uuid}]`"
|
||||
>
|
||||
<img
|
||||
v-if="modelValue || computedPreviousValue"
|
||||
:src="attachmentsIndex[modelValue || computedPreviousValue].url"
|
||||
v-if="modelValue || computedPreviousValue || computedPrefillData"
|
||||
:src="previousValueUrl"
|
||||
:alt="field.name || t('initials')"
|
||||
class="mx-auto bg-white border border-base-300 rounded max-h-44"
|
||||
>
|
||||
@@ -150,12 +150,12 @@
|
||||
class="absolute top-0 right-0 left-0 bottom-0"
|
||||
/>
|
||||
<label
|
||||
v-if="!isDrawInitials && !modelValue && !computedPreviousValue"
|
||||
v-if="!isDrawInitials && !modelValue && !computedPreviousValue && !computedPrefillData"
|
||||
for="initials_text_input"
|
||||
class="absolute top-0 right-0 left-0 bottom-0"
|
||||
/>
|
||||
<canvas
|
||||
v-show="!modelValue && !computedPreviousValue"
|
||||
v-show="!modelValue && !computedPreviousValue && !computedPrefillData"
|
||||
ref="canvas"
|
||||
role="img"
|
||||
:aria-label="t('initials_drawing_area')"
|
||||
@@ -166,7 +166,7 @@
|
||||
/>
|
||||
</div>
|
||||
<input
|
||||
v-if="!isDrawInitials && !modelValue && !computedPreviousValue"
|
||||
v-if="!isDrawInitials && !modelValue && !computedPreviousValue && !computedPrefillData"
|
||||
id="initials_text_input"
|
||||
ref="textInput"
|
||||
class="base-input !text-2xl w-full mt-6 text-center"
|
||||
@@ -236,6 +236,11 @@ export default {
|
||||
required: false,
|
||||
default: ''
|
||||
},
|
||||
prefillData: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: null
|
||||
},
|
||||
nativePlatform: {
|
||||
type: String,
|
||||
required: false,
|
||||
@@ -263,10 +268,24 @@ export default {
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
},
|
||||
computedPrefillData () {
|
||||
if (this.isUsePreviousValue && this.field.required === true && !this.previousValue && !this.modelValue) {
|
||||
return this.prefillData
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
},
|
||||
previousValueUrl () {
|
||||
if (this.computedPrefillData) {
|
||||
return this.computedPrefillData.url
|
||||
} else {
|
||||
return this.attachmentsIndex[this.modelValue || this.computedPreviousValue].url
|
||||
}
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.isInitialsStarted = !!this.computedPreviousValue
|
||||
this.isInitialsStarted = !!(this.computedPreviousValue || this.computedPrefillData)
|
||||
},
|
||||
async mounted () {
|
||||
this.$nextTick(() => {
|
||||
@@ -407,7 +426,48 @@ export default {
|
||||
this.updateWrittenInitials({ target: this.$refs.textInput })
|
||||
}
|
||||
},
|
||||
submitPrefillAttachment () {
|
||||
if (this.dryRun) {
|
||||
const attachment = { uuid: Math.random().toString(), url: this.prefillData.url }
|
||||
|
||||
this.$emit('attached', attachment)
|
||||
this.$emit('update:model-value', attachment.uuid)
|
||||
|
||||
return Promise.resolve(attachment)
|
||||
}
|
||||
|
||||
const formData = new FormData()
|
||||
|
||||
formData.append('prefill_token', this.prefillData.token)
|
||||
|
||||
return fetch(this.baseUrl + `/s/${this.submitterSlug}/prefill_attachments`, {
|
||||
method: 'POST',
|
||||
headers: { ...this.fetchOptions.headers },
|
||||
body: formData
|
||||
}).then(async (resp) => {
|
||||
if (!resp.ok) {
|
||||
const data = await resp.json()
|
||||
|
||||
return Promise.reject(new Error(data.error))
|
||||
}
|
||||
|
||||
const attachment = await resp.json()
|
||||
|
||||
this.$emit('attached', attachment)
|
||||
this.$emit('update:model-value', attachment.uuid)
|
||||
|
||||
return attachment
|
||||
}).catch((error) => {
|
||||
this.remove()
|
||||
|
||||
return Promise.reject(error)
|
||||
})
|
||||
},
|
||||
async submit () {
|
||||
if (this.computedPrefillData) {
|
||||
return this.submitPrefillAttachment()
|
||||
}
|
||||
|
||||
if (this.modelValue || this.computedPreviousValue) {
|
||||
if (this.computedPreviousValue) {
|
||||
this.$emit('update:model-value', this.computedPreviousValue)
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
<span
|
||||
v-else-if="withTypedSignature && format !== 'drawn_or_upload' && format !== 'typed_or_upload' && format !== 'typed' && format !== 'drawn' && format !== 'upload'"
|
||||
class="md:tooltip ml-2"
|
||||
:class="{ 'hidden sm:inline': modelValue || computedPreviousValue }"
|
||||
:class="{ 'hidden sm:inline': modelValue || computedPreviousValue || computedPrefillData }"
|
||||
:data-tip="t('type_text')"
|
||||
>
|
||||
<button
|
||||
@@ -68,7 +68,7 @@
|
||||
<span
|
||||
v-if="format !== 'typed' && format !== 'drawn' && format !== 'upload' && format !== 'drawn_or_typed'"
|
||||
class="md:tooltip"
|
||||
:class="{ 'hidden sm:inline': modelValue || computedPreviousValue }"
|
||||
:class="{ 'hidden sm:inline': modelValue || computedPreviousValue || computedPrefillData }"
|
||||
:data-tip="t('take_photo')"
|
||||
>
|
||||
<button
|
||||
@@ -95,7 +95,7 @@
|
||||
</button>
|
||||
</span>
|
||||
<button
|
||||
v-if="modelValue || computedPreviousValue"
|
||||
v-if="modelValue || computedPreviousValue || computedPrefillData"
|
||||
type="button"
|
||||
class="btn btn-outline btn-sm font-medium reupload-button"
|
||||
@click="remove"
|
||||
@@ -107,7 +107,7 @@
|
||||
{{ t(format === 'upload' ? 'reupload' : 'redraw') }}
|
||||
</button>
|
||||
<span
|
||||
v-if="withQrButton && !modelValue && !computedPreviousValue && format !== 'typed_or_upload' && format !== 'typed' && format !== 'upload'"
|
||||
v-if="withQrButton && !modelValue && !computedPreviousValue && !computedPrefillData && format !== 'typed_or_upload' && format !== 'typed' && format !== 'upload'"
|
||||
class="md:tooltip before:translate-x-[-90%]"
|
||||
:data-tip="t('sign_on_the_touchscreen')"
|
||||
>
|
||||
@@ -154,20 +154,14 @@
|
||||
type="hidden"
|
||||
:name="`values[${field.uuid}]`"
|
||||
>
|
||||
<input
|
||||
v-if="isTouchAttachment"
|
||||
:value="touchAttachmentUuid"
|
||||
type="hidden"
|
||||
name="touch_attachment_uuid"
|
||||
>
|
||||
<img
|
||||
v-if="modelValue || computedPreviousValue"
|
||||
:src="attachmentsIndex[modelValue || computedPreviousValue].url"
|
||||
v-if="modelValue || computedPreviousValue || computedPrefillData"
|
||||
:src="previousValueUrl"
|
||||
:alt="field.name || t('signature')"
|
||||
class="mx-auto bg-white border border-base-300 rounded max-h-44"
|
||||
>
|
||||
<FileDropzone
|
||||
v-if="format === 'upload' && !modelValue && !computedPreviousValue"
|
||||
v-if="format === 'upload' && !modelValue && !computedPreviousValue && !computedPrefillData"
|
||||
:message="`${t('upload')} ${field.name || t('signature')}`"
|
||||
:submitter-slug="submitterSlug"
|
||||
:dry-run="dryRun"
|
||||
@@ -179,7 +173,7 @@
|
||||
class="relative select-none"
|
||||
>
|
||||
<div
|
||||
v-if="!modelValue && !computedPreviousValue && !isShowQr && !isTextSignature && isSignatureStarted"
|
||||
v-if="!modelValue && !computedPreviousValue && !computedPrefillData && !isShowQr && !isTextSignature && isSignatureStarted"
|
||||
class="absolute top-0.5 right-0.5"
|
||||
>
|
||||
<button
|
||||
@@ -199,7 +193,7 @@
|
||||
class="absolute top-0 right-0 left-0 bottom-0"
|
||||
/>
|
||||
<canvas
|
||||
v-show="!modelValue && !computedPreviousValue"
|
||||
v-show="!modelValue && !computedPreviousValue && !computedPrefillData"
|
||||
ref="canvas"
|
||||
role="img"
|
||||
:aria-label="t('signature_drawing_area')"
|
||||
@@ -244,7 +238,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<input
|
||||
v-if="isTextSignature && !modelValue && !computedPreviousValue"
|
||||
v-if="isTextSignature && !modelValue && !computedPreviousValue && !computedPrefillData"
|
||||
id="signature_text_input"
|
||||
ref="textInput"
|
||||
class="base-input !text-2xl w-full mt-6"
|
||||
@@ -440,10 +434,10 @@ export default {
|
||||
required: false,
|
||||
default: ''
|
||||
},
|
||||
touchAttachmentUuid: {
|
||||
type: String,
|
||||
prefillData: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: ''
|
||||
default: null
|
||||
},
|
||||
reason: {
|
||||
type: String,
|
||||
@@ -471,14 +465,13 @@ export default {
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
emits: ['attached', 'update:model-value', 'start', 'minimize', 'update:reason', 'touch-attachment'],
|
||||
emits: ['attached', 'update:model-value', 'start', 'minimize', 'update:reason'],
|
||||
data () {
|
||||
return {
|
||||
isSignatureStarted: false,
|
||||
isShowQr: false,
|
||||
isOtherReason: false,
|
||||
isUsePreviousValue: true,
|
||||
isTouchAttachment: false,
|
||||
isTextSignature: !this.signatureSrc && (!!this.signatureText || this.field.preferences?.format === 'typed' || this.field.preferences?.format === 'typed_or_upload'),
|
||||
uploadImageInputKey: Math.random().toString()
|
||||
}
|
||||
@@ -506,10 +499,24 @@ export default {
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
},
|
||||
computedPrefillData () {
|
||||
if (this.isUsePreviousValue && this.field.required === true && !this.previousValue && !this.modelValue) {
|
||||
return this.prefillData
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
},
|
||||
previousValueUrl () {
|
||||
if (this.computedPrefillData) {
|
||||
return this.computedPrefillData.url
|
||||
} else {
|
||||
return this.attachmentsIndex[this.modelValue || this.computedPreviousValue].url
|
||||
}
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.isSignatureStarted = !!this.computedPreviousValue
|
||||
this.isSignatureStarted = !!(this.computedPreviousValue || this.computedPrefillData)
|
||||
|
||||
if (this.withSigningReason) {
|
||||
this.field.preferences ||= {}
|
||||
@@ -890,15 +897,49 @@ export default {
|
||||
console.error(e)
|
||||
}
|
||||
},
|
||||
async submit () {
|
||||
if (this.modelValue || this.computedPreviousValue) {
|
||||
if (this.touchAttachmentUuid && this.computedPreviousValue === this.touchAttachmentUuid && !Object.values(this.values).includes(this.touchAttachmentUuid)) {
|
||||
this.isTouchAttachment = true
|
||||
this.$emit('touch-attachment', this.touchAttachmentUuid)
|
||||
} else {
|
||||
this.isTouchAttachment = false
|
||||
submitPrefillAttachment () {
|
||||
if (this.dryRun) {
|
||||
const attachment = { uuid: Math.random().toString(), url: this.prefillData.url }
|
||||
|
||||
this.$emit('attached', attachment)
|
||||
this.$emit('update:model-value', attachment.uuid)
|
||||
|
||||
return Promise.resolve(attachment)
|
||||
}
|
||||
|
||||
const formData = new FormData()
|
||||
|
||||
formData.append('prefill_token', this.prefillData.token)
|
||||
|
||||
return fetch(this.baseUrl + `/s/${this.submitterSlug}/prefill_attachments`, {
|
||||
method: 'POST',
|
||||
headers: { ...this.fetchOptions.headers },
|
||||
body: formData
|
||||
}).then(async (resp) => {
|
||||
if (!resp.ok) {
|
||||
const data = await resp.json()
|
||||
|
||||
return Promise.reject(new Error(data.error))
|
||||
}
|
||||
|
||||
const attachment = await resp.json()
|
||||
|
||||
this.$emit('attached', attachment)
|
||||
this.$emit('update:model-value', attachment.uuid)
|
||||
|
||||
return attachment
|
||||
}).catch((error) => {
|
||||
this.remove()
|
||||
|
||||
return Promise.reject(error)
|
||||
})
|
||||
},
|
||||
async submit () {
|
||||
if (this.computedPrefillData) {
|
||||
return this.submitPrefillAttachment()
|
||||
}
|
||||
|
||||
if (this.modelValue || this.computedPreviousValue) {
|
||||
if (this.computedPreviousValue) {
|
||||
this.$emit('update:model-value', this.computedPreviousValue)
|
||||
}
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
<% 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 %>
|
||||
<% 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-native-platform="<%= turbo_native_ios? ? 'ios' : (turbo_native_android? ? 'android' : '') %>" data-attribution="<%= !turbo_native_app? %>" 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="<%= Docuseal.multitenant? || 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-view-token="<%= Submitter.signed_id_verifier.generate(submitter.slug, purpose: :view_form, expires_in: 1.minute) unless 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-native-platform="<%= turbo_native_ios? ? 'ios' : (turbo_native_android? ? 'android' : '') %>" data-attribution="<%= !turbo_native_app? %>" 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="<%= Docuseal.multitenant? || 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-prefill-signature="<%= local_assigns[:prefill_signature]&.to_json %>" data-prefill-initials="<%= local_assigns[:prefill_initials]&.to_json %>" data-current-user="<%= local_assigns[:current_user_data]&.to_json %>" data-remember-signature="<%= configs[:prefill_signature] %>" data-dry-run="<%= local_assigns[:dry_run] %>" data-view-token="<%= Submitter.signed_id_verifier.generate(submitter.slug, purpose: :view_form, expires_in: 1.minute) unless 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>
|
||||
@@ -177,7 +177,7 @@
|
||||
<div class="fixed bottom-0 w-full h-0 z-50">
|
||||
<div class="mx-auto" style="max-width: 1000px">
|
||||
<div class="relative md:mx-32">
|
||||
<%= render 'submit_form/submission_form', attachments_index: @attachments_index, submitter: @submitter, signature_attachment: @signature_attachment, configs: @form_configs, dry_run: local_assigns[:dry_run], expand: local_assigns[:expand], scroll_padding: local_assigns.fetch(:scroll_padding, turbo_native_app? ? '-80px' : '-110px'), schema: %>
|
||||
<%= render 'submit_form/submission_form', attachments_index: @attachments_index, submitter: @submitter, prefill_signature: @prefill_signature, prefill_initials: @prefill_initials, current_user_data: @current_user_data, configs: @form_configs, dry_run: local_assigns[:dry_run], expand: local_assigns[:expand], scroll_padding: local_assigns.fetch(:scroll_padding, turbo_native_app? ? '-80px' : '-110px'), schema: %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -163,6 +163,7 @@ Rails.application.routes.draw do
|
||||
resource :click_email, only: %i[create], controller: 'submit_form_click_email'
|
||||
resources :view, only: %i[create], controller: 'submit_form_view'
|
||||
resources :upload, only: %i[create], controller: 'submit_form_upload'
|
||||
resources :prefill_attachments, only: %i[create], controller: 'submit_form_prefill_attachments'
|
||||
resources :debug, only: %i[index], controller: 'submissions_debug' if Rails.env.development?
|
||||
get :completed
|
||||
get :delegated
|
||||
|
||||
@@ -129,6 +129,13 @@ module Submitters
|
||||
ActiveStorage::Attachment.create!(blob:, name: 'attachments', record: submitter)
|
||||
end
|
||||
|
||||
def build_prefill_attachment_data(submitter, attachment)
|
||||
token = ApplicationRecord.signed_id_verifier.generate([attachment.uuid, submitter.slug],
|
||||
purpose: :prefill_attachment)
|
||||
|
||||
{ token:, url: attachment.url }
|
||||
end
|
||||
|
||||
def normalize_preferences(account, user, params)
|
||||
preferences = {}
|
||||
|
||||
|
||||
+13
-21
@@ -1,30 +1,28 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Submitters
|
||||
module MaybeAssignDefaultBrowserSignature
|
||||
SIGNED_UUID_PURPPOSE = 'signature'
|
||||
module FindRememberedSignature
|
||||
SIGNED_UUID_PURPOSE = 'signature'
|
||||
|
||||
module_function
|
||||
|
||||
def call(submitter, params, cookies = nil, attachments = [])
|
||||
attachments = attachments.select { |e| e.record_id == submitter.id && e.record_type == 'Submitter' }
|
||||
|
||||
def call(submitter, params, cookies = nil)
|
||||
if params[:signed_signature_uuids].present?
|
||||
find_storage_signature(submitter, params[:signed_signature_uuids], attachments)
|
||||
find_storage_signature(submitter, params[:signed_signature_uuids])
|
||||
elsif cookies
|
||||
find_session_signature(submitter, cookies, attachments)
|
||||
find_session_signature(submitter, cookies)
|
||||
end
|
||||
end
|
||||
|
||||
def sign_signature_uuid(uuid)
|
||||
ApplicationRecord.signed_id_verifier.generate(uuid, purpose: SIGNED_UUID_PURPPOSE)
|
||||
ApplicationRecord.signed_id_verifier.generate(uuid, purpose: SIGNED_UUID_PURPOSE)
|
||||
end
|
||||
|
||||
def verify_signature_uuid(signed_uuid)
|
||||
ApplicationRecord.signed_id_verifier.verified(signed_uuid, purpose: SIGNED_UUID_PURPPOSE)
|
||||
ApplicationRecord.signed_id_verifier.verified(signed_uuid, purpose: SIGNED_UUID_PURPOSE)
|
||||
end
|
||||
|
||||
def find_storage_signature(submitter, signed_uuids, attachments)
|
||||
def find_storage_signature(submitter, signed_uuids)
|
||||
signed_uuid = signed_uuids[submitter.email]
|
||||
|
||||
return if signed_uuid.blank?
|
||||
@@ -33,10 +31,10 @@ module Submitters
|
||||
|
||||
return if uuid.blank?
|
||||
|
||||
find_signature_from_uuid(submitter, uuid, attachments)
|
||||
find_signature_from_uuid(submitter, uuid)
|
||||
end
|
||||
|
||||
def find_session_signature(submitter, cookies, attachments)
|
||||
def find_session_signature(submitter, cookies)
|
||||
values =
|
||||
begin
|
||||
JSON.parse(cookies.encrypted[:signature_uuids].presence || '{}')
|
||||
@@ -50,23 +48,17 @@ module Submitters
|
||||
|
||||
return if uuid.blank?
|
||||
|
||||
find_signature_from_uuid(submitter, uuid, attachments)
|
||||
find_signature_from_uuid(submitter, uuid)
|
||||
end
|
||||
|
||||
def find_signature_from_uuid(submitter, uuid, attachments)
|
||||
def find_signature_from_uuid(submitter, uuid)
|
||||
signature_attachment = ActiveStorage::Attachment.find_by(uuid:)
|
||||
|
||||
return unless signature_attachment
|
||||
|
||||
return if signature_attachment.record.email != submitter.email
|
||||
|
||||
existing_attachment = attachments.find do |a|
|
||||
a.blob_id == signature_attachment.blob_id && submitter.id == a.record_id
|
||||
end
|
||||
|
||||
return existing_attachment if existing_attachment
|
||||
|
||||
submitter.attachments_attachments.create_or_find_by!(blob_id: signature_attachment.blob_id)
|
||||
signature_attachment
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,45 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Submitters
|
||||
module MaybeUpdateDefaultValues
|
||||
module_function
|
||||
|
||||
def call(submitter, current_user)
|
||||
return if current_user.blank? || current_user.email != submitter.email
|
||||
|
||||
user = current_user
|
||||
|
||||
fields = submitter.submission.template_fields || submitter.submission.template.fields
|
||||
|
||||
fields.each do |field|
|
||||
next if field['submitter_uuid'] != submitter.uuid
|
||||
|
||||
default_value = get_default_value_for_field(field, user, submitter)
|
||||
|
||||
submitter.values[field['uuid']] ||= default_value if default_value.present?
|
||||
end
|
||||
|
||||
submitter.save!
|
||||
end
|
||||
|
||||
def get_default_value_for_field(field, user, submitter)
|
||||
field_name = field['name'].to_s.downcase
|
||||
|
||||
if field_name.in?(['full name', 'legal name'])
|
||||
user.full_name
|
||||
elsif field_name == 'first name'
|
||||
user.first_name
|
||||
elsif field_name == 'last name'
|
||||
user.last_name
|
||||
elsif field['type'] == 'initials' && (initials = UserConfigs.load_initials(user))
|
||||
attachment = ActiveStorage::Attachment.find_or_create_by!(
|
||||
blob_id: initials.blob_id,
|
||||
name: 'attachments',
|
||||
record: submitter
|
||||
)
|
||||
|
||||
attachment.uuid
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -54,10 +54,6 @@ module Submitters
|
||||
reason_field = maybe_set_signature_reason!(values, submitter, params)
|
||||
validate_values!(reason_field ? values.except(reason_field['uuid']) : values, submitter, params, request)
|
||||
|
||||
if (touch_attachment_uuid = params[:touch_attachment_uuid].presence)
|
||||
ActiveStorage::Attachment.where(uuid: touch_attachment_uuid, record: submitter).touch_all(:created_at)
|
||||
end
|
||||
|
||||
if params[:completed] == 'true'
|
||||
maybe_invite_via_field(submitter, request)
|
||||
|
||||
|
||||
Reference in new issue
Block a user