mirror of
				https://github.com/NodeBB/NodeBB.git
				synced 2025-10-31 02:55:58 +01:00 
			
		
		
		
	feat: #7743 plugins
This commit is contained in:
		| @@ -10,30 +10,31 @@ module.exports = function (Plugins) { | ||||
| 	}; | ||||
|  | ||||
| 	Plugins.internals = { | ||||
| 		_register: function (data, callback) { | ||||
| 		_register: function (data) { | ||||
| 			Plugins.loadedHooks[data.hook] = Plugins.loadedHooks[data.hook] || []; | ||||
| 			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.hook`*, the name of the NodeBB hook | ||||
| 			`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) | ||||
| 	*/ | ||||
| 	Plugins.registerHook = function (id, data, callback) { | ||||
| 		callback = callback || function () {}; | ||||
|  | ||||
| 		if (!data.hook) { | ||||
| 			winston.warn('[plugins/' + id + '] registerHook called with invalid data.hook', data); | ||||
| 			return callback(); | ||||
| 	Plugins.registerHook = function (id, data) { | ||||
| 		if (!data.hook || !data.method) { | ||||
| 			winston.warn('[plugins/' + id + '] registerHook called with invalid data.hook/method', data); | ||||
| 			return; | ||||
| 		} | ||||
|  | ||||
| 		var method; | ||||
|  | ||||
| 		if (Plugins.deprecatedHooks[data.hook]) { | ||||
| 			winston.warn('[plugins/' + id + '] Hook `' + data.hook + '` is deprecated, ' + | ||||
| 				(Plugins.deprecatedHooks[data.hook] ? | ||||
| @@ -42,37 +43,34 @@ module.exports = function (Plugins) { | ||||
| 				)); | ||||
| 		} | ||||
|  | ||||
| 		if (data.hook && data.method) { | ||||
| 			data.id = id; | ||||
| 			if (!data.priority) { | ||||
| 				data.priority = 10; | ||||
| 			} | ||||
| 		data.id = id; | ||||
| 		if (!data.priority) { | ||||
| 			data.priority = 10; | ||||
| 		} | ||||
|  | ||||
| 			if (Array.isArray(data.method) && data.method.every(method => typeof method === 'function' || typeof method === 'string')) { | ||||
| 				// Go go gadget recursion! | ||||
| 				async.eachSeries(data.method, function (method, next) { | ||||
| 					const singularData = Object.assign({}, data, { method: method }); | ||||
| 					Plugins.registerHook(id, singularData, next); | ||||
| 				}, callback); | ||||
| 			} else if (typeof data.method === 'string' && data.method.length > 0) { | ||||
| 				method = data.method.split('.').reduce(function (memo, prop) { | ||||
| 					if (memo && memo[prop]) { | ||||
| 						return memo[prop]; | ||||
| 					} | ||||
| 					// Couldn't find method by path, aborting | ||||
| 					return null; | ||||
| 				}, Plugins.libraries[data.id]); | ||||
| 		if (Array.isArray(data.method) && data.method.every(method => typeof method === 'function' || typeof method === 'string')) { | ||||
| 			// Go go gadget recursion! | ||||
| 			data.method.forEach(function (method) { | ||||
| 				const singularData = Object.assign({}, data, { method: method }); | ||||
| 				Plugins.registerHook(id, singularData); | ||||
| 			}); | ||||
| 		} else if (typeof data.method === 'string' && data.method.length > 0) { | ||||
| 			const method = data.method.split('.').reduce(function (memo, prop) { | ||||
| 				if (memo && memo[prop]) { | ||||
| 					return memo[prop]; | ||||
| 				} | ||||
| 				// Couldn't find method by path, aborting | ||||
| 				return null; | ||||
| 			}, Plugins.libraries[data.id]); | ||||
|  | ||||
| 				// Write the actual method reference to the hookObj | ||||
| 				data.method = method; | ||||
| 			// Write the actual method reference to the hookObj | ||||
| 			data.method = method; | ||||
|  | ||||
| 				Plugins.internals._register(data, callback); | ||||
| 			} else if (typeof data.method === 'function') { | ||||
| 				Plugins.internals._register(data, callback); | ||||
| 			} else { | ||||
| 				winston.warn('[plugins/' + id + '] Hook method mismatch: ' + data.hook + ' => ' + data.method); | ||||
| 				return callback(); | ||||
| 			} | ||||
| 			Plugins.internals._register(data); | ||||
| 		} else if (typeof data.method === 'function') { | ||||
| 			Plugins.internals._register(data); | ||||
| 		} else { | ||||
| 			winston.warn('[plugins/' + id + '] Hook method mismatch: ' + data.hook + ' => ' + data.method); | ||||
| 		} | ||||
| 	}; | ||||
|  | ||||
| @@ -83,52 +81,33 @@ module.exports = function (Plugins) { | ||||
| 		}); | ||||
| 	}; | ||||
|  | ||||
| 	Plugins.fireHook = function (hook, params, callback) { | ||||
| 		callback = typeof callback === 'function' ? callback : function () {}; | ||||
| 		function done(err, result) { | ||||
| 			if (err) { | ||||
| 				return callback(err); | ||||
| 			} | ||||
| 			if (hook !== 'action:plugins.firehook') { | ||||
| 				Plugins.fireHook('action:plugins.firehook', { hook: hook, params: params }); | ||||
| 			} | ||||
| 			if (result !== undefined) { | ||||
| 				callback(null, result); | ||||
| 			} else { | ||||
| 				callback(); | ||||
| 			} | ||||
| 		} | ||||
| 		var hookList = Plugins.loadedHooks[hook]; | ||||
| 		var hookType = hook.split(':')[0]; | ||||
| 	Plugins.fireHook = async function (hook, params) { | ||||
| 		const hookList = Plugins.loadedHooks[hook]; | ||||
| 		const 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: | ||||
|  | ||||
| 		if (!hookTypeToMethod[hookType]) { | ||||
| 			winston.warn('[plugins] Unknown hookType: ' + hookType + ', hook : ' + hook); | ||||
| 			callback(); | ||||
| 			break; | ||||
| 			return; | ||||
| 		} | ||||
| 		const result = await hookTypeToMethod[hookType](hook, hookList, params); | ||||
|  | ||||
| 		if (hook !== 'action:plugins.firehook') { | ||||
| 			Plugins.fireHook('action:plugins.firehook', { hook: hook, params: params }); | ||||
| 		} | ||||
| 		if (result !== undefined) { | ||||
| 			return result; | ||||
| 		} | ||||
| 	}; | ||||
|  | ||||
| 	function fireFilterHook(hook, hookList, params, callback) { | ||||
| 	async function fireFilterHook(hook, hookList, params) { | ||||
| 		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 (global.env === 'development') { | ||||
| 					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) | ||||
| 				); | ||||
| 			} | ||||
| 		}, callback); | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	function fireActionHook(hook, hookList, params, callback) { | ||||
| 	async function fireActionHook(hook, hookList, params) { | ||||
| 		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 (global.env === 'development') { | ||||
| 					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); | ||||
| 			next(); | ||||
| 		}, callback); | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	function fireStaticHook(hook, hookList, params, callback) { | ||||
| 	async function fireStaticHook(hook, hookList, params) { | ||||
| 		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') { | ||||
| 				let timedOut = false; | ||||
| 				const timeoutId = setTimeout(function () { | ||||
| @@ -201,14 +180,14 @@ module.exports = function (Plugins) { | ||||
| 			} else { | ||||
| 				next(); | ||||
| 			} | ||||
| 		}, callback); | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	function fireResponseHook(hook, hookList, params, callback) { | ||||
| 	async function fireResponseHook(hook, hookList, params) { | ||||
| 		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 (global.env === 'development') { | ||||
| 					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); | ||||
| 			next(); | ||||
| 		}, callback); | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	Plugins.hasListeners = function (hook) { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user