diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 99db0beddb0de..9bd78810b19f7 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -516,40 +516,42 @@ def category_badge(category, opts = nil) CategoryBadge.html_for(category, opts).html_safe end - def self.all_connectors - @all_connectors = Dir.glob("plugins/*/app/views/connectors/**/*.html.erb") - end - - PLUGIN_OUTLET_TEMPLATE_CACHE = Concurrent::Map.new + SERVER_PLUGIN_OUTLET_PLUGINS_PREFIXES = [Rails.root.join("plugins/").to_s] + private_constant :SERVER_PLUGIN_OUTLET_PLUGINS_PREFIXES - def server_plugin_outlet(name, locals: {}) - return "" if !GlobalSetting.load_plugins? - - matcher = Regexp.new("/connectors/#{name}/.*\.html\.erb$") - erbs = ApplicationHelper.all_connectors.select { |c| c =~ matcher } - return "" if erbs.blank? - - erbs - .map do |erb| - cache_key = [erb, locals.keys.sort] + if Rails.env.test? + SERVER_PLUGIN_OUTLET_PLUGINS_PREFIXES << Rails.root.join("spec/fixtures/plugins/").to_s + end - template = - PLUGIN_OUTLET_TEMPLATE_CACHE.compute_if_absent(cache_key) do - source = File.read(erb) - handler = ActionView::Template.handler_for_extension("erb") + SERVER_PLUGIN_OUTLET_CONNECTOR_TEMPLATES = + SERVER_PLUGIN_OUTLET_PLUGINS_PREFIXES.each_with_object({}) do |plugins_prefix, connectors| + Dir + .glob("#{plugins_prefix}*/app/views/connectors/**/*.html.erb") + .each do |template_path| + template_path =~ Regexp.new("/connectors/(.*)/.*\.html\.erb$") + outlet_name = Regexp.last_match(1) + connectors[outlet_name] ||= [] + connectors[outlet_name] << begin ActionView::Template.new( - source, + File.read(template_path), "discourse_plugin_outlet__#{name}", - handler, - locals: locals.keys, + ActionView::Template.handler_for_extension("erb"), + locals: [], format: :html, - virtual_path: erb, + virtual_path: template_path, ) end + end + end + private_constant :SERVER_PLUGIN_OUTLET_CONNECTOR_TEMPLATES - render template: template, locals: locals - end + def server_plugin_outlet(name, locals: {}) + return "" if !GlobalSetting.load_plugins? + return "" if !SERVER_PLUGIN_OUTLET_CONNECTOR_TEMPLATES.key?(name) + + SERVER_PLUGIN_OUTLET_CONNECTOR_TEMPLATES[name] + .map { |template| render template:, locals: } .join .html_safe end diff --git a/config/initializers/development.rb b/config/initializers/development.rb deleted file mode 100644 index 6f87eee7d099a..0000000000000 --- a/config/initializers/development.rb +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true - -if Rails.env.development? - Rails.application.reloader.to_prepare { ApplicationHelper::PLUGIN_OUTLET_TEMPLATE_CACHE.clear } -end diff --git a/spec/fixtures/plugins/my_plugin/app/views/connectors/topic_header/template_1.html.erb b/spec/fixtures/plugins/my_plugin/app/views/connectors/topic_header/template_1.html.erb new file mode 100644 index 0000000000000..a3a87d213c129 --- /dev/null +++ b/spec/fixtures/plugins/my_plugin/app/views/connectors/topic_header/template_1.html.erb @@ -0,0 +1 @@ +Fixture from my_plugin template 1: <%= @topic_view.topic.title %> diff --git a/spec/fixtures/plugins/my_plugin/app/views/connectors/topic_header/template_2.html.erb b/spec/fixtures/plugins/my_plugin/app/views/connectors/topic_header/template_2.html.erb new file mode 100644 index 0000000000000..a764779135709 --- /dev/null +++ b/spec/fixtures/plugins/my_plugin/app/views/connectors/topic_header/template_2.html.erb @@ -0,0 +1 @@ +Fixture from my_plugin template 2: <%= @topic_view.topic.title %> diff --git a/spec/fixtures/plugins/my_plugin_2/app/views/connectors/topic_header/template_1.html.erb b/spec/fixtures/plugins/my_plugin_2/app/views/connectors/topic_header/template_1.html.erb new file mode 100644 index 0000000000000..f3e5e96aecef9 --- /dev/null +++ b/spec/fixtures/plugins/my_plugin_2/app/views/connectors/topic_header/template_1.html.erb @@ -0,0 +1 @@ +Fixture from my_plugin_2 template 1: <%= @topic_view.topic.title %> diff --git a/spec/fixtures/plugins/my_plugin_2/app/views/connectors/topic_header/template_2.html.erb b/spec/fixtures/plugins/my_plugin_2/app/views/connectors/topic_header/template_2.html.erb new file mode 100644 index 0000000000000..e5ae169e6283d --- /dev/null +++ b/spec/fixtures/plugins/my_plugin_2/app/views/connectors/topic_header/template_2.html.erb @@ -0,0 +1 @@ +Fixture from my_plugin_2 template 2: <%= @topic_view.topic.title %> diff --git a/spec/fixtures/plugins/my_plugin_3/app/views/connectors/some_other_outlet/template_1.html.erb b/spec/fixtures/plugins/my_plugin_3/app/views/connectors/some_other_outlet/template_1.html.erb new file mode 100644 index 0000000000000..7b4c63cf889b3 --- /dev/null +++ b/spec/fixtures/plugins/my_plugin_3/app/views/connectors/some_other_outlet/template_1.html.erb @@ -0,0 +1 @@ +Fixture from my_plugin_3 template 1: <%= @topic_view.topic.title %> diff --git a/spec/requests/topics_controller_spec.rb b/spec/requests/topics_controller_spec.rb index f1667a1a1a693..3b840f580cd7c 100644 --- a/spec/requests/topics_controller_spec.rb +++ b/spec/requests/topics_controller_spec.rb @@ -40,34 +40,35 @@ describe "topic_header plugin outlet" do fab!(:another_topic) { Fabricate(:topic, title: "Another topic by me") } - let(:tmp_dir) { Dir.mktmpdir } - let(:template_dir) do - path = File.join(tmp_dir, "connectors", "topic_header") - FileUtils.mkdir_p(path) - path - end - let(:template_file) do - file = Tempfile.new(%w[test_template .html.erb], template_dir) - file.write("Topic title from outlet: <%= @topic_view.topic.title %>") - file.close - file - end + before { global_setting(:load_plugins?, true) } - before do - global_setting(:load_plugins?, true) - ApplicationHelper.stubs(:all_connectors).returns([template_file.path]) - end - - after { FileUtils.remove_entry(tmp_dir) if tmp_dir } - - it "doesn't leak state between requests" do + it "renders the connector templates from multiple plugins" do get "/t/#{topic.slug}/#{topic.id}" + expect(response.status).to eq(200) - expect(response.body).to include("Topic title from outlet: #{topic.title}") + expect(response.body).to include("Fixture from my_plugin template 1: #{topic.title}") + expect(response.body).to include("Fixture from my_plugin template 2: #{topic.title}") + expect(response.body).to include("Fixture from my_plugin_2 template 1: #{topic.title}") + expect(response.body).to include("Fixture from my_plugin_2 template 2: #{topic.title}") + expect(response.body).not_to include("Fixture from my_plugin_3 template 1: #{topic.title}") get "/t/#{another_topic.slug}/#{another_topic.id}" + expect(response.status).to eq(200) - expect(response.body).to include("Topic title from outlet: #{another_topic.title}") + expect(response.body).to include("Fixture from my_plugin template 1: #{another_topic.title}") + expect(response.body).to include("Fixture from my_plugin template 2: #{another_topic.title}") + + expect(response.body).to include( + "Fixture from my_plugin_2 template 1: #{another_topic.title}", + ) + + expect(response.body).to include( + "Fixture from my_plugin_2 template 2: #{another_topic.title}", + ) + + expect(response.body).not_to include( + "Fixture from my_plugin_3 template 1: #{another_topic.title}", + ) end end