From 48a8973910dacb4ab93d7470d9b5a2b8b51666ad Mon Sep 17 00:00:00 2001 From: Pete Matsyburka Date: Tue, 16 Dec 2025 11:38:37 +0200 Subject: [PATCH 01/10] add with field labels --- app/javascript/form.js | 1 + app/javascript/submission_form/form.vue | 7 ++++++- app/models/account_config.rb | 1 + app/views/submit_form/_submission_form.html.erb | 2 +- lib/submitters/form_configs.rb | 4 +++- 5 files changed, 12 insertions(+), 3 deletions(-) diff --git a/app/javascript/form.js b/app/javascript/form.js index b8253f86..1fbd54c8 100644 --- a/app/javascript/form.js +++ b/app/javascript/form.js @@ -37,6 +37,7 @@ safeRegisterElement('submission-form', class extends HTMLElement { withSignatureId: this.dataset.withSignatureId === 'true', requireSigningReason: this.dataset.requireSigningReason === 'true', withConfetti: this.dataset.withConfetti !== 'false', + withFieldLabels: this.dataset.withFieldLabels !== 'false', withDisclosure: this.dataset.withDisclosure === 'true', reuseSignature: this.dataset.reuseSignature !== 'false', withTypedSignature: this.dataset.withTypedSignature !== 'false', diff --git a/app/javascript/submission_form/form.vue b/app/javascript/submission_form/form.vue index 66b04b64..b793d6ae 100644 --- a/app/javascript/submission_form/form.vue +++ b/app/javascript/submission_form/form.vue @@ -8,7 +8,7 @@ :scroll-el="scrollEl" :with-signature-id="withSignatureId" :attachments-index="attachmentsIndex" - :with-label="!isAnonymousChecboxes && showFieldNames" + :with-label="withFieldLabels && !isAnonymousChecboxes && showFieldNames" :current-step="currentStepFields" :scroll-padding="scrollPadding" @focus-step="[saveStep(), currentField.type !== 'checkbox' ? isFormVisible = true : '', goToStep($event, false, true)]" @@ -737,6 +737,11 @@ export default { required: false, default: true }, + withFieldLabels: { + type: Boolean, + required: false, + default: true + }, withConfetti: { type: Boolean, required: false, diff --git a/app/models/account_config.rb b/app/models/account_config.rb index c2893360..ab8c9049 100644 --- a/app/models/account_config.rb +++ b/app/models/account_config.rb @@ -49,6 +49,7 @@ class AccountConfig < ApplicationRecord WITH_SUBMITTER_TIMEZONE_KEY = 'with_submitter_timezone' REQUIRE_SIGNING_REASON_KEY = 'require_signing_reason' REUSE_SIGNATURE_KEY = 'reuse_signature' + WITH_FIELD_LABELS_KEY = 'with_field_labels' COMBINE_PDF_RESULT_KEY = 'combine_pdf_result_key' DOCUMENT_FILENAME_FORMAT_KEY = 'document_filename_format' POLICY_LINKS_KEY = 'policy_links' diff --git a/app/views/submit_form/_submission_form.html.erb b/app/views/submit_form/_submission_form.html.erb index bccf174b..0c13d8c7 100644 --- a/app/views/submit_form/_submission_form.html.erb +++ b/app/views/submit_form/_submission_form.html.erb @@ -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 %> - + diff --git a/lib/submitters/form_configs.rb b/lib/submitters/form_configs.rb index 84c5fc65..38d18993 100644 --- a/lib/submitters/form_configs.rb +++ b/lib/submitters/form_configs.rb @@ -11,6 +11,7 @@ module Submitters AccountConfig::ENFORCE_SIGNING_ORDER_KEY, AccountConfig::REQUIRE_SIGNING_REASON_KEY, AccountConfig::REUSE_SIGNATURE_KEY, + AccountConfig::WITH_FIELD_LABELS_KEY, AccountConfig::ALLOW_TO_PARTIAL_DOWNLOAD_KEY, AccountConfig::ALLOW_TYPED_SIGNATURE, AccountConfig::WITH_SUBMITTER_TIMEZONE_KEY, @@ -35,13 +36,14 @@ module Submitters enforce_signing_order = find_safe_value(configs, AccountConfig::ENFORCE_SIGNING_ORDER_KEY) == true with_submitter_timezone = find_safe_value(configs, AccountConfig::WITH_SUBMITTER_TIMEZONE_KEY) == true 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) attrs = { completed_button:, with_typed_signature:, with_confetti:, reuse_signature:, with_decline:, 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_signature_id_reason:, with_signature_id:, with_field_labels: } keys.each do |key| attrs[key.to_sym] = configs.find { |e| e.key == key.to_s }&.value From 881d1897151ac1838ac1882f12bf294cc7ae001e Mon Sep 17 00:00:00 2001 From: Pete Matsyburka Date: Tue, 16 Dec 2025 12:34:18 +0200 Subject: [PATCH 02/10] fix phone --- app/javascript/submission_form/phone_step.vue | 2 +- app/views/submissions/_detailed_form.html.erb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/javascript/submission_form/phone_step.vue b/app/javascript/submission_form/phone_step.vue index d80ac512..b854c384 100644 --- a/app/javascript/submission_form/phone_step.vue +++ b/app/javascript/submission_form/phone_step.vue @@ -100,7 +100,7 @@ "> - <%= tag.input type: 'tel', pattern: '^\+[0-9\s\-]+$', autocomplete: 'off', class: 'base-input !h-10 mt-1.5 w-full', placeholder: local_assigns[:require_phone_2fa] == true ? t(:phone) : "#{t('phone')} (#{t('optional')})", id: "detailed_phone_#{item['uuid']}", required: local_assigns[:require_phone_2fa] == true %> + <%= tag.input type: 'tel', pattern: '^\+[0-9\s\-]+$', name: 'submission[1][submitters][][phone]', autocomplete: 'off', class: 'base-input !h-10 mt-1.5 w-full', placeholder: local_assigns[:require_phone_2fa] == true ? t(:phone) : "#{t('phone')} (#{t('optional')})", id: "detailed_phone_#{item['uuid']}", required: local_assigns[:require_phone_2fa] == true %> From 178809dad7aab1a786fe3bf7dcd19f3a6ce9f2ed Mon Sep 17 00:00:00 2001 From: Pete Matsyburka Date: Tue, 16 Dec 2025 21:54:36 +0200 Subject: [PATCH 03/10] fix field render --- lib/submissions/generate_result_attachments.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/submissions/generate_result_attachments.rb b/lib/submissions/generate_result_attachments.rb index 12e74676..893202b1 100644 --- a/lib/submissions/generate_result_attachments.rb +++ b/lib/submissions/generate_result_attachments.rb @@ -648,7 +648,7 @@ module Submissions text_params = { font:, fill_color:, font_size: } text_params[:line_height] = text_params[:font_size] * (FONTS_LINE_HEIGHT[font_name] || 1) - text = HexaPDF::Layout::TextFragment.create(value, **text_params) + text = HexaPDF::Layout::TextFragment.create(value.tr("\u00A0", ' '), **text_params) lines = layouter.fit([text], area['w'] * width, height).lines box_height = lines.sum(&:height) From abf17eb09f34d452485a0f20523cae888486b18a Mon Sep 17 00:00:00 2001 From: Pete Matsyburka Date: Thu, 18 Dec 2025 12:12:40 +0200 Subject: [PATCH 04/10] localhost list --- lib/send_webhook_request.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/send_webhook_request.rb b/lib/send_webhook_request.rb index f9845e16..e6106756 100644 --- a/lib/send_webhook_request.rb +++ b/lib/send_webhook_request.rb @@ -3,7 +3,7 @@ module SendWebhookRequest USER_AGENT = 'DocuSeal.com Webhook' - LOCALHOSTS = %w[0.0.0.0 127.0.0.1 localhost].freeze + LOCALHOSTS = DownloadUtils::LOCALHOSTS MANUAL_ATTEMPT = 99_999 AUTOMATED_RETRY_RANGE = 1..(MANUAL_ATTEMPT - 1) From 964b5d4e74c197f43170f22686257676d789f67b Mon Sep 17 00:00:00 2001 From: Pete Matsyburka Date: Thu, 18 Dec 2025 12:45:33 +0200 Subject: [PATCH 05/10] validate webhook --- app/jobs/send_test_webhook_request_job.rb | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/app/jobs/send_test_webhook_request_job.rb b/app/jobs/send_test_webhook_request_job.rb index 682a3aac..158363ec 100644 --- a/app/jobs/send_test_webhook_request_job.rb +++ b/app/jobs/send_test_webhook_request_job.rb @@ -7,6 +7,9 @@ class SendTestWebhookRequestJob USER_AGENT = 'DocuSeal.com Webhook' + HttpsError = Class.new(StandardError) + LocalhostError = Class.new(StandardError) + def perform(params = {}) submitter = Submitter.find_by(id: params['submitter_id']) @@ -16,6 +19,17 @@ class SendTestWebhookRequestJob return unless webhook_url + if Docuseal.multitenant? + uri = begin + URI(webhook_url.url) + rescue URI::Error + Addressable::URI.parse(webhook_url.url).normalize + end + + raise HttpsError, 'Only HTTPS is allowed.' if uri.scheme != 'https' + raise LocalhostError, "Can't send to localhost." if uri.host.in?(SendWebhookRequest::LOCALHOSTS) + end + Faraday.post(webhook_url.url, { event_type: 'form.completed', From d525e0597a5a811fe224b592b9692260213e2fca Mon Sep 17 00:00:00 2001 From: Pete Matsyburka Date: Fri, 19 Dec 2025 11:55:25 +0200 Subject: [PATCH 06/10] recipient fields config --- app/controllers/submissions_controller.rb | 5 ++-- app/models/account_config.rb | 1 + app/views/submissions/_detailed_form.html.erb | 19 +++++++------- app/views/submissions/new.html.erb | 5 ++-- lib/accounts.rb | 4 +++ lib/submissions.rb | 4 +-- lib/submissions/create_from_submitters.rb | 14 +++++----- lib/submissions/normalize_param_utils.rb | 15 ++++++----- lib/submitters/normalize_values.rb | 26 ++++++++++++++++--- 9 files changed, 63 insertions(+), 30 deletions(-) diff --git a/app/controllers/submissions_controller.rb b/app/controllers/submissions_controller.rb index acb95259..f10cb1f0 100644 --- a/app/controllers/submissions_controller.rb +++ b/app/controllers/submissions_controller.rb @@ -53,14 +53,15 @@ class SubmissionsController < ApplicationController else submissions_attrs = submissions_params[:submission].to_h.values - submissions_attrs, = - Submissions::NormalizeParamUtils.normalize_submissions_params!(submissions_attrs, @template) + submissions_attrs, _, new_fields = + Submissions::NormalizeParamUtils.normalize_submissions_params!(submissions_attrs, @template, add_fields: true) Submissions.create_from_submitters(template: @template, user: current_user, source: :invite, submitters_order: params[:preserve_order] == '1' ? 'preserved' : 'random', submissions_attrs:, + new_fields:, params: params.merge('send_completed_email' => true)) end diff --git a/app/models/account_config.rb b/app/models/account_config.rb index ab8c9049..3964e973 100644 --- a/app/models/account_config.rb +++ b/app/models/account_config.rb @@ -45,6 +45,7 @@ class AccountConfig < ApplicationRecord WITH_SIGNATURE_ID = 'with_signature_id' WITH_FILE_LINKS_KEY = 'with_file_links' WITH_SIGNATURE_ID_REASON_KEY = 'with_signature_id_reason' + RECIPIENT_FORM_FIELDS_KEY = 'recipient_form_fields' WITH_AUDIT_VALUES_KEY = 'with_audit_values' WITH_SUBMITTER_TIMEZONE_KEY = 'with_submitter_timezone' REQUIRE_SIGNING_REASON_KEY = 'require_signing_reason' diff --git a/app/views/submissions/_detailed_form.html.erb b/app/views/submissions/_detailed_form.html.erb index 3131a644..31b969bc 100644 --- a/app/views/submissions/_detailed_form.html.erb +++ b/app/views/submissions/_detailed_form.html.erb @@ -12,7 +12,8 @@
<% submitters.each_with_index do |item, index| %> - <% prefillable_fields = local_assigns[:prefillable_fields].to_a.select { |f| f['submitter_uuid'] == item['uuid'] } %> + <% prefillable_fields = local_assigns[:prefillable_fields].to_a.select { |f| f['submitter_uuid'] == item['uuid'] }.presence %> + <% prefillable_fields ||= local_assigns[:recipient_form_fields].presence %> <% if submitters.size > 1 %>
- <% if params[:selfsign].blank? && local_assigns[:prefillable_fields].blank? %> + <% if params[:selfsign].blank? && local_assigns[:prefillable_fields].blank? && local_assigns[:recipient_form_fields].blank? %> <%= svg_icon('user_plus', class: 'w-4 h-4 stroke-2') %> <%= t('add_new') %> diff --git a/app/views/submissions/new.html.erb b/app/views/submissions/new.html.erb index e70b181f..46088dbb 100644 --- a/app/views/submissions/new.html.erb +++ b/app/views/submissions/new.html.erb @@ -1,7 +1,8 @@ <% require_phone_2fa = @template.preferences['require_phone_2fa'] == true %> <% require_email_2fa = @template.preferences['require_email_2fa'] == true %> <% prefillable_fields = @template.fields.select { |f| f['prefillable'] } %> -<% only_detailed = require_phone_2fa || require_email_2fa || prefillable_fields.present? %> +<% recipient_form_fields = Accounts.load_recipient_form_fields(current_account) if prefillable_fields.blank? %> +<% only_detailed = require_phone_2fa || require_email_2fa || prefillable_fields.present? || recipient_form_fields.present? %> <%= render 'shared/turbo_modal_large', title: params[:selfsign] ? t('add_recipients') : t('add_new_recipients') do %> <% options = [only_detailed ? nil : [t('via_email'), 'email'], only_detailed ? nil : [t('via_phone'), 'phone'], [t('detailed'), 'detailed'], [t('upload_list'), 'list']].compact %> @@ -26,7 +27,7 @@ <% end %>
- <%= render 'detailed_form', template: @template, require_phone_2fa:, require_email_2fa:, prefillable_fields: %> + <%= render 'detailed_form', template: @template, require_phone_2fa:, require_email_2fa:, prefillable_fields:, recipient_form_fields: %>