From d4c16086a3440ff6c02f1bd290f277c085279756 Mon Sep 17 00:00:00 2001 From: "Misty (Bot)" Date: Wed, 8 Jul 2020 20:13:42 +0000 Subject: [PATCH 01/29] chore: update changelog for v1.14.1 --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 401006c4cd..7c8441877e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +#### 1.14.2.3 (2020-07-08) + +##### New Features + +* add tools to recent/unread (#8477) (658dd03b) + +##### Bug Fixes + +* **deps:** update dependency nodebb-theme-persona to v10.1.60 (#8478) (14eafcb6) + #### 1.14.0 (2020-07-02) ##### Chores From 31203b162fdeca4a29bf924dc791bf81be2670c7 Mon Sep 17 00:00:00 2001 From: "Misty (Bot)" Date: Wed, 8 Jul 2020 20:13:42 +0000 Subject: [PATCH 02/29] chore: incrementing version number - v1.14.1 (cherry picked from commit ddf5c3bf7fad00243fc85c90f6f2a19dbb29e977) Signed-off-by: Misty (Bot) --- install/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/package.json b/install/package.json index c223bbd994..8209666cab 100644 --- a/install/package.json +++ b/install/package.json @@ -2,7 +2,7 @@ "name": "nodebb", "license": "GPL-3.0", "description": "NodeBB Forum", - "version": "1.14.1-beta.3", + "version": "1.14.1", "homepage": "http://www.nodebb.org", "repository": { "type": "git", From 26c744090ee2aed7a09de1a9ba244b173f5fe4d4 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 8 Jul 2020 16:15:19 -0400 Subject: [PATCH 03/29] Revert "chore: update changelog for v1.14.1" This reverts commit d4c16086a3440ff6c02f1bd290f277c085279756. --- CHANGELOG.md | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c8441877e..401006c4cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,3 @@ -#### 1.14.2.3 (2020-07-08) - -##### New Features - -* add tools to recent/unread (#8477) (658dd03b) - -##### Bug Fixes - -* **deps:** update dependency nodebb-theme-persona to v10.1.60 (#8478) (14eafcb6) - #### 1.14.0 (2020-07-02) ##### Chores From 60bf488f546fbb8ad1cb35427f6c336334db9c40 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 8 Jul 2020 16:19:58 -0400 Subject: [PATCH 04/29] fix(docs): bad changelog --- CHANGELOG.md | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 401006c4cd..2ca34e9da0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,70 @@ +#### 1.14.1 (2020-07-08) + +##### Chores + +* incrementing version number - v1.14.1 (31203b16) +* update changelog for v1.14.1 (d4c16086) +* incrementing version number - v1.14.1-beta.3 (e8ecef6b) +* incrementing version number - v1.14.1-beta.2 (b8d9b6b1) +* incrementing version number - v1.14.1-beta.1 (be85123a) +* incrementing version number - v1.14.1-beta.0 (c279875a) +* incrementing version number - v1.14.0 (bb73d6a4) +* update changelog for v1.14.0 (cffae0f1) + +##### New Features + +* add tools to recent/unread (#8477) (658dd03b) +* fire new hooks on chat message editing (4f51838d) +* add back redis tests (bdc4d9e7) +* remove redis test (8461a179) +* use covered query (057b783d) +* add js-enabled.css to list of preloaded css files (da29b947) +* zscan (#8457) (723fe8e8) +* fix blocksCount not being returned on user profile (bd228d5e) + +##### Bug Fixes + +* **deps:** + * update dependency nodebb-theme-persona to v10.1.60 (#8478) (14eafcb6) + * bump nodebb-plugin-composer-default to 6.3.48 (943a344a) + * update dependency nodebb-plugin-dbsearch to v4.1.1 (#8476) (9f06f12c) + * update dependency nodebb-plugin-composer-default to v6.3.47 (#8473) (857900f1) + * update dependency nodebb-plugin-dbsearch to v4.1.0 (#8471) (eb51cfd4) + * update dependency nodebb-theme-persona to v10.1.59 (#8468) (ee38e05d) + * update dependency nodebb-widget-essentials to v4.1.1 (#8466) (519e035d) + * update dependency @nodebb/socket.io-adapter-mongo to v3.0.1 (#8464) (412ca4ae) +* #8474 (c2ca02df) +* show stack properly (7b04d897) +* editing chat messages does not go through content sanity checks (9a6b87d2) +* don't show blocked users under nested replies (d6c619cf) +* tests (87dd6c83) +* handle scan/zscan returning duplicate elements on redis (746222d6) +* #8467, fix url to merged topic in subfolder installs (9eb748b9) +* openapi (5f1865c0) +* openapi (65c0adc7) +* dont allow searching by email/ip if not privileged (ac6b571e) +* missing backgroundImage #8386 (fef04fcf) +* dont allow searching by ip/banned/flagged for regular users (02ac44cc) +* admin privileges client-side regression (f3441fce) +* only add blocksCount for self and admins (59a2ace6) +* tests (fd20e5c6) +* better changelog (f992af05) +* **tests:** + * another shot in the dark (8853cd1a) + * shot in the dark (9458d90b) +* **openapi:** tests (c468942f) + +##### Other Changes + +* update changelog for v1.14.1" (26c74409) +* //github.com/NodeBB/NodeBB (0d9461b1) +* //github.com/NodeBB/NodeBB (ace312e0) +* post.changeOwner (b60e1cbf) + +##### Reverts + +* bad changelog (a761e31f) + #### 1.14.0 (2020-07-02) ##### Chores From ab244ca6cc43adb0a597a234764ff834f9e747a8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 8 Jul 2020 17:24:32 -0400 Subject: [PATCH 05/29] fix(deps): update dependency nodebb-plugin-composer-default to v6.3.49 (#8479) Co-authored-by: Renovate Bot --- install/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/package.json b/install/package.json index 8209666cab..7b8c353d76 100644 --- a/install/package.json +++ b/install/package.json @@ -80,7 +80,7 @@ "@nodebb/mubsub": "^1.6.0", "@nodebb/socket.io-adapter-mongo": "3.0.1", "nconf": "^0.10.0", - "nodebb-plugin-composer-default": "6.3.48", + "nodebb-plugin-composer-default": "6.3.49", "nodebb-plugin-dbsearch": "4.1.1", "nodebb-plugin-emoji": "^3.3.0", "nodebb-plugin-emoji-android": "2.0.0", From 4adbf87c6c5d79470d7a889eb81145a018bee8ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Wed, 8 Jul 2020 21:36:48 -0400 Subject: [PATCH 06/29] feat: warn user if whitelisted tags are less than minTags --- public/language/en-GB/admin/manage/categories.json | 1 + public/src/admin/manage/category.js | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/public/language/en-GB/admin/manage/categories.json b/public/language/en-GB/admin/manage/categories.json index a8949c654b..70b282ca02 100644 --- a/public/language/en-GB/admin/manage/categories.json +++ b/public/language/en-GB/admin/manage/categories.json @@ -76,6 +76,7 @@ "alert.user-search": "Search for a user here...", "alert.find-group": "Find a Group", "alert.group-search": "Search for a group here...", + "alert.not-enough-whitelisted-tags": "Whitelisted tags are less than minimum tags, you need to create more whitelisted tags!", "collapse-all": "Collapse All", "expand-all": "Expand All", "disable-on-create": "Disable on create" diff --git a/public/src/admin/manage/category.js b/public/src/admin/manage/category.js index f4c75c38e6..497b99ca4a 100644 --- a/public/src/admin/manage/category.js +++ b/public/src/admin/manage/category.js @@ -48,6 +48,11 @@ define('admin/manage/category', [ $('[data-name="bgColor"], [data-name="color"]').each(enableColorPicker); $('#save').on('click', function () { + var tags = $('#tag-whitelist').val() ? $('#tag-whitelist').val().split(',') : []; + if (tags.length && tags.length < parseInt($('#cid-min-tags').val(), 10)) { + return app.alertError('[[admin/manage/categories:alert.not-enough-whitelisted-tags]]'); + } + if (Object.keys(modified_categories).length) { socket.emit('admin.categories.update', modified_categories, function (err, result) { if (err) { From fca4ee312e929a3e742b9c33a88dacbe19ba6218 Mon Sep 17 00:00:00 2001 From: "Misty (Bot)" Date: Thu, 9 Jul 2020 01:58:10 +0000 Subject: [PATCH 07/29] chore: incrementing version number - v1.14.2-beta.0 --- install/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/package.json b/install/package.json index 7b8c353d76..e61a29a824 100644 --- a/install/package.json +++ b/install/package.json @@ -2,7 +2,7 @@ "name": "nodebb", "license": "GPL-3.0", "description": "NodeBB Forum", - "version": "1.14.1", + "version": "1.14.2-beta.0", "homepage": "http://www.nodebb.org", "repository": { "type": "git", From c513b88dff57d76dfd0dd551ec6a801d66195a58 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 12:51:05 -0400 Subject: [PATCH 08/29] feat: #8427, daily downvote limits --- install/data/defaults.json | 2 + .../en-GB/admin/settings/reputation.json | 2 + public/language/en-GB/error.json | 2 + src/posts/votes.js | 34 +++++++++++++++-- src/views/admin/settings/reputation.tpl | 5 ++- test/posts.js | 38 ++++++++++++++++++- 6 files changed, 77 insertions(+), 6 deletions(-) diff --git a/install/data/defaults.json b/install/data/defaults.json index 8d0112ba74..b9f5f039f9 100644 --- a/install/data/defaults.json +++ b/install/data/defaults.json @@ -70,6 +70,8 @@ "reputation:disabled": 0, "downvote:disabled": 0, "disableSignatures": 0, + "downvotesPerDay": 10, + "downvotesPerUserPerDay": 3, "min:rep:downvote": 0, "min:rep:flag": 0, "min:rep:profile-picture": 0, diff --git a/public/language/en-GB/admin/settings/reputation.json b/public/language/en-GB/admin/settings/reputation.json index 910909ff65..77fe61ac6a 100644 --- a/public/language/en-GB/admin/settings/reputation.json +++ b/public/language/en-GB/admin/settings/reputation.json @@ -5,6 +5,8 @@ "votes-are-public": "All Votes Are Public", "thresholds": "Activity Thresholds", "min-rep-downvote": "Minimum reputation to downvote posts", + "downvotes-per-day": "Downvotes per day (set to 0 for unlimited downvotes)", + "downvotes-per-user-per-day": "Downvotes per user per day (set to 0 for unlimited downvotes)", "min-rep-flag": "Minimum reputation to flag posts", "min-rep-website": "Minimum reputation to add \"Website\" to user profile", "min-rep-aboutme": "Minimum reputation to add \"About me\" to user profile", diff --git a/public/language/en-GB/error.json b/public/language/en-GB/error.json index 521ea9a7e7..1158b27847 100644 --- a/public/language/en-GB/error.json +++ b/public/language/en-GB/error.json @@ -165,6 +165,8 @@ "not-enough-reputation-min-rep-cover-picture": "You do not have enough reputation to add a cover picture", "already-flagged": "You have already flagged this post", "self-vote": "You cannot vote on your own post", + "too-many-downvotes-today": "You can only downvote %1 times a day", + "too-many-downvotes-today-user": "You can only downvote a user %1 times a day", "reload-failed": "NodeBB encountered a problem while reloading: \"%1\". NodeBB will continue to serve the existing client-side assets, although you should undo what you did just prior to reloading.", diff --git a/src/posts/votes.js b/src/posts/votes.js index 92ed268d4c..fd224695d4 100644 --- a/src/posts/votes.js +++ b/src/posts/votes.js @@ -119,18 +119,17 @@ module.exports = function (Posts) { } async function unvote(pid, uid, command) { - const [owner, voteStatus, reputation] = await Promise.all([ + const [owner, voteStatus] = await Promise.all([ Posts.getPostField(pid, 'uid'), Posts.hasVoted(pid, uid), - user.getUserField(uid, 'reputation'), ]); if (parseInt(uid, 10) === parseInt(owner, 10)) { throw new Error('[[error:self-vote]]'); } - if (command === 'downvote' && reputation < meta.config['min:rep:downvote']) { - throw new Error('[[error:not-enough-reputation-to-downvote]]'); + if (command === 'downvote') { + await checkDownvoteLimitation(pid, uid); } let hook; @@ -159,6 +158,33 @@ module.exports = function (Posts) { return await vote(voteStatus.upvoted ? 'downvote' : 'upvote', true, pid, uid); } + async function checkDownvoteLimitation(pid, uid) { + const oneDay = 86400000; + const [reputation, targetUid, downvotedPids] = await Promise.all([ + user.getUserField(uid, 'reputation'), + Posts.getPostField(pid, 'uid'), + db.getSortedSetRevRangeByScore( + 'uid:' + uid + ':downvote', 0, -1, '+inf', Date.now() - oneDay + ), + ]); + + if (reputation < meta.config['min:rep:downvote']) { + throw new Error('[[error:not-enough-reputation-to-downvote]]'); + } + + if (meta.config.downvotesPerDay && downvotedPids.length >= meta.config.downvotesPerDay) { + throw new Error('[[error:too-many-downvotes-today, ' + meta.config.downvotesPerDay + ']]'); + } + + if (meta.config.downvotesPerUserPerDay) { + const postData = await Posts.getPostsFields(downvotedPids, ['uid']); + const targetDownvotes = postData.filter(p => p.uid === targetUid).length; + if (targetDownvotes >= meta.config.downvotesPerUserPerDay) { + throw new Error('[[error:too-many-downvotes-today-user, ' + meta.config.downvotesPerUserPerDay + ']]'); + } + } + } + async function vote(type, unvote, pid, uid) { uid = parseInt(uid, 10); if (uid <= 0) { diff --git a/src/views/admin/settings/reputation.tpl b/src/views/admin/settings/reputation.tpl index b05e7e8238..4d802383b9 100644 --- a/src/views/admin/settings/reputation.tpl +++ b/src/views/admin/settings/reputation.tpl @@ -32,7 +32,10 @@
[[admin/settings/reputation:thresholds]]
- [[admin/settings/reputation:min-rep-downvote]]

