Merge from docusealco/wip

master 2.2.6
Alex Turchyn 1 day ago committed by GitHub
commit ae891932c5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -1,27 +1,14 @@
name: Build Docker Images
on:
workflow_dispatch:
inputs:
version:
description: Version
type: string
required: true
image:
description: QEMU image
type: string
required: false
default: tonistiigi/binfmt:latest
os:
description: OS
type: string
required: false
default: ubuntu-24.04-arm
push:
tags:
- "*.*.*"
jobs:
build:
runs-on: ${{ inputs.os }}
timeout-minutes: 20
runs-on: ubuntu-24.04-arm
timeout-minutes: 30
steps:
- name: Checkout code
@ -34,18 +21,16 @@ jobs:
uses: docker/metadata-action@v4
with:
images: docuseal/docuseal
tags: latest,${{ inputs.version }}
tags: type=semver,pattern={{version}}
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
with:
image: ${{ inputs.image }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Create .version file
run: echo ${{ inputs.version }} > .version
run: echo ${{ github.ref_name }} > .version
- name: Login to Docker Hub
uses: docker/login-action@v3

@ -182,13 +182,13 @@ module Api
def submissions_params
permitted_attrs = [
:send_email, :send_sms, :bcc_completed, :completed_redirect_url, :reply_to, :go_to_last,
:require_phone_2fa, :expire_at, :name,
:require_phone_2fa, :require_email_2fa, :expire_at, :name,
{
variables: {},
message: %i[subject body],
submitters: [[:send_email, :send_sms, :completed_redirect_url, :uuid, :name, :email, :role,
:completed, :phone, :application_key, :external_id, :reply_to, :go_to_last,
:require_phone_2fa, :order,
:require_phone_2fa, :require_email_2fa, :order, :invite_by,
{ metadata: {}, values: {}, roles: [], readonly_fields: [], message: %i[subject body],
fields: [:name, :uuid, :default_value, :value, :title, :description,
:readonly, :required, :validation_pattern, :invalid_message,

@ -84,7 +84,7 @@ module Api
submitter_params.permit(
:send_email, :send_sms, :reply_to, :completed_redirect_url, :uuid, :name, :email, :role,
:completed, :phone, :application_key, :external_id, :go_to_last, :require_phone_2fa,
:completed, :phone, :application_key, :external_id, :go_to_last, :require_phone_2fa, :require_email_2fa,
{ metadata: {}, values: {}, readonly_fields: [], message: %i[subject body],
fields: [[:name, :uuid, :default_value, :value, :required,
:readonly, :validation_pattern, :invalid_message,

@ -12,6 +12,14 @@ class SubmissionsController < ApplicationController
authorize!(:create, Submission)
end
FIELD_ICONS = {
'text' => 'text_size', 'signature' => 'writing_sign', 'date' => 'calendar_event',
'number' => 'square_number_1', 'image' => 'photo', 'initials' => 'letter_case_upper',
'file' => 'paperclip', 'select' => 'select', 'checkbox' => 'checkbox', 'radio' => 'circle_dot',
'stamp' => 'rubber_stamp', 'cells' => 'columns_3', 'multiple' => 'checks', 'phone' => 'phone_check',
'payment' => 'credit_card', 'verification' => 'id'
}.freeze
def show
@submission = Submissions.preload_with_pages(@submission)

@ -537,7 +537,7 @@
/>
<div
v-if="stepFields.length < 80"
class="flex justify-center mt-3 sm:mt-4 mb-0 sm:mb-1"
class="flex justify-center mt-3 sm:mt-4 mb-0 sm:mb-1 select-none"
>
<div class="flex items-center flex-wrap steps-progress">
<a

@ -13,7 +13,7 @@ const en = {
esignature_disclosure: 'eSignature Disclosure',
signature: 'Signature',
initials: 'Initials',
drawn_signature_on_a_touchscreen_device: 'Drawn signature on a touchscreen device',
sign_on_the_touchscreen: 'Sign on the touchscreen',
approved: 'Approved',
reviewed: 'Reviewed',
other: 'Other',
@ -120,7 +120,7 @@ const es = {
select_a_reason: 'Selecciona una razón',
value_is_invalid: 'El valor no es válido',
verification_code_is_invalid: 'El código de verificación no es válido',
drawn_signature_on_a_touchscreen_device: 'Firma dibujada en un dispositivo con pantalla táctil',
sign_on_the_touchscreen: 'Firmar en pantalla táctil',
scan_the_qr_code_with_the_camera_app_to_open_the_form_on_mobile_and_draw_your_signature: 'Escanea el código QR con la aplicación de la cámara para abrir el formulario en el móvil y dibujar tu firma',
by_clicking_you_agree_to_the: 'Al hacer clic en "{button}", usted acepta el',
electronic_signature_disclosure: 'Divulgación de Firma Electrónica',
@ -221,7 +221,7 @@ const it = {
select_a_reason: 'Seleziona una ragione',
value_is_invalid: 'Il valore non è valido',
verification_code_is_invalid: 'Il codice di verifica non è valido',
drawn_signature_on_a_touchscreen_device: 'Firma disegnata su un dispositivo con schermo tattile',
sign_on_the_touchscreen: 'Firma su schermo tattile',
scan_the_qr_code_with_the_camera_app_to_open_the_form_on_mobile_and_draw_your_signature: "Scansiona il codice QR con l'app della fotocamera per aprire il modulo sul cellulare e disegnare la tua firma",
by_clicking_you_agree_to_the: 'Cliccando su "{button}", accetti il',
electronic_signature_disclosure: 'Divulgazione della Firma Elettronica',
@ -316,7 +316,7 @@ const de = {
esignature_disclosure: 'Hinweis zur eSignatur',
signature: 'Unterschrift',
initials: 'Initialen',
drawn_signature_on_a_touchscreen_device: 'Auf einem Touchscreen-Gerät gezeichnete Unterschrift',
sign_on_the_touchscreen: 'Auf Touchscreen signieren',
approved: 'Genehmigt',
reviewed: 'Geprüft',
other: 'Sonstiges',
@ -417,7 +417,7 @@ const fr = {
esignature_disclosure: 'Déclaration eSignature',
signature: 'Signature',
initials: 'Initiales',
drawn_signature_on_a_touchscreen_device: 'Signature dessinée sur un appareil à écran tactile',
sign_on_the_touchscreen: 'Signer sur écran tactile',
approved: 'Approuvé',
reviewed: 'Révisé',
other: 'Autre',
@ -524,7 +524,7 @@ const pl = {
select_a_reason: 'Wybierz powód',
value_is_invalid: 'Wartość jest nieprawidłowa',
verification_code_is_invalid: 'Kod weryfikacyjny jest nieprawidłowy',
drawn_signature_on_a_touchscreen_device: 'Podpis odręczny na urządzeniu z ekranem dotykowym',
sign_on_the_touchscreen: 'Podpisz na ekranie dotykowym',
scan_the_qr_code_with_the_camera_app_to_open_the_form_on_mobile_and_draw_your_signature: 'Zeskanuj kod QR za pomocą aplikacji aparatu, aby otworzyć formularz na telefonie i narysować swój podpis',
by_clicking_you_agree_to_the: 'Klikając na "{button}", zgadzasz się na',
electronic_signature_disclosure: 'Ujawnienie Elektronicznej Sygnatury',
@ -625,7 +625,7 @@ const uk = {
select_a_reason: 'Виберіть причину',
value_is_invalid: 'Значення є неправильним',
verification_code_is_invalid: 'Код підтвердження є неправильним',
drawn_signature_on_a_touchscreen_device: 'Підпис на сенсорному пристрої',
sign_on_the_touchscreen: 'Підписати на сенсорному екрані',
scan_the_qr_code_with_the_camera_app_to_open_the_form_on_mobile_and_draw_your_signature: 'Скануйте QR-код за допомогою програми камери, щоб відкрити форму на мобільному пристрої та намалювати свій підпис',
by_clicking_you_agree_to_the: 'Натиснувши на "{button}", ви погоджуєтеся з',
electronic_signature_disclosure: 'Розголошення Електронного Підпису',
@ -726,7 +726,7 @@ const cs = {
select_a_reason: 'Vyberte důvod',
value_is_invalid: 'Hodnota je neplatná',
verification_code_is_invalid: 'Ověřovací kód je neplatný',
drawn_signature_on_a_touchscreen_device: 'Namalovaný podpis na dotykovém zařízení',
sign_on_the_touchscreen: 'Podepsat na dotykové obrazovce',
scan_the_qr_code_with_the_camera_app_to_open_the_form_on_mobile_and_draw_your_signature: 'Naskenujte QR kód pomocí aplikace fotoaparátu, abyste otevřeli formulář na mobilním zařízení a nakreslili svůj podpis',
by_clicking_you_agree_to_the: 'Kliknutím na "{button}" souhlasíte s',
electronic_signature_disclosure: 'Zveřejněním Elektronického Podpisu',
@ -827,7 +827,7 @@ const pt = {
select_a_reason: 'Selecione um motivo',
value_is_invalid: 'Valor é inválido',
verification_code_is_invalid: 'Código de verificação é inválido',
drawn_signature_on_a_touchscreen_device: 'Assinatura desenhada em um dispositivo com tela sensível ao toque',
sign_on_the_touchscreen: 'Assinar na tela sensível',
scan_the_qr_code_with_the_camera_app_to_open_the_form_on_mobile_and_draw_your_signature: 'Escaneie o código QR com o aplicativo da câmera para abrir o formulário no celular e desenhar sua assinatura',
by_clicking_you_agree_to_the: 'Ao clicar em "{button}", você concorda com o',
electronic_signature_disclosure: 'Divulgação de Assinatura Eletrônica',
@ -928,7 +928,7 @@ const he = {
select_a_reason: 'בחר סיבה',
value_is_invalid: 'ערך לא תקין',
verification_code_is_invalid: 'קוד האימות אינו תקין',
drawn_signature_on_a_touchscreen_device: 'חתימה שנוצרה במכשיר עם מסך מגע',
sign_on_the_touchscreen: 'חתום על מסך המגע',
scan_the_qr_code_with_the_camera_app_to_open_the_form_on_mobile_and_draw_your_signature: 'סרוק את קוד ה-QR באמצעות אפליקציית המצלמה כדי לפתוח את הטופס במובייל ולצייר את החתימה שלך',
by_clicking_you_agree_to_the: 'על ידי לחיצה על "{button}", אתה מסכים ל',
electronic_signature_disclosure: 'חשיפת חתימה אלקטרונית',
@ -1029,7 +1029,7 @@ const nl = {
select_a_reason: 'Selecteer een reden',
value_is_invalid: 'Waarde is ongeldig',
verification_code_is_invalid: 'Verificatiecode is ongeldig',
drawn_signature_on_a_touchscreen_device: 'Getekende handtekening op een apparaat met een touchscreen',
sign_on_the_touchscreen: 'Onderteken op touchscreen',
scan_the_qr_code_with_the_camera_app_to_open_the_form_on_mobile_and_draw_your_signature: 'Scan de QR-code met de camera-app om het formulier op mobiel te openen en uw handtekening te tekenen',
by_clicking_you_agree_to_the: 'Door op "{button}" te klikken, gaat u akkoord met de',
electronic_signature_disclosure: 'Openbaarmaking van Elektronische Handtekening',
@ -1131,7 +1131,7 @@ const ar = {
value_is_invalid: 'القيمة غير صالحة',
verification_code_is_invalid: 'رمز التحقق غير صالح',
already_paid: 'تم الدفع بالفعل',
drawn_signature_on_a_touchscreen_device: 'توقيع مرسوم على جهاز بشاشة تعمل باللمس',
sign_on_the_touchscreen: 'وقع على شاشة اللمس',
scan_the_qr_code_with_the_camera_app_to_open_the_form_on_mobile_and_draw_your_signature: 'امسح رمز الاستجابة السريعة باستخدام تطبيق الكاميرا لفتح النموذج على الهاتف المحمول ورسم توقيعك',
by_clicking_you_agree_to_the: 'بالنقر فوق "{button}"، أنت توافق على',
electronic_signature_disclosure: 'كشف التوقيع الإلكتروني',
@ -1229,7 +1229,7 @@ const ko = {
reviewed_by: '검토자',
authored_by: '작성자',
select_a_reason: '이유 선택',
drawn_signature_on_a_touchscreen_device: '터치스크린 장치에서 그린 서명',
sign_on_the_touchscreen: '터치스크린에서 서명',
scan_the_qr_code_with_the_camera_app_to_open_the_form_on_mobile_and_draw_your_signature: '카메라 앱으로 QR 코드를 스캔하여 모바일에서 양식을 열고 서명을 그리세요',
by_clicking_you_agree_to_the: '"{button}"를 클릭함으로써, 다음에 동의하게 됩니다',
electronic_signature_disclosure: '전자 서명 공개',
@ -1326,7 +1326,7 @@ const ja = {
esignature_disclosure: '電子署名開示',
signature: '署名',
initials: 'イニシャル',
drawn_signature_on_a_touchscreen_device: 'タッチスクリーンデバイスで描かれた署名',
sign_on_the_touchscreen: 'タッチスクリーンで署名',
approved: '承認済み',
reviewed: '確認済み',
other: 'その他',

@ -19,7 +19,7 @@
<div class="space-x-2 flex flex-none">
<span
v-if="isDrawInitials"
class="tooltip"
class="md:tooltip"
:data-tip="t('type_initial')"
>
<a
@ -36,7 +36,7 @@
</span>
<span
v-else
class="tooltip ml-2"
class="md:tooltip ml-2"
:data-tip="t('draw_initials')"
>
<a
@ -52,7 +52,7 @@
</a>
</span>
<span
class="tooltip"
class="md:tooltip"
:data-tip="t('click_to_upload')"
>
<label class="btn btn-outline btn-sm font-medium inline-flex flex-nowrap upload-image-button">

@ -22,7 +22,7 @@
<div class="space-x-2 flex flex-none">
<span
v-if="isTextSignature && format !== 'typed_or_upload' && format !== 'typed' && format !== 'upload'"
class="tooltip"
class="md:tooltip"
:data-tip="t('draw_signature')"
>
<a
@ -39,7 +39,7 @@
</span>
<span
v-else-if="withTypedSignature && format !== 'drawn_or_upload' && format !== 'typed_or_upload' && format !== 'typed' && format !== 'drawn' && format !== 'upload'"
class="tooltip ml-2"
class="md:tooltip ml-2"
:class="{ 'hidden sm:inline': modelValue || computedPreviousValue }"
:data-tip="t('type_text')"
>
@ -57,7 +57,7 @@
</span>
<span
v-if="format !== 'typed' && format !== 'drawn' && format !== 'upload' && format !== 'drawn_or_typed'"
class="tooltip"
class="md:tooltip"
:class="{ 'hidden sm:inline': modelValue || computedPreviousValue }"
:data-tip="t('take_photo')"
>
@ -86,8 +86,8 @@
</a>
<span
v-if="withQrButton && !modelValue && !computedPreviousValue && format !== 'typed_or_upload' && format !== 'typed' && format !== 'upload'"
class="tooltip before:translate-x-[-90%]"
:data-tip="t('drawn_signature_on_a_touchscreen_device')"
class="md:tooltip before:translate-x-[-90%]"
:data-tip="t('sign_on_the_touchscreen')"
>
<a
href="#"
@ -142,7 +142,7 @@
/>
<div
v-else
class="relative"
class="relative select-none"
>
<div
v-if="!modelValue && !computedPreviousValue && !isShowQr && !isTextSignature && isSignatureStarted"
@ -176,7 +176,7 @@
class="top-0 bottom-0 right-0 left-0 absolute bg-base-content/10 rounded-2xl"
>
<div
class="absolute top-1.5 right-1.5 tooltip"
class="absolute top-1.5 right-1.5 md:tooltip"
>
<a
href="#"
@ -281,7 +281,7 @@
<div
v-else-if="withDisclosure"
dir="auto"
class="text-base-content/60 text-xs text-center w-full mt-1"
class="text-base-content/60 text-xs text-center w-full mt-1 select-none"
>
{{ t('by_clicking_you_agree_to_the').replace('{button}', buttonText.charAt(0).toUpperCase() + buttonText.slice(1)) }} <a
href="https://www.docuseal.com/esign-disclosure"

@ -6,7 +6,9 @@ class SendSubmitterVerificationEmailJob
def perform(params = {})
submitter = Submitter.find(params['submitter_id'])
SubmitterMailer.otp_verification_email(submitter).deliver_now!
locale = params['locale'].presence || submitter.account.locale
SubmitterMailer.otp_verification_email(submitter, locale:).deliver_now!
SubmissionEvent.create!(submitter_id: params['submitter_id'],
event_type: 'send_2fa_email',

@ -144,13 +144,13 @@ class SubmitterMailer < ApplicationMailer
end
end
def otp_verification_email(submitter)
def otp_verification_email(submitter, locale: nil)
@submitter = submitter
@otp_code = EmailVerificationCodes.generate([submitter.email.downcase.strip, submitter.slug].join(':'))
assign_message_metadata('otp_verification_email', submitter)
I18n.with_locale(submitter.account.locale) do
I18n.with_locale(locale || submitter.account.locale) do
mail(to: submitter.email, subject: I18n.t('email_verification'))
end
end

@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" class="<%= local_assigns[:class] %>" width="44" height="44" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M4 5m0 2a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v12a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2z"></path>
<path d="M16 3l0 4"></path>
<path d="M8 3l0 4"></path>
<path d="M4 11l16 0"></path>
<path d="M8 15h2v2h-2z"></path>
</svg>

After

Width:  |  Height:  |  Size: 450 B

@ -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" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M9 11l3 3l8 -8"></path>
<path d="M20 12v6a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2v-12a2 2 0 0 1 2 -2h9"></path>
</svg>

After

Width:  |  Height:  |  Size: 346 B

@ -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" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M7 12l5 5l10 -10"></path>
<path d="M2 12l5 5m5 -5l5 -5"></path>
</svg>

After

Width:  |  Height:  |  Size: 303 B

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" class="<%= local_assigns[:class] %>" width="44" height="44" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 3m0 1a1 1 0 0 1 1 -1h16a1 1 0 0 1 1 1v16a1 1 0 0 1 -1 1h-16a1 1 0 0 1 -1 -1zm6 -1v18m6 -18v18"></path>
</svg>

After

Width:  |  Height:  |  Size: 343 B

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" class="<%= local_assigns[:class] %>" width="44" height="44" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 5m0 3a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v8a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3z"></path>
<path d="M3 10l18 0"></path>
<path d="M7 15l.01 0"></path>
<path d="M11 15l2 0"></path>
</svg>

After

Width:  |  Height:  |  Size: 419 B

@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" class="<%= local_assigns[:class] %>" width="44" height="44" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 4m0 3a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v10a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3z"></path>
<path d="M9 10m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0"></path>
<path d="M15 8l2 0"></path>
<path d="M15 12l2 0"></path>
<path d="M7 16l10 0"></path>
</svg>

After

Width:  |  Height:  |  Size: 478 B

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" class="<%= local_assigns[:class] %>" width="44" height="44" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 19v-10.5a3.5 3.5 0 0 1 7 0v10.5"></path>
<path d="M3 13h7"></path>
<path d="M14 19v-10.5a3.5 3.5 0 0 1 7 0v10.5"></path>
<path d="M14 13h7"></path>
</svg>

After

Width:  |  Height:  |  Size: 394 B

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" class="<%= local_assigns[:class] %>" width="44" height="44" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M15 8h.01"></path>
<path d="M3 6a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v12a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3v-12z"></path>
<path d="M3 16l5 -5c.928 -.893 2.072 -.893 3 0l5 5"></path>
<path d="M14 14l1 -1c.928 -.893 2.072 -.893 3 0l3 3"></path>
</svg>

After

Width:  |  Height:  |  Size: 481 B

@ -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" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 17.85h-18c0 -4.05 1.421 -4.05 3.79 -4.05c5.21 0 1.21 -4.59 1.21 -6.8a4 4 0 1 1 8 0c0 2.21 -4 6.8 1.21 6.8c2.369 0 3.79 0 3.79 4.05z"></path>
<path d="M5 21h14"></path>
</svg>

After

Width:  |  Height:  |  Size: 411 B

@ -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" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 3m0 2a2 2 0 0 1 2 -2h14a2 2 0 0 1 2 2v14a2 2 0 0 1 -2 2h-14a2 2 0 0 1 -2 -2z"></path>
<path d="M9 11l3 3l3 -3"></path>
</svg>

After

Width:  |  Height:  |  Size: 361 B

@ -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" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 3m0 2a2 2 0 0 1 2 -2h14a2 2 0 0 1 2 2v14a2 2 0 0 1 -2 2h-14a2 2 0 0 1 -2 -2z"></path>
<path d="M10 10l2 -2v8"></path>
</svg>

After

Width:  |  Height:  |  Size: 360 B

@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" class="<%= local_assigns[:class] %>" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 7v-2h13v2"></path>
<path d="M10 5v14"></path>
<path d="M12 19h-4"></path>
<path d="M15 13v-1h6v1"></path>
<path d="M18 12v7"></path>
<path d="M17 19h2"></path>
</svg>

After

Width:  |  Height:  |  Size: 410 B

@ -1,6 +1,6 @@
<a target="_blank" href="<%= Docuseal::GITHUB_URL %>" rel="noopener noreferrer nofollow" class="relative flex items-center rounded-full px-2 py-0.5 text-xs leading-4 mt-1 text-base-content border border-base-300 tooltip tooltip-bottom" data-tip="Give a star on GitHub">
<span class="flex items-center justify-between space-x-0.5 font-medium">
<%= svg_icon('start', class: 'h-3 w-3') %>
<span>10k</span>
<span>11k</span>
</span>
</a>

@ -50,12 +50,13 @@
<%= svg_icon('check', class: "aspect-square #{area['w'] > area['h'] ? '!w-auto !h-full' : '!w-full !h-auto'}") %>
</div>
<% elsif field['type'].in?(%w[multiple radio]) && area['option_uuid'] %>
<% option = field['options']&.find { |o| o['uuid'] == area['option_uuid'] } %>
<% option_name = option['value'].presence || "Option #{field['options'].index(option) + 1}" %>
<% if option && Array.wrap(value).include?(option_name) %>
<div class="w-full flex items-center justify-center">
<%= svg_icon('check', class: "aspect-square #{area['w'] > area['h'] ? '!w-auto !h-full' : '!w-full !h-auto'}") %>
</div>
<% if (option = field['options']&.find { |o| o['uuid'] == area['option_uuid'] }) %>
<% option_name = option['value'].presence || "Option #{field['options'].index(option) + 1}" %>
<% if Array.wrap(value).include?(option_name) %>
<div class="w-full flex items-center justify-center">
<%= svg_icon('check', class: "aspect-square #{area['w'] > area['h'] ? '!w-auto !h-full' : '!w-full !h-auto'}") %>
</div>
<% end %>
<% end %>
<% elsif field['type'] == 'cells' && area['cell_w'].to_f > 0.0 %>
<% cell_width = area['cell_w'] / area['w'] * 100 %>

@ -4,7 +4,7 @@
<% font_scale = 1040.0 / PdfUtils::US_LETTER_W %>
<% configs = AccountConfig.where(account_id: @submission.account_id, key: [AccountConfig::COMBINE_PDF_RESULT_KEY, AccountConfig::WITH_SIGNATURE_ID, AccountConfig::WITH_SUBMITTER_TIMEZONE_KEY, AccountConfig::WITH_SIGNATURE_ID_REASON_KEY]) %>
<% with_signature_id = configs.find { |e| e.key == AccountConfig::WITH_SIGNATURE_ID }&.value == true %>
<% is_combined_enabled = configs.find { |e| e.key == AccountConfig::COMBINE_PDF_RESULT_KEY }&.value == true %>
<% is_combined_enabled = configs.find { |e| e.key == AccountConfig::COMBINE_PDF_RESULT_KEY }&.value == true && !@submission.template_fields&.any? { |f| f['type'] == 'verification' } %>
<% with_submitter_timezone = configs.find { |e| e.key == AccountConfig::WITH_SUBMITTER_TIMEZONE_KEY }&.value == true %>
<% with_signature_id_reason = configs.find { |e| e.key == AccountConfig::WITH_SIGNATURE_ID_REASON_KEY }&.value != false %>
<div style="max-width: 1600px" class="mx-auto pl-4">
@ -93,8 +93,10 @@
<div class="pr-3.5 pl-0.5">
<% fields_index = Templates.build_field_areas_index(@submission.template_fields || @submission.template.fields) %>
<% submitters_index = @submission.submitters.index_by(&:uuid) %>
<% submitters_order_index = nil %>
<% attachments_index = ActiveStorage::Attachment.where(record: @submission.submitters, name: :attachments).preload(:blob).index_by(&:uuid) %>
<% page_blob_struct = Struct.new(:url, :metadata, keyword_init: true) %>
<% bg_classes = %w[bg-red-100 bg-sky-100 bg-emerald-100 bg-yellow-100 bg-purple-100 bg-pink-100 bg-cyan-100 bg-orange-100 bg-lime-100 bg-indigo-100] %>
<% schema.each do |item| %>
<% document = @submission.schema_documents.find { |e| e.uuid == item['attachment_uuid'] } %>
<% document_annots_index = document.metadata.dig('pdf', 'annotations')&.group_by { |e| e['page'] } || {} %>
@ -111,19 +113,29 @@
<% fields_index.dig(document.uuid, index)&.each do |(area, field)| %>
<% value = values[field['uuid']].presence || (field['default_value'] != '{{date}}' && field['readonly'] == true && field['conditions'].blank? && field['default_value'].present? ? Submitters::SubmitValues.template_default_value_for_submitter(field['default_value'], @submission.submitters.find { |e| e.uuid == field['submitter_uuid'] }, with_time: false) : nil) %>
<% value ||= field['default_value'] if field['type'] == 'heading' %>
<% next if value.blank? %>
<% submitter = submitters_index[field['submitter_uuid']] %>
<% if (mask = field.dig('preferences', 'mask').presence) && signed_in? && can?(:read, @submission) %>
<span class="group">
<span class="hidden group-hover:inline">
<%= render 'submissions/value', font_scale:, area:, field:, attachments_index:, value:, locale: @submission.account.locale, timezone: @submission.account.timezone, submitter:, with_signature_id: %>
</span>
<span class="group-hover:hidden">
<%= render 'submissions/value', font_scale:, area:, field:, attachments_index:, value: Array.wrap(value).map { |e| TextUtils.mask_value(e, mask) }.join(', '), locale: @submission.account.locale, timezone: @submission.account.timezone, submitter:, with_signature_id: %>
<% if value.present? %>
<% if (mask = field.dig('preferences', 'mask').presence) && signed_in? && can?(:read, @submission) %>
<span class="group">
<span class="hidden group-hover:inline">
<%= render 'submissions/value', font_scale:, area:, field:, attachments_index:, value:, locale: @submission.account.locale, timezone: @submission.account.timezone, submitter:, with_signature_id: %>
</span>
<span class="group-hover:hidden">
<%= render 'submissions/value', font_scale:, area:, field:, attachments_index:, value: Array.wrap(value).map { |e| TextUtils.mask_value(e, mask) }.join(', '), locale: @submission.account.locale, timezone: @submission.account.timezone, submitter:, with_signature_id: %>
</span>
</span>
</span>
<% else %>
<%= render 'submissions/value', page_width: width, page_height: height, font_scale:, area:, field:, attachments_index:, value: mask.present? ? Array.wrap(value).map { |e| TextUtils.mask_value(e, mask) }.join(', ') : value, locale: @submission.account.locale, timezone: @submission.account.timezone, submitter:, with_signature_id:, with_submitter_timezone:, with_signature_id_reason: %>
<% else %>
<%= render 'submissions/value', page_width: width, page_height: height, font_scale:, area:, field:, attachments_index:, value: mask.present? ? Array.wrap(value).map { |e| TextUtils.mask_value(e, mask) }.join(', ') : value, locale: @submission.account.locale, timezone: @submission.account.timezone, submitter:, with_signature_id:, with_submitter_timezone:, with_signature_id_reason: %>
<% end %>
<% elsif field['readonly'] != true && submitter && !submitter.completed_at? %>
<% submitters_order_index ||= (@submission.template_submitters || @submission.template.submitters).each_with_index.to_h { |s, i| [s['uuid'], i] } %>
<% submitter_index = submitters_order_index[submitter.uuid] %>
<% bg_class = bg_classes[submitter_index % bg_classes.size] %>
<div class="absolute overflow-visible" style="width: <%= area['w'] * 100 %>%; height: <%= area['h'] * 100 %>%; left: <%= area['x'] * 100 %>%; top: <%= area['y'] * 100 %>%;">
<div class="flex h-full w-full bg-opacity-80 justify-center items-center <%= bg_class %>">
<%= svg_icon(SubmissionsController::FIELD_ICONS[field['type']], class: 'max-h-10 w-full h-full stroke-2 opacity-50') %>
</div>
</div>
<% end %>
<% end %>
</div>

@ -1,7 +1,7 @@
{
"ignored_warnings": [
{
"fingerprint": "bbd1bdad94998e53a48921859065e06cd1595502d4dc40362afcaa90307b591a",
"fingerprint": "de39648cd3f79d51e95e45f05fc77da49b0ad68bf80458061151016c0ef78208",
"note": "Permitted parameters are necessary for creating submitters via API"
},
{

@ -876,6 +876,7 @@ en: &en
you_requested_to_reset_your_password_use_the_link_below_to_continue: You requested to reset your password. Use the link below to continue
if_you_didnt_request_this_you_can_ignore_this_email: "If you didn't request this, please ignore this email."
your_password_wont_change_until_you_open_the_link_above_and_set_a_new_one: "Your password won't change until you open the link above and set a new one."
too_many_requests_try_again_later: Too many requests, try again later.
devise:
confirmations:
confirmed: Your email address has been successfully confirmed.
@ -1847,6 +1848,7 @@ es: &es
you_requested_to_reset_your_password_use_the_link_below_to_continue: Solicitaste restablecer tu contraseña. Usa el enlace a continuación para continuar.
if_you_didnt_request_this_you_can_ignore_this_email: "Si no solicitaste esto, puedes ignorar este correo electrónico."
your_password_wont_change_until_you_open_the_link_above_and_set_a_new_one: "Tu contraseña no cambiará hasta que abras el enlace anterior y establezcas una nueva."
too_many_requests_try_again_later: Demasiadas solicitudes. Intenta de nuevo más tarde.
devise:
confirmations:
confirmed: Tu dirección de correo electrónico ha sido confirmada correctamente.
@ -2819,6 +2821,7 @@ it: &it
you_requested_to_reset_your_password_use_the_link_below_to_continue: Hai richiesto di reimpostare la tua password. Usa il link qui sotto per continuare.
if_you_didnt_request_this_you_can_ignore_this_email: "Se non hai richiesto questo, puoi ignorare questa email."
your_password_wont_change_until_you_open_the_link_above_and_set_a_new_one: "La tua password non cambierà finché non apri il link sopra e ne imposti una nuova."
too_many_requests_try_again_later: Troppe richieste. Riprova più tardi.
devise:
confirmations:
confirmed: Il tuo indirizzo email è stato confermato con successo.
@ -3787,6 +3790,7 @@ fr: &fr
you_requested_to_reset_your_password_use_the_link_below_to_continue: Vous avez demandé à réinitialiser votre mot de passe. Utilisez le lien ci-dessous pour continuer.
if_you_didnt_request_this_you_can_ignore_this_email: "Si vous n'avez pas fait cette demande, veuillez ignorer cet e-mail."
your_password_wont_change_until_you_open_the_link_above_and_set_a_new_one: "Votre mot de passe ne changera pas tant que vous naurez pas ouvert le lien ci-dessus et défini un nouveau mot de passe."
too_many_requests_try_again_later: Trop de demandes. Réessayez plus tard.
devise:
confirmations:
confirmed: Votre adresse e-mail a été confirmée avec succès.
@ -4758,6 +4762,7 @@ pt: &pt
you_requested_to_reset_your_password_use_the_link_below_to_continue: Você solicitou a redefinição da sua senha. Use o link abaixo para continuar.
if_you_didnt_request_this_you_can_ignore_this_email: "Se você não solicitou isso, pode ignorar este e-mail."
your_password_wont_change_until_you_open_the_link_above_and_set_a_new_one: "Sua senha não será alterada até que você abra o link acima e defina uma nova."
too_many_requests_try_again_later: Muitas solicitações. Tente novamente mais tarde.
devise:
confirmations:
confirmed: Seu endereço de e-mail foi confirmado com sucesso.
@ -5729,6 +5734,7 @@ de: &de
you_requested_to_reset_your_password_use_the_link_below_to_continue: Sie haben angefordert, Ihr Passwort zurückzusetzen. Verwenden Sie den untenstehenden Link, um fortzufahren.
if_you_didnt_request_this_you_can_ignore_this_email: "Wenn Sie dies nicht angefordert haben, können Sie diese E-Mail ignorieren."
your_password_wont_change_until_you_open_the_link_above_and_set_a_new_one: "Ihr Passwort wird erst geändert, wenn Sie den obigen Link öffnen und ein neues festlegen."
too_many_requests_try_again_later: Zu viele Anfragen. Versuchen Sie es später erneut.
devise:
confirmations:
confirmed: Ihre E-Mail-Adresse wurde erfolgreich bestätigt.
@ -5938,6 +5944,7 @@ pl:
the_sender_has_requested_a_two_factor_authentication_via_one_time_password_sent_to_your_email_html: Nadawca zażądał uwierzytelniania dwuskładnikowego za pośrednictwem hasła jednorazowego wysłanego na Twój adres e-mail <b>%{email}</b>.
please_contact_the_requester_to_specify_your_email_for_two_factor_authentication: Skontaktuj się z nadawcą, aby podać swój adres e-mail do uwierzytelniania dwuskładnikowego.
rate_limit_exceeded: Przekroczono limit
too_many_requests_try_again_later: Zbyt wiele żądań. Spróbuj ponownie później.
uk:
require_phone_2fa_to_open: Вимагати двофакторну автентифікацію через телефон для відкриття
@ -6034,6 +6041,7 @@ uk:
the_sender_has_requested_a_two_factor_authentication_via_one_time_password_sent_to_your_email_html: Відправник запросив двофакторну автентифікацію за допомогою одноразового пароля, відправленого на вашу електронну пошту <b>%{email}</b>.
please_contact_the_requester_to_specify_your_email_for_two_factor_authentication: Будь ласка, зв'яжіться з відправником, щоб вказати вашу електронну пошту для двофакторної автентифікації.
rate_limit_exceeded: Перевищено ліміт
too_many_requests_try_again_later: Забагато запитів. Спробуйте пізніше.
cs:
require_phone_2fa_to_open: Vyžadovat otevření pomocí telefonního 2FA
@ -6130,6 +6138,7 @@ cs:
the_sender_has_requested_a_two_factor_authentication_via_one_time_password_sent_to_your_email_html: Odesílatel požádal o dvoufaktorové ověření pomocí jednorázového hesla odeslaného na vaši e-mailovou adresu <b>%{email}</b>.
please_contact_the_requester_to_specify_your_email_for_two_factor_authentication: Prosím kontaktujte odesílatele a uveďte svůj e-mail pro dvoufaktorové ověření.
rate_limit_exceeded: Překročena hranice
too_many_requests_try_again_later: Příliš mnoho požadavků. Zkuste to později.
he:
require_phone_2fa_to_open: דרוש אימות דו-שלבי באמצעות טלפון לפתיחה
@ -6226,6 +6235,7 @@ he:
the_sender_has_requested_a_two_factor_authentication_via_one_time_password_sent_to_your_email_html: השולח ביקש אימות דו-שלבי באמצעות סיסמה חד-פעמית שנשלחה לכתובת הדוא"ל שלך <b>%{email}</b>.
please_contact_the_requester_to_specify_your_email_for_two_factor_authentication: אנא פנה לשולח וציין את כתובת הדוא"ל שלך לאימות דו-שלבי.
rate_limit_exceeded: חריגה ממגבלת
too_many_requests_try_again_later: יותר מדי בקשות. נסה שוב מאוחר יותר.
nl: &nl
templates_that_require_email_or_phone_2fa_cannot_be_used_via_a_shared_link: Sjablonen waarvoor e-mail- of telefoon-2FA vereist is, kunnen niet via een gedeelde link worden gebruikt.
@ -7081,6 +7091,7 @@ nl: &nl
you_requested_to_reset_your_password_use_the_link_below_to_continue: Je hebt gevraagd je wachtwoord te resetten. Gebruik de onderstaande link om verder te gaan.
if_you_didnt_request_this_you_can_ignore_this_email: "Als je dit niet hebt aangevraagd, kun je deze e-mail negeren."
your_password_wont_change_until_you_open_the_link_above_and_set_a_new_one: "Je wachtwoord wordt niet gewijzigd totdat je de bovenstaande link opent en een nieuw wachtwoord instelt."
too_many_requests_try_again_later: Te veel verzoeken. Probeer het later opnieuw.
devise:
confirmations:
confirmed: Je e-mailadres is succesvol bevestigd.
@ -7290,6 +7301,7 @@ ar:
the_sender_has_requested_a_two_factor_authentication_via_one_time_password_sent_to_your_email_html: طلب المرسل المصادقة الثنائية عبر كلمة مرور لمرة واحدة مرسلة إلى عنوان بريدك الإلكتروني <b>%{email}</b>.
please_contact_the_requester_to_specify_your_email_for_two_factor_authentication: يرجى الاتصال بالمرسل لتحديد عنوان بريدك الإلكتروني للمصادقة الثنائية.
rate_limit_exceeded: تم تجاوز الحد المسموح به
too_many_requests_try_again_later: طلبات كثيرة جدًا. حاول مرة أخرى لاحقًا.
ko:
require_phone_2fa_to_open: 휴대폰 2FA를 열 때 요구함
@ -7386,6 +7398,7 @@ ko:
the_sender_has_requested_a_two_factor_authentication_via_one_time_password_sent_to_your_email_html: 발신자가 <b>%{email}</b> 이메일 주소로 전송된 일회용 비밀번호를 통해 2단계 인증을 요청했습니다.
please_contact_the_requester_to_specify_your_email_for_two_factor_authentication: 2단계 인증을 위해 이메일 주소를 지정하려면 발신자에게 문의하세요.
rate_limit_exceeded: 속도 제한 초과
too_many_requests_try_again_later: 요청이 너무 많습니다. 나중에 다시 시도하세요.
ja:
require_phone_2fa_to_open: 電話による2段階認証が必要です
@ -7482,6 +7495,7 @@ ja:
the_sender_has_requested_a_two_factor_authentication_via_one_time_password_sent_to_your_email_html: 送信者は、<b>%{email}</b> メールアドレスに送信されたワンタイムパスワードによる2段階認証を要求しました。
please_contact_the_requester_to_specify_your_email_for_two_factor_authentication: 2段階認証用にメールアドレスを指定するために、送信者にお問い合わせください。
rate_limit_exceeded: レート制限を超えました
too_many_requests_try_again_later: リクエストが多すぎます。後でもう一度お試しください。
en-US:
<<: *en

@ -9,6 +9,8 @@ class AddIsFirstToCompletedSubmitters < ActiveRecord::Migration[8.0]
add_index :completed_submitters, %i[account_id completed_at],
where: 'is_first = TRUE',
name: 'index_completed_submitters_account_id_completed_at_is_first'
add_index :completed_submitters, :submission_id, unique: true, where: 'is_first = TRUE'
add_index :completed_submitters, :submission_id, unique: adapter_name != 'Mysql2',
where: 'is_first = TRUE'
end
end

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -151,7 +151,8 @@ const token = jwt.sign({
"stamp",
"payment",
"phone",
"verification"
"verification",
"strikethrough"
]
},
"drawFieldType": {
@ -194,7 +195,8 @@ const token = jwt.sign({
"stamp",
"payment",
"phone",
"verification"
"verification",
"strikethrough"
]
},
"role": {
@ -321,7 +323,8 @@ const token = jwt.sign({
"stamp",
"payment",
"phone",
"verification"
"verification",
"strikethrough"
]
},
"role": {
@ -444,6 +447,12 @@ const token = jwt.sign({
"default": true,
"description": "Set `false` to now show the fields list on the right. Fields list is displayed by default."
},
"withFieldsDetection": {
"type": "boolean",
"required": false,
"default": false,
"description": "Display a button to automatically detect and add fields to the document with AI."
},
"withFieldPlaceholder": {
"type": "boolean",
"required": false,
@ -483,7 +492,7 @@ const token = jwt.sign({
"type": "string",
"required": false,
"default": "en",
"description": "UI language, 'en', 'es', 'de', 'fr', 'pt', 'he', 'ar' languages are available."
"description": "UI language, 'en', 'es', 'de', 'fr', 'pt', 'nl', 'he', 'ar' languages are available."
},
"i18n": {
"type": "object",

@ -118,7 +118,8 @@
"stamp",
"payment",
"phone",
"verification"
"verification",
"strikethrough"
]
},
"role": {
@ -249,7 +250,8 @@
"stamp",
"payment",
"phone",
"verification"
"verification",
"strikethrough"
]
},
"role": {
@ -336,7 +338,8 @@
"stamp",
"payment",
"phone",
"verification"
"verification",
"strikethrough"
]
},
"data-draw-field-type": {
@ -408,6 +411,12 @@
"default": true,
"description": "Set `false` to now show the fields list on the right. Fields list is displayed by default."
},
"data-with-fields-detection": {
"type": "boolean",
"required": false,
"default": false,
"description": "Display a button to automatically detect and add fields to the document with AI."
},
"data-with-field-placeholder": {
"type": "boolean",
"required": false,
@ -453,7 +462,7 @@
"type": "string",
"required": false,
"default": "en",
"description": "UI language, 'en', 'es', 'de', 'fr', 'pt', 'he', 'ar' languages are available."
"description": "UI language, 'en', 'es', 'de', 'fr', 'pt', 'nl', 'he', 'ar' languages are available."
},
"data-background-color": {
"type": "string",

@ -142,7 +142,8 @@ const token = jwt.sign({
"stamp",
"payment",
"phone",
"verification"
"verification",
"strikethrough"
]
},
"drawFieldType": {
@ -185,7 +186,8 @@ const token = jwt.sign({
"stamp",
"payment",
"phone",
"verification"
"verification",
"strikethrough"
]
},
"role": {
@ -312,7 +314,8 @@ const token = jwt.sign({
"stamp",
"payment",
"phone",
"verification"
"verification",
"strikethrough"
]
},
"role": {
@ -435,6 +438,12 @@ const token = jwt.sign({
"default": true,
"description": "Set `false` to now show the fields list on the right. Fields list is displayed by default."
},
"withFieldsDetection": {
"type": "boolean",
"required": false,
"default": false,
"description": "Display a button to automatically detect and add fields to the document with AI."
},
"withFieldPlaceholder": {
"type": "boolean",
"required": false,
@ -474,7 +483,7 @@ const token = jwt.sign({
"type": "string",
"required": false,
"default": "en",
"description": "UI language, 'en', 'es', 'de', 'fr', 'pt', 'he', 'ar' languages are available."
"description": "UI language, 'en', 'es', 'de', 'fr', 'pt', 'nl', 'he', 'ar' languages are available."
},
"i18n": {
"type": "object",

@ -163,7 +163,8 @@ const token = jwt.sign({
"stamp",
"payment",
"phone",
"verification"
"verification",
"strikethrough"
]
},
"draw-field-type": {
@ -206,7 +207,8 @@ const token = jwt.sign({
"stamp",
"payment",
"phone",
"verification"
"verification",
"strikethrough"
]
},
"role": {
@ -333,7 +335,8 @@ const token = jwt.sign({
"stamp",
"payment",
"phone",
"verification"
"verification",
"strikethrough"
]
},
"role": {
@ -450,6 +453,12 @@ const token = jwt.sign({
"default": true,
"description": "Set `false` to now show the fields list on the right. Fields list is displayed by default."
},
"with-fields-detection": {
"type": "boolean",
"required": false,
"default": false,
"description": "Display a button to automatically detect and add fields to the document with AI."
},
"with-field-placeholder": {
"type": "boolean",
"required": false,
@ -483,7 +492,7 @@ const token = jwt.sign({
"type": "string",
"required": false,
"default": "en",
"description": "UI language, 'en', 'es', 'de', 'fr', 'pt', 'he', 'ar' languages are available."
"description": "UI language, 'en', 'es', 'de', 'fr', 'pt', 'nl', 'he', 'ar' languages are available."
},
"i18n": {
"type": "object",

@ -30,7 +30,7 @@ export class AppComponent {}
"src": {
"type": "string",
"required": true,
"description": "Public URL of the document signing form. There are two types of URLs: <li><code>/d/{slug}</code> - template form signing URL can be copied from the template page in the admin dashboard. Also template \"slug\" key can be obtained via the <code>/templates</code> API.</li><li><code>/s/{slug}</code> - individual signer URL. Signer \"slug\" key can be obtained via the <code>/submissions</code> API which is used to initiate signature requests for a template form with recipients.</li>"
"description": "Public URL of the document signing form. There are two types of URLs: <ul class=\"list-inside list-disc\"><li><code>/d/{slug}</code> - template form signing URL can be copied from the template page in the admin dashboard. Also template \"slug\" key can be obtained via the <code>/templates</code> API.</li><li><code>/s/{slug}</code> - individual signer URL. Signer \"slug\" key can be obtained via the <code>/submissions</code> API which is used to initiate signature requests for a template form with recipients.</li></ul>"
},
"email": {
"type": "string",

@ -26,7 +26,7 @@
"data-src": {
"type": "string",
"required": true,
"description": "Public URL of the document signing form. There are two types of URLs: <li><code>/d/{slug}</code> - template form signing URL can be copied from the template page in the admin dashboard. Also template \"slug\" key can be obtained via the <code>/templates</code> API.</li><li><code>/s/{slug}</code> - individual signer URL. Signer \"slug\" key can be obtained via the <code>/submissions</code> API which is used to initiate signature requests for a template form with recipients.</li>"
"description": "Public URL of the document signing form. There are two types of URLs: <ul class=\"list-inside list-disc\"><li><code>/d/{slug}</code> - template form signing URL can be copied from the template page in the admin dashboard. Also template \"slug\" key can be obtained via the <code>/templates</code> API.</li><li><code>/s/{slug}</code> - individual signer URL. Signer \"slug\" key can be obtained via the <code>/submissions</code> API which is used to initiate signature requests for a template form with recipients.</li></ul>"
},
"data-email": {
"type": "string",

@ -27,7 +27,7 @@ export function App() {
"src": {
"type": "string",
"required": true,
"description": "Public URL of the document signing form. There are two types of URLs: <li><code>/d/{slug}</code> - template form signing URL can be copied from the template page in the admin dashboard. Also template \"slug\" key can be obtained via the <code>/templates</code> API.</li><li><code>/s/{slug}</code> - individual signer URL. Signer \"slug\" key can be obtained via the <code>/submissions</code> API which is used to initiate signature requests for a template form with recipients.</li>"
"description": "Public URL of the document signing form. There are two types of URLs: <ul class=\"list-inside list-disc\"><li><code>/d/{slug}</code> - template form signing URL can be copied from the template page in the admin dashboard. Also template \"slug\" key can be obtained via the <code>/templates</code> API.</li><li><code>/s/{slug}</code> - individual signer URL. Signer \"slug\" key can be obtained via the <code>/submissions</code> API which is used to initiate signature requests for a template form with recipients.</li></ul>"
},
"email": {
"type": "string",

@ -36,7 +36,7 @@ export default {
"src": {
"type": "string",
"required": true,
"description": "Public URL of the document signing form. There are two types of URLs: <li><code>/d/{slug}</code> - template form signing URL can be copied from the template page in the admin dashboard. Also template \"slug\" key can be obtained via the <code>/templates</code> API.</li><li><code>/s/{slug}</code> - individual signer URL. Signer \"slug\" key can be obtained via the <code>/submissions</code> API which is used to initiate signature requests for a template form with recipients.</li>"
"description": "Public URL of the document signing form. There are two types of URLs: <ul class=\"list-inside list-disc\"><li><code>/d/{slug}</code> - template form signing URL can be copied from the template page in the admin dashboard. Also template \"slug\" key can be obtained via the <code>/templates</code> API.</li><li><code>/s/{slug}</code> - individual signer URL. Signer \"slug\" key can be obtained via the <code>/submissions</code> API which is used to initiate signature requests for a template form with recipients.</li></ul>"
},
"email": {
"type": "string",

@ -251,7 +251,8 @@
"stamp",
"payment",
"phone",
"verification"
"verification",
"strikethrough"
]
},
"required": {
@ -277,6 +278,10 @@
"type": "string",
"description": "Font color of the field value."
},
"background": {
"type": "string",
"description": "Field box background color."
},
"align": {
"type": "string",
"description": "Horizontal alignment of the field text value."
@ -300,6 +305,13 @@
"mask": {
"type": "boolean",
"description": "Indicates if the field is masked on the document."
},
"reasons": {
"description": "An array of signature reasons to choose from.",
"type": "array",
"items": {
"type": "string"
}
}
}
},
@ -714,7 +726,8 @@
"stamp",
"payment",
"phone",
"verification"
"verification",
"strikethrough"
]
},
"required": {
@ -740,6 +753,10 @@
"type": "string",
"description": "Font color of the field value."
},
"background": {
"type": "string",
"description": "Field box background color."
},
"align": {
"type": "string",
"description": "Horizontal alignment of the field text value."
@ -763,6 +780,13 @@
"mask": {
"type": "boolean",
"description": "Indicates if the field is masked on the document."
},
"reasons": {
"description": "An array of signature reasons to choose from.",
"type": "array",
"items": {
"type": "string"
}
}
}
},
@ -1782,6 +1806,11 @@
"description": "Set to `true` to require phone 2FA verification via a one-time code sent to the phone number in order to access the documents.",
"default": false
},
"require_email_2fa": {
"type": "boolean",
"description": "Set to `true` to require email 2FA verification via a one-time code sent to the email address in order to access the documents.",
"default": false
},
"message": {
"type": "object",
"properties": {
@ -1933,6 +1962,15 @@
],
"default": "black"
},
"background": {
"type": "string",
"description": "Field box background color.",
"enum": [
"black",
"white",
"blue"
]
},
"align": {
"type": "string",
"description": "Horizontal alignment of the field text value.",
@ -1986,6 +2024,13 @@
}
],
"default": false
},
"reasons": {
"description": "An array of signature reasons to choose from.",
"type": "array",
"items": {
"type": "string"
}
}
}
}
@ -2622,6 +2667,10 @@
"event_timestamp": {
"type": "string",
"description": "Date and time when the event was triggered."
},
"data": {
"type": "object",
"description": "Additional event details object."
}
}
}
@ -2733,7 +2782,8 @@
"id": 1,
"submitter_id": 2,
"event_type": "view_form",
"event_timestamp": "2023-12-14T15:47:24.566Z"
"event_timestamp": "2023-12-14T15:47:24.566Z",
"data": {}
}
],
"documents": [
@ -3318,7 +3368,8 @@
"stamp",
"payment",
"phone",
"verification"
"verification",
"strikethrough"
]
},
"role": {
@ -3471,6 +3522,15 @@
"description": "Set to `true` to require phone 2FA verification via a one-time code sent to the phone number in order to access the documents.",
"default": false
},
"require_email_2fa": {
"type": "boolean",
"description": "Set to `true` to require email 2FA verification via a one-time code sent to the email address in order to access the documents.",
"default": false
},
"invite_by": {
"type": "string",
"description": "Set the role name of the previous party that should invite this party via email."
},
"fields": {
"type": "array",
"description": "A list of configurations for document form fields.",
@ -3609,6 +3669,15 @@
],
"default": "black"
},
"background": {
"type": "string",
"description": "Field box background color.",
"enum": [
"black",
"white",
"blue"
]
},
"align": {
"type": "string",
"description": "Horizontal alignment of the field text value.",
@ -3662,6 +3731,13 @@
}
],
"default": false
},
"reasons": {
"description": "An array of signature reasons to choose from.",
"type": "array",
"items": {
"type": "string"
}
}
}
}
@ -3744,7 +3820,6 @@
"type": "object",
"required": [
"id",
"submission_id",
"uuid",
"email",
"slug",
@ -3833,6 +3908,53 @@
"awaiting"
]
},
"values": {
"type": "array",
"description": "An array of pre-filled values for the submitter.",
"items": {
"type": "object",
"required": [
"field",
"value"
],
"properties": {
"field": {
"type": "string",
"description": "Document template field name."
},
"value": {
"oneOf": [
{
"type": "string"
},
{
"type": "number"
},
{
"type": "boolean"
},
{
"type": "array",
"items": {
"oneOf": [
{
"type": "string"
},
{
"type": "number"
},
{
"type": "boolean"
}
]
}
}
],
"description": "Pre-filled value of the field."
}
}
}
},
"role": {
"type": "string",
"description": "The role of the submitter."
@ -3944,7 +4066,8 @@
"stamp",
"payment",
"phone",
"verification"
"verification",
"strikethrough"
]
},
"required": {
@ -3970,6 +4093,10 @@
"type": "string",
"description": "Font color of the field value."
},
"background": {
"type": "string",
"description": "Field box background color."
},
"align": {
"type": "string",
"description": "Horizontal alignment of the field text value."
@ -3993,6 +4120,13 @@
"mask": {
"type": "boolean",
"description": "Indicates if the field is masked on the document."
},
"reasons": {
"description": "An array of signature reasons to choose from.",
"type": "array",
"items": {
"type": "string"
}
}
}
},
@ -4040,27 +4174,6 @@
}
}
},
"documents": {
"type": "array",
"description": "List of documents attached to the one-off submission.",
"items": {
"type": "object",
"required": [
"attachment_uuid",
"name"
],
"properties": {
"attachment_uuid": {
"type": "string",
"description": "Unique indentifier of attached document to the one-off submission."
},
"name": {
"type": "string",
"description": "Name of the attached document to the one-off submission."
}
}
}
},
"expire_at": {
"type": "string",
"description": "Specify the expiration date and time after which the submission becomes unavailable for signature.",
@ -4158,7 +4271,7 @@
"Submissions"
],
"summary": "Create a submission from DOCX",
"description": "The API endpoint provides functionality to create a one-off submission request from a DOCX file with dynamic content variables. Use <code>[[variable_name]]</code> text tags to define dynamic content variables in the document. See <a href=\"https://www.docuseal.com/examples/demo_template.docx\" target=\"_blank\" class=\"link font-bold\">https://www.docuseal.com/examples/demo_template.docx</a> for the specific text variable syntax, including dynamic content tables and list. You can also use the <code>{{signature}}</code> fillable field syntax to define fillable fields, as in a PDF.<br><b>Related Guides</b><br><a href=\"https://www.docuseal.com/guides/use-embedded-text-field-tags-in-the-pdf-to-create-a-fillable-form\" class=\"link\">Use embedded text field tags to create a fillable form</a>",
"description": "The API endpoint provides functionality to create a one-off submission request from a DOCX file with dynamic content variables. Use <code>[[variable_name]]</code> text tags to define dynamic content variables in the document. See <a href=\"https://www.docuseal.com/examples/demo_template.docx\" target=\"_blank\" class=\"link font-bold\">https://www.docuseal.com/examples/demo_template.docx</a> for the specific text variable syntax, including dynamic content tables and list. You can also use the <code>{{signature}}</code> field syntax to define fillable fields, as in a PDF.<br><b>Related Guides</b><br><a href=\"https://www.docuseal.com/guides/use-dynamic-content-variables-in-docx-to-create-personalized-documents\" class=\"link\">Use dynamic content variables in DOCX to create personalized documents</a>",
"operationId": "createSubmissionFromDocx",
"parameters": [],
"requestBody": {
@ -4189,7 +4302,7 @@
},
"variables": {
"type": "object",
"description": "Dynamic content variables object",
"description": "Dynamic content variables object. Variable values can be strings, numbers, arrays, objects, or HTML content used to generate styled text, paragraphs, and tables in DOCX.",
"example": {
"variable_name": "value"
}
@ -4327,6 +4440,15 @@
"description": "Set to `true` to require phone 2FA verification via a one-time code sent to the phone number in order to access the documents.",
"default": false
},
"require_email_2fa": {
"type": "boolean",
"description": "Set to `true` to require email 2FA verification via a one-time code sent to the email address in order to access the documents.",
"default": false
},
"invite_by": {
"type": "string",
"description": "Set the role name of the previous party that should invite this party via email."
},
"fields": {
"type": "array",
"description": "A list of configurations for document form fields.",
@ -4465,6 +4587,15 @@
],
"default": "black"
},
"background": {
"type": "string",
"description": "Field box background color.",
"enum": [
"black",
"white",
"blue"
]
},
"align": {
"type": "string",
"description": "Horizontal alignment of the field text value.",
@ -4518,6 +4649,13 @@
}
],
"default": false
},
"reasons": {
"description": "An array of signature reasons to choose from.",
"type": "array",
"items": {
"type": "string"
}
}
}
}
@ -4595,7 +4733,6 @@
"type": "object",
"required": [
"id",
"submission_id",
"uuid",
"email",
"slug",
@ -4684,6 +4821,53 @@
"awaiting"
]
},
"values": {
"type": "array",
"description": "An array of pre-filled values for the submitter.",
"items": {
"type": "object",
"required": [
"field",
"value"
],
"properties": {
"field": {
"type": "string",
"description": "Document template field name."
},
"value": {
"oneOf": [
{
"type": "string"
},
{
"type": "number"
},
{
"type": "boolean"
},
{
"type": "array",
"items": {
"oneOf": [
{
"type": "string"
},
{
"type": "number"
},
{
"type": "boolean"
}
]
}
}
],
"description": "Pre-filled value of the field."
}
}
}
},
"role": {
"type": "string",
"description": "The role of the submitter."
@ -4795,7 +4979,8 @@
"stamp",
"payment",
"phone",
"verification"
"verification",
"strikethrough"
]
},
"required": {
@ -4821,6 +5006,10 @@
"type": "string",
"description": "Font color of the field value."
},
"background": {
"type": "string",
"description": "Field box background color."
},
"align": {
"type": "string",
"description": "Horizontal alignment of the field text value."
@ -4844,6 +5033,13 @@
"mask": {
"type": "boolean",
"description": "Indicates if the field is masked on the document."
},
"reasons": {
"description": "An array of signature reasons to choose from.",
"type": "array",
"items": {
"type": "string"
}
}
}
},
@ -4891,27 +5087,6 @@
}
}
},
"documents": {
"type": "array",
"description": "List of documents attached to the one-off submission.",
"items": {
"type": "object",
"required": [
"attachment_uuid",
"name"
],
"properties": {
"attachment_uuid": {
"type": "string",
"description": "Unique indentifier of attached document to the one-off submission."
},
"name": {
"type": "string",
"description": "Name of the attached document to the one-off submission."
}
}
}
},
"expire_at": {
"type": "string",
"description": "Specify the expiration date and time after which the submission becomes unavailable for signature.",
@ -5198,6 +5373,15 @@
"description": "Set to `true` to require phone 2FA verification via a one-time code sent to the phone number in order to access the documents.",
"default": false
},
"require_email_2fa": {
"type": "boolean",
"description": "Set to `true` to require email 2FA verification via a one-time code sent to the email address in order to access the documents.",
"default": false
},
"invite_by": {
"type": "string",
"description": "Set the role name of the previous party that should invite this party via email."
},
"fields": {
"type": "array",
"description": "A list of configurations for document form fields.",
@ -5336,6 +5520,15 @@
],
"default": "black"
},
"background": {
"type": "string",
"description": "Field box background color.",
"enum": [
"black",
"white",
"blue"
]
},
"align": {
"type": "string",
"description": "Horizontal alignment of the field text value.",
@ -5389,6 +5582,13 @@
}
],
"default": false
},
"reasons": {
"description": "An array of signature reasons to choose from.",
"type": "array",
"items": {
"type": "string"
}
}
}
}
@ -5461,7 +5661,6 @@
"type": "object",
"required": [
"id",
"submission_id",
"uuid",
"email",
"slug",
@ -5550,6 +5749,53 @@
"awaiting"
]
},
"values": {
"type": "array",
"description": "An array of pre-filled values for the submitter.",
"items": {
"type": "object",
"required": [
"field",
"value"
],
"properties": {
"field": {
"type": "string",
"description": "Document template field name."
},
"value": {
"oneOf": [
{
"type": "string"
},
{
"type": "number"
},
{
"type": "boolean"
},
{
"type": "array",
"items": {
"oneOf": [
{
"type": "string"
},
{
"type": "number"
},
{
"type": "boolean"
}
]
}
}
],
"description": "Pre-filled value of the field."
}
}
}
},
"role": {
"type": "string",
"description": "The role of the submitter."
@ -5661,7 +5907,8 @@
"stamp",
"payment",
"phone",
"verification"
"verification",
"strikethrough"
]
},
"required": {
@ -5687,6 +5934,10 @@
"type": "string",
"description": "Font color of the field value."
},
"background": {
"type": "string",
"description": "Field box background color."
},
"align": {
"type": "string",
"description": "Horizontal alignment of the field text value."
@ -5710,6 +5961,13 @@
"mask": {
"type": "boolean",
"description": "Indicates if the field is masked on the document."
},
"reasons": {
"description": "An array of signature reasons to choose from.",
"type": "array",
"items": {
"type": "string"
}
}
}
},
@ -5757,27 +6015,6 @@
}
}
},
"documents": {
"type": "array",
"description": "List of documents attached to the one-off submission.",
"items": {
"type": "object",
"required": [
"attachment_uuid",
"name"
],
"properties": {
"attachment_uuid": {
"type": "string",
"description": "Unique indentifier of attached document to the one-off submission."
},
"name": {
"type": "string",
"description": "Name of the attached document to the one-off submission."
}
}
}
},
"expire_at": {
"type": "string",
"description": "Specify the expiration date and time after which the submission becomes unavailable for signature.",
@ -6078,6 +6315,10 @@
"event_timestamp": {
"type": "string",
"description": "Date and time when the event was triggered."
},
"data": {
"type": "object",
"description": "Additional event details object."
}
}
}
@ -6185,7 +6426,8 @@
"id": 12,
"submitter_id": 7,
"event_type": "view_form",
"event_timestamp": "2023-12-14T15:47:17.351Z"
"event_timestamp": "2023-12-14T15:47:17.351Z",
"data": {}
}
],
"values": [
@ -6435,6 +6677,15 @@
],
"default": "black"
},
"background": {
"type": "string",
"description": "Field box background color.",
"enum": [
"black",
"white",
"blue"
]
},
"align": {
"type": "string",
"description": "Horizontal alignment of the field text value.",
@ -6488,6 +6739,13 @@
}
],
"default": false
},
"reasons": {
"description": "An array of signature reasons to choose from.",
"type": "array",
"items": {
"type": "string"
}
}
}
}
@ -6990,6 +7248,10 @@
"event_timestamp": {
"type": "string",
"description": "Date and time when the event was triggered."
},
"data": {
"type": "object",
"description": "Additional event details object."
}
}
}
@ -7126,7 +7388,8 @@
"id": 12,
"submitter_id": 7,
"event_type": "view_form",
"event_timestamp": "2023-12-14T15:48:17.351Z"
"event_timestamp": "2023-12-14T15:48:17.351Z",
"data": {}
}
],
"values": [
@ -7346,7 +7609,8 @@
"stamp",
"payment",
"phone",
"verification"
"verification",
"strikethrough"
]
},
"required": {
@ -7372,6 +7636,10 @@
"type": "string",
"description": "Font color of the field value."
},
"background": {
"type": "string",
"description": "Field box background color."
},
"align": {
"type": "string",
"description": "Horizontal alignment of the field text value."
@ -7395,6 +7663,13 @@
"mask": {
"type": "boolean",
"description": "Indicates if the field is masked on the document."
},
"reasons": {
"description": "An array of signature reasons to choose from.",
"type": "array",
"items": {
"type": "string"
}
}
}
},
@ -7793,7 +8068,8 @@
"stamp",
"payment",
"phone",
"verification"
"verification",
"strikethrough"
]
},
"required": {
@ -7819,6 +8095,10 @@
"type": "string",
"description": "Font color of the field value."
},
"background": {
"type": "string",
"description": "Field box background color."
},
"align": {
"type": "string",
"description": "Horizontal alignment of the field text value."
@ -7842,6 +8122,13 @@
"mask": {
"type": "boolean",
"description": "Indicates if the field is masked on the document."
},
"reasons": {
"description": "An array of signature reasons to choose from.",
"type": "array",
"items": {
"type": "string"
}
}
}
},
@ -8294,7 +8581,8 @@
"stamp",
"payment",
"phone",
"verification"
"verification",
"strikethrough"
]
},
"required": {
@ -8320,6 +8608,10 @@
"type": "string",
"description": "Font color of the field value."
},
"background": {
"type": "string",
"description": "Field box background color."
},
"align": {
"type": "string",
"description": "Horizontal alignment of the field text value."
@ -8343,6 +8635,13 @@
"mask": {
"type": "boolean",
"description": "Indicates if the field is masked on the document."
},
"reasons": {
"description": "An array of signature reasons to choose from.",
"type": "array",
"items": {
"type": "string"
}
}
}
},
@ -8674,7 +8973,8 @@
"stamp",
"payment",
"phone",
"verification"
"verification",
"strikethrough"
]
},
"role": {
@ -8812,6 +9112,15 @@
],
"default": "black"
},
"background": {
"type": "string",
"description": "Field box background color.",
"enum": [
"black",
"white",
"blue"
]
},
"align": {
"type": "string",
"description": "Horizontal alignment of the field text value.",
@ -8865,6 +9174,13 @@
}
],
"default": false
},
"reasons": {
"description": "An array of signature reasons to choose from.",
"type": "array",
"items": {
"type": "string"
}
}
}
}
@ -8989,7 +9305,8 @@
"stamp",
"payment",
"phone",
"verification"
"verification",
"strikethrough"
]
},
"required": {
@ -9015,6 +9332,10 @@
"type": "string",
"description": "Font color of the field value."
},
"background": {
"type": "string",
"description": "Field box background color."
},
"align": {
"type": "string",
"description": "Horizontal alignment of the field text value."
@ -9038,6 +9359,13 @@
"mask": {
"type": "boolean",
"description": "Indicates if the field is masked on the document."
},
"reasons": {
"description": "An array of signature reasons to choose from.",
"type": "array",
"items": {
"type": "string"
}
}
}
},
@ -9369,7 +9697,8 @@
"stamp",
"payment",
"phone",
"verification"
"verification",
"strikethrough"
]
},
"role": {
@ -9515,6 +9844,15 @@
],
"default": "black"
},
"background": {
"type": "string",
"description": "Field box background color.",
"enum": [
"black",
"white",
"blue"
]
},
"align": {
"type": "string",
"description": "Horizontal alignment of the field text value.",
@ -9568,6 +9906,13 @@
}
],
"default": false
},
"reasons": {
"description": "An array of signature reasons to choose from.",
"type": "array",
"items": {
"type": "string"
}
}
}
}
@ -9702,7 +10047,8 @@
"stamp",
"payment",
"phone",
"verification"
"verification",
"strikethrough"
]
},
"required": {
@ -9728,6 +10074,10 @@
"type": "string",
"description": "Font color of the field value."
},
"background": {
"type": "string",
"description": "Field box background color."
},
"align": {
"type": "string",
"description": "Horizontal alignment of the field text value."
@ -9751,6 +10101,13 @@
"mask": {
"type": "boolean",
"description": "Indicates if the field is masked on the document."
},
"reasons": {
"description": "An array of signature reasons to choose from.",
"type": "array",
"items": {
"type": "string"
}
}
}
},
@ -10169,7 +10526,8 @@
"stamp",
"payment",
"phone",
"verification"
"verification",
"strikethrough"
]
},
"required": {
@ -10195,6 +10553,10 @@
"type": "string",
"description": "Font color of the field value."
},
"background": {
"type": "string",
"description": "Field box background color."
},
"align": {
"type": "string",
"description": "Horizontal alignment of the field text value."
@ -10218,6 +10580,13 @@
"mask": {
"type": "boolean",
"description": "Indicates if the field is masked on the document."
},
"reasons": {
"description": "An array of signature reasons to choose from.",
"type": "array",
"items": {
"type": "string"
}
}
}
},

