From 8fe0293ebe08a8850d2655b39d58fe76a0b60b7a Mon Sep 17 00:00:00 2001 From: Alex Turchyn Date: Mon, 2 Nov 2026 20:50:32 +0200 Subject: [PATCH 01/16] update hero image --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e2f6e8b92..a4e5939d7 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ DocuSeal is an open source platform that provides secure and efficient digital d ☁️ Try in Cloud -[![Demo](https://github.com/docusealco/docuseal/assets/5418788/d8703ea3-361a-423f-8bfe-eff1bd9dbe14)](https://demo.docuseal.tech) +[![Demo](https://github.com/user-attachments/assets/8344a341-96cf-4df3-aaae-5458a21a65b3)](https://demo.docuseal.tech) ## Features - PDF form fields builder (WYSIWYG) From c0c5ba48a075c3940b18d24cacd80b43919cd2fd Mon Sep 17 00:00:00 2001 From: Pete Matsyburka Date: Wed, 4 Nov 2026 00:05:39 +0200 Subject: [PATCH 02/16] block active storage routes --- config/initializers/active_storage.rb | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/config/initializers/active_storage.rb b/config/initializers/active_storage.rb index be1236ac9..d00137bc2 100644 --- a/config/initializers/active_storage.rb +++ b/config/initializers/active_storage.rb @@ -62,11 +62,32 @@ end ActiveStorage::LogSubscriber.detach_from(:active_storage) if Rails.env.production? +# rubocop:disable-next Metrics/BlockLength Rails.configuration.to_prepare do ActiveStorage::DiskController.after_action do response.set_header('cache-control', 'public, max-age=31536000') if action_name == 'show' end + ActiveStorage::DiskController.prepend_before_action(only: :update) do + head :forbidden + end + + ActiveStorage::Blobs::RedirectController.prepend_before_action do + head :forbidden + end + + ActiveStorage::Representations::RedirectController.prepend_before_action do + head :forbidden + end + + ActiveStorage::Representations::ProxyController.prepend_before_action do + head :forbidden + end + + ActiveStorage::Blobs::ProxyController.prepend_before_action do + head :forbidden + end + ActiveStorage::Blobs::ProxyController.before_action do response.set_header('Access-Control-Allow-Origin', '*') response.set_header('Access-Control-Allow-Methods', 'GET') From 0bc620fa90af99f6f3b774a0c0525981edf144e8 Mon Sep 17 00:00:00 2001 From: Pete Matsyburka Date: Wed, 4 Nov 2026 00:26:50 +0200 Subject: [PATCH 03/16] cleanup routes --- config/application.rb | 2 +- config/initializers/active_storage.rb | 43 ++------------------------- config/routes.rb | 8 ++--- 3 files changed, 7 insertions(+), 46 deletions(-) diff --git a/config/application.rb b/config/application.rb index 95dbb4c2d..751377c07 100644 --- a/config/application.rb +++ b/config/application.rb @@ -25,7 +25,7 @@ module DocuSeal config.active_storage.routes_prefix = '' - config.active_storage.draw_routes = ENV['MULTITENANT'] != 'true' + config.active_storage.draw_routes = false config.active_storage.analyzers = [] diff --git a/config/initializers/active_storage.rb b/config/initializers/active_storage.rb index d00137bc2..a1a545398 100644 --- a/config/initializers/active_storage.rb +++ b/config/initializers/active_storage.rb @@ -62,48 +62,9 @@ end ActiveStorage::LogSubscriber.detach_from(:active_storage) if Rails.env.production? -# rubocop:disable-next Metrics/BlockLength Rails.configuration.to_prepare do - ActiveStorage::DiskController.after_action do - response.set_header('cache-control', 'public, max-age=31536000') if action_name == 'show' - end - - ActiveStorage::DiskController.prepend_before_action(only: :update) do - head :forbidden - end - - ActiveStorage::Blobs::RedirectController.prepend_before_action do - head :forbidden - end - - ActiveStorage::Representations::RedirectController.prepend_before_action do - head :forbidden - end - - ActiveStorage::Representations::ProxyController.prepend_before_action do - head :forbidden - end - - ActiveStorage::Blobs::ProxyController.prepend_before_action do - head :forbidden - end - - ActiveStorage::Blobs::ProxyController.before_action do - response.set_header('Access-Control-Allow-Origin', '*') - response.set_header('Access-Control-Allow-Methods', 'GET') - response.set_header('Access-Control-Allow-Headers', '*') - response.set_header('Access-Control-Max-Age', '1728000') - end - - ActiveStorage::Blobs::RedirectController.before_action do - response.set_header('Access-Control-Allow-Origin', '*') - response.set_header('Access-Control-Allow-Methods', 'GET') - response.set_header('Access-Control-Allow-Headers', '*') - response.set_header('Access-Control-Max-Age', '1728000') - end - - ActiveStorage::DirectUploadsController.before_action do - head :forbidden + ActiveStorage::DiskController.after_action(only: :show) do + response.set_header('cache-control', 'public, max-age=31536000') end LoadActiveStorageConfigs.call diff --git a/config/routes.rb b/config/routes.rb index 812581ae1..70d51f6c6 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -125,12 +125,12 @@ Rails.application.routes.draw do resource :blobs_proxy, only: %i[show], path: '/blobs_proxy/:signed_uuid/*filename', controller: 'api/active_storage_blobs_proxy' - if Docuseal.multitenant? - get '/disk/:encoded_key/*filename' => 'active_storage/disk#show', as: :rails_disk_service if Rails.env.local? - - ActiveSupport.run_load_hooks(:multitenant_routes, self) + if !Docuseal.multitenant? || Rails.env.local? + get '/disk/:encoded_key/*filename' => 'active_storage/disk#show', as: :rails_disk_service end + ActiveSupport.run_load_hooks(:multitenant_routes, self) if Docuseal.multitenant? + resources :start_form, only: %i[show update], path: 'd', param: 'slug' do get :completed end From 4fec8db589bcf8be28375152df0ad667bc0eaeec Mon Sep 17 00:00:00 2001 From: Alex Turchyn Date: Wed, 4 Nov 2026 01:30:09 +0200 Subject: [PATCH 04/16] fix native turbo modal --- app/controllers/users_controller.rb | 4 ++-- app/views/shared/_turbo_modal.html.erb | 2 +- app/views/shared/_turbo_modal_large.html.erb | 8 +++++++- app/views/users/_form.html.erb | 2 +- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 6298c0815..2e52b103c 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -32,11 +32,11 @@ class UsersController < ApplicationController end def new - render layout: turbo_native_android? ? 'plain' : true + render layout: turbo_native_app? ? 'plain' : true end def edit - render layout: turbo_native_android? ? 'plain' : true + render layout: turbo_native_app? ? 'plain' : true end def create diff --git a/app/views/shared/_turbo_modal.html.erb b/app/views/shared/_turbo_modal.html.erb index dc4074fa8..6425ed165 100644 --- a/app/views/shared/_turbo_modal.html.erb +++ b/app/views/shared/_turbo_modal.html.erb @@ -1,4 +1,4 @@ -<% if turbo_native_ios? || (turbo_native_app? && !turbo_frame_request?) %> +<% if turbo_native_app? %> <% content_for(:native_modal, 'true') %> <% content_for(:native_title, local_assigns[:title]) if local_assigns[:title] %> diff --git a/app/views/shared/_turbo_modal_large.html.erb b/app/views/shared/_turbo_modal_large.html.erb index 1ae76d786..088ee63fd 100644 --- a/app/views/shared/_turbo_modal_large.html.erb +++ b/app/views/shared/_turbo_modal_large.html.erb @@ -1,4 +1,10 @@ -<% if turbo_frame_request? %> +<% if turbo_native_app? && turbo_frame_request? %> + + + <%= yield %> + + +<% elsif turbo_frame_request? %>
diff --git a/app/views/users/_form.html.erb b/app/views/users/_form.html.erb index b8cde23ae..bc3914de5 100644 --- a/app/views/users/_form.html.erb +++ b/app/views/users/_form.html.erb @@ -1,4 +1,4 @@ -<%= form_for user, html: { class: "space-y-4 #{'px-4' if turbo_native_android?}" }, data: { turbo_frame: :_top } do |f| %> +<%= form_for user, html: { class: "space-y-4 #{'px-4' if turbo_native_app?}" }, data: { turbo_frame: :_top } do |f| %>
From 49463d611c449759d96bf82bf1ae536845163a3b Mon Sep 17 00:00:00 2001 From: Pete Matsyburka Date: Wed, 4 Nov 2026 12:35:39 +0200 Subject: [PATCH 05/16] unify native modals --- app/controllers/submissions_export_controller.rb | 2 +- app/controllers/submissions_filters_controller.rb | 2 +- app/controllers/template_folders_controller.rb | 2 +- app/controllers/templates_clone_controller.rb | 2 +- app/controllers/templates_folders_controller.rb | 2 +- app/controllers/user_initials_controller.rb | 2 +- app/controllers/user_signatures_controller.rb | 2 +- app/javascript/template_builder/conditions_modal.vue | 2 +- app/javascript/template_builder/description_modal.vue | 2 +- .../template_builder/documents_editor_modal.vue | 2 +- app/javascript/template_builder/font_modal.vue | 2 +- app/javascript/template_builder/formula_modal.vue | 2 +- app/javascript/template_builder/revisions_modal.vue | 2 +- app/views/layouts/application.html.erb | 4 +--- app/views/shared/_turbo_drawer.html.erb | 4 ++-- app/views/shared/_turbo_modal.html.erb | 1 - app/views/shared/_turbo_modal_large.html.erb | 10 +++------- app/views/submissions/new.html.erb | 2 +- app/views/submissions_export/new.html.erb | 2 +- app/views/submissions_filters/_filter_modal.html.erb | 2 +- app/views/submitters/edit.html.erb | 2 +- app/views/template_folders/edit.html.erb | 2 +- app/views/templates/new.html.erb | 2 +- app/views/templates_clone/_form.html.erb | 2 +- app/views/templates_folders/edit.html.erb | 4 ++-- app/views/templates_preferences/show.html.erb | 6 +++--- app/views/templates_share_link/show.html.erb | 2 +- app/views/user_initials/edit.html.erb | 4 ++-- app/views/user_signatures/edit.html.erb | 4 ++-- app/views/users/_form.html.erb | 2 +- 30 files changed, 37 insertions(+), 44 deletions(-) diff --git a/app/controllers/submissions_export_controller.rb b/app/controllers/submissions_export_controller.rb index 7a42d6a5c..1c83545a7 100644 --- a/app/controllers/submissions_export_controller.rb +++ b/app/controllers/submissions_export_controller.rb @@ -26,6 +26,6 @@ class SubmissionsExportController < ApplicationController end def new - render layout: turbo_native_android? ? 'plain' : true + render layout: 'plain' end end diff --git a/app/controllers/submissions_filters_controller.rb b/app/controllers/submissions_filters_controller.rb index ecebfefca..37e8ed914 100644 --- a/app/controllers/submissions_filters_controller.rb +++ b/app/controllers/submissions_filters_controller.rb @@ -14,6 +14,6 @@ class SubmissionsFiltersController < ApplicationController def show return head :not_found unless ALLOWED_NAMES.include?(params[:name]) - render params[:name], layout: turbo_native_android? ? 'plain' : true + render params[:name], layout: 'plain' end end diff --git a/app/controllers/template_folders_controller.rb b/app/controllers/template_folders_controller.rb index 8b24d2020..2ef30c202 100644 --- a/app/controllers/template_folders_controller.rb +++ b/app/controllers/template_folders_controller.rb @@ -42,7 +42,7 @@ class TemplateFoldersController < ApplicationController end def edit - render layout: turbo_native_android? ? 'plain' : true + render layout: 'plain' end def update diff --git a/app/controllers/templates_clone_controller.rb b/app/controllers/templates_clone_controller.rb index 8d5f62546..d524a886a 100644 --- a/app/controllers/templates_clone_controller.rb +++ b/app/controllers/templates_clone_controller.rb @@ -8,7 +8,7 @@ class TemplatesCloneController < ApplicationController @template = Template.new(name: "#{@base_template.name} (#{I18n.t('clone')})") - render layout: turbo_native_android? ? 'plain' : true + render layout: 'plain' end def create diff --git a/app/controllers/templates_folders_controller.rb b/app/controllers/templates_folders_controller.rb index 6f22669b0..b62a9d406 100644 --- a/app/controllers/templates_folders_controller.rb +++ b/app/controllers/templates_folders_controller.rb @@ -4,7 +4,7 @@ class TemplatesFoldersController < ApplicationController load_and_authorize_resource :template def edit - render layout: turbo_native_android? ? 'plain' : true + render layout: 'plain' end def update diff --git a/app/controllers/user_initials_controller.rb b/app/controllers/user_initials_controller.rb index 1306444cc..1f78092c7 100644 --- a/app/controllers/user_initials_controller.rb +++ b/app/controllers/user_initials_controller.rb @@ -5,7 +5,7 @@ class UserInitialsController < ApplicationController authorize_resource :user_config def edit - render layout: turbo_native_android? ? 'plain' : true + render layout: 'plain' end def update diff --git a/app/controllers/user_signatures_controller.rb b/app/controllers/user_signatures_controller.rb index 353e26092..aa6833b03 100644 --- a/app/controllers/user_signatures_controller.rb +++ b/app/controllers/user_signatures_controller.rb @@ -5,7 +5,7 @@ class UserSignaturesController < ApplicationController authorize_resource :user_config def edit - render layout: turbo_native_android? ? 'plain' : true + render layout: 'plain' end def update diff --git a/app/javascript/template_builder/conditions_modal.vue b/app/javascript/template_builder/conditions_modal.vue index c625f43e5..4e29c8de8 100644 --- a/app/javascript/template_builder/conditions_modal.vue +++ b/app/javascript/template_builder/conditions_modal.vue @@ -7,7 +7,7 @@ class="absolute top-0 bottom-0 right-0 left-0" @click.prevent="$emit('close')" /> -
+
-
+
-
+
-
+
-
+
<% end %> <% end %> - <% unless content_for?(:native_modal) %> - - <% end %> + <%= render turbo_native_app? ? 'shared/native_navbar' : 'shared/navbar' %>
diff --git a/app/views/shared/_turbo_drawer.html.erb b/app/views/shared/_turbo_drawer.html.erb index eaa9418c6..637c2f504 100644 --- a/app/views/shared/_turbo_drawer.html.erb +++ b/app/views/shared/_turbo_drawer.html.erb @@ -1,7 +1,7 @@ -<% if turbo_native_app? && !turbo_frame_request? %> +<% if turbo_native_app? %> <% content_for(:native_title, local_assigns[:title]) if local_assigns[:title] %> - + <%= yield %> diff --git a/app/views/shared/_turbo_modal.html.erb b/app/views/shared/_turbo_modal.html.erb index 6425ed165..d12eec6a3 100644 --- a/app/views/shared/_turbo_modal.html.erb +++ b/app/views/shared/_turbo_modal.html.erb @@ -1,5 +1,4 @@ <% if turbo_native_app? %> - <% content_for(:native_modal, 'true') %> <% content_for(:native_title, local_assigns[:title]) if local_assigns[:title] %> diff --git a/app/views/shared/_turbo_modal_large.html.erb b/app/views/shared/_turbo_modal_large.html.erb index 088ee63fd..235fbff14 100644 --- a/app/views/shared/_turbo_modal_large.html.erb +++ b/app/views/shared/_turbo_modal_large.html.erb @@ -1,6 +1,7 @@ -<% if turbo_native_app? && turbo_frame_request? %> +<% if turbo_native_app? %> + <% content_for(:native_title, local_assigns[:title]) if local_assigns[:title] %> - + <%= yield %> @@ -23,11 +24,6 @@
-<% elsif turbo_native_app? %> - <% content_for(:native_title, local_assigns[:title]) if local_assigns[:title] %> - - <%= yield %> - <% else %> <% if local_assigns[:title] %>
diff --git a/app/views/submissions/new.html.erb b/app/views/submissions/new.html.erb index c869745d4..d6e9b3060 100644 --- a/app/views/submissions/new.html.erb +++ b/app/views/submissions/new.html.erb @@ -26,7 +26,7 @@
<% end %> -
+
<% unless only_detailed %>
<%= render 'email_form', template: @template, variables_form:, can_send_emails:, viewer_submitter_uuids: %> diff --git a/app/views/submissions_export/new.html.erb b/app/views/submissions_export/new.html.erb index 52e25fdd1..3143752cd 100644 --- a/app/views/submissions_export/new.html.erb +++ b/app/views/submissions_export/new.html.erb @@ -1,6 +1,6 @@ <% filter_params = params.permit(:q, :archived, *Submissions::Filter::ALLOWED_PARAMS) %> <%= render 'shared/turbo_modal', title: t('export'), close_after_submit: false do %> -
+
<%= button_to template_submissions_export_index_path(@template), params: { format: :xlsx, **filter_params }, method: :get, data: { turbo_frame: :_top } do %>
diff --git a/app/views/submissions_filters/_filter_modal.html.erb b/app/views/submissions_filters/_filter_modal.html.erb index b7d45e168..a3318718a 100644 --- a/app/views/submissions_filters/_filter_modal.html.erb +++ b/app/views/submissions_filters/_filter_modal.html.erb @@ -1,7 +1,7 @@ <% path_uri = Addressable::URI.parse(params[:path].to_s) %> <% path = url_for(**Rails.application.routes.recognize_path(path_uri.path, method: :get)) %> <%= render 'shared/turbo_modal', title: local_assigns[:title] do %> - <%= form_for '', url: path, method: :get, data: { turbo_frame: :_top }, html: { autocomplete: :off, class: ('px-4' if turbo_native_android?) } do |f| %> + <%= form_for '', url: path, method: :get, data: { turbo_frame: :_top }, html: { autocomplete: :off, style: ('padding-inline: 18px' if turbo_native_app?) } do |f| %> <%= hidden_field_tag :q, params[:q] if params[:q].present? %> <%= hidden_field_tag :scope, params[:scope] if params[:scope].present? %> <%= hidden_field_tag :archived, params[:archived] if params[:archived].present? %> diff --git a/app/views/submitters/edit.html.erb b/app/views/submitters/edit.html.erb index 6be3ae03c..142998c3d 100644 --- a/app/views/submitters/edit.html.erb +++ b/app/views/submitters/edit.html.erb @@ -1,5 +1,5 @@ <%= render 'shared/turbo_modal_large', title: t('edit_recipient') do %> -
+
<%= form_for '', url: submitter_path(@submitter), method: :patch, html: { autocomplete: 'off' }, data: { turbo_frame: :_top } do |f| %>
diff --git a/app/views/template_folders/edit.html.erb b/app/views/template_folders/edit.html.erb index 4b8cbbf5d..2c1699037 100644 --- a/app/views/template_folders/edit.html.erb +++ b/app/views/template_folders/edit.html.erb @@ -1,5 +1,5 @@ <%= render 'shared/turbo_modal', title: t('rename_folder') do %> - <%= form_for @template_folder, url: folder_path(@template_folder), data: { turbo_frame: :_top }, html: { autocomplete: :off, class: ('px-4' if turbo_native_android?) } do |f| %> + <%= form_for @template_folder, url: folder_path(@template_folder), data: { turbo_frame: :_top }, html: { autocomplete: :off, style: ('padding-inline: 18px' if turbo_native_app?) } do |f| %>
<%= f.text_field :name, required: true, placeholder: "#{t('folder_name')}...", class: 'base-input w-full', autofocus: true, dir: 'auto' %>
diff --git a/app/views/templates/new.html.erb b/app/views/templates/new.html.erb index 414faaa21..84e7f4004 100644 --- a/app/views/templates/new.html.erb +++ b/app/views/templates/new.html.erb @@ -1,5 +1,5 @@ <%= render 'shared/turbo_modal', title: @base_template ? t('clone_template') : t('new_document_template') do %> -
+
<%= render 'templates/file_form' %>
<% end %> diff --git a/app/views/templates_clone/_form.html.erb b/app/views/templates_clone/_form.html.erb index ebb320584..8c3f60fb7 100644 --- a/app/views/templates_clone/_form.html.erb +++ b/app/views/templates_clone/_form.html.erb @@ -1,4 +1,4 @@ -<%= form_for @template, url: template_clone_index_path(@base_template), data: { turbo_frame: :_top }, html: { autocomplete: :off, class: ('px-4' if turbo_native_android?) } do |f| %> +<%= form_for @template, url: template_clone_index_path(@base_template), data: { turbo_frame: :_top }, html: { autocomplete: :off, style: ('padding-inline: 18px' if turbo_native_app?) } do |f| %> <% accounts = Account.accessible_by(true_ability).where.not(id: true_user.account.testing_accounts).where(User.where(User.arel_table[:account_id].eq(Account.arel_table[:id])).arel.exists).active %> <% with_accounts = (can?(:manage, :tenants) || true_user != current_user) && accounts.where.not(id: current_account.id).exists? %> <% if with_accounts %> diff --git a/app/views/templates_folders/edit.html.erb b/app/views/templates_folders/edit.html.erb index 26bd1643f..2123a3d21 100644 --- a/app/views/templates_folders/edit.html.erb +++ b/app/views/templates_folders/edit.html.erb @@ -1,7 +1,7 @@ <% with_subfolder = @template.folder.name != TemplateFolder::DEFAULT_NAME && params[:subfolder] != 'false' %> <%= render 'shared/turbo_modal', title: t('move_into_folder') do %> <% if with_subfolder %> - <%= form_for '', url: template_folder_path(@template), method: :put, data: { turbo_frame: :_top }, html: { id: 'subfolder_form', autocomplete: :off, class: ('px-4' if turbo_native_android?) } do |f| %> + <%= form_for '', url: template_folder_path(@template), method: :put, data: { turbo_frame: :_top }, html: { id: 'subfolder_form', autocomplete: :off, style: ('padding-inline: 18px' if turbo_native_app?) } do |f| %> <%= f.hidden_field :parent_name, value: @template.folder.parent_folder&.name || @template.folder.name %>
@@ -25,7 +25,7 @@
<% end %> <% end %> - <%= form_for '', url: template_folder_path(@template), method: :put, data: { turbo_frame: :_top }, html: { id: 'folder_form', autocomplete: :off, class: "#{turbo_native_android? ? 'mt-1.5 px-4' : 'mt-6'} #{'hidden' if with_subfolder}" } do |f| %> + <%= form_for '', url: template_folder_path(@template), method: :put, data: { turbo_frame: :_top }, html: { id: 'folder_form', autocomplete: :off, class: "#{turbo_native_android? ? 'mt-1.5' : 'mt-6'} #{'hidden' if with_subfolder}", style: ('padding-inline: 18px' if turbo_native_app?) } do |f| %>
<%= f.text_field :name, required: true, placeholder: "#{t('new_folder_name')}...", class: 'base-input w-full', autofocus: true %> diff --git a/app/views/templates_preferences/show.html.erb b/app/views/templates_preferences/show.html.erb index 404355d4f..c6922da3f 100644 --- a/app/views/templates_preferences/show.html.erb +++ b/app/views/templates_preferences/show.html.erb @@ -18,7 +18,7 @@
<% end %> -
+
<%= render 'access' %> <%= form_for @template, url: template_preferences_path(@template), method: :post, html: { autocomplete: 'off', class: 'mt-2' }, data: { close_on_submit: false } do |f| %> @@ -146,14 +146,14 @@ <%= render 'templates_code_modal/preferences', class: 'pt-2' %>
<% if show_recipients %> -