style changes

This commit is contained in:
Barış Soner Uşaklı
2017-05-27 01:44:26 -04:00
parent bcc566fb68
commit 890c2eff70
16 changed files with 908 additions and 907 deletions

View File

@@ -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));

View File

@@ -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');

View File

@@ -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'));

View File

@@ -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();

View File

@@ -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) {

View File

@@ -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];

View File

@@ -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);
}

View File

@@ -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);
};

View File

@@ -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({

View File

@@ -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;

View File

@@ -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();

View File

@@ -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.

View File

@@ -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));

View File

@@ -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');

View File

@@ -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);
});
};

View File

@@ -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);
};