diff --git a/app/controllers/account_configs_controller.rb b/app/controllers/account_configs_controller.rb index 21c380a2..4146c130 100644 --- a/app/controllers/account_configs_controller.rb +++ b/app/controllers/account_configs_controller.rb @@ -15,6 +15,7 @@ class AccountConfigsController < ApplicationController AccountConfig::DOWNLOAD_LINKS_AUTH_KEY, AccountConfig::FORCE_SSO_AUTH_KEY, AccountConfig::FLATTEN_RESULT_PDF_KEY, + AccountConfig::ENFORCE_SIGNING_ORDER_KEY, AccountConfig::WITH_SIGNATURE_ID, AccountConfig::COMBINE_PDF_RESULT_KEY, AccountConfig::REQUIRE_SIGNING_REASON_KEY, diff --git a/app/controllers/submissions_download_controller.rb b/app/controllers/submissions_download_controller.rb index 9ba81b91..e4af5f49 100644 --- a/app/controllers/submissions_download_controller.rb +++ b/app/controllers/submissions_download_controller.rb @@ -23,9 +23,9 @@ class SubmissionsDownloadController < ApplicationController last_submitter = submitter.submission.submitters.where.not(completed_at: nil).order(:completed_at).last - Submissions::EnsureResultGenerated.call(last_submitter) + return head :not_found unless last_submitter - return head :not_found unless last_submitter.completed_at? + Submissions::EnsureResultGenerated.call(last_submitter) if last_submitter.completed_at < TTL.ago && !signature_valid && !current_user_submitter?(last_submitter) Rollbar.info("TTL: #{last_submitter.id}") if defined?(Rollbar) diff --git a/app/controllers/submit_form_controller.rb b/app/controllers/submit_form_controller.rb index 90343331..fcd0fdc8 100644 --- a/app/controllers/submit_form_controller.rb +++ b/app/controllers/submit_form_controller.rb @@ -20,7 +20,11 @@ class SubmitFormController < ApplicationController @submitter.account.archived_at? return render :expired if submission.expired? return render :declined if @submitter.declined_at? - return render :awaiting if submission.template.preferences['submitters_order'] == 'preserved' && + + @form_configs = Submitters::FormConfigs.call(@submitter, CONFIG_KEYS) + + return render :awaiting if (@form_configs[:enforce_signing_order] || + submission.template.preferences['submitters_order'] == 'preserved') && !Submitters.current_submitter_order?(@submitter) Submitters.preload_with_pages(@submitter) @@ -29,8 +33,6 @@ class SubmitFormController < ApplicationController @attachments_index = build_attachments_index(submission) - @form_configs = Submitters::FormConfigs.call(@submitter, CONFIG_KEYS) - return unless @form_configs[:prefill_signature] if (user_signature = UserConfigs.load_signature(current_user)) @@ -70,6 +72,10 @@ class SubmitFormController < ApplicationController Submitters::SubmitValues.call(submitter, params, request) head :ok + rescue Submitters::SubmitValues::RequiredFieldError => e + Rollbar.warning("Required field #{submitter.id}: #{e.message}") if defined?(Rollbar) + + render json: { field_uuid: e.message }, status: :unprocessable_entity rescue Submitters::SubmitValues::ValidationError => e render json: { error: e.message }, status: :unprocessable_entity end diff --git a/app/javascript/application.js b/app/javascript/application.js index d35641ad..7ae6836f 100644 --- a/app/javascript/application.js +++ b/app/javascript/application.js @@ -35,8 +35,6 @@ import IndeterminateCheckbox from './elements/indeterminate_checkbox' import * as TurboInstantClick from './lib/turbo_instant_click' -import './images/preview.png' - TurboInstantClick.start() document.addEventListener('turbo:before-cache', () => { diff --git a/app/javascript/draw.js b/app/javascript/draw.js index c42c3c72..a4936e8d 100644 --- a/app/javascript/draw.js +++ b/app/javascript/draw.js @@ -1,5 +1,6 @@ import SignaturePad from 'signature_pad' import { cropCanvasAndExportToPNG } from './submission_form/crop_canvas' +import { isValidSignatureCanvas } from './submission_form/validate_signature' window.customElements.define('draw-signature', class extends HTMLElement { connectedCallback () { @@ -43,6 +44,8 @@ window.customElements.define('draw-signature', class extends HTMLElement { return response }) + }).catch(error => { + console.log(error) }).finally(() => { this.submitButton.disabled = false }) @@ -65,26 +68,26 @@ window.customElements.define('draw-signature', class extends HTMLElement { } async submitImage () { - return new Promise((resolve, reject) => { - cropCanvasAndExportToPNG(this.canvas, { errorOnTooSmall: true }).then(async (blob) => { - const file = new File([blob], 'signature.png', { type: 'image/png' }) - - const formData = new FormData() - - formData.append('file', file) - formData.append('submitter_slug', this.dataset.slug) - formData.append('name', 'attachments') - formData.append('remember_signature', 'true') - - return fetch('/api/attachments', { - method: 'POST', - body: formData - }).then((resp) => resp.json()).then((attachment) => { - return resolve(attachment) - }) - }).catch((error) => { - return reject(error) - }) + if (!isValidSignatureCanvas(this.pad.toData())) { + alert('Signature is too small or simple. Please redraw.') + + return Promise.reject(new Error('Image too small or simple')) + } + + return cropCanvasAndExportToPNG(this.canvas).then(async (blob) => { + const file = new File([blob], 'signature.png', { type: 'image/png' }) + + const formData = new FormData() + + formData.append('file', file) + formData.append('submitter_slug', this.dataset.slug) + formData.append('name', 'attachments') + formData.append('remember_signature', 'true') + + return fetch('/api/attachments', { + method: 'POST', + body: formData + }).then(resp => resp.json()) }) } diff --git a/app/javascript/images/preview.png b/app/javascript/images/preview.png deleted file mode 100644 index 81acd3a9..00000000 Binary files a/app/javascript/images/preview.png and /dev/null differ diff --git a/app/javascript/submission_form/crop_canvas.js b/app/javascript/submission_form/crop_canvas.js index 472d2764..977c5ade 100644 --- a/app/javascript/submission_form/crop_canvas.js +++ b/app/javascript/submission_form/crop_canvas.js @@ -1,4 +1,4 @@ -function cropCanvasAndExportToPNG (canvas, { errorOnTooSmall } = { errorOnTooSmall: false }) { +function cropCanvasAndExportToPNG (canvas) { const ctx = canvas.getContext('2d') const width = canvas.width @@ -33,10 +33,6 @@ function cropCanvasAndExportToPNG (canvas, { errorOnTooSmall } = { errorOnTooSma croppedCanvas.height = croppedHeight const croppedCtx = croppedCanvas.getContext('2d') - if (errorOnTooSmall && (croppedWidth < 20 || croppedHeight < 20)) { - return Promise.reject(new Error('Image too small')) - } - croppedCtx.drawImage(canvas, leftmost, topmost, croppedWidth, croppedHeight, 0, 0, croppedWidth, croppedHeight) return new Promise((resolve, reject) => { diff --git a/app/javascript/submission_form/form.vue b/app/javascript/submission_form/form.vue index e76ed805..01b7f4b7 100644 --- a/app/javascript/submission_form/form.vue +++ b/app/javascript/submission_form/form.vue @@ -1404,7 +1404,21 @@ export default { if (response.status === 422 || response.status === 500) { const data = await response.json() - if (data.error) { + if (data.field_uuid) { + const field = this.fieldsUuidIndex[data.field_uuid] + + if (field) { + const step = this.stepFields.findIndex((fields) => fields.includes(field)) + + if (step !== -1) { + this.goToStep(step, this.autoscrollFields) + + this.showFillAllRequiredFields = true + } + } + + return Promise.reject(new Error('Required field: ' + data.field_uuid)) + } else if (data.error) { const i18nKey = data.error.replace(/\s+/g, '_').toLowerCase() alert(this.t(i18nKey) !== i18nKey ? this.t(i18nKey) : data.error) @@ -1439,11 +1453,7 @@ export default { this.isSubmittingComplete = false }) }).catch(error => { - if (error?.message === 'Image too small') { - alert(this.t('signature_is_too_small_please_redraw')) - } else { - console.log(error) - } + console.log(error) }).finally(() => { this.isSubmitting = false this.isSubmittingComplete = false diff --git a/app/javascript/submission_form/i18n.js b/app/javascript/submission_form/i18n.js index ab1e0685..252058e7 100644 --- a/app/javascript/submission_form/i18n.js +++ b/app/javascript/submission_form/i18n.js @@ -93,7 +93,7 @@ const en = { reupload: 'Reupload', upload: 'Upload', files: 'Files', - signature_is_too_small_please_redraw: 'Signature is too small. Please redraw.', + signature_is_too_small_or_simple_please_redraw: 'Signature is too small or simple. Please redraw.', wait_countdown_seconds: 'Wait {countdown} seconds' } @@ -191,7 +191,7 @@ const es = { reupload: 'Volver a subir', upload: 'Subir', files: 'Archivos', - signature_is_too_small_please_redraw: 'La firma es demasiado pequeña. Por favor, dibújala de nuevo.', + signature_is_too_small_or_simple_please_redraw: 'La firma es demasiado pequeña o simple. Por favor, vuelve a dibujarla.', wait_countdown_seconds: 'Espera {countdown} segundos' } @@ -289,7 +289,7 @@ const it = { reupload: 'Ricarica', upload: 'Carica', files: 'File', - signature_is_too_small_please_redraw: 'La firma è troppo piccola. Ridisegnala per favore.', + signature_is_too_small_or_simple_please_redraw: 'La firma è troppo piccola o semplice. Ridisegnala, per favore.', wait_countdown_seconds: 'Attendi {countdown} secondi' } @@ -387,7 +387,7 @@ const de = { reupload: 'Erneut hochladen', upload: 'Hochladen', files: 'Dateien', - signature_is_too_small_please_redraw: 'Die Unterschrift ist zu klein. Bitte erneut zeichnen.', + signature_is_too_small_or_simple_please_redraw: 'Die Unterschrift ist zu klein oder zu einfach. Bitte erneut zeichnen.', wait_countdown_seconds: 'Warte {countdown} Sekunden' } @@ -485,7 +485,7 @@ const fr = { reupload: 'Recharger', upload: 'Télécharger', files: 'Fichiers', - signature_is_too_small_please_redraw: 'La signature est trop petite. Veuillez la redessiner.', + signature_is_too_small_or_simple_please_redraw: 'La signature est trop petite ou trop simple. Veuillez la redessiner.', wait_countdown_seconds: 'Attendez {countdown} secondes' } @@ -583,7 +583,7 @@ const pl = { reupload: 'Ponowne przesłanie', upload: 'Przesyłanie', files: 'Pliki', - signature_is_too_small_please_redraw: 'Podpis jest zbyt mały. Proszę narysować go ponownie.' + signature_is_too_small_or_simple_please_redraw: 'Podpis jest zbyt mały lub zbyt prosty. Proszę narysować go ponownie.' } const uk = { @@ -680,7 +680,7 @@ const uk = { reupload: 'Перезавантажити', upload: 'Завантажити', files: 'Файли', - signature_is_too_small_please_redraw: 'Підпис занадто малий. Будь ласка, перемалюйте його.', + signature_is_too_small_or_simple_please_redraw: 'Підпис занадто маленький або надто простий. Будь ласка, перемалюйте.', wait_countdown_seconds: 'Зачекайте {countdown} секунд' } @@ -778,7 +778,7 @@ const cs = { reupload: 'Znovu nahrát', upload: 'Nahrát', files: 'Soubory', - signature_is_too_small_please_redraw: 'Podpis je příliš malý. Prosím, překreslete ho.', + signature_is_too_small_or_simple_please_redraw: 'Podpis je příliš malý nebo jednoduchý. Nakreslete jej prosím znovu.', wait_countdown_seconds: 'Počkejte {countdown} sekund' } @@ -876,7 +876,7 @@ const pt = { reupload: 'Reenviar', upload: 'Carregar', files: 'Arquivos', - signature_is_too_small_please_redraw: 'A assinatura é muito pequena. Por favor, redesenhe-a.', + signature_is_too_small_or_simple_please_redraw: 'A assinatura é muito pequena ou simples. Por favor, redesenhe.', wait_countdown_seconds: 'Aguarde {countdown} segundos' } @@ -975,7 +975,7 @@ const he = { reupload: 'העלה שוב', upload: 'העלאה', files: 'קבצים', - signature_is_too_small_please_redraw: 'החתימה קטנה מדי. אנא צייר מחדש.', + signature_is_too_small_or_simple_please_redraw: 'החתימה קטנה או פשוטה מדי. אנא חתום מחדש.', wait_countdown_seconds: 'המתן {countdown} שניות' } @@ -1074,7 +1074,7 @@ const nl = { reupload: 'Opnieuw uploaden', upload: 'Uploaden', files: 'Bestanden', - signature_is_too_small_please_redraw: 'De handtekening is te klein. Teken deze opnieuw, alstublieft.', + signature_is_too_small_or_simple_please_redraw: 'De handtekening is te klein of te eenvoudig. Teken opnieuw.', wait_countdown_seconds: 'Wacht {countdown} seconden' } @@ -1172,7 +1172,7 @@ const ar = { reupload: 'إعادة التحميل', upload: 'تحميل', files: 'الملفات', - signature_is_too_small_please_redraw: 'التوقيع صغير جدًا. يرجى إعادة الرسم.', + signature_is_too_small_or_simple_please_redraw: 'التوقيع صغير جدًا أو بسيط جدًا. يرجى إعادة رسمه.', wait_countdown_seconds: 'انتظر {countdown} ثانية' } @@ -1269,7 +1269,7 @@ const ko = { reupload: '다시 업로드', upload: '업로드', files: '파일', - signature_is_too_small_please_redraw: '서명이 너무 작습니다. 다시 그려주세요.', + signature_is_too_small_or_simple_please_redraw: '서명이 너무 작거나 단순합니다. 다시 그려주세요.', wait_countdown_seconds: '{countdown}초 기다리세요' } diff --git a/app/javascript/submission_form/initials_step.vue b/app/javascript/submission_form/initials_step.vue index 8abaab7b..99b1bc8a 100644 --- a/app/javascript/submission_form/initials_step.vue +++ b/app/javascript/submission_form/initials_step.vue @@ -118,11 +118,17 @@ :src="attachmentsIndex[modelValue || computedPreviousValue].url" class="mx-auto bg-white border border-base-300 rounded max-h-44" > - +