mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-11-01 19:46:01 +01:00
Merge remote-tracking branch 'origin/master' into flagging-refactor
This commit is contained in:
@@ -52,7 +52,7 @@
|
||||
"morgan": "^1.3.2",
|
||||
"mousetrap": "^1.5.3",
|
||||
"nconf": "~0.8.2",
|
||||
"nodebb-plugin-composer-default": "4.3.0",
|
||||
"nodebb-plugin-composer-default": "4.3.1",
|
||||
"nodebb-plugin-dbsearch": "1.0.4",
|
||||
"nodebb-plugin-emoji-extended": "1.1.1",
|
||||
"nodebb-plugin-emoji-one": "1.1.5",
|
||||
|
||||
@@ -107,6 +107,7 @@ helpers.getUserDataByUserSlug = function (userslug, callerUID, callback) {
|
||||
userData.isModerator = isModerator;
|
||||
userData.isAdminOrGlobalModerator = isAdmin || isGlobalModerator;
|
||||
userData.isAdminOrGlobalModeratorOrModerator = isAdmin || isGlobalModerator || isModerator;
|
||||
userData.isSelfOrAdminOrGlobalModerator = isSelf || isAdmin || isGlobalModerator;
|
||||
userData.canEdit = isAdmin || (isGlobalModerator && !results.isTargetAdmin);
|
||||
userData.canBan = isAdmin || (isGlobalModerator && !results.isTargetAdmin);
|
||||
userData.canChangePassword = isAdmin || (isSelf && parseInt(meta.config['password:disableEdit'], 10) !== 1);
|
||||
|
||||
@@ -13,13 +13,9 @@ sessionController.revoke = function (req, res, next) {
|
||||
}
|
||||
|
||||
var _id;
|
||||
var uid;
|
||||
var uid = res.locals.uid;
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
user.getUidByUserslug(req.params.userslug, next);
|
||||
},
|
||||
function (_uid, next) {
|
||||
uid = _uid;
|
||||
if (!uid) {
|
||||
return next(new Error('[[error:no-session-found]]'));
|
||||
}
|
||||
|
||||
@@ -162,14 +162,24 @@ groupsController.members = function (req, res, callback) {
|
||||
groupsController.uploadCover = function (req, res, next) {
|
||||
var params = JSON.parse(req.body.params);
|
||||
|
||||
groups.updateCover(req.uid, {
|
||||
file: req.files.files[0].path,
|
||||
groupName: params.groupName
|
||||
}, function (err, image) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
groups.ownership.isOwner(req.uid, params.groupName, next);
|
||||
},
|
||||
function (isOwner, next) {
|
||||
if (!isOwner) {
|
||||
return next(new Error('[[error:no-privileges]]'));
|
||||
}
|
||||
|
||||
groups.updateCover(req.uid, {
|
||||
file: req.files.files[0].path,
|
||||
groupName: params.groupName
|
||||
}, next);
|
||||
}
|
||||
], function (err, image) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
res.json([{url: image.url.startsWith('http') ? image.url : nconf.get('relative_path') + image.url}]);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -26,13 +26,15 @@ helpers.notAllowed = function (req, res, error) {
|
||||
if (res.locals.isAPI) {
|
||||
res.status(403).json({
|
||||
path: req.path.replace(/^\/api/, ''),
|
||||
loggedIn: !!req.uid, error: error,
|
||||
loggedIn: !!req.uid,
|
||||
error: error,
|
||||
title: '[[global:403.title]]'
|
||||
});
|
||||
} else {
|
||||
res.status(403).render('403', {
|
||||
path: req.path,
|
||||
loggedIn: !!req.uid, error: error,
|
||||
loggedIn: !!req.uid,
|
||||
error: error,
|
||||
title: '[[global:403.title]]'
|
||||
});
|
||||
}
|
||||
|
||||
@@ -132,7 +132,12 @@ var fallbackTransport;
|
||||
delete data.from_name;
|
||||
|
||||
winston.verbose('[emailer] Sending email to uid ' + data.uid);
|
||||
fallbackTransport.sendMail(data, callback);
|
||||
fallbackTransport.sendMail(data, function (err) {
|
||||
if (err) {
|
||||
winston.error(err);
|
||||
}
|
||||
callback();
|
||||
});
|
||||
};
|
||||
|
||||
function render(tpl, params, next) {
|
||||
|
||||
@@ -10,7 +10,6 @@ var mime = require('mime');
|
||||
var winston = require('winston');
|
||||
|
||||
var db = require('../database');
|
||||
var file = require('../file');
|
||||
var uploadsController = require('../controllers/uploads');
|
||||
|
||||
module.exports = function (Groups) {
|
||||
@@ -25,7 +24,7 @@ module.exports = function (Groups) {
|
||||
Groups.updateCover = function (uid, data, callback) {
|
||||
|
||||
// Position only? That's fine
|
||||
if (!data.imageData && data.position) {
|
||||
if (!data.imageData && !data.file && data.position) {
|
||||
return Groups.updateCoverPosition(data.groupName, data.position, callback);
|
||||
}
|
||||
|
||||
@@ -66,26 +65,19 @@ module.exports = function (Groups) {
|
||||
Groups.setGroupField(data.groupName, 'cover:thumb:url', uploadData.url, next);
|
||||
},
|
||||
function (next) {
|
||||
fs.unlink(tempPath, next); // Delete temporary file
|
||||
if (data.position) {
|
||||
Groups.updateCoverPosition(data.groupName, data.position, next);
|
||||
} else {
|
||||
next(null);
|
||||
}
|
||||
}
|
||||
], function (err) {
|
||||
if (err) {
|
||||
return fs.unlink(tempPath, function (unlinkErr) {
|
||||
if (unlinkErr) {
|
||||
winston.error(unlinkErr);
|
||||
}
|
||||
|
||||
callback(err); // send back original error
|
||||
});
|
||||
}
|
||||
|
||||
if (data.position) {
|
||||
Groups.updateCoverPosition(data.groupName, data.position, function (err) {
|
||||
callback(err, {url: url});
|
||||
});
|
||||
} else {
|
||||
fs.unlink(tempPath, function (unlinkErr) {
|
||||
if (unlinkErr) {
|
||||
winston.error(unlinkErr);
|
||||
}
|
||||
callback(err, {url: url});
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -49,6 +49,30 @@ middleware.authenticate = function (req, res, next) {
|
||||
controllers.helpers.notAllowed(req, res);
|
||||
};
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
user.isAdminOrGlobalMod(req.uid, function (err, ok) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
} else if (ok) {
|
||||
return next();
|
||||
} else {
|
||||
controllers.helpers.notAllowed(req, res);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
controllers.helpers.notAllowed(req, res);
|
||||
}
|
||||
};
|
||||
|
||||
middleware.pageView = function (req, res, next) {
|
||||
analytics.pageView({
|
||||
ip: req.ip,
|
||||
|
||||
50
src/posts.js
50
src/posts.js
@@ -187,39 +187,37 @@ var plugins = require('./plugins');
|
||||
return callback(null, []);
|
||||
}
|
||||
|
||||
user.getSettings(uid, function (err, settings) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
user.getSettings(uid, next);
|
||||
},
|
||||
function (settings, next) {
|
||||
var byVotes = settings.topicPostSort === 'most_votes';
|
||||
var sets = posts.map(function (post) {
|
||||
return byVotes ? 'tid:' + post.tid + ':posts:votes' : 'tid:' + post.tid + ':posts';
|
||||
});
|
||||
|
||||
var byVotes = settings.topicPostSort === 'most_votes';
|
||||
var sets = posts.map(function (post) {
|
||||
return byVotes ? 'tid:' + post.tid + ':posts:votes' : 'tid:' + post.tid + ':posts';
|
||||
});
|
||||
|
||||
var uniqueSets = _.uniq(sets);
|
||||
var method = 'sortedSetsRanks';
|
||||
if (uniqueSets.length === 1) {
|
||||
method = 'sortedSetRanks';
|
||||
sets = uniqueSets[0];
|
||||
}
|
||||
|
||||
var pids = posts.map(function (post) {
|
||||
return post.pid;
|
||||
});
|
||||
|
||||
db[method](sets, pids, function (err, indices) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
var uniqueSets = _.uniq(sets);
|
||||
var method = 'sortedSetsRanks';
|
||||
if (uniqueSets.length === 1) {
|
||||
method = 'sortedSetRanks';
|
||||
sets = uniqueSets[0];
|
||||
}
|
||||
|
||||
var pids = posts.map(function (post) {
|
||||
return post.pid;
|
||||
});
|
||||
|
||||
db[method](sets, pids, next);
|
||||
},
|
||||
function (indices, next) {
|
||||
for (var i = 0; i < indices.length; ++i) {
|
||||
indices[i] = utils.isNumber(indices[i]) ? parseInt(indices[i], 10) + 1 : 0;
|
||||
}
|
||||
|
||||
callback(null, indices);
|
||||
});
|
||||
});
|
||||
next(null, indices);
|
||||
}
|
||||
], callback);
|
||||
};
|
||||
|
||||
Posts.updatePostVoteCount = function (postData, callback) {
|
||||
|
||||
@@ -28,7 +28,7 @@ module.exports = function (app, middleware, controllers) {
|
||||
setupPageRoute(app, '/user/:userslug/info', middleware, accountMiddlewares, controllers.accounts.info.get);
|
||||
setupPageRoute(app, '/user/:userslug/settings', middleware, accountMiddlewares, controllers.accounts.settings.get);
|
||||
|
||||
app.delete('/api/user/:userslug/session/:uuid', [middleware.requireUser], controllers.accounts.session.revoke);
|
||||
app.delete('/api/user/:userslug/session/:uuid', [middleware.exposeUid, middleware.ensureSelfOrGlobalPrivilege], controllers.accounts.session.revoke);
|
||||
|
||||
setupPageRoute(app, '/notifications', middleware, [middleware.authenticate], controllers.accounts.notifications.get);
|
||||
setupPageRoute(app, '/user/:userslug/chats/:roomid?', middleware, middlewares, controllers.accounts.chats.get);
|
||||
|
||||
@@ -104,19 +104,20 @@ User.sendValidationEmail = function (socket, uids, callback) {
|
||||
return callback(new Error('[[error:email-confirmations-are-disabled]]'));
|
||||
}
|
||||
|
||||
user.getUsersFields(uids, ['uid', 'email'], function (err, usersData) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
user.getUsersFields(uids, ['uid', 'email'], next);
|
||||
},
|
||||
function (usersData, next) {
|
||||
async.eachLimit(usersData, 50, function (userData, next) {
|
||||
if (userData.email && userData.uid) {
|
||||
user.email.sendValidationEmail(userData.uid, userData.email, next);
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
}, next);
|
||||
}
|
||||
|
||||
async.eachLimit(usersData, 50, function (userData, next) {
|
||||
if (userData.email && userData.uid) {
|
||||
user.email.sendValidationEmail(userData.uid, userData.email, next);
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
}, callback);
|
||||
});
|
||||
], callback);
|
||||
};
|
||||
|
||||
User.sendPasswordResetEmail = function (socket, uids, callback) {
|
||||
@@ -220,33 +221,37 @@ User.deleteInvitation = function (socket, data, callback) {
|
||||
};
|
||||
|
||||
User.acceptRegistration = function (socket, data, callback) {
|
||||
user.acceptRegistration(data.username, function (err, uid) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
user.acceptRegistration(data.username, next);
|
||||
},
|
||||
function (uid, next) {
|
||||
events.log({
|
||||
type: 'registration-approved',
|
||||
uid: socket.uid,
|
||||
ip: socket.ip,
|
||||
targetUid: uid
|
||||
});
|
||||
next(null, uid);
|
||||
}
|
||||
events.log({
|
||||
type: 'registration-approved',
|
||||
uid: socket.uid,
|
||||
ip: socket.ip,
|
||||
targetUid: uid,
|
||||
});
|
||||
callback();
|
||||
});
|
||||
], callback);
|
||||
};
|
||||
|
||||
User.rejectRegistration = function (socket, data, callback) {
|
||||
user.rejectRegistration(data.username, function (err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
user.rejectRegistration(data.username, next);
|
||||
},
|
||||
function (next) {
|
||||
events.log({
|
||||
type: 'registration-rejected',
|
||||
uid: socket.uid,
|
||||
ip: socket.ip,
|
||||
username: data.username,
|
||||
});
|
||||
next();
|
||||
}
|
||||
events.log({
|
||||
type: 'registration-rejected',
|
||||
uid: socket.uid,
|
||||
ip: socket.ip,
|
||||
username: data.username,
|
||||
});
|
||||
callback();
|
||||
});
|
||||
], callback);
|
||||
};
|
||||
|
||||
User.restartJobs = function (socket, data, callback) {
|
||||
|
||||
@@ -277,13 +277,18 @@ SocketGroups.cover.update = function (socket, data, callback) {
|
||||
return callback(new Error('[[error:no-privileges]]'));
|
||||
}
|
||||
|
||||
groups.ownership.isOwner(socket.uid, data.groupName, function (err, isOwner) {
|
||||
if (err || !isOwner) {
|
||||
return callback(err || new Error('[[error:no-privileges]]'));
|
||||
}
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
groups.ownership.isOwner(socket.uid, data.groupName, next);
|
||||
},
|
||||
function (isOwner, next) {
|
||||
if (!isOwner) {
|
||||
return next(new Error('[[error:no-privileges]]'));
|
||||
}
|
||||
|
||||
groups.updateCover(socket.uid, data, callback);
|
||||
});
|
||||
groups.updateCover(socket.uid, data, next);
|
||||
}
|
||||
], callback);
|
||||
};
|
||||
|
||||
SocketGroups.cover.remove = function (socket, data, callback) {
|
||||
@@ -291,13 +296,18 @@ SocketGroups.cover.remove = function (socket, data, callback) {
|
||||
return callback(new Error('[[error:no-privileges]]'));
|
||||
}
|
||||
|
||||
groups.ownership.isOwner(socket.uid, data.groupName, function (err, isOwner) {
|
||||
if (err || !isOwner) {
|
||||
return callback(err || new Error('[[error:no-privileges]]'));
|
||||
}
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
groups.ownership.isOwner(socket.uid, data.groupName, next);
|
||||
},
|
||||
function (isOwner, next) {
|
||||
if (!isOwner) {
|
||||
return next(new Error('[[error:no-privileges]]'));
|
||||
}
|
||||
|
||||
groups.removeCover(data, callback);
|
||||
});
|
||||
groups.removeCover(data, next);
|
||||
}
|
||||
], callback);
|
||||
};
|
||||
|
||||
module.exports = SocketGroups;
|
||||
|
||||
@@ -72,15 +72,21 @@ SocketUser.emailConfirm = function (socket, data, callback) {
|
||||
}
|
||||
|
||||
if (parseInt(meta.config.requireEmailConfirmation, 10) !== 1) {
|
||||
callback();
|
||||
return callback(new Error('[[error:email-confirmations-are-disabled]]'));
|
||||
}
|
||||
user.getUserField(socket.uid, 'email', function (err, email) {
|
||||
if (err || !email) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
user.email.sendValidationEmail(socket.uid, email, callback);
|
||||
});
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
user.getUserField(socket.uid, 'email', next);
|
||||
},
|
||||
function (email, next) {
|
||||
if (!email) {
|
||||
return callback();
|
||||
}
|
||||
|
||||
user.email.sendValidationEmail(socket.uid, email, next);
|
||||
}
|
||||
], callback);
|
||||
};
|
||||
|
||||
|
||||
@@ -109,39 +115,37 @@ SocketUser.reset.commit = function (socket, data, callback) {
|
||||
if (!data || !data.code || !data.password) {
|
||||
return callback(new Error('[[error:invalid-data]]'));
|
||||
}
|
||||
var uid;
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
async.parallel({
|
||||
uid: async.apply(db.getObjectField, 'reset:uid', data.code),
|
||||
reset: async.apply(user.reset.commit, data.code, data.password)
|
||||
}, next);
|
||||
},
|
||||
function (results, next) {
|
||||
uid = results.uid;
|
||||
events.log({
|
||||
type: 'password-reset',
|
||||
uid: uid,
|
||||
ip: socket.ip
|
||||
});
|
||||
|
||||
async.parallel({
|
||||
uid: async.apply(db.getObjectField, 'reset:uid', data.code),
|
||||
reset: async.apply(user.reset.commit, data.code, data.password)
|
||||
}, function (err, results) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
var uid = results.uid;
|
||||
var now = new Date();
|
||||
var parsedDate = now.getFullYear() + '/' + (now.getMonth() + 1) + '/' + now.getDate();
|
||||
|
||||
user.getUserField(uid, 'username', function (err, username) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
user.getUserField(uid, 'username', next);
|
||||
},
|
||||
function (username, next) {
|
||||
var now = new Date();
|
||||
var parsedDate = now.getFullYear() + '/' + (now.getMonth() + 1) + '/' + now.getDate();
|
||||
emailer.send('reset_notify', uid, {
|
||||
username: username,
|
||||
date: parsedDate,
|
||||
site_title: meta.config.title || 'NodeBB',
|
||||
subject: '[[email:reset.notify.subject]]'
|
||||
});
|
||||
});
|
||||
|
||||
events.log({
|
||||
type: 'password-reset',
|
||||
uid: uid,
|
||||
ip: socket.ip
|
||||
});
|
||||
callback();
|
||||
});
|
||||
next();
|
||||
}
|
||||
], callback);
|
||||
};
|
||||
|
||||
SocketUser.isFollowing = function (socket, data, callback) {
|
||||
@@ -224,16 +228,10 @@ SocketUser.saveSettings = function (socket, data, callback) {
|
||||
};
|
||||
|
||||
SocketUser.setTopicSort = function (socket, sort, callback) {
|
||||
if (!socket.uid) {
|
||||
return callback();
|
||||
}
|
||||
user.setSetting(socket.uid, 'topicPostSort', sort, callback);
|
||||
};
|
||||
|
||||
SocketUser.setCategorySort = function (socket, sort, callback) {
|
||||
if (!socket.uid) {
|
||||
return callback();
|
||||
}
|
||||
user.setSetting(socket.uid, 'categoryTopicSort', sort, callback);
|
||||
};
|
||||
|
||||
|
||||
@@ -46,18 +46,19 @@ module.exports = function (User) {
|
||||
};
|
||||
|
||||
function sendNotificationToAdmins(username, callback) {
|
||||
notifications.create({
|
||||
bodyShort: '[[notifications:new_register, ' + username + ']]',
|
||||
nid: 'new_register:' + username,
|
||||
path: '/admin/manage/registration',
|
||||
mergeId: 'new_register'
|
||||
}, function (err, notification) {
|
||||
if (err || !notification) {
|
||||
return callback(err);
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
notifications.create({
|
||||
bodyShort: '[[notifications:new_register, ' + username + ']]',
|
||||
nid: 'new_register:' + username,
|
||||
path: '/admin/manage/registration',
|
||||
mergeId: 'new_register'
|
||||
}, next);
|
||||
},
|
||||
function (notification, next) {
|
||||
notifications.pushGroup(notification, 'administrators', next);
|
||||
}
|
||||
|
||||
notifications.pushGroup(notification, 'administrators', callback);
|
||||
});
|
||||
], callback);
|
||||
}
|
||||
|
||||
User.acceptRegistration = function (username, callback) {
|
||||
@@ -78,6 +79,12 @@ module.exports = function (User) {
|
||||
uid = _uid;
|
||||
User.setUserField(uid, 'password', userData.hashedPassword, next);
|
||||
},
|
||||
function (next) {
|
||||
removeFromQueue(username, next);
|
||||
},
|
||||
function (next) {
|
||||
markNotificationRead(username, next);
|
||||
},
|
||||
function (next) {
|
||||
var title = meta.config.title || meta.config.browserTitle || 'NodeBB';
|
||||
translator.translate('[[email:welcome-to, ' + title + ']]', meta.config.defaultLang, function (subject) {
|
||||
@@ -92,12 +99,6 @@ module.exports = function (User) {
|
||||
emailer.send('registration_accepted', uid, data, next);
|
||||
});
|
||||
},
|
||||
function (next) {
|
||||
removeFromQueue(username, next);
|
||||
},
|
||||
function (next) {
|
||||
markNotificationRead(username, next);
|
||||
},
|
||||
function (next) {
|
||||
next(null, uid);
|
||||
}
|
||||
@@ -153,13 +154,11 @@ module.exports = function (User) {
|
||||
},
|
||||
function (users, next) {
|
||||
users = users.map(function (user, index) {
|
||||
if (!user) {
|
||||
return null;
|
||||
if (user) {
|
||||
user.timestampISO = utils.toISOString(data[index].score);
|
||||
delete user.hashedPassword;
|
||||
}
|
||||
|
||||
user.timestampISO = utils.toISOString(data[index].score);
|
||||
delete user.hashedPassword;
|
||||
|
||||
return user;
|
||||
}).filter(Boolean);
|
||||
|
||||
|
||||
@@ -160,6 +160,9 @@ module.exports = function (User) {
|
||||
};
|
||||
|
||||
User.setSetting = function (uid, key, value, callback) {
|
||||
if (!parseInt(uid, 10)) {
|
||||
return callback();
|
||||
}
|
||||
db.setObjectField('user:' + uid + ':settings', key, value, callback);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -12,6 +12,7 @@ var Categories = require('../src/categories');
|
||||
var Topics = require('../src/topics');
|
||||
var User = require('../src/user');
|
||||
var groups = require('../src/groups');
|
||||
var privileges = require('../src/privileges');
|
||||
|
||||
describe('Categories', function () {
|
||||
var categoryObj;
|
||||
@@ -312,7 +313,7 @@ describe('Categories', function () {
|
||||
var socketCategories = require('../src/socket.io/admin/categories');
|
||||
var cid;
|
||||
before(function (done) {
|
||||
Categories.create({
|
||||
socketCategories.create({uid: adminUid}, {
|
||||
name: 'update name',
|
||||
description: 'update description',
|
||||
parentCid: categoryObj.cid,
|
||||
@@ -388,9 +389,160 @@ describe('Categories', function () {
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
it('should get all categories', function (done) {
|
||||
socketCategories.getAll({uid: adminUid}, {}, function (err, data) {
|
||||
assert.ifError(err);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should get all category names', function (done) {
|
||||
socketCategories.getNames({uid: adminUid}, {}, function (err, data) {
|
||||
assert.ifError(err);
|
||||
assert(Array.isArray(data));
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should give privilege', function (done) {
|
||||
socketCategories.setPrivilege({uid: adminUid}, {cid: categoryObj.cid, privilege: ['groups:topics:delete'], set: true, member: 'registered-users'}, function (err) {
|
||||
assert.ifError(err);
|
||||
privileges.categories.can('topics:delete', categoryObj.cid, posterUid, function (err, canDeleteTopcis) {
|
||||
assert.ifError(err);
|
||||
assert(canDeleteTopcis);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should remove privilege', function (done) {
|
||||
socketCategories.setPrivilege({uid: adminUid}, {cid: categoryObj.cid, privilege: 'groups:topics:delete', set: false, member: 'registered-users'}, function (err) {
|
||||
assert.ifError(err);
|
||||
privileges.categories.can('topics:delete', categoryObj.cid, posterUid, function (err, canDeleteTopcis) {
|
||||
assert.ifError(err);
|
||||
assert(!canDeleteTopcis);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should get privilege settings', function (done) {
|
||||
socketCategories.getPrivilegeSettings({uid: adminUid}, categoryObj.cid, function (err, data) {
|
||||
assert.ifError(err);
|
||||
assert(data);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should copy privileges to children', function (done) {
|
||||
var parentCid;
|
||||
var child1Cid;
|
||||
var child2Cid;
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
Categories.create({name: 'parent'}, next);
|
||||
},
|
||||
function (category, next) {
|
||||
parentCid = category.cid;
|
||||
Categories.create({name: 'child1', parentCid: parentCid}, next);
|
||||
},
|
||||
function (category, next) {
|
||||
child1Cid = category.cid;
|
||||
Categories.create({name: 'child2', parentCid: child1Cid}, next);
|
||||
},
|
||||
function (category, next) {
|
||||
child2Cid = category.cid;
|
||||
socketCategories.setPrivilege({uid: adminUid}, {cid: parentCid, privilege: 'groups:topics:delete', set: true, member: 'registered-users'}, next);
|
||||
},
|
||||
function (next) {
|
||||
socketCategories.copyPrivilegesToChildren({uid: adminUid}, parentCid, next);
|
||||
},
|
||||
function (next) {
|
||||
privileges.categories.can('topics:delete', child2Cid, posterUid, next);
|
||||
},
|
||||
function (canDelete, next) {
|
||||
assert(canDelete);
|
||||
next();
|
||||
}
|
||||
], done);
|
||||
});
|
||||
|
||||
it('should copy settings from', function (done) {
|
||||
var child1Cid;
|
||||
var parentCid;
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
Categories.create({name: 'parent', description: 'copy me'}, next);
|
||||
},
|
||||
function (category, next) {
|
||||
parentCid = category.cid;
|
||||
Categories.create({name: 'child1'}, next);
|
||||
},
|
||||
function (category, next) {
|
||||
child1Cid = category.cid;
|
||||
socketCategories.copySettingsFrom({uid: adminUid}, {fromCid: parentCid, toCid: child1Cid}, next);
|
||||
},
|
||||
function (canDelete, next) {
|
||||
Categories.getCategoryField(child1Cid, 'description', next);
|
||||
},
|
||||
function (description, next) {
|
||||
assert.equal(description, 'copy me');
|
||||
next();
|
||||
}
|
||||
], done);
|
||||
});
|
||||
|
||||
it('should copy privileges from', function (done) {
|
||||
var child1Cid;
|
||||
var parentCid;
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
Categories.create({name: 'parent', description: 'copy me'}, next);
|
||||
},
|
||||
function (category, next) {
|
||||
parentCid = category.cid;
|
||||
Categories.create({name: 'child1'}, next);
|
||||
},
|
||||
function (category, next) {
|
||||
child1Cid = category.cid;
|
||||
socketCategories.setPrivilege({uid: adminUid}, {cid: parentCid, privilege: 'groups:topics:delete', set: true, member: 'registered-users'}, next);
|
||||
},
|
||||
function (next) {
|
||||
socketCategories.copyPrivilegesFrom({uid: adminUid}, {fromCid: parentCid, toCid: child1Cid}, next);
|
||||
},
|
||||
function (next) {
|
||||
privileges.categories.can('topics:delete', child1Cid, posterUid, next);
|
||||
},
|
||||
function (canDelete, next) {
|
||||
assert(canDelete);
|
||||
next();
|
||||
}
|
||||
], done);
|
||||
});
|
||||
});
|
||||
|
||||
it('should get active users', function (done) {
|
||||
Categories.create({
|
||||
name: 'test'
|
||||
}, function (err, category) {
|
||||
assert.ifError(err);
|
||||
Topics.post({
|
||||
uid: posterUid,
|
||||
cid: category.cid,
|
||||
title: 'Test Topic Title',
|
||||
content: 'The content of test topic'
|
||||
}, function (err) {
|
||||
assert.ifError(err);
|
||||
Categories.getActiveUsers(category.cid, function (err, uids) {
|
||||
assert.ifError(err);
|
||||
assert.equal(uids[0], posterUid);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
after(function (done) {
|
||||
|
||||
@@ -533,8 +533,8 @@ describe('Controllers', function () {
|
||||
}
|
||||
}, function (err, res, body) {
|
||||
assert.ifError(err);
|
||||
assert.equal(res.statusCode, 500);
|
||||
assert.equal(body, '[[error:no-session-found]]');
|
||||
assert.equal(res.statusCode, 403);
|
||||
assert.equal(body, '{"path":"/user/doesnotexist/session/1112233","loggedIn":true,"title":"[[global:403.title]]"}');
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -794,7 +794,14 @@ describe('Controllers', function () {
|
||||
user.create({username: 'follower'}, function (err, _uid) {
|
||||
assert.ifError(err);
|
||||
uid = _uid;
|
||||
socketUser.follow({uid: uid}, {uid: fooUid}, done);
|
||||
socketUser.follow({uid: uid}, {uid: fooUid}, function (err) {
|
||||
assert.ifError(err);
|
||||
socketUser.isFollowing({uid: uid}, {uid: fooUid}, function (err, isFollowing) {
|
||||
assert.ifError(err);
|
||||
assert(isFollowing);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -829,6 +836,25 @@ describe('Controllers', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('post redirect', function () {
|
||||
it('should 404 for invalid pid', function (done) {
|
||||
request(nconf.get('url') + '/post/fail', function (err, res) {
|
||||
assert.ifError(err);
|
||||
assert.equal(res.statusCode, 404);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should return correct post path', function (done) {
|
||||
request(nconf.get('url') + '/api/post/' + pid, function (err, res, body) {
|
||||
assert.ifError(err);
|
||||
assert.equal(res.statusCode, 308);
|
||||
assert.equal(body, '"/topic/1/test-topic-title/1"');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
after(function (done) {
|
||||
var analytics = require('../src/analytics');
|
||||
analytics.writeData(function (err) {
|
||||
|
||||
139
test/groups.js
139
test/groups.js
File diff suppressed because one or more lines are too long
@@ -112,4 +112,58 @@ helpers.uploadFile = function (uploadEndPoint, filePath, body, jar, csrf_token,
|
||||
}
|
||||
callback(err, res, body);
|
||||
});
|
||||
};
|
||||
|
||||
helpers.registerUser = function (data, callback) {
|
||||
var jar = request.jar();
|
||||
request({
|
||||
url: nconf.get('url') + '/api/config',
|
||||
json: true,
|
||||
jar: jar
|
||||
}, function (err, response, body) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
request.post(nconf.get('url') + '/register', {
|
||||
form: data,
|
||||
json: true,
|
||||
jar: jar,
|
||||
headers: {
|
||||
'x-csrf-token': body.csrf_token
|
||||
}
|
||||
}, function (err, res, body) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
callback(null, jar);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
//http://stackoverflow.com/a/14387791/583363
|
||||
helpers.copyFile = function (source, target, callback) {
|
||||
|
||||
var cbCalled = false;
|
||||
|
||||
var rd = fs.createReadStream(source);
|
||||
rd.on("error", function (err) {
|
||||
done(err);
|
||||
});
|
||||
var wr = fs.createWriteStream(target);
|
||||
wr.on("error", function (err) {
|
||||
done(err);
|
||||
});
|
||||
wr.on("close", function () {
|
||||
done();
|
||||
});
|
||||
rd.pipe(wr);
|
||||
|
||||
function done(err) {
|
||||
if (!cbCalled) {
|
||||
callback(err);
|
||||
cbCalled = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -138,7 +138,7 @@
|
||||
nconf.set('base_templates_path', path.join(nconf.get('themes_path'), 'nodebb-theme-persona/templates'));
|
||||
nconf.set('theme_templates_path', meta.config['theme:templates'] ? path.join(nconf.get('themes_path'), meta.config['theme:id'], meta.config['theme:templates']) : nconf.get('base_templates_path'));
|
||||
nconf.set('theme_config', path.join(nconf.get('themes_path'), 'nodebb-theme-persona', 'theme.json'));
|
||||
nconf.set('bcrypt_rounds', 4);
|
||||
nconf.set('bcrypt_rounds', 1);
|
||||
|
||||
require('../../build').buildTargets(['js', 'clientCSS', 'acpCSS', 'tpl'], next);
|
||||
},
|
||||
|
||||
@@ -343,6 +343,55 @@ describe('Post\'s', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('move', function () {
|
||||
var replyPid;
|
||||
var tid;
|
||||
var moveTid;
|
||||
var socketPosts = require('../src/socket.io/posts');
|
||||
|
||||
before(function (done) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
topics.post({
|
||||
uid: voterUid,
|
||||
cid: cid,
|
||||
title: 'topic 1',
|
||||
content: 'some content'
|
||||
}, next);
|
||||
},
|
||||
function (data, next) {
|
||||
tid = data.topicData.tid;
|
||||
topics.post({
|
||||
uid: voterUid,
|
||||
cid: cid,
|
||||
title: 'topic 2',
|
||||
content: 'some content'
|
||||
}, next);
|
||||
},
|
||||
function (data, next) {
|
||||
moveTid = data.topicData.tid;
|
||||
topics.reply({
|
||||
uid: voterUid,
|
||||
tid: tid,
|
||||
timestamp: Date.now(),
|
||||
content: 'A reply to move'
|
||||
}, function (err, data) {
|
||||
assert.ifError(err);
|
||||
replyPid = data.pid;
|
||||
socketPosts.movePost({uid: globalModUid}, {pid: replyPid, tid: moveTid}, next);
|
||||
});
|
||||
},
|
||||
function (next) {
|
||||
posts.getPostField(replyPid, 'tid', next);
|
||||
},
|
||||
function (tid, next) {
|
||||
assert(tid, moveTid);
|
||||
next();
|
||||
}
|
||||
], done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('flagging a post', function () {
|
||||
var meta = require('../src/meta');
|
||||
var socketPosts = require('../src/socket.io/posts');
|
||||
|
||||
@@ -253,13 +253,45 @@ describe('socket.io', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('should validate emails', function (done) {
|
||||
|
||||
|
||||
describe('validation emails', function () {
|
||||
var socketAdmin = require('../src/socket.io/admin');
|
||||
socketAdmin.user.validateEmail({uid: adminUid}, [regularUid], function (err) {
|
||||
assert.ifError(err);
|
||||
user.getUserField(regularUid, 'email:confirmed', function (err, emailConfirmed) {
|
||||
var meta = require('../src/meta');
|
||||
|
||||
it('should validate emails', function (done) {
|
||||
socketAdmin.user.validateEmail({uid: adminUid}, [regularUid], function (err) {
|
||||
assert.ifError(err);
|
||||
assert.equal(parseInt(emailConfirmed, 10), 1);
|
||||
user.getUserField(regularUid, 'email:confirmed', function (err, emailConfirmed) {
|
||||
assert.ifError(err);
|
||||
assert.equal(parseInt(emailConfirmed, 10), 1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should error with invalid uids', function (done) {
|
||||
var socketAdmin = require('../src/socket.io/admin');
|
||||
socketAdmin.user.sendValidationEmail({uid: adminUid}, null, function (err) {
|
||||
assert.equal(err.message, '[[error:invalid-data]]');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should error if email validation is not required', function (done) {
|
||||
var socketAdmin = require('../src/socket.io/admin');
|
||||
socketAdmin.user.sendValidationEmail({uid: adminUid}, [regularUid], function (err) {
|
||||
assert.equal(err.message, '[[error:email-confirmations-are-disabled]]');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should send validation email', function (done) {
|
||||
var socketAdmin = require('../src/socket.io/admin');
|
||||
meta.config.requireEmailConfirmation = 1;
|
||||
socketAdmin.user.sendValidationEmail({uid: adminUid}, [regularUid], function (err) {
|
||||
assert.ifError(err);
|
||||
meta.config.requireEmailConfirmation = 0;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
218
test/user.js
218
test/user.js
@@ -170,6 +170,7 @@ describe('User', function () {
|
||||
});
|
||||
|
||||
describe('.search()', function () {
|
||||
var socketUser = require('../src/socket.io/user');
|
||||
it('should return an object containing an array of matching users', function (done) {
|
||||
User.search({query: 'john'}, function (err, searchData) {
|
||||
assert.ifError(err);
|
||||
@@ -178,6 +179,30 @@ describe('User', function () {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should search user', function (done) {
|
||||
socketUser.search({uid: testUid}, {query: 'john'}, function (err, searchData) {
|
||||
assert.ifError(err);
|
||||
assert.equal(searchData.users[0].username, 'John Smith');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should error for guest', function (done) {
|
||||
Meta.config.allowGuestUserSearching = 0;
|
||||
socketUser.search({uid: 0}, {query: 'john'}, function (err) {
|
||||
assert.equal(err.message, '[[error:not-logged-in]]');
|
||||
Meta.config.allowGuestUserSearching = 1;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should error with invalid data', function (done) {
|
||||
socketUser.search({uid: testUid}, null, function (err) {
|
||||
assert.equal(err.message, '[[error:invalid-data]]');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('.delete()', function () {
|
||||
@@ -659,6 +684,199 @@ describe('User', function () {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail if data is invalid', function (done) {
|
||||
socketUser.emailExists({uid: testUid}, null, function (err) {
|
||||
assert.equal(err.message, '[[error:invalid-data]]');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should return true if email exists', function (done) {
|
||||
socketUser.emailExists({uid: testUid}, {email: 'john@example.com'}, function (err, exists) {
|
||||
assert.ifError(err);
|
||||
assert(exists);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should return false if email does not exist', function (done) {
|
||||
socketUser.emailExists({uid: testUid}, {email: 'does@not.exist'}, function (err, exists) {
|
||||
assert.ifError(err);
|
||||
assert(!exists);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should error if requireEmailConfirmation is disabled', function (done) {
|
||||
socketUser.emailConfirm({uid: testUid}, {}, function (err) {
|
||||
assert.equal(err.message, '[[error:email-confirmations-are-disabled]]');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should send email confirm', function (done) {
|
||||
Meta.config.requireEmailConfirmation = 1;
|
||||
socketUser.emailConfirm({uid: testUid}, {}, function (err) {
|
||||
assert.ifError(err);
|
||||
Meta.config.requireEmailConfirmation = 0;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should send reset email', function (done) {
|
||||
socketUser.reset.send({uid: 0}, 'john@example.com', function (err) {
|
||||
assert.ifError(err);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should return invalid-data error', function (done) {
|
||||
socketUser.reset.send({uid: 0}, null, function (err) {
|
||||
assert.equal(err.message, '[[error:invalid-data]]');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not error', function (done) {
|
||||
socketUser.reset.send({uid: 0}, 'doestnot@exist.com', function (err) {
|
||||
assert.ifError(err);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should commit reset', function (done) {
|
||||
db.getObject('reset:uid', function (err, data) {
|
||||
assert.ifError(err);
|
||||
var code = Object.keys(data)[0];
|
||||
socketUser.reset.commit({uid: 0}, {code: code, password: 'swordfish'}, function (err) {
|
||||
assert.ifError(err);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should save user settings', function (done) {
|
||||
var data = {
|
||||
uid: 1,
|
||||
settings: {
|
||||
bootswatchSkin: 'default',
|
||||
homePageRoute: 'none',
|
||||
homePageCustom: '',
|
||||
openOutgoingLinksInNewTab: 0,
|
||||
scrollToMyPost: 1,
|
||||
delayImageLoading: 1,
|
||||
userLang: 'en-GB',
|
||||
usePagination: 1,
|
||||
topicsPerPage: '10',
|
||||
postsPerPage: '5',
|
||||
showemail: 1,
|
||||
showfullname: 1,
|
||||
restrictChat: 0,
|
||||
followTopicsOnCreate: 1,
|
||||
followTopicsOnReply: 1,
|
||||
notificationSound: '',
|
||||
incomingChatSound: '',
|
||||
outgoingChatSound: ''
|
||||
}
|
||||
};
|
||||
socketUser.saveSettings({uid: testUid}, data, function (err) {
|
||||
assert.ifError(err);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should set moderation note', function (done) {
|
||||
User.create({username: 'noteadmin'}, function (err, adminUid) {
|
||||
assert.ifError(err);
|
||||
groups.join('administrators', adminUid, function (err) {
|
||||
assert.ifError(err);
|
||||
socketUser.setModerationNote({uid: adminUid}, {uid: testUid, note: 'this is a test user'}, function (err) {
|
||||
assert.ifError(err);
|
||||
User.getUserField(testUid, 'moderationNote', function (err, note) {
|
||||
assert.ifError(err);
|
||||
assert.equal(note, 'this is a test user');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('approval queue', function () {
|
||||
var socketAdmin = require('../src/socket.io/admin');
|
||||
|
||||
var oldRegistrationType;
|
||||
var adminUid;
|
||||
before(function (done) {
|
||||
oldRegistrationType = Meta.config.registrationType;
|
||||
Meta.config.registrationType = 'admin-approval';
|
||||
User.create({username: 'admin', password: '123456'}, function (err, uid) {
|
||||
assert.ifError(err);
|
||||
adminUid = uid;
|
||||
groups.join('administrators', uid, done);
|
||||
});
|
||||
});
|
||||
|
||||
after(function (done) {
|
||||
Meta.config.registrationType = oldRegistrationType;
|
||||
done();
|
||||
});
|
||||
|
||||
it('should add user to approval queue', function (done) {
|
||||
helpers.registerUser({
|
||||
username: 'rejectme',
|
||||
password: '123456',
|
||||
email: 'reject@me.com'
|
||||
}, function (err) {
|
||||
assert.ifError(err);
|
||||
helpers.loginUser('admin', '123456', function (err, jar) {
|
||||
assert.ifError(err);
|
||||
request(nconf.get('url') + '/api/admin/manage/registration', {jar: jar, json: true}, function (err, res, body) {
|
||||
assert.ifError(err);
|
||||
assert.equal(body.users[0].username, 'rejectme');
|
||||
assert.equal(body.users[0].email, 'reject@me.com');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should reject user registration', function (done) {
|
||||
socketAdmin.user.rejectRegistration({uid: adminUid}, {username: 'rejectme'}, function (err) {
|
||||
assert.ifError(err);
|
||||
User.getRegistrationQueue(0, -1, function (err, users) {
|
||||
assert.ifError(err);
|
||||
assert.equal(users.length, 0);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should accept user registration', function (done) {
|
||||
helpers.registerUser({
|
||||
username: 'acceptme',
|
||||
password: '123456',
|
||||
email: 'accept@me.com'
|
||||
}, function (err) {
|
||||
assert.ifError(err);
|
||||
socketAdmin.user.acceptRegistration({uid: adminUid}, {username: 'acceptme'}, function (err, uid) {
|
||||
assert.ifError(err);
|
||||
User.exists(uid, function (err, exists) {
|
||||
assert.ifError(err);
|
||||
assert(exists);
|
||||
User.getRegistrationQueue(0, -1, function (err, users) {
|
||||
assert.ifError(err);
|
||||
assert.equal(users.length, 0);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user