diff --git a/.rubocop.yml b/.rubocop.yml index 294fc484..ffb49de1 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -48,6 +48,9 @@ Style/MultipleComparison: Style/NumericPredicate: Enabled: false +Style/MinMaxComparison: + Enabled: false + Naming/PredicateMethod: Enabled: false diff --git a/Dockerfile b/Dockerfile index 8d1f58c5..3ce83e55 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,7 +9,7 @@ RUN apk --no-cache add fontforge wget && \ wget https://cdn.jsdelivr.net/gh/notofonts/notofonts.github.io/fonts/NotoSansSymbols2/hinted/ttf/NotoSansSymbols2-Regular.ttf && \ wget https://github.com/Maxattax97/gnu-freefont/raw/master/ttf/FreeSans.ttf && \ wget https://github.com/impallari/DancingScript/raw/master/OFL.txt && \ - wget -O /model.onnx "https://github.com/docusealco/fields-detection/releases/download/2.0.0/model_704_int8.onnx" && \ + wget -O /model.onnx "https://github.com/docusealco/fields-detection/releases/download/2.1.0/model_704_int8.onnx" && \ wget -O pdfium-linux.tgz "https://github.com/docusealco/pdfium-binaries/releases/latest/download/pdfium-linux-$(uname -m | sed 's/x86_64/x64/;s/aarch64/arm64/').tgz" && \ mkdir -p /pdfium-linux && \ tar -xzf pdfium-linux.tgz -C /pdfium-linux @@ -53,6 +53,8 @@ WORKDIR /app RUN apk add --no-cache sqlite-dev libpq-dev mariadb-dev vips-dev yaml-dev redis libheif vips-heif gcompat ttf-freefont && mkdir /fonts && rm /usr/share/fonts/freefont/FreeSans.otf +RUN addgroup -g 2000 docuseal && adduser -u 2000 -G docuseal -s /bin/sh -D -h /home/docuseal docuseal + RUN echo $'.include = /etc/ssl/openssl.cnf\n\ \n\ [provider_sect]\n\ @@ -92,7 +94,10 @@ COPY --from=webpack /app/public/packs ./public/packs RUN ln -s /fonts /app/public/fonts RUN bundle exec bootsnap precompile -j 1 --gemfile app/ lib/ +RUN chown -R docuseal:docuseal /app + WORKDIR /data/docuseal +ENV HOME=/home/docuseal ENV WORKDIR=/data/docuseal EXPOSE 3000 diff --git a/app/controllers/api/submission_documents_controller.rb b/app/controllers/api/submission_documents_controller.rb index 148d499f..56940f0f 100644 --- a/app/controllers/api/submission_documents_controller.rb +++ b/app/controllers/api/submission_documents_controller.rb @@ -5,34 +5,17 @@ module Api load_and_authorize_resource :submission def index + is_merge = params[:merge] == 'true' && + (@submission.schema_documents || @submission.template.schema_documents).size > 1 + documents = if @submission.submitters.all?(&:completed_at?) - last_submitter = @submission.submitters.max_by(&:completed_at) - - if last_submitter.documents_attachments.blank? - last_submitter.documents_attachments = Submissions::EnsureResultGenerated.call(last_submitter) - end - - last_submitter.documents_attachments + build_completed_documents(@submission, merge: is_merge) else - values_hash = Submissions::GeneratePreviewAttachments.build_values_hash(@submission) - - if @submission.preview_documents.present? && - @submission.preview_documents.all? { |s| s.metadata['values_hash'] == values_hash } - @submission.preview_documents - else - ApplicationRecord.no_touching do - @submission.preview_documents.each(&:destroy) - end - - Submissions::GeneratePreviewAttachments.call(@submission, values_hash:) - end + build_preview_documents(@submission, merge: is_merge) end - ActiveRecord::Associations::Preloader.new( - records: documents, - associations: [:blob] - ).call + ActiveRecord::Associations::Preloader.new(records: documents, associations: [:blob]).call expires_at = Accounts.link_expires_at(current_account) @@ -43,5 +26,50 @@ module Api end } end + + private + + def build_completed_documents(submission, merge: false) + last_submitter = submission.submitters.max_by(&:completed_at) + + if merge + if submission.merged_document_attachment.blank? + submission.merged_document_attachment = + Submissions::GenerateCombinedAttachment.call(last_submitter, with_audit: false) + end + + [submission.merged_document_attachment] + else + if last_submitter.documents_attachments.blank? + last_submitter.documents_attachments = Submissions::EnsureResultGenerated.call(last_submitter) + end + + last_submitter.documents_attachments + end + end + + def build_preview_documents(submission, merge: false) + values_hash = Submissions::GeneratePreviewAttachments.build_values_hash(submission) + + if merge + if submission.preview_merged_document_attachment.present? && + submission.preview_merged_document_attachment.metadata['values_hash'] == values_hash + [submission.preview_merged_document_attachment] + else + ApplicationRecord.no_touching { submission.preview_merged_document_attachment&.destroy } + + Submissions::GeneratePreviewAttachments.call(submission, values_hash:, merge: true) + end + elsif submission.preview_documents.present? && + submission.preview_documents.all? { |s| s.metadata['values_hash'] == values_hash } + submission.preview_documents + else + ApplicationRecord.no_touching do + submission.preview_documents.each(&:destroy) + end + + Submissions::GeneratePreviewAttachments.call(submission, values_hash:) + end + end end end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index cd8d228c..191f1dc8 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -17,6 +17,7 @@ class ApplicationController < ActionController::Base helper_method :button_title, :current_account, + :true_ability, :form_link_host, :svg_icon @@ -102,6 +103,10 @@ class ApplicationController < ActionController::Base current_user&.account end + def true_ability + @true_ability ||= Ability.new(true_user) + end + def maybe_redirect_to_setup redirect_to setup_index_path unless User.exists? end diff --git a/app/controllers/templates_clone_controller.rb b/app/controllers/templates_clone_controller.rb new file mode 100644 index 00000000..802a97e0 --- /dev/null +++ b/app/controllers/templates_clone_controller.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +class TemplatesCloneController < ApplicationController + load_and_authorize_resource :template, instance_name: :base_template + + def new + authorize!(:create, Template) + + @template = Template.new(name: "#{@base_template.name} (#{I18n.t('clone')})") + end + + def create + ActiveRecord::Associations::Preloader.new( + records: [@base_template], + associations: [schema_documents: :preview_images_attachments] + ).call + + @template = Templates::Clone.call(@base_template, author: current_user, + name: params.dig(:template, :name), + folder_name: params[:folder_name]) + + authorize!(:create, @template) + + if params[:account_id].present? && true_ability.authorize!(:manage, Account.find(params[:account_id])) + @template.account_id = params[:account_id] + @template.author = true_user if true_user.account_id == @template.account_id + @template.folder = @template.account.default_template_folder if @template.account_id != current_account.id + else + @template.account = current_account + end + + Templates.maybe_assign_access(@template) + + if @template.save + Templates::CloneAttachments.call(template: @template, original_template: @base_template) + + SearchEntries.enqueue_reindex(@template) + + WebhookUrls.enqueue_events(@template, 'template.created') + + maybe_redirect_to_template(@template) + else + render turbo_stream: turbo_stream.replace(:modal, partial: 'templates_clone/form'), status: :unprocessable_content + end + end + + private + + def maybe_redirect_to_template(template) + if template.account == current_account + redirect_to(edit_template_path(template)) + else + redirect_back(fallback_location: root_path, notice: I18n.t('template_has_been_cloned')) + end + end +end diff --git a/app/controllers/templates_controller.rb b/app/controllers/templates_controller.rb index c96360d9..39b45044 100644 --- a/app/controllers/templates_controller.rb +++ b/app/controllers/templates_controller.rb @@ -3,8 +3,6 @@ class TemplatesController < ApplicationController load_and_authorize_resource :template - before_action :load_base_template, only: %i[new create] - def show submissions = @template.submissions.accessible_by(current_ability) submissions = submissions.active if @template.archived_at.blank? @@ -26,9 +24,7 @@ class TemplatesController < ApplicationController redirect_to root_path end - def new - @template.name = "#{@base_template.name} (#{I18n.t('clone')})" if @base_template - end + def new; end def edit ActiveRecord::Associations::Preloader.new( @@ -48,37 +44,18 @@ class TemplatesController < ApplicationController end def create - if @base_template - ActiveRecord::Associations::Preloader.new( - records: [@base_template], - associations: [schema_documents: :preview_images_attachments] - ).call - - @template = Templates::Clone.call(@base_template, author: current_user, - name: params.dig(:template, :name), - folder_name: params[:folder_name]) - else - @template.author = current_user - @template.folder = TemplateFolders.find_or_create_by_name(current_user, params[:folder_name]) - end - - if params[:account_id].present? && authorized_clone_account_id?(params[:account_id]) - @template.account_id = params[:account_id] - @template.folder = @template.account.default_template_folder if @template.account_id != current_account.id - else - @template.account = current_account - end + @template.author = current_user + @template.folder = TemplateFolders.find_or_create_by_name(current_user, params[:folder_name]) + @template.account = current_account Templates.maybe_assign_access(@template) if @template.save - Templates::CloneAttachments.call(template: @template, original_template: @base_template) if @base_template - SearchEntries.enqueue_reindex(@template) WebhookUrls.enqueue_events(@template, 'template.created') - maybe_redirect_to_template(@template) + redirect_to(edit_template_path(@template)) else render turbo_stream: turbo_stream.replace(:modal, template: 'templates/new'), status: :unprocessable_content end @@ -132,23 +109,4 @@ class TemplatesController < ApplicationController areas: [%i[x y w h cell_w attachment_uuid option_uuid page]] }]] } ) end - - def authorized_clone_account_id?(account_id) - true_user.account_id.to_s == account_id.to_s || - true_user.account.linked_accounts.accessible_by(current_ability).exists?(id: account_id) - end - - def maybe_redirect_to_template(template) - if template.account == current_account - redirect_to(edit_template_path(@template)) - else - redirect_back(fallback_location: root_path, notice: I18n.t('template_has_been_cloned')) - end - end - - def load_base_template - return if params[:base_template_id].blank? - - @base_template = Template.accessible_by(current_ability).find_by(id: params[:base_template_id]) - end end diff --git a/app/javascript/submission_form/area.vue b/app/javascript/submission_form/area.vue index 1740fe0e..30a0255e 100644 --- a/app/javascript/submission_form/area.vue +++ b/app/javascript/submission_form/area.vue @@ -461,7 +461,7 @@ export default { phone: IconPhoneCheck, payment: IconCreditCard, verification: IconId, - kba: IconUserScan, + kba: IconUserScan } }, image () { diff --git a/app/javascript/submission_form/kba_step.vue b/app/javascript/submission_form/kba_step.vue index 9abe24f0..15958ad3 100644 --- a/app/javascript/submission_form/kba_step.vue +++ b/app/javascript/submission_form/kba_step.vue @@ -10,9 +10,15 @@ {{ field.name || 'Knowledge Based Authentication' }} - Question {{ currentQuestionIndex + 1 }} / {{ questions.length }} + Question {{ currentQuestionIndex + 1 }} / {{ questions.length }} + + Time left: {{ formattedTimeLeft }} +