From a4928268eaf7703410e5c20fb8836da7df3107c8 Mon Sep 17 00:00:00 2001 From: Pete Matsyburka Date: Mon, 9 Dec 2024 21:14:31 +0200 Subject: [PATCH 01/16] fix folder templates --- app/controllers/template_folders_controller.rb | 3 ++- app/controllers/templates_dashboard_controller.rb | 12 +++--------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/app/controllers/template_folders_controller.rb b/app/controllers/template_folders_controller.rb index dd7010bb..a3418015 100644 --- a/app/controllers/template_folders_controller.rb +++ b/app/controllers/template_folders_controller.rb @@ -4,7 +4,8 @@ class TemplateFoldersController < ApplicationController load_and_authorize_resource :template_folder def show - @templates = @template_folder.templates.active.preload(:author, :template_accesses).order(id: :desc) + @templates = @template_folder.templates.active.accessible_by(current_ability) + .preload(:author, :template_accesses).order(id: :desc) @templates = Templates.search(@templates, params[:q]) @pagy, @templates = pagy(@templates, limit: 12) diff --git a/app/controllers/templates_dashboard_controller.rb b/app/controllers/templates_dashboard_controller.rb index 3a6a66df..7c806fa9 100644 --- a/app/controllers/templates_dashboard_controller.rb +++ b/app/controllers/templates_dashboard_controller.rb @@ -9,7 +9,9 @@ class TemplatesDashboardController < ApplicationController FOLDERS_PER_PAGE = 18 def index - @template_folders = filter_template_folders(@template_folders) + @template_folders = @template_folders.where(id: @templates.active.select(:folder_id)).order(id: :desc) + + @template_folders = TemplateFolders.search(@template_folders, params[:q]) @pagy, @template_folders = pagy( @template_folders, @@ -36,14 +38,6 @@ class TemplatesDashboardController < ApplicationController private - def filter_template_folders(template_folders) - rel = template_folders.joins(:active_templates) - .order(id: :desc) - .distinct - - TemplateFolders.search(rel, params[:q]) - end - def filter_templates(templates) rel = templates.active.preload(:author, :template_accesses).order(id: :desc) From f380200e039a08be2cb367537fc09db70c37f0db Mon Sep 17 00:00:00 2001 From: Pete Matsyburka Date: Tue, 10 Dec 2024 17:41:27 +0200 Subject: [PATCH 02/16] add clone document params --- app/controllers/api/templates_clone_controller.rb | 4 +++- lib/templates/clone.rb | 2 +- lib/templates/clone_attachments.rb | 8 ++++++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/app/controllers/api/templates_clone_controller.rb b/app/controllers/api/templates_clone_controller.rb index f869fa28..e7e1db7e 100644 --- a/app/controllers/api/templates_clone_controller.rb +++ b/app/controllers/api/templates_clone_controller.rb @@ -23,7 +23,9 @@ module Api cloned_template.source = :api cloned_template.save! - schema_documents = Templates::CloneAttachments.call(template: cloned_template, original_template: @template) + schema_documents = Templates::CloneAttachments.call(template: cloned_template, + original_template: @template, + documents: params[:documents]) WebhookUrls.for_account_id(cloned_template.account_id, 'template.created').each do |webhook_url| SendTemplateCreatedWebhookRequestJob.perform_async('template_id' => cloned_template.id, diff --git a/lib/templates/clone.rb b/lib/templates/clone.rb index 45bd5350..b4b68231 100644 --- a/lib/templates/clone.rb +++ b/lib/templates/clone.rb @@ -10,7 +10,7 @@ module Templates template.external_id = external_id template.author = author template.preferences = original_template.preferences.deep_dup - template.name = name || "#{original_template.name} (#{I18n.t('clone')})" + template.name = name.presence || "#{original_template.name} (#{I18n.t('clone')})" template.assign_attributes(original_template.slice(:folder_id, :schema)) diff --git a/lib/templates/clone_attachments.rb b/lib/templates/clone_attachments.rb index 970214b3..0b52a2fa 100644 --- a/lib/templates/clone_attachments.rb +++ b/lib/templates/clone_attachments.rb @@ -4,17 +4,21 @@ module Templates module CloneAttachments module_function - def call(template:, original_template:) + def call(template:, original_template:, documents: []) schema_uuids_replacements = {} cloned_schema = original_template.schema.deep_dup cloned_fields = template.fields.deep_dup - cloned_schema.each do |schema_item| + cloned_schema.each_with_index do |schema_item, index| new_schema_item_uuid = SecureRandom.uuid schema_uuids_replacements[schema_item['attachment_uuid']] = new_schema_item_uuid schema_item['attachment_uuid'] = new_schema_item_uuid + + new_name = documents&.dig(index, 'name') + + schema_item['name'] = new_name if new_name.present? end cloned_fields.each do |field| From d3033c072af024632c3d3d356eb1824b59980e4f Mon Sep 17 00:00:00 2001 From: Pete Matsyburka Date: Tue, 10 Dec 2024 22:22:17 +0200 Subject: [PATCH 03/16] update document name on clone --- lib/templates/clone.rb | 8 +++++++- lib/templates/clone_attachments.rb | 9 +++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/templates/clone.rb b/lib/templates/clone.rb index b4b68231..60739c2e 100644 --- a/lib/templates/clone.rb +++ b/lib/templates/clone.rb @@ -12,7 +12,13 @@ module Templates template.preferences = original_template.preferences.deep_dup template.name = name.presence || "#{original_template.name} (#{I18n.t('clone')})" - template.assign_attributes(original_template.slice(:folder_id, :schema)) + template.assign_attributes(original_template.slice(:folder_id, :schema).deep_dup) + + if name.present? && template.schema.size == 1 && + original_template.schema.first['name'] == original_template.name && + template.name != "#{original_template.name} (#{I18n.t('clone')})" + template.schema.first['name'] = template.name + end template.folder = TemplateFolders.find_or_create_by_name(author, folder_name) if folder_name.present? diff --git a/lib/templates/clone_attachments.rb b/lib/templates/clone_attachments.rb index 0b52a2fa..8b08735d 100644 --- a/lib/templates/clone_attachments.rb +++ b/lib/templates/clone_attachments.rb @@ -7,10 +7,7 @@ module Templates def call(template:, original_template:, documents: []) schema_uuids_replacements = {} - cloned_schema = original_template.schema.deep_dup - cloned_fields = template.fields.deep_dup - - cloned_schema.each_with_index do |schema_item, index| + template.schema.each_with_index do |schema_item, index| new_schema_item_uuid = SecureRandom.uuid schema_uuids_replacements[schema_item['attachment_uuid']] = new_schema_item_uuid @@ -21,7 +18,7 @@ module Templates schema_item['name'] = new_name if new_name.present? end - cloned_fields.each do |field| + template.fields.each do |field| next if field['areas'].blank? field['areas'].each do |area| @@ -29,7 +26,7 @@ module Templates end end - template.update!(schema: cloned_schema, fields: cloned_fields) + template.save! original_template.schema_documents.map do |document| new_document = From ab1f4cf297438e98f2af9d762ddce841eb4f7f09 Mon Sep 17 00:00:00 2001 From: Pete Matsyburka Date: Tue, 10 Dec 2024 23:25:53 +0200 Subject: [PATCH 04/16] refactor clone --- lib/templates/clone.rb | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/lib/templates/clone.rb b/lib/templates/clone.rb index 60739c2e..cd1118a3 100644 --- a/lib/templates/clone.rb +++ b/lib/templates/clone.rb @@ -12,7 +12,16 @@ module Templates template.preferences = original_template.preferences.deep_dup template.name = name.presence || "#{original_template.name} (#{I18n.t('clone')})" - template.assign_attributes(original_template.slice(:folder_id, :schema).deep_dup) + if folder_name.present? + template.folder = TemplateFolders.find_or_create_by_name(author, folder_name) + else + template.folder_id = original_template.folder_id + end + + template.submitters, template.fields, template.schema = + update_submitters_and_fields_and_schema(original_template.submitters.deep_dup, + original_template.fields.deep_dup, + original_template.schema.deep_dup) if name.present? && template.schema.size == 1 && original_template.schema.first['name'] == original_template.name && @@ -20,20 +29,13 @@ module Templates template.schema.first['name'] = template.name end - template.folder = TemplateFolders.find_or_create_by_name(author, folder_name) if folder_name.present? - - template.submitters, template.fields = clone_submitters_and_fields(original_template) - template end - def clone_submitters_and_fields(original_template) + def update_submitters_and_fields_and_schema(cloned_submitters, cloned_fields, cloned_schema) submitter_uuids_replacements = {} field_uuids_replacements = {} - cloned_submitters = original_template['submitters'].deep_dup - cloned_fields = original_template['fields'].deep_dup - cloned_submitters.each do |submitter| new_submitter_uuid = SecureRandom.uuid @@ -50,20 +52,28 @@ module Templates field['submitter_uuid'] = submitter_uuids_replacements[field['submitter_uuid']] end - replace_fields_regexp = Regexp.union(field_uuids_replacements.keys) + replace_fields_regexp = nil cloned_fields.each do |field| Array.wrap(field['conditions']).each do |condition| condition['field_uuid'] = field_uuids_replacements[condition['field_uuid']] end - if field.dig('preferences', 'formula').present? - field['preferences']['formula'] = - field['preferences']['formula'].gsub(replace_fields_regexp, field_uuids_replacements) + next if field.dig('preferences', 'formula').blank? + + replace_fields_regexp ||= Regexp.union(field_uuids_replacements.keys) + + field['preferences']['formula'] = + field['preferences']['formula'].gsub(replace_fields_regexp, field_uuids_replacements) + end + + cloned_schema.each do |field| + Array.wrap(field['conditions']).each do |condition| + condition['field_uuid'] = field_uuids_replacements[condition['field_uuid']] end end - [cloned_submitters, cloned_fields] + [cloned_submitters, cloned_fields, cloned_schema] end end end From b317094192c7544a0190a9d15275a14b616fad0c Mon Sep 17 00:00:00 2001 From: Pete Matsyburka Date: Wed, 11 Dec 2024 19:32:29 +0200 Subject: [PATCH 05/16] custom field width height --- app/javascript/template_builder/builder.vue | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/javascript/template_builder/builder.vue b/app/javascript/template_builder/builder.vue index f140d7b3..43cac48b 100644 --- a/app/javascript/template_builder/builder.vue +++ b/app/javascript/template_builder/builder.vue @@ -1304,6 +1304,16 @@ export default { fieldArea.h = lastArea.h } + if (field.width) { + fieldArea.w = field.width / area.maskW + delete field.width + } + + if (field.height) { + fieldArea.h = field.height / area.maskH + delete field.height + } + field.areas.push(fieldArea) this.selectedAreaRef.value = fieldArea From b6a2aae97041a82fced56ea1d6475d1a2ac70c27 Mon Sep 17 00:00:00 2001 From: Alex Turchyn Date: Tue, 10 Dec 2024 23:18:07 +0200 Subject: [PATCH 06/16] restrict user invites --- app/controllers/users_controller.rb | 19 +++------------ config/locales/i18n.yml | 6 +++++ spec/factories/accounts.rb | 15 ++++++++++++ spec/rails_helper.rb | 4 +++ spec/system/team_settings_spec.rb | 38 +++++++++++++++++++++++++++++ 5 files changed, 67 insertions(+), 15 deletions(-) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index d11e9ba8..4a195104 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -24,14 +24,10 @@ class UsersController < ApplicationController def edit; end def create - existing_user = User.accessible_by(current_ability).find_by(email: @user.email) + if User.accessible_by(current_ability).exists?(email: @user.email) + @user.errors.add(:email, I18n.t('already_exists')) - if existing_user - existing_user.archived_at = nil - existing_user.assign_attributes(user_params) - existing_user.account = current_account - - @user = existing_user + return render turbo_stream: turbo_stream.replace(:modal, template: 'users/new'), status: :unprocessable_entity end @user.role = User::ADMIN_ROLE unless role_valid?(@user.role) @@ -83,14 +79,7 @@ class UsersController < ApplicationController end def build_user - @user = current_account.users.find_by(email: user_params[:email])&.tap do |user| - user.assign_attributes(user_params) - user.archived_at = nil - end - - @user ||= current_account.users.new(user_params) - - @user + @user = current_account.users.new(user_params) end def user_params diff --git a/config/locales/i18n.yml b/config/locales/i18n.yml index 87c69fcd..3e142f59 100644 --- a/config/locales/i18n.yml +++ b/config/locales/i18n.yml @@ -660,6 +660,7 @@ en: &en policy_links: Policy Links markdown_content_e_g: Markdown content, e.g. privacy_policy: Privacy Policy + use_the_edit_form_to_move_it_to_another_team: Use the edit form to move it to another team. submission_event_names: send_email_to_html: 'Email sent to %{submitter_name}' send_reminder_email_to_html: 'Reminder email sent to %{submitter_name}' @@ -1340,6 +1341,7 @@ es: &es policy_links: Enlaces de Políticas markdown_content_e_g: Contenido Markdown, por ej. privacy_policy: Política de Privacidad + use_the_edit_form_to_move_it_to_another_team: Usa el formulario de edición para moverlo a otro equipo. submission_event_names: send_email_to_html: 'Correo electrónico enviado a %{submitter_name}' send_reminder_email_to_html: 'Correo de recordatorio enviado a %{submitter_name}' @@ -2020,6 +2022,7 @@ it: &it policy_links: Collegamenti alle Politiche markdown_content_e_g: Contenuto Markdown, ad es. privacy_policy: Politica sulla Privacy + use_the_edit_form_to_move_it_to_another_team: Usa il modulo di modifica per spostarlo in un altro team. submission_event_names: send_email_to_html: 'E-mail inviato a %{submitter_name}' send_reminder_email_to_html: 'E-mail di promemoria inviato a %{submitter_name}' @@ -2701,6 +2704,7 @@ fr: &fr policy_links: Liens des Politiques markdown_content_e_g: Contenu Markdown, par ex. privacy_policy: Politique de Confidentialité + use_the_edit_form_to_move_it_to_another_team: Utilisez le formulaire de modification pour le déplacer vers une autre équipe. submission_event_names: send_email_to_html: 'E-mail envoyé à %{submitter_name}' send_reminder_email_to_html: 'E-mail de rappel envoyé à %{submitter_name}' @@ -3381,6 +3385,7 @@ pt: &pt policy_links: Links de Políticas markdown_content_e_g: Conteúdo Markdown, ex. privacy_policy: Política de Privacidade + use_the_edit_form_to_move_it_to_another_team: Use o formulário de edição para movê-lo para outra equipe. submission_event_names: send_email_to_html: 'E-mail enviado para %{submitter_name}' send_reminder_email_to_html: 'E-mail de lembrete enviado para %{submitter_name}' @@ -4061,6 +4066,7 @@ de: &de policy_links: Richtlinien-Links markdown_content_e_g: Markdown-Inhalt, z. B. privacy_policy: Datenschutzrichtlinie + use_the_edit_form_to_move_it_to_another_team: Verwenden Sie das Bearbeitungsformular, um ihn in ein anderes Team zu verschieben. submission_event_names: send_email_to_html: 'E-Mail gesendet an %{submitter_name}' send_reminder_email_to_html: 'Erinnerungs-E-Mail gesendet an %{submitter_name}' diff --git a/spec/factories/accounts.rb b/spec/factories/accounts.rb index b0551e06..6a5229bf 100644 --- a/spec/factories/accounts.rb +++ b/spec/factories/accounts.rb @@ -6,6 +6,10 @@ FactoryBot.define do locale { 'en-US' } timezone { 'UTC' } + transient do + teams_count { 2 } + end + trait :with_testing_account do after(:create) do |account| testing_account = account.dup.tap { |a| a.name = "Testing - #{account.name}" } @@ -14,5 +18,16 @@ FactoryBot.define do account.save! end end + + trait :with_teams do + after(:create) do |account, evaluator| + Array.new(evaluator.teams_count) do |i| + Account.create!( + name: "Team #{i}", + linked_account_account: AccountLinkedAccount.new(account_type: :linked, account:) + ) + end + end + end end end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 1157da61..e2195ceb 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -68,6 +68,10 @@ RSpec.configure do |config| config.before do |example| Sidekiq::Testing.inline! if example.metadata[:sidekiq] == :inline end + + config.before(multitenant: true) do + allow(Docuseal).to receive(:multitenant?).and_return(true) + end end ActiveSupport.run_load_hooks(:rails_specs, self) diff --git a/spec/system/team_settings_spec.rb b/spec/system/team_settings_spec.rb index 3472917a..077c5104 100644 --- a/spec/system/team_settings_spec.rb +++ b/spec/system/team_settings_spec.rb @@ -4,6 +4,7 @@ require 'rails_helper' RSpec.describe 'Team Settings' do let(:account) { create(:account) } + let(:second_account) { create(:account) } let(:current_user) { create(:user, account:) } before do @@ -56,6 +57,43 @@ RSpec.describe 'Team Settings' do end end + it "doesn't create a new user if a user already exists" do + click_link 'New User' + + within '#modal' do + fill_in 'First name', with: 'Michael' + fill_in 'Last name', with: 'Jordan' + fill_in 'Email', with: users.first.email + fill_in 'Password', with: 'password' + + expect do + click_button 'Submit' + end.not_to change(User, :count) + end + + expect(page).to have_content('Email already exists') + end + + it "doesn't create a new user if a user belongs to another account" do + user = create(:user, account: second_account) + visit settings_users_path + + click_link 'New User' + + within '#modal' do + fill_in 'First name', with: 'Michael' + fill_in 'Last name', with: 'Jordan' + fill_in 'Email', with: user.email + fill_in 'Password', with: 'password' + + expect do + click_button 'Submit' + end.not_to change(User, :count) + + expect(page).to have_content('Email has already been taken') + end + end + it 'updates a user' do first(:link, 'Edit').click From 1881b6c50f8263a7e0b8a2bc201a5abfa160245f Mon Sep 17 00:00:00 2001 From: Pete Matsyburka Date: Thu, 12 Dec 2024 18:38:14 +0200 Subject: [PATCH 07/16] add sidekiq queue --- config/sidekiq.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/config/sidekiq.yml b/config/sidekiq.yml index 2df9d73e..01ba85a5 100644 --- a/config/sidekiq.yml +++ b/config/sidekiq.yml @@ -1,6 +1,7 @@ queues: - [default, 1] - [webhooks, 1] + - [sms, 2] - [images, 1] - [mailers, 1] - [recurrent, 1] From 5340af0bc692b3036a04c0001ab357bd55587278 Mon Sep 17 00:00:00 2001 From: Pete Matsyburka Date: Thu, 12 Dec 2024 19:16:24 +0200 Subject: [PATCH 08/16] remove reason: --- app/javascript/submission_form/area.vue | 2 +- app/views/submissions/_value.html.erb | 3 ++- lib/submissions/generate_result_attachments.rb | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/javascript/submission_form/area.vue b/app/javascript/submission_form/area.vue index 2c3fc5d4..b969f644 100644 --- a/app/javascript/submission_form/area.vue +++ b/app/javascript/submission_form/area.vue @@ -74,7 +74,7 @@ ID: {{ signature.uuid }}
- {{ t('reason') }}: {{ values[field.preferences?.reason_field_uuid] || t('digitally_signed_by') }} {{ submitter.name }} + {{ t('reason') }}: {{ values[field.preferences?.reason_field_uuid] || t('digitally_signed_by') }} {{ submitter.name }} diff --git a/app/views/submissions/_value.html.erb b/app/views/submissions/_value.html.erb index ad501cac..ab269352 100644 --- a/app/views/submissions/_value.html.erb +++ b/app/views/submissions/_value.html.erb @@ -12,7 +12,8 @@ ID: <%= attachment.uuid %>
- <%= t('reason') %>: <%= submitter.values[field.dig('preferences', 'reason_field_uuid')].presence || t('digitally_signed_by') %> <%= submitter.name %> + <% reason_value = submitter.values[field.dig('preferences', 'reason_field_uuid')].presence %> + <% if reason_value %><%= t('reason') %>: <% end %><%= reason_value || t('digitally_signed_by') %> <%= submitter.name %> <% if submitter.email %> <<%= submitter.email %>> <% end %> diff --git a/lib/submissions/generate_result_attachments.rb b/lib/submissions/generate_result_attachments.rb index 317fdc37..8842e9d9 100644 --- a/lib/submissions/generate_result_attachments.rb +++ b/lib/submissions/generate_result_attachments.rb @@ -236,7 +236,7 @@ module Submissions reason_string = I18n.with_locale(submitter.account.locale) do - "#{I18n.t('reason')}: #{reason_value || I18n.t('digitally_signed_by')} " \ + "#{reason_value ? "#{I18n.t('reason')}: " : ''}#{reason_value || I18n.t('digitally_signed_by')} " \ "#{submitter.name}#{submitter.email.present? ? " <#{submitter.email}>" : ''}\n" \ "#{I18n.l(attachment.created_at.in_time_zone(submitter.account.timezone), format: :long)} " \ "#{TimeUtils.timezone_abbr(submitter.account.timezone, attachment.created_at)}" From 29fbdfbe2fce2a4fa46e4eb12388430338f40da5 Mon Sep 17 00:00:00 2001 From: Pete Matsyburka Date: Fri, 13 Dec 2024 00:24:29 +0200 Subject: [PATCH 09/16] cells align --- app/javascript/submission_form/area.vue | 1 + .../template_builder/field_settings.vue | 4 ++-- app/views/submissions/_value.html.erb | 2 +- lib/submissions/generate_result_attachments.rb | 15 ++++++++++++--- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/app/javascript/submission_form/area.vue b/app/javascript/submission_form/area.vue index b969f644..d3f7015e 100644 --- a/app/javascript/submission_form/area.vue +++ b/app/javascript/submission_form/area.vue @@ -168,6 +168,7 @@
@@ -36,7 +36,7 @@ @change="[field.preferences ||= {}, field.preferences.align = $event.target.value, save()]" >