'use strict'; var async = require('async'); var validator = require('validator'); var nconf = require('nconf'); var winston = require('winston'); var db = require('../database'); var meta = require('../meta'); var plugins = require('../plugins'); var utils = require('../utils'); module.exports = function (User) { var iconBackgrounds = [ '#f44336', '#e91e63', '#9c27b0', '#673ab7', '#3f51b5', '#2196f3', '#009688', '#1b5e20', '#33691e', '#827717', '#e65100', '#ff5722', '#795548', '#607d8b', ]; var fieldWhitelist = [ 'uid', 'username', 'userslug', 'email', 'email:confirmed', 'joindate', 'lastonline', 'picture', 'fullname', 'location', 'birthday', 'website', 'aboutme', 'signature', 'uploadedpicture', 'profileviews', 'reputation', 'postcount', 'topiccount', 'lastposttime', 'banned', 'banned:expire', 'status', 'flags', 'followerCount', 'followingCount', 'cover:url', 'cover:position', 'groupTitle', ]; User.getUserField = function (uid, field, callback) { User.getUserFields(uid, [field], function (err, user) { callback(err, user ? user[field] : null); }); }; User.getUserFields = function (uid, fields, callback) { User.getUsersFields([uid], fields, function (err, users) { callback(err, users ? users[0] : null); }); }; User.getUsersFields = function (uids, fields, callback) { if (!Array.isArray(uids) || !uids.length) { return callback(null, []); } uids = uids.map(function (uid) { return isNaN(uid) ? 0 : uid; }); var fieldsToRemove = []; function addField(field) { if (fields.indexOf(field) === -1) { fields.push(field); fieldsToRemove.push(field); } } if (fields.length && fields.indexOf('uid') === -1) { fields.push('uid'); } if (fields.indexOf('picture') !== -1) { addField('uploadedpicture'); } if (fields.indexOf('status') !== -1) { addField('lastonline'); } var uniqueUids = uids.filter(function (uid, index) { return index === uids.indexOf(uid); }); async.waterfall([ function (next) { plugins.fireHook('filter:user.whitelistFields', { uids: uids, whitelist: fieldWhitelist.slice() }, next); }, function (results, next) { if (fields.length) { fields = fields.filter(function (field) { var isFieldWhitelisted = field && results.whitelist.includes(field); if (!isFieldWhitelisted) { winston.verbose('[user/getUsersFields] ' + field + ' removed because it is not whitelisted, see `filter:user.whietlistFields`'); } return isFieldWhitelisted; }); } else { fields = results.whitelist; } db.getObjectsFields(uidsToUserKeys(uniqueUids), fields, next); }, function (users, next) { users = uidsToUsers(uids, uniqueUids, users); modifyUserData(users, fieldsToRemove, next); }, ], callback); }; User.getMultipleUserFields = function (uids, fields, callback) { winston.warn('[deprecated] User.getMultipleUserFields is deprecated please use User.getUsersFields'); User.getUsersFields(uids, fields, callback); }; User.getUserData = function (uid, callback) { User.getUsersData([uid], function (err, users) { callback(err, users ? users[0] : null); }); }; User.getUsersData = function (uids, callback) { User.getUsersFields(uids, [], callback); }; function uidsToUsers(uids, uniqueUids, usersData) { var ref = uniqueUids.reduce(function (memo, cur, idx) { memo[cur] = idx; return memo; }, {}); var users = uids.map(function (uid) { return usersData[ref[uid]]; }); return users; } function uidsToUserKeys(uids) { return uids.map(function (uid) { return 'user:' + uid; }); } function modifyUserData(users, fieldsToRemove, callback) { users.forEach(function (user) { if (!user) { return; } if (user.hasOwnProperty('groupTitle')) { parseGroupTitle(user); } if (user.hasOwnProperty('username')) { user.username = validator.escape(user.username ? user.username.toString() : ''); } if (!parseInt(user.uid, 10)) { user.uid = 0; user.username = '[[global:guest]]'; user.userslug = ''; user.picture = User.getDefaultAvatar(); user['icon:text'] = '?'; user['icon:bgColor'] = '#aaa'; } if (user.picture && user.picture === user.uploadedpicture) { user.uploadedpicture = user.picture.startsWith('http') ? user.picture : nconf.get('relative_path') + user.picture; user.picture = user.uploadedpicture; } else if (user.uploadedpicture) { user.uploadedpicture = user.uploadedpicture.startsWith('http') ? user.uploadedpicture : nconf.get('relative_path') + user.uploadedpicture; } if (meta.config.defaultAvatar && !user.picture) { user.picture = User.getDefaultAvatar(); } if (user.hasOwnProperty('status') && parseInt(user.lastonline, 10)) { user.status = User.getStatus(user); } for (var i = 0; i < fieldsToRemove.length; i += 1) { user[fieldsToRemove[i]] = undefined; } // User Icons if (user.hasOwnProperty('picture') && user.username && parseInt(user.uid, 10) && !meta.config.defaultAvatar) { user['icon:text'] = (user.username[0] || '').toUpperCase(); user['icon:bgColor'] = iconBackgrounds[Array.prototype.reduce.call(user.username, function (cur, next) { return cur + next.charCodeAt(); }, 0) % iconBackgrounds.length]; } if (user.hasOwnProperty('joindate')) { user.joindateISO = utils.toISOString(user.joindate); } if (user.hasOwnProperty('lastonline')) { user.lastonlineISO = utils.toISOString(user.lastonline) || user.joindateISO; } if (user.hasOwnProperty('banned:expire')) { user.banned_until = parseInt(user['banned:expire'], 10) || 0; user.banned_until_readable = user.banned_until ? new Date(user.banned_until).toString() : 'Not Banned'; } }); plugins.fireHook('filter:users.get', users, callback); } function parseGroupTitle(user) { try { user.groupTitleArray = JSON.parse(user.groupTitle); } catch (err) { user.groupTitleArray = [user.groupTitle]; } if (!Array.isArray(user.groupTitleArray)) { user.groupTitleArray = [user.groupTitleArray]; } if (parseInt(meta.config.allowMultipleBadges, 10) !== 1) { user.groupTitleArray = [user.groupTitleArray[0]]; } } User.getDefaultAvatar = function () { if (!meta.config.defaultAvatar) { return ''; } return meta.config.defaultAvatar.startsWith('http') ? meta.config.defaultAvatar : nconf.get('relative_path') + meta.config.defaultAvatar; }; User.setUserField = function (uid, field, value, callback) { callback = callback || function () {}; async.waterfall([ function (next) { db.setObjectField('user:' + uid, field, value, next); }, function (next) { plugins.fireHook('action:user.set', { uid: uid, field: field, value: value, type: 'set' }); next(); }, ], callback); }; User.setUserFields = function (uid, data, callback) { callback = callback || function () {}; async.waterfall([ function (next) { db.setObject('user:' + uid, data, next); }, function (next) { for (var field in data) { if (data.hasOwnProperty(field)) { plugins.fireHook('action:user.set', { uid: uid, field: field, value: data[field], type: 'set' }); } } next(); }, ], callback); }; User.incrementUserFieldBy = function (uid, field, value, callback) { incrDecrUserFieldBy(uid, field, value, 'increment', callback); }; User.decrementUserFieldBy = function (uid, field, value, callback) { incrDecrUserFieldBy(uid, field, -value, 'decrement', callback); }; function incrDecrUserFieldBy(uid, field, value, type, callback) { callback = callback || function () {}; async.waterfall([ function (next) { db.incrObjectFieldBy('user:' + uid, field, value, next); }, function (value, next) { plugins.fireHook('action:user.set', { uid: uid, field: field, value: value, type: type }); next(null, value); }, ], callback); } };