| @ -0,0 +1,4 @@ | |||||||
|  | # frozen_string_literal: true | ||||||
|  | 
 | ||||||
|  | class PwaController < ActionController::Base | ||||||
|  | end | ||||||
| @ -0,0 +1,25 @@ | |||||||
|  | # frozen_string_literal: true | ||||||
|  | 
 | ||||||
|  | class SubmissionEventsController < ApplicationController | ||||||
|  |   SUBMISSION_EVENT_ICONS = { | ||||||
|  |     'view_form' => 'eye', | ||||||
|  |     'start_form' => 'player_play', | ||||||
|  |     'complete_form' => 'check', | ||||||
|  |     'send_email' => 'mail_forward', | ||||||
|  |     'click_email' => 'hand_click', | ||||||
|  |     'api_complete_form' => 'check', | ||||||
|  |     'send_reminder_email' => 'mail_forward', | ||||||
|  |     'send_2fa_sms' => '2fa', | ||||||
|  |     'send_sms' => 'send', | ||||||
|  |     'phone_verified' => 'phone_check', | ||||||
|  |     'click_sms' => 'hand_click', | ||||||
|  |     'decline_form' => 'x', | ||||||
|  |     'start_verification' => 'player_play', | ||||||
|  |     'complete_verification' => 'check', | ||||||
|  |     'invite_party' => 'user_plus' | ||||||
|  |   }.freeze | ||||||
|  | 
 | ||||||
|  |   load_and_authorize_resource :submission | ||||||
|  | 
 | ||||||
|  |   def index; end | ||||||
|  | end | ||||||
| @ -0,0 +1,58 @@ | |||||||
|  | # frozen_string_literal: true | ||||||
|  | 
 | ||||||
|  | class SubmittersResubmitController < ApplicationController | ||||||
|  |   load_and_authorize_resource :submitter, parent: false | ||||||
|  | 
 | ||||||
|  |   def update | ||||||
|  |     return redirect_to submit_form_path(slug: @submitter.slug) if @submitter.email != current_user.email | ||||||
|  | 
 | ||||||
|  |     submission = @submitter.template.submissions.new(created_by_user: current_user, | ||||||
|  |                                                      submitters_order: :preserved, | ||||||
|  |                                                      **@submitter.submission.slice(:template_fields, | ||||||
|  |                                                                                    :account_id, | ||||||
|  |                                                                                    :template_schema, | ||||||
|  |                                                                                    :template_submitters, | ||||||
|  |                                                                                    :preferences)) | ||||||
|  | 
 | ||||||
|  |     @submitter.submission.submitters.each do |submitter| | ||||||
|  |       new_submitter = submission.submitters.new(submitter.slice(:uuid, :email, :phone, :name, | ||||||
|  |                                                                 :preferences, :metadata, :account_id)) | ||||||
|  | 
 | ||||||
|  |       next unless submitter.uuid == @submitter.uuid | ||||||
|  | 
 | ||||||
|  |       assign_submitter_values(new_submitter, submitter) | ||||||
|  | 
 | ||||||
|  |       @new_submitter ||= new_submitter | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     submission.save! | ||||||
|  | 
 | ||||||
|  |     redirect_to submit_form_path(slug: @new_submitter.slug) | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   private | ||||||
|  | 
 | ||||||
|  |   def assign_submitter_values(new_submitter, submitter) | ||||||
|  |     attachments_index = submitter.attachments.index_by(&:uuid) | ||||||
|  | 
 | ||||||
|  |     submitter.submission.template_fields.each do |field| | ||||||
|  |       next if field['submitter_uuid'] != submitter.uuid | ||||||
|  |       next if field['default_value'] == '{{date}}' | ||||||
|  |       next if field['type'] == 'stamp' | ||||||
|  |       next if field['type'] == 'signature' | ||||||
|  |       next if field.dig('preferences', 'formula').present? | ||||||
|  | 
 | ||||||
|  |       value = submitter.values[field['uuid']] | ||||||
|  | 
 | ||||||
|  |       next if value.blank? | ||||||
|  | 
 | ||||||
