From 20a082ae8e6e461993da5a2840c3b9eb954ef742 Mon Sep 17 00:00:00 2001 From: Pete Matsyburka Date: Mon, 21 Jul 2025 16:26:08 +0300 Subject: [PATCH 01/14] adjust image field validation --- app/javascript/submission_form/dropzone.vue | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/app/javascript/submission_form/dropzone.vue b/app/javascript/submission_form/dropzone.vue index 29ec6798..89e01b3b 100644 --- a/app/javascript/submission_form/dropzone.vue +++ b/app/javascript/submission_form/dropzone.vue @@ -112,11 +112,23 @@ export default { onSelectFiles (e) { e.preventDefault() - this.uploadFiles(this.$refs.input.files).then(() => { - if (this.$refs.input) { - this.$refs.input.value = '' + const files = Array.from(this.$refs.input.files).filter((f) => { + if (this.accept === 'image/*') { + return f.type.startsWith('image') + } else { + return true } }) + + if (this.accept === 'image/*' && !files.length) { + alert(this.t('please_upload_an_image_file')) + } else { + this.uploadFiles(files).then(() => { + if (this.$refs.input) { + this.$refs.input.value = '' + } + }) + } }, async uploadFiles (files) { this.isLoading = true From 631a442e2a864d652678b615571e4b8c79a0547f Mon Sep 17 00:00:00 2001 From: Pete Matsyburka Date: Mon, 21 Jul 2025 16:43:03 +0300 Subject: [PATCH 02/14] fix builder file drop modal close --- app/javascript/template_builder/hover_dropzone.vue | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/javascript/template_builder/hover_dropzone.vue b/app/javascript/template_builder/hover_dropzone.vue index 5b3ff397..575c832c 100644 --- a/app/javascript/template_builder/hover_dropzone.vue +++ b/app/javascript/template_builder/hover_dropzone.vue @@ -2,6 +2,8 @@ - <% if params[:selfsign].blank? %> + <% if params[:selfsign].blank? && local_assigns[:prefillable_fields].blank? %> <%= svg_icon('user_plus', class: 'w-4 h-4 stroke-2') %> <%= t('add_new') %> @@ -51,7 +85,9 @@
<%= render('submitters_order', f:, template:) if Accounts.can_send_emails?(current_account) %> <%= render 'send_email', f:, template: %> - <%= render 'send_sms', f: %> + <% if has_phone_field %> + <%= render 'send_sms', f: %> + <% end %>
<%= f.button button_title(title: t('add_recipients')), class: 'base-button' %> diff --git a/app/views/submissions/_send_email.html.erb b/app/views/submissions/_send_email.html.erb index 785eac90..d7f357c0 100644 --- a/app/views/submissions/_send_email.html.erb +++ b/app/views/submissions/_send_email.html.erb @@ -4,7 +4,7 @@ <% can_send_emails = Accounts.can_send_emails?(current_account) %>
<%= f.label :send_email, for: uuid = SecureRandom.uuid, class: 'flex items-center cursor-pointer' do %> - <%= f.check_box :send_email, id: uuid, class: 'base-checkbox', disabled: !can_send_emails || local_assigns[:disable_email], checked: can_send_emails && !local_assigns.key?(:resend_email) && !local_assigns[:disable_email] %> + <%= f.check_box :send_email, id: uuid, class: 'base-checkbox', disabled: !can_send_emails || local_assigns[:disable_email], checked: can_send_emails && !local_assigns.key?(:resend_email) && !local_assigns[:disable_email] && template&.preferences&.dig('request_email_enabled') != false %> <%= local_assigns[:resend_email] ? t('re_send_email') : t('send_email') %> <% end %>
diff --git a/app/views/submissions/new.html.erb b/app/views/submissions/new.html.erb index b9874d75..ccea6b4b 100644 --- a/app/views/submissions/new.html.erb +++ b/app/views/submissions/new.html.erb @@ -1,11 +1,13 @@ <% require_phone_2fa = @template.preferences['require_phone_2fa'] == true %> +<% prefillable_fields = @template.fields.select { |f| f['prefillable'] } %> +<% only_detailed = require_phone_2fa || prefillable_fields.present? %> <%= render 'shared/turbo_modal_large', title: params[:selfsign] ? t('add_recipients') : t('add_new_recipients') do %> - <% options = [require_phone_2fa ? nil : [t('via_email'), 'email'], require_phone_2fa ? nil : [t('via_phone'), 'phone'], [t('detailed'), 'detailed'], [t('upload_list'), 'list']].compact %> + <% options = [only_detailed ? nil : [t('via_email'), 'email'], only_detailed ? nil : [t('via_phone'), 'phone'], [t('detailed'), 'detailed'], [t('upload_list'), 'list']].compact %>
<% options.each_with_index do |(label, value), index| %>
- <%= radio_button_tag 'option', value, value == (require_phone_2fa ? 'detailed' : 'email'), class: 'peer hidden', data: { action: 'change:toggle-visible#trigger' } %> + <%= radio_button_tag 'option', value, value == (only_detailed ? 'detailed' : 'email'), class: 'peer hidden', data: { action: 'change:toggle-visible#trigger' } %> @@ -14,7 +16,7 @@
- <% unless require_phone_2fa %> + <% unless only_detailed %>
<%= render 'email_form', template: @template %>
@@ -22,8 +24,8 @@ <%= render 'phone_form', template: @template %>
<% end %> -
- <%= render 'detailed_form', template: @template, require_phone_2fa: %> +
+ <%= render 'detailed_form', template: @template, require_phone_2fa:, prefillable_fields: %>
+ <%= render 'templates_prefillable_fields/form', template: @template %> <%= form_for @template, url: template_preferences_path(@template), method: :post, html: { autocomplete: 'off', class: 'mt-1' }, data: { close_on_submit: false } do |f| %> <% configs = AccountConfigs.find_or_initialize_for_key(current_account, AccountConfig::SUBMITTER_COMPLETED_EMAIL_KEY).value %> @@ -68,7 +69,7 @@ <%= ff.url_field :completed_redirect_url, required: false, class: 'base-input', dir: 'auto' %>
<%= ff.fields_for :completed_message, ff.object.completed_message do |fff| %> -
+
<%= fff.label :body, t('completion_message'), class: 'label' %> <%= fff.text_area :body, required: false, class: 'base-input w-full py-2', dir: 'auto' %> @@ -162,6 +163,14 @@ <% end %>
<% end %> + <%= f.fields_for :preferences, Struct.new(:request_email_enabled).new(@template.preferences['request_email_enabled']) do |ff| %> +
+ + <%= 'Send signature request email' %> + + <%= ff.check_box :request_email_enabled, { checked: ff.object.request_email_enabled != false, class: 'toggle', onchange: 'this.form.requestSubmit()' }, 'true', 'false' %> +
+ <% end %>
<%= f.button button_title(title: t('save'), disabled_with: t('saving')), class: 'base-button' %>
diff --git a/app/views/templates_prefillable_fields/_form.html.erb b/app/views/templates_prefillable_fields/_form.html.erb new file mode 100644 index 00000000..bdfd92f7 --- /dev/null +++ b/app/views/templates_prefillable_fields/_form.html.erb @@ -0,0 +1,16 @@ +<% select_fields = template.fields.filter_map { |f| [f['name'], f['uuid']] if f['name'].present? && f['type'].in?(TemplatesPrefillableFieldsController::PREFILLABLE_FIELD_TYPES) } %> +<% if select_fields.present? %> +
+ <%= form_for '', url: template_prefillable_fields_path(template), method: :post, data: { close_on_submit: false } do |f| %> +
+ <%= f.hidden_field :prefillable, value: 'true' %> + <%= f.label :field_uuid, t(:invite_form_fields), class: 'label' %> +
+ <%= select_tag :field_uuid, options_for_select(select_fields), prompt: t(:select_field), class: 'base-select w-full join-item', dir: 'auto', required: true %> + <%= f.button button_title(title: t('add'), disabled_with: t('add')), class: 'base-button join-item !px-6' %> +
+
+ <% end %> + <%= render partial: 'templates_prefillable_fields/list', locals: { template: } %> +
+<% end %> diff --git a/app/views/templates_prefillable_fields/_list.html.erb b/app/views/templates_prefillable_fields/_list.html.erb new file mode 100644 index 00000000..416619c6 --- /dev/null +++ b/app/views/templates_prefillable_fields/_list.html.erb @@ -0,0 +1,6 @@ +
+ <% template.fields.each do |f| %> + <% next unless f['prefillable'] %> + <%= button_to button_title(title: f['name'].presence || f['type'].capitalize, disabled_with: f['name'].presence || f['type'].capitalize, icon: svg_icon('x', class: 'w-4 h-4'), icon_disabled: svg_icon('loader', class: 'w-4 h-4 animate-spin')), template_prefillable_fields_path(template), params: { field_uuid: f['uuid'], prefillable: 'false' }, class: 'badge badge-lg badge-primary space-x-1 pr-3 pl-2', form: { data: { close_on_submit: false } } %> + <% end %> +
diff --git a/config/locales/i18n.yml b/config/locales/i18n.yml index c56f70d3..9c263909 100644 --- a/config/locales/i18n.yml +++ b/config/locales/i18n.yml @@ -23,6 +23,8 @@ en: &en pro: Pro thanks: Thanks private: Private + select: Select + invite_form_fields: Invite form fields default_parties: Default parties authenticate_embedded_form_preview_with_token: Authenticate embedded form preview with token stripe_integration: Stripe Integration @@ -896,6 +898,8 @@ en: &en range_without_total: "%{from}-%{to} events" es: &es + select: Seleccionar + invite_form_fields: Invitar campos del formulario pro: Pro default_parties: Partes predeterminadas authenticate_embedded_form_preview_with_token: Autenticar vista previa del formulario incrustado con token @@ -1772,6 +1776,8 @@ es: &es range_without_total: "%{from}-%{to} eventos" it: &it + select: Seleziona + invite_form_fields: Invita campi modulo pro: Pro default_parties: Parti predefiniti authenticate_embedded_form_preview_with_token: "Autentica l'anteprima del modulo incorporato con il token" @@ -2648,6 +2654,8 @@ it: &it range_without_total: "%{from}-%{to} eventi" fr: &fr + select: Sélectionner + invite_form_fields: Inviter des champs de formulaire pro: Pro default_parties: Parties par défaut authenticate_embedded_form_preview_with_token: Authentifier l’aperçu du formulaire intégré avec un jeton @@ -3527,6 +3535,8 @@ fr: &fr range_without_total: "%{from} à %{to} événements" pt: &pt + select: Selecionar + invite_form_fields: Convidar campos do formulário pro: Pro default_parties: Partes padrão authenticate_embedded_form_preview_with_token: Autenticar visualização incorporada do formulário com token @@ -4404,6 +4414,8 @@ pt: &pt range_without_total: "%{from}-%{to} eventos" de: &de + select: Auswählen + invite_form_fields: Formularfelder einladen pro: Pro default_parties: Standardparteien authenticate_embedded_form_preview_with_token: Authentifizieren Sie die eingebettete Formularvorschau mit Token diff --git a/config/routes.rb b/config/routes.rb index 20015262..76ed7af9 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -108,6 +108,7 @@ Rails.application.routes.draw do resource :preferences, only: %i[show create], controller: 'templates_preferences' resource :share_link, only: %i[show create], controller: 'templates_share_link' resources :recipients, only: %i[create], controller: 'templates_recipients' + resources :prefillable_fields, only: %i[create], controller: 'templates_prefillable_fields' resources :submissions_export, only: %i[index new] end resources :preview_document_page, only: %i[show], path: '/preview/:signed_uuid' From 16d6d58efb097a757f16f84406f35d8ccf6dffab Mon Sep 17 00:00:00 2001 From: Pete Matsyburka Date: Sun, 27 Jul 2025 10:42:56 +0300 Subject: [PATCH 13/14] fix typo --- config/locales/i18n.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/locales/i18n.yml b/config/locales/i18n.yml index 9c263909..99ec82ef 100644 --- a/config/locales/i18n.yml +++ b/config/locales/i18n.yml @@ -784,10 +784,10 @@ en: &en code: Code custom_html_emails: Custom HTML emails connect_your_email_to_send_html_emails: Connect your email to send HTML emails - connect_your_email_or_outlook_account_or_add_smtp_settings_to_send_custom_html_emails: Connect your Gmail or Oulook account or add SMTP settings to send custom HTML emails. + connect_your_email_or_outlook_account_or_add_smtp_settings_to_send_custom_html_emails: Connect your Gmail or Outlook account or add SMTP settings to send custom HTML emails. connect_gmail_or_outlook: Connect Gmail or Outlook connect_your_email_to_bulk_send: Connect your email to bulk send - connect_your_email_or_outlook_account_or_add_smtp_settings_to_bulk_send: Connect your Gmail or Oulook account or add SMTP settings to bulk send. + connect_your_email_or_outlook_account_or_add_smtp_settings_to_bulk_send: Connect your Gmail or Outlook account or add SMTP settings to bulk send. are_you_sure_you_want_to_add_recipients_without_sending_to_send_emails_it_requires_to_connect_gmail_or_outlook: Are you sure you want to add recipients without sending? To send emails it requires to connect Gmail or Outlook. template_name_has_been_completed_by_submitters_html: '"{template.name}" has been completed by {submission.submitters}' please_check_the_copy_of_your_template_name_in_the_email_attachments_html: 'Please check the copy of your "{template.name}" in the email attachments.' From 14915d37b4462e8c37eefa23f4ebd507081801e7 Mon Sep 17 00:00:00 2001 From: Pete Matsyburka Date: Mon, 28 Jul 2025 08:45:07 +0300 Subject: [PATCH 14/14] fix build --- .github/workflows/docker.yml | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 8bbd5052..c2c317e5 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -1,9 +1,17 @@ name: Build Docker Images on: - push: - tags: - - "*.*.*" + workflow_dispatch: + inputs: + version: + description: Version + type: string + required: true + image: + description: QEMU image + type: string + required: false + default: tonistiigi/binfmt:latest jobs: build: @@ -16,23 +24,23 @@ jobs: with: submodules: recursive - - - name: Docker meta + - name: Docker meta id: meta uses: docker/metadata-action@v4 with: - images: | - docuseal/docuseal - tags: | - type=semver,pattern={{version}} + images: docuseal/docuseal + tags: type=semver,pattern={{version}} + - name: Set up QEMU uses: docker/setup-qemu-action@v3 + with: + image: ${{ inputs.image }} - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Create .version file - run: echo ${{ github.ref_name }} > .version + run: echo ${{ inputs.version }} > .version - name: Login to Docker Hub uses: docker/login-action@v3