diff --git a/app/controllers/submissions_archived_controller.rb b/app/controllers/submissions_archived_controller.rb index c13745ff..3c3f49b6 100644 --- a/app/controllers/submissions_archived_controller.rb +++ b/app/controllers/submissions_archived_controller.rb @@ -8,6 +8,9 @@ class SubmissionsArchivedController < ApplicationController @submissions = @submissions.where.not(archived_at: nil) .or(@submissions.where.not(templates: { archived_at: nil })) .preload(:created_by_user, template: :author) + + @submissions = @submissions.preload(:template_accesses) unless current_user.role.in?(%w[admin superadmin]) + @submissions = Submissions.search(@submissions, params[:q], search_template: true) @submissions = Submissions::Filter.call(@submissions, current_user, params) diff --git a/app/controllers/submissions_dashboard_controller.rb b/app/controllers/submissions_dashboard_controller.rb index 164e893a..f71e10b5 100644 --- a/app/controllers/submissions_dashboard_controller.rb +++ b/app/controllers/submissions_dashboard_controller.rb @@ -10,6 +10,8 @@ class SubmissionsDashboardController < ApplicationController .where(templates: { archived_at: nil }) .preload(:created_by_user, template: :author) + @submissions = @submissions.preload(:template_accesses) unless current_user.role.in?(%w[admin superadmin]) + @submissions = Submissions.search(@submissions, params[:q], search_template: true) @submissions = Submissions::Filter.call(@submissions, current_user, params) diff --git a/app/controllers/submitters_autocomplete_controller.rb b/app/controllers/submitters_autocomplete_controller.rb index 37b00c6e..e86e129e 100644 --- a/app/controllers/submitters_autocomplete_controller.rb +++ b/app/controllers/submitters_autocomplete_controller.rb @@ -9,7 +9,8 @@ class SubmittersAutocompleteController < ApplicationController def index submitters = search_submitters(@submitters) - values = submitters.limit(LIMIT).group(SELECT_COLUMNS.join(', ')).pluck(SELECT_COLUMNS.join(', ')) + arel_columns = SELECT_COLUMNS.map { |col| Submitter.arel_table[col] } + values = submitters.limit(LIMIT).group(arel_columns).pluck(arel_columns) attrs = values.map { |row| SELECT_COLUMNS.zip(row).to_h } attrs = attrs.uniq { |e| e[params[:field]] } if params[:field].present? diff --git a/app/controllers/template_folders_controller.rb b/app/controllers/template_folders_controller.rb index 87ce435c..dd7010bb 100644 --- a/app/controllers/template_folders_controller.rb +++ b/app/controllers/template_folders_controller.rb @@ -4,7 +4,7 @@ class TemplateFoldersController < ApplicationController load_and_authorize_resource :template_folder def show - @templates = @template_folder.templates.active.preload(:author).order(id: :desc) + @templates = @template_folder.templates.active.preload(:author, :template_accesses).order(id: :desc) @templates = Templates.search(@templates, params[:q]) @pagy, @templates = pagy(@templates, limit: 12) diff --git a/app/controllers/templates_archived_controller.rb b/app/controllers/templates_archived_controller.rb index 1f0f1752..f75e83a1 100644 --- a/app/controllers/templates_archived_controller.rb +++ b/app/controllers/templates_archived_controller.rb @@ -4,7 +4,7 @@ class TemplatesArchivedController < ApplicationController load_and_authorize_resource :template, parent: false def index - @templates = @templates.where.not(archived_at: nil).preload(:author, :folder).order(id: :desc) + @templates = @templates.where.not(archived_at: nil).preload(:author, :folder, :template_accesses).order(id: :desc) @templates = Templates.search(@templates, params[:q]) @pagy, @templates = pagy(@templates, limit: 12) diff --git a/app/controllers/templates_controller.rb b/app/controllers/templates_controller.rb index 0d414755..1b52c23b 100644 --- a/app/controllers/templates_controller.rb +++ b/app/controllers/templates_controller.rb @@ -22,6 +22,8 @@ class TemplatesController < ApplicationController submissions.order(id: :desc) end + submissions = submissions.preload(:template_accesses) unless current_user.role.in?(%w[admin superadmin]) + @pagy, @submissions = pagy(submissions.preload(submitters: :start_form_submission_events)) rescue ActiveRecord::RecordNotFound redirect_to root_path diff --git a/app/controllers/templates_dashboard_controller.rb b/app/controllers/templates_dashboard_controller.rb index be9dea75..3a6a66df 100644 --- a/app/controllers/templates_dashboard_controller.rb +++ b/app/controllers/templates_dashboard_controller.rb @@ -45,7 +45,7 @@ class TemplatesDashboardController < ApplicationController end def filter_templates(templates) - rel = templates.active.preload(:author).order(id: :desc) + rel = templates.active.preload(:author, :template_accesses).order(id: :desc) if params[:q].blank? if Docuseal.multitenant? && !current_account.testing? diff --git a/app/models/submission.rb b/app/models/submission.rb index e6aa35df..8567aaa1 100644 --- a/app/models/submission.rb +++ b/app/models/submission.rb @@ -57,6 +57,8 @@ class Submission < ApplicationRecord has_many_attached :preview_documents + has_many :template_accesses, primary_key: :template_id, foreign_key: :template_id, dependent: nil, inverse_of: false + has_many :template_schema_documents, ->(e) { where(uuid: (e.template_schema.presence || e.template.schema).pluck('attachment_uuid')) }, through: :template, source: :documents_attachments diff --git a/app/models/submitter.rb b/app/models/submitter.rb index c735a095..97a93773 100644 --- a/app/models/submitter.rb +++ b/app/models/submitter.rb @@ -54,6 +54,7 @@ class Submitter < ApplicationRecord has_many_attached :documents has_many_attached :attachments has_many_attached :preview_documents + has_many :template_accesses, through: :template has_many :document_generation_events, dependent: :destroy has_many :submission_events, dependent: :destroy diff --git a/app/models/template.rb b/app/models/template.rb index ec4ecec8..28441a9a 100644 --- a/app/models/template.rb +++ b/app/models/template.rb @@ -62,6 +62,7 @@ class Template < ApplicationRecord has_many :submissions, dependent: :destroy has_many :template_sharings, dependent: :destroy + has_many :template_accesses, dependent: :destroy scope :active, -> { where(archived_at: nil) } scope :archived, -> { where.not(archived_at: nil) } diff --git a/app/models/template_access.rb b/app/models/template_access.rb new file mode 100644 index 00000000..7e459513 --- /dev/null +++ b/app/models/template_access.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +# == Schema Information +# +# Table name: template_accesses +# +# id :bigint not null, primary key +# created_at :datetime not null +# updated_at :datetime not null +# template_id :bigint not null +# user_id :bigint not null +# +# Indexes +# +# index_template_accesses_on_template_id_and_user_id (template_id,user_id) UNIQUE +# +# Foreign Keys +# +# fk_rails_... (template_id => templates.id) +# +class TemplateAccess < ApplicationRecord + ADMIN_USER_ID = -1 + + belongs_to :template + belongs_to :user, optional: true +end diff --git a/app/views/submissions/show.html.erb b/app/views/submissions/show.html.erb index 163697f9..45365d17 100644 --- a/app/views/submissions/show.html.erb +++ b/app/views/submissions/show.html.erb @@ -122,7 +122,7 @@ <%= (@submission.template_submitters || @submission.template.submitters).find { |e| e['uuid'] == submitter&.uuid }&.dig('name') || "#{(index + 1).ordinalize} Submitter" %> - <% if signed_in? && can?(:update, submitter) && !submitter.completed_at? && !submitter.declined_at? && !@submission.archived_at? && !@submission.expired? && !submitter.start_form_submission_events.any? %> + <% if signed_in? && can?(:update, @submission) && !submitter.completed_at? && !submitter.declined_at? && !@submission.archived_at? && !@submission.expired? && !submitter.start_form_submission_events.any? %> <%= link_to edit_submitter_path(submitter), class: 'shrink-0 inline md:hidden md:group-hover:inline', data: { turbo_frame: 'modal' } do %> <%= svg_icon('pencil', class: 'w-5 h-5') %> @@ -174,15 +174,15 @@ <% end %> - <% if signed_in? && submitter && submitter.email && !submitter.completed_at && !@submission.archived_at? && can?(:update, submitter) && Accounts.can_send_emails?(current_account) && !@submission.expired? && !submitter.declined_at? %> + <% if signed_in? && submitter && submitter.email && !submitter.completed_at && !@submission.archived_at? && can?(:update, @submission) && Accounts.can_send_emails?(current_account) && !@submission.expired? && !submitter.declined_at? %>
<%= button_to button_title(title: submitter.sent_at? ? t('re_send_email') : t('send_email'), disabled_with: t('sending')), submitter_send_email_index_path(submitter_slug: submitter.slug), class: 'btn btn-sm btn-primary w-full' %>
<% end %> - <% if signed_in? && submitter && submitter.phone && !submitter.completed_at && !@submission.archived_at? && can?(:update, submitter) && !@submission.expired? && !submitter.declined_at? %> + <% if signed_in? && submitter && submitter.phone && !submitter.completed_at && !@submission.archived_at? && can?(:update, @submission) && !@submission.expired? && !submitter.declined_at? %> <%= render 'submissions/send_sms_button', submitter: %> <% end %> - <% if signed_in? && submitter && !submitter.completed_at? && !@submission.archived_at? && can?(:create, submitter) && !@submission.expired? && !submitter.declined_at? %> + <% if signed_in? && submitter && !submitter.completed_at? && !@submission.archived_at? && can?(:create, @submission) && !@submission.expired? && !submitter.declined_at? %>
<%= t('sign_in_person') %> diff --git a/app/views/templates/_access_icon.html.erb b/app/views/templates/_access_icon.html.erb new file mode 100644 index 00000000..e69de29b diff --git a/app/views/templates/_submission.html.erb b/app/views/templates/_submission.html.erb index ad0d490b..7b3488b7 100644 --- a/app/views/templates/_submission.html.erb +++ b/app/views/templates/_submission.html.erb @@ -51,7 +51,7 @@ <%= submitter.name || submitter.email || submitter.phone %> - <% if can?(:update, submitter) && !submitter.start_form_submission_events.any? && !submission.archived_at? && !submission.expired? && !submitter.declined_at? %> + <% if can?(:update, submission) && !submitter.start_form_submission_events.any? && !submission.archived_at? && !submission.expired? && !submitter.declined_at? %> <%= link_to edit_submitter_path(submitter), class: 'shrink-0', data: { turbo_frame: 'modal' } do %> <%= svg_icon('pencil', class: 'w-5 h-5') %> @@ -144,7 +144,7 @@ <%= submitter.name || submitter.email || submitter.phone %> - <% if can?(:update, submitter) && !submitter.start_form_submission_events.any? && !submission.archived_at? && !submission.expired? && !submitter.declined_at? %> + <% if can?(:update, submission) && !submitter.start_form_submission_events.any? && !submission.archived_at? && !submission.expired? && !submitter.declined_at? %> <%= link_to edit_submitter_path(submitter), class: 'shrink-0', data: { turbo_frame: 'modal' } do %> <%= svg_icon('pencil', class: 'w-5 h-5') %> diff --git a/app/views/templates/_template.html.erb b/app/views/templates/_template.html.erb index e0dbb6a1..4f954b31 100644 --- a/app/views/templates/_template.html.erb +++ b/app/views/templates/_template.html.erb @@ -1,6 +1,9 @@
+ <% if template.template_accesses.present? %> + <%= svg_icon('lock', class: 'w-6 h-6 inline -translate-y-[4px]') %> + <% end %> <% template.name.split(/(_)/).each do |item| %><%= item %><% end %>
diff --git a/app/views/templates/_title.html.erb b/app/views/templates/_title.html.erb index 83726b21..8a0628cb 100644 --- a/app/views/templates/_title.html.erb +++ b/app/views/templates/_title.html.erb @@ -1,12 +1,15 @@
-

