diff --git a/public/language/en-GB/admin/manage/users.json b/public/language/en-GB/admin/manage/users.json index 96b024fec5..1fef96f56d 100644 --- a/public/language/en-GB/admin/manage/users.json +++ b/public/language/en-GB/admin/manage/users.json @@ -18,6 +18,7 @@ "purge": "Delete User(s) and Content", "download-csv": "Download CSV", "manage-groups": "Manage Groups", + "set-reputation": "Set Reputation", "add-group": "Add Group", "create": "Create User", "invite": "Invite by Email", diff --git a/public/src/admin/manage/users.js b/public/src/admin/manage/users.js index 1fb8e2701d..613389d9f9 100644 --- a/public/src/admin/manage/users.js +++ b/public/src/admin/manage/users.js @@ -1,8 +1,8 @@ 'use strict'; define('admin/manage/users', [ - 'translator', 'benchpress', 'autocomplete', 'api', 'slugify', 'bootbox', 'alerts', 'accounts/invite', -], function (translator, Benchpress, autocomplete, api, slugify, bootbox, alerts, AccountInvite) { + 'translator', 'benchpress', 'autocomplete', 'api', 'slugify', 'bootbox', 'alerts', 'accounts/invite', 'helpers', +], function (translator, Benchpress, autocomplete, api, slugify, bootbox, alerts, AccountInvite, helpers) { const Users = {}; Users.init = function () { @@ -141,6 +141,53 @@ define('admin/manage/users', [ }); }); + $('.set-reputation').on('click', function () { + const uids = getSelectedUids(); + if (!uids.length) { + alerts.error('[[error:no-users-selected]]'); + return false; + } + let currentValue = ''; + if (uids.length === 1) { + const user = ajaxify.data.users.find(u => u && u.uid === parseInt(uids[0], 10)); + if (user) { + currentValue = String(user.reputation); + } + } + const modal = bootbox.dialog({ + message: ``, + title: '[[admin/manage/users:set-reputation]]', + onEscape: true, + buttons: { + submit: { + label: '[[global:save]]', + callback: function () { + const newReputation = modal.find('#new-reputation').val(); + if (!utils.isNumber(newReputation)) { + alerts.error('[[error:invalid-data]]'); + return false; + } + socket.emit('admin.user.setReputation', { + value: newReputation, + uids: uids, + }).then(() => { + uids.forEach((uid) => { + $(`[component="user/reputation"][data-uid="${uid}"]`).text(helpers.formattedNumber(newReputation)); + const user = ajaxify.data.users.find(u => u && u.uid === parseInt(uid, 10)); + if (user) { + user.reputation = newReputation; + } + }); + }).catch(alerts.error); + }, + }, + }, + }); + modal.on('shown.bs.modal', () => { + modal.find('#new-reputation').selectRange(0, modal.find('#new-reputation').val().length); + }); + }); + $('.ban-user').on('click', function () { const uids = getSelectedUids(); if (!uids.length) { diff --git a/src/socket.io/admin/user.js b/src/socket.io/admin/user.js index afe47e4d82..6c5e0c5b28 100644 --- a/src/socket.io/admin/user.js +++ b/src/socket.io/admin/user.js @@ -8,6 +8,7 @@ const groups = require('../../groups'); const user = require('../../user'); const events = require('../../events'); const translator = require('../../translator'); +const utils = require('../../utils'); const sockets = require('..'); const User = module.exports; @@ -146,6 +147,21 @@ User.loadGroups = async function (socket, uids) { return { users: userData }; }; +User.setReputation = async function (socket, data) { + if (!data || !Array.isArray(data.uids) || !utils.isNumber(data.value)) { + throw new Error('[[error:invalid-data]]'); + } + + await Promise.all([ + db.setObjectBulk( + data.uids.map(uid => ([`user:${uid}`, { reputation: parseInt(data.value, 10) }])) + ), + db.sortedSetAddBulk( + data.uids.map(uid => (['users:reputation', data.value, uid])) + ), + ]); +}; + User.exportUsersCSV = async function (socket) { await events.log({ type: 'exportUsersCSV', diff --git a/src/views/admin/manage/users.tpl b/src/views/admin/manage/users.tpl index 7d417e9b5b..23127a9876 100644 --- a/src/views/admin/manage/users.tpl +++ b/src/views/admin/manage/users.tpl @@ -45,6 +45,7 @@