Skip to content

Commit f87ef67

Browse files
authored
Merge from docusealco/wip
2 parents 2521eb5 + daba250 commit f87ef67

File tree

10 files changed

+404
-36
lines changed

10 files changed

+404
-36
lines changed

app/controllers/template_documents_controller.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@
33
class TemplateDocumentsController < ApplicationController
44
load_and_authorize_resource :template
55

6+
FILES_TTL = 5.minutes
7+
8+
def index
9+
render json: @template.schema_documents.map { |d| ActiveStorage::Blob.proxy_url(d.blob, expires_at: FILES_TTL.from_now.to_i) }
10+
end
11+
612
def create
713
if params[:blobs].blank? && params[:files].blank?
814
return render json: { error: I18n.t('file_is_missing') }, status: :unprocessable_content

app/controllers/templates_debug_controller.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ def show
1717
fields = Templates::FindAcroFields.call(pdf, attachment, data)
1818
end
1919

20-
fields = Templates::DetectFields.call(StringIO.new(data), attachment:) if fields.blank?
20+
fields, = Templates::DetectFields.call(StringIO.new(data), attachment:) if fields.blank?
2121

2222
attachment.metadata['pdf'] ||= {}
2323
attachment.metadata['pdf']['fields'] = fields

app/javascript/application.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ safeRegisterElement('template-builder', class extends HTMLElement {
167167
withConditions: this.dataset.withConditions === 'true',
168168
withGoogleDrive: this.dataset.withGoogleDrive === 'true',
169169
withReplaceAndCloneUpload: true,
170+
withDownload: true,
170171
currencies: (this.dataset.currencies || '').split(',').filter(Boolean),
171172
acceptFileTypes: this.dataset.acceptFileTypes,
172173
showTourStartForm: this.dataset.showTourStartForm === 'true'

app/javascript/template_builder/builder.vue

Lines changed: 106 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,10 @@
175175
{{ t('save') }}
176176
</span>
177177
</button>
178-
<div class="dropdown dropdown-end">
178+
<div
179+
class="dropdown dropdown-end"
180+
:class="{ 'dropdown-open': isDownloading }"
181+
>
179182
<label
180183
tabindex="0"
181184
class="base-button !rounded-l-none !pl-1 !pr-2 !border-l-neutral-500"
@@ -209,6 +212,30 @@
209212
<span class="whitespace-nowrap">{{ t('preferences') }}</span>
210213
</a>
211214
</li>
215+
<li v-if="withDownload">
216+
<button
217+
class="flex space-x-2"
218+
:disabled="isDownloading"
219+
@click.stop.prevent="download"
220+
>
221+
<IconInnerShadowTop
222+
v-if="isDownloading"
223+
class="animate-spin w-6 h-6 flex-shrink-0"
224+
/>
225+
<IconDownload
226+
v-else
227+
class="w-6 h-6 flex-shrink-0"
228+
/>
229+
<span
230+
v-if="isDownloading"
231+
class="whitespace-nowrap"
232+
>{{ t('downloading_') }}</span>
233+
<span
234+
v-else
235+
class="whitespace-nowrap"
236+
>{{ t('download') }}</span>
237+
</button>
238+
</li>
212239
</ul>
213240
</div>
214241
</span>
@@ -457,6 +484,7 @@
457484
:show-tour-start-form="showTourStartForm"
458485
@add-field="addField"
459486
@set-draw="[drawField = $event.field, drawOption = $event.option]"
487+
@select-submitter="selectedSubmitter = $event"
460488
@set-draw-type="[drawFieldType = $event, showDrawField = true]"
461489
@set-drag="dragField = $event"
462490
@set-drag-placeholder="$refs.dragPlaceholder.dragPlaceholder = $event"
@@ -511,7 +539,7 @@ import DocumentPreview from './preview'
511539
import DocumentControls from './controls'
512540
import MobileFields from './mobile_fields'
513541
import FieldSubmitter from './field_submitter'
514-
import { IconPlus, IconUsersPlus, IconDeviceFloppy, IconChevronDown, IconEye, IconWritingSign, IconInnerShadowTop, IconInfoCircle, IconAdjustments } from '@tabler/icons-vue'
542+
import { IconPlus, IconUsersPlus, IconDeviceFloppy, IconChevronDown, IconEye, IconWritingSign, IconInnerShadowTop, IconInfoCircle, IconAdjustments, IconDownload } from '@tabler/icons-vue'
515543
import { v4 } from 'uuid'
516544
import { ref, computed, toRaw } from 'vue'
517545
import * as i18n from './i18n'
@@ -537,6 +565,7 @@ export default {
537565
Contenteditable,
538566
IconUsersPlus,
539567
IconChevronDown,
568+
IconDownload,
540569
IconAdjustments,
541570
IconEye,
542571
IconDeviceFloppy
@@ -584,6 +613,11 @@ export default {
584613
required: false,
585614
default: null
586615
},
616+
withDownload: {
617+
type: Boolean,
618+
required: false,
619+
default: false
620+
},
587621
backgroundColor: {
588622
type: String,
589623
required: false,
@@ -805,6 +839,7 @@ export default {
805839
return {
806840
documentRefs: [],
807841
isBreakpointLg: false,
842+
isDownloading: false,
808843
isLoadingBlankPage: false,
809844
isSaving: false,
810845
selectedSubmitter: null,
@@ -963,6 +998,75 @@ export default {
963998
},
964999
methods: {
9651000
toRaw,
1001+
download () {
1002+
this.isDownloading = true
1003+
1004+
this.baseFetch(`/templates/${this.template.id}/documents`).then(async (response) => {
1005+
if (response.ok) {
1006+
const urls = await response.json()
1007+
const isMobileSafariIos = 'ontouchstart' in window && navigator.maxTouchPoints > 0 && /AppleWebKit/i.test(navigator.userAgent)
1008+
const isSafariIos = isMobileSafariIos || /iPhone|iPad|iPod/i.test(navigator.userAgent)
1009+
1010+
if (isSafariIos && urls.length > 1) {
1011+
this.downloadSafariIos(urls)
1012+
} else {
1013+
this.downloadUrls(urls)
1014+
}
1015+
} else {
1016+
alert(this.t('failed_to_download_files'))
1017+
}
1018+
})
1019+
},
1020+
downloadUrls (urls) {
1021+
const fileRequests = urls.map((url) => {
1022+
return () => {
1023+
return fetch(url).then(async (resp) => {
1024+
const blobUrl = URL.createObjectURL(await resp.blob())
1025+
const link = document.createElement('a')
1026+
1027+
link.href = blobUrl
1028+
link.setAttribute('download', decodeURI(url.split('/').pop()))
1029+
1030+
link.click()
1031+
1032+
URL.revokeObjectURL(blobUrl)
1033+
})
1034+
}
1035+
})
1036+
1037+
fileRequests.reduce(
1038+
(prevPromise, request) => prevPromise.then(() => request()),
1039+
Promise.resolve()
1040+
).finally(() => {
1041+
this.isDownloading = false
1042+
})
1043+
},
1044+
downloadSafariIos (urls) {
1045+
const fileRequests = urls.map((url) => {
1046+
return fetch(url).then(async (resp) => {
1047+
const blob = await resp.blob()
1048+
const blobUrl = URL.createObjectURL(blob.slice(0, blob.size, 'application/octet-stream'))
1049+
const link = document.createElement('a')
1050+
1051+
link.href = blobUrl
1052+
link.setAttribute('download', decodeURI(url.split('/').pop()))
1053+
1054+
return link
1055+
})
1056+
})
1057+
1058+
Promise.all(fileRequests).then((links) => {
1059+
links.forEach((link, index) => {
1060+
setTimeout(() => {
1061+
link.click()
1062+
1063+
URL.revokeObjectURL(link.href)
1064+
}, index * 50)
1065+
})
1066+
}).finally(() => {
1067+
this.isDownloading = false
1068+
})
1069+
},
9661070
onDragover (e) {
9671071
if (this.$refs.dragPlaceholder?.dragPlaceholder) {
9681072
this.$refs.dragPlaceholder.isMask = e.target.id === 'mask'

app/javascript/template_builder/fields.vue

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,16 @@
222222
width="22"
223223
class="animate-spin"
224224
/>
225-
<span class="hidden md:inline">
225+
<span
226+
v-if="analyzingProgress"
227+
class="hidden md:inline"
228+
>
229+
{{ Math.round(analyzingProgress * 100) }}% {{ t('analyzing_') }}
230+
</span>
231+
<span
232+
v-else
233+
class="hidden md:inline"
234+
>
226235
{{ fieldPagesLoaded }} / {{ numberOfPages }} {{ t('processing_') }}
227236
</span>
228237
</template>
@@ -363,10 +372,11 @@ export default {
363372
default: false
364373
}
365374
},
366-
emits: ['add-field', 'set-draw', 'set-draw-type', 'set-drag', 'drag-end', 'scroll-to-area', 'change-submitter', 'set-drag-placeholder'],
375+
emits: ['add-field', 'set-draw', 'set-draw-type', 'set-drag', 'drag-end', 'scroll-to-area', 'change-submitter', 'set-drag-placeholder', 'select-submitter'],
367376
data () {
368377
return {
369378
fieldPagesLoaded: null,
379+
analyzingProgress: 0,
370380
defaultFieldsSearch: ''
371381
}
372382
},
@@ -448,8 +458,6 @@ export default {
448458
while (true) {
449459
const { value, done } = await reader.read()
450460
451-
if (done) break
452-
453461
buffer += decoder.decode(value, { stream: true })
454462
455463
const lines = buffer.split('\n\n')
@@ -464,10 +472,21 @@ export default {
464472
if (data.error) {
465473
alert(data.error)
466474
475+
this.template.fields = data.fields || fields
476+
467477
break
478+
} else if (data.analyzing) {
479+
this.analyzingProgress = data.progress
468480
} else if (data.completed) {
469481
this.fieldPagesLoaded = null
470-
this.template.fields = fields
482+
483+
if (data.submitters) {
484+
this.template.submitters = data.submitters
485+
this.$emit('select-submitter', this.template.submitters[0])
486+
}
487+
488+
this.template.fields = data.fields || fields
489+
471490
this.save()
472491
473492
break
@@ -484,11 +503,14 @@ export default {
484503
}
485504
}
486505
}
506+
507+
if (done) break
487508
}
488509
}).catch(error => {
489510
console.error('Error in streaming message: ', error)
490511
}).finally(() => {
491512
this.fieldPagesLoaded = null
513+
this.analyzingProgress = null
492514
this.isFieldsLoading = false
493515
})
494516
},

app/javascript/template_builder/i18n.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
const en = {
2+
analyzing_: 'Analyzing...',
3+
download: 'Download',
4+
downloading_: 'Downloading...',
25
view: 'View',
36
autodetect_fields: 'Autodetect fields',
47
payment_link: 'Payment link',
@@ -185,6 +188,9 @@ const en = {
185188
}
186189

187190
const es = {
191+
analyzing_: 'Analizando...',
192+
download: 'Descargar',
193+
downloading_: 'Descargando...',
188194
view: 'Vista',
189195
payment_link: 'Enlace de pago',
190196
strikeout: 'Tachar',
@@ -370,6 +376,9 @@ const es = {
370376
}
371377

372378
const it = {
379+
analyzing_: 'Analisi...',
380+
download: 'Scarica',
381+
downloading_: 'Download in corso...',
373382
view: 'Vista',
374383
payment_link: 'Link di pagamento',
375384
strikeout: 'Barrato',
@@ -555,6 +564,9 @@ const it = {
555564
}
556565

557566
const pt = {
567+
analyzing_: 'Analisando...',
568+
download: 'Baixar',
569+
downloading_: 'Baixando...',
558570
view: 'Visualizar',
559571
payment_link: 'Link de pagamento',
560572
strikeout: 'Tachado',
@@ -740,6 +752,9 @@ const pt = {
740752
}
741753

742754
const fr = {
755+
analyzing_: 'Analyse...',
756+
download: 'Télécharger',
757+
downloading_: 'Téléchargement...',
743758
view: 'Voir',
744759
payment_link: 'Lien de paiement',
745760
strikeout: 'Rature',
@@ -925,6 +940,9 @@ const fr = {
925940
}
926941

927942
const de = {
943+
analyzing_: 'Analysiere...',
944+
download: 'Download',
945+
downloading_: 'Download...',
928946
view: 'Anzeigen',
929947
payment_link: 'Zahlungslink',
930948
strikeout: 'Durchstreichen',
@@ -1110,6 +1128,9 @@ const de = {
11101128
}
11111129

11121130
const nl = {
1131+
analyzing_: 'Analyseren...',
1132+
download: 'Downloaden',
1133+
downloading_: 'Downloaden...',
11131134
view: 'Bekijken',
11141135
payment_link: 'Betaallink',
11151136
strikeout: 'Doorhalen',

app/views/submissions/show.html.erb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@
8181
<% document = @submission.schema_documents.find { |a| item['attachment_uuid'] == a.uuid } %>
8282
<% if document.preview_images.first %>
8383
<scroll-to data-selector-id="page-<%= document.uuid %>-0" class="block cursor-pointer">
84-
<img src="<%= Docuseal::URL_CACHE.fetch([document.id, document.uuid, 0].join(':'), expires_in: 10.minutes) { document.preview_images.first.url } %>" width="<%= document.preview_images.first.metadata['width'] %>" height="<%= document.preview_images.first.metadata['height'] %>" class="rounded border" loading="lazy">
84+
<img src="<%= (document.preview_images.find { |e| e.filename.base.to_i.zero? } || document.preview_images.first).url %>" width="<%= document.preview_images.first.metadata['width'] %>" height="<%= document.preview_images.first.metadata['height'] %>" class="rounded border" loading="lazy">
8585
<div class="pb-2 pt-1.5 text-center" dir="auto">
8686
<%= item['name'].presence || document.filename.base %>
8787
</div>
@@ -103,7 +103,7 @@
103103
<% (document.metadata.dig('pdf', 'number_of_pages') || (document.preview_images.loaded? ? preview_images_index.size : document.preview_images.size)).times do |index| %>
104104
<% page = preview_images_index[index] || page_blob_struct.new(metadata: lazyload_metadata, url: preview_document_page_path(document.signed_uuid, "#{index}.jpg")) %>
105105
<page-container id="<%= "page-#{document.uuid}-#{index}" %>" class="block before:border before:absolute before:top-0 before:bottom-0 before:left-0 before:right-0 before:rounded relative mb-4" style="container-type: size; aspect-ratio: <%= width = page.metadata['width'] %> / <%= height = page.metadata['height'] %>">
106-
<img loading="lazy" src="<%= Docuseal::URL_CACHE.fetch([document.id, document.uuid, index].join(':'), expires_in: 10.minutes) { page.url } %>" width="<%= width %>" class="rounded" height="<%= height %>">
106+
<img loading="lazy" src="<%= page.url %>" width="<%= width %>" class="rounded" height="<%= height %>">
107107
<div class="top-0 bottom-0 left-0 right-0 absolute">
108108
<% document_annots_index[index]&.each do |annot| %>
109109
<%= render 'submissions/annotation', annot: %>

config/routes.rb

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,11 +98,9 @@
9898
resources :submissions_filters, only: %i[show], param: 'name'
9999
resources :templates, only: %i[new create edit update show destroy] do
100100
resource :debug, only: %i[show], controller: 'templates_debug' if Rails.env.development?
101-
resources :documents, only: %i[create], controller: 'template_documents'
101+
resources :documents, only: %i[index create], controller: 'template_documents'
102102
resources :clone_and_replace, only: %i[create], controller: 'templates_clone_and_replace'
103-
if !Docuseal.multitenant? || Docuseal.demo?
104-
resources :detect_fields, only: %i[create], controller: 'templates_detect_fields'
105-
end
103+
resources :detect_fields, only: %i[create], controller: 'templates_detect_fields' unless Docuseal.multitenant?
106104
resources :restore, only: %i[create], controller: 'templates_restore'
107105
resources :archived, only: %i[index], controller: 'templates_archived_submissions'
108106
resources :submissions, only: %i[new create]

lib/puma/plugin/sidekiq_embed.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ def start_sidekiq!
3939
configs = Sidekiq.configure_embed do |config|
4040
config.logger.level = Logger::INFO
4141
sidekiq_config = YAML.load_file('config/sidekiq.yml')
42+
sidekiq_config['queues'] << 'fields' if ENV['DEMO'] == 'true'
4243
config.queues = sidekiq_config['queues']
4344
config.concurrency = ENV.fetch('SIDEKIQ_THREADS', 5).to_i
4445
config.merge!(sidekiq_config)

0 commit comments

Comments
 (0)