- <% template.name.split(/(_)/).each do |item| %><%= item %><% end %> - <% if template.archived_at? %> - <%= t('archived') %> - <% end %> -

+
+

+ <% template.name.split(/(_)/).each do |item| %><%= item %><% end %> + <% if template.archived_at? %> + <%= t('archived') %> + <% end %> +

+ <%= render 'templates/access_icon', template: @template %> +
<% if @template.account_id == current_account.id %>
diff --git a/app/views/templates_preferences/_access.html.erb b/app/views/templates_preferences/_access.html.erb new file mode 100644 index 00000000..e69de29b diff --git a/app/views/templates_preferences/show.html.erb b/app/views/templates_preferences/show.html.erb index 72437309..1059bcca 100644 --- a/app/views/templates_preferences/show.html.erb +++ b/app/views/templates_preferences/show.html.erb @@ -19,6 +19,7 @@ <% end %>
+ <%= render 'access' %> <%= form_for @template, url: template_preferences_path(@template), method: :post, html: { autocomplete: 'off', class: 'mt-2' }, data: { close_on_submit: false } do |f| %> <%= f.fields_for :preferences, Struct.new(:bcc_completed).new(@template.preferences['bcc_completed']) do |ff| %> diff --git a/config/locales/i18n.yml b/config/locales/i18n.yml index 05e22545..87c69fcd 100644 --- a/config/locales/i18n.yml +++ b/config/locales/i18n.yml @@ -23,6 +23,15 @@ en: &en unarchive: Unarchive first_party: 'First Party' remove_filter: Remove filter + add: Add + adding: Adding + owner: Owner + select_user: Select user + team_member_permissions: Team member permissions + entire_team: Entire team + admin_only: Admin only + accessiable_by: Accessiable by + team_access: Team access document_download_filename_format: Document download filename format document_name: Document Name docuseal_trusted_signature: DocuSeal Trusted Signature @@ -689,6 +698,15 @@ en: &en read: Read your data es: &es + add: Agregar + adding: Agregando + owner: Propietario + select_user: Seleccionar usuario + team_member_permissions: Permisos de miembros del equipo + entire_team: Todo el equipo + admin_only: Solo administrador + accessiable_by: Accesible por + team_access: Acceso del equipo remove_filter: Eliminar filtro document_download_filename_format: Formato del nombre del archivo de descarga del documento document_name: Nombre del documento @@ -1360,6 +1378,15 @@ es: &es read: Leer tus datos it: &it + add: Aggiungi + adding: Aggiungendo + owner: Proprietario + select_user: Seleziona utente + team_member_permissions: Permessi membri del team + entire_team: Intero team + admin_only: Solo amministratore + accessiable_by: Accessibile da + team_access: Accesso al team remove_filter: Rimuovi filtro document_download_filename_format: Formato del nome file scaricato document_name: Nome del Documento @@ -2031,6 +2058,15 @@ it: &it read: Leggi i tuoi dati fr: &fr + add: Ajouter + adding: Ajout + owner: Propriétaire + select_user: Sélectionner un utilisateur + team_member_permissions: Permissions des membres de l'équipe + entire_team: Équipe entière + admin_only: Administrateur uniquement + accessiable_by: Accessible par + team_access: Accès à l'équipe remove_filter: Supprimer le filtre document_download_filename_format: Format du nom de fichier du téléchargement de document document_name: Nom du document @@ -2703,6 +2739,15 @@ fr: &fr read: Lire vos données pt: &pt + add: Adicionar + adding: Adicionando + owner: Proprietário + select_user: Selecionar usuário + team_member_permissions: Permissões de membro da equipe + entire_team: Toda a equipe + admin_only: Somente administrador + accessiable_by: Acessível por + team_access: Acesso à equipe remove_filter: Remover filtro document_download_filename_format: Formato do nome do arquivo de download do documento document_name: Nome do documento @@ -3374,6 +3419,15 @@ pt: &pt read: Ler seus dados de: &de + add: Hinzufügen + adding: Hinzufügen + owner: Eigentümer + select_user: Benutzer auswählen + team_member_permissions: Berechtigungen für Teammitglieder + entire_team: Gesamtes Team + admin_only: Nur Administratoren + accessiable_by: Zugänglich für + team_access: Teamzugang remove_filter: Filter entfernen document_download_filename_format: Format des Dateinamens beim Herunterladen von Dokumenten document_name: Dokumentname diff --git a/db/migrate/20241207172237_create_template_accesses.rb b/db/migrate/20241207172237_create_template_accesses.rb new file mode 100644 index 00000000..bdf2d2ce --- /dev/null +++ b/db/migrate/20241207172237_create_template_accesses.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +class CreateTemplateAccesses < ActiveRecord::Migration[7.2] + def change + create_table :template_accesses do |t| + t.references :template, null: false, foreign_key: true, index: false + t.references :user, null: false, foreign_key: false, index: false + + t.index %i[template_id user_id], unique: true + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 92e6a982..89cf0fae 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.2].define(version: 2024_10_29_192232) do +ActiveRecord::Schema[7.2].define(version: 2024_12_07_172237) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -275,6 +275,14 @@ ActiveRecord::Schema[7.2].define(version: 2024_10_29_192232) do t.index ["submission_id"], name: "index_submitters_on_submission_id" end + create_table "template_accesses", force: :cascade do |t| + t.bigint "template_id", null: false + t.bigint "user_id", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["template_id", "user_id"], name: "index_template_accesses_on_template_id_and_user_id", unique: true + end + create_table "template_folders", force: :cascade do |t| t.string "name", null: false t.bigint "author_id", null: false @@ -393,6 +401,7 @@ ActiveRecord::Schema[7.2].define(version: 2024_10_29_192232) do add_foreign_key "submissions", "templates" add_foreign_key "submissions", "users", column: "created_by_user_id" add_foreign_key "submitters", "submissions" + add_foreign_key "template_accesses", "templates" add_foreign_key "template_folders", "accounts" add_foreign_key "template_folders", "users", column: "author_id" add_foreign_key "template_sharings", "templates"