add personalization settings

pull/105/head
Alex Turchyn 2 years ago
parent 545b18086a
commit 187b354605

@ -24,6 +24,7 @@ gem 'pg', require: false
gem 'premailer-rails' gem 'premailer-rails'
gem 'puma' gem 'puma'
gem 'rails' gem 'rails'
gem 'rails_autolink'
gem 'rails-i18n' gem 'rails-i18n'
gem 'rollbar', require: ENV.key?('ROLLBAR_ACCESS_TOKEN') gem 'rollbar', require: ENV.key?('ROLLBAR_ACCESS_TOKEN')
gem 'ruby-vips' gem 'ruby-vips'

@ -379,6 +379,10 @@ GEM
rails-i18n (7.0.7) rails-i18n (7.0.7)
i18n (>= 0.7, < 2) i18n (>= 0.7, < 2)
railties (>= 6.0.0, < 8) railties (>= 6.0.0, < 8)
rails_autolink (1.1.8)
actionview (> 3.1)
activesupport (> 3.1)
railties (> 3.1)
railties (7.0.5) railties (7.0.5)
actionpack (= 7.0.5) actionpack (= 7.0.5)
activesupport (= 7.0.5) activesupport (= 7.0.5)
@ -554,6 +558,7 @@ DEPENDENCIES
puma puma
rails rails
rails-i18n rails-i18n
rails_autolink
rollbar rollbar
rspec-rails rspec-rails
rubocop rubocop

@ -0,0 +1,20 @@
# frozen_string_literal: true
class PersonalizationSettingsController < ApplicationController
def show; end
def create
account_config =
current_account.account_configs.find_or_initialize_by(key: encrypted_config_params[:key])
account_config.update!(encrypted_config_params)
redirect_back(fallback_location: settings_personalization_path, notice: 'Settings have been saved.')
end
private
def encrypted_config_params
params.require(:account_config).permit!
end
end

@ -36,7 +36,9 @@ class StartFormController < ApplicationController
end end
def completed def completed
@submitter = Submitter.where(submission: @template.submissions).find_by!(email: params[:email]) @submitter = Submitter.where(submission: @template.submissions)
.where.not(completed_at: nil)
.find_by!(email: params[:email])
end end
private private

@ -6,6 +6,10 @@ class ApplicationMailer < ActionMailer::Base
register_interceptor ActionMailerConfigsInterceptor register_interceptor ActionMailerConfigsInterceptor
before_action do
ActiveStorage::Current.url_options = Docuseal.default_url_options
end
def default_url_options def default_url_options
Docuseal.default_url_options Docuseal.default_url_options
end end

@ -4,15 +4,26 @@ class SubmitterMailer < ApplicationMailer
DEFAULT_MESSAGE = %(You have been invited to submit the "%<name>s" form:) DEFAULT_MESSAGE = %(You have been invited to submit the "%<name>s" form:)
def invitation_email(submitter, message: '') def invitation_email(submitter, message: '')
@current_account = submitter.submission.template.account
@submitter = submitter @submitter = submitter
@message = message.presence || format(DEFAULT_MESSAGE, name: submitter.submission.template.name) @message = message.presence || format(DEFAULT_MESSAGE, name: submitter.submission.template.name)
@email_config = @current_account.account_configs.find_by(key: AccountConfig::SUBMITTER_INVITATION_EMAIL_KEY)
subject =
if @email_config
ReplaceEmailVariables.call(@email_config.value['subject'], submitter:)
else
'You have been invited to submit a form'
end
mail(to: @submitter.email, mail(to: @submitter.email,
subject: 'You have been invited to submit a form', subject:,
reply_to: submitter.submission.created_by_user&.friendly_name) reply_to: submitter.submission.created_by_user&.friendly_name)
end end
def completed_email(submitter, user) def completed_email(submitter, user)
@current_account = submitter.submission.template.account
@submitter = submitter @submitter = submitter
@user = user @user = user
@ -21,6 +32,7 @@ class SubmitterMailer < ApplicationMailer
end end
def documents_copy_email(submitter) def documents_copy_email(submitter)
@current_account = submitter.submission.template.account
@submitter = submitter @submitter = submitter
Submissions::EnsureResultGenerated.call(@submitter) Submissions::EnsureResultGenerated.call(@submitter)

