diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..d694a0bf --- /dev/null +++ b/.env.example @@ -0,0 +1 @@ +HOST=hostname diff --git a/.gitattributes b/.gitattributes index 28cee3ff..c1f3ae47 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,2 @@ *.html linguist-detectable=false +bin/* text eol=lf diff --git a/.gitignore b/.gitignore index d14f4595..ffe6cdcc 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ /public/assets /config/master.key +/config/config.yml /public/packs /public/packs-test @@ -38,3 +39,7 @@ yarn-debug.log* /ee dump.rdb *.onnx +pg_data/ +caddy/ +docuseal/ +.env diff --git a/Dockerfile b/Dockerfile index b1341fcf..3b5e35d8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -40,7 +40,7 @@ COPY ./tailwind.application.config.js ./tailwind.application.config.js COPY ./app/javascript ./app/javascript COPY ./app/views ./app/views -RUN echo "gem 'shakapacker'" > Gemfile && ./bin/shakapacker +RUN echo "gem 'shakapacker'" > Gemfile && sed -i 's/\r$//' bin/shakapacker && ./bin/shakapacker FROM ruby:4.0.1-alpine AS app diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 592c006d..be319fc4 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -6,9 +6,12 @@ class ApplicationController < ActionController::Base include ActiveStorage::SetCurrent include Pagy::Method + helper WhitelabelHelper + check_authorization unless: :devise_controller? around_action :with_locale + before_action :enforce_licence before_action :sign_in_for_demo, if: -> { Docuseal.demo? } before_action :maybe_redirect_to_setup, unless: :signed_in? before_action :authenticate_user!, unless: :devise_controller? @@ -37,7 +40,7 @@ class ApplicationController < ActionController::Base rescue_from CanCan::AccessDenied do |e| Rollbar.warning(e) if defined?(Rollbar) - redirect_to root_path, alert: e.message + redirect_to root_path, alert: I18n.t('unauthorized.default', locale: current_account&.locale) end end @@ -65,12 +68,13 @@ class ApplicationController < ActionController::Base private def with_locale(&) - return yield unless current_account - - locale = params[:lang].presence if Rails.env.development? - locale ||= current_account.locale + locale = if current_account + (params[:lang].presence if Rails.env.development?) || current_account.locale + else + request.env['HTTP_ACCEPT_LANGUAGE'].to_s[BROWSER_LOCALE_REGEXP].to_s.split('-').first.presence + end - I18n.with_locale(locale, &) + I18n.with_locale(locale || I18n.default_locale, &) end def with_browser_locale(&) @@ -95,6 +99,21 @@ class ApplicationController < ActionController::Base sign_in(User.active.order('random()').take) unless signed_in? end + def enforce_licence + return if request.path == '/up' + return if request.path.start_with?('/assets', '/packs') + + Whitelabel.ensure_valid! + rescue Whitelabel::ConfigError => e + Rails.logger.error(e.message) + + if request.format.json? + render json: { error: 'service_unavailable' }, status: :service_unavailable + else + render plain: 'Service unavailable.', status: :service_unavailable + end + end + def current_account current_user&.account end @@ -122,6 +141,8 @@ class ApplicationController < ActionController::Base end def maybe_redirect_com + # NOTE: upstream DocuSeal cloud redirect — no-op for self-hosted / white-label + return unless Docuseal.multitenant? return if request.domain != 'docuseal.co' redirect_to request.url.gsub('.co/', '.com/'), allow_other_host: true, status: :moved_permanently diff --git a/app/controllers/embed_scripts_controller.rb b/app/controllers/embed_scripts_controller.rb index a40ed79c..35fb5c5f 100644 --- a/app/controllers/embed_scripts_controller.rb +++ b/app/controllers/embed_scripts_controller.rb @@ -1,39 +1,43 @@ # frozen_string_literal: true class EmbedScriptsController < ActionController::Metal - DUMMY_SCRIPT = <<~JAVASCRIPT.freeze - const DummyBuilder = class extends HTMLElement { - connectedCallback() { - this.innerHTML = ` -
- `; - } - }; + def show + headers['Content-Type'] = 'application/javascript' - const DummyForm = class extends DummyBuilder {}; + self.response_body = dummy_script - if (!window.customElements.get('docuseal-builder')) { - window.customElements.define('docuseal-builder', DummyBuilder); - } + self.status = 200 + end - if (!window.customElements.get('docuseal-form')) { - window.customElements.define('docuseal-form', DummyForm); - } - JAVASCRIPT + private - def show - headers['Content-Type'] = 'application/javascript' + def dummy_script + <<~JAVASCRIPT + const DummyBuilder = class extends HTMLElement { + connectedCallback() { + this.innerHTML = ` + + `; + } + }; - self.response_body = DUMMY_SCRIPT + const DummyForm = class extends DummyBuilder {}; - self.status = 200 + if (!window.customElements.get('docuseal-builder')) { + window.customElements.define('docuseal-builder', DummyBuilder); + } + + if (!window.customElements.get('docuseal-form')) { + window.customElements.define('docuseal-form', DummyForm); + } + JAVASCRIPT end end diff --git a/app/controllers/errors_controller.rb b/app/controllers/errors_controller.rb index 0c9e3632..190aefd5 100644 --- a/app/controllers/errors_controller.rb +++ b/app/controllers/errors_controller.rb @@ -2,7 +2,7 @@ class ErrorsController < ActionController::Base ENTERPRISE_FEATURE_MESSAGE = - 'This feature is available in Pro Edition: https://www.docuseal.com/pricing' + "This feature is available in Pro Edition: #{Whitelabel.website_url}/pricing" ENTERPRISE_PATHS = [ '/submissions/html', diff --git a/app/controllers/esign_settings_controller.rb b/app/controllers/esign_settings_controller.rb index 81c75fae..ba961372 100644 --- a/app/controllers/esign_settings_controller.rb +++ b/app/controllers/esign_settings_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class EsignSettingsController < ApplicationController - DEFAULT_CERT_NAME = 'DocuSeal Self-Host Autogenerated' + DEFAULT_CERT_NAME = Whitelabel.cert_name CertFormRecord = Struct.new(:name, :file, :password, keyword_init: true) do include ActiveModel::Validations diff --git a/app/controllers/personalization_settings_controller.rb b/app/controllers/personalization_settings_controller.rb index d9d33490..75ab6487 100644 --- a/app/controllers/personalization_settings_controller.rb +++ b/app/controllers/personalization_settings_controller.rb @@ -1,16 +1,6 @@ # frozen_string_literal: true class PersonalizationSettingsController < ApplicationController - ALLOWED_KEYS = [ - AccountConfig::FORM_COMPLETED_BUTTON_KEY, - AccountConfig::SUBMITTER_INVITATION_EMAIL_KEY, - AccountConfig::SUBMITTER_INVITATION_REMINDER_EMAIL_KEY, - AccountConfig::SUBMITTER_DOCUMENTS_COPY_EMAIL_KEY, - AccountConfig::SUBMITTER_COMPLETED_EMAIL_KEY, - AccountConfig::FORM_COMPLETED_MESSAGE_KEY, - *(Docuseal.multitenant? ? [] : [AccountConfig::POLICY_LINKS_KEY]) - ].freeze - InvalidKey = Class.new(StandardError) before_action :load_and_authorize_account_config, only: :create @@ -45,11 +35,23 @@ class PersonalizationSettingsController < ApplicationController authorize!(:create, @account_config) - raise InvalidKey unless ALLOWED_KEYS.include?(@account_config.key) + raise InvalidKey unless allowed_keys.include?(@account_config.key) @account_config end + def allowed_keys + [ + AccountConfig::FORM_COMPLETED_BUTTON_KEY, + AccountConfig::SUBMITTER_INVITATION_EMAIL_KEY, + AccountConfig::SUBMITTER_INVITATION_REMINDER_EMAIL_KEY, + AccountConfig::SUBMITTER_DOCUMENTS_COPY_EMAIL_KEY, + AccountConfig::SUBMITTER_COMPLETED_EMAIL_KEY, + AccountConfig::FORM_COMPLETED_MESSAGE_KEY, + *(Docuseal.multitenant? ? [] : [AccountConfig::POLICY_LINKS_KEY]) + ] + end + def account_config_params attrs = params.require(:account_config).permit(:key, :value, { value: {} }, { value: [] }) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 9affdf4c..7d10be5d 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -16,6 +16,9 @@ class UsersController < ApplicationController @users.active.where.not(role: 'integration') end + # Restrict visibility to roles at or below the current user's rank. + @users = @users.where(role: Whitelabel.manageable_roles(current_user.role)) + @pagy, @users = pagy(@users.preload(account: :account_accesses).where(account: current_account).order(id: :desc)) end @@ -40,12 +43,18 @@ class UsersController < ApplicationController end @user.password = SecureRandom.hex if @user.password.blank? - @user.role = User::ADMIN_ROLE unless role_valid?(@user.role) + @user.role = User.admin_role unless role_valid?(@user.role) if @user.save - UserMailer.invitation_email(@user).deliver_later! + begin + UserMailer.invitation_email(@user).deliver_later! + + redirect_back fallback_location: settings_users_path, notice: I18n.t('user_has_been_invited') + rescue StandardError => e + Rollbar.error(e) if defined?(Rollbar) - redirect_back fallback_location: settings_users_path, notice: I18n.t('user_has_been_invited') + redirect_back fallback_location: settings_users_path, alert: I18n.t('failed') + end else render turbo_stream: turbo_stream.replace(:modal, template: 'users/new'), status: :unprocessable_content end @@ -92,7 +101,8 @@ class UsersController < ApplicationController private def role_valid?(role) - User::ROLES.include?(role) + # Role must exist AND be at or below the current user's own rank. + Whitelabel.manageable_roles(current_user.role).include?(role.to_s) end def build_user diff --git a/app/helpers/whitelabel_helper.rb b/app/helpers/whitelabel_helper.rb new file mode 100644 index 00000000..4adc50c5 --- /dev/null +++ b/app/helpers/whitelabel_helper.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +# ============================================================================= +# WhitelabelHelper — makes Whitelabel config available in all views +# ============================================================================= +# Include this in ApplicationController to use `wl` in all ERB templates. +# +# Usage in views: +# <%= wl.brand_name %> +# <%= wl.logo_path %> +# <%= wl.support_email %> +# ============================================================================= + +module WhitelabelHelper + def wl + Whitelabel + end +end diff --git a/app/javascript/application.js b/app/javascript/application.js index 8889609f..a8dcbbaa 100644 --- a/app/javascript/application.js +++ b/app/javascript/application.js @@ -182,6 +182,8 @@ safeRegisterElement('template-builder', class extends HTMLElement { showTourStartForm: this.dataset.showTourStartForm === 'true' }) + this.app.config.globalProperties.document = document + this.component = this.app.mount(this.appElem) this.appendChild(this.appElem) diff --git a/app/javascript/submission_form/completed.vue b/app/javascript/submission_form/completed.vue index c87f9c4f..a69da76e 100644 --- a/app/javascript/submission_form/completed.vue +++ b/app/javascript/submission_form/completed.vue @@ -1,36 +1,18 @@ -- Run on your own host using Docker container, or deploy on your favorite managed PaaS with a single click. -
-- Review and sign digital documents online from any device. - Docuseal document forms are optimized for screens of all sizes. -
-- Host it on your hardware under a VPN to ensure that important documents can be accesses only within your organization. -
-
- Source code is available under github.com/docusealco.
- Open-source contributors are always ready to help!
-
+
+ +
+
+
- <% if @current_account&.testing? %> - <%= t('sent_using_product_name_in_testing_mode_html', product_url: "#{Docuseal::PRODUCT_EMAIL_URL}/start", product_name: Docuseal.product_name) %> - <% else %> - <%= t('sent_using_product_name_free_document_signing_html', product_url: "#{Docuseal::PRODUCT_EMAIL_URL}/start", product_name: Docuseal.product_name) %> - <% end %> + --- +
++ <%= Whitelabel.email_attribution_html.html_safe %>
diff --git a/app/views/shared/_github.html.erb b/app/views/shared/_github.html.erb index 5bcc786a..e69de29b 100644 --- a/app/views/shared/_github.html.erb +++ b/app/views/shared/_github.html.erb @@ -1,6 +0,0 @@ - - - <%= svg_icon('start', class: 'h-3 w-3') %> - 11k - - diff --git a/app/views/shared/_github_button.html.erb b/app/views/shared/_github_button.html.erb index 5bb4bdde..c7ac2858 100644 --- a/app/views/shared/_github_button.html.erb +++ b/app/views/shared/_github_button.html.erb @@ -1,10 +1,12 @@ - - - - - - Star on GitHub - - +<% if Whitelabel.show_github_button? && Whitelabel.github_url.present? %> + + + + + + Star on GitHub + + +<% end %> diff --git a/app/views/shared/_logo.html.erb b/app/views/shared/_logo.html.erb index ce505b8f..d3b69a79 100644 --- a/app/views/shared/_logo.html.erb +++ b/app/views/shared/_logo.html.erb @@ -1,4 +1 @@ - +