+ [[admin/settings/reputation:min-rep-downvote]]

+ [[admin/settings/reputation:downvotes-per-day]]

+ [[admin/settings/reputation:downvotes-per-user-per-day]]

[[admin/settings/reputation:min-rep-flag]]

[[admin/settings/reputation:min-rep-website]]

[[admin/settings/reputation:min-rep-aboutme]]

diff --git a/test/posts.js b/test/posts.js index 9bc9fa09ef..df8b113f89 100644 --- a/test/posts.js +++ b/test/posts.js @@ -242,6 +242,42 @@ describe('Post\'s', function () { }); }); }); + + it('should prevent downvoting more than total daily limit', async () => { + const oldValue = meta.config.downvotesPerDay; + meta.config.downvotesPerDay = 1; + let err; + const p1 = await topics.reply({ + uid: voteeUid, + tid: topicData.tid, + content: 'raw content', + }); + try { + await socketPosts.downvote({ uid: voterUid }, { pid: p1.pid, room_id: 'topic_1' }); + } catch (_err) { + err = _err; + } + assert.equal(err.message, '[[error:too-many-downvotes-today, 1]]'); + meta.config.downvotesPerDay = oldValue; + }); + + it('should prevent downvoting target user more than total daily limit', async () => { + const oldValue = meta.config.downvotesPerUserPerDay; + meta.config.downvotesPerUserPerDay = 1; + let err; + const p1 = await topics.reply({ + uid: voteeUid, + tid: topicData.tid, + content: 'raw content', + }); + try { + await socketPosts.downvote({ uid: voterUid }, { pid: p1.pid, room_id: 'topic_1' }); + } catch (_err) { + err = _err; + } + assert.equal(err.message, '[[error:too-many-downvotes-today-user, 1]]'); + meta.config.downvotesPerUserPerDay = oldValue; + }); }); describe('bookmarking', function () { @@ -910,7 +946,7 @@ describe('Post\'s', function () { it('should get pid index', function (done) { socketPosts.getPidIndex({ uid: voterUid }, { pid: pid, tid: topicData.tid, topicPostSort: 'oldest_to_newest' }, function (err, index) { assert.ifError(err); - assert.equal(index, 2); + assert.equal(index, 4); done(); }); }); From 73ddf1cb98900bcc213e2f194e8f82bea437d7ba 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 18:12:51 -0400 Subject: [PATCH 09/29] fix: groups.updateCover --- src/socket.io/groups.js | 13 +++++++++++-- test/groups.js | 20 +++++++++++++++++--- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/socket.io/groups.js b/src/socket.io/groups.js index d0b78f2880..3282e76b09 100644 --- a/src/socket.io/groups.js +++ b/src/socket.io/groups.js @@ -368,8 +368,15 @@ SocketGroups.cover.update = async (socket, data) => { if (!socket.uid) { throw new Error('[[error:no-privileges]]'); } + if (data.file || (!data.imageData && !data.position)) { + throw new Error('[[error:invalid-data]]'); + } await canModifyGroup(socket.uid, data.groupName); - return await groups.updateCover(socket.uid, data); + return await groups.updateCover(socket.uid, { + groupName: data.groupName, + imageData: data.imageData, + position: data.position, + }); }; SocketGroups.cover.remove = async (socket, data) => { @@ -378,7 +385,9 @@ SocketGroups.cover.remove = async (socket, data) => { } await canModifyGroup(socket.uid, data.groupName); - await groups.removeCover(data); + await groups.removeCover({ + groupName: data.groupName, + }); }; async function canModifyGroup(uid, groupName) { diff --git a/test/groups.js b/test/groups.js index 1e1e794335..a3b6470021 100644 --- a/test/groups.js +++ b/test/groups.js @@ -1387,9 +1387,9 @@ describe('Groups', function () { }); it('should fail if user is not logged in or not owner', function (done) { - socketGroups.cover.update({ uid: 0 }, {}, function (err) { + socketGroups.cover.update({ uid: 0 }, { imageData: 'asd' }, function (err) { assert.equal(err.message, '[[error:no-privileges]]'); - socketGroups.cover.update({ uid: regularUid }, { groupName: 'Test' }, function (err) { + socketGroups.cover.update({ uid: regularUid }, { groupName: 'Test', imageData: 'asd' }, function (err) { assert.equal(err.message, '[[error:no-privileges]]'); done(); }); @@ -1404,7 +1404,7 @@ describe('Groups', function () { type: 'image/png', }, }; - socketGroups.cover.update({ uid: adminUid }, data, function (err, data) { + Groups.updateCover({ uid: adminUid }, data, function (err, data) { assert.ifError(err); Groups.getGroupFields('Test', ['cover:url'], function (err, groupData) { assert.ifError(err); @@ -1434,6 +1434,20 @@ describe('Groups', function () { }); }); + it('should fail to upload group cover with invalid image', function (done) { + var data = { + groupName: 'Test', + file: { + path: imagePath, + type: 'image/png', + }, + }; + socketGroups.cover.update({ uid: adminUid }, data, function (err) { + assert.equal(err.message, '[[error:invalid-data]]'); + done(); + }); + }); + it('should fail to upload group cover with invalid image', function (done) { var data = { groupName: 'Test', From 46a66863000b468eaa6d80f3a82f45a3f9fd04cf 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 19:08:01 -0400 Subject: [PATCH 10/29] fix: category search in selector --- public/language/en-GB/modules.json | 2 ++ public/src/modules/categorySearch.js | 4 +++- src/meta/build.js | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/public/language/en-GB/modules.json b/public/language/en-GB/modules.json index a0f3dfe641..47d62c74d7 100644 --- a/public/language/en-GB/modules.json +++ b/public/language/en-GB/modules.json @@ -60,6 +60,8 @@ "composer.upload-file": "Upload File", "composer.zen_mode": "Zen Mode", "composer.select_category": "Select a category", + "composer.textarea.placeholder": "Enter your post content here, drag and drop images", + "bootbox.ok": "OK", "bootbox.cancel": "Cancel", diff --git a/public/src/modules/categorySearch.js b/public/src/modules/categorySearch.js index 1774fd2d16..5e8f2fcd26 100644 --- a/public/src/modules/categorySearch.js +++ b/public/src/modules/categorySearch.js @@ -11,7 +11,9 @@ define('categorySearch', function () { if (!searchEl.length) { return; } - var toggleVisibility = searchEl.parent('[component="category/dropdown"]').length > 0; + var toggleVisibility = searchEl.parent('[component="category/dropdown"]').length > 0 || + searchEl.parent('[component="category-selector"]').length > 0; + var categoryEls = el.find('[component="category/list"] [data-cid]'); el.on('show.bs.dropdown', function () { function revealParents(cid) { diff --git a/src/meta/build.js b/src/meta/build.js index 5fe5c953b2..c67960fdaa 100644 --- a/src/meta/build.js +++ b/src/meta/build.js @@ -218,7 +218,7 @@ exports.build = function (targets, options, callback) { }, ], function (err) { if (err) { - winston.error('[build] Encountered error during build step\n' + err.stack); + winston.error('[build] Encountered error during build step\n' + err.stack ? err.stack : err); return callback(err); } From 6235b31c2be9b379afaff4b9b7f974d7697305d3 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 19:11:26 -0400 Subject: [PATCH 11/29] feat: up composer --- install/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/package.json b/install/package.json index e61a29a824..a867b9ed1f 100644 --- a/install/package.json +++ b/install/package.json @@ -80,7 +80,7 @@ "@nodebb/mubsub": "^1.6.0", "@nodebb/socket.io-adapter-mongo": "3.0.1", "nconf": "^0.10.0", - "nodebb-plugin-composer-default": "6.3.49", + "nodebb-plugin-composer-default": "6.3.50", "nodebb-plugin-dbsearch": "4.1.1", "nodebb-plugin-emoji": "^3.3.0", "nodebb-plugin-emoji-android": "2.0.0", @@ -172,4 +172,4 @@ "url": "https://github.com/barisusakli" } ] -} \ No newline at end of file +} From 599c5015c8c02520da145109cb7a8a9cb153b254 Mon Sep 17 00:00:00 2001 From: Andrew Rodrigues Date: Thu, 9 Jul 2020 21:45:17 -0400 Subject: [PATCH 12/29] chore: up theme --- install/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/package.json b/install/package.json index a867b9ed1f..c6ba21e854 100644 --- a/install/package.json +++ b/install/package.json @@ -90,7 +90,7 @@ "nodebb-plugin-spam-be-gone": "0.7.2", "nodebb-rewards-essentials": "0.1.3", "nodebb-theme-lavender": "5.0.11", - "nodebb-theme-persona": "10.1.60", + "nodebb-theme-persona": "10.1.61", "nodebb-theme-slick": "1.2.29", "nodebb-theme-vanilla": "11.1.32", "nodebb-widget-essentials": "4.1.1", From 4e9743abb3dc2f5b0b878f2c566b3f1f528483b3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 9 Jul 2020 23:15:35 -0400 Subject: [PATCH 13/29] fix(deps): update dependency nodebb-theme-persona to v10.1.62 (#8485) Co-authored-by: Renovate Bot --- install/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/package.json b/install/package.json index c6ba21e854..f9cbf0f7e9 100644 --- a/install/package.json +++ b/install/package.json @@ -90,7 +90,7 @@ "nodebb-plugin-spam-be-gone": "0.7.2", "nodebb-rewards-essentials": "0.1.3", "nodebb-theme-lavender": "5.0.11", - "nodebb-theme-persona": "10.1.61", + "nodebb-theme-persona": "10.1.62", "nodebb-theme-slick": "1.2.29", "nodebb-theme-vanilla": "11.1.32", "nodebb-widget-essentials": "4.1.1", 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 14/29] 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]]
From 393f19b461d041725ac6c7c88a38997e4483eb80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Fri, 10 Jul 2020 08:39:49 -0400 Subject: [PATCH 15/29] feat: pass connection options to socket.io-adapter-mongo --- src/database/mongo.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/database/mongo.js b/src/database/mongo.js index d6ec3ccf8a..1339872839 100644 --- a/src/database/mongo.js +++ b/src/database/mongo.js @@ -168,7 +168,7 @@ mongoModule.close = function (callback) { mongoModule.socketAdapter = function () { const mongoAdapter = require('@nodebb/socket.io-adapter-mongo'); - return mongoAdapter(connection.getConnectionString()); + return mongoAdapter(connection.getConnectionString(), connection.getConnectionOptions()); }; require('./mongo/main')(mongoModule); From b3a88331bfb9ce0be6608f245541c9c6ecca74f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Fri, 10 Jul 2020 11:06:18 -0400 Subject: [PATCH 16/29] fix: #8488 --- src/views/admin/partials/widget-settings.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/admin/partials/widget-settings.tpl b/src/views/admin/partials/widget-settings.tpl index 8ce3b8485f..87d60ea2f1 100644 --- a/src/views/admin/partials/widget-settings.tpl +++ b/src/views/admin/partials/widget-settings.tpl @@ -17,7 +17,7 @@
- From 15aabfd3a527c1ee04e68a15e83c874d36e2dbf9 Mon Sep 17 00:00:00 2001 From: Andrew Rodrigues Date: Fri, 10 Jul 2020 12:23:44 -0400 Subject: [PATCH 17/29] chore: up theme --- install/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/package.json b/install/package.json index f9cbf0f7e9..5ba4e4e6f4 100644 --- a/install/package.json +++ b/install/package.json @@ -90,7 +90,7 @@ "nodebb-plugin-spam-be-gone": "0.7.2", "nodebb-rewards-essentials": "0.1.3", "nodebb-theme-lavender": "5.0.11", - "nodebb-theme-persona": "10.1.62", + "nodebb-theme-persona": "10.1.63", "nodebb-theme-slick": "1.2.29", "nodebb-theme-vanilla": "11.1.32", "nodebb-widget-essentials": "4.1.1", From 00d39fb32c519b251726091c91c46a666ec7c6b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Fri, 10 Jul 2020 13:12:14 -0400 Subject: [PATCH 18/29] feat: #8460, export groups members as csv --- .../language/en-GB/admin/manage/groups.json | 2 + public/openapi/read.yaml | 20 ++++++++++ public/src/admin/manage/groups.js | 2 +- src/controllers/admin/groups.js | 29 +++++++++++++++ src/routes/admin.js | 1 + src/views/admin/manage/groups.tpl | 18 +++++---- test/controllers-admin.js | 37 +++++++++++++++++++ 7 files changed, 100 insertions(+), 9 deletions(-) diff --git a/public/language/en-GB/admin/manage/groups.json b/public/language/en-GB/admin/manage/groups.json index c3d60d4eed..c61a539a7d 100644 --- a/public/language/en-GB/admin/manage/groups.json +++ b/public/language/en-GB/admin/manage/groups.json @@ -8,6 +8,8 @@ "hidden": "Hidden", "private": "Private", "edit": "Edit", + "delete": "Delete", + "download-csv": "Download CSV", "search-placeholder": "Search", "create": "Create Group", "description-placeholder": "A short description about your group", diff --git a/public/openapi/read.yaml b/public/openapi/read.yaml index 4603316f9f..28a502a571 100644 --- a/public/openapi/read.yaml +++ b/public/openapi/read.yaml @@ -2276,6 +2276,26 @@ paths: schema: type: string format: binary + /api/admin/groups/{groupname}/csv: + get: + tags: + - admin + summary: Get members of a group (.csv) + parameters: + - in: header + name: referer + schema: + type: string + required: true + example: /admin/manage/groups + responses: + "200": + description: "A CSV file containing all users in the group" + content: + text/csv: + schema: + type: string + format: binary /api/admin/analytics: get: tags: diff --git a/public/src/admin/manage/groups.js b/public/src/admin/manage/groups.js index 5a70d96d99..1d52341df5 100644 --- a/public/src/admin/manage/groups.js +++ b/public/src/admin/manage/groups.js @@ -52,7 +52,7 @@ define('admin/manage/groups', ['translator', 'benchpress'], function (translator }); }); - $('.groups-list').on('click', 'button[data-action]', function () { + $('.groups-list').on('click', '[data-action]', function () { var el = $(this); var action = el.attr('data-action'); var groupName = el.parents('tr[data-groupname]').attr('data-groupname'); diff --git a/src/controllers/admin/groups.js b/src/controllers/admin/groups.js index 33e3971e8c..b17c4c23d5 100644 --- a/src/controllers/admin/groups.js +++ b/src/controllers/admin/groups.js @@ -1,11 +1,14 @@ 'use strict'; +const nconf = require('nconf'); const validator = require('validator'); const db = require('../../database'); +const user = require('../../user'); const groups = require('../../groups'); const meta = require('../../meta'); const pagination = require('../../pagination'); +const events = require('../../events'); const groupsController = module.exports; @@ -60,3 +63,29 @@ async function getGroupNames() { const groupNames = await db.getSortedSetRange('groups:createtime', 0, -1); return groupNames.filter(name => name !== 'registered-users' && !groups.isPrivilegeGroup(name)); } + +groupsController.getCSV = async function (req, res) { + const referer = req.headers.referer; + + if (!referer || !referer.replace(nconf.get('url'), '').startsWith('/admin/manage/groups')) { + return res.status(403).send('[[error:invalid-origin]]'); + } + await events.log({ + type: 'getGroupCSV', + uid: req.uid, + ip: req.ip, + }); + const groupName = req.params.groupname; + const members = (await groups.getMembersOfGroups([groupName]))[0]; + const fields = ['email', 'username', 'uid']; + const userData = await user.getUsersFields(members, fields); + let csvContent = fields.join(',') + '\n'; + csvContent += userData.reduce((memo, user) => { + memo += user.email + ',' + user.username + ',' + user.uid + '\n'; + return memo; + }, ''); + + res.attachment(validator.escape(groupName) + '_members.csv'); + res.setHeader('Content-Type', 'text/csv'); + res.end(csvContent); +}; diff --git a/src/routes/admin.js b/src/routes/admin.js index 708a66a485..5fa9ecb698 100644 --- a/src/routes/admin.js +++ b/src/routes/admin.js @@ -69,6 +69,7 @@ module.exports = function (app, middleware, controllers) { function apiRoutes(router, middleware, controllers) { router.get('/api/admin/users/csv', middleware.authenticate, helpers.tryRoute(controllers.admin.users.getCSV)); + router.get('/api/admin/groups/:groupname/csv', middleware.authenticate, helpers.tryRoute(controllers.admin.groups.getCSV)); router.get('/api/admin/analytics', middleware.authenticate, helpers.tryRoute(controllers.admin.dashboard.getAnalytics)); const multipart = require('connect-multiparty'); diff --git a/src/views/admin/manage/groups.tpl b/src/views/admin/manage/groups.tpl index 54ab2914be..bdfde889bb 100644 --- a/src/views/admin/manage/groups.tpl +++ b/src/views/admin/manage/groups.tpl @@ -19,7 +19,7 @@ - {groups.displayName} + {groups.displayName} {groups.userTitle} @@ -42,13 +42,15 @@ {groups.memberCount} -
- - [[admin/manage/groups:edit]] - - - - + diff --git a/test/controllers-admin.js b/test/controllers-admin.js index 283c77de8f..52ab458986 100644 --- a/test/controllers-admin.js +++ b/test/controllers-admin.js @@ -364,6 +364,43 @@ describe('Admin Controllers', function () { }); }); + it('should return 403 if no referer', function (done) { + request(nconf.get('url') + '/api/admin/groups/administrators/csv', { jar: jar }, function (err, res, body) { + assert.ifError(err); + assert.equal(res.statusCode, 403); + assert.equal(body, '[[error:invalid-origin]]'); + done(); + }); + }); + + it('should return 403 if referer is not /api/admin/groups/administrators/csv', function (done) { + request(nconf.get('url') + '/api/admin/groups/administrators/csv', { + jar: jar, + headers: { + referer: '/topic/1/test', + }, + }, function (err, res, body) { + assert.ifError(err); + assert.equal(res.statusCode, 403); + assert.equal(body, '[[error:invalid-origin]]'); + done(); + }); + }); + + it('should load /api/admin/groups/administrators/csv', function (done) { + request(nconf.get('url') + '/api/admin/groups/administrators/csv', { + jar: jar, + headers: { + referer: nconf.get('url') + '/admin/manage/groups', + }, + }, function (err, res, body) { + assert.ifError(err); + assert.equal(res.statusCode, 200); + assert(body); + done(); + }); + }); + it('should load /admin/advanced/hooks', function (done) { request(nconf.get('url') + '/api/admin/advanced/hooks', { jar: jar, json: true }, function (err, res, body) { assert.ifError(err); From 3379d65f360d01c817b5806bcd6a6a4043838d2b Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 10 Jul 2020 14:39:59 -0400 Subject: [PATCH 19/29] feat: fire action:flag.showModal on flag modal appearance --- public/src/modules/flags.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/public/src/modules/flags.js b/public/src/modules/flags.js index 8c02ee9b0a..9ddf27dd9a 100644 --- a/public/src/modules/flags.js +++ b/public/src/modules/flags.js @@ -49,6 +49,11 @@ define('flags', function () { }); flagModal.modal('show'); + $(window).trigger('action:flag.showModal', { + modalEl: flagModal, + type: data.type, + id: data.id, + }); flagModal.find('#flag-reason-custom').on('keyup blur change', checkFlagButtonEnable); }); From 7730e7da23b36d1184ad78affa95f036d1d76c58 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sat, 11 Jul 2020 17:44:02 +0000 Subject: [PATCH 20/29] fix(deps): update dependency nodebb-plugin-mentions to v2.9.0 --- install/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/package.json b/install/package.json index 5ba4e4e6f4..28c874d7ef 100644 --- a/install/package.json +++ b/install/package.json @@ -85,7 +85,7 @@ "nodebb-plugin-emoji": "^3.3.0", "nodebb-plugin-emoji-android": "2.0.0", "nodebb-plugin-markdown": "8.11.2", - "nodebb-plugin-mentions": "2.8.3", + "nodebb-plugin-mentions": "2.9.0", "nodebb-plugin-soundpack-default": "1.0.0", "nodebb-plugin-spam-be-gone": "0.7.2", "nodebb-rewards-essentials": "0.1.3", From 2286ee2a619db524f96bb3d01f40efcafd887143 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 13 Jul 2020 15:17:02 -0400 Subject: [PATCH 21/29] fix: invalid session error modal during logout A brief flash can be seen, but this is exacerbated by slow connections and/or slow server response. --- public/src/app.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/public/src/app.js b/public/src/app.js index a302e36873..fb7f310a94 100644 --- a/public/src/app.js +++ b/public/src/app.js @@ -6,6 +6,7 @@ app = window.app || {}; app.isFocused = true; app.currentRoom = null; app.widgets = {}; +app.flags = {}; app.cacheBuster = null; (function () { @@ -117,6 +118,9 @@ app.cacheBuster = null; headers: { 'x-csrf-token': config.csrf_token, }, + beforeSend: function () { + app.flags._logout = true; + }, success: function (data) { $(window).trigger('action:app.loggedOut', data); if (redirect) { @@ -169,6 +173,10 @@ app.cacheBuster = null; }; app.handleInvalidSession = function () { + if (app.flags._logout) { + return; + } + socket.disconnect(); bootbox.alert({ title: '[[error:invalid-session]]', From 0ca7e28ad037ba13c382a283730104a77a9594a2 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Mon, 13 Jul 2020 03:33:03 +0000 Subject: [PATCH 22/29] chore(deps): update commitlint monorepo to v9.1.1 --- install/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/package.json b/install/package.json index 28c874d7ef..46eb375d7e 100644 --- a/install/package.json +++ b/install/package.json @@ -133,8 +133,8 @@ }, "devDependencies": { "@apidevtools/swagger-parser": "9.0.1", - "@commitlint/cli": "9.0.1", - "@commitlint/config-angular": "9.0.1", + "@commitlint/cli": "9.1.1", + "@commitlint/config-angular": "9.1.1", "coveralls": "3.1.0", "eslint": "7.3.1", "eslint-config-airbnb-base": "14.1.0", From 5e984d10f00600429d2e15daec1abcd498aa386e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 13 Jul 2020 16:02:32 -0400 Subject: [PATCH 23/29] fix: #8500 --- public/src/client/unread.js | 1 - 1 file changed, 1 deletion(-) diff --git a/public/src/client/unread.js b/public/src/client/unread.js index 504f72e613..3d0dfa5fe1 100644 --- a/public/src/client/unread.js +++ b/public/src/client/unread.js @@ -14,7 +14,6 @@ define('forum/unread', ['topicSelect', 'components', 'topicList'], function (top app.enterRoom('unread_topics'); topicList.init('unread'); - topicSelect.init(); updateUnreadTopicCount('/' + ajaxify.data.selectedFilter.url, ajaxify.data.topicCount); From 9d1465d0da02fb4190ae68b1832e49bf2975f298 Mon Sep 17 00:00:00 2001 From: "Misty (Bot)" Date: Tue, 14 Jul 2020 13:25:39 +0000 Subject: [PATCH 24/29] chore: incrementing version number - v1.14.2-beta.1 --- install/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/package.json b/install/package.json index 46eb375d7e..efdc32d47b 100644 --- a/install/package.json +++ b/install/package.json @@ -2,7 +2,7 @@ "name": "nodebb", "license": "GPL-3.0", "description": "NodeBB Forum", - "version": "1.14.2-beta.0", + "version": "1.14.2-beta.1", "homepage": "http://www.nodebb.org", "repository": { "type": "git", @@ -172,4 +172,4 @@ "url": "https://github.com/barisusakli" } ] -} +} \ No newline at end of file From 63fb2ad7d923058f7f8f7c0d59efa76e6a918f4b Mon Sep 17 00:00:00 2001 From: Andrew Rodrigues Date: Tue, 14 Jul 2020 12:59:29 -0400 Subject: [PATCH 25/29] chore: up theme --- install/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/package.json b/install/package.json index efdc32d47b..d21e98624a 100644 --- a/install/package.json +++ b/install/package.json @@ -90,7 +90,7 @@ "nodebb-plugin-spam-be-gone": "0.7.2", "nodebb-rewards-essentials": "0.1.3", "nodebb-theme-lavender": "5.0.11", - "nodebb-theme-persona": "10.1.63", + "nodebb-theme-persona": "10.1.64", "nodebb-theme-slick": "1.2.29", "nodebb-theme-vanilla": "11.1.32", "nodebb-widget-essentials": "4.1.1", @@ -172,4 +172,4 @@ "url": "https://github.com/barisusakli" } ] -} \ No newline at end of file +} From f85a45c7222eeea457405f44dd48979facd07341 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 13 Jul 2020 20:29:05 -0400 Subject: [PATCH 26/29] feat: logic for flag note deletion, #8499 --- public/language/en-GB/flags.json | 4 +++- public/src/client/flags/detail.js | 22 +++++++++++++++++++++- src/flags.js | 24 ++++++++++++++++++++++++ src/socket.io/flags.js | 19 +++++++++++++++++++ 4 files changed, 67 insertions(+), 2 deletions(-) diff --git a/public/language/en-GB/flags.json b/public/language/en-GB/flags.json index fb8708fc16..a8bd07ad4f 100644 --- a/public/language/en-GB/flags.json +++ b/public/language/en-GB/flags.json @@ -43,6 +43,9 @@ "notes": "Flag Notes", "add-note": "Add Note", "no-notes": "No shared notes.", + "delete-note-confirm": "Are you sure you want to delete this flag note?", + "note-added": "Note Added", + "note-deleted": "Note Deleted", "history": "Account & Flag History", "no-history": "No flag history.", @@ -53,7 +56,6 @@ "state-resolved": "Resolved", "state-rejected": "Rejected", "no-assignee": "Not Assigned", - "note-added": "Note Added", "modal-title": "Report Inappropriate Content", "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", diff --git a/public/src/client/flags/detail.js b/public/src/client/flags/detail.js index b847b4391f..bd509831ef 100644 --- a/public/src/client/flags/detail.js +++ b/public/src/client/flags/detail.js @@ -8,7 +8,7 @@ define('forum/flags/detail', ['forum/flags/list', 'components', 'translator', 'b $('#state').val(ajaxify.data.state).removeAttr('disabled'); $('#assignee').val(ajaxify.data.assignee).removeAttr('disabled'); - $('[data-action]').on('click', function () { + $('#content > div').on('click', '[data-action]', function () { var action = this.getAttribute('data-action'); var uid = $(this).parents('[data-uid]').attr('data-uid'); @@ -75,6 +75,26 @@ define('forum/flags/detail', ['forum/flags/list', 'components', 'translator', 'b case 'restore-post': postAction('restore', ajaxify.data.target.pid, ajaxify.data.target.tid); break; + + case 'delete-note': + var datetime = this.closest('[data-datetime]').getAttribute('data-datetime'); + bootbox.confirm('[[flags:delete-note-confirm]]', function (ok) { + if (ok) { + socket.emit('flags.deleteNote', { + flagId: ajaxify.data.flagId, + datetime: datetime, + }, function (err, payload) { + if (err) { + return app.alertError(err.message); + } + + app.alertSuccess('[[flags:note-deleted]]'); + Detail.reloadNotes(payload.notes); + Detail.reloadHistory(payload.history); + }); + } + }); + break; } }); diff --git a/src/flags.js b/src/flags.js index 6668895098..5c0987c212 100644 --- a/src/flags.js +++ b/src/flags.js @@ -228,6 +228,21 @@ Flags.validate = async function (payload) { Flags.getNotes = async function (flagId) { let notes = await db.getSortedSetRevRangeWithScores('flag:' + flagId + ':notes', 0, -1); + notes = await modifyNotes(notes); + return notes; +}; + +Flags.getNote = async function (flagId, datetime) { + let notes = await db.getSortedSetRangeByScoreWithScores('flag:' + flagId + ':notes', 0, 1, datetime, datetime); + if (!notes.length) { + throw new Error('[[error:invalid-data]]'); + } + + notes = await modifyNotes(notes); + return notes[0]; +}; + +async function modifyNotes(notes) { const uids = []; notes = notes.map(function (note) { const noteObj = JSON.parse(note.value); @@ -245,6 +260,15 @@ Flags.getNotes = async function (flagId) { note.content = validator.escape(note.content); return note; }); +} + +Flags.deleteNote = async function (flagId, datetime) { + const note = await db.getSortedSetRangeByScore('flag:' + flagId + ':notes', 0, 1, datetime, datetime); + if (!note.length) { + throw new Error('[[error:invalid-data]]'); + } + + await db.sortedSetRemove('flag:' + flagId + ':notes', note[0]); }; Flags.create = async function (type, id, uid, reason, timestamp) { diff --git a/src/socket.io/flags.js b/src/socket.io/flags.js index e2c9a56215..9d72101c37 100644 --- a/src/socket.io/flags.js +++ b/src/socket.io/flags.js @@ -62,4 +62,23 @@ SocketFlags.appendNote = async function (socket, data) { return { notes: notes, history: history }; }; +SocketFlags.deleteNote = async function (socket, data) { + if (!data || !(data.flagId && data.datetime)) { + throw new Error('[[error:invalid-data]]'); + } + + const note = await flags.getNote(data.flagId, data.datetime); + if (note.uid !== socket.uid) { + throw new Error('[[error:no-privileges]]'); + } + + await flags.deleteNote(data.flagId, data.datetime); + + const [notes, history] = await Promise.all([ + flags.getNotes(data.flagId), + flags.getHistory(data.flagId), + ]); + return { notes: notes, history: history }; +}; + require('../promisify')(SocketFlags); From 14417209621945038deef805665467adbf0c4255 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 14 Jul 2020 13:38:23 -0400 Subject: [PATCH 27/29] feat: logic for flag note editing, #8499 --- public/.eslintrc | 1 + public/src/client/flags/detail.js | 25 ++++++++++++++++++++++++- src/flags.js | 4 ++++ src/socket.io/flags.js | 2 +- 4 files changed, 30 insertions(+), 2 deletions(-) diff --git a/public/.eslintrc b/public/.eslintrc index 865785241c..322f86a92d 100644 --- a/public/.eslintrc +++ b/public/.eslintrc @@ -21,6 +21,7 @@ "es6": false }, "rules": { + "block-scoped-var": "off", "no-dupe-class-members": "off", "no-var": "off", "object-shorthand": "off", diff --git a/public/src/client/flags/detail.js b/public/src/client/flags/detail.js index bd509831ef..31d9e9d156 100644 --- a/public/src/client/flags/detail.js +++ b/public/src/client/flags/detail.js @@ -11,6 +11,7 @@ define('forum/flags/detail', ['forum/flags/list', 'components', 'translator', 'b $('#content > div').on('click', '[data-action]', function () { var action = this.getAttribute('data-action'); var uid = $(this).parents('[data-uid]').attr('data-uid'); + var noteEl = document.getElementById('note'); switch (action) { case 'assign': @@ -33,7 +34,8 @@ define('forum/flags/detail', ['forum/flags/list', 'components', 'translator', 'b case 'appendNote': socket.emit('flags.appendNote', { flagId: ajaxify.data.flagId, - note: document.getElementById('note').value, + note: noteEl.value, + datetime: noteEl.getAttribute('data-datetime'), }, function (err, payload) { if (err) { return app.alertError(err.message); @@ -41,6 +43,9 @@ define('forum/flags/detail', ['forum/flags/list', 'components', 'translator', 'b app.alertSuccess('[[flags:note-added]]'); Detail.reloadNotes(payload.notes); Detail.reloadHistory(payload.history); + + noteEl.setAttribute('data-action', 'appendNote'); + noteEl.removeAttribute('data-datetime'); }); break; @@ -95,6 +100,23 @@ define('forum/flags/detail', ['forum/flags/list', 'components', 'translator', 'b } }); break; + + case 'prepare-edit': + var selectedNoteEl = this.closest('[data-index]'); + var index = selectedNoteEl.getAttribute('data-index'); + var textareaEl = document.getElementById('note'); + textareaEl.value = ajaxify.data.notes[index].content; + textareaEl.setAttribute('data-datetime', ajaxify.data.notes[index].datetime); + + var siblings = selectedNoteEl.parentElement.children; + for (var el in siblings) { + if (siblings.hasOwnProperty(el)) { + siblings[el].classList.remove('editing'); + } + } + selectedNoteEl.classList.add('editing'); + textareaEl.focus(); + break; } }); @@ -123,6 +145,7 @@ define('forum/flags/detail', ['forum/flags/list', 'components', 'translator', 'b } Detail.reloadNotes = function (notes) { + ajaxify.data.notes = notes; Benchpress.parse('flags/detail', 'notes', { notes: notes, }, function (html) { diff --git a/src/flags.js b/src/flags.js index 5c0987c212..2a8927902a 100644 --- a/src/flags.js +++ b/src/flags.js @@ -499,7 +499,11 @@ Flags.appendHistory = async function (flagId, uid, changeset) { }; Flags.appendNote = async function (flagId, uid, note, datetime) { + if (datetime) { + await Flags.deleteNote(flagId, datetime); + } datetime = datetime || Date.now(); + const payload = JSON.stringify([uid, note]); await db.sortedSetAdd('flag:' + flagId + ':notes', datetime, payload); await Flags.appendHistory(flagId, uid, { diff --git a/src/socket.io/flags.js b/src/socket.io/flags.js index 9d72101c37..7039903f8d 100644 --- a/src/socket.io/flags.js +++ b/src/socket.io/flags.js @@ -53,7 +53,7 @@ SocketFlags.appendNote = async function (socket, data) { if (!allowed) { throw new Error('[[no-privileges]]'); } - await flags.appendNote(data.flagId, socket.uid, data.note); + await flags.appendNote(data.flagId, socket.uid, data.note, data.datetime); const [notes, history] = await Promise.all([ flags.getNotes(data.flagId), From 65240a178e482758818c25bfdfebc98555846bb1 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 14 Jul 2020 13:40:51 -0400 Subject: [PATCH 28/29] fix: #8499 --- install/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/package.json b/install/package.json index d21e98624a..6ef75e8c0c 100644 --- a/install/package.json +++ b/install/package.json @@ -90,9 +90,9 @@ "nodebb-plugin-spam-be-gone": "0.7.2", "nodebb-rewards-essentials": "0.1.3", "nodebb-theme-lavender": "5.0.11", - "nodebb-theme-persona": "10.1.64", + "nodebb-theme-persona": "10.1.65", "nodebb-theme-slick": "1.2.29", - "nodebb-theme-vanilla": "11.1.32", + "nodebb-theme-vanilla": "11.1.33", "nodebb-widget-essentials": "4.1.1", "nodemailer": "^6.4.6", "passport": "^0.4.1", From c54287fe9c6c4784083896db332962fede9bee08 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Wed, 15 Jul 2020 14:36:10 +0000 Subject: [PATCH 29/29] fix(deps): update dependency nodebb-plugin-mentions to v2.9.1 --- install/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/package.json b/install/package.json index 6ef75e8c0c..91e726f1ea 100644 --- a/install/package.json +++ b/install/package.json @@ -85,7 +85,7 @@ "nodebb-plugin-emoji": "^3.3.0", "nodebb-plugin-emoji-android": "2.0.0", "nodebb-plugin-markdown": "8.11.2", - "nodebb-plugin-mentions": "2.9.0", + "nodebb-plugin-mentions": "2.9.1", "nodebb-plugin-soundpack-default": "1.0.0", "nodebb-plugin-spam-be-gone": "0.7.2", "nodebb-rewards-essentials": "0.1.3",