');
- }
- });
- };
-
Posts.showBottomPostBar = function () {
var mainPost = components.get('post', 'index', 0);
var placeHolder = $('.post-bar-placeholder');
diff --git a/public/src/modules/helpers.js b/public/src/modules/helpers.js
index 7e93618cc7..3f02a86758 100644
--- a/public/src/modules/helpers.js
+++ b/public/src/modules/helpers.js
@@ -16,9 +16,9 @@
return false;
}
var properties = item.properties;
-
+ var loggedIn = data.config ? data.config.loggedIn : false;
if (properties) {
- if ((properties.loggedIn && !data.config.loggedIn) ||
+ if ((properties.loggedIn && !loggedIn) ||
(properties.globalMod && !data.isGlobalMod && !data.isAdmin) ||
(properties.adminOnly && !data.isAdmin) ||
(properties.searchInstalled && !data.searchEnabled)) {
@@ -26,11 +26,11 @@
}
}
- if (item.route.match('/users') && data.privateUserInfo && !data.config.loggedIn) {
+ if (item.route.match('/users') && data.privateUserInfo && !loggedIn) {
return false;
}
- if (item.route.match('/tags') && data.privateTagListing && !data.config.loggedIn) {
+ if (item.route.match('/tags') && data.privateTagListing && !loggedIn) {
return false;
}
diff --git a/src/controllers/404.js b/src/controllers/404.js
new file mode 100644
index 0000000000..bc4e2e1d00
--- /dev/null
+++ b/src/controllers/404.js
@@ -0,0 +1,47 @@
+'use strict';
+
+var nconf = require('nconf');
+var winston = require('winston');
+var validator = require('validator');
+
+var meta = require('../meta');
+var plugins = require('../plugins');
+
+exports.handle404 = function (req, res) {
+ var relativePath = nconf.get('relative_path');
+ var isClientScript = new RegExp('^' + relativePath + '\\/assets\\/src\\/.+\\.js');
+
+ if (plugins.hasListeners('action:meta.override404')) {
+ return plugins.fireHook('action:meta.override404', {
+ req: req,
+ res: res,
+ error: {},
+ });
+ }
+
+ if (isClientScript.test(req.url)) {
+ res.type('text/javascript').status(200).send('');
+ } else if (req.path.startsWith(relativePath + '/assets/uploads') || (req.get('accept') && req.get('accept').indexOf('text/html') === -1) || req.path === '/favicon.ico') {
+ meta.errors.log404(req.path || '');
+ res.sendStatus(404);
+ } else if (req.accepts('html')) {
+ if (process.env.NODE_ENV === 'development') {
+ winston.warn('Route requested but not found: ' + req.url);
+ }
+
+ meta.errors.log404(req.path.replace(/^\/api/, '') || '');
+ res.status(404);
+
+ var path = String(req.path || '');
+
+ if (res.locals.isAPI) {
+ return res.json({ path: validator.escape(path.replace(/^\/api/, '')), title: '[[global:404.title]]' });
+ }
+ var middleware = require('../middleware');
+ middleware.buildHeader(req, res, function () {
+ res.render('404', { path: validator.escape(path), title: '[[global:404.title]]' });
+ });
+ } else {
+ res.status(404).type('txt').send('Not found');
+ }
+};
diff --git a/src/controllers/api.js b/src/controllers/api.js
index 66392e6160..cfa78a6908 100644
--- a/src/controllers/api.js
+++ b/src/controllers/api.js
@@ -13,9 +13,8 @@ var privileges = require('../privileges');
var plugins = require('../plugins');
var widgets = require('../widgets');
var translator = require('../../public/src/modules/translator');
-var accountHelpers = require('../controllers/accounts/helpers');
-var apiController = {};
+var apiController = module.exports;
apiController.getConfig = function (req, res, next) {
var config = {};
@@ -62,7 +61,7 @@ apiController.getConfig = function (req, res, next) {
config.categoryTopicSort = meta.config.categoryTopicSort || 'newest_to_oldest';
config.csrf_token = req.csrfToken();
config.searchEnabled = plugins.hasListeners('filter:search.query');
- config.bootswatchSkin = 'default';
+ config.bootswatchSkin = meta.config.bootswatchSkin || 'default';
var timeagoCutoff = meta.config.timeagoCutoff === undefined ? 30 : meta.config.timeagoCutoff;
config.timeagoCutoff = timeagoCutoff !== '' ? Math.max(0, parseInt(timeagoCutoff, 10)) : timeagoCutoff;
@@ -220,92 +219,6 @@ apiController.getObject = function (req, res, next) {
});
};
-apiController.getCurrentUser = function (req, res, next) {
- if (!req.uid) {
- return res.status(401).json('not-authorized');
- }
- async.waterfall([
- function (next) {
- user.getUserField(req.uid, 'userslug', next);
- },
- function (userslug, next) {
- accountHelpers.getUserDataByUserSlug(userslug, req.uid, next);
- },
- ], function (err, userData) {
- if (err) {
- return next(err);
- }
- res.json(userData);
- });
-};
-
-apiController.getUserByUID = function (req, res, next) {
- byType('uid', req, res, next);
-};
-
-apiController.getUserByUsername = function (req, res, next) {
- byType('username', req, res, next);
-};
-
-apiController.getUserByEmail = function (req, res, next) {
- byType('email', req, res, next);
-};
-
-function byType(type, req, res, next) {
- apiController.getUserDataByField(req.uid, type, req.params[type], function (err, data) {
- if (err || !data) {
- return next(err);
- }
- res.json(data);
- });
-}
-
-apiController.getUserDataByField = function (callerUid, field, fieldValue, callback) {
- async.waterfall([
- function (next) {
- if (field === 'uid') {
- next(null, fieldValue);
- } else if (field === 'username') {
- user.getUidByUsername(fieldValue, next);
- } else if (field === 'email') {
- user.getUidByEmail(fieldValue, next);
- } else {
- next();
- }
- },
- function (uid, next) {
- if (!uid) {
- return next();
- }
- apiController.getUserDataByUID(callerUid, uid, next);
- },
- ], callback);
-};
-
-apiController.getUserDataByUID = function (callerUid, uid, callback) {
- if (!parseInt(callerUid, 10) && parseInt(meta.config.privateUserInfo, 10) === 1) {
- return callback(new Error('[[error:no-privileges]]'));
- }
-
- if (!parseInt(uid, 10)) {
- return callback(new Error('[[error:no-user]]'));
- }
-
- async.parallel({
- userData: async.apply(user.getUserData, uid),
- settings: async.apply(user.getSettings, uid),
- }, function (err, results) {
- if (err || !results.userData) {
- return callback(err || new Error('[[error:no-user]]'));
- }
-
- results.userData.email = results.settings.showemail ? results.userData.email : undefined;
- results.userData.fullname = results.settings.showfullname ? results.userData.fullname : undefined;
-
- callback(null, results.userData);
- });
-};
-
apiController.getModerators = function (req, res, next) {
categories.getModerators(req.params.cid, function (err, moderators) {
if (err) {
@@ -314,16 +227,3 @@ apiController.getModerators = function (req, res, next) {
res.json({ moderators: moderators });
});
};
-
-
-apiController.getRecentPosts = function (req, res, next) {
- posts.getRecentPosts(req.uid, 0, 19, req.params.term, function (err, data) {
- if (err) {
- return next(err);
- }
-
- res.json(data);
- });
-};
-
-module.exports = apiController;
diff --git a/src/controllers/categories.js b/src/controllers/categories.js
index d5cd04b504..e02f107b4a 100644
--- a/src/controllers/categories.js
+++ b/src/controllers/categories.js
@@ -13,9 +13,6 @@ categoriesController.list = function (req, res, next) {
res.locals.metaTags = [{
name: 'title',
content: String(meta.config.title || 'NodeBB'),
- }, {
- name: 'description',
- content: String(meta.config.description || ''),
}, {
property: 'og:title',
content: '[[pages:categories]]',
diff --git a/src/controllers/errors.js b/src/controllers/errors.js
new file mode 100644
index 0000000000..6ab0dc6471
--- /dev/null
+++ b/src/controllers/errors.js
@@ -0,0 +1,63 @@
+'use strict';
+
+var nconf = require('nconf');
+var winston = require('winston');
+var validator = require('validator');
+
+exports.handleURIErrors = function (err, req, res, next) {
+ // Handle cases where malformed URIs are passed in
+ if (err instanceof URIError) {
+ var tidMatch = req.path.match(/^\/topic\/(\d+)\//);
+ var cidMatch = req.path.match(/^\/category\/(\d+)\//);
+
+ if (tidMatch) {
+ res.redirect(nconf.get('relative_path') + tidMatch[0]);
+ } else if (cidMatch) {
+ res.redirect(nconf.get('relative_path') + cidMatch[0]);
+ } else {
+ winston.warn('[controller] Bad request: ' + req.path);
+ if (res.locals.isAPI) {
+ res.status(400).json({
+ error: '[[global:400.title]]',
+ });
+ } else {
+ var middleware = require('../middleware');
+ middleware.buildHeader(req, res, function () {
+ res.render('400', { error: validator.escape(String(err.message)) });
+ });
+ }
+ }
+ } else {
+ next(err);
+ }
+};
+
+// this needs to have four arguments or express treats it as `(req, res, next)`
+// don't remove `next`!
+exports.handleErrors = function (err, req, res, next) { // eslint-disable-line no-unused-vars
+ switch (err.code) {
+ case 'EBADCSRFTOKEN':
+ winston.error(req.path + '\n', err.message);
+ return res.sendStatus(403);
+ case 'blacklisted-ip':
+ return res.status(403).type('text/plain').send(err.message);
+ }
+
+ if (parseInt(err.status, 10) === 302 && err.path) {
+ return res.locals.isAPI ? res.status(302).json(err.path) : res.redirect(err.path);
+ }
+
+ winston.error(req.path + '\n', err.stack);
+
+ res.status(err.status || 500);
+
+ var path = String(req.path || '');
+ if (res.locals.isAPI) {
+ res.json({ path: validator.escape(path), error: err.message });
+ } else {
+ var middleware = require('../middleware');
+ middleware.buildHeader(req, res, function () {
+ res.render('500', { path: validator.escape(path), error: validator.escape(String(err.message)) });
+ });
+ }
+};
diff --git a/src/controllers/index.js b/src/controllers/index.js
index 59f5527ef4..49c1c4c2c2 100644
--- a/src/controllers/index.js
+++ b/src/controllers/index.js
@@ -3,34 +3,35 @@
var async = require('async');
var nconf = require('nconf');
var validator = require('validator');
-var winston = require('winston');
var meta = require('../meta');
var user = require('../user');
var plugins = require('../plugins');
var helpers = require('./helpers');
-var Controllers = {
- topics: require('./topics'),
- posts: require('./posts'),
- categories: require('./categories'),
- category: require('./category'),
- unread: require('./unread'),
- recent: require('./recent'),
- popular: require('./popular'),
- tags: require('./tags'),
- search: require('./search'),
- users: require('./users'),
- groups: require('./groups'),
- accounts: require('./accounts'),
- authentication: require('./authentication'),
- api: require('./api'),
- admin: require('./admin'),
- globalMods: require('./globalmods'),
- mods: require('./mods'),
- sitemap: require('./sitemap'),
-};
-
+var Controllers = module.exports;
+
+Controllers.topics = require('./topics');
+Controllers.posts = require('./posts');
+Controllers.categories = require('./categories');
+Controllers.category = require('./category');
+Controllers.unread = require('./unread');
+Controllers.recent = require('./recent');
+Controllers.popular = require('./popular');
+Controllers.tags = require('./tags');
+Controllers.search = require('./search');
+Controllers.user = require('./user');
+Controllers.users = require('./users');
+Controllers.groups = require('./groups');
+Controllers.accounts = require('./accounts');
+Controllers.authentication = require('./authentication');
+Controllers.api = require('./api');
+Controllers.admin = require('./admin');
+Controllers.globalMods = require('./globalmods');
+Controllers.mods = require('./mods');
+Controllers.sitemap = require('./sitemap');
+Controllers['404'] = require('./404');
+Controllers.errors = require('./errors');
Controllers.home = function (req, res, next) {
var route = meta.config.homePageRoute || (meta.config.homePageCustom || '').replace(/^\/+/, '') || 'categories';
@@ -321,19 +322,18 @@ Controllers.manifest = function (req, res) {
res.status(200).json(manifest);
};
-Controllers.outgoing = function (req, res) {
+Controllers.outgoing = function (req, res, next) {
var url = req.query.url || '';
- var data = {
+
+ if (!url) {
+ return next();
+ }
+
+ res.render('outgoing', {
outgoing: validator.escape(String(url)),
title: meta.config.title,
breadcrumbs: helpers.buildBreadcrumbs([{ text: '[[notifications:outgoing_link]]' }]),
- };
-
- if (url) {
- res.render('outgoing', data);
- } else {
- res.status(404).redirect(nconf.get('relative_path') + '/404');
- }
+ });
};
Controllers.termsOfUse = function (req, res, next) {
@@ -346,102 +346,3 @@ Controllers.termsOfUse = function (req, res, next) {
Controllers.ping = function (req, res) {
res.status(200).send(req.path === '/sping' ? 'healthy' : '200');
};
-
-Controllers.handle404 = function (req, res) {
- var relativePath = nconf.get('relative_path');
- var isClientScript = new RegExp('^' + relativePath + '\\/assets\\/src\\/.+\\.js');
-
- if (plugins.hasListeners('action:meta.override404')) {
- return plugins.fireHook('action:meta.override404', {
- req: req,
- res: res,
- error: {},
- });
- }
-
- if (isClientScript.test(req.url)) {
- res.type('text/javascript').status(200).send('');
- } else if (req.path.startsWith(relativePath + '/assets/uploads') || (req.get('accept') && req.get('accept').indexOf('text/html') === -1) || req.path === '/favicon.ico') {
- meta.errors.log404(req.path || '');
- res.sendStatus(404);
- } else if (req.accepts('html')) {
- if (process.env.NODE_ENV === 'development') {
- winston.warn('Route requested but not found: ' + req.url);
- }
-
- meta.errors.log404(req.path.replace(/^\/api/, '') || '');
- res.status(404);
-
- var path = String(req.path || '');
-
- if (res.locals.isAPI) {
- return res.json({ path: validator.escape(path.replace(/^\/api/, '')), title: '[[global:404.title]]' });
- }
- var middleware = require('../middleware');
- middleware.buildHeader(req, res, function () {
- res.render('404', { path: validator.escape(path), title: '[[global:404.title]]' });
- });
- } else {
- res.status(404).type('txt').send('Not found');
- }
-};
-
-Controllers.handleURIErrors = function (err, req, res, next) {
- // Handle cases where malformed URIs are passed in
- if (err instanceof URIError) {
- var tidMatch = req.path.match(/^\/topic\/(\d+)\//);
- var cidMatch = req.path.match(/^\/category\/(\d+)\//);
-
- if (tidMatch) {
- res.redirect(nconf.get('relative_path') + tidMatch[0]);
- } else if (cidMatch) {
- res.redirect(nconf.get('relative_path') + cidMatch[0]);
- } else {
- winston.warn('[controller] Bad request: ' + req.path);
- if (res.locals.isAPI) {
- res.status(400).json({
- error: '[[global:400.title]]',
- });
- } else {
- var middleware = require('../middleware');
- middleware.buildHeader(req, res, function () {
- res.render('400', { error: validator.escape(String(err.message)) });
- });
- }
- }
- } else {
- next(err);
- }
-};
-
-// this needs to have four arguments or express treats it as `(req, res, next)`
-// don't remove `next`!
-Controllers.handleErrors = function (err, req, res, next) { // eslint-disable-line no-unused-vars
- switch (err.code) {
- case 'EBADCSRFTOKEN':
- winston.error(req.path + '\n', err.message);
- return res.sendStatus(403);
- case 'blacklisted-ip':
- return res.status(403).type('text/plain').send(err.message);
- }
-
- if (parseInt(err.status, 10) === 302 && err.path) {
- return res.locals.isAPI ? res.status(302).json(err.path) : res.redirect(err.path);
- }
-
- winston.error(req.path + '\n', err.stack);
-
- res.status(err.status || 500);
-
- var path = String(req.path || '');
- if (res.locals.isAPI) {
- res.json({ path: validator.escape(path), error: err.message });
- } else {
- var middleware = require('../middleware');
- middleware.buildHeader(req, res, function () {
- res.render('500', { path: validator.escape(path), error: validator.escape(String(err.message)) });
- });
- }
-};
-
-module.exports = Controllers;
diff --git a/src/controllers/posts.js b/src/controllers/posts.js
index 90596d5b34..8afb3f5729 100644
--- a/src/controllers/posts.js
+++ b/src/controllers/posts.js
@@ -1,24 +1,38 @@
'use strict';
+var async = require('async');
+
var posts = require('../posts');
var helpers = require('./helpers');
-var postsController = {};
+var postsController = module.exports;
-postsController.redirectToPost = function (req, res, callback) {
+postsController.redirectToPost = function (req, res, next) {
var pid = parseInt(req.params.pid, 10);
if (!pid) {
- return callback();
+ return next();
}
- posts.generatePostPath(pid, req.uid, function (err, path) {
- if (err || !path) {
- return callback(err);
- }
-
- helpers.redirect(res, path);
- });
+ async.waterfall([
+ function (next) {
+ posts.generatePostPath(pid, req.uid, next);
+ },
+ function (path, next) {
+ if (!path) {
+ return next();
+ }
+ helpers.redirect(res, path);
+ },
+ ], next);
};
-
-module.exports = postsController;
+postsController.getRecentPosts = function (req, res, next) {
+ async.waterfall([
+ function (next) {
+ posts.getRecentPosts(req.uid, 0, 19, req.params.term, next);
+ },
+ function (data) {
+ res.json(data);
+ },
+ ], next);
+};
diff --git a/src/controllers/sitemap.js b/src/controllers/sitemap.js
index 453aed4f6e..846c6facb2 100644
--- a/src/controllers/sitemap.js
+++ b/src/controllers/sitemap.js
@@ -1,68 +1,57 @@
'use strict';
+var async = require('async');
+
var sitemap = require('../sitemap');
var meta = require('../meta');
-var sitemapController = {};
-sitemapController.render = function (req, res, next) {
- sitemap.render(function (err, tplData) {
- if (err) {
- return next(err);
- }
+var sitemapController = module.exports;
- req.app.render('sitemap', tplData, function (err, xml) {
- if (err) {
- return next(err);
- }
+sitemapController.render = function (req, res, next) {
+ async.waterfall([
+ function (next) {
+ sitemap.render(next);
+ },
+ function (tplData, next) {
+ req.app.render('sitemap', tplData, next);
+ },
+ function (xml) {
res.header('Content-Type', 'application/xml');
res.send(xml);
- });
- });
+ },
+ ], next);
};
sitemapController.getPages = function (req, res, next) {
- if (parseInt(meta.config['feeds:disableSitemap'], 10) === 1) {
- return next();
- }
-
- sitemap.getPages(function (err, xml) {
- if (err) {
- return next(err);
- }
- res.header('Content-Type', 'application/xml');
- res.send(xml);
- });
+ sendSitemap(sitemap.getPages, res, next);
};
sitemapController.getCategories = function (req, res, next) {
- if (parseInt(meta.config['feeds:disableSitemap'], 10) === 1) {
- return next();
- }
-
- sitemap.getCategories(function (err, xml) {
- if (err) {
- return next(err);
- }
- res.header('Content-Type', 'application/xml');
- res.send(xml);
- });
+ sendSitemap(sitemap.getCategories, res, next);
};
sitemapController.getTopicPage = function (req, res, next) {
+ sendSitemap(function (callback) {
+ sitemap.getTopicPage(parseInt(req.params[0], 10), callback);
+ }, res, next);
+};
+
+function sendSitemap(method, res, callback) {
if (parseInt(meta.config['feeds:disableSitemap'], 10) === 1) {
- return next();
+ return callback();
}
+ async.waterfall([
+ function (next) {
+ method(next);
+ },
+ function (xml) {
+ if (!xml) {
+ return callback();
+ }
- sitemap.getTopicPage(parseInt(req.params[0], 10), function (err, xml) {
- if (err) {
- return next(err);
- } else if (!xml) {
- return next();
- }
-
- res.header('Content-Type', 'application/xml');
- res.send(xml);
- });
-};
+ res.header('Content-Type', 'application/xml');
+ res.send(xml);
+ },
+ ], callback);
+}
-module.exports = sitemapController;
diff --git a/src/controllers/uploads.js b/src/controllers/uploads.js
index 25375e91a9..7a309d5200 100644
--- a/src/controllers/uploads.js
+++ b/src/controllers/uploads.js
@@ -66,7 +66,7 @@ function uploadAsImage(req, uploadedFile, callback) {
file.isFileTypeAllowed(uploadedFile.path, next);
},
function (next) {
- uploadFile(req.uid, uploadedFile, next);
+ uploadsController.uploadFile(req.uid, uploadedFile, next);
},
function (fileObj, next) {
if (parseInt(meta.config.maximumImageWidth, 10) === 0) {
@@ -90,7 +90,7 @@ function uploadAsFile(req, uploadedFile, callback) {
if (parseInt(meta.config.allowFileUploads, 10) !== 1) {
return next(new Error('[[error:uploads-are-disabled]]'));
}
- uploadFile(req.uid, uploadedFile, next);
+ uploadsController.uploadFile(req.uid, uploadedFile, next);
},
], callback);
}
@@ -161,7 +161,7 @@ uploadsController.uploadThumb = function (req, res, next) {
}, next);
}
- uploadFile(req.uid, uploadedFile, next);
+ uploadsController.uploadFile(req.uid, uploadedFile, next);
},
], next);
}, next);
@@ -192,7 +192,7 @@ uploadsController.uploadGroupCover = function (uid, uploadedFile, callback) {
], callback);
};
-function uploadFile(uid, uploadedFile, callback) {
+uploadsController.uploadFile = function (uid, uploadedFile, callback) {
if (plugins.hasListeners('filter:uploadFile')) {
return plugins.fireHook('filter:uploadFile', {
file: uploadedFile,
@@ -217,7 +217,7 @@ function uploadFile(uid, uploadedFile, callback) {
}
saveFileToLocal(uploadedFile, callback);
-}
+};
function saveFileToLocal(uploadedFile, callback) {
var extension = file.typeToExtension(uploadedFile.type);
diff --git a/src/controllers/user.js b/src/controllers/user.js
new file mode 100644
index 0000000000..0f93f549b3
--- /dev/null
+++ b/src/controllers/user.js
@@ -0,0 +1,99 @@
+'use strict';
+
+var async = require('async');
+
+var user = require('../user');
+var meta = require('../meta');
+var accountHelpers = require('./accounts/helpers');
+
+var userController = module.exports;
+
+userController.getCurrentUser = function (req, res, next) {
+ if (!req.uid) {
+ return res.status(401).json('not-authorized');
+ }
+ async.waterfall([
+ function (next) {
+ user.getUserField(req.uid, 'userslug', next);
+ },
+ function (userslug, next) {
+ accountHelpers.getUserDataByUserSlug(userslug, req.uid, next);
+ },
+ function (userData) {
+ res.json(userData);
+ },
+ ], next);
+};
+
+
+userController.getUserByUID = function (req, res, next) {
+ byType('uid', req, res, next);
+};
+
+userController.getUserByUsername = function (req, res, next) {
+ byType('username', req, res, next);
+};
+
+userController.getUserByEmail = function (req, res, next) {
+ byType('email', req, res, next);
+};
+
+function byType(type, req, res, next) {
+ async.waterfall([
+ function (next) {
+ userController.getUserDataByField(req.uid, type, req.params[type], next);
+ },
+ function (data, next) {
+ if (!data) {
+ return next();
+ }
+ res.json(data);
+ },
+ ], next);
+}
+
+userController.getUserDataByField = function (callerUid, field, fieldValue, callback) {
+ async.waterfall([
+ function (next) {
+ if (field === 'uid') {
+ next(null, fieldValue);
+ } else if (field === 'username') {
+ user.getUidByUsername(fieldValue, next);
+ } else if (field === 'email') {
+ user.getUidByEmail(fieldValue, next);
+ } else {
+ next(null, null);
+ }
+ },
+ function (uid, next) {
+ if (!uid) {
+ return next(null, null);
+ }
+ userController.getUserDataByUID(callerUid, uid, next);
+ },
+ ], callback);
+};
+
+userController.getUserDataByUID = function (callerUid, uid, callback) {
+ if (!parseInt(callerUid, 10) && parseInt(meta.config.privateUserInfo, 10) === 1) {
+ return callback(new Error('[[error:no-privileges]]'));
+ }
+
+ if (!parseInt(uid, 10)) {
+ return callback(new Error('[[error:no-user]]'));
+ }
+
+ async.parallel({
+ userData: async.apply(user.getUserData, uid),
+ settings: async.apply(user.getSettings, uid),
+ }, function (err, results) {
+ if (err || !results.userData) {
+ return callback(err || new Error('[[error:no-user]]'));
+ }
+
+ results.userData.email = results.settings.showemail ? results.userData.email : undefined;
+ results.userData.fullname = results.settings.showfullname ? results.userData.fullname : undefined;
+
+ callback(null, results.userData);
+ });
+};
diff --git a/src/controllers/users.js b/src/controllers/users.js
index ae3643bb8d..45cb5f1e21 100644
--- a/src/controllers/users.js
+++ b/src/controllers/users.js
@@ -8,9 +8,7 @@ var pagination = require('../pagination');
var db = require('../database');
var helpers = require('./helpers');
-
-var usersController = {};
-
+var usersController = module.exports;
usersController.index = function (req, res, next) {
var section = req.query.section || 'joindate';
@@ -33,62 +31,65 @@ usersController.index = function (req, res, next) {
};
usersController.search = function (req, res, next) {
- async.parallel({
- search: function (next) {
- user.search({
- query: req.query.term,
- searchBy: req.query.searchBy || 'username',
- page: req.query.page || 1,
- sortBy: req.query.sortBy,
- onlineOnly: req.query.onlineOnly === 'true',
- bannedOnly: req.query.bannedOnly === 'true',
- flaggedOnly: req.query.flaggedOnly === 'true',
+ async.waterfall([
+ function (next) {
+ async.parallel({
+ search: function (next) {
+ user.search({
+ query: req.query.term,
+ searchBy: req.query.searchBy || 'username',
+ page: req.query.page || 1,
+ sortBy: req.query.sortBy,
+ onlineOnly: req.query.onlineOnly === 'true',
+ bannedOnly: req.query.bannedOnly === 'true',
+ flaggedOnly: req.query.flaggedOnly === 'true',
+ }, next);
+ },
+ isAdminOrGlobalMod: function (next) {
+ user.isAdminOrGlobalMod(req.uid, next);
+ },
}, next);
},
- isAdminOrGlobalMod: function (next) {
- user.isAdminOrGlobalMod(req.uid, next);
+ function (results, next) {
+ var section = req.query.section || 'joindate';
+
+ results.search.isAdminOrGlobalMod = results.isAdminOrGlobalMod;
+ results.search.pagination = pagination.create(req.query.page, results.search.pageCount, req.query);
+ results.search['section_' + section] = true;
+ render(req, res, results.search, next);
},
- }, function (err, results) {
- if (err) {
- return next(err);
- }
-
- var section = req.query.section || 'joindate';
-
- results.search.isAdminOrGlobalMod = results.isAdminOrGlobalMod;
- results.search.pagination = pagination.create(req.query.page, results.search.pageCount, req.query);
- results.search['section_' + section] = true;
- render(req, res, results.search, next);
- });
+ ], next);
};
usersController.getOnlineUsers = function (req, res, next) {
- async.parallel({
- users: function (next) {
- usersController.getUsers('users:online', req.uid, req.query, next);
- },
- guests: function (next) {
- require('../socket.io/admin/rooms').getTotalGuestCount(next);
+ async.waterfall([
+ function (next) {
+ async.parallel({
+ users: function (next) {
+ usersController.getUsers('users:online', req.uid, req.query, next);
+ },
+ guests: function (next) {
+ require('../socket.io/admin/rooms').getTotalGuestCount(next);
+ },
+ }, next);
},
- }, function (err, results) {
- if (err) {
- return next(err);
- }
- var userData = results.users;
- var hiddenCount = 0;
- if (!userData.isAdminOrGlobalMod) {
- userData.users = userData.users.filter(function (user) {
- if (user && user.status === 'offline') {
- hiddenCount += 1;
- }
- return user && user.status !== 'offline';
- });
- }
+ function (results, next) {
+ var userData = results.users;
+ var hiddenCount = 0;
+ if (!userData.isAdminOrGlobalMod) {
+ userData.users = userData.users.filter(function (user) {
+ if (user && user.status === 'offline') {
+ hiddenCount += 1;
+ }
+ return user && user.status !== 'offline';
+ });
+ }
- userData.anonymousUserCount = results.guests + hiddenCount;
+ userData.anonymousUserCount = results.guests + hiddenCount;
- render(req, res, userData, next);
- });
+ render(req, res, userData, next);
+ },
+ ], next);
};
usersController.getUsersSortedByPosts = function (req, res, next) {
@@ -107,41 +108,36 @@ usersController.getUsersSortedByJoinDate = function (req, res, next) {
};
usersController.getBannedUsers = function (req, res, next) {
- usersController.getUsers('users:banned', req.uid, req.query, function (err, userData) {
- if (err) {
- return next(err);
- }
-
- if (!userData.isAdminOrGlobalMod) {
- return next();
- }
-
- render(req, res, userData, next);
- });
+ renderIfAdminOrGlobalMod('users:banned', req, res, next);
};
usersController.getFlaggedUsers = function (req, res, next) {
- usersController.getUsers('users:flags', req.uid, req.query, function (err, userData) {
- if (err) {
- return next(err);
- }
-
- if (!userData.isAdminOrGlobalMod) {
- return next();
- }
-
- render(req, res, userData, next);
- });
+ renderIfAdminOrGlobalMod('users:flags', req, res, next);
};
-usersController.renderUsersPage = function (set, req, res, next) {
- usersController.getUsers(set, req.uid, req.query, function (err, userData) {
- if (err) {
- return next(err);
- }
+function renderIfAdminOrGlobalMod(set, req, res, next) {
+ async.waterfall([
+ function (next) {
+ user.isAdminOrGlobalMod(req.uid, next);
+ },
+ function (isAdminOrGlobalMod, next) {
+ if (!isAdminOrGlobalMod) {
+ return helpers.notAllowed(req, res);
+ }
+ usersController.renderUsersPage(set, req, res, next);
+ },
+ ], next);
+}
- render(req, res, userData, next);
- });
+usersController.renderUsersPage = function (set, req, res, next) {
+ async.waterfall([
+ function (next) {
+ usersController.getUsers(set, req.uid, req.query, next);
+ },
+ function (userData, next) {
+ render(req, res, userData, next);
+ },
+ ], next);
};
usersController.getUsers = function (set, uid, query, callback) {
@@ -169,59 +165,62 @@ usersController.getUsers = function (set, uid, query, callback) {
var start = Math.max(0, page - 1) * resultsPerPage;
var stop = start + resultsPerPage - 1;
- async.parallel({
- isAdminOrGlobalMod: function (next) {
- user.isAdminOrGlobalMod(uid, next);
+ async.waterfall([
+ function (next) {
+ async.parallel({
+ isAdminOrGlobalMod: function (next) {
+ user.isAdminOrGlobalMod(uid, next);
+ },
+ usersData: function (next) {
+ usersController.getUsersAndCount(set, uid, start, stop, next);
+ },
+ }, next);
},
- usersData: function (next) {
- usersController.getUsersAndCount(set, uid, start, stop, next);
+ function (results, next) {
+ var pageCount = Math.ceil(results.usersData.count / resultsPerPage);
+ var userData = {
+ users: results.usersData.users,
+ pagination: pagination.create(page, pageCount, query),
+ userCount: results.usersData.count,
+ title: setToData[set].title || '[[pages:users/latest]]',
+ breadcrumbs: helpers.buildBreadcrumbs(breadcrumbs),
+ isAdminOrGlobalMod: results.isAdminOrGlobalMod,
+ };
+ userData['section_' + (query.section || 'joindate')] = true;
+ next(null, userData);
},
- }, function (err, results) {
- if (err) {
- return callback(err);
- }
-
- var pageCount = Math.ceil(results.usersData.count / resultsPerPage);
- var userData = {
- users: results.usersData.users,
- pagination: pagination.create(page, pageCount, query),
- userCount: results.usersData.count,
- title: setToData[set].title || '[[pages:users/latest]]',
- breadcrumbs: helpers.buildBreadcrumbs(breadcrumbs),
- isAdminOrGlobalMod: results.isAdminOrGlobalMod,
- };
- userData['section_' + (query.section || 'joindate')] = true;
- callback(null, userData);
- });
+ ], callback);
};
usersController.getUsersAndCount = function (set, uid, start, stop, callback) {
- async.parallel({
- users: function (next) {
- user.getUsersFromSet(set, uid, start, stop, next);
+ async.waterfall([
+ function (next) {
+ async.parallel({
+ users: function (next) {
+ user.getUsersFromSet(set, uid, start, stop, next);
+ },
+ count: function (next) {
+ if (set === 'users:online') {
+ var now = Date.now();
+ db.sortedSetCount('users:online', now - 300000, '+inf', next);
+ } else if (set === 'users:banned') {
+ db.sortedSetCard('users:banned', next);
+ } else if (set === 'users:flags') {
+ db.sortedSetCard('users:flags', next);
+ } else {
+ db.getObjectField('global', 'userCount', next);
+ }
+ },
+ }, next);
},
- count: function (next) {
- if (set === 'users:online') {
- var now = Date.now();
- db.sortedSetCount('users:online', now - 300000, '+inf', next);
- } else if (set === 'users:banned') {
- db.sortedSetCard('users:banned', next);
- } else if (set === 'users:flags') {
- db.sortedSetCard('users:flags', next);
- } else {
- db.getObjectField('global', 'userCount', next);
- }
+ function (results, next) {
+ results.users = results.users.filter(function (user) {
+ return user && parseInt(user.uid, 10);
+ });
+
+ next(null, results);
},
- }, function (err, results) {
- if (err) {
- return callback(err);
- }
- results.users = results.users.filter(function (user) {
- return user && parseInt(user.uid, 10);
- });
-
- callback(null, results);
- });
+ ], callback);
};
function render(req, res, data, next) {
@@ -232,16 +231,15 @@ function render(req, res, data, next) {
data.adminInviteOnly = registrationType === 'admin-invite-only';
data['reputation:disabled'] = parseInt(meta.config['reputation:disabled'], 10) === 1;
- user.getInvitesNumber(req.uid, function (err, numInvites) {
- if (err) {
- return next(err);
- }
-
- res.append('X-Total-Count', data.userCount);
- data.invites = numInvites;
+ async.waterfall([
+ function (next) {
+ user.getInvitesNumber(req.uid, next);
+ },
+ function (numInvites) {
+ res.append('X-Total-Count', data.userCount);
+ data.invites = numInvites;
- res.render('users', data);
- });
+ res.render('users', data);
+ },
+ ], next);
}
-
-module.exports = usersController;
diff --git a/src/groups.js b/src/groups.js
index 5ed854b944..fd8c4a958f 100644
--- a/src/groups.js
+++ b/src/groups.js
@@ -6,215 +6,197 @@ var validator = require('validator');
var user = require('./user');
var db = require('./database');
var plugins = require('./plugins');
-var posts = require('./posts');
-var privileges = require('./privileges');
var utils = require('../public/src/utils');
-(function (Groups) {
- require('./groups/create')(Groups);
- require('./groups/delete')(Groups);
- require('./groups/update')(Groups);
- require('./groups/membership')(Groups);
- require('./groups/ownership')(Groups);
- require('./groups/search')(Groups);
- require('./groups/cover')(Groups);
-
- var ephemeralGroups = ['guests'];
-
- var internals = {
- getEphemeralGroup: function (groupName) {
- return {
- name: groupName,
- slug: utils.slugify(groupName),
- description: '',
- deleted: '0',
- hidden: '0',
- system: '1',
- };
- },
- removeEphemeralGroups: function (groups) {
- for (var x = groups.length; x >= 0; x -= 1) {
- if (ephemeralGroups.indexOf(groups[x]) !== -1) {
- groups.splice(x, 1);
- }
- }
-
- return groups;
- },
+var Groups = module.exports;
+
+require('./groups/data')(Groups);
+require('./groups/create')(Groups);
+require('./groups/delete')(Groups);
+require('./groups/update')(Groups);
+require('./groups/membership')(Groups);
+require('./groups/ownership')(Groups);
+require('./groups/search')(Groups);
+require('./groups/cover')(Groups);
+require('./groups/posts')(Groups);
+require('./groups/user')(Groups);
+
+
+Groups.ephemeralGroups = ['guests'];
+
+Groups.getEphemeralGroup = function (groupName) {
+ return {
+ name: groupName,
+ slug: utils.slugify(groupName),
+ description: '',
+ deleted: '0',
+ hidden: '0',
+ system: '1',
};
+};
- Groups.internals = internals;
-
- var isPrivilegeGroupRegex = /^cid:\d+:privileges:[\w:]+$/;
- Groups.isPrivilegeGroup = function (groupName) {
- return isPrivilegeGroupRegex.test(groupName);
- };
-
- Groups.getEphemeralGroups = function () {
- return ephemeralGroups;
- };
-
- Groups.getGroupsFromSet = function (set, uid, start, stop, callback) {
- async.waterfall([
- function (next) {
- if (set === 'groups:visible:name') {
- db.getSortedSetRangeByLex(set, '-', '+', start, stop - start + 1, next);
- } else {
- db.getSortedSetRevRange(set, start, stop, next);
- }
- },
- function (groupNames, next) {
- if (set === 'groups:visible:name') {
- groupNames = groupNames.map(function (name) {
- return name.split(':')[1];
- });
- }
-
- Groups.getGroupsAndMembers(groupNames, next);
- },
- ], callback);
- };
-
- Groups.getGroups = function (set, start, stop, callback) {
- db.getSortedSetRevRange(set, start, stop, callback);
- };
-
- Groups.getGroupsAndMembers = function (groupNames, callback) {
- async.parallel({
- groups: function (next) {
- Groups.getGroupsData(groupNames, next);
- },
- members: function (next) {
- Groups.getMemberUsers(groupNames, 0, 3, next);
- },
- }, function (err, data) {
- if (err) {
- return callback(err);
+Groups.removeEphemeralGroups = function (groups) {
+ for (var x = groups.length; x >= 0; x -= 1) {
+ if (Groups.ephemeralGroups.indexOf(groups[x]) !== -1) {
+ groups.splice(x, 1);
+ }
+ }
+
+ return groups;
+};
+
+var isPrivilegeGroupRegex = /^cid:\d+:privileges:[\w:]+$/;
+Groups.isPrivilegeGroup = function (groupName) {
+ return isPrivilegeGroupRegex.test(groupName);
+};
+
+Groups.getGroupsFromSet = function (set, uid, start, stop, callback) {
+ async.waterfall([
+ function (next) {
+ if (set === 'groups:visible:name') {
+ db.getSortedSetRangeByLex(set, '-', '+', start, stop - start + 1, next);
+ } else {
+ db.getSortedSetRevRange(set, start, stop, next);
+ }
+ },
+ function (groupNames, next) {
+ if (set === 'groups:visible:name') {
+ groupNames = groupNames.map(function (name) {
+ return name.split(':')[1];
+ });
}
+ Groups.getGroupsAndMembers(groupNames, next);
+ },
+ ], callback);
+};
+
+Groups.getGroups = function (set, start, stop, callback) {
+ db.getSortedSetRevRange(set, start, stop, callback);
+};
+
+Groups.getGroupsAndMembers = function (groupNames, callback) {
+ async.waterfall([
+ function (next) {
+ async.parallel({
+ groups: function (next) {
+ Groups.getGroupsData(groupNames, next);
+ },
+ members: function (next) {
+ Groups.getMemberUsers(groupNames, 0, 3, next);
+ },
+ }, next);
+ },
+ function (data, next) {
data.groups.forEach(function (group, index) {
- if (!group) {
- return;
+ if (group) {
+ group.members = data.members[index] || [];
+ group.truncated = group.memberCount > data.members.length;
}
-
- group.members = data.members[index] || [];
- group.truncated = group.memberCount > data.members.length;
});
+ next(null, data.groups);
+ },
+ ], callback);
+};
+
+Groups.get = function (groupName, options, callback) {
+ if (!groupName) {
+ return callback(new Error('[[error:invalid-group]]'));
+ }
+
+ var stop = -1;
+
+ var results;
+ async.waterfall([
+ function (next) {
+ async.parallel({
+ base: function (next) {
+ db.getObject('group:' + groupName, next);
+ },
+ members: function (next) {
+ if (options.truncateUserList) {
+ stop = (parseInt(options.userListCount, 10) || 4) - 1;
+ }
- callback(null, data.groups);
- });
- };
-
- Groups.get = function (groupName, options, callback) {
- if (!groupName) {
- return callback(new Error('[[error:invalid-group]]'));
- }
-
- var stop = -1;
-
- async.parallel({
- base: function (next) {
- db.getObject('group:' + groupName, next);
- },
- members: function (next) {
- if (options.truncateUserList) {
- stop = (parseInt(options.userListCount, 10) || 4) - 1;
- }
-
- Groups.getOwnersAndMembers(groupName, options.uid, 0, stop, next);
- },
- pending: function (next) {
- async.waterfall([
- function (next) {
- db.getSetMembers('group:' + groupName + ':pending', next);
- },
- function (uids, next) {
- user.getUsersData(uids, next);
- },
- ], next);
- },
- invited: function (next) {
- async.waterfall([
- function (next) {
- db.getSetMembers('group:' + groupName + ':invited', next);
- },
- function (uids, next) {
- user.getUsersData(uids, next);
- },
- ], next);
- },
- isMember: async.apply(Groups.isMember, options.uid, groupName),
- isPending: async.apply(Groups.isPending, options.uid, groupName),
- isInvited: async.apply(Groups.isInvited, options.uid, groupName),
- isOwner: async.apply(Groups.ownership.isOwner, options.uid, groupName),
- }, function (err, results) {
- if (err) {
- return callback(err);
- } else if (!results.base) {
+ Groups.getOwnersAndMembers(groupName, options.uid, 0, stop, next);
+ },
+ pending: function (next) {
+ Groups.getUsersFromSet('group:' + groupName + ':pending', next);
+ },
+ invited: function (next) {
+ Groups.getUsersFromSet('group:' + groupName + ':invited', next);
+ },
+ isMember: async.apply(Groups.isMember, options.uid, groupName),
+ isPending: async.apply(Groups.isPending, options.uid, groupName),
+ isInvited: async.apply(Groups.isInvited, options.uid, groupName),
+ isOwner: async.apply(Groups.ownership.isOwner, options.uid, groupName),
+ }, next);
+ },
+ function (_results, next) {
+ results = _results;
+ if (!results.base) {
return callback(new Error('[[error:no-group]]'));
}
-
- results.base['cover:url'] = results.base['cover:url'] || require('./coverPhoto').getDefaultGroupCover(groupName);
- results.base['cover:position'] = validator.escape(String(results.base['cover:position'] || '50% 50%'));
- results.base.labelColor = validator.escape(String(results.base.labelColor || '#000000'));
- results.base.icon = validator.escape(String(results.base.icon || ''));
-
- plugins.fireHook('filter:parse.raw', results.base.description, function (err, descriptionParsed) {
- if (err) {
- return callback(err);
- }
-
- Groups.escapeGroupData(results.base);
-
- results.base.descriptionParsed = descriptionParsed;
- results.base.userTitleEnabled = results.base.userTitleEnabled ? !!parseInt(results.base.userTitleEnabled, 10) : true;
- results.base.createtimeISO = utils.toISOString(results.base.createtime);
- results.base.members = results.members;
- results.base.membersNextStart = stop + 1;
- results.base.pending = results.pending.filter(Boolean);
- results.base.invited = results.invited.filter(Boolean);
- results.base.deleted = !!parseInt(results.base.deleted, 10);
- results.base.hidden = !!parseInt(results.base.hidden, 10);
- results.base.system = !!parseInt(results.base.system, 10);
- results.base.memberCount = parseInt(results.base.memberCount, 10);
- results.base.private = (results.base.private === null || results.base.private === undefined) ? true : !!parseInt(results.base.private, 10);
- results.base.disableJoinRequests = parseInt(results.base.disableJoinRequests, 10) === 1;
- results.base.isMember = results.isMember;
- results.base.isPending = results.isPending;
- results.base.isInvited = results.isInvited;
- results.base.isOwner = results.isOwner;
-
- plugins.fireHook('filter:group.get', { group: results.base }, function (err, data) {
- callback(err, data ? data.group : null);
- });
- });
- });
- };
-
- Groups.getOwners = function (groupName, callback) {
- db.getSetMembers('group:' + groupName + ':owners', callback);
- };
-
- Groups.getOwnersAndMembers = function (groupName, uid, start, stop, callback) {
- async.parallel({
- owners: function (next) {
- async.waterfall([
- function (next) {
- db.getSetMembers('group:' + groupName + ':owners', next);
- },
- function (uids, next) {
- user.getUsers(uids, uid, next);
- },
- ], next);
- },
- members: function (next) {
- user.getUsersFromSet('group:' + groupName + ':members', uid, start, stop, next);
- },
- }, function (err, results) {
- if (err) {
- return callback(err);
- }
-
+ plugins.fireHook('filter:parse.raw', results.base.description, next);
+ },
+ function (descriptionParsed, next) {
+ var groupData = results.base;
+ Groups.escapeGroupData(groupData);
+
+ groupData.descriptionParsed = descriptionParsed;
+ groupData.userTitleEnabled = groupData.userTitleEnabled ? !!parseInt(groupData.userTitleEnabled, 10) : true;
+ groupData.createtimeISO = utils.toISOString(groupData.createtime);
+ groupData.members = results.members;
+ groupData.membersNextStart = stop + 1;
+ groupData.pending = results.pending.filter(Boolean);
+ groupData.invited = results.invited.filter(Boolean);
+ groupData.deleted = !!parseInt(groupData.deleted, 10);
+ groupData.hidden = !!parseInt(groupData.hidden, 10);
+ groupData.system = !!parseInt(groupData.system, 10);
+ groupData.memberCount = parseInt(groupData.memberCount, 10);
+ groupData.private = (groupData.private === null || groupData.private === undefined) ? true : !!parseInt(groupData.private, 10);
+ groupData.disableJoinRequests = parseInt(groupData.disableJoinRequests, 10) === 1;
+ groupData.isMember = results.isMember;
+ groupData.isPending = results.isPending;
+ groupData.isInvited = results.isInvited;
+ groupData.isOwner = results.isOwner;
+ groupData['cover:url'] = groupData['cover:url'] || require('./coverPhoto').getDefaultGroupCover(groupName);
+ groupData['cover:position'] = validator.escape(String(groupData['cover:position'] || '50% 50%'));
+ groupData.labelColor = validator.escape(String(groupData.labelColor || '#000000'));
+ groupData.icon = validator.escape(String(groupData.icon || ''));
+
+ plugins.fireHook('filter:group.get', { group: groupData }, next);
+ },
+ function (results, next) {
+ next(null, results.group);
+ },
+ ], callback);
+};
+
+Groups.getOwners = function (groupName, callback) {
+ db.getSetMembers('group:' + groupName + ':owners', callback);
+};
+
+Groups.getOwnersAndMembers = function (groupName, uid, start, stop, callback) {
+ async.waterfall([
+ function (next) {
+ async.parallel({
+ owners: function (next) {
+ async.waterfall([
+ function (next) {
+ db.getSetMembers('group:' + groupName + ':owners', next);
+ },
+ function (uids, next) {
+ user.getUsers(uids, uid, next);
+ },
+ ], next);
+ },
+ members: function (next) {
+ user.getUsersFromSet('group:' + groupName + ':members', uid, start, stop, next);
+ },
+ }, next);
+ },
+ function (results, next) {
var ownerUids = [];
results.owners.forEach(function (user) {
if (user) {
@@ -228,225 +210,94 @@ var utils = require('../public/src/utils');
});
results.members = results.owners.concat(results.members);
- callback(null, results.members);
- });
- };
-
- Groups.escapeGroupData = function (group) {
- if (group) {
- group.nameEncoded = encodeURIComponent(group.name);
- group.displayName = validator.escape(String(group.name));
- group.description = validator.escape(String(group.description || ''));
- group.userTitle = validator.escape(String(group.userTitle || '')) || group.displayName;
- }
- };
-
- Groups.getByGroupslug = function (slug, options, callback) {
- db.getObjectField('groupslug:groupname', slug, function (err, groupName) {
- if (err) {
- return callback(err);
- } else if (!groupName) {
- return callback(new Error('[[error:no-group]]'));
+ next(null, results.members);
+ },
+ ], callback);
+};
+
+Groups.escapeGroupData = function (group) {
+ if (group) {
+ group.nameEncoded = encodeURIComponent(group.name);
+ group.displayName = validator.escape(String(group.name));
+ group.description = validator.escape(String(group.description || ''));
+ group.userTitle = validator.escape(String(group.userTitle || '')) || group.displayName;
+ }
+};
+
+Groups.getByGroupslug = function (slug, options, callback) {
+ async.waterfall([
+ function (next) {
+ db.getObjectField('groupslug:groupname', slug, next);
+ },
+ function (groupName, next) {
+ if (!groupName) {
+ return next(new Error('[[error:no-group]]'));
}
+ Groups.get(groupName, options, next);
+ },
+ ], callback);
+};
- Groups.get(groupName, options, callback);
- });
- };
-
- Groups.getGroupNameByGroupSlug = function (slug, callback) {
- db.getObjectField('groupslug:groupname', slug, callback);
- };
-
- Groups.getGroupFields = function (groupName, fields, callback) {
- Groups.getMultipleGroupFields([groupName], fields, function (err, groups) {
- callback(err, groups ? groups[0] : null);
- });
- };
+Groups.getGroupNameByGroupSlug = function (slug, callback) {
+ db.getObjectField('groupslug:groupname', slug, callback);
+};
- Groups.getMultipleGroupFields = function (groups, fields, callback) {
- db.getObjectsFields(groups.map(function (group) {
- return 'group:' + group;
- }), fields, callback);
- };
+Groups.isPrivate = function (groupName, callback) {
+ isFieldOn(groupName, 'private', callback);
+};
- Groups.setGroupField = function (groupName, field, value, callback) {
- db.setObjectField('group:' + groupName, field, value, function (err) {
- if (err) {
- return callback(err);
- }
- plugins.fireHook('action:group.set', { field: field, value: value, type: 'set' });
- callback();
- });
- };
+Groups.isHidden = function (groupName, callback) {
+ isFieldOn(groupName, 'hidden', callback);
+};
- Groups.isPrivate = function (groupName, callback) {
- db.getObjectField('group:' + groupName, 'private', function (err, isPrivate) {
- if (err) {
- return callback(err);
- }
-
- callback(null, parseInt(isPrivate, 10) !== 0);
- });
- };
-
- Groups.isHidden = function (groupName, callback) {
- db.getObjectField('group:' + groupName, 'hidden', function (err, isHidden) {
- if (err) {
- return callback(err);
- }
+function isFieldOn(groupName, field, callback) {
+ async.waterfall([
+ function (next) {
+ db.getObjectField('group:' + groupName, field, next);
+ },
+ function (value, next) {
+ next(null, parseInt(value, 10) === 1);
+ },
+ ], callback);
+}
- callback(null, parseInt(isHidden, 10) === 1);
+Groups.exists = function (name, callback) {
+ if (Array.isArray(name)) {
+ var slugs = name.map(function (groupName) {
+ return utils.slugify(groupName);
});
- };
-
- Groups.exists = function (name, callback) {
- if (Array.isArray(name)) {
- var slugs = name.map(function (groupName) {
- return utils.slugify(groupName);
- });
- async.parallel([
- function (next) {
- next(null, slugs.map(function (slug) {
- return ephemeralGroups.indexOf(slug) !== -1;
- }));
- },
- async.apply(db.isSortedSetMembers, 'groups:createtime', name),
- ], function (err, results) {
- if (err) {
- return callback(err);
- }
- callback(null, name.map(function (n, index) {
- return results[0][index] || results[1][index];
- }));
- });
- } else {
- var slug = utils.slugify(name);
- async.parallel([
- function (next) {
- next(null, ephemeralGroups.indexOf(slug) !== -1);
- },
- async.apply(db.isSortedSetMember, 'groups:createtime', name),
- ], function (err, results) {
- callback(err, !err ? (results[0] || results[1]) : null);
- });
- }
- };
-
- Groups.existsBySlug = function (slug, callback) {
- if (Array.isArray(slug)) {
- db.isObjectFields('groupslug:groupname', slug, callback);
- } else {
- db.isObjectField('groupslug:groupname', slug, callback);
- }
- };
-
- Groups.getLatestMemberPosts = function (groupName, max, uid, callback) {
- async.waterfall([
+ async.parallel([
function (next) {
- Groups.getMembers(groupName, 0, -1, next);
- },
- function (uids, next) {
- if (!Array.isArray(uids) || !uids.length) {
- return callback(null, []);
- }
- var keys = uids.map(function (uid) {
- return 'uid:' + uid + ':posts';
- });
- db.getSortedSetRevRange(keys, 0, max - 1, next);
- },
- function (pids, next) {
- privileges.posts.filter('read', pids, uid, next);
- },
- function (pids, next) {
- posts.getPostSummaryByPids(pids, uid, { stripTags: false }, next);
+ next(null, slugs.map(function (slug) {
+ return Groups.ephemeralGroups.indexOf(slug) !== -1;
+ }));
},
- ], callback);
- };
-
- Groups.getGroupData = function (groupName, callback) {
- Groups.getGroupsData([groupName], function (err, groupsData) {
- callback(err, Array.isArray(groupsData) && groupsData[0] ? groupsData[0] : null);
- });
- };
-
- Groups.getGroupsData = function (groupNames, callback) {
- if (!Array.isArray(groupNames) || !groupNames.length) {
- return callback(null, []);
- }
-
- var keys = groupNames.map(function (groupName) {
- return 'group:' + groupName;
- });
-
- var ephemeralIdx = groupNames.reduce(function (memo, cur, idx) {
- if (ephemeralGroups.indexOf(cur) !== -1) {
- memo.push(idx);
- }
- return memo;
- }, []);
-
- db.getObjects(keys, function (err, groupData) {
+ async.apply(db.isSortedSetMembers, 'groups:createtime', name),
+ ], function (err, results) {
if (err) {
return callback(err);
}
-
- if (ephemeralIdx.length) {
- ephemeralIdx.forEach(function (idx) {
- groupData[idx] = internals.getEphemeralGroup(groupNames[idx]);
- });
- }
-
- groupData.forEach(function (group) {
- if (group) {
- Groups.escapeGroupData(group);
- group.userTitleEnabled = group.userTitleEnabled ? parseInt(group.userTitleEnabled, 10) === 1 : true;
- group.labelColor = validator.escape(String(group.labelColor || '#000000'));
- group.icon = validator.escape(String(group.icon || ''));
- group.createtimeISO = utils.toISOString(group.createtime);
- group.hidden = parseInt(group.hidden, 10) === 1;
- group.system = parseInt(group.system, 10) === 1;
- group.private = (group.private === null || group.private === undefined) ? true : !!parseInt(group.private, 10);
- group.disableJoinRequests = parseInt(group.disableJoinRequests, 10) === 1;
-
- group['cover:url'] = group['cover:url'] || require('./coverPhoto').getDefaultGroupCover(group.name);
- group['cover:thumb:url'] = group['cover:thumb:url'] || group['cover:url'];
- group['cover:position'] = validator.escape(String(group['cover:position'] || '50% 50%'));
- }
- });
-
- plugins.fireHook('filter:groups.get', { groups: groupData }, function (err, data) {
- callback(err, data ? data.groups : null);
- });
+ callback(null, name.map(function (n, index) {
+ return results[0][index] || results[1][index];
+ }));
});
- };
-
- Groups.getUserGroups = function (uids, callback) {
- Groups.getUserGroupsFromSet('groups:visible:createtime', uids, callback);
- };
-
- Groups.getUserGroupsFromSet = function (set, uids, callback) {
- async.waterfall([
+ } else {
+ var slug = utils.slugify(name);
+ async.parallel([
function (next) {
- db.getSortedSetRevRange(set, 0, -1, next);
- },
- function (groupNames, next) {
- async.map(uids, function (uid, next) {
- Groups.isMemberOfGroups(uid, groupNames, function (err, isMembers) {
- if (err) {
- return next(err);
- }
-
- var memberOf = [];
- isMembers.forEach(function (isMember, index) {
- if (isMember) {
- memberOf.push(groupNames[index]);
- }
- });
-
- Groups.getGroupsData(memberOf, next);
- });
- }, next);
+ next(null, Groups.ephemeralGroups.indexOf(slug) !== -1);
},
- ], callback);
- };
-}(module.exports));
+ async.apply(db.isSortedSetMember, 'groups:createtime', name),
+ ], function (err, results) {
+ callback(err, !err ? (results[0] || results[1]) : null);
+ });
+ }
+};
+
+Groups.existsBySlug = function (slug, callback) {
+ if (Array.isArray(slug)) {
+ db.isObjectFields('groupslug:groupname', slug, callback);
+ } else {
+ db.isObjectField('groupslug:groupname', slug, callback);
+ }
+};
diff --git a/src/groups/data.js b/src/groups/data.js
new file mode 100644
index 0000000000..24e7f99a02
--- /dev/null
+++ b/src/groups/data.js
@@ -0,0 +1,93 @@
+'use strict';
+
+var async = require('async');
+var validator = require('validator');
+
+var db = require('../database');
+var plugins = require('../plugins');
+var utils = require('../../public/src/utils');
+
+module.exports = function (Groups) {
+ Groups.getGroupsData = function (groupNames, callback) {
+ if (!Array.isArray(groupNames) || !groupNames.length) {
+ return callback(null, []);
+ }
+
+ var keys = groupNames.map(function (groupName) {
+ return 'group:' + groupName;
+ });
+
+ var ephemeralIdx = groupNames.reduce(function (memo, cur, idx) {
+ if (Groups.ephemeralGroups.indexOf(cur) !== -1) {
+ memo.push(idx);
+ }
+ return memo;
+ }, []);
+
+ async.waterfall([
+ function (next) {
+ db.getObjects(keys, next);
+ },
+ function (groupData, next) {
+ if (ephemeralIdx.length) {
+ ephemeralIdx.forEach(function (idx) {
+ groupData[idx] = Groups.getEphemeralGroup(groupNames[idx]);
+ });
+ }
+
+ groupData.forEach(function (group) {
+ if (group) {
+ Groups.escapeGroupData(group);
+ group.userTitleEnabled = group.userTitleEnabled ? parseInt(group.userTitleEnabled, 10) === 1 : true;
+ group.labelColor = validator.escape(String(group.labelColor || '#000000'));
+ group.icon = validator.escape(String(group.icon || ''));
+ group.createtimeISO = utils.toISOString(group.createtime);
+ group.hidden = parseInt(group.hidden, 10) === 1;
+ group.system = parseInt(group.system, 10) === 1;
+ group.private = (group.private === null || group.private === undefined) ? true : !!parseInt(group.private, 10);
+ group.disableJoinRequests = parseInt(group.disableJoinRequests, 10) === 1;
+
+ group['cover:url'] = group['cover:url'] || require('../coverPhoto').getDefaultGroupCover(group.name);
+ group['cover:thumb:url'] = group['cover:thumb:url'] || group['cover:url'];
+ group['cover:position'] = validator.escape(String(group['cover:position'] || '50% 50%'));
+ }
+ });
+
+ plugins.fireHook('filter:groups.get', { groups: groupData }, next);
+ },
+ function (results, next) {
+ next(null, results.groups);
+ },
+ ], callback);
+ };
+
+ Groups.getGroupData = function (groupName, callback) {
+ Groups.getGroupsData([groupName], function (err, groupsData) {
+ callback(err, Array.isArray(groupsData) && groupsData[0] ? groupsData[0] : null);
+ });
+ };
+
+ Groups.getGroupFields = function (groupName, fields, callback) {
+ Groups.getMultipleGroupFields([groupName], fields, function (err, groups) {
+ callback(err, groups ? groups[0] : null);
+ });
+ };
+
+ Groups.getMultipleGroupFields = function (groups, fields, callback) {
+ db.getObjectsFields(groups.map(function (group) {
+ return 'group:' + group;
+ }), fields, callback);
+ };
+
+ Groups.setGroupField = function (groupName, field, value, callback) {
+ async.waterfall([
+ function (next) {
+ db.setObjectField('group:' + groupName, field, value, next);
+ },
+ function (next) {
+ plugins.fireHook('action:group.set', { field: field, value: value, type: 'set' });
+ next();
+ },
+ ], callback);
+ };
+};
diff --git a/src/groups/membership.js b/src/groups/membership.js
index 31a32da17c..6bcafd2343 100644
--- a/src/groups/membership.js
+++ b/src/groups/membership.js
@@ -413,32 +413,33 @@ module.exports = function (Groups) {
};
Groups.getMemberCount = function (groupName, callback) {
- db.getObjectField('group:' + groupName, 'memberCount', function (err, count) {
- if (err) {
- return callback(err);
- }
- callback(null, parseInt(count, 10));
- });
+ async.waterfall([
+ function (next) {
+ db.getObjectField('group:' + groupName, 'memberCount', next);
+ },
+ function (count, next) {
+ next(null, parseInt(count, 10));
+ },
+ ], callback);
};
Groups.isMemberOfGroupList = function (uid, groupListKey, callback) {
- db.getSortedSetRange('group:' + groupListKey + ':members', 0, -1, function (err, groupNames) {
- if (err) {
- return callback(err);
- }
- groupNames = Groups.internals.removeEphemeralGroups(groupNames);
- if (groupNames.length === 0) {
- return callback(null, false);
- }
-
- Groups.isMemberOfGroups(uid, groupNames, function (err, isMembers) {
- if (err) {
- return callback(err);
+ async.waterfall([
+ function (next) {
+ db.getSortedSetRange('group:' + groupListKey + ':members', 0, -1, next);
+ },
+ function (groupNames, next) {
+ groupNames = Groups.removeEphemeralGroups(groupNames);
+ if (groupNames.length === 0) {
+ return callback(null, false);
}
- callback(null, isMembers.indexOf(true) !== -1);
- });
- });
+ Groups.isMemberOfGroups(uid, groupNames, next);
+ },
+ function (isMembers, next) {
+ next(null, isMembers.indexOf(true) !== -1);
+ },
+ ], callback);
};
Groups.isMemberOfGroupsList = function (uid, groupListKeys, callback) {
@@ -446,19 +447,20 @@ module.exports = function (Groups) {
return 'group:' + groupName + ':members';
});
- db.getSortedSetsMembers(sets, function (err, members) {
- if (err) {
- return callback(err);
- }
-
- var uniqueGroups = _.unique(_.flatten(members));
- uniqueGroups = Groups.internals.removeEphemeralGroups(uniqueGroups);
-
- Groups.isMemberOfGroups(uid, uniqueGroups, function (err, isMembers) {
- if (err) {
- return callback(err);
- }
+ var uniqueGroups;
+ var members;
+ async.waterfall([
+ function (next) {
+ db.getSortedSetsMembers(sets, next);
+ },
+ function (_members, next) {
+ members = _members;
+ uniqueGroups = _.unique(_.flatten(members));
+ uniqueGroups = Groups.removeEphemeralGroups(uniqueGroups);
+ Groups.isMemberOfGroups(uid, uniqueGroups, next);
+ },
+ function (isMembers, next) {
var map = {};
uniqueGroups.forEach(function (groupName, index) {
@@ -474,62 +476,63 @@ module.exports = function (Groups) {
return false;
});
- callback(null, result);
- });
- });
+ next(null, result);
+ },
+ ], callback);
};
Groups.isMembersOfGroupList = function (uids, groupListKey, callback) {
- db.getSortedSetRange('group:' + groupListKey + ':members', 0, -1, function (err, groupNames) {
- if (err) {
- return callback(err);
- }
+ var groupNames;
+ var results = [];
+ uids.forEach(function () {
+ results.push(false);
+ });
- var results = [];
- uids.forEach(function () {
- results.push(false);
- });
+ async.waterfall([
+ function (next) {
+ db.getSortedSetRange('group:' + groupListKey + ':members', 0, -1, next);
+ },
+ function (_groupNames, next) {
+ groupNames = Groups.removeEphemeralGroups(_groupNames);
- groupNames = Groups.internals.removeEphemeralGroups(groupNames);
- if (groupNames.length === 0) {
- return callback(null, results);
- }
+ if (groupNames.length === 0) {
+ return callback(null, results);
+ }
- async.each(groupNames, function (groupName, next) {
- Groups.isMembers(uids, groupName, function (err, isMembers) {
- if (err) {
- return next(err);
- }
+ async.map(groupNames, function (groupName, next) {
+ Groups.isMembers(uids, groupName, next);
+ }, next);
+ },
+ function (isGroupMembers, next) {
+ isGroupMembers.forEach(function (isMembers) {
results.forEach(function (isMember, index) {
if (!isMember && isMembers[index]) {
results[index] = true;
}
});
- next();
});
- }, function (err) {
- callback(err, results);
- });
- });
+ next(null, results);
+ },
+ ], callback);
};
Groups.isInvited = function (uid, groupName, callback) {
if (!uid) {
- return callback(null, false);
+ return setImmediate(callback, null, false);
}
db.isSetMember('group:' + groupName + ':invited', uid, callback);
};
Groups.isPending = function (uid, groupName, callback) {
if (!uid) {
- return callback(null, false);
+ return setImmediate(callback, null, false);
}
db.isSetMember('group:' + groupName + ':pending', uid, callback);
};
Groups.getPending = function (groupName, callback) {
if (!groupName) {
- return callback(null, []);
+ return setImmediate(callback, null, []);
}
db.getSetMembers('group:' + groupName + ':pending', callback);
};
diff --git a/src/groups/posts.js b/src/groups/posts.js
new file mode 100644
index 0000000000..6f4d65520d
--- /dev/null
+++ b/src/groups/posts.js
@@ -0,0 +1,32 @@
+'use strict';
+
+var async = require('async');
+
+var db = require('../database');
+var privileges = require('../privileges');
+var posts = require('../posts');
+
+module.exports = function (Groups) {
+ Groups.getLatestMemberPosts = function (groupName, max, uid, callback) {
+ async.waterfall([
+ function (next) {
+ Groups.getMembers(groupName, 0, -1, next);
+ },
+ function (uids, next) {
+ if (!Array.isArray(uids) || !uids.length) {
+ return callback(null, []);
+ }
+ var keys = uids.map(function (uid) {
+ return 'uid:' + uid + ':posts';
+ });
+ db.getSortedSetRevRange(keys, 0, max - 1, next);
+ },
+ function (pids, next) {
+ privileges.posts.filter('read', pids, uid, next);
+ },
+ function (pids, next) {
+ posts.getPostSummaryByPids(pids, uid, { stripTags: false }, next);
+ },
+ ], callback);
+ };
+};
diff --git a/src/groups/search.js b/src/groups/search.js
index 3c580b5e8d..62df79696c 100644
--- a/src/groups/search.js
+++ b/src/groups/search.js
@@ -16,7 +16,7 @@ module.exports = function (Groups) {
async.apply(db.getObjectValues, 'groupslug:groupname'),
function (groupNames, next) {
// Ephemeral groups and the registered-users groups are searchable
- groupNames = Groups.getEphemeralGroups().concat(groupNames).concat('registered-users');
+ groupNames = Groups.ephemeralGroups.concat(groupNames).concat('registered-users');
groupNames = groupNames.filter(function (name) {
return name.toLowerCase().indexOf(query) !== -1 && name !== 'administrators' && !Groups.isPrivilegeGroup(name);
});
diff --git a/src/groups/user.js b/src/groups/user.js
new file mode 100644
index 0000000000..9a68478ade
--- /dev/null
+++ b/src/groups/user.js
@@ -0,0 +1,50 @@
+'use strict';
+
+var async = require('async');
+
+var db = require('../database');
+var user = require('../user');
+
+module.exports = function (Groups) {
+ Groups.getUsersFromSet = function (set, callback) {
+ async.waterfall([
+ function (next) {
+ db.getSetMembers(set, next);
+ },
+ function (uids, next) {
+ user.getUsersData(uids, next);
+ },
+ ], callback);
+ };
+
+ Groups.getUserGroups = function (uids, callback) {
+ Groups.getUserGroupsFromSet('groups:visible:createtime', uids, callback);
+ };
+
+ Groups.getUserGroupsFromSet = function (set, uids, callback) {
+ async.waterfall([
+ function (next) {
+ db.getSortedSetRevRange(set, 0, -1, next);
+ },
+ function (groupNames, next) {
+ async.map(uids, function (uid, next) {
+ async.waterfall([
+ function (next) {
+ Groups.isMemberOfGroups(uid, groupNames, next);
+ },
+ function (isMembers, next) {
+ var memberOf = [];
+ isMembers.forEach(function (isMember, index) {
+ if (isMember) {
+ memberOf.push(groupNames[index]);
+ }
+ });
+
+ Groups.getGroupsData(memberOf, next);
+ },
+ ], next);
+ }, next);
+ },
+ ], callback);
+ };
+};
diff --git a/src/meta/js.js b/src/meta/js.js
index b82aa34d25..dffd927055 100644
--- a/src/meta/js.js
+++ b/src/meta/js.js
@@ -58,6 +58,7 @@ module.exports = function (Meta) {
'public/src/client/topic/fork.js',
'public/src/client/topic/move.js',
'public/src/client/topic/posts.js',
+ 'public/src/client/topic/images.js',
'public/src/client/topic/postTools.js',
'public/src/client/topic/threadTools.js',
'public/src/client/categories.js',
diff --git a/src/meta/tags.js b/src/meta/tags.js
index 56269633ae..fa88fa30b8 100644
--- a/src/meta/tags.js
+++ b/src/meta/tags.js
@@ -131,10 +131,10 @@ module.exports = function (Meta) {
}
});
- if (!hasDescription) {
+ if (!hasDescription && Meta.config.description) {
meta.push({
name: 'description',
- content: validator.escape(String(Meta.config.description || '')),
+ content: validator.escape(String(Meta.config.description)),
});
}
}
diff --git a/src/meta/themes.js b/src/meta/themes.js
index e764010454..0d3be261e0 100644
--- a/src/meta/themes.js
+++ b/src/meta/themes.js
@@ -114,7 +114,7 @@ module.exports = function (Meta) {
themeData['theme:templates'] = config.templates ? config.templates : '';
themeData['theme:src'] = '';
- db.setObject('config', themeData, next);
+ Meta.configs.setMultiple(themeData, next);
// Re-set the themes path (for when NodeBB is reloaded)
Meta.themes.setPath(config);
@@ -125,7 +125,10 @@ module.exports = function (Meta) {
break;
case 'bootswatch':
- Meta.configs.set('theme:src', data.src, callback);
+ Meta.configs.setMultiple({
+ 'theme:src': data.src,
+ bootswatchSkin: data.id.toLowerCase(),
+ }, callback);
break;
}
};
diff --git a/src/middleware/header.js b/src/middleware/header.js
index accbbf2fe6..44a541f4bf 100644
--- a/src/middleware/header.js
+++ b/src/middleware/header.js
@@ -42,7 +42,6 @@ module.exports = function (middleware) {
middleware.renderHeader = function (req, res, data, callback) {
var registrationType = meta.config.registrationType || 'normal';
var templateValues = {
- bootswatchCSS: meta.config['theme:src'],
title: meta.config.title || '',
description: meta.config.description || '',
'cache-buster': meta.config['cache-buster'] || '',
@@ -117,9 +116,7 @@ module.exports = function (middleware) {
results.user['email:confirmed'] = parseInt(results.user['email:confirmed'], 10) === 1;
results.user.isEmailConfirmSent = !!results.isEmailConfirmSent;
- if (res.locals.config && parseInt(meta.config.disableCustomUserSkins, 10) !== 1 && res.locals.config.bootswatchSkin !== 'default') {
- templateValues.bootswatchCSS = '//maxcdn.bootstrapcdn.com/bootswatch/latest/' + res.locals.config.bootswatchSkin + '/bootstrap.min.css';
- }
+ setBootswatchCSS(templateValues, res.locals.config);
templateValues.browserTitle = controllers.helpers.buildTitle(data.title);
templateValues.navigation = results.navigation;
@@ -191,5 +188,21 @@ module.exports = function (middleware) {
return title;
}
+
+ function setBootswatchCSS(obj, config) {
+ if (config && config.bootswatchSkin !== 'default') {
+ var skinToUse = '';
+
+ if (parseInt(meta.config.disableCustomUserSkins, 10) !== 1) {
+ skinToUse = config.bootswatchSkin;
+ } else if (meta.config.bootswatchSkin !== 'default') {
+ skinToUse = meta.config.bootswatchSkin;
+ }
+
+ if (skinToUse) {
+ obj.bootswatchCSS = '//maxcdn.bootstrapcdn.com/bootswatch/latest/' + skinToUse + '/bootstrap.min.css';
+ }
+ }
+ }
};
diff --git a/src/middleware/index.js b/src/middleware/index.js
index 5a3f9aa5ba..de0bd6797e 100644
--- a/src/middleware/index.js
+++ b/src/middleware/index.js
@@ -53,22 +53,24 @@ middleware.ensureSelfOrGlobalPrivilege = function (req, res, next) {
The "self" part of this middleware hinges on you having used
middleware.exposeUid prior to invoking this middleware.
*/
- if (req.user) {
- if (req.user.uid === res.locals.uid) {
- return next();
- }
+ async.waterfall([
+ function (next) {
+ if (!req.uid) {
+ return setImmediate(next, null, false);
+ }
- user.isAdminOrGlobalMod(req.uid, function (err, ok) {
- if (err) {
- return next(err);
- } else if (ok) {
- return next();
+ if (req.uid === parseInt(res.locals.uid, 10)) {
+ return setImmediate(next, null, true);
}
- controllers.helpers.notAllowed(req, res);
- });
- } else {
- controllers.helpers.notAllowed(req, res);
- }
+ user.isAdminOrGlobalMod(req.uid, next);
+ },
+ function (isAdminOrGlobalMod, next) {
+ if (!isAdminOrGlobalMod) {
+ return controllers.helpers.notAllowed(req, res);
+ }
+ next();
+ },
+ ], next);
};
middleware.ensureSelfOrPrivileged = function (req, res, next) {
diff --git a/src/privileges/categories.js b/src/privileges/categories.js
index ff3d16148f..6678f5d5aa 100644
--- a/src/privileges/categories.js
+++ b/src/privileges/categories.js
@@ -96,7 +96,7 @@ module.exports = function (privileges) {
return groupName.indexOf(':privileges:') === -1 && uniqueGroups.indexOf(groupName) !== -1;
});
- groupNames = groups.getEphemeralGroups().concat(groupNames);
+ groupNames = groups.ephemeralGroups.concat(groupNames);
var registeredUsersIndex = groupNames.indexOf('registered-users');
if (registeredUsersIndex !== -1) {
groupNames.splice(0, 0, groupNames.splice(registeredUsersIndex, 1)[0]);
@@ -155,34 +155,36 @@ module.exports = function (privileges) {
privileges.categories.get = function (cid, uid, callback) {
var privs = ['topics:create', 'topics:read', 'read'];
- async.parallel({
- privileges: function (next) {
- helpers.isUserAllowedTo(privs, uid, cid, next);
- },
- isAdministrator: function (next) {
- user.isAdministrator(uid, next);
+ async.waterfall([
+ function (next) {
+ async.parallel({
+ privileges: function (next) {
+ helpers.isUserAllowedTo(privs, uid, cid, next);
+ },
+ isAdministrator: function (next) {
+ user.isAdministrator(uid, next);
+ },
+ isModerator: function (next) {
+ user.isModerator(uid, cid, next);
+ },
+ }, next);
},
- isModerator: function (next) {
- user.isModerator(uid, cid, next);
+ function (results, next) {
+ var privData = _.object(privs, results.privileges);
+ var isAdminOrMod = results.isAdministrator || results.isModerator;
+
+ plugins.fireHook('filter:privileges.categories.get', {
+ 'topics:create': privData['topics:create'] || isAdminOrMod,
+ 'topics:read': privData['topics:read'] || isAdminOrMod,
+ read: privData.read || isAdminOrMod,
+ cid: cid,
+ uid: uid,
+ editable: isAdminOrMod,
+ view_deleted: isAdminOrMod,
+ isAdminOrMod: isAdminOrMod,
+ }, next);
},
- }, function (err, results) {
- if (err) {
- return callback(err);
- }
- var privData = _.object(privs, results.privileges);
- var isAdminOrMod = results.isAdministrator || results.isModerator;
-
- plugins.fireHook('filter:privileges.categories.get', {
- 'topics:create': privData['topics:create'] || isAdminOrMod,
- 'topics:read': privData['topics:read'] || isAdminOrMod,
- read: privData.read || isAdminOrMod,
- cid: cid,
- uid: uid,
- editable: isAdminOrMod,
- view_deleted: isAdminOrMod,
- isAdminOrMod: isAdminOrMod,
- }, callback);
- });
+ ], callback);
};
privileges.categories.isAdminOrMod = function (cid, uid, callback) {
@@ -213,29 +215,29 @@ module.exports = function (privileges) {
return callback(null, false);
}
- categories.getCategoryField(cid, 'disabled', function (err, disabled) {
- if (err) {
- return callback(err);
- }
-
- if (parseInt(disabled, 10) === 1) {
- return callback(null, false);
- }
-
- helpers.some([
- function (next) {
- helpers.isUserAllowedTo(privilege, uid, [cid], function (err, results) {
- next(err, Array.isArray(results) && results.length ? results[0] : false);
- });
- },
- function (next) {
- user.isModerator(uid, cid, next);
- },
- function (next) {
- user.isAdministrator(uid, next);
- },
- ], callback);
- });
+ async.waterfall([
+ function (next) {
+ categories.getCategoryField(cid, 'disabled', next);
+ },
+ function (disabled, next) {
+ if (parseInt(disabled, 10) === 1) {
+ return callback(null, false);
+ }
+ helpers.some([
+ function (next) {
+ helpers.isUserAllowedTo(privilege, uid, [cid], function (err, results) {
+ next(err, Array.isArray(results) && results.length ? results[0] : false);
+ });
+ },
+ function (next) {
+ user.isModerator(uid, cid, next);
+ },
+ function (next) {
+ user.isAdministrator(uid, next);
+ },
+ ], next);
+ },
+ ], callback);
};
privileges.categories.filterCids = function (privilege, cids, uid, callback) {
@@ -247,18 +249,19 @@ module.exports = function (privileges) {
return array.indexOf(cid) === index;
});
- privileges.categories.getBase(privilege, cids, uid, function (err, results) {
- if (err) {
- return callback(err);
- }
-
- cids = cids.filter(function (cid, index) {
- return !results.categories[index].disabled &&
- (results.allowedTo[index] || results.isAdmin || results.isModerators[index]);
- });
+ async.waterfall([
+ function (next) {
+ privileges.categories.getBase(privilege, cids, uid, next);
+ },
+ function (results, next) {
+ cids = cids.filter(function (cid, index) {
+ return !results.categories[index].disabled &&
+ (results.allowedTo[index] || results.isAdmin || results.isModerators[index]);
+ });
- callback(null, cids.filter(Boolean));
- });
+ next(null, cids.filter(Boolean));
+ },
+ ], callback);
};
privileges.categories.getBase = function (privilege, cids, uid, callback) {
@@ -287,26 +290,27 @@ module.exports = function (privileges) {
return array.indexOf(uid) === index;
});
- async.parallel({
- allowedTo: function (next) {
- helpers.isUsersAllowedTo(privilege, uids, cid, next);
- },
- isModerators: function (next) {
- user.isModerator(uids, cid, next);
+ async.waterfall([
+ function (next) {
+ async.parallel({
+ allowedTo: function (next) {
+ helpers.isUsersAllowedTo(privilege, uids, cid, next);
+ },
+ isModerators: function (next) {
+ user.isModerator(uids, cid, next);
+ },
+ isAdmin: function (next) {
+ user.isAdministrator(uids, next);
+ },
+ }, next);
},
- isAdmin: function (next) {
- user.isAdministrator(uids, next);
+ function (results, next) {
+ uids = uids.filter(function (uid, index) {
+ return results.allowedTo[index] || results.isModerators[index] || results.isAdmin[index];
+ });
+ next(null, uids);
},
- }, function (err, results) {
- if (err) {
- return callback(err);
- }
-
- uids = uids.filter(function (uid, index) {
- return results.allowedTo[index] || results.isModerators[index] || results.isAdmin[index];
- });
- callback(null, uids);
- });
+ ], callback);
};
privileges.categories.give = function (privileges, cid, groupName, callback) {
@@ -324,23 +328,24 @@ module.exports = function (privileges) {
}
privileges.categories.canMoveAllTopics = function (currentCid, targetCid, uid, callback) {
- async.parallel({
- isAdministrator: function (next) {
- user.isAdministrator(uid, next);
- },
- moderatorOfCurrent: function (next) {
- user.isModerator(uid, currentCid, next);
+ async.waterfall([
+ function (next) {
+ async.parallel({
+ isAdministrator: function (next) {
+ user.isAdministrator(uid, next);
+ },
+ moderatorOfCurrent: function (next) {
+ user.isModerator(uid, currentCid, next);
+ },
+ moderatorOfTarget: function (next) {
+ user.isModerator(uid, targetCid, next);
+ },
+ }, next);
},
- moderatorOfTarget: function (next) {
- user.isModerator(uid, targetCid, next);
+ function (results, next) {
+ next(null, results.isAdministrator || (results.moderatorOfCurrent && results.moderatorOfTarget));
},
- }, function (err, results) {
- if (err) {
- return callback(err);
- }
-
- callback(null, results.isAdministrator || (results.moderatorOfCurrent && results.moderatorOfTarget));
- });
+ ], callback);
};
privileges.categories.userPrivileges = function (cid, uid, callback) {
diff --git a/src/routes/api.js b/src/routes/api.js
index 16ead5c588..9b5a7f77c7 100644
--- a/src/routes/api.js
+++ b/src/routes/api.js
@@ -11,17 +11,17 @@ module.exports = function (app, middleware, controllers) {
router.get('/config', middleware.applyCSRF, controllers.api.getConfig);
router.get('/widgets/render', controllers.api.renderWidgets);
- router.get('/me', middleware.checkGlobalPrivacySettings, controllers.api.getCurrentUser);
- router.get('/user/uid/:uid', middleware.checkGlobalPrivacySettings, controllers.api.getUserByUID);
- router.get('/user/username/:username', middleware.checkGlobalPrivacySettings, controllers.api.getUserByUsername);
- router.get('/user/email/:email', middleware.checkGlobalPrivacySettings, controllers.api.getUserByEmail);
+ router.get('/me', middleware.checkGlobalPrivacySettings, controllers.user.getCurrentUser);
+ router.get('/user/uid/:uid', middleware.checkGlobalPrivacySettings, controllers.user.getUserByUID);
+ router.get('/user/username/:username', middleware.checkGlobalPrivacySettings, controllers.user.getUserByUsername);
+ router.get('/user/email/:email', middleware.checkGlobalPrivacySettings, controllers.user.getUserByEmail);
router.get('/:type/pid/:id', controllers.api.getObject);
router.get('/:type/tid/:id', controllers.api.getObject);
router.get('/:type/cid/:id', controllers.api.getObject);
router.get('/categories/:cid/moderators', controllers.api.getModerators);
- router.get('/recent/posts/:term?', controllers.api.getRecentPosts);
+ router.get('/recent/posts/:term?', controllers.posts.getRecentPosts);
router.get('/unread/:filter?/total', middleware.authenticate, controllers.unread.unreadTotal);
router.get('/topic/teaser/:topic_id', controllers.topics.teaser);
router.get('/topic/pagination/:topic_id', controllers.topics.pagination);
diff --git a/src/routes/index.js b/src/routes/index.js
index f1a833fbf6..6d5fcfc018 100644
--- a/src/routes/index.js
+++ b/src/routes/index.js
@@ -199,9 +199,9 @@ module.exports = function (app, middleware, hotswapIds) {
});
app.use(relativePath + '/assets/vendor/jquery/timeago/locales', middleware.processTimeagoLocales);
- app.use(controllers.handle404);
- app.use(controllers.handleURIErrors);
- app.use(controllers.handleErrors);
+ app.use(controllers['404'].handle404);
+ app.use(controllers.errors.handleURIErrors);
+ app.use(controllers.errors.handleErrors);
// Add plugin routes
async.series([
diff --git a/src/socket.io/topics.js b/src/socket.io/topics.js
index 5eb9ce61dd..3b8a69d0f4 100644
--- a/src/socket.io/topics.js
+++ b/src/socket.io/topics.js
@@ -1,12 +1,14 @@
'use strict';
+var async = require('async');
+
var topics = require('../topics');
var websockets = require('./index');
var user = require('../user');
var apiController = require('../controllers/api');
var socketHelpers = require('./helpers');
-var SocketTopics = {};
+var SocketTopics = module.exports;
require('./topics/unread')(SocketTopics);
require('./topics/move')(SocketTopics);
@@ -23,18 +25,19 @@ SocketTopics.post = function (socket, data, callback) {
data.req = websockets.reqFromSocket(socket);
data.timestamp = Date.now();
- topics.post(data, function (err, result) {
- if (err) {
- return callback(err);
- }
-
- callback(null, result.topicData);
+ async.waterfall([
+ function (next) {
+ topics.post(data, next);
+ },
+ function (result, next) {
+ next(null, result.topicData);
- socket.emit('event:new_post', { posts: [result.postData] });
- socket.emit('event:new_topic', result.topicData);
+ socket.emit('event:new_post', { posts: [result.postData] });
+ socket.emit('event:new_topic', result.topicData);
- socketHelpers.notifyNew(socket.uid, 'newTopic', { posts: [result.postData], topic: result.topicData });
- });
+ socketHelpers.notifyNew(socket.uid, 'newTopic', { posts: [result.postData], topic: result.topicData });
+ },
+ ], callback);
};
SocketTopics.postcount = function (socket, tid, callback) {
@@ -61,7 +64,7 @@ SocketTopics.createTopicFromPosts = function (socket, data, callback) {
};
SocketTopics.changeWatching = function (socket, data, callback) {
- if (!data.tid || !data.type) {
+ if (!data || !data.tid || !data.type) {
return callback(new Error('[[error:invalid-data]]'));
}
var commands = ['follow', 'unfollow', 'ignore'];
@@ -90,20 +93,23 @@ SocketTopics.isFollowed = function (socket, tid, callback) {
};
SocketTopics.search = function (socket, data, callback) {
+ if (!data) {
+ return callback(new Error('[[error:invalid-data]]'));
+ }
topics.search(data.tid, data.term, callback);
};
SocketTopics.isModerator = function (socket, tid, callback) {
- topics.getTopicField(tid, 'cid', function (err, cid) {
- if (err) {
- return callback(err);
- }
- user.isModerator(socket.uid, cid, callback);
- });
+ async.waterfall([
+ function (next) {
+ topics.getTopicField(tid, 'cid', next);
+ },
+ function (cid, next) {
+ user.isModerator(socket.uid, cid, next);
+ },
+ ], callback);
};
SocketTopics.getTopic = function (socket, tid, callback) {
apiController.getTopicData(tid, socket.uid, callback);
};
-
-module.exports = SocketTopics;
diff --git a/src/socket.io/user.js b/src/socket.io/user.js
index f7208b116a..78f696a19b 100644
--- a/src/socket.io/user.js
+++ b/src/socket.io/user.js
@@ -12,7 +12,7 @@ var meta = require('../meta');
var events = require('../events');
var emailer = require('../emailer');
var db = require('../database');
-var apiController = require('../controllers/api');
+var userController = require('../controllers/user');
var privileges = require('../privileges');
var SocketUser = {};
@@ -303,15 +303,15 @@ SocketUser.invite = function (socket, email, callback) {
};
SocketUser.getUserByUID = function (socket, uid, callback) {
- apiController.getUserDataByField(socket.uid, 'uid', uid, callback);
+ userController.getUserDataByField(socket.uid, 'uid', uid, callback);
};
SocketUser.getUserByUsername = function (socket, username, callback) {
- apiController.getUserDataByField(socket.uid, 'username', username, callback);
+ userController.getUserDataByField(socket.uid, 'username', username, callback);
};
SocketUser.getUserByEmail = function (socket, email, callback) {
- apiController.getUserDataByField(socket.uid, 'email', email, callback);
+ userController.getUserDataByField(socket.uid, 'email', email, callback);
};
SocketUser.setModerationNote = function (socket, data, callback) {
diff --git a/src/topics.js b/src/topics.js
index 0eba6545fd..77cea4ec98 100644
--- a/src/topics.js
+++ b/src/topics.js
@@ -317,7 +317,7 @@ var social = require('./social');
term: term,
}, callback);
} else {
- callback(new Error('no-plugins-available'), []);
+ callback(new Error('[[error:no-plugins-available]]'), []);
}
};
}(exports));
diff --git a/src/topics/create.js b/src/topics/create.js
index d0e2c37e7b..418098cbd9 100644
--- a/src/topics/create.js
+++ b/src/topics/create.js
@@ -330,7 +330,7 @@ module.exports = function (Topics) {
function check(item, min, max, minError, maxError, callback) {
// Trim and remove HTML (latter for composers that send in HTML, like redactor)
if (typeof item === 'string') {
- item = S(item.trim()).stripTags().s;
+ item = S(item).stripTags().s.trim();
}
if (!item || item.length < parseInt(min, 10)) {
diff --git a/src/user.js b/src/user.js
index 6cd1490520..48098feadf 100644
--- a/src/user.js
+++ b/src/user.js
@@ -6,388 +6,324 @@ var _ = require('underscore');
var groups = require('./groups');
var plugins = require('./plugins');
var db = require('./database');
-var topics = require('./topics');
var privileges = require('./privileges');
var meta = require('./meta');
-(function (User) {
- User.email = require('./user/email');
- User.notifications = require('./user/notifications');
- User.reset = require('./user/reset');
- User.digest = require('./user/digest');
-
- require('./user/data')(User);
- require('./user/auth')(User);
- require('./user/bans')(User);
- require('./user/create')(User);
- require('./user/posts')(User);
- require('./user/topics')(User);
- require('./user/categories')(User);
- require('./user/follow')(User);
- require('./user/profile')(User);
- require('./user/admin')(User);
- require('./user/delete')(User);
- require('./user/settings')(User);
- require('./user/search')(User);
- require('./user/jobs')(User);
- require('./user/picture')(User);
- require('./user/approval')(User);
- require('./user/invite')(User);
- require('./user/password')(User);
- require('./user/info')(User);
-
- User.updateLastOnlineTime = function (uid, callback) {
- callback = callback || function () {};
- db.getObjectFields('user:' + uid, ['status', 'lastonline'], function (err, userData) {
- var now = Date.now();
- if (err || userData.status === 'offline' || now - parseInt(userData.lastonline, 10) < 300000) {
- return callback(err);
- }
- User.setUserField(uid, 'lastonline', now, callback);
- });
- };
-
- User.updateOnlineUsers = function (uid, callback) {
- callback = callback || function () {};
-
+var User = module.exports;
+
+User.email = require('./user/email');
+User.notifications = require('./user/notifications');
+User.reset = require('./user/reset');
+User.digest = require('./user/digest');
+
+require('./user/data')(User);
+require('./user/auth')(User);
+require('./user/bans')(User);
+require('./user/create')(User);
+require('./user/posts')(User);
+require('./user/topics')(User);
+require('./user/categories')(User);
+require('./user/follow')(User);
+require('./user/profile')(User);
+require('./user/admin')(User);
+require('./user/delete')(User);
+require('./user/settings')(User);
+require('./user/search')(User);
+require('./user/jobs')(User);
+require('./user/picture')(User);
+require('./user/approval')(User);
+require('./user/invite')(User);
+require('./user/password')(User);
+require('./user/info')(User);
+require('./user/online')(User);
+
+User.getUidsFromSet = function (set, start, stop, callback) {
+ if (set === 'users:online') {
+ var count = parseInt(stop, 10) === -1 ? stop : stop - start + 1;
var now = Date.now();
- async.waterfall([
- function (next) {
- db.sortedSetScore('users:online', uid, next);
- },
- function (userOnlineTime, next) {
- if (now - parseInt(userOnlineTime, 10) < 300000) {
- return callback();
- }
- db.sortedSetAdd('users:online', now, uid, next);
- },
- function (next) {
- topics.pushUnreadCount(uid);
- plugins.fireHook('action:user.online', { uid: uid, timestamp: now });
- next();
- },
- ], callback);
- };
-
- User.getUidsFromSet = function (set, start, stop, callback) {
- if (set === 'users:online') {
- var count = parseInt(stop, 10) === -1 ? stop : stop - start + 1;
- var now = Date.now();
- db.getSortedSetRevRangeByScore(set, start, count, '+inf', now - 300000, callback);
- } else {
- db.getSortedSetRevRange(set, start, stop, callback);
- }
- };
-
- User.getUsersFromSet = function (set, uid, start, stop, callback) {
- async.waterfall([
- function (next) {
- User.getUidsFromSet(set, start, stop, next);
- },
- function (uids, next) {
- User.getUsers(uids, uid, next);
- },
- ], callback);
- };
-
- User.getUsersWithFields = function (uids, fields, uid, callback) {
- async.waterfall([
- function (next) {
- plugins.fireHook('filter:users.addFields', { fields: fields }, next);
- },
- function (data, next) {
- data.fields = data.fields.filter(function (field, index, array) {
- return array.indexOf(field) === index;
- });
-
- async.parallel({
- userData: function (next) {
- User.getUsersFields(uids, data.fields, next);
- },
- isAdmin: function (next) {
- User.isAdministrator(uids, next);
- },
- }, next);
- },
- function (results, next) {
- results.userData.forEach(function (user, index) {
- if (user) {
- user.status = User.getStatus(user);
- user.administrator = results.isAdmin[index];
- user.banned = parseInt(user.banned, 10) === 1;
- user.banned_until = parseInt(user['banned:expire'], 10) || 0;
- user.banned_until_readable = user.banned_until ? new Date(user.banned_until).toString() : 'Not Banned';
- user['email:confirmed'] = parseInt(user['email:confirmed'], 10) === 1;
- }
- });
- plugins.fireHook('filter:userlist.get', { users: results.userData, uid: uid }, next);
- },
- function (data, next) {
- next(null, data.users);
- },
- ], callback);
- };
-
- User.getUsers = function (uids, uid, callback) {
- var fields = ['uid', 'username', 'userslug', 'picture', 'status', 'flags',
- 'banned', 'banned:expire', 'joindate', 'postcount', 'reputation', 'email:confirmed', 'lastonline'];
-
- User.getUsersWithFields(uids, fields, uid, callback);
- };
-
- User.getStatus = function (userData) {
- var isOnline = (Date.now() - parseInt(userData.lastonline, 10)) < 300000;
- return isOnline ? (userData.status || 'online') : 'offline';
- };
-
- User.isOnline = function (uid, callback) {
- if (Array.isArray(uid)) {
- db.sortedSetScores('users:online', uid, function (err, lastonline) {
- if (err) {
- return callback(err);
- }
- var now = Date.now();
- var isOnline = uid.map(function (uid, index) {
- return now - lastonline[index] < 300000;
- });
- callback(null, isOnline);
+ db.getSortedSetRevRangeByScore(set, start, count, '+inf', now - 300000, callback);
+ } else {
+ db.getSortedSetRevRange(set, start, stop, callback);
+ }
+};
+
+User.getUsersFromSet = function (set, uid, start, stop, callback) {
+ async.waterfall([
+ function (next) {
+ User.getUidsFromSet(set, start, stop, next);
+ },
+ function (uids, next) {
+ User.getUsers(uids, uid, next);
+ },
+ ], callback);
+};
+
+User.getUsersWithFields = function (uids, fields, uid, callback) {
+ async.waterfall([
+ function (next) {
+ plugins.fireHook('filter:users.addFields', { fields: fields }, next);
+ },
+ function (data, next) {
+ data.fields = data.fields.filter(function (field, index, array) {
+ return array.indexOf(field) === index;
});
- } else {
- db.sortedSetScore('users:online', uid, function (err, lastonline) {
- if (err) {
- return callback(err);
+
+ async.parallel({
+ userData: function (next) {
+ User.getUsersFields(uids, data.fields, next);
+ },
+ isAdmin: function (next) {
+ User.isAdministrator(uids, next);
+ },
+ }, next);
+ },
+ function (results, next) {
+ results.userData.forEach(function (user, index) {
+ if (user) {
+ user.status = User.getStatus(user);
+ user.administrator = results.isAdmin[index];
+ user.banned = parseInt(user.banned, 10) === 1;
+ user.banned_until = parseInt(user['banned:expire'], 10) || 0;
+ user.banned_until_readable = user.banned_until ? new Date(user.banned_until).toString() : 'Not Banned';
+ user['email:confirmed'] = parseInt(user['email:confirmed'], 10) === 1;
}
- var isOnline = Date.now() - parseInt(lastonline, 10) < 300000;
- callback(null, isOnline);
});
- }
- };
-
- User.exists = function (uid, callback) {
- db.isSortedSetMember('users:joindate', uid, callback);
- };
-
- User.existsBySlug = function (userslug, callback) {
- User.getUidByUserslug(userslug, function (err, exists) {
- callback(err, !!exists);
- });
- };
-
- User.getUidByUsername = function (username, callback) {
- if (!username) {
- return callback(null, 0);
- }
- db.sortedSetScore('username:uid', username, callback);
- };
-
- User.getUidsByUsernames = function (usernames, callback) {
- db.sortedSetScores('username:uid', usernames, callback);
- };
-
- User.getUidByUserslug = function (userslug, callback) {
- if (!userslug) {
- return callback(null, 0);
- }
- db.sortedSetScore('userslug:uid', userslug, callback);
- };
-
- User.getUsernamesByUids = function (uids, callback) {
- User.getUsersFields(uids, ['username'], function (err, users) {
- if (err) {
- return callback(err);
- }
-
+ plugins.fireHook('filter:userlist.get', { users: results.userData, uid: uid }, next);
+ },
+ function (data, next) {
+ next(null, data.users);
+ },
+ ], callback);
+};
+
+User.getUsers = function (uids, uid, callback) {
+ var fields = ['uid', 'username', 'userslug', 'picture', 'status', 'flags',
+ 'banned', 'banned:expire', 'joindate', 'postcount', 'reputation', 'email:confirmed', 'lastonline'];
+
+ User.getUsersWithFields(uids, fields, uid, callback);
+};
+
+User.getStatus = function (userData) {
+ var isOnline = (Date.now() - parseInt(userData.lastonline, 10)) < 300000;
+ return isOnline ? (userData.status || 'online') : 'offline';
+};
+
+User.exists = function (uid, callback) {
+ db.isSortedSetMember('users:joindate', uid, callback);
+};
+
+User.existsBySlug = function (userslug, callback) {
+ User.getUidByUserslug(userslug, function (err, exists) {
+ callback(err, !!exists);
+ });
+};
+
+User.getUidByUsername = function (username, callback) {
+ if (!username) {
+ return callback(null, 0);
+ }
+ db.sortedSetScore('username:uid', username, callback);
+};
+
+User.getUidsByUsernames = function (usernames, callback) {
+ db.sortedSetScores('username:uid', usernames, callback);
+};
+
+User.getUidByUserslug = function (userslug, callback) {
+ if (!userslug) {
+ return callback(null, 0);
+ }
+ db.sortedSetScore('userslug:uid', userslug, callback);
+};
+
+User.getUsernamesByUids = function (uids, callback) {
+ async.waterfall([
+ function (next) {
+ User.getUsersFields(uids, ['username'], next);
+ },
+ function (users, next) {
users = users.map(function (user) {
return user.username;
});
- callback(null, users);
- });
- };
-
- User.getUsernameByUserslug = function (slug, callback) {
- async.waterfall([
- function (next) {
- User.getUidByUserslug(slug, next);
- },
- function (uid, next) {
- User.getUserField(uid, 'username', next);
- },
- ], callback);
- };
-
- User.getUidByEmail = function (email, callback) {
- db.sortedSetScore('email:uid', email.toLowerCase(), callback);
- };
-
- User.getUidsByEmails = function (emails, callback) {
- emails = emails.map(function (email) {
- return email && email.toLowerCase();
- });
- db.sortedSetScores('email:uid', emails, callback);
- };
-
- User.getUsernameByEmail = function (email, callback) {
- db.sortedSetScore('email:uid', email.toLowerCase(), function (err, uid) {
- if (err) {
- return callback(err);
- }
- User.getUserField(uid, 'username', callback);
- });
- };
-
- User.isModerator = function (uid, cid, callback) {
- privileges.users.isModerator(uid, cid, callback);
- };
-
- User.isModeratorOfAnyCategory = function (uid, callback) {
- User.getModeratedCids(uid, function (err, cids) {
- callback(err, Array.isArray(cids) ? !!cids.length : false);
- });
- };
-
- User.isAdministrator = function (uid, callback) {
- privileges.users.isAdministrator(uid, callback);
- };
-
- User.isGlobalModerator = function (uid, callback) {
- privileges.users.isGlobalModerator(uid, callback);
- };
-
- User.isPrivileged = function (uid, callback) {
- async.parallel([
- async.apply(User.isAdministrator, uid),
- async.apply(User.isGlobalModerator, uid),
- async.apply(User.isModeratorOfAnyCategory, uid),
- ], function (err, results) {
- callback(err, results ? results.some(Boolean) : false);
- });
- };
-
- User.isAdminOrGlobalMod = function (uid, callback) {
- async.parallel({
- isAdmin: async.apply(User.isAdministrator, uid),
- isGlobalMod: async.apply(User.isGlobalModerator, uid),
- }, function (err, results) {
- callback(err, results ? (results.isAdmin || results.isGlobalMod) : false);
- });
- };
-
- User.isAdminOrSelf = function (callerUid, uid, callback) {
- if (parseInt(callerUid, 10) === parseInt(uid, 10)) {
- return callback();
- }
- User.isAdministrator(callerUid, function (err, isAdmin) {
- if (err || !isAdmin) {
- return callback(err || new Error('[[error:no-privileges]]'));
- }
- callback();
- });
- };
-
- User.isAdminOrGlobalModOrSelf = function (callerUid, uid, callback) {
- if (parseInt(callerUid, 10) === parseInt(uid, 10)) {
- return callback();
- }
- User.isAdminOrGlobalMod(callerUid, function (err, isAdminOrGlobalMod) {
- if (err || !isAdminOrGlobalMod) {
- return callback(err || new Error('[[error:no-privileges]]'));
- }
- callback();
- });
- };
-
- User.getAdminsandGlobalMods = function (callback) {
- async.parallel({
- admins: async.apply(groups.getMembers, 'administrators', 0, -1),
- mods: async.apply(groups.getMembers, 'Global Moderators', 0, -1),
- }, function (err, results) {
- if (err) {
- return callback(err);
+ next(null, users);
+ },
+ ], callback);
+};
+
+User.getUsernameByUserslug = function (slug, callback) {
+ async.waterfall([
+ function (next) {
+ User.getUidByUserslug(slug, next);
+ },
+ function (uid, next) {
+ User.getUserField(uid, 'username', next);
+ },
+ ], callback);
+};
+
+User.getUidByEmail = function (email, callback) {
+ db.sortedSetScore('email:uid', email.toLowerCase(), callback);
+};
+
+User.getUidsByEmails = function (emails, callback) {
+ emails = emails.map(function (email) {
+ return email && email.toLowerCase();
+ });
+ db.sortedSetScores('email:uid', emails, callback);
+};
+
+User.getUsernameByEmail = function (email, callback) {
+ async.waterfall([
+ function (next) {
+ db.sortedSetScore('email:uid', email.toLowerCase(), next);
+ },
+ function (uid, next) {
+ User.getUserField(uid, 'username', next);
+ },
+ ], callback);
+};
+
+User.isModerator = function (uid, cid, callback) {
+ privileges.users.isModerator(uid, cid, callback);
+};
+
+User.isModeratorOfAnyCategory = function (uid, callback) {
+ User.getModeratedCids(uid, function (err, cids) {
+ callback(err, Array.isArray(cids) ? !!cids.length : false);
+ });
+};
+
+User.isAdministrator = function (uid, callback) {
+ privileges.users.isAdministrator(uid, callback);
+};
+
+User.isGlobalModerator = function (uid, callback) {
+ privileges.users.isGlobalModerator(uid, callback);
+};
+
+User.isAdminOrGlobalMod = function (uid, callback) {
+ async.parallel({
+ isAdmin: async.apply(User.isAdministrator, uid),
+ isGlobalMod: async.apply(User.isGlobalModerator, uid),
+ }, function (err, results) {
+ callback(err, results ? (results.isAdmin || results.isGlobalMod) : false);
+ });
+};
+
+User.isAdminOrSelf = function (callerUid, uid, callback) {
+ isSelfOrMethod(callerUid, uid, User.isAdministrator, callback);
+};
+
+User.isAdminOrGlobalModOrSelf = function (callerUid, uid, callback) {
+ isSelfOrMethod(callerUid, uid, User.isAdminOrGlobalMod, callback);
+};
+
+function isSelfOrMethod(callerUid, uid, method, callback) {
+ if (parseInt(callerUid, 10) === parseInt(uid, 10)) {
+ return callback();
+ }
+ async.waterfall([
+ function (next) {
+ method(callerUid, next);
+ },
+ function (isPass, next) {
+ if (!isPass) {
+ return next(new Error('[[error:no-privileges]]'));
}
- var uids = results.admins.concat(results.mods).filter(function (uid, index, array) {
- return uid && array.indexOf(uid) === index;
+ next();
+ },
+ ], callback);
+}
+
+User.getAdminsandGlobalMods = function (callback) {
+ async.waterfall([
+ function (next) {
+ async.parallel([
+ async.apply(groups.getMembers, 'administrators', 0, -1),
+ async.apply(groups.getMembers, 'Global Moderators', 0, -1),
+ ], next);
+ },
+ function (results, next) {
+ User.getUsersData(_.union(results), next);
+ },
+ ], callback);
+};
+
+User.getAdminsandGlobalModsandModerators = function (callback) {
+ async.waterfall([
+ function (next) {
+ async.parallel([
+ async.apply(groups.getMembers, 'administrators', 0, -1),
+ async.apply(groups.getMembers, 'Global Moderators', 0, -1),
+ async.apply(User.getModeratorUids),
+ ], next);
+ },
+ function (results, next) {
+ User.getUsersData(_.union.apply(_, results), next);
+ },
+ ], callback);
+};
+
+User.getModeratorUids = function (callback) {
+ async.waterfall([
+ async.apply(db.getSortedSetRange, 'categories:cid', 0, -1),
+ function (cids, next) {
+ var groupNames = cids.map(function (cid) {
+ return 'cid:' + cid + ':privileges:mods';
});
- User.getUsersData(uids, callback);
- });
- };
-
- User.getAdminsandGlobalModsandModerators = function (callback) {
- async.parallel([
- async.apply(groups.getMembers, 'administrators', 0, -1),
- async.apply(groups.getMembers, 'Global Moderators', 0, -1),
- async.apply(User.getModeratorUids),
- ], function (err, results) {
- if (err) {
- return callback(err);
- }
- User.getUsersData(_.union.apply(_, results), callback);
- });
- };
+ groups.getMembersOfGroups(groupNames, next);
+ },
+ function (memberSets, next) {
+ next(null, _.union.apply(_, memberSets));
+ },
+ ], callback);
+};
+
+User.getModeratedCids = function (uid, callback) {
+ var cids;
+ async.waterfall([
+ function (next) {
+ db.getSortedSetRange('categories:cid', 0, -1, next);
+ },
+ function (_cids, next) {
+ cids = _cids;
+ User.isModerator(uid, cids, next);
+ },
+ function (isMods, next) {
+ cids = cids.filter(function (cid, index) {
+ return cid && isMods[index];
+ });
+ next(null, cids);
+ },
+ ], callback);
+};
+
+User.addInterstitials = function (callback) {
+ plugins.registerHook('core', {
+ hook: 'filter:register.interstitial',
+ method: function (data, callback) {
+ if (meta.config.termsOfUse && !data.userData.acceptTos) {
+ data.interstitials.push({
+ template: 'partials/acceptTos',
+ data: {
+ termsOfUse: meta.config.termsOfUse,
+ },
+ callback: function (userData, formData, next) {
+ if (formData['agree-terms'] === 'on') {
+ userData.acceptTos = true;
+ }
- User.getModeratorUids = function (callback) {
- async.waterfall([
- async.apply(db.getSortedSetRange, 'categories:cid', 0, -1),
- function (cids, next) {
- var groupNames = cids.map(function (cid) {
- return 'cid:' + cid + ':privileges:mods';
+ next(userData.acceptTos ? null : new Error('[[register:terms_of_use_error]]'));
+ },
});
+ }
- groups.getMembersOfGroups(groupNames, function (err, memberSets) {
- if (err) {
- return next(err);
- }
-
- next(null, _.union.apply(_, memberSets));
- });
- },
- ], callback);
- };
-
- User.getModeratedCids = function (uid, callback) {
- var cids;
- async.waterfall([
- function (next) {
- db.getSortedSetRange('categories:cid', 0, -1, next);
- },
- function (_cids, next) {
- cids = _cids;
- User.isModerator(uid, cids, next);
- },
- function (isMods, next) {
- cids = cids.filter(function (cid, index) {
- return cid && isMods[index];
- });
- next(null, cids);
- },
- ], callback);
- };
-
- User.addInterstitials = function (callback) {
- plugins.registerHook('core', {
- hook: 'filter:register.interstitial',
- method: function (data, callback) {
- if (meta.config.termsOfUse && !data.userData.acceptTos) {
- data.interstitials.push({
- template: 'partials/acceptTos',
- data: {
- termsOfUse: meta.config.termsOfUse,
- },
- callback: function (userData, formData, next) {
- if (formData['agree-terms'] === 'on') {
- userData.acceptTos = true;
- }
-
- next(userData.acceptTos ? null : new Error('[[register:terms_of_use_error]]'));
- },
- });
- }
+ callback(null, data);
+ },
+ });
- callback(null, data);
- },
- });
+ callback();
+};
- callback();
- };
-}(exports));
diff --git a/src/user/admin.js b/src/user/admin.js
index 28668cff80..d463523f89 100644
--- a/src/user/admin.js
+++ b/src/user/admin.js
@@ -16,13 +16,7 @@ module.exports = function (User) {
};
User.getIPs = function (uid, stop, callback) {
- db.getSortedSetRevRange('uid:' + uid + ':ip', 0, stop, function (err, ips) {
- if (err) {
- return callback(err);
- }
-
- callback(null, ips);
- });
+ db.getSortedSetRevRange('uid:' + uid + ':ip', 0, stop, callback);
};
User.getUsersCSV = function (callback) {
diff --git a/src/user/data.js b/src/user/data.js
index 2dca1b4143..22810999f8 100644
--- a/src/user/data.js
+++ b/src/user/data.js
@@ -1,5 +1,6 @@
'use strict';
+var async = require('async');
var validator = require('validator');
var nconf = require('nconf');
var winston = require('winston');
@@ -63,6 +64,7 @@ module.exports = function (User) {
addField('lastonline');
}
+<<<<<<< HEAD
db.getObjectsFields(keys, fields, function (err, users) {
if (err) {
return callback(err);
@@ -74,6 +76,16 @@ module.exports = function (User) {
modifyUserData(users, fieldsToRemove, callback);
});
+=======
+ async.waterfall([
+ function (next) {
+ db.getObjectsFields(keys, fields, next);
+ },
+ function (users, next) {
+ modifyUserData(users, fieldsToRemove, next);
+ },
+ ], callback);
+>>>>>>> master
};
User.getMultipleUserFields = function (uids, fields, callback) {
@@ -105,6 +117,7 @@ module.exports = function (User) {
return 'user:' + uid;
});
+<<<<<<< HEAD
db.getObjects(keys, function (err, users) {
if (err) {
return callback(err);
@@ -116,6 +129,16 @@ module.exports = function (User) {
modifyUserData(users, [], callback);
});
+=======
+ async.waterfall([
+ function (next) {
+ db.getObjects(keys, next);
+ },
+ function (users, next) {
+ modifyUserData(users, [], next);
+ },
+ ], callback);
+>>>>>>> master
};
function modifyUserData(users, fieldsToRemove, callback) {
@@ -178,51 +201,53 @@ module.exports = function (User) {
User.setUserField = function (uid, field, value, callback) {
callback = callback || function () {};
- db.setObjectField('user:' + uid, field, value, function (err) {
- if (err) {
- return callback(err);
- }
- plugins.fireHook('action:user.set', { uid: uid, field: field, value: value, type: 'set' });
- callback();
- });
+ async.waterfall([
+ function (next) {
+ db.setObjectField('user:' + uid, field, value, next);
+ },
+ function (next) {
+ plugins.fireHook('action:user.set', { uid: uid, field: field, value: value, type: 'set' });
+ next();
+ },
+ ], callback);
};
User.setUserFields = function (uid, data, callback) {
callback = callback || function () {};
- db.setObject('user:' + uid, data, function (err) {
- if (err) {
- return callback(err);
- }
- for (var field in data) {
- if (data.hasOwnProperty(field)) {
- plugins.fireHook('action:user.set', { uid: uid, field: field, value: data[field], type: 'set' });
+ async.waterfall([
+ function (next) {
+ db.setObject('user:' + uid, data, next);
+ },
+ function (next) {
+ for (var field in data) {
+ if (data.hasOwnProperty(field)) {
+ plugins.fireHook('action:user.set', { uid: uid, field: field, value: data[field], type: 'set' });
+ }
}
- }
- callback();
- });
+ next();
+ },
+ ], callback);
};
User.incrementUserFieldBy = function (uid, field, value, callback) {
- callback = callback || function () {};
- db.incrObjectFieldBy('user:' + uid, field, value, function (err, value) {
- if (err) {
- return callback(err);
- }
- plugins.fireHook('action:user.set', { uid: uid, field: field, value: value, type: 'increment' });
-
- callback(null, value);
- });
+ incrDecrUserFieldBy(uid, field, value, 'increment', callback);
};
User.decrementUserFieldBy = function (uid, field, value, callback) {
- callback = callback || function () {};
- db.incrObjectFieldBy('user:' + uid, field, -value, function (err, value) {
- if (err) {
- return callback(err);
- }
- plugins.fireHook('action:user.set', { uid: uid, field: field, value: value, type: 'decrement' });
-
- callback(null, value);
- });
+ incrDecrUserFieldBy(uid, field, -value, 'decrement', callback);
};
+
+ function incrDecrUserFieldBy(uid, field, value, type, callback) {
+ callback = callback || function () {};
+ async.waterfall([
+ function (next) {
+ db.incrObjectFieldBy('user:' + uid, field, value, next);
+ },
+ function (value, next) {
+ plugins.fireHook('action:user.set', { uid: uid, field: field, value: value, type: type });
+
+ next(null, value);
+ },
+ ], callback);
+ }
};
diff --git a/src/user/online.js b/src/user/online.js
new file mode 100644
index 0000000000..6cb19cd22f
--- /dev/null
+++ b/src/user/online.js
@@ -0,0 +1,70 @@
+'use strict';
+
+var async = require('async');
+
+var db = require('../database');
+var topics = require('../topics');
+var plugins = require('../plugins');
+
+module.exports = function (User) {
+ User.updateLastOnlineTime = function (uid, callback) {
+ callback = callback || function () {};
+ db.getObjectFields('user:' + uid, ['status', 'lastonline'], function (err, userData) {
+ var now = Date.now();
+ if (err || userData.status === 'offline' || now - parseInt(userData.lastonline, 10) < 300000) {
+ return callback(err);
+ }
+ User.setUserField(uid, 'lastonline', now, callback);
+ });
+ };
+
+ User.updateOnlineUsers = function (uid, callback) {
+ callback = callback || function () {};
+
+ var now = Date.now();
+ async.waterfall([
+ function (next) {
+ db.sortedSetScore('users:online', uid, next);
+ },
+ function (userOnlineTime, next) {
+ if (now - parseInt(userOnlineTime, 10) < 300000) {
+ return callback();
+ }
+ db.sortedSetAdd('users:online', now, uid, next);
+ },
+ function (next) {
+ topics.pushUnreadCount(uid);
+ plugins.fireHook('action:user.online', { uid: uid, timestamp: now });
+ next();
+ },
+ ], callback);
+ };
+
+ User.isOnline = function (uid, callback) {
+ var now = Date.now();
+ async.waterfall([
+ function (next) {
+ if (Array.isArray(uid)) {
+ db.sortedSetScores('users:online', uid, next);
+ } else {
+ db.sortedSetScore('users:online', uid, next);
+ }
+ },
+ function (lastonline, next) {
+ function checkOnline(lastonline) {
+ return now - lastonline < 300000;
+ }
+
+ var isOnline;
+ if (Array.isArray(uid)) {
+ isOnline = uid.map(function (uid, index) {
+ return checkOnline(lastonline[index]);
+ });
+ } else {
+ isOnline = checkOnline(lastonline);
+ }
+ next(null, isOnline);
+ },
+ ], callback);
+ };
+};
diff --git a/src/user/picture.js b/src/user/picture.js
index be489364b9..13288774b2 100644
--- a/src/user/picture.js
+++ b/src/user/picture.js
@@ -50,12 +50,7 @@ module.exports = function (User) {
}, next);
},
function (image, next) {
- User.setUserFields(uid, {
- uploadedpicture: image.url,
- picture: image.url,
- }, function (err) {
- next(err, image);
- });
+ next(null, image);
},
], callback);
};
diff --git a/src/user/profile.js b/src/user/profile.js
index 84c142f35b..3071977949 100644
--- a/src/user/profile.js
+++ b/src/user/profile.js
@@ -18,6 +18,14 @@ module.exports = function (User) {
var updateUid = data.uid;
var oldData;
+ if (data.aboutme !== undefined && data.aboutme.length > meta.config.maximumAboutMeLength) {
+ return callback(new Error('[[error:about-me-too-long, ' + meta.config.maximumAboutMeLength + ']]'));
+ }
+
+ if (data.signature !== undefined && data.signature.length > meta.config.maximumSignatureLength) {
+ return callback(new Error('[[error:signature-too-long, ' + meta.config.maximumSignatureLength + ']]'));
+ }
+
async.waterfall([
function (next) {
plugins.fireHook('filter:user.updateProfile', { uid: uid, data: data, fields: fields }, next);
@@ -27,8 +35,6 @@ module.exports = function (User) {
data = data.data;
async.series([
- async.apply(isAboutMeValid, data),
- async.apply(isSignatureValid, data),
async.apply(isEmailAvailable, data, updateUid),
async.apply(isUsernameAvailable, data, updateUid),
async.apply(isGroupTitleValid, data),
@@ -68,22 +74,6 @@ module.exports = function (User) {
], callback);
};
- function isAboutMeValid(data, callback) {
- if (data.aboutme !== undefined && data.aboutme.length > meta.config.maximumAboutMeLength) {
- callback(new Error('[[error:about-me-too-long, ' + meta.config.maximumAboutMeLength + ']]'));
- } else {
- callback();
- }
- }
-
- function isSignatureValid(data, callback) {
- if (data.signature !== undefined && data.signature.length > meta.config.maximumSignatureLength) {
- callback(new Error('[[error:signature-too-long, ' + meta.config.maximumSignatureLength + ']]'));
- } else {
- callback();
- }
- }
-
function isEmailAvailable(data, uid, callback) {
if (!data.email) {
return callback();
diff --git a/src/user/settings.js b/src/user/settings.js
index 870dd2e59c..bef20e0087 100644
--- a/src/user/settings.js
+++ b/src/user/settings.js
@@ -74,7 +74,7 @@ module.exports = function (User) {
settings.restrictChat = parseInt(getSetting(settings, 'restrictChat', 0), 10) === 1;
settings.topicSearchEnabled = parseInt(getSetting(settings, 'topicSearchEnabled', 0), 10) === 1;
settings.delayImageLoading = parseInt(getSetting(settings, 'delayImageLoading', 1), 10) === 1;
- settings.bootswatchSkin = settings.bootswatchSkin || 'default';
+ settings.bootswatchSkin = settings.bootswatchSkin || meta.config.bootswatchSkin || 'default';
settings.scrollToMyPost = parseInt(getSetting(settings, 'scrollToMyPost', 1), 10) === 1;
callback(null, settings);
diff --git a/src/views/admin/partials/installed_plugin_item.tpl b/src/views/admin/partials/installed_plugin_item.tpl
index b76ae9f964..2e95606ef8 100644
--- a/src/views/admin/partials/installed_plugin_item.tpl
+++ b/src/views/admin/partials/installed_plugin_item.tpl
@@ -36,13 +36,7 @@
-
-<<<<<<< HEAD
-
-
-=======
->>>>>>> `admin/extend` translations
{installed.id}
diff --git a/src/webserver.js b/src/webserver.js
index 4ce61b1c2e..7d3c6aa9a2 100644
--- a/src/webserver.js
+++ b/src/webserver.js
@@ -61,21 +61,20 @@ module.exports.listen = function (callback) {
logger.init(app);
- initializeNodeBB(function (err) {
- if (err) {
- return callback(err);
- }
-
- winston.info('NodeBB Ready');
+ async.waterfall([
+ initializeNodeBB,
+ function (next) {
+ winston.info('NodeBB Ready');
- require('./socket.io').server.emit('event:nodebb.ready', {
- 'cache-buster': meta.config['cache-buster'],
- });
+ require('./socket.io').server.emit('event:nodebb.ready', {
+ 'cache-buster': meta.config['cache-buster'],
+ });
- plugins.fireHook('action:nodebb.ready');
+ plugins.fireHook('action:nodebb.ready');
- listen(callback);
- });
+ listen(next);
+ },
+ ], callback);
};
function initializeNodeBB(callback) {
@@ -107,7 +106,9 @@ function initializeNodeBB(callback) {
meta.blacklist.load,
], next);
},
- ], callback);
+ ], function (err) {
+ callback(err);
+ });
}
function setupExpressApp(app) {
diff --git a/test/categories.js b/test/categories.js
index bdd91602de..8ef059d8cb 100644
--- a/test/categories.js
+++ b/test/categories.js
@@ -390,6 +390,7 @@ describe('Categories', function () {
it('should get all categories', function (done) {
socketCategories.getAll({ uid: adminUid }, {}, function (err, data) {
assert.ifError(err);
+ assert(data);
done();
});
});
@@ -615,6 +616,72 @@ describe('Categories', function () {
});
+ describe('privileges', function () {
+ var privileges = require('../src/privileges');
+
+ it('should return empty array if uids is empty array', function (done) {
+ privileges.categories.filterUids('find', categoryObj.cid, [], function (err, uids) {
+ assert.ifError(err);
+ assert.equal(uids.length, 0);
+ done();
+ });
+ });
+
+ it('should filter uids by privilege', function (done) {
+ privileges.categories.filterUids('find', categoryObj.cid, [1, 2, 3, 4], function (err, uids) {
+ assert.ifError(err);
+ assert.deepEqual(uids, [1, 2]);
+ done();
+ });
+ });
+
+ it('should load user privileges', function (done) {
+ privileges.categories.userPrivileges(categoryObj.cid, 1, function (err, data) {
+ assert.ifError(err);
+ assert.deepEqual(data, {
+ find: false,
+ mods: false,
+ 'posts:delete': false,
+ read: false,
+ 'topics:reply': false,
+ 'topics:read': false,
+ 'topics:create': false,
+ 'topics:delete': false,
+ 'posts:edit': false,
+ });
+
+ done();
+ });
+ });
+
+ it('should load group privileges', function (done) {
+ privileges.categories.groupPrivileges(categoryObj.cid, 'registered-users', function (err, data) {
+ assert.ifError(err);
+ assert.deepEqual(data, {
+ 'groups:find': true,
+ 'groups:posts:edit': true,
+ 'groups:topics:delete': false,
+ 'groups:topics:create': true,
+ 'groups:topics:reply': true,
+ 'groups:posts:delete': true,
+ 'groups:read': true,
+ 'groups:topics:read': true,
+ });
+
+ done();
+ });
+ });
+
+ it('should return false if cid is falsy', function (done) {
+ privileges.categories.isUserAllowedTo('find', null, adminUid, function (err, isAllowed) {
+ assert.ifError(err);
+ assert.equal(isAllowed, false);
+ done();
+ });
+ });
+ });
+
+
after(function (done) {
db.emptydb(done);
});
diff --git a/test/controllers-admin.js b/test/controllers-admin.js
index ad59b4705d..5a17ca96a9 100644
--- a/test/controllers-admin.js
+++ b/test/controllers-admin.js
@@ -21,6 +21,7 @@ describe('Admin Controllers', function () {
var jar;
before(function (done) {
+ groups.resetCache();
async.series({
category: function (next) {
categories.create({
@@ -43,9 +44,10 @@ describe('Admin Controllers', function () {
cid = results.category.cid;
topics.post({ uid: adminUid, title: 'test topic title', content: 'test topic content', cid: results.category.cid }, function (err, result) {
+ assert.ifError(err);
tid = result.topicData.tid;
pid = result.postData.pid;
- done(err);
+ done();
});
});
});
diff --git a/test/controllers.js b/test/controllers.js
index d79235d16f..e86ae0c00e 100644
--- a/test/controllers.js
+++ b/test/controllers.js
@@ -27,7 +27,7 @@ describe('Controllers', function () {
}, next);
},
user: function (next) {
- user.create({ username: 'foo', password: 'barbar' }, next);
+ user.create({ username: 'foo', password: 'barbar', email: 'foo@test.com' }, next);
},
navigation: function (next) {
var navigation = require('../src/navigation/admin');
@@ -498,11 +498,23 @@ describe('Controllers', function () {
hidden: 0,
}, function (err) {
assert.ifError(err);
- request(nconf.get('url') + '/groups/group-details', function (err, res, body) {
+ groups.join('group-details', fooUid, function (err) {
assert.ifError(err);
- assert.equal(res.statusCode, 200);
- assert(body);
- done();
+ topics.post({
+ uid: fooUid,
+ title: 'topic title',
+ content: 'test topic content',
+ cid: cid,
+ }, function (err) {
+ assert.ifError(err);
+ request(nconf.get('url') + '/api/groups/group-details', { json: true }, function (err, res, body) {
+ assert.ifError(err);
+ assert.equal(res.statusCode, 200);
+ assert(body);
+ assert.equal(body.posts[0].content, 'test topic content');
+ done();
+ });
+ });
});
});
});
@@ -532,6 +544,15 @@ describe('Controllers', function () {
});
});
+ it('should get recent posts', function (done) {
+ request(nconf.get('url') + '/api/recent/posts/month', function (err, res, body) {
+ assert.ifError(err);
+ assert.equal(res.statusCode, 200);
+ assert(body);
+ done();
+ });
+ });
+
it('should get post data', function (done) {
request(nconf.get('url') + '/api/post/pid/' + pid, function (err, res, body) {
assert.ifError(err);
@@ -890,6 +911,42 @@ describe('Controllers', function () {
},
], done);
});
+
+ it('should 404 if user does not exist', function (done) {
+ request(nconf.get('url') + '/api/user/email/doesnotexist', function (err, res, body) {
+ assert.ifError(err);
+ assert.equal(res.statusCode, 404);
+ assert(body);
+ done();
+ });
+ });
+
+ it('should load user by uid', function (done) {
+ request(nconf.get('url') + '/api/user/uid/' + fooUid, function (err, res, body) {
+ assert.ifError(err);
+ assert.equal(res.statusCode, 200);
+ assert(body);
+ done();
+ });
+ });
+
+ it('should load user by username', function (done) {
+ request(nconf.get('url') + '/api/user/username/foo', function (err, res, body) {
+ assert.ifError(err);
+ assert.equal(res.statusCode, 200);
+ assert(body);
+ done();
+ });
+ });
+
+ it('should load user by email', function (done) {
+ request(nconf.get('url') + '/api/user/email/foo@test.com', function (err, res, body) {
+ assert.ifError(err);
+ assert.equal(res.statusCode, 200);
+ assert(body);
+ done();
+ });
+ });
});
describe('account follow page', function () {
@@ -943,7 +1000,7 @@ describe('Controllers', function () {
describe('post redirect', function () {
it('should 404 for invalid pid', function (done) {
- request(nconf.get('url') + '/post/fail', function (err, res) {
+ request(nconf.get('url') + '/api/post/fail', function (err, res) {
assert.ifError(err);
assert.equal(res.statusCode, 404);
done();
diff --git a/test/posts.js b/test/posts.js
index 0ea8074d28..9800410a29 100644
--- a/test/posts.js
+++ b/test/posts.js
@@ -11,6 +11,7 @@ var categories = require('../src/categories');
var privileges = require('../src/privileges');
var user = require('../src/user');
var groups = require('../src/groups');
+var socketPosts = require('../src/socket.io/posts');
describe('Post\'s', function () {
var voterUid;
@@ -66,7 +67,6 @@ describe('Post\'s', function () {
});
describe('voting', function () {
- var socketPosts = require('../src/socket.io/posts');
it('should upvote a post', function (done) {
socketPosts.upvote({ uid: voterUid }, { pid: postData.pid, room_id: 'topic_1' }, function (err, result) {
assert.ifError(err);
@@ -138,7 +138,7 @@ describe('Post\'s', function () {
describe('bookmarking', function () {
it('should bookmark a post', function (done) {
- posts.bookmark(postData.pid, voterUid, function (err, data) {
+ socketPosts.bookmark({ uid: voterUid }, { pid: postData.pid, room_id: 'topic_' + postData.tid }, function (err, data) {
assert.ifError(err);
assert.equal(data.isBookmarked, true);
posts.hasBookmarked(postData.pid, voterUid, function (err, hasBookmarked) {
@@ -150,7 +150,7 @@ describe('Post\'s', function () {
});
it('should unbookmark a post', function (done) {
- posts.unbookmark(postData.pid, voterUid, function (err, data) {
+ socketPosts.unbookmark({ uid: voterUid }, { pid: postData.pid, room_id: 'topic_' + postData.tid }, function (err, data) {
assert.ifError(err);
assert.equal(data.isBookmarked, false);
posts.hasBookmarked([postData.pid], voterUid, function (err, hasBookmarked) {
@@ -163,8 +163,6 @@ describe('Post\'s', function () {
});
describe('post tools', function () {
- var socketPosts = require('../src/socket.io/posts');
-
it('should error if data is invalid', function (done) {
socketPosts.loadPostTools({ uid: globalModUid }, null, function (err) {
assert.equal(err.message, '[[error:invalid-data]]');
@@ -209,7 +207,6 @@ describe('Post\'s', function () {
var mainPid;
var replyPid;
- var socketPosts = require('../src/socket.io/posts');
before(function (done) {
createTopicWithReply(function (topicPostData, replyData) {
tid = topicPostData.topicData.tid;
@@ -299,7 +296,6 @@ describe('Post\'s', function () {
var pid;
var replyPid;
var tid;
- var socketPosts = require('../src/socket.io/posts');
var meta = require('../src/meta');
before(function (done) {
topics.post({
@@ -430,7 +426,6 @@ describe('Post\'s', function () {
var replyPid;
var tid;
var moveTid;
- var socketPosts = require('../src/socket.io/posts');
before(function (done) {
async.waterfall([
@@ -554,7 +549,6 @@ describe('Post\'s', function () {
});
});
- var socketPosts = require('../src/socket.io/posts');
it('should error with invalid data', function (done) {
socketPosts.reply({ uid: 0 }, null, function (err) {
assert.equal(err.message, '[[error:invalid-data]]');
diff --git a/test/socket.io.js b/test/socket.io.js
index c534d0969c..fc24d24e30 100644
--- a/test/socket.io.js
+++ b/test/socket.io.js
@@ -419,11 +419,16 @@ describe('socket.io', function () {
});
it('should set theme to bootswatch', function (done) {
- socketAdmin.themes.set({ uid: adminUid }, { type: 'bootswatch', src: 'darkly' }, function (err) {
+ socketAdmin.themes.set({ uid: adminUid }, {
+ type: 'bootswatch',
+ src: '//maxcdn.bootstrapcdn.com/bootswatch/latest/darkly/bootstrap.min.css',
+ id: 'darkly',
+ }, function (err) {
assert.ifError(err);
- meta.configs.get('theme:src', function (err, id) {
+ meta.configs.getFields(['theme:src', 'bootswatchSkin'], function (err, fields) {
assert.ifError(err);
- assert.equal(id, 'darkly');
+ assert.equal(fields['theme:src'], '//maxcdn.bootstrapcdn.com/bootswatch/latest/darkly/bootstrap.min.css');
+ assert.equal(fields.bootswatchSkin, 'darkly');
done();
});
});
diff --git a/test/topics.js b/test/topics.js
index 3c6e7efe71..097ffb8c37 100644
--- a/test/topics.js
+++ b/test/topics.js
@@ -12,6 +12,7 @@ var User = require('../src/user');
var groups = require('../src/groups');
var helpers = require('./helpers');
var socketPosts = require('../src/socket.io/posts');
+var socketTopics = require('../src/socket.io/topics');
describe('Topic\'s', function () {
var topic;
@@ -49,11 +50,34 @@ describe('Topic\'s', function () {
});
describe('.post', function () {
+ it('should fail to create topic with invalid data', function (done) {
+ socketTopics.post({ uid: 0 }, null, function (err) {
+ assert.equal(err.message, '[[error:invalid-data]]');
+ done();
+ });
+ });
+
it('should create a new topic with proper parameters', function (done) {
topics.post({ uid: topic.userId, title: topic.title, content: topic.content, cid: topic.categoryId }, function (err, result) {
- assert.equal(err, null, 'was created with error');
- assert.ok(result);
+ assert.ifError(err);
+ assert(result);
+ topic.tid = result.topicData.tid;
+ done();
+ });
+ });
+ it('should get post count', function (done) {
+ socketTopics.postcount({ uid: adminUid }, topic.tid, function (err, count) {
+ assert.ifError(err);
+ assert.equal(count, 1);
+ done();
+ });
+ });
+
+ it('should load topic', function (done) {
+ socketTopics.getTopic({ uid: adminUid }, topic.tid, function (err, data) {
+ assert.ifError(err);
+ assert.equal(data.tid, topic.tid);
done();
});
});
@@ -246,7 +270,7 @@ describe('Topic\'s', function () {
var newTopic;
var followerUid;
var moveCid;
- var socketTopics = require('../src/socket.io/topics');
+
before(function (done) {
async.waterfall([
function (next) {
@@ -589,8 +613,7 @@ describe('Topic\'s', function () {
assert.ok(result);
replies.push(result);
next();
- }
- );
+ });
}
before(function (done) {
@@ -619,25 +642,45 @@ describe('Topic\'s', function () {
function (next) { postReply(next); },
function (next) {
topicPids = replies.map(function (reply) { return reply.pid; });
- topics.setUserBookmark(newTopic.tid, topic.userId, originalBookmark, next);
+ socketTopics.bookmark({ uid: topic.userId }, { tid: newTopic.tid, index: originalBookmark }, next);
}],
done);
});
+ it('should fail with invalid data', function (done) {
+ socketTopics.bookmark({ uid: topic.userId }, null, function (err) {
+ assert.equal(err.message, '[[error:invalid-data]]');
+ done();
+ });
+ });
+
it('should have 12 replies', function (done) {
assert.equal(12, replies.length);
done();
});
+ it('should fail with invalid data', function (done) {
+ socketTopics.createTopicFromPosts({ uid: 0 }, null, function (err) {
+ assert.equal(err.message, '[[error:not-logged-in]]');
+ done();
+ });
+ });
+
+ it('should fail with invalid data', function (done) {
+ socketTopics.createTopicFromPosts({ uid: 1 }, null, function (err) {
+ assert.equal(err.message, '[[error:invalid-data]]');
+ done();
+ });
+ });
+
it('should not update the user\'s bookmark', function (done) {
async.waterfall([
function (next) {
- topics.createTopicFromPosts(
- topic.userId,
- 'Fork test, no bookmark update',
- topicPids.slice(-2),
- newTopic.tid,
- next);
+ socketTopics.createTopicFromPosts({ uid: topic.userId }, {
+ title: 'Fork test, no bookmark update',
+ pids: topicPids.slice(-2),
+ fromTid: newTopic.tid,
+ }, next);
},
function (forkedTopicData, next) {
topics.getUserBookmark(newTopic.tid, topic.userId, next);
@@ -1388,6 +1431,13 @@ describe('Topic\'s', function () {
});
});
+ it('should error if not logged in', function (done) {
+ socketTopics.changeWatching({ uid: 0 }, { tid: tid, type: 'ignore' }, function (err) {
+ assert.equal(err.message, '[[error:not-logged-in]]');
+ done();
+ });
+ });
+
it('should filter ignoring uids', function (done) {
socketTopics.changeWatching({ uid: followerUid }, { tid: tid, type: 'ignore' }, function (err) {
assert.ifError(err);
@@ -1418,7 +1468,7 @@ describe('Topic\'s', function () {
topics.toggleFollow(tid, followerUid, function (err, isFollowing) {
assert.ifError(err);
assert(isFollowing);
- topics.isFollowing([tid], followerUid, function (err, isFollowing) {
+ socketTopics.isFollowed({ uid: followerUid }, tid, function (err, isFollowing) {
assert.ifError(err);
assert(isFollowing);
done();
@@ -1427,6 +1477,44 @@ describe('Topic\'s', function () {
});
});
+ describe('topics search', function () {
+ it('should error with invalid data', function (done) {
+ socketTopics.search({ uid: adminUid }, null, function (err) {
+ assert.equal(err.message, '[[error:invalid-data]]');
+ done();
+ });
+ });
+
+ it('should error if no search plugin', function (done) {
+ socketTopics.search({ uid: adminUid }, { tid: topic.tid, term: 'test' }, function (err) {
+ assert.equal(err.message, '[[error:no-plugins-available]]');
+ done();
+ });
+ });
+
+ it('should return results', function (done) {
+ var plugins = require('../src/plugins');
+ plugins.registerHook('myTestPlugin', {
+ hook: 'filter:topic.search',
+ method: function (data, callback) {
+ callback(null, [1, 2, 3]);
+ },
+ });
+ socketTopics.search({ uid: adminUid }, { tid: topic.tid, term: 'test' }, function (err, results) {
+ assert.ifError(err);
+ assert.deepEqual(results, [1, 2, 3]);
+ done();
+ });
+ });
+ });
+
+ it('should check if user is moderator', function (done) {
+ socketTopics.isModerator({ uid: adminUid }, topic.tid, function (err, isModerator) {
+ assert.ifError(err);
+ assert(!isModerator);
+ done();
+ });
+ });
after(function (done) {
db.emptydb(done);