mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-11-01 11:35:55 +01:00
style changes
This commit is contained in:
@@ -55,7 +55,7 @@ module.exports = function (redisClient, module) {
|
|||||||
if (!Array.isArray(fields) || !fields.length) {
|
if (!Array.isArray(fields) || !fields.length) {
|
||||||
return callback(null, keys.map(function () { return {}; }));
|
return callback(null, keys.map(function () { return {}; }));
|
||||||
}
|
}
|
||||||
var multi = redisClient.multi();
|
var multi = redisClient.multi();
|
||||||
|
|
||||||
for (var x = 0; x < keys.length; x += 1) {
|
for (var x = 0; x < keys.length; x += 1) {
|
||||||
multi.hmget.apply(multi, [keys[x]].concat(fields));
|
multi.hmget.apply(multi, [keys[x]].concat(fields));
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
var db = require('../database');
|
var db = require('../database');
|
||||||
var plugins = require('../plugins');
|
var plugins = require('../plugins');
|
||||||
|
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ function checkSetupFlag(next) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function checkCIFlag(next) {
|
function checkCIFlag(next) {
|
||||||
var ciVals;
|
var ciVals;
|
||||||
try {
|
try {
|
||||||
ciVals = JSON.parse(nconf.get('ci'));
|
ciVals = JSON.parse(nconf.get('ci'));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -188,7 +188,7 @@ function setupDefaultConfigs(next) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function enableDefaultTheme(next) {
|
function enableDefaultTheme(next) {
|
||||||
var meta = require('./meta');
|
var meta = require('./meta');
|
||||||
|
|
||||||
meta.configs.get('theme:id', function (err, id) {
|
meta.configs.get('theme:id', function (err, id) {
|
||||||
if (err || id) {
|
if (err || id) {
|
||||||
@@ -470,7 +470,7 @@ function enableDefaultPlugins(next) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setCopyrightWidget(next) {
|
function setCopyrightWidget(next) {
|
||||||
var db = require('./database');
|
var db = require('./database');
|
||||||
async.parallel({
|
async.parallel({
|
||||||
footerJSON: function (next) {
|
footerJSON: function (next) {
|
||||||
fs.readFile(path.join(__dirname, '../', 'install/data/footer.json'), next);
|
fs.readFile(path.join(__dirname, '../', 'install/data/footer.json'), next);
|
||||||
@@ -534,7 +534,7 @@ install.setup = function (callback) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
install.save = function (server_conf, callback) {
|
install.save = function (server_conf, callback) {
|
||||||
var serverConfigPath = path.join(__dirname, '../config.json');
|
var serverConfigPath = path.join(__dirname, '../config.json');
|
||||||
|
|
||||||
if (nconf.get('config')) {
|
if (nconf.get('config')) {
|
||||||
serverConfigPath = path.resolve(__dirname, '../', nconf.get('config'));
|
serverConfigPath = path.resolve(__dirname, '../', nconf.get('config'));
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ var path = require('path');
|
|||||||
var async = require('async');
|
var async = require('async');
|
||||||
|
|
||||||
var Languages = module.exports;
|
var Languages = module.exports;
|
||||||
var languagesPath = path.join(__dirname, '../build/public/language');
|
var languagesPath = path.join(__dirname, '../build/public/language');
|
||||||
|
|
||||||
Languages.init = function (next) {
|
Languages.init = function (next) {
|
||||||
next();
|
next();
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ module.exports = function (Meta) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getImports(files, prefix, extension, callback) {
|
function getImports(files, prefix, extension, callback) {
|
||||||
var pluginDirectories = [];
|
var pluginDirectories = [];
|
||||||
var source = '';
|
var source = '';
|
||||||
|
|
||||||
files.forEach(function (styleFile) {
|
files.forEach(function (styleFile) {
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ module.exports = function (Meta) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Meta.themes.set = function (data, callback) {
|
Meta.themes.set = function (data, callback) {
|
||||||
var themeData = {
|
var themeData = {
|
||||||
'theme:type': data.type,
|
'theme:type': data.type,
|
||||||
'theme:id': data.id,
|
'theme:id': data.id,
|
||||||
'theme:staticDir': '',
|
'theme:staticDir': '',
|
||||||
@@ -141,7 +141,7 @@ module.exports = function (Meta) {
|
|||||||
function (data, next) {
|
function (data, next) {
|
||||||
var themeId = data.currentThemeId || 'nodebb-theme-persona';
|
var themeId = data.currentThemeId || 'nodebb-theme-persona';
|
||||||
|
|
||||||
var themeObj = data.themesData.filter(function (themeObj) {
|
var themeObj = data.themesData.filter(function (themeObj) {
|
||||||
return themeObj.id === themeId;
|
return themeObj.id === themeId;
|
||||||
})[0];
|
})[0];
|
||||||
|
|
||||||
|
|||||||
@@ -1,37 +1,34 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
(function (module) {
|
|
||||||
var fork = require('child_process').fork;
|
|
||||||
var path = require('path');
|
|
||||||
|
|
||||||
module.hash = function (rounds, password, callback) {
|
var fork = require('child_process').fork;
|
||||||
forkChild({ type: 'hash', rounds: rounds, password: password }, callback);
|
var path = require('path');
|
||||||
};
|
|
||||||
|
|
||||||
module.compare = function (password, hash, callback) {
|
exports.hash = function (rounds, password, callback) {
|
||||||
if (!hash || !password) {
|
forkChild({ type: 'hash', rounds: rounds, password: password }, callback);
|
||||||
return setImmediate(callback, null, false);
|
};
|
||||||
}
|
|
||||||
forkChild({ type: 'compare', password: password, hash: hash }, callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
function forkChild(message, callback) {
|
exports.compare = function (password, hash, callback) {
|
||||||
var forkProcessParams = {};
|
if (!hash || !password) {
|
||||||
if (global.v8debug || parseInt(process.execArgv.indexOf('--debug'), 10) !== -1) {
|
return setImmediate(callback, null, false);
|
||||||
forkProcessParams = { execArgv: ['--debug=' + (5859), '--nolazy'] };
|
|
||||||
}
|
|
||||||
var child = fork(path.join(__dirname, 'bcrypt'), [], forkProcessParams);
|
|
||||||
|
|
||||||
child.on('message', function (msg) {
|
|
||||||
if (msg.err) {
|
|
||||||
return callback(new Error(msg.err));
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(null, msg.result);
|
|
||||||
});
|
|
||||||
|
|
||||||
child.send(message);
|
|
||||||
}
|
}
|
||||||
|
forkChild({ type: 'compare', password: password, hash: hash }, callback);
|
||||||
|
};
|
||||||
|
|
||||||
return module;
|
function forkChild(message, callback) {
|
||||||
}(exports));
|
var forkProcessParams = {};
|
||||||
|
if (global.v8debug || parseInt(process.execArgv.indexOf('--debug'), 10) !== -1) {
|
||||||
|
forkProcessParams = { execArgv: ['--debug=' + (5859), '--nolazy'] };
|
||||||
|
}
|
||||||
|
var child = fork(path.join(__dirname, 'bcrypt'), [], forkProcessParams);
|
||||||
|
|
||||||
|
child.on('message', function (msg) {
|
||||||
|
if (msg.err) {
|
||||||
|
return callback(new Error(msg.err));
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(null, msg.result);
|
||||||
|
});
|
||||||
|
|
||||||
|
child.send(message);
|
||||||
|
}
|
||||||
|
|||||||
638
src/plugins.js
638
src/plugins.js
@@ -14,351 +14,351 @@ var file = require('./file');
|
|||||||
var app;
|
var app;
|
||||||
var middleware;
|
var middleware;
|
||||||
|
|
||||||
(function (Plugins) {
|
var Plugins = module.exports;
|
||||||
require('./plugins/install')(Plugins);
|
|
||||||
require('./plugins/load')(Plugins);
|
|
||||||
require('./plugins/hooks')(Plugins);
|
|
||||||
Plugins.data = require('./plugins/data');
|
|
||||||
|
|
||||||
Plugins.getPluginPaths = Plugins.data.getPluginPaths;
|
require('./plugins/install')(Plugins);
|
||||||
Plugins.loadPluginInfo = Plugins.data.loadPluginInfo;
|
require('./plugins/load')(Plugins);
|
||||||
|
require('./plugins/hooks')(Plugins);
|
||||||
|
Plugins.data = require('./plugins/data');
|
||||||
|
|
||||||
Plugins.pluginsData = {};
|
Plugins.getPluginPaths = Plugins.data.getPluginPaths;
|
||||||
Plugins.libraries = {};
|
Plugins.loadPluginInfo = Plugins.data.loadPluginInfo;
|
||||||
Plugins.loadedHooks = {};
|
|
||||||
Plugins.staticDirs = {};
|
|
||||||
Plugins.cssFiles = [];
|
|
||||||
Plugins.lessFiles = [];
|
|
||||||
Plugins.clientScripts = [];
|
|
||||||
Plugins.acpScripts = [];
|
|
||||||
Plugins.libraryPaths = [];
|
|
||||||
Plugins.versionWarning = [];
|
|
||||||
Plugins.soundpacks = [];
|
|
||||||
Plugins.languageData = {};
|
|
||||||
|
|
||||||
Plugins.initialized = false;
|
Plugins.pluginsData = {};
|
||||||
|
Plugins.libraries = {};
|
||||||
|
Plugins.loadedHooks = {};
|
||||||
|
Plugins.staticDirs = {};
|
||||||
|
Plugins.cssFiles = [];
|
||||||
|
Plugins.lessFiles = [];
|
||||||
|
Plugins.clientScripts = [];
|
||||||
|
Plugins.acpScripts = [];
|
||||||
|
Plugins.libraryPaths = [];
|
||||||
|
Plugins.versionWarning = [];
|
||||||
|
Plugins.soundpacks = [];
|
||||||
|
Plugins.languageData = {};
|
||||||
|
|
||||||
Plugins.requireLibrary = function (pluginID, libraryPath) {
|
Plugins.initialized = false;
|
||||||
Plugins.libraries[pluginID] = require(libraryPath);
|
|
||||||
Plugins.libraryPaths.push(libraryPath);
|
|
||||||
};
|
|
||||||
|
|
||||||
Plugins.init = function (nbbApp, nbbMiddleware, callback) {
|
Plugins.requireLibrary = function (pluginID, libraryPath) {
|
||||||
callback = callback || function () {};
|
Plugins.libraries[pluginID] = require(libraryPath);
|
||||||
if (Plugins.initialized) {
|
Plugins.libraryPaths.push(libraryPath);
|
||||||
return callback();
|
};
|
||||||
}
|
|
||||||
|
|
||||||
if (nbbApp) {
|
Plugins.init = function (nbbApp, nbbMiddleware, callback) {
|
||||||
app = nbbApp;
|
callback = callback || function () {};
|
||||||
middleware = nbbMiddleware;
|
if (Plugins.initialized) {
|
||||||
hotswap.prepare(nbbApp);
|
return callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nbbApp) {
|
||||||
|
app = nbbApp;
|
||||||
|
middleware = nbbMiddleware;
|
||||||
|
hotswap.prepare(nbbApp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (global.env === 'development') {
|
||||||
|
winston.verbose('[plugins] Initializing plugins system');
|
||||||
|
}
|
||||||
|
|
||||||
|
Plugins.reload(function (err) {
|
||||||
|
if (err) {
|
||||||
|
winston.error('[plugins] NodeBB encountered a problem while loading plugins', err.message);
|
||||||
|
return callback(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (global.env === 'development') {
|
if (global.env === 'development') {
|
||||||
winston.verbose('[plugins] Initializing plugins system');
|
winston.info('[plugins] Plugins OK');
|
||||||
}
|
}
|
||||||
|
|
||||||
Plugins.reload(function (err) {
|
Plugins.initialized = true;
|
||||||
if (err) {
|
callback();
|
||||||
winston.error('[plugins] NodeBB encountered a problem while loading plugins', err.message);
|
});
|
||||||
return callback(err);
|
};
|
||||||
}
|
|
||||||
|
|
||||||
if (global.env === 'development') {
|
Plugins.reload = function (callback) {
|
||||||
winston.info('[plugins] Plugins OK');
|
// Resetting all local plugin data
|
||||||
}
|
Plugins.libraries = {};
|
||||||
|
Plugins.loadedHooks = {};
|
||||||
|
Plugins.staticDirs = {};
|
||||||
|
Plugins.versionWarning = [];
|
||||||
|
Plugins.cssFiles.length = 0;
|
||||||
|
Plugins.lessFiles.length = 0;
|
||||||
|
Plugins.clientScripts.length = 0;
|
||||||
|
Plugins.acpScripts.length = 0;
|
||||||
|
Plugins.libraryPaths.length = 0;
|
||||||
|
|
||||||
Plugins.initialized = true;
|
async.waterfall([
|
||||||
callback();
|
Plugins.getPluginPaths,
|
||||||
});
|
function (paths, next) {
|
||||||
};
|
async.eachSeries(paths, Plugins.loadPlugin, next);
|
||||||
|
},
|
||||||
Plugins.reload = function (callback) {
|
function (next) {
|
||||||
// Resetting all local plugin data
|
// If some plugins are incompatible, throw the warning here
|
||||||
Plugins.libraries = {};
|
if (Plugins.versionWarning.length && nconf.get('isPrimary') === 'true') {
|
||||||
Plugins.loadedHooks = {};
|
process.stdout.write('\n');
|
||||||
Plugins.staticDirs = {};
|
winston.warn('[plugins/load] The following plugins may not be compatible with your version of NodeBB. This may cause unintended behaviour or crashing. In the event of an unresponsive NodeBB caused by this plugin, run `./nodebb reset -p PLUGINNAME` to disable it.');
|
||||||
Plugins.versionWarning = [];
|
for (var x = 0, numPlugins = Plugins.versionWarning.length; x < numPlugins; x += 1) {
|
||||||
Plugins.cssFiles.length = 0;
|
process.stdout.write(' * '.yellow + Plugins.versionWarning[x] + '\n');
|
||||||
Plugins.lessFiles.length = 0;
|
|
||||||
Plugins.clientScripts.length = 0;
|
|
||||||
Plugins.acpScripts.length = 0;
|
|
||||||
Plugins.libraryPaths.length = 0;
|
|
||||||
|
|
||||||
async.waterfall([
|
|
||||||
Plugins.getPluginPaths,
|
|
||||||
function (paths, next) {
|
|
||||||
async.eachSeries(paths, Plugins.loadPlugin, next);
|
|
||||||
},
|
|
||||||
function (next) {
|
|
||||||
// If some plugins are incompatible, throw the warning here
|
|
||||||
if (Plugins.versionWarning.length && nconf.get('isPrimary') === 'true') {
|
|
||||||
process.stdout.write('\n');
|
|
||||||
winston.warn('[plugins/load] The following plugins may not be compatible with your version of NodeBB. This may cause unintended behaviour or crashing. In the event of an unresponsive NodeBB caused by this plugin, run `./nodebb reset -p PLUGINNAME` to disable it.');
|
|
||||||
for (var x = 0, numPlugins = Plugins.versionWarning.length; x < numPlugins; x += 1) {
|
|
||||||
process.stdout.write(' * '.yellow + Plugins.versionWarning[x] + '\n');
|
|
||||||
}
|
|
||||||
process.stdout.write('\n');
|
|
||||||
}
|
}
|
||||||
|
process.stdout.write('\n');
|
||||||
|
}
|
||||||
|
|
||||||
Object.keys(Plugins.loadedHooks).forEach(function (hook) {
|
Object.keys(Plugins.loadedHooks).forEach(function (hook) {
|
||||||
var hooks = Plugins.loadedHooks[hook];
|
var hooks = Plugins.loadedHooks[hook];
|
||||||
hooks.sort(function (a, b) {
|
hooks.sort(function (a, b) {
|
||||||
return a.priority - b.priority;
|
return a.priority - b.priority;
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
next();
|
next();
|
||||||
},
|
},
|
||||||
], callback);
|
], callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
Plugins.reloadRoutes = function (callback) {
|
||||||
|
var router = express.Router();
|
||||||
|
|
||||||
|
router.hotswapId = 'plugins';
|
||||||
|
router.render = function () {
|
||||||
|
app.render.apply(app, arguments);
|
||||||
};
|
};
|
||||||
|
|
||||||
Plugins.reloadRoutes = function (callback) {
|
var controllers = require('./controllers');
|
||||||
var router = express.Router();
|
Plugins.fireHook('static:app.load', { app: app, router: router, middleware: middleware, controllers: controllers }, function (err) {
|
||||||
|
if (err) {
|
||||||
|
winston.error('[plugins] Encountered error while executing post-router plugins hooks: ' + err.message);
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
router.hotswapId = 'plugins';
|
hotswap.replace('plugins', router);
|
||||||
router.render = function () {
|
winston.verbose('[plugins] All plugins reloaded and rerouted');
|
||||||
app.render.apply(app, arguments);
|
callback();
|
||||||
};
|
});
|
||||||
|
};
|
||||||
|
|
||||||
var controllers = require('./controllers');
|
Plugins.getTemplates = function (callback) {
|
||||||
Plugins.fireHook('static:app.load', { app: app, router: router, middleware: middleware, controllers: controllers }, function (err) {
|
var templates = {};
|
||||||
if (err) {
|
var tplName;
|
||||||
winston.error('[plugins] Encountered error while executing post-router plugins hooks: ' + err.message);
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
hotswap.replace('plugins', router);
|
Plugins.data.getActive(function (err, plugins) {
|
||||||
winston.verbose('[plugins] All plugins reloaded and rerouted');
|
if (err) {
|
||||||
callback();
|
return callback(err);
|
||||||
});
|
}
|
||||||
};
|
|
||||||
|
|
||||||
Plugins.getTemplates = function (callback) {
|
async.eachSeries(plugins, function (plugin, next) {
|
||||||
var templates = {};
|
if (plugin.templates || plugin.id.startsWith('nodebb-theme-')) {
|
||||||
var tplName;
|
winston.verbose('[plugins] Loading templates (' + plugin.id + ')');
|
||||||
|
var templatesPath = path.join(__dirname, '../node_modules', plugin.id, plugin.templates || 'templates');
|
||||||
|
file.walk(templatesPath, function (err, pluginTemplates) {
|
||||||
|
if (pluginTemplates) {
|
||||||
|
pluginTemplates.forEach(function (pluginTemplate) {
|
||||||
|
if (pluginTemplate.endsWith('.tpl')) {
|
||||||
|
tplName = '/' + pluginTemplate.replace(templatesPath, '').substring(1);
|
||||||
|
|
||||||
Plugins.data.getActive(function (err, plugins) {
|
if (templates.hasOwnProperty(tplName)) {
|
||||||
if (err) {
|
winston.verbose('[plugins] ' + tplName + ' replaced by ' + plugin.id);
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
async.eachSeries(plugins, function (plugin, next) {
|
|
||||||
if (plugin.templates || plugin.id.startsWith('nodebb-theme-')) {
|
|
||||||
winston.verbose('[plugins] Loading templates (' + plugin.id + ')');
|
|
||||||
var templatesPath = path.join(__dirname, '../node_modules', plugin.id, plugin.templates || 'templates');
|
|
||||||
file.walk(templatesPath, function (err, pluginTemplates) {
|
|
||||||
if (pluginTemplates) {
|
|
||||||
pluginTemplates.forEach(function (pluginTemplate) {
|
|
||||||
if (pluginTemplate.endsWith('.tpl')) {
|
|
||||||
tplName = '/' + pluginTemplate.replace(templatesPath, '').substring(1);
|
|
||||||
|
|
||||||
if (templates.hasOwnProperty(tplName)) {
|
|
||||||
winston.verbose('[plugins] ' + tplName + ' replaced by ' + plugin.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
templates[tplName] = pluginTemplate;
|
|
||||||
} else {
|
|
||||||
winston.warn('[plugins] Skipping ' + pluginTemplate + ' by plugin ' + plugin.id);
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
} else if (err) {
|
|
||||||
winston.error(err);
|
|
||||||
} else {
|
|
||||||
winston.warn('[plugins/' + plugin.id + '] A templates directory was defined for this plugin, but was not found.');
|
|
||||||
}
|
|
||||||
|
|
||||||
next(false);
|
templates[tplName] = pluginTemplate;
|
||||||
});
|
} else {
|
||||||
} else {
|
winston.warn('[plugins] Skipping ' + pluginTemplate + ' by plugin ' + plugin.id);
|
||||||
next(false);
|
|
||||||
}
|
|
||||||
}, function (err) {
|
|
||||||
callback(err, templates);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Plugins.get = function (id, callback) {
|
|
||||||
var url = (nconf.get('registry') || 'https://packages.nodebb.org') + '/api/v1/plugins/' + id;
|
|
||||||
|
|
||||||
require('request')(url, {
|
|
||||||
json: true,
|
|
||||||
}, function (err, res, body) {
|
|
||||||
if (res.statusCode === 404 || !body.payload) {
|
|
||||||
return callback(err, {});
|
|
||||||
}
|
|
||||||
|
|
||||||
Plugins.normalise([body.payload], function (err, normalised) {
|
|
||||||
normalised = normalised.filter(function (plugin) {
|
|
||||||
return plugin.id === id;
|
|
||||||
});
|
|
||||||
return callback(err, !err ? normalised[0] : undefined);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Plugins.list = function (matching, callback) {
|
|
||||||
if (arguments.length === 1 && typeof matching === 'function') {
|
|
||||||
callback = matching;
|
|
||||||
matching = true;
|
|
||||||
}
|
|
||||||
var 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 : '');
|
|
||||||
|
|
||||||
require('request')(url, {
|
|
||||||
json: true,
|
|
||||||
}, function (err, res, body) {
|
|
||||||
if (err) {
|
|
||||||
winston.error('Error parsing plugins : ' + err.message);
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
Plugins.normalise(body, callback);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Plugins.normalise = function (apiReturn, callback) {
|
|
||||||
var pluginMap = {};
|
|
||||||
var dependencies = require(path.join(nconf.get('base_dir'), 'package.json')).dependencies;
|
|
||||||
apiReturn = apiReturn || [];
|
|
||||||
for (var i = 0; i < apiReturn.length; i += 1) {
|
|
||||||
apiReturn[i].id = apiReturn[i].name;
|
|
||||||
apiReturn[i].installed = false;
|
|
||||||
apiReturn[i].active = false;
|
|
||||||
apiReturn[i].url = apiReturn[i].url || (apiReturn[i].repository ? apiReturn[i].repository.url : '');
|
|
||||||
pluginMap[apiReturn[i].name] = apiReturn[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
// If it errored out because a package.json or plugin.json couldn't be read, no need to do this stuff
|
|
||||||
if (plugin.error) {
|
|
||||||
pluginMap[plugin.id] = pluginMap[plugin.id] || {};
|
|
||||||
pluginMap[plugin.id].installed = true;
|
|
||||||
pluginMap[plugin.id].error = true;
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
|
|
||||||
pluginMap[plugin.id] = pluginMap[plugin.id] || {};
|
|
||||||
pluginMap[plugin.id].id = pluginMap[plugin.id].id || plugin.id;
|
|
||||||
pluginMap[plugin.id].name = plugin.name || pluginMap[plugin.id].name;
|
|
||||||
pluginMap[plugin.id].description = plugin.description;
|
|
||||||
pluginMap[plugin.id].url = pluginMap[plugin.id].url || plugin.url;
|
|
||||||
pluginMap[plugin.id].installed = true;
|
|
||||||
pluginMap[plugin.id].isTheme = !!plugin.id.match('nodebb-theme-');
|
|
||||||
pluginMap[plugin.id].error = plugin.error || false;
|
|
||||||
pluginMap[plugin.id].active = plugin.active;
|
|
||||||
pluginMap[plugin.id].version = plugin.version;
|
|
||||||
pluginMap[plugin.id].settingsRoute = plugin.settingsRoute;
|
|
||||||
|
|
||||||
// If package.json defines a version to use, stick to that
|
|
||||||
if (dependencies.hasOwnProperty(plugin.id) && semver.valid(dependencies[plugin.id])) {
|
|
||||||
pluginMap[plugin.id].latest = dependencies[plugin.id];
|
|
||||||
} else {
|
|
||||||
pluginMap[plugin.id].latest = pluginMap[plugin.id].latest || plugin.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 = [];
|
|
||||||
|
|
||||||
for (var key in pluginMap) {
|
|
||||||
if (pluginMap.hasOwnProperty(key)) {
|
|
||||||
pluginArray.push(pluginMap[key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pluginArray.sort(function (a, b) {
|
|
||||||
if (a.name > b.name) {
|
|
||||||
return 1;
|
|
||||||
} else if (a.name < b.name) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
callback(null, pluginArray);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Plugins.showInstalled = function (callback) {
|
|
||||||
var npmPluginPath = path.join(__dirname, '../node_modules');
|
|
||||||
|
|
||||||
async.waterfall([
|
|
||||||
async.apply(fs.readdir, npmPluginPath),
|
|
||||||
|
|
||||||
function (dirs, next) {
|
|
||||||
dirs = dirs.filter(function (dir) {
|
|
||||||
return dir.startsWith('nodebb-plugin-') ||
|
|
||||||
dir.startsWith('nodebb-widget-') ||
|
|
||||||
dir.startsWith('nodebb-rewards-') ||
|
|
||||||
dir.startsWith('nodebb-theme-');
|
|
||||||
}).map(function (dir) {
|
|
||||||
return path.join(npmPluginPath, dir);
|
|
||||||
});
|
|
||||||
|
|
||||||
async.filter(dirs, function (dir, callback) {
|
|
||||||
fs.stat(dir, function (err, stats) {
|
|
||||||
if (err) {
|
|
||||||
if (err.code === 'ENOENT') {
|
|
||||||
return callback(null, false);
|
|
||||||
}
|
}
|
||||||
return callback(err);
|
});
|
||||||
}
|
} else if (err) {
|
||||||
callback(null, stats.isDirectory());
|
winston.error(err);
|
||||||
});
|
} else {
|
||||||
}, next);
|
winston.warn('[plugins/' + plugin.id + '] A templates directory was defined for this plugin, but was not found.');
|
||||||
},
|
}
|
||||||
|
|
||||||
function (files, next) {
|
next(false);
|
||||||
var plugins = [];
|
|
||||||
|
|
||||||
async.each(files, 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);
|
|
||||||
});
|
});
|
||||||
},
|
} else {
|
||||||
], callback);
|
next(false);
|
||||||
};
|
}
|
||||||
}(exports));
|
}, function (err) {
|
||||||
|
callback(err, templates);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Plugins.get = function (id, callback) {
|
||||||
|
var url = (nconf.get('registry') || 'https://packages.nodebb.org') + '/api/v1/plugins/' + id;
|
||||||
|
|
||||||
|
require('request')(url, {
|
||||||
|
json: true,
|
||||||
|
}, function (err, res, body) {
|
||||||
|
if (res.statusCode === 404 || !body.payload) {
|
||||||
|
return callback(err, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
Plugins.normalise([body.payload], function (err, normalised) {
|
||||||
|
normalised = normalised.filter(function (plugin) {
|
||||||
|
return plugin.id === id;
|
||||||
|
});
|
||||||
|
return callback(err, !err ? normalised[0] : undefined);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Plugins.list = function (matching, callback) {
|
||||||
|
if (arguments.length === 1 && typeof matching === 'function') {
|
||||||
|
callback = matching;
|
||||||
|
matching = true;
|
||||||
|
}
|
||||||
|
var 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 : '');
|
||||||
|
|
||||||
|
require('request')(url, {
|
||||||
|
json: true,
|
||||||
|
}, function (err, res, body) {
|
||||||
|
if (err) {
|
||||||
|
winston.error('Error parsing plugins : ' + err.message);
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
Plugins.normalise(body, callback);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Plugins.normalise = function (apiReturn, callback) {
|
||||||
|
var pluginMap = {};
|
||||||
|
var dependencies = require(path.join(nconf.get('base_dir'), 'package.json')).dependencies;
|
||||||
|
apiReturn = apiReturn || [];
|
||||||
|
for (var i = 0; i < apiReturn.length; i += 1) {
|
||||||
|
apiReturn[i].id = apiReturn[i].name;
|
||||||
|
apiReturn[i].installed = false;
|
||||||
|
apiReturn[i].active = false;
|
||||||
|
apiReturn[i].url = apiReturn[i].url || (apiReturn[i].repository ? apiReturn[i].repository.url : '');
|
||||||
|
pluginMap[apiReturn[i].name] = apiReturn[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
// If it errored out because a package.json or plugin.json couldn't be read, no need to do this stuff
|
||||||
|
if (plugin.error) {
|
||||||
|
pluginMap[plugin.id] = pluginMap[plugin.id] || {};
|
||||||
|
pluginMap[plugin.id].installed = true;
|
||||||
|
pluginMap[plugin.id].error = true;
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
pluginMap[plugin.id] = pluginMap[plugin.id] || {};
|
||||||
|
pluginMap[plugin.id].id = pluginMap[plugin.id].id || plugin.id;
|
||||||
|
pluginMap[plugin.id].name = plugin.name || pluginMap[plugin.id].name;
|
||||||
|
pluginMap[plugin.id].description = plugin.description;
|
||||||
|
pluginMap[plugin.id].url = pluginMap[plugin.id].url || plugin.url;
|
||||||
|
pluginMap[plugin.id].installed = true;
|
||||||
|
pluginMap[plugin.id].isTheme = !!plugin.id.match('nodebb-theme-');
|
||||||
|
pluginMap[plugin.id].error = plugin.error || false;
|
||||||
|
pluginMap[plugin.id].active = plugin.active;
|
||||||
|
pluginMap[plugin.id].version = plugin.version;
|
||||||
|
pluginMap[plugin.id].settingsRoute = plugin.settingsRoute;
|
||||||
|
|
||||||
|
// If package.json defines a version to use, stick to that
|
||||||
|
if (dependencies.hasOwnProperty(plugin.id) && semver.valid(dependencies[plugin.id])) {
|
||||||
|
pluginMap[plugin.id].latest = dependencies[plugin.id];
|
||||||
|
} else {
|
||||||
|
pluginMap[plugin.id].latest = pluginMap[plugin.id].latest || plugin.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 = [];
|
||||||
|
|
||||||
|
for (var key in pluginMap) {
|
||||||
|
if (pluginMap.hasOwnProperty(key)) {
|
||||||
|
pluginArray.push(pluginMap[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pluginArray.sort(function (a, b) {
|
||||||
|
if (a.name > b.name) {
|
||||||
|
return 1;
|
||||||
|
} else if (a.name < b.name) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
callback(null, pluginArray);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Plugins.showInstalled = function (callback) {
|
||||||
|
var npmPluginPath = path.join(__dirname, '../node_modules');
|
||||||
|
|
||||||
|
async.waterfall([
|
||||||
|
async.apply(fs.readdir, npmPluginPath),
|
||||||
|
|
||||||
|
function (dirs, next) {
|
||||||
|
dirs = dirs.filter(function (dir) {
|
||||||
|
return dir.startsWith('nodebb-plugin-') ||
|
||||||
|
dir.startsWith('nodebb-widget-') ||
|
||||||
|
dir.startsWith('nodebb-rewards-') ||
|
||||||
|
dir.startsWith('nodebb-theme-');
|
||||||
|
}).map(function (dir) {
|
||||||
|
return path.join(npmPluginPath, dir);
|
||||||
|
});
|
||||||
|
|
||||||
|
async.filter(dirs, function (dir, callback) {
|
||||||
|
fs.stat(dir, function (err, stats) {
|
||||||
|
if (err) {
|
||||||
|
if (err.code === 'ENOENT') {
|
||||||
|
return callback(null, false);
|
||||||
|
}
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
callback(null, stats.isDirectory());
|
||||||
|
});
|
||||||
|
}, next);
|
||||||
|
},
|
||||||
|
|
||||||
|
function (files, next) {
|
||||||
|
var plugins = [];
|
||||||
|
|
||||||
|
async.each(files, 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);
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var LRU = require('lru-cache');
|
var LRU = require('lru-cache');
|
||||||
var meta = require('../meta');
|
var meta = require('../meta');
|
||||||
|
|
||||||
var cache = LRU({
|
var cache = LRU({
|
||||||
|
|||||||
@@ -2,16 +2,16 @@
|
|||||||
|
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
var passport = require('passport');
|
var passport = require('passport');
|
||||||
var passportLocal = require('passport-local').Strategy;
|
var passportLocal = require('passport-local').Strategy;
|
||||||
var nconf = require('nconf');
|
var nconf = require('nconf');
|
||||||
var winston = require('winston');
|
var winston = require('winston');
|
||||||
var express = require('express');
|
var express = require('express');
|
||||||
|
|
||||||
var controllers = require('../controllers');
|
var controllers = require('../controllers');
|
||||||
var plugins = require('../plugins');
|
var plugins = require('../plugins');
|
||||||
var hotswap = require('../hotswap');
|
var hotswap = require('../hotswap');
|
||||||
|
|
||||||
var loginStrategies = [];
|
var loginStrategies = [];
|
||||||
|
|
||||||
var Auth = module.exports;
|
var Auth = module.exports;
|
||||||
|
|
||||||
|
|||||||
@@ -233,7 +233,7 @@ function generateTopicsFeed(feedOptions, feedTopics, callback) {
|
|||||||
|
|
||||||
feedTopics = feedTopics.filter(Boolean);
|
feedTopics = feedTopics.filter(Boolean);
|
||||||
|
|
||||||
var feed = new rss(feedOptions);
|
var feed = new rss(feedOptions);
|
||||||
|
|
||||||
if (feedTopics.length > 0) {
|
if (feedTopics.length > 0) {
|
||||||
feed.pubDate = new Date(parseInt(feedTopics[0].lastposttime, 10)).toUTCString();
|
feed.pubDate = new Date(parseInt(feedTopics[0].lastposttime, 10)).toUTCString();
|
||||||
@@ -338,7 +338,7 @@ function generateForPostsFeed(feedOptions, posts) {
|
|||||||
feedOptions.feed_url = nconf.get('url') + feedOptions.feed_url;
|
feedOptions.feed_url = nconf.get('url') + feedOptions.feed_url;
|
||||||
feedOptions.site_url = nconf.get('url') + feedOptions.site_url;
|
feedOptions.site_url = nconf.get('url') + feedOptions.site_url;
|
||||||
|
|
||||||
var feed = new rss(feedOptions);
|
var feed = new rss(feedOptions);
|
||||||
|
|
||||||
if (posts.length > 0) {
|
if (posts.length > 0) {
|
||||||
feed.pubDate = new Date(parseInt(posts[0].timestamp, 10)).toUTCString();
|
feed.pubDate = new Date(parseInt(posts[0].timestamp, 10)).toUTCString();
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var SocketPlugins = {};
|
var SocketPlugins = {};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This file is provided exclusively so that plugins can require it and add their own socket listeners.
|
This file is provided exclusively so that plugins can require it and add their own socket listeners.
|
||||||
|
|||||||
596
src/topics.js
596
src/topics.js
@@ -12,322 +12,326 @@ var categories = require('./categories');
|
|||||||
var privileges = require('./privileges');
|
var privileges = require('./privileges');
|
||||||
var social = require('./social');
|
var social = require('./social');
|
||||||
|
|
||||||
(function (Topics) {
|
var Topics = module.exports;
|
||||||
require('./topics/data')(Topics);
|
|
||||||
require('./topics/create')(Topics);
|
|
||||||
require('./topics/delete')(Topics);
|
|
||||||
require('./topics/unread')(Topics);
|
|
||||||
require('./topics/recent')(Topics);
|
|
||||||
require('./topics/popular')(Topics);
|
|
||||||
require('./topics/user')(Topics);
|
|
||||||
require('./topics/fork')(Topics);
|
|
||||||
require('./topics/posts')(Topics);
|
|
||||||
require('./topics/follow')(Topics);
|
|
||||||
require('./topics/tags')(Topics);
|
|
||||||
require('./topics/teaser')(Topics);
|
|
||||||
require('./topics/suggested')(Topics);
|
|
||||||
require('./topics/tools')(Topics);
|
|
||||||
require('./topics/thumb')(Topics);
|
|
||||||
require('./topics/bookmarks')(Topics);
|
|
||||||
|
|
||||||
Topics.exists = function (tid, callback) {
|
require('./topics/data')(Topics);
|
||||||
db.isSortedSetMember('topics:tid', tid, callback);
|
require('./topics/create')(Topics);
|
||||||
};
|
require('./topics/delete')(Topics);
|
||||||
|
require('./topics/unread')(Topics);
|
||||||
|
require('./topics/recent')(Topics);
|
||||||
|
require('./topics/popular')(Topics);
|
||||||
|
require('./topics/user')(Topics);
|
||||||
|
require('./topics/fork')(Topics);
|
||||||
|
require('./topics/posts')(Topics);
|
||||||
|
require('./topics/follow')(Topics);
|
||||||
|
require('./topics/tags')(Topics);
|
||||||
|
require('./topics/teaser')(Topics);
|
||||||
|
require('./topics/suggested')(Topics);
|
||||||
|
require('./topics/tools')(Topics);
|
||||||
|
require('./topics/thumb')(Topics);
|
||||||
|
require('./topics/bookmarks')(Topics);
|
||||||
|
|
||||||
Topics.getPageCount = function (tid, uid, callback) {
|
Topics.exists = function (tid, callback) {
|
||||||
var postCount;
|
db.isSortedSetMember('topics:tid', tid, callback);
|
||||||
async.waterfall([
|
};
|
||||||
function (next) {
|
|
||||||
Topics.getTopicField(tid, 'postcount', next);
|
|
||||||
},
|
|
||||||
function (_postCount, next) {
|
|
||||||
if (!parseInt(_postCount, 10)) {
|
|
||||||
return callback(null, 1);
|
|
||||||
}
|
|
||||||
postCount = _postCount;
|
|
||||||
user.getSettings(uid, next);
|
|
||||||
},
|
|
||||||
function (settings, next) {
|
|
||||||
next(null, Math.ceil((parseInt(postCount, 10) - 1) / settings.postsPerPage));
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
Topics.getTidPage = function (tid, uid, callback) {
|
Topics.getPageCount = function (tid, uid, callback) {
|
||||||
console.warn('[Topics.getTidPage] deprecated!');
|
var postCount;
|
||||||
callback(null, 1);
|
async.waterfall([
|
||||||
};
|
function (next) {
|
||||||
|
Topics.getTopicField(tid, 'postcount', next);
|
||||||
|
},
|
||||||
|
function (_postCount, next) {
|
||||||
|
if (!parseInt(_postCount, 10)) {
|
||||||
|
return callback(null, 1);
|
||||||
|
}
|
||||||
|
postCount = _postCount;
|
||||||
|
user.getSettings(uid, next);
|
||||||
|
},
|
||||||
|
function (settings, next) {
|
||||||
|
next(null, Math.ceil((parseInt(postCount, 10) - 1) / settings.postsPerPage));
|
||||||
|
},
|
||||||
|
], callback);
|
||||||
|
};
|
||||||
|
|
||||||
Topics.getTopicsFromSet = function (set, uid, start, stop, callback) {
|
Topics.getTidPage = function (tid, uid, callback) {
|
||||||
async.waterfall([
|
console.warn('[Topics.getTidPage] deprecated!');
|
||||||
function (next) {
|
callback(null, 1);
|
||||||
db.getSortedSetRevRange(set, start, stop, next);
|
};
|
||||||
},
|
|
||||||
function (tids, next) {
|
|
||||||
Topics.getTopics(tids, uid, next);
|
|
||||||
},
|
|
||||||
function (topics, next) {
|
|
||||||
next(null, { topics: topics, nextStart: stop + 1 });
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
Topics.getTopics = function (tids, uid, callback) {
|
Topics.getTopicsFromSet = function (set, uid, start, stop, callback) {
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
function (next) {
|
function (next) {
|
||||||
privileges.topics.filterTids('read', tids, uid, next);
|
db.getSortedSetRevRange(set, start, stop, next);
|
||||||
},
|
},
|
||||||
function (tids, next) {
|
function (tids, next) {
|
||||||
Topics.getTopicsByTids(tids, uid, next);
|
Topics.getTopics(tids, uid, next);
|
||||||
},
|
},
|
||||||
], callback);
|
function (topics, next) {
|
||||||
};
|
next(null, { topics: topics, nextStart: stop + 1 });
|
||||||
|
},
|
||||||
|
], callback);
|
||||||
|
};
|
||||||
|
|
||||||
Topics.getTopicsByTids = function (tids, uid, callback) {
|
Topics.getTopics = function (tids, uid, callback) {
|
||||||
if (!Array.isArray(tids) || !tids.length) {
|
async.waterfall([
|
||||||
return callback(null, []);
|
function (next) {
|
||||||
}
|
privileges.topics.filterTids('read', tids, uid, next);
|
||||||
|
},
|
||||||
|
function (tids, next) {
|
||||||
|
Topics.getTopicsByTids(tids, uid, next);
|
||||||
|
},
|
||||||
|
], callback);
|
||||||
|
};
|
||||||
|
|
||||||
var uids;
|
Topics.getTopicsByTids = function (tids, uid, callback) {
|
||||||
var cids;
|
if (!Array.isArray(tids) || !tids.length) {
|
||||||
var topics;
|
return callback(null, []);
|
||||||
|
}
|
||||||
|
|
||||||
async.waterfall([
|
var uids;
|
||||||
function (next) {
|
var cids;
|
||||||
Topics.getTopicsData(tids, next);
|
var topics;
|
||||||
},
|
|
||||||
function (_topics, next) {
|
|
||||||
function mapFilter(array, field) {
|
|
||||||
return array.map(function (topic) {
|
|
||||||
return topic && topic[field] && topic[field].toString();
|
|
||||||
}).filter(function (value, index, array) {
|
|
||||||
return utils.isNumber(value) && array.indexOf(value) === index;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
topics = _topics;
|
async.waterfall([
|
||||||
uids = mapFilter(topics, 'uid');
|
function (next) {
|
||||||
cids = mapFilter(topics, 'cid');
|
Topics.getTopicsData(tids, next);
|
||||||
|
},
|
||||||
async.parallel({
|
function (_topics, next) {
|
||||||
users: function (next) {
|
function mapFilter(array, field) {
|
||||||
user.getUsersFields(uids, ['uid', 'username', 'fullname', 'userslug', 'reputation', 'postcount', 'picture', 'signature', 'banned', 'status'], next);
|
return array.map(function (topic) {
|
||||||
},
|
return topic && topic[field] && topic[field].toString();
|
||||||
categories: function (next) {
|
}).filter(function (value, index, array) {
|
||||||
categories.getCategoriesFields(cids, ['cid', 'name', 'slug', 'icon', 'image', 'bgColor', 'color', 'disabled'], next);
|
return utils.isNumber(value) && array.indexOf(value) === index;
|
||||||
},
|
|
||||||
hasRead: function (next) {
|
|
||||||
Topics.hasReadTopics(tids, uid, next);
|
|
||||||
},
|
|
||||||
isIgnored: function (next) {
|
|
||||||
Topics.isIgnoring(tids, uid, next);
|
|
||||||
},
|
|
||||||
bookmarks: function (next) {
|
|
||||||
Topics.getUserBookmarks(tids, uid, next);
|
|
||||||
},
|
|
||||||
teasers: function (next) {
|
|
||||||
Topics.getTeasers(topics, uid, next);
|
|
||||||
},
|
|
||||||
tags: function (next) {
|
|
||||||
Topics.getTopicsTagsObjects(tids, next);
|
|
||||||
},
|
|
||||||
}, next);
|
|
||||||
},
|
|
||||||
function (results, next) {
|
|
||||||
var users = _.object(uids, results.users);
|
|
||||||
var categories = _.object(cids, results.categories);
|
|
||||||
|
|
||||||
for (var i = 0; i < topics.length; i += 1) {
|
|
||||||
if (topics[i]) {
|
|
||||||
topics[i].category = categories[topics[i].cid];
|
|
||||||
topics[i].user = users[topics[i].uid];
|
|
||||||
topics[i].teaser = results.teasers[i];
|
|
||||||
topics[i].tags = results.tags[i];
|
|
||||||
|
|
||||||
topics[i].isOwner = parseInt(topics[i].uid, 10) === parseInt(uid, 10);
|
|
||||||
topics[i].pinned = parseInt(topics[i].pinned, 10) === 1;
|
|
||||||
topics[i].locked = parseInt(topics[i].locked, 10) === 1;
|
|
||||||
topics[i].deleted = parseInt(topics[i].deleted, 10) === 1;
|
|
||||||
topics[i].ignored = results.isIgnored[i];
|
|
||||||
topics[i].unread = !results.hasRead[i] && !results.isIgnored[i];
|
|
||||||
topics[i].bookmark = results.bookmarks[i];
|
|
||||||
topics[i].unreplied = !topics[i].teaser;
|
|
||||||
|
|
||||||
topics[i].icons = [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
topics = topics.filter(function (topic) {
|
|
||||||
return topic && topic.category && !topic.category.disabled;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
plugins.fireHook('filter:topics.get', { topics: topics, uid: uid }, next);
|
|
||||||
},
|
|
||||||
function (data, next) {
|
|
||||||
next(null, data.topics);
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
Topics.getTopicWithPosts = function (topicData, set, uid, start, stop, reverse, callback) {
|
|
||||||
async.waterfall([
|
|
||||||
function (next) {
|
|
||||||
async.parallel({
|
|
||||||
posts: async.apply(getMainPostAndReplies, topicData, set, uid, start, stop, reverse),
|
|
||||||
category: async.apply(Topics.getCategoryData, topicData.tid),
|
|
||||||
threadTools: async.apply(plugins.fireHook, 'filter:topic.thread_tools', { topic: topicData, uid: uid, tools: [] }),
|
|
||||||
isFollowing: async.apply(Topics.isFollowing, [topicData.tid], uid),
|
|
||||||
isIgnoring: async.apply(Topics.isIgnoring, [topicData.tid], uid),
|
|
||||||
bookmark: async.apply(Topics.getUserBookmark, topicData.tid, uid),
|
|
||||||
postSharing: async.apply(social.getActivePostSharing),
|
|
||||||
deleter: async.apply(getDeleter, topicData),
|
|
||||||
related: function (next) {
|
|
||||||
async.waterfall([
|
|
||||||
function (next) {
|
|
||||||
Topics.getTopicTagsObjects(topicData.tid, next);
|
|
||||||
},
|
|
||||||
function (tags, next) {
|
|
||||||
topicData.tags = tags;
|
|
||||||
Topics.getRelatedTopics(topicData, uid, next);
|
|
||||||
},
|
|
||||||
], next);
|
|
||||||
},
|
|
||||||
}, next);
|
|
||||||
},
|
|
||||||
function (results, next) {
|
|
||||||
topicData.posts = results.posts;
|
|
||||||
topicData.category = results.category;
|
|
||||||
topicData.thread_tools = results.threadTools.tools;
|
|
||||||
topicData.isFollowing = results.isFollowing[0];
|
|
||||||
topicData.isNotFollowing = !results.isFollowing[0] && !results.isIgnoring[0];
|
|
||||||
topicData.isIgnoring = results.isIgnoring[0];
|
|
||||||
topicData.bookmark = results.bookmark;
|
|
||||||
topicData.postSharing = results.postSharing;
|
|
||||||
topicData.deleter = results.deleter;
|
|
||||||
topicData.deletedTimestampISO = utils.toISOString(topicData.deletedTimestamp);
|
|
||||||
topicData.related = results.related || [];
|
|
||||||
|
|
||||||
topicData.unreplied = parseInt(topicData.postcount, 10) === 1;
|
|
||||||
topicData.deleted = parseInt(topicData.deleted, 10) === 1;
|
|
||||||
topicData.locked = parseInt(topicData.locked, 10) === 1;
|
|
||||||
topicData.pinned = parseInt(topicData.pinned, 10) === 1;
|
|
||||||
|
|
||||||
topicData.icons = [];
|
|
||||||
|
|
||||||
plugins.fireHook('filter:topic.get', { topic: topicData, uid: uid }, next);
|
|
||||||
},
|
|
||||||
function (data, next) {
|
|
||||||
next(null, data.topic);
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
function getMainPostAndReplies(topic, set, uid, start, stop, reverse, callback) {
|
|
||||||
async.waterfall([
|
|
||||||
function (next) {
|
|
||||||
if (stop > 0) {
|
|
||||||
stop -= 1;
|
|
||||||
if (start > 0) {
|
|
||||||
start -= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
posts.getPidsFromSet(set, start, stop, reverse, next);
|
|
||||||
},
|
|
||||||
function (pids, next) {
|
|
||||||
if (!pids.length && !topic.mainPid) {
|
|
||||||
return callback(null, []);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (topic.mainPid && start === 0) {
|
|
||||||
pids.unshift(topic.mainPid);
|
|
||||||
}
|
|
||||||
posts.getPostsByPids(pids, uid, next);
|
|
||||||
},
|
|
||||||
function (posts, next) {
|
|
||||||
if (!posts.length) {
|
|
||||||
return next(null, []);
|
|
||||||
}
|
|
||||||
var replies = posts;
|
|
||||||
if (topic.mainPid && start === 0) {
|
|
||||||
posts[0].index = 0;
|
|
||||||
replies = posts.slice(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
Topics.calculatePostIndices(replies, start, stop, topic.postcount, reverse);
|
|
||||||
|
|
||||||
Topics.addPostData(posts, uid, next);
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDeleter(topicData, callback) {
|
|
||||||
if (!topicData.deleterUid) {
|
|
||||||
return setImmediate(callback, null, null);
|
|
||||||
}
|
|
||||||
user.getUserFields(topicData.deleterUid, ['username', 'userslug', 'picture'], callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
Topics.getMainPost = function (tid, uid, callback) {
|
|
||||||
Topics.getMainPosts([tid], uid, function (err, mainPosts) {
|
|
||||||
callback(err, Array.isArray(mainPosts) && mainPosts.length ? mainPosts[0] : null);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Topics.getMainPids = function (tids, callback) {
|
|
||||||
if (!Array.isArray(tids) || !tids.length) {
|
|
||||||
return callback(null, []);
|
|
||||||
}
|
|
||||||
|
|
||||||
Topics.getTopicsFields(tids, ['mainPid'], function (err, topicData) {
|
|
||||||
if (err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
topics = _topics;
|
||||||
|
uids = mapFilter(topics, 'uid');
|
||||||
|
cids = mapFilter(topics, 'cid');
|
||||||
|
|
||||||
|
async.parallel({
|
||||||
|
users: function (next) {
|
||||||
|
user.getUsersFields(uids, ['uid', 'username', 'fullname', 'userslug', 'reputation', 'postcount', 'picture', 'signature', 'banned', 'status'], next);
|
||||||
|
},
|
||||||
|
categories: function (next) {
|
||||||
|
categories.getCategoriesFields(cids, ['cid', 'name', 'slug', 'icon', 'image', 'bgColor', 'color', 'disabled'], next);
|
||||||
|
},
|
||||||
|
hasRead: function (next) {
|
||||||
|
Topics.hasReadTopics(tids, uid, next);
|
||||||
|
},
|
||||||
|
isIgnored: function (next) {
|
||||||
|
Topics.isIgnoring(tids, uid, next);
|
||||||
|
},
|
||||||
|
bookmarks: function (next) {
|
||||||
|
Topics.getUserBookmarks(tids, uid, next);
|
||||||
|
},
|
||||||
|
teasers: function (next) {
|
||||||
|
Topics.getTeasers(topics, uid, next);
|
||||||
|
},
|
||||||
|
tags: function (next) {
|
||||||
|
Topics.getTopicsTagsObjects(tids, next);
|
||||||
|
},
|
||||||
|
}, next);
|
||||||
|
},
|
||||||
|
function (results, next) {
|
||||||
|
var users = _.object(uids, results.users);
|
||||||
|
var categories = _.object(cids, results.categories);
|
||||||
|
|
||||||
|
for (var i = 0; i < topics.length; i += 1) {
|
||||||
|
if (topics[i]) {
|
||||||
|
topics[i].category = categories[topics[i].cid];
|
||||||
|
topics[i].user = users[topics[i].uid];
|
||||||
|
topics[i].teaser = results.teasers[i];
|
||||||
|
topics[i].tags = results.tags[i];
|
||||||
|
|
||||||
|
topics[i].isOwner = parseInt(topics[i].uid, 10) === parseInt(uid, 10);
|
||||||
|
topics[i].pinned = parseInt(topics[i].pinned, 10) === 1;
|
||||||
|
topics[i].locked = parseInt(topics[i].locked, 10) === 1;
|
||||||
|
topics[i].deleted = parseInt(topics[i].deleted, 10) === 1;
|
||||||
|
topics[i].ignored = results.isIgnored[i];
|
||||||
|
topics[i].unread = !results.hasRead[i] && !results.isIgnored[i];
|
||||||
|
topics[i].bookmark = results.bookmarks[i];
|
||||||
|
topics[i].unreplied = !topics[i].teaser;
|
||||||
|
|
||||||
|
topics[i].icons = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
topics = topics.filter(function (topic) {
|
||||||
|
return topic && topic.category && !topic.category.disabled;
|
||||||
|
});
|
||||||
|
|
||||||
|
plugins.fireHook('filter:topics.get', { topics: topics, uid: uid }, next);
|
||||||
|
},
|
||||||
|
function (data, next) {
|
||||||
|
next(null, data.topics);
|
||||||
|
},
|
||||||
|
], callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
Topics.getTopicWithPosts = function (topicData, set, uid, start, stop, reverse, callback) {
|
||||||
|
async.waterfall([
|
||||||
|
function (next) {
|
||||||
|
async.parallel({
|
||||||
|
posts: async.apply(getMainPostAndReplies, topicData, set, uid, start, stop, reverse),
|
||||||
|
category: async.apply(Topics.getCategoryData, topicData.tid),
|
||||||
|
threadTools: async.apply(plugins.fireHook, 'filter:topic.thread_tools', { topic: topicData, uid: uid, tools: [] }),
|
||||||
|
isFollowing: async.apply(Topics.isFollowing, [topicData.tid], uid),
|
||||||
|
isIgnoring: async.apply(Topics.isIgnoring, [topicData.tid], uid),
|
||||||
|
bookmark: async.apply(Topics.getUserBookmark, topicData.tid, uid),
|
||||||
|
postSharing: async.apply(social.getActivePostSharing),
|
||||||
|
deleter: async.apply(getDeleter, topicData),
|
||||||
|
related: function (next) {
|
||||||
|
async.waterfall([
|
||||||
|
function (next) {
|
||||||
|
Topics.getTopicTagsObjects(topicData.tid, next);
|
||||||
|
},
|
||||||
|
function (tags, next) {
|
||||||
|
topicData.tags = tags;
|
||||||
|
Topics.getRelatedTopics(topicData, uid, next);
|
||||||
|
},
|
||||||
|
], next);
|
||||||
|
},
|
||||||
|
}, next);
|
||||||
|
},
|
||||||
|
function (results, next) {
|
||||||
|
topicData.posts = results.posts;
|
||||||
|
topicData.category = results.category;
|
||||||
|
topicData.thread_tools = results.threadTools.tools;
|
||||||
|
topicData.isFollowing = results.isFollowing[0];
|
||||||
|
topicData.isNotFollowing = !results.isFollowing[0] && !results.isIgnoring[0];
|
||||||
|
topicData.isIgnoring = results.isIgnoring[0];
|
||||||
|
topicData.bookmark = results.bookmark;
|
||||||
|
topicData.postSharing = results.postSharing;
|
||||||
|
topicData.deleter = results.deleter;
|
||||||
|
topicData.deletedTimestampISO = utils.toISOString(topicData.deletedTimestamp);
|
||||||
|
topicData.related = results.related || [];
|
||||||
|
|
||||||
|
topicData.unreplied = parseInt(topicData.postcount, 10) === 1;
|
||||||
|
topicData.deleted = parseInt(topicData.deleted, 10) === 1;
|
||||||
|
topicData.locked = parseInt(topicData.locked, 10) === 1;
|
||||||
|
topicData.pinned = parseInt(topicData.pinned, 10) === 1;
|
||||||
|
|
||||||
|
topicData.icons = [];
|
||||||
|
|
||||||
|
plugins.fireHook('filter:topic.get', { topic: topicData, uid: uid }, next);
|
||||||
|
},
|
||||||
|
function (data, next) {
|
||||||
|
next(null, data.topic);
|
||||||
|
},
|
||||||
|
], callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
function getMainPostAndReplies(topic, set, uid, start, stop, reverse, callback) {
|
||||||
|
async.waterfall([
|
||||||
|
function (next) {
|
||||||
|
if (stop > 0) {
|
||||||
|
stop -= 1;
|
||||||
|
if (start > 0) {
|
||||||
|
start -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
posts.getPidsFromSet(set, start, stop, reverse, next);
|
||||||
|
},
|
||||||
|
function (pids, next) {
|
||||||
|
if (!pids.length && !topic.mainPid) {
|
||||||
|
return callback(null, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (topic.mainPid && start === 0) {
|
||||||
|
pids.unshift(topic.mainPid);
|
||||||
|
}
|
||||||
|
posts.getPostsByPids(pids, uid, next);
|
||||||
|
},
|
||||||
|
function (posts, next) {
|
||||||
|
if (!posts.length) {
|
||||||
|
return next(null, []);
|
||||||
|
}
|
||||||
|
var replies = posts;
|
||||||
|
if (topic.mainPid && start === 0) {
|
||||||
|
posts[0].index = 0;
|
||||||
|
replies = posts.slice(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Topics.calculatePostIndices(replies, start, stop, topic.postcount, reverse);
|
||||||
|
|
||||||
|
Topics.addPostData(posts, uid, next);
|
||||||
|
},
|
||||||
|
], callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDeleter(topicData, callback) {
|
||||||
|
if (!topicData.deleterUid) {
|
||||||
|
return setImmediate(callback, null, null);
|
||||||
|
}
|
||||||
|
user.getUserFields(topicData.deleterUid, ['username', 'userslug', 'picture'], callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
Topics.getMainPost = function (tid, uid, callback) {
|
||||||
|
Topics.getMainPosts([tid], uid, function (err, mainPosts) {
|
||||||
|
callback(err, Array.isArray(mainPosts) && mainPosts.length ? mainPosts[0] : null);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Topics.getMainPids = function (tids, callback) {
|
||||||
|
if (!Array.isArray(tids) || !tids.length) {
|
||||||
|
return callback(null, []);
|
||||||
|
}
|
||||||
|
async.waterfall([
|
||||||
|
function (next) {
|
||||||
|
Topics.getTopicsFields(tids, ['mainPid'], next);
|
||||||
|
},
|
||||||
|
function (topicData, next) {
|
||||||
var mainPids = topicData.map(function (topic) {
|
var mainPids = topicData.map(function (topic) {
|
||||||
return topic && topic.mainPid;
|
return topic && topic.mainPid;
|
||||||
});
|
});
|
||||||
callback(null, mainPids);
|
next(null, mainPids);
|
||||||
});
|
},
|
||||||
};
|
], callback);
|
||||||
|
};
|
||||||
|
|
||||||
Topics.getMainPosts = function (tids, uid, callback) {
|
Topics.getMainPosts = function (tids, uid, callback) {
|
||||||
Topics.getMainPids(tids, function (err, mainPids) {
|
async.waterfall([
|
||||||
if (err) {
|
function (next) {
|
||||||
return callback(err);
|
Topics.getMainPids(tids, next);
|
||||||
}
|
},
|
||||||
getMainPosts(mainPids, uid, callback);
|
function (mainPids, next) {
|
||||||
});
|
getMainPosts(mainPids, uid, next);
|
||||||
};
|
},
|
||||||
|
], callback);
|
||||||
|
};
|
||||||
|
|
||||||
function getMainPosts(mainPids, uid, callback) {
|
function getMainPosts(mainPids, uid, callback) {
|
||||||
posts.getPostsByPids(mainPids, uid, function (err, postData) {
|
async.waterfall([
|
||||||
if (err) {
|
function (next) {
|
||||||
return callback(err);
|
posts.getPostsByPids(mainPids, uid, next);
|
||||||
}
|
},
|
||||||
|
function (postData, next) {
|
||||||
postData.forEach(function (post) {
|
postData.forEach(function (post) {
|
||||||
if (post) {
|
if (post) {
|
||||||
post.index = 0;
|
post.index = 0;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Topics.addPostData(postData, uid, callback);
|
Topics.addPostData(postData, uid, next);
|
||||||
});
|
},
|
||||||
|
], callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
Topics.isLocked = function (tid, callback) {
|
||||||
|
Topics.getTopicField(tid, 'locked', function (err, locked) {
|
||||||
|
callback(err, parseInt(locked, 10) === 1);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Topics.search = function (tid, term, callback) {
|
||||||
|
if (plugins.hasListeners('filter:topic.search')) {
|
||||||
|
plugins.fireHook('filter:topic.search', {
|
||||||
|
tid: tid,
|
||||||
|
term: term,
|
||||||
|
}, callback);
|
||||||
|
} else {
|
||||||
|
callback(new Error('[[error:no-plugins-available]]'), []);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
Topics.isLocked = function (tid, callback) {
|
|
||||||
Topics.getTopicField(tid, 'locked', function (err, locked) {
|
|
||||||
callback(err, parseInt(locked, 10) === 1);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Topics.search = function (tid, term, callback) {
|
|
||||||
if (plugins.hasListeners('filter:topic.search')) {
|
|
||||||
plugins.fireHook('filter:topic.search', {
|
|
||||||
tid: tid,
|
|
||||||
term: term,
|
|
||||||
}, callback);
|
|
||||||
} else {
|
|
||||||
callback(new Error('[[error:no-plugins-available]]'), []);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}(exports));
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
var _ = require('underscore');
|
var _ = require('underscore');
|
||||||
|
|
||||||
var groups = require('./groups');
|
var groups = require('./groups');
|
||||||
|
|||||||
@@ -12,106 +12,106 @@ var db = require('../database');
|
|||||||
var meta = require('../meta');
|
var meta = require('../meta');
|
||||||
var emailer = require('../emailer');
|
var emailer = require('../emailer');
|
||||||
|
|
||||||
(function (UserEmail) {
|
var UserEmail = module.exports;
|
||||||
UserEmail.exists = function (email, callback) {
|
|
||||||
user.getUidByEmail(email.toLowerCase(), function (err, exists) {
|
|
||||||
callback(err, !!exists);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
UserEmail.available = function (email, callback) {
|
UserEmail.exists = function (email, callback) {
|
||||||
db.isSortedSetMember('email:uid', email.toLowerCase(), function (err, exists) {
|
user.getUidByEmail(email.toLowerCase(), function (err, exists) {
|
||||||
callback(err, !exists);
|
callback(err, !!exists);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
UserEmail.sendValidationEmail = function (uid, email, callback) {
|
UserEmail.available = function (email, callback) {
|
||||||
callback = callback || function () {};
|
db.isSortedSetMember('email:uid', email.toLowerCase(), function (err, exists) {
|
||||||
var confirm_code = utils.generateUUID();
|
callback(err, !exists);
|
||||||
var confirm_link = nconf.get('url') + '/confirm/' + confirm_code;
|
});
|
||||||
|
};
|
||||||
|
|
||||||
var emailInterval = meta.config.hasOwnProperty('emailConfirmInterval') ? parseInt(meta.config.emailConfirmInterval, 10) : 10;
|
UserEmail.sendValidationEmail = function (uid, email, callback) {
|
||||||
|
callback = callback || function () {};
|
||||||
|
var confirm_code = utils.generateUUID();
|
||||||
|
var confirm_link = nconf.get('url') + '/confirm/' + confirm_code;
|
||||||
|
|
||||||
async.waterfall([
|
var emailInterval = meta.config.hasOwnProperty('emailConfirmInterval') ? parseInt(meta.config.emailConfirmInterval, 10) : 10;
|
||||||
function (next) {
|
|
||||||
db.get('uid:' + uid + ':confirm:email:sent', next);
|
async.waterfall([
|
||||||
},
|
function (next) {
|
||||||
function (sent, next) {
|
db.get('uid:' + uid + ':confirm:email:sent', next);
|
||||||
if (sent) {
|
},
|
||||||
return next(new Error('[[error:confirm-email-already-sent, ' + emailInterval + ']]'));
|
function (sent, next) {
|
||||||
}
|
if (sent) {
|
||||||
db.set('uid:' + uid + ':confirm:email:sent', 1, next);
|
return next(new Error('[[error:confirm-email-already-sent, ' + emailInterval + ']]'));
|
||||||
},
|
}
|
||||||
function (next) {
|
db.set('uid:' + uid + ':confirm:email:sent', 1, next);
|
||||||
db.pexpireAt('uid:' + uid + ':confirm:email:sent', Date.now() + (emailInterval * 60 * 1000), next);
|
},
|
||||||
},
|
function (next) {
|
||||||
function (next) {
|
db.pexpireAt('uid:' + uid + ':confirm:email:sent', Date.now() + (emailInterval * 60 * 1000), next);
|
||||||
plugins.fireHook('filter:user.verify.code', confirm_code, next);
|
},
|
||||||
},
|
function (next) {
|
||||||
function (_confirm_code, next) {
|
plugins.fireHook('filter:user.verify.code', confirm_code, next);
|
||||||
confirm_code = _confirm_code;
|
},
|
||||||
db.setObject('confirm:' + confirm_code, {
|
function (_confirm_code, next) {
|
||||||
email: email.toLowerCase(),
|
confirm_code = _confirm_code;
|
||||||
|
db.setObject('confirm:' + confirm_code, {
|
||||||
|
email: email.toLowerCase(),
|
||||||
|
uid: uid,
|
||||||
|
}, next);
|
||||||
|
},
|
||||||
|
function (next) {
|
||||||
|
db.expireAt('confirm:' + confirm_code, Math.floor((Date.now() / 1000) + (60 * 60 * 24)), next);
|
||||||
|
},
|
||||||
|
function (next) {
|
||||||
|
user.getUserField(uid, 'username', next);
|
||||||
|
},
|
||||||
|
function (username, next) {
|
||||||
|
var title = meta.config.title || meta.config.browserTitle || 'NodeBB';
|
||||||
|
translator.translate('[[email:welcome-to, ' + title + ']]', meta.config.defaultLang, function (subject) {
|
||||||
|
var data = {
|
||||||
|
site_title: title,
|
||||||
|
username: username,
|
||||||
|
confirm_link: confirm_link,
|
||||||
|
confirm_code: confirm_code,
|
||||||
|
|
||||||
|
subject: subject,
|
||||||
|
template: 'welcome',
|
||||||
uid: uid,
|
uid: uid,
|
||||||
}, next);
|
};
|
||||||
},
|
|
||||||
function (next) {
|
|
||||||
db.expireAt('confirm:' + confirm_code, Math.floor((Date.now() / 1000) + (60 * 60 * 24)), next);
|
|
||||||
},
|
|
||||||
function (next) {
|
|
||||||
user.getUserField(uid, 'username', next);
|
|
||||||
},
|
|
||||||
function (username, next) {
|
|
||||||
var title = meta.config.title || meta.config.browserTitle || 'NodeBB';
|
|
||||||
translator.translate('[[email:welcome-to, ' + title + ']]', meta.config.defaultLang, function (subject) {
|
|
||||||
var data = {
|
|
||||||
site_title: title,
|
|
||||||
username: username,
|
|
||||||
confirm_link: confirm_link,
|
|
||||||
confirm_code: confirm_code,
|
|
||||||
|
|
||||||
subject: subject,
|
if (plugins.hasListeners('action:user.verify')) {
|
||||||
template: 'welcome',
|
plugins.fireHook('action:user.verify', { uid: uid, data: data });
|
||||||
uid: uid,
|
next();
|
||||||
};
|
} else {
|
||||||
|
emailer.send('welcome', uid, data, next);
|
||||||
if (plugins.hasListeners('action:user.verify')) {
|
|
||||||
plugins.fireHook('action:user.verify', { uid: uid, data: data });
|
|
||||||
next();
|
|
||||||
} else {
|
|
||||||
emailer.send('welcome', uid, data, next);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
function (next) {
|
|
||||||
next(null, confirm_code);
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
UserEmail.confirm = function (code, callback) {
|
|
||||||
async.waterfall([
|
|
||||||
function (next) {
|
|
||||||
db.getObject('confirm:' + code, next);
|
|
||||||
},
|
|
||||||
function (confirmObj, next) {
|
|
||||||
if (!confirmObj || !confirmObj.uid || !confirmObj.email) {
|
|
||||||
return next(new Error('[[error:invalid-data]]'));
|
|
||||||
}
|
}
|
||||||
async.series([
|
});
|
||||||
async.apply(user.setUserField, confirmObj.uid, 'email:confirmed', 1),
|
},
|
||||||
async.apply(db.delete, 'confirm:' + code),
|
function (next) {
|
||||||
async.apply(db.delete, 'uid:' + confirmObj.uid + ':confirm:email:sent'),
|
next(null, confirm_code);
|
||||||
function (next) {
|
},
|
||||||
db.sortedSetRemove('users:notvalidated', confirmObj.uid, next);
|
], callback);
|
||||||
},
|
};
|
||||||
function (next) {
|
|
||||||
plugins.fireHook('action:user.email.confirmed', { uid: confirmObj.uid, email: confirmObj.email }, next);
|
UserEmail.confirm = function (code, callback) {
|
||||||
},
|
async.waterfall([
|
||||||
], next);
|
function (next) {
|
||||||
},
|
db.getObject('confirm:' + code, next);
|
||||||
], function (err) {
|
},
|
||||||
callback(err);
|
function (confirmObj, next) {
|
||||||
});
|
if (!confirmObj || !confirmObj.uid || !confirmObj.email) {
|
||||||
};
|
return next(new Error('[[error:invalid-data]]'));
|
||||||
}(exports));
|
}
|
||||||
|
async.series([
|
||||||
|
async.apply(user.setUserField, confirmObj.uid, 'email:confirmed', 1),
|
||||||
|
async.apply(db.delete, 'confirm:' + code),
|
||||||
|
async.apply(db.delete, 'uid:' + confirmObj.uid + ':confirm:email:sent'),
|
||||||
|
function (next) {
|
||||||
|
db.sortedSetRemove('users:notvalidated', confirmObj.uid, next);
|
||||||
|
},
|
||||||
|
function (next) {
|
||||||
|
plugins.fireHook('action:user.email.confirmed', { uid: confirmObj.uid, email: confirmObj.email }, next);
|
||||||
|
},
|
||||||
|
], next);
|
||||||
|
},
|
||||||
|
], function (err) {
|
||||||
|
callback(err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -12,157 +12,157 @@ var db = require('../database');
|
|||||||
var meta = require('../meta');
|
var meta = require('../meta');
|
||||||
var emailer = require('../emailer');
|
var emailer = require('../emailer');
|
||||||
|
|
||||||
(function (UserReset) {
|
var UserReset = module.exports;
|
||||||
var twoHours = 7200000;
|
|
||||||
|
|
||||||
UserReset.validate = function (code, callback) {
|
var twoHours = 7200000;
|
||||||
async.waterfall([
|
|
||||||
function (next) {
|
|
||||||
db.getObjectField('reset:uid', code, next);
|
|
||||||
},
|
|
||||||
function (uid, next) {
|
|
||||||
if (!uid) {
|
|
||||||
return callback(null, false);
|
|
||||||
}
|
|
||||||
db.sortedSetScore('reset:issueDate', code, next);
|
|
||||||
},
|
|
||||||
function (issueDate, next) {
|
|
||||||
next(null, parseInt(issueDate, 10) > Date.now() - twoHours);
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
UserReset.generate = function (uid, callback) {
|
UserReset.validate = function (code, callback) {
|
||||||
var code = utils.generateUUID();
|
async.waterfall([
|
||||||
async.parallel([
|
function (next) {
|
||||||
async.apply(db.setObjectField, 'reset:uid', code, uid),
|
db.getObjectField('reset:uid', code, next);
|
||||||
async.apply(db.sortedSetAdd, 'reset:issueDate', Date.now(), code),
|
},
|
||||||
], function (err) {
|
function (uid, next) {
|
||||||
callback(err, code);
|
if (!uid) {
|
||||||
});
|
return callback(null, false);
|
||||||
};
|
}
|
||||||
|
db.sortedSetScore('reset:issueDate', code, next);
|
||||||
|
},
|
||||||
|
function (issueDate, next) {
|
||||||
|
next(null, parseInt(issueDate, 10) > Date.now() - twoHours);
|
||||||
|
},
|
||||||
|
], callback);
|
||||||
|
};
|
||||||
|
|
||||||
function canGenerate(uid, callback) {
|
UserReset.generate = function (uid, callback) {
|
||||||
async.waterfall([
|
var code = utils.generateUUID();
|
||||||
function (next) {
|
async.parallel([
|
||||||
db.sortedSetScore('reset:issueDate:uid', uid, next);
|
async.apply(db.setObjectField, 'reset:uid', code, uid),
|
||||||
},
|
async.apply(db.sortedSetAdd, 'reset:issueDate', Date.now(), code),
|
||||||
function (score, next) {
|
], function (err) {
|
||||||
if (score > Date.now() - (1000 * 60)) {
|
callback(err, code);
|
||||||
return next(new Error('[[error:cant-reset-password-more-than-once-a-minute]]'));
|
});
|
||||||
}
|
};
|
||||||
next();
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
UserReset.send = function (email, callback) {
|
function canGenerate(uid, callback) {
|
||||||
var uid;
|
async.waterfall([
|
||||||
async.waterfall([
|
function (next) {
|
||||||
function (next) {
|
db.sortedSetScore('reset:issueDate:uid', uid, next);
|
||||||
user.getUidByEmail(email, next);
|
},
|
||||||
},
|
function (score, next) {
|
||||||
function (_uid, next) {
|
if (score > Date.now() - (1000 * 60)) {
|
||||||
if (!_uid) {
|
return next(new Error('[[error:cant-reset-password-more-than-once-a-minute]]'));
|
||||||
return next(new Error('[[error:invalid-email]]'));
|
}
|
||||||
}
|
next();
|
||||||
|
},
|
||||||
|
], callback);
|
||||||
|
}
|
||||||
|
|
||||||
uid = _uid;
|
UserReset.send = function (email, callback) {
|
||||||
canGenerate(uid, next);
|
var uid;
|
||||||
},
|
async.waterfall([
|
||||||
function (next) {
|
function (next) {
|
||||||
db.sortedSetAdd('reset:issueDate:uid', Date.now(), uid, next);
|
user.getUidByEmail(email, next);
|
||||||
},
|
},
|
||||||
function (next) {
|
function (_uid, next) {
|
||||||
UserReset.generate(uid, next);
|
if (!_uid) {
|
||||||
},
|
return next(new Error('[[error:invalid-email]]'));
|
||||||
function (code, next) {
|
}
|
||||||
translator.translate('[[email:password-reset-requested, ' + (meta.config.title || 'NodeBB') + ']]', meta.config.defaultLang, function (subject) {
|
|
||||||
next(null, subject, code);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
function (subject, code, next) {
|
|
||||||
var reset_link = nconf.get('url') + '/reset/' + code;
|
|
||||||
emailer.send('reset', uid, {
|
|
||||||
site_title: (meta.config.title || 'NodeBB'),
|
|
||||||
reset_link: reset_link,
|
|
||||||
subject: subject,
|
|
||||||
template: 'reset',
|
|
||||||
uid: uid,
|
|
||||||
}, next);
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
UserReset.commit = function (code, password, callback) {
|
uid = _uid;
|
||||||
var uid;
|
canGenerate(uid, next);
|
||||||
async.waterfall([
|
},
|
||||||
function (next) {
|
function (next) {
|
||||||
user.isPasswordValid(password, next);
|
db.sortedSetAdd('reset:issueDate:uid', Date.now(), uid, next);
|
||||||
},
|
},
|
||||||
function (next) {
|
function (next) {
|
||||||
UserReset.validate(code, next);
|
UserReset.generate(uid, next);
|
||||||
},
|
},
|
||||||
function (validated, next) {
|
function (code, next) {
|
||||||
if (!validated) {
|
translator.translate('[[email:password-reset-requested, ' + (meta.config.title || 'NodeBB') + ']]', meta.config.defaultLang, function (subject) {
|
||||||
return next(new Error('[[error:reset-code-not-valid]]'));
|
next(null, subject, code);
|
||||||
}
|
});
|
||||||
db.getObjectField('reset:uid', code, next);
|
},
|
||||||
},
|
function (subject, code, next) {
|
||||||
function (_uid, next) {
|
var reset_link = nconf.get('url') + '/reset/' + code;
|
||||||
uid = _uid;
|
emailer.send('reset', uid, {
|
||||||
if (!uid) {
|
site_title: (meta.config.title || 'NodeBB'),
|
||||||
return next(new Error('[[error:reset-code-not-valid]]'));
|
reset_link: reset_link,
|
||||||
}
|
subject: subject,
|
||||||
|
template: 'reset',
|
||||||
|
uid: uid,
|
||||||
|
}, next);
|
||||||
|
},
|
||||||
|
], callback);
|
||||||
|
};
|
||||||
|
|
||||||
user.hashPassword(password, next);
|
UserReset.commit = function (code, password, callback) {
|
||||||
},
|
var uid;
|
||||||
function (hash, next) {
|
async.waterfall([
|
||||||
async.parallel([
|
function (next) {
|
||||||
async.apply(user.setUserField, uid, 'password', hash),
|
user.isPasswordValid(password, next);
|
||||||
async.apply(db.deleteObjectField, 'reset:uid', code),
|
},
|
||||||
async.apply(db.sortedSetRemove, 'reset:issueDate', code),
|
function (next) {
|
||||||
async.apply(db.sortedSetRemove, 'reset:issueDate:uid', uid),
|
UserReset.validate(code, next);
|
||||||
async.apply(user.reset.updateExpiry, uid),
|
},
|
||||||
async.apply(user.auth.resetLockout, uid),
|
function (validated, next) {
|
||||||
], next);
|
if (!validated) {
|
||||||
},
|
return next(new Error('[[error:reset-code-not-valid]]'));
|
||||||
], callback);
|
}
|
||||||
};
|
db.getObjectField('reset:uid', code, next);
|
||||||
|
},
|
||||||
|
function (_uid, next) {
|
||||||
|
uid = _uid;
|
||||||
|
if (!uid) {
|
||||||
|
return next(new Error('[[error:reset-code-not-valid]]'));
|
||||||
|
}
|
||||||
|
|
||||||
UserReset.updateExpiry = function (uid, callback) {
|
user.hashPassword(password, next);
|
||||||
var oneDay = 1000 * 60 * 60 * 24;
|
},
|
||||||
var expireDays = parseInt(meta.config.passwordExpiryDays || 0, 10);
|
function (hash, next) {
|
||||||
var expiry = Date.now() + (oneDay * expireDays);
|
async.parallel([
|
||||||
|
async.apply(user.setUserField, uid, 'password', hash),
|
||||||
|
async.apply(db.deleteObjectField, 'reset:uid', code),
|
||||||
|
async.apply(db.sortedSetRemove, 'reset:issueDate', code),
|
||||||
|
async.apply(db.sortedSetRemove, 'reset:issueDate:uid', uid),
|
||||||
|
async.apply(user.reset.updateExpiry, uid),
|
||||||
|
async.apply(user.auth.resetLockout, uid),
|
||||||
|
], next);
|
||||||
|
},
|
||||||
|
], callback);
|
||||||
|
};
|
||||||
|
|
||||||
callback = callback || function () {};
|
UserReset.updateExpiry = function (uid, callback) {
|
||||||
user.setUserField(uid, 'passwordExpiry', expireDays > 0 ? expiry : 0, callback);
|
var oneDay = 1000 * 60 * 60 * 24;
|
||||||
};
|
var expireDays = parseInt(meta.config.passwordExpiryDays || 0, 10);
|
||||||
|
var expiry = Date.now() + (oneDay * expireDays);
|
||||||
|
|
||||||
UserReset.clean = function (callback) {
|
callback = callback || function () {};
|
||||||
async.waterfall([
|
user.setUserField(uid, 'passwordExpiry', expireDays > 0 ? expiry : 0, callback);
|
||||||
function (next) {
|
};
|
||||||
async.parallel({
|
|
||||||
tokens: function (next) {
|
|
||||||
db.getSortedSetRangeByScore('reset:issueDate', 0, -1, '-inf', Date.now() - twoHours, next);
|
|
||||||
},
|
|
||||||
uids: function (next) {
|
|
||||||
db.getSortedSetRangeByScore('reset:issueDate:uid', 0, -1, '-inf', Date.now() - twoHours, next);
|
|
||||||
},
|
|
||||||
}, next);
|
|
||||||
},
|
|
||||||
function (results, next) {
|
|
||||||
if (!results.tokens.length && !results.uids.length) {
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
|
|
||||||
winston.verbose('[UserReset.clean] Removing ' + results.tokens.length + ' reset tokens from database');
|
UserReset.clean = function (callback) {
|
||||||
async.parallel([
|
async.waterfall([
|
||||||
async.apply(db.deleteObjectFields, 'reset:uid', results.tokens),
|
function (next) {
|
||||||
async.apply(db.sortedSetRemove, 'reset:issueDate', results.tokens),
|
async.parallel({
|
||||||
async.apply(db.sortedSetRemove, 'reset:issueDate:uid', results.uids),
|
tokens: function (next) {
|
||||||
], next);
|
db.getSortedSetRangeByScore('reset:issueDate', 0, -1, '-inf', Date.now() - twoHours, next);
|
||||||
},
|
},
|
||||||
], callback);
|
uids: function (next) {
|
||||||
};
|
db.getSortedSetRangeByScore('reset:issueDate:uid', 0, -1, '-inf', Date.now() - twoHours, next);
|
||||||
}(exports));
|
},
|
||||||
|
}, next);
|
||||||
|
},
|
||||||
|
function (results, next) {
|
||||||
|
if (!results.tokens.length && !results.uids.length) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
winston.verbose('[UserReset.clean] Removing ' + results.tokens.length + ' reset tokens from database');
|
||||||
|
async.parallel([
|
||||||
|
async.apply(db.deleteObjectFields, 'reset:uid', results.tokens),
|
||||||
|
async.apply(db.sortedSetRemove, 'reset:issueDate', results.tokens),
|
||||||
|
async.apply(db.sortedSetRemove, 'reset:issueDate:uid', results.uids),
|
||||||
|
], next);
|
||||||
|
},
|
||||||
|
], callback);
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user