From d8b1291088d345b55522f5f85b13a03339efa38d Mon Sep 17 00:00:00 2001 From: Misty Release Bot Date: Mon, 22 Aug 2022 16:13:58 +0000 Subject: [PATCH 1/5] chore: incrementing version number - v2.4.5 --- install/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/package.json b/install/package.json index 0f1d15ae17..58760a2909 100644 --- a/install/package.json +++ b/install/package.json @@ -2,7 +2,7 @@ "name": "nodebb", "license": "GPL-3.0", "description": "NodeBB Forum", - "version": "2.4.4", + "version": "2.4.5", "homepage": "http://www.nodebb.org", "repository": { "type": "git", From d505cc47ecc7fb66bac73d278768ed2d9f3ace2f Mon Sep 17 00:00:00 2001 From: Misty Release Bot Date: Mon, 22 Aug 2022 16:14:00 +0000 Subject: [PATCH 2/5] chore: update changelog for v2.4.5 --- CHANGELOG.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 37ac7ed189..425a480f12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,21 @@ +#### v2.4.5 (2022-08-22) + +##### Chores + +* incrementing version number - v2.4.4 (d5525c87) +* update changelog for v2.4.4 (77e492b8) +* incrementing version number - v2.4.3 (9c647c6c) +* incrementing version number - v2.4.2 (3aa7b855) +* incrementing version number - v2.4.1 (60cbd148) +* incrementing version number - v2.4.0 (4834cde3) +* incrementing version number - v2.3.1 (d2425942) +* incrementing version number - v2.3.0 (046ea120) + +##### Bug Fixes + +* wrap passport.authenticate to pass in keepSessionInfo if not already set (9b96c33d) +* parseInt caller.uid closes #10849 (bc37a5c5) + #### v2.4.4 (2022-08-18) ##### Chores From b424ba46379b1f0ee62dfed613aa32fa6ee5ef38 Mon Sep 17 00:00:00 2001 From: gasoved Date: Sat, 20 Aug 2022 08:38:14 +0300 Subject: [PATCH 3/5] test: User.hidePrivateData --- test/user.js | 192 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 173 insertions(+), 19 deletions(-) diff --git a/test/user.js b/test/user.js index 875e5fab1e..9252b25044 100644 --- a/test/user.js +++ b/test/user.js @@ -2548,21 +2548,45 @@ describe('User', () => { describe('hideEmail/hideFullname', () => { const COMMON_PW = '123456'; - let uid; - let jar; - let regularUserUid; + const hidingUser = { + username: 'hiddenemail', + email: 'should@be.hidden', + fullname: 'baris soner usakli', + password: COMMON_PW, + }; + const regularUser = { + username: 'regularUser', + email: 'regular@example.com', + fullname: 'regular user', + password: COMMON_PW, + }; + let hidingUserJar; + let adminUid; + let adminJar; + let globalModJar; + let regularUserJar; before(async () => { - uid = await User.create({ - username: 'hiddenemail', - email: 'should@be.hidden', - fullname: 'baris soner usakli', - }); - regularUserUid = await User.create({ - username: 'regularUser', + adminUid = await User.create({ + username: 'adminhideemail', password: COMMON_PW, }); - ({ jar } = await helpers.loginUser('regularUser', COMMON_PW)); + await groups.join('administrators', adminUid); + ({ jar: adminJar } = await helpers.loginUser('adminhideemail', COMMON_PW)); + + // Edge case: In a grepped test, this user should not be created as the first user to have its email not confirmed + hidingUser.uid = await User.create(hidingUser); + ({ jar: hidingUserJar } = await helpers.loginUser(hidingUser.username, COMMON_PW)); + + const globalModUid = await User.create({ + username: 'globalmodhideemail', + password: COMMON_PW, + }); + await groups.join('Global Moderators', globalModUid); + ({ jar: globalModJar } = await helpers.loginUser('globalmodhideemail', COMMON_PW)); + + regularUser.uid = await User.create(regularUser); + ({ jar: regularUserJar } = await helpers.loginUser(regularUser.username, COMMON_PW)); }); after((done) => { @@ -2571,22 +2595,152 @@ describe('User', () => { done(); }); - it('should hide email and fullname', async () => { + async function assertPrivacy({ expectVisible, jar, v3Api, emailOnly }) { + const path = v3Api ? `v3/users/${hidingUser.uid}` : `user/${hidingUser.username}`; + const response = await requestAsync(`${nconf.get('url')}/api/${path}`, { json: true, jar }); + const { response: userData } = v3Api ? response : { response }; + + assert.strictEqual(userData.email, expectVisible ? hidingUser.email : ''); + if (!emailOnly) { + assert.strictEqual(userData.fullname, expectVisible ? hidingUser.fullname : ''); + } + } + + it('should hide unconfirmed emails on profile pages', async () => { + await assertPrivacy({ v3Api: false, emailOnly: true }); + await assertPrivacy({ v3Api: false, jar: hidingUserJar, emailOnly: true }); + await assertPrivacy({ v3Api: false, jar: adminJar, emailOnly: true }); + await assertPrivacy({ v3Api: false, jar: globalModJar, emailOnly: true }); + await assertPrivacy({ v3Api: false, jar: regularUserJar, emailOnly: true }); + + // Let's confirm for afterwards + await User.email.confirmByUid(hidingUser.uid); + }); + + it('should hide from guests by default', async () => { + await assertPrivacy({ v3Api: false }); + }); + + it('should hide from unprivileged users by default', async () => { + await assertPrivacy({ v3Api: false, jar: regularUserJar }); + await assertPrivacy({ v3Api: true, jar: regularUserJar }); + }); + + it('should be visible to self by default', async () => { + await assertPrivacy({ v3Api: false, jar: hidingUserJar, expectVisible: true }); + await assertPrivacy({ v3Api: true, jar: hidingUserJar, expectVisible: true }); + }); + + it('should be visible to privileged users by default', async () => { + await assertPrivacy({ v3Api: false, jar: adminJar, expectVisible: true }); + await assertPrivacy({ v3Api: true, jar: adminJar, expectVisible: true }); + await assertPrivacy({ v3Api: false, jar: globalModJar, expectVisible: true }); + await assertPrivacy({ v3Api: true, jar: globalModJar, expectVisible: true }); + }); + + it('should hide from guests (system-wide: hide, by-user: hide)', async () => { + meta.config.hideEmail = 1; + meta.config.hideFullname = 1; + // Explicitly set user's privacy settings to hide its email and fullname + const data = { uid: hidingUser.uid, settings: { showemail: 0, showfullname: 0 } }; + await apiUser.updateSettings({ uid: hidingUser.uid }, data); + + await assertPrivacy({ v3Api: false }); + }); + + it('should hide from unprivileged users (system-wide: hide, by-user: hide)', async () => { + await assertPrivacy({ v3Api: false, jar: regularUserJar }); + await assertPrivacy({ v3Api: true, jar: regularUserJar }); + }); + + it('should be visible to self (system-wide: hide, by-user: hide)', async () => { + await assertPrivacy({ v3Api: false, jar: hidingUserJar, expectVisible: true }); + await assertPrivacy({ v3Api: true, jar: hidingUserJar, expectVisible: true }); + }); + + it('should be visible to privileged users (system-wide: hide, by-user: hide)', async () => { + await assertPrivacy({ v3Api: false, jar: adminJar, expectVisible: true }); + await assertPrivacy({ v3Api: true, jar: adminJar, expectVisible: true }); + await assertPrivacy({ v3Api: false, jar: globalModJar, expectVisible: true }); + await assertPrivacy({ v3Api: true, jar: globalModJar, expectVisible: true }); + }); + + it('should hide from guests (system-wide: show, by-user: hide)', async () => { + meta.config.hideEmail = 0; + meta.config.hideFullname = 0; + + await assertPrivacy({ v3Api: false }); + }); + + it('should hide from unprivileged users (system-wide: show, by-user: hide)', async () => { + await assertPrivacy({ v3Api: false, jar: regularUserJar }); + await assertPrivacy({ v3Api: true, jar: regularUserJar }); + }); + + it('should be visible to self (system-wide: show, by-user: hide)', async () => { + await assertPrivacy({ v3Api: false, jar: hidingUserJar, expectVisible: true }); + await assertPrivacy({ v3Api: true, jar: hidingUserJar, expectVisible: true }); + }); + + it('should be visible to privileged users (system-wide: show, by-user: hide)', async () => { + await assertPrivacy({ v3Api: false, jar: adminJar, expectVisible: true }); + await assertPrivacy({ v3Api: true, jar: adminJar, expectVisible: true }); + await assertPrivacy({ v3Api: false, jar: globalModJar, expectVisible: true }); + await assertPrivacy({ v3Api: true, jar: globalModJar, expectVisible: true }); + }); + + it('should be visible to guests (system-wide: show, by-user: show)', async () => { + meta.config.hideEmail = 0; + meta.config.hideFullname = 0; + + // Set user's individual privacy settings to show its email and fullname + const data = { uid: hidingUser.uid, settings: { showemail: 1, showfullname: 1 } }; + await apiUser.updateSettings({ uid: hidingUser.uid }, data); + + await assertPrivacy({ v3Api: false, expectVisible: true }); + }); + + it('should be visible to unprivileged users (system-wide: show, by-user: show)', async () => { + await assertPrivacy({ v3Api: false, jar: regularUserJar, expectVisible: true }); + await assertPrivacy({ v3Api: true, jar: regularUserJar, expectVisible: true }); + }); + + // System-wide "hide" prioritized over individual users' settings + it('should hide from guests (system-wide: hide, by-user: show)', async () => { meta.config.hideEmail = 1; meta.config.hideFullname = 1; - const userData1 = await requestAsync(`${nconf.get('url')}/api/user/hiddenemail`, { json: true }); - assert.strictEqual(userData1.fullname, ''); - assert.strictEqual(userData1.email, ''); + await assertPrivacy({ v3Api: false }); + }); - const { response } = await requestAsync(`${nconf.get('url')}/api/v3/users/${uid}`, { json: true, jar: jar }); - assert.strictEqual(response.fullname, ''); - assert.strictEqual(response.email, ''); + it('should hide from unprivileged users (system-wide: hide, by-user: show)', async () => { + await assertPrivacy({ v3Api: false, jar: regularUserJar }); + await assertPrivacy({ v3Api: true, jar: regularUserJar }); + }); + + it('should be visible to self (system-wide: hide, by-user: show)', async () => { + await assertPrivacy({ v3Api: false, jar: hidingUserJar, expectVisible: true }); + await assertPrivacy({ v3Api: true, jar: hidingUserJar, expectVisible: true }); + }); + + it('should be visible to privileged users (system-wide: hide, by-user: show)', async () => { + await assertPrivacy({ v3Api: false, jar: adminJar, expectVisible: true }); + await assertPrivacy({ v3Api: true, jar: adminJar, expectVisible: true }); + await assertPrivacy({ v3Api: false, jar: globalModJar, expectVisible: true }); + await assertPrivacy({ v3Api: true, jar: globalModJar, expectVisible: true }); + }); + + it('should handle array of user data (system-wide: hide)', async () => { + const userData = await User.hidePrivateData([hidingUser, regularUser], hidingUser.uid); + assert.strictEqual(userData[0].fullname, hidingUser.fullname); + assert.strictEqual(userData[0].email, hidingUser.email); + assert.strictEqual(userData[1].fullname, ''); + assert.strictEqual(userData[1].email, ''); }); it('should hide fullname in topic list and topic', (done) => { Topics.post({ - uid: uid, + uid: hidingUser.uid, title: 'Topic hidden', content: 'lorem ipsum', cid: testCid, From c1936e87f0482ac8954d9d919435b40ab5859535 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 26 Aug 2022 10:58:55 -0400 Subject: [PATCH 4/5] fix: add dropup handler to thread tools menu, updated how post tools menu adds dropup handler --- public/src/client/topic.js | 6 ++++++ public/src/client/topic/postTools.js | 8 +++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/public/src/client/topic.js b/public/src/client/topic.js index 0c111bcf7f..5facb841a2 100644 --- a/public/src/client/topic.js +++ b/public/src/client/topic.js @@ -177,6 +177,12 @@ define('forum/topic', [ Topic.applyDropup.call(this); } }); + hooks.onPage('action:topic.tools.load', ({ element }) => { + Topic.applyDropup.call(element.get(0).parentNode); + }); + hooks.onPage('action:post.tools.load', ({ element }) => { + Topic.applyDropup.call(element.get(0).parentNode); + }); } function addRepliesHandler() { diff --git a/public/src/client/topic/postTools.js b/public/src/client/topic/postTools.js index 28b1119a3f..e9b5602145 100644 --- a/public/src/client/topic/postTools.js +++ b/public/src/client/topic/postTools.js @@ -32,7 +32,6 @@ define('forum/topic/postTools', [ function renderMenu() { $('[component="topic"]').on('show.bs.dropdown', '.moderator-tools', function () { - const self = this; const $this = $(this); const dropdownMenu = $this.find('.dropdown-menu'); if (dropdownMenu.html()) { @@ -50,15 +49,14 @@ define('forum/topic/postTools', [ const html = await app.parseAndTranslate('partials/topic/post-menu-list', data); const clipboard = require('clipboard'); - // eslint-disable-next-line import/no-unresolved - const topic = require('forum/topic'); dropdownMenu.html(html); dropdownMenu.get(0).classList.toggle('hidden', false); - topic.applyDropup.call(self); new clipboard('[data-clipboard-text]'); - hooks.fire('action:post.tools.load'); + hooks.fire('action:post.tools.load', { + element: dropdownMenu, + }); }); }); } From a088eb19afc35a1b2478ea2df02903dad1f9c464 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 26 Aug 2022 11:03:04 -0400 Subject: [PATCH 5/5] fix: empty thread tools container on open --- public/src/client/topic/threadTools.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/public/src/client/topic/threadTools.js b/public/src/client/topic/threadTools.js index 403e9abfba..0a6daa1030 100644 --- a/public/src/client/topic/threadTools.js +++ b/public/src/client/topic/threadTools.js @@ -180,12 +180,15 @@ define('forum/topic/threadTools', [ return; } + dropdownMenu.toggleClass('hidden', true); socket.emit('topics.loadTopicTools', { tid: ajaxify.data.tid, cid: ajaxify.data.cid }, function (err, data) { if (err) { return alerts.error(err); } app.parseAndTranslate('partials/topic/topic-menu-list', data, function (html) { dropdownMenu.html(html); + dropdownMenu.toggleClass('hidden', false); + hooks.fire('action:topic.tools.load', { element: dropdownMenu, });