mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-10-26 08:36:12 +01:00
@@ -260,5 +260,6 @@ User.search = function (socket, data, callback) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
User.restartJobs = function (socket, data, callback) {
|
User.restartJobs = function (socket, data, callback) {
|
||||||
user.startJobs(callback);
|
user.startJobs();
|
||||||
|
callback();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var async = require('async');
|
|
||||||
var _ = require('lodash');
|
var _ = require('lodash');
|
||||||
|
|
||||||
var groups = require('../groups');
|
var groups = require('../groups');
|
||||||
@@ -9,6 +8,7 @@ var db = require('../database');
|
|||||||
var privileges = require('../privileges');
|
var privileges = require('../privileges');
|
||||||
var categories = require('../categories');
|
var categories = require('../categories');
|
||||||
var meta = require('../meta');
|
var meta = require('../meta');
|
||||||
|
const utils = require('../utils');
|
||||||
|
|
||||||
var User = module.exports;
|
var User = module.exports;
|
||||||
|
|
||||||
@@ -40,68 +40,51 @@ require('./online')(User);
|
|||||||
require('./blocks')(User);
|
require('./blocks')(User);
|
||||||
require('./uploads')(User);
|
require('./uploads')(User);
|
||||||
|
|
||||||
User.getUidsFromSet = function (set, start, stop, callback) {
|
User.exists = async function (uid) {
|
||||||
|
return await db.exists('user:' + uid);
|
||||||
|
};
|
||||||
|
|
||||||
|
User.existsBySlug = async function (userslug) {
|
||||||
|
const exists = await User.getUidByUserslug(userslug);
|
||||||
|
return !!exists;
|
||||||
|
};
|
||||||
|
|
||||||
|
User.getUidsFromSet = async function (set, start, stop) {
|
||||||
if (set === 'users:online') {
|
if (set === 'users:online') {
|
||||||
var count = parseInt(stop, 10) === -1 ? stop : stop - start + 1;
|
const count = parseInt(stop, 10) === -1 ? stop : stop - start + 1;
|
||||||
var now = Date.now();
|
const now = Date.now();
|
||||||
db.getSortedSetRevRangeByScore(set, start, count, '+inf', now - (meta.config.onlineCutoff * 60000), callback);
|
return await db.getSortedSetRevRangeByScore(set, start, count, '+inf', now - (meta.config.onlineCutoff * 60000));
|
||||||
} else {
|
|
||||||
db.getSortedSetRevRange(set, start, stop, callback);
|
|
||||||
}
|
}
|
||||||
|
return await db.getSortedSetRevRange(set, start, stop);
|
||||||
};
|
};
|
||||||
|
|
||||||
User.getUsersFromSet = function (set, uid, start, stop, callback) {
|
User.getUsersFromSet = async function (set, uid, start, stop) {
|
||||||
async.waterfall([
|
const uids = await User.getUidsFromSet(set, start, stop);
|
||||||
function (next) {
|
return await User.getUsers(uids, uid);
|
||||||
User.getUidsFromSet(set, start, stop, next);
|
|
||||||
},
|
|
||||||
function (uids, next) {
|
|
||||||
User.getUsers(uids, uid, next);
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
User.getUsersWithFields = function (uids, fields, uid, callback) {
|
User.getUsersWithFields = async function (uids, fields, uid) {
|
||||||
async.waterfall([
|
let results = await plugins.fireHook('filter:users.addFields', { fields: fields });
|
||||||
function (next) {
|
results.fields = _.uniq(results.fields);
|
||||||
plugins.fireHook('filter:users.addFields', { fields: fields }, next);
|
const [userData, isAdmin] = await Promise.all([
|
||||||
},
|
User.getUsersFields(uids, results.fields),
|
||||||
function (data, next) {
|
User.isAdministrator(uids),
|
||||||
data.fields = _.uniq(data.fields);
|
]);
|
||||||
|
userData.forEach(function (user, index) {
|
||||||
async.parallel({
|
if (user) {
|
||||||
userData: function (next) {
|
user.administrator = isAdmin[index];
|
||||||
User.getUsersFields(uids, data.fields, next);
|
}
|
||||||
},
|
});
|
||||||
isAdmin: function (next) {
|
results = await plugins.fireHook('filter:userlist.get', { users: userData, uid: uid });
|
||||||
User.isAdministrator(uids, next);
|
return results.users;
|
||||||
},
|
|
||||||
}, next);
|
|
||||||
},
|
|
||||||
function (results, next) {
|
|
||||||
results.userData.forEach(function (user, index) {
|
|
||||||
if (user) {
|
|
||||||
user.administrator = results.isAdmin[index];
|
|
||||||
|
|
||||||
if (user.hasOwnProperty('status')) {
|
|
||||||
user.status = User.getStatus(user);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
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) {
|
User.getUsers = async function (uids, uid) {
|
||||||
User.getUsersWithFields(uids, [
|
return await User.getUsersWithFields(uids, [
|
||||||
'uid', 'username', 'userslug', 'picture', 'status',
|
'uid', 'username', 'userslug', 'picture', 'status',
|
||||||
'postcount', 'reputation', 'email:confirmed', 'lastonline',
|
'postcount', 'reputation', 'email:confirmed', 'lastonline',
|
||||||
'flags', 'banned', 'banned:expire', 'joindate',
|
'flags', 'banned', 'banned:expire', 'joindate',
|
||||||
], uid, callback);
|
], uid);
|
||||||
};
|
};
|
||||||
|
|
||||||
User.getStatus = function (userData) {
|
User.getStatus = function (userData) {
|
||||||
@@ -112,211 +95,135 @@ User.getStatus = function (userData) {
|
|||||||
return isOnline ? (userData.status || 'online') : 'offline';
|
return isOnline ? (userData.status || 'online') : 'offline';
|
||||||
};
|
};
|
||||||
|
|
||||||
User.exists = function (uid, callback) {
|
User.getUidByUsername = async function (username) {
|
||||||
db.exists('user:' + uid, callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
User.existsBySlug = function (userslug, callback) {
|
|
||||||
User.getUidByUserslug(userslug, function (err, exists) {
|
|
||||||
callback(err, !!exists);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
User.getUidByUsername = function (username, callback) {
|
|
||||||
if (!username) {
|
if (!username) {
|
||||||
return callback(null, 0);
|
return 0;
|
||||||
}
|
}
|
||||||
db.sortedSetScore('username:uid', username, callback);
|
return await db.sortedSetScore('username:uid', username);
|
||||||
};
|
};
|
||||||
|
|
||||||
User.getUidsByUsernames = function (usernames, callback) {
|
User.getUidsByUsernames = async function (usernames) {
|
||||||
db.sortedSetScores('username:uid', usernames, callback);
|
return await db.sortedSetScores('username:uid', usernames);
|
||||||
};
|
};
|
||||||
|
|
||||||
User.getUidByUserslug = function (userslug, callback) {
|
User.getUidByUserslug = async function (userslug) {
|
||||||
if (!userslug) {
|
if (!userslug) {
|
||||||
return callback(null, 0);
|
return 0;
|
||||||
}
|
}
|
||||||
db.sortedSetScore('userslug:uid', userslug, callback);
|
return await db.sortedSetScore('userslug:uid', userslug);
|
||||||
};
|
};
|
||||||
|
|
||||||
User.getUsernamesByUids = function (uids, callback) {
|
User.getUsernamesByUids = async function (uids) {
|
||||||
async.waterfall([
|
const users = await User.getUsersFields(uids, ['username']);
|
||||||
function (next) {
|
return users.map(user => user.username);
|
||||||
User.getUsersFields(uids, ['username'], next);
|
|
||||||
},
|
|
||||||
function (users, next) {
|
|
||||||
users = users.map(function (user) {
|
|
||||||
return user.username;
|
|
||||||
});
|
|
||||||
|
|
||||||
next(null, users);
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
User.getUsernameByUserslug = function (slug, callback) {
|
User.getUsernameByUserslug = async function (slug) {
|
||||||
async.waterfall([
|
const uid = await User.getUidByUserslug(slug);
|
||||||
function (next) {
|
return await User.getUserField(uid, 'username');
|
||||||
User.getUidByUserslug(slug, next);
|
|
||||||
},
|
|
||||||
function (uid, next) {
|
|
||||||
User.getUserField(uid, 'username', next);
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
User.getUidByEmail = function (email, callback) {
|
User.getUidByEmail = async function (email) {
|
||||||
db.sortedSetScore('email:uid', email.toLowerCase(), callback);
|
return await db.sortedSetScore('email:uid', email.toLowerCase());
|
||||||
};
|
};
|
||||||
|
|
||||||
User.getUidsByEmails = function (emails, callback) {
|
User.getUidsByEmails = async function (emails) {
|
||||||
emails = emails.map(function (email) {
|
emails = emails.map(email => email && email.toLowerCase());
|
||||||
return email && email.toLowerCase();
|
return await db.sortedSetScores('email:uid', emails);
|
||||||
});
|
|
||||||
db.sortedSetScores('email:uid', emails, callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
User.getUsernameByEmail = function (email, callback) {
|
User.getUsernameByEmail = async function (email) {
|
||||||
async.waterfall([
|
const uid = await db.sortedSetScore('email:uid', String(email).toLowerCase());
|
||||||
function (next) {
|
return await User.getUserField(uid, 'username');
|
||||||
db.sortedSetScore('email:uid', email.toLowerCase(), next);
|
|
||||||
},
|
|
||||||
function (uid, next) {
|
|
||||||
User.getUserField(uid, 'username', next);
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
User.isModerator = function (uid, cid, callback) {
|
User.isModerator = async function (uid, cid) {
|
||||||
privileges.users.isModerator(uid, cid, callback);
|
return await privileges.users.isModerator(uid, cid);
|
||||||
};
|
};
|
||||||
|
|
||||||
User.isModeratorOfAnyCategory = function (uid, callback) {
|
User.isModeratorOfAnyCategory = async function (uid) {
|
||||||
User.getModeratedCids(uid, function (err, cids) {
|
const cids = await User.getModeratedCids(uid);
|
||||||
callback(err, Array.isArray(cids) ? !!cids.length : false);
|
return Array.isArray(cids) ? !!cids.length : false;
|
||||||
|
};
|
||||||
|
|
||||||
|
User.isAdministrator = async function (uid) {
|
||||||
|
return await privileges.users.isAdministrator(uid);
|
||||||
|
};
|
||||||
|
|
||||||
|
User.isGlobalModerator = async function (uid) {
|
||||||
|
return await privileges.users.isGlobalModerator(uid);
|
||||||
|
};
|
||||||
|
|
||||||
|
User.getPrivileges = async function (uid) {
|
||||||
|
return await utils.promiseParallel({
|
||||||
|
isAdmin: User.isAdministrator(uid),
|
||||||
|
isGlobalModerator: User.isGlobalModerator(uid),
|
||||||
|
isModeratorOfAnyCategory: User.isModeratorOfAnyCategory(uid),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
User.isAdministrator = function (uid, callback) {
|
User.isPrivileged = async function (uid) {
|
||||||
privileges.users.isAdministrator(uid, callback);
|
const results = await User.getPrivileges(uid);
|
||||||
|
return results ? (results.isAdmin || results.isGlobalModerator || results.isModeratorOfAnyCategory) : false;
|
||||||
};
|
};
|
||||||
|
|
||||||
User.isGlobalModerator = function (uid, callback) {
|
User.isAdminOrGlobalMod = async function (uid) {
|
||||||
privileges.users.isGlobalModerator(uid, callback);
|
const [isAdmin, isGlobalMod] = await Promise.all([
|
||||||
|
User.isAdministrator(uid),
|
||||||
|
User.isGlobalModerator(uid),
|
||||||
|
]);
|
||||||
|
return isAdmin || isGlobalMod;
|
||||||
};
|
};
|
||||||
|
|
||||||
User.getPrivileges = function (uid, callback) {
|
User.isAdminOrSelf = async function (callerUid, uid) {
|
||||||
async.parallel({
|
await isSelfOrMethod(callerUid, uid, User.isAdministrator);
|
||||||
isAdmin: async.apply(User.isAdministrator, uid),
|
|
||||||
isGlobalModerator: async.apply(User.isGlobalModerator, uid),
|
|
||||||
isModeratorOfAnyCategory: async.apply(User.isModeratorOfAnyCategory, uid),
|
|
||||||
}, callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
User.isPrivileged = function (uid, callback) {
|
User.isAdminOrGlobalModOrSelf = async function (callerUid, uid) {
|
||||||
User.getPrivileges(uid, function (err, results) {
|
await isSelfOrMethod(callerUid, uid, User.isAdminOrGlobalMod);
|
||||||
callback(err, results ? (results.isAdmin || results.isGlobalModerator || results.isModeratorOfAnyCategory) : false);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
User.isAdminOrGlobalMod = function (uid, callback) {
|
User.isPrivilegedOrSelf = async function (callerUid, uid) {
|
||||||
async.parallel({
|
await isSelfOrMethod(callerUid, uid, User.isPrivileged);
|
||||||
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) {
|
async function isSelfOrMethod(callerUid, uid, method) {
|
||||||
isSelfOrMethod(callerUid, uid, User.isAdministrator, callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
User.isAdminOrGlobalModOrSelf = function (callerUid, uid, callback) {
|
|
||||||
isSelfOrMethod(callerUid, uid, User.isAdminOrGlobalMod, callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
User.isPrivilegedOrSelf = function (callerUid, uid, callback) {
|
|
||||||
isSelfOrMethod(callerUid, uid, User.isPrivileged, callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
function isSelfOrMethod(callerUid, uid, method, callback) {
|
|
||||||
if (parseInt(callerUid, 10) === parseInt(uid, 10)) {
|
if (parseInt(callerUid, 10) === parseInt(uid, 10)) {
|
||||||
return callback();
|
return;
|
||||||
|
}
|
||||||
|
const isPass = await method(callerUid);
|
||||||
|
if (!isPass) {
|
||||||
|
throw new Error('[[error:no-privileges]]');
|
||||||
}
|
}
|
||||||
async.waterfall([
|
|
||||||
function (next) {
|
|
||||||
method(callerUid, next);
|
|
||||||
},
|
|
||||||
function (isPass, next) {
|
|
||||||
if (!isPass) {
|
|
||||||
return next(new Error('[[error:no-privileges]]'));
|
|
||||||
}
|
|
||||||
next();
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
User.getAdminsandGlobalMods = function (callback) {
|
User.getAdminsandGlobalMods = async function () {
|
||||||
async.waterfall([
|
const results = await groups.getMembersOfGroups(['administrators', 'Global Moderators']);
|
||||||
function (next) {
|
return await User.getUsersData(_.union.apply(_, results));
|
||||||
async.parallel([
|
|
||||||
async.apply(groups.getMembers, 'administrators', 0, -1),
|
|
||||||
async.apply(groups.getMembers, 'Global Moderators', 0, -1),
|
|
||||||
], next);
|
|
||||||
},
|
|
||||||
function (results, next) {
|
|
||||||
User.getUsersData(_.union.apply(_, results), next);
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
User.getAdminsandGlobalModsandModerators = function (callback) {
|
User.getAdminsandGlobalModsandModerators = async function () {
|
||||||
async.waterfall([
|
const results = await Promise.all([
|
||||||
function (next) {
|
groups.getMembers('administrators', 0, -1),
|
||||||
async.parallel([
|
groups.getMembers('Global Moderators', 0, -1),
|
||||||
async.apply(groups.getMembers, 'administrators', 0, -1),
|
User.getModeratorUids(),
|
||||||
async.apply(groups.getMembers, 'Global Moderators', 0, -1),
|
]);
|
||||||
async.apply(User.getModeratorUids),
|
return await User.getUsersData(_.union.apply(_, results));
|
||||||
], next);
|
|
||||||
},
|
|
||||||
function (results, next) {
|
|
||||||
User.getUsersData(_.union.apply(_, results), next);
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
User.getModeratorUids = function (callback) {
|
User.getModeratorUids = async function () {
|
||||||
async.waterfall([
|
const cids = await categories.getAllCidsFromSet('categories:cid');
|
||||||
async.apply(categories.getAllCidsFromSet, 'categories:cid'),
|
const uids = await categories.getModeratorUids(cids);
|
||||||
function (cids, next) {
|
return _.union(...uids);
|
||||||
categories.getModeratorUids(cids, next);
|
|
||||||
},
|
|
||||||
function (uids, next) {
|
|
||||||
next(null, _.union(...uids));
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
User.getModeratedCids = function (uid, callback) {
|
User.getModeratedCids = async function (uid) {
|
||||||
if (parseInt(uid, 10) <= 0) {
|
if (parseInt(uid, 10) <= 0) {
|
||||||
return setImmediate(callback, null, []);
|
return [];
|
||||||
}
|
}
|
||||||
var cids;
|
const cids = await categories.getAllCidsFromSet('categories:cid');
|
||||||
async.waterfall([
|
const isMods = await User.isModerator(uid, cids);
|
||||||
function (next) {
|
return cids.filter((cid, index) => cid && isMods[index]);
|
||||||
categories.getAllCidsFromSet('categories:cid', next);
|
|
||||||
},
|
|
||||||
function (_cids, next) {
|
|
||||||
cids = _cids;
|
|
||||||
User.isModerator(uid, cids, next);
|
|
||||||
},
|
|
||||||
function (isMods, next) {
|
|
||||||
cids = cids.filter((cid, index) => cid && isMods[index]);
|
|
||||||
next(null, cids);
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
User.addInterstitials = function (callback) {
|
User.addInterstitials = function (callback) {
|
||||||
|
|||||||
262
src/user/info.js
262
src/user/info.js
@@ -1,6 +1,5 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var async = require('async');
|
|
||||||
var _ = require('lodash');
|
var _ = require('lodash');
|
||||||
var validator = require('validator');
|
var validator = require('validator');
|
||||||
|
|
||||||
@@ -11,181 +10,126 @@ var topics = require('../topics');
|
|||||||
var utils = require('../../public/src/utils');
|
var utils = require('../../public/src/utils');
|
||||||
|
|
||||||
module.exports = function (User) {
|
module.exports = function (User) {
|
||||||
User.getLatestBanInfo = function (uid, callback) {
|
User.getLatestBanInfo = async function (uid) {
|
||||||
// Simply retrieves the last record of the user's ban, even if they've been unbanned since then.
|
// Simply retrieves the last record of the user's ban, even if they've been unbanned since then.
|
||||||
async.waterfall([
|
const record = await db.getSortedSetRevRange('uid:' + uid + ':bans:timestamp', 0, 0);
|
||||||
function (next) {
|
if (!record.length) {
|
||||||
db.getSortedSetRevRange('uid:' + uid + ':bans:timestamp', 0, 0, next);
|
throw new Error('no-ban-info');
|
||||||
},
|
}
|
||||||
function (record, next) {
|
const banInfo = await db.getObject(record[0]);
|
||||||
if (!record.length) {
|
const expire = parseInt(banInfo.expire, 10);
|
||||||
return next(new Error('no-ban-info'));
|
const expire_readable = utils.toISOString(expire);
|
||||||
}
|
return {
|
||||||
db.getObject(record[0], next);
|
uid: uid,
|
||||||
},
|
timestamp: banInfo.timestamp,
|
||||||
function (banInfo, next) {
|
banned_until: expire,
|
||||||
const expire = parseInt(banInfo.expire, 10);
|
expiry: expire, /* backward compatible alias */
|
||||||
const expire_readable = utils.toISOString(expire);
|
banned_until_readable: expire_readable,
|
||||||
|
expiry_readable: expire_readable, /* backward compatible alias */
|
||||||
next(null, {
|
reason: validator.escape(String(banInfo.reason || '')),
|
||||||
uid: uid,
|
};
|
||||||
timestamp: banInfo.timestamp,
|
|
||||||
banned_until: expire,
|
|
||||||
expiry: expire, /* backward compatible alias */
|
|
||||||
banned_until_readable: expire_readable,
|
|
||||||
expiry_readable: expire_readable, /* backward compatible alias */
|
|
||||||
reason: validator.escape(String(banInfo.reason || '')),
|
|
||||||
});
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
User.getModerationHistory = function (uid, callback) {
|
User.getModerationHistory = async function (uid) {
|
||||||
async.waterfall([
|
let [flags, bans] = await Promise.all([
|
||||||
function (next) {
|
db.getSortedSetRevRangeWithScores('flags:byTargetUid:' + uid, 0, 19),
|
||||||
async.parallel({
|
db.getSortedSetRevRange('uid:' + uid + ':bans:timestamp', 0, 19),
|
||||||
flags: async.apply(db.getSortedSetRevRangeWithScores, 'flags:byTargetUid:' + uid, 0, 19),
|
]);
|
||||||
bans: async.apply(db.getSortedSetRevRange, 'uid:' + uid + ':bans:timestamp', 0, 19),
|
|
||||||
}, next);
|
// Get pids from flag objects
|
||||||
},
|
const keys = flags.map(flagObj => 'flag:' + flagObj.value);
|
||||||
function (data, next) {
|
const payload = await db.getObjectsFields(keys, ['type', 'targetId']);
|
||||||
// Get pids from flag objects
|
|
||||||
var keys = data.flags.map(function (flagObj) {
|
// Only pass on flag ids from posts
|
||||||
return 'flag:' + flagObj.value;
|
flags = payload.reduce(function (memo, cur, idx) {
|
||||||
|
if (cur.type === 'post') {
|
||||||
|
memo.push({
|
||||||
|
value: parseInt(cur.targetId, 10),
|
||||||
|
score: flags[idx].score,
|
||||||
});
|
});
|
||||||
db.getObjectsFields(keys, ['type', 'targetId'], function (err, payload) {
|
}
|
||||||
if (err) {
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only pass on flag ids from posts
|
return memo;
|
||||||
data.flags = payload.reduce(function (memo, cur, idx) {
|
}, []);
|
||||||
if (cur.type === 'post') {
|
|
||||||
memo.push({
|
|
||||||
value: parseInt(cur.targetId, 10),
|
|
||||||
score: data.flags[idx].score,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return memo;
|
[flags, bans] = await Promise.all([
|
||||||
}, []);
|
getFlagMetadata(flags),
|
||||||
|
formatBanData(bans),
|
||||||
|
]);
|
||||||
|
|
||||||
getFlagMetadata(data, next);
|
return {
|
||||||
});
|
flags: flags,
|
||||||
},
|
bans: bans,
|
||||||
function (data, next) {
|
};
|
||||||
formatBanData(data, next);
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
User.getHistory = function (set, callback) {
|
User.getHistory = async function (set) {
|
||||||
async.waterfall([
|
const data = await db.getSortedSetRevRangeWithScores(set, 0, -1);
|
||||||
function (next) {
|
return data.map(function (set) {
|
||||||
db.getSortedSetRevRangeWithScores(set, 0, -1, next);
|
set.timestamp = set.score;
|
||||||
},
|
set.timestampISO = utils.toISOString(set.score);
|
||||||
function (data, next) {
|
set.value = validator.escape(String(set.value.split(':')[0]));
|
||||||
next(null, data.map(function (set) {
|
delete set.score;
|
||||||
set.timestamp = set.score;
|
return set;
|
||||||
set.timestampISO = utils.toISOString(set.score);
|
|
||||||
set.value = validator.escape(String(set.value.split(':')[0]));
|
|
||||||
delete set.score;
|
|
||||||
return set;
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
function getFlagMetadata(data, callback) {
|
|
||||||
var pids = data.flags.map(function (flagObj) {
|
|
||||||
return parseInt(flagObj.value, 10);
|
|
||||||
});
|
});
|
||||||
async.waterfall([
|
};
|
||||||
function (next) {
|
|
||||||
posts.getPostsFields(pids, ['tid'], next);
|
|
||||||
},
|
|
||||||
function (postData, next) {
|
|
||||||
var tids = postData.map(function (post) {
|
|
||||||
return post.tid;
|
|
||||||
});
|
|
||||||
|
|
||||||
topics.getTopicsFields(tids, ['title'], next);
|
async function getFlagMetadata(flags) {
|
||||||
},
|
const pids = flags.map(flagObj => parseInt(flagObj.value, 10));
|
||||||
function (topicData, next) {
|
const postData = await posts.getPostsFields(pids, ['tid']);
|
||||||
data.flags = data.flags.map(function (flagObj, idx) {
|
const tids = postData.map(post => post.tid);
|
||||||
flagObj.pid = flagObj.value;
|
|
||||||
flagObj.timestamp = flagObj.score;
|
|
||||||
flagObj.timestampISO = new Date(flagObj.score).toISOString();
|
|
||||||
flagObj.timestampReadable = new Date(flagObj.score).toString();
|
|
||||||
|
|
||||||
delete flagObj.value;
|
const topicData = await topics.getTopicsFields(tids, ['title']);
|
||||||
delete flagObj.score;
|
flags = flags.map(function (flagObj, idx) {
|
||||||
|
flagObj.pid = flagObj.value;
|
||||||
|
flagObj.timestamp = flagObj.score;
|
||||||
|
flagObj.timestampISO = new Date(flagObj.score).toISOString();
|
||||||
|
flagObj.timestampReadable = new Date(flagObj.score).toString();
|
||||||
|
|
||||||
return _.extend(flagObj, topicData[idx]);
|
delete flagObj.value;
|
||||||
});
|
delete flagObj.score;
|
||||||
next(null, data);
|
|
||||||
},
|
return _.extend(flagObj, topicData[idx]);
|
||||||
], callback);
|
});
|
||||||
|
return flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatBanData(data, callback) {
|
async function formatBanData(bans) {
|
||||||
var banData;
|
const banData = await db.getObjects(bans);
|
||||||
async.waterfall([
|
const uids = banData.map(banData => banData.fromUid);
|
||||||
function (next) {
|
const usersData = await user.getUsersFields(uids, ['uid', 'username', 'userslug', 'picture']);
|
||||||
db.getObjects(data.bans, next);
|
return banData.map(function (banObj, index) {
|
||||||
},
|
banObj.user = usersData[index];
|
||||||
function (_banData, next) {
|
banObj.until = parseInt(banObj.expire, 10);
|
||||||
banData = _banData;
|
banObj.untilReadable = new Date(banObj.until).toString();
|
||||||
var uids = banData.map(banData => banData.fromUid);
|
banObj.timestampReadable = new Date(banObj.timestamp).toString();
|
||||||
|
banObj.timestampISO = utils.toISOString(banObj.timestamp);
|
||||||
user.getUsersFields(uids, ['uid', 'username', 'userslug', 'picture'], next);
|
banObj.reason = validator.escape(String(banObj.reason || '')) || '[[user:info.banned-no-reason]]';
|
||||||
},
|
return banObj;
|
||||||
function (usersData, next) {
|
});
|
||||||
data.bans = banData.map(function (banObj, index) {
|
|
||||||
banObj.user = usersData[index];
|
|
||||||
banObj.until = parseInt(banObj.expire, 10);
|
|
||||||
banObj.untilReadable = new Date(banObj.until).toString();
|
|
||||||
banObj.timestampReadable = new Date(banObj.timestamp).toString();
|
|
||||||
banObj.timestampISO = utils.toISOString(banObj.timestamp);
|
|
||||||
banObj.reason = validator.escape(String(banObj.reason || '')) || '[[user:info.banned-no-reason]]';
|
|
||||||
return banObj;
|
|
||||||
});
|
|
||||||
next(null, data);
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
User.getModerationNotes = function (uid, start, stop, callback) {
|
User.getModerationNotes = async function (uid, start, stop) {
|
||||||
var noteData;
|
const noteIds = await db.getSortedSetRevRange('uid:' + uid + ':moderation:notes', start, stop);
|
||||||
async.waterfall([
|
const keys = noteIds.map(id => 'uid:' + uid + ':moderation:note:' + id);
|
||||||
function (next) {
|
const notes = await db.getObjects(keys);
|
||||||
db.getSortedSetRevRange('uid:' + uid + ':moderation:notes', start, stop, next);
|
const uids = [];
|
||||||
},
|
|
||||||
function (noteIds, next) {
|
|
||||||
const keys = noteIds.map(id => 'uid:' + uid + ':moderation:note:' + id);
|
|
||||||
db.getObjects(keys, next);
|
|
||||||
},
|
|
||||||
function (notes, next) {
|
|
||||||
var uids = [];
|
|
||||||
noteData = notes.map(function (note) {
|
|
||||||
if (note) {
|
|
||||||
uids.push(note.uid);
|
|
||||||
note.timestampISO = utils.toISOString(note.timestamp);
|
|
||||||
note.note = validator.escape(String(note.note));
|
|
||||||
}
|
|
||||||
return note;
|
|
||||||
});
|
|
||||||
|
|
||||||
User.getUsersFields(uids, ['uid', 'username', 'userslug', 'picture'], next);
|
const noteData = notes.map(function (note) {
|
||||||
},
|
if (note) {
|
||||||
function (userData, next) {
|
uids.push(note.uid);
|
||||||
noteData.forEach(function (note, index) {
|
note.timestampISO = utils.toISOString(note.timestamp);
|
||||||
if (note) {
|
note.note = validator.escape(String(note.note));
|
||||||
note.user = userData[index];
|
}
|
||||||
}
|
return note;
|
||||||
});
|
});
|
||||||
next(null, noteData);
|
|
||||||
},
|
const userData = await User.getUsersFields(uids, ['uid', 'username', 'userslug', 'picture']);
|
||||||
], callback);
|
noteData.forEach(function (note, index) {
|
||||||
|
if (note) {
|
||||||
|
note.user = userData[index];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return noteData;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,175 +12,95 @@ var translator = require('../translator');
|
|||||||
var utils = require('../utils');
|
var utils = require('../utils');
|
||||||
|
|
||||||
module.exports = function (User) {
|
module.exports = function (User) {
|
||||||
User.getInvites = function (uid, callback) {
|
User.getInvites = async function (uid) {
|
||||||
async.waterfall([
|
const emails = await db.getSetMembers('invitation:uid:' + uid);
|
||||||
function (next) {
|
return emails.map(email => validator.escape(String(email)));
|
||||||
db.getSetMembers('invitation:uid:' + uid, next);
|
|
||||||
},
|
|
||||||
function (emails, next) {
|
|
||||||
emails = emails.map(function (email) {
|
|
||||||
return validator.escape(String(email));
|
|
||||||
});
|
|
||||||
next(null, emails);
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
User.getInvitesNumber = function (uid, callback) {
|
User.getInvitesNumber = async function (uid) {
|
||||||
db.setCount('invitation:uid:' + uid, callback);
|
return await db.setCount('invitation:uid:' + uid);
|
||||||
};
|
};
|
||||||
|
|
||||||
User.getInvitingUsers = function (callback) {
|
User.getInvitingUsers = async function () {
|
||||||
db.getSetMembers('invitation:uids', callback);
|
return await db.getSetMembers('invitation:uids');
|
||||||
};
|
};
|
||||||
|
|
||||||
User.getAllInvites = function (callback) {
|
User.getAllInvites = async function () {
|
||||||
var uids;
|
const uids = await User.getInvitingUsers();
|
||||||
async.waterfall([
|
const invitations = await async.map(uids, User.getInvites);
|
||||||
User.getInvitingUsers,
|
return invitations.map(function (invites, index) {
|
||||||
function (_uids, next) {
|
return {
|
||||||
uids = _uids;
|
uid: uids[index],
|
||||||
async.map(uids, User.getInvites, next);
|
invitations: invites,
|
||||||
},
|
};
|
||||||
function (invitations, next) {
|
});
|
||||||
invitations = invitations.map(function (invites, index) {
|
|
||||||
return {
|
|
||||||
uid: uids[index],
|
|
||||||
invitations: invites,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
next(null, invitations);
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
User.sendInvitationEmail = function (uid, email, callback) {
|
User.sendInvitationEmail = async function (uid, email) {
|
||||||
callback = callback || function () {};
|
const token = utils.generateUUID();
|
||||||
|
const registerLink = nconf.get('url') + '/register?token=' + token + '&email=' + encodeURIComponent(email);
|
||||||
|
|
||||||
var token = utils.generateUUID();
|
const expireDays = meta.config.inviteExpiration;
|
||||||
var registerLink = nconf.get('url') + '/register?token=' + token + '&email=' + encodeURIComponent(email);
|
const expireIn = expireDays * 86400000;
|
||||||
|
|
||||||
var expireDays = meta.config.inviteExpiration;
|
const exists = await User.getUidByEmail(email);
|
||||||
var expireIn = expireDays * 86400000;
|
if (exists) {
|
||||||
|
throw new Error('[[error:email-taken]]');
|
||||||
async.waterfall([
|
|
||||||
function (next) {
|
|
||||||
User.getUidByEmail(email, next);
|
|
||||||
},
|
|
||||||
function (exists, next) {
|
|
||||||
if (exists) {
|
|
||||||
return next(new Error('[[error:email-taken]]'));
|
|
||||||
}
|
|
||||||
db.setAdd('invitation:uid:' + uid, email, next);
|
|
||||||
},
|
|
||||||
function (next) {
|
|
||||||
db.setAdd('invitation:uids', uid, next);
|
|
||||||
},
|
|
||||||
function (next) {
|
|
||||||
db.set('invitation:email:' + email, token, next);
|
|
||||||
},
|
|
||||||
function (next) {
|
|
||||||
db.pexpireAt('invitation:email:' + email, Date.now() + expireIn, next);
|
|
||||||
},
|
|
||||||
function (next) {
|
|
||||||
User.getUserField(uid, 'username', next);
|
|
||||||
},
|
|
||||||
function (username, next) {
|
|
||||||
var title = meta.config.title || meta.config.browserTitle || 'NodeBB';
|
|
||||||
translator.translate('[[email:invite, ' + title + ']]', meta.config.defaultLang, function (subject) {
|
|
||||||
var data = {
|
|
||||||
site_title: title,
|
|
||||||
registerLink: registerLink,
|
|
||||||
subject: subject,
|
|
||||||
username: username,
|
|
||||||
template: 'invitation',
|
|
||||||
expireDays: expireDays,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Append default data to this email payload
|
|
||||||
data = Object.assign({}, emailer._defaultPayload, data);
|
|
||||||
|
|
||||||
emailer.sendToEmail('invitation', email, meta.config.defaultLang, data, next);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
User.verifyInvitation = function (query, callback) {
|
|
||||||
if (!query.token || !query.email) {
|
|
||||||
return callback(new Error('[[error:invalid-data]]'));
|
|
||||||
}
|
}
|
||||||
|
await db.setAdd('invitation:uid:' + uid, email);
|
||||||
|
await db.setAdd('invitation:uids', uid);
|
||||||
|
await db.set('invitation:email:' + email, token);
|
||||||
|
await db.pexpireAt('invitation:email:' + email, Date.now() + expireIn);
|
||||||
|
const username = await User.getUserField(uid, 'username');
|
||||||
|
const title = meta.config.title || meta.config.browserTitle || 'NodeBB';
|
||||||
|
const subject = await translator.translate('[[email:invite, ' + title + ']]', meta.config.defaultLang);
|
||||||
|
let data = {
|
||||||
|
site_title: title,
|
||||||
|
registerLink: registerLink,
|
||||||
|
subject: subject,
|
||||||
|
username: username,
|
||||||
|
template: 'invitation',
|
||||||
|
expireDays: expireDays,
|
||||||
|
};
|
||||||
|
|
||||||
async.waterfall([
|
// Append default data to this email payload
|
||||||
function (next) {
|
data = Object.assign({}, emailer._defaultPayload, data);
|
||||||
db.get('invitation:email:' + query.email, next);
|
|
||||||
},
|
|
||||||
function (token, next) {
|
|
||||||
if (!token || token !== query.token) {
|
|
||||||
return next(new Error('[[error:invalid-token]]'));
|
|
||||||
}
|
|
||||||
|
|
||||||
next();
|
await emailer.sendToEmail('invitation', email, meta.config.defaultLang, data);
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
User.deleteInvitation = function (invitedBy, email, callback) {
|
User.verifyInvitation = async function (query) {
|
||||||
callback = callback || function () {};
|
if (!query.token || !query.email) {
|
||||||
async.waterfall([
|
throw new Error('[[error:invalid-data]]');
|
||||||
function getInvitedByUid(next) {
|
}
|
||||||
User.getUidByUsername(invitedBy, next);
|
const token = await db.get('invitation:email:' + query.email);
|
||||||
},
|
if (!token || token !== query.token) {
|
||||||
function deleteRegistries(invitedByUid, next) {
|
throw new Error('[[error:invalid-token]]');
|
||||||
if (!invitedByUid) {
|
}
|
||||||
return next(new Error('[[error:invalid-username]]'));
|
|
||||||
}
|
|
||||||
async.parallel([
|
|
||||||
function (next) {
|
|
||||||
deleteFromReferenceList(invitedByUid, email, next);
|
|
||||||
},
|
|
||||||
function (next) {
|
|
||||||
db.delete('invitation:email:' + email, next);
|
|
||||||
},
|
|
||||||
], function (err) {
|
|
||||||
next(err);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
User.deleteInvitationKey = function (email, callback) {
|
User.deleteInvitation = async function (invitedBy, email) {
|
||||||
callback = callback || function () {};
|
const invitedByUid = await User.getUidByUsername(invitedBy);
|
||||||
|
if (!invitedByUid) {
|
||||||
async.waterfall([
|
throw new Error('[[error:invalid-username]]');
|
||||||
function (next) {
|
}
|
||||||
User.getInvitingUsers(next);
|
await Promise.all([
|
||||||
},
|
deleteFromReferenceList(invitedByUid, email),
|
||||||
function (uids, next) {
|
db.delete('invitation:email:' + email),
|
||||||
async.each(uids, function (uid, next) {
|
]);
|
||||||
deleteFromReferenceList(uid, email, next);
|
|
||||||
}, next);
|
|
||||||
},
|
|
||||||
function (next) {
|
|
||||||
db.delete('invitation:email:' + email, next);
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function deleteFromReferenceList(uid, email, callback) {
|
User.deleteInvitationKey = async function (email) {
|
||||||
async.waterfall([
|
const uids = await User.getInvitingUsers();
|
||||||
function (next) {
|
await Promise.all(uids.map(uid => deleteFromReferenceList(uid, email)));
|
||||||
db.setRemove('invitation:uid:' + uid, email, next);
|
await db.delete('invitation:email:' + email);
|
||||||
},
|
};
|
||||||
function (next) {
|
|
||||||
db.setCount('invitation:uid:' + uid, next);
|
async function deleteFromReferenceList(uid, email) {
|
||||||
},
|
await db.setRemove('invitation:uid:' + uid, email);
|
||||||
function (count, next) {
|
const count = await db.setCount('invitation:uid:' + uid);
|
||||||
if (count === 0) {
|
if (count === 0) {
|
||||||
return db.setRemove('invitation:uids', uid, next);
|
await db.setRemove('invitation:uids', uid);
|
||||||
}
|
}
|
||||||
setImmediate(next);
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ var meta = require('../meta');
|
|||||||
var jobs = {};
|
var jobs = {};
|
||||||
|
|
||||||
module.exports = function (User) {
|
module.exports = function (User) {
|
||||||
User.startJobs = function (callback) {
|
User.startJobs = function () {
|
||||||
winston.verbose('[user/jobs] (Re-)starting user jobs...');
|
winston.verbose('[user/jobs] (Re-)starting user jobs...');
|
||||||
|
|
||||||
var started = 0;
|
var started = 0;
|
||||||
@@ -33,10 +33,6 @@ module.exports = function (User) {
|
|||||||
started += 1;
|
started += 1;
|
||||||
|
|
||||||
winston.verbose('[user/jobs] ' + started + ' jobs started');
|
winston.verbose('[user/jobs] ' + started + ' jobs started');
|
||||||
|
|
||||||
if (typeof callback === 'function') {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function startDigestJob(name, cronString, term) {
|
function startDigestJob(name, cronString, term) {
|
||||||
|
|||||||
@@ -1891,10 +1891,8 @@ describe('User', function () {
|
|||||||
|
|
||||||
describe('user jobs', function () {
|
describe('user jobs', function () {
|
||||||
it('should start user jobs', function (done) {
|
it('should start user jobs', function (done) {
|
||||||
User.startJobs(function (err) {
|
User.startJobs();
|
||||||
assert.ifError(err);
|
done();
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should stop user jobs', function (done) {
|
it('should stop user jobs', function (done) {
|
||||||
|
|||||||
Reference in New Issue
Block a user