diff --git a/app/controllers/api/submissions_controller.rb b/app/controllers/api/submissions_controller.rb index fc89c1d3..41b73fb9 100644 --- a/app/controllers/api/submissions_controller.rb +++ b/app/controllers/api/submissions_controller.rb @@ -10,7 +10,7 @@ module Api end def index - submissions = Submissions.search(@submissions, params[:q]) + submissions = Submissions.search(current_user, @submissions, params[:q]) submissions = filter_submissions(submissions, params) submissions = paginate(submissions.preload(:created_by_user, :submitters, @@ -80,6 +80,8 @@ module Api end end + SearchEntries.enqueue_reindex(submissions) + render json: build_create_json(submissions) rescue Submitters::NormalizeValues::BaseError, Submissions::CreateFromSubmitters::BaseError, DownloadUtils::UnableToDownload => e diff --git a/app/controllers/api/submitters_controller.rb b/app/controllers/api/submitters_controller.rb index 4d53d3cb..1731968f 100644 --- a/app/controllers/api/submitters_controller.rb +++ b/app/controllers/api/submitters_controller.rb @@ -5,7 +5,7 @@ module Api load_and_authorize_resource :submitter def index - submitters = Submitters.search(@submitters, params[:q]) + submitters = Submitters.search(current_user, @submitters, params[:q]) submitters = filter_submitters(submitters, params) @@ -65,6 +65,8 @@ module Api Submitters.send_signature_requests([@submitter]) end + SearchEntries.enqueue_reindex(@submitter) + render json: Submitters::SerializeForApi.call(@submitter, with_template: false, with_urls: true, with_events: false, diff --git a/app/controllers/api/templates_clone_controller.rb b/app/controllers/api/templates_clone_controller.rb index bf9aa671..2619a243 100644 --- a/app/controllers/api/templates_clone_controller.rb +++ b/app/controllers/api/templates_clone_controller.rb @@ -33,6 +33,8 @@ module Api 'webhook_url_id' => webhook_url.id) end + SearchEntries.enqueue_reindex(cloned_template) + render json: Templates::SerializeForApi.call(cloned_template, schema_documents) end end diff --git a/app/controllers/api/templates_controller.rb b/app/controllers/api/templates_controller.rb index 25c0c537..a36732a3 100644 --- a/app/controllers/api/templates_controller.rb +++ b/app/controllers/api/templates_controller.rb @@ -65,6 +65,8 @@ module Api @template.update!(template_params) + SearchEntries.enqueue_reindex(@template) + WebhookUrls.for_account_id(@template.account_id, 'template.updated').each do |webhook_url| SendTemplateUpdatedWebhookRequestJob.perform_async('template_id' => @template.id, 'webhook_url_id' => webhook_url.id) @@ -86,7 +88,7 @@ module Api private def filter_templates(templates, params) - templates = Templates.search(templates, params[:q]) + templates = Templates.search(current_user, templates, params[:q]) templates = params[:archived].in?(['true', true]) ? templates.archived : templates.active templates = templates.where(external_id: params[:application_key]) if params[:application_key].present? templates = templates.where(external_id: params[:external_id]) if params[:external_id].present? diff --git a/app/controllers/start_form_controller.rb b/app/controllers/start_form_controller.rb index b2957c1c..fb2a9786 100644 --- a/app/controllers/start_form_controller.rb +++ b/app/controllers/start_form_controller.rb @@ -53,6 +53,8 @@ class StartFormController < ApplicationController if is_new_record enqueue_submission_create_webhooks(@submitter) + SearchEntries.enqueue_reindex(@submitter) + if @submitter.submission.expire_at? ProcessSubmissionExpiredJob.perform_at(@submitter.submission.expire_at, 'submission_id' => @submitter.submission_id) diff --git a/app/controllers/submissions_archived_controller.rb b/app/controllers/submissions_archived_controller.rb index 9bd67da2..793da755 100644 --- a/app/controllers/submissions_archived_controller.rb +++ b/app/controllers/submissions_archived_controller.rb @@ -9,7 +9,7 @@ class SubmissionsArchivedController < ApplicationController .or(@submissions.where.not(templates: { archived_at: nil })) .preload(:template_accesses, :created_by_user, template: :author) - @submissions = Submissions.search(@submissions, params[:q], search_template: true) + @submissions = Submissions.search(current_user, @submissions, params[:q], search_template: true) @submissions = Submissions::Filter.call(@submissions, current_user, params) @submissions = if params[:completed_at_from].present? || params[:completed_at_to].present? diff --git a/app/controllers/submissions_controller.rb b/app/controllers/submissions_controller.rb index 0240b5be..98d7cb63 100644 --- a/app/controllers/submissions_controller.rb +++ b/app/controllers/submissions_controller.rb @@ -8,6 +8,10 @@ class SubmissionsController < ApplicationController prepend_before_action :maybe_redirect_com, only: %i[show] + before_action only: :create do + authorize!(:create, Submission) + end + def show @submission = Submissions.preload_with_pages(@submission) @@ -26,8 +30,6 @@ class SubmissionsController < ApplicationController end def create - authorize!(:create, Submission) - save_template_message(@template, params) if params[:save_message] == '1' if params[:is_custom_message] != '1' @@ -56,6 +58,8 @@ class SubmissionsController < ApplicationController Submissions.send_signature_requests(submissions) + SearchEntries.enqueue_reindex(submissions) + redirect_to template_path(@template), notice: I18n.t('new_recipients_have_been_added') rescue Submissions::CreateFromSubmitters::BaseError => e render turbo_stream: turbo_stream.replace(:submitters_error, diff --git a/app/controllers/submissions_dashboard_controller.rb b/app/controllers/submissions_dashboard_controller.rb index 3d0950a6..f0851741 100644 --- a/app/controllers/submissions_dashboard_controller.rb +++ b/app/controllers/submissions_dashboard_controller.rb @@ -10,7 +10,7 @@ class SubmissionsDashboardController < ApplicationController .where(templates: { archived_at: nil }) .preload(:template_accesses, :created_by_user, template: :author) - @submissions = Submissions.search(@submissions, params[:q], search_template: true) + @submissions = Submissions.search(current_user, @submissions, params[:q], search_template: true) @submissions = Submissions::Filter.call(@submissions, current_user, params) @submissions = if params[:completed_at_from].present? || params[:completed_at_to].present? diff --git a/app/controllers/submitters_autocomplete_controller.rb b/app/controllers/submitters_autocomplete_controller.rb index e86e129e..70f02747 100644 --- a/app/controllers/submitters_autocomplete_controller.rb +++ b/app/controllers/submitters_autocomplete_controller.rb @@ -22,13 +22,17 @@ class SubmittersAutocompleteController < ApplicationController def search_submitters(submitters) if SELECT_COLUMNS.include?(params[:field]) - column = Submitter.arel_table[params[:field].to_sym] + if Docuseal.fulltext_search?(current_user) + Submitters.fulltext_search_field(current_user, submitters, params[:q], params[:field]) + else + column = Submitter.arel_table[params[:field].to_sym] - term = "#{params[:q].downcase}%" + term = "#{params[:q].downcase}%" - submitters.where(column.matches(term)) + submitters.where(column.matches(term)) + end else - Submitters.search(submitters, params[:q]) + Submitters.search(current_user, submitters, params[:q]) end end end diff --git a/app/controllers/submitters_controller.rb b/app/controllers/submitters_controller.rb index 3adb3c89..697b3dc1 100644 --- a/app/controllers/submitters_controller.rb +++ b/app/controllers/submitters_controller.rb @@ -38,6 +38,8 @@ class SubmittersController < ApplicationController if @submitter.save maybe_resend_email_sms(@submitter, params) + SearchEntries.enqueue_reindex(@submitter) + redirect_back fallback_location: submission_path(submission), notice: I18n.t('changes_have_been_saved') else redirect_back fallback_location: submission_path(submission), alert: I18n.t('unable_to_save') diff --git a/app/controllers/template_folders_controller.rb b/app/controllers/template_folders_controller.rb index c22f2fed..25dad2c1 100644 --- a/app/controllers/template_folders_controller.rb +++ b/app/controllers/template_folders_controller.rb @@ -6,7 +6,7 @@ class TemplateFoldersController < ApplicationController def show @templates = @template_folder.templates.active.accessible_by(current_ability) .preload(:author, :template_accesses) - @templates = Templates.search(@templates, params[:q]) + @templates = Templates.search(current_user, @templates, params[:q]) @templates = Templates::Order.call(@templates, current_user, cookies.permanent[:dashboard_templates_order]) @pagy, @templates = pagy_auto(@templates, limit: 12) diff --git a/app/controllers/templates_archived_controller.rb b/app/controllers/templates_archived_controller.rb index f52e5af7..34d2b601 100644 --- a/app/controllers/templates_archived_controller.rb +++ b/app/controllers/templates_archived_controller.rb @@ -5,7 +5,7 @@ class TemplatesArchivedController < ApplicationController def index @templates = @templates.where.not(archived_at: nil).preload(:author, :folder, :template_accesses).order(id: :desc) - @templates = Templates.search(@templates, params[:q]) + @templates = Templates.search(current_user, @templates, params[:q]) @pagy, @templates = pagy_auto(@templates, limit: 12) end diff --git a/app/controllers/templates_archived_submissions_controller.rb b/app/controllers/templates_archived_submissions_controller.rb index 9c9da083..39aa0877 100644 --- a/app/controllers/templates_archived_submissions_controller.rb +++ b/app/controllers/templates_archived_submissions_controller.rb @@ -6,7 +6,7 @@ class TemplatesArchivedSubmissionsController < ApplicationController def index @submissions = @submissions.where.not(archived_at: nil) - @submissions = Submissions.search(@submissions, params[:q], search_values: true) + @submissions = Submissions.search(current_user, @submissions, params[:q], search_values: true) @submissions = Submissions::Filter.call(@submissions, current_user, params) @submissions = if params[:completed_at_from].present? || params[:completed_at_to].present? diff --git a/app/controllers/templates_clone_and_replace_controller.rb b/app/controllers/templates_clone_and_replace_controller.rb index 0d75416b..21989067 100644 --- a/app/controllers/templates_clone_and_replace_controller.rb +++ b/app/controllers/templates_clone_and_replace_controller.rb @@ -22,6 +22,8 @@ class TemplatesCloneAndReplaceController < ApplicationController Templates::CloneAttachments.call(template: cloned_template, original_template: @template, excluded_attachment_uuids: documents.map(&:uuid)) + SearchEntries.enqueue_reindex(cloned_template) + respond_to do |f| f.html { redirect_to edit_template_path(cloned_template) } f.json { render json: { id: cloned_template.id } } diff --git a/app/controllers/templates_controller.rb b/app/controllers/templates_controller.rb index b3d0ccec..40c8dbd1 100644 --- a/app/controllers/templates_controller.rb +++ b/app/controllers/templates_controller.rb @@ -8,7 +8,7 @@ class TemplatesController < ApplicationController def show submissions = @template.submissions.accessible_by(current_ability) submissions = submissions.active if @template.archived_at.blank? - submissions = Submissions.search(submissions, params[:q], search_values: true) + submissions = Submissions.search(current_user, submissions, params[:q], search_values: true) submissions = Submissions::Filter.call(submissions, current_user, params.except(:status)) @base_submissions = submissions @@ -72,6 +72,8 @@ class TemplatesController < ApplicationController if @template.save Templates::CloneAttachments.call(template: @template, original_template: @base_template) if @base_template + SearchEntries.enqueue_reindex(@template) + enqueue_template_created_webhooks(@template) maybe_redirect_to_template(@template) @@ -81,7 +83,13 @@ class TemplatesController < ApplicationController end def update - @template.update!(template_params) + @template.assign_attributes(template_params) + + is_name_changed = @template.name_changed? + + @template.save! + + SearchEntries.enqueue_reindex(@template) if is_name_changed enqueue_template_updated_webhooks(@template) diff --git a/app/controllers/templates_dashboard_controller.rb b/app/controllers/templates_dashboard_controller.rb index b4806fbc..3649ac0f 100644 --- a/app/controllers/templates_dashboard_controller.rb +++ b/app/controllers/templates_dashboard_controller.rb @@ -57,7 +57,7 @@ class TemplatesDashboardController < ApplicationController end end - Templates.search(rel, params[:q]) + Templates.search(current_user, rel, params[:q]) end def sort_template_folders(template_folders, current_user, order) diff --git a/app/controllers/templates_uploads_controller.rb b/app/controllers/templates_uploads_controller.rb index 4cd0072f..45403a7a 100644 --- a/app/controllers/templates_uploads_controller.rb +++ b/app/controllers/templates_uploads_controller.rb @@ -25,6 +25,8 @@ class TemplatesUploadsController < ApplicationController enqueue_template_created_webhooks(@template) + SearchEntries.enqueue_reindex(@template) + redirect_to edit_template_path(@template) rescue Templates::CreateAttachments::PdfEncrypted render turbo_stream: turbo_stream.append(params[:form_id], html: helpers.tag.prompt_password) diff --git a/app/javascript/elements/submitter_autocomplete.js b/app/javascript/elements/submitter_autocomplete.js index 5fc539bf..bf24739a 100644 --- a/app/javascript/elements/submitter_autocomplete.js +++ b/app/javascript/elements/submitter_autocomplete.js @@ -36,7 +36,9 @@ export default class extends HTMLElement { } fetch = (text, resolve) => { - const q = text.split(/[;,\s]+/).pop().trim() + const q = this.dataset.field === 'email' + ? text.split(/[;,\s]+/).pop().trim() + : text if (q) { const queryParams = new URLSearchParams({ q, field: this.dataset.field }) diff --git a/app/jobs/reindex_search_entry_job.rb b/app/jobs/reindex_search_entry_job.rb new file mode 100644 index 00000000..c5650021 --- /dev/null +++ b/app/jobs/reindex_search_entry_job.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +class ReindexSearchEntryJob + include Sidekiq::Job + + InvalidFormat = Class.new(StandardError) + + def perform(params = {}) + entry = SearchEntry.find_or_initialize_by(params.slice('record_type', 'record_id')) + + SearchEntries.reindex_record(entry.record) + end +end diff --git a/app/models/search_entry.rb b/app/models/search_entry.rb new file mode 100644 index 00000000..bc641669 --- /dev/null +++ b/app/models/search_entry.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +# == Schema Information +# +# Table name: search_entries +# +# id :bigint not null, primary key +# record_type :string not null +# tsvector :tsvector not null +# created_at :datetime not null +# updated_at :datetime not null +# account_id :bigint not null +# record_id :bigint not null +# +# Indexes +# +# index_search_entries_on_account_id_tsvector_submission (account_id,tsvector) WHERE ((record_type)::text = 'Submission'::text) USING gin +# index_search_entries_on_account_id_tsvector_submitter (account_id,tsvector) WHERE ((record_type)::text = 'Submitter'::text) USING gin +# index_search_entries_on_account_id_tsvector_template (account_id,tsvector) WHERE ((record_type)::text = 'Template'::text) USING gin +# index_search_entries_on_record_id_and_record_type (record_id,record_type) UNIQUE +# +class SearchEntry < ApplicationRecord + belongs_to :record, polymorphic: true + belongs_to :account +end diff --git a/app/models/submission.rb b/app/models/submission.rb index f0fe283c..31547d87 100644 --- a/app/models/submission.rb +++ b/app/models/submission.rb @@ -38,6 +38,8 @@ class Submission < ApplicationRecord belongs_to :account belongs_to :created_by_user, class_name: 'User', optional: true + has_one :search_entry, as: :record, inverse_of: :record, dependent: :destroy + has_many :submitters, dependent: :destroy has_many :submission_events, dependent: :destroy diff --git a/app/models/submitter.rb b/app/models/submitter.rb index 91322f96..fd9dfc6b 100644 --- a/app/models/submitter.rb +++ b/app/models/submitter.rb @@ -41,6 +41,7 @@ class Submitter < ApplicationRecord belongs_to :submission belongs_to :account has_one :template, through: :submission + has_one :search_entry, as: :record, inverse_of: :record, dependent: :destroy attribute :values, :string, default: -> { {} } attribute :preferences, :string, default: -> { {} } diff --git a/app/models/template.rb b/app/models/template.rb index faa9e696..dbfadb79 100644 --- a/app/models/template.rb +++ b/app/models/template.rb @@ -42,6 +42,8 @@ class Template < ApplicationRecord belongs_to :account belongs_to :folder, class_name: 'TemplateFolder' + has_one :search_entry, as: :record, inverse_of: :record, dependent: :destroy + before_validation :maybe_set_default_folder, on: :create attribute :preferences, :string, default: -> { {} } diff --git a/app/views/submissions_dashboard/index.html.erb b/app/views/submissions_dashboard/index.html.erb index 2ba16b18..286e0b91 100644 --- a/app/views/submissions_dashboard/index.html.erb +++ b/app/views/submissions_dashboard/index.html.erb @@ -26,7 +26,7 @@ <% view_archived_html = capture do %> - <% if current_account.submissions.where.not(archived_at: nil).exists? %> + <% if can?(:manage, :countless) || current_account.submissions.where.not(archived_at: nil).exists? %>
diff --git a/app/views/templates/show.html.erb b/app/views/templates/show.html.erb index 18188523..c749ef1f 100644 --- a/app/views/templates/show.html.erb +++ b/app/views/templates/show.html.erb @@ -113,7 +113,7 @@ <% end %> <% view_archived_html = capture do %> - <% if @template.submissions.where.not(archived_at: nil).exists? && !@template.archived_at? %> + <% if (can?(:manage, :countless) || @template.submissions.where.not(archived_at: nil).exists?) && !@template.archived_at? %> diff --git a/app/views/templates_dashboard/index.html.erb b/app/views/templates_dashboard/index.html.erb index 99f4d80e..f1a4c3de 100644 --- a/app/views/templates_dashboard/index.html.erb +++ b/app/views/templates_dashboard/index.html.erb @@ -1,4 +1,4 @@ -<% has_archived = current_account.templates.where.not(archived_at: nil).exists? %> +<% has_archived = can?(:manage, :countless) || current_account.templates.where.not(archived_at: nil).exists? %> <% show_dropzone = params[:q].blank? && @pagy.pages == 1 && ((@template_folders.size < 10 && @templates.size.zero?) || (@template_folders.size < 7 && @templates.size < 4) || (@template_folders.size < 4 && @templates.size < 7)) %> <% if Docuseal.demo? %><%= render 'shared/demo_alert' %><% end %>