@ -2,6 +2,7 @@
class UserMailer < ApplicationMailer class UserMailer < ApplicationMailer
def invitation_email(user) def invitation_email(user)
@current_account = user.account
@user = user @user = user
@token = @user.send(:set_reset_password_token) @token = @user.send(:set_reset_password_token)

@ -14,6 +14,7 @@
class Account < ApplicationRecord class Account < ApplicationRecord
has_many :users, dependent: :destroy has_many :users, dependent: :destroy
has_many :encrypted_configs, dependent: :destroy has_many :encrypted_configs, dependent: :destroy
has_many :account_configs, dependent: :destroy
has_many :templates, dependent: :destroy has_many :templates, dependent: :destroy
has_many :submissions, through: :templates has_many :submissions, through: :templates
has_many :submitters, through: :submissions has_many :submitters, through: :submissions

@ -0,0 +1,41 @@
# frozen_string_literal: true
# == Schema Information
#
# Table name: account_configs
#
# id :bigint not null, primary key
# key :string not null
# value :text not null
# created_at :datetime not null
# updated_at :datetime not null
# account_id :bigint not null
#
# Indexes
#
# index_account_configs_on_account_id (account_id)
# index_account_configs_on_account_id_and_key (account_id,key) UNIQUE
#
# Foreign Keys
#
# fk_rails_... (account_id => accounts.id)
#
class AccountConfig < ApplicationRecord
SUBMITTER_INVITATION_EMAIL_KEY = 'submitter_invitation_email'
DEFAULT_VALUES = {
SUBMITTER_INVITATION_EMAIL_KEY => {
'subject' => 'You have been invited to submit a form',
'body' => "Hi there,\n\n" \
"You have been invited to submit the \"{{template.name}}\" form:\n\n" \
"{{submitter.link}}\n\n" \
"Please contact us by replying to this email if you didn't request this.\n\n" \
"Thanks,\n" \
'{{account.name}}'
}
}.freeze
belongs_to :account
serialize :value, JSON
end

@ -8,11 +8,6 @@
</head> </head>
<body> <body>
<%= yield %> <%= yield %>
<p> <%= render partial: 'shared/mailer_attribution' %>
---
</p>
<p>
Sent using <a href="<%= Docuseal::PRODUCT_URL %>"><%= Docuseal::PRODUCT_NAME %></a> free document signing.
</p>
</body> </body>
</html> </html>

@ -0,0 +1 @@
<%= render 'logo_placeholder' %>

@ -0,0 +1,11 @@
<div class="alert my-4">
<%= svg_icon('info_circle', class: 'w-6 h-6') %>
<div>
<p class="font-bold">Unlock with DocuSeal Enterprise</p>
<p>
Display your company name and logo when signing documents.
<br>
<a class="link font-medium" target="_blank" href="<%= Docuseal::PRODUCT_URL + '/pricing' %>">Learn More</a>
</p>
</div>
</div>

@ -0,0 +1,27 @@
<div class="flex flex-wrap space-y-4 md:flex-nowrap md:space-y-0">
<%= render 'shared/settings_nav' %>
<div class="flex-grow max-w-xl mx-auto">
<p class="text-4xl font-bold mb-4">Signature Request Email</p>
<%= form_for AccountConfigs.find_or_initialize_for_key(current_account, AccountConfig::SUBMITTER_INVITATION_EMAIL_KEY), url: settings_personalization_path, method: :post, html: { autocomplete: 'off', class: 'space-y-4' } do |f| %>
<%= f.hidden_field :key %>
<%= f.fields_for :value, OpenStruct.new(f.object.value) do |ff| %>
<div class="form-control">
<%= ff.label :subject, class: 'label' %>
<%= ff.text_field :subject, required: true, class: 'base-input' %>
</div>
<div class="form-control">
<%= ff.label :body, class: 'label' %>
<autoresize-textarea>
<%= ff.text_area :body, required: true, class: 'base-input w-full py-2' %>
</autoresize-textarea>
</div>
<% end %>
<div class="form-control pt-2">
<%= f.button button_title(title: 'Save', disabled_with: 'Saving'), class: 'base-button' %>
</div>
<% end %>
<p class="text-4xl font-bold mb-4 mt-8">Company Logo</p>
<%= render 'logo_form' %>
</div>
<div class="w-0 md:w-52"></div>
</div>

