diff --git a/.rubocop.yml b/.rubocop.yml index 02967126..0e332ff6 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -28,7 +28,7 @@ Lint/MissingSuper: Enabled: false Metrics/ParameterLists: - Max: 10 + Max: 12 Metrics/MethodLength: Max: 30 diff --git a/Dockerfile b/Dockerfile index fedff0f5..f8c4398f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -48,7 +48,7 @@ ENV OPENSSL_CONF=/etc/openssl_legacy.cnf WORKDIR /app -RUN apk add --no-cache libpq vips redis vips-heif fontconfig onnxruntime +RUN apk add --no-cache libpq vips redis vips-heif onnxruntime RUN addgroup -g 2000 docuseal && adduser -u 2000 -G docuseal -s /bin/sh -D -h /home/docuseal docuseal diff --git a/app/controllers/submit_form_controller.rb b/app/controllers/submit_form_controller.rb index 4155b9f6..a65c2237 100644 --- a/app/controllers/submit_form_controller.rb +++ b/app/controllers/submit_form_controller.rb @@ -79,6 +79,8 @@ class SubmitFormController < ApplicationController render json: { field_uuid: e.message }, status: :unprocessable_content rescue Submitters::SubmitValues::ValidationError => e + Rollbar.warning("Validation error #{@submitter.id}: #{e.message}") if defined?(Rollbar) + render json: { error: e.message }, status: :unprocessable_content end diff --git a/app/controllers/submit_form_metadata_controller.rb b/app/controllers/submit_form_metadata_controller.rb new file mode 100644 index 00000000..49bf8666 --- /dev/null +++ b/app/controllers/submit_form_metadata_controller.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +class SubmitFormMetadataController < ApplicationController + skip_before_action :authenticate_user! + skip_authorization_check + + def index + submitter = Submitter.find_by!(slug: params[:submit_form_slug]) + + return head :not_found if submitter.declined_at? || + submitter.completed_at? || + submitter.submission.archived_at? || + submitter.submission.expired? || + submitter.submission.template&.archived_at? || + submitter.account.archived_at? || + !Submitters::AuthorizedForForm.call(submitter, current_user, request) + + submission = submitter.submission + values = submission.submitters.reduce({}) { |acc, sub| acc.merge(sub.values) } + schema = Submissions.filtered_conditions_schema(submission, values:, include_submitter_uuid: submitter.uuid) + + documents = schema.filter_map do |item| + submission.schema_documents.find { |a| a.uuid == item['attachment_uuid'] } + end + + ActiveRecord::Associations::Preloader.new(records: documents, associations: %i[blob record]).call + + text_runs = documents.to_h do |document| + [ + document.uuid, + DocumentMetadatas.find_or_create_for_document(document, account_id: document.record.account_id).text_runs + ] + end + + render json: { text_runs: } + end +end diff --git a/app/controllers/templates_share_link_qr_controller.rb b/app/controllers/templates_share_link_qr_controller.rb new file mode 100644 index 00000000..e16c0eec --- /dev/null +++ b/app/controllers/templates_share_link_qr_controller.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +class TemplatesShareLinkQrController < ApplicationController + load_and_authorize_resource :template + + def show + return render :disabled, layout: 'plain' unless @template.shared_link? + + shared_link_url = start_form_url(slug: @template.slug, host: form_link_host) + + @qr_svg_code = RQRCode::QRCode.new(shared_link_url, level: :m).as_svg(viewbox: true) + + @page_size = + if TimeUtils.timezone_abbr(current_account.timezone, Time.current.beginning_of_year).in?(TimeUtils::US_TIMEZONES) + 'Letter' + else + 'A4' + end + + render :show, layout: false + end +end diff --git a/app/controllers/webhook_settings_controller.rb b/app/controllers/webhook_settings_controller.rb index f9245f49..77fa22bf 100644 --- a/app/controllers/webhook_settings_controller.rb +++ b/app/controllers/webhook_settings_controller.rb @@ -32,9 +32,13 @@ class WebhookSettingsController < ApplicationController def new; end def create - @webhook_url.save! + if @webhook_url.url.present? + @webhook_url.save! - redirect_to settings_webhooks_path, notice: I18n.t('webhook_url_has_been_saved') + redirect_to settings_webhooks_path, notice: I18n.t('webhook_url_has_been_saved') + else + redirect_back fallback_location: settings_webhooks_path + end end def update diff --git a/app/javascript/elements/download_button.js b/app/javascript/elements/download_button.js index 160fc770..2a4b6210 100644 --- a/app/javascript/elements/download_button.js +++ b/app/javascript/elements/download_button.js @@ -5,6 +5,12 @@ export default targetable(class extends HTMLElement { connectedCallback () { this.addEventListener('click', () => this.downloadFiles()) + this.addEventListener('keydown', (e) => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault() + this.downloadFiles() + } + }) } toggleState () { diff --git a/app/javascript/elements/modal_button.js b/app/javascript/elements/modal_button.js new file mode 100644 index 00000000..0dbc75a8 --- /dev/null +++ b/app/javascript/elements/modal_button.js @@ -0,0 +1,18 @@ +export default class extends HTMLElement { + connectedCallback () { + const dialog = document.getElementById(this.dataset.target) + + this.querySelector('button').addEventListener('click', () => { + if (dialog) { + dialog.inert = false + dialog.showModal() + } + }) + + if (dialog) { + dialog.addEventListener('close', () => { + dialog.inert = true + }) + } + } +} diff --git a/app/javascript/elements/scroll_buttons.js b/app/javascript/elements/scroll_buttons.js index 6525adf9..08ca40ab 100644 --- a/app/javascript/elements/scroll_buttons.js +++ b/app/javascript/elements/scroll_buttons.js @@ -47,11 +47,13 @@ export default class extends HTMLElement { this.classList.remove('hidden', '-translate-y-10', 'opacity-0') this.classList.add('translate-y-0', 'opacity-100') + this.inert = false } hideButtons () { this.classList.remove('translate-y-0', 'opacity-100') this.classList.add('-translate-y-10', 'opacity-0') + this.inert = true setTimeout(() => { if (this.classList.contains('-translate-y-10')) { diff --git a/app/javascript/form.js b/app/javascript/form.js index f8561dc0..c8d5790e 100644 --- a/app/javascript/form.js +++ b/app/javascript/form.js @@ -7,6 +7,7 @@ import FetchForm from './elements/fetch_form' import ScrollButtons from './elements/scroll_buttons' import PageContainer from './elements/page_container' import SubmitForm from './elements/submit_form' +import ModalButton from './elements/modal_button' const safeRegisterElement = (name, element, options = {}) => !window.customElements.get(name) && window.customElements.define(name, element, options) @@ -16,6 +17,7 @@ safeRegisterElement('fetch-form', FetchForm) safeRegisterElement('scroll-buttons', ScrollButtons) safeRegisterElement('page-container', PageContainer) safeRegisterElement('submit-form', SubmitForm) +safeRegisterElement('modal-button', ModalButton) safeRegisterElement('submission-form', class extends HTMLElement { connectedCallback () { this.appElem = document.createElement('div') diff --git a/app/javascript/submission_form/accessibility_areas.vue b/app/javascript/submission_form/accessibility_areas.vue new file mode 100644 index 00000000..6e720e93 --- /dev/null +++ b/app/javascript/submission_form/accessibility_areas.vue @@ -0,0 +1,434 @@ + + + diff --git a/app/javascript/submission_form/area.vue b/app/javascript/submission_form/area.vue index affd57a6..e3138254 100644 --- a/app/javascript/submission_form/area.vue +++ b/app/javascript/submission_form/area.vue @@ -1,9 +1,14 @@