diff --git a/public/openapi/components/schemas/UserObject.yaml b/public/openapi/components/schemas/UserObject.yaml index 9b217cee8b..b258ea123d 100644 --- a/public/openapi/components/schemas/UserObject.yaml +++ b/public/openapi/components/schemas/UserObject.yaml @@ -331,6 +331,12 @@ UserObjectFull: example: - administrators - Staff + iconBackgrounds: + type: array + items: + type: string + description: A valid CSS colour code + example: '#fff' muted: type: boolean description: Whether or not the user has been muted. diff --git a/public/openapi/read/admin/config.yaml b/public/openapi/read/admin/config.yaml index 61e035d2fe..147d917eb9 100644 --- a/public/openapi/read/admin/config.yaml +++ b/public/openapi/read/admin/config.yaml @@ -152,12 +152,6 @@ get: type: boolean enableQuickReply: type: boolean - iconBackgrounds: - type: array - items: - type: string - description: A valid CSS colour code - example: '#fff' emailPrompt: type: number useragent: diff --git a/public/openapi/read/config.yaml b/public/openapi/read/config.yaml index ceb99c0d58..2fd51603a5 100644 --- a/public/openapi/read/config.yaml +++ b/public/openapi/read/config.yaml @@ -152,12 +152,6 @@ get: type: boolean enableQuickReply: type: boolean - iconBackgrounds: - type: array - items: - type: string - description: A valid CSS colour code - example: '#fff' emailPrompt: type: number useragent: diff --git a/public/src/modules/accounts/picture.js b/public/src/modules/accounts/picture.js index d8ab277f24..ddd3005453 100644 --- a/public/src/modules/accounts/picture.js +++ b/public/src/modules/accounts/picture.js @@ -27,7 +27,7 @@ define('accounts/picture', [ icon: { text: ajaxify.data['icon:text'], bgColor: ajaxify.data['icon:bgColor'] }, defaultAvatar: ajaxify.data.defaultAvatar, allowProfileImageUploads: ajaxify.data.allowProfileImageUploads, - iconBackgrounds: config.iconBackgrounds, + iconBackgrounds: ajaxify.data.iconBackgrounds, user: { uid: ajaxify.data.uid, username: ajaxify.data.username, diff --git a/src/api/users.js b/src/api/users.js index 931e75b36b..c4f4add772 100644 --- a/src/api/users.js +++ b/src/api/users.js @@ -656,7 +656,7 @@ usersAPI.changePicture = async (caller, data) => { picture = returnData && returnData.picture; } - const validBackgrounds = await user.getIconBackgrounds(caller.uid); + const validBackgrounds = await user.getIconBackgrounds(); if (!validBackgrounds.includes(data.bgColor)) { data.bgColor = validBackgrounds[0]; } diff --git a/src/controllers/accounts/helpers.js b/src/controllers/accounts/helpers.js index 38acdf9364..d6d4ee54b8 100644 --- a/src/controllers/accounts/helpers.js +++ b/src/controllers/accounts/helpers.js @@ -32,11 +32,7 @@ helpers.getUserDataByUserSlug = async function (userslug, callerUID, query = {}) await parseAboutMe(results.userData); let { userData } = results; - const { userSettings } = results; - const { isAdmin } = results; - const { isGlobalModerator } = results; - const { isModerator } = results; - const { canViewInfo } = results; + const { userSettings, isAdmin, isGlobalModerator, isModerator, canViewInfo } = results; const isSelf = parseInt(callerUID, 10) === parseInt(userData.uid, 10); if (meta.config['reputation:disabled']) { @@ -84,6 +80,7 @@ helpers.getUserDataByUserSlug = async function (userslug, callerUID, query = {}) userData.isFollowing = results.isFollowing; userData.canChat = results.canChat; userData.hasPrivateChat = results.hasPrivateChat; + userData.iconBackgrounds = results.iconBackgrounds; userData.showHidden = results.canEdit; // remove in v1.19.0 userData.allowProfilePicture = !userData.isSelf || !!meta.config['reputation:disabled'] || userData.reputation >= meta.config['min:rep:profile-picture']; userData.allowCoverPicture = !userData.isSelf || !!meta.config['reputation:disabled'] || userData.reputation >= meta.config['min:rep:cover-picture']; @@ -160,6 +157,7 @@ async function getAllData(uid, callerUID) { canViewInfo: privileges.global.can('view:users:info', callerUID), canChat: canChat(callerUID, uid), hasPrivateChat: messaging.hasPrivateChat(callerUID, uid), + iconBackgrounds: user.getIconBackgrounds(), }); } diff --git a/src/controllers/api.js b/src/controllers/api.js index 22574a9ce6..56b629f6db 100644 --- a/src/controllers/api.js +++ b/src/controllers/api.js @@ -88,7 +88,6 @@ apiController.loadConfig = async function (req) { thumbs: { size: meta.config.topicThumbSize, }, - iconBackgrounds: await user.getIconBackgrounds(req.uid), emailPrompt: meta.config.emailPrompt, useragent: req.useragent, fontawesome: { diff --git a/src/user/data.js b/src/user/data.js index e7be8a8f5a..99459f475b 100644 --- a/src/user/data.js +++ b/src/user/data.js @@ -44,6 +44,8 @@ module.exports = function (User) { 'email:confirmed': 0, }; + let iconBackgrounds; + User.getUsersFields = async function (uids, fields) { if (!Array.isArray(uids) || !uids.length) { return []; @@ -187,8 +189,11 @@ module.exports = function (User) { ['showfullname'] )); } + if (!iconBackgrounds) { + iconBackgrounds = await User.getIconBackgrounds(); + } - await Promise.all(users.map(async (user) => { + users.forEach((user) => { if (!user) { return; } @@ -204,7 +209,7 @@ module.exports = function (User) { user.email = validator.escape(user.email ? user.email.toString() : ''); } - if (!parseInt(user.uid, 10)) { + if (!user.uid) { for (const [key, value] of Object.entries(User.guestData)) { user[key] = value; } @@ -234,15 +239,12 @@ module.exports = function (User) { } // User Icons - if (requestedFields.includes('picture') && user.username && parseInt(user.uid, 10) && !meta.config.defaultAvatar) { - const iconBackgrounds = await User.getIconBackgrounds(user.uid); - let bgColor = await User.getUserField(user.uid, 'icon:bgColor'); - if (!iconBackgrounds.includes(bgColor)) { - bgColor = Array.prototype.reduce.call(user.username, (cur, next) => cur + next.charCodeAt(), 0); - bgColor = iconBackgrounds[bgColor % iconBackgrounds.length]; + if (requestedFields.includes('picture') && user.username && user.uid && !meta.config.defaultAvatar) { + if (!iconBackgrounds.includes(user['icon:bgColor'])) { + const nameAsIndex = Array.from(user.username).reduce((cur, next) => cur + next.charCodeAt(), 0); + user['icon:bgColor'] = iconBackgrounds[nameAsIndex % iconBackgrounds.length]; } user['icon:text'] = (user.username[0] || '').toUpperCase(); - user['icon:bgColor'] = bgColor; } if (user.hasOwnProperty('joindate')) { @@ -253,6 +255,14 @@ module.exports = function (User) { user.lastonlineISO = utils.toISOString(user.lastonline) || user.joindateISO; } + if (user.hasOwnProperty('mutedUntil')) { + user.muted = user.mutedUntil > Date.now(); + } + }); + + // TODO get rid of single calls + // dont do anything if user is not banned? + await Promise.all(users.map(async (user) => { if (user.hasOwnProperty('banned') || user.hasOwnProperty('banned:expire')) { const result = await User.bans.calcExpiredFromUserData(user); user.banned = result.banned; @@ -264,10 +274,6 @@ module.exports = function (User) { user.banned = false; } } - - if (user.hasOwnProperty('mutedUntil')) { - user.muted = user.mutedUntil > Date.now(); - } })); return await plugins.hooks.fire('filter:users.get', users); @@ -313,14 +319,20 @@ module.exports = function (User) { } } - User.getIconBackgrounds = async (uid = 0) => { - let iconBackgrounds = [ + + User.getIconBackgrounds = async () => { + if (iconBackgrounds) { + return iconBackgrounds; + } + + const _iconBackgrounds = [ '#f44336', '#e91e63', '#9c27b0', '#673ab7', '#3f51b5', '#2196f3', '#009688', '#1b5e20', '#33691e', '#827717', '#e65100', '#ff5722', '#795548', '#607d8b', ]; - ({ iconBackgrounds } = await plugins.hooks.fire('filter:user.iconBackgrounds', { uid, iconBackgrounds })); + const data = await plugins.hooks.fire('filter:user.iconBackgrounds', { iconBackgrounds: _iconBackgrounds }); + iconBackgrounds = data.iconBackgrounds; return iconBackgrounds; }; diff --git a/test/user.js b/test/user.js index 6ac7de44a9..25c0ddc6f0 100644 --- a/test/user.js +++ b/test/user.js @@ -607,7 +607,7 @@ describe('User', () => { it('should return an icon text and valid background if username and picture is explicitly requested', async () => { const payload = await User.getUserFields(testUid, ['username', 'picture']); - const validBackgrounds = await User.getIconBackgrounds(testUid); + const validBackgrounds = await User.getIconBackgrounds(); assert.strictEqual(payload['icon:text'], userData.username.slice(0, 1).toUpperCase()); assert(payload['icon:bgColor']); assert(validBackgrounds.includes(payload['icon:bgColor'])); @@ -616,7 +616,7 @@ describe('User', () => { it('should return a valid background, even if an invalid background colour is set', async () => { await User.setUserField(testUid, 'icon:bgColor', 'teal'); const payload = await User.getUserFields(testUid, ['username', 'picture']); - const validBackgrounds = await User.getIconBackgrounds(testUid); + const validBackgrounds = await User.getIconBackgrounds(); assert(payload['icon:bgColor']); assert(validBackgrounds.includes(payload['icon:bgColor']));