From 3dcf53877328ec12dc46d9d249ffaba5985dc538 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Thu, 9 Jul 2020 23:57:54 -0400 Subject: [PATCH] feat: #8023, allow wildcard search for uid/email --- public/language/en-GB/admin/manage/users.json | 2 +- public/openapi/read.yaml | 19 ++++++ public/src/admin/manage/users.js | 63 +++++++++++-------- src/controllers/admin/users.js | 54 ++++++++++++++-- src/socket.io/admin/user.js | 1 + src/user/search.js | 6 +- src/views/admin/manage/users.tpl | 13 ++-- 7 files changed, 118 insertions(+), 40 deletions(-) diff --git a/public/language/en-GB/admin/manage/users.json b/public/language/en-GB/admin/manage/users.json index 93add0d7a4..2712dece9d 100644 --- a/public/language/en-GB/admin/manage/users.json +++ b/public/language/en-GB/admin/manage/users.json @@ -108,5 +108,5 @@ "alerts.prompt-email": "Emails: ", "alerts.email-sent-to": "An invitation email has been sent to %1", - "alerts.x-users-found": "%1 user(s) found! Search took %2 ms." + "alerts.x-users-found": "%1 user(s) found, (%2 seconds)" } \ No newline at end of file diff --git a/public/openapi/read.yaml b/public/openapi/read.yaml index 29ac388079..4603316f9f 100644 --- a/public/openapi/read.yaml +++ b/public/openapi/read.yaml @@ -954,11 +954,30 @@ paths: properties: search_display: type: string + matchCount: + type: number + query: + type: string + uidQuery: + type: string + usernameQuery: + type: string + emailQuery: + type: string + ipQuery: + type: string + pageCount: + type: number + resultsPerPage: + type: number + timing: + type: number users: type: array items: $ref: components/schemas/UserObject.yaml#/UserObjectACP - $ref: components/schemas/CommonProps.yaml#/CommonProps + - $ref: components/schemas/Pagination.yaml#/Pagination /api/admin/manage/users/latest: get: tags: diff --git a/public/src/admin/manage/users.js b/public/src/admin/manage/users.js index 033613ee88..cef83479be 100644 --- a/public/src/admin/manage/users.js +++ b/public/src/admin/manage/users.js @@ -376,33 +376,10 @@ define('admin/manage/users', ['translator', 'benchpress', 'autocomplete'], funct timeoutId = setTimeout(function () { $('.fa-spinner').removeClass('hidden'); - - socket.emit('admin.user.search', { searchBy: type, query: $this.val() }, function (err, data) { - if (err) { - return app.alertError(err.message); - } - - Benchpress.parse('admin/manage/users', 'users', data, function (html) { - translator.translate(html, function (html) { - html = $(html); - $('.users-table tbody tr').remove(); - $('.users-table tbody').append(html); - html.find('.timeago').timeago(); - $('.fa-spinner').addClass('hidden'); - - if (data && data.users.length === 0) { - $('#user-notfound-notify').translateHtml('[[admin/manage/users:search.not-found]]') - .removeClass('hide') - .addClass('label-danger') - .removeClass('label-success'); - } else { - $('#user-notfound-notify').translateHtml(translator.compile('admin/manage/users:alerts.x-users-found', data.users.length, data.timing)) - .removeClass('hide') - .addClass('label-success') - .removeClass('label-danger'); - } - }); - }); + loadSearchPage({ + searchBy: type, + query: $this.val(), + page: 1, }); }, 250); }); @@ -412,6 +389,38 @@ define('admin/manage/users', ['translator', 'benchpress', 'autocomplete'], funct handleInvite(); }; + function loadSearchPage(query) { + var qs = decodeURIComponent($.param(query)); + $.get(config.relative_path + '/api/admin/manage/users/search?' + qs, renderSearchResults).fail(function (xhrErr) { + if (xhrErr && xhrErr.responseJSON && xhrErr.responseJSON.error) { + app.alertError(xhrErr.responseJSON.error); + } + }); + } + + function renderSearchResults(data) { + Benchpress.parse('partials/paginator', { pagination: data.pagination }, function (html) { + $('.pagination-container').replaceWith(html); + }); + + app.parseAndTranslate('admin/manage/users', 'users', data, function (html) { + $('.users-table tbody tr').remove(); + $('.users-table tbody').append(html); + html.find('.timeago').timeago(); + $('.fa-spinner').addClass('hidden'); + + if (data && data.users.length === 0) { + $('#user-notfound-notify').translateHtml('[[admin/manage/users:search.not-found]]') + .removeClass('hidden'); + $('#user-found-notify').addClass('hidden'); + } else { + $('#user-found-notify').translateHtml(translator.compile('admin/manage/users:alerts.x-users-found', data.matchCount, data.timing)) + .removeClass('hidden'); + $('#user-notfound-notify').addClass('hidden'); + } + }); + } + function handleInvite() { $('[component="user/invite"]').on('click', function () { bootbox.prompt('[[admin/manage/users:alerts.prompt-email]]', function (email) { diff --git a/src/controllers/admin/users.js b/src/controllers/admin/users.js index c6c6673fc0..d58bc9077e 100644 --- a/src/controllers/admin/users.js +++ b/src/controllers/admin/users.js @@ -1,6 +1,7 @@ 'use strict'; const nconf = require('nconf'); +const validator = require('validator'); const user = require('../../user'); const meta = require('../../meta'); @@ -15,11 +16,56 @@ const usersController = module.exports; const userFields = ['uid', 'username', 'userslug', 'email', 'postcount', 'joindate', 'banned', 'reputation', 'picture', 'flags', 'lastonline', 'email:confirmed']; -usersController.search = function (req, res) { - res.render('admin/manage/users', { - search_display: '', - users: [], +usersController.search = async function (req, res) { + const page = parseInt(req.query.page, 10) || 1; + let resultsPerPage = parseInt(req.query.resultsPerPage, 10) || 50; + if (![50, 100, 250, 500].includes(resultsPerPage)) { + resultsPerPage = 50; + } + const searchData = await user.search({ + uid: req.uid, + query: req.query.query, + searchBy: req.query.searchBy, + page: page, + resultsPerPage: resultsPerPage, + findUids: async function (query, searchBy, hardCap) { + if (!query || query.length < 2) { + return []; + } + hardCap = hardCap || resultsPerPage * 10; + if (!query.endsWith('*')) { + query += '*'; + } + + const data = await db.getSortedSetScan({ + key: searchBy + ':sorted', + match: query, + limit: hardCap, + }); + return data.map(data => data.split(':')[1]); + }, + }); + + const uids = searchData.users.map(user => user && user.uid); + const userInfo = await user.getUsersFields(uids, ['email', 'flags', 'lastonline', 'joindate']); + + searchData.users.forEach(function (user, index) { + if (user && userInfo[index]) { + user.email = userInfo[index].email; + user.flags = userInfo[index].flags || 0; + user.lastonlineISO = userInfo[index].lastonlineISO; + user.joindateISO = userInfo[index].joindateISO; + } }); + searchData.query = validator.escape(String(req.query.query || '')); + searchData.uidQuery = req.query.searchBy === 'uid' ? searchData.query : ''; + searchData.usernameQuery = req.query.searchBy === 'username' ? searchData.query : ''; + searchData.emailQuery = req.query.searchBy === 'email' ? searchData.query : ''; + searchData.ipQuery = req.query.searchBy === 'uid' ? searchData.query : ''; + searchData.resultsPerPage = resultsPerPage; + searchData.pagination = pagination.create(page, searchData.pageCount, req.query); + searchData.search_display = ''; + res.render('admin/manage/users', searchData); }; usersController.sortByJoinDate = async function (req, res) { diff --git a/src/socket.io/admin/user.js b/src/socket.io/admin/user.js index fde65426cd..f9686d2a21 100644 --- a/src/socket.io/admin/user.js +++ b/src/socket.io/admin/user.js @@ -178,6 +178,7 @@ async function deleteUsers(socket, uids, method) { } User.search = async function (socket, data) { + // TODO: deprecate const searchData = await user.search({ query: data.query, searchBy: data.searchBy, diff --git a/src/user/search.js b/src/user/search.js index 6c7d4724eb..969ed68126 100644 --- a/src/user/search.js +++ b/src/user/search.js @@ -37,9 +37,9 @@ module.exports = function (User) { }; if (paginate) { - var resultsPerPage = meta.config.userSearchResultsPerPage; - var start = Math.max(0, page - 1) * resultsPerPage; - var stop = start + resultsPerPage; + const resultsPerPage = data.resultsPerPage || meta.config.userSearchResultsPerPage; + const start = Math.max(0, page - 1) * resultsPerPage; + const stop = start + resultsPerPage; searchResult.pageCount = Math.ceil(uids.length / resultsPerPage); uids = uids.slice(start, stop); } diff --git a/src/views/admin/manage/users.tpl b/src/views/admin/manage/users.tpl index 41a4e96beb..f31382e671 100644 --- a/src/views/admin/manage/users.tpl +++ b/src/views/admin/manage/users.tpl @@ -57,23 +57,26 @@
-
+
-
+
-
+
-
+
- [[admin/manage/users:search.not-found]]
+ +
[[admin/manage/users:alerts.x-users-found, {matchCount}, {timing}]]
+ +
[[admin/manage/users:search.not-found]]