From 1bbf3244157c75f80395dc1530aa6b595424303b Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Fri, 26 Feb 2021 13:00:31 -0500 Subject: [PATCH] FIX: When using Ember CLI theme Javascript/HTML was not being inserted (#12227) This also supports plugins with custom HTML. --- .../public/assets/scripts/discourse-boot.js | 40 ++++++++++++++----- app/controllers/bootstrap_controller.rb | 37 +++++++++++++++++ spec/requests/bootstrap_controller_spec.rb | 19 +++++++++ 3 files changed, 86 insertions(+), 10 deletions(-) diff --git a/app/assets/javascripts/discourse/public/assets/scripts/discourse-boot.js b/app/assets/javascripts/discourse/public/assets/scripts/discourse-boot.js index 054c67a3770..1d943729380 100644 --- a/app/assets/javascripts/discourse/public/assets/scripts/discourse-boot.js +++ b/app/assets/javascripts/discourse/public/assets/scripts/discourse-boot.js @@ -187,25 +187,27 @@ fetch("/bootstrap.json") .then((res) => res.json()) .then((data) => { - config.bootstrap = data.bootstrap; + let bootstrap = data.bootstrap; + + config.bootstrap = bootstrap; // We know better, we packaged this. config.bootstrap.setup_data.markdown_it_url = "/assets/discourse-markdown.js"; - let locale = data.bootstrap.locale_script; + let locale = bootstrap.locale_script; - if (data.bootstrap.csrf_token) { + if (bootstrap.csrf_token) { const csrfParam = document.createElement("meta"); csrfParam.setAttribute("name", "csrf-param"); csrfParam.setAttribute("content", "authenticity_token"); head.append(csrfParam); const csrfToken = document.createElement("meta"); csrfToken.setAttribute("name", "csrf-token"); - csrfToken.setAttribute("content", data.bootstrap.csrf_token); + csrfToken.setAttribute("content", bootstrap.csrf_token); head.append(csrfToken); } - (data.bootstrap.stylesheets || []).forEach((s) => { + (bootstrap.stylesheets || []).forEach((s) => { let link = document.createElement("link"); link.setAttribute("rel", "stylesheet"); link.setAttribute("type", "text/css"); @@ -222,9 +224,9 @@ head.append(link); }); - let pluginJs = data.bootstrap.plugin_js; + let pluginJs = bootstrap.plugin_js; if (isTesting()) { - // pluginJs = pluginJs.concat(data.bootstrap.plugin_test_js); + // pluginJs = pluginJs.concat(bootstrap.plugin_test_js); } pluginJs.forEach((src) => { @@ -233,19 +235,37 @@ head.append(script); }); - if (data.bootstrap.theme_ids) { + if (bootstrap.theme_ids) { let theme_ids = document.createElement("meta"); theme_ids.setAttribute("name", "discourse_theme_ids"); - theme_ids.setAttribute("content", data.bootstrap.theme_ids); + theme_ids.setAttribute("content", bootstrap.theme_ids); head.append(theme_ids); } + let themeHtml = bootstrap.theme_html; + let html = bootstrap.html; + + head.insertAdjacentHTML("beforeend", themeHtml.translations || ""); + head.insertAdjacentHTML("beforeend", themeHtml.js || ""); + head.insertAdjacentHTML("beforeend", themeHtml.head_tag || ""); + + head.insertAdjacentHTML("afterbegin", html.before_script_load || ""); + head.insertAdjacentHTML("beforeend", html.before_head_close || ""); + + let main = document.getElementById("main"); + main.insertAdjacentHTML("beforebegin", themeHtml.header || ""); + main.insertAdjacentHTML("beforebegin", html.header || ""); + + let body = document.getElementsByTagName("body")[0]; + body.insertAdjacentHTML("beforeend", themeHtml.body_tag || ""); + body.insertAdjacentHTML("beforeend", html.before_body_close || ""); + loadScript(locale).then(() => { define("I18n", ["exports"], function (exports) { return I18n; }); window.__widget_helpers = require("discourse-widget-hbs/helpers").default; - let extras = (data.bootstrap.extra_locales || []).map(loadScript); + let extras = (bootstrap.extra_locales || []).map(loadScript); return Promise.all(extras).then(() => { const event = new CustomEvent("discourse-booted", { detail: config }); document.dispatchEvent(event); diff --git a/app/controllers/bootstrap_controller.rb b/app/controllers/bootstrap_controller.rb index b0378359c5f..e80b9777f05 100644 --- a/app/controllers/bootstrap_controller.rb +++ b/app/controllers/bootstrap_controller.rb @@ -58,6 +58,8 @@ class BootstrapController < ApplicationController plugin_test_js: [script_asset_path("plugin_tests")], setup_data: client_side_setup_data, preloaded: @preloaded, + html: create_html, + theme_html: create_theme_html, } bootstrap[:extra_locales] = extra_locales if extra_locales.present? bootstrap[:csrf_token] = form_authenticity_token if current_user @@ -88,4 +90,39 @@ private end end + def create_html + html = {} + return html unless allow_plugins? + + add_plugin_html(html, :before_body_close) + add_plugin_html(html, :before_head_close) + add_plugin_html(html, :before_script_load) + add_plugin_html(html, :header) + + html + end + + def add_plugin_html(html, key) + add_if_present(html, key, DiscoursePluginRegistry.build_html("server:#{key.to_s.dasherize}", self)) + end + + def create_theme_html + theme_html = {} + return theme_html if customization_disabled? + + theme_view = mobile_view? ? :mobile : :desktop + + add_if_present(theme_html, :body_tag, Theme.lookup_field(theme_ids, theme_view, 'body_tag')) + add_if_present(theme_html, :head_tag, Theme.lookup_field(theme_ids, theme_view, 'head_tag')) + add_if_present(theme_html, :header, Theme.lookup_field(theme_ids, theme_view, 'header')) + add_if_present(theme_html, :translations, Theme.lookup_field(theme_ids, :translations, I18n.locale)) + add_if_present(theme_html, :js, Theme.lookup_field(theme_ids, :extra_js, nil)) + + theme_html + end + + def add_if_present(hash, key, val) + hash[key] = val if val.present? + end + end diff --git a/spec/requests/bootstrap_controller_spec.rb b/spec/requests/bootstrap_controller_spec.rb index 4cd735351d1..29aaa72a69d 100644 --- a/spec/requests/bootstrap_controller_spec.rb +++ b/spec/requests/bootstrap_controller_spec.rb @@ -4,6 +4,18 @@ require 'rails_helper' describe BootstrapController do + let(:theme) { Fabricate(:theme, enabled: true) } + + before do + DiscoursePluginRegistry.register_html_builder('server:before-head-close') { 'wat' } + theme.set_field(target: :desktop, name: :header, value: '

custom header

').save + SiteSetting.default_theme_id = theme.id + end + + after do + DiscoursePluginRegistry.reset! + end + it "returns data as anonymous" do get "/bootstrap.json" expect(response.status).to eq(200) @@ -14,9 +26,16 @@ describe BootstrapController do bootstrap = json['bootstrap'] expect(bootstrap).to be_present expect(bootstrap['title']).to be_present + expect(bootstrap['theme_ids']).to eq([theme.id]) expect(bootstrap['setup_data']['base_url']).to eq(Discourse.base_url) expect(bootstrap['stylesheets']).to be_present + expect(bootstrap['html']).to be_present + expect(bootstrap['html']['before_head_close']).to eq('wat') + + expect(bootstrap['theme_html']).to be_present + expect(bootstrap['theme_html']['header']).to eq('

custom header

') + preloaded = bootstrap['preloaded'] expect(preloaded['site']).to be_present expect(preloaded['siteSettings']).to be_present