var fs = require('fs'), path = require('path'), RDB = require('./redis.js'), async = require('async'), winston = require('winston'), eventEmitter = require('events').EventEmitter, plugins = { libraries: {}, loadedHooks: {}, staticDirs: {}, cssFiles: [], // Events readyEvent: new eventEmitter, init: function() { if (this.initialized) return; if (global.env === 'development') winston.info('[plugins] Initializing plugins system'); var _self = this; // Read the list of activated plugins and require their libraries async.waterfall([ function(next) { RDB.smembers('plugins:active', next); }, function(plugins, next) { if (plugins && Array.isArray(plugins) && plugins.length > 0) { async.each(plugins, function(plugin, next) { var pluginPath = path.join(__dirname, '../plugins/', plugin), modulePath = path.join(__dirname, '../node_modules/', plugin); if (fs.existsSync(pluginPath)) _self.loadPlugin(pluginPath, next); else if (fs.existsSync(modulePath)) _self.loadPlugin(modulePath, next); else { if (global.env === 'development') winston.warn('[plugins] Plugin \'' + plugin + '\' not found'); next(); // Ignore this plugin silently } }, next); } else next(); }, function(next) { if (global.env === 'development') winston.info('[plugins] Sorting hooks to fire in priority sequence'); Object.keys(_self.loadedHooks).forEach(function(hook) { var hooks = _self.loadedHooks[hook]; hooks = hooks.sort(function(a, b) { return a[3] - b[3]; }); }); next(); } ], function(err) { if (err) { if (global.env === 'development') winston.info('[plugins] NodeBB encountered a problem while loading plugins', err.message); return; } if (global.env === 'development') winston.info('[plugins] Plugins OK'); _self.initialized = true; _self.readyEvent.emit('ready'); }); }, ready: function(callback) { if (!this.initialized) this.readyEvent.once('ready', callback); else callback(); }, initialized: false, loadPlugin: function(pluginPath, callback) { var _self = this; fs.readFile(path.join(pluginPath, 'plugin.json'), function(err, data) { if (err) return callback(err); var pluginData = JSON.parse(data), libraryPath, staticDir; async.parallel([ function(next) { if (pluginData.library) { libraryPath = path.join(pluginPath, pluginData.library); fs.exists(libraryPath, function(exists) { if (exists) { _self.libraries[pluginData.id] = require(libraryPath); if (pluginData.hooks && Array.isArray(pluginData.hooks) && pluginData.hooks.length > 0) { async.each(pluginData.hooks, function(hook, next) { _self.registerHook(pluginData.id, hook, next); }, next); } } }); } else next(); }, function(next) { // Static Directories for Plugins 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(next) { // CSS Files for plugins if (pluginData.css && pluginData.css instanceof Array) { for(var x=0,numCss=pluginData.css.length;x