|  |       if field['type'].in?(%w[image file initials]) | ||||||
|  |         Array.wrap(value).each do |attachment_uuid| | ||||||
|  |           new_submitter.attachments << attachments_index[attachment_uuid].dup | ||||||
|  |         end | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       new_submitter.values[field['uuid']] = value | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
| After Width: | Height: | Size: 426 B | 
| After Width: | Height: | Size: 397 B | 
| After Width: | Height: | Size: 711 B | 
| After Width: | Height: | Size: 484 B | 
| After Width: | Height: | Size: 401 B | 
| After Width: | Height: | Size: 382 B | 
| After Width: | Height: | Size: 261 B | 
| @ -0,0 +1,25 @@ | |||||||
|  | { | ||||||
|  |   "name": "<%= Docuseal.product_name %>", | ||||||
|  |   "short_name": "<%= Docuseal.product_name %>", | ||||||
|  |   "id": "/", | ||||||
|  |   "icons": [ | ||||||
|  |     { | ||||||
|  |       "src": "/logo.svg", | ||||||
|  |       "type": "image/svg+xml", | ||||||
|  |       "sizes": "any" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "src": "/apple-touch-icon.png", | ||||||
|  |       "type": "image/png", | ||||||
|  |       "sizes": "192x192" | ||||||
|  |     } | ||||||
|  |   ], | ||||||
|  |   "start_url": "/", | ||||||
|  |   "display": "standalone", | ||||||
|  |   "scope": "/", | ||||||
|  |   "orientation": "natural", | ||||||
|  |   "description": "<%= Docuseal.product_name %> is an open source platform that provides secure and efficient digital document signing and processing.", | ||||||
|  |   "categories": ["productivity", "utilities"], | ||||||
|  |   "theme_color": "#FAF7F4", | ||||||
|  |   "background_color": "#FAF7F4" | ||||||
|  | } | ||||||
| @ -0,0 +1,49 @@ | |||||||
|  | <% event_colors = %w[bg-red-200 bg-sky-200 bg-emerald-200 bg-yellow-200 bg-purple-200 bg-pink-200 bg-cyan-200 bg-orange-200 bg-lime-200 bg-indigo-200] %> | ||||||
|  | <% submitters_uuids = (@submission.template_submitters || @submission.template.submitters).pluck('uuid') %> | ||||||
|  | <%= render 'shared/turbo_modal_large', title: t('event_log') do %> | ||||||
|  |   <div class="pl-8 pr-4 py-4"> | ||||||
|  |     <ol class="relative border-s border-base-300 space-y-6"> | ||||||
|  |       <li class="ml-7"> | ||||||
|  |         <span class="absolute flex items-center justify-center w-7 h-7 rounded-full -start-3.5 ring-8 ring-base-100 text-base-content bg-gray-200"> | ||||||
|  |           <%= svg_icon('file_text', class: 'w-4 h-4') %> | ||||||
|  |         </span> | ||||||
|  |         <p class="text-sm leading-none text-base-content/60 pt-1.5"> | ||||||
|  |           <%= l(@submission.created_at.in_time_zone(current_account.timezone), format: :long, locale: current_account.locale) %> | ||||||
|  |         </p> | ||||||
|  |         <p class="text-base-content/80 mt-1"> | ||||||
|  |           <% if @submission.source == 'invite' %> | ||||||
|  |             <%= t('submission_created_by_email_html', email: @submission.created_by_user.email) %> | ||||||
|  |           <% elsif @submission.created_by_user %> | ||||||
|  |             <%= t('submission_created_by_email_via_source_html', email: @submission.created_by_user.email, source: t("submission_sources.#{@submission.source}")) %> | ||||||
|  |           <% else %> | ||||||
|  |             <%= t('submission_created_via_source_html', source: t("submission_sources.#{@submission.source}")) %> | ||||||
|  |           <% end %> | ||||||
|  |         </p> | ||||||
|  |       </li> | ||||||
|  |       <% @submission.submission_events.order(:event_timestamp).each do |event| %> | ||||||
|  |         <% submitter = @submission.submitters.find { |e| e.id == event.submitter_id } %> | ||||||
|  |         <% bg_class = event_colors[submitters_uuids.index(submitter.uuid) % event_colors.length] %> | ||||||
|  |         <% submitter_name = event.event_type.include?('sms') || event.event_type.include?('phone') ? (event.data['phone'] || submitter.phone) : (submitter.name || submitter.email || submitter.phone) %> | ||||||
|  |         <li class="ml-7"> | ||||||
|  |           <span class="absolute flex items-center justify-center w-7 h-7 rounded-full -start-3.5 ring-8 ring-base-100 text-base-content <%= bg_class %>"> | ||||||
|  |             <%= svg_icon(SubmissionEventsController::SUBMISSION_EVENT_ICONS.fetch(event.event_type, 'circle_dot'), class: 'w-4 h-4') %> | ||||||
|  |           </span> | ||||||
|  |           <p class="text-sm leading-none text-base-content/60 pt-1.5"> | ||||||
|  |             <%= l(event.event_timestamp.in_time_zone(current_account.timezone), format: :long, locale: current_account.locale) %> | ||||||
|  |           </p> | ||||||
|  |           <p class="text-base-content/80 mt-1"> | ||||||
|  |             <% if event.event_type == 'complete_verification' %> | ||||||
|  |               <%= t('submission_event_names.complete_verification_by_html', provider: event.data['method'], submitter_name:) %> | ||||||
|  |             <% elsif event.event_type == 'invite_party' && (invited_submitter = @submission.submitters.find { |e| e.uuid == event.data['uuid'] }) && (name = @submission.template_submitters.find { |e| e['uuid'] == event.data['uuid'] }&.dig('name')) %> | ||||||
|  |               <%= t('submission_event_names.invite_party_by_html', invited_submitter_name: [invited_submitter.name || invited_submitter.email || invited_submitter.phone, name].join(' '), submitter_name:) %> | ||||||
|  |             <% elsif event.event_type.include?('send_') %> | ||||||
|  |               <%= t("submission_event_names.#{event.event_type}_to_html", submitter_name:) %> | ||||||
|  |             <% else %> | ||||||
|  |               <%= t("submission_event_names.#{event.event_type}_by_html", submitter_name:) %> | ||||||
|  |             <% end %> | ||||||
|  |           </p> | ||||||
|  |         </li> | ||||||
|  |       <% end %> | ||||||
|  |     </ol> | ||||||
|  |   </div> | ||||||
|  | <% end %> | ||||||
| @ -0,0 +1 @@ | |||||||
|  | <%= render partial: 'submissions/annotation', collection: annots, as: :annot %> | ||||||
| @ -1,5 +1,5 @@ | |||||||
| <% data_attachments = attachments_index.values.select { |e| e.record_id == submitter.id }.to_json(only: %i[uuid created_at], methods: %i[url filename content_type]) %> | <% data_attachments = attachments_index.values.select { |e| e.record_id == submitter.id }.to_json(only: %i[uuid created_at], methods: %i[url filename content_type]) %> | ||||||
| <% data_fields = (submitter.submission.template_fields || submitter.submission.template.fields).select { |f| f['submitter_uuid'] == submitter.uuid }.to_json %> | <% data_fields = Submissions.filtered_conditions_fields(submitter).to_json %> | ||||||
| <% invite_submitters = (submitter.submission.template_submitters || submitter.submission.template.submitters).select { |s| s['invite_by_uuid'] == submitter.uuid && submitter.submission.submitters.none? { |e| e.uuid == s['uuid'] } }.to_json %> | <% invite_submitters = (submitter.submission.template_submitters || submitter.submission.template.submitters).select { |s| s['invite_by_uuid'] == submitter.uuid && submitter.submission.submitters.none? { |e| e.uuid == s['uuid'] } }.to_json %> | ||||||
| <% optional_invite_submitters = (submitter.submission.template_submitters || submitter.submission.template.submitters).select { |s| s['optional_invite_by_uuid'] == submitter.uuid && submitter.submission.submitters.none? { |e| e.uuid == s['uuid'] } }.to_json %> | <% optional_invite_submitters = (submitter.submission.template_submitters || submitter.submission.template.submitters).select { |s| s['optional_invite_by_uuid'] == submitter.uuid && submitter.submission.submitters.none? { |e| e.uuid == s['uuid'] } }.to_json %> | ||||||
| <submission-form data-is-demo="<%= Docuseal.demo? %>" data-schema="<%= schema.to_json %>" data-reuse-signature="<%= configs[:reuse_signature] %>" data-require-signing-reason="<%= configs[:require_signing_reason] %>" data-with-signature-id="<%= configs[:with_signature_id] %>" data-with-confetti="<%= configs[:with_confetti] %>" data-completed-redirect-url="<%= submitter.preferences['completed_redirect_url'].presence || submitter.submission.template.preferences['completed_redirect_url'] %>" data-completed-message="<%= (configs[:completed_message]&.compact_blank.presence || submitter.submission.template.preferences['completed_message'] || {}).to_json %>" data-completed-button="<%= configs[:completed_button].to_json %>" data-go-to-last="<%= submitter.preferences.key?('go_to_last') ? submitter.preferences['go_to_last'] : submitter.opened_at? %>" data-submitter="<%= submitter.to_json(only: %i[uuid slug name phone email]) %>" data-can-send-email="<%= Accounts.can_send_emails?(submitter.submission.account) %>" data-optional-invite-submitters="<%= optional_invite_submitters %>" data-invite-submitters="<%= invite_submitters %>" data-attachments="<%= data_attachments %>" data-fields="<%= data_fields %>" data-values="<%= submitter.values.to_json %>" data-with-typed-signature="<%= configs[:with_typed_signature] %>" data-previous-signature-value="<%= local_assigns[:signature_attachment]&.uuid %>" data-remember-signature="<%= configs[:prefill_signature] %>" data-dry-run="<%= local_assigns[:dry_run] %>" data-expand="<%= local_assigns[:expand] %>" data-scroll-padding="<%= local_assigns[:scroll_padding] %>" data-language="<%= I18n.locale.to_s.split('-').first %>"></submission-form> | <submission-form data-is-demo="<%= Docuseal.demo? %>" data-schema="<%= schema.to_json %>" data-reuse-signature="<%= configs[:reuse_signature] %>" data-require-signing-reason="<%= configs[:require_signing_reason] %>" data-with-signature-id="<%= configs[:with_signature_id] %>" data-with-confetti="<%= configs[:with_confetti] %>" data-completed-redirect-url="<%= submitter.preferences['completed_redirect_url'].presence || submitter.submission.template.preferences['completed_redirect_url'] %>" data-completed-message="<%= (configs[:completed_message]&.compact_blank.presence || submitter.submission.template.preferences['completed_message'] || {}).to_json %>" data-completed-button="<%= configs[:completed_button].to_json %>" data-go-to-last="<%= submitter.preferences.key?('go_to_last') ? submitter.preferences['go_to_last'] : submitter.opened_at? %>" data-submitter="<%= submitter.to_json(only: %i[uuid slug name phone email]) %>" data-can-send-email="<%= Accounts.can_send_emails?(submitter.submission.account) %>" data-optional-invite-submitters="<%= optional_invite_submitters %>" data-invite-submitters="<%= invite_submitters %>" data-attachments="<%= data_attachments %>" data-fields="<%= data_fields %>" data-values="<%= submitter.values.to_json %>" data-with-typed-signature="<%= configs[:with_typed_signature] %>" data-previous-signature-value="<%= local_assigns[:signature_attachment]&.uuid %>" data-remember-signature="<%= configs[:prefill_signature] %>" data-dry-run="<%= local_assigns[:dry_run] %>" data-expand="<%= local_assigns[:expand] %>" data-scroll-padding="<%= local_assigns[:scroll_padding] %>" data-language="<%= I18n.locale.to_s.split('-').first %>"></submission-form> | ||||||
|  | |||||||
| After Width: | Height: | Size: 26 KiB | 
| @ -0,0 +1,26 @@ | |||||||
|  | # frozen_string_literal: true | ||||||
|  | 
 | ||||||
|  | module SendWebhookRequest | ||||||
|  |   USER_AGENT = 'DocuSeal.com Webhook' | ||||||
|  | 
 | ||||||
|  |   module_function | ||||||
|  | 
 | ||||||
|  |   def call(webhook_url, event_type:, data:) | ||||||
|  |     Faraday.post(webhook_url.url) do |req| | ||||||
|  |       req.headers['Content-Type'] = 'application/json' | ||||||
|  |       req.headers['User-Agent'] = USER_AGENT | ||||||
|  |       req.headers.merge!(webhook_url.secret.to_h) if webhook_url.secret.present? | ||||||
|  | 
 | ||||||
|  |       req.body = { | ||||||
|  |         event_type: event_type, | ||||||
|  |         timestamp: Time.current, | ||||||
|  |         data: data | ||||||
|  |       }.to_json | ||||||
|  | 
 | ||||||
|  |       req.options.read_timeout = 8 | ||||||
|  |       req.options.open_timeout = 8 | ||||||
|  |     end | ||||||
|  |   rescue Faraday::Error | ||||||
|  |     nil | ||||||
|  |   end | ||||||
|  | end | ||||||