@ -281,6 +281,10 @@ Get submission creation, completion, expiration, and archiving notifications usi
"event_timestamp": {
"type": "string",
"description": "Date and time when the event was triggered."
},
"data": {
"type": "object",
"description": "Additional event details object."
}
}
}

@ -1,7 +1,35 @@
# frozen_string_literal: true
module DownloadUtils
LOCALHOSTS = %w[0.0.0.0 127.0.0.1 localhost].freeze
LOCALHOSTS = Set[
'0.0.0.0',
'127.0.0.1',
'127.0.1.1',
'localhost',
'localhost.localdomain',
'::1',
'[::1]',
'ip6-localhost',
'ip6-loopback',
'127.0.0.0',
'127.255.255.255',
'::',
'0:0:0:0:0:0:0:1',
'[0:0:0:0:0:0:0:1]',
'0000:0000:0000:0000:0000:0000:0000:0001',
'[0000:0000:0000:0000:0000:0000:0000:0001]',
'::0',
'0::0',
'::ffff:127.0.0.1',
'[::ffff:127.0.0.1]',
'::ffff:7f00:1',
'[::ffff:7f00:1]',
'local',
'localhost.local',
'ip6-localnet',
'ip6-allnodes',
'ip6-allrouters'
].freeze
UnableToDownload = Class.new(StandardError)
@ -14,10 +42,7 @@ module DownloadUtils
Addressable::URI.parse(url).normalize
end
if Docuseal.multitenant?
raise UnableToDownload, "Error loading: #{uri}. Only HTTPS is allowed." if uri.scheme != 'https'
raise UnableToDownload, "Error loading: #{uri}. Can't download from localhost." if uri.host.in?(LOCALHOSTS)
end
validate_uri!(uri) if Docuseal.multitenant?
resp = conn.get(uri)
@ -26,9 +51,16 @@ module DownloadUtils
resp
end
def validate_uri!(uri)
raise UnableToDownload, "Error loading: #{uri}. Only HTTPS is allowed." if uri.scheme != 'https'
raise UnableToDownload, "Error loading: #{uri}. Can't download from localhost." if uri.host.in?(LOCALHOSTS)
end
def conn
Faraday.new do |faraday|
faraday.response :follow_redirects
faraday.response :follow_redirects, callback: lambda { |_, new_env|
validate_uri!(new_env[:url]) if Docuseal.multitenant?
}
end
end
end

