Skip to content

Commit 372f1f0

Browse files
authored
FIX: Don’t apply callbacks from disabled plugins (#35630) (#35703)
Currently, when a plugin is disabled, its various callbacks are still taken into account. This can lead to performance issues. This patch adds a check to most of the `register_*` methods a plugin can use, so if the plugin is disabled, those callbacks won’t be applied.
1 parent 814709b commit 372f1f0

File tree

11 files changed

+196
-83
lines changed

11 files changed

+196
-83
lines changed

app/models/site.rb

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ def self.reset_preloaded_category_custom_fields
2525
cattr_accessor :markdown_additional_options
2626
self.markdown_additional_options = {}
2727

28-
def self.add_categories_callbacks(&block)
29-
categories_callbacks << block
28+
def self.add_categories_callbacks(enabled: -> { true }, &block)
29+
categories_callbacks << { block:, enabled: }
3030
end
3131

3232
def self.categories_callbacks
@@ -178,7 +178,10 @@ def categories
178178

179179
categories.reject! { |c| c[:parent_category_id] && !by_id[c[:parent_category_id]] }
180180

181-
self.class.categories_callbacks.each { |callback| callback.call(categories, @guardian) }
181+
self.class.categories_callbacks.each do |callback|
182+
next unless callback[:enabled].call
183+
callback[:block].call(categories, @guardian)
184+
end
182185

183186
categories
184187
end

app/models/topic_list.rb

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,15 @@ def self.preload(topics, object)
2121
@preload.each { |preload| preload.call(topics, object) } if @preload
2222
end
2323

24-
def self.on_preload_user_ids(&blk)
25-
(@preload_user_ids ||= Set.new) << blk
24+
def self.on_preload_user_ids(enabled: -> { true }, &block)
25+
(@preload_user_ids ||= Set.new) << { block:, enabled: }
2626
end
2727

2828
def self.preload_user_ids(topics, user_ids, object)
2929
if @preload_user_ids
3030
@preload_user_ids.each do |preload_user_ids|
31-
user_ids = preload_user_ids.call(topics, user_ids, object)
31+
next unless preload_user_ids[:enabled].call
32+
user_ids = preload_user_ids[:block].call(topics, user_ids, object)
3233
end
3334
end
3435
user_ids

lib/plugin/instance.rb

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -293,31 +293,31 @@ def add_filter_custom_filter(name, &block)
293293
# scope.where("word_count = 42")
294294
# end
295295
def register_custom_filter_by_status(status, &block)
296-
TopicsFilter.add_filter_by_status(status, &block)
296+
TopicsFilter.add_filter_by_status(status, enabled: method(:enabled?), &block)
297297
end
298298

299299
# Allows to define custom search order. Example usage:
300300
# Search.advanced_order(:chars) do |posts|
301301
# posts.reorder("(SELECT LENGTH(raw) FROM posts WHERE posts.topic_id = subquery.topic_id) DESC")
302302
# end
303303
def register_search_advanced_order(trigger, &block)
304-
Search.advanced_order(trigger, &block)
304+
Search.advanced_order(trigger, enabled: method(:enabled?), &block)
305305
end
306306

307307
# Allows to define custom search filters. Example usage:
308308
# Search.advanced_filter(/^min_chars:(\d+)$/) do |posts, match|
309309
# posts.where("(SELECT LENGTH(p2.raw) FROM posts p2 WHERE p2.id = posts.id) >= ?", match.to_i)
310310
# end
311311
def register_search_advanced_filter(trigger, &block)
312-
Search.advanced_filter(trigger, &block)
312+
Search.advanced_filter(trigger, enabled: method(:enabled?), &block)
313313
end
314314

315315
# Allows to define TopicView posts filters. Example usage:
316316
# TopicView.advanced_filter do |posts, opts|
317317
# posts.where(wiki: true)
318318
# end
319319
def register_topic_view_posts_filter(trigger, &block)
320-
TopicView.add_custom_filter(trigger, &block)
320+
TopicView.add_custom_filter(trigger, enabled: method(:enabled?), &block)
321321
end
322322

323323
# Allows to add more user IDs to the list of preloaded users. This can be
@@ -327,7 +327,7 @@ def register_topic_view_posts_filter(trigger, &block)
327327
# user_ids << Discourse::SYSTEM_USER_ID
328328
# end
329329
def register_topic_list_preload_user_ids(&block)
330-
TopicList.on_preload_user_ids(&block)
330+
TopicList.on_preload_user_ids(enabled: method(:enabled?), &block)
331331
end
332332

333333
# Allow to eager load additional tables in Search. Useful to avoid N+1 performance problems.
@@ -338,7 +338,7 @@ def register_topic_list_preload_user_ids(&block)
338338
# OR
339339
# register_search_topic_eager_load(%i(example_table))
340340
def register_search_topic_eager_load(tables = nil, &block)
341-
Search.custom_topic_eager_load(tables, &block)
341+
Search.custom_topic_eager_load(tables, enabled: method(:enabled?), &block)
342342
end
343343

344344
# Request a new size for topic thumbnails
@@ -359,7 +359,7 @@ def register_topic_thumbnail_size(size)
359359
# end
360360
# end
361361
def register_site_categories_callback(&block)
362-
Site.add_categories_callbacks(&block)
362+
Site.add_categories_callbacks(enabled: method(:enabled?), &block)
363363
end
364364

365365
def register_upload_unused(&block)
@@ -736,8 +736,7 @@ def register_html_builder(name, &block)
736736
end
737737

738738
def register_email_poller(poller)
739-
plugin = self
740-
DiscoursePluginRegistry.register_mail_poller(poller) if plugin.enabled?
739+
DiscoursePluginRegistry.register_mail_poller(poller) if enabled?
741740
end
742741

743742
def register_asset(file, opts = nil)

lib/search.rb

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -365,24 +365,24 @@ def execute(readonly_mode: Discourse.readonly_mode?)
365365
@results
366366
end
367367

368-
def self.advanced_order(trigger, &block)
369-
advanced_orders[trigger] = block
368+
def self.advanced_order(trigger, enabled: -> { true }, &block)
369+
advanced_orders[trigger] = { block:, enabled: }
370370
end
371371

372372
def self.advanced_orders
373373
@advanced_orders ||= {}
374374
end
375375

376-
def self.advanced_filter(trigger, name: nil, &block)
377-
advanced_filters[trigger] = { block:, name: }
376+
def self.advanced_filter(trigger, name: nil, enabled: -> { true }, &block)
377+
advanced_filters[trigger] = { block:, name:, enabled: }
378378
end
379379

380380
def self.advanced_filters
381381
@advanced_filters ||= {}
382382
end
383383

384-
def self.custom_topic_eager_load(tables = nil, &block)
385-
(@custom_topic_eager_loads ||= []) << (tables || block)
384+
def self.custom_topic_eager_load(tables = nil, enabled: -> { true }, &block)
385+
(@custom_topic_eager_loads ||= []) << { tables:, block:, enabled: }
386386
end
387387

388388
def self.custom_topic_eager_loads
@@ -880,8 +880,8 @@ def apply_order(
880880
end
881881

882882
if @order
883-
advanced_order = Search.advanced_orders&.fetch(@order, nil)
884-
posts = advanced_order.call(posts) if advanced_order
883+
advanced_order = Search.advanced_orders[@order]
884+
posts = advanced_order[:block].call(posts) if advanced_order.try(:[], :enabled).try(:call)
885885
end
886886

887887
posts
@@ -934,6 +934,7 @@ def process_advanced_search!(term)
934934
Search.advanced_filters.each do |matcher, options|
935935
block = options[:block]
936936
name = options[:name]
937+
next unless options[:enabled].call
937938

938939
case_insensitive_matcher =
939940
Regexp.new(matcher.source, matcher.options | Regexp::IGNORECASE)
@@ -1539,8 +1540,9 @@ def posts_eager_loads(query)
15391540
topic_eager_loads << :tags if SiteSetting.tagging_enabled
15401541

15411542
Search.custom_topic_eager_loads.each do |custom_loads|
1543+
next unless custom_loads[:enabled].call
15421544
topic_eager_loads.concat(
1543-
custom_loads.is_a?(Array) ? custom_loads : custom_loads.call(search_pms: @search_pms).to_a,
1545+
custom_loads[:tables] || custom_loads[:block].call(search_pms: @search_pms).to_a,
15441546
)
15451547
end
15461548

lib/topic_view.rb

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ def self.allowed_post_custom_fields(user, topic)
7676
wpcf.flatten.uniq
7777
end
7878

79-
def self.add_custom_filter(key, &blk)
80-
custom_filters[key] = blk
79+
def self.add_custom_filter(key, enabled: -> { true }, &block)
80+
custom_filters[key] = { block:, enabled: }
8181
end
8282

8383
def self.custom_filters
@@ -1017,7 +1017,11 @@ def setup_filtered_posts
10171017
end
10181018

10191019
if @filter.present? && @filter.to_s != "summary" && TopicView.custom_filters[@filter].present?
1020-
@filtered_posts = TopicView.custom_filters[@filter].call(@filtered_posts, self)
1020+
@filtered_posts =
1021+
TopicView.custom_filters[@filter][:block].call(
1022+
@filtered_posts,
1023+
self,
1024+
) if TopicView.custom_filters[@filter][:enabled].call
10211025
end
10221026

10231027
if @best.present?

lib/topics_filter.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,8 @@ def filter_from_query_string(query_string)
107107
@scope
108108
end
109109

110-
def self.add_filter_by_status(status, &blk)
111-
custom_status_filters[status] = blk
110+
def self.add_filter_by_status(status, enabled: -> { true }, &block)
111+
custom_status_filters[status] = { block:, enabled: }
112112
end
113113

114114
def self.custom_status_filters
@@ -137,7 +137,7 @@ def filter_status(status:, category_id: nil)
137137
@scope = @scope.joins(:category).where("NOT categories.read_restricted")
138138
else
139139
if custom_filter = TopicsFilter.custom_status_filters[status]
140-
@scope = custom_filter.call(@scope)
140+
@scope = custom_filter[:block].call(@scope) if custom_filter[:enabled].call
141141
end
142142
end
143143

plugins/discourse-topic-voting/plugin.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ module ::DiscourseTopicVoting
4747

4848
if TopicQuery.respond_to?(:results_filter_callbacks)
4949
TopicQuery.results_filter_callbacks << ->(_type, result, user, options) do
50+
return result unless SiteSetting.topic_voting_enabled
51+
5052
result = result.includes(:topic_vote_count)
5153

5254
if user

spec/lib/plugin/instance_spec.rb

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -853,24 +853,60 @@ def scrub(reason, guardian)
853853
describe "#register_site_categories_callback" do
854854
fab!(:category)
855855

856-
it "adds a callback to the Site#categories" do
857-
instance = Plugin::Instance.new
858-
859-
site_guardian = Guardian.new
856+
let(:instance) { Plugin::Instance.new }
857+
let(:site_guardian) { Guardian.new }
858+
let(:site) { Site.new(site_guardian) }
860859

860+
before do
861+
allow(instance).to receive(:enabled?).and_call_original
861862
instance.register_site_categories_callback do |categories, guardian|
862863
categories.each { |category| category[:test_field] = "test" }
863864

864865
expect(guardian).to eq(site_guardian)
865866
end
867+
end
866868

867-
site = Site.new(site_guardian)
868-
869-
expect(site.categories.first[:test_field]).to eq("test")
870-
ensure
869+
after do
871870
Site.clear_cache
872871
Site.categories_callbacks.clear
873872
end
873+
874+
it "adds a callback to the Site#categories" do
875+
expect(site.categories.first[:test_field]).to eq("test")
876+
end
877+
878+
context "when the plugin is disabled" do
879+
before { allow(instance).to receive(:enabled?).and_return(false) }
880+
881+
it "does not run the callback" do
882+
expect(site.categories.first[:test_field]).to be_blank
883+
end
884+
end
885+
end
886+
887+
describe "#register_topic_list_preload_user_ids" do
888+
subject(:preload_user_ids) { TopicList.preload_user_ids(stub, [], stub) }
889+
890+
let(:instance) { Plugin::Instance.new }
891+
892+
before do
893+
allow(instance).to receive(:enabled?).and_call_original
894+
instance.register_topic_list_preload_user_ids { |topics, user_ids, object| [1] }
895+
end
896+
897+
after { TopicList.instance_variable_set(:@preload_user_ids, nil) }
898+
899+
it "adds a callback to TopicList.preload_user_ids" do
900+
expect(preload_user_ids).to include(1)
901+
end
902+
903+
context "when the plugin is disabled" do
904+
before { allow(instance).to receive(:enabled?).and_return(false) }
905+
906+
it "does not run the callback" do
907+
expect(preload_user_ids).to be_empty
908+
end
909+
end
874910
end
875911

876912
describe "#register_notification_consolidation_plan" do

0 commit comments

Comments
 (0)