diff --git a/app.js b/app.js index 5c7a39c8de..9ddb8dd6e4 100644 --- a/app.js +++ b/app.js @@ -75,7 +75,7 @@ if (fs.existsSync(__dirname + '/config.json') && (!nconf.get('setup') && !nconf. templates = require('./public/src/templates.js'), webserver = require('./src/webserver.js'), websockets = require('./src/websockets.js'), - plugins = require('./src/plugins'), + plugins = require('./src/plugins'), // Don't remove this - plugins initializes itself admin = { 'categories': require('./src/admin/categories.js') }; diff --git a/src/plugins.js b/src/plugins.js index edbef8267a..900292c622 100644 --- a/src/plugins.js +++ b/src/plugins.js @@ -3,9 +3,15 @@ var fs = require('fs'), RDB = require('./redis.js'), async = require('async'), winston = require('winston'), + eventEmitter = require('events').EventEmitter, plugins = { libraries: {}, loadedHooks: {}, + staticDirs: {}, + + // Events + readyEvent: new eventEmitter, + init: function() { if (this.initialized) return; if (global.env === 'development') winston.info('[plugins] Initializing plugins system'); @@ -50,8 +56,13 @@ var fs = require('fs'), } if (global.env === 'development') winston.info('[plugins] Plugins OK'); + + _self.readyEvent.emit('ready'); }); }, + ready: function(callback) { + this.readyEvent.once('ready', callback); + }, initialized: false, loadPlugin: function(pluginPath, callback) { var _self = this; @@ -59,19 +70,48 @@ var fs = require('fs'), fs.readFile(path.join(pluginPath, 'plugin.json'), function(err, data) { if (err) return callback(err); - var pluginData = JSON.parse(data); - _self.libraries[pluginData.id] = require(path.join(pluginPath, pluginData.library)); - if (pluginData.hooks) { - for(var x=0,numHooks=pluginData.hooks.length;x 0) { + async.each(pluginData.hooks, function(hook, next) { + _self.registerHook(pluginData.id, hook, next); + }, next); + } + } + }); + } else next(); + }, + function(next) { + if (pluginData.staticDir) { + staticDir = path.join(pluginPath, pluginData.staticDir); + + fs.exists(staticDir, function(exists) { + if (exists) { + _self.staticDirs[pluginData.id] = staticDir; + next(); + } else next(); + }); + } else next(); + } + ], function(err) { + if (!err) { + if (global.env === 'development') winston.info('[plugins] Loaded plugin: ' + pluginData.id); + callback(); + } else callback(new Error('Could not load plugin system')) + }); }); }, - registerHook: function(id, data) { + registerHook: function(id, data, callback) { /* `data` is an object consisting of (* is required): `data.hook`*, the name of the NodeBB hook @@ -89,6 +129,7 @@ var fs = require('fs'), _self.loadedHooks[data.hook].push([id, data.method, !!data.callbacked, data.priority]); if (global.env === 'development') winston.info('[plugins] Hook registered: ' + data.hook + ' will call ' + id); + callback(); } else return; }, fireHook: function(hook, args, callback) { diff --git a/src/webserver.js b/src/webserver.js index f475a4ca35..b28f71f6ae 100644 --- a/src/webserver.js +++ b/src/webserver.js @@ -21,7 +21,8 @@ var express = require('express'), testBed = require('./routes/testbed.js'), auth = require('./routes/authentication.js'), meta = require('./meta.js'), - feed = require('./feed'); + feed = require('./feed'), + plugins = require('./plugins'); (function(app) { var templates = null; @@ -81,6 +82,29 @@ var express = require('express'), next(); }); + // Static Directories for NodeBB Plugins + app.configure(function() { + var tailMiddlewares = []; + + plugins.ready(function() { + // Remove some middlewares until the router is gone + // This is not recommended behaviour: http://stackoverflow.com/a/13691542/122353 + // Also: https://www.exratione.com/2013/03/nodejs-abusing-express-3-to-enable-late-addition-of-middleware/ + tailMiddlewares.push(app.stack.pop()); + tailMiddlewares.push(app.stack.pop()); + tailMiddlewares.push(app.stack.pop()); + for(d in plugins.staticDirs) { + app.use(nconf.get('relative_path') + '/plugins/' + d, express.static(plugins.staticDirs[d])); + } + + // Push the removed middlewares back onto the application stack + tailMiddlewares.reverse(); + app.stack.push(tailMiddlewares.shift()); + app.stack.push(tailMiddlewares.shift()); + app.stack.push(tailMiddlewares.shift()); + }); + }); + module.exports.init = function() { templates = global.templates; }