Compare commits

...
27 Commits
Author SHA1 Message Date
Alex Turchyn 4ffd198505 adjust demo 2023-07-12 01:36:33 +03:00
Alex Turchyn d595e7d52b adjust readme 2023-07-10 21:15:59 +03:00
Alex Turchyn aabbf2dc7f update Discord invite link 2023-07-10 00:13:49 +03:00
Alex Turchyn 95e4bf945c use sha256 on rails key_derivation_salt 2023-07-09 22:00:22 +03:00
Alex Turchyn 15523d8377 do not allow to remove current user 2023-07-09 16:30:51 +03:00
Alex Turchyn e2cda2bd5c update schema 2023-07-09 16:30:51 +03:00
Alex Turchyn 12b98fdcd4 move DigitalOcean button to another repository 2023-07-09 15:05:35 +03:00
Alex Turchyn be208a1080 disable RedundantConstantBase in erblint 2023-07-09 15:00:32 +03:00
Alex Turchyn 9268c60f9e remove old preview image 2023-07-09 14:59:32 +03:00
Alex Turchyn 52b310e1a8 add lang attrs 2023-07-09 14:59:32 +03:00
Alex Turchyn 16757acbe0 increase tokens length 2023-07-09 14:59:31 +03:00
Alex Turchyn dcbdc9e60c fix submit form 2023-07-08 20:59:54 +03:00
Alex Turchyn 254aa9e950 add demo alert 2023-07-08 20:59:50 +03:00
Alex Turchyn 28290c4635 form builder hints 2023-07-08 18:53:29 +03:00
Alex Turchyn a72882ce0f adjust form templates 2023-07-08 18:53:28 +03:00
Alex Turchyn fd0b57c651 update readme 2023-07-08 14:24:59 +03:00
Alex Turchyn 780ced3e7f omit html in github code stats 2023-07-08 13:13:32 +03:00
Alex Turchyn f8f11b6fa6 update readme 2023-07-08 01:36:37 +03:00
Alex Turchyn bdaaa5276c fix pdf generation images 2023-07-07 21:29:38 +03:00
Alex Turchyn e149e08f33 fix style 2023-07-07 01:45:50 +03:00
Alex Turchyn 47bd0571f9 add deployment to DigitalOcean 2023-07-07 01:31:36 +03:00
Alex Turchyn 5352a25ede fix pdf generation images 2023-07-07 00:54:32 +03:00
Alex Turchyn ead316df00 add docker compose 2023-07-06 23:11:08 +03:00
Alex Turchyn dac7594aeb do not require optional gems 2023-07-05 21:42:41 +03:00
Alex Turchyn f4f226ac63 configure db pool size 2023-07-05 21:19:39 +03:00
Alex Turchyn 2112b2f3d3 minor UI fixes 2023-07-05 20:52:59 +03:00
Alex Turchyn b112fc0c81 add analytics tools 2023-07-05 20:40:49 +03:00
46 changed files with 376 additions and 149 deletions

No files matched your search

+2
View File
@@ -30,3 +30,5 @@ linters:
Enabled: false Enabled: false
Style/ZeroLengthPredicate: Style/ZeroLengthPredicate:
Enabled: false Enabled: false
Style/RedundantConstantBase:
Enabled: false
+1
View File
@@ -0,0 +1 @@
*.html linguist-detectable=false
+7 -7
View File
@@ -4,28 +4,28 @@ source 'https://rubygems.org'
ruby '3.2.2' ruby '3.2.2'
gem 'audited' gem 'aws-sdk-s3', require: false
gem 'aws-sdk-s3' gem 'azure-storage-blob', require: false
gem 'azure-storage-blob'
gem 'bootsnap', require: false gem 'bootsnap', require: false
gem 'devise' gem 'devise'
gem 'dotenv', require: false gem 'dotenv', require: false
gem 'faraday' gem 'faraday'
gem 'google-cloud-storage' gem 'google-cloud-storage', require: false
gem 'hexapdf' gem 'hexapdf'
gem 'image_processing' gem 'image_processing'
gem 'lograge' gem 'lograge'
gem 'mysql2' gem 'mysql2', require: false
gem 'oj' gem 'oj'
gem 'pagy' gem 'pagy'
gem 'pg' gem 'pg', require: false
gem 'premailer-rails' gem 'premailer-rails'
gem 'puma' gem 'puma'
gem 'rails' gem 'rails'
gem 'rails-i18n' gem 'rails-i18n'
gem 'rollbar', require: ENV.key?('ROLLBAR_ACCESS_TOKEN')
gem 'ruby-vips' gem 'ruby-vips'
gem 'shakapacker' gem 'shakapacker'
gem 'sqlite3' gem 'sqlite3', require: false
gem 'strip_attributes' gem 'strip_attributes'
gem 'turbo-rails' gem 'turbo-rails'
gem 'tzinfo-data' gem 'tzinfo-data'
+2 -4
View File
@@ -72,9 +72,6 @@ GEM
activerecord (>= 3.2, < 8.0) activerecord (>= 3.2, < 8.0)
rake (>= 10.4, < 14.0) rake (>= 10.4, < 14.0)
ast (2.4.2) ast (2.4.2)
audited (5.3.3)
activerecord (>= 5.0, < 7.1)
request_store (~> 1.2)
aws-eventstream (1.2.0) aws-eventstream (1.2.0)
aws-partitions (1.781.0) aws-partitions (1.781.0)
aws-sdk-core (3.175.0) aws-sdk-core (3.175.0)
@@ -379,6 +376,7 @@ GEM
railties (>= 5.2) railties (>= 5.2)
retriable (3.1.2) retriable (3.1.2)
rexml (3.2.5) rexml (3.2.5)
rollbar (3.4.0)
rspec-core (3.12.2) rspec-core (3.12.2)
rspec-support (~> 3.12.0) rspec-support (~> 3.12.0)
rspec-expectations (3.12.3) rspec-expectations (3.12.3)
@@ -488,7 +486,6 @@ PLATFORMS
DEPENDENCIES DEPENDENCIES
annotate annotate
audited
aws-sdk-s3 aws-sdk-s3
azure-storage-blob azure-storage-blob
better_html better_html
@@ -517,6 +514,7 @@ DEPENDENCIES
puma puma
rails rails
rails-i18n rails-i18n
rollbar
rspec-rails rspec-rails
rubocop rubocop
rubocop-performance rubocop-performance
+63 -13
View File
@@ -1,24 +1,74 @@
# README <h1 align="center" style="border-bottom: none">
<div>
<a href="https://www.docuseal.co">
<img alt="DocuSeal" src="https://github.com/docusealco/docuseal/assets/5418788/c12cd051-81cd-4402-bc3a-92f2cfdc1b06" width="80" />
<br>
</a>
DocuSeal
</div>
</h1>
<h3 align="center">
Open source document filling and signing
</h3>
<p align="center">
<a href="https://hub.docker.com/r/docuseal/docuseal">
<img alt="Docker releases" src="https://img.shields.io/docker/v/docuseal/docuseal">
</a>
<a href="https://discord.gg/qygYCDGck9">
<img src="https://img.shields.io/discord/1125112641170448454?logo=discord"/>
</a>
<a href="https://twitter.com/intent/follow?screen_name=docusealco">
<img src="https://img.shields.io/twitter/follow/docusealco?style=social" alt="Follow @docusealco" />
</a>
</p>
<p>
DocuSeal is an open source platform that provides secure and efficient digital document signing and processing. Create PDF forms to have them filled and signed online on any device with an easy-to-use, mobile-optimized web tool.
</p>
<h2 align="center">
<a href="https://demo.docuseal.co">✨ Live Demo</a>
</h2>
This README would normally document whatever steps are necessary to get the [![Demo](https://github.com/docusealco/docuseal/assets/5418788/d8703ea3-361a-423f-8bfe-eff1bd9dbe14)](https://demo.docuseal.co)
application up and running.
Things you may want to cover: ## Features
- [x] PDF form fields builder (WYSIWYG)
- [x] 10 field types available (Signature, Date, File, Checkbox etc.)
- [x] Multiple submitters per document
- [x] Automated emails via SMTP
- [x] Files storage on AWS S3, Google Storage, or Azure
- [x] Automatic PDF eSignature
- [x] PDF signature verification
- [x] Users management
- [x] Mobile-optimized
- [x] Easy to deploy in minutes
* Ruby version ## Deploy
* System dependencies | Heroku | Railway | DigitalOcean |
| ---------------------------------------------------------------------------- | --------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| [<img alt="Deploy on Heroku" src="https://www.herokucdn.com/deploy/button.svg" height="40">](https://heroku.com/deploy?template=https://github.com/docusealco/docuseal-heroku) | [<img alt="Deploy on Railway" src="https://railway.app/button.svg" height="40">](https://railway.app/template/IGoDnc?referralCode=ruU7JR) | [<img alt="Deploy on DigitalOcean" src="https://www.deploytodo.com/do-btn-blue.svg" height="40">](https://cloud.digitalocean.com/apps/new?repo=https://github.com/docusealco/docuseal-digitalocean/tree/master&refcode=421d50f53990) |
* Configuration
* Database creation #### Docker
* Database initialization ```sh
docker run --name docuseal -p 3000:3000 -v.:/data docuseal/docuseal
```
* How to run the test suite By default DocuSeal docker container uses an SQLite database to store data and configurations. Alternatively, it is possible use PostgreSQL or MySQL databases by specifying the `DATABASE_URL` env variable.
* Services (job queues, cache servers, search engines, etc.) #### Docker Compose
* Deployment instructions Download docker-compose.yml into your private server:
```sh
curl https://raw.githubusercontent.com/docusealco/docuseal/master/docker-compose.yml > docker-compose.yml
```
* ... Run the app under a custom domain over https using docker compose (make sure your DNS points to the server to automatically issue ssl certs with Caddy):
```sh
HOST=your-domain-name.com docker-compose up
```
## License
DocuSeal is released under the GNU Affero General Public License v3.0.
+1 -1
View File
@@ -23,7 +23,7 @@ class ApplicationController < ActionController::Base
private private
def sign_in_for_demo def sign_in_for_demo
sign_in(User.first) unless signed_in? sign_in(User.order('random()').take) unless signed_in?
end end
def current_account def current_account
+3 -3
View File
@@ -10,9 +10,9 @@ class SubmitFormController < ApplicationController
Submitter.preload(submission: { template: { documents_attachments: { preview_images_attachments: :blob } } }) Submitter.preload(submission: { template: { documents_attachments: { preview_images_attachments: :blob } } })
.find_by!(slug: params[:slug]) .find_by!(slug: params[:slug])
cookies.signed[:submitter_sid] = @submitter.signed_id return redirect_to submit_form_completed_path(@submitter.slug) if @submitter.completed_at?
redirect_to submit_form_completed_path(@submitter.slug) if @submitter.completed_at? cookies[:submitter_sid] = @submitter.signed_id
end end
def update def update
@@ -41,7 +41,7 @@ class SubmitFormController < ApplicationController
private private
def normalized_values def normalized_values
params[:values].to_unsafe_h.transform_values do |v| params.fetch(:values, {}).to_unsafe_h.transform_values do |v|
if params[:cast_boolean] == 'true' if params[:cast_boolean] == 'true'
v == 'true' v == 'true'
else else
+9 -3
View File
@@ -24,24 +24,30 @@ class UsersController < ApplicationController
if @user.save if @user.save
UserMailer.invitation_email(@user).deliver_later! UserMailer.invitation_email(@user).deliver_later!
redirect_to settings_users_path, notice: 'User has been invited.' redirect_to settings_users_path, notice: 'User has been invited'
else else
render turbo_stream: turbo_stream.replace(:modal, template: 'users/new'), status: :unprocessable_entity render turbo_stream: turbo_stream.replace(:modal, template: 'users/new'), status: :unprocessable_entity
end end
end end
def update 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)
redirect_to settings_users_path, notice: 'User has been updated.' redirect_to settings_users_path, notice: 'User has been updated'
else else
render turbo_stream: turbo_stream.replace(:modal, template: 'users/edit'), status: :unprocessable_entity render turbo_stream: turbo_stream.replace(:modal, template: 'users/edit'), status: :unprocessable_entity
end end
end end
def destroy def destroy
if Docuseal.demo? || @user.id == current_user.id
return redirect_to settings_users_path, notice: 'Unable to remove user'
end
@user.update!(deleted_at: Time.current) @user.update!(deleted_at: Time.current)
redirect_to settings_users_path, notice: 'User has been removed.' redirect_to settings_users_path, notice: 'User has been removed'
end end
private private
+1
View File
@@ -14,6 +14,7 @@ window.customElements.define('submission-form', class extends HTMLElement {
authenticityToken: this.dataset.authenticityToken, authenticityToken: this.dataset.authenticityToken,
canSendEmail: this.dataset.canSendEmail === 'true', canSendEmail: this.dataset.canSendEmail === 'true',
isDirectUpload: this.dataset.isDirectUpload === 'true', isDirectUpload: this.dataset.isDirectUpload === 'true',
isDemo: this.dataset.isDemo === 'true',
values: reactive(JSON.parse(this.dataset.values)), values: reactive(JSON.parse(this.dataset.values)),
attachments: reactive(JSON.parse(this.dataset.attachments)), attachments: reactive(JSON.parse(this.dataset.attachments)),
fields: JSON.parse(this.dataset.fields) fields: JSON.parse(this.dataset.fields)
+26 -1
View File
@@ -40,18 +40,38 @@
Download Download
</span> </span>
</button> </button>
<a
v-if="isDemo"
target="_blank"
href="https://github.com/docusealco/docuseal"
class="white-button flex items-center space-x-1 w-full"
>
<IconBrandGithub />
<span>
Star on Github
</span>
</a>
</div>
<div class="text-center mt-4">
Signed with
<a
href="https://www.docuseal.co"
target="_blank"
class="underline"
>DocuSeal</a> - open source documents software
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import { IconCircleCheck, IconMail, IconDownload, IconInnerShadowTop } from '@tabler/icons-vue' import { IconCircleCheck, IconBrandGithub, IconMail, IconDownload, IconInnerShadowTop } from '@tabler/icons-vue'
export default { export default {
name: 'FormCompleted', name: 'FormCompleted',
components: { components: {
IconCircleCheck, IconCircleCheck,
IconInnerShadowTop, IconInnerShadowTop,
IconBrandGithub,
IconMail, IconMail,
IconDownload IconDownload
}, },
@@ -60,6 +80,11 @@ export default {
type: String, type: String,
required: true required: true
}, },
isDemo: {
type: Boolean,
required: false,
default: false
},
canSendEmail: { canSendEmail: {
type: Boolean, type: Boolean,
required: false, required: false,
+1 -1
View File
@@ -29,7 +29,7 @@
{{ message }} {{ message }}
</div> </div>
<div class="text-xs"> <div class="text-xs">
<span class="font-medium">Click to upload</span> or drag and drop <span class="font-medium">Click to upload</span> or drag and drop files
</div> </div>
</div> </div>
</div> </div>
+18 -12
View File
@@ -21,18 +21,12 @@
name="authenticity_token" name="authenticity_token"
:value="authenticityToken" :value="authenticityToken"
> >
<input
v-if="currentStep === stepFields.length - 1"
type="hidden"
name="completed"
value="true"
>
<input <input
value="put" value="put"
name="_method" name="_method"
type="hidden" type="hidden"
> >
<div class="mt-4"> <div class="md:mt-4">
<div v-if="['cells', 'text'].includes(currentField.type)"> <div v-if="['cells', 'text'].includes(currentField.type)">
<label <label
v-if="currentField.name" v-if="currentField.name"
@@ -226,7 +220,7 @@
@attached="[attachments.push($event), $refs.areas.scrollIntoField(currentField)]" @attached="[attachments.push($event), $refs.areas.scrollIntoField(currentField)]"
/> />
</div> </div>
<div class="mt-8"> <div class="mt-6 md:mt-8">
<button <button
type="submit" type="submit"
class="base-button w-full flex justify-center" class="base-button w-full flex justify-center"
@@ -252,6 +246,7 @@
</form> </form>
<FormCompleted <FormCompleted
v-else v-else
:is-demo="isDemo"
:can-send-email="canSendEmail" :can-send-email="canSendEmail"
:submitter-slug="submitterSlug" :submitter-slug="submitterSlug"
/> />
@@ -263,7 +258,7 @@
href="#" href="#"
class="inline border border-base-300 h-3 w-3 rounded-full mx-1" class="inline border border-base-300 h-3 w-3 rounded-full mx-1"
:class="{ 'bg-base-300': index === currentStep, 'bg-base-content': index < currentStep || isCompleted, 'bg-white': index > currentStep }" :class="{ 'bg-base-300': index === currentStep, 'bg-base-content': index < currentStep || isCompleted, 'bg-white': index > currentStep }"
@click.prevent="isCompleted ? '' : goToStep(step, true)" @click.prevent="isCompleted ? '' : [saveStep(), goToStep(step, true)]"
/> />
</div> </div>
</div> </div>
@@ -323,6 +318,11 @@ export default {
required: true, required: true,
default: false default: false
}, },
isDemo: {
type: Boolean,
required: true,
default: false
},
values: { values: {
type: Object, type: Object,
required: false, required: false,
@@ -401,13 +401,13 @@ export default {
} }
}) })
}, },
saveStep () { saveStep (formData) {
if (this.isCompleted) { if (this.isCompleted) {
return Promise.resolve({}) return Promise.resolve({})
} else { } else {
return fetch(this.submitPath, { return fetch(this.submitPath, {
method: 'POST', method: 'POST',
body: new FormData(this.$refs.form) body: formData || new FormData(this.$refs.form)
}) })
} }
}, },
@@ -420,7 +420,13 @@ export default {
await stepPromise() await stepPromise()
this.saveStep().then(response => { const formData = new FormData(this.$refs.form)
if (this.currentStep === this.stepFields.length - 1) {
formData.append('completed', 'true')
}
this.saveStep(formData).then(response => {
const nextStep = this.stepFields[this.currentStep + 1] const nextStep = this.stepFields[this.currentStep + 1]
if (nextStep) { if (nextStep) {
+1 -1
View File
@@ -29,7 +29,7 @@
{{ message }} {{ message }}
</div> </div>
<div class="text-sm"> <div class="text-sm">
<span class="font-medium">Click to upload</span> or drag and drop <span class="font-medium">Click to upload</span> or drag and drop files
</div> </div>
</div> </div>
</div> </div>
@@ -67,6 +67,25 @@
</div> </div>
</button> </button>
</div> </div>
<div
v-if="fields.length < 4"
class="text-xs p-2 border border-base-200 rounded"
>
<ui>
<li>
Draw a text field on the page with a mouse
</li>
<li>
Single click of the page to add a checkbox
</li>
<li>
Drag &amp; drop any other field type on the page
</li>
<li>
Click on the field type above to add it to the form without drawing it on the document
</li>
</ui>
</div>
</template> </template>
<script> <script>
+2 -2
View File
@@ -13,7 +13,7 @@
# slug :string not null # slug :string not null
# ua :string # ua :string
# uuid :string not null # uuid :string not null
# values :string not null # values :text not null
# created_at :datetime not null # created_at :datetime not null
# updated_at :datetime not null # updated_at :datetime not null
# submission_id :bigint not null # submission_id :bigint not null
@@ -32,7 +32,7 @@ class Submitter < ApplicationRecord
belongs_to :submission belongs_to :submission
attribute :values, :string, default: -> { {} } attribute :values, :string, default: -> { {} }
attribute :slug, :string, default: -> { SecureRandom.base58(8) } attribute :slug, :string, default: -> { SecureRandom.base58(10) }
serialize :values, JSON serialize :values, JSON
+4 -4
View File
@@ -6,11 +6,11 @@
# #
# id :bigint not null, primary key # id :bigint not null, primary key
# deleted_at :datetime # deleted_at :datetime
# fields :string not null # fields :text not null
# name :string not null # name :string not null
# schema :string not null # schema :text not null
# slug :string not null # slug :string not null
# submitters :string not null # submitters :text not null
# created_at :datetime not null # created_at :datetime not null
# updated_at :datetime not null # updated_at :datetime not null
# account_id :bigint not null # account_id :bigint not null
@@ -36,7 +36,7 @@ class Template < ApplicationRecord
attribute :fields, :string, default: -> { [] } attribute :fields, :string, default: -> { [] }
attribute :schema, :string, default: -> { [] } attribute :schema, :string, default: -> { [] }
attribute :submitters, :string, default: -> { [{ name: DEFAULT_SUBMITTER_NAME, uuid: SecureRandom.uuid }] } attribute :submitters, :string, default: -> { [{ name: DEFAULT_SUBMITTER_NAME, uuid: SecureRandom.uuid }] }
attribute :slug, :string, default: -> { SecureRandom.base58(8) } attribute :slug, :string, default: -> { SecureRandom.base58(10) }
serialize :fields, JSON serialize :fields, JSON
serialize :schema, JSON serialize :schema, JSON
+1
View File
@@ -1,3 +1,4 @@
<% if Docuseal.demo? %><%= render 'shared/demo_alert' %><% end %>
<% if @templates.any? %> <% if @templates.any? %>
<div class="flex justify-between mb-4 items-center"> <div class="flex justify-between mb-4 items-center">
<h1 class="text-4xl font-bold">Templates</h1> <h1 class="text-4xl font-bold">Templates</h1>
+1 -1
View File
@@ -28,7 +28,7 @@
Verify Signed PDF Verify Signed PDF
</div> </div>
<div class="text-xs"> <div class="text-xs">
<span class="font-medium">Click to upload</span> or drag and drop <span class="font-medium">Click to upload</span> or drag and drop files
</div> </div>
</div> </div>
<input id="file" name="files[]" class="hidden" data-action="change:file-dropzone#onSelectFiles" data-target="file-dropzone.input" type="file" accept="application/pdf" multiple> <input id="file" name="files[]" class="hidden" data-action="change:file-dropzone#onSelectFiles" data-target="file-dropzone.input" type="file" accept="application/pdf" multiple>
+6
View File
@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" class="<%= local_assigns[:class] %>" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M3 19c3.333 -2 5 -4 5 -6c0 -3 -1 -3 -2 -3s-2.032 1.085 -2 3c.034 2.048 1.658 2.877 2.5 4c1.5 2 2.5 2.5 3.5 1c.667 -1 1.167 -1.833 1.5 -2.5c1 2.333 2.333 3.5 4 3.5h2.5"></path>
<path d="M20 17v-12c0 -1.121 -.879 -2 -2 -2s-2 .879 -2 2v12l2 2l2 -2z"></path>
<path d="M16 7h4"></path>
</svg>

After

Width:  |  Height:  |  Size: 584 B

+2 -1
View File
@@ -1,5 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<html data-theme="docuseal"> <html data-theme="docuseal" lang="en">
<head> <head>
<title> <title>
DocuSeal | Open Source Document Filling and Signing DocuSeal | Open Source Document Filling and Signing
@@ -10,6 +10,7 @@
<%= csp_meta_tag %> <%= csp_meta_tag %>
<%= javascript_pack_tag 'application', defer: true %> <%= javascript_pack_tag 'application', defer: true %>
<%= stylesheet_pack_tag 'application', media: 'all' %> <%= stylesheet_pack_tag 'application', media: 'all' %>
<%= render 'shared/posthog' if ENV['POSTHOG_TOKEN'] %>
</head> </head>
<body> <body>
<turbo-frame id="modal"></turbo-frame> <turbo-frame id="modal"></turbo-frame>
+2 -2
View File
@@ -1,16 +1,16 @@
<!DOCTYPE html> <!DOCTYPE html>
<html data-theme="docuseal"> <html data-theme="docuseal" lang="en">
<head> <head>
<title> <title>
DocuSeal | Open Source Document Filling and Signing DocuSeal | Open Source Document Filling and Signing
</title> </title>
<%= render 'shared/meta' %> <%= render 'shared/meta' %>
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="robots" content="noindex">
<%= csrf_meta_tags %> <%= csrf_meta_tags %>
<%= csp_meta_tag %> <%= csp_meta_tag %>
<%= javascript_pack_tag 'form', defer: true %> <%= javascript_pack_tag 'form', defer: true %>
<%= stylesheet_pack_tag 'form', media: 'all' %> <%= stylesheet_pack_tag 'form', media: 'all' %>
<%= render 'shared/posthog' if ENV['POSTHOG_TOKEN'] %>
</head> </head>
<body> <body>
<%= yield %> <%= yield %>
+2 -1
View File
@@ -1,5 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<html data-theme="docuseal"> <html data-theme="docuseal" lang="en">
<head> <head>
<title> <title>
DocuSeal | Open Source Document Filling and Signing DocuSeal | Open Source Document Filling and Signing
@@ -10,6 +10,7 @@
<%= csp_meta_tag %> <%= csp_meta_tag %>
<%= javascript_pack_tag 'application', defer: true %> <%= javascript_pack_tag 'application', defer: true %>
<%= stylesheet_pack_tag 'application', media: 'all' %> <%= stylesheet_pack_tag 'application', media: 'all' %>
<%= render 'shared/posthog' if ENV['POSTHOG_TOKEN'] %>
</head> </head>
<body> <body>
<% if flash.present? %><%= render 'shared/flash' %><% end %> <% if flash.present? %><%= render 'shared/flash' %><% end %>
+11
View File
@@ -0,0 +1,11 @@
<div class="mx-auto mt-1.5">
<div class="py-3 rounded-2xl flex items-center justify-between mx-4 md:mx-0">
<div class="w-full text-center">
<span class="font-bold">DocuSeal Demo Environment</span>
<br>
Feel free to
<a href="<%= new_template_path %>" data-turbo-frame="modal" class="inline underline font-medium">create a new template</a> document form or
<a href="<%= start_form_url(slug: ::Template.first&.slug) %>" target="_blank" class="inline underline font-medium">submit the existing one</a> 😊
</div>
</div>
</div>
+10
View File
@@ -0,0 +1,10 @@
<a href="https://github.com/docusealco/docuseal" class="btn btn-neutral btn-sm btn-outline inline-flex items-center justify-center" target="_blank" alt="Star on GitHub" style="height: 37px">
<span>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path fill="currentColor" d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
</svg>
</span>
<span class="flex">
<span class="hidden lg:block">Star on&nbsp</span>GitHub
</span>
</a>
+3
View File
@@ -1,3 +1,6 @@
<% if Docuseal.demo? || (request.path != '/' && !devise_controller?) %>
<meta name="robots" content="noindex">
<% end %>
<meta name="description" content="Open source, self-hosted tool to streamline document filling and signing. Create custom PDF forms to complete and sign with an easy to use online tool."> <meta name="description" content="Open source, self-hosted tool to streamline document filling and signing. Create custom PDF forms to complete and sign with an easy to use online tool.">
<meta property="og:title" content="DocuSeal | Open Source Document Filling and Signing"> <meta property="og:title" content="DocuSeal | Open Source Document Filling and Signing">
<meta property="og:description" content="Open source, self-hosted tool to streamline document filling and signing. Create custom PDF forms to complete and sign with an easy to use online tool."> <meta property="og:description" content="Open source, self-hosted tool to streamline document filling and signing. Create custom PDF forms to complete and sign with an easy to use online tool.">
+11 -5
View File
@@ -4,13 +4,19 @@
<%= render 'shared/logo' %> <%= render 'shared/logo' %>
<span>DocuSeal</span> <span>DocuSeal</span>
</a> </a>
<a href="<%= Docuseal::GITHUB_URL %>" target="_blank" class="inline"> <% unless Docuseal.demo? %>
<img alt="GitHub Repo stars" src="https://img.shields.io/github/stars/docusealco/docuseal?style=social&label=" style="height: 22px"> <a href="<%= Docuseal::GITHUB_URL %>" target="_blank" class="inline">
</a> <img alt="GitHub Repo stars" src="https://www.docuseal.co/github-badge.svg" style="height: 22px">
</a>
<% end %>
</div> </div>
<% if signed_in? %> <% if signed_in? %>
<div class="space-x-6"> <div class="space-x-4 flex items-center">
<%= link_to 'Settings', settings_profile_index_path, class: 'hidden md:inline font-medium text-lg' %> <% if Docuseal.demo? %>
<%= render 'shared/github_button' %>
<% else %>
<%= link_to 'Settings', settings_profile_index_path, class: 'hidden md:inline font-medium text-lg mr-2' %>
<% end %>
<div class="dropdown dropdown-end"> <div class="dropdown dropdown-end">
<label tabindex="0" class="cursor-pointer bg-base-content text-purple-300 rounded-full p-2 w-9 justify-center flex"> <label tabindex="0" class="cursor-pointer bg-base-content text-purple-300 rounded-full p-2 w-9 justify-center flex">
<span class="text-sm align-text-top"><%= current_user.initials %></span> <span class="text-sm align-text-top"><%= current_user.initials %></span>
+4
View File
@@ -0,0 +1,4 @@
<script>
!function(t,e){var o,n,p,r;e.__SV||(window.posthog=e,e._i=[],e.init=function(i,s,a){function g(t,e){var o=e.split(".");2==o.length&&(t=t[o[0]],e=o[1]),t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}}(p=t.createElement("script")).type="text/javascript",p.async=!0,p.src=s.api_host+"/static/array.js",(r=t.getElementsByTagName("script")[0]).parentNode.insertBefore(p,r);var u=e;for(void 0!==a?u=e[a]=[]:a="posthog",u.people=u.people||[],u.toString=function(t){var e="posthog";return"posthog"!==a&&(e+="."+a),t||(e+=" (stub)"),e},u.people.toString=function(){return u.toString(1)+".people (stub)"},o="capture identify alias people.set people.set_once set_config register register_once unregister opt_out_capturing has_opted_out_capturing opt_in_capturing reset isFeatureEnabled onFeatureFlags getFeatureFlag getFeatureFlagPayload reloadFeatureFlags group updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures getActiveMatchingSurveys getSurveys".split(" "),n=0;n<o.length;n++)g(u,o[n]);e._i.push([i,s,a])},e.__SV=1)}(document,window.posthog||[]);
posthog.init('<%= ENV.fetch('POSTHOG_TOKEN', nil) %>',{api_host:'https://app.posthog.com',autocapture: false,enable_recording_console_log: true,capture_pageview: false,session_recording:{maskAllInputs: false,maskInputOptions: {password: true}}})
</script>
+4 -1
View File
@@ -1,4 +1,4 @@
<div class="block w-full md:w-52"> <div class="block w-full md:w-52 flex-none">
<menu-active> <menu-active>
<ul class="menu px-0"> <ul class="menu px-0">
<li class="menu-title py-0"><span>Settings</span></li> <li class="menu-title py-0"><span>Settings</span></li>
@@ -44,5 +44,8 @@
<%= svg_icon('send', class: 'h-5 w-5') %> <%= svg_icon('send', class: 'h-5 w-5') %>
Tell about us Tell about us
</a> </a>
<a href="mailto:<%= Docuseal::SUPPORT_EMAIL %>" target="_blank" class="w-full block mt-4 underline text-center">
<%= Docuseal::SUPPORT_EMAIL %>
</a>
</div> </div>
</div> </div>
+1 -6
View File
@@ -12,12 +12,7 @@
<div class="flex items-center bg-base-300 rounded-xl p-4 mb-4"> <div class="flex items-center bg-base-300 rounded-xl p-4 mb-4">
<div class="flex items-center"> <div class="flex items-center">
<div class="mr-3"> <div class="mr-3">
<svg xmlns="http://www.w3.org/2000/svg" class="w-10 h-10" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <%= svg_icon('writing_sign', class: 'w-10 h-10') %>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M3 19c3.333 -2 5 -4 5 -6c0 -3 -1 -3 -2 -3s-2.032 1.085 -2 3c.034 2.048 1.658 2.877 2.5 4c1.5 2 2.5 2.5 3.5 1c.667 -1 1.167 -1.833 1.5 -2.5c1 2.333 2.333 3.5 4 3.5h2.5" />
<path d="M20 17v-12c0 -1.121 -.879 -2 -2 -2s-2 .879 -2 2v12l2 2l2 -2z" />
<path d="M16 7h4" />
</svg>
</div> </div>
<div> <div>
<p class="text-lg font-bold mb-1"><%= @submitter.submission.template.name %></p> <p class="text-lg font-bold mb-1"><%= @submitter.submission.template.name %></p>
+29 -36
View File
@@ -1,43 +1,36 @@
<div class="max-w-xl mx-auto px-2 mt-12 mb-4"> <div class="max-w-md mx-auto px-2 mt-12 mb-4">
<div class="card bg-base-200"> <div class="space-y-6 mx-auto">
<div class="card-body"> <div class="space-y-6">
<div class="space-y-4 w-full md:px-10 mx-auto"> <div class="text-center w-full space-y-6">
<div class="space-y-4"> <a href="/" class="flex justify-center">
<a href="/" class="flex justify-center"> <span class="mr-3">
<span class="mr-3"> <%= render 'shared/logo', width: '50px', height: '50px' %>
<%= render 'shared/logo', width: '50px', height: '50px' %> </span>
</span> <h1 class="text-5xl font-bold text-center">DocuSeal</h1>
<h1 class="text-5xl font-bold text-center">DocuSeal</h1> </a>
</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 the form</p> </div>
<div class="flex items-center bg-base-content/10 rounded-xl p-4"> <div class="flex items-center bg-base-300 rounded-xl p-4 mb-4">
<div class="flex items-center"> <div class="flex items-center">
<div class="mr-3"> <div class="mr-3">
<svg xmlns="http://www.w3.org/2000/svg" class="w-10 h-10" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <%= svg_icon('writing_sign', class: 'w-10 h-10') %>
<path stroke="none" d="M0 0h24v24H0z" fill="none" /> </div>
<path d="M3 19c3.333 -2 5 -4 5 -6c0 -3 -1 -3 -2 -3s-2.032 1.085 -2 3c.034 2.048 1.658 2.877 2.5 4c1.5 2 2.5 2.5 3.5 1c.667 -1 1.167 -1.833 1.5 -2.5c1 2.333 2.333 3.5 4 3.5h2.5" /> <div>
<path d="M20 17v-12c0 -1.121 -.879 -2 -2 -2s-2 .879 -2 2v12l2 2l2 -2z" /> <p class="text-lg font-bold mb-1"><%= @template.name %></p>
<path d="M16 7h4" /> <p class="text-sm">Invited by <%= @template.account.name %></p>
</svg>
</div>
<div>
<p class="font-bold mb-1"><%= @template.name %></p>
<p class="text-sm">Invited by <%= @template.account.name %></p>
</div>
</div>
</div> </div>
</div> </div>
<%= form_for @submitter, url: start_form_path(@template.slug), data: { turbo_frame: :_top }, method: :put, html: { class: 'space-y-4' } do |f| %>
<div class="form-control !mt-0">
<%= f.label :email, class: 'label' %>
<%= f.email_field :email, required: true, class: 'base-input', placeholder: 'Provide your email to start' %>
</div>
<div class="form-control">
<%= f.button button_title(title: 'Start', disabled_with: 'Starting'), class: 'base-button' %>
</div>
<% end %>
</div> </div>
</div> </div>
<%= form_for @submitter, url: start_form_path(@template.slug), data: { turbo_frame: :_top }, method: :put, html: { class: 'space-y-4' } do |f| %>
<div class="form-control !mt-0">
<%= f.label :email, class: 'label' %>
<%= f.email_field :email, required: true, class: 'base-input', placeholder: 'Provide your email to start' %>
</div>
<div class="form-control">
<%= f.button button_title(title: 'Start', disabled_with: 'Starting'), class: 'base-button' %>
</div>
<% end %>
</div> </div>
</div> </div>
<%= render 'shared/attribution' %> <%= render 'shared/attribution' %>
+3 -3
View File
@@ -1,5 +1,5 @@
<div style="max-width: 1600px" class="mx-auto pl-4"> <div style="max-width: 1600px" class="mx-auto pl-4">
<div class="flex justify-between py-1.5 items-center pr-4"> <div class="flex justify-between py-1.5 items-center pr-4 sticky top-0 md:relative z-10 bg-base-100">
<a href="<%= template_path(@submission.template) %>" class="flex space-x-3 py-1"> <a href="<%= template_path(@submission.template) %>" class="flex space-x-3 py-1">
<span><%= render 'shared/logo', width: 40, height: 40 %></span> <span><%= render 'shared/logo', width: 40, height: 40 %></span>
<span class="text-3xl font-semibold focus:text-clip"><%= @submission.template.name %></span> <span class="text-3xl font-semibold focus:text-clip"><%= @submission.template.name %></span>
@@ -21,7 +21,7 @@
<% end %> <% end %>
</div> </div>
</div> </div>
<div class="flex" style="max-height: calc(100vh - 60px)"> <div class="flex md:max-h-[calc(100vh-60px)]">
<div class="overflow-y-auto overflow-x-hidden hidden lg:block w-52 flex-none pr-3 mt-0.5 pt-0.5"> <div class="overflow-y-auto overflow-x-hidden hidden lg:block w-52 flex-none pr-3 mt-0.5 pt-0.5">
<% @submission.template.schema.each do |item| %> <% @submission.template.schema.each do |item| %>
<% document = @submission.template.documents.find { |a| item['attachment_uuid'] == a.uuid } %> <% document = @submission.template.documents.find { |a| item['attachment_uuid'] == a.uuid } %>
@@ -82,7 +82,7 @@
</div> </div>
<% if submitter && !submitter.completed_at? %> <% if submitter && !submitter.completed_at? %>
<div class="mt-2 mb-1"> <div class="mt-2 mb-1">
<a class="btn btn-sm btn-primary w-full" target="_blank" href="<%= submit_form_url(slug: submitter.slug) %>"> <a class="btn btn-sm btn-primary w-full" target="_blank" href="<%= submit_form_path(slug: submitter.slug) %>">
Submit Form Submit Form
</a> </a>
</div> </div>
+1 -6
View File
@@ -12,12 +12,7 @@
<div class="flex items-center bg-base-300 rounded-xl p-4 mb-4"> <div class="flex items-center bg-base-300 rounded-xl p-4 mb-4">
<div class="flex items-center"> <div class="flex items-center">
<div class="mr-3"> <div class="mr-3">
<svg xmlns="http://www.w3.org/2000/svg" class="w-10 h-10" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <%= svg_icon('writing_sign', class: 'w-10 h-10') %>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M3 19c3.333 -2 5 -4 5 -6c0 -3 -1 -3 -2 -3s-2.032 1.085 -2 3c.034 2.048 1.658 2.877 2.5 4c1.5 2 2.5 2.5 3.5 1c.667 -1 1.167 -1.833 1.5 -2.5c1 2.333 2.333 3.5 4 3.5h2.5" />
<path d="M20 17v-12c0 -1.121 -.879 -2 -2 -2s-2 .879 -2 2v12l2 2l2 -2z" />
<path d="M16 7h4" />
</svg>
</div> </div>
<div> <div>
<p class="text-lg font-bold mb-1"><%= @submitter.submission.template.name %></p> <p class="text-lg font-bold mb-1"><%= @submitter.submission.template.name %></p>
+1 -1
View File
@@ -33,7 +33,7 @@
<div class="mx-auto" style="max-width: 1000px"> <div class="mx-auto" style="max-width: 1000px">
<div class="relative md:mx-32"> <div class="relative md:mx-32">
<div id="form_container" class="shadow-md bg-base-100 absolute bottom-0 md:bottom-4 w-full border border-base-200 border p-4 rounded"> <div id="form_container" class="shadow-md bg-base-100 absolute bottom-0 md:bottom-4 w-full border border-base-200 border p-4 rounded">
<submission-form data-is-direct-upload="<%= Docuseal.active_storage_public? %>" data-submitter-uuid="<%= @submitter.uuid %>" data-submitter-slug="<%= @submitter.slug %>" data-can-send-email="<%= Accounts.can_send_emails?(Struct.new(:id).new(@submitter.submission.template.account_id)) %>" data-attachments="<%= attachments_index.values.select { |e| e.record_id == @submitter.id }.to_json(only: %i[uuid], methods: %i[url filename content_type]) %>" data-fields="<%= @submitter.submission.template.fields.select { |f| f['submitter_uuid'] == @submitter.uuid }.to_json %>" data-values="<%= @submitter.values.to_json %>" data-authenticity-token="<%= form_authenticity_token %>"></submission-form> <submission-form data-is-demo="<%= Docuseal.demo? %>" data-is-direct-upload="<%= Docuseal.active_storage_public? %>" data-submitter-uuid="<%= @submitter.uuid %>" data-submitter-slug="<%= @submitter.slug %>" data-can-send-email="<%= Accounts.can_send_emails?(Struct.new(:id).new(@submitter.submission.template.account_id)) %>" data-attachments="<%= attachments_index.values.select { |e| e.record_id == @submitter.id }.to_json(only: %i[uuid], methods: %i[url filename content_type]) %>" data-fields="<%= @submitter.submission.template.fields.select { |f| f['submitter_uuid'] == @submitter.uuid }.to_json %>" data-values="<%= @submitter.values.to_json %>" data-authenticity-token="<%= form_authenticity_token %>"></submission-form>
</div> </div>
</div> </div>
</div> </div>
+12 -6
View File
@@ -157,13 +157,19 @@
<% else %> <% else %>
<div class="card bg-base-200"> <div class="card bg-base-200">
<div class="card-body text-center py-16"> <div class="card-body text-center py-16">
<div class="max-w-md mx-auto"> <div class="max-w-lg mx-auto">
<p class="text-3xl font-bold text-base-content mb-4">There are no Submissions</p> <p class="text-3xl font-bold text-base-content mb-4">There are no Submissions</p>
<p class="text-gray-600">Send an invitation to fill and submit the documents</p> <p class="text-gray-600">Send an invitation to fill and complete the form</p>
<%= link_to new_template_submission_path(@template), class: 'base-button mt-6', data: { turbo_frame: 'modal' } do %> <div class="space-x-2">
<%= svg_icon('plus', class: 'w-6 h-6 stroke-2') %> <%= link_to new_template_submission_path(@template), class: 'base-button mt-6', data: { turbo_frame: 'modal' } do %>
<span class="mr-1">Add Recipients</span> <%= svg_icon('plus', class: 'w-6 h-6 stroke-2') %>
<% end %> <span class="mr-1">Add Recipients</span>
<% 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') %>
<span class="mr-1">Submit it Yourself</span>
<% end %>
</div>
</div> </div>
</div> </div>
</div> </div>
+1 -1
View File
@@ -1,4 +1,4 @@
<div class="flex flex-wrap space-y-4 md:flex-nowrap md:space-y-0 md:space-x-2"> <div class="flex flex-wrap space-y-4 md:flex-nowrap md:space-y-0 md:space-x-10">
<%= render 'shared/settings_nav' %> <%= render 'shared/settings_nav' %>
<div class="flex-grow"> <div class="flex-grow">
<div class="flex justify-between mb-4"> <div class="flex justify-between mb-4">
+10 -1
View File
@@ -1,6 +1,7 @@
default: &default default: &default
adapter: postgresql adapter: postgresql
encoding: unicode encoding: unicode
pool: <%= ENV.fetch('RAILS_MAX_THREADS', 15) %>
development: development:
<<: *default <<: *default
@@ -10,8 +11,16 @@ test:
<<: *default <<: *default
database: docuseal_test database: docuseal_test
<% if ENV['DATABASE_URL'].to_s.empty? %>
production: production:
<% if ENV['DATABASE_URL'].to_s.empty? %>
adapter: sqlite3 adapter: sqlite3
database: <%= ENV['WORKDIR'] || '.' %>/db.sqlite3 database: <%= ENV['WORKDIR'] || '.' %>/db.sqlite3
<% elsif ENV['DATABASE_URL'].match?(/\Apostgres/) %>
<<: *default
url: <%= ENV['DATABASE_URL'] %>
<% elsif ENV['DATABASE_URL'].match?(/\Amysql/) %>
adapter: mysql2
encoding: utf8mb4
pool: <%= ENV.fetch('RAILS_MAX_THREADS', 15) %>
url: <%= ENV['DATABASE_URL'] %>
<% end %> <% end %>
+1 -1
View File
@@ -110,7 +110,7 @@ Rails.application.configure do
config.active_record.encryption = { config.active_record.encryption = {
primary_key: encryption_secret.first(32), primary_key: encryption_secret.first(32),
deterministic_key: encryption_secret.last(32), deterministic_key: encryption_secret.last(32),
key_derivation_salt: encryption_secret key_derivation_salt: Digest::SHA256.hexdigest(encryption_secret)
} }
# Do not dump schema after migrations. # Do not dump schema after migrations.
+1 -1
View File
@@ -15,7 +15,7 @@ Rails.configuration.to_prepare do
ActiveStorage::DirectUploadsController.before_action do ActiveStorage::DirectUploadsController.before_action do
next if current_user next if current_user
next if Submitter.find_signed(cookies.signed[:submitter_sid]) next if Submitter.find_signed(cookies[:submitter_sid])
head :forbidden head :forbidden
end end
+13
View File
@@ -0,0 +1,13 @@
# frozen_string_literal: true
if defined?(Rollbar)
Rollbar.configure do |config|
config.access_token = ENV.fetch('ROLLBAR_ACCESS_TOKEN', nil)
config.enabled = true
config.exception_level_filters['ActionController::RoutingError'] = 'ignore'
config.environment = ENV['ROLLBAR_ENV'].presence || Rails.env
end
end
+4 -4
View File
@@ -86,7 +86,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_07_01_075115) do
t.string "uuid", null: false t.string "uuid", null: false
t.string "email", null: false t.string "email", null: false
t.string "slug", null: false t.string "slug", null: false
t.string "values", null: false t.text "values", null: false
t.string "ua" t.string "ua"
t.string "ip" t.string "ip"
t.datetime "sent_at" t.datetime "sent_at"
@@ -102,9 +102,9 @@ ActiveRecord::Schema[7.0].define(version: 2023_07_01_075115) do
create_table "templates", force: :cascade do |t| create_table "templates", force: :cascade do |t|
t.string "slug", null: false t.string "slug", null: false
t.string "name", null: false t.string "name", null: false
t.string "schema", null: false t.text "schema", null: false
t.string "fields", null: false t.text "fields", null: false
t.string "submitters", null: false t.text "submitters", null: false
t.bigint "author_id", null: false t.bigint "author_id", null: false
t.bigint "account_id", null: false t.bigint "account_id", null: false
t.datetime "deleted_at" t.datetime "deleted_at"
+41
View File
@@ -0,0 +1,41 @@
version: '3'
services:
app:
depends_on:
postgres:
condition: service_healthy
image: docuseal/docuseal:latest
ports:
- 3000:3000
volumes:
- .:/data
environment:
FORCE_SSL: 'true'
DATABASE_URL: postgresql://postgres:postgres@postgres:5432/docuseal
postgres:
image: postgres:latest
volumes:
- './pg_data:/var/lib/postgresql/data'
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: docuseal
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
caddy:
image: caddy:latest
command: caddy reverse-proxy --from $HOST --to app:3000
ports:
- 80:80
- 443:443
- 443:443/udp
volumes:
- .:/data
environment:
- HOST=${HOST}
+25
View File
@@ -3,6 +3,31 @@
module Accounts module Accounts
module_function module_function
def create_duplicate(account)
new_account = account.dup
new_user = account.users.first.dup
new_user.account = new_account
new_user.encrypted_password = SecureRandom.hex
new_user.email = "#{SecureRandom.hex}@docuseal.co"
account.templates.each do |template|
new_template = template.dup
new_template.account = new_account
new_template.slug = SecureRandom.base58(10)
new_template.save!
Templates::CloneAttachments.call(template: new_template, original_template: template)
end
new_user.save!(validate: false)
new_account
end
def load_signing_certs(account) def load_signing_certs(account)
certs = certs =
if Docuseal.multitenant? if Docuseal.multitenant?
+2 -1
View File
@@ -6,9 +6,10 @@ module Docuseal
PRODUCT_NAME = 'DocuSeal' PRODUCT_NAME = 'DocuSeal'
DEFAULT_APP_URL = 'http://localhost:3000' DEFAULT_APP_URL = 'http://localhost:3000'
GITHUB_URL = 'https://github.com/docusealco/docuseal' GITHUB_URL = 'https://github.com/docusealco/docuseal'
DISCORD_URL = 'https://discord.com/invite/B5wg5wKk' DISCORD_URL = 'https://discord.gg/qygYCDGck9'
TWITTER_URL = 'https://twitter.com/docusealco' TWITTER_URL = 'https://twitter.com/docusealco'
TWITTER_HANDLE = '@docusealco' TWITTER_HANDLE = '@docusealco'
SUPPORT_EMAIL = 'support@docuseal.co'
CERTS = JSON.parse(ENV.fetch('CERTS', '{}')) CERTS = JSON.parse(ENV.fetch('CERTS', '{}'))
+13 -18
View File
@@ -6,7 +6,7 @@ module Submissions
FONT_NAME = 'Helvetica' FONT_NAME = 'Helvetica'
INFO_CREATOR = "#{Docuseal::PRODUCT_NAME} (#{Docuseal::PRODUCT_URL})".freeze INFO_CREATOR = "#{Docuseal::PRODUCT_NAME} (#{Docuseal::PRODUCT_URL})".freeze
SIGN_REASON = 'Signed by %<email>s with docuseal.co' SIGN_REASON = 'Signed by %<email>s with DocuSeal.co'
TEXT_LEFT_MARGIN = 1 TEXT_LEFT_MARGIN = 1
TEXT_TOP_MARGIN = 1 TEXT_TOP_MARGIN = 1
@@ -40,25 +40,29 @@ module Submissions
value = submitter.values[field['uuid']] value = submitter.values[field['uuid']]
next if Array.wrap(value).compact_blank.blank?
canvas = page.canvas(type: :overlay) canvas = page.canvas(type: :overlay)
case field['type'] case field['type']
when 'image', 'signature' when 'image', 'signature'
attachment = submitter.attachments.find { |a| a.uuid == value } attachment = submitter.attachments.find { |a| a.uuid == value }
io = StringIO.new(download_supported_image_data(attachment)) image = Vips::Image.new_from_buffer(attachment.download, '').autorot
scale = [(area['w'] * width) / attachment.metadata['width'], scale = [(area['w'] * width) / image.width,
(area['h'] * height) / attachment.metadata['height']].min (area['h'] * height) / image.height].min
io = StringIO.new(image.resize([scale * 4, 1].min).write_to_buffer('.png'))
canvas.image( canvas.image(
io, io,
at: [ at: [
(area['x'] * width) + (area['w'] * width / 2) - ((attachment.metadata['width'] * scale) / 2), (area['x'] * width) + (area['w'] * width / 2) - ((image.width * scale) / 2),
height - (area['y'] * height) - (attachment.metadata['height'] * scale / 2) - (area['h'] * height / 2) height - (area['y'] * height) - (image.height * scale / 2) - (area['h'] * height / 2)
], ],
width: attachment.metadata['width'] * scale, width: image.width * scale,
height: attachment.metadata['height'] * scale height: image.height * scale
) )
when 'file' when 'file'
page[:Annots] ||= [] page[:Annots] ||= []
@@ -244,7 +248,7 @@ module Submissions
page.box.height = attachment.metadata['height'] * scale page.box.height = attachment.metadata['height'] * scale
page.canvas.image( page.canvas.image(
StringIO.new(download_supported_image_data(attachment)), StringIO.new(attachment.preview_images.first.download),
at: [0, 0], at: [0, 0],
width: page.box.width, width: page.box.width,
height: page.box.height height: page.box.height
@@ -253,15 +257,6 @@ module Submissions
pdf pdf
end end
def download_supported_image_data(attachment)
if SUPPORTED_IMAGE_TYPES.include?(attachment.content_type)
attachment.download
else
Vips::Image.new_from_buffer(attachment.download, '')
.write_to_buffer('.png')
end
end
def h def h
Rails.application.routes.url_helpers Rails.application.routes.url_helpers
end end
+1 -1
View File
@@ -28,7 +28,7 @@ module Templates
ActiveStorage::Attachment.where(name: ATTACHMENT_NAME, record: attachment).destroy_all ActiveStorage::Attachment.where(name: ATTACHMENT_NAME, record: attachment).destroy_all
image = Vips::Image.new_from_buffer(binary, '') image = Vips::Image.new_from_buffer(binary, '')
image = image.resize(MAX_WIDTH / image.width.to_f) image = image.autorot.resize(MAX_WIDTH / image.width.to_f)
io = StringIO.new(image.write_to_buffer(FORMAT, Q: Q, interlace: true)) io = StringIO.new(image.write_to_buffer(FORMAT, Q: Q, interlace: true))
Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB