diff --git a/Dockerfile b/Dockerfile index 3f6bb3c6..a4896f63 100644 --- a/Dockerfile +++ b/Dockerfile @@ -48,7 +48,8 @@ ENV OPENSSL_CONF=/etc/openssl_legacy.cnf WORKDIR /app -RUN apk add --no-cache libpq vips redis onnxruntime +RUN apk add --no-cache libpq vips redis onnxruntime && \ + rm -f /usr/bin/onnx_test_runner /usr/bin/onnxruntime_test RUN addgroup -g 2000 docuseal && adduser -u 2000 -G docuseal -s /bin/sh -D -h /home/docuseal docuseal diff --git a/Gemfile b/Gemfile index 7ed8fdeb..36783f0d 100644 --- a/Gemfile +++ b/Gemfile @@ -21,7 +21,6 @@ gem 'faraday' gem 'faraday-follow_redirects' gem 'google-cloud-storage', require: false gem 'hexapdf' -gem 'image_processing' gem 'jwt', require: false gem 'lograge' gem 'numo-narray-alt', require: false diff --git a/Gemfile.lock b/Gemfile.lock index 8d15dd29..933cf9a8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -264,9 +264,6 @@ GEM strscan (>= 3.1.2) i18n (1.14.8) concurrent-ruby (~> 1.0) - image_processing (1.14.0) - mini_magick (>= 4.9.5, < 6) - ruby-vips (>= 2.0.17, < 3) io-console (0.8.2) irb (1.18.0) pp (>= 0.6.0) @@ -274,7 +271,7 @@ GEM rdoc (>= 4.0.0) reline (>= 0.4.2) jmespath (1.6.2) - json (2.19.5) + json (2.19.7) jwt (3.2.0) base64 language_server-protocol (3.17.0.5) @@ -308,8 +305,6 @@ GEM marcel (1.1.0) matrix (0.4.3) method_source (1.1.0) - mini_magick (5.3.1) - logger mini_mime (1.1.5) minitest (6.0.6) drb (~> 2.0) @@ -434,7 +429,7 @@ GEM erb psych (>= 4.0.0) tsort - redis-client (0.28.0) + redis-client (0.29.0) connection_pool regexp_parser (2.11.3) reline (0.6.3) @@ -516,12 +511,12 @@ GEM rack-proxy (>= 0.6.1) railties (>= 5.2) semantic_range (>= 2.3.0) - sidekiq (8.1.2) + sidekiq (8.1.6) connection_pool (>= 3.0.0) json (>= 2.16.0) logger (>= 1.7.0) rack (>= 3.2.0) - redis-client (>= 0.26.0) + redis-client (>= 0.29.0) signet (0.21.0) addressable (~> 2.8) faraday (>= 0.17.5, < 3.a) @@ -624,7 +619,6 @@ DEPENDENCIES foreman google-cloud-storage hexapdf - image_processing jwt letter_opener_web lograge diff --git a/app/controllers/api/attachments_controller.rb b/app/controllers/api/attachments_controller.rb index 1d24f8ca..2bb0db92 100644 --- a/app/controllers/api/attachments_controller.rb +++ b/app/controllers/api/attachments_controller.rb @@ -11,8 +11,6 @@ module Api @submitter = Submitter.find_by!(slug: params[:submitter_slug]) unless can_upload?(@submitter) - Rollbar.error("Can't upload: #{@submitter.id}") if defined?(Rollbar) - return render json: { error: I18n.t('form_has_been_archived') }, status: :unprocessable_content end @@ -33,9 +31,11 @@ module Api return render json: { error: "#{params[:type]} error, try to sign on another device" }, status: :unprocessable_content end + + metadata = { analyzed: true, identified: true, width: image.width, height: image.height } end - attachment = Submitters.create_attachment!(@submitter, file) + attachment = Submitters.create_attachment!(@submitter, file, metadata:) if params[:remember_signature] == 'true' && @submitter.email.present? cookies.encrypted[:signature_uuids] = build_new_cookie_signatures_json(@submitter, attachment) diff --git a/app/controllers/api/templates_controller.rb b/app/controllers/api/templates_controller.rb index 87b756ea..b3d05cec 100644 --- a/app/controllers/api/templates_controller.rb +++ b/app/controllers/api/templates_controller.rb @@ -9,20 +9,7 @@ module Api templates = paginate(templates.preload(:author, folder: :parent_folder)) - schema_documents = - ActiveStorage::Attachment.where(record_id: templates.map(&:id), - record_type: 'Template', - name: :documents, - uuid: templates.flat_map { |t| t.schema.pluck('attachment_uuid') }) - .preload(:blob) - - preview_image_attachments = - ActiveStorage::Attachment.joins(:blob) - .where(blob: { filename: ['0.png', '0.jpg'] }) - .where(record_id: schema_documents.map(&:id), - record_type: 'ActiveStorage::Attachment', - name: :preview_images) - .preload(:blob) + schema_documents, dynamic_documents, preview_image_attachments = preload_relations(templates) expires_at = Accounts.link_expires_at(current_account) @@ -30,6 +17,7 @@ module Api data: templates.map do |t| Templates::SerializeForApi.call(t, schema_documents: schema_documents.select { |e| e.record_id == t.id }, + dynamic_documents:, preview_image_attachments:, expires_at:) end, @@ -88,6 +76,41 @@ module Api private + def preload_relations(templates) + schema_documents = + ActiveStorage::Attachment.where(record_id: templates.map(&:id), + record_type: 'Template', + name: :documents, + uuid: templates.flat_map { |t| t.schema.pluck('attachment_uuid') }) + .preload(:blob) + + dynamic_document_uuids = + templates.flat_map { |t| t.schema.select { |item| item['dynamic'] }.pluck('attachment_uuid') } + + dynamic_documents = + if dynamic_document_uuids.present? + DynamicDocument.where(template: templates.map(&:id)) + .where(uuid: dynamic_document_uuids) + .preload(current_version: { document_attachment: :blob }) + .select(:id, :uuid, :template_id, :sha1, :created_at, :updated_at) + else + DynamicDocument.none + end + + preview_attachment_ids = + schema_documents.map(&:id) + dynamic_documents.filter_map { |d| d.current_version&.document_attachment&.id } + + preview_image_attachments = + ActiveStorage::Attachment.joins(:blob) + .where(blob: { filename: ['0.png', '0.jpg'] }) + .where(record_id: preview_attachment_ids, + record_type: 'ActiveStorage::Attachment', + name: :preview_images) + .preload(:blob) + + [schema_documents, dynamic_documents, preview_image_attachments] + end + def filter_templates(templates, params) templates = Templates.search(current_user, templates, params[:q]) templates = params[:archived].in?(['true', true]) ? templates.archived : templates.active diff --git a/app/controllers/start_form_controller.rb b/app/controllers/start_form_controller.rb index ed9c2629..22fa4ffa 100644 --- a/app/controllers/start_form_controller.rb +++ b/app/controllers/start_form_controller.rb @@ -101,11 +101,18 @@ class StartFormController < ApplicationController def load_resubmit_submitter @resubmit_submitter = if params[:resubmit].present? && !params[:resubmit].in?([true, 'true']) - Submitter.find_by(slug: params[:resubmit]) + submitter = Submitter.find_by(slug: params[:resubmit]) + + submitter if submitter && can_resubmit?(submitter) end end + def can_resubmit?(submitter) + submitter.account.account_configs.find_or_initialize_by(key: AccountConfig::ALLOW_TO_RESUBMIT).value != false + end + def authorize_start! + return redirect_to submit_form_path(@resubmit_submitter.slug) if @resubmit_submitter && @template.archived_at? return redirect_to start_form_path(@template.slug) if @template.archived_at? return if @resubmit_submitter diff --git a/app/controllers/templates_controller.rb b/app/controllers/templates_controller.rb index f32e1e1e..ae54665a 100644 --- a/app/controllers/templates_controller.rb +++ b/app/controllers/templates_controller.rb @@ -1,10 +1,6 @@ # frozen_string_literal: true class TemplatesController < ApplicationController - TEMPLATE_FIELDS = %i[id author_id folder_id external_id name slug - schema fields submitters variables_schema preferences - shared_link source archived_at created_at updated_at].freeze - load_and_authorize_resource :template def show @@ -31,19 +27,7 @@ class TemplatesController < ApplicationController def new; end def edit - ActiveRecord::Associations::Preloader.new( - records: [@template], - associations: [{ schema_documents: [:blob, { preview_images_attachments: :blob }] }] - ).call - - @template_data = - @template.as_json(only: TEMPLATE_FIELDS).merge( - documents: @template.schema_documents.as_json( - only: %i[id uuid], - methods: %i[metadata signed_key], - include: { preview_images: { only: %i[id], methods: %i[url metadata filename] } } - ) - ).to_json + @template_data = Templates.serialize_for_builder(@template) render :edit, layout: 'plain' end diff --git a/app/controllers/templates_detect_fields_controller.rb b/app/controllers/templates_detect_fields_controller.rb index 56b06cd5..a0cd54ff 100644 --- a/app/controllers/templates_detect_fields_controller.rb +++ b/app/controllers/templates_detect_fields_controller.rb @@ -16,7 +16,12 @@ class TemplatesDetectFieldsController < ApplicationController page_number = params[:page].presence&.to_i documents.each do |document| - io = StringIO.new(document.download) + io = + if document.image? + StringIO.new(document.preview_images.joins(:blob).find_by(blob: { filename: ['0.png', '0.jpg'] }).download) + else + StringIO.new(document.download) + end Templates::DetectFields.call(io, attachment: document, page_number:) do |(attachment_uuid, page, fields)| sse.write({ attachment_uuid:, page:, fields: }) diff --git a/app/controllers/templates_preview_controller.rb b/app/controllers/templates_preview_controller.rb index e132b131..602fc622 100644 --- a/app/controllers/templates_preview_controller.rb +++ b/app/controllers/templates_preview_controller.rb @@ -4,18 +4,7 @@ class TemplatesPreviewController < ApplicationController load_and_authorize_resource :template def show - ActiveRecord::Associations::Preloader.new( - records: [@template], - associations: [{ schema_documents: { preview_images_attachments: :blob } }] - ).call - - @template_data = - @template.as_json.merge( - documents: @template.schema_documents.as_json( - methods: %i[metadata signed_key], - include: { preview_images: { methods: %i[url metadata filename] } } - ) - ).to_json + @template_data = Templates.serialize_for_builder(@template) render :show, layout: 'plain' end diff --git a/app/controllers/templates_uploads_controller.rb b/app/controllers/templates_uploads_controller.rb index 7d8b1bae..ea4febf0 100644 --- a/app/controllers/templates_uploads_controller.rb +++ b/app/controllers/templates_uploads_controller.rb @@ -5,7 +5,9 @@ class TemplatesUploadsController < ApplicationController layout 'plain' - def show; end + def show + redirect_to root_path if params[:url].blank? + end def create url_params = create_file_params_from_url if params[:url].present? diff --git a/app/javascript/application.js b/app/javascript/application.js index bec3cc0b..6aa39fc8 100644 --- a/app/javascript/application.js +++ b/app/javascript/application.js @@ -54,6 +54,7 @@ import GoogleDriveFilePicker from './elements/google_drive_file_picker' import OpenModal from './elements/open_modal' import BarChart from './elements/bar_chart' import FieldCondition from './elements/field_condition' +import ConfirmUpload from './elements/confirm_upload' import * as TurboInstantClick from './lib/turbo_instant_click' @@ -146,6 +147,7 @@ safeRegisterElement('google-drive-file-picker', GoogleDriveFilePicker) safeRegisterElement('open-modal', OpenModal) safeRegisterElement('bar-chart', BarChart) safeRegisterElement('field-condition', FieldCondition) +safeRegisterElement('confirm-upload', ConfirmUpload) safeRegisterElement('template-builder', class extends HTMLElement { connectedCallback () { @@ -197,10 +199,35 @@ safeRegisterElement('template-builder', class extends HTMLElement { } onSubmit = (e) => { - if (e.detail.success && e.detail?.formSubmission?.formElement?.id === 'submitters_form') { - e.detail.fetchResponse.response.json().then((data) => { - this.component.template.submitters = data.submitters - }) + if (e.detail.success) { + if (e.detail?.formSubmission?.formElement?.id === 'submitters_form') { + e.detail.fetchResponse.response.json().then((data) => { + this.component.template.submitters = data.submitters + }) + } + + if (e.detail?.formSubmission?.formElement?.action?.endsWith('/prefillable_fields')) { + e.detail.fetchResponse.response.text().then((data) => { + const doc = new DOMParser().parseFromString(data, 'text/html') + const fragment = doc.querySelector('turbo-stream template').content + + const prefillableUuidsIndex = {} + + fragment.querySelectorAll('[name="field_uuid"]').forEach((field) => { + prefillableUuidsIndex[field.value] = true + }) + + this.component.template.fields.forEach((field) => { + if (prefillableUuidsIndex[field.uuid]) { + field.prefillable = true + field.readonly = true + } else if (field.prefillable) { + delete field.prefillable + delete field.readonly + } + }) + }) + } } } diff --git a/app/javascript/elements/confirm_upload.js b/app/javascript/elements/confirm_upload.js new file mode 100644 index 00000000..aa39601d --- /dev/null +++ b/app/javascript/elements/confirm_upload.js @@ -0,0 +1,27 @@ +import { target, targetable } from '@github/catalyst/lib/targetable' + +export default targetable(class extends HTMLElement { + static [target.static] = [ + 'prompt', + 'processing', + 'logo' + ] + + connectedCallback () { + this.form.addEventListener('submit', this.onSubmit) + } + + disconnectedCallback () { + this.form.removeEventListener('submit', this.onSubmit) + } + + onSubmit = () => { + this.prompt.classList.add('hidden') + this.processing.classList.remove('hidden') + this.logo.classList.add('animate-bounce') + } + + get form () { + return this.querySelector('form') + } +}) diff --git a/app/javascript/submission_form/date_step.vue b/app/javascript/submission_form/date_step.vue index 245573da..f60fd6eb 100644 --- a/app/javascript/submission_form/date_step.vue +++ b/app/javascript/submission_form/date_step.vue @@ -27,6 +27,7 @@ +