diff --git a/app/controllers/alchemy/admin/pages_controller.rb b/app/controllers/alchemy/admin/pages_controller.rb index 4e90bdb622..4340bb570e 100644 --- a/app/controllers/alchemy/admin/pages_controller.rb +++ b/app/controllers/alchemy/admin/pages_controller.rb @@ -85,6 +85,7 @@ def edit elsif page_needs_lock? @page.lock_to!(current_alchemy_user) end + @preview_url = Alchemy::Admin::PREVIEW_URL.url_for(@page) @layoutpage = @page.layoutpage? end diff --git a/app/views/alchemy/admin/pages/edit.html.erb b/app/views/alchemy/admin/pages/edit.html.erb index d887e5c550..d9801d114a 100644 --- a/app/views/alchemy/admin/pages/edit.html.erb +++ b/app/views/alchemy/admin/pages/edit.html.erb @@ -190,7 +190,7 @@ } }); - Alchemy.PreviewWindow.init('<%= admin_page_path(@page) %>'); + Alchemy.PreviewWindow.init('<%= @preview_url %>'); $('#preview_size').bind('open.selectBoxIt', function (e) { $('#top_menu').css('z-index', 5000); diff --git a/config/alchemy/config.yml b/config/alchemy/config.yml index 9e5c07c899..63510d0c48 100644 --- a/config/alchemy/config.yml +++ b/config/alchemy/config.yml @@ -62,6 +62,19 @@ url_nesting: true # In Alchemy's Admin, change how many items you would get shown per page by Kaminari items_per_page: 15 +# === Preview window URL configuration +# +# By default Alchemy uses its internal page preview renderer, +# but you can configure it to be any URL instead. +# +# Basic Auth is supported. +# +# preview: +# host: https://www.my-static-site.com +# auth: +# username: <%= ENV["BASIC_AUTH_USERNAME"] %> +# password: <%= ENV["BASIC_AUTH_PASSWORD"] %> + # === Picture rendering settings # # Alchemy uses Dragonfly to render images. Use {size: "XXXxYYY", crop: BOOLEAN [true]} to resize images. diff --git a/lib/alchemy/admin/preview_url.rb b/lib/alchemy/admin/preview_url.rb new file mode 100644 index 0000000000..a599e56beb --- /dev/null +++ b/lib/alchemy/admin/preview_url.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +require "uri" + +module Alchemy + module Admin + # = Preview window URL configuration + # + # By default Alchemy uses its internal page preview renderer, + # but you can configure it to be any URL instead. + # + # Basic Auth is supported. + # + # == Example config/alchemy/config.yml + # + # preview: + # host: https://www.my-static-site.com + # auth: + # username: <%= ENV["BASIC_AUTH_USERNAME"] %> + # password: <%= ENV["BASIC_AUTH_PASSWORD"] %> + # + class PreviewUrl + class MissingProtocolError < StandardError; end + + def initialize(routes:) + @routes = routes.url_helpers + @preview_config = Alchemy::Config.get(:preview) + end + + def url_for(page) + if preview_config + uri_class.build( + host: uri.host, + path: "/#{page.urlname}", + userinfo: userinfo, + ).to_s + else + routes.admin_page_path(page) + end + end + + private + + attr_reader :preview_config, :routes + + def uri + URI(preview_config["host"]) + end + + def uri_class + if uri.class == URI::Generic + raise MissingProtocolError, "Please provide the protocol with preview['host']" + else + uri.class + end + end + + def userinfo + auth = preview_config["auth"] + auth ? "#{auth["username"]}:#{auth["password"]}" : nil + end + end + end +end diff --git a/lib/alchemy/engine.rb b/lib/alchemy/engine.rb index 4e488f59cb..6c8f6e83e5 100644 --- a/lib/alchemy/engine.rb +++ b/lib/alchemy/engine.rb @@ -9,6 +9,10 @@ class Engine < Rails::Engine Alchemy::LOOKUP_CONTEXT = ActionView::LookupContext.new(Rails.root.join("app", "views", "alchemy")) end + initializer "alchemy.admin.preview_url" do + Alchemy::Admin::PREVIEW_URL = Alchemy::Admin::PreviewUrl.new(routes: Alchemy::Engine.routes) + end + initializer "alchemy.dependency_tracker" do [:erb, :slim, :haml].each do |handler| ActionView::DependencyTracker.register_tracker(handler, CacheDigests::TemplateTracker) @@ -39,9 +43,10 @@ class Engine < Rails::Engine if Rails.env.development? initializer "alchemy.webpacker.proxy" do |app| app.middleware.insert_before( - 0, Webpacker::DevServerProxy, + 0, + Webpacker::DevServerProxy, ssl_verify_none: true, - webpacker: Alchemy.webpacker + webpacker: Alchemy.webpacker, ) end end diff --git a/lib/alchemy_cms.rb b/lib/alchemy_cms.rb index 2d34ab5c6b..79085fad40 100644 --- a/lib/alchemy_cms.rb +++ b/lib/alchemy_cms.rb @@ -30,6 +30,7 @@ module Alchemy # Require globally used Alchemy mixins require_relative "alchemy/ability_helper" require_relative "alchemy/admin/locale" +require_relative "alchemy/admin/preview_url" require_relative "alchemy/auth_accessors" require_relative "alchemy/cache_digests/template_tracker" require_relative "alchemy/config" diff --git a/spec/libraries/admin/preview_url_spec.rb b/spec/libraries/admin/preview_url_spec.rb new file mode 100644 index 0000000000..c2bdf9de5c --- /dev/null +++ b/spec/libraries/admin/preview_url_spec.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +require "rails_helper" + +RSpec.describe Alchemy::Admin::PreviewUrl do + let(:preview_url) do + described_class.new(routes: Alchemy::Engine.routes) + end + + describe "#url_for" do + let(:page) { create(:alchemy_page) } + + subject { preview_url.url_for(page) } + + context "without preview configured" do + it "returns the admin pages preview url" do + is_expected.to eq "/admin/pages/#{page.id}" + end + end + + context "with preview configured" do + context "without protocol" do + before do + stub_alchemy_config(:preview, { + "host" => "www.example.com", + }) + end + + it "raises error" do + expect { subject }.to raise_error(Alchemy::Admin::PreviewUrl::MissingProtocolError) + end + end + + context "as http url" do + before do + stub_alchemy_config(:preview, { + "host" => "http://www.example.com", + }) + end + + it "returns the configured preview url" do + is_expected.to eq "http://www.example.com/#{page.urlname}" + end + end + + context "as https url" do + before do + stub_alchemy_config(:preview, { + "host" => "https://www.example.com", + }) + end + + it "returns the configured preview url with https" do + is_expected.to eq "https://www.example.com/#{page.urlname}" + end + end + + context "and with basic auth configured" do + before do + stub_alchemy_config(:preview, { + "host" => "https://www.example.com", + "auth" => { + "username" => "foo", + "password" => "baz", + }, + }) + end + + it "returns the configured preview url with userinfo" do + is_expected.to eq "https://foo:baz@www.example.com/#{page.urlname}" + end + end + end + end +end