mirror of
https://github.com/discourse/discourse.git
synced 2025-04-24 18:34:29 +08:00
Helpers for plugins to support enabling/disabling
This commit is contained in:
parent
530b20d339
commit
25daca8f23
app
assets/javascripts/discourse/helpers
controllers
serializers
lib
spec/components/plugin
@ -39,18 +39,35 @@
|
|||||||
Nobody says hello :'(
|
Nobody says hello :'(
|
||||||
{{/plugin-outlet}}
|
{{/plugin-outlet}}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Disabling
|
||||||
|
|
||||||
|
If a plugin returns a disabled status, the outlets will not be wired up for it.
|
||||||
|
The list of disabled plugins is returned via the `Site` singleton.
|
||||||
|
|
||||||
**/
|
**/
|
||||||
|
|
||||||
var _connectorCache;
|
var _connectorCache;
|
||||||
|
|
||||||
function findOutlets(collection, callback) {
|
function findOutlets(collection, callback) {
|
||||||
Ember.keys(collection).forEach(function(i) {
|
|
||||||
if (i.indexOf("/connectors/") !== -1) {
|
var disabledPlugins = Discourse.Site.currentProp('disabled_plugins') || [];
|
||||||
var segments = i.split("/"),
|
|
||||||
|
Ember.keys(collection).forEach(function(res) {
|
||||||
|
if (res.indexOf("/connectors/") !== -1) {
|
||||||
|
// Skip any disabled plugins
|
||||||
|
for (var i=0; i<disabledPlugins.length; i++) {
|
||||||
|
if (res.indexOf("/" + disabledPlugins[i] + "/") !== -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var segments = res.split("/"),
|
||||||
outletName = segments[segments.length-2],
|
outletName = segments[segments.length-2],
|
||||||
uniqueName = segments[segments.length-1];
|
uniqueName = segments[segments.length-1];
|
||||||
|
|
||||||
callback(outletName, i, uniqueName);
|
|
||||||
|
callback(outletName, res, uniqueName);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -59,18 +76,18 @@ function buildConnectorCache() {
|
|||||||
_connectorCache = {};
|
_connectorCache = {};
|
||||||
|
|
||||||
var uniqueViews = {};
|
var uniqueViews = {};
|
||||||
findOutlets(requirejs._eak_seen, function(outletName, idx, uniqueName) {
|
findOutlets(requirejs._eak_seen, function(outletName, resource, uniqueName) {
|
||||||
_connectorCache[outletName] = _connectorCache[outletName] || [];
|
_connectorCache[outletName] = _connectorCache[outletName] || [];
|
||||||
|
|
||||||
var viewClass = require(idx, null, null, true).default;
|
var viewClass = require(resource, null, null, true).default;
|
||||||
uniqueViews[uniqueName] = viewClass;
|
uniqueViews[uniqueName] = viewClass;
|
||||||
_connectorCache[outletName].pushObject(viewClass);
|
_connectorCache[outletName].pushObject(viewClass);
|
||||||
});
|
});
|
||||||
|
|
||||||
findOutlets(Ember.TEMPLATES, function(outletName, idx, uniqueName) {
|
findOutlets(Ember.TEMPLATES, function(outletName, resource, uniqueName) {
|
||||||
_connectorCache[outletName] = _connectorCache[outletName] || [];
|
_connectorCache[outletName] = _connectorCache[outletName] || [];
|
||||||
|
|
||||||
var mixin = {templateName: idx.replace('javascripts/', '')},
|
var mixin = {templateName: resource.replace('javascripts/', '')},
|
||||||
viewClass = uniqueViews[uniqueName];
|
viewClass = uniqueViews[uniqueName];
|
||||||
|
|
||||||
if (viewClass) {
|
if (viewClass) {
|
||||||
@ -81,7 +98,6 @@ function buildConnectorCache() {
|
|||||||
}
|
}
|
||||||
_connectorCache[outletName].pushObject(viewClass.extend(mixin));
|
_connectorCache[outletName].pushObject(viewClass.extend(mixin));
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function(connectionName, options) {
|
export default function(connectionName, options) {
|
||||||
|
@ -130,6 +130,16 @@ class ApplicationController < ActionController::Base
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class PluginDisabled < Exception; end
|
||||||
|
|
||||||
|
# If a controller requires a plugin, it will raise an exception if that plugin is
|
||||||
|
# disabled. This allows plugins to be disabled programatically.
|
||||||
|
def self.requires_plugin(plugin_name)
|
||||||
|
before_filter do
|
||||||
|
raise PluginDisabled.new if Discourse.disabled_plugin_names.include?(plugin_name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def set_current_user_for_logs
|
def set_current_user_for_logs
|
||||||
if current_user
|
if current_user
|
||||||
Logster.add_to_env(request.env,"username",current_user.username)
|
Logster.add_to_env(request.env,"username",current_user.username)
|
||||||
|
@ -9,7 +9,8 @@ class SiteSerializer < ApplicationSerializer
|
|||||||
:top_menu_items,
|
:top_menu_items,
|
||||||
:anonymous_top_menu_items,
|
:anonymous_top_menu_items,
|
||||||
:uncategorized_category_id, # this is hidden so putting it here
|
:uncategorized_category_id, # this is hidden so putting it here
|
||||||
:is_readonly
|
:is_readonly,
|
||||||
|
:disabled_plugins
|
||||||
|
|
||||||
has_many :categories, serializer: BasicCategorySerializer, embed: :objects
|
has_many :categories, serializer: BasicCategorySerializer, embed: :objects
|
||||||
has_many :post_action_types, embed: :objects
|
has_many :post_action_types, embed: :objects
|
||||||
@ -51,4 +52,8 @@ class SiteSerializer < ApplicationSerializer
|
|||||||
Discourse.readonly_mode?
|
Discourse.readonly_mode?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def disabled_plugins
|
||||||
|
Discourse.disabled_plugin_names
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -84,6 +84,11 @@ module Discourse
|
|||||||
@plugins.each { |plugin| plugin.activate! }
|
@plugins.each { |plugin| plugin.activate! }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.disabled_plugin_names
|
||||||
|
return [] if @plugins.blank?
|
||||||
|
@plugins.select {|p| !p.enabled?}.map(&:name)
|
||||||
|
end
|
||||||
|
|
||||||
def self.plugins
|
def self.plugins
|
||||||
@plugins
|
@plugins
|
||||||
end
|
end
|
||||||
|
@ -39,15 +39,35 @@ class Plugin::Instance
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def name
|
def enabled?
|
||||||
metadata.name
|
return @enabled_site_setting ? SiteSetting.send(@enabled_site_setting) : true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
delegate :name, to: :metadata
|
||||||
|
|
||||||
def add_to_serializer(serializer, attr, &block)
|
def add_to_serializer(serializer, attr, &block)
|
||||||
klass = "#{serializer.to_s.classify}Serializer".constantize
|
klass = "#{serializer.to_s.classify}Serializer".constantize
|
||||||
|
|
||||||
klass.attributes(attr)
|
klass.attributes(attr)
|
||||||
klass.send(:define_method, attr, &block)
|
klass.send(:define_method, attr, &block)
|
||||||
|
|
||||||
|
# Don't include serialized methods if the plugin is disabled
|
||||||
|
plugin = self
|
||||||
|
klass.send(:define_method, "include_#{attr}?") do
|
||||||
|
plugin.enabled?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Extend a class but check that the plugin is enabled
|
||||||
|
def add_to_class(klass, attr, &block)
|
||||||
|
klass = klass.to_s.classify.constantize
|
||||||
|
|
||||||
|
hidden_method_name = "#{attr}_without_enable_check".to_sym
|
||||||
|
klass.send(:define_method, hidden_method_name, &block)
|
||||||
|
|
||||||
|
plugin = self
|
||||||
|
klass.send(:define_method, attr) do |*args|
|
||||||
|
send(hidden_method_name, *args) if plugin.enabled?
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# will make sure all the assets this plugin needs are registered
|
# will make sure all the assets this plugin needs are registered
|
||||||
@ -95,13 +115,20 @@ class Plugin::Instance
|
|||||||
initializers << block
|
initializers << block
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# A proxy to `DiscourseEvent.on` which does nothing if the plugin is disabled
|
||||||
|
def on(event_name, &block)
|
||||||
|
DiscourseEvent.on(event_name) do |*args|
|
||||||
|
block.call(*args) if enabled?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def notify_after_initialize
|
def notify_after_initialize
|
||||||
color_schemes.each do |c|
|
color_schemes.each do |c|
|
||||||
ColorScheme.create_from_base(name: c[:name], colors: c[:colors]) unless ColorScheme.where(name: c[:name]).exists?
|
ColorScheme.create_from_base(name: c[:name], colors: c[:colors]) unless ColorScheme.where(name: c[:name]).exists?
|
||||||
end
|
end
|
||||||
|
|
||||||
initializers.each do |callback|
|
initializers.each do |callback|
|
||||||
callback.call
|
callback.call(self)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -235,6 +262,10 @@ class Plugin::Instance
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def enabled_site_setting(setting)
|
||||||
|
@enabled_site_setting = setting
|
||||||
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def register_assets!
|
def register_assets!
|
||||||
|
@ -23,6 +23,60 @@ describe Plugin::Instance do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "enabling/disabling" do
|
||||||
|
|
||||||
|
it "is enabled by default" do
|
||||||
|
expect(Plugin::Instance.new.enabled?).to eq(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with a plugin that extends things" do
|
||||||
|
|
||||||
|
class Trout; end
|
||||||
|
class TroutSerializer < ApplicationSerializer; end
|
||||||
|
|
||||||
|
class TroutPlugin < Plugin::Instance
|
||||||
|
attr_accessor :enabled
|
||||||
|
def enabled?; @enabled; end
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
@plugin = TroutPlugin.new
|
||||||
|
@trout = Trout.new
|
||||||
|
|
||||||
|
# New method
|
||||||
|
@plugin.add_to_class(:trout, :status?) { "evil" }
|
||||||
|
|
||||||
|
# DiscourseEvent
|
||||||
|
@hello_count = 0
|
||||||
|
@plugin.on(:hello) { @hello_count += 1 }
|
||||||
|
|
||||||
|
# Serializer
|
||||||
|
@plugin.add_to_serializer(:trout, :scales) { 1024 }
|
||||||
|
@serializer = TroutSerializer.new(@trout)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "checks enabled/disabled functionality for extensions" do
|
||||||
|
|
||||||
|
# with an enabled plugin
|
||||||
|
@plugin.enabled = true
|
||||||
|
expect(@trout.status?).to eq("evil")
|
||||||
|
DiscourseEvent.trigger(:hello)
|
||||||
|
expect(@hello_count).to eq(1)
|
||||||
|
expect(@serializer.scales).to eq(1024)
|
||||||
|
expect(@serializer.include_scales?).to eq(true)
|
||||||
|
|
||||||
|
# When a plugin is disabled
|
||||||
|
@plugin.enabled = false
|
||||||
|
expect(@trout.status?).to eq(nil)
|
||||||
|
DiscourseEvent.trigger(:hello)
|
||||||
|
expect(@hello_count).to eq(1)
|
||||||
|
expect(@serializer.scales).to eq(1024)
|
||||||
|
expect(@serializer.include_scales?).to eq(false)
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context "register asset" do
|
context "register asset" do
|
||||||
it "populates the DiscoursePluginRegistry" do
|
it "populates the DiscoursePluginRegistry" do
|
||||||
plugin = Plugin::Instance.new nil, "/tmp/test.rb"
|
plugin = Plugin::Instance.new nil, "/tmp/test.rb"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user