Merge from docusealco/wip

This commit is contained in:
Alex Turchyn authored and GitHub committed 2026-10-19 14:57:12 +03:00
commit 8f8f69f0bc
22 files changed
+90 -34

No files matched your search

+4 -2
View File
@@ -52,7 +52,7 @@ ENV OPENSSL_CONF=/etc/openssl_legacy.cnf
WORKDIR /app
RUN apk add --no-cache libpq vips redis onnxruntime leptonica && \
RUN apk add --no-cache libpq vips redis onnxruntime leptonica jemalloc font-liberation font-carlito && \
rm -f /usr/bin/onnx_test_runner /usr/bin/onnxruntime_test
RUN addgroup -g 2000 docuseal && adduser -u 2000 -G docuseal -s /bin/sh -D -h /home/docuseal docuseal
@@ -91,7 +91,7 @@ COPY --chown=docuseal:docuseal --from=download /model.onnx /app/tmp/model.onnx
COPY --chown=docuseal:docuseal --from=webpack /app/public/packs ./public/packs
RUN mkdir -p /app/public/fonts && ln -s /fonts/DancingScript-Regular.otf /app/public/fonts/ && \
mkdir -p /usr/share/fonts/noto && ln -s /fonts/GoNotoKurrent-Regular.ttf /usr/share/fonts/noto/ && ln -s /fonts/GoNotoKurrent-Bold.ttf /usr/share/fonts/noto/ && fc-cache -f && \
mkdir -p /usr/share/fonts/noto/bold && ln -s /fonts/GoNotoKurrent-Regular.ttf /usr/share/fonts/noto/ && ln -s /fonts/GoNotoKurrent-Bold.ttf /usr/share/fonts/noto/bold/ && fc-cache -f && \
bundle exec bootsnap precompile -j 1 --gemfile app/ lib/ && \
chown -R docuseal:docuseal /app/tmp/cache
@@ -100,6 +100,8 @@ ENV HOME=/home/docuseal
ENV WORKDIR=/data/docuseal
ENV VIPS_MAX_COORD=17000
ENV VIPS_BLOCK_UNTRUSTED=1
# ENV LD_PRELOAD=/usr/lib/libjemalloc.so.2
ENV MALLOC_CONF=dirty_decay_ms:1000,narenas:2,background_thread:true
EXPOSE 3000
CMD ["/app/bin/bundle", "exec", "puma", "-C", "/app/config/puma.rb", "--dir", "/app"]
+1
View File
@@ -45,6 +45,7 @@ gem 'trilogy', require: false
gem 'turbo-rails'
gem 'twitter_cldr', require: false
gem 'tzinfo-data'
gem 'wordcraft', require: false, group: :ce
gem 'xlsxtream', require: false
group :development, :test do
+11
View File
@@ -572,6 +572,16 @@ GEM
base64
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5)
wordcraft (0.8.0-aarch64-linux-gnu)
ffi (>= 1.15)
wordcraft (0.8.0-aarch64-linux-musl)
ffi (>= 1.15)
wordcraft (0.8.0-arm64-darwin)
ffi (>= 1.15)
wordcraft (0.8.0-x86_64-linux-gnu)
ffi (>= 1.15)
wordcraft (0.8.0-x86_64-linux-musl)
ffi (>= 1.15)
xlsxtream (3.1.0)
zip_kit (>= 6.2, < 7)
xpath (3.2.0)
@@ -650,6 +660,7 @@ DEPENDENCIES
tzinfo-data
web-console
webmock
wordcraft
xlsxtream
RUBY VERSION
@@ -38,7 +38,7 @@ module Api
attachment = Submitters.create_attachment!(@submitter, file, metadata:)
if params[:remember_signature] == 'true' && @submitter.email.present?
cookies.encrypted[:signature_uuids] = build_new_cookie_signatures_json(@submitter, attachment)
cookies.permanent.encrypted[:signature_uuids] = build_new_cookie_signatures_json(@submitter, attachment)
end
render json: attachment.as_json(only: %i[uuid created_at], methods: %i[url filename content_type])
@@ -34,7 +34,7 @@ class SubmitFormUploadController < ActionController::API
attachment = Submitters.create_attachment!(@submitter, file, metadata:)
if params[:remember_signature] == 'true' && @submitter.email.present?
cookies.encrypted[:signature_uuids] = build_new_cookie_signatures_json(@submitter, attachment)
cookies.permanent.encrypted[:signature_uuids] = build_new_cookie_signatures_json(@submitter, attachment)
end
render json: attachment.as_json(only: %i[uuid created_at], methods: %i[url filename content_type])
@@ -8,7 +8,9 @@ class TemplateSharingsTestingController < ApplicationController
end
def create
testing_account = Accounts.find_or_create_testing_user(true_user.account).account
account = can?(:manage, :tenants_test) ? current_account : true_user.account
testing_account = Accounts.find_or_create_testing_user(account).account
if params[:value] == '1'
TemplateSharing.create!(ability: :manage, account: testing_account, template: @template)
+1 -1
View File
@@ -22,7 +22,7 @@ export default class extends HTMLElement {
}
async flashFrom (fetchResponse) {
const html = await fetchResponse?.responseHTML
const html = await fetchResponse?.responseHTML.catch(() => null)
const flash = html && new DOMParser().parseFromString(html, 'text/html').getElementById('native_flash')
if (flash?.dataset.alert) return { alert: flash.dataset.alert }
+11 -2
View File
@@ -3,11 +3,19 @@
v-if="!error"
class="label text-xl sm:text-2xl py-0 mb-2 sm:mb-3.5 field-name-label"
>
<span class="flex items-end">
<MarkdownContent
v-if="field.title"
:string="field.title"
/>
<template v-else>{{ field.name || 'Knowledge Based Authentication' }}</template>
<span
class="tooltip tooltip-bottom ml-2 mb-1"
data-tip="To verify your identity, the details you enter are checked against third-party records, which may include credit bureau data, and used to generate questions based on those records. Only these identity details are shared with the identity verification provider; this document and your tax return information are not shared with it or with any credit reporting company. If the check is recorded on your credit report, it appears as a soft inquiry, which does not affect your credit score and is not shown to lenders. This check does not give the sender of this document or the IRS access to your credit report."
>
<IconInfoCircle class="w-4 h-4 text-neutral-500" />
</span>
</span>
<span
v-if="questions"
class="float-right font-normal text-neutral-500 mt-1 whitespace-nowrap text-right text-sm"
@@ -282,13 +290,14 @@
<script>
import MarkdownContent from './markdown_content'
import { IconInnerShadowTop } from '@tabler/icons-vue'
import { IconInnerShadowTop, IconInfoCircle } from '@tabler/icons-vue'
export default {
name: 'KbaStep',
components: {
MarkdownContent,
IconInnerShadowTop
IconInnerShadowTop,
IconInfoCircle
},
inject: ['baseUrl', 't'],
props: {
@@ -27,13 +27,17 @@ tiptapStylesheet.replaceSync(
font-feature-settings: "liga" 0;
display: flex;
flex-flow: column nowrap;
min-height: inherit;
flex-grow: 1;
}
.ProseMirror > article {
.ProseMirror > article:last-of-type {
margin-bottom: auto;
}
.ProseMirror p.doc-trailing-break::after {
content: none;
}
.ProseMirror [contenteditable="false"] {
white-space: normal;
}
+2 -2
View File
@@ -22,13 +22,13 @@
<% end %>
<% if can?(:create, ::Template) && controller_name.in?(%w[templates_dashboard submissions_dashboard template_folders templates_archived submissions_archived]) %>
<% folder_name = @template_folder&.full_name %>
<native-action data-placement="create" data-icon="upload" data-native="upload" data-label="<%= t('upload') %>" data-url="<%= templates_upload_path(folder_name: folder_name) %>" data-accept="image/*, application/pdf, application/zip, application/json<%= ", #{Templates::CreateAttachments::DOCUMENT_EXTENSIONS.join(', ')}" if Docuseal.advanced_formats? %>"></native-action>
<native-action data-placement="create" data-icon="upload" data-native="upload" data-label="<%= t('upload') %>" data-url="<%= templates_upload_path(folder_name: folder_name) %>" data-accept="image/*, application/pdf, application/zip, application/json, <%= Templates::CreateAttachments.document_extensions.join(', ') %>"></native-action>
<native-action data-placement="create" data-icon="scan" data-native="scan" data-label="<%= t('scan') %>" data-url="<%= templates_upload_path(folder_name: folder_name) %>"></native-action>
<native-action data-placement="create" data-icon="pencil_plus">
<%= link_to t('create'), new_template_path(folder_name: folder_name), data: { turbo_frame: :modal, native_modal: :medium } %>
</native-action>
<% unless controller_name.in?(%w[templates_archived submissions_archived]) %>
<native-action data-placement="menu" data-menu="<%= t('new') %>" data-menu-icon="pencil_plus" data-icon="upload" data-native="upload" data-label="<%= t('upload') %>" data-url="<%= templates_upload_path(folder_name: folder_name) %>" data-accept="image/*, application/pdf, application/zip, application/json<%= ", #{Templates::CreateAttachments::DOCUMENT_EXTENSIONS.join(', ')}" if Docuseal.advanced_formats? %>"></native-action>
<native-action data-placement="menu" data-menu="<%= t('new') %>" data-menu-icon="pencil_plus" data-icon="upload" data-native="upload" data-label="<%= t('upload') %>" data-url="<%= templates_upload_path(folder_name: folder_name) %>" data-accept="image/*, application/pdf, application/zip, application/json, <%= Templates::CreateAttachments.document_extensions.join(', ') %>"></native-action>
<native-action data-placement="menu" data-menu="<%= t('new') %>" data-menu-icon="pencil_plus" data-icon="scan" data-native="scan" data-label="<%= t('scan') %>" data-url="<%= templates_upload_path(folder_name: folder_name) %>"></native-action>
<native-action data-placement="menu" data-menu="<%= t('new') %>" data-menu-icon="pencil_plus" data-icon="pencil_plus">
<%= link_to t('create'), new_template_path(folder_name: folder_name), data: { turbo_frame: :modal, native_modal: :medium } %>
+2 -2
View File
@@ -93,14 +93,14 @@
</li>
<% end %>
<% if !Docuseal.demo? && can?(:manage, EncryptedConfig) && (current_user == true_user || current_account.testing?) %>
<% if Docuseal.multitenant? || !turbo_native_ios? %>
<% unless turbo_native_app? %>
<li>
<%= link_to Docuseal.multitenant? ? console_redirect_index_path(redir: "#{Docuseal::CONSOLE_URL}#{'/test' if current_account.testing?}/api") : "#{Docuseal::CONSOLE_URL}/on_premises", class: 'text-base hover:bg-base-300', data: { turbo: false } do %>
<% if Docuseal.multitenant? %> API <% else %> <%= t('console') %> <% end %>
<% end %>
</li>
<% end %>
<% if Docuseal.multitenant? %>
<% if Docuseal.multitenant? && !turbo_native_app? %>
<li>
<%= link_to console_redirect_index_path(redir: "#{Docuseal::CONSOLE_URL}#{'/test' if current_account.testing?}/embedding/form"), class: 'text-base hover:bg-base-300', data: { turbo: false } do %>
<%= t('embedding') %>
+11 -11
View File
@@ -227,39 +227,39 @@
</div>
<% if submitter&.name.present? %>
<div class="flex items-center space-x-1 mt-1">
<%= svg_icon('user', class: 'w-5 h-5') %>
<span>
<%= svg_icon('user', class: 'w-5 h-5 shrink-0') %>
<span class="truncate">
<%= submitter&.name %>
</span>
</div>
<% end %>
<% if submitter&.email.present? %>
<div class="flex items-center space-x-1 mt-1">
<%= svg_icon('mail', class: 'w-5 h-5') %>
<span>
<%= svg_icon('mail', class: 'w-5 h-5 shrink-0') %>
<span class="truncate">
<%= submitter.email || 'N/A' %>
</span>
</div>
<% end %>
<% if submitter&.phone.present? %>
<div class="flex items-center space-x-1 mt-1">
<%= svg_icon('phone', class: 'w-5 h-5') %>
<span>
<%= svg_icon('phone', class: 'w-5 h-5 shrink-0') %>
<span class="truncate">
<%= submitter.phone %>
</span>
</div>
<% end %>
<div class="flex items-center space-x-1 mt-1">
<% if submitter&.viewer? %>
<%= svg_icon(submitter.opened_at? ? 'file_check' : 'file_unknown', class: 'w-5 h-5') %>
<%= svg_icon(submitter.opened_at? ? 'file_check' : 'file_unknown', class: 'w-5 h-5 shrink-0') %>
<% elsif submitter && !submitter.completed_at? && @submission.completed_at? %>
<%= svg_icon('writing_off', class: 'w-5 h-5') %>
<%= svg_icon('writing_off', class: 'w-5 h-5 shrink-0') %>
<% elsif @submission.expire_at? && submitter && !submitter.completed_at? %>
<%= svg_icon('clock_exclamation', class: 'w-5 h-5') %>
<%= svg_icon('clock_exclamation', class: 'w-5 h-5 shrink-0') %>
<% else %>
<%= svg_icon('writing', class: 'w-5 h-5') %>
<%= svg_icon('writing', class: 'w-5 h-5 shrink-0') %>
<% end %>
<span>
<span class="truncate">
<% if submitter&.declined_at? %>
<%= t('declined_on_time', time: l(submitter.declined_at.in_time_zone(@submission.account.timezone), format: :short, locale: @submission.account.locale)) %>
<% elsif submitter&.viewer? %>
+1 -1
View File
@@ -31,7 +31,7 @@
</div>
</span>
</div>
<input id="file_dropzone_input" name="files[]" class="hidden" data-action="change:file-dropzone#onSelectFiles" data-target="file-dropzone.input" type="file" accept="image/*, application/pdf, application/zip, application/json<%= ", #{Templates::CreateAttachments::DOCUMENT_EXTENSIONS.join(', ')}" if Docuseal.advanced_formats? %>" multiple>
<input id="file_dropzone_input" name="files[]" class="hidden" data-action="change:file-dropzone#onSelectFiles" data-target="file-dropzone.input" type="file" accept="image/*, application/pdf, application/zip, application/json, <%= Templates::CreateAttachments.document_extensions.join(', ') %>" multiple>
</div>
</label>
</file-dropzone>
+1 -1
View File
@@ -16,7 +16,7 @@
</label>
<input type="hidden" name="form_id" value="<%= form_id %>">
<convert-upload>
<input id="upload_template" name="files[]" class="hidden" type="file" accept="image/*, application/pdf, application/zip, application/json<%= ", #{Templates::CreateAttachments::DOCUMENT_EXTENSIONS.join(', ')}" if Docuseal.advanced_formats? %>" multiple>
<input id="upload_template" name="files[]" class="hidden" type="file" accept="image/*, application/pdf, application/zip, application/json, <%= Templates::CreateAttachments.document_extensions.join(', ') %>" multiple>
</convert-upload>
<input hidden name="folder_name" value="<%= local_assigns[:folder_name] %>">
<% end %>
+1 -1
View File
@@ -10,4 +10,4 @@
<% account_configs = current_account.account_configs.where(key: [AccountConfig::TEMPLATE_CUSTOM_FIELDS_KEY, AccountConfig::TEMPLATE_DATE_FORMATS_KEY]).to_a %>
<% custom_fields = account_configs.find { |e| e.key == AccountConfig::TEMPLATE_CUSTOM_FIELDS_KEY }&.value || [] %>
<% date_formats = account_configs.find { |e| e.key == AccountConfig::TEMPLATE_DATE_FORMATS_KEY }&.value || [] %>
<template-builder data-native-platform="<%= turbo_native_ios? ? 'ios' : (turbo_native_android? ? 'android' : '') %>" class="grid" data-template="<%= @template_data.to_json %>" data-custom-fields="<%= custom_fields.to_json %>" data-date-formats="<%= date_formats.to_json %>" data-with-sign-yourself-button="<%= !@template.archived_at? %>" data-with-fields-detection="true" data-with-send-button="<%= !@template.archived_at? && can?(:create, @template.submissions.new(account: current_account)) %>" data-with-revisions-menu="<%= @template.template_versions.exists? %>" data-locale="<%= I18n.locale %>" data-show-tour-start-form="<%= @show_tour_start_form %>"></template-builder>
<template-builder data-native-platform="<%= turbo_native_ios? ? 'ios' : (turbo_native_android? ? 'android' : '') %>" data-accept-file-types="image/*, application/pdf, application/zip, application/json, <%= Templates::CreateAttachments.document_extensions.join(', ') %>" class="grid" data-template="<%= @template_data.to_json %>" data-custom-fields="<%= custom_fields.to_json %>" data-date-formats="<%= date_formats.to_json %>" data-with-sign-yourself-button="<%= !@template.archived_at? %>" data-with-fields-detection="true" data-with-send-button="<%= !@template.archived_at? && can?(:create, @template.submissions.new(account: current_account)) %>" data-with-revisions-menu="<%= @template.template_versions.exists? %>" data-locale="<%= I18n.locale %>" data-show-tour-start-form="<%= @show_tour_start_form %>"></template-builder>
+1 -1
View File
@@ -26,7 +26,7 @@
<%= t('share_template_with_test_mode') %>
</span>
<submit-form data-on="change">
<%= f.check_box :value, class: 'toggle', checked: @template.template_sharings.exists?(account_id: true_user.account.testing_accounts) %>
<%= f.check_box :value, class: 'toggle', checked: @template.template_sharings.exists?(account_id: (can?(:manage, :tenants_test) ? current_account : true_user.account).testing_accounts) %>
</submit-form>
</div>
<% end %>
+1 -1
View File
@@ -127,4 +127,4 @@
<%= render 'shared/pagination', pagy: @related_submissions_pagy, items_name: 'submissions', next_page_path: submissions_path(q: params[:q]) %>
<% end %>
<% end %>
<%= render 'shared/review_form' %>
<%= render 'shared/review_form' unless turbo_native_app? %>
@@ -194,7 +194,7 @@
<%= t('share_template_with_test_mode') %>
</span>
<submit-form data-on="change" class="flex">
<%= f.check_box :value, class: 'toggle', checked: @template.template_sharings.exists?(account_id: true_user.account.testing_accounts) %>
<%= f.check_box :value, class: 'toggle', checked: @template.template_sharings.exists?(account_id: (can?(:manage, :tenants_test) ? current_account : true_user.account).testing_accounts) %>
</submit-form>
</div>
<% end %>
+1
View File
@@ -9,3 +9,4 @@ autoload :OnnxRuntime, 'onnxruntime'
autoload :RQRCode, 'rqrcode'
autoload :ArabicLetterConnector, 'arabic-letter-connector/logic'
autoload :JWT, 'jwt'
autoload :WordCraft, 'wordcraft'
+6 -3
View File
@@ -267,7 +267,10 @@ module Submissions
is_id_verified = submission_events.any?(&:complete_verification?)
is_kba_passed = submission_events.any?(&:complete_kba?)
complete_kba_event = submission_events.find(&:complete_kba?) || SubmissionEvent.new
kba_answers = complete_kba_event.data['answers']
kba_score = " (#{kba_answers['correct']}/#{kba_answers['asked']})" if kba_answers
info_rows = [
[
@@ -297,8 +300,8 @@ module Submissions
is_id_verified && {
text: "#{I18n.t('identity_verification')}: #{I18n.t('verified')}\n"
},
is_kba_passed && {
text: "#{I18n.t('knowledge_based_authentication')}: #{I18n.t('passed')}\n"
complete_kba_event.persisted? && {
text: "#{I18n.t('knowledge_based_authentication')}: #{I18n.t('passed')}#{kba_score}\n"
},
completed_event.data['ip'] && { text: "IP: #{completed_event.data['ip']}\n" },
completed_event.data['sid'] && { text: "#{I18n.t('session_id')}: #{completed_event.data['sid']}\n" },
@@ -602,6 +602,8 @@ module Submissions
cell_valign = field.dig('preferences', 'valign').to_s.presence_in(TEXT_VALIGNS) || 'center'
cell_layouter = cell_layouters[cell_valign]
value = value.to_s
if (mask = field.dig('preferences', 'mask').presence)
value = TextUtils.mask_value(value, mask)
end
+21
View File
@@ -6,6 +6,7 @@ module Templates
ZIP_CONTENT_TYPE = 'application/zip'
X_ZIP_CONTENT_TYPE = 'application/x-zip-compressed'
JSON_CONTENT_TYPE = 'application/json'
WORD_EXTENSIONS = %w[.docx .doc .rtf].freeze
DOCUMENT_EXTENSIONS = %w[.docx .doc .xlsx .xls .odt .rtf].freeze
DOCUMENT_CONTENT_TYPES = %w[
@@ -134,7 +135,27 @@ module Templates
return [handle_pdf_or_image(template, file, file.read, params, extract_fields:), []]
end
if File.extname(file.original_filename.to_s).downcase.in?(WORD_EXTENSIONS)
return [handle_word_document(template, file, params), []]
end
raise InvalidFileType, "#{file.content_type}/#{dynamic}"
end
def handle_word_document(template, file, params)
pdf = WordCraft.open_buffer(file, &:to_pdf)
pdf_file = ActionDispatch::Http::UploadedFile.new(
tempfile: StringIO.new(pdf),
filename: "#{File.basename(file.original_filename, '.*')}.pdf",
type: PDF_CONTENT_TYPE
)
handle_pdf_or_image(template, pdf_file, pdf, params)
end
def document_extensions
Docuseal.advanced_formats? ? DOCUMENT_EXTENSIONS : WORD_EXTENSIONS
end
end
end