feat: add consent banner requirement for signers (B6) (#16)

* feat: add consent banner requirement for signers (B6)

- Add REQUIRE_CONSENT_KEY to AccountConfig for per-account toggle
- Add consent_given event type to SubmissionEvent for audit trail
- Add consent checkbox UI on last step of signing form (form.vue)
- Add isLastStep computed property and onConsentChange method
- Create API endpoint (submitter_consents#create) to record consent
- Add requireConsent prop passing from server via data attribute
- Add settings toggle in Account preferences page
- Add i18n translations for all 14 languages (JS + Rails)
- Submit button disabled until consent checkbox is checked

* fix: add REQUIRE_CONSENT_KEY to AccountConfigsController ALLOWED_KEYS

* fix: keep consent checkbox visible after checking (don't remove from DOM)

* fix: bind checkbox checked state to consentAccepted for step navigation sync

---------

Co-authored-by: mario.pander <developbob50@gmail.com>
pull/639/head
devin-ai-integration[bot] 1 week ago committed by GitHub
parent 39743a5d69
commit 2c315dd9b1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -26,7 +26,8 @@ class AccountConfigsController < ApplicationController
AccountConfig::REQUIRE_SIGNING_REASON_KEY,
AccountConfig::DOCUMENT_FILENAME_FORMAT_KEY,
AccountConfig::ENABLE_MCP_KEY,
AccountConfig::IP_ALLOWLIST_KEY
AccountConfig::IP_ALLOWLIST_KEY,
AccountConfig::REQUIRE_CONSENT_KEY
].freeze
InvalidKey = Class.new(StandardError)

@ -0,0 +1,16 @@
# frozen_string_literal: true
module Api
class SubmitterConsentsController < ApiBaseController
skip_before_action :authenticate_user!
skip_authorization_check
def create
@submitter = Submitter.find_by!(slug: params[:submitter_slug])
SubmissionEvents.create_with_tracking_data(@submitter, 'consent_given', request)
render json: {}
end
end
end

@ -42,6 +42,7 @@ safeRegisterElement('submission-form', class extends HTMLElement {
withConfetti: this.dataset.withConfetti === 'true',
withFieldLabels: this.dataset.withFieldLabels !== 'false',
withDisclosure: this.dataset.withDisclosure === 'true',
requireConsent: this.dataset.requireConsent === 'true',
reuseSignature: this.dataset.reuseSignature !== 'false',
withTypedSignature: this.dataset.withTypedSignature !== 'false',
authenticityToken: document.querySelector('meta[name="csrf-token"]')?.content,

@ -570,12 +570,26 @@
v-if="(currentField.type !== 'payment' && currentField.type !== 'verification' && currentField.type !== 'kba') || submittedValues[currentField.uuid]"
:class="currentField.type === 'signature' ? 'mt-2' : 'mt-4 md:mt-6'"
>
<label
v-if="requireConsent && isLastStep"
class="flex items-start space-x-2 mb-3 cursor-pointer consent-label"
>
<input
type="checkbox"
class="base-checkbox mt-0.5 !h-5 !w-5 shrink-0"
:checked="consentAccepted"
@change="onConsentChange($event)"
>
<span class="text-xs leading-tight">
{{ t('i_consent_to_sign_this_document_electronically') }}
</span>
</label>
<button
id="submit_form_button"
ref="submitButton"
type="submit"
class="base-button w-full flex justify-center submit-form-button"
:disabled="isButtonDisabled"
:disabled="isButtonDisabled || (requireConsent && isLastStep && !consentAccepted)"
>
<span class="flex">
<IconInnerShadowTop
@ -857,6 +871,11 @@ export default {
required: false,
default: false
},
requireConsent: {
type: Boolean,
required: false,
default: false
},
reuseSignature: {
type: Boolean,
required: false,
@ -1024,7 +1043,9 @@ export default {
isSubmittingComplete: false,
submittedValues: {},
recalculateButtonDisabledKey: '',
isAccessibilityMode: false
isAccessibilityMode: false,
consentAccepted: false,
consentRecorded: false
}
},
computed: {
@ -1071,6 +1092,13 @@ export default {
})
})
},
isLastStep () {
if (this.onlyRequiredFields) {
return !this.findNextStep(this.currentStep)
} else {
return this.stepFields.length === this.currentStep + 1
}
},
submitButtonText () {
if (this.alwaysMinimize) {
return this.t('submit')
@ -1354,6 +1382,19 @@ export default {
t (key) {
return this.i18n[key] || i18n[this.selectedLanguage]?.[key] || i18n.en[key] || key
},
onConsentChange (event) {
this.consentAccepted = event.target.checked
if (this.consentAccepted && !this.consentRecorded && !this.dryRun) {
this.consentRecorded = true
fetch(this.baseUrl + '/api/submitter_consents', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ submitter_slug: this.submitterSlug })
})
}
},
onOrientationChange (event) {
this.orientation = event.target.type
},

@ -105,7 +105,8 @@ const en = {
browser_privacy_settings_block_canvas: 'Your browser privacy settings restrict use of the drawing canvas. Please use a different browser or device, or disable privacy settings that block canvas in order to sign.',
wait_countdown_seconds: 'Wait {countdown} seconds',
enter_screen_reader_mode: 'Enter screen reader mode',
generated_by_docuseal: 'Generated by DocuSeal'
generated_by_docuseal: 'Generated by DocuSeal',
i_consent_to_sign_this_document_electronically: 'I consent to sign this document electronically'
}
const es = {
@ -215,7 +216,8 @@ const es = {
browser_privacy_settings_block_canvas: 'La configuración de privacidad de su navegador restringe el uso del lienzo de dibujo. Utilice un navegador o dispositivo diferente, o desactive la configuración de privacidad que bloquea el lienzo para firmar.',
wait_countdown_seconds: 'Espera {countdown} segundos',
enter_screen_reader_mode: 'Activar modo lector de pantalla',
generated_by_docuseal: 'Generado por DocuSeal'
generated_by_docuseal: 'Generado por DocuSeal',
i_consent_to_sign_this_document_electronically: 'Consiento en firmar este documento electrónicamente'
}
const it = {
@ -325,7 +327,8 @@ const it = {
browser_privacy_settings_block_canvas: 'Le impostazioni sulla privacy del browser limitano l\'uso dell\'area di disegno. Utilizza un browser o dispositivo diverso oppure disattiva le impostazioni sulla privacy che bloccano il canvas per firmare.',
wait_countdown_seconds: 'Attendi {countdown} secondi',
enter_screen_reader_mode: 'Attiva modalità lettore di schermo',
generated_by_docuseal: 'Generato da DocuSeal'
generated_by_docuseal: 'Generato da DocuSeal',
i_consent_to_sign_this_document_electronically: 'Acconsento a firmare questo documento elettronicamente'
}
const de = {
@ -435,7 +438,8 @@ const de = {
browser_privacy_settings_block_canvas: 'Die Datenschutzeinstellungen Ihres Browsers schränken die Nutzung der Zeichenfläche ein. Bitte verwenden Sie einen anderen Browser oder ein anderes Gerät oder deaktivieren Sie die Datenschutzeinstellungen, die Canvas blockieren, um zu unterschreiben.',
wait_countdown_seconds: 'Bitte {countdown} Sekunden warten',
enter_screen_reader_mode: 'Screenreader-Modus aktivieren',
generated_by_docuseal: 'Erstellt von DocuSeal'
generated_by_docuseal: 'Erstellt von DocuSeal',
i_consent_to_sign_this_document_electronically: 'Ich stimme zu, dieses Dokument elektronisch zu unterzeichnen'
}
const fr = {
@ -545,7 +549,8 @@ const fr = {
browser_privacy_settings_block_canvas: 'Les paramètres de confidentialité de votre navigateur empêchent l\'utilisation du canevas de dessin. Veuillez utiliser un autre navigateur ou appareil, ou désactiver les paramètres de confidentialité qui bloquent le canevas pour signer.',
wait_countdown_seconds: 'Veuillez patienter {countdown} secondes',
enter_screen_reader_mode: 'Activer le mode lecteur d\'écran',
generated_by_docuseal: 'Généré par DocuSeal'
generated_by_docuseal: 'Généré par DocuSeal',
i_consent_to_sign_this_document_electronically: 'Je consens à signer ce document électroniquement'
}
const pl = {
@ -655,7 +660,8 @@ const pl = {
browser_privacy_settings_block_canvas: 'Ustawienia prywatności przeglądarki blokują użycie obszaru rysowania. Użyj innej przeglądarki lub urządzenia albo wyłącz ustawienia prywatności blokujące canvas, aby podpisać.',
wait_countdown_seconds: 'Poczekaj {countdown} sekund',
enter_screen_reader_mode: 'Włącz tryb czytnika ekranu',
generated_by_docuseal: 'Wygenerowano przez DocuSeal'
generated_by_docuseal: 'Wygenerowano przez DocuSeal',
i_consent_to_sign_this_document_electronically: 'Wyrażam zgodę na podpisanie tego dokumentu elektronicznie'
}
const uk = {
@ -765,7 +771,8 @@ const uk = {
browser_privacy_settings_block_canvas: 'Налаштування конфіденційності вашого браузера блокують використання полотна для малювання. Будь ласка, скористайтеся іншим браузером або пристроєм, або вимкніть налаштування конфіденційності, що блокують canvas, щоб підписати.',
wait_countdown_seconds: 'Зачекайте {countdown} секунд',
enter_screen_reader_mode: 'Увімкнути режим читання з екрану',
generated_by_docuseal: 'Створено за допомогою DocuSeal'
generated_by_docuseal: 'Створено за допомогою DocuSeal',
i_consent_to_sign_this_document_electronically: 'Я даю згоду на електронний підпис цього документа'
}
const cs = {
@ -875,7 +882,8 @@ const cs = {
browser_privacy_settings_block_canvas: 'Nastavení soukromí vašeho prohlížeče omezuje použití kreslicího plátna. Použijte prosím jiný prohlížeč nebo zařízení, nebo vypněte nastavení soukromí blokující canvas pro podepsání.',
wait_countdown_seconds: 'Počkejte {countdown} sekund',
enter_screen_reader_mode: 'Zapnout režim čtečky obrazovky',
generated_by_docuseal: 'Vygenerováno pomocí DocuSeal'
generated_by_docuseal: 'Vygenerováno pomocí DocuSeal',
i_consent_to_sign_this_document_electronically: 'Souhlasím s elektronickým podpisem tohoto dokumentu'
}
const pt = {
@ -985,7 +993,8 @@ const pt = {
browser_privacy_settings_block_canvas: 'As configurações de privacidade do seu navegador restringem o uso da área de desenho. Use um navegador ou dispositivo diferente, ou desative as configurações de privacidade que bloqueiam o canvas para assinar.',
wait_countdown_seconds: 'Aguarde {countdown} segundos',
enter_screen_reader_mode: 'Ativar modo leitor de tela',
generated_by_docuseal: 'Gerado pelo DocuSeal'
generated_by_docuseal: 'Gerado pelo DocuSeal',
i_consent_to_sign_this_document_electronically: 'Consinto em assinar este documento eletronicamente'
}
const he = {
@ -1095,7 +1104,8 @@ const he = {
browser_privacy_settings_block_canvas: 'הגדרות הפרטיות של הדפדפן שלך מגבילות את השימוש באזור הציור. אנא השתמש בדפדפן או מכשיר אחר, או בטל את הגדרות הפרטיות החוסמות canvas כדי לחתום.',
wait_countdown_seconds: 'המתן {countdown} שניות',
enter_screen_reader_mode: 'הפעל מצב קורא מסך',
generated_by_docuseal: 'נוצר על ידי DocuSeal'
generated_by_docuseal: 'נוצר על ידי DocuSeal',
i_consent_to_sign_this_document_electronically: 'אני מסכים/ה לחתום על מסמך זה באופן אלקטרוני'
}
const nl = {
@ -1205,7 +1215,8 @@ const nl = {
browser_privacy_settings_block_canvas: 'De privacyinstellingen van uw browser beperken het gebruik van het tekenveld. Gebruik een andere browser of ander apparaat, of schakel de privacyinstellingen uit die canvas blokkeren om te ondertekenen.',
wait_countdown_seconds: 'Wacht {countdown} seconden',
enter_screen_reader_mode: 'Schermlezer-modus inschakelen',
generated_by_docuseal: 'Gegenereerd door DocuSeal'
generated_by_docuseal: 'Gegenereerd door DocuSeal',
i_consent_to_sign_this_document_electronically: 'Ik geef toestemming om dit document elektronisch te ondertekenen'
}
const ar = {
@ -1315,7 +1326,8 @@ const ar = {
browser_privacy_settings_block_canvas: 'إعدادات الخصوصية في متصفحك تمنع استخدام لوحة الرسم. يرجى استخدام متصفح أو جهاز مختلف، أو تعطيل إعدادات الخصوصية التي تحظر canvas للتوقيع.',
wait_countdown_seconds: 'انتظر {countdown} ثانية',
enter_screen_reader_mode: 'تفعيل وضع قارئ الشاشة',
generated_by_docuseal: 'تم إنشاؤه بواسطة DocuSeal'
generated_by_docuseal: 'تم إنشاؤه بواسطة DocuSeal',
i_consent_to_sign_this_document_electronically: 'أوافق على توقيع هذا المستند إلكترونيًا'
}
const ko = {
@ -1425,7 +1437,8 @@ const ko = {
browser_privacy_settings_block_canvas: '브라우저 개인정보 보호 설정으로 인해 그리기 캔버스를 사용할 수 없습니다. 다른 브라우저나 기기를 사용하거나, 서명을 위해 캔버스를 차단하는 개인정보 보호 설정을 비활성화해 주세요.',
wait_countdown_seconds: '{countdown}초 기다리세요',
enter_screen_reader_mode: '스크린 리더 모드 활성화',
generated_by_docuseal: 'DocuSeal로 생성됨'
generated_by_docuseal: 'DocuSeal로 생성됨',
i_consent_to_sign_this_document_electronically: '본 문서에 전자적으로 서명하는 것에 동의합니다'
}
const ja = {
@ -1535,7 +1548,8 @@ const ja = {
browser_privacy_settings_block_canvas: 'ブラウザのプライバシー設定により、描画キャンバスの使用が制限されています。別のブラウザまたはデバイスを使用するか、署名するためにキャンバスをブロックするプライバシー設定を無効にしてください。',
wait_countdown_seconds: '{countdown} 秒お待ちください',
enter_screen_reader_mode: 'スクリーンリーダーモードを有効にする',
generated_by_docuseal: 'DocuSealで生成'
generated_by_docuseal: 'DocuSealで生成',
i_consent_to_sign_this_document_electronically: 'この文書に電子的に署名することに同意します'
}
const i18n = { en, es, it, de, fr, pl, uk, cs, pt, he, nl, ar, ko, ja }

@ -66,6 +66,7 @@ class AccountConfig < ApplicationRecord
BRAND_NAME_KEY = 'brand_name'
BRAND_NAME_FONT_KEY = 'brand_name_font'
IP_ALLOWLIST_KEY = 'ip_allowlist'
REQUIRE_CONSENT_KEY = 'require_consent'
BRAND_NAME_FONTS = [
'Inter',

@ -65,7 +65,8 @@ class SubmissionEvent < ApplicationRecord
complete_form: 'complete_form',
decline_form: 'decline_form',
delegate_form: 'delegate_form',
api_complete_form: 'api_complete_form'
api_complete_form: 'api_complete_form',
consent_given: 'consent_given'
}, scope: false
private

@ -97,6 +97,23 @@
</div>
<% end %>
<% end %>
<% account_config = AccountConfig.find_or_initialize_by(account: current_account, key: AccountConfig::REQUIRE_CONSENT_KEY) %>
<% if can?(:manage, account_config) %>
<%= form_for account_config, url: account_configs_path, method: :post do |f| %>
<%= f.hidden_field :key %>
<div class="flex items-center justify-between gap-4 py-2.5">
<div class="flex items-center space-x-1">
<span class="text-left"><%= t('require_consent_before_signing') %></span>
<span class="tooltip tooltip-top flex cursor-pointer" data-tip="<%= t('require_signers_to_explicitly_consent_to_signing_the_document_electronically_before_they_can_submit') %>">
<%= svg_icon('info_circle', class: 'hidden md:inline-block w-4 h-4 shrink-0') %>
</span>
</div>
<submit-form data-on="change" class="flex">
<%= f.check_box :value, class: 'toggle', checked: account_config.value == true %>
</submit-form>
</div>
<% end %>
<% end %>
<% account_config = AccountConfig.find_or_initialize_by(account: current_account, key: AccountConfig::ALLOW_TYPED_SIGNATURE) %>
<% if can?(:manage, account_config) %>
<%= form_for account_config, url: account_configs_path, method: :post do |f| %>

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

@ -215,6 +215,8 @@ en: &en
force_2fa_with_authenticator_app: Force 2FA with Authenticator App
add_signature_id_to_the_documents: Add signature ID to the documents
require_signing_reason: Require signing reason
require_consent_before_signing: Require consent before signing
require_signers_to_explicitly_consent_to_signing_the_document_electronically_before_they_can_submit: Require signers to explicitly consent to signing the document electronically before they can submit
allow_typed_text_signatures: Allow typed text signatures
allow_to_resubmit_completed_forms: Allow to resubmit completed forms
allow_to_decline_documents: Allow to decline documents
@ -1280,6 +1282,8 @@ es: &es
force_2fa_with_authenticator_app: Forzar 2FA con la aplicación Authenticator
add_signature_id_to_the_documents: Agregar ID de firma a los documentos
require_signing_reason: Requerir motivo de firma
require_consent_before_signing: Requerir consentimiento antes de firmar
require_signers_to_explicitly_consent_to_signing_the_document_electronically_before_they_can_submit: Requerir que los firmantes consientan explícitamente firmar el documento electrónicamente antes de enviar
allow_typed_text_signatures: Permitir firmas de texto mecanografiadas
allow_to_resubmit_completed_forms: Permitir reenviar formularios completados
allow_to_decline_documents: Permitir rechazar documentos
@ -2338,6 +2342,8 @@ it: &it
force_2fa_with_authenticator_app: "Forzare 2FA con l'app Authenticator"
add_signature_id_to_the_documents: Aggiungere ID firma ai documenti
require_signing_reason: Richiedere il motivo della firma
require_consent_before_signing: Richiedere il consenso prima della firma
require_signers_to_explicitly_consent_to_signing_the_document_electronically_before_they_can_submit: Richiedere ai firmatari di acconsentire esplicitamente alla firma elettronica del documento prima dell'invio
allow_typed_text_signatures: Permettere firme con testo digitato
allow_to_resubmit_completed_forms: Permettere di reinviare i moduli completati
allow_to_decline_documents: Permettere di rifiutare i documenti
@ -3400,6 +3406,8 @@ fr: &fr
force_2fa_with_authenticator_app: Imposer la 2FA avec une application dauthentification
add_signature_id_to_the_documents: Ajouter lID de signature aux documents
require_signing_reason: Exiger un motif de signature
require_consent_before_signing: Exiger le consentement avant la signature
require_signers_to_explicitly_consent_to_signing_the_document_electronically_before_they_can_submit: Exiger des signataires qu'ils consentent explicitement à signer le document électroniquement avant de soumettre
allow_typed_text_signatures: Autoriser les signatures tapées
allow_to_resubmit_completed_forms: Autoriser la nouvelle soumission des formulaires complétés
allow_to_decline_documents: Autoriser le refus de documents
@ -4455,6 +4463,8 @@ pt: &pt
force_2fa_with_authenticator_app: Forçar 2FA com aplicativo Authenticator
add_signature_id_to_the_documents: Adicionar ID de assinatura aos documentos
require_signing_reason: Requerer motivo para assinatura
require_consent_before_signing: Exigir consentimento antes de assinar
require_signers_to_explicitly_consent_to_signing_the_document_electronically_before_they_can_submit: Exigir que os signatários consintam explicitamente em assinar o documento eletronicamente antes de enviar
allow_typed_text_signatures: Permitir assinaturas digitadas
allow_to_resubmit_completed_forms: Permitir reenviar formulários concluídos
allow_to_decline_documents: Permitir recusar documentos
@ -5513,6 +5523,8 @@ de: &de
force_2fa_with_authenticator_app: 2FA mit Authenticator-App erzwingen
add_signature_id_to_the_documents: Signatur-ID zu den Dokumenten hinzufügen
require_signing_reason: Grund für die Signatur erforderlich
require_consent_before_signing: Zustimmung vor dem Unterzeichnen erforderlich
require_signers_to_explicitly_consent_to_signing_the_document_electronically_before_they_can_submit: Unterzeichner müssen vor dem Absenden ausdrücklich der elektronischen Unterzeichnung des Dokuments zustimmen
allow_typed_text_signatures: Getippte Unterschriften zulassen
allow_to_resubmit_completed_forms: Erneutes Einreichen abgeschlossener Formulare zulassen
allow_to_decline_documents: Ablehnen von Dokumenten erlauben
@ -6488,6 +6500,8 @@ pl:
ip_allowlist: Lista dozwolonych adresów IP
restrict_access_to_your_account_by_ip_address_enter_one_ip_or_cidr_range_per_line: Ogranicz dostęp do konta według adresu IP. Wprowadź jeden adres IP lub zakres CIDR w każdym wierszu.
access_denied_ip_not_allowed: Odmowa dostępu. Twój adres IP nie znajduje się na liście dozwolonych.
require_consent_before_signing: Wymagaj zgody przed podpisaniem
require_signers_to_explicitly_consent_to_signing_the_document_electronically_before_they_can_submit: Wymagaj od podpisujących wyraźnej zgody na elektroniczne podpisanie dokumentu przed wysłaniem
uk:
require_phone_2fa_to_open: Вимагати двофакторну автентифікацію через телефон для відкриття
@ -6593,6 +6607,8 @@ uk:
ip_allowlist: Список дозволених IP-адрес
restrict_access_to_your_account_by_ip_address_enter_one_ip_or_cidr_range_per_line: Обмежте доступ до облікового запису за IP-адресою. Введіть одну IP-адресу або діапазон CIDR на кожному рядку.
access_denied_ip_not_allowed: Доступ заборонено. Ваша IP-адреса не в списку дозволених.
require_consent_before_signing: Вимагати згоду перед підписанням
require_signers_to_explicitly_consent_to_signing_the_document_electronically_before_they_can_submit: Вимагати від підписантів явної згоди на електронний підпис документа перед відправленням
cs:
require_phone_2fa_to_open: Vyžadovat otevření pomocí telefonního 2FA
@ -6698,6 +6714,8 @@ cs:
ip_allowlist: Seznam povolených IP adres
restrict_access_to_your_account_by_ip_address_enter_one_ip_or_cidr_range_per_line: Omezte přístup k účtu podle IP adresy. Zadejte jednu IP adresu nebo rozsah CIDR na každý řádek.
access_denied_ip_not_allowed: Přístup odepřen. Vaše IP adresa není na seznamu povolených.
require_consent_before_signing: Vyžadovat souhlas před podpisem
require_signers_to_explicitly_consent_to_signing_the_document_electronically_before_they_can_submit: Vyžadovat od podepisujících výslovný souhlas s elektronickým podpisem dokumentu před odesláním
he:
require_phone_2fa_to_open: דרוש אימות דו-שלבי באמצעות טלפון לפתיחה
@ -6803,6 +6821,8 @@ he:
ip_allowlist: רשימת IP מורשים
restrict_access_to_your_account_by_ip_address_enter_one_ip_or_cidr_range_per_line: הגבל גישה לחשבון לפי כתובת IP. הזן כתובת IP אחת או טווח CIDR בכל שורה.
access_denied_ip_not_allowed: הגישה נדחתה. כתובת ה-IP שלך אינה ברשימת המורשים.
require_consent_before_signing: דרוש הסכמה לפני החתימה
require_signers_to_explicitly_consent_to_signing_the_document_electronically_before_they_can_submit: דרוש מהחותמים להסכים במפורש לחתום על המסמך באופן אלקטרוני לפני השליחה
nl: &nl
knowledge_based_authentication: Kennisgebaseerde authenticatie
@ -6992,6 +7012,8 @@ nl: &nl
force_2fa_with_authenticator_app: 2FA afdwingen met authenticator-app
add_signature_id_to_the_documents: Handtekening-ID toevoegen aan de documenten
require_signing_reason: Reden voor ondertekening verplicht stellen
require_consent_before_signing: Toestemming vereist voor ondertekening
require_signers_to_explicitly_consent_to_signing_the_document_electronically_before_they_can_submit: Ondertekenaars moeten expliciet toestemming geven voor het elektronisch ondertekenen van het document voordat ze kunnen indienen
allow_typed_text_signatures: Handtekeningen met getypte tekst toestaan
allow_to_resubmit_completed_forms: Opnieuw indienen van voltooide formulieren toestaan
allow_to_decline_documents: Weigeren van documenten toestaan
@ -7963,6 +7985,8 @@ ar:
ip_allowlist: قائمة عناوين IP المسموح بها
restrict_access_to_your_account_by_ip_address_enter_one_ip_or_cidr_range_per_line: تقييد الوصول إلى حسابك حسب عنوان IP. أدخل عنوان IP واحد أو نطاق CIDR في كل سطر.
access_denied_ip_not_allowed: تم رفض الوصول. عنوان IP الخاص بك غير مسموح به.
require_consent_before_signing: طلب الموافقة قبل التوقيع
require_signers_to_explicitly_consent_to_signing_the_document_electronically_before_they_can_submit: يجب على الموقعين الموافقة صراحة على توقيع المستند إلكترونيًا قبل الإرسال
ko:
require_phone_2fa_to_open: 휴대폰 2FA를 열 때 요구함
@ -8068,6 +8092,8 @@ ko:
ip_allowlist: IP 허용 목록
restrict_access_to_your_account_by_ip_address_enter_one_ip_or_cidr_range_per_line: IP 주소별로 계정 접근을 제한합니다. 한 줄에 하나의 IP 주소 또는 CIDR 범위를 입력하세요.
access_denied_ip_not_allowed: 접근이 거부되었습니다. 귀하의 IP 주소가 허용 목록에 없습니다.
require_consent_before_signing: 서명 전 동의 필요
require_signers_to_explicitly_consent_to_signing_the_document_electronically_before_they_can_submit: 서명자가 제출 전에 전자 서명에 명시적으로 동의해야 합니다
ja:
require_phone_2fa_to_open: 電話による2段階認証が必要です
@ -8173,6 +8199,8 @@ ja:
ip_allowlist: IP許可リスト
restrict_access_to_your_account_by_ip_address_enter_one_ip_or_cidr_range_per_line: IPアドレスによるアカウントへのアクセスを制限します。1行に1つのIPアドレスまたはCIDR範囲を入力してください。
access_denied_ip_not_allowed: アクセスが拒否されました。お使いのIPアドレスは許可リストにありません。
require_consent_before_signing: 署名前に同意が必要
require_signers_to_explicitly_consent_to_signing_the_document_electronically_before_they_can_submit: 署名者は送信前に電子署名に明示的に同意する必要があります
en-US:
<<: *en

@ -28,6 +28,7 @@ Rails.application.routes.draw do
resources :attachments, only: %i[create]
resources :submitter_email_clicks, only: %i[create]
resources :submitter_form_views, only: %i[create]
resources :submitter_consents, only: %i[create]
resources :submitters, only: %i[index show update]
resources :submissions, only: %i[index show create destroy] do
resources :documents, only: %i[index], controller: 'submission_documents'

@ -18,6 +18,7 @@ module Submitters
AccountConfig::WITH_SUBMITTER_TIMEZONE_KEY,
AccountConfig::WITH_TIMESTAMP_SECONDS_KEY,
AccountConfig::WITH_SIGNATURE_ID_REASON_KEY,
AccountConfig::REQUIRE_CONSENT_KEY,
*(Docuseal.multitenant? ? [] : [AccountConfig::POLICY_LINKS_KEY])].freeze
module_function
@ -42,12 +43,14 @@ module Submitters
with_signature_id_reason = find_safe_value(configs, AccountConfig::WITH_SIGNATURE_ID_REASON_KEY) != false
with_field_labels = find_safe_value(configs, AccountConfig::WITH_FIELD_LABELS_KEY) != false
policy_links = find_safe_value(configs, AccountConfig::POLICY_LINKS_KEY)
require_consent = find_safe_value(configs, AccountConfig::REQUIRE_CONSENT_KEY) == true
attrs = { completed_button:, with_typed_signature:, with_confetti:,
reuse_signature:, with_decline:, with_delegate:, with_partial_download:,
policy_links:, enforce_signing_order:, completed_message:,
require_signing_reason:, prefill_signature:, with_submitter_timezone:,
with_signature_id_reason:, with_signature_id:, with_field_labels:, with_timestamp_seconds: }
with_signature_id_reason:, with_signature_id:, with_field_labels:, with_timestamp_seconds:,
require_consent: }
keys.each do |key|
attrs[key.to_sym] = configs.find { |e| e.key == key.to_s }&.value

Loading…
Cancel
Save