├── .annotaterb.yml ├── .dockerignore ├── .erb_lint.yml ├── .eslintrc ├── .gitattributes ├── .github └── workflows │ ├── ci.yml │ └── docker.yml ├── .gitignore ├── .rspec ├── .rubocop.yml ├── .version ├── Dockerfile ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── Procfile ├── Procfile.dev ├── README.md ├── Rakefile ├── SECURITY.md ├── app ├── controllers │ ├── account_configs_controller.rb │ ├── accounts_controller.rb │ ├── api │ │ ├── active_storage_blobs_proxy_controller.rb │ │ ├── active_storage_blobs_proxy_legacy_controller.rb │ │ ├── api_base_controller.rb │ │ ├── attachments_controller.rb │ │ ├── form_events_controller.rb │ │ ├── submission_documents_controller.rb │ │ ├── submission_events_controller.rb │ │ ├── submissions_controller.rb │ │ ├── submitter_email_clicks_controller.rb │ │ ├── submitter_form_views_controller.rb │ │ ├── submitters_controller.rb │ │ ├── templates_clone_controller.rb │ │ ├── templates_controller.rb │ │ ├── tools_controller.rb │ │ └── users_controller.rb │ ├── api_settings_controller.rb │ ├── application_controller.rb │ ├── console_redirect_controller.rb │ ├── dashboard_controller.rb │ ├── email_smtp_settings_controller.rb │ ├── embed_scripts_controller.rb │ ├── encrypted_user_configs_controller.rb │ ├── enquiries_controller.rb │ ├── errors_controller.rb │ ├── esign_settings_controller.rb │ ├── invitations_controller.rb │ ├── mfa_setup_controller.rb │ ├── newsletters_controller.rb │ ├── notifications_settings_controller.rb │ ├── passwords_controller.rb │ ├── personalization_settings_controller.rb │ ├── preview_document_page_controller.rb │ ├── profile_controller.rb │ ├── pwa_controller.rb │ ├── send_submission_email_controller.rb │ ├── sessions_controller.rb │ ├── setup_controller.rb │ ├── sms_settings_controller.rb │ ├── sso_settings_controller.rb │ ├── start_form_controller.rb │ ├── storage_settings_controller.rb │ ├── submission_events_controller.rb │ ├── submissions_archived_controller.rb │ ├── submissions_controller.rb │ ├── submissions_dashboard_controller.rb │ ├── submissions_debug_controller.rb │ ├── submissions_download_controller.rb │ ├── submissions_export_controller.rb │ ├── submissions_filters_controller.rb │ ├── submissions_preview_controller.rb │ ├── submissions_unarchive_controller.rb │ ├── submit_form_controller.rb │ ├── submit_form_decline_controller.rb │ ├── submit_form_download_controller.rb │ ├── submit_form_draw_signature_controller.rb │ ├── submit_form_invite_controller.rb │ ├── submit_form_values_controller.rb │ ├── submitters_autocomplete_controller.rb │ ├── submitters_controller.rb │ ├── submitters_resubmit_controller.rb │ ├── submitters_send_email_controller.rb │ ├── template_documents_controller.rb │ ├── template_folders_autocomplete_controller.rb │ ├── template_folders_controller.rb │ ├── template_sharings_testing_controller.rb │ ├── templates_archived_controller.rb │ ├── templates_archived_submissions_controller.rb │ ├── templates_clone_and_replace_controller.rb │ ├── templates_code_modal_controller.rb │ ├── templates_controller.rb │ ├── templates_dashboard_controller.rb │ ├── templates_debug_controller.rb │ ├── templates_folders_controller.rb │ ├── templates_form_preview_controller.rb │ ├── templates_preferences_controller.rb │ ├── templates_preview_controller.rb │ ├── templates_recipients_controller.rb │ ├── templates_restore_controller.rb │ ├── templates_uploads_controller.rb │ ├── testing_accounts_controller.rb │ ├── testing_api_settings_controller.rb │ ├── timestamp_server_controller.rb │ ├── user_configs_controller.rb │ ├── user_initials_controller.rb │ ├── user_signatures_controller.rb │ ├── users_controller.rb │ ├── verify_pdf_signature_controller.rb │ ├── webhook_preferences_controller.rb │ ├── webhook_secret_controller.rb │ └── webhook_settings_controller.rb ├── javascript │ ├── application.js │ ├── application.scss │ ├── draw.js │ ├── elements │ │ ├── app_tour.js │ │ ├── autoresize_textarea.js │ │ ├── checkbox_group.js │ │ ├── clipboard_copy.js │ │ ├── dashboard_dropzone.js │ │ ├── disable_hidden.js │ │ ├── download_button.js │ │ ├── dynamic_list.js │ │ ├── emails_textarea.js │ │ ├── fetch_form.js │ │ ├── file_dropzone.js │ │ ├── folder_autocomplete.js │ │ ├── indeterminate_checkbox.js │ │ ├── linked_input.js │ │ ├── masked_input.js │ │ ├── menu_active.js │ │ ├── password_input.js │ │ ├── prompt_password.js │ │ ├── scroll_buttons.js │ │ ├── search_input.js │ │ ├── set_date_button.js │ │ ├── set_origin_url.js │ │ ├── set_timezone.js │ │ ├── signature_form.js │ │ ├── submit_form.js │ │ ├── submitter_autocomplete.js │ │ ├── toggle_attribute.js │ │ ├── toggle_cookies.js │ │ ├── toggle_on_submit.js │ │ ├── toggle_submit.js │ │ ├── toggle_visible.js │ │ └── turbo_modal.js │ ├── form.js │ ├── form.scss │ ├── lib │ │ └── turbo_instant_click.js │ ├── rollbar.js │ ├── submission_form │ │ ├── appears_on.vue │ │ ├── area.vue │ │ ├── areas.vue │ │ ├── attachment_step.vue │ │ ├── completed.vue │ │ ├── crop_canvas.js │ │ ├── date_step.vue │ │ ├── dropzone.vue │ │ ├── form.vue │ │ ├── formula_areas.vue │ │ ├── i18n.js │ │ ├── image_step.vue │ │ ├── initials_step.vue │ │ ├── invite_form.vue │ │ ├── markdown_content.vue │ │ ├── multi_select_step.vue │ │ ├── number_step.vue │ │ ├── payment_step.vue │ │ ├── phone_data.js │ │ ├── phone_step.vue │ │ ├── signature_step.vue │ │ ├── text_step.vue │ │ ├── validate_signature.js │ │ └── verification_step.vue │ └── template_builder │ │ ├── area.vue │ │ ├── builder.vue │ │ ├── conditions_modal.vue │ │ ├── contenteditable.vue │ │ ├── controls.vue │ │ ├── description_modal.vue │ │ ├── document.vue │ │ ├── drag_placeholder.vue │ │ ├── dropzone.vue │ │ ├── field.vue │ │ ├── field_settings.vue │ │ ├── field_submitter.vue │ │ ├── field_type.vue │ │ ├── fields.vue │ │ ├── font_modal.vue │ │ ├── formula_modal.vue │ │ ├── hover_dropzone.vue │ │ ├── i18n.js │ │ ├── icon_drag.vue │ │ ├── import_list.vue │ │ ├── logo.vue │ │ ├── mobile_draw_field.vue │ │ ├── mobile_fields.vue │ │ ├── page.vue │ │ ├── payment_settings.vue │ │ ├── preview.vue │ │ ├── replace.vue │ │ └── upload.vue ├── jobs │ ├── application_job.rb │ ├── generate_preview_images_job.rb │ ├── process_submission_expired_job.rb │ ├── process_submitter_completion_job.rb │ ├── send_form_completed_webhook_request_job.rb │ ├── send_form_declined_webhook_request_job.rb │ ├── send_form_started_webhook_request_job.rb │ ├── send_form_viewed_webhook_request_job.rb │ ├── send_submission_archived_webhook_request_job.rb │ ├── send_submission_completed_webhook_request_job.rb │ ├── send_submission_created_webhook_request_job.rb │ ├── send_submission_expired_webhook_request_job.rb │ ├── send_submitter_invitation_email_job.rb │ ├── send_template_created_webhook_request_job.rb │ └── send_template_updated_webhook_request_job.rb ├── mailers │ ├── application_mailer.rb │ ├── settings_mailer.rb │ ├── submitter_mailer.rb │ └── user_mailer.rb ├── models │ ├── access_token.rb │ ├── account.rb │ ├── account_access.rb │ ├── account_config.rb │ ├── account_linked_account.rb │ ├── application_record.rb │ ├── completed_document.rb │ ├── completed_submitter.rb │ ├── document_generation_event.rb │ ├── email_event.rb │ ├── email_message.rb │ ├── encrypted_config.rb │ ├── encrypted_user_config.rb │ ├── submission.rb │ ├── submission_event.rb │ ├── submitter.rb │ ├── template.rb │ ├── template_access.rb │ ├── template_folder.rb │ ├── template_sharing.rb │ ├── user.rb │ ├── user_config.rb │ └── webhook_url.rb └── views │ ├── accounts │ ├── _compliances.html.erb │ ├── _integrations.html.erb │ └── show.html.erb │ ├── api_settings │ └── index.html.erb │ ├── dashboard │ └── _toggle_view.html.erb │ ├── devise │ ├── mailer │ │ └── reset_password_instructions.html.erb │ ├── passwords │ │ ├── edit.html.erb │ │ └── new.html.erb │ ├── sessions │ │ ├── _extra_links.html.erb │ │ ├── _hidden_fields.html.erb │ │ ├── _otp_form.html.erb │ │ ├── new.html.erb │ │ └── otp.html.erb │ └── shared │ │ ├── _error_messages.html.erb │ │ ├── _links.html.erb │ │ └── _select_server.html.erb │ ├── email_smtp_settings │ └── index.html.erb │ ├── errors │ ├── 404.html │ ├── 422.html │ └── 500.html │ ├── esign_settings │ ├── _alert.html.erb │ ├── _default_signature_row.html.erb │ ├── new.html.erb │ └── show.html.erb │ ├── icons │ ├── _2fa.html.erb │ ├── _adjustments.html.erb │ ├── _adjustments_horizontal.html.erb │ ├── _alert_circle.html.erb │ ├── _api.html.erb │ ├── _archive.html.erb │ ├── _arrow_right.html.erb │ ├── _arrow_sort.html.erb │ ├── _arrow_up_right.html.erb │ ├── _brand_discord.html.erb │ ├── _brand_docker.html.erb │ ├── _brand_github.html.erb │ ├── _brand_google.html.erb │ ├── _brand_microsoft.html.erb │ ├── _brand_openai.html.erb │ ├── _brand_twitter.html.erb │ ├── _building.html.erb │ ├── _calendar.html.erb │ ├── _calendar_check.html.erb │ ├── _certificate.html.erb │ ├── _check.html.erb │ ├── _chevron_down.html.erb │ ├── _chevron_left.html.erb │ ├── _circle_arrow_right.html.erb │ ├── _circle_check.html.erb │ ├── _circle_check_filled.html.erb │ ├── _circle_dot.html.erb │ ├── _circle_question.html.erb │ ├── _clipboard.html.erb │ ├── _clipboard_copy.html.erb │ ├── _clock.html.erb │ ├── _clock_cancel.html.erb │ ├── _clock_edit.html.erb │ ├── _clock_exclamation.html.erb │ ├── _cloud_upload.html.erb │ ├── _code.html.erb │ ├── _code_circle.html.erb │ ├── _copy.html.erb │ ├── _corner_down_right.html.erb │ ├── _corner_right_down.html.erb │ ├── _device_floppy.html.erb │ ├── _devices.html.erb │ ├── _discount_check_filled.html.erb │ ├── _dots_vertical.html.erb │ ├── _download.html.erb │ ├── _eu_flag.html.erb │ ├── _exclamation_circle.html.erb │ ├── _external_link.html.erb │ ├── _eye.html.erb │ ├── _eye_off.html.erb │ ├── _file_text.html.erb │ ├── _file_upload.html.erb │ ├── _files.html.erb │ ├── _filter.html.erb │ ├── _folder.html.erb │ ├── _folder_share.html.erb │ ├── _forms.html.erb │ ├── _hand_click.html.erb │ ├── _info_circle.html.erb │ ├── _layout_grid.html.erb │ ├── _layout_list.html.erb │ ├── _link.html.erb │ ├── _list.html.erb │ ├── _loader.html.erb │ ├── _lock.html.erb │ ├── _lock_access.html.erb │ ├── _lock_open.html.erb │ ├── _login.html.erb │ ├── _logout.html.erb │ ├── _logs.html.erb │ ├── _mail.html.erb │ ├── _mail_forward.html.erb │ ├── _mail_opened.html.erb │ ├── _mail_up.html.erb │ ├── _paperclip.html.erb │ ├── _pencil.html.erb │ ├── _pencil_share.html.erb │ ├── _phone.html.erb │ ├── _phone_check.html.erb │ ├── _player_play.html.erb │ ├── _plus.html.erb │ ├── _reload.html.erb │ ├── _rotate.html.erb │ ├── _save.html.erb │ ├── _search.html.erb │ ├── _send.html.erb │ ├── _settings.html.erb │ ├── _shield_check.html.erb │ ├── _sort_ascending_letters.html.erb │ ├── _sort_descending_numbers.html.erb │ ├── _sort_descending_small_big.html.erb │ ├── _sparkles.html.erb │ ├── _terminal.html.erb │ ├── _trash.html.erb │ ├── _upload.html.erb │ ├── _usa_flag.html.erb │ ├── _user.html.erb │ ├── _user_number.html.erb │ ├── _user_plus.html.erb │ ├── _users.html.erb │ ├── _users_plus.html.erb │ ├── _waving_hand.html.erb │ ├── _world.html.erb │ ├── _writing.html.erb │ ├── _writing_sign.html.erb │ ├── _x.html.erb │ ├── _x_circle.html.erb │ └── _zoom_check.html.erb │ ├── invitations │ └── edit.html.erb │ ├── layouts │ ├── _head_tags.html.erb │ ├── application.html.erb │ ├── form.html.erb │ ├── mailer.html.erb │ └── plain.html.erb │ ├── mfa_setup │ ├── _form.html.erb │ ├── edit.html.erb │ ├── new.html.erb │ └── show.html.erb │ ├── newsletters │ └── show.html.erb │ ├── notifications_settings │ ├── _bcc_form.html.erb │ ├── _reminder_banner.html.erb │ ├── _reminder_form.html.erb │ ├── _reminder_placeholder.html.erb │ └── index.html.erb │ ├── pages │ └── landing.html.erb │ ├── personalization_settings │ ├── _documents_copy_email_form.html.erb │ ├── _form_completed_button_form.html.erb │ ├── _form_completed_message_form.html.erb │ ├── _form_customization_settings.html.erb │ ├── _form_policy_links_form.html.erb │ ├── _form_toggle_options.html.erb │ ├── _logo_form.html.erb │ ├── _logo_placeholder.html.erb │ ├── _signature_request_email_form.html.erb │ ├── _submitter_completed_email_form.html.erb │ └── show.html.erb │ ├── profile │ ├── _email_configs.html.erb │ └── index.html.erb │ ├── pwa │ └── manifest.json.erb │ ├── scripts │ ├── _autosize_field.html.erb │ └── _server_selector.html.erb │ ├── send_submission_email │ └── success.html.erb │ ├── settings_mailer │ └── smtp_successful_setup.html.erb │ ├── setup │ └── index.html.erb │ ├── shared │ ├── _app_tour.html.erb │ ├── _attribution.html.erb │ ├── _button_title.html.erb │ ├── _classes.html.erb │ ├── _clipboard_copy.html.erb │ ├── _demo_alert.html.erb │ ├── _email_attribution.html.erb │ ├── _field_error.html.erb │ ├── _flash.html.erb │ ├── _github.html.erb │ ├── _github_button.html.erb │ ├── _html_modal.html.erb │ ├── _logo.html.erb │ ├── _mailer_attribution.html.erb │ ├── _meta.html.erb │ ├── _navbar.html.erb │ ├── _navbar_buttons.html.erb │ ├── _navbar_warning.html.erb │ ├── _pagination.html.erb │ ├── _plausible.html.erb │ ├── _posthog.html.erb │ ├── _powered_by.html.erb │ ├── _review_form.html.erb │ ├── _search_input.html.erb │ ├── _settings_nav.html.erb │ ├── _settings_nav_extra.html.erb │ ├── _settings_nav_extra2.html.erb │ ├── _templates_order_select.html.erb │ ├── _test_alert.html.erb │ ├── _test_mode_toggle.html.erb │ ├── _title.html.erb │ ├── _turbo_drawer.html.erb │ ├── _turbo_modal.html.erb │ └── _turbo_modal_large.html.erb │ ├── sms_settings │ ├── _placeholder.html.erb │ └── index.html.erb │ ├── sso_settings │ ├── _placeholder.html.erb │ └── index.html.erb │ ├── start_form │ ├── _banner.html.erb │ ├── _docuseal_logo.html.erb │ ├── completed.html.erb │ └── show.html.erb │ ├── storage_settings │ ├── _aws_form.html.erb │ ├── _azure_form.html.erb │ ├── _disk_form.html.erb │ ├── _google_cloud_form.html.erb │ └── index.html.erb │ ├── submission_events │ └── index.html.erb │ ├── submissions │ ├── _annotation.html.erb │ ├── _bulk_send_placeholder.html.erb │ ├── _detailed_form.html.erb │ ├── _email_form.html.erb │ ├── _email_stats.html.erb │ ├── _error.html.erb │ ├── _link_form.html.erb │ ├── _list_form.html.erb │ ├── _logo.html.erb │ ├── _message_fields.html.erb │ ├── _phone_form.html.erb │ ├── _preview_tags.html.erb │ ├── _send_email.html.erb │ ├── _send_sms.html.erb │ ├── _send_sms_button.html.erb │ ├── _submitters_order.html.erb │ ├── _value.html.erb │ ├── new.html.erb │ └── show.html.erb │ ├── submissions_archived │ └── index.html.erb │ ├── submissions_dashboard │ └── index.html.erb │ ├── submissions_export │ └── new.html.erb │ ├── submissions_filters │ ├── _actions.html.erb │ ├── _applied_filters.html.erb │ ├── _date_buttons.html.erb │ ├── _filter_button.html.erb │ ├── _filter_modal.html.erb │ ├── author.html.erb │ ├── completed_at.html.erb │ ├── created_at.html.erb │ └── status.html.erb │ ├── submissions_preview │ └── completed.html.erb │ ├── submit_form │ ├── _annotations.html.erb │ ├── _banner.html.erb │ ├── _decline_form.html.erb │ ├── _docuseal_logo.html.erb │ ├── _submission_form.html.erb │ ├── archived.html.erb │ ├── awaiting.html.erb │ ├── completed.html.erb │ ├── declined.html.erb │ ├── expired.html.erb │ ├── show.html.erb │ └── success.html.erb │ ├── submit_form_draw_signature │ └── show.html.erb │ ├── submitter_mailer │ ├── _custom_content.html.erb │ ├── completed_email.html.erb │ ├── declined_email.html.erb │ ├── documents_copy_email.html.erb │ └── invitation_email.html.erb │ ├── submitters │ └── edit.html.erb │ ├── template_folders │ ├── _folder.html.erb │ ├── edit.html.erb │ └── show.html.erb │ ├── templates │ ├── _access_icon.html.erb │ ├── _dashboard_dropzone.html.erb │ ├── _dropzone.html.erb │ ├── _embedding.html.erb │ ├── _submission.html.erb │ ├── _template.html.erb │ ├── _title.html.erb │ ├── _upload_button.html.erb │ ├── edit.html.erb │ ├── new.html.erb │ └── show.html.erb │ ├── templates_archived │ └── index.html.erb │ ├── templates_archived_submissions │ └── index.html.erb │ ├── templates_code_modal │ ├── _placeholder.html.erb │ ├── _preferences.html.erb │ └── show.html.erb │ ├── templates_dashboard │ └── index.html.erb │ ├── templates_folders │ └── edit.html.erb │ ├── templates_form_preview │ └── show.html.erb │ ├── templates_preferences │ ├── _access.html.erb │ ├── _form_fields.html.erb │ └── show.html.erb │ ├── templates_preview │ └── show.html.erb │ ├── templates_uploads │ └── show.html.erb │ ├── testing_api_settings │ └── index.html.erb │ ├── user_initials │ └── edit.html.erb │ ├── user_mailer │ └── invitation_email.html.erb │ ├── user_signatures │ └── edit.html.erb │ ├── users │ ├── _bottom_links.html.erb │ ├── _extra_buttons.html.erb │ ├── _form.html.erb │ ├── _role_select.html.erb │ ├── edit.html.erb │ ├── index.html.erb │ └── new.html.erb │ ├── verify_pdf_signature │ └── _result.html.erb │ ├── webhook_secret │ └── show.erb │ └── webhook_settings │ ├── index.html.erb │ ├── new.html.erb │ └── show.html.erb ├── bin ├── bundle ├── rails ├── rake ├── setup ├── shakapacker ├── shakapacker-dev-server └── yarn ├── config.ru ├── config ├── application.rb ├── boot.rb ├── brakeman.ignore ├── database.yml ├── dotenv.rb ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ └── test.rb ├── initializers │ ├── active_storage.rb │ ├── better_html.rb │ ├── csv.rb │ ├── devise.rb │ ├── field_error_proc.rb │ ├── filter_parameter_logging.rb │ ├── hexapdf.rb │ ├── migrate.rb │ ├── oj.rb │ ├── pagy.rb │ ├── sidekiq.rb │ └── twitter_cldr.rb ├── locales │ └── i18n.yml ├── puma.rb ├── routes.rb ├── secrets.yml ├── shakapacker.yml ├── sidekiq.yml ├── storage.yml └── webpack │ └── webpack.config.js ├── db ├── migrate │ ├── 20230515182743_create_accounts.rb │ ├── 20230515182744_devise_create_users.rb │ ├── 20230515193039_create_encrypted_configs.rb │ ├── 20230515205356_create_active_storage_tables.active_storage.rb │ ├── 20230516180929_create_templates.rb │ ├── 20230519144036_create_submissions.rb │ ├── 20230612182744_create_submitters.rb │ ├── 20230701075115_create_document_generation_events.rb │ ├── 20230726062428_add_template_fields_to_submission.rb │ ├── 20230803200825_add_uuid_to_users.rb │ ├── 20230805124454_add_source_to_submissions.rb │ ├── 20230806140534_create_access_tokens.rb │ ├── 20230815190540_create_account_configs.rb │ ├── 20230819113427_remove_user_first_last_name_not_null.rb │ ├── 20230819190316_add_source_to_templates.rb │ ├── 20230902171216_add_phone_and_name_to_submitters.rb │ ├── 20230909213212_add_submitters_order_to_submissions.rb │ ├── 20230910084410_create_submission_events.rb │ ├── 20230915200635_add_devise_two_factor_to_users.rb │ ├── 20230920195319_create_template_folders.rb │ ├── 20230920202947_add_folder_id_to_templates.rb │ ├── 20230922072041_create_user_configs.rb │ ├── 20230924184657_create_encrypted_user_configs.rb │ ├── 20231007052818_update_checkboxes_required.rb │ ├── 20231017213639_add_application_key_to_submitters.rb │ ├── 20231102171817_add_application_key_to_templates.rb │ ├── 20231112224432_update_field_options.rb │ ├── 20231119222105_add_slug_to_submissions.rb │ ├── 20231122203341_add_preferences_to_submitters.rb │ ├── 20231122203347_add_preferences_to_submissions.rb │ ├── 20231122212612_create_email_messages.rb │ ├── 20231229220819_rename_deleted_at_to_archived_at.rb │ ├── 20240120192055_create_account_linked_accounts.rb │ ├── 20240131212010_rename_application_key_to_external_id.rb │ ├── 20240201210319_add_submitter_metadata.rb │ ├── 20240203113454_add_account_id_to_submissions.rb │ ├── 20240203113455_create_template_sharings.rb │ ├── 20240218082157_add_uuid_to_active_storage_blobs.rb │ ├── 20240228074821_add_index_on_submission_events.rb │ ├── 20240228083356_add_index_on_blobs_checksum.rb │ ├── 20240405165329_add_uuid_to_accounts.rb │ ├── 20240416170023_add_preferences_to_templates.rb │ ├── 20240428072623_add_archived_at_to_accounts.rb │ ├── 20240428112400_populate_account_linked_accounts.rb │ ├── 20240604070648_create_email_events.rb │ ├── 20240624102526_add_index_on_external_id.rb │ ├── 20240714172222_create_webhook_urls.rb │ ├── 20240720063826_add_index_on_email_email_events.rb │ ├── 20240720063827_create_doorkeeper_tables.rb │ ├── 20240801125558_add_expire_at_to_submissions.rb │ ├── 20240816064633_add_account_id_to_submitters.rb │ ├── 20240816072859_populate_submitter_account_id.rb │ ├── 20240816075917_update_submission_account_id_index.rb │ ├── 20240816121641_remove_active_storage_uniq_index.rb │ ├── 20240820180922_add_declined_at_to_submitters.rb │ ├── 20241018115034_create_completed_submitters_and_documents.rb │ ├── 20241022125135_populate_completed_submitters_and_documents.rb │ ├── 20241026161207_add_unique_index_on_completed_submitters.rb │ ├── 20241028162000_add_secret_to_webhook_urls.rb │ ├── 20241029192232_populate_webhook_urls.rb │ ├── 20241207172237_create_template_accesses.rb │ ├── 20250225111255_create_console1984_tables.console1984.rb │ └── 20250518070555_account_accesses.rb └── schema.rb ├── docker-compose.yml ├── docs ├── api │ ├── csharp.md │ ├── go.md │ ├── java.md │ ├── javascript.md │ ├── nodejs.md │ ├── php.md │ ├── python.md │ ├── ruby.md │ ├── shell.md │ └── typescript.md ├── embedding │ ├── form-builder-angular.md │ ├── form-builder-javascript.md │ ├── form-builder-react.md │ ├── form-builder-vue.md │ ├── signing-form-angular.md │ ├── signing-form-javascript.md │ ├── signing-form-react.md │ └── signing-form-vue.md ├── openapi.json └── webhooks │ ├── form-webhook.md │ ├── submission-webhook.md │ └── template-webhook.md ├── lib ├── abilities │ └── template_conditions.rb ├── ability.rb ├── account_configs.rb ├── accounts.rb ├── action_mailer_configs_interceptor.rb ├── action_mailer_events_observer.rb ├── api_path_consider_json_middleware.rb ├── docuseal.rb ├── download_utils.rb ├── email_messages.rb ├── generate_certificate.rb ├── json_web_token.rb ├── load_active_storage_configs.rb ├── load_bmp.rb ├── load_ico.rb ├── markdown_to_html.rb ├── normalize_client_ip_middleware.rb ├── number_utils.rb ├── params │ ├── base_validator.rb │ └── submission_create_validator.rb ├── pdf_icons.rb ├── pdf_icons │ ├── check.png │ ├── logo.png │ ├── logo_new.png │ ├── paperclip.png │ └── stamp-logo.png ├── pdf_utils.rb ├── pdfium.rb ├── puma │ └── plugin │ │ ├── redis_server.rb │ │ └── sidekiq_embed.rb ├── rate_limit.rb ├── replace_email_variables.rb ├── send_webhook_request.rb ├── submission_events.rb ├── submissions.rb ├── submissions │ ├── assign_defined_submitters.rb │ ├── create_from_submitters.rb │ ├── ensure_result_generated.rb │ ├── filter.rb │ ├── generate_audit_trail.rb │ ├── generate_combined_attachment.rb │ ├── generate_export_files.rb │ ├── generate_preview_attachments.rb │ ├── generate_result_attachments.rb │ ├── normalize_param_utils.rb │ ├── serialize_for_api.rb │ └── timestamp_handler.rb ├── submitters.rb ├── submitters │ ├── create_stamp_attachment.rb │ ├── form_configs.rb │ ├── generate_font_image.rb │ ├── maybe_assign_default_browser_signature.rb │ ├── maybe_assign_default_email_signature.rb │ ├── maybe_update_default_values.rb │ ├── normalize_values.rb │ ├── serialize_for_api.rb │ ├── serialize_for_webhook.rb │ ├── submit_values.rb │ └── validate_sending.rb ├── tasks │ ├── .keep │ └── annotate_rb.rake ├── template_folders.rb ├── templates.rb ├── templates │ ├── build_annotations.rb │ ├── clone.rb │ ├── clone_attachments.rb │ ├── create_attachments.rb │ ├── find_acro_fields.rb │ ├── order.rb │ ├── process_document.rb │ ├── replace_attachments.rb │ └── serialize_for_api.rb ├── text_utils.rb ├── time_utils.rb ├── user_configs.rb └── webhook_urls.rb ├── log └── .keep ├── package.json ├── postcss.config.js ├── public ├── apple-icon-180x180.png ├── apple-touch-icon-precomposed.png ├── apple-touch-icon.png ├── favicon-16x16.png ├── favicon-32x32.png ├── favicon-96x96.png ├── favicon.ico ├── logo.svg ├── preview.png ├── robots.txt └── service-worker.js ├── spec ├── factories │ ├── account_configs.rb │ ├── accounts.rb │ ├── completed_documents.rb │ ├── encrypted_configs.rb │ ├── submission_events.rb │ ├── submissions.rb │ ├── submitters.rb │ ├── template_accesses.rb │ ├── template_folders.rb │ ├── templates.rb │ ├── user_configs.rb │ ├── users.rb │ └── webhook_urls.rb ├── fixtures │ ├── sample-document.pdf │ └── sample-image.png ├── jobs │ ├── process_submitter_completion_job_spec.rb │ ├── send_form_completed_webhook_request_job_spec.rb │ ├── send_form_declined_webhook_request_job_spec.rb │ ├── send_form_started_webhook_request_job_spec.rb │ ├── send_form_viewed_webhook_request_job_spec.rb │ ├── send_submission_archived_webhook_request_job_spec.rb │ ├── send_submission_completed_webhook_request_job_spec.rb │ ├── send_submission_created_webhook_request_job_spec.rb │ ├── send_template_created_webhook_request_job_spec.rb │ └── send_template_updated_webhook_request_job_spec.rb ├── lib │ └── params │ │ └── base_validator_spec.rb ├── mailers │ └── previews │ │ ├── settings_mailer_preview.rb │ │ ├── submitter_mailer_preview.rb │ │ └── user_mailer_preview.rb ├── rails_helper.rb ├── requests │ ├── submissions_spec.rb │ ├── submitters_spec.rb │ ├── templates_spec.rb │ └── tools_spec.rb ├── signing_form_helper.rb ├── spec_helper.rb └── system │ ├── account_settings_spec.rb │ ├── api_settings_spec.rb │ ├── dashboard_spec.rb │ ├── email_settings_spec.rb │ ├── esign_spec.rb │ ├── newsletters_spec.rb │ ├── notifications_settings_spec.rb │ ├── personalization_settings_spec.rb │ ├── personalization_spec.rb │ ├── profile_settings_spec.rb │ ├── setup_spec.rb │ ├── sign_in_spec.rb │ ├── signing_form_spec.rb │ ├── storage_settings_spec.rb │ ├── submission_preview_spec.rb │ ├── team_settings_spec.rb │ ├── template_builder_spec.rb │ ├── template_spec.rb │ └── webhook_settings_spec.rb ├── tailwind.application.config.js ├── tailwind.config.js ├── tailwind.form.config.js ├── tmp ├── .keep └── pids │ └── .keep └── yarn.lock /.dockerignore: -------------------------------------------------------------------------------- 1 | /db/*.sqlite3 2 | 3 | **/node_modules 4 | /node_modules 5 | /coverage 6 | /doc 7 | /.git 8 | /.github 9 | /.bundle 10 | /.yardoc 11 | 12 | /log/* 13 | /tmp/* 14 | !/log/.keep 15 | !/tmp/.keep 16 | 17 | /tmp/pids/* 18 | !/tmp/pids/.keep 19 | 20 | /storage/* 21 | !/storage/.keep 22 | 23 | .byebug_history 24 | 25 | /config/master.key 26 | /public/packs-test 27 | /public/packs 28 | /attachments 29 | /docuseal 30 | .DS_Store 31 | .env 32 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "standard", 4 | "plugin:vue/vue3-recommended" 5 | ], 6 | "env": { 7 | "browser": true 8 | }, 9 | "rules": { 10 | "vue/no-deprecated-html-element-is": 0, 11 | "vue/no-mutating-props": 0, 12 | "vue/one-component-per-file": 0 13 | }, 14 | "parserOptions": { 15 | "ecmaVersion": 2022, 16 | "parser": "@babel/eslint-parser" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.html linguist-detectable=false 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle 2 | 3 | /db/*.sqlite3 4 | /db/*.sqlite3-* 5 | 6 | /log/* 7 | /tmp/* 8 | !/log/.keep 9 | !/tmp/.keep 10 | 11 | /tmp/pids/* 12 | !/tmp/pids/ 13 | !/tmp/pids/.keep 14 | 15 | /storage/* 16 | !/storage/.keep 17 | /tmp/storage/* 18 | !/tmp/storage/ 19 | !/tmp/storage/.keep 20 | 21 | /public/assets 22 | 23 | /config/master.key 24 | 25 | /public/packs 26 | /public/packs-test 27 | /node_modules 28 | /yarn-error.log 29 | yarn-debug.log* 30 | .yarn-integrity 31 | 32 | .env 33 | .DS_Store 34 | 35 | /coverage 36 | /attachments 37 | /docuseal 38 | /ee 39 | dump.rdb 40 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --require rails_helper 2 | -------------------------------------------------------------------------------- /.version: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docusealco/docuseal/c6eae66127e1cabd7c2406758c8aa96e99bd496b/.version -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: puma -p $PORT -C ./config/puma.rb 2 | -------------------------------------------------------------------------------- /Procfile.dev: -------------------------------------------------------------------------------- 1 | web: PORT=3000 bundle exec rails s -p 3000 2 | webpacker: bundle exec ./bin/shakapacker-dev-server 3 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'config/application' 4 | 5 | Rails.application.load_tasks 6 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Reporting a Vulnerability 2 | 3 | If you come across any security concern or vulnarability, please report the information via email to security@docuseal.com instead of opening a GitHub issue. We will promptly respond and will collaborate with you to validate the issue, and resolve it ASAP. 4 | 5 | **We have a bug bounty program to reward security researchers.** 6 | 7 | Out of scope vulnerabilities: 8 | 9 | - CSRF 10 | - DNSSEC, CAA, CSP headers 11 | - DNS or email security related 12 | - Rate Limiting 13 | 14 | Note: We reserve the right to classify any reported vulnerability as out of scope for the bug bounty program. 15 | -------------------------------------------------------------------------------- /app/controllers/api/submitter_email_clicks_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Api 4 | class SubmitterEmailClicksController < ApiBaseController 5 | skip_before_action :authenticate_user! 6 | skip_authorization_check 7 | 8 | def create 9 | @submitter = Submitter.find_by!(slug: params[:submitter_slug]) 10 | 11 | if params[:t] == SubmissionEvents.build_tracking_param(@submitter, 'click_email') 12 | SubmissionEvents.create_with_tracking_data(@submitter, 'click_email', request) 13 | end 14 | 15 | render json: {} 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /app/controllers/api/submitter_form_views_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Api 4 | class SubmitterFormViewsController < ApiBaseController 5 | skip_before_action :authenticate_user! 6 | skip_authorization_check 7 | 8 | def create 9 | @submitter = Submitter.find_by!(slug: params[:submitter_slug]) 10 | 11 | @submitter.opened_at = Time.current 12 | @submitter.save 13 | 14 | SubmissionEvents.create_with_tracking_data(@submitter, 'view_form', request) 15 | 16 | WebhookUrls.for_account_id(@submitter.account_id, 'form.viewed').each do |webhook_url| 17 | SendFormViewedWebhookRequestJob.perform_async('submitter_id' => @submitter.id, 18 | 'webhook_url_id' => webhook_url.id) 19 | end 20 | 21 | render json: {} 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /app/controllers/api/users_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Api 4 | class UsersController < ApiBaseController 5 | authorize_resource :current_user 6 | 7 | def show 8 | render json: current_user.as_json(only: %i[id first_name last_name email]) 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/controllers/api_settings_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class ApiSettingsController < ApplicationController 4 | def index 5 | authorize!(:read, current_user.access_token) 6 | end 7 | 8 | def create 9 | authorize!(:manage, current_user.access_token) 10 | 11 | current_user.access_token.token = SecureRandom.base58(AccessToken::TOKEN_LENGTH) 12 | 13 | current_user.access_token.save! 14 | 15 | redirect_back(fallback_location: settings_api_index_path, notice: I18n.t('api_token_has_been_updated')) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /app/controllers/encrypted_user_configs_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class EncryptedUserConfigsController < ApplicationController 4 | load_and_authorize_resource :encrypted_user_config 5 | 6 | def destroy 7 | @encrypted_user_config.destroy! 8 | 9 | redirect_back(fallback_location: root_path) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/controllers/enquiries_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class EnquiriesController < ApplicationController 4 | skip_before_action :authenticate_user! 5 | skip_authorization_check 6 | 7 | def create 8 | if params[:talk_to_sales] == 'on' 9 | Faraday.post(Docuseal::ENQUIRIES_URL, 10 | enquiry_params.merge(type: :talk_to_sales).to_json, 11 | 'Content-Type' => 'application/json') 12 | end 13 | 14 | head :ok 15 | end 16 | 17 | private 18 | 19 | def enquiry_params 20 | params.require(:user).permit(:email) 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /app/controllers/invitations_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class InvitationsController < Devise::PasswordsController 4 | end 5 | -------------------------------------------------------------------------------- /app/controllers/newsletters_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class NewslettersController < ApplicationController 4 | skip_authorization_check 5 | 6 | def show; end 7 | 8 | def update 9 | Faraday.post(Docuseal::NEWSLETTER_URL, newsletter_params.to_json, 'Content-Type' => 'application/json') 10 | rescue StandardError => e 11 | Rails.logger.error(e) 12 | ensure 13 | redirect_to root_path 14 | end 15 | 16 | private 17 | 18 | def newsletter_params 19 | params.require(:user).permit(:email) 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /app/controllers/passwords_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class PasswordsController < Devise::PasswordsController 4 | class Current < ActiveSupport::CurrentAttributes 5 | attribute :user 6 | end 7 | 8 | def create 9 | super do |resource| 10 | resource.errors.clear unless Docuseal.multitenant? 11 | end 12 | end 13 | 14 | def update 15 | super do |resource| 16 | Current.user = resource 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /app/controllers/pwa_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class PwaController < ActionController::Base 4 | end 5 | -------------------------------------------------------------------------------- /app/controllers/sms_settings_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class SmsSettingsController < ApplicationController 4 | before_action :load_encrypted_config 5 | authorize_resource :encrypted_config, only: :index 6 | authorize_resource :encrypted_config, parent: false, except: :index 7 | 8 | def index; end 9 | 10 | private 11 | 12 | def load_encrypted_config 13 | @encrypted_config = 14 | EncryptedConfig.find_or_initialize_by(account: current_account, key: 'sms_configs') 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /app/controllers/sso_settings_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class SsoSettingsController < ApplicationController 4 | before_action :load_encrypted_config 5 | authorize_resource :encrypted_config, only: :index 6 | 7 | def index; end 8 | 9 | private 10 | 11 | def load_encrypted_config 12 | @encrypted_config = 13 | EncryptedConfig.find_or_initialize_by(account: current_account, key: 'saml_configs') 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/controllers/submission_events_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class SubmissionEventsController < ApplicationController 4 | SUBMISSION_EVENT_ICONS = { 5 | 'view_form' => 'eye', 6 | 'start_form' => 'player_play', 7 | 'complete_form' => 'check', 8 | 'send_email' => 'mail_forward', 9 | 'click_email' => 'hand_click', 10 | 'api_complete_form' => 'check', 11 | 'send_reminder_email' => 'mail_forward', 12 | 'send_2fa_sms' => '2fa', 13 | 'send_sms' => 'send', 14 | 'phone_verified' => 'phone_check', 15 | 'click_sms' => 'hand_click', 16 | 'decline_form' => 'x', 17 | 'start_verification' => 'player_play', 18 | 'complete_verification' => 'check', 19 | 'invite_party' => 'user_plus' 20 | }.freeze 21 | 22 | load_and_authorize_resource :submission 23 | 24 | def index; end 25 | end 26 | -------------------------------------------------------------------------------- /app/controllers/submissions_filters_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class SubmissionsFiltersController < ApplicationController 4 | ALLOWED_NAMES = %w[ 5 | author 6 | completed_at 7 | status 8 | created_at 9 | ].freeze 10 | 11 | skip_authorization_check 12 | 13 | def show 14 | return head :not_found unless ALLOWED_NAMES.include?(params[:name]) 15 | 16 | render params[:name] 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /app/controllers/submissions_unarchive_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class SubmissionsUnarchiveController < ApplicationController 4 | load_and_authorize_resource :submission 5 | 6 | def create 7 | @submission.update!(archived_at: nil) 8 | 9 | redirect_to submission_path(@submission), notice: I18n.t('submission_has_been_unarchived') 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/controllers/submit_form_draw_signature_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class SubmitFormDrawSignatureController < ApplicationController 4 | layout false 5 | 6 | around_action :with_browser_locale, only: %i[show] 7 | skip_before_action :authenticate_user! 8 | skip_authorization_check 9 | 10 | def show 11 | @submitter = Submitter.find_by!(slug: params[:slug]) 12 | 13 | return redirect_to submit_form_completed_path(@submitter.slug) if @submitter.completed_at? 14 | 15 | if @submitter.submission.template.archived_at? || @submitter.submission.archived_at? 16 | return redirect_to submit_form_path(@submitter.slug) 17 | end 18 | 19 | render :show 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /app/controllers/template_folders_autocomplete_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class TemplateFoldersAutocompleteController < ApplicationController 4 | load_and_authorize_resource :template_folder, parent: false 5 | 6 | LIMIT = 100 7 | 8 | def index 9 | template_folders = @template_folders.joins(:templates).where(templates: { archived_at: nil }).distinct 10 | template_folders = TemplateFolders.search(template_folders, params[:q]).limit(LIMIT) 11 | 12 | render json: template_folders.as_json(only: %i[name archived_at]) 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/controllers/template_sharings_testing_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class TemplateSharingsTestingController < ApplicationController 4 | load_and_authorize_resource :template, parent: true 5 | 6 | before_action do 7 | authorize!(:manage, TemplateSharing.new(template: @template)) 8 | end 9 | 10 | def create 11 | testing_account = Accounts.find_or_create_testing_user(true_user.account).account 12 | 13 | if params[:value] == '1' 14 | TemplateSharing.create!(ability: :manage, account: testing_account, template: @template) 15 | else 16 | TemplateSharing.find_by(template: @template, account: testing_account)&.destroy! 17 | end 18 | 19 | head :ok 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /app/controllers/templates_archived_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class TemplatesArchivedController < ApplicationController 4 | load_and_authorize_resource :template, parent: false 5 | 6 | def index 7 | @templates = @templates.where.not(archived_at: nil).preload(:author, :folder, :template_accesses).order(id: :desc) 8 | @templates = Templates.search(@templates, params[:q]) 9 | 10 | @pagy, @templates = pagy(@templates, limit: 12) 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/controllers/templates_code_modal_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class TemplatesCodeModalController < ApplicationController 4 | load_and_authorize_resource :template 5 | 6 | def show; end 7 | end 8 | -------------------------------------------------------------------------------- /app/controllers/templates_folders_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class TemplatesFoldersController < ApplicationController 4 | load_and_authorize_resource :template 5 | 6 | def edit; end 7 | 8 | def update 9 | @template.folder = TemplateFolders.find_or_create_by_name(current_user, params[:name]) 10 | 11 | if @template.save 12 | redirect_back(fallback_location: template_path(@template), notice: I18n.t('document_template_has_been_moved')) 13 | else 14 | redirect_back(fallback_location: template_path(@template), notice: I18n.t('unable_to_move_template_into_folder')) 15 | end 16 | end 17 | 18 | private 19 | 20 | def template_folder_params 21 | params.require(:template_folder).permit(:name) 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /app/controllers/templates_preview_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class TemplatesPreviewController < ApplicationController 4 | load_and_authorize_resource :template 5 | 6 | def show 7 | ActiveRecord::Associations::Preloader.new( 8 | records: [@template], 9 | associations: [schema_documents: { preview_images_attachments: :blob }] 10 | ).call 11 | 12 | @template_data = 13 | @template.as_json.merge( 14 | documents: @template.schema_documents.as_json( 15 | methods: %i[metadata signed_uuid], 16 | include: { preview_images: { methods: %i[url metadata filename] } } 17 | ) 18 | ).to_json 19 | 20 | render :show, layout: 'plain' 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /app/controllers/templates_restore_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class TemplatesRestoreController < ApplicationController 4 | load_and_authorize_resource :template 5 | 6 | def create 7 | @template.update!(archived_at: nil) 8 | 9 | redirect_to template_path(@template), notice: I18n.t('template_has_been_unarchived') 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/controllers/testing_accounts_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class TestingAccountsController < ApplicationController 4 | skip_authorization_check only: :destroy 5 | 6 | def show 7 | authorize!(:manage, current_account) 8 | authorize!(:manage, current_user) 9 | 10 | impersonate_user(Accounts.find_or_create_testing_user(true_user.account)) 11 | 12 | redirect_back(fallback_location: root_path) 13 | end 14 | 15 | def destroy 16 | stop_impersonating_user 17 | 18 | redirect_back(fallback_location: root_path) 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /app/controllers/testing_api_settings_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class TestingApiSettingsController < ApplicationController 4 | def index 5 | authorize!(:manage, current_user.access_token) 6 | 7 | @webhook_url = current_account.webhook_urls.first_or_initialize 8 | 9 | authorize!(:manage, @webhook_url) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/controllers/verify_pdf_signature_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class VerifyPdfSignatureController < ApplicationController 4 | skip_authorization_check 5 | 6 | def create 7 | pdfs = 8 | params[:files].map do |file| 9 | HexaPDF::Document.new(io: file.open) 10 | end 11 | 12 | trusted_certs = Accounts.load_trusted_certs(current_account) 13 | 14 | render turbo_stream: turbo_stream.replace('result', partial: 'result', 15 | locals: { pdfs:, files: params[:files], trusted_certs: }) 16 | rescue HexaPDF::MalformedPDFError 17 | render turbo_stream: turbo_stream.replace('result', html: helpers.tag.div(I18n.t('invalid_pdf'), id: 'result')) 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /app/controllers/webhook_preferences_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class WebhookPreferencesController < ApplicationController 4 | load_and_authorize_resource :webhook_url, parent: false 5 | 6 | def update 7 | webhook_preferences_params[:events].each do |event, val| 8 | @webhook_url.events.delete(event) if val == '0' 9 | @webhook_url.events.push(event) if val == '1' && @webhook_url.events.exclude?(event) 10 | end 11 | 12 | @webhook_url.save! 13 | 14 | head :ok 15 | end 16 | 17 | private 18 | 19 | def webhook_preferences_params 20 | params.require(:webhook_url).permit(events: {}) 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /app/controllers/webhook_secret_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class WebhookSecretController < ApplicationController 4 | load_and_authorize_resource :webhook_url, parent: false 5 | 6 | def show; end 7 | 8 | def update 9 | @webhook_url.update!(secret: { 10 | webhook_secret_params[:key] => webhook_secret_params[:value] 11 | }.compact_blank) 12 | 13 | redirect_back(fallback_location: settings_webhook_path(@webhook_url), 14 | notice: I18n.t('webhook_secret_has_been_saved')) 15 | end 16 | 17 | private 18 | 19 | def webhook_secret_params 20 | params.require(:webhook_url).permit(secret: %i[key value]).fetch(:secret, {}) 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /app/javascript/elements/checkbox_group.js: -------------------------------------------------------------------------------- 1 | export default class extends HTMLElement { 2 | connectedCallback () { 3 | this.items.forEach((item) => { 4 | item.addEventListener('change', (e) => { 5 | this.items.forEach((item) => { 6 | item.checked = item === e.target && e.target.checked 7 | }) 8 | }) 9 | }) 10 | } 11 | 12 | get items () { 13 | return this.querySelectorAll('input[type="checkbox"]') 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/javascript/elements/clipboard_copy.js: -------------------------------------------------------------------------------- 1 | export default class extends HTMLElement { 2 | connectedCallback () { 3 | this.clearChecked() 4 | 5 | this.addEventListener('click', (e) => { 6 | e.stopPropagation() 7 | 8 | const text = this.dataset.text || this.innerText.trim() 9 | 10 | if (navigator.clipboard) { 11 | navigator.clipboard.writeText(text) 12 | } else { 13 | if (e.target.tagName !== 'INPUT') { 14 | alert(`Clipboard not available. Make sure you're using https://\nCopy text: ${text}`) 15 | } 16 | } 17 | }) 18 | } 19 | 20 | clearChecked () { 21 | this.querySelectorAll('input').forEach((e) => { 22 | e.checked = false 23 | }) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/javascript/elements/fetch_form.js: -------------------------------------------------------------------------------- 1 | export default class extends HTMLElement { 2 | connectedCallback () { 3 | this.form.addEventListener('submit', (e) => { 4 | e.preventDefault() 5 | 6 | this.submit() 7 | }) 8 | 9 | if (this.dataset.onload === 'true') { 10 | this.form.querySelector('button').click() 11 | } 12 | } 13 | 14 | submit () { 15 | fetch(this.form.action, { 16 | method: this.form.method, 17 | body: new FormData(this.form) 18 | }).then(async (resp) => { 19 | if (!resp.ok) { 20 | try { 21 | const data = JSON.parse(await resp.text()) 22 | 23 | if (data.error) { 24 | alert(data.error) 25 | } 26 | } catch (err) { 27 | console.error(err) 28 | } 29 | } 30 | }) 31 | } 32 | 33 | get form () { 34 | return this.querySelector('form') 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/javascript/elements/linked_input.js: -------------------------------------------------------------------------------- 1 | export default class extends HTMLElement { 2 | connectedCallback () { 3 | if (this.target) { 4 | this.input.value = this.target.value 5 | 6 | this.target.addEventListener('input', (e) => { 7 | this.input.value = e.target.value 8 | }) 9 | 10 | this.target.addEventListener('linked-input.update', (e) => { 11 | this.input.value = e.target.value 12 | }) 13 | } 14 | } 15 | 16 | get input () { 17 | return this.querySelector('input') 18 | } 19 | 20 | get target () { 21 | if (this.dataset.targetId) { 22 | const listItem = this.closest('[data-targets="dynamic-list.items"]') 23 | 24 | if (listItem) { 25 | return listItem.querySelector(`#${this.dataset.targetId}`) 26 | } else { 27 | return document.getElementById(this.dataset.targetId) 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/javascript/elements/masked_input.js: -------------------------------------------------------------------------------- 1 | export default class extends HTMLElement { 2 | connectedCallback () { 3 | const maskedToken = this.input.value 4 | 5 | this.input.addEventListener('focus', () => { 6 | this.input.value = this.dataset.token 7 | this.input.select() 8 | }) 9 | 10 | this.input.addEventListener('focusout', () => { 11 | this.input.value = maskedToken 12 | }) 13 | } 14 | 15 | get input () { 16 | return this.querySelector('input') 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/javascript/elements/menu_active.js: -------------------------------------------------------------------------------- 1 | export default class extends HTMLElement { 2 | connectedCallback () { 3 | this.querySelectorAll('a').forEach((link) => { 4 | if (document.location.pathname.startsWith(link.pathname) && !link.getAttribute('href').startsWith('http')) { 5 | link.classList.add('bg-base-300') 6 | } 7 | }) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /app/javascript/elements/prompt_password.js: -------------------------------------------------------------------------------- 1 | export default class extends HTMLElement { 2 | connectedCallback () { 3 | const input = document.createElement('input') 4 | 5 | input.type = 'hidden' 6 | input.name = 'password' 7 | input.value = prompt('Enter PDF password') 8 | 9 | this.form.append(input) 10 | 11 | this.form.requestSubmit() 12 | 13 | this.remove() 14 | } 15 | 16 | get form () { 17 | return this.closest('form') 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/javascript/elements/search_input.js: -------------------------------------------------------------------------------- 1 | export default class extends HTMLElement { 2 | connectedCallback () { 3 | this.input.addEventListener('focus', () => { 4 | if (this.title) { 5 | this.title.classList.add('hidden', 'md:block') 6 | this.input.classList.add('w-60') 7 | } 8 | }) 9 | 10 | this.input.addEventListener('blur', (e) => { 11 | if (this.title && !e.target.value) { 12 | this.title.classList.remove('hidden') 13 | this.input.classList.remove('w-60') 14 | } 15 | }) 16 | } 17 | 18 | get input () { 19 | return this.querySelector('input') 20 | } 21 | 22 | get title () { 23 | return document.querySelector(this.dataset.title) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/javascript/elements/set_date_button.js: -------------------------------------------------------------------------------- 1 | export default class extends HTMLElement { 2 | connectedCallback () { 3 | this.button.addEventListener('click', () => { 4 | this.fromInput.value = this.dataset.fromValue || '' 5 | this.toInput.value = this.dataset.toValue || '' 6 | }) 7 | } 8 | 9 | get button () { 10 | return this.querySelector('button') 11 | } 12 | 13 | get fromInput () { 14 | return document.getElementById(this.dataset.fromId) 15 | } 16 | 17 | get toInput () { 18 | return document.getElementById(this.dataset.toId) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/javascript/elements/set_origin_url.js: -------------------------------------------------------------------------------- 1 | export default class extends HTMLElement { 2 | connectedCallback () { 3 | if (this.dataset.inputId) { 4 | document.getElementById(this.dataset.inputId).value = document.location.origin 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /app/javascript/elements/set_timezone.js: -------------------------------------------------------------------------------- 1 | export default class extends HTMLElement { 2 | connectedCallback () { 3 | if (this.dataset.inputId) { 4 | const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone 5 | 6 | if (this.dataset.params === 'true') { 7 | const params = new URLSearchParams(this.input.value) 8 | 9 | params.set('timezone', timezone) 10 | 11 | this.input.value = params.toString() 12 | } else { 13 | this.input.value = timezone 14 | } 15 | } 16 | } 17 | 18 | get input () { 19 | return document.getElementById(this.dataset.inputId) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/javascript/elements/submit_form.js: -------------------------------------------------------------------------------- 1 | export default class extends HTMLElement { 2 | connectedCallback () { 3 | this.querySelector('form').requestSubmit() 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /app/javascript/elements/toggle_cookies.js: -------------------------------------------------------------------------------- 1 | export default class extends HTMLElement { 2 | connectedCallback () { 3 | this.button.addEventListener('click', () => { 4 | const expirationDate = new Date() 5 | 6 | expirationDate.setFullYear(expirationDate.getFullYear() + 10) 7 | 8 | const expires = expirationDate.toUTCString() 9 | 10 | document.cookie = this.dataset.key + '=' + this.dataset.value + '; expires=' + expires + '; path=/' 11 | }) 12 | } 13 | 14 | get button () { 15 | return this.querySelector('button') 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/javascript/elements/toggle_submit.js: -------------------------------------------------------------------------------- 1 | export default class extends HTMLElement { 2 | connectedCallback () { 3 | this.button.disabled = false 4 | 5 | this.form.addEventListener('submit', () => { 6 | this.button.disabled = true 7 | }) 8 | } 9 | 10 | disconnectedCallback () { 11 | this.button.disabled = false 12 | } 13 | 14 | get button () { 15 | return this.querySelector('[type="submit"]') 16 | } 17 | 18 | get form () { 19 | return this.querySelector('form') || this.closest('form') 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/javascript/elements/toggle_visible.js: -------------------------------------------------------------------------------- 1 | import { actionable } from '@github/catalyst/lib/actionable' 2 | 3 | export default actionable(class extends HTMLElement { 4 | trigger (event) { 5 | const elementIds = JSON.parse(this.dataset.elementIds) 6 | 7 | elementIds.forEach((elementId) => { 8 | document.getElementById(elementId).classList.toggle('hidden', (event.target.dataset.toggleId || event.target.value) !== elementId) 9 | }) 10 | } 11 | }) 12 | -------------------------------------------------------------------------------- /app/javascript/template_builder/icon_drag.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 32 | -------------------------------------------------------------------------------- /app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class ApplicationJob < ActiveJob::Base 4 | retry_on StandardError, wait: 6.seconds, attempts: 5 unless Docuseal.multitenant? 5 | end 6 | -------------------------------------------------------------------------------- /app/jobs/generate_preview_images_job.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class GeneratePreviewImagesJob 4 | include Sidekiq::Job 5 | 6 | sidekiq_options queue: :images 7 | 8 | def perform(params = {}) 9 | attachment = ActiveStorage::Attachment.find(params['attachment_id']) 10 | 11 | max_page = [attachment.metadata['pdf']['number_of_pages'].to_i - 1, 12 | Templates::ProcessDocument::MAX_NUMBER_OF_PAGES_PROCESSED].min 13 | 14 | Templates::ProcessDocument.generate_document_preview_images(attachment, attachment.download, (1..max_page), 15 | concurrency: 1) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /app/jobs/process_submission_expired_job.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class ProcessSubmissionExpiredJob 4 | include Sidekiq::Job 5 | 6 | def perform(params = {}) 7 | submission = Submission.find(params['submission_id']) 8 | 9 | return if submission.archived_at? 10 | return if submission.template.archived_at? 11 | return if submission.submitters.where.not(declined_at: nil).exists? 12 | return unless submission.submitters.exists?(completed_at: nil) 13 | 14 | WebhookUrls.for_account_id(submission.account_id, %w[submission.expired]).each do |webhook| 15 | SendSubmissionExpiredWebhookRequestJob.perform_async('submission_id' => submission.id, 16 | 'webhook_url_id' => webhook.id) 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /app/jobs/send_submitter_invitation_email_job.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class SendSubmitterInvitationEmailJob 4 | include Sidekiq::Job 5 | 6 | def perform(params = {}) 7 | submitter = Submitter.find(params['submitter_id']) 8 | 9 | return if submitter.submission.source == 'invite' && !Accounts.can_send_emails?(submitter.account, on_events: true) 10 | 11 | unless Accounts.can_send_invitation_emails?(submitter.account) 12 | Rollbar.warning("Skip email: #{submitter.account.id}") if defined?(Rollbar) 13 | 14 | return 15 | end 16 | 17 | mail = SubmitterMailer.invitation_email(submitter) 18 | 19 | Submitters::ValidateSending.call(submitter, mail) 20 | 21 | mail.deliver_now! 22 | 23 | SubmissionEvent.create!(submitter:, event_type: 'send_email') 24 | 25 | submitter.sent_at ||= Time.current 26 | submitter.save! 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /app/mailers/settings_mailer.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class SettingsMailer < ApplicationMailer 4 | def smtp_successful_setup(email) 5 | mail(to: email, from: email, subject: 'SMTP has been configured') 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/mailers/user_mailer.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class UserMailer < ApplicationMailer 4 | def invitation_email(user, invited_by: nil) 5 | @current_account = invited_by&.account || user.account 6 | @user = user 7 | @token = @user.send(:set_reset_password_token) 8 | 9 | assign_message_metadata('user_invitation', @user) 10 | 11 | I18n.with_locale(@current_account.locale) do 12 | mail(to: @user.friendly_name, 13 | subject: I18n.t('you_are_invited_to_product_name', product_name: Docuseal.product_name)) 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /app/models/account_access.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # == Schema Information 4 | # 5 | # Table name: account_accesses 6 | # 7 | # id :bigint not null, primary key 8 | # created_at :datetime not null 9 | # updated_at :datetime not null 10 | # account_id :bigint not null 11 | # user_id :bigint not null 12 | # 13 | # Indexes 14 | # 15 | # index_account_accesses_on_account_id_and_user_id (account_id,user_id) UNIQUE 16 | # 17 | # Foreign Keys 18 | # 19 | # fk_rails_... (account_id => accounts.id) 20 | # 21 | class AccountAccess < ApplicationRecord 22 | belongs_to :account 23 | belongs_to :user 24 | end 25 | -------------------------------------------------------------------------------- /app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class ApplicationRecord < ActiveRecord::Base 4 | primary_abstract_class 5 | 6 | strip_attributes 7 | end 8 | -------------------------------------------------------------------------------- /app/models/completed_document.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # == Schema Information 4 | # 5 | # Table name: completed_documents 6 | # 7 | # id :bigint not null, primary key 8 | # sha256 :string not null 9 | # created_at :datetime not null 10 | # updated_at :datetime not null 11 | # submitter_id :bigint not null 12 | # 13 | # Indexes 14 | # 15 | # index_completed_documents_on_sha256 (sha256) 16 | # index_completed_documents_on_submitter_id (submitter_id) 17 | # 18 | class CompletedDocument < ApplicationRecord 19 | belongs_to :submitter, optional: true 20 | 21 | has_one :completed_submitter, primary_key: :submitter_id, inverse_of: :completed_documents, dependent: :destroy 22 | end 23 | -------------------------------------------------------------------------------- /app/models/encrypted_user_config.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # == Schema Information 4 | # 5 | # Table name: encrypted_user_configs 6 | # 7 | # id :bigint not null, primary key 8 | # key :string not null 9 | # value :text not null 10 | # created_at :datetime not null 11 | # updated_at :datetime not null 12 | # user_id :bigint not null 13 | # 14 | # Indexes 15 | # 16 | # index_encrypted_user_configs_on_user_id (user_id) 17 | # index_encrypted_user_configs_on_user_id_and_key (user_id,key) UNIQUE 18 | # 19 | # Foreign Keys 20 | # 21 | # fk_rails_... (user_id => users.id) 22 | # 23 | class EncryptedUserConfig < ApplicationRecord 24 | belongs_to :user 25 | 26 | encrypts :value 27 | 28 | serialize :value, coder: JSON 29 | end 30 | -------------------------------------------------------------------------------- /app/models/template_access.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # == Schema Information 4 | # 5 | # Table name: template_accesses 6 | # 7 | # id :bigint not null, primary key 8 | # created_at :datetime not null 9 | # updated_at :datetime not null 10 | # template_id :bigint not null 11 | # user_id :bigint not null 12 | # 13 | # Indexes 14 | # 15 | # index_template_accesses_on_template_id_and_user_id (template_id,user_id) UNIQUE 16 | # 17 | # Foreign Keys 18 | # 19 | # fk_rails_... (template_id => templates.id) 20 | # 21 | class TemplateAccess < ApplicationRecord 22 | ADMIN_USER_ID = -1 23 | 24 | belongs_to :template 25 | belongs_to :user, optional: true 26 | end 27 | -------------------------------------------------------------------------------- /app/models/template_sharing.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # == Schema Information 4 | # 5 | # Table name: template_sharings 6 | # 7 | # id :bigint not null, primary key 8 | # ability :string not null 9 | # created_at :datetime not null 10 | # updated_at :datetime not null 11 | # account_id :bigint not null 12 | # template_id :bigint not null 13 | # 14 | # Indexes 15 | # 16 | # index_template_sharings_on_account_id_and_template_id (account_id,template_id) UNIQUE 17 | # index_template_sharings_on_template_id (template_id) 18 | # 19 | # Foreign Keys 20 | # 21 | # fk_rails_... (template_id => templates.id) 22 | # 23 | class TemplateSharing < ApplicationRecord 24 | ALL_ID = -1 25 | 26 | belongs_to :template 27 | belongs_to :account, optional: true 28 | end 29 | -------------------------------------------------------------------------------- /app/views/accounts/_compliances.html.erb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docusealco/docuseal/c6eae66127e1cabd7c2406758c8aa96e99bd496b/app/views/accounts/_compliances.html.erb -------------------------------------------------------------------------------- /app/views/accounts/_integrations.html.erb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docusealco/docuseal/c6eae66127e1cabd7c2406758c8aa96e99bd496b/app/views/accounts/_integrations.html.erb -------------------------------------------------------------------------------- /app/views/devise/mailer/reset_password_instructions.html.erb: -------------------------------------------------------------------------------- 1 |

Hello <%= @resource.email %>!

2 |

Someone has requested a link to change your password. You can do this through the link below.

3 |

<%= link_to 'Change my password', edit_password_url(@resource, reset_password_token: @token) %>

4 |

If you didn't request this, please ignore this email.

5 |

Your password won't change until you access the link above and create a new one.

6 | -------------------------------------------------------------------------------- /app/views/devise/passwords/new.html.erb: -------------------------------------------------------------------------------- 1 |
2 |

3 | <%= t('forgot_your_password_') %> 4 |

5 | <%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post, class: 'space-y-6' }) do |f| %> 6 |
7 | <%= render 'devise/shared/error_messages', resource: %> 8 |
9 | <%= f.label :email, t(:email), class: 'label' %> 10 | <%= f.email_field :email, autofocus: true, autocomplete: 'email', class: 'base-input' %> 11 |
12 |
13 |
14 | <%= f.button button_title(title: t('reset_password'), disabled_with: t('resetting_password')), class: 'base-button' %> 15 |
16 | <% end %> 17 |
18 | -------------------------------------------------------------------------------- /app/views/devise/sessions/_extra_links.html.erb: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/views/devise/sessions/_hidden_fields.html.erb: -------------------------------------------------------------------------------- 1 | <%= hidden_field_tag :redir, params[:redir] if params[:redir].present? %> 2 | -------------------------------------------------------------------------------- /app/views/devise/sessions/_otp_form.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= label_tag 'user[otp_attempt]', t('two_factor_code_from_authenticator_app'), class: 'label' %> 3 | <%= text_field_tag 'user[otp_attempt]', nil, autofocus: true, autocomplete: 'off', placeholder: 'XXX-XXX', required: true, class: 'base-input' %> 4 |
5 | -------------------------------------------------------------------------------- /app/views/devise/sessions/otp.html.erb: -------------------------------------------------------------------------------- 1 |
2 |

3 | <%= t('sign_in') %> 4 |

5 | <%= form_for(resource, as: resource_name, html: { class: 'space-y-6' }, data: { turbo: params[:redir].blank? }, url: session_path(resource_name)) do |f| %> 6 | <%= f.hidden_field :email %> 7 | <%= f.hidden_field :password %> 8 | <% if params[:redir].present? %> 9 | <%= hidden_field_tag :redir, params[:redir] %> 10 | <% end %> 11 | <%= render 'otp_form', **local_assigns %> 12 |
13 | <%= f.button button_title(title: t('sign_in'), disabled_with: t('signing_in')), class: 'base-button' %> 14 |
15 | <% end %> 16 |
17 | -------------------------------------------------------------------------------- /app/views/devise/shared/_error_messages.html.erb: -------------------------------------------------------------------------------- 1 | <% if resource.errors.any? %> 2 |
3 |
4 | <%= svg_icon('x_circle', class: 'stroke-current shrink-0 h-6 w-6') %> 5 |
6 |

7 | <%= I18n.t('errors.messages.not_saved', 8 | count: resource.errors.count, 9 | resource: resource.class.model_name.human.downcase) %>

10 | <% resource.errors.full_messages.each do |message| %> 11 |

<%= message %>

12 | <% end %> 13 |
14 |
15 |
16 | <% end %> 17 | -------------------------------------------------------------------------------- /app/views/esign_settings/_alert.html.erb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docusealco/docuseal/c6eae66127e1cabd7c2406758c8aa96e99bd496b/app/views/esign_settings/_alert.html.erb -------------------------------------------------------------------------------- /app/views/icons/_2fa.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/views/icons/_adjustments.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/views/icons/_adjustments_horizontal.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/views/icons/_alert_circle.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/views/icons/_api.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/views/icons/_archive.html.erb: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/views/icons/_arrow_right.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/views/icons/_arrow_sort.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/views/icons/_arrow_up_right.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/views/icons/_brand_discord.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/views/icons/_brand_docker.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/views/icons/_brand_github.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/views/icons/_brand_microsoft.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/views/icons/_brand_openai.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/views/icons/_brand_twitter.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/views/icons/_building.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/views/icons/_calendar.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/views/icons/_calendar_check.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/views/icons/_certificate.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/views/icons/_check.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/views/icons/_chevron_down.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/views/icons/_chevron_left.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/views/icons/_circle_arrow_right.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/views/icons/_circle_check.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/views/icons/_circle_check_filled.html.erb: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /app/views/icons/_circle_dot.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/views/icons/_circle_question.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/views/icons/_clipboard.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/views/icons/_clipboard_copy.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/views/icons/_clock.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/views/icons/_clock_cancel.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/views/icons/_clock_edit.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/views/icons/_clock_exclamation.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/views/icons/_cloud_upload.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/views/icons/_code.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/views/icons/_code_circle.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/views/icons/_copy.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/views/icons/_corner_down_right.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/views/icons/_corner_right_down.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/views/icons/_device_floppy.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/views/icons/_devices.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/views/icons/_dots_vertical.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/views/icons/_download.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/views/icons/_exclamation_circle.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/views/icons/_external_link.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/views/icons/_eye.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/views/icons/_eye_off.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/views/icons/_file_text.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/views/icons/_file_upload.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/views/icons/_files.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/views/icons/_filter.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/views/icons/_folder.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/views/icons/_folder_share.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/views/icons/_forms.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/views/icons/_hand_click.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/views/icons/_info_circle.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/views/icons/_layout_grid.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/views/icons/_layout_list.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/views/icons/_link.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/views/icons/_list.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/views/icons/_loader.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/views/icons/_lock.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/views/icons/_lock_access.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/views/icons/_lock_open.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/views/icons/_login.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/views/icons/_logout.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/views/icons/_logs.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/views/icons/_mail.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/views/icons/_mail_forward.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/views/icons/_mail_opened.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/views/icons/_mail_up.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/views/icons/_paperclip.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/views/icons/_pencil.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/views/icons/_pencil_share.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/views/icons/_phone.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/views/icons/_phone_check.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/views/icons/_player_play.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/views/icons/_plus.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/views/icons/_reload.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/views/icons/_rotate.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/views/icons/_save.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/views/icons/_search.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/views/icons/_send.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/views/icons/_settings.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/views/icons/_shield_check.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/views/icons/_sort_ascending_letters.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/views/icons/_sort_descending_numbers.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/views/icons/_sort_descending_small_big.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/views/icons/_sparkles.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/views/icons/_terminal.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/views/icons/_trash.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/views/icons/_upload.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/views/icons/_usa_flag.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/views/icons/_user.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/views/icons/_user_number.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <%= number %> 6 | 7 | -------------------------------------------------------------------------------- /app/views/icons/_user_plus.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/views/icons/_users.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/views/icons/_users_plus.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/views/icons/_world.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/views/icons/_writing.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/views/icons/_writing_sign.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/views/icons/_x.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/views/icons/_x_circle.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/views/icons/_zoom_check.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/views/layouts/_head_tags.html.erb: -------------------------------------------------------------------------------- 1 | 2 | <%= content_for(:html_title) || (signed_in? ? 'DocuSeal' : 'DocuSeal | Open Source Document Signing') %> 3 | 4 | <%= render 'shared/meta' %> 5 | -------------------------------------------------------------------------------- /app/views/layouts/form.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= render 'layouts/head_tags' %> 5 | 6 | <%= csrf_meta_tags %> 7 | <%= csp_meta_tag %> 8 | <% if ENV['ROLLBAR_CLIENT_TOKEN'] %> 9 | 10 | <%= javascript_pack_tag 'rollbar', 'form', defer: true %> 11 | <% else %> 12 | <%= javascript_pack_tag 'form', defer: true %> 13 | <% end %> 14 | <%= stylesheet_pack_tag 'form', media: 'all' %> 15 | <%= render 'shared/posthog' if ENV['POSTHOG_TOKEN'] %> 16 | 17 | 18 | <%= yield %> 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/views/layouts/mailer.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | <%= yield %> 10 | <%= render partial: 'shared/mailer_attribution' %> 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/views/mfa_setup/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_for '', url: mfa_setup_path, data: { turbo_frame: :_top }, html: { id: 'mfa_form' } do |f| %> 2 |

3 | <%= t('use_an_authenticator_mobile_app_like_google_authenticator_or_1password_to_scan_the_qr_code_below') %> 4 |

5 |
6 | <%== RQRCode::QRCode.new(@provision_url).as_svg(viewbox: true, svg_attributes: { class: 'md:w-80 md:h-80 my-4 mx-auto' }) %> 7 |
8 |
9 | <%= f.text_field :otp_attempt, required: true, placeholder: 'XXX-XXX', class: 'base-input text-center' %> 10 | 11 | <%= @error_message %> 12 | 13 |
14 |
15 | <%= f.button button_title(title: t('save')), class: 'base-button' %> 16 |
17 | <% end %> 18 | -------------------------------------------------------------------------------- /app/views/mfa_setup/edit.html.erb: -------------------------------------------------------------------------------- 1 | <%= render 'shared/turbo_modal', title: t('remove_2fa') do %> 2 | <%= form_for '', url: mfa_setup_path, method: :delete, data: { turbo_frame: :_top } do |f| %> 3 |
4 | <%= f.text_field :otp_attempt, required: true, placeholder: 'XXX-XXX', class: 'base-input text-center' %> 5 | 6 | <%= @error_message %> 7 | 8 |
9 |
10 | <%= f.button button_title(title: t('remove_2fa')), class: 'base-button' %> 11 |
12 | <% end %> 13 | <% end %> 14 | -------------------------------------------------------------------------------- /app/views/mfa_setup/new.html.erb: -------------------------------------------------------------------------------- 1 | <%= render 'shared/turbo_modal', title: t('setup_2fa') do %> 2 | <%= render 'mfa_setup/form' %> 3 | <% end %> 4 | -------------------------------------------------------------------------------- /app/views/mfa_setup/show.html.erb: -------------------------------------------------------------------------------- 1 |
2 |

3 | <%= t('setup_2fa') %> 4 |

5 | <%= render 'mfa_setup/form' %> 6 |
7 | -------------------------------------------------------------------------------- /app/views/newsletters/show.html.erb: -------------------------------------------------------------------------------- 1 |
2 |

👨‍💻 <%= t('developer_newsletters') %>

3 | <%= form_for current_user, url: newsletter_path do |f| %> 4 |
5 | <%= f.email_field :email, placeholder: t('email'), required: true, class: 'base-input' %> 6 |
7 |
8 | <%= f.button button_title, class: 'base-button' %> 9 |
10 |
11 | <%= t('skip') %> 12 |
13 | <% end %> 14 |
15 | -------------------------------------------------------------------------------- /app/views/notifications_settings/_reminder_banner.html.erb: -------------------------------------------------------------------------------- 1 | <%= render 'reminder_placeholder' %> 2 | -------------------------------------------------------------------------------- /app/views/notifications_settings/_reminder_placeholder.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= svg_icon('info_circle', class: 'w-6 h-6') %> 3 |
4 |

5 | <%= t('unlock_with_docuseal_pro') %> 6 |

7 |

8 | <%= t('send_automatic_email_reminders_to_your_recipients') %> 9 |
10 | " data-turbo="false"> 11 | <%= t('learn_more') %> 12 | 13 |

14 |
15 |
16 | -------------------------------------------------------------------------------- /app/views/personalization_settings/_form_customization_settings.html.erb: -------------------------------------------------------------------------------- 1 | <%= render 'form_toggle_options' %> 2 | -------------------------------------------------------------------------------- /app/views/personalization_settings/_form_toggle_options.html.erb: -------------------------------------------------------------------------------- 1 | <% account_config = AccountConfig.where(account: current_account, key: AccountConfig::FORM_WITH_CONFETTI_KEY).first_or_initialize(value: true) %> 2 | <% if can?(:manage, account_config) %> 3 |
4 | <%= form_for account_config, url: account_configs_path, method: :post do |f| %> 5 | <%= f.hidden_field :key %> 6 |
7 | 8 | <%= t('show_confetti_on_successful_completion') %> 9 | 10 | <%= f.check_box :value, { class: 'toggle', checked: account_config.value != false, onchange: 'this.form.requestSubmit()' }, '1', '0' %> 11 |
12 | <% end %> 13 |
14 | <% end %> 15 | -------------------------------------------------------------------------------- /app/views/personalization_settings/_logo_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= render 'logo_placeholder' %> 2 | -------------------------------------------------------------------------------- /app/views/personalization_settings/_logo_placeholder.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= svg_icon('info_circle', class: 'w-6 h-6') %> 3 |
4 |

5 | <%= t('unlock_with_docuseal_pro') %> 6 |

7 |

8 | <%= t('display_your_company_name_and_logo_when_signing_documents') %> 9 |
10 | " data-turbo="false"> 11 | <%= t('learn_more') %> 12 | 13 |

14 |
15 |
16 | -------------------------------------------------------------------------------- /app/views/profile/_email_configs.html.erb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docusealco/docuseal/c6eae66127e1cabd7c2406758c8aa96e99bd496b/app/views/profile/_email_configs.html.erb -------------------------------------------------------------------------------- /app/views/pwa/manifest.json.erb: -------------------------------------------------------------------------------- 1 | { 2 | "name": "<%= Docuseal.product_name %>", 3 | "short_name": "<%= Docuseal.product_name %>", 4 | "id": "/", 5 | "icons": [ 6 | { 7 | "src": "/logo.svg", 8 | "type": "image/svg+xml", 9 | "sizes": "any" 10 | }, 11 | { 12 | "src": "/apple-touch-icon.png", 13 | "type": "image/png", 14 | "sizes": "192x192" 15 | } 16 | ], 17 | "start_url": "/", 18 | "display": "standalone", 19 | "scope": "/", 20 | "orientation": "natural", 21 | "description": "<%= Docuseal.product_name %> is an open source platform that provides secure and efficient digital document signing and processing.", 22 | "categories": ["productivity", "utilities"], 23 | "theme_color": "#FAF7F4", 24 | "background_color": "#FAF7F4" 25 | } 26 | -------------------------------------------------------------------------------- /app/views/scripts/_autosize_field.html.erb: -------------------------------------------------------------------------------- 1 | 20 | -------------------------------------------------------------------------------- /app/views/send_submission_email/success.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | <%= render 'start_form/banner' %> 6 |
7 |
8 | <%= t('email_has_been_sent') %> 9 |
10 |
11 |
12 |
13 | <%= render 'shared/attribution', link_path: '/start' %> 14 | -------------------------------------------------------------------------------- /app/views/settings_mailer/smtp_successful_setup.html.erb: -------------------------------------------------------------------------------- 1 |

SMTP has been successfully configured!

2 | -------------------------------------------------------------------------------- /app/views/shared/_app_tour.html.erb: -------------------------------------------------------------------------------- 1 | <% if current_user.created_at > 2.weeks.ago || params[:tour] == 'true' %> 2 | <% user_config = current_user.user_configs.find_by(key: UserConfig::SHOW_APP_TOUR) || UserConfig.new(key: UserConfig::SHOW_APP_TOUR, user: current_user) %> 3 | <% if user_config.new_record? || user_config.value || params[:tour] == 'true' %> 4 | 5 | <% end %> 6 | <% end %> 7 | -------------------------------------------------------------------------------- /app/views/shared/_attribution.html.erb: -------------------------------------------------------------------------------- 1 | <%= render 'shared/powered_by', with_counter: local_assigns[:with_counter], link_path: local_assigns[:link_path] %> 2 | -------------------------------------------------------------------------------- /app/views/shared/_button_title.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | <%= icon %> 4 | <% if title %> 5 | <%= title %> 6 | <% end %> 7 | 8 | 9 | 10 | 11 | <%= local_assigns[:icon_disabled] || svg_icon('loader', class: 'w-5 h-5 animate-spin') %> 12 | <%= disabled_with %>... 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/views/shared/_classes.html.erb: -------------------------------------------------------------------------------- 1 | <% 'stats stat stat-figure stat-title stat-value text-accent w-fit hover:bg-white dropdown-open border-base-content/30 before:border-base-content/30' %> 2 | -------------------------------------------------------------------------------- /app/views/shared/_demo_alert.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | <%= t('demo_environment') %> 5 |
6 | <%= t('create_a_new_template_document_form_or_submit_the_existing_one_html', new_template_link: new_template_path, start_form_link: start_form_url(slug: ::Template.first&.slug)) %> 😊 7 |
8 |
9 |
10 | -------------------------------------------------------------------------------- /app/views/shared/_email_attribution.html.erb: -------------------------------------------------------------------------------- 1 |

2 | --- 3 |

4 |

5 | <% if @current_account&.testing? %> 6 | <%= t('sent_using_product_name_in_testing_mode_html', product_url: "#{Docuseal::PRODUCT_EMAIL_URL}/start", product_name: Docuseal.product_name) %> 7 | <% else %> 8 | <%= t('sent_using_product_name_free_document_signing_html', product_url: "#{Docuseal::PRODUCT_EMAIL_URL}/start", product_name: Docuseal.product_name) %> 9 | <% end %> 10 |

11 | -------------------------------------------------------------------------------- /app/views/shared/_field_error.html.erb: -------------------------------------------------------------------------------- 1 | <% 'input-error' %> 2 | 3 | <%= message %> 4 | 5 | -------------------------------------------------------------------------------- /app/views/shared/_flash.html.erb: -------------------------------------------------------------------------------- 1 | 18 | -------------------------------------------------------------------------------- /app/views/shared/_github.html.erb: -------------------------------------------------------------------------------- 1 | 2 | GitHub Repo stars 3 | 4 | -------------------------------------------------------------------------------- /app/views/shared/_html_modal.html.erb: -------------------------------------------------------------------------------- 1 | <% uuid = local_assigns[:uuid] || SecureRandom.uuid %> 2 | 3 | 4 | 20 | -------------------------------------------------------------------------------- /app/views/shared/_mailer_attribution.html.erb: -------------------------------------------------------------------------------- 1 | <%= render 'shared/email_attribution' %> 2 | -------------------------------------------------------------------------------- /app/views/shared/_navbar_buttons.html.erb: -------------------------------------------------------------------------------- 1 | <%= link_to Docuseal.multitenant? ? console_redirect_index_path(redir: "#{Docuseal::CONSOLE_URL}/plans") : "#{Docuseal::CLOUD_URL}/sign_up?#{{ redir: "#{Docuseal::CONSOLE_URL}/on_premises" }.to_query}", class: 'hidden md:inline-flex btn btn-warning btn-sm', data: { prefetch: false } do %> 2 | <%= t('upgrade') %> 3 | <% end %> 4 | <% if signed_in? && current_user != true_user %> 5 | 6 | <%= render 'shared/test_alert' %> 7 | <% end %> 8 | 9 | -------------------------------------------------------------------------------- /app/views/shared/_navbar_warning.html.erb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docusealco/docuseal/c6eae66127e1cabd7c2406758c8aa96e99bd496b/app/views/shared/_navbar_warning.html.erb -------------------------------------------------------------------------------- /app/views/shared/_plausible.html.erb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docusealco/docuseal/c6eae66127e1cabd7c2406758c8aa96e99bd496b/app/views/shared/_plausible.html.erb -------------------------------------------------------------------------------- /app/views/shared/_powered_by.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <% if local_assigns[:with_counter] %> 3 | <% count = CompletedSubmitter.distinct.count(:submission_id) %> 4 | <% if count > 1 %> 5 | <%= t('count_documents_signed_with_html', count:) %> 6 | <% else %> 7 | <%= t('powered_by') %> 8 | <% end %> 9 | <% else %> 10 | <%= t('powered_by') %> 11 | <% end %> 12 | <%= Docuseal.product_name %> - <%= t('open_source_documents_software') %> 13 |
14 | -------------------------------------------------------------------------------- /app/views/shared/_review_form.html.erb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docusealco/docuseal/c6eae66127e1cabd7c2406758c8aa96e99bd496b/app/views/shared/_review_form.html.erb -------------------------------------------------------------------------------- /app/views/shared/_settings_nav_extra.html.erb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docusealco/docuseal/c6eae66127e1cabd7c2406758c8aa96e99bd496b/app/views/shared/_settings_nav_extra.html.erb -------------------------------------------------------------------------------- /app/views/shared/_settings_nav_extra2.html.erb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docusealco/docuseal/c6eae66127e1cabd7c2406758c8aa96e99bd496b/app/views/shared/_settings_nav_extra2.html.erb -------------------------------------------------------------------------------- /app/views/shared/_test_alert.html.erb: -------------------------------------------------------------------------------- 1 | <% if signed_in? && current_user != true_user && current_account.testing? %> 2 | 18 | <% end %> 19 | -------------------------------------------------------------------------------- /app/views/shared/_test_mode_toggle.html.erb: -------------------------------------------------------------------------------- 1 | <% if can?(:manage, EncryptedConfig) || (current_user != true_user && current_account.testing?) %> 2 | <%= form_for '', url: testing_account_path, method: current_account.testing? ? :delete : :get, html: { class: 'flex' } do |f| %> 3 | 9 | <% end %> 10 | <% end %> 11 | -------------------------------------------------------------------------------- /app/views/shared/_title.html.erb: -------------------------------------------------------------------------------- 1 | <%= render 'shared/logo' %> 2 | DocuSeal 3 | -------------------------------------------------------------------------------- /app/views/shared/_turbo_modal.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 17 |
18 |
19 | -------------------------------------------------------------------------------- /app/views/sms_settings/_placeholder.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= svg_icon('info_circle', class: 'w-6 h-6') %> 3 |
4 |

5 | <%= t('send_signature_requests_via_sms') %> 6 |

7 |

8 | <%= t('unlock_with_docuseal_pro') %> 9 |
10 | " data-turbo="false"> 11 | <%= t('learn_more') %> 12 | 13 |

14 |
15 |
16 | -------------------------------------------------------------------------------- /app/views/sms_settings/index.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= render 'shared/settings_nav' %> 3 |
4 |

SMS

5 | <%= render 'placeholder' %> 6 |
7 |
8 |
9 | -------------------------------------------------------------------------------- /app/views/sso_settings/_placeholder.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= svg_icon('info_circle', class: 'w-6 h-6') %> 3 |
4 |

5 | <%= t('single_sign_on_with_saml_2_0') %> 6 |

7 |

8 | <%= t('unlock_with_docuseal_pro') %> 9 |
10 | " data-turbo="false"> 11 | <%= t('learn_more') %> 12 | 13 |

14 |
15 |
16 | -------------------------------------------------------------------------------- /app/views/sso_settings/index.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= render 'shared/settings_nav' %> 3 |
4 |

SAML SSO

5 | <%= render 'placeholder' %> 6 |
7 |
8 |
9 | -------------------------------------------------------------------------------- /app/views/start_form/_banner.html.erb: -------------------------------------------------------------------------------- 1 | <%= render 'start_form/docuseal_logo' %> 2 | -------------------------------------------------------------------------------- /app/views/start_form/_docuseal_logo.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | <%= render 'shared/logo', width: '50px', height: '50px' %> 4 | 5 |

DocuSeal

6 |
7 | -------------------------------------------------------------------------------- /app/views/storage_settings/_disk_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= f.fields_for :value do |ff| %> 2 | <%= ff.hidden_field :service, value: 'disk' %> 3 | <% end %> 4 |
5 | <%= svg_icon('info_circle', class: 'stroke-current flex-shrink-0 w-6 h-6') %> 6 |
7 |

8 | <%= t('store_all_files_on_disk') %> 9 |

10 |

11 | <%= t('no_configs_are_needed_but_make_sure_your_disk_is_persistent') %> 12 |
13 | (<%= t('not_suitable_for_heroku_and_other_paas') %>) 14 |

15 |
16 |
17 | -------------------------------------------------------------------------------- /app/views/submissions/_annotation.html.erb: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/views/submissions/_bulk_send_placeholder.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= svg_icon('info_circle', class: 'w-6 h-6') %> 3 |
4 |

5 | <%= t('bulk_send_from_excel_xlsx_or_csv') %> 6 |

7 |

8 | <%= t('unlock_with_docuseal_pro') %> 9 |
10 | " data-turbo="false"> 11 | <%= t('learn_more') %> 12 | 13 |

14 |
15 |
16 | -------------------------------------------------------------------------------- /app/views/submissions/_email_stats.html.erb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docusealco/docuseal/c6eae66127e1cabd7c2406758c8aa96e99bd496b/app/views/submissions/_email_stats.html.erb -------------------------------------------------------------------------------- /app/views/submissions/_error.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <% if local_assigns[:error] %> 3 |
4 | <%= local_assigns[:error] %> 5 |
6 | <% end %> 7 |
8 | -------------------------------------------------------------------------------- /app/views/submissions/_link_form.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 | <%= render 'shared/clipboard_copy', icon: 'copy', text: start_form_url(slug: template.slug), class: 'base-button w-full block', icon_class: 'w-6 h-6 text-white', copy_title: t('copy'), copied_title: t('copied') %> 6 |
7 |
8 |
9 | <%= t('or_embed_on_your_website') %> 10 |
11 | <%= render 'templates/embedding', template: %> 12 |
13 | 14 | <%= t('close') %> 15 | 16 |
17 |
18 | -------------------------------------------------------------------------------- /app/views/submissions/_list_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= render 'submissions/bulk_send_placeholder' %> 2 | -------------------------------------------------------------------------------- /app/views/submissions/_logo.html.erb: -------------------------------------------------------------------------------- 1 | <%= render 'shared/logo', width: 40, height: 40 %> 2 | -------------------------------------------------------------------------------- /app/views/submissions/_message_fields.html.erb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docusealco/docuseal/c6eae66127e1cabd7c2406758c8aa96e99bd496b/app/views/submissions/_message_fields.html.erb -------------------------------------------------------------------------------- /app/views/submissions/_preview_tags.html.erb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docusealco/docuseal/c6eae66127e1cabd7c2406758c8aa96e99bd496b/app/views/submissions/_preview_tags.html.erb -------------------------------------------------------------------------------- /app/views/submissions/_send_sms.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= render 'sms_settings/placeholder' %> 3 |
4 | -------------------------------------------------------------------------------- /app/views/submissions/_send_sms_button.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 | <%= link_to submitter.sent_at? ? t('re_send_sms') : t('send_sms'), Docuseal.multitenant? ? console_redirect_index_path(redir: "#{Docuseal::CONSOLE_URL}/plans") : "#{Docuseal::CLOUD_URL}/sign_up?#{{ redir: "#{Docuseal::CONSOLE_URL}/on_premises" }.to_query}", class: 'btn btn-sm btn-primary text-gray-400 w-full' %> 4 |
5 |
6 | -------------------------------------------------------------------------------- /app/views/submissions_filters/_actions.html.erb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docusealco/docuseal/c6eae66127e1cabd7c2406758c8aa96e99bd496b/app/views/submissions_filters/_actions.html.erb -------------------------------------------------------------------------------- /app/views/submissions_filters/author.html.erb: -------------------------------------------------------------------------------- 1 | <%= render 'filter_modal', title: t('author'), default_params: params.permit(*(Submissions::Filter::ALLOWED_PARAMS - ['author'])) do %> 2 |
3 |
4 | <%= select_tag :author, options_for_select(current_account.users.accessible_by(current_ability).where.not(role: :integration).where(account: current_account).map { |u| [u.full_name, u.email] }, params[:author].presence || current_user.email), required: true, class: 'base-select' %> 5 |
6 |
7 | <% end %> 8 | -------------------------------------------------------------------------------- /app/views/submissions_filters/status.html.erb: -------------------------------------------------------------------------------- 1 | <%= render 'filter_modal', title: t('status'), default_params: params.permit(*(Submissions::Filter::ALLOWED_PARAMS - %w[status])) do %> 2 |
3 |
4 |
5 | <% ['', 'pending', 'completed', 'partially_completed', 'sent', 'opened', 'declined', 'expired'].each do |status| %> 6 | 10 | <% end %> 11 |
12 |
13 |
14 | <% end %> 15 | -------------------------------------------------------------------------------- /app/views/submit_form/_annotations.html.erb: -------------------------------------------------------------------------------- 1 | <%= render partial: 'submissions/annotation', collection: annots, as: :annot %> 2 | -------------------------------------------------------------------------------- /app/views/submit_form/_banner.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= render 'submit_form/docuseal_logo' %> 3 |
4 | -------------------------------------------------------------------------------- /app/views/submit_form/_decline_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_for '', url: submit_form_decline_index_path(submitter.slug), method: :post do |f| %> 2 |
3 | <%= t(:notify_the_sender_with_the_reason_you_declined) %> 4 |
5 |
6 | <%= f.text_area :reason, required: true, class: 'base-input w-full py-2 h-40', dir: 'auto', placeholder: t('provide_a_reason'), style: 'height: 200px', rows: '10' %> 7 |
8 | 9 | <%= f.button button_title(title: t(:decline)), class: 'base-button' %> 10 | 11 | <% end %> 12 | -------------------------------------------------------------------------------- /app/views/submit_form/_docuseal_logo.html.erb: -------------------------------------------------------------------------------- 1 | 2 | <%= render 'shared/logo', class: 'w-9 h-9 md:w-12 md:h-12' %> 3 | <%= Docuseal.product_name %> 4 | 5 | -------------------------------------------------------------------------------- /app/views/submit_form/success.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 | <%= svg_icon('circle_check', class: 'w-10 h-10 text-green-600') %> 8 |
9 |
10 | <%= t('completed_successfully') %> 11 |
12 |
13 |
14 |
15 |
16 |
17 | <%= render 'shared/attribution', link_path: '/start' %> 18 | -------------------------------------------------------------------------------- /app/views/submitter_mailer/_custom_content.html.erb: -------------------------------------------------------------------------------- 1 | <%= auto_link(simple_format(MarkdownToHtml.call(h(ReplaceEmailVariables.call(local_assigns[:content], submitter: local_assigns[:submitter], sig: local_assigns[:sig]))))) %> 2 | -------------------------------------------------------------------------------- /app/views/submitter_mailer/completed_email.html.erb: -------------------------------------------------------------------------------- 1 | <% if @body.present? %> 2 | <%= render 'custom_content', content: @body, submitter: @submitter %> 3 | <% else %> 4 |

<%= t('hi_there') %>,

5 |

<%= I18n.t(:name_has_been_completed_by_submitters, name: @submitter.submission.template.name, submitters: @submitter.submission.submitters.order(:completed_at).map { |e| e.name || e.email || e.phone }.uniq.join(', ')) %>

6 |

<%= link_to submission_url(@submitter.submission), submission_url(@submitter.submission) %>

7 | <% end %> 8 | -------------------------------------------------------------------------------- /app/views/submitter_mailer/declined_email.html.erb: -------------------------------------------------------------------------------- 1 |

<%= t('hi_there') %>,

2 |

<%= t('name_declined_by_submitter_with_the_following_reason', name: @submitter.submission.template.name, submitter: @submitter.name || @submitter.email || @submitter.phone) %>

3 | <%= simple_format(h(@submitter.submission_events.find_by(event_type: :decline_form).data['reason'])) %> 4 |

<%= link_to submission_url(@submitter.submission), submission_url(@submitter.submission) %>

5 | -------------------------------------------------------------------------------- /app/views/submitter_mailer/documents_copy_email.html.erb: -------------------------------------------------------------------------------- 1 | <% if @body.present? %> 2 | <%= render 'custom_content', content: @body, submitter: @submitter, sig: @sig %> 3 | <% else %> 4 |

<%= t('hi_there') %>,

5 |

<%= t('please_check_the_copy_of_your_name_in_the_email_attachments', name: @submitter.submission.template.name) %> 6 |

<%= t('alternatively_you_can_review_and_download_your_copy_using_the_link_below') %>

7 |

8 | <%= link_to @submitter.template.name, submissions_preview_url(@submitter.submission.slug, { sig: @sig }.compact) %> 9 |

10 |

11 | <%= t('thanks') %>,
<%= @current_account.name %> 12 |

13 | <% end %> 14 | -------------------------------------------------------------------------------- /app/views/template_folders/_folder.html.erb: -------------------------------------------------------------------------------- 1 | <% is_long = folder.name.size > 32 %> 2 | 3 | <% if !is_long %> 4 | <%= svg_icon('folder', class: 'w-6 h-6') %> 5 | <% end %> 6 |
7 | <% if is_long %> 8 | <%= svg_icon('folder', class: 'w-6 h-6 inline') %> 9 | <% end %> 10 | <%= folder.name %> 11 |
12 |
13 | -------------------------------------------------------------------------------- /app/views/template_folders/edit.html.erb: -------------------------------------------------------------------------------- 1 | <%= render 'shared/turbo_modal', title: t('rename_folder') do %> 2 | <%= form_for @template_folder, url: folder_path(@template_folder), data: { turbo_frame: :_top }, html: { autocomplete: :off } do |f| %> 3 |
4 | <%= f.text_field :name, required: true, placeholder: "#{t('folder_name')}...", class: 'base-input w-full', autofocus: true, dir: 'auto' %> 5 |
6 |
7 | <%= f.button button_title(title: t('rename'), disabled_with: t('saving')), class: 'base-button' %> 8 |
9 | <% end %> 10 | <% end %> 11 | -------------------------------------------------------------------------------- /app/views/templates/_access_icon.html.erb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docusealco/docuseal/c6eae66127e1cabd7c2406758c8aa96e99bd496b/app/views/templates/_access_icon.html.erb -------------------------------------------------------------------------------- /app/views/templates_code_modal/_placeholder.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= svg_icon('info_circle', class: 'w-6 h-6') %> 3 |
4 |

5 | <%= t('api_and_embedding') %> 6 |

7 |

8 | <%= t('unlock_with_docuseal_pro') %> 9 |
10 | " data-turbo="false"> 11 | <%= t('learn_more') %> 12 | 13 |

14 |
15 |
16 | -------------------------------------------------------------------------------- /app/views/templates_code_modal/_preferences.html.erb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docusealco/docuseal/c6eae66127e1cabd7c2406758c8aa96e99bd496b/app/views/templates_code_modal/_preferences.html.erb -------------------------------------------------------------------------------- /app/views/templates_folders/edit.html.erb: -------------------------------------------------------------------------------- 1 | <%= render 'shared/turbo_modal', title: t('move_into_folder') do %> 2 | <%= form_for '', url: template_folder_path(@template), method: :put, data: { turbo_frame: :_top }, html: { autocomplete: :off } do |f| %> 3 |
4 | 5 | <%= f.text_field :name, required: true, placeholder: "#{t('new_folder_name')}...", class: 'base-input w-full', autofocus: true %> 6 | 7 |
8 |
9 | <%= f.button button_title(title: t('move'), disabled_with: t('moving')), class: 'base-button' %> 10 |
11 | <% end %> 12 | <% end %> 13 | -------------------------------------------------------------------------------- /app/views/templates_preferences/_access.html.erb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docusealco/docuseal/c6eae66127e1cabd7c2406758c8aa96e99bd496b/app/views/templates_preferences/_access.html.erb -------------------------------------------------------------------------------- /app/views/templates_preferences/_form_fields.html.erb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docusealco/docuseal/c6eae66127e1cabd7c2406758c8aa96e99bd496b/app/views/templates_preferences/_form_fields.html.erb -------------------------------------------------------------------------------- /app/views/templates_preview/show.html.erb: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/views/templates_uploads/show.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
4 |
5 | <%= render 'shared/logo', width: 50, height: 50, class: 'mx-auto animate-bounce' %> 6 | 7 | <%= t('processing') %>... 8 | 9 |
10 |
11 |
12 | 13 | <%= form_for '', url: templates_upload_path, method: :post, class: 'hidden' do %> 14 | 15 | 16 | 17 | <% end %> 18 | 19 | -------------------------------------------------------------------------------- /app/views/user_mailer/invitation_email.html.erb: -------------------------------------------------------------------------------- 1 |

<%= @user.first_name.present? ? t('hello_name', name: @user.first_name) : t('hi_there') %>,

2 |

<%= t('you_have_been_invited_to_account_name_product_name_please_sign_up_using_the_link_below_', account_name: @user.account.name, product_name: Docuseal.product_name) %>

3 |

<%= link_to t('sign_up'), invitation_url(reset_password_token: @token) %>

4 |

<%= t('please_contact_us_by_replying_to_this_email_if_you_didn_t_request_this') %>

5 |

6 | <%= t('thanks') %>,
<%= @current_account.name %> 7 |

8 | -------------------------------------------------------------------------------- /app/views/users/_bottom_links.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <% if %w[archived integration].include?(params[:status]) %> 3 | <%= link_to t('view_active'), settings_users_path, class: 'link text-sm' %> 4 | <% else %> 5 | <% archived_exists = current_account.users.archived.where.not(role: 'integration').exists? %> 6 | <% if current_account.users.active.exists?(role: 'integration') %> 7 | <%= link_to t('view_embedding_users'), settings_integration_users_path, class: 'link text-sm' %> 8 | <% if archived_exists %> 9 | | 10 | <% end %> 11 | <% end %> 12 | <% if archived_exists %> 13 | <%= link_to t('view_archived'), settings_archived_users_path, class: 'link text-sm' %> 14 | <% end %> 15 | <% end %> 16 |
17 | -------------------------------------------------------------------------------- /app/views/users/_extra_buttons.html.erb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docusealco/docuseal/c6eae66127e1cabd7c2406758c8aa96e99bd496b/app/views/users/_extra_buttons.html.erb -------------------------------------------------------------------------------- /app/views/users/edit.html.erb: -------------------------------------------------------------------------------- 1 | <%= render 'shared/turbo_modal', title: t('edit_user') do %> 2 | <%= render 'form', user: @user %> 3 | <% end %> 4 | -------------------------------------------------------------------------------- /app/views/users/new.html.erb: -------------------------------------------------------------------------------- 1 | <%= render 'shared/turbo_modal', title: t('new_user') do %> 2 | <%= render 'form', user: @user %> 3 | <% end %> 4 | -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_PATH = File.expand_path("../config/application", __dir__) 3 | require_relative "../config/boot" 4 | require "rails/commands" 5 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative "../config/boot" 3 | require "rake" 4 | Rake.application.run 5 | -------------------------------------------------------------------------------- /bin/shakapacker: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | ENV["RAILS_ENV"] ||= "development" 4 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", __FILE__) 5 | 6 | require "bundler/setup" 7 | require "shakapacker" 8 | require "shakapacker/webpack_runner" 9 | 10 | APP_ROOT = File.expand_path("..", __dir__) 11 | Dir.chdir(APP_ROOT) do 12 | Shakapacker::WebpackRunner.run(ARGV) 13 | end 14 | -------------------------------------------------------------------------------- /bin/shakapacker-dev-server: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | ENV["RAILS_ENV"] ||= "development" 4 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", __FILE__) 5 | 6 | require "bundler/setup" 7 | require "shakapacker" 8 | require "shakapacker/dev_server_runner" 9 | 10 | APP_ROOT = File.expand_path("..", __dir__) 11 | Dir.chdir(APP_ROOT) do 12 | Shakapacker::DevServerRunner.run(ARGV) 13 | end 14 | -------------------------------------------------------------------------------- /bin/yarn: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | APP_ROOT = File.expand_path("..", __dir__) 4 | Dir.chdir(APP_ROOT) do 5 | yarn = ENV["PATH"].split(File::PATH_SEPARATOR). 6 | select { |dir| File.expand_path(dir) != __dir__ }. 7 | product(["yarn", "yarnpkg", "yarn.cmd", "yarn.ps1"]). 8 | map { |dir, file| File.expand_path(file, dir) }. 9 | find { |file| File.executable?(file) } 10 | 11 | if yarn 12 | exec yarn, *ARGV 13 | else 14 | $stderr.puts "Yarn executable was not detected in the system." 15 | $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install" 16 | exit 1 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'config/environment' 4 | 5 | run Rails.application 6 | Rails.application.load_server 7 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) 4 | 5 | require_relative 'dotenv' 6 | 7 | require 'bundler/setup' # Set up gems listed in the Gemfile. 8 | require 'bootsnap/setup' # Speed up boot time by caching expensive operations. 9 | -------------------------------------------------------------------------------- /config/brakeman.ignore: -------------------------------------------------------------------------------- 1 | { 2 | "ignored_warnings": [ 3 | { 4 | "fingerprint": "25f4ce5fee1e1180fa1919dc4ee78db3ab3457a956e4679503aa745771a43836", 5 | "note": "Permitted parameters are necessary for creating submitters via API" 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Load the Rails application. 4 | require_relative 'application' 5 | 6 | # Initialize the Rails application. 7 | Rails.application.initialize! 8 | -------------------------------------------------------------------------------- /config/initializers/better_html.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | if defined?(BetterHtml) 4 | BetterHtml.configure do |config| 5 | config.template_exclusion_filter = ->(filename) { filename.include?('/gems/') } 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /config/initializers/csv.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'csv' 4 | -------------------------------------------------------------------------------- /config/initializers/field_error_proc.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | ActionView::Base.field_error_proc = proc do |html_tag, instance| 4 | next html_tag if html_tag.starts_with?(' 9 | -------------------------------------------------------------------------------- /config/sidekiq.yml: -------------------------------------------------------------------------------- 1 | queues: 2 | - [default, 1] 3 | - [webhooks, 1] 4 | - [sms, 2] 5 | - [images, 1] 6 | - [mailers, 1] 7 | - [recurrent, 1] 8 | - [rollbar, 1] 9 | 10 | production: 11 | :concurrency: 15 12 | :max_retries: 3 13 | -------------------------------------------------------------------------------- /db/migrate/20230515182743_create_accounts.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class CreateAccounts < ActiveRecord::Migration[7.0] 4 | def change 5 | create_table :accounts do |t| 6 | t.string :name, null: false 7 | t.string :timezone, null: false 8 | t.string :locale, null: false 9 | 10 | t.timestamps 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20230515193039_create_encrypted_configs.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class CreateEncryptedConfigs < ActiveRecord::Migration[7.0] 4 | def change 5 | create_table :encrypted_configs do |t| 6 | t.references :account, null: false, foreign_key: true, index: true 7 | t.string :key, null: false 8 | t.text :value, null: false 9 | 10 | t.index %i[account_id key], unique: true 11 | 12 | t.timestamps 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /db/migrate/20230516180929_create_templates.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class CreateTemplates < ActiveRecord::Migration[7.0] 4 | def change 5 | create_table :templates do |t| 6 | t.string :slug, null: false, index: { unique: true } 7 | t.string :name, null: false 8 | t.text :schema, null: false 9 | t.text :fields, null: false 10 | t.text :submitters, null: false 11 | 12 | t.references :author, null: false, foreign_key: { to_table: :users }, index: true 13 | t.references :account, null: false, foreign_key: true, index: true 14 | 15 | t.datetime :deleted_at 16 | t.timestamps 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /db/migrate/20230519144036_create_submissions.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class CreateSubmissions < ActiveRecord::Migration[7.0] 4 | def change 5 | create_table :submissions do |t| 6 | t.references :template, null: false, foreign_key: true, index: true 7 | t.references :created_by_user, null: true, foreign_key: { to_table: :users }, index: true 8 | 9 | t.datetime :deleted_at 10 | 11 | t.timestamps 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /db/migrate/20230612182744_create_submitters.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class CreateSubmitters < ActiveRecord::Migration[7.0] 4 | def change 5 | create_table :submitters do |t| 6 | t.references :submission, null: false, foreign_key: true, index: true 7 | 8 | t.string :uuid, null: false 9 | t.string :email, null: false, index: true 10 | t.string :slug, null: false, index: { unique: true } 11 | t.text :values, null: false 12 | t.string :ua 13 | t.string :ip 14 | 15 | t.datetime :sent_at 16 | t.datetime :opened_at 17 | t.datetime :completed_at 18 | 19 | t.timestamps 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /db/migrate/20230701075115_create_document_generation_events.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class CreateDocumentGenerationEvents < ActiveRecord::Migration[7.0] 4 | def change 5 | create_table :document_generation_events do |t| 6 | t.references :submitter, null: false, foreign_key: true, index: true 7 | t.string :event_name, null: false 8 | 9 | t.index %i[submitter_id event_name], unique: true, where: "event_name IN ('start', 'complete')" 10 | 11 | t.timestamps 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /db/migrate/20230803200825_add_uuid_to_users.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AddUuidToUsers < ActiveRecord::Migration[7.0] 4 | class MigrationUser < ApplicationRecord 5 | self.table_name = 'users' 6 | end 7 | 8 | def up 9 | add_column :users, :uuid, :string 10 | add_index :users, :uuid, unique: true 11 | 12 | MigrationUser.all.each do |user| 13 | user.update_columns(uuid: SecureRandom.uuid) 14 | end 15 | 16 | change_column_null :users, :uuid, false 17 | end 18 | 19 | def down 20 | drop_column :users, :uuid 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /db/migrate/20230805124454_add_source_to_submissions.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AddSourceToSubmissions < ActiveRecord::Migration[7.0] 4 | class MigrationSubmission < ApplicationRecord 5 | self.table_name = 'submissions' 6 | end 7 | 8 | def up 9 | add_column :submissions, :source, :string 10 | 11 | MigrationSubmission.where(source: nil).update_all(source: :invite) 12 | 13 | change_column_null :submissions, :source, false 14 | end 15 | 16 | def down 17 | drop_column :submissions, :source 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /db/migrate/20230806140534_create_access_tokens.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class CreateAccessTokens < ActiveRecord::Migration[7.0] 4 | def change 5 | create_table :access_tokens do |t| 6 | t.references :user, null: false, foreign_key: true, index: true 7 | t.text :token, null: false 8 | t.string :sha256, null: false, index: { unique: true } 9 | 10 | t.timestamps 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20230815190540_create_account_configs.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class CreateAccountConfigs < ActiveRecord::Migration[7.0] 4 | def change 5 | create_table :account_configs do |t| 6 | t.references :account, null: false, foreign_key: true, index: true 7 | t.string :key, null: false 8 | t.text :value, null: false 9 | 10 | t.index %i[account_id key], unique: true 11 | 12 | t.timestamps 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /db/migrate/20230819113427_remove_user_first_last_name_not_null.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class RemoveUserFirstLastNameNotNull < ActiveRecord::Migration[7.0] 4 | def change 5 | change_column_null :users, :first_name, true 6 | change_column_null :users, :last_name, true 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /db/migrate/20230819190316_add_source_to_templates.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AddSourceToTemplates < ActiveRecord::Migration[7.0] 4 | class MigrationTemplate < ApplicationRecord 5 | self.table_name = 'templates' 6 | end 7 | 8 | def up 9 | add_column :templates, :source, :text 10 | 11 | MigrationTemplate.where(source: nil).update_all(source: :native) 12 | 13 | change_column_null :templates, :source, false 14 | end 15 | 16 | def down 17 | drop_column :templates, :source 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /db/migrate/20230902171216_add_phone_and_name_to_submitters.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AddPhoneAndNameToSubmitters < ActiveRecord::Migration[7.0] 4 | def change 5 | add_column :submitters, :name, :string 6 | add_column :submitters, :phone, :string 7 | 8 | change_column_null :submitters, :email, true 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20230909213212_add_submitters_order_to_submissions.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AddSubmittersOrderToSubmissions < ActiveRecord::Migration[7.0] 4 | class MigrationSubmission < ApplicationRecord 5 | self.table_name = 'submissions' 6 | end 7 | 8 | def change 9 | add_column :submissions, :submitters_order, :string 10 | 11 | MigrationSubmission.where(submitters_order: nil).update_all(submitters_order: 'random') 12 | 13 | change_column_null :submissions, :submitters_order, false 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /db/migrate/20230910084410_create_submission_events.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class CreateSubmissionEvents < ActiveRecord::Migration[7.0] 4 | def change 5 | create_table :submission_events do |t| 6 | t.references :submission, null: false, foreign_key: true, index: true 7 | t.references :submitter, null: true, foreign_key: true, index: true 8 | t.text :data, null: false 9 | t.string :event_type, null: false 10 | t.datetime :event_timestamp, null: false 11 | 12 | t.timestamps 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /db/migrate/20230915200635_add_devise_two_factor_to_users.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AddDeviseTwoFactorToUsers < ActiveRecord::Migration[7.0] 4 | def change 5 | add_column :users, :otp_secret, :string 6 | add_column :users, :consumed_timestep, :integer 7 | add_column :users, :otp_required_for_login, :boolean, default: false, null: false 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20230920195319_create_template_folders.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class CreateTemplateFolders < ActiveRecord::Migration[7.0] 4 | def change 5 | create_table :template_folders do |t| 6 | t.string :name, null: false 7 | 8 | t.references :author, null: false, foreign_key: { to_table: :users }, index: true 9 | t.references :account, null: false, foreign_key: true, index: true 10 | 11 | t.datetime :deleted_at 12 | t.timestamps 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /db/migrate/20230922072041_create_user_configs.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class CreateUserConfigs < ActiveRecord::Migration[7.0] 4 | def change 5 | create_table :user_configs do |t| 6 | t.references :user, null: false, foreign_key: true, index: true 7 | t.string :key, null: false 8 | t.text :value, null: false 9 | 10 | t.index %i[user_id key], unique: true 11 | 12 | t.timestamps 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /db/migrate/20230924184657_create_encrypted_user_configs.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class CreateEncryptedUserConfigs < ActiveRecord::Migration[7.0] 4 | def change 5 | create_table :encrypted_user_configs do |t| 6 | t.references :user, null: false, foreign_key: true, index: true 7 | t.string :key, null: false 8 | t.text :value, null: false 9 | 10 | t.index %i[user_id key], unique: true 11 | 12 | t.timestamps 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /db/migrate/20231007052818_update_checkboxes_required.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class UpdateCheckboxesRequired < ActiveRecord::Migration[7.0] 4 | class MigrationTemplate < ApplicationRecord 5 | self.table_name = 'templates' 6 | end 7 | 8 | def up 9 | MigrationTemplate.find_each do |template| 10 | fields = JSON.parse(template.fields) 11 | 12 | fields.each do |field| 13 | field['required'] = false if field['type'] == 'checkbox' 14 | end 15 | 16 | template.update_columns(fields: fields.to_json) if JSON.parse(template.fields) != fields 17 | end 18 | end 19 | 20 | def down 21 | nil 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /db/migrate/20231017213639_add_application_key_to_submitters.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AddApplicationKeyToSubmitters < ActiveRecord::Migration[7.0] 4 | def change 5 | add_column :submitters, :application_key, :string 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20231102171817_add_application_key_to_templates.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AddApplicationKeyToTemplates < ActiveRecord::Migration[7.0] 4 | def change 5 | add_column :templates, :application_key, :string 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20231119222105_add_slug_to_submissions.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AddSlugToSubmissions < ActiveRecord::Migration[7.0] 4 | class MigrationSubmission < ApplicationRecord 5 | self.table_name = 'submissions' 6 | end 7 | 8 | def up 9 | add_column :submissions, :slug, :string 10 | 11 | MigrationSubmission.where(slug: nil).find_each do |submission| 12 | submission.update_columns(slug: SecureRandom.base58(14)) 13 | end 14 | 15 | change_column_null :submissions, :slug, false 16 | 17 | add_index :submissions, :slug, unique: true 18 | end 19 | 20 | def down 21 | remove_column :submissions, :slug 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /db/migrate/20231122203341_add_preferences_to_submitters.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AddPreferencesToSubmitters < ActiveRecord::Migration[7.0] 4 | class MigrationSubmitter < ApplicationRecord 5 | self.table_name = 'submitters' 6 | end 7 | 8 | def change 9 | add_column :submitters, :preferences, :text 10 | 11 | MigrationSubmitter.where(preferences: nil).update_all(preferences: '{}') 12 | 13 | change_column_null :submitters, :preferences, false 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /db/migrate/20231122203347_add_preferences_to_submissions.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AddPreferencesToSubmissions < ActiveRecord::Migration[7.0] 4 | class MigrationSubmission < ApplicationRecord 5 | self.table_name = 'submissions' 6 | end 7 | 8 | def change 9 | add_column :submissions, :preferences, :text 10 | 11 | MigrationSubmission.where(preferences: nil).update_all(preferences: '{}') 12 | 13 | change_column_null :submissions, :preferences, false 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /db/migrate/20231122212612_create_email_messages.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class CreateEmailMessages < ActiveRecord::Migration[7.0] 4 | def change 5 | create_table :email_messages do |t| 6 | t.string :uuid, null: false, index: true 7 | t.references :author, null: false, foreign_key: { to_table: :users }, index: false 8 | t.references :account, null: false, foreign_key: true, index: true 9 | t.text :subject, null: false 10 | t.text :body, null: false 11 | t.string :sha1, null: false, index: true 12 | 13 | t.timestamps 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /db/migrate/20231229220819_rename_deleted_at_to_archived_at.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class RenameDeletedAtToArchivedAt < ActiveRecord::Migration[7.1] 4 | def change 5 | rename_column :templates, :deleted_at, :archived_at 6 | rename_column :submissions, :deleted_at, :archived_at 7 | rename_column :users, :deleted_at, :archived_at 8 | rename_column :template_folders, :deleted_at, :archived_at 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20240120192055_create_account_linked_accounts.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class CreateAccountLinkedAccounts < ActiveRecord::Migration[7.1] 4 | def change 5 | create_table :account_linked_accounts do |t| 6 | t.references :account, null: false, foreign_key: true 7 | t.references :linked_account, null: false, foreign_key: { to_table: :accounts } 8 | t.text :account_type, null: false 9 | 10 | t.index %i[account_id linked_account_id], unique: true 11 | 12 | t.timestamps 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /db/migrate/20240131212010_rename_application_key_to_external_id.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class RenameApplicationKeyToExternalId < ActiveRecord::Migration[7.1] 4 | def change 5 | rename_column :templates, :application_key, :external_id 6 | rename_column :submitters, :application_key, :external_id 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /db/migrate/20240201210319_add_submitter_metadata.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AddSubmitterMetadata < ActiveRecord::Migration[7.1] 4 | class MigrationSubmitter < ApplicationRecord 5 | self.table_name = 'submitters' 6 | end 7 | 8 | def change 9 | add_column :submitters, :metadata, :text 10 | 11 | MigrationSubmitter.where(metadata: nil).update_all(metadata: '{}') 12 | 13 | change_column_null :submitters, :metadata, false 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /db/migrate/20240203113454_add_account_id_to_submissions.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AddAccountIdToSubmissions < ActiveRecord::Migration[7.1] 4 | class MigrationSubmission < ApplicationRecord 5 | self.table_name = 'submissions' 6 | end 7 | 8 | class MigrationTemplate < ApplicationRecord 9 | self.table_name = 'templates' 10 | end 11 | 12 | class MigrationAccount < ApplicationRecord 13 | self.table_name = 'accounts' 14 | end 15 | 16 | def change 17 | add_reference :submissions, :account, index: true, null: true 18 | 19 | MigrationAccount.all.each do |account| 20 | MigrationSubmission.where(template_id: MigrationTemplate.where(account_id: account.id).select(:id)) 21 | .update_all(account_id: account.id) 22 | end 23 | 24 | change_column_null :submissions, :account_id, false 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /db/migrate/20240203113455_create_template_sharings.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class CreateTemplateSharings < ActiveRecord::Migration[7.1] 4 | def change 5 | create_table :template_sharings do |t| 6 | t.references :template, null: false, foreign_key: true, index: true 7 | t.references :account, null: false, foreign_key: false, index: false 8 | t.string :ability, null: false 9 | 10 | t.index %i[account_id template_id], unique: true 11 | 12 | t.timestamps 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /db/migrate/20240218082157_add_uuid_to_active_storage_blobs.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AddUuidToActiveStorageBlobs < ActiveRecord::Migration[7.1] 4 | def change 5 | add_column :active_storage_blobs, :uuid, :string 6 | 7 | add_index :active_storage_blobs, :uuid, unique: true 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20240228074821_add_index_on_submission_events.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AddIndexOnSubmissionEvents < ActiveRecord::Migration[7.1] 4 | def change 5 | add_index :submission_events, :created_at 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20240228083356_add_index_on_blobs_checksum.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AddIndexOnBlobsChecksum < ActiveRecord::Migration[7.1] 4 | def change 5 | add_index :active_storage_blobs, :checksum 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20240405165329_add_uuid_to_accounts.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AddUuidToAccounts < ActiveRecord::Migration[7.1] 4 | class MigrationAccount < ApplicationRecord 5 | self.table_name = 'accounts' 6 | end 7 | 8 | def up 9 | add_column :accounts, :uuid, :string 10 | 11 | MigrationAccount.all.each do |account| 12 | account.update_columns(uuid: SecureRandom.uuid) 13 | end 14 | 15 | add_index :accounts, :uuid, unique: true 16 | 17 | change_column_null :accounts, :uuid, false 18 | end 19 | 20 | def down 21 | drop_column :account, :uuid 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /db/migrate/20240416170023_add_preferences_to_templates.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AddPreferencesToTemplates < ActiveRecord::Migration[7.1] 4 | class MigrationTemplate < ApplicationRecord 5 | self.table_name = 'templates' 6 | end 7 | 8 | def change 9 | add_column :templates, :preferences, :text 10 | 11 | MigrationTemplate.where(preferences: nil).update_all(preferences: '{}') 12 | 13 | change_column_null :templates, :preferences, false 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /db/migrate/20240428072623_add_archived_at_to_accounts.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AddArchivedAtToAccounts < ActiveRecord::Migration[7.1] 4 | def change 5 | add_column :accounts, :archived_at, :datetime 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20240604070648_create_email_events.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class CreateEmailEvents < ActiveRecord::Migration[7.1] 4 | def change 5 | create_table :email_events do |t| 6 | t.references :account, null: false, foreign_key: true, index: true 7 | t.references :emailable, polymorphic: true, index: true, null: false 8 | t.string :message_id, null: false, index: true 9 | t.string :tag, null: false 10 | t.string :event_type, null: false 11 | t.string :email, null: false 12 | t.text :data, null: false 13 | t.datetime :event_datetime, null: false 14 | t.datetime :created_at, null: false 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /db/migrate/20240624102526_add_index_on_external_id.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AddIndexOnExternalId < ActiveRecord::Migration[7.1] 4 | def change 5 | add_index :submitters, :external_id 6 | add_index :templates, :external_id 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /db/migrate/20240714172222_create_webhook_urls.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class CreateWebhookUrls < ActiveRecord::Migration[7.1] 4 | def change 5 | create_table :webhook_urls do |t| 6 | t.references :account, null: false, foreign_key: true, index: true 7 | t.text :url, null: false 8 | t.text :events, null: false 9 | t.string :sha1, null: false, index: true 10 | 11 | t.timestamps 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /db/migrate/20240720063826_add_index_on_email_email_events.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AddIndexOnEmailEmailEvents < ActiveRecord::Migration[7.1] 4 | def change 5 | add_index :email_events, :email 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20240801125558_add_expire_at_to_submissions.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AddExpireAtToSubmissions < ActiveRecord::Migration[7.1] 4 | def change 5 | add_column :submissions, :expire_at, :datetime 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20240816064633_add_account_id_to_submitters.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AddAccountIdToSubmitters < ActiveRecord::Migration[7.1] 4 | def change 5 | add_reference :submitters, :account, index: false, null: true 6 | 7 | add_index :submitters, %i[account_id id] 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20240816075917_update_submission_account_id_index.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class UpdateSubmissionAccountIdIndex < ActiveRecord::Migration[7.1] 4 | def change 5 | add_index :submissions, %i[account_id id] 6 | remove_index :submissions, :account_id 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /db/migrate/20240816121641_remove_active_storage_uniq_index.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class RemoveActiveStorageUniqIndex < ActiveRecord::Migration[7.1] 4 | def change 5 | remove_index :active_storage_attachments, %i[record_type record_id name blob_id], 6 | unique: true, 7 | name: 'index_active_storage_attachments_uniqueness' 8 | 9 | add_index :active_storage_attachments, %i[record_type record_id name blob_id] 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20240820180922_add_declined_at_to_submitters.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AddDeclinedAtToSubmitters < ActiveRecord::Migration[7.1] 4 | def change 5 | add_column :submitters, :declined_at, :datetime 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20241018115034_create_completed_submitters_and_documents.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class CreateCompletedSubmittersAndDocuments < ActiveRecord::Migration[7.2] 4 | def change 5 | create_table :completed_submitters do |t| 6 | t.bigint :submitter_id, null: false, index: true 7 | t.bigint :submission_id, null: false 8 | t.bigint :account_id, null: false, index: true 9 | t.bigint :template_id, null: false 10 | t.string :source, null: false 11 | t.integer :sms_count, null: false 12 | t.datetime :completed_at, null: false 13 | 14 | t.timestamps 15 | end 16 | 17 | create_table :completed_documents do |t| 18 | t.bigint :submitter_id, null: false, index: true 19 | t.string :sha256, null: false, index: true 20 | 21 | t.timestamps 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /db/migrate/20241026161207_add_unique_index_on_completed_submitters.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AddUniqueIndexOnCompletedSubmitters < ActiveRecord::Migration[7.2] 4 | def change 5 | remove_index :completed_submitters, :submitter_id 6 | add_index :completed_submitters, :submitter_id, unique: true 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /db/migrate/20241028162000_add_secret_to_webhook_urls.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AddSecretToWebhookUrls < ActiveRecord::Migration[7.2] 4 | class MigrationWebhookUrl < ApplicationRecord 5 | self.table_name = 'webhook_urls' 6 | 7 | serialize :secret, coder: JSON 8 | 9 | encrypts :url, :secret 10 | end 11 | 12 | def change 13 | add_column :webhook_urls, :secret, :text 14 | 15 | MigrationWebhookUrl.all.each do |url| 16 | url.update_columns(secret: {}) 17 | end 18 | 19 | change_column_null :webhook_urls, :secret, false 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /db/migrate/20241207172237_create_template_accesses.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class CreateTemplateAccesses < ActiveRecord::Migration[7.2] 4 | def change 5 | create_table :template_accesses do |t| 6 | t.references :template, null: false, foreign_key: true, index: false 7 | t.references :user, null: false, foreign_key: false, index: false 8 | 9 | t.index %i[template_id user_id], unique: true 10 | 11 | t.timestamps 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /db/migrate/20250518070555_account_accesses.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AccountAccesses < ActiveRecord::Migration[8.0] 4 | def change 5 | create_table :account_accesses do |t| 6 | t.references :account, null: false, foreign_key: true, index: false 7 | t.references :user, null: false, foreign_key: false, index: false 8 | 9 | t.index %i[account_id user_id], unique: true 10 | 11 | t.timestamps 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/api_path_consider_json_middleware.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class ApiPathConsiderJsonMiddleware 4 | def initialize(app) 5 | @app = app 6 | end 7 | 8 | def call(env) 9 | if env['PATH_INFO'].starts_with?('/api') && 10 | (!env['PATH_INFO'].ends_with?('/documents') || env['REQUEST_METHOD'] != 'POST') && 11 | !env['PATH_INFO'].ends_with?('/attachments') && 12 | !env['PATH_INFO'].ends_with?('/submitter_sms_clicks') && 13 | !env['PATH_INFO'].ends_with?('/submitter_email_clicks') 14 | env['CONTENT_TYPE'] = 'application/json' 15 | end 16 | 17 | @app.call(env) 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/email_messages.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module EmailMessages 4 | module_function 5 | 6 | def find_or_create_for_account_user(account, user, subject, body) 7 | subject = I18n.t(:you_are_invited_to_sign_a_document) if subject.blank? 8 | 9 | sha1 = Digest::SHA1.hexdigest({ subject:, body: }.to_json) 10 | 11 | message = account.email_messages.find_by(sha1:) 12 | 13 | message ||= account.email_messages.create!(author: user, subject:, body:) 14 | 15 | message 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/json_web_token.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module JsonWebToken 4 | module_function 5 | 6 | def encode(payload) 7 | JWT.encode(payload, Rails.application.secret_key_base) 8 | end 9 | 10 | def decode(token) 11 | JWT.decode(token, Rails.application.secret_key_base)[0] 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/markdown_to_html.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module MarkdownToHtml 4 | LINK_REGEXP = %r{\[([^\]]+)\]\((https?://[^)]+)\)} 5 | LINK_REPLACE = '\1' 6 | 7 | module_function 8 | 9 | def call(text) 10 | text.gsub(LINK_REGEXP, LINK_REPLACE) 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/normalize_client_ip_middleware.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class NormalizeClientIpMiddleware 4 | def initialize(app) 5 | @app = app 6 | end 7 | 8 | def call(env) 9 | if env['HTTP_CLIENT_IP'].present? && env['HTTP_X_CLIENT_IP'].present? && 10 | env['HTTP_CLIENT_IP'].starts_with?("#{env['HTTP_X_CLIENT_IP']}:") 11 | env['HTTP_CLIENT_IP'] = env['HTTP_X_CLIENT_IP'] 12 | end 13 | 14 | @app.call(env) 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/number_utils.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module NumberUtils 4 | FORMAT_LOCALES = { 5 | 'dot' => 'de', 6 | 'space' => 'fr', 7 | 'comma' => 'en', 8 | 'usd' => 'en', 9 | 'eur' => 'fr', 10 | 'gbp' => 'en' 11 | }.freeze 12 | 13 | CURRENCY_SYMBOLS = { 14 | 'usd' => '$', 15 | 'eur' => '€', 16 | 'gbp' => '£' 17 | }.freeze 18 | 19 | module_function 20 | 21 | def format_number(number, format) 22 | locale = FORMAT_LOCALES[format] 23 | 24 | if CURRENCY_SYMBOLS[format] 25 | ApplicationController.helpers.number_to_currency(number, locale:, precision: 2, unit: CURRENCY_SYMBOLS[format]) 26 | elsif locale 27 | ApplicationController.helpers.number_with_delimiter(number, locale:) 28 | else 29 | number 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib/pdf_icons/check.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docusealco/docuseal/c6eae66127e1cabd7c2406758c8aa96e99bd496b/lib/pdf_icons/check.png -------------------------------------------------------------------------------- /lib/pdf_icons/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docusealco/docuseal/c6eae66127e1cabd7c2406758c8aa96e99bd496b/lib/pdf_icons/logo.png -------------------------------------------------------------------------------- /lib/pdf_icons/logo_new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docusealco/docuseal/c6eae66127e1cabd7c2406758c8aa96e99bd496b/lib/pdf_icons/logo_new.png -------------------------------------------------------------------------------- /lib/pdf_icons/paperclip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docusealco/docuseal/c6eae66127e1cabd7c2406758c8aa96e99bd496b/lib/pdf_icons/paperclip.png -------------------------------------------------------------------------------- /lib/pdf_icons/stamp-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docusealco/docuseal/c6eae66127e1cabd7c2406758c8aa96e99bd496b/lib/pdf_icons/stamp-logo.png -------------------------------------------------------------------------------- /lib/rate_limit.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RateLimit 4 | LimitApproached = Class.new(StandardError) 5 | 6 | STORE = ActiveSupport::Cache::MemoryStore.new 7 | 8 | module_function 9 | 10 | def call(key, limit:, ttl:, enabled: Docuseal.multitenant?) 11 | return true unless enabled 12 | 13 | value = STORE.increment(key, 1, expires_in: ttl) 14 | 15 | raise LimitApproached if value > limit 16 | 17 | true 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/submission_events.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module SubmissionEvents 4 | TRACKING_PARAM_LENGTH = 6 5 | 6 | module_function 7 | 8 | def build_tracking_param(submitter, event_type = 'click_email') 9 | Base64.urlsafe_encode64( 10 | Digest::SHA1.digest([submitter.slug, event_type, Rails.application.secret_key_base].join(':')) 11 | ).first(TRACKING_PARAM_LENGTH) 12 | end 13 | 14 | def create_with_tracking_data(submitter, event_type, request, data = {}) 15 | SubmissionEvent.create!(submitter:, event_type:, data: { 16 | ip: request.remote_ip, 17 | ua: request.user_agent, 18 | sid: request.session.id.to_s, 19 | uid: request.env['warden'].user(:user)&.id, 20 | **data 21 | }.compact_blank) 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/submitters/validate_sending.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Submitters 4 | module ValidateSending 5 | InvalidEmail = Class.new(StandardError) 6 | 7 | module_function 8 | 9 | def call(submitter, _mail) 10 | raise InvalidEmail unless submitter.email.to_s.include?('@') 11 | 12 | true 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/tasks/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docusealco/docuseal/c6eae66127e1cabd7c2406758c8aa96e99bd496b/lib/tasks/.keep -------------------------------------------------------------------------------- /lib/tasks/annotate_rb.rake: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # This rake task was added by annotate_rb gem. 4 | 5 | # Can set `ANNOTATERB_SKIP_ON_DB_TASKS` to be anything to skip this 6 | if Rails.env.development? && ENV['ANNOTATERB_SKIP_ON_DB_TASKS'].nil? 7 | require 'annotate_rb' 8 | 9 | AnnotateRb::Core.load_rake_tasks 10 | end 11 | -------------------------------------------------------------------------------- /lib/template_folders.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module TemplateFolders 4 | module_function 5 | 6 | def search(folders, keyword) 7 | return folders if keyword.blank? 8 | 9 | folders.where(TemplateFolder.arel_table[:name].lower.matches("%#{keyword.downcase}%")) 10 | end 11 | 12 | def find_or_create_by_name(author, name) 13 | return author.account.default_template_folder if name.blank? || name == TemplateFolder::DEFAULT_NAME 14 | 15 | author.account.template_folders.create_with(author:, account: author.account).find_or_create_by(name:) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/user_configs.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module UserConfigs 4 | module_function 5 | 6 | def load_signature(user) 7 | return if user.blank? 8 | 9 | uuid = user.user_configs.find_or_initialize_by(key: UserConfig::SIGNATURE_KEY).value 10 | 11 | ActiveStorage::Attachment.find_by(uuid:, record: user, name: 'signature') if uuid.present? 12 | end 13 | 14 | def load_initials(user) 15 | return if user.blank? 16 | 17 | uuid = user.user_configs.find_or_initialize_by(key: UserConfig::INITIALS_KEY).value 18 | 19 | ActiveStorage::Attachment.find_by(uuid:, record: user, name: 'initials') if uuid.present? 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docusealco/docuseal/c6eae66127e1cabd7c2406758c8aa96e99bd496b/log/.keep -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | require('postcss-import'), 4 | require('tailwindcss/nesting'), 5 | require('tailwindcss'), 6 | require('autoprefixer') 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /public/apple-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docusealco/docuseal/c6eae66127e1cabd7c2406758c8aa96e99bd496b/public/apple-icon-180x180.png -------------------------------------------------------------------------------- /public/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docusealco/docuseal/c6eae66127e1cabd7c2406758c8aa96e99bd496b/public/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docusealco/docuseal/c6eae66127e1cabd7c2406758c8aa96e99bd496b/public/apple-touch-icon.png -------------------------------------------------------------------------------- /public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docusealco/docuseal/c6eae66127e1cabd7c2406758c8aa96e99bd496b/public/favicon-16x16.png -------------------------------------------------------------------------------- /public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docusealco/docuseal/c6eae66127e1cabd7c2406758c8aa96e99bd496b/public/favicon-32x32.png -------------------------------------------------------------------------------- /public/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docusealco/docuseal/c6eae66127e1cabd7c2406758c8aa96e99bd496b/public/favicon-96x96.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docusealco/docuseal/c6eae66127e1cabd7c2406758c8aa96e99bd496b/public/favicon.ico -------------------------------------------------------------------------------- /public/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docusealco/docuseal/c6eae66127e1cabd7c2406758c8aa96e99bd496b/public/preview.png -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Allow: / 3 | -------------------------------------------------------------------------------- /public/service-worker.js: -------------------------------------------------------------------------------- 1 | self.addEventListener('install', () => { 2 | console.log('DocuSeal App installed') 3 | }) 4 | 5 | self.addEventListener('activate', () => { 6 | console.log('DocuSeal App activated') 7 | }) 8 | 9 | self.addEventListener('fetch', (event) => { 10 | event.respondWith(fetch(event.request)) 11 | }) 12 | -------------------------------------------------------------------------------- /spec/factories/account_configs.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | FactoryBot.define do 4 | factory :account_config do 5 | account 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /spec/factories/completed_documents.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | FactoryBot.define do 4 | factory :completed_document do 5 | submitter 6 | sha256 { SecureRandom.hex(32) } 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /spec/factories/encrypted_configs.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | FactoryBot.define do 4 | factory :encrypted_config do 5 | account 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /spec/factories/submission_events.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | FactoryBot.define do 4 | factory :submission_event do 5 | submission 6 | submitter 7 | event_type { 'view_form' } 8 | event_timestamp { Time.zone.now } 9 | data do 10 | { 11 | ip: Faker::Internet.ip_v4_address, 12 | ua: Faker::Internet.user_agent, 13 | sid: SecureRandom.base58(10), 14 | uid: Faker::Number.number(digits: 4) 15 | } 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /spec/factories/submitters.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | FactoryBot.define do 4 | factory :submitter do 5 | submission 6 | email { Faker::Internet.email } 7 | name { Faker::Name.name } 8 | 9 | before(:create) do |submitter, _| 10 | submitter.account_id = submitter.submission.account_id 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/factories/template_accesses.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | FactoryBot.define do 4 | factory :template_access do 5 | template 6 | user 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /spec/factories/template_folders.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | FactoryBot.define do 4 | factory :template_folder do 5 | account 6 | 7 | author factory: %i[user] 8 | name { Faker::Book.title } 9 | 10 | trait :with_templates do 11 | after(:create) do |template_folder| 12 | create_list(:template, 2, folder: template_folder, account: template_folder.account) 13 | end 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /spec/factories/user_configs.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | FactoryBot.define do 4 | factory :user_config do 5 | user 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /spec/factories/users.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | FactoryBot.define do 4 | factory :user do 5 | account 6 | first_name { Faker::Name.first_name } 7 | last_name { Faker::Name.last_name } 8 | password { 'password' } 9 | role { User::ADMIN_ROLE } 10 | email { Faker::Internet.email } 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /spec/factories/webhook_urls.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | FactoryBot.define do 4 | factory :webhook_url do 5 | account 6 | url { Faker::Internet.url } 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /spec/fixtures/sample-document.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docusealco/docuseal/c6eae66127e1cabd7c2406758c8aa96e99bd496b/spec/fixtures/sample-document.pdf -------------------------------------------------------------------------------- /spec/fixtures/sample-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docusealco/docuseal/c6eae66127e1cabd7c2406758c8aa96e99bd496b/spec/fixtures/sample-image.png -------------------------------------------------------------------------------- /spec/mailers/previews/settings_mailer_preview.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class SettingsMailerPreview < ActionMailer::Preview 4 | def smtp_successful_setup 5 | SettingsMailer.smtp_successful_setup('example@gmail.com') 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /spec/mailers/previews/submitter_mailer_preview.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class SubmitterMailerPreview < ActionMailer::Preview 4 | def invitation_email 5 | SubmitterMailer.invitation_email(Submitter.last) 6 | end 7 | 8 | def completed_email 9 | submitter = Submitter.where.not(completed_at: nil).joins(:documents_attachments).last 10 | 11 | SubmitterMailer.completed_email(submitter, User.last) 12 | end 13 | 14 | def declined_email 15 | submitter = Submitter.where.not(declined_at: nil).last 16 | 17 | SubmitterMailer.declined_email(submitter, User.last) 18 | end 19 | 20 | def documents_copy_email 21 | submitter = Submitter.where.not(completed_at: nil).joins(:documents_attachments).last 22 | 23 | SubmitterMailer.documents_copy_email(submitter) 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /spec/mailers/previews/user_mailer_preview.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class UserMailerPreview < ActionMailer::Preview 4 | def invitation_email 5 | user = User.first 6 | user.account.locale = I18n.locale 7 | UserMailer.invitation_email(user) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | RSpec.configure do |config| 4 | config.expect_with :rspec do |expectations| 5 | expectations.include_chain_clauses_in_custom_matcher_descriptions = true 6 | end 7 | 8 | config.mock_with :rspec do |mocks| 9 | mocks.verify_partial_doubles = true 10 | end 11 | 12 | config.shared_context_metadata_behavior = :apply_to_host_groups 13 | end 14 | -------------------------------------------------------------------------------- /spec/system/api_settings_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | RSpec.describe 'API Settings' do 4 | let!(:account) { create(:account) } 5 | let!(:user) { create(:user, account:) } 6 | 7 | before do 8 | sign_in(user) 9 | visit settings_api_index_path 10 | end 11 | 12 | it 'shows verify signed PDF page' do 13 | expect(page).to have_content('API') 14 | token = user.access_token.token 15 | expect(page).to have_field('X-Auth-Token', with: token.sub(token[5..], '*' * token[5..].size)) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /spec/system/esign_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | RSpec.describe 'PDF Signature Settings' do 4 | let!(:account) { create(:account) } 5 | let!(:user) { create(:user, account:) } 6 | 7 | before do 8 | sign_in(user) 9 | visit settings_esign_path 10 | end 11 | 12 | it 'shows verify signed PDF page' do 13 | expect(page).to have_content('PDF Signature') 14 | expect(page).to have_content('Upload signed PDF file to validate its signature') 15 | expect(page).to have_content('Verify Signed PDF') 16 | expect(page).to have_content('Click to upload or drag and drop files') 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /spec/system/personalization_settings_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | RSpec.describe 'Personalization Settings', :js do 4 | let!(:account) { create(:account) } 5 | let!(:user) { create(:user, account:) } 6 | 7 | before do 8 | sign_in(user) 9 | visit settings_personalization_path 10 | end 11 | 12 | it 'shows the notifications settings page' do 13 | expect(page).to have_content('Email Templates') 14 | expect(page).to have_content('Company Logo') 15 | expect(page).to have_content('Submission Form') 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /spec/system/personalization_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | RSpec.describe 'Personalization' do 4 | let!(:account) { create(:account) } 5 | let!(:user) { create(:user, account:) } 6 | 7 | before do 8 | sign_in(user) 9 | visit settings_personalization_path 10 | end 11 | 12 | it 'shows the personalization page' do 13 | expect(page).to have_content('Email Templates') 14 | expect(page).to have_content('Signature Request Email') 15 | expect(page).to have_content('Completed Notification Email') 16 | expect(page).to have_content('Documents Copy Email') 17 | expect(page).to have_content('Company Logo') 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /tailwind.application.config.js: -------------------------------------------------------------------------------- 1 | const baseConfigs = require('./tailwind.config.js') 2 | 3 | module.exports = { 4 | ...baseConfigs, 5 | content: [ 6 | './app/javascript/**/*.{js,vue}', 7 | './app/views/**/*.erb' 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | require('daisyui') 4 | ], 5 | daisyui: { 6 | themes: [ 7 | { 8 | docuseal: { 9 | 'color-scheme': 'light', 10 | primary: '#e4e0e1', 11 | secondary: '#ef9fbc', 12 | accent: '#eeaf3a', 13 | neutral: '#291334', 14 | 'base-100': '#faf7f5', 15 | 'base-200': '#efeae6', 16 | 'base-300': '#e7e2df', 17 | 'base-content': '#291334', 18 | '--rounded-btn': '1.9rem', 19 | '--tab-border': '2px', 20 | '--tab-radius': '.5rem' 21 | } 22 | } 23 | ] 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tailwind.form.config.js: -------------------------------------------------------------------------------- 1 | const baseConfigs = require('./tailwind.config.js') 2 | 3 | module.exports = { 4 | ...baseConfigs, 5 | content: [ 6 | './app/javascript/submission_form/**/*.vue', 7 | './app/views/submit_form/**/*.erb', 8 | './app/views/start_form/**/*.erb', 9 | './app/views/shared/_html_modal.html.erb', 10 | './app/views/shared/_button_title.html.erb', 11 | './app/views/shared/_attribution.html.erb', 12 | './app/views/scripts/_autosize_field.html.erb', 13 | './app/views/send_submission_email/**/*.erb' 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /tmp/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docusealco/docuseal/c6eae66127e1cabd7c2406758c8aa96e99bd496b/tmp/.keep -------------------------------------------------------------------------------- /tmp/pids/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docusealco/docuseal/c6eae66127e1cabd7c2406758c8aa96e99bd496b/tmp/pids/.keep --------------------------------------------------------------------------------