@ -71,7 +71,11 @@ module Params
end
def validate_submitter(submitter_params)
required(submitter_params, %i[email phone name])
if submitter_params['invite_by'].present?
required(submitter_params, :role)
else
required(submitter_params, %i[email phone name])
end
type(submitter_params, :name, String)
type(submitter_params, :reply_to, String)

@ -81,7 +81,7 @@ module Submissions
next if submission.submitters.blank?
maybe_add_invite_submitters(submission, template)
maybe_add_invite_submitters(submission, template, attrs[:submitters])
submission.template = nil unless with_template
@ -102,8 +102,16 @@ module Submissions
end
end
def maybe_add_invite_submitters(submission, template)
def maybe_add_invite_submitters(submission, template, submitter_attrs)
template.submitters.each_with_index do |item, index|
submitter_attr = submitter_attrs.find { |e| e['role'].to_s.casecmp?(item['name'].to_s) }
if submitter_attr && submitter_attr['invite_by'].present?
invite_by_uuid = template.submitters.find { |s| s['name'] == submitter_attr['invite_by'] }&.dig('uuid')
item = item.merge('invite_by_uuid' => invite_by_uuid) if invite_by_uuid
end
next if item['invite_by_uuid'].blank? && item['optional_invite_by_uuid'].blank?
next if submission.template_submitters.any? { |e| e['uuid'] == item['uuid'] }

