mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-10-26 16:46:12 +01:00
feat: #7743 plugins
This commit is contained in:
@@ -213,3 +213,5 @@ events.output = function (numEvents) {
|
|||||||
process.exit(0);
|
process.exit(0);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
require('./promisify')(events);
|
||||||
|
|||||||
@@ -216,7 +216,7 @@ Data.getModules = async function getModules(pluginData) {
|
|||||||
await Promise.all(Object.keys(pluginModules).map(key => processModule(key)));
|
await Promise.all(Object.keys(pluginModules).map(key => processModule(key)));
|
||||||
|
|
||||||
const len = Object.keys(modules).length;
|
const len = Object.keys(modules).length;
|
||||||
winston.info('[plugins] Found ' + len + ' AMD-style module(s) for plugin ' + pluginData.id);
|
winston.verbose('[plugins] Found ' + len + ' AMD-style module(s) for plugin ' + pluginData.id);
|
||||||
return modules;
|
return modules;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -10,30 +10,31 @@ module.exports = function (Plugins) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Plugins.internals = {
|
Plugins.internals = {
|
||||||
_register: function (data, callback) {
|
_register: function (data) {
|
||||||
Plugins.loadedHooks[data.hook] = Plugins.loadedHooks[data.hook] || [];
|
Plugins.loadedHooks[data.hook] = Plugins.loadedHooks[data.hook] || [];
|
||||||
Plugins.loadedHooks[data.hook].push(data);
|
Plugins.loadedHooks[data.hook].push(data);
|
||||||
|
|
||||||
callback();
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const hookTypeToMethod = {
|
||||||
|
filter: fireFilterHook,
|
||||||
|
action: fireActionHook,
|
||||||
|
static: fireStaticHook,
|
||||||
|
response: fireResponseHook,
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
`data` is an object consisting of (* is required):
|
`data` is an object consisting of (* is required):
|
||||||
`data.hook`*, the name of the NodeBB hook
|
`data.hook`*, the name of the NodeBB hook
|
||||||
`data.method`*, the method called in that plugin (can be an array of functions)
|
`data.method`*, the method called in that plugin (can be an array of functions)
|
||||||
`data.priority`, the relative priority of the method when it is eventually called (default: 10)
|
`data.priority`, the relative priority of the method when it is eventually called (default: 10)
|
||||||
*/
|
*/
|
||||||
Plugins.registerHook = function (id, data, callback) {
|
Plugins.registerHook = function (id, data) {
|
||||||
callback = callback || function () {};
|
if (!data.hook || !data.method) {
|
||||||
|
winston.warn('[plugins/' + id + '] registerHook called with invalid data.hook/method', data);
|
||||||
if (!data.hook) {
|
return;
|
||||||
winston.warn('[plugins/' + id + '] registerHook called with invalid data.hook', data);
|
|
||||||
return callback();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var method;
|
|
||||||
|
|
||||||
if (Plugins.deprecatedHooks[data.hook]) {
|
if (Plugins.deprecatedHooks[data.hook]) {
|
||||||
winston.warn('[plugins/' + id + '] Hook `' + data.hook + '` is deprecated, ' +
|
winston.warn('[plugins/' + id + '] Hook `' + data.hook + '` is deprecated, ' +
|
||||||
(Plugins.deprecatedHooks[data.hook] ?
|
(Plugins.deprecatedHooks[data.hook] ?
|
||||||
@@ -42,7 +43,6 @@ module.exports = function (Plugins) {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.hook && data.method) {
|
|
||||||
data.id = id;
|
data.id = id;
|
||||||
if (!data.priority) {
|
if (!data.priority) {
|
||||||
data.priority = 10;
|
data.priority = 10;
|
||||||
@@ -50,12 +50,12 @@ module.exports = function (Plugins) {
|
|||||||
|
|
||||||
if (Array.isArray(data.method) && data.method.every(method => typeof method === 'function' || typeof method === 'string')) {
|
if (Array.isArray(data.method) && data.method.every(method => typeof method === 'function' || typeof method === 'string')) {
|
||||||
// Go go gadget recursion!
|
// Go go gadget recursion!
|
||||||
async.eachSeries(data.method, function (method, next) {
|
data.method.forEach(function (method) {
|
||||||
const singularData = Object.assign({}, data, { method: method });
|
const singularData = Object.assign({}, data, { method: method });
|
||||||
Plugins.registerHook(id, singularData, next);
|
Plugins.registerHook(id, singularData);
|
||||||
}, callback);
|
});
|
||||||
} else if (typeof data.method === 'string' && data.method.length > 0) {
|
} else if (typeof data.method === 'string' && data.method.length > 0) {
|
||||||
method = data.method.split('.').reduce(function (memo, prop) {
|
const method = data.method.split('.').reduce(function (memo, prop) {
|
||||||
if (memo && memo[prop]) {
|
if (memo && memo[prop]) {
|
||||||
return memo[prop];
|
return memo[prop];
|
||||||
}
|
}
|
||||||
@@ -66,13 +66,11 @@ module.exports = function (Plugins) {
|
|||||||
// Write the actual method reference to the hookObj
|
// Write the actual method reference to the hookObj
|
||||||
data.method = method;
|
data.method = method;
|
||||||
|
|
||||||
Plugins.internals._register(data, callback);
|
Plugins.internals._register(data);
|
||||||
} else if (typeof data.method === 'function') {
|
} else if (typeof data.method === 'function') {
|
||||||
Plugins.internals._register(data, callback);
|
Plugins.internals._register(data);
|
||||||
} else {
|
} else {
|
||||||
winston.warn('[plugins/' + id + '] Hook method mismatch: ' + data.hook + ' => ' + data.method);
|
winston.warn('[plugins/' + id + '] Hook method mismatch: ' + data.hook + ' => ' + data.method);
|
||||||
return callback();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -83,52 +81,33 @@ module.exports = function (Plugins) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Plugins.fireHook = function (hook, params, callback) {
|
Plugins.fireHook = async function (hook, params) {
|
||||||
callback = typeof callback === 'function' ? callback : function () {};
|
const hookList = Plugins.loadedHooks[hook];
|
||||||
function done(err, result) {
|
const hookType = hook.split(':')[0];
|
||||||
if (err) {
|
if (hook !== 'action:plugins.firehook') {
|
||||||
return callback(err);
|
winston.verbose('[plugins/fireHook] ' + hook);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!hookTypeToMethod[hookType]) {
|
||||||
|
winston.warn('[plugins] Unknown hookType: ' + hookType + ', hook : ' + hook);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const result = await hookTypeToMethod[hookType](hook, hookList, params);
|
||||||
|
|
||||||
if (hook !== 'action:plugins.firehook') {
|
if (hook !== 'action:plugins.firehook') {
|
||||||
Plugins.fireHook('action:plugins.firehook', { hook: hook, params: params });
|
Plugins.fireHook('action:plugins.firehook', { hook: hook, params: params });
|
||||||
}
|
}
|
||||||
if (result !== undefined) {
|
if (result !== undefined) {
|
||||||
callback(null, result);
|
return result;
|
||||||
} else {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var hookList = Plugins.loadedHooks[hook];
|
|
||||||
var hookType = hook.split(':')[0];
|
|
||||||
if (hook !== 'action:plugins.firehook') {
|
|
||||||
winston.verbose('[plugins/fireHook] ' + hook);
|
|
||||||
}
|
|
||||||
switch (hookType) {
|
|
||||||
case 'filter':
|
|
||||||
fireFilterHook(hook, hookList, params, done);
|
|
||||||
break;
|
|
||||||
case 'action':
|
|
||||||
fireActionHook(hook, hookList, params, done);
|
|
||||||
break;
|
|
||||||
case 'static':
|
|
||||||
fireStaticHook(hook, hookList, params, done);
|
|
||||||
break;
|
|
||||||
case 'response':
|
|
||||||
fireResponseHook(hook, hookList, params, done);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
winston.warn('[plugins] Unknown hookType: ' + hookType + ', hook : ' + hook);
|
|
||||||
callback();
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function fireFilterHook(hook, hookList, params, callback) {
|
async function fireFilterHook(hook, hookList, params) {
|
||||||
if (!Array.isArray(hookList) || !hookList.length) {
|
if (!Array.isArray(hookList) || !hookList.length) {
|
||||||
return callback(null, params);
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
async.reduce(hookList, params, function (params, hookObj, next) {
|
return await async.reduce(hookList, params, function (params, hookObj, next) {
|
||||||
if (typeof hookObj.method !== 'function') {
|
if (typeof hookObj.method !== 'function') {
|
||||||
if (global.env === 'development') {
|
if (global.env === 'development') {
|
||||||
winston.warn('[plugins] Expected method for hook \'' + hook + '\' in plugin \'' + hookObj.id + '\' not found, skipping.');
|
winston.warn('[plugins] Expected method for hook \'' + hook + '\' in plugin \'' + hookObj.id + '\' not found, skipping.');
|
||||||
@@ -142,14 +121,14 @@ module.exports = function (Plugins) {
|
|||||||
err => setImmediate(next, err)
|
err => setImmediate(next, err)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}, callback);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function fireActionHook(hook, hookList, params, callback) {
|
async function fireActionHook(hook, hookList, params) {
|
||||||
if (!Array.isArray(hookList) || !hookList.length) {
|
if (!Array.isArray(hookList) || !hookList.length) {
|
||||||
return callback();
|
return;
|
||||||
}
|
}
|
||||||
async.each(hookList, function (hookObj, next) {
|
await async.each(hookList, function (hookObj, next) {
|
||||||
if (typeof hookObj.method !== 'function') {
|
if (typeof hookObj.method !== 'function') {
|
||||||
if (global.env === 'development') {
|
if (global.env === 'development') {
|
||||||
winston.warn('[plugins] Expected method for hook \'' + hook + '\' in plugin \'' + hookObj.id + '\' not found, skipping.');
|
winston.warn('[plugins] Expected method for hook \'' + hook + '\' in plugin \'' + hookObj.id + '\' not found, skipping.');
|
||||||
@@ -159,14 +138,14 @@ module.exports = function (Plugins) {
|
|||||||
|
|
||||||
hookObj.method(params);
|
hookObj.method(params);
|
||||||
next();
|
next();
|
||||||
}, callback);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function fireStaticHook(hook, hookList, params, callback) {
|
async function fireStaticHook(hook, hookList, params) {
|
||||||
if (!Array.isArray(hookList) || !hookList.length) {
|
if (!Array.isArray(hookList) || !hookList.length) {
|
||||||
return callback();
|
return;
|
||||||
}
|
}
|
||||||
async.each(hookList, function (hookObj, next) {
|
await async.each(hookList, function (hookObj, next) {
|
||||||
if (typeof hookObj.method === 'function') {
|
if (typeof hookObj.method === 'function') {
|
||||||
let timedOut = false;
|
let timedOut = false;
|
||||||
const timeoutId = setTimeout(function () {
|
const timeoutId = setTimeout(function () {
|
||||||
@@ -201,14 +180,14 @@ module.exports = function (Plugins) {
|
|||||||
} else {
|
} else {
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
}, callback);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function fireResponseHook(hook, hookList, params, callback) {
|
async function fireResponseHook(hook, hookList, params) {
|
||||||
if (!Array.isArray(hookList) || !hookList.length) {
|
if (!Array.isArray(hookList) || !hookList.length) {
|
||||||
return callback();
|
return;
|
||||||
}
|
}
|
||||||
async.eachSeries(hookList, function (hookObj, next) {
|
await async.eachSeries(hookList, function (hookObj, next) {
|
||||||
if (typeof hookObj.method !== 'function') {
|
if (typeof hookObj.method !== 'function') {
|
||||||
if (global.env === 'development') {
|
if (global.env === 'development') {
|
||||||
winston.warn('[plugins] Expected method for hook \'' + hook + '\' in plugin \'' + hookObj.id + '\' not found, skipping.');
|
winston.warn('[plugins] Expected method for hook \'' + hook + '\' in plugin \'' + hookObj.id + '\' not found, skipping.');
|
||||||
@@ -223,7 +202,7 @@ module.exports = function (Plugins) {
|
|||||||
|
|
||||||
hookObj.method(params);
|
hookObj.method(params);
|
||||||
next();
|
next();
|
||||||
}, callback);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Plugins.hasListeners = function (hook) {
|
Plugins.hasListeners = function (hook) {
|
||||||
|
|||||||
@@ -1,16 +1,19 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var fs = require('fs');
|
const fs = require('fs');
|
||||||
var path = require('path');
|
const path = require('path');
|
||||||
var async = require('async');
|
const async = require('async');
|
||||||
var winston = require('winston');
|
const winston = require('winston');
|
||||||
var semver = require('semver');
|
const semver = require('semver');
|
||||||
var nconf = require('nconf');
|
const nconf = require('nconf');
|
||||||
|
const util = require('util');
|
||||||
|
|
||||||
|
const readdirAsync = util.promisify(fs.readdir);
|
||||||
|
|
||||||
var app;
|
var app;
|
||||||
var middleware;
|
var middleware;
|
||||||
|
|
||||||
var Plugins = module.exports;
|
const Plugins = module.exports;
|
||||||
|
|
||||||
require('./install')(Plugins);
|
require('./install')(Plugins);
|
||||||
require('./load')(Plugins);
|
require('./load')(Plugins);
|
||||||
@@ -65,10 +68,9 @@ Plugins.requireLibrary = function (pluginID, libraryPath) {
|
|||||||
Plugins.libraryPaths.push(libraryPath);
|
Plugins.libraryPaths.push(libraryPath);
|
||||||
};
|
};
|
||||||
|
|
||||||
Plugins.init = function (nbbApp, nbbMiddleware, callback) {
|
Plugins.init = async function (nbbApp, nbbMiddleware) {
|
||||||
callback = callback || function () {};
|
|
||||||
if (Plugins.initialized) {
|
if (Plugins.initialized) {
|
||||||
return callback();
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nbbApp) {
|
if (nbbApp) {
|
||||||
@@ -80,22 +82,15 @@ Plugins.init = function (nbbApp, nbbMiddleware, callback) {
|
|||||||
winston.verbose('[plugins] Initializing plugins system');
|
winston.verbose('[plugins] Initializing plugins system');
|
||||||
}
|
}
|
||||||
|
|
||||||
Plugins.reload(function (err) {
|
await Plugins.reload();
|
||||||
if (err) {
|
|
||||||
winston.error('[plugins] NodeBB encountered a problem while loading plugins', err);
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (global.env === 'development') {
|
if (global.env === 'development') {
|
||||||
winston.info('[plugins] Plugins OK');
|
winston.info('[plugins] Plugins OK');
|
||||||
}
|
}
|
||||||
|
|
||||||
Plugins.initialized = true;
|
Plugins.initialized = true;
|
||||||
callback();
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Plugins.reload = function (callback) {
|
Plugins.reload = async function () {
|
||||||
// Resetting all local plugin data
|
// Resetting all local plugin data
|
||||||
Plugins.libraries = {};
|
Plugins.libraries = {};
|
||||||
Plugins.loadedHooks = {};
|
Plugins.loadedHooks = {};
|
||||||
@@ -109,12 +104,12 @@ Plugins.reload = function (callback) {
|
|||||||
Plugins.libraryPaths.length = 0;
|
Plugins.libraryPaths.length = 0;
|
||||||
Plugins.loadedPlugins.length = 0;
|
Plugins.loadedPlugins.length = 0;
|
||||||
|
|
||||||
async.waterfall([
|
const paths = await Plugins.getPluginPaths();
|
||||||
Plugins.getPluginPaths,
|
for (const path of paths) {
|
||||||
function (paths, next) {
|
/* eslint-disable no-await-in-loop */
|
||||||
async.eachSeries(paths, Plugins.loadPlugin, next);
|
await Plugins.loadPlugin(path);
|
||||||
},
|
}
|
||||||
function (next) {
|
|
||||||
// If some plugins are incompatible, throw the warning here
|
// If some plugins are incompatible, throw the warning here
|
||||||
if (Plugins.versionWarning.length && nconf.get('isPrimary') === 'true') {
|
if (Plugins.versionWarning.length && nconf.get('isPrimary') === 'true') {
|
||||||
console.log('');
|
console.log('');
|
||||||
@@ -126,98 +121,75 @@ Plugins.reload = function (callback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Object.keys(Plugins.loadedHooks).forEach(function (hook) {
|
Object.keys(Plugins.loadedHooks).forEach(function (hook) {
|
||||||
var hooks = Plugins.loadedHooks[hook];
|
Plugins.loadedHooks[hook].sort((a, b) => a.priority - b.priority);
|
||||||
hooks.sort(function (a, b) {
|
|
||||||
return a.priority - b.priority;
|
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
next();
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Plugins.reloadRoutes = function (router, callback) {
|
Plugins.reloadRoutes = async function (router) {
|
||||||
var controllers = require('../controllers');
|
var controllers = require('../controllers');
|
||||||
Plugins.fireHook('static:app.load', { app: app, router: router, middleware: middleware, controllers: controllers }, function (err) {
|
await Plugins.fireHook('static:app.load', { app: app, router: router, middleware: middleware, controllers: controllers });
|
||||||
if (err) {
|
|
||||||
winston.error('[plugins] Encountered error while executing post-router plugins hooks', err);
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
winston.verbose('[plugins] All plugins reloaded and rerouted');
|
winston.verbose('[plugins] All plugins reloaded and rerouted');
|
||||||
callback();
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Plugins.get = function (id, callback) {
|
function request(url, callback) {
|
||||||
var url = (nconf.get('registry') || 'https://packages.nodebb.org') + '/api/v1/plugins/' + id;
|
|
||||||
|
|
||||||
require('request')(url, {
|
require('request')(url, {
|
||||||
json: true,
|
json: true,
|
||||||
}, function (err, res, body) {
|
}, function (err, res, body) {
|
||||||
if (res.statusCode === 404 || !body.payload) {
|
if (res.statusCode === 404 || !body) {
|
||||||
return callback(err, {});
|
return callback(err, {});
|
||||||
}
|
}
|
||||||
|
callback(err, body);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const requestAsync = util.promisify(request);
|
||||||
|
|
||||||
Plugins.normalise([body.payload], function (err, normalised) {
|
Plugins.get = async function (id) {
|
||||||
normalised = normalised.filter(function (plugin) {
|
const url = (nconf.get('registry') || 'https://packages.nodebb.org') + '/api/v1/plugins/' + id;
|
||||||
return plugin.id === id;
|
const body = await requestAsync(url);
|
||||||
});
|
|
||||||
return callback(err, !err ? normalised[0] : undefined);
|
let normalised = await Plugins.normalise([body ? body.payload : {}]);
|
||||||
});
|
normalised = normalised.filter(plugin => plugin.id === id);
|
||||||
});
|
return normalised.length ? normalised[0] : undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
Plugins.list = function (matching, callback) {
|
Plugins.list = async function (matching) {
|
||||||
if (arguments.length === 1 && typeof matching === 'function') {
|
if (matching === undefined) {
|
||||||
callback = matching;
|
|
||||||
matching = true;
|
matching = true;
|
||||||
}
|
}
|
||||||
var version = require(path.join(nconf.get('base_dir'), 'package.json')).version;
|
const version = require(path.join(nconf.get('base_dir'), 'package.json')).version;
|
||||||
var url = (nconf.get('registry') || 'https://packages.nodebb.org') + '/api/v1/plugins' + (matching !== false ? '?version=' + version : '');
|
const url = (nconf.get('registry') || 'https://packages.nodebb.org') + '/api/v1/plugins' + (matching !== false ? '?version=' + version : '');
|
||||||
|
try {
|
||||||
require('request')(url, {
|
const body = await requestAsync(url);
|
||||||
json: true,
|
return await Plugins.normalise(body);
|
||||||
}, function (err, res, body) {
|
} catch (err) {
|
||||||
if (err || (res && res.statusCode !== 200)) {
|
winston.error('Error loading ' + url, err);
|
||||||
winston.error('Error loading ' + url, err || body);
|
return await Plugins.normalise([]);
|
||||||
return Plugins.normalise([], callback);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Plugins.normalise(body, callback);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Plugins.normalise = function (apiReturn, callback) {
|
Plugins.normalise = async function (apiReturn) {
|
||||||
var themeNamePattern = /^(@.*?\/)?nodebb-theme-.*$/;
|
const themeNamePattern = /^(@.*?\/)?nodebb-theme-.*$/;
|
||||||
var pluginMap = {};
|
const pluginMap = {};
|
||||||
var dependencies = require(path.join(nconf.get('base_dir'), 'package.json')).dependencies;
|
const dependencies = require(path.join(nconf.get('base_dir'), 'package.json')).dependencies;
|
||||||
apiReturn = Array.isArray(apiReturn) ? apiReturn : [];
|
apiReturn = Array.isArray(apiReturn) ? apiReturn : [];
|
||||||
for (var i = 0; i < apiReturn.length; i += 1) {
|
apiReturn.forEach(function (packageData) {
|
||||||
apiReturn[i].id = apiReturn[i].name;
|
packageData.id = packageData.name;
|
||||||
apiReturn[i].installed = false;
|
packageData.installed = false;
|
||||||
apiReturn[i].active = false;
|
packageData.active = false;
|
||||||
apiReturn[i].url = apiReturn[i].url || (apiReturn[i].repository ? apiReturn[i].repository.url : '');
|
packageData.url = packageData.url || (packageData.repository ? packageData.repository.url : '');
|
||||||
pluginMap[apiReturn[i].name] = apiReturn[i];
|
pluginMap[packageData.name] = packageData;
|
||||||
}
|
|
||||||
|
|
||||||
Plugins.showInstalled(function (err, installedPlugins) {
|
|
||||||
if (err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
installedPlugins = installedPlugins.filter(function (plugin) {
|
|
||||||
return plugin && !plugin.system;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
async.each(installedPlugins, function (plugin, next) {
|
let installedPlugins = await Plugins.showInstalled();
|
||||||
|
installedPlugins = installedPlugins.filter(plugin => plugin && !plugin.system);
|
||||||
|
|
||||||
|
installedPlugins.forEach(function (plugin) {
|
||||||
// If it errored out because a package.json or plugin.json couldn't be read, no need to do this stuff
|
// If it errored out because a package.json or plugin.json couldn't be read, no need to do this stuff
|
||||||
if (plugin.error) {
|
if (plugin.error) {
|
||||||
pluginMap[plugin.id] = pluginMap[plugin.id] || {};
|
pluginMap[plugin.id] = pluginMap[plugin.id] || {};
|
||||||
pluginMap[plugin.id].installed = true;
|
pluginMap[plugin.id].installed = true;
|
||||||
pluginMap[plugin.id].error = true;
|
pluginMap[plugin.id].error = true;
|
||||||
return next();
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
pluginMap[plugin.id] = pluginMap[plugin.id] || {};
|
pluginMap[plugin.id] = pluginMap[plugin.id] || {};
|
||||||
@@ -240,13 +212,9 @@ Plugins.normalise = function (apiReturn, callback) {
|
|||||||
pluginMap[plugin.id].latest = pluginMap[plugin.id].latest || plugin.version;
|
pluginMap[plugin.id].latest = pluginMap[plugin.id].latest || plugin.version;
|
||||||
}
|
}
|
||||||
pluginMap[plugin.id].outdated = semver.gt(pluginMap[plugin.id].latest, pluginMap[plugin.id].version);
|
pluginMap[plugin.id].outdated = semver.gt(pluginMap[plugin.id].latest, pluginMap[plugin.id].version);
|
||||||
next();
|
});
|
||||||
}, function (err) {
|
|
||||||
if (err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
var pluginArray = [];
|
const pluginArray = [];
|
||||||
|
|
||||||
for (var key in pluginMap) {
|
for (var key in pluginMap) {
|
||||||
if (pluginMap.hasOwnProperty(key)) {
|
if (pluginMap.hasOwnProperty(key)) {
|
||||||
@@ -263,24 +231,39 @@ Plugins.normalise = function (apiReturn, callback) {
|
|||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
callback(null, pluginArray);
|
return pluginArray;
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Plugins.nodeModulesPath = path.join(__dirname, '../../node_modules');
|
Plugins.nodeModulesPath = path.join(__dirname, '../../node_modules');
|
||||||
|
|
||||||
Plugins.showInstalled = function (callback) {
|
Plugins.showInstalled = async function () {
|
||||||
var pluginNamePattern = /^(@.*?\/)?nodebb-(theme|plugin|widget|rewards)-.*$/;
|
const dirs = await readdirAsync(Plugins.nodeModulesPath);
|
||||||
|
|
||||||
async.waterfall([
|
let pluginPaths = await findNodeBBModules(dirs);
|
||||||
function (next) {
|
pluginPaths = pluginPaths.map(dir => path.join(Plugins.nodeModulesPath, dir));
|
||||||
fs.readdir(Plugins.nodeModulesPath, next);
|
|
||||||
},
|
|
||||||
function (dirs, next) {
|
|
||||||
var pluginPaths = [];
|
|
||||||
|
|
||||||
async.each(dirs, function (dirname, next) {
|
async function load(file) {
|
||||||
|
try {
|
||||||
|
const pluginData = await Plugins.loadPluginInfo(file);
|
||||||
|
const isActive = await Plugins.isActive(pluginData.name);
|
||||||
|
delete pluginData.hooks;
|
||||||
|
delete pluginData.library;
|
||||||
|
pluginData.active = isActive;
|
||||||
|
pluginData.installed = true;
|
||||||
|
pluginData.error = false;
|
||||||
|
return pluginData;
|
||||||
|
} catch (err) {
|
||||||
|
winston.error(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const plugins = await Promise.all(pluginPaths.map(file => load(file)));
|
||||||
|
return plugins.filter(Boolean);
|
||||||
|
};
|
||||||
|
|
||||||
|
async function findNodeBBModules(dirs) {
|
||||||
|
const pluginNamePattern = /^(@.*?\/)?nodebb-(theme|plugin|widget|rewards)-.*$/;
|
||||||
|
const pluginPaths = [];
|
||||||
|
await async.each(dirs, function (dirname, next) {
|
||||||
var dirPath = path.join(Plugins.nodeModulesPath, dirname);
|
var dirPath = path.join(Plugins.nodeModulesPath, dirname);
|
||||||
|
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
@@ -326,49 +309,8 @@ Plugins.showInstalled = function (callback) {
|
|||||||
}, cb);
|
}, cb);
|
||||||
},
|
},
|
||||||
], next);
|
], next);
|
||||||
}, function (err) {
|
|
||||||
next(err, pluginPaths);
|
|
||||||
});
|
});
|
||||||
},
|
return pluginPaths;
|
||||||
|
|
||||||
function (dirs, next) {
|
|
||||||
dirs = dirs.map(function (dir) {
|
|
||||||
return path.join(Plugins.nodeModulesPath, dir);
|
|
||||||
});
|
|
||||||
var plugins = [];
|
|
||||||
|
|
||||||
async.each(dirs, function (file, next) {
|
|
||||||
async.waterfall([
|
|
||||||
function (next) {
|
|
||||||
Plugins.loadPluginInfo(file, next);
|
|
||||||
},
|
|
||||||
function (pluginData, next) {
|
|
||||||
Plugins.isActive(pluginData.name, function (err, active) {
|
|
||||||
if (err) {
|
|
||||||
return next(new Error('no-active-state'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
delete pluginData.hooks;
|
|
||||||
delete pluginData.library;
|
|
||||||
pluginData.active = active;
|
|
||||||
pluginData.installed = true;
|
|
||||||
pluginData.error = false;
|
|
||||||
next(null, pluginData);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
], function (err, pluginData) {
|
|
||||||
if (err) {
|
|
||||||
return next(); // Silently fail
|
|
||||||
}
|
|
||||||
|
|
||||||
plugins.push(pluginData);
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
}, function (err) {
|
|
||||||
next(err, plugins);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
Plugins.async = require('../promisify')(Plugins);
|
Plugins.async = require('../promisify')(Plugins);
|
||||||
|
|||||||
@@ -1,21 +1,23 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var winston = require('winston');
|
const winston = require('winston');
|
||||||
var async = require('async');
|
const path = require('path');
|
||||||
var path = require('path');
|
const fs = require('fs');
|
||||||
var fs = require('fs');
|
const nconf = require('nconf');
|
||||||
var nconf = require('nconf');
|
const os = require('os');
|
||||||
var os = require('os');
|
const cproc = require('child_process');
|
||||||
var cproc = require('child_process');
|
const util = require('util');
|
||||||
|
|
||||||
var db = require('../database');
|
const db = require('../database');
|
||||||
var meta = require('../meta');
|
const meta = require('../meta');
|
||||||
var pubsub = require('../pubsub');
|
const pubsub = require('../pubsub');
|
||||||
var events = require('../events');
|
const events = require('../events');
|
||||||
|
|
||||||
var packageManager = nconf.get('package_manager') === 'yarn' ? 'yarn' : 'npm';
|
const statAsync = util.promisify(fs.stat);
|
||||||
var packageManagerExecutable = packageManager;
|
|
||||||
var packageManagerCommands = {
|
const packageManager = nconf.get('package_manager') === 'yarn' ? 'yarn' : 'npm';
|
||||||
|
let packageManagerExecutable = packageManager;
|
||||||
|
const packageManagerCommands = {
|
||||||
yarn: {
|
yarn: {
|
||||||
install: 'add',
|
install: 'add',
|
||||||
uninstall: 'remove',
|
uninstall: 'remove',
|
||||||
@@ -45,83 +47,43 @@ module.exports = function (Plugins) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Plugins.toggleActive = function (id, callback) {
|
Plugins.toggleActive = async function (id) {
|
||||||
callback = callback || function () {};
|
const isActive = await Plugins.isActive(id);
|
||||||
var isActive;
|
|
||||||
async.waterfall([
|
|
||||||
function (next) {
|
|
||||||
Plugins.isActive(id, next);
|
|
||||||
},
|
|
||||||
function (_isActive, next) {
|
|
||||||
isActive = _isActive;
|
|
||||||
if (isActive) {
|
if (isActive) {
|
||||||
db.sortedSetRemove('plugins:active', id, next);
|
await db.sortedSetRemove('plugins:active', id);
|
||||||
} else {
|
} else {
|
||||||
db.sortedSetCard('plugins:active', function (err, count) {
|
const count = await db.sortedSetCard('plugins:active');
|
||||||
if (err) {
|
await db.sortedSetAdd('plugins:active', count, id);
|
||||||
return next(err);
|
|
||||||
}
|
}
|
||||||
db.sortedSetAdd('plugins:active', count, id, next);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
function (next) {
|
|
||||||
meta.reloadRequired = true;
|
meta.reloadRequired = true;
|
||||||
Plugins.fireHook(isActive ? 'action:plugin.deactivate' : 'action:plugin.activate', { id: id });
|
Plugins.fireHook(isActive ? 'action:plugin.deactivate' : 'action:plugin.activate', { id: id });
|
||||||
setImmediate(next);
|
await events.log({
|
||||||
},
|
|
||||||
function (next) {
|
|
||||||
events.log({
|
|
||||||
type: 'plugin-' + (isActive ? 'deactivate' : 'activate'),
|
type: 'plugin-' + (isActive ? 'deactivate' : 'activate'),
|
||||||
text: id,
|
text: id,
|
||||||
}, next);
|
|
||||||
},
|
|
||||||
], function (err) {
|
|
||||||
if (err) {
|
|
||||||
winston.warn('[plugins] Could not toggle active state on plugin \'' + id + '\'');
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
callback(null, { id: id, active: !isActive });
|
|
||||||
});
|
});
|
||||||
|
return { id: id, active: !isActive };
|
||||||
};
|
};
|
||||||
|
|
||||||
Plugins.toggleInstall = function (id, version, callback) {
|
Plugins.toggleInstall = async function (id, version) {
|
||||||
pubsub.publish('plugins:toggleInstall', { hostname: os.hostname(), id: id, version: version });
|
pubsub.publish('plugins:toggleInstall', { hostname: os.hostname(), id: id, version: version });
|
||||||
toggleInstall(id, version, callback);
|
return await toggleInstall(id, version);
|
||||||
};
|
};
|
||||||
|
|
||||||
function toggleInstall(id, version, callback) {
|
const runPackageManagerCommandAsync = util.promisify(runPackageManagerCommand);
|
||||||
var installed;
|
|
||||||
var type;
|
async function toggleInstall(id, version) {
|
||||||
async.waterfall([
|
const [installed, active] = await Promise.all([
|
||||||
function (next) {
|
Plugins.isInstalled(id),
|
||||||
Plugins.isInstalled(id, next);
|
Plugins.isActive(id),
|
||||||
},
|
]);
|
||||||
function (_installed, next) {
|
const type = installed ? 'uninstall' : 'install';
|
||||||
installed = _installed;
|
|
||||||
type = installed ? 'uninstall' : 'install';
|
|
||||||
Plugins.isActive(id, next);
|
|
||||||
},
|
|
||||||
function (active, next) {
|
|
||||||
if (active) {
|
if (active) {
|
||||||
Plugins.toggleActive(id, function (err) {
|
await Plugins.toggleActive(id);
|
||||||
next(err);
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
setImmediate(next);
|
await runPackageManagerCommandAsync(type, id, version || 'latest');
|
||||||
},
|
const pluginData = await Plugins.get(id);
|
||||||
function (next) {
|
|
||||||
runPackageManagerCommand(type, id, version || 'latest', next);
|
|
||||||
},
|
|
||||||
function (next) {
|
|
||||||
Plugins.get(id, next);
|
|
||||||
},
|
|
||||||
function (pluginData, next) {
|
|
||||||
Plugins.fireHook('action:plugin.' + type, { id: id, version: version });
|
Plugins.fireHook('action:plugin.' + type, { id: id, version: version });
|
||||||
setImmediate(next, null, pluginData);
|
return pluginData;
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function runPackageManagerCommand(command, pkgName, version, callback) {
|
function runPackageManagerCommand(command, pkgName, version, callback) {
|
||||||
@@ -139,37 +101,34 @@ module.exports = function (Plugins) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Plugins.upgrade = function (id, version, callback) {
|
|
||||||
|
Plugins.upgrade = async function (id, version) {
|
||||||
pubsub.publish('plugins:upgrade', { hostname: os.hostname(), id: id, version: version });
|
pubsub.publish('plugins:upgrade', { hostname: os.hostname(), id: id, version: version });
|
||||||
upgrade(id, version, callback);
|
return await upgrade(id, version);
|
||||||
};
|
};
|
||||||
|
|
||||||
function upgrade(id, version, callback) {
|
async function upgrade(id, version) {
|
||||||
async.waterfall([
|
await runPackageManagerCommandAsync('install', id, version || 'latest');
|
||||||
async.apply(runPackageManagerCommand, 'install', id, version || 'latest'),
|
const isActive = await Plugins.isActive(id);
|
||||||
function (next) {
|
|
||||||
Plugins.isActive(id, next);
|
|
||||||
},
|
|
||||||
function (isActive, next) {
|
|
||||||
meta.reloadRequired = isActive;
|
meta.reloadRequired = isActive;
|
||||||
next(null, isActive);
|
return isActive;
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Plugins.isInstalled = function (id, callback) {
|
Plugins.isInstalled = async function (id) {
|
||||||
var pluginDir = path.join(__dirname, '../../node_modules', id);
|
const pluginDir = path.join(__dirname, '../../node_modules', id);
|
||||||
|
try {
|
||||||
fs.stat(pluginDir, function (err, stats) {
|
const stats = await statAsync(pluginDir);
|
||||||
callback(null, err ? false : stats.isDirectory());
|
return stats.isDirectory();
|
||||||
});
|
} catch (err) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Plugins.isActive = function (id, callback) {
|
Plugins.isActive = async function (id) {
|
||||||
db.isSortedSetMember('plugins:active', id, callback);
|
return await db.isSortedSetMember('plugins:active', id);
|
||||||
};
|
};
|
||||||
|
|
||||||
Plugins.getActive = function (callback) {
|
Plugins.getActive = async function () {
|
||||||
db.getSortedSetRange('plugins:active', 0, -1, callback);
|
return await db.getSortedSetRange('plugins:active', 0, -1);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var path = require('path');
|
const path = require('path');
|
||||||
var semver = require('semver');
|
const semver = require('semver');
|
||||||
var async = require('async');
|
const async = require('async');
|
||||||
var winston = require('winston');
|
const winston = require('winston');
|
||||||
var nconf = require('nconf');
|
const nconf = require('nconf');
|
||||||
var _ = require('lodash');
|
const _ = require('lodash');
|
||||||
|
|
||||||
var meta = require('../meta');
|
const meta = require('../meta');
|
||||||
|
|
||||||
module.exports = function (Plugins) {
|
module.exports = function (Plugins) {
|
||||||
function registerPluginAssets(pluginData, fields, callback) {
|
async function registerPluginAssets(pluginData, fields) {
|
||||||
function add(dest, arr) {
|
function add(dest, arr) {
|
||||||
dest.push.apply(dest, arr || []);
|
dest.push.apply(dest, arr || []);
|
||||||
}
|
}
|
||||||
|
|
||||||
var handlers = {
|
const handlers = {
|
||||||
staticDirs: function (next) {
|
staticDirs: function (next) {
|
||||||
Plugins.data.getStaticDirectories(pluginData, next);
|
Plugins.data.getStaticDirectories(pluginData, next);
|
||||||
},
|
},
|
||||||
@@ -45,20 +45,16 @@ module.exports = function (Plugins) {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
var methods;
|
var methods = {};
|
||||||
if (Array.isArray(fields)) {
|
if (Array.isArray(fields)) {
|
||||||
methods = fields.reduce(function (prev, field) {
|
fields.forEach(function (field) {
|
||||||
prev[field] = handlers[field];
|
methods[field] = handlers[field];
|
||||||
return prev;
|
});
|
||||||
}, {});
|
|
||||||
} else {
|
} else {
|
||||||
methods = handlers;
|
methods = handlers;
|
||||||
}
|
}
|
||||||
|
|
||||||
async.parallel(methods, function (err, results) {
|
const results = await async.parallel(methods);
|
||||||
if (err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.assign(Plugins.staticDirs, results.staticDirs || {});
|
Object.assign(Plugins.staticDirs, results.staticDirs || {});
|
||||||
add(Plugins.cssFiles, results.cssFiles);
|
add(Plugins.cssFiles, results.cssFiles);
|
||||||
@@ -75,13 +71,10 @@ module.exports = function (Plugins) {
|
|||||||
Plugins.languageData.namespaces = _.union(Plugins.languageData.namespaces, results.languageData.namespaces);
|
Plugins.languageData.namespaces = _.union(Plugins.languageData.namespaces, results.languageData.namespaces);
|
||||||
}
|
}
|
||||||
Plugins.pluginsData[pluginData.id] = pluginData;
|
Plugins.pluginsData[pluginData.id] = pluginData;
|
||||||
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Plugins.prepareForBuild = function (targets, callback) {
|
Plugins.prepareForBuild = async function (targets) {
|
||||||
var map = {
|
const map = {
|
||||||
'plugin static dirs': ['staticDirs'],
|
'plugin static dirs': ['staticDirs'],
|
||||||
'requirejs modules': ['modules'],
|
'requirejs modules': ['modules'],
|
||||||
'client js bundle': ['clientScripts'],
|
'client js bundle': ['clientScripts'],
|
||||||
@@ -92,7 +85,7 @@ module.exports = function (Plugins) {
|
|||||||
languages: ['languageData'],
|
languages: ['languageData'],
|
||||||
};
|
};
|
||||||
|
|
||||||
var fields = _.uniq(_.flatMap(targets, target => map[target] || []));
|
const fields = _.uniq(_.flatMap(targets, target => map[target] || []));
|
||||||
|
|
||||||
// clear old data before build
|
// clear old data before build
|
||||||
fields.forEach((field) => {
|
fields.forEach((field) => {
|
||||||
@@ -116,43 +109,34 @@ module.exports = function (Plugins) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
winston.verbose('[plugins] loading the following fields from plugin data: ' + fields.join(', '));
|
winston.verbose('[plugins] loading the following fields from plugin data: ' + fields.join(', '));
|
||||||
|
const plugins = await Plugins.data.getActive();
|
||||||
async.waterfall([
|
await Promise.all(plugins.map(p => registerPluginAssets(p, fields)));
|
||||||
Plugins.data.getActive,
|
|
||||||
function (plugins, next) {
|
|
||||||
async.each(plugins, function (pluginData, next) {
|
|
||||||
registerPluginAssets(pluginData, fields, next);
|
|
||||||
}, next);
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var themeNamePattern = /(@.*?\/)?nodebb-theme-.*$/;
|
const themeNamePattern = /(@.*?\/)?nodebb-theme-.*$/;
|
||||||
|
|
||||||
Plugins.loadPlugin = function (pluginPath, callback) {
|
Plugins.loadPlugin = async function (pluginPath) {
|
||||||
Plugins.data.loadPluginInfo(pluginPath, function (err, pluginData) {
|
let pluginData;
|
||||||
if (err) {
|
try {
|
||||||
|
pluginData = await Plugins.data.loadPluginInfo(pluginPath);
|
||||||
|
} catch (err) {
|
||||||
if (err.message === '[[error:parse-error]]') {
|
if (err.message === '[[error:parse-error]]') {
|
||||||
return callback();
|
return;
|
||||||
}
|
}
|
||||||
|
if (!themeNamePattern.test(pluginPath)) {
|
||||||
return callback(themeNamePattern.test(pluginPath) ? null : err);
|
throw err;
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
checkVersion(pluginData);
|
checkVersion(pluginData);
|
||||||
|
|
||||||
async.parallel([
|
try {
|
||||||
function (next) {
|
registerHooks(pluginData);
|
||||||
registerHooks(pluginData, next);
|
await registerPluginAssets(pluginData, ['soundpack']);
|
||||||
},
|
} catch (err) {
|
||||||
function (next) {
|
|
||||||
registerPluginAssets(pluginData, ['soundpack'], next);
|
|
||||||
},
|
|
||||||
], function (err) {
|
|
||||||
if (err) {
|
|
||||||
winston.error(err.stack);
|
winston.error(err.stack);
|
||||||
winston.verbose('[plugins] Could not load plugin : ' + pluginData.id);
|
winston.verbose('[plugins] Could not load plugin : ' + pluginData.id);
|
||||||
return callback();
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pluginData.private) {
|
if (!pluginData.private) {
|
||||||
@@ -163,9 +147,6 @@ module.exports = function (Plugins) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
winston.verbose('[plugins] Loaded plugin: ' + pluginData.id);
|
winston.verbose('[plugins] Loaded plugin: ' + pluginData.id);
|
||||||
callback();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function checkVersion(pluginData) {
|
function checkVersion(pluginData) {
|
||||||
@@ -184,28 +165,24 @@ module.exports = function (Plugins) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function registerHooks(pluginData, callback) {
|
function registerHooks(pluginData) {
|
||||||
if (!pluginData.library) {
|
if (!pluginData.library) {
|
||||||
return callback();
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var libraryPath = path.join(pluginData.path, pluginData.library);
|
const libraryPath = path.join(pluginData.path, pluginData.library);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!Plugins.libraries[pluginData.id]) {
|
if (!Plugins.libraries[pluginData.id]) {
|
||||||
Plugins.requireLibrary(pluginData.id, libraryPath);
|
Plugins.requireLibrary(pluginData.id, libraryPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Array.isArray(pluginData.hooks) && pluginData.hooks.length > 0) {
|
if (Array.isArray(pluginData.hooks)) {
|
||||||
async.each(pluginData.hooks, function (hook, next) {
|
pluginData.hooks.forEach(hook => Plugins.registerHook(pluginData.id, hook));
|
||||||
Plugins.registerHook(pluginData.id, hook, next);
|
|
||||||
}, callback);
|
|
||||||
} else {
|
|
||||||
callback();
|
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
winston.warn('[plugins] Unable to parse library for: ' + pluginData.id);
|
winston.warn('[plugins] Unable to parse library for: ' + pluginData.id);
|
||||||
callback(err);
|
throw err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user