diff --git a/Gemfile.lock b/Gemfile.lock index 13217069..22585096 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -116,7 +116,7 @@ GEM erubi (~> 1.4) parser (>= 2.4) smart_properties - bigdecimal (4.1.0) + bigdecimal (4.1.2) bindex (0.8.1) bootsnap (1.23.0) msgpack (~> 1.2) @@ -161,7 +161,7 @@ GEM irb (~> 1.10) reline (>= 0.3.8) declarative (0.0.20) - devise (5.0.3) + devise (5.0.4) bcrypt (~> 3.0) orm_adapter (~> 0.1) railties (>= 7.0) @@ -268,13 +268,13 @@ GEM mini_magick (>= 4.9.5, < 6) ruby-vips (>= 2.0.17, < 3) io-console (0.8.2) - irb (1.17.0) + irb (1.18.0) pp (>= 0.6.0) prism (>= 1.3.0) rdoc (>= 4.0.0) reline (>= 0.4.2) jmespath (1.6.2) - json (2.19.3) + json (2.19.5) jwt (3.1.2) base64 language_server-protocol (3.17.0.5) @@ -311,7 +311,7 @@ GEM mini_magick (5.3.1) logger mini_mime (1.1.5) - minitest (6.0.3) + minitest (6.0.6) drb (~> 2.0) prism (~> 1.5) msgpack (1.8.0) @@ -429,7 +429,7 @@ GEM tsort (>= 0.2) zeitwerk (~> 2.6) rainbow (3.1.1) - rake (13.3.1) + rake (13.4.2) rdoc (7.2.0) erb psych (>= 4.0.0) diff --git a/app/controllers/submissions_resend_email_controller.rb b/app/controllers/submissions_resend_email_controller.rb new file mode 100644 index 00000000..a68c7ad4 --- /dev/null +++ b/app/controllers/submissions_resend_email_controller.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +class SubmissionsResendEmailController < ApplicationController + load_and_authorize_resource :submission + + before_action do + authorize!(:manage, :resend_all) + authorize!(:update, @submission) + end + + def create + submitters = @submission.submitters.reject(&:completed_at?).select { |s| s.email.present? && !s.declined_at? } + + if Docuseal.multitenant? + recent_submitter_ids = + SubmissionEvent.where(submitter_id: submitters.map(&:id), + event_type: 'send_email', + created_at: 10.hours.ago..Time.current).pluck(:submitter_id).to_set + + submitters = submitters.reject { |s| recent_submitter_ids.include?(s.id) } + end + + submitters.each do |submitter| + SendSubmitterInvitationEmailJob.perform_async('submitter_id' => submitter.id) + + submitter.sent_at ||= Time.current + submitter.save! + end + + notice = + if submitters.empty? + I18n.t('email_has_been_sent_already') + else + I18n.t('emails_have_been_sent_to_n_recipients', count: submitters.size) + end + + redirect_back(fallback_location: submission_path(@submission), notice:) + end +end diff --git a/app/controllers/submissions_unarchive_controller.rb b/app/controllers/submissions_unarchive_controller.rb index 5a60a60b..8bd320a6 100644 --- a/app/controllers/submissions_unarchive_controller.rb +++ b/app/controllers/submissions_unarchive_controller.rb @@ -4,6 +4,8 @@ class SubmissionsUnarchiveController < ApplicationController load_and_authorize_resource :submission def create + authorize!(:update, @submission) + @submission.update!(archived_at: nil) redirect_to submission_path(@submission), notice: I18n.t('submission_has_been_unarchived') diff --git a/app/controllers/submitters_send_email_controller.rb b/app/controllers/submitters_send_email_controller.rb index 51c7c1b7..f616c976 100644 --- a/app/controllers/submitters_send_email_controller.rb +++ b/app/controllers/submitters_send_email_controller.rb @@ -4,6 +4,8 @@ class SubmittersSendEmailController < ApplicationController load_and_authorize_resource :submitter def create + authorize!(:update, @submitter) + if Docuseal.multitenant? && SubmissionEvent.exists?(submitter: @submitter, event_type: 'send_email', created_at: 10.hours.ago..Time.current) diff --git a/app/controllers/template_documents_controller.rb b/app/controllers/template_documents_controller.rb index db8ba66c..fafa6c14 100644 --- a/app/controllers/template_documents_controller.rb +++ b/app/controllers/template_documents_controller.rb @@ -10,6 +10,8 @@ class TemplateDocumentsController < ApplicationController end def create + authorize!(:update, @template) + if params[:blobs].blank? && params[:files].blank? return render json: { error: I18n.t('file_is_missing') }, status: :unprocessable_content end diff --git a/app/controllers/templates_clone_and_replace_controller.rb b/app/controllers/templates_clone_and_replace_controller.rb index de64d86d..015d1830 100644 --- a/app/controllers/templates_clone_and_replace_controller.rb +++ b/app/controllers/templates_clone_and_replace_controller.rb @@ -13,6 +13,9 @@ class TemplatesCloneAndReplaceController < ApplicationController cloned_template = Templates::Clone.call(@template, author: current_user) cloned_template.name = File.basename(params[:files].first.original_filename, '.*') + + authorize!(:create, cloned_template) + cloned_template.save! documents = Templates::ReplaceAttachments.call(cloned_template, params, extract_fields: true) diff --git a/app/controllers/templates_controller.rb b/app/controllers/templates_controller.rb index 2d612166..a6a5cbe8 100644 --- a/app/controllers/templates_controller.rb +++ b/app/controllers/templates_controller.rb @@ -78,8 +78,6 @@ class TemplatesController < ApplicationController WebhookUrls.enqueue_events(@template, 'template.updated') - TemplateVersions.find_or_create_for(@template, author: current_user) if params[:revision] - head :ok end diff --git a/app/controllers/templates_folders_controller.rb b/app/controllers/templates_folders_controller.rb index 3e83023a..05ee62d8 100644 --- a/app/controllers/templates_folders_controller.rb +++ b/app/controllers/templates_folders_controller.rb @@ -6,6 +6,8 @@ class TemplatesFoldersController < ApplicationController def edit; end def update + authorize!(:update, @template) + name = [params[:parent_name], params[:name]].compact_blank.join(' / ') @template.folder = TemplateFolders.find_or_create_by_name(current_user, name) diff --git a/app/controllers/templates_restore_controller.rb b/app/controllers/templates_restore_controller.rb index d6d0505e..422b69cf 100644 --- a/app/controllers/templates_restore_controller.rb +++ b/app/controllers/templates_restore_controller.rb @@ -4,6 +4,8 @@ class TemplatesRestoreController < ApplicationController load_and_authorize_resource :template def create + authorize!(:update, @template) + @template.update!(archived_at: nil) WebhookUrls.enqueue_events(@template, 'template.updated') diff --git a/app/controllers/templates_versions_controller.rb b/app/controllers/templates_versions_controller.rb index c9a67b8f..0dbd20aa 100644 --- a/app/controllers/templates_versions_controller.rb +++ b/app/controllers/templates_versions_controller.rb @@ -14,4 +14,12 @@ class TemplatesVersionsController < ApplicationController render json: TemplateVersions.serialize(version) end + + def create + authorize!(:update, @template) + + TemplateVersions.find_or_create_for(@template, author: current_user) + + head :ok + end end diff --git a/app/javascript/application.js b/app/javascript/application.js index af25f2b4..311b1612 100644 --- a/app/javascript/application.js +++ b/app/javascript/application.js @@ -47,7 +47,6 @@ import ScrollTo from './elements/scroll_to' import SetValue from './elements/set_value' import ReviewForm from './elements/review_form' import ShowOnValue from './elements/show_on_value' -import CustomValidation from './elements/custom_validation' import ToggleClasses from './elements/toggle_classes' import AutosizeField from './elements/autosize_field' import GoogleDriveFilePicker from './elements/google_drive_file_picker' @@ -139,7 +138,6 @@ safeRegisterElement('scroll-to', ScrollTo) safeRegisterElement('set-value', SetValue) safeRegisterElement('review-form', ReviewForm) safeRegisterElement('show-on-value', ShowOnValue) -safeRegisterElement('custom-validation', CustomValidation) safeRegisterElement('toggle-classes', ToggleClasses) safeRegisterElement('autosize-field', AutosizeField) safeRegisterElement('google-drive-file-picker', GoogleDriveFilePicker) diff --git a/app/javascript/elements/custom_validation.js b/app/javascript/elements/custom_validation.js deleted file mode 100644 index ac738679..00000000 --- a/app/javascript/elements/custom_validation.js +++ /dev/null @@ -1,14 +0,0 @@ -export default class extends HTMLElement { - connectedCallback () { - const input = this.querySelector('input') - const invalidMessage = this.dataset.invalidMessage || '' - - input.addEventListener('invalid', () => { - input.setCustomValidity(input.value ? invalidMessage : '') - }) - - input.addEventListener('input', () => { - input.setCustomValidity('') - }) - } -} diff --git a/app/javascript/form.js b/app/javascript/form.js index c8d5790e..75158250 100644 --- a/app/javascript/form.js +++ b/app/javascript/form.js @@ -52,7 +52,9 @@ safeRegisterElement('submission-form', class extends HTMLElement { completedMessage: JSON.parse(this.dataset.completedMessage || '{}'), completedRedirectUrl: this.dataset.completedRedirectUrl, attachments: reactive(JSON.parse(this.dataset.attachments)), - fields: JSON.parse(this.dataset.fields) + fields: JSON.parse(this.dataset.fields), + completeButtonContainer: document.getElementById('complete_button_container'), + completeButtonScrollContainer: document.getElementById('complete_button_container_scroll') }) this.app.mount(this.appElem) diff --git a/app/javascript/submission_form/area.vue b/app/javascript/submission_form/area.vue index 69a453ac..9cfa44a1 100644 --- a/app/javascript/submission_form/area.vue +++ b/app/javascript/submission_form/area.vue @@ -643,6 +643,10 @@ export default { return new Intl.NumberFormat('de-DE').format(number) } else if (format === 'space') { return new Intl.NumberFormat('fr-FR').format(number) + } else if (format === 'percent') { + return `${number}%` + } else if (format === 'percent_space') { + return `${String(number).replace('.', ',')} %` } else { return number } diff --git a/app/javascript/submission_form/form.vue b/app/javascript/submission_form/form.vue index 2d948bc3..e63c1844 100644 --- a/app/javascript/submission_form/form.vue +++ b/app/javascript/submission_form/form.vue @@ -113,6 +113,31 @@ + + +