@ -522,10 +522,17 @@ module Submissions
if field['type'].in?(%w[multiple radio])
option = field['options']&.find { |o| o['uuid'] == area['option_uuid'] }
option_name = option['value'].presence
option_name ||= "#{I18n.t('option', locale: locale)} #{field['options'].index(option) + 1}"
value =
if option
option_name = option['value'].presence
option_name ||= "#{I18n.t('option', locale: locale)} #{field['options'].index(option) + 1}"
value = Array.wrap(value).include?(option_name)
Array.wrap(value).include?(option_name)
else
Rollbar.error("Invalid option: #{field['uuid']}") if defined?(Rollbar)
false
end
end
next unless value == true

@ -106,7 +106,8 @@ module Submitters
if AccountConfig.exists?(account_id: submitter.submission.account_id,
key: AccountConfig::COMBINE_PDF_RESULT_KEY,
value: true) &&
submitter.submission.submitters.all?(&:completed_at?)
submitter.submission.submitters.all?(&:completed_at?) &&
submitter.submission.template_fields.none? { |f| f['type'] == 'verification' }
return [submitter.submission.combined_document_attachment || Submissions::EnsureCombinedGenerated.call(submitter)]
end
@ -157,6 +158,7 @@ module Submitters
preferences['send_email'] = params['send_email'].in?(TRUE_VALUES) if params.key?('send_email')
preferences['send_sms'] = params['send_sms'].in?(TRUE_VALUES) if params.key?('send_sms')
preferences['require_phone_2fa'] = params['require_phone_2fa'].in?(TRUE_VALUES) if params.key?('require_phone_2fa')
preferences['require_email_2fa'] = params['require_email_2fa'].in?(TRUE_VALUES) if params.key?('require_email_2fa')
preferences['bcc_completed'] = params['bcc_completed'] if params.key?('bcc_completed')
preferences['reply_to'] = params['reply_to'] if params.key?('reply_to')
preferences['go_to_last'] = params['go_to_last'] if params.key?('go_to_last')

Loading…
Cancel
Save