From 9944a9b1a55fd75b9b874170dc76050f0c3db33c Mon Sep 17 00:00:00 2001 From: Pete Matsyburka Date: Mon, 7 Sep 2026 17:43:42 +0300 Subject: [PATCH 01/15] adjust bind address --- config/dotenv.rb | 2 +- lib/puma/plugin/redis_server.rb | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/config/dotenv.rb b/config/dotenv.rb index cb0f8e7c..0a6aedf0 100644 --- a/config/dotenv.rb +++ b/config/dotenv.rb @@ -105,6 +105,6 @@ if ENV['REDIS_URL'].to_s.empty? redis_password = Digest::SHA1.hexdigest("redis#{ENV.fetch('SECRET_KEY_BASE', '')}") - ENV['REDIS_URL'] = "redis://default:#{redis_password}@0.0.0.0:6379/0" + ENV['REDIS_URL'] = "redis://default:#{redis_password}@127.0.0.1:16379/0" ENV['LOCAL_REDIS_URL'] = ENV.fetch('REDIS_URL', nil) end diff --git a/lib/puma/plugin/redis_server.rb b/lib/puma/plugin/redis_server.rb index cad448f9..e7bf7f28 100644 --- a/lib/puma/plugin/redis_server.rb +++ b/lib/puma/plugin/redis_server.rb @@ -54,6 +54,7 @@ Puma::Plugin.create do Dir.chdir(ENV.fetch('WORKDIR', nil)) unless ENV['WORKDIR'].to_s.empty? exec('redis-server', '--requirepass', Digest::SHA1.hexdigest("redis#{ENV.fetch('SECRET_KEY_BASE', '')}"), + '--bind', '127.0.0.1', '--port', '16379', '--loglevel', 'warning') end end From 307036eae64516a56489cfd5ac8aa6dde13edeae Mon Sep 17 00:00:00 2001 From: Pete Matsyburka Date: Tue, 8 Sep 2026 09:12:47 +0300 Subject: [PATCH 02/15] fix invite party --- app/controllers/submit_form_invite_controller.rb | 2 +- lib/submitters/submit_values.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/submit_form_invite_controller.rb b/app/controllers/submit_form_invite_controller.rb index 8a231e1c..0976d028 100644 --- a/app/controllers/submit_form_invite_controller.rb +++ b/app/controllers/submit_form_invite_controller.rb @@ -23,7 +23,7 @@ class SubmitFormInviteController < ApplicationController @submitter.submission.submitters.create!(uuid: attrs[:uuid], email:, account_id: @submitter.account_id) - SubmissionEvents.create_with_tracking_data(@submitter, 'invite_party', request, { uuid: @submitter.uuid }) + SubmissionEvents.create_with_tracking_data(@submitter, 'invite_party', request, { uuid: attrs[:uuid] }) end @submitter.submission.update!(submitters_order: :preserved) diff --git a/lib/submitters/submit_values.rb b/lib/submitters/submit_values.rb index 3686b71c..e4ef2030 100644 --- a/lib/submitters/submit_values.rb +++ b/lib/submitters/submit_values.rb @@ -490,7 +490,7 @@ module Submitters submission.submitters.create!(uuid: s['uuid'], email:, phone:, account_id: submitter.account_id) - SubmissionEvents.create_with_tracking_data(submitter, 'invite_party', request, { uuid: submitter.uuid }) + SubmissionEvents.create_with_tracking_data(submitter, 'invite_party', request, { uuid: s['uuid'] }) is_invited = true end From 781da32a07eba544db669d27cff30176fc074462 Mon Sep 17 00:00:00 2001 From: Pete Matsyburka Date: Tue, 8 Sep 2026 13:53:35 +0300 Subject: [PATCH 03/15] replace filename slashes --- app/controllers/templates_uploads_controller.rb | 2 +- lib/submissions/generate_audit_trail.rb | 2 +- lib/submissions/generate_combined_attachment.rb | 2 +- lib/submissions/generate_preview_attachments.rb | 4 ++-- lib/submissions/generate_result_attachments.rb | 2 +- lib/submitters.rb | 2 +- lib/templates/create_attachments.rb | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/controllers/templates_uploads_controller.rb b/app/controllers/templates_uploads_controller.rb index d389bfe3..8ba06980 100644 --- a/app/controllers/templates_uploads_controller.rb +++ b/app/controllers/templates_uploads_controller.rb @@ -63,7 +63,7 @@ class TemplatesUploadsController < ApplicationController tempfile.write(DownloadUtils.call(params[:url], validate: true).body) tempfile.rewind - filename = URI.decode_www_form_component(params[:filename]) if params[:filename].present? + filename = URI.decode_www_form_component(params[:filename]).tr('/', '-') if params[:filename].present? filename ||= File.basename(URI.decode_www_form_component(params[:url])) file = ActionDispatch::Http::UploadedFile.new( diff --git a/lib/submissions/generate_audit_trail.rb b/lib/submissions/generate_audit_trail.rb index 1e7bc33d..17733511 100644 --- a/lib/submissions/generate_audit_trail.rb +++ b/lib/submissions/generate_audit_trail.rb @@ -62,7 +62,7 @@ module Submissions ActiveStorage::Attachment.create!( blob: ActiveStorage::Blob.create_and_upload!( io: io.tap(&:rewind), filename: "#{I18n.t('audit_log')} - " \ - "#{submission.name || submission.template&.name}.pdf" + "#{submission.name || submission.template&.name}.pdf".tr('/', '-') ), name: 'audit_trail', record: submission diff --git a/lib/submissions/generate_combined_attachment.rb b/lib/submissions/generate_combined_attachment.rb index d8a60bf9..45aadb1c 100644 --- a/lib/submissions/generate_combined_attachment.rb +++ b/lib/submissions/generate_combined_attachment.rb @@ -37,7 +37,7 @@ module Submissions ActiveStorage::Attachment.create!( blob: ActiveStorage::Blob.create_and_upload!( - io: io.tap(&:rewind), filename: "#{submission.name || submission.template&.name}.pdf" + io: io.tap(&:rewind), filename: "#{submission.name || submission.template&.name}.pdf".tr('/', '-') ), name: with_audit ? 'combined_document' : 'merged_document', record: submission diff --git a/lib/submissions/generate_preview_attachments.rb b/lib/submissions/generate_preview_attachments.rb index eb9011db..a35d18f0 100644 --- a/lib/submissions/generate_preview_attachments.rb +++ b/lib/submissions/generate_preview_attachments.rb @@ -71,7 +71,7 @@ module Submissions submission:, values_hash:, name: 'preview_merged_document', - filename: "#{submission.name || template&.name}.pdf" + filename: "#{submission.name || template&.name}.pdf".tr('/', '-') ) ApplicationRecord.no_touching { attachment.save! } @@ -115,7 +115,7 @@ module Submissions submitter:, uuid: GenerateResultAttachments.images_pdf_uuid(original_documents.select(&:image?)), values_hash:, - filename: "#{submission.name || template&.name}.pdf" + filename: "#{submission.name || template&.name}.pdf".tr('/', '-') ) ApplicationRecord.no_touching do diff --git a/lib/submissions/generate_result_attachments.rb b/lib/submissions/generate_result_attachments.rb index c221d38a..44a047de 100644 --- a/lib/submissions/generate_result_attachments.rb +++ b/lib/submissions/generate_result_attachments.rb @@ -814,7 +814,7 @@ module Submissions end ActiveStorage::Attachment.new( - blob: ActiveStorage::Blob.create_and_upload!(io: io.tap(&:rewind), filename: "#{name}.pdf"), + blob: ActiveStorage::Blob.create_and_upload!(io: io.tap(&:rewind), filename: "#{name}.pdf".tr('/', '-')), metadata: { original_uuid: uuid, analyzed: true, sha256: Base64.urlsafe_encode64(Digest::SHA256.digest(io.string)) }, diff --git a/lib/submitters.rb b/lib/submitters.rb index 3ea1aa07..091623c2 100644 --- a/lib/submitters.rb +++ b/lib/submitters.rb @@ -238,7 +238,7 @@ module Submitters I18n.l(completed_at.in_time_zone(submitter.account.timezone), format: :short) end - "#{filename}.#{blob.filename.extension}" + "#{filename}.#{blob.filename.extension}".tr('/', '-') end def send_shared_link_email_verification_code(submitter, request:) diff --git a/lib/templates/create_attachments.rb b/lib/templates/create_attachments.rb index f5b47fd9..6f8f695d 100644 --- a/lib/templates/create_attachments.rb +++ b/lib/templates/create_attachments.rb @@ -76,7 +76,7 @@ module Templates blob = ActiveStorage::Blob.create_and_upload!( io: StringIO.new(document_data), - filename: file.original_filename, + filename: file.original_filename.tr('/', '-'), metadata: { **metadata, identified: file.content_type == PDF_CONTENT_TYPE, From 4b00c896e41ae2da05b0affb27d70c9efa9dff9b Mon Sep 17 00:00:00 2001 From: Pete Matsyburka Date: Tue, 8 Sep 2026 18:52:12 +0300 Subject: [PATCH 04/15] reset signature on delegate --- .../submit_form_delegate_controller.rb | 3 ++- lib/submitters.rb | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/app/controllers/submit_form_delegate_controller.rb b/app/controllers/submit_form_delegate_controller.rb index 6448a750..4a702d65 100644 --- a/app/controllers/submit_form_delegate_controller.rb +++ b/app/controllers/submit_form_delegate_controller.rb @@ -32,7 +32,8 @@ class SubmitFormDelegateController < ApplicationController SubmissionEvents.create_with_tracking_data(@submitter, 'delegate_form', request, { old_email: @submitter.email, email: }) - @submitter.update!(email:, phone: nil, name: nil, slug: SecureRandom.base58(14)) + @submitter.update!(email:, phone: nil, name: nil, slug: SecureRandom.base58(14), + values: Submitters.fetch_values_without_signature(@submitter)) end SendSubmitterInvitationEmailJob.perform_async('submitter_id' => @submitter.id) diff --git a/lib/submitters.rb b/lib/submitters.rb index 091623c2..52be8ffc 100644 --- a/lib/submitters.rb +++ b/lib/submitters.rb @@ -165,6 +165,21 @@ module Submitters preferences end + def fetch_values_without_signature(submitter) + fields = submitter.submission.template_fields || submitter.template.fields + default_values = submitter.preferences['default_values'] || {} + + field_uuids = fields.filter_map do |field| + next if field['submitter_uuid'] != submitter.uuid + next unless field['type'].in?(%w[signature initials]) + next if default_values[field['uuid']].present? + + field['uuid'] + end + + submitter.values.except(*field_uuids) + end + def send_signature_requests(submitters, delay_seconds: nil) submitters.each_with_index do |submitter, index| next if submitter.email.blank? From b8c1691bd781d347787610a1b43d71b745c2dfc9 Mon Sep 17 00:00:00 2001 From: Pete Matsyburka Date: Wed, 9 Sep 2026 12:46:28 +0300 Subject: [PATCH 05/15] optimize xlsx export --- Gemfile | 3 ++- Gemfile.lock | 9 +++++---- config/initializers/autoload.rb | 2 +- lib/submissions/generate_export_files.rb | 19 ++++++++----------- 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/Gemfile b/Gemfile index e2d74068..99e771b9 100644 --- a/Gemfile +++ b/Gemfile @@ -36,7 +36,7 @@ gem 'rotp' gem 'rouge', require: false gem 'rqrcode', require: false gem 'ruby-vips' -gem 'rubyXL', require: false +gem 'rubyzip', require: false gem 'shakapacker' gem 'sidekiq' gem 'sqlite3', require: false @@ -45,6 +45,7 @@ gem 'trilogy', require: false gem 'turbo-rails' gem 'twitter_cldr', require: false gem 'tzinfo-data' +gem 'xlsxtream', require: false group :development, :test do gem 'better_html' diff --git a/Gemfile.lock b/Gemfile.lock index d320a892..5dc77e9f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -497,9 +497,6 @@ GEM ruby-vips (2.3.0) ffi (~> 1.12) logger - rubyXL (3.4.35) - nokogiri (>= 1.10.8) - rubyzip (>= 3.2.2) rubyzip (3.2.2) securerandom (0.4.1) semantic_range (3.1.1) @@ -575,10 +572,13 @@ GEM base64 websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) + xlsxtream (3.1.0) + zip_kit (>= 6.2, < 7) xpath (3.2.0) nokogiri (~> 1.8) yaml (0.4.0) zeitwerk (2.8.2) + zip_kit (6.3.4) PLATFORMS aarch64-linux @@ -638,7 +638,7 @@ DEPENDENCIES rubocop-rails rubocop-rspec ruby-vips - rubyXL + rubyzip shakapacker sidekiq simplecov @@ -650,6 +650,7 @@ DEPENDENCIES tzinfo-data web-console webmock + xlsxtream RUBY VERSION ruby 4.0.5 diff --git a/config/initializers/autoload.rb b/config/initializers/autoload.rb index 99dc4962..fd307b35 100644 --- a/config/initializers/autoload.rb +++ b/config/initializers/autoload.rb @@ -2,7 +2,7 @@ autoload :CSV, 'csv' autoload :CSVSafe, 'csv-safe' -autoload :RubyXL, 'rubyXL' +autoload :Xlsxtream, 'xlsxtream' autoload :Zip, 'zip' autoload :Numo, 'numo/narray' autoload :OnnxRuntime, 'onnxruntime' diff --git a/lib/submissions/generate_export_files.rb b/lib/submissions/generate_export_files.rb index f1096db2..57e751b4 100644 --- a/lib/submissions/generate_export_files.rb +++ b/lib/submissions/generate_export_files.rb @@ -19,22 +19,19 @@ module Submissions end def rows_to_xlsx(rows) - workbook = RubyXL::Workbook.new - worksheet = workbook[0] - worksheet.sheet_name = Time.current.to_date.to_s - headers = build_headers(rows) - headers.each_with_index do |column_name, column_index| - worksheet.add_cell(0, column_index, column_name) - end - rows.each.with_index(1) do |row, row_index| - extract_columns(row, headers).each_with_index do |value, column_index| - worksheet.add_cell(row_index, column_index, value) + io = StringIO.new + + Xlsxtream::Workbook.open(io) do |workbook| + workbook.write_worksheet(Time.current.to_date.to_s) do |sheet| + sheet << headers.to_a + + rows.each { |row| sheet << extract_columns(row, headers) } end end - workbook.stream.string + io.string end def rows_to_csv(rows) From a3828ec390df992b979807031ca3ce50a35dfadc Mon Sep 17 00:00:00 2001 From: Pete Matsyburka Date: Wed, 9 Sep 2026 13:35:20 +0300 Subject: [PATCH 06/15] xlsx write native boolean --- lib/submissions/generate_export_files.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/submissions/generate_export_files.rb b/lib/submissions/generate_export_files.rb index 57e751b4..ee115a77 100644 --- a/lib/submissions/generate_export_files.rb +++ b/lib/submissions/generate_export_files.rb @@ -147,8 +147,6 @@ module Submissions ActiveStorage::Blob.proxy_url(attachment.blob, expires_at:) if attachment end - elsif submitter_value == true || submitter_value == false - submitter_value.to_s else submitter_value end From ae7b0ce6080496872f451c587211181e3f14f0d6 Mon Sep 17 00:00:00 2001 From: Alex Turchyn Date: Tue, 25 Aug 2026 14:30:10 +0300 Subject: [PATCH 07/15] fix settings collapse spacing --- .../_documents_copy_email_form.html.erb | 2 +- .../_form_completed_button_form.html.erb | 2 +- .../_form_completed_message_form.html.erb | 2 +- .../personalization_settings/_form_policy_links_form.html.erb | 2 +- .../_signature_request_email_form.html.erb | 2 +- .../_submitter_completed_email_form.html.erb | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/views/personalization_settings/_documents_copy_email_form.html.erb b/app/views/personalization_settings/_documents_copy_email_form.html.erb index 4cc21d79..0f35fc69 100644 --- a/app/views/personalization_settings/_documents_copy_email_form.html.erb +++ b/app/views/personalization_settings/_documents_copy_email_form.html.erb @@ -6,7 +6,7 @@
- <%= form_for AccountConfigs.find_or_initialize_for_key(current_account, AccountConfig::SUBMITTER_DOCUMENTS_COPY_EMAIL_KEY), url: settings_personalization_path, method: :post, html: { autocomplete: 'off', class: 'space-y-4' } do |f| %> + <%= form_for AccountConfigs.find_or_initialize_for_key(current_account, AccountConfig::SUBMITTER_DOCUMENTS_COPY_EMAIL_KEY), url: settings_personalization_path, method: :post, html: { autocomplete: 'off', class: 'flex flex-col gap-4' } do |f| %> <%= f.hidden_field :key %> <%= f.fields_for :value, Struct.new(:subject, :body, :reply_to, :attach_audit_log, :attach_documents, :bcc_recipients, :enabled).new(*f.object.value.values_at('subject', 'body', 'reply_to', 'attach_audit_log', 'attach_documents', 'bcc_recipients', 'enabled')) do |ff| %>
diff --git a/app/views/personalization_settings/_form_completed_button_form.html.erb b/app/views/personalization_settings/_form_completed_button_form.html.erb index 65017a91..e8d27aed 100644 --- a/app/views/personalization_settings/_form_completed_button_form.html.erb +++ b/app/views/personalization_settings/_form_completed_button_form.html.erb @@ -6,7 +6,7 @@
- <%= form_for AccountConfigs.find_or_initialize_for_key(current_account, AccountConfig::FORM_COMPLETED_BUTTON_KEY), url: settings_personalization_path, method: :post, html: { autocomplete: 'off', class: 'space-y-4' } do |f| %> + <%= form_for AccountConfigs.find_or_initialize_for_key(current_account, AccountConfig::FORM_COMPLETED_BUTTON_KEY), url: settings_personalization_path, method: :post, html: { autocomplete: 'off', class: 'flex flex-col gap-4' } do |f| %> <%= f.hidden_field :key %> <%= f.fields_for :value, Struct.new(:title, :url).new(*(f.object.value || {}).values_at('title', 'url')) do |ff| %>
diff --git a/app/views/personalization_settings/_form_completed_message_form.html.erb b/app/views/personalization_settings/_form_completed_message_form.html.erb index 6030bffe..5f10da65 100644 --- a/app/views/personalization_settings/_form_completed_message_form.html.erb +++ b/app/views/personalization_settings/_form_completed_message_form.html.erb @@ -6,7 +6,7 @@
- <%= form_for AccountConfigs.find_or_initialize_for_key(current_account, AccountConfig::FORM_COMPLETED_MESSAGE_KEY), url: settings_personalization_path, method: :post, html: { autocomplete: 'off', class: 'space-y-4' } do |f| %> + <%= form_for AccountConfigs.find_or_initialize_for_key(current_account, AccountConfig::FORM_COMPLETED_MESSAGE_KEY), url: settings_personalization_path, method: :post, html: { autocomplete: 'off', class: 'flex flex-col gap-4' } do |f| %> <%= f.hidden_field :key %> <%= f.fields_for :value, Struct.new(:title, :body).new(*(f.object.value || {}).values_at('title', 'body')) do |ff| %>
diff --git a/app/views/personalization_settings/_form_policy_links_form.html.erb b/app/views/personalization_settings/_form_policy_links_form.html.erb index cabf1b83..ecf967e4 100644 --- a/app/views/personalization_settings/_form_policy_links_form.html.erb +++ b/app/views/personalization_settings/_form_policy_links_form.html.erb @@ -7,7 +7,7 @@
- <%= form_for AccountConfigs.find_or_initialize_for_key(current_account, AccountConfig::POLICY_LINKS_KEY), url: settings_personalization_path, method: :post, html: { autocomplete: 'off', class: 'space-y-4' } do |f| %> + <%= form_for AccountConfigs.find_or_initialize_for_key(current_account, AccountConfig::POLICY_LINKS_KEY), url: settings_personalization_path, method: :post, html: { autocomplete: 'off', class: 'flex flex-col gap-4' } do |f| %> <%= f.hidden_field :key %>
diff --git a/app/views/personalization_settings/_signature_request_email_form.html.erb b/app/views/personalization_settings/_signature_request_email_form.html.erb index 1ba5dad9..dc54826f 100644 --- a/app/views/personalization_settings/_signature_request_email_form.html.erb +++ b/app/views/personalization_settings/_signature_request_email_form.html.erb @@ -6,7 +6,7 @@
- <%= form_for AccountConfigs.find_or_initialize_for_key(current_account, AccountConfig::SUBMITTER_INVITATION_EMAIL_KEY), url: settings_personalization_path, method: :post, html: { autocomplete: 'off', class: 'space-y-4' } do |f| %> + <%= form_for AccountConfigs.find_or_initialize_for_key(current_account, AccountConfig::SUBMITTER_INVITATION_EMAIL_KEY), url: settings_personalization_path, method: :post, html: { autocomplete: 'off', class: 'flex flex-col gap-4' } do |f| %> <%= f.hidden_field :key %> <%= f.fields_for :value, Struct.new(:subject, :body, :reply_to).new(*f.object.value.values_at('subject', 'body', 'reply_to')) do |ff| %>
diff --git a/app/views/personalization_settings/_submitter_completed_email_form.html.erb b/app/views/personalization_settings/_submitter_completed_email_form.html.erb index afd13aa8..f80ff410 100644 --- a/app/views/personalization_settings/_submitter_completed_email_form.html.erb +++ b/app/views/personalization_settings/_submitter_completed_email_form.html.erb @@ -6,7 +6,7 @@
- <%= form_for AccountConfigs.find_or_initialize_for_key(current_account, AccountConfig::SUBMITTER_COMPLETED_EMAIL_KEY), url: settings_personalization_path, method: :post, html: { autocomplete: 'off', class: 'space-y-4' } do |f| %> + <%= form_for AccountConfigs.find_or_initialize_for_key(current_account, AccountConfig::SUBMITTER_COMPLETED_EMAIL_KEY), url: settings_personalization_path, method: :post, html: { autocomplete: 'off', class: 'flex flex-col gap-4' } do |f| %> <%= f.hidden_field :key %> <%= f.fields_for :value, Struct.new(:subject, :body, :attach_audit_log, :attach_documents).new(*f.object.value.values_at('subject', 'body', 'attach_audit_log', 'attach_documents')) do |ff| %>
From 1416cf72e8051a4a6c021f9d646edd663a1bcf98 Mon Sep 17 00:00:00 2001 From: Pete Matsyburka Date: Thu, 10 Sep 2026 15:29:27 +0300 Subject: [PATCH 08/15] sms retry 30 seconds --- app/javascript/submission_form/phone_step.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/javascript/submission_form/phone_step.vue b/app/javascript/submission_form/phone_step.vue index d3e95bed..c3e90fb3 100644 --- a/app/javascript/submission_form/phone_step.vue +++ b/app/javascript/submission_form/phone_step.vue @@ -261,7 +261,7 @@ export default { } }, resendCode () { - if (this.codeSentAt && Date.now() - this.codeSentAt < 15000) { + if (this.codeSentAt && Date.now() - this.codeSentAt < 30000) { this.startResendCodeCountdown() } else { this.isResendLoading = true @@ -274,7 +274,7 @@ export default { } }, startResendCodeCountdown () { - this.resendCodeCountdown = 15 - parseInt((Date.now() - this.codeSentAt) / 1000) + this.resendCodeCountdown = 30 - parseInt((Date.now() - this.codeSentAt) / 1000) this.interval = setInterval(() => { this.resendCodeCountdown-- From 660b24710ec375c3102cc2c28ee85332bf5b9682 Mon Sep 17 00:00:00 2001 From: Pete Matsyburka Date: Thu, 10 Sep 2026 10:22:06 +0300 Subject: [PATCH 09/15] do not email archived user --- app/controllers/submit_form_decline_controller.rb | 3 ++- app/jobs/process_submitter_completion_job.rb | 2 +- lib/users.rb | 4 ++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/controllers/submit_form_decline_controller.rb b/app/controllers/submit_form_decline_controller.rb index ca50e60c..139175cf 100644 --- a/app/controllers/submit_form_decline_controller.rb +++ b/app/controllers/submit_form_decline_controller.rb @@ -26,7 +26,8 @@ class SubmitFormDeclineController < ApplicationController user = @submitter.submission.created_by_user || @submitter.template.author - if user.user_configs.find_by(key: UserConfig::RECEIVE_DECLINED_EMAIL)&.value != false + if Users.send_emails?(user) && + user.user_configs.find_by(key: UserConfig::RECEIVE_DECLINED_EMAIL)&.value != false SubmitterMailer.declined_email(@submitter, user).deliver_later! end diff --git a/app/jobs/process_submitter_completion_job.rb b/app/jobs/process_submitter_completion_job.rb index a6c02dc6..1dc1da13 100644 --- a/app/jobs/process_submitter_completion_job.rb +++ b/app/jobs/process_submitter_completion_job.rb @@ -133,7 +133,7 @@ class ProcessSubmitterCompletionJob user_submitter = submission.submitters.find { |s| s.email == user.email } is_sent_to_user = - if user.role != 'integration' && + if user.role != 'integration' && Users.send_emails?(user) && (!user_submitter || user_submitter.preferences['send_email'] == false || !is_copy_email_enabled) && user.user_configs.find_by(key: UserConfig::RECEIVE_COMPLETED_EMAIL)&.value != false SubmitterMailer.completed_email(submitter, user).deliver_later! diff --git a/lib/users.rb b/lib/users.rb index 49989a4f..9508fb78 100644 --- a/lib/users.rb +++ b/lib/users.rb @@ -3,6 +3,10 @@ module Users module_function + def send_emails?(user) + !user.archived_at? + end + def generate_csv(users) headers = %w[email first_name last_name role current_sign_in_at last_sign_in_at updated_at created_at] From bd03498b0f44b771143128714e143654db680dcc Mon Sep 17 00:00:00 2001 From: Alex Turchyn Date: Fri, 11 Sep 2026 02:08:33 +0300 Subject: [PATCH 10/15] adjust show archived --- app/views/submissions_dashboard/index.html.erb | 2 +- app/views/templates_dashboard/index.html.erb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/submissions_dashboard/index.html.erb b/app/views/submissions_dashboard/index.html.erb index 9bfaf39a..3cfaf4c6 100644 --- a/app/views/submissions_dashboard/index.html.erb +++ b/app/views/submissions_dashboard/index.html.erb @@ -28,7 +28,7 @@
<% view_archived_html = capture do %> - <% if can?(:manage, :countless) || current_account.submissions.where.not(archived_at: nil).exists? %> + <% if can?(:manage, :countless) || current_account.submissions.archived.exists? || current_account.templates.where(Submission.where(Submission.arel_table[:template_id].eq(::Template.arel_table[:id])).arel.exists).archived.exists? %> diff --git a/app/views/templates_dashboard/index.html.erb b/app/views/templates_dashboard/index.html.erb index 5406bf68..a65abca6 100644 --- a/app/views/templates_dashboard/index.html.erb +++ b/app/views/templates_dashboard/index.html.erb @@ -1,4 +1,4 @@ -<% has_archived = can?(:manage, :countless) || current_account.templates.where.not(archived_at: nil).exists? %> +<% has_archived = can?(:manage, :countless) || current_account.templates.archived.exists? %> <% show_dropzone = params[:q].blank? && @pagy.pages == 1 && ((@template_folders.size < 10 && @templates.size.zero?) || (@template_folders.size < 7 && @templates.size < 4) || (@template_folders.size < 4 && @templates.size < 7)) %> <% if Docuseal.demo? %><%= render 'shared/demo_alert' %><% end %> From 92b297a620d12b73fe87b8d211d7eb61e2361997 Mon Sep 17 00:00:00 2001 From: Pete Matsyburka Date: Fri, 11 Sep 2026 13:00:26 +0300 Subject: [PATCH 11/15] fix friendly name --- app/models/submitter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/submitter.rb b/app/models/submitter.rb index 3d21e2d4..0a7f70b8 100644 --- a/app/models/submitter.rb +++ b/app/models/submitter.rb @@ -90,7 +90,7 @@ class Submitter < ApplicationRecord def friendly_name if name.present? && email.present? && email.exclude?(',') - %("#{name.delete('"')}" <#{email}>) + %("#{name.delete('"').squish}" <#{email}>) else email end From 994c1b3155d7204b8eee3d8b9ed394607d066023 Mon Sep 17 00:00:00 2001 From: Pete Matsyburka Date: Sat, 12 Sep 2026 18:29:56 +0300 Subject: [PATCH 12/15] keep pasted field roles --- app/javascript/template_builder/builder.vue | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/app/javascript/template_builder/builder.vue b/app/javascript/template_builder/builder.vue index f0146f39..46db0dc3 100644 --- a/app/javascript/template_builder/builder.vue +++ b/app/javascript/template_builder/builder.vue @@ -2371,7 +2371,6 @@ export default { const areaCopy = JSON.parse(JSON.stringify(area)) delete fieldCopy.areas - delete fieldCopy.submitter_uuid areaCopy.relativeX = area.x - minX areaCopy.relativeY = area.y - minY @@ -2381,6 +2380,7 @@ export default { const clipboardData = { items, + submitters: this.template.submitters.map((submitter) => ({ uuid: submitter.uuid, name: submitter.name })), templateId: this.template.id, timestamp: Date.now(), isGroup: true @@ -2491,6 +2491,20 @@ export default { const fieldUuidIndex = {} const fieldOptionsMap = {} + const submitterUuidsMap = {} + + const copiedSubmitterUuids = [...new Set(data.items.map((item) => item.field.submitter_uuid).filter(Boolean))] + + if (copiedSubmitterUuids.length > 1) { + copiedSubmitterUuids.forEach((uuid) => { + const name = data.submitters?.find((submitter) => submitter.uuid === uuid)?.name + + const submitter = this.template.submitters.find((s) => s.uuid === uuid) || + (name && this.template.submitters.find((s) => s.name.toLowerCase() === name.toLowerCase())) + + submitterUuidsMap[uuid] = (submitter || this.selectedSubmitter).uuid + }) + } data.items.forEach((item) => { const field = JSON.parse(JSON.stringify(item.field)) @@ -2515,7 +2529,7 @@ export default { const newField = fieldUuidIndex[field.uuid] || { ...field, uuid: v4(), - submitter_uuid: this.selectedSubmitter.uuid, + submitter_uuid: submitterUuidsMap[field.submitter_uuid] || this.selectedSubmitter.uuid, areas: [] } From 71be6db3588cbb27d65243ed7274cb7e20aa77d5 Mon Sep 17 00:00:00 2001 From: Pete Matsyburka Date: Sat, 12 Sep 2026 18:34:39 +0300 Subject: [PATCH 13/15] adjust delegated values --- app/controllers/submit_form_delegate_controller.rb | 2 +- lib/submitters.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/controllers/submit_form_delegate_controller.rb b/app/controllers/submit_form_delegate_controller.rb index 4a702d65..da0e4ed1 100644 --- a/app/controllers/submit_form_delegate_controller.rb +++ b/app/controllers/submit_form_delegate_controller.rb @@ -33,7 +33,7 @@ class SubmitFormDelegateController < ApplicationController { old_email: @submitter.email, email: }) @submitter.update!(email:, phone: nil, name: nil, slug: SecureRandom.base58(14), - values: Submitters.fetch_values_without_signature(@submitter)) + values: Submitters.fetch_values_for_delegate(@submitter)) end SendSubmitterInvitationEmailJob.perform_async('submitter_id' => @submitter.id) diff --git a/lib/submitters.rb b/lib/submitters.rb index 52be8ffc..b8de2bde 100644 --- a/lib/submitters.rb +++ b/lib/submitters.rb @@ -165,13 +165,13 @@ module Submitters preferences end - def fetch_values_without_signature(submitter) + def fetch_values_for_delegate(submitter) fields = submitter.submission.template_fields || submitter.template.fields default_values = submitter.preferences['default_values'] || {} field_uuids = fields.filter_map do |field| next if field['submitter_uuid'] != submitter.uuid - next unless field['type'].in?(%w[signature initials]) + next unless field['type'].in?(%w[signature phone verification kba initials]) next if default_values[field['uuid']].present? field['uuid'] From 9941122d90532c2355c4bcb120726f27ffe2d897 Mon Sep 17 00:00:00 2001 From: Pete Matsyburka Date: Mon, 14 Sep 2026 11:08:40 +0300 Subject: [PATCH 14/15] adjust i18n --- app/views/webhook_settings/show.html.erb | 2 +- config/locales/i18n.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/webhook_settings/show.html.erb b/app/views/webhook_settings/show.html.erb index 49f4f1c6..47911b85 100644 --- a/app/views/webhook_settings/show.html.erb +++ b/app/views/webhook_settings/show.html.erb @@ -83,7 +83,7 @@ <% if @webhook_events.present? || params[:status].present? %>
-

<%= t('events_log') %>

+

<%= t('event_log') %>

<%= link_to t('all'), url_for(params: request.query_parameters.except('status', 'page')), style: 'margin-bottom: -1px', class: "tab h-10 text-base #{params[:status].blank? ? 'tab-active tab-bordered' : 'pb-[3px]'}" %> <%= link_to t('succeeded'), url_for(params: request.query_parameters.except('page').merge('status' => 'success')), style: 'margin-bottom: -1px', class: "tab h-10 text-base #{params[:status] == 'success' ? 'tab-active tab-bordered' : 'pb-[3px]'}" %> diff --git a/config/locales/i18n.yml b/config/locales/i18n.yml index 869795e2..9e20f38d 100644 --- a/config/locales/i18n.yml +++ b/config/locales/i18n.yml @@ -8034,7 +8034,7 @@ nl: &nl awaiting: Wachten document_id: Document-ID envelope_id: Envelop-ID - event_log: Gebeurtenissenlogboek + event_log: Gebeurtenislogboek verify: Verifiëren testing_log_not_for_production_use: Testlogboek - Niet voor productiegebruik original_sha256: Oorspronkelijke SHA256 From 85fbae18137647e517cc0637b0816ace4c3a53b8 Mon Sep 17 00:00:00 2001 From: Pete Matsyburka Date: Mon, 14 Sep 2026 14:48:00 +0300 Subject: [PATCH 15/15] validate webhook header --- app/controllers/webhook_secret_controller.rb | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app/controllers/webhook_secret_controller.rb b/app/controllers/webhook_secret_controller.rb index b9b8a3f2..94a659e4 100644 --- a/app/controllers/webhook_secret_controller.rb +++ b/app/controllers/webhook_secret_controller.rb @@ -3,11 +3,19 @@ class WebhookSecretController < ApplicationController load_and_authorize_resource :webhook_url, parent: false + HEADER_NAME_REGEXP = /\A[\w-]+\z/ + def show; end def update + key = webhook_secret_params[:key] + + if key.present? && !HEADER_NAME_REGEXP.match?(key) + return redirect_back(fallback_location: settings_webhook_path(@webhook_url), alert: I18n.t('unable_to_save')) + end + @webhook_url.update!(secret: { - webhook_secret_params[:key] => webhook_secret_params[:value] + key => webhook_secret_params[:value] }.compact_blank) redirect_back(fallback_location: settings_webhook_path(@webhook_url),