mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-20 22:42:53 -05:00
Initial Plugin API Implementation
Implements basic functionality described in #227 for loading plugins from a specific directory and having a specific workflow with an init() method and a disable() method.
This commit is contained in:
parent
8d7336a1fb
commit
0238909281
10 changed files with 444 additions and 83 deletions
|
@ -41,7 +41,7 @@ config.activeTheme = 'casper';
|
||||||
|
|
||||||
|
|
||||||
config.activePlugins = [
|
config.activePlugins = [
|
||||||
'fancyFirstChar.js'
|
'FancyFirstChar'
|
||||||
];
|
];
|
||||||
|
|
||||||
// Default Navigation Items
|
// Default Navigation Items
|
||||||
|
|
62
content/plugins/FancyFirstChar/index.js
Normal file
62
content/plugins/FancyFirstChar/index.js
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
var util = require('util'),
|
||||||
|
_ = require('underscore'),
|
||||||
|
fancifyPlugin;
|
||||||
|
|
||||||
|
fancifyPlugin = {
|
||||||
|
|
||||||
|
// Fancify a single post body
|
||||||
|
fancify: function (originalContent) {
|
||||||
|
var newContent,
|
||||||
|
firstCharIndex = 0;
|
||||||
|
|
||||||
|
if (originalContent.substr(0, 1) === '<') {
|
||||||
|
firstCharIndex = originalContent.indexOf('>') + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
newContent = originalContent.substr(0, firstCharIndex);
|
||||||
|
newContent += '<span class="fancyChar">';
|
||||||
|
newContent += originalContent.substr(firstCharIndex, 1);
|
||||||
|
newContent += '</span>';
|
||||||
|
newContent += originalContent.substr(firstCharIndex + 1, originalContent.length - firstCharIndex - 1);
|
||||||
|
|
||||||
|
return newContent;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Fancify a collection of posts
|
||||||
|
fancifyPosts: function (posts) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
if (_.isArray(posts)) {
|
||||||
|
_.each(posts, function (post) {
|
||||||
|
post.content = self.fancify(post.content);
|
||||||
|
});
|
||||||
|
} else if (posts.hasOwnProperty('content')) {
|
||||||
|
posts.content = this.fancify(posts.content);
|
||||||
|
}
|
||||||
|
|
||||||
|
return posts;
|
||||||
|
},
|
||||||
|
|
||||||
|
install: function () {
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
uninstall: function () {
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
// Registers the prePostsRender filter to alter the content.
|
||||||
|
activate: function (ghost) {
|
||||||
|
ghost.registerFilter('prePostsRender', this.fancifyPosts);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Unregister any filters.
|
||||||
|
deactivate: function (ghost) {
|
||||||
|
ghost.unregisterFilter("prePostsRender", this.fancifyPosts);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Ensure our this context in the important methods
|
||||||
|
_.bindAll(fancifyPlugin, "fancifyPosts", "fancify", "activate", "deactivate");
|
||||||
|
|
||||||
|
module.exports = fancifyPlugin;
|
|
@ -1,41 +0,0 @@
|
||||||
var _ = require('underscore');
|
|
||||||
|
|
||||||
var fancyFirstChar;
|
|
||||||
|
|
||||||
function fancify(originalContent) {
|
|
||||||
var newContent,
|
|
||||||
firstCharIndex = 0;
|
|
||||||
|
|
||||||
if (originalContent.substr(0, 1) === '<') {
|
|
||||||
firstCharIndex = originalContent.indexOf('>') + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
newContent = originalContent.substr(0, firstCharIndex);
|
|
||||||
newContent += '<span class="fancyChar">';
|
|
||||||
newContent += originalContent.substr(firstCharIndex, 1);
|
|
||||||
newContent += '</span>';
|
|
||||||
newContent += originalContent.substr(firstCharIndex + 1, originalContent.length - firstCharIndex - 1);
|
|
||||||
|
|
||||||
return newContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fancyFirstChar = {
|
|
||||||
init: function (ghost) {
|
|
||||||
ghost.registerFilter('prePostsRender', function (posts) {
|
|
||||||
if (_.isArray(posts)) {
|
|
||||||
_.each(posts, function (post) {
|
|
||||||
post.content = fancify(post.content);
|
|
||||||
});
|
|
||||||
} else if (posts.hasOwnProperty('content')) {
|
|
||||||
posts.content = fancify(posts.content);
|
|
||||||
}
|
|
||||||
|
|
||||||
return posts;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
activate: function () {},
|
|
||||||
deactivate: function () {}
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = fancyFirstChar;
|
|
|
@ -15,6 +15,8 @@ var config = require('./../config'),
|
||||||
|
|
||||||
models = require('./server/models'),
|
models = require('./server/models'),
|
||||||
|
|
||||||
|
plugins = require('./server/plugins'),
|
||||||
|
|
||||||
requireTree = require('./server/require-tree'),
|
requireTree = require('./server/require-tree'),
|
||||||
themePath = path.resolve(__dirname + '../../content/themes'),
|
themePath = path.resolve(__dirname + '../../content/themes'),
|
||||||
pluginPath = path.resolve(__dirname + '../../content/plugins'),
|
pluginPath = path.resolve(__dirname + '../../content/plugins'),
|
||||||
|
@ -77,6 +79,8 @@ Ghost = function () {
|
||||||
// Holds the persistent notifications
|
// Holds the persistent notifications
|
||||||
instance.notifications = [];
|
instance.notifications = [];
|
||||||
|
|
||||||
|
instance.availablePlugins = {};
|
||||||
|
|
||||||
app = express();
|
app = express();
|
||||||
|
|
||||||
polyglot = new Polyglot();
|
polyglot = new Polyglot();
|
||||||
|
@ -120,36 +124,12 @@ Ghost.prototype.init = function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
return when.join(instance.dataProvider.init(), instance.getPaths()).then(function () {
|
return when.join(instance.dataProvider.init(), instance.getPaths()).then(function () {
|
||||||
return self.loadPlugins();
|
return self.initPlugins();
|
||||||
}, errors.logAndThrowError).then(function () {
|
}, errors.logAndThrowError).then(function () {
|
||||||
return self.updateSettingsCache();
|
return self.updateSettingsCache();
|
||||||
}, errors.logAndThrowError);
|
}, errors.logAndThrowError);
|
||||||
};
|
};
|
||||||
|
|
||||||
Ghost.prototype.loadPlugins = function () {
|
|
||||||
var self = this,
|
|
||||||
pluginPaths = _.values(self.paths().availablePlugins),
|
|
||||||
pluginPromises = [];
|
|
||||||
|
|
||||||
_.each(self.config().activePlugins, function (plugin) {
|
|
||||||
var match = _.find(pluginPaths, function (path) {
|
|
||||||
return new RegExp(plugin + '$').test(path);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (match) {
|
|
||||||
pluginPromises.push(require(path.join(pluginPath, plugin)));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return when.all(pluginPromises).then(function (plugins) {
|
|
||||||
_.each(plugins, function (plugin) {
|
|
||||||
if (_.isFunction(plugin.init)) {
|
|
||||||
plugin.init(self);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, errors.logAndThrowError);
|
|
||||||
};
|
|
||||||
|
|
||||||
Ghost.prototype.updateSettingsCache = function (settings) {
|
Ghost.prototype.updateSettingsCache = function (settings) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
|
@ -246,6 +226,25 @@ Ghost.prototype.registerFilter = function (name, priority, fn) {
|
||||||
this.filterCallbacks[name][priority].push(fn);
|
this.filterCallbacks[name][priority].push(fn);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} name
|
||||||
|
* @param {integer} priority
|
||||||
|
* @param {Function} fn
|
||||||
|
*/
|
||||||
|
Ghost.prototype.unregisterFilter = function (name, priority, fn) {
|
||||||
|
// Curry the priority optional parameter to a default of 5
|
||||||
|
if (_.isFunction(priority)) {
|
||||||
|
fn = priority;
|
||||||
|
priority = defaults.filterPriority;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if it even exists
|
||||||
|
if (this.filterCallbacks[name] && this.filterCallbacks[name][priority]) {
|
||||||
|
// Remove the function from the list of filter funcs
|
||||||
|
this.filterCallbacks[name][priority] = _.without(this.filterCallbacks[name][priority], fn);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} name [description]
|
* @param {string} name [description]
|
||||||
* @param {*} args
|
* @param {*} args
|
||||||
|
@ -280,10 +279,25 @@ Ghost.prototype.doFilter = function (name, args, callback) {
|
||||||
callback(args);
|
callback(args);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise plugins. Will load from config.activePlugins by default
|
||||||
|
*
|
||||||
|
* @param {Array} pluginsToLoad
|
||||||
|
*/
|
||||||
|
Ghost.prototype.initPlugins = function (pluginsToLoad) {
|
||||||
|
pluginsToLoad = pluginsToLoad || config.activePlugins;
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
return plugins.init(this, pluginsToLoad).then(function (loadedPlugins) {
|
||||||
|
// Extend the loadedPlugins onto the available plugins
|
||||||
|
_.extend(self.availablePlugins, loadedPlugins);
|
||||||
|
}, errors.logAndThrowError);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialise Theme
|
* Initialise Theme
|
||||||
*
|
*
|
||||||
* @todo Tod (?) Old comment
|
|
||||||
* @param {Object} app
|
* @param {Object} app
|
||||||
*/
|
*/
|
||||||
Ghost.prototype.initTheme = function (app) {
|
Ghost.prototype.initTheme = function (app) {
|
||||||
|
|
|
@ -95,6 +95,10 @@ settings = {
|
||||||
return dataProvider.Settings.browse(options).then(settingsObject, errors.logAndThrowError);
|
return dataProvider.Settings.browse(options).then(settingsObject, errors.logAndThrowError);
|
||||||
},
|
},
|
||||||
read: function read(options) {
|
read: function read(options) {
|
||||||
|
if (_.isString(options)) {
|
||||||
|
options = { key: options };
|
||||||
|
}
|
||||||
|
|
||||||
return dataProvider.Settings.read(options.key).then(function (setting) {
|
return dataProvider.Settings.read(options.key).then(function (setting) {
|
||||||
if (!setting) {
|
if (!setting) {
|
||||||
return when.reject("Unable to find setting: " + options.key);
|
return when.reject("Unable to find setting: " + options.key);
|
||||||
|
@ -103,9 +107,26 @@ settings = {
|
||||||
return _.pick(setting.toJSON(), 'key', 'value');
|
return _.pick(setting.toJSON(), 'key', 'value');
|
||||||
}, errors.logAndThrowError);
|
}, errors.logAndThrowError);
|
||||||
},
|
},
|
||||||
edit: function edit(settings) {
|
edit: function edit(key, value) {
|
||||||
settings = settingsCollection(settings);
|
// Check for passing a collection of settings first
|
||||||
return dataProvider.Settings.edit(settings).then(settingsObject, errors.logAndThrowError);
|
if (_.isObject(key)) {
|
||||||
|
key = settingsCollection(key);
|
||||||
|
return dataProvider.Settings.edit(key).then(settingsObject, errors.logAndThrowError);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dataProvider.Settings.read(key).then(function (setting) {
|
||||||
|
if (!setting) {
|
||||||
|
return when.reject("Unable to find setting: " + key);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_.isString(value)) {
|
||||||
|
value = JSON.stringify(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
setting.set('value', value);
|
||||||
|
|
||||||
|
return dataProvider.Settings.edit(setting);
|
||||||
|
}, errors.logAndThrowError);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
50
core/server/plugins/GhostPlugin.js
Normal file
50
core/server/plugins/GhostPlugin.js
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
|
||||||
|
var GhostPlugin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GhostPlugin is the base class for a standard plugin.
|
||||||
|
* @class
|
||||||
|
* @parameter {Ghost} The current Ghost app instance
|
||||||
|
*/
|
||||||
|
GhostPlugin = function (ghost) {
|
||||||
|
this.app = ghost;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A method that will be called on installation.
|
||||||
|
* Can optionally return a promise if async.
|
||||||
|
* @parameter {Ghost} The current Ghost app instance
|
||||||
|
*/
|
||||||
|
GhostPlugin.prototype.install = function (ghost) {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A method that will be called on uninstallation.
|
||||||
|
* Can optionally return a promise if async.
|
||||||
|
* @parameter {Ghost} The current Ghost app instance
|
||||||
|
*/
|
||||||
|
GhostPlugin.prototype.uninstall = function (ghost) {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A method that will be called when the plugin is enabled.
|
||||||
|
* Can optionally return a promise if async.
|
||||||
|
* @parameter {Ghost} The current Ghost app instance
|
||||||
|
*/
|
||||||
|
GhostPlugin.prototype.activate = function (ghost) {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A method that will be called when the plugin is disabled.
|
||||||
|
* Can optionally return a promise if async.
|
||||||
|
* @parameter {Ghost} The current Ghost app instance
|
||||||
|
*/
|
||||||
|
GhostPlugin.prototype.deactivate = function (ghost) {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = GhostPlugin;
|
||||||
|
|
72
core/server/plugins/index.js
Normal file
72
core/server/plugins/index.js
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
|
||||||
|
var _ = require("underscore"),
|
||||||
|
when = require('when'),
|
||||||
|
ghostApi,
|
||||||
|
loader = require("./loader"),
|
||||||
|
GhostPlugin = require("./GhostPlugin");
|
||||||
|
|
||||||
|
function getInstalledPlugins() {
|
||||||
|
if (!ghostApi) {
|
||||||
|
ghostApi = require('../api');
|
||||||
|
}
|
||||||
|
|
||||||
|
return ghostApi.settings.read("installedPlugins").then(function (installed) {
|
||||||
|
installed = installed || "[]";
|
||||||
|
|
||||||
|
try {
|
||||||
|
installed = JSON.parse(installed.value);
|
||||||
|
} catch (e) {
|
||||||
|
return when.reject(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return installed;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveInstalledPlugins(installedPlugins) {
|
||||||
|
return getInstalledPlugins().then(function (currentInstalledPlugins) {
|
||||||
|
var updatedPluginsInstalled = _.uniq(installedPlugins.concat(currentInstalledPlugins));
|
||||||
|
|
||||||
|
return ghostApi.settings.edit("installedPlugins", updatedPluginsInstalled);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
GhostPlugin: GhostPlugin,
|
||||||
|
|
||||||
|
init: function (ghost, pluginsToLoad) {
|
||||||
|
// Grab all installed plugins, install any not already installed that are in pluginsToLoad.
|
||||||
|
return getInstalledPlugins().then(function (installedPlugins) {
|
||||||
|
var loadedPlugins = {},
|
||||||
|
recordLoadedPlugin = function (name, loadedPlugin) {
|
||||||
|
// After loading the plugin, add it to our hash of loaded plugins
|
||||||
|
loadedPlugins[name] = loadedPlugin;
|
||||||
|
|
||||||
|
return when.resolve(loadedPlugin);
|
||||||
|
},
|
||||||
|
loadPromises = _.map(pluginsToLoad, function (plugin) {
|
||||||
|
// If already installed, just activate the plugin
|
||||||
|
if (_.contains(installedPlugins, plugin)) {
|
||||||
|
return loader.activatePluginByName(plugin, ghost).then(function (loadedPlugin) {
|
||||||
|
return recordLoadedPlugin(plugin, loadedPlugin);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Install, then activate the plugin
|
||||||
|
return loader.installPluginByName(plugin, ghost).then(function () {
|
||||||
|
return loader.activatePluginByName(plugin, ghost);
|
||||||
|
}).then(function (loadedPlugin) {
|
||||||
|
return recordLoadedPlugin(plugin, loadedPlugin);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return when.all(loadPromises).then(function () {
|
||||||
|
// Save our installed plugins to settings
|
||||||
|
return saveInstalledPlugins(_.keys(loadedPlugins));
|
||||||
|
}).then(function () {
|
||||||
|
// Return the hash of all loaded plugins
|
||||||
|
return when.resolve(loadedPlugins);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
81
core/server/plugins/loader.js
Normal file
81
core/server/plugins/loader.js
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
|
||||||
|
var path = require("path"),
|
||||||
|
_ = require("underscore"),
|
||||||
|
when = require("when"),
|
||||||
|
ghostInstance,
|
||||||
|
pluginRootDirectory = path.join(process.cwd(), "content/plugins"),
|
||||||
|
loader;
|
||||||
|
|
||||||
|
function getGhostInstance() {
|
||||||
|
if (ghostInstance) {
|
||||||
|
return ghostInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
var Ghost = require("../../ghost");
|
||||||
|
|
||||||
|
ghostInstance = new Ghost();
|
||||||
|
|
||||||
|
return ghostInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPluginByName(name, ghost) {
|
||||||
|
ghost = ghost || getGhostInstance();
|
||||||
|
|
||||||
|
// Grab the plugin class to instantiate
|
||||||
|
var PluginClass = require(loader.getPluginRelativePath(name)),
|
||||||
|
plugin;
|
||||||
|
|
||||||
|
// Check for an actual class, otherwise just use whatever was returned
|
||||||
|
if (_.isFunction(PluginClass)) {
|
||||||
|
plugin = new PluginClass(ghost);
|
||||||
|
} else {
|
||||||
|
plugin = PluginClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
return plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The loader is responsible for loading plugins
|
||||||
|
loader = {
|
||||||
|
// Get a relative path to the given plugins root, defaults
|
||||||
|
// to be relative to __dirname
|
||||||
|
getPluginRelativePath: function (name, relativeTo) {
|
||||||
|
relativeTo = relativeTo || __dirname;
|
||||||
|
|
||||||
|
return path.relative(relativeTo, path.join(pluginRootDirectory, name));
|
||||||
|
},
|
||||||
|
|
||||||
|
// Load a plugin and return the instantiated plugin
|
||||||
|
installPluginByName: function (name, ghost) {
|
||||||
|
var plugin = getPluginByName(name, ghost);
|
||||||
|
|
||||||
|
// Check for an install() method on the plugin.
|
||||||
|
if (!_.isFunction(plugin.install)) {
|
||||||
|
return when.reject(new Error("Error loading plugin named " + name + "; no install() method defined."));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrapping the install() with a when because it's possible
|
||||||
|
// to not return a promise from it.
|
||||||
|
return when(plugin.install(ghost)).then(function () {
|
||||||
|
return when.resolve(plugin);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// Activate a plugin and return it
|
||||||
|
activatePluginByName: function (name, ghost) {
|
||||||
|
var plugin = getPluginByName(name, ghost);
|
||||||
|
|
||||||
|
// Check for an activate() method on the plugin.
|
||||||
|
if (!_.isFunction(plugin.activate)) {
|
||||||
|
return when.reject(new Error("Error loading plugin named " + name + "; no activate() method defined."));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrapping the activate() with a when because it's possible
|
||||||
|
// to not return a promise from it.
|
||||||
|
return when(plugin.activate(ghost)).then(function () {
|
||||||
|
return when.resolve(plugin);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = loader;
|
108
core/test/ghost/plugins_spec.js
Normal file
108
core/test/ghost/plugins_spec.js
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
/*globals describe, beforeEach, it*/
|
||||||
|
var _ = require("underscore"),
|
||||||
|
when = require('when'),
|
||||||
|
should = require('should'),
|
||||||
|
sinon = require('sinon'),
|
||||||
|
errors = require('../../server/errorHandling'),
|
||||||
|
helpers = require('./helpers'),
|
||||||
|
plugins = require('../../server/plugins'),
|
||||||
|
GhostPlugin = plugins.GhostPlugin,
|
||||||
|
loader = require('../../server/plugins/loader')
|
||||||
|
|
||||||
|
describe('Plugins', function () {
|
||||||
|
|
||||||
|
before(function (done) {
|
||||||
|
helpers.resetData().then(function () {
|
||||||
|
done();
|
||||||
|
}, done);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('GhostPlugin Class', function () {
|
||||||
|
|
||||||
|
should.exist(GhostPlugin);
|
||||||
|
|
||||||
|
it('sets app instance', function () {
|
||||||
|
var fakeGhost = {fake: true},
|
||||||
|
plugin = new GhostPlugin(fakeGhost);
|
||||||
|
|
||||||
|
plugin.app.should.equal(fakeGhost);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has default install, uninstall, activate and deactivate methods', function () {
|
||||||
|
var fakeGhost = {fake: true},
|
||||||
|
plugin = new GhostPlugin(fakeGhost);
|
||||||
|
|
||||||
|
_.isFunction(plugin.install).should.equal(true);
|
||||||
|
_.isFunction(plugin.uninstall).should.equal(true);
|
||||||
|
_.isFunction(plugin.activate).should.equal(true);
|
||||||
|
_.isFunction(plugin.deactivate).should.equal(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('loader', function () {
|
||||||
|
|
||||||
|
// TODO: These depend heavily on the FancyFirstChar plugin being present.
|
||||||
|
|
||||||
|
it('can load FancyFirstChar by name and unload', function (done) {
|
||||||
|
var fancyPlugin = require("../../../content/plugins/FancyFirstChar"),
|
||||||
|
fakeGhost = {
|
||||||
|
registerFilter: function () { return; },
|
||||||
|
unregisterFilter: function () { return; }
|
||||||
|
},
|
||||||
|
installMock = sinon.stub(fancyPlugin, "install"),
|
||||||
|
uninstallMock = sinon.stub(fancyPlugin, "uninstall"),
|
||||||
|
registerMock = sinon.stub(fakeGhost, "registerFilter"),
|
||||||
|
unregisterMock = sinon.stub(fakeGhost, "unregisterFilter");
|
||||||
|
|
||||||
|
loader.installPluginByName("FancyFirstChar", fakeGhost).then(function (loadedPlugin) {
|
||||||
|
|
||||||
|
should.exist(loadedPlugin);
|
||||||
|
|
||||||
|
installMock.called.should.equal(true);
|
||||||
|
|
||||||
|
loadedPlugin.activate(fakeGhost);
|
||||||
|
|
||||||
|
// Registers the filter
|
||||||
|
registerMock.called.should.equal(true);
|
||||||
|
|
||||||
|
loadedPlugin.deactivate(fakeGhost);
|
||||||
|
|
||||||
|
// Unregisters the filter
|
||||||
|
unregisterMock.called.should.equal(true);
|
||||||
|
|
||||||
|
loadedPlugin.uninstall(fakeGhost);
|
||||||
|
|
||||||
|
done();
|
||||||
|
}, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("can initialize an array of plugins", function (done) {
|
||||||
|
|
||||||
|
var fakeGhost = {
|
||||||
|
registerFilter: function () { return; },
|
||||||
|
unregisterFilter: function () { return; }
|
||||||
|
},
|
||||||
|
installSpy = sinon.spy(loader, "installPluginByName"),
|
||||||
|
activateSpy = sinon.spy(loader, "activatePluginByName");
|
||||||
|
|
||||||
|
plugins.init(fakeGhost, ["FancyFirstChar"]).then(function (loadedPlugins) {
|
||||||
|
should.exist(loadedPlugins);
|
||||||
|
should.exist(loadedPlugins["FancyFirstChar"]);
|
||||||
|
|
||||||
|
installSpy.called.should.equal(true);
|
||||||
|
activateSpy.called.should.equal(true);
|
||||||
|
|
||||||
|
var api = require("../../server/api");
|
||||||
|
|
||||||
|
return api.settings.read("installedPlugins").then(function (setting) {
|
||||||
|
should.exist(setting);
|
||||||
|
|
||||||
|
setting.value.should.equal('["FancyFirstChar"]')
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
}, done);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
|
@ -25,15 +25,9 @@ describe("Ghost API", function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("uses init() to initialize", function (done) {
|
it("uses init() to initialize", function (done) {
|
||||||
var fakeDataProvider = {
|
var dataProviderInitMock = sinon.stub(ghost.dataProvider, "init", function () {
|
||||||
init: function () {
|
|
||||||
return when.resolve();
|
return when.resolve();
|
||||||
}
|
});
|
||||||
},
|
|
||||||
dataProviderInitSpy = sinon.spy(fakeDataProvider, "init"),
|
|
||||||
oldDataProvider = ghost.dataProvider;
|
|
||||||
|
|
||||||
ghost.dataProvider = fakeDataProvider;
|
|
||||||
|
|
||||||
should.not.exist(ghost.settings());
|
should.not.exist(ghost.settings());
|
||||||
|
|
||||||
|
@ -41,12 +35,12 @@ describe("Ghost API", function () {
|
||||||
|
|
||||||
should.exist(ghost.settings());
|
should.exist(ghost.settings());
|
||||||
|
|
||||||
dataProviderInitSpy.called.should.equal(true);
|
dataProviderInitMock.called.should.equal(true);
|
||||||
|
|
||||||
ghost.dataProvider = oldDataProvider;
|
dataProviderInitMock.restore();
|
||||||
|
|
||||||
done();
|
done();
|
||||||
}).then(null, done);
|
}, done);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue