add cancan for authorization
This commit is contained in:
@@ -7,6 +7,7 @@ ruby '3.2.2'
|
||||
gem 'aws-sdk-s3', require: false
|
||||
gem 'azure-storage-blob', require: false
|
||||
gem 'bootsnap', require: false
|
||||
gem 'cancancan'
|
||||
gem 'devise'
|
||||
gem 'devise-two-factor'
|
||||
gem 'dotenv', require: false
|
||||
|
||||
@@ -114,6 +114,7 @@ GEM
|
||||
bullet (7.0.7)
|
||||
activesupport (>= 3.0.0)
|
||||
uniform_notifier (~> 1.11)
|
||||
cancancan (3.5.0)
|
||||
capybara (3.39.2)
|
||||
addressable
|
||||
matrix
|
||||
@@ -566,6 +567,7 @@ DEPENDENCIES
|
||||
better_html
|
||||
bootsnap
|
||||
bullet
|
||||
cancancan
|
||||
capybara
|
||||
cuprite
|
||||
debug
|
||||
|
||||
@@ -9,6 +9,9 @@ class AccountsController < ApplicationController
|
||||
'de-DE' => 'German (Germany)'
|
||||
}.freeze
|
||||
|
||||
before_action :load_account
|
||||
authorize_resource :account
|
||||
|
||||
def show; end
|
||||
|
||||
def update
|
||||
@@ -35,6 +38,10 @@ class AccountsController < ApplicationController
|
||||
|
||||
private
|
||||
|
||||
def load_account
|
||||
@account = current_account
|
||||
end
|
||||
|
||||
def account_params
|
||||
params.require(:account).permit(:name, :timezone, :locale)
|
||||
end
|
||||
|
||||
@@ -5,6 +5,15 @@ module Api
|
||||
include ActiveStorage::SetCurrent
|
||||
|
||||
before_action :authenticate_user!
|
||||
check_authorization
|
||||
|
||||
if Rails.env.production?
|
||||
rescue_from CanCan::AccessDenied do |e|
|
||||
Rollbar.error(e) if defined?(Rollbar)
|
||||
|
||||
render json: { error: e.message }, status: :forbidden
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
module Api
|
||||
class AttachmentsController < ApiBaseController
|
||||
skip_before_action :authenticate_user!
|
||||
skip_authorization_check
|
||||
|
||||
def create
|
||||
submitter = Submitter.find_by!(slug: params[:submitter_slug])
|
||||
|
||||
@@ -5,21 +5,25 @@ module Api
|
||||
UnknownFieldName = Class.new(StandardError)
|
||||
UnknownSubmitterName = Class.new(StandardError)
|
||||
|
||||
def create
|
||||
template = current_account.templates.find(params[:template_id])
|
||||
load_and_authorize_resource :template
|
||||
|
||||
before_action do
|
||||
authorize!(:create, Submission)
|
||||
end
|
||||
|
||||
def create
|
||||
submissions =
|
||||
if (emails = (params[:emails] || params[:email]).presence)
|
||||
Submissions.create_from_emails(template:,
|
||||
Submissions.create_from_emails(template: @template,
|
||||
user: current_user,
|
||||
source: :api,
|
||||
mark_as_sent: params[:send_email] != 'false',
|
||||
emails:)
|
||||
else
|
||||
submissions_attrs = normalize_submissions_params!(submissions_params[:submission], template)
|
||||
submissions_attrs = normalize_submissions_params!(submissions_params[:submission], @template)
|
||||
|
||||
Submissions.create_from_submitters(
|
||||
template:,
|
||||
template: @template,
|
||||
user: current_user,
|
||||
source: :api,
|
||||
mark_as_sent: params[:send_email] != 'false',
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
module Api
|
||||
class SubmitterEmailClicksController < ApiBaseController
|
||||
skip_before_action :authenticate_user!
|
||||
skip_authorization_check
|
||||
|
||||
def create
|
||||
submitter = Submitter.find_by!(slug: params[:submitter_slug])
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
module Api
|
||||
class SubmitterFormViewsController < ApiBaseController
|
||||
skip_before_action :authenticate_user!
|
||||
skip_authorization_check
|
||||
|
||||
def create
|
||||
submitter = Submitter.find_by!(slug: params[:submitter_slug])
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
module Api
|
||||
class TemplatesController < ApiBaseController
|
||||
before_action :load_template, only: %i[show update]
|
||||
load_and_authorize_resource :template
|
||||
|
||||
def index
|
||||
render json: current_account.templates
|
||||
render json: @templates
|
||||
end
|
||||
|
||||
def show
|
||||
@@ -28,9 +28,5 @@ module Api
|
||||
fields: [[:uuid, :submitter_uuid, :name, :type, :required,
|
||||
{ options: [], areas: [%i[x y w h cell_w attachment_uuid page]] }]])
|
||||
end
|
||||
|
||||
def load_template
|
||||
@template = current_account.templates.find(params[:id])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
module Api
|
||||
class TemplatesDocumentsController < ApiBaseController
|
||||
load_and_authorize_resource :template
|
||||
|
||||
def create
|
||||
return head :unprocessable_entity if params[:blobs].blank? && params[:files].blank?
|
||||
|
||||
@template = current_account.templates.find(params[:template_id])
|
||||
|
||||
documents = Templates::CreateAttachments.call(@template, params)
|
||||
|
||||
schema = documents.map do |doc|
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ApiSettingsController < ApplicationController
|
||||
def index; end
|
||||
def index
|
||||
authorize!(:read, current_user.access_token)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,6 +4,8 @@ class ApplicationController < ActionController::Base
|
||||
include ActiveStorage::SetCurrent
|
||||
include Pagy::Backend
|
||||
|
||||
check_authorization unless: :devise_controller?
|
||||
|
||||
before_action :sign_in_for_demo, if: -> { Docuseal.demo? }
|
||||
before_action :maybe_redirect_to_setup, unless: :signed_in?
|
||||
before_action :authenticate_user!, unless: :devise_controller?
|
||||
@@ -16,6 +18,14 @@ class ApplicationController < ActionController::Base
|
||||
redirect_to request.path
|
||||
end
|
||||
|
||||
if Rails.env.production?
|
||||
rescue_from CanCan::AccessDenied do |e|
|
||||
Rollbar.error(e) if defined?(Rollbar)
|
||||
|
||||
redirect_back(fallback_location: root_path, alert: e.message)
|
||||
end
|
||||
end
|
||||
|
||||
def default_url_options
|
||||
Docuseal.default_url_options
|
||||
end
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
class ConsoleRedirectController < ApplicationController
|
||||
skip_before_action :authenticate_user!
|
||||
skip_authorization_check
|
||||
|
||||
def index
|
||||
return redirect_to(new_user_session_path({ redir: params[:redir] }.compact)) if current_user.blank?
|
||||
|
||||
@@ -3,13 +3,29 @@
|
||||
class DashboardController < ApplicationController
|
||||
skip_before_action :authenticate_user!, only: %i[index]
|
||||
|
||||
before_action :maybe_redirect_product_url
|
||||
before_action :maybe_render_landing
|
||||
|
||||
load_and_authorize_resource :template, parent: false
|
||||
|
||||
def index
|
||||
return redirect_to Docuseal::PRODUCT_URL, allow_other_host: true if Docuseal.multitenant? && !signed_in?
|
||||
return render 'pages/landing' unless signed_in?
|
||||
@templates = @templates.active.preload(:author).order(id: :desc)
|
||||
@templates = Templates.search(@templates, params[:q])
|
||||
|
||||
templates = current_account.templates.active.preload(:author).order(id: :desc)
|
||||
templates = Templates.search(templates, params[:q])
|
||||
@pagy, @templates = pagy(@templates, items: 12)
|
||||
end
|
||||
|
||||
@pagy, @templates = pagy(templates, items: 12)
|
||||
private
|
||||
|
||||
def maybe_redirect_product_url
|
||||
return if !Docuseal.multitenant? || signed_in?
|
||||
|
||||
redirect_to Docuseal::PRODUCT_URL, allow_other_host: true
|
||||
end
|
||||
|
||||
def maybe_render_landing
|
||||
return if signed_in?
|
||||
|
||||
render 'pages/landing'
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
class EmailSettingsController < ApplicationController
|
||||
before_action :load_encrypted_config
|
||||
authorize_resource :encrypted_config, only: :index
|
||||
authorize_resource :encrypted_config, parent: false, only: :create
|
||||
|
||||
def index; end
|
||||
|
||||
|
||||
@@ -11,9 +11,12 @@ class EsignSettingsController < ApplicationController
|
||||
end
|
||||
end
|
||||
|
||||
before_action :load_encrypted_config
|
||||
authorize_resource :encrypted_config, parent: false, only: %i[new create]
|
||||
authorize_resource :encrypted_config, only: %i[update destroy show]
|
||||
|
||||
def show
|
||||
cert_data = EncryptedConfig.find_by(account: current_account,
|
||||
key: EncryptedConfig::ESIGN_CERTS_KEY)&.value || {}
|
||||
cert_data = @encrypted_config.value || {}
|
||||
|
||||
default_pkcs = GenerateCertificate.load_pkcs(cert_data) if cert_data['cert'].present?
|
||||
|
||||
@@ -42,10 +45,7 @@ class EsignSettingsController < ApplicationController
|
||||
def create
|
||||
@cert_record = CertFormRecord.new(**cert_params)
|
||||
|
||||
cert_configs = EncryptedConfig.find_or_initialize_by(account: current_account,
|
||||
key: EncryptedConfig::ESIGN_CERTS_KEY)
|
||||
|
||||
if (cert_configs.value && cert_configs.value['custom']&.any? { |e| e['name'] == @cert_record.name }) ||
|
||||
if (@encrypted_config.value && @encrypted_config.value['custom']&.any? { |e| e['name'] == @cert_record.name }) ||
|
||||
@cert_record.name == DEFAULT_CERT_NAME
|
||||
|
||||
@cert_record.errors.add(:name, 'already exists')
|
||||
@@ -54,7 +54,7 @@ class EsignSettingsController < ApplicationController
|
||||
status: :unprocessable_entity
|
||||
end
|
||||
|
||||
save_new_cert!(cert_configs, @cert_record)
|
||||
save_new_cert!(@encrypted_config, @cert_record)
|
||||
|
||||
redirect_to settings_esign_path, notice: 'Certificate has been successfully added!'
|
||||
rescue OpenSSL::PKCS12::PKCS12Error
|
||||
@@ -64,29 +64,31 @@ class EsignSettingsController < ApplicationController
|
||||
end
|
||||
|
||||
def update
|
||||
cert_configs = EncryptedConfig.find_by(account: current_account, key: EncryptedConfig::ESIGN_CERTS_KEY)
|
||||
@encrypted_config.value['custom'].each { |e| e['status'] = 'validate' }
|
||||
|
||||
cert_configs.value['custom'].each { |e| e['status'] = 'validate' }
|
||||
custom_cert_data = cert_configs.value['custom'].find { |e| e['name'] == params[:name] }
|
||||
custom_cert_data = @encrypted_config.value['custom'].find { |e| e['name'] == params[:name] }
|
||||
custom_cert_data['status'] = 'default' if custom_cert_data
|
||||
|
||||
cert_configs.save!
|
||||
@encrypted_config.save!
|
||||
|
||||
redirect_to settings_esign_path, notice: 'Default certificate has been selected'
|
||||
end
|
||||
|
||||
def destroy
|
||||
cert_configs = EncryptedConfig.find_by(account: current_account, key: EncryptedConfig::ESIGN_CERTS_KEY)
|
||||
@encrypted_config.value['custom'].reject! { |e| e['name'] == params[:name] }
|
||||
|
||||
cert_configs.value['custom'].reject! { |e| e['name'] == params[:name] }
|
||||
|
||||
cert_configs.save!
|
||||
@encrypted_config.save!
|
||||
|
||||
redirect_to settings_esign_path, notice: 'Certificate has been removed'
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_encrypted_config
|
||||
@encrypted_config = EncryptedConfig.find_or_initialize_by(account: current_account,
|
||||
key: EncryptedConfig::ESIGN_CERTS_KEY)
|
||||
end
|
||||
|
||||
def save_new_cert!(cert_configs, cert_record)
|
||||
pkcs = OpenSSL::PKCS12.new(cert_record.file.read, cert_record.password)
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class MfaSetupController < ApplicationController
|
||||
before_action do
|
||||
authorize!(:update, current_user)
|
||||
end
|
||||
|
||||
def new
|
||||
current_user.otp_secret ||= User.generate_otp_secret
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class NewslettersController < ApplicationController
|
||||
skip_authorization_check
|
||||
|
||||
def show; end
|
||||
|
||||
def update
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class PersonalizationSettingsController < ApplicationController
|
||||
def show; end
|
||||
def show
|
||||
authorize!(:read, AccountConfig)
|
||||
end
|
||||
|
||||
def create
|
||||
account_config =
|
||||
current_account.account_configs.find_or_initialize_by(key: encrypted_config_params[:key])
|
||||
|
||||
authorize!(:create, account_config)
|
||||
|
||||
account_config.update!(encrypted_config_params)
|
||||
|
||||
redirect_back(fallback_location: settings_personalization_path, notice: 'Settings have been saved.')
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ProfileController < ApplicationController
|
||||
before_action do
|
||||
authorize!(:manage, current_user)
|
||||
end
|
||||
|
||||
def index; end
|
||||
|
||||
def update_contact
|
||||
|
||||
@@ -5,6 +5,7 @@ class SendSubmissionEmailController < ApplicationController
|
||||
|
||||
skip_before_action :authenticate_user!
|
||||
skip_before_action :verify_authenticity_token
|
||||
skip_authorization_check
|
||||
|
||||
def success; end
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
class SetupController < ApplicationController
|
||||
skip_before_action :maybe_redirect_to_setup
|
||||
skip_before_action :authenticate_user!
|
||||
skip_authorization_check
|
||||
|
||||
before_action :redirect_to_root_if_signed, if: :signed_in?
|
||||
before_action :ensure_first_user_not_created!
|
||||
|
||||
@@ -1,5 +1,16 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class SmsSettingsController < ApplicationController
|
||||
before_action :load_encrypted_config
|
||||
authorize_resource :encrypted_config, only: :index
|
||||
authorize_resource :encrypted_config, parent: false, except: :index
|
||||
|
||||
def index; end
|
||||
|
||||
private
|
||||
|
||||
def load_encrypted_config
|
||||
@encrypted_config =
|
||||
EncryptedConfig.find_or_initialize_by(account: current_account, key: 'sms_configs')
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,6 +4,7 @@ class StartFormController < ApplicationController
|
||||
layout 'form'
|
||||
|
||||
skip_before_action :authenticate_user!
|
||||
skip_authorization_check
|
||||
|
||||
before_action :load_template
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
class StorageSettingsController < ApplicationController
|
||||
before_action :load_encrypted_config
|
||||
authorize_resource :encrypted_config, only: :index
|
||||
authorize_resource :encrypted_config, parent: false, only: :create
|
||||
|
||||
def index; end
|
||||
|
||||
|
||||
@@ -2,19 +2,26 @@
|
||||
|
||||
class SubmissionsController < ApplicationController
|
||||
before_action :load_template, only: %i[new create]
|
||||
authorize_resource :template, only: %i[new create]
|
||||
|
||||
load_and_authorize_resource :submission, only: %i[show destroy]
|
||||
|
||||
def show
|
||||
@submission =
|
||||
Submission.joins(:template).where(template: { account_id: current_account.id })
|
||||
.preload(:template, template_schema_documents: [:blob, { preview_images_attachments: :blob }])
|
||||
.find(params[:id])
|
||||
ActiveRecord::Associations::Preloader.new(
|
||||
records: [@submission],
|
||||
associations: [:template, { template_schema_documents: [:blob, { preview_images_attachments: :blob }] }]
|
||||
).call
|
||||
|
||||
render :show, layout: 'plain'
|
||||
end
|
||||
|
||||
def new; end
|
||||
def new
|
||||
authorize!(:new, Submission)
|
||||
end
|
||||
|
||||
def create
|
||||
authorize!(:create, Submission)
|
||||
|
||||
submissions =
|
||||
if params[:emails].present?
|
||||
Submissions.create_from_emails(template: @template,
|
||||
@@ -37,12 +44,9 @@ class SubmissionsController < ApplicationController
|
||||
end
|
||||
|
||||
def destroy
|
||||
submission = Submission.joins(:template).where(template: { account_id: current_account.id })
|
||||
.find(params[:id])
|
||||
@submission.update!(deleted_at: Time.current)
|
||||
|
||||
submission.update!(deleted_at: Time.current)
|
||||
|
||||
redirect_back(fallback_location: template_path(submission.template), notice: 'Submission has been archived')
|
||||
redirect_back(fallback_location: template_path(@submission.template), notice: 'Submission has been archived')
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -4,6 +4,7 @@ class SubmissionsDebugController < ApplicationController
|
||||
layout 'plain'
|
||||
|
||||
skip_before_action :authenticate_user!
|
||||
skip_authorization_check
|
||||
|
||||
def index
|
||||
@submitter = Submitter.preload({ attachments_attachments: :blob },
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
class SubmissionsDownloadController < ApplicationController
|
||||
skip_before_action :authenticate_user!
|
||||
skip_authorization_check
|
||||
|
||||
def index
|
||||
submitter = Submitter.find_by(slug: params[:submitter_slug])
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class SubmissionsExportController < ApplicationController
|
||||
before_action :load_template
|
||||
load_and_authorize_resource :template
|
||||
load_and_authorize_resource :submission, through: :template, parent: false, only: :index
|
||||
|
||||
def index
|
||||
submissions = @template.submissions.active
|
||||
.preload(submitters: { documents_attachments: :blob,
|
||||
attachments_attachments: :blob })
|
||||
.order(id: :asc)
|
||||
submissions = @submissions.active
|
||||
.preload(submitters: { documents_attachments: :blob,
|
||||
attachments_attachments: :blob })
|
||||
.order(id: :asc)
|
||||
|
||||
if params[:format] == 'csv'
|
||||
send_data Submissions::GenerateExportFiles.call(submissions, format: params[:format]),
|
||||
@@ -19,10 +20,4 @@ class SubmissionsExportController < ApplicationController
|
||||
end
|
||||
|
||||
def new; end
|
||||
|
||||
private
|
||||
|
||||
def load_template
|
||||
@template = current_account.templates.find(params[:template_id])
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,6 +4,7 @@ class SubmitFormController < ApplicationController
|
||||
layout 'form'
|
||||
|
||||
skip_before_action :authenticate_user!
|
||||
skip_authorization_check
|
||||
|
||||
def show
|
||||
@submitter =
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class SubmittersSendEmailController < ApplicationController
|
||||
load_and_authorize_resource :submitter, id_param: :submitter_slug, find_by: :slug
|
||||
|
||||
def create
|
||||
submitter = Submitter.joins(:template)
|
||||
.where(template: { account_id: current_account.id })
|
||||
.find_by!(slug: params[:submitter_slug])
|
||||
SubmitterMailer.invitation_email(@submitter).deliver_later!
|
||||
|
||||
SubmitterMailer.invitation_email(submitter).deliver_later!
|
||||
SubmissionEvent.create!(submitter: @submitter, event_type: 'send_email')
|
||||
|
||||
SubmissionEvent.create!(submitter:, event_type: 'send_email')
|
||||
@submitter.sent_at ||= Time.current
|
||||
@submitter.save!
|
||||
|
||||
submitter.sent_at ||= Time.current
|
||||
submitter.save!
|
||||
|
||||
redirect_back(fallback_location: submission_path(submitter.submission), notice: 'Email has been sent')
|
||||
redirect_back(fallback_location: submission_path(@submitter.submission), notice: 'Email has been sent')
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class TemplatesArchivedController < ApplicationController
|
||||
def index
|
||||
templates = current_account.templates.where.not(deleted_at: nil).preload(:author).order(id: :desc)
|
||||
templates = Templates.search(templates, params[:q])
|
||||
load_and_authorize_resource :template, parent: false
|
||||
|
||||
@pagy, @templates = pagy(templates, items: 12)
|
||||
def index
|
||||
@templates = @templates.where.not(deleted_at: nil).preload(:author).order(id: :desc)
|
||||
@templates = Templates.search(@templates, params[:q])
|
||||
|
||||
@pagy, @templates = pagy(@templates, items: 12)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class TemplatesArchivedSubmissionsController < ApplicationController
|
||||
def show
|
||||
@template = current_account.templates.find(params[:template_id])
|
||||
load_and_authorize_resource :template
|
||||
load_and_authorize_resource :submission, through: :template, parent: false
|
||||
|
||||
submissions = @template.submissions.where.not(deleted_at: nil)
|
||||
submissions = Submissions.search(submissions, params[:q])
|
||||
def index
|
||||
@submissions = @submissions.where.not(deleted_at: nil)
|
||||
@submissions = Submissions.search(@submissions, params[:q])
|
||||
|
||||
@pagy, @submissions = pagy(submissions.preload(:submitters).order(id: :desc))
|
||||
@pagy, @submissions = pagy(@submissions.preload(:submitters).order(id: :desc))
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
redirect_to root_path
|
||||
end
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class TemplatesController < ApplicationController
|
||||
load_and_authorize_resource :template
|
||||
|
||||
before_action :load_base_template, only: %i[new create]
|
||||
|
||||
def show
|
||||
@template = current_account.templates.find(params[:id])
|
||||
submissions = @template.submissions
|
||||
submissions = submissions.active if @template.deleted_at.blank?
|
||||
submissions = Submissions.search(submissions, params[:q])
|
||||
@@ -15,19 +16,19 @@ class TemplatesController < ApplicationController
|
||||
end
|
||||
|
||||
def new
|
||||
@template = current_account.templates.new
|
||||
@template.name = "#{@base_template.name} (Clone)" if @base_template
|
||||
end
|
||||
|
||||
def edit
|
||||
@template = current_account.templates.preload(schema_documents: { preview_images_attachments: :blob })
|
||||
.find(params[:id])
|
||||
ActiveRecord::Associations::Preloader.new(
|
||||
records: [@template],
|
||||
associations: [schema_documents: { preview_images_attachments: :blob }]
|
||||
).call
|
||||
|
||||
render :edit, layout: 'plain'
|
||||
end
|
||||
|
||||
def create
|
||||
@template = current_account.templates.new(template_params)
|
||||
@template.author = current_user
|
||||
@template.assign_attributes(@base_template.slice(:fields, :schema, :submitters)) if @base_template
|
||||
|
||||
@@ -41,7 +42,6 @@ class TemplatesController < ApplicationController
|
||||
end
|
||||
|
||||
def destroy
|
||||
@template = current_account.templates.find(params[:id])
|
||||
@template.update!(deleted_at: Time.current)
|
||||
|
||||
redirect_back(fallback_location: root_path, notice: 'Template has been archived.')
|
||||
@@ -56,8 +56,6 @@ class TemplatesController < ApplicationController
|
||||
def load_base_template
|
||||
return if params[:base_template_id].blank?
|
||||
|
||||
@base_template = current_account.templates
|
||||
.preload(documents_attachments: :preview_images_attachments)
|
||||
.find_by(id: params[:base_template_id])
|
||||
@base_template = current_account.templates.find_by(id: params[:base_template_id])
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class TemplatesRestoreController < ApplicationController
|
||||
load_and_authorize_resource :template
|
||||
|
||||
def create
|
||||
template = current_account.templates.find(params[:template_id])
|
||||
@template.update!(deleted_at: nil)
|
||||
|
||||
template.update!(deleted_at: nil)
|
||||
|
||||
redirect_to template_path(template), notice: 'Template has been unarchived'
|
||||
redirect_to template_path(@template), notice: 'Template has been unarchived'
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class TemplatesUploadsController < ApplicationController
|
||||
load_and_authorize_resource :template, parent: false
|
||||
|
||||
def create
|
||||
template = current_account.templates.new(author: current_user)
|
||||
template.name = File.basename(params[:files].first.original_filename, '.*')
|
||||
@template.author = current_user
|
||||
@template.name = File.basename(params[:files].first.original_filename, '.*')
|
||||
|
||||
template.save!
|
||||
@template.save!
|
||||
|
||||
documents = Templates::CreateAttachments.call(template, params)
|
||||
documents = Templates::CreateAttachments.call(@template, params)
|
||||
|
||||
schema = documents.map { |doc| { attachment_uuid: doc.uuid, name: doc.filename.base } }
|
||||
|
||||
template.update!(schema:)
|
||||
@template.update!(schema:)
|
||||
|
||||
redirect_to edit_template_path(template)
|
||||
redirect_to edit_template_path(@template)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,26 +1,20 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class UsersController < ApplicationController
|
||||
before_action :load_user, only: %i[edit update destroy]
|
||||
load_and_authorize_resource :user, only: %i[index edit new update destroy]
|
||||
|
||||
before_action :build_user, only: :create
|
||||
authorize_resource :user, only: :create
|
||||
|
||||
def index
|
||||
@pagy, @users = pagy(current_account.users.active.order(id: :desc))
|
||||
@pagy, @users = pagy(@users.active.order(id: :desc))
|
||||
end
|
||||
|
||||
def new
|
||||
@user = current_account.users.new
|
||||
end
|
||||
def new; end
|
||||
|
||||
def edit; end
|
||||
|
||||
def create
|
||||
@user = current_account.users.find_by(email: user_params[:email])&.tap do |user|
|
||||
user.assign_attributes(user_params)
|
||||
user.deleted_at = nil
|
||||
end
|
||||
|
||||
@user ||= current_account.users.new(user_params)
|
||||
|
||||
if @user.save
|
||||
UserMailer.invitation_email(@user).deliver_later!
|
||||
|
||||
@@ -33,7 +27,7 @@ class UsersController < ApplicationController
|
||||
def update
|
||||
return redirect_to settings_users_path, notice: 'Unable to update user.' if Docuseal.demo?
|
||||
|
||||
if @user.update(user_params.compact_blank)
|
||||
if @user.update(user_params.compact_blank.except(current_user == @user ? :role : nil))
|
||||
redirect_to settings_users_path, notice: 'User has been updated'
|
||||
else
|
||||
render turbo_stream: turbo_stream.replace(:modal, template: 'users/edit'), status: :unprocessable_entity
|
||||
@@ -52,11 +46,18 @@ class UsersController < ApplicationController
|
||||
|
||||
private
|
||||
|
||||
def load_user
|
||||
@user = current_account.users.find(params[:id])
|
||||
def build_user
|
||||
@user = current_account.users.find_by(email: user_params[:email])&.tap do |user|
|
||||
user.assign_attributes(user_params)
|
||||
user.deleted_at = nil
|
||||
end
|
||||
|
||||
@user ||= current_account.users.new(user_params)
|
||||
|
||||
@user
|
||||
end
|
||||
|
||||
def user_params
|
||||
params.require(:user).permit(:email, :first_name, :last_name, :password)
|
||||
params.require(:user).permit(:email, :first_name, :last_name, :password, :role)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class VerifyPdfSignatureController < ApplicationController
|
||||
skip_authorization_check
|
||||
|
||||
def create
|
||||
pdfs =
|
||||
params[:files].map do |file|
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class WebhookSettingsController < ApplicationController
|
||||
def show
|
||||
@encrypted_config =
|
||||
current_account.encrypted_configs.find_or_initialize_by(key: EncryptedConfig::WEBHOOK_URL_KEY)
|
||||
end
|
||||
before_action :load_encrypted_config
|
||||
authorize_resource :encrypted_config, parent: false
|
||||
|
||||
def show; end
|
||||
|
||||
def create
|
||||
@encrypted_config =
|
||||
current_account.encrypted_configs.find_or_initialize_by(key: EncryptedConfig::WEBHOOK_URL_KEY)
|
||||
|
||||
@encrypted_config.update!(encrypted_config_params)
|
||||
|
||||
redirect_back(fallback_location: settings_webhooks_path, notice: 'Webhook URL has been saved.')
|
||||
@@ -25,6 +22,11 @@ class WebhookSettingsController < ApplicationController
|
||||
|
||||
private
|
||||
|
||||
def load_encrypted_config
|
||||
@encrypted_config =
|
||||
current_account.encrypted_configs.find_or_initialize_by(key: EncryptedConfig::WEBHOOK_URL_KEY)
|
||||
end
|
||||
|
||||
def encrypted_config_params
|
||||
params.require(:encrypted_config).permit(:value)
|
||||
end
|
||||
|
||||
@@ -29,9 +29,11 @@
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<div class="form-control pt-2">
|
||||
<%= f.button button_title(title: 'Update', disabled_with: 'Updating'), class: 'base-button' %>
|
||||
</div>
|
||||
<% if can?(:update, current_account) %>
|
||||
<div class="form-control pt-2">
|
||||
<%= f.button button_title(title: 'Update', disabled_with: 'Updating'), class: 'base-button' %>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="w-0 md:w-52"></div>
|
||||
|
||||
@@ -6,10 +6,12 @@
|
||||
<% if params[:q].present? || @pagy.pages > 1 %>
|
||||
<%= render 'shared/search_input' %>
|
||||
<% end %>
|
||||
<%= render 'templates/upload_button' %>
|
||||
<%= link_to new_template_path, class: 'btn btn-primary text-base btn-md gap-2', data: { turbo_frame: :modal } do %>
|
||||
<%= svg_icon('plus', class: 'w-6 h-6 stroke-2') %>
|
||||
<span class="hidden md:block">Create</span>
|
||||
<% if can?(:create, ::Template) %>
|
||||
<%= render 'templates/upload_button' %>
|
||||
<%= link_to new_template_path, class: 'btn btn-primary text-base btn-md gap-2', data: { turbo_frame: :modal } do %>
|
||||
<%= svg_icon('plus', class: 'w-6 h-6 stroke-2') %>
|
||||
<span class="hidden md:block">Create</span>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<%= f.label :password, 'Password (optional)', class: 'label' %>
|
||||
<%= f.text_field :password, class: 'base-input' %>
|
||||
<%= f.password_field :password, class: 'base-input' %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-control pt-2">
|
||||
|
||||
@@ -40,9 +40,11 @@
|
||||
</div>
|
||||
<div class="flex justify-between items-end mb-4 mt-8">
|
||||
<h2 class="text-3xl font-bold">Signing Certificates</h2>
|
||||
<%= link_to new_settings_esign_path, class: 'btn btn-primary btn-md', data: { turbo_frame: 'modal' } do %>
|
||||
<%= svg_icon('plus', class: 'w-6 h-6') %>
|
||||
<span>Upload Cert</span>
|
||||
<% if can?(:create, @encrypted_config) %>
|
||||
<%= link_to new_settings_esign_path, class: 'btn btn-primary btn-md', data: { turbo_frame: 'modal' } do %>
|
||||
<%= svg_icon('plus', class: 'w-6 h-6') %>
|
||||
<span>Upload Cert</span>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
<%= render 'alert' %>
|
||||
@@ -77,14 +79,14 @@
|
||||
<span class="badge badge-lg badge-info badge-outline">
|
||||
<%= item['status'] %>
|
||||
</span>
|
||||
<% else %>
|
||||
<% elsif can?(:update, @encrypted_config) %>
|
||||
<%= button_to settings_esign_path, method: :put, params: { name: item['name'] }, class: 'btn btn-outline btn-neutral btn-xs whitespace-nowrap', title: 'Delete', data: { turbo_confirm: 'Are you sure?' } do %>
|
||||
Make Default
|
||||
<% end %>
|
||||
<% end %>
|
||||
</td>
|
||||
<td>
|
||||
<% if item['name'] != EsignSettingsController::DEFAULT_CERT_NAME && item['status'] != 'default' %>
|
||||
<% if item['name'] != EsignSettingsController::DEFAULT_CERT_NAME && item['status'] != 'default' && can?(:destroy, @encrypted_config) %>
|
||||
<%= button_to settings_esign_path, params: { name: item['name'] }, method: :delete, class: 'btn btn-outline btn-error btn-xs', title: 'Delete', data: { turbo_confirm: 'Are you sure?' } do %>
|
||||
Remove
|
||||
<% end %>
|
||||
|
||||
@@ -10,33 +10,49 @@
|
||||
<%= link_to 'Account', settings_account_path, class: 'text-base hover:bg-base-300' %>
|
||||
</li>
|
||||
<% unless Docuseal.multitenant? %>
|
||||
<% if can?(:read, EncryptedConfig.new(key: EncryptedConfig::EMAIL_SMTP_KEY, account: current_account)) %>
|
||||
<li>
|
||||
<%= link_to 'Email', settings_email_index_path, class: 'text-base hover:bg-base-300' %>
|
||||
</li>
|
||||
<% end %>
|
||||
<% if can?(:read, EncryptedConfig.new(key: EncryptedConfig::FILES_STORAGE_KEY, account: current_account)) %>
|
||||
<li>
|
||||
<%= link_to 'Storage', settings_storage_index_path, class: 'text-base hover:bg-base-300' %>
|
||||
</li>
|
||||
<% end %>
|
||||
<% if can?(:read, EncryptedConfig.new(key: 'submitter_invitation_sms', account: current_account)) %>
|
||||
<li>
|
||||
<%= link_to 'SMS', settings_sms_path, class: 'text-base hover:bg-base-300' %>
|
||||
</li>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% if can?(:read, EncryptedConfig.new(key: EncryptedConfig::ESIGN_CERTS_KEY, account: current_account)) %>
|
||||
<li>
|
||||
<%= link_to 'Email', settings_email_index_path, class: 'text-base hover:bg-base-300' %>
|
||||
</li>
|
||||
<li>
|
||||
<%= link_to 'Storage', settings_storage_index_path, class: 'text-base hover:bg-base-300' %>
|
||||
</li>
|
||||
<li>
|
||||
<%= link_to 'SMS', settings_sms_path, class: 'text-base hover:bg-base-300' %>
|
||||
<%= link_to 'E-Signature', settings_esign_path, class: 'text-base hover:bg-base-300' %>
|
||||
</li>
|
||||
<% end %>
|
||||
<% if can?(:read, User) %>
|
||||
<li>
|
||||
<%= link_to 'Team', settings_users_path, class: 'text-base hover:bg-base-300' %>
|
||||
</li>
|
||||
<% end %>
|
||||
<li>
|
||||
<%= link_to 'E-Signature', settings_esign_path, class: 'text-base hover:bg-base-300' %>
|
||||
</li>
|
||||
<li>
|
||||
<%= link_to 'Team', settings_users_path, class: 'text-base hover:bg-base-300' %>
|
||||
</li>
|
||||
<% if Docuseal.demo? || !Docuseal.multitenant? %>
|
||||
<% if can?(:read, AccessToken) %>
|
||||
<li>
|
||||
<%= link_to 'API', settings_api_index_path, class: 'text-base hover:bg-base-300' %>
|
||||
</li>
|
||||
<% end %>
|
||||
<% if can?(:read, EncryptedConfig.new(key: EncryptedConfig::WEBHOOK_URL_KEY, account: current_account)) %>
|
||||
<li>
|
||||
<%= link_to 'Webhooks', settings_webhooks_path, class: 'text-base hover:bg-base-300' %>
|
||||
</li>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% if can?(:read, AccountConfig) %>
|
||||
<li>
|
||||
<%= link_to 'API', settings_api_index_path, class: 'text-base hover:bg-base-300' %>
|
||||
</li>
|
||||
<li>
|
||||
<%= link_to 'Webhooks', settings_webhooks_path, class: 'text-base hover:bg-base-300' %>
|
||||
<%= link_to 'Personalization', settings_personalization_path, class: 'text-base hover:bg-base-300' %>
|
||||
</li>
|
||||
<% end %>
|
||||
<li>
|
||||
<%= link_to 'Personalization', settings_personalization_path, class: 'text-base hover:bg-base-300' %>
|
||||
</li>
|
||||
<% unless Docuseal.demo? %>
|
||||
<li>
|
||||
<%= link_to Docuseal.multitenant? ? console_redirect_index_path : Docuseal::CONSOLE_URL, class: 'text-base hover:bg-base-300', data: { prefetch: false } do %>
|
||||
|
||||
@@ -108,15 +108,15 @@
|
||||
<%= submitter&.completed_at? ? l(submitter.completed_at.in_time_zone(current_account.timezone), format: :long, locale: current_account.locale) : 'Not completed yet' %>
|
||||
</span>
|
||||
</div>
|
||||
<% if submitter && submitter.email && !submitter.completed_at %>
|
||||
<% if submitter && submitter.email && !submitter.completed_at && can?(:update, submitter) %>
|
||||
<div class="mt-2 mb-1">
|
||||
<%= button_to button_title(title: submitter.sent_at? ? 'Re-send Email' : 'Send Email', disabled_with: 'Sending'), submitter_send_email_index_path(submitter_slug: submitter.slug), class: 'btn btn-sm btn-primary w-full' %>
|
||||
</div>
|
||||
<% end %>
|
||||
<% if submitter && submitter.phone && !submitter.completed_at %>
|
||||
<% if submitter && submitter.phone && !submitter.completed_at && can?(:update, submitter) %>
|
||||
<%= render 'send_sms_button', submitter: %>
|
||||
<% end %>
|
||||
<% if submitter && !submitter.completed_at? %>
|
||||
<% if submitter && !submitter.completed_at? && can?(:create, submitter) %>
|
||||
<div class="mt-2 mb-1">
|
||||
<a class="btn btn-sm btn-primary w-full" target="_blank" href="<%= submit_form_path(slug: submitter.slug) %>">
|
||||
Submit Form
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
<%= render 'shared/clipboard_copy', text: submit_form_url(slug: submitter.slug), class: 'btn btn-sm btn-neutral text-white md:w-36 flex', icon_class: 'w-6 h-6 text-white', copy_title: 'Copy Link', copy_title_md: 'Copy', copied_title_md: 'Copied' %>
|
||||
<% end %>
|
||||
<span class="btn btn-outline btn-sm w-20 md:w-24">View</span>
|
||||
<% unless submission.deleted_at? %>
|
||||
<% if !submission.deleted_at? && can?(:destroy, submission) %>
|
||||
<%= button_to button_title(title: nil, disabled_with: 'Remov', icon: svg_icon('trash', class: 'w-6 h-6')), submission_path(submission), class: 'btn btn-outline btn-sm', title: 'Delete', method: :delete, data: { turbo_confirm: 'Are you sure?' }, onclick: 'event.stopPropagation()' %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
@@ -15,20 +15,22 @@
|
||||
</div>
|
||||
</a>
|
||||
<div class="absolute top-0 bottom-0 w-0 pt-7 space-y-1.5 hidden md:group-hover:block" style="right: 40px">
|
||||
<% if template.deleted_at? %>
|
||||
<% if template.deleted_at? && can?(:update, template) %>
|
||||
<%= button_to template_restore_index_path(template), class: 'btn btn-xs hover:btn-outline bg-base-200 btn-circle' do %>
|
||||
<%= svg_icon('rotate', class: 'w-4 h-4 enabled') %>
|
||||
<%= svg_icon('loader', class: 'w-4 h-4 animate-spin disabled') %>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<% elsif can?(:update, template) %>
|
||||
<a href="<%= edit_template_path(template) %>" class="btn btn-xs hover:btn-outline bg-base-200 btn-circle">
|
||||
<%= svg_icon('pencil', class: 'w-4 h-4') %>
|
||||
</a>
|
||||
<% end %>
|
||||
<a href="<%= new_template_path(base_template_id: template.id) %>" data-turbo-frame="modal" class="btn btn-xs hover:btn-outline bg-base-200 btn-circle">
|
||||
<%= svg_icon('copy', class: 'w-4 h-4') %>
|
||||
</a>
|
||||
<% unless template.deleted_at? %>
|
||||
<% if can?(:create, template) %>
|
||||
<a href="<%= new_template_path(base_template_id: template.id) %>" data-turbo-frame="modal" class="btn btn-xs hover:btn-outline bg-base-200 btn-circle">
|
||||
<%= svg_icon('copy', class: 'w-4 h-4') %>
|
||||
</a>
|
||||
<% end %>
|
||||
<% if !template.deleted_at? && can?(:destroy, template) %>
|
||||
<%= button_to template_path(template), data: { turbo_confirm: 'Are you sure?' }, method: :delete, class: 'btn btn-xs hover:btn-outline bg-base-200 btn-circle', aria_label: 'Restore' do %>
|
||||
<%= svg_icon('trash', class: 'w-4 h-4 enabled') %>
|
||||
<%= svg_icon('loader', class: 'w-4 h-4 animate-spin disabled') %>
|
||||
|
||||
@@ -6,14 +6,16 @@
|
||||
<% end %>
|
||||
</h1>
|
||||
<div class="flex md:justify-between space-x-2 flex-none">
|
||||
<% unless template.deleted_at? %>
|
||||
<% if !template.deleted_at? && can?(:destroy, template) %>
|
||||
<%= button_to button_title(title: 'Remove', disabled_with: 'Removing', icon: svg_icon('trash', class: 'w-6 h-6')), template_path(template), class: 'btn btn-outline btn-sm', method: :delete, data: { turbo_confirm: 'Are you sure?' } %>
|
||||
<% end %>
|
||||
<%= link_to new_template_path(base_template_id: template.id), class: 'btn btn-outline btn-sm', data: { turbo_frame: :modal } do %>
|
||||
<%= svg_icon('copy', class: 'w-6 h-6') %>
|
||||
<span>Clone</span>
|
||||
<% if can?(:create, template) %>
|
||||
<%= link_to new_template_path(base_template_id: template.id), class: 'btn btn-outline btn-sm', data: { turbo_frame: :modal } do %>
|
||||
<%= svg_icon('copy', class: 'w-6 h-6') %>
|
||||
<span>Clone</span>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% unless template.deleted_at? %>
|
||||
<% if !template.deleted_at? && can?(:update, template) %>
|
||||
<%= link_to edit_template_path(template), class: 'btn btn-outline btn-sm' do %>
|
||||
<span class="flex items-center justify-center space-x-2">
|
||||
<%= svg_icon('pencil', class: 'w-6 h-6') %>
|
||||
@@ -21,7 +23,7 @@
|
||||
</span>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% if template.deleted_at? %>
|
||||
<% if template.deleted_at? && can?(:create, template) %>
|
||||
<%= button_to button_title(title: 'Restore', disabled_with: 'Restoring', icon: svg_icon('rotate', class: 'w-6 h-6')), template_restore_index_path(template), class: 'btn btn-outline btn-sm' %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<%= render 'shared/clipboard_copy', text: start_form_url(slug: @template.slug), class: 'base-button', icon_class: 'w-6 h-6 text-white', copy_title: 'Copy Share Link', copied_title: 'Copied to Clipboard', copy_title_md: 'Copy', copied_title_md: 'Copied' %>
|
||||
</span>
|
||||
<% end %>
|
||||
<% if (!@pagy.count.zero? || params[:q].present?) && !@template.deleted_at? %>
|
||||
<% if (!@pagy.count.zero? || params[:q].present?) && !@template.deleted_at? && can?(:create, Submission) %>
|
||||
<%= link_to new_template_submission_path(@template), class: 'order-1 btn btn-primary text-base', data: { turbo_frame: 'modal' } do %>
|
||||
<%= svg_icon('plus', class: 'w-6 h-6 stroke-2') %>
|
||||
<span>Add <span class="hidden md:inline">Recipients</span></span>
|
||||
@@ -31,7 +31,7 @@
|
||||
<% view_archived_html = capture do %>
|
||||
<% if @template.submissions.where.not(deleted_at: nil).exists? && !@template.deleted_at? %>
|
||||
<div>
|
||||
<a href="<%= template_archived_path(@template) %>" class="link text-sm">View Archived</a>
|
||||
<a href="<%= template_archived_index_path(@template) %>" class="link text-sm">View Archived</a>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
@@ -50,9 +50,11 @@
|
||||
<% if @template.deleted_at.blank? && params[:q].blank? %>
|
||||
<p class="text-gray-600">Send an invitation to fill and complete the form</p>
|
||||
<div class="space-x-2">
|
||||
<%= link_to new_template_submission_path(@template), class: 'base-button mt-6', data: { turbo_frame: 'modal' } do %>
|
||||
<%= svg_icon('plus', class: 'w-6 h-6 stroke-2') %>
|
||||
<span class="mr-1">Add Recipients</span>
|
||||
<% if can?(:create, Submission) %>
|
||||
<%= link_to new_template_submission_path(@template), class: 'base-button mt-6', data: { turbo_frame: 'modal' } do %>
|
||||
<%= svg_icon('plus', class: 'w-6 h-6 stroke-2') %>
|
||||
<span class="mr-1">Add Recipients</span>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<%= link_to start_form_url(slug: @template.slug), class: 'white-button mt-6', target: '_blank', rel: 'noopener' do %>
|
||||
<%= svg_icon('writing', class: 'w-6 h-6') %>
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
<%= f.label :password, class: 'label' %>
|
||||
<%= f.password_field :password, required: user.new_record?, class: 'base-input' %>
|
||||
</div>
|
||||
<% if f.object != current_user %>
|
||||
<%= render 'role_select', f: %>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="form-control pt-2">
|
||||
<%= f.button button_title, class: 'base-button' %>
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
<div class="form-control">
|
||||
<%= f.label :role, class: 'label' %>
|
||||
<%= f.select :role, nil, {}, class: 'base-select' do %>
|
||||
<option value="admin">Admin</option>
|
||||
<option value="editor" disabled>Editor</option>
|
||||
<option value="viewer" disabled>Viewer</option>
|
||||
<% end %>
|
||||
<a class="text-sm mt-3 px-4 py-2 bg-base-300 rounded-full block" target="_blank" href="<%= "#{Docuseal::CONSOLE_URL}/#{Docuseal.multitenant? ? 'plans' : 'on_premise'}" %>">
|
||||
<%= svg_icon('info_circle', class: 'w-4 h-4 inline align-text-bottom') %>
|
||||
Unlock more user roles with DocuSeal Enterprise.
|
||||
<span class="link font-medium">Learn More</a>
|
||||
</a>
|
||||
</div>
|
||||
@@ -3,9 +3,11 @@
|
||||
<div class="flex-grow">
|
||||
<div class="flex justify-between mb-4">
|
||||
<h1 class="text-4xl font-bold">Team</h1>
|
||||
<%= link_to new_user_path, class: 'btn btn-primary btn-md gap-2', data: { turbo_frame: 'modal' } do %>
|
||||
<%= svg_icon('plus', class: 'w-6 h-6') %>
|
||||
<span>New User</span>
|
||||
<% if can?(:create, User.new(account: current_account)) %>
|
||||
<%= link_to new_user_path, class: 'btn btn-primary btn-md gap-2', data: { turbo_frame: 'modal' } do %>
|
||||
<%= svg_icon('plus', class: 'w-6 h-6') %>
|
||||
<span>New User</span>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="overflow-x-auto">
|
||||
@@ -46,11 +48,15 @@
|
||||
<%= user.last_sign_in_at ? l(user.last_sign_in_at.in_time_zone(current_account.timezone), format: :short, locale: current_account.locale) : '-' %>
|
||||
</td>
|
||||
<td class="flex items-center space-x-2 justify-end">
|
||||
<%= link_to edit_user_path(user), class: 'btn btn-outline btn-xs', title: 'Edit', data: { turbo_frame: 'modal' } do %>
|
||||
Edit
|
||||
<% if can?(:update, user) %>
|
||||
<%= link_to edit_user_path(user), class: 'btn btn-outline btn-xs', title: 'Edit', data: { turbo_frame: 'modal' } do %>
|
||||
Edit
|
||||
<% end %>
|
||||
<% end %>
|
||||
<%= button_to user_path(user), method: :delete, class: 'btn btn-outline btn-error btn-xs', title: 'Delete', data: { turbo_confirm: 'Are you sure?' } do %>
|
||||
Remove
|
||||
<% if can?(:destroy, user) && user != current_user %>
|
||||
<%= button_to user_path(user), method: :delete, class: 'btn btn-outline btn-error btn-xs', title: 'Delete', data: { turbo_confirm: 'Are you sure?' } do %>
|
||||
Remove
|
||||
<% end %>
|
||||
<% end %>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ Rails.application.routes.draw do
|
||||
resources :templates_archived, only: %i[index], path: 'archived'
|
||||
resources :templates, only: %i[new create edit show destroy] do
|
||||
resources :restore, only: %i[create], controller: 'templates_restore'
|
||||
resource :archived, only: %i[show], controller: 'templates_archived_submissions'
|
||||
resources :archived, only: %i[index], controller: 'templates_archived_submissions'
|
||||
resources :submissions, only: %i[new create]
|
||||
resources :submissions_export, only: %i[index new]
|
||||
end
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Ability
|
||||
include CanCan::Ability
|
||||
|
||||
def initialize(user)
|
||||
can :manage, Template, account_id: user.account_id
|
||||
can :manage, Submission, template: { account_id: user.account_id }
|
||||
can :manage, Submitter, template: { account_id: user.account_id }
|
||||
can :manage, User, account_id: user.account_id
|
||||
can :manage, EncryptedConfig, account_id: user.account_id
|
||||
can :manage, AccountConfig, account_id: user.account_id
|
||||
can :manage, Account, id: user.account_id
|
||||
can :manage, AccessToken, user_id: user.id
|
||||
end
|
||||
end
|
||||
@@ -5,7 +5,7 @@ module Templates
|
||||
module_function
|
||||
|
||||
def call(template:, original_template:)
|
||||
original_template.documents.each do |document|
|
||||
original_template.documents.preload(:preview_images_attachments).each do |document|
|
||||
new_document = ActiveStorage::Attachment.create!(
|
||||
uuid: document.uuid,
|
||||
blob_id: document.blob_id,
|
||||
|
||||
@@ -23,6 +23,7 @@ RSpec.describe 'Dashboard Page' do
|
||||
context 'when there are templates' do
|
||||
let!(:authors) { create_list(:user, 5, account:) }
|
||||
let!(:templates) { authors.map { |author| create(:template, account:, author:) } }
|
||||
let!(:other_template) { create(:template) }
|
||||
|
||||
before do
|
||||
visit root_path
|
||||
@@ -35,6 +36,7 @@ RSpec.describe 'Dashboard Page' do
|
||||
end
|
||||
|
||||
expect(page).to have_content('Templates')
|
||||
expect(page).not_to have_content(other_template.name)
|
||||
expect(page).to have_link('Create', href: new_template_path)
|
||||
end
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ RSpec.describe 'Team Settings' do
|
||||
|
||||
context 'when multiple users' do
|
||||
let!(:users) { create_list(:user, 2, account:) }
|
||||
let!(:other_user) { create(:user) }
|
||||
|
||||
before do
|
||||
visit settings_users_path
|
||||
@@ -22,6 +23,7 @@ RSpec.describe 'Team Settings' do
|
||||
users.each do |user|
|
||||
expect(page).to have_content(user.full_name)
|
||||
expect(page).to have_content(user.email)
|
||||
expect(page).not_to have_content(other_user.email)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -84,13 +86,7 @@ RSpec.describe 'Team Settings' do
|
||||
end
|
||||
|
||||
it 'does not allow to remove the current user' do
|
||||
expect do
|
||||
accept_confirm('Are you sure?') do
|
||||
first(:button, 'Delete').click
|
||||
end
|
||||
end.not_to(change { User.admins.count })
|
||||
|
||||
expect(page).to have_content('Unable to remove user')
|
||||
expect(page).not_to have_content('User has been removed')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user