|
|
|
@ -1,6 +1,5 @@
|
|
|
|
|
'use strict';
|
|
|
|
|
|
|
|
|
|
var async = require('async');
|
|
|
|
|
var _ = require('lodash');
|
|
|
|
|
|
|
|
|
|
var groups = require('../groups');
|
|
|
|
@ -9,6 +8,7 @@ var db = require('../database');
|
|
|
|
|
var privileges = require('../privileges');
|
|
|
|
|
var categories = require('../categories');
|
|
|
|
|
var meta = require('../meta');
|
|
|
|
|
const utils = require('../utils');
|
|
|
|
|
|
|
|
|
|
var User = module.exports;
|
|
|
|
|
|
|
|
|
@ -40,68 +40,51 @@ require('./online')(User);
|
|
|
|
|
require('./blocks')(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') {
|
|
|
|
|
var count = parseInt(stop, 10) === -1 ? stop : stop - start + 1;
|
|
|
|
|
var now = Date.now();
|
|
|
|
|
db.getSortedSetRevRangeByScore(set, start, count, '+inf', now - (meta.config.onlineCutoff * 60000), callback);
|
|
|
|
|
} else {
|
|
|
|
|
db.getSortedSetRevRange(set, start, stop, callback);
|
|
|
|
|
const count = parseInt(stop, 10) === -1 ? stop : stop - start + 1;
|
|
|
|
|
const now = Date.now();
|
|
|
|
|
return await db.getSortedSetRevRangeByScore(set, start, count, '+inf', now - (meta.config.onlineCutoff * 60000));
|
|
|
|
|
}
|
|
|
|
|
return await db.getSortedSetRevRange(set, start, stop);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
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.getUsersFromSet = async function (set, uid, start, stop) {
|
|
|
|
|
const uids = await User.getUidsFromSet(set, start, stop);
|
|
|
|
|
return await User.getUsers(uids, uid);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
User.getUsersWithFields = function (uids, fields, uid, callback) {
|
|
|
|
|
async.waterfall([
|
|
|
|
|
function (next) {
|
|
|
|
|
plugins.fireHook('filter:users.addFields', { fields: fields }, next);
|
|
|
|
|
},
|
|
|
|
|
function (data, next) {
|
|
|
|
|
data.fields = _.uniq(data.fields);
|
|
|
|
|
|
|
|
|
|
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.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.getUsersWithFields = async function (uids, fields, uid) {
|
|
|
|
|
let results = await plugins.fireHook('filter:users.addFields', { fields: fields });
|
|
|
|
|
results.fields = _.uniq(results.fields);
|
|
|
|
|
const [userData, isAdmin] = await Promise.all([
|
|
|
|
|
User.getUsersFields(uids, results.fields),
|
|
|
|
|
User.isAdministrator(uids),
|
|
|
|
|
]);
|
|
|
|
|
userData.forEach(function (user, index) {
|
|
|
|
|
if (user) {
|
|
|
|
|
user.administrator = isAdmin[index];
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
results = await plugins.fireHook('filter:userlist.get', { users: userData, uid: uid });
|
|
|
|
|
return results.users;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
User.getUsers = function (uids, uid, callback) {
|
|
|
|
|
User.getUsersWithFields(uids, [
|
|
|
|
|
User.getUsers = async function (uids, uid) {
|
|
|
|
|
return await User.getUsersWithFields(uids, [
|
|
|
|
|
'uid', 'username', 'userslug', 'picture', 'status',
|
|
|
|
|
'postcount', 'reputation', 'email:confirmed', 'lastonline',
|
|
|
|
|
'flags', 'banned', 'banned:expire', 'joindate',
|
|
|
|
|
], uid, callback);
|
|
|
|
|
], uid);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
User.getStatus = function (userData) {
|
|
|
|
@ -112,211 +95,135 @@ User.getStatus = function (userData) {
|
|
|
|
|
return isOnline ? (userData.status || 'online') : 'offline';
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
User.exists = function (uid, callback) {
|
|
|
|
|
db.exists('user:' + uid, callback);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
User.existsBySlug = function (userslug, callback) {
|
|
|
|
|
User.getUidByUserslug(userslug, function (err, exists) {
|
|
|
|
|
callback(err, !!exists);
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
User.getUidByUsername = function (username, callback) {
|
|
|
|
|
User.getUidByUsername = async function (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) {
|
|
|
|
|
db.sortedSetScores('username:uid', usernames, callback);
|
|
|
|
|
User.getUidsByUsernames = async function (usernames) {
|
|
|
|
|
return await db.sortedSetScores('username:uid', usernames);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
User.getUidByUserslug = function (userslug, callback) {
|
|
|
|
|
User.getUidByUserslug = async function (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) {
|
|
|
|
|
async.waterfall([
|
|
|
|
|
function (next) {
|
|
|
|
|
User.getUsersFields(uids, ['username'], next);
|
|
|
|
|
},
|
|
|
|
|
function (users, next) {
|
|
|
|
|
users = users.map(function (user) {
|
|
|
|
|
return user.username;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
next(null, users);
|
|
|
|
|
},
|
|
|
|
|
], callback);
|
|
|
|
|
User.getUsernamesByUids = async function (uids) {
|
|
|
|
|
const users = await User.getUsersFields(uids, ['username']);
|
|
|
|
|
return users.map(user => user.username);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
User.getUsernameByUserslug = function (slug, callback) {
|
|
|
|
|
async.waterfall([
|
|
|
|
|
function (next) {
|
|
|
|
|
User.getUidByUserslug(slug, next);
|
|
|
|
|
},
|
|
|
|
|
function (uid, next) {
|
|
|
|
|
User.getUserField(uid, 'username', next);
|
|
|
|
|
},
|
|
|
|
|
], callback);
|
|
|
|
|
User.getUsernameByUserslug = async function (slug) {
|
|
|
|
|
const uid = await User.getUidByUserslug(slug);
|
|
|
|
|
return await User.getUserField(uid, 'username');
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
User.getUidByEmail = function (email, callback) {
|
|
|
|
|
db.sortedSetScore('email:uid', email.toLowerCase(), callback);
|
|
|
|
|
User.getUidByEmail = async function (email) {
|
|
|
|
|
return await db.sortedSetScore('email:uid', email.toLowerCase());
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
User.getUidsByEmails = function (emails, callback) {
|
|
|
|
|
emails = emails.map(function (email) {
|
|
|
|
|
return email && email.toLowerCase();
|
|
|
|
|
});
|
|
|
|
|
db.sortedSetScores('email:uid', emails, callback);
|
|
|
|
|
User.getUidsByEmails = async function (emails) {
|
|
|
|
|
emails = emails.map(email => email && email.toLowerCase());
|
|
|
|
|
return await db.sortedSetScores('email:uid', emails);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
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.getUsernameByEmail = async function (email) {
|
|
|
|
|
const uid = await db.sortedSetScore('email:uid', String(email).toLowerCase());
|
|
|
|
|
return await User.getUserField(uid, 'username');
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
User.isModerator = function (uid, cid, callback) {
|
|
|
|
|
privileges.users.isModerator(uid, cid, callback);
|
|
|
|
|
User.isModerator = async function (uid, cid) {
|
|
|
|
|
return await privileges.users.isModerator(uid, cid);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
User.isModeratorOfAnyCategory = function (uid, callback) {
|
|
|
|
|
User.getModeratedCids(uid, function (err, cids) {
|
|
|
|
|
callback(err, Array.isArray(cids) ? !!cids.length : false);
|
|
|
|
|
});
|
|
|
|
|
User.isModeratorOfAnyCategory = async function (uid) {
|
|
|
|
|
const cids = await User.getModeratedCids(uid);
|
|
|
|
|
return Array.isArray(cids) ? !!cids.length : false;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
User.isAdministrator = function (uid, callback) {
|
|
|
|
|
privileges.users.isAdministrator(uid, callback);
|
|
|
|
|
User.isAdministrator = async function (uid) {
|
|
|
|
|
return await privileges.users.isAdministrator(uid);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
User.isGlobalModerator = function (uid, callback) {
|
|
|
|
|
privileges.users.isGlobalModerator(uid, callback);
|
|
|
|
|
User.isGlobalModerator = async function (uid) {
|
|
|
|
|
return await privileges.users.isGlobalModerator(uid);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
User.getPrivileges = function (uid, callback) {
|
|
|
|
|
async.parallel({
|
|
|
|
|
isAdmin: async.apply(User.isAdministrator, uid),
|
|
|
|
|
isGlobalModerator: async.apply(User.isGlobalModerator, uid),
|
|
|
|
|
isModeratorOfAnyCategory: async.apply(User.isModeratorOfAnyCategory, uid),
|
|
|
|
|
}, callback);
|
|
|
|
|
User.getPrivileges = async function (uid) {
|
|
|
|
|
return await utils.promiseParallel({
|
|
|
|
|
isAdmin: User.isAdministrator(uid),
|
|
|
|
|
isGlobalModerator: User.isGlobalModerator(uid),
|
|
|
|
|
isModeratorOfAnyCategory: User.isModeratorOfAnyCategory(uid),
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
User.isPrivileged = function (uid, callback) {
|
|
|
|
|
User.getPrivileges(uid, function (err, results) {
|
|
|
|
|
callback(err, results ? (results.isAdmin || results.isGlobalModerator || results.isModeratorOfAnyCategory) : false);
|
|
|
|
|
});
|
|
|
|
|
User.isPrivileged = async function (uid) {
|
|
|
|
|
const results = await User.getPrivileges(uid);
|
|
|
|
|
return results ? (results.isAdmin || results.isGlobalModerator || results.isModeratorOfAnyCategory) : 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.isAdminOrGlobalMod = async function (uid) {
|
|
|
|
|
const [isAdmin, isGlobalMod] = await Promise.all([
|
|
|
|
|
User.isAdministrator(uid),
|
|
|
|
|
User.isGlobalModerator(uid),
|
|
|
|
|
]);
|
|
|
|
|
return isAdmin || isGlobalMod;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
User.isAdminOrSelf = function (callerUid, uid, callback) {
|
|
|
|
|
isSelfOrMethod(callerUid, uid, User.isAdministrator, callback);
|
|
|
|
|
User.isAdminOrSelf = async function (callerUid, uid) {
|
|
|
|
|
await isSelfOrMethod(callerUid, uid, User.isAdministrator);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
User.isAdminOrGlobalModOrSelf = function (callerUid, uid, callback) {
|
|
|
|
|
isSelfOrMethod(callerUid, uid, User.isAdminOrGlobalMod, callback);
|
|
|
|
|
User.isAdminOrGlobalModOrSelf = async function (callerUid, uid) {
|
|
|
|
|
await isSelfOrMethod(callerUid, uid, User.isAdminOrGlobalMod);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
User.isPrivilegedOrSelf = function (callerUid, uid, callback) {
|
|
|
|
|
isSelfOrMethod(callerUid, uid, User.isPrivileged, callback);
|
|
|
|
|
User.isPrivilegedOrSelf = async function (callerUid, uid) {
|
|
|
|
|
await isSelfOrMethod(callerUid, uid, User.isPrivileged);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function isSelfOrMethod(callerUid, uid, method, callback) {
|
|
|
|
|
async function isSelfOrMethod(callerUid, uid, method) {
|
|
|
|
|
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) {
|
|
|
|
|
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.apply(_, results), next);
|
|
|
|
|
},
|
|
|
|
|
], callback);
|
|
|
|
|
User.getAdminsandGlobalMods = async function () {
|
|
|
|
|
const results = await groups.getMembersOfGroups(['administrators', 'Global Moderators']);
|
|
|
|
|
return await User.getUsersData(_.union.apply(_, results));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
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.getAdminsandGlobalModsandModerators = async function () {
|
|
|
|
|
const results = await Promise.all([
|
|
|
|
|
groups.getMembers('administrators', 0, -1),
|
|
|
|
|
groups.getMembers('Global Moderators', 0, -1),
|
|
|
|
|
User.getModeratorUids(),
|
|
|
|
|
]);
|
|
|
|
|
return await User.getUsersData(_.union.apply(_, results));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
User.getModeratorUids = function (callback) {
|
|
|
|
|
async.waterfall([
|
|
|
|
|
async.apply(categories.getAllCidsFromSet, 'categories:cid'),
|
|
|
|
|
function (cids, next) {
|
|
|
|
|
categories.getModeratorUids(cids, next);
|
|
|
|
|
},
|
|
|
|
|
function (uids, next) {
|
|
|
|
|
next(null, _.union(...uids));
|
|
|
|
|
},
|
|
|
|
|
], callback);
|
|
|
|
|
User.getModeratorUids = async function () {
|
|
|
|
|
const cids = await categories.getAllCidsFromSet('categories:cid');
|
|
|
|
|
const uids = await categories.getModeratorUids(cids);
|
|
|
|
|
return _.union(...uids);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
User.getModeratedCids = function (uid, callback) {
|
|
|
|
|
User.getModeratedCids = async function (uid) {
|
|
|
|
|
if (parseInt(uid, 10) <= 0) {
|
|
|
|
|
return setImmediate(callback, null, []);
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
var cids;
|
|
|
|
|
async.waterfall([
|
|
|
|
|
function (next) {
|
|
|
|
|
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);
|
|
|
|
|
const cids = await categories.getAllCidsFromSet('categories:cid');
|
|
|
|
|
const isMods = await User.isModerator(uid, cids);
|
|
|
|
|
return cids.filter((cid, index) => cid && isMods[index]);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
User.addInterstitials = function (callback) {
|
|
|
|
|