@ -2,12 +2,7 @@
<div class="space-y-6 mx-auto"> <div class="space-y-6 mx-auto">
<div class="space-y-6"> <div class="space-y-6">
<div class="flex items-center justify-center"> <div class="flex items-center justify-center">
<a href="/" class="flex items-center"> <%= render 'start_form/docuseal_logo' %>
<div class="mr-3">
<%= render 'shared/logo', width: '50px', height: '50px' %>
</div>
<h1 class="text-5xl font-bold text-center">DocuSeal</h1>
</a>
</div> </div>
<div class="text-center text-4xl font-bold"> <div class="text-center text-4xl font-bold">
Email has been sent Email has been sent

@ -0,0 +1,6 @@
<p>
---
</p>
<p>
Sent using <a href="<%= Docuseal::PRODUCT_URL %>"><%= Docuseal::PRODUCT_NAME %></a> free document signing.
</p>

@ -0,0 +1 @@
<%= render 'shared/email_attribution' %>

@ -31,6 +31,9 @@
<%= link_to 'Webhooks', settings_webhooks_path, class: 'text-base hover:bg-base-300' %> <%= link_to 'Webhooks', settings_webhooks_path, class: 'text-base hover:bg-base-300' %>
</li> </li>
<% end %> <% end %>
<li>
<%= link_to 'Personalization', settings_personalization_path, class: 'text-base hover:bg-base-300' %>
</li>
<% unless Docuseal.demo? %> <% unless Docuseal.demo? %>
<li> <li>
<%= link_to Docuseal.multitenant? ? console_redirect_index_path : Docuseal::CONSOLE_URL, class: 'text-base hover:bg-base-300', data: { prefetch: false } do %> <%= link_to Docuseal.multitenant? ? console_redirect_index_path : Docuseal::CONSOLE_URL, class: 'text-base hover:bg-base-300', data: { prefetch: false } do %>

@ -0,0 +1 @@
<%= render 'docuseal_logo' %>

@ -0,0 +1,6 @@
<a href="/" class="flex justify-center items-center">
<span class="mr-3">
<%= render 'shared/logo', width: '50px', height: '50px' %>
</span>
<h1 class="text-5xl font-bold text-center">DocuSeal</h1>
</a>

@ -2,12 +2,7 @@
<div class="space-y-6 mx-auto"> <div class="space-y-6 mx-auto">
<div class="space-y-6"> <div class="space-y-6">
<div class="flex items-center justify-center"> <div class="flex items-center justify-center">
<a href="/" class="flex items-center"> <%= render 'banner' %>
<div class="mr-3">
<%= render 'shared/logo', width: '50px', height: '50px' %>
</div>
<h1 class="text-5xl font-bold text-center">DocuSeal</h1>
</a>
</div> </div>
<div class="flex items-center bg-base-200 rounded-xl p-4 mb-4"> <div class="flex items-center bg-base-200 rounded-xl p-4 mb-4">
<div class="flex items-center"> <div class="flex items-center">

@ -2,12 +2,7 @@
<div class="space-y-6 mx-auto"> <div class="space-y-6 mx-auto">
<div class="space-y-6"> <div class="space-y-6">
<div class="text-center w-full space-y-6"> <div class="text-center w-full space-y-6">
<a href="/" class="flex justify-center items-center"> <%= render 'banner' %>
<span class="mr-3">
<%= render 'shared/logo', width: '50px', height: '50px' %>
</span>
<h1 class="text-5xl font-bold text-center">DocuSeal</h1>
</a>
<p class="text-xl font-semibold text-center">You have been invited to submit a form</p> <p class="text-xl font-semibold text-center">You have been invited to submit a form</p>
</div> </div>
<div class="flex items-center bg-base-200 rounded-xl p-4 mb-4"> <div class="flex items-center bg-base-200 rounded-xl p-4 mb-4">

@ -40,7 +40,7 @@
<div class="form-control"> <div class="form-control">
<% is_smtp_configured = Accounts.can_send_emails?(current_account) %> <% is_smtp_configured = Accounts.can_send_emails?(current_account) %>
<%= f.label :send_email, class: 'flex items-center cursor-pointer' do %> <%= f.label :send_email, class: 'flex items-center cursor-pointer' do %>
<%= f.check_box :send_email, class: 'base-checkbox', disabled: !is_smtp_configured, onchange: "message_field.classList.toggle('hidden', !event.currentTarget.checked)" %> <%= f.check_box :send_email, class: 'base-checkbox', disabled: !is_smtp_configured, onchange: "window.message_field && message_field.classList.toggle('hidden', !event.currentTarget.checked)" %>
<span class="label">Send Email</span> <span class="label">Send Email</span>
<% end %> <% end %>
<% unless is_smtp_configured %> <% unless is_smtp_configured %>
@ -57,6 +57,7 @@
</div> </div>
<% end %> <% end %>
</div> </div>
<% unless AccountConfig.exists?(key: AccountConfig::SUBMITTER_INVITATION_EMAIL_KEY) %>
<div id="message_field" class="card card-compact bg-base-200 hidden"> <div id="message_field" class="card card-compact bg-base-200 hidden">
<div class="card-body"> <div class="card-body">
<div class="form-control space-y-2"> <div class="form-control space-y-2">
@ -72,6 +73,7 @@
</div> </div>
</div> </div>
</div> </div>
<% end %>
<div class="form-control"> <div class="form-control">
<%= f.button button_title(title: 'Add Recipients'), class: 'base-button' %> <%= f.button button_title(title: 'Add Recipients'), class: 'base-button' %>
</div> </div>

@ -0,0 +1 @@
<%= render 'docuseal_logo' %>

@ -0,0 +1,4 @@
<a href="<%= root_path %>" class="mx-auto text-2xl md:text-3xl font-bold items-center flex space-x-3">
<%= render 'shared/logo', class: 'w-9 h-9 md:w-12 md:h-12' %>
<span><%= Docuseal::PRODUCT_NAME %></span>
</a>

@ -2,12 +2,7 @@
<div class="space-y-6 mx-auto"> <div class="space-y-6 mx-auto">
<div class="space-y-6"> <div class="space-y-6">
<div class="flex items-center justify-center"> <div class="flex items-center justify-center">
<a href="/" class="flex items-center"> <%= render 'start_form/banner' %>
<div class="mr-3">
<%= render 'shared/logo', width: '50px', height: '50px' %>
</div>
<h1 class="text-5xl font-bold text-center">DocuSeal</h1>
</a>
</div> </div>
<div class="flex items-center bg-base-200 rounded-xl p-4 mb-4"> <div class="flex items-center bg-base-200 rounded-xl p-4 mb-4">
<div class="flex items-center"> <div class="flex items-center">

@ -5,10 +5,7 @@
<div id="scrollbox"> <div id="scrollbox">
<div class="mx-auto block pb-72" style="max-width: 1000px"> <div class="mx-auto block pb-72" style="max-width: 1000px">
<div class="mt-4 flex"> <div class="mt-4 flex">
<a href="<%= root_path %>" class="mx-auto text-2xl md:text-3xl font-bold items-center flex space-x-3"> <%= render 'banner' %>
<%= render 'shared/logo', class: 'w-9 h-9 md:w-12 md:h-12' %>
<span>DocuSeal</span>
</a>
</div> </div>
<% (@submitter.submission.template_schema || @submitter.submission.template.schema).each do |item| %> <% (@submitter.submission.template_schema || @submitter.submission.template.schema).each do |item| %>
<% document = @submitter.submission.template.documents.find { |a| a.uuid == item['attachment_uuid'] } %> <% document = @submitter.submission.template.documents.find { |a| a.uuid == item['attachment_uuid'] } %>

@ -9,5 +9,5 @@
</ul> </ul>
<% end %> <% end %>
<p> <p>
Thanks,<br><%= @submitter.submission.template.account.name %> Thanks,<br><%= @current_account.name %>
</p> </p>

@ -1,7 +1,11 @@
<p>Hi there,</p> <% if @email_config %>
<%= simple_format(@message) %> <%= auto_link(simple_format(h(ReplaceEmailVariables.call(@email_config.value['body'], submitter: @submitter)))) %>
<p><%= link_to 'Submit Form', submit_form_url(slug: @submitter.slug) %></p> <% else %>
<p>Please contact us by replying to this email if you didn't request this.</p> <p>Hi there,</p>
<p> <%= simple_format(@message) %>
Thanks,<br><%= @submitter.submission.template.account.name %> <p><%= link_to 'Submit Form', submit_form_url(slug: @submitter.slug) %></p>
</p> <p>Please contact us by replying to this email if you didn't request this.</p>
<p>
Thanks,<br><%= @current_account.name %>
</p>
<% end %>

@ -3,5 +3,5 @@
<p><%= link_to 'Sign up', invitation_url(reset_password_token: @token) %></p> <p><%= link_to 'Sign up', invitation_url(reset_password_token: @token) %></p>
<p>Please contact us by replying to this email if you didn't request this.</p> <p>Please contact us by replying to this email if you didn't request this.</p>
<p> <p>
Thanks,<br><%= @user.account.name %> Thanks,<br><%= @current_account.name %>
</p> </p>

@ -70,6 +70,7 @@ Rails.application.routes.draw do
end end
resources :esign, only: %i[index create], controller: 'esign_settings' resources :esign, only: %i[index create], controller: 'esign_settings'
resources :users, only: %i[index] resources :users, only: %i[index]
resource :personalization, only: %i[show create], controller: 'personalization_settings'
if !Docuseal.multitenant? || Docuseal.demo? if !Docuseal.multitenant? || Docuseal.demo?
resources :api, only: %i[index], controller: 'api_settings' resources :api, only: %i[index], controller: 'api_settings'
resource :webhooks, only: %i[show create update], controller: 'webhook_settings' resource :webhooks, only: %i[show create update], controller: 'webhook_settings'

@ -0,0 +1,15 @@
# frozen_string_literal: true
class CreateAccountConfigs < ActiveRecord::Migration[7.0]
def change
create_table :account_configs do |t|
t.references :account, null: false, foreign_key: true, index: true
t.string :key, null: false
t.text :value, null: false
t.index %i[account_id key], unique: true
t.timestamps
end
end
end

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.0].define(version: 2023_08_06_140534) do ActiveRecord::Schema[7.0].define(version: 2023_08_15_190540) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
@ -24,6 +24,16 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_06_140534) do
t.index ["user_id"], name: "index_access_tokens_on_user_id" t.index ["user_id"], name: "index_access_tokens_on_user_id"
end end
create_table "account_configs", force: :cascade do |t|
t.bigint "account_id", null: false
t.string "key", null: false
t.text "value", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["account_id", "key"], name: "index_account_configs_on_account_id_and_key", unique: true
t.index ["account_id"], name: "index_account_configs_on_account_id"
end
create_table "accounts", force: :cascade do |t| create_table "accounts", force: :cascade do |t|
t.string "name", null: false t.string "name", null: false
t.string "timezone", null: false t.string "timezone", null: false
@ -159,6 +169,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_06_140534) do
end end
add_foreign_key "access_tokens", "users" add_foreign_key "access_tokens", "users"
add_foreign_key "account_configs", "accounts"
add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id" add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id" add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
add_foreign_key "document_generation_events", "submitters" add_foreign_key "document_generation_events", "submitters"

@ -0,0 +1,10 @@
# frozen_string_literal: true
module AccountConfigs
module_function
def find_or_initialize_for_key(account, key)
account.account_configs.find_by(key:) ||
account.account_configs.new(key:, value: AccountConfig::DEFAULT_VALUES[key])
end
end

@ -0,0 +1,21 @@
# frozen_string_literal: true
module ReplaceEmailVariables
TEMAPLTE_NAME = '{{template.name}}'
SUBMITTER_LINK = '{{submitter.link}}'
ACCOUNT_NAME = '{{account.name}}'
module_function
def call(text, submitter:)
link =
Rails.application.routes.url_helpers.submit_form_url(
slug: submitter.slug, **Docuseal.default_url_options
)
text = text.gsub(TEMAPLTE_NAME, submitter.template.name)
text = text.gsub(SUBMITTER_LINK, link)
text.gsub(ACCOUNT_NAME, submitter.template.account.name)
end
end
Loading…
Cancel
Save