From 1437c62f3a17b9467a283d5fe1ab6911ab36b43f Mon Sep 17 00:00:00 2001 From: "Misty (Bot)" Date: Thu, 17 Dec 2020 23:59:27 +0000 Subject: [PATCH 01/98] chore: update changelog for v1.16.0 --- CHANGELOG.md | 139 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 969c75f886..dfd9ca2d29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,142 @@ +#### v1.16.0 (2020-12-17) + +##### Breaking Changes + +* enable topic thumbnails across the board [breaking] (9342d611) +* #8808, remove utils.slugify (4a0d8833) + +##### Chores + +* **deps:** + * update dependency husky to v4.3.6 (2371b432) + * bump persona to 10.3.9 (91899329) + * bump composer to 6.5.1, re: #9067 (228cfa67) + * update dependency husky to v4.3.5 (48a31763) + * update dependency husky to v4.3.4 (cf5c482d) + * update dependency eslint to v7.15.0 (f4c4d671) + * update dependency lint-staged to v10.5.3 (3e6f7359) +* appease codeclimate (9f62df15) +* add comment for clarification (6037f5ee) +* incrementing version number - v1.15.5 (57cd1343) +* update changelog for v1.15.5 (b0299326) +* **i18n:** fallbacks for new topic thumb keys (15f1a089) + +##### New Features + +* add new client side hooks (a15ef53c) +* remove max age since cache is cleared when thumbs change (ab96f526) +* show alt text instead of images in teasers (#9107) (d28581eb) +* migration of old topic thumbs to new format (74d73313) +* allow plugins to override ACP relogin challenge (4c87f301) +* add user.email.confirmByUid for sso plugins (80de572a) +* add thumbs to category data return (24e754d1) +* broken test for bad topic thumbs logic (ce8057f3) +* clent-side modal for managing topic thumbs (a30c8ab5) +* raise maximum thumb size to 512 (37c367d6) +* associate topic thumbs with post uploads (for the mainPid) (1c5cdb51) +* helper method to get thumbs by pid (cb7e4cda) +* closes #9048, tests for topic thumbs routes, write API schema (59506833) +* tests for topic thumbs (4152aa55) +* server-side work for #9047 (ef7d6db9) +* core work for #9042, thumb deletion now accepts uuids (b5d910f5) +* more work on topic thumbs refactor (90497e3e) +* expose uploaded thumbnails to client-side via API (1257aa98) +* server-side routes for handling multiple topic thumbnails (7e9e08f7) +* allow uploadThumb controller to be called in code (98cd9e35) +* move upgrade script and make it shorter (60e7de0d) +* allow clicks on navigator, clean dupe code (74274b60) +* socket.io 3 changes (#8845) (1c45fa1b) +* **deps:** update lavender to allow category sections (6d186be0) + +##### Bug Fixes + +* **deps:** + * update dependency nodebb-plugin-composer-default to v6.5.4 (#9120) (fff0cea6) + * update dependency nodebb-theme-slick to v1.3.7 (#9112) (30688b1b) + * update dependency nodebb-theme-lavender to v5.0.17 (#9111) (877f4673) + * update dependency nodebb-theme-vanilla to v11.3.10 (ff18cdfa) + * update dependency validator to v13.5.2 (#9094) (5d718348) + * update dependency nodebb-theme-vanilla to v11.3.9 (#9091) (f37dbeed) + * update dependency nodebb-plugin-composer-default to v6.5.3 (d036408d) + * update dependency nodebb-plugin-composer-default to v6.5.2 (b07fb9ab) + * bump composer-default to 6.5.0 (0db49121) + * update dependency autoprefixer to v10.1.0 (024d1fef) + * update dependency nodebb-theme-persona to v10.3.8 (#9084) (25f697b1) + * update socket.io packages to v3.0.4 (62463430) + * update dependency nodebb-theme-persona to v10.3.7 (c22cdb51) + * update dependency nodebb-theme-persona to v10.3.6 (#9077) (5937fbaf) + * update dependency nodebb-plugin-mentions to v2.13.6 (#9071) (a535350f) + * update dependency nodebb-theme-slick to v1.3.6 (#9072) (19c438c6) + * update dependency nodebb-widget-essentials to v5 (#9070) (d7f5efd9) + * update dependency nodebb-plugin-markdown to v8.12.4 (8fb814ba) + * update dependency nodebb-theme-persona to v10.3.5 (#9060) (0d082280) + * update dependency nodebb-theme-persona to v10.3.4 (#9059) (84e4e480) + * update dependency nodebb-theme-persona to v10.3.3 (3d7e2e1e) + * update dependency nodebb-theme-persona to v10.3.2 (#9056) (f49ce4ad) + * update dependency nodebb-theme-persona to v10.3.1 (#9054) (344caf5c) + * update dependency nodebb-theme-lavender to v5.0.15 (#9053) (e7d72d8a) + * update dependency nodebb-theme-persona to v10.3.0 (#9052) (dcd6fbaf) +* api usage (feecd665) +* #9117, lower query before search (4404e32e) +* #9114, fix client side groups update for memberPostCids (3ed55799) +* test (2dee3cbe) +* don't check "select all" if there are no enabled checkboxes (3ba05755) +* #9074, fix svg uploads (8f938eba) +* #9100 topic thumbs in OG image tags (ab987408) +* update version removal comments to 1.17 for some features (378a3a69) +* postgres is slow:tm: (05dd8597) +* derp? (f8dff94a) +* attempted fix for psql test in topic thumbs (9a4ea04a) +* use getSortedSetRange instead of getSortedSetsMembers (edf67f34) +* tests (bd5c4a5c) +* bad topic thumbs logic on local thumb upload (e83baa97) +* #9092, Topic thumbnails do not work with third-party uploaders (3e54b70c) +* move topic thumb tests to root level, so they actually get run by mocha (dd448e2b) +* tests for topic thumbs (9681557f) +* iteration logic bug (2170c400) +* spec (ae943974) +* changes to thumb resizing logic (67cf5e83) +* use file lib instead of direct fs module access (08736b18) +* added back missing topic thumb tests that were removed in last commit (c043cfeb) +* tests (5ec3b3d0) +* hack uploader to handle a response from v3 write api (41379e27) +* #9055, non-standard API response from addThumbs route (340387c1) +* do not allow thumb deletion route to arbitrarily delete other files in uploads folder (c09c238e) +* missing file added (ef10b6b7) +* references to since-removed Topics.thumbs.resizeAndUpload (1f0c1cd2) +* #9041, remove Topics.thumbs.resizeAndUpload() (43dc3e3e) +* #9040 (708b1c33) +* spec (1949d20a) +* #9085, dont prevent admins from deleting other users (0f480be6) +* show errors when user delete fails (ff2aa17b) +* dont start logout timer if adminReloginDuration is disabled (dd9ed236) +* #9045, no post usage info if '/files/' path received (efa4eca0) +* reconnectin no longer fires on socket.io 3 (13d5a144) +* default values, clamp postsPerPage/topicsPerPage to max (1f32d387) +* #9081, load raw settings before merging (9da0ed40) +* #9068 (86f0f82b) +* remove old utils.slugify tests (10cfdd4c) +* dont strip tags (792e9e70) +* #9065, settings v2/v3 conflict (91c20cec) +* #9063, missing handler for passwordless accounts in admin.checkPrivileges middleware (970ccb5a) +* timeago in navigation (a389a31b) +* navigation fixes (163d1a39) +* cache some jquery objects (73d2f51d) +* add ev.cancelable (63d08395) +* #9046, pretranslate string (790f4e45) +* redirect external with absolute urls (648f6215) +* external path for subfolder installs (458bfc0f) +* **spec:** broken link to status component (d31aae16) + +##### Performance Improvements + +* don't load thumbs if disabled globally, cache thumb results (2d5a224b) +* dont build identical langs (bb6cc49c) + +##### Refactors + +* topic thumbs lib to topics.thumbs (4fc9da81) + #### v1.15.5 (2020-12-03) ##### Chores From 6d01fd5028af63ec8ad677432dbd45b8e179f2ac Mon Sep 17 00:00:00 2001 From: "Misty (Bot)" Date: Thu, 17 Dec 2020 23:59:26 +0000 Subject: [PATCH 02/98] chore: incrementing version number - v1.16.0 (cherry picked from commit 67ccc2176039a1d388aba150df565183a0c50765) Signed-off-by: Misty (Bot) --- install/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/package.json b/install/package.json index 6922e93307..eb79ff2527 100644 --- a/install/package.json +++ b/install/package.json @@ -2,7 +2,7 @@ "name": "nodebb", "license": "GPL-3.0", "description": "NodeBB Forum", - "version": "1.15.5", + "version": "1.16.0", "homepage": "http://www.nodebb.org", "repository": { "type": "git", @@ -189,4 +189,4 @@ "url": "https://github.com/barisusakli" } ] -} +} \ No newline at end of file From b7b588f5c8fe22a7b684df0fb56d35f49b6d13e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Thu, 17 Dec 2020 20:30:13 -0500 Subject: [PATCH 03/98] fix: trigger action:posts.edited --- public/src/client/topic/events.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/public/src/client/topic/events.js b/public/src/client/topic/events.js index bb42e1e026..d98195434b 100644 --- a/public/src/client/topic/events.js +++ b/public/src/client/topic/events.js @@ -145,6 +145,8 @@ define('forum/topic/events', [ $(window).trigger('action:posts.edited', data); }); }); + } else { + $(window).trigger('action:posts.edited', data); } if (data.topic.tags && tagsUpdated(data.topic.tags)) { From db4c68639d8938af90ac4760abda6f68fb7981aa Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Fri, 18 Dec 2020 01:32:09 +0000 Subject: [PATCH 04/98] fix(deps): update dependency nodebb-theme-persona to v10.3.11 --- install/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/package.json b/install/package.json index eb79ff2527..7d792f7d2d 100644 --- a/install/package.json +++ b/install/package.json @@ -101,7 +101,7 @@ "nodebb-plugin-spam-be-gone": "0.7.7", "nodebb-rewards-essentials": "0.1.4", "nodebb-theme-lavender": "5.0.17", - "nodebb-theme-persona": "10.3.9", + "nodebb-theme-persona": "10.3.11", "nodebb-theme-slick": "1.3.7", "nodebb-theme-vanilla": "11.3.10", "nodebb-widget-essentials": "5.0.0", From b8d4709eec9c2cc6766763ab26bab8ac4d10500f Mon Sep 17 00:00:00 2001 From: psychobunny Date: Fri, 18 Dec 2020 10:03:36 -0500 Subject: [PATCH 05/98] fix(pwa): #9127 service-worker.js missing on subfolder installs --- public/src/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/src/app.js b/public/src/app.js index 72dd0faf87..5e87d55086 100644 --- a/public/src/app.js +++ b/public/src/app.js @@ -786,7 +786,7 @@ app.cacheBuster = null; function registerServiceWorker() { if ('serviceWorker' in navigator) { - navigator.serviceWorker.register('/service-worker.js') + navigator.serviceWorker.register(config.relative_path + '/service-worker.js') .then(function () { console.info('ServiceWorker registration succeeded.'); }).catch(function (err) { From 55a55ea284fa692bd8fe0577cdcde7a5b652773a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 18 Dec 2020 11:01:44 -0500 Subject: [PATCH 06/98] chore(deps): update actions/setup-node action to v2 (#9115) Co-authored-by: Renovate Bot --- .github/workflows/test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index d812450608..a648be6ce0 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -80,7 +80,7 @@ jobs: - run: cp install/package.json package.json - name: Install Node - uses: actions/setup-node@v1 + uses: actions/setup-node@v2 with: node-version: ${{ matrix.node }} From a2152dd10035f6544fe1e556c8fa0a8a6fab0346 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Fri, 18 Dec 2020 11:18:49 -0500 Subject: [PATCH 07/98] feat: update html-to-text closes https://github.com/NodeBB/NodeBB/pull/8810 --- install/package.json | 2 +- src/emailer.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/install/package.json b/install/package.json index 7d792f7d2d..f24311ad37 100644 --- a/install/package.json +++ b/install/package.json @@ -69,7 +69,7 @@ "express-useragent": "^1.0.13", "graceful-fs": "^4.2.3", "helmet": "^4.0.0", - "html-to-text": "^5.1.1", + "html-to-text": "6.0.0", "ipaddr.js": "^2.0.0", "jquery": "3.5.1", "jquery-deserialize": "2.0.0-rc1", diff --git a/src/emailer.js b/src/emailer.js index 83e16f5d97..4b9ae4a575 100644 --- a/src/emailer.js +++ b/src/emailer.js @@ -5,7 +5,7 @@ const nconf = require('nconf'); const Benchpress = require('benchpressjs'); const nodemailer = require('nodemailer'); const wellKnownServices = require('nodemailer/lib/well-known/services'); -const htmlToText = require('html-to-text'); +const { htmlToText } = require('html-to-text'); const url = require('url'); const path = require('path'); const fs = require('fs'); @@ -287,8 +287,8 @@ Emailer.sendToEmail = async (template, email, language, params) => { from_name: meta.config['email:from_name'] || 'NodeBB', subject: '[' + meta.config.title + '] ' + _.unescape(subject), html: html, - plaintext: htmlToText.fromString(html, { - ignoreImage: true, + plaintext: htmlToText(html, { + tags: { img: { format: 'skip' } }, }), template: template, uid: params.uid, From dbe85630e3fb9a93c88239425017a83fefa852c1 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Sat, 12 Dec 2020 13:25:36 -0500 Subject: [PATCH 08/98] feat: additional test to ensure any new routes added to express have a corresponding schema doc re: #9103 --- test/api.js | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/test/api.js b/test/api.js index 1f43e67e13..141af73fb2 100644 --- a/test/api.js +++ b/test/api.js @@ -169,8 +169,40 @@ describe('API', async () => { readApi = await SwaggerParser.dereference(readApiPath); writeApi = await SwaggerParser.dereference(writeApiPath); - generateTests(readApi, Object.keys(readApi.paths)); - generateTests(writeApi, Object.keys(writeApi.paths), writeApi.servers[0].url); + it('should grab all mounted routes and ensure a schema exists', async () => { + const webserver = require('../src/webserver'); + const buildPaths = function (stack, prefix) { + const paths = stack.reduce((memo, cur) => { + if (cur.route && cur.route.path && typeof cur.route.path === 'string' && cur.route.path.startsWith('/api/')) { + memo.push({ + method: Object.keys(cur.route.methods)[0], + path: (prefix || '') + cur.route.path, + }); + } else if (cur.name === 'router') { + const prefix = cur.regexp.toString().replace('/^', '').replace('\\/?(?=\\/|$)/i', '').replace(/\\\//g, '/'); + memo = memo.concat(buildPaths(cur.handle.stack, prefix)); + } + return memo; + }, []); + + return paths; + }; + const paths = buildPaths(webserver.app._router.stack); + + // For each express path, query for existence in read and write api schemas + paths.forEach((pathObj) => { + describe(`${pathObj.method.toUpperCase()} ${pathObj.path}`, () => { + it('should be defined in schema docs', () => { + const schema = pathObj.path.startsWith('/api/v3') ? writeApi : readApi; + const normalizedPath = pathObj.path.replace(/\/:([^\\/]+)/g, '/{$1}').replace(/\?/g, ''); + assert(schema.paths.hasOwnProperty(normalizedPath)); + }); + }); + }); + }); + + // generateTests(readApi, Object.keys(readApi.paths)); + // generateTests(writeApi, Object.keys(writeApi.paths), writeApi.servers[0].url); function generateTests(api, paths, prefix) { // Iterate through all documented paths, make a call to it, and compare the result body with what is defined in the spec From df8d62ba06daf4feb8a5466cf92e811dea8a0a7b Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 14 Dec 2020 15:24:46 -0500 Subject: [PATCH 09/98] feat: normalize paths before comparison --- public/openapi/read.yaml | 2 +- test/api.js | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/public/openapi/read.yaml b/public/openapi/read.yaml index 94481e01ba..f60727f0e9 100644 --- a/public/openapi/read.yaml +++ b/public/openapi/read.yaml @@ -225,7 +225,7 @@ paths: $ref: 'read/me.yaml' /api/me/*: $ref: 'read/me.yaml' - "/api/uid/{uid}/*": + "/api/uid/{uid*}": $ref: 'read/uid/uid.yaml' "/api/user/{userslug}": $ref: 'read/user/userslug.yaml' diff --git a/test/api.js b/test/api.js index 141af73fb2..9a1f6292f5 100644 --- a/test/api.js +++ b/test/api.js @@ -187,7 +187,10 @@ describe('API', async () => { return paths; }; - const paths = buildPaths(webserver.app._router.stack); + const paths = buildPaths(webserver.app._router.stack).map(function normalize(pathObj) { + pathObj.path = pathObj.path.replace(/\/:([^\\/]+)/g, '/{$1}'); + return pathObj; + }); // For each express path, query for existence in read and write api schemas paths.forEach((pathObj) => { From 878ee067158dd73d1cf17ce1e336711990026e84 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 15 Dec 2020 13:54:55 -0500 Subject: [PATCH 10/98] refactor: schema backreference test to use map instead of reduce, properly check write-api routes --- public/openapi/write.yaml | 16 ++++++++++++++- src/routes/write/index.js | 1 + test/api.js | 43 ++++++++++++++++++++++++++------------- 3 files changed, 45 insertions(+), 15 deletions(-) diff --git a/public/openapi/write.yaml b/public/openapi/write.yaml index 73555fe7ac..b827cb127c 100644 --- a/public/openapi/write.yaml +++ b/public/openapi/write.yaml @@ -23,11 +23,25 @@ info: servers: - url: /api/v3 tags: + - name: utilities + description: Utility calls to test Write API functionality - name: users - description: 'Account related calls (create, modify, delete, etc.)' + description: Account related calls (create, modify, delete, etc.) + - name: groups + description: Calls related to user groups - name: categories description: Administrative calls to manage categories + - name: topics + description: Topic-based calls (create, modify, delete, etc.) + - name: posts + description: Individual post-related calls (create, modify, delete, etc.) + - name: admin + description: Administrative calls + - name: files + description: File upload routes paths: + /ping: + $ref: 'write/ping.yaml' /users/: $ref: 'write/users.yaml' /users/{uid}: diff --git a/src/routes/write/index.js b/src/routes/write/index.js index 539668d508..6cf266acdb 100644 --- a/src/routes/write/index.js +++ b/src/routes/write/index.js @@ -48,6 +48,7 @@ Write.reload = async (params) => { router.post('/api/v3/ping', middleware.authenticate, function (req, res) { helpers.formatApiResponse(200, res, { uid: req.user.uid, + received: req.body, }); }); diff --git a/test/api.js b/test/api.js index 9a1f6292f5..fefcdb92e6 100644 --- a/test/api.js +++ b/test/api.js @@ -172,31 +172,46 @@ describe('API', async () => { it('should grab all mounted routes and ensure a schema exists', async () => { const webserver = require('../src/webserver'); const buildPaths = function (stack, prefix) { - const paths = stack.reduce((memo, cur) => { - if (cur.route && cur.route.path && typeof cur.route.path === 'string' && cur.route.path.startsWith('/api/')) { - memo.push({ - method: Object.keys(cur.route.methods)[0], - path: (prefix || '') + cur.route.path, - }); - } else if (cur.name === 'router') { - const prefix = cur.regexp.toString().replace('/^', '').replace('\\/?(?=\\/|$)/i', '').replace(/\\\//g, '/'); - memo = memo.concat(buildPaths(cur.handle.stack, prefix)); + const paths = stack.map((dispatch) => { + if (dispatch.route && dispatch.route.path && typeof dispatch.route.path === 'string') { + if (!prefix && !dispatch.route.path.startsWith('/api/')) { + return null; + } + return { + method: Object.keys(dispatch.route.methods)[0], + path: (prefix || '') + dispatch.route.path, + }; + } else if (dispatch.name === 'router') { + const prefix = dispatch.regexp.toString().replace('/^', '').replace('\\/?(?=\\/|$)/i', '').replace(/\\\//g, '/'); + return buildPaths(dispatch.handle.stack, prefix); } - return memo; - }, []); - return paths; + // Drop any that aren't actual routes (middlewares, error handlers, etc.) + return null; + }); + + return paths.flat(); }; - const paths = buildPaths(webserver.app._router.stack).map(function normalize(pathObj) { + + let paths = buildPaths(webserver.app._router.stack).filter(Boolean).map(function normalize(pathObj) { pathObj.path = pathObj.path.replace(/\/:([^\\/]+)/g, '/{$1}'); return pathObj; }); + const exclusionPrefixes = ['/api/admin/plugins']; + paths = paths.filter(function filterExclusions(path) { + return !exclusionPrefixes.some(prefix => path.path.startsWith(prefix)); + }); + // For each express path, query for existence in read and write api schemas paths.forEach((pathObj) => { describe(`${pathObj.method.toUpperCase()} ${pathObj.path}`, () => { it('should be defined in schema docs', () => { - const schema = pathObj.path.startsWith('/api/v3') ? writeApi : readApi; + let schema = readApi; + if (pathObj.path.startsWith('/api/v3')) { + schema = writeApi; + pathObj.path = pathObj.path.replace('/api/v3', ''); + } const normalizedPath = pathObj.path.replace(/\/:([^\\/]+)/g, '/{$1}').replace(/\?/g, ''); assert(schema.paths.hasOwnProperty(normalizedPath)); }); From d85181e031445dc94f6e1b0abb59f2352c95578f Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 15 Dec 2020 13:56:07 -0500 Subject: [PATCH 11/98] feat: add schema for api ping routes --- public/openapi/write/ping.yaml | 56 ++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 public/openapi/write/ping.yaml diff --git a/public/openapi/write/ping.yaml b/public/openapi/write/ping.yaml new file mode 100644 index 0000000000..8e2b0c7540 --- /dev/null +++ b/public/openapi/write/ping.yaml @@ -0,0 +1,56 @@ +get: + tags: + - utilities + summary: test route + description: This route responds with a simple `200 OK` if the Write API is enabled. Since there is no way of disabling the Write API, this will always return a success. However, it is also a good way to ensure the instance you are calling supports v3 of the Write API. + responses: + '200': + description: pingback + content: + application/json: + schema: + type: object + properties: + status: + $ref: ../components/schemas/Status.yaml#/Status + response: + type: object + properties: + pong: + type: boolean + example: true +post: + tags: + - utilities + summary: test route + description: | + Requires authentication. This route bounces back the data payload sent to it, and the uid the token resolved to. + + It is also a good way to ensure the instance you are calling supports v3 of the Write API. Also, as it requires authentication, it is a good way to check if the passed-in token is a valid token. + requestBody: + required: false + content: + application/json: + schema: + type: object + additionalProperties: {} + responses: + '200': + description: pingback + content: + application/json: + schema: + type: object + properties: + status: + $ref: ../components/schemas/Status.yaml#/Status + response: + type: object + properties: + uid: + type: number + description: The `uid` that the passed-in token resolves to. + received: + type: object + description: A free-form object containing the data that was passed to it. It reflects the data payload as the Write API understands it, and it may be useful to call this route to see how a request body is parsed, if at all. + additionalProperties: {} \ No newline at end of file From eef052c1bf9e47af60c5ad2180a5624a6221113e Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 16 Dec 2020 14:09:25 -0500 Subject: [PATCH 12/98] fix: add missing token generation route to write api spec --- public/openapi/write.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/public/openapi/write.yaml b/public/openapi/write.yaml index b827cb127c..bb113bf7ef 100644 --- a/public/openapi/write.yaml +++ b/public/openapi/write.yaml @@ -58,6 +58,8 @@ paths: $ref: 'write/users/uid/follow.yaml' /users/{uid}/ban: $ref: 'write/users/uid/ban.yaml' + /users/{uid}/tokens: + $ref: 'write/users/uid/tokens.yaml' /users/{uid}/tokens/{token}: $ref: 'write/users/uid/tokens/token.yaml' /users/{uid}/sessions/{uuid}: From 7fc329de2f69a614ba3db50caa66e7a120e44b27 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 16 Dec 2020 14:12:15 -0500 Subject: [PATCH 13/98] fix: modify backreference test to not check router.all() calls --- test/api.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/api.js b/test/api.js index fefcdb92e6..6384481903 100644 --- a/test/api.js +++ b/test/api.js @@ -199,7 +199,7 @@ describe('API', async () => { }); const exclusionPrefixes = ['/api/admin/plugins']; paths = paths.filter(function filterExclusions(path) { - return !exclusionPrefixes.some(prefix => path.path.startsWith(prefix)); + return path.method !== '_all' && !exclusionPrefixes.some(prefix => path.path.startsWith(prefix)); }); From d6de925348e227a0f01e880143a30b6c92019288 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 16 Dec 2020 20:09:28 -0500 Subject: [PATCH 14/98] feat: add missing schema for category update and deletion Deleted CategoryObj component, since CategoryObject is used more --- .../components/schemas/CategoryObj.yaml | 72 ------------------- .../components/schemas/CategoryObject.yaml | 14 ++-- public/openapi/read/categories.yaml | 2 +- public/openapi/write.yaml | 2 + public/openapi/write/categories.yaml | 11 ++- public/openapi/write/categories/cid.yaml | 68 ++++++++++++++++++ public/openapi/write/ping.yaml | 1 + 7 files changed, 89 insertions(+), 81 deletions(-) delete mode 100644 public/openapi/components/schemas/CategoryObj.yaml create mode 100644 public/openapi/write/categories/cid.yaml diff --git a/public/openapi/components/schemas/CategoryObj.yaml b/public/openapi/components/schemas/CategoryObj.yaml deleted file mode 100644 index 7c3ac6a83f..0000000000 --- a/public/openapi/components/schemas/CategoryObj.yaml +++ /dev/null @@ -1,72 +0,0 @@ -CategoryObj: - properties: - cid: - type: number - example: 1 - name: - type: string - example: My New Category - description: - type: string - example: Lorem ipsum, dolor sit amet - descriptionParsed: - type: string - example: Lorem ipsum, dolor sit amet - icon: - type: string - example: bullhorn - bgColor: - type: string - example: '#ffffff' - color: - type: string - example: '#000000' - slug: - type: string - example: 1/my-new-category - parentCid: - type: number - example: 0 - topic_count: - type: number - example: 0 - post_count: - type: number - example: 0 - disabled: - type: number - example: 0 - order: - type: number - example: 5 - link: - type: number - example: 'https://example.org' - numRecentReplies: - type: number - example: 1 - class: - type: string - example: col-md-3 col-xs-6 - imageClass: - type: string - example: cover - isSection: - type: number - example: 0 - totalPostCount: - type: number - example: 0 - totalTopicCount: - type: number - example: 0 - tagWhitelist: - type: array - example: - - some-tag - - another-tag - unread-class: - type: string - backgroundImage: - type: string - example: '/assets/images/covers/Circuit1.png' \ No newline at end of file diff --git a/public/openapi/components/schemas/CategoryObject.yaml b/public/openapi/components/schemas/CategoryObject.yaml index c19a5f192c..bbeca1cf9f 100644 --- a/public/openapi/components/schemas/CategoryObject.yaml +++ b/public/openapi/components/schemas/CategoryObject.yaml @@ -57,6 +57,12 @@ CategoryObject: description: The `background-position` of the category background image, if one is set isSection: type: number + minTags: + type: number + description: Minimum tags per topic in this category + maxTags: + type: number + description: Maximum tags per topic in this category postQueue: type: number totalPostCount: @@ -64,10 +70,4 @@ CategoryObject: description: The number of posts in the category totalTopicCount: type: number - description: The number of topics in the category - minTags: - type: number - description: Minimum tags per topic in this category - maxTags: - type: number - description: Maximum tags per topic in this category \ No newline at end of file + description: The number of topics in the category \ No newline at end of file diff --git a/public/openapi/read/categories.yaml b/public/openapi/read/categories.yaml index 723501a410..36f7b3b797 100644 --- a/public/openapi/read/categories.yaml +++ b/public/openapi/read/categories.yaml @@ -13,7 +13,7 @@ get: Subcategories are also returned, nested under a category's `children` property. responses: "200": - description: A list of category objectscurrently available to the accessing user + description: A list of category objects currently available to the accessing user content: application/json: schema: diff --git a/public/openapi/write.yaml b/public/openapi/write.yaml index bb113bf7ef..2405718f33 100644 --- a/public/openapi/write.yaml +++ b/public/openapi/write.yaml @@ -70,6 +70,8 @@ paths: $ref: 'write/users/uid/invites/groups.yaml' /categories/: $ref: 'write/categories.yaml' + /categories/{cid}: + $ref: 'write/categories/cid.yaml' /groups/: $ref: 'write/groups.yaml' /groups/{slug}: diff --git a/public/openapi/write/categories.yaml b/public/openapi/write/categories.yaml index dc90d8f41f..5c26b53633 100644 --- a/public/openapi/write/categories.yaml +++ b/public/openapi/write/categories.yaml @@ -54,4 +54,13 @@ post: status: $ref: ../components/schemas/Status.yaml#/Status response: - $ref: ../components/schemas/CategoryObj.yaml#/CategoryObj \ No newline at end of file + allOf: + - $ref: ../components/schemas/CategoryObject.yaml#/CategoryObject + - type: object + properties: + tagWhitelist: + type: array + items: + type: string + unread-class: + type: string \ No newline at end of file diff --git a/public/openapi/write/categories/cid.yaml b/public/openapi/write/categories/cid.yaml new file mode 100644 index 0000000000..a719178060 --- /dev/null +++ b/public/openapi/write/categories/cid.yaml @@ -0,0 +1,68 @@ +put: + tags: + - topics + summary: update a category + description: This operation updates an existing category. + parameters: + - in: path + name: cid + schema: + type: number + required: true + description: a valid category id + example: 1 + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: {} + additionalProperties: {} + responses: + '200': + description: category successfully updated + content: + application/json: + schema: + type: object + properties: + status: + $ref: ../../components/schemas/Status.yaml#/Status + response: + allOf: + - $ref: ../../components/schemas/CategoryObject.yaml#/CategoryObject + - type: object + properties: + tagWhitelist: + type: array + items: + type: string + unread-class: + type: string +delete: + tags: + - topics + summary: delete a category + description: This operation deletes and purges a category and all of its topics and posts (careful, there is no confirmation!) + parameters: + - in: path + name: cid + schema: + type: number + required: true + description: a valid category id + example: 1 + responses: + '200': + description: Category successfully deleted + content: + application/json: + schema: + type: object + properties: + status: + $ref: ../../components/schemas/Status.yaml#/Status + response: + type: object + properties: {} \ No newline at end of file diff --git a/public/openapi/write/ping.yaml b/public/openapi/write/ping.yaml index 8e2b0c7540..67d3dd62ba 100644 --- a/public/openapi/write/ping.yaml +++ b/public/openapi/write/ping.yaml @@ -33,6 +33,7 @@ post: application/json: schema: type: object + properties: {} additionalProperties: {} responses: '200': From a96293574363d8e4f5b14a5909da3cb58157492a Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 16 Dec 2020 20:10:15 -0500 Subject: [PATCH 15/98] fix: bad error message for request body api test --- test/api.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/test/api.js b/test/api.js index 6384481903..c2a78641be 100644 --- a/test/api.js +++ b/test/api.js @@ -235,6 +235,7 @@ describe('API', async () => { const qs = {}; Object.keys(context).forEach((_method) => { + // Only test GET routes in the Read API if (api.info.title === 'NodeBB Read API' && _method !== 'get') { return; } @@ -279,17 +280,18 @@ describe('API', async () => { it('should contain a valid request body (if present) with application/json or multipart/form-data type if POST/PUT/DELETE', () => { if (['post', 'put', 'delete'].includes(method) && context[method].hasOwnProperty('requestBody')) { - assert(context[method].requestBody); - assert(context[method].requestBody.content); + const failMessage = `${method.toUpperCase()} ${path} has a malformed request body`; + assert(context[method].requestBody, failMessage); + assert(context[method].requestBody.content, failMessage); if (context[method].requestBody.content.hasOwnProperty('application/json')) { - assert(context[method].requestBody.content['application/json']); - assert(context[method].requestBody.content['application/json'].schema); - assert(context[method].requestBody.content['application/json'].schema.properties); + assert(context[method].requestBody.content['application/json'], failMessage); + assert(context[method].requestBody.content['application/json'].schema, failMessage); + assert(context[method].requestBody.content['application/json'].schema.properties, failMessage); } else if (context[method].requestBody.content.hasOwnProperty('multipart/form-data')) { - assert(context[method].requestBody.content['multipart/form-data']); - assert(context[method].requestBody.content['multipart/form-data'].schema); - assert(context[method].requestBody.content['multipart/form-data'].schema.properties); + assert(context[method].requestBody.content['multipart/form-data'], failMessage); + assert(context[method].requestBody.content['multipart/form-data'].schema, failMessage); + assert(context[method].requestBody.content['multipart/form-data'].schema.properties, failMessage); } } }); From 990f107736985f880203bb1aa6c947c97b0d6fbc Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 16 Dec 2020 20:12:19 -0500 Subject: [PATCH 16/98] fix: broken tests from last round of fixes --- public/openapi/write/categories/cid.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/openapi/write/categories/cid.yaml b/public/openapi/write/categories/cid.yaml index a719178060..af7790088c 100644 --- a/public/openapi/write/categories/cid.yaml +++ b/public/openapi/write/categories/cid.yaml @@ -10,7 +10,7 @@ put: type: number required: true description: a valid category id - example: 1 + example: 2 requestBody: required: true content: @@ -52,7 +52,7 @@ delete: type: number required: true description: a valid category id - example: 1 + example: 2 responses: '200': description: Category successfully deleted From 8e5a413e4e2b46b5ac8ee5de5fe4e0418ba51fd2 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 16 Dec 2020 20:14:36 -0500 Subject: [PATCH 17/98] chore: minor reordering of lines --- public/openapi/write.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/public/openapi/write.yaml b/public/openapi/write.yaml index 2405718f33..f7a5190927 100644 --- a/public/openapi/write.yaml +++ b/public/openapi/write.yaml @@ -68,16 +68,16 @@ paths: $ref: 'write/users/uid/invites.yaml' /users/{uid}/invites/groups: $ref: 'write/users/uid/invites/groups.yaml' - /categories/: - $ref: 'write/categories.yaml' - /categories/{cid}: - $ref: 'write/categories/cid.yaml' /groups/: $ref: 'write/groups.yaml' /groups/{slug}: $ref: 'write/groups/slug.yaml' /groups/{slug}/membership/{uid}: $ref: 'write/groups/slug/membership/uid.yaml' + /categories/: + $ref: 'write/categories.yaml' + /categories/{cid}: + $ref: 'write/categories/cid.yaml' /topics: $ref: 'write/topics.yaml' /topics/{tid}: From c079051b19e1de5d54bf25f3e57de5bfe724a5cc Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 16 Dec 2020 20:17:24 -0500 Subject: [PATCH 18/98] fix: errors in write-api schema --- public/openapi/write.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/openapi/write.yaml b/public/openapi/write.yaml index f7a5190927..ab6f8753d9 100644 --- a/public/openapi/write.yaml +++ b/public/openapi/write.yaml @@ -78,7 +78,7 @@ paths: $ref: 'write/categories.yaml' /categories/{cid}: $ref: 'write/categories/cid.yaml' - /topics: + /topics/: $ref: 'write/topics.yaml' /topics/{tid}: $ref: 'write/topics/tid.yaml' @@ -106,5 +106,5 @@ paths: $ref: 'write/posts/pid/bookmark.yaml' /admin/settings/{setting}: $ref: 'write/admin/settings/setting.yaml' - /files: + /files/: $ref: 'write/files.yaml' \ No newline at end of file From 438fa5c88f92f03d7b1e0d7d4cce5621ea3ff413 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Thu, 17 Dec 2020 14:01:08 -0500 Subject: [PATCH 19/98] fix: send fewer items to client-side for ACP settings/email page --- src/controllers/admin/settings.js | 2 +- src/views/admin/settings/email.tpl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/controllers/admin/settings.js b/src/controllers/admin/settings.js index e49a4755ae..46bca36835 100644 --- a/src/controllers/admin/settings.js +++ b/src/controllers/admin/settings.js @@ -22,7 +22,7 @@ settingsController.email = async (req, res) => { res.render('admin/settings/email', { emails: emails, - sendable: emails.filter(e => !e.path.includes('_plaintext') && !e.path.includes('partials')), + sendable: emails.filter(e => !e.path.includes('_plaintext') && !e.path.includes('partials')).map(tpl => tpl.path), services: emailer.listServices(), }); }; diff --git a/src/views/admin/settings/email.tpl b/src/views/admin/settings/email.tpl index 3e54b08e1f..6b856526ef 100644 --- a/src/views/admin/settings/email.tpl +++ b/src/views/admin/settings/email.tpl @@ -121,7 +121,7 @@ From 9de35ec5a39f278bf2046d51234f61c5da73a447 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Thu, 17 Dec 2020 14:09:48 -0500 Subject: [PATCH 20/98] feat: add missing schemas for various ACP settings routes --- public/openapi/read.yaml | 12 +++++- public/openapi/read/admin.yaml | 19 ++++++++ public/openapi/read/admin/settings/email.yaml | 43 +++++++++++++++++++ public/openapi/read/admin/settings/post.yaml | 18 ++++++++ public/openapi/read/admin/settings/user.yaml | 25 +++++++++++ 5 files changed, 115 insertions(+), 2 deletions(-) create mode 100644 public/openapi/read/admin.yaml create mode 100644 public/openapi/read/admin/settings/email.yaml create mode 100644 public/openapi/read/admin/settings/post.yaml create mode 100644 public/openapi/read/admin/settings/user.yaml diff --git a/public/openapi/read.yaml b/public/openapi/read.yaml index f60727f0e9..a449b0e9c8 100644 --- a/public/openapi/read.yaml +++ b/public/openapi/read.yaml @@ -57,8 +57,12 @@ tags: paths: /api/: $ref: 'read/index.yaml' + /api/admin: + $ref: 'read/admin.yaml' /api/admin/dashboard: $ref: 'read/admin/dashboard.yaml' + "/api/admin/settings/{term}": + $ref: 'read/admin/settings/term.yaml' /api/admin/settings/languages: $ref: 'read/admin/settings/languages.yaml' /api/admin/settings/navigation: @@ -67,6 +71,12 @@ paths: $ref: 'read/admin/settings/homepage.yaml' /api/admin/settings/social: $ref: 'read/admin/settings/social.yaml' + /api/admin/settings/email: + $ref: 'read/admin/settings/email.yaml' + /api/admin/settings/user: + $ref: 'read/admin/settings/user.yaml' + /api/admin/settings/post: + $ref: 'read/admin/settings/post.yaml' /api/admin/manage/categories: $ref: 'read/admin/manage/categories.yaml' "/api/admin/manage/categories/{category_id}": @@ -91,8 +101,6 @@ paths: $ref: 'read/admin/manage/uploads.yaml' /api/admin/manage/digest: $ref: 'read/admin/manage/digest.yaml' - "/api/admin/settings/{term}": - $ref: 'read/admin/settings/term.yaml' "/api/admin/appearance/{term}": $ref: 'read/admin/appearance/term.yaml' /api/admin/extend/plugins: diff --git a/public/openapi/read/admin.yaml b/public/openapi/read/admin.yaml new file mode 100644 index 0000000000..27ec1b9d93 --- /dev/null +++ b/public/openapi/read/admin.yaml @@ -0,0 +1,19 @@ +get: + tags: + - admin + summary: Get administrative index + description: | + Internally, NodeBB will redirect you to a different page based on your privilege levels. + + The default is "dashboard" for superadmins and those with the "dashboard" privilege. If the requesting user is neither, then they will be redirected to a page that they have privileges to view (e.g. `/categories`, `/privileges`, `/users`, or `/settings/general`). + + Failing that, the request will be denied. + responses: + "200": + description: | + A JSON object containing data for the default admin index. + content: + application/json: + schema: + properties: {} + additionalProperties: {} \ No newline at end of file diff --git a/public/openapi/read/admin/settings/email.yaml b/public/openapi/read/admin/settings/email.yaml new file mode 100644 index 0000000000..e2d9b76257 --- /dev/null +++ b/public/openapi/read/admin/settings/email.yaml @@ -0,0 +1,43 @@ +get: + tags: + - admin + summary: Get emailer settings + responses: + "200": + description: "" + content: + application/json: + schema: + allOf: + - type: object + properties: + emails: + type: array + items: + type: object + properties: + path: + type: string + description: The name of the email template + fullpath: + type: string + description: Full system path to the email template + text: + type: string + description: Customized email template text, if applicable, otherwise identical to `original` + original: + type: string + description: The email template text as provided by NodeBB core + isCustom: + type: boolean + sendable: + type: array + items: + type: string + description: The name of the email template + services: + type: array + items: + type: string + description: A list of email services which can be used to send emails on behalf of NodeBB + - $ref: ../../../components/schemas/CommonProps.yaml#/CommonProps \ No newline at end of file diff --git a/public/openapi/read/admin/settings/post.yaml b/public/openapi/read/admin/settings/post.yaml new file mode 100644 index 0000000000..f8273c8b8c --- /dev/null +++ b/public/openapi/read/admin/settings/post.yaml @@ -0,0 +1,18 @@ +get: + tags: + - admin + summary: Get post settings + responses: + "200": + description: "" + content: + application/json: + schema: + allOf: + - type: object + properties: + groupsExemptFromPostQueue: + type: array + items: + $ref: ../../../components/schemas/GroupObject.yaml#/GroupDataObject + - $ref: ../../../components/schemas/CommonProps.yaml#/CommonProps \ No newline at end of file diff --git a/public/openapi/read/admin/settings/user.yaml b/public/openapi/read/admin/settings/user.yaml new file mode 100644 index 0000000000..6dccccedee --- /dev/null +++ b/public/openapi/read/admin/settings/user.yaml @@ -0,0 +1,25 @@ +get: + tags: + - admin + summary: Get user settings + responses: + "200": + description: "" + content: + application/json: + schema: + allOf: + - type: object + properties: + notificationSettings: + type: array + items: + type: object + properties: + name: + type: string + description: The notification type + label: + type: string + description: The language key for the notification type (for localisation client-side) + - $ref: ../../../components/schemas/CommonProps.yaml#/CommonProps \ No newline at end of file From 600807fbe16518b1c7fd61b9d51366c6a60ebbe2 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Thu, 17 Dec 2020 14:14:07 -0500 Subject: [PATCH 21/98] fix: don't return deleted: 0 for ephemeral groups --- src/groups/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/groups/index.js b/src/groups/index.js index 9cb83e7ab4..d8392623da 100644 --- a/src/groups/index.js +++ b/src/groups/index.js @@ -39,7 +39,6 @@ Groups.getEphemeralGroup = function (groupName) { name: groupName, slug: slugify(groupName), description: '', - deleted: 0, hidden: 0, system: 1, }; From b9a61d2d453a689b318847efbbb0f029c8141486 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Thu, 17 Dec 2020 14:17:01 -0500 Subject: [PATCH 22/98] fix: api tests --- public/openapi/read/uid/uid.yaml | 2 +- test/api.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/public/openapi/read/uid/uid.yaml b/public/openapi/read/uid/uid.yaml index 6552e78b9e..dccb23e01a 100644 --- a/public/openapi/read/uid/uid.yaml +++ b/public/openapi/read/uid/uid.yaml @@ -8,7 +8,7 @@ get: For example, to go to `uid` 15's list of topics made, you can navigate to `/api/uid/15/topics`, which will send you to the appropriate canonical URL for that user's topics. parameters: - - name: uid + - name: uid* in: path required: true schema: diff --git a/test/api.js b/test/api.js index c2a78641be..7a00ae3d29 100644 --- a/test/api.js +++ b/test/api.js @@ -246,7 +246,7 @@ describe('API', async () => { return; } - const names = (path.match(/{[\w\-_]+}?/g) || []).map(match => match.slice(1, -1)); + const names = (path.match(/{[\w\-_*]+}?/g) || []).map(match => match.slice(1, -1)); assert(context[method].parameters.map(param => (param.in === 'path' ? param.name : null)).filter(Boolean).every(name => names.includes(name)), `${method.toUpperCase()} ${path} has parameter(s) in path that are not defined in schema`); }); From 14c51e3c60cb56934f14a81ce555c2cb280b9f6d Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Thu, 17 Dec 2020 15:02:09 -0500 Subject: [PATCH 23/98] feat: add registration/complete route, fix some other tests --- public/openapi/read.yaml | 10 ++++++--- public/openapi/read/register.yaml | 26 +--------------------- public/openapi/read/register/complete.yaml | 23 +++++++++++++++++++ test/api.js | 6 +++++ 4 files changed, 37 insertions(+), 28 deletions(-) create mode 100644 public/openapi/read/register/complete.yaml diff --git a/public/openapi/read.yaml b/public/openapi/read.yaml index a449b0e9c8..f4e684e1e2 100644 --- a/public/openapi/read.yaml +++ b/public/openapi/read.yaml @@ -123,6 +123,8 @@ paths: $ref: 'read/admin/advanced/errors/export.yaml' /api/admin/advanced/cache: $ref: 'read/admin/advanced/cache.yaml' + /api/admin/advanced/cache/dump: + $ref: 'read/admin/advanced/cache.yaml' # todo: @baris to fix, see gh#9122 /api/admin/development/logger: $ref: 'read/admin/development/logger.yaml' /api/admin/development/info: @@ -165,11 +167,11 @@ paths: $ref: 'read/user/uid/userslug/export/uploads.yaml' "/api/user/uid/{userslug}/export/profile": $ref: 'read/user/uid/userslug/export/profile.yaml' - "/api/post/pid/{id}": + "/api/{type}/pid/{id}": $ref: 'read/post/pid/id.yaml' - "/api/topic/tid/{id}": + "/api/{type}/tid/{id}": $ref: 'read/topic/tid/id.yaml' - "/api/category/cid/{id}": + "/api/{type}/cid/{id}": $ref: 'read/category/cid/id.yaml' /api/categories: $ref: 'read/categories.yaml' @@ -197,6 +199,8 @@ paths: $ref: 'read/login.yaml' /api/register: $ref: 'read/register.yaml' + /api/register/complete: + $ref: 'read/register/complete.yaml' /api/search: $ref: 'read/search.yaml' "/api/reset": diff --git a/public/openapi/read/register.yaml b/public/openapi/read/register.yaml index 199b496e03..74001416cc 100644 --- a/public/openapi/read/register.yaml +++ b/public/openapi/read/register.yaml @@ -54,28 +54,4 @@ get: title: type: string - $ref: ../components/schemas/Breadcrumbs.yaml#/Breadcrumbs - - $ref: ../components/schemas/CommonProps.yaml#/CommonProps - # /api/register/complete: - # get: - # tags: - # - authentication - # summary: /api/register/complete - # responses: - # "200": - # description: "" - # content: - # application/json: - # schema: - # allOf: - # - type: object - # properties: - # title: - # type: string - # errors: - # type: array - # items: {} - # sections: - # type: array - # items: - # type: string - # - $ref: ../components/schemas/CommonProps.yaml#/CommonProps \ No newline at end of file + - $ref: ../components/schemas/CommonProps.yaml#/CommonProps \ No newline at end of file diff --git a/public/openapi/read/register/complete.yaml b/public/openapi/read/register/complete.yaml new file mode 100644 index 0000000000..fe6471c850 --- /dev/null +++ b/public/openapi/read/register/complete.yaml @@ -0,0 +1,23 @@ +get: + tags: + - authentication + summary: /api/register/complete + responses: + "200": + description: "" + content: + application/json: + schema: + allOf: + - type: object + properties: + title: + type: string + errors: + type: array + items: {} + sections: + type: array + items: + type: string + - $ref: ../../components/schemas/CommonProps.yaml#/CommonProps \ No newline at end of file diff --git a/test/api.js b/test/api.js index 7a00ae3d29..cd9b7e0141 100644 --- a/test/api.js +++ b/test/api.js @@ -212,6 +212,12 @@ describe('API', async () => { schema = writeApi; pathObj.path = pathObj.path.replace('/api/v3', ''); } + + // Don't check non-GET routes in Read API + if (schema === readApi && pathObj.method !== 'get') { + return; + } + const normalizedPath = pathObj.path.replace(/\/:([^\\/]+)/g, '/{$1}').replace(/\?/g, ''); assert(schema.paths.hasOwnProperty(normalizedPath)); }); From cb32e32ae307d0111597220a5551e5a6997cd2dd Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Thu, 17 Dec 2020 21:54:38 -0500 Subject: [PATCH 24/98] feat: fix more tests, add more routes, update api test suite --- public/openapi/read.yaml | 2 +- public/openapi/read/admin/advanced/cache.yaml | 9 +++++ public/openapi/read/category/cid/id.yaml | 6 ++++ public/openapi/read/post/pid/id.yaml | 6 ++++ public/openapi/read/register/complete.yaml | 9 ++++- public/openapi/read/topic/tid/id.yaml | 6 ++++ test/api.js | 35 ++++++++++++++++--- 7 files changed, 66 insertions(+), 7 deletions(-) diff --git a/public/openapi/read.yaml b/public/openapi/read.yaml index f4e684e1e2..f6e6312f37 100644 --- a/public/openapi/read.yaml +++ b/public/openapi/read.yaml @@ -124,7 +124,7 @@ paths: /api/admin/advanced/cache: $ref: 'read/admin/advanced/cache.yaml' /api/admin/advanced/cache/dump: - $ref: 'read/admin/advanced/cache.yaml' # todo: @baris to fix, see gh#9122 + $ref: 'read/admin/advanced/cache/dump.yaml' /api/admin/development/logger: $ref: 'read/admin/development/logger.yaml' /api/admin/development/info: diff --git a/public/openapi/read/admin/advanced/cache.yaml b/public/openapi/read/admin/advanced/cache.yaml index 06d7890971..c6daf4fbea 100644 --- a/public/openapi/read/admin/advanced/cache.yaml +++ b/public/openapi/read/admin/advanced/cache.yaml @@ -2,6 +2,15 @@ get: tags: - admin summary: Get system cache info + parameters: + - in: query + name: name + schema: + type: string + enum: ['post', 'object', 'group', 'local'] + required: false + description: Specify cache to dump if calling `/dump` + example: 'post' responses: "200": description: "" diff --git a/public/openapi/read/category/cid/id.yaml b/public/openapi/read/category/cid/id.yaml index e70f774b07..298147dea3 100644 --- a/public/openapi/read/category/cid/id.yaml +++ b/public/openapi/read/category/cid/id.yaml @@ -3,6 +3,12 @@ get: - shorthand summary: Get category data parameters: + - name: type + in: path + required: true + schema: + type: string + example: category - name: id in: path required: true diff --git a/public/openapi/read/post/pid/id.yaml b/public/openapi/read/post/pid/id.yaml index 527ee4ec29..58dd822f30 100644 --- a/public/openapi/read/post/pid/id.yaml +++ b/public/openapi/read/post/pid/id.yaml @@ -3,6 +3,12 @@ get: - shorthand summary: Get post data parameters: + - name: type + in: path + required: true + schema: + type: string + example: post - name: id in: path required: true diff --git a/public/openapi/read/register/complete.yaml b/public/openapi/read/register/complete.yaml index fe6471c850..b0308c7094 100644 --- a/public/openapi/read/register/complete.yaml +++ b/public/openapi/read/register/complete.yaml @@ -3,8 +3,15 @@ get: - authentication summary: /api/register/complete responses: + "302": + description: If there are no additional registration steps to complete, then the user is redirected back to the registration page (`/register`) + headers: + Location: + schema: + type: string + example: /register "200": - description: "" + description: '' content: application/json: schema: diff --git a/public/openapi/read/topic/tid/id.yaml b/public/openapi/read/topic/tid/id.yaml index 5305a99c7f..dd77ad2670 100644 --- a/public/openapi/read/topic/tid/id.yaml +++ b/public/openapi/read/topic/tid/id.yaml @@ -3,6 +3,12 @@ get: - shorthand summary: Get topic data parameters: + - name: type + in: path + required: true + schema: + type: string + example: topic - name: id in: path required: true diff --git a/test/api.js b/test/api.js index cd9b7e0141..3a9c82f4ee 100644 --- a/test/api.js +++ b/test/api.js @@ -324,6 +324,9 @@ describe('API', async () => { method: method, jar: !unauthenticatedRoutes.includes(path) ? jar : undefined, json: true, + followRedirect: false, // all responses are significant (e.g. 302) + simple: false, // don't throw on non-200 (e.g. 302) + resolveWithFullResponse: true, // send full request back (to check statusCode) headers: headers, qs: qs, body: body, @@ -343,17 +346,39 @@ describe('API', async () => { } }); + it('response status code should match one of the schema defined responses', () => { + // HACK: allow HTTP 418 I am a teapot, for now 👇 + assert(context[method].responses.hasOwnProperty('418') || Object.keys(context[method].responses).includes(String(response.statusCode)), `${method.toUpperCase()} ${path} sent back unexpected HTTP status code: ${response.statusCode}`); + }); + // Recursively iterate through schema properties, comparing type - it('response should match schema definition', () => { - const has200 = context[method].responses['200']; - if (!has200) { + it('response body should match schema definition', () => { + const http302 = context[method].responses['302']; + if (http302 && response.statusCode === 302) { + // Compare headers instead + const expectedHeaders = Object.keys(http302.headers).reduce((memo, name) => { + memo[name] = http302.headers[name].schema.example; + return memo; + }, {}); + + for (const header in expectedHeaders) { + if (expectedHeaders.hasOwnProperty(header)) { + assert(response.headers[header.toLowerCase()]); + assert(response.headers[header.toLowerCase()] === expectedHeaders[header]); + } + } return; } - const hasJSON = has200.content && has200.content['application/json']; + const http200 = context[method].responses['200']; + if (!http200) { + return; + } + + const hasJSON = http200.content && http200.content['application/json']; if (hasJSON) { schema = context[method].responses['200'].content['application/json'].schema; - compare(schema, response, method.toUpperCase(), path, 'root'); + compare(schema, response.body, method.toUpperCase(), path, 'root'); } // TODO someday: text/csv, binary file type checking? From 9c2de86a6e6a188df95913771ecacfa9f36a1bf7 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Thu, 17 Dec 2020 23:35:37 -0500 Subject: [PATCH 25/98] fix: two more routes --- public/openapi/read.yaml | 2 ++ .../read/admin/advanced/cache/dump.yaml | 23 +++++++++++++++++++ public/openapi/read/confirm/code.yaml | 22 ++++++++++++++++++ public/openapi/read/login.yaml | 2 +- public/openapi/read/register.yaml | 2 +- public/openapi/read/register/complete.yaml | 2 +- test/api.js | 2 +- 7 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 public/openapi/read/admin/advanced/cache/dump.yaml create mode 100644 public/openapi/read/confirm/code.yaml diff --git a/public/openapi/read.yaml b/public/openapi/read.yaml index f6e6312f37..c6e3afcb99 100644 --- a/public/openapi/read.yaml +++ b/public/openapi/read.yaml @@ -201,6 +201,8 @@ paths: $ref: 'read/register.yaml' /api/register/complete: $ref: 'read/register/complete.yaml' + "/api/confirm/{code}": + $ref: 'read/confirm/code.yaml' /api/search: $ref: 'read/search.yaml' "/api/reset": diff --git a/public/openapi/read/admin/advanced/cache/dump.yaml b/public/openapi/read/admin/advanced/cache/dump.yaml new file mode 100644 index 0000000000..2be4e38f9b --- /dev/null +++ b/public/openapi/read/admin/advanced/cache/dump.yaml @@ -0,0 +1,23 @@ +get: + tags: + - admin + summary: Get system cache info + parameters: + - in: query + name: name + schema: + type: string + enum: ['post', 'object', 'group', 'local'] + required: false + description: Specify cache to dump if calling `/dump` + example: 'post' + responses: + "200": + description: "" + content: + application/json: + schema: + type: object + properties: {} + additionalProperties: + description: The type of response is dependent on the database used. Please examine the output. \ No newline at end of file diff --git a/public/openapi/read/confirm/code.yaml b/public/openapi/read/confirm/code.yaml new file mode 100644 index 0000000000..d066ede5f9 --- /dev/null +++ b/public/openapi/read/confirm/code.yaml @@ -0,0 +1,22 @@ +get: + tags: + - authentication + summary: Verify an email address + responses: + "200": + description: Email address verified, or confirmation code was incorrect + content: + application/json: + schema: + allOf: + - type: object + properties: + title: + type: string + error: + type: string + description: Translation key for client-side localisation + required: + - title + - $ref: ../../components/schemas/Breadcrumbs.yaml#/Breadcrumbs + - $ref: ../../components/schemas/CommonProps.yaml#/CommonProps \ No newline at end of file diff --git a/public/openapi/read/login.yaml b/public/openapi/read/login.yaml index a480ef74b9..4e5b3944a9 100644 --- a/public/openapi/read/login.yaml +++ b/public/openapi/read/login.yaml @@ -1,7 +1,7 @@ get: tags: - authentication - summary: /api/login + summary: Log in a user responses: "200": description: "" diff --git a/public/openapi/read/register.yaml b/public/openapi/read/register.yaml index 74001416cc..b4ec6d4fbc 100644 --- a/public/openapi/read/register.yaml +++ b/public/openapi/read/register.yaml @@ -1,7 +1,7 @@ get: tags: - authentication - summary: /api/register + summary: Register a new user responses: "200": description: "" diff --git a/public/openapi/read/register/complete.yaml b/public/openapi/read/register/complete.yaml index b0308c7094..d6f8e48a35 100644 --- a/public/openapi/read/register/complete.yaml +++ b/public/openapi/read/register/complete.yaml @@ -1,7 +1,7 @@ get: tags: - authentication - summary: /api/register/complete + summary: Complete a user's registration responses: "302": description: If there are no additional registration steps to complete, then the user is redirected back to the registration page (`/register`) diff --git a/test/api.js b/test/api.js index 3a9c82f4ee..3154cd0c14 100644 --- a/test/api.js +++ b/test/api.js @@ -197,7 +197,7 @@ describe('API', async () => { pathObj.path = pathObj.path.replace(/\/:([^\\/]+)/g, '/{$1}'); return pathObj; }); - const exclusionPrefixes = ['/api/admin/plugins']; + const exclusionPrefixes = ['/api/admin/plugins', '/api/compose']; paths = paths.filter(function filterExclusions(path) { return path.method !== '_all' && !exclusionPrefixes.some(prefix => path.path.startsWith(prefix)); }); From f416dc177abe3bc8f7cc40c7dc1e18de743fd6af Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 18 Dec 2020 11:23:16 -0500 Subject: [PATCH 26/98] fix: all tests, wrap up work --- public/openapi/read.yaml | 12 +++++++- .../read/admin/groups/groupname/csv.yaml | 6 ++++ public/openapi/read/confirm/code.yaml | 1 - public/openapi/read/outgoing.yaml | 29 +++++++++++++++++++ public/openapi/read/tos.yaml | 18 ++++++++++++ test/api.js | 14 +++++---- 6 files changed, 72 insertions(+), 8 deletions(-) create mode 100644 public/openapi/read/outgoing.yaml create mode 100644 public/openapi/read/tos.yaml diff --git a/public/openapi/read.yaml b/public/openapi/read.yaml index c6e3afcb99..f33624b521 100644 --- a/public/openapi/read.yaml +++ b/public/openapi/read.yaml @@ -54,6 +54,8 @@ tags: description: Disparate method of categorizing topics - name: shorthand description: Convenience and utility routes for accessing other part of the API + - name: other + description: Other one-off routes that do not fit in a section of their own paths: /api/: $ref: 'read/index.yaml' @@ -177,6 +179,8 @@ paths: $ref: 'read/categories.yaml' "/api/categories/{cid}/moderators": $ref: 'read/categories/cid/moderators.yaml' + "/api/topic/{topic_id}/{slug}": + $ref: 'read/topic/topic_id.yaml' "/api/topic/{topic_id}/{slug}/{post_index}": $ref: 'read/topic/topic_id.yaml' /api/recent: @@ -203,6 +207,8 @@ paths: $ref: 'read/register/complete.yaml' "/api/confirm/{code}": $ref: 'read/confirm/code.yaml' + /api/tos: + $ref: 'read/tos.yaml' /api/search: $ref: 'read/search.yaml' "/api/reset": @@ -231,6 +237,8 @@ paths: $ref: 'read/popular.yaml' /api/top: $ref: 'read/top.yaml' + "/api/category/{category_id}/{slug}": + $ref: 'read/category/category_id.yaml' "/api/category/{category_id}/{slug}/{topic_index}": $ref: 'read/category/category_id.yaml' /api/self: @@ -300,4 +308,6 @@ paths: "/api/groups/{slug}": $ref: 'read/groups/slug.yaml' "/api/groups/{slug}/members": - $ref: 'read/groups/slug/members.yaml' \ No newline at end of file + $ref: 'read/groups/slug/members.yaml' + /api/outgoing: + $ref: 'read/outgoing.yaml' \ No newline at end of file diff --git a/public/openapi/read/admin/groups/groupname/csv.yaml b/public/openapi/read/admin/groups/groupname/csv.yaml index 43a5bf8e59..e774a1b4a1 100644 --- a/public/openapi/read/admin/groups/groupname/csv.yaml +++ b/public/openapi/read/admin/groups/groupname/csv.yaml @@ -9,6 +9,12 @@ get: type: string required: true example: /admin/manage/groups + - in: path + name: groupname + schema: + type: string + required: true + example: registered-users responses: "200": description: "A CSV file containing all users in the group" diff --git a/public/openapi/read/confirm/code.yaml b/public/openapi/read/confirm/code.yaml index d066ede5f9..c3e75649b8 100644 --- a/public/openapi/read/confirm/code.yaml +++ b/public/openapi/read/confirm/code.yaml @@ -18,5 +18,4 @@ get: description: Translation key for client-side localisation required: - title - - $ref: ../../components/schemas/Breadcrumbs.yaml#/Breadcrumbs - $ref: ../../components/schemas/CommonProps.yaml#/CommonProps \ No newline at end of file diff --git a/public/openapi/read/outgoing.yaml b/public/openapi/read/outgoing.yaml new file mode 100644 index 0000000000..ffde8242bf --- /dev/null +++ b/public/openapi/read/outgoing.yaml @@ -0,0 +1,29 @@ +get: + tags: + - other + summary: Warn before navigating externally + parameters: + - in: query + name: url + schema: + type: string + description: URL of the page to warn the user about + example: https://example.org + description: This route presents a warning to a user notifying them that the page they are about to view is hosted externally. They then have the option of continuing onwards or going back to where they came from. + responses: + "200": + description: Warning page presented + content: + application/json: + schema: + allOf: + - type: object + properties: + outgoing: + type: string + description: Escaped URL of the page to navigate to + title: + description: The page title + type: string + - $ref: ../components/schemas/Breadcrumbs.yaml#/Breadcrumbs + - $ref: ../components/schemas/CommonProps.yaml#/CommonProps \ No newline at end of file diff --git a/public/openapi/read/tos.yaml b/public/openapi/read/tos.yaml new file mode 100644 index 0000000000..1c66f4bdc9 --- /dev/null +++ b/public/openapi/read/tos.yaml @@ -0,0 +1,18 @@ +get: + tags: + - authentication + summary: Get forum terms of service + description: This route allows you to view the forum terms of service. + responses: + "200": + description: Terms of service retrieved. + content: + application/json: + schema: + allOf: + - type: object + properties: + termsOfUse: + type: string + description: Full text of the configured terms of service/terms of use. + - $ref: ../components/schemas/CommonProps.yaml#/CommonProps \ No newline at end of file diff --git a/test/api.js b/test/api.js index 3154cd0c14..8e047336b1 100644 --- a/test/api.js +++ b/test/api.js @@ -100,6 +100,7 @@ describe('API', async () => { }], }); meta.config.allowTopicsThumbnail = 1; + meta.config.termsOfUse = 'I, for one, welcome our new test-drive overlords'; // Create a category const testCategory = await categories.create({ name: 'test' }); @@ -225,8 +226,8 @@ describe('API', async () => { }); }); - // generateTests(readApi, Object.keys(readApi.paths)); - // generateTests(writeApi, Object.keys(writeApi.paths), writeApi.servers[0].url); + generateTests(readApi, Object.keys(readApi.paths)); + generateTests(writeApi, Object.keys(writeApi.paths), writeApi.servers[0].url); function generateTests(api, paths, prefix) { // Iterate through all documented paths, make a call to it, and compare the result body with what is defined in the spec @@ -252,8 +253,9 @@ describe('API', async () => { return; } - const names = (path.match(/{[\w\-_*]+}?/g) || []).map(match => match.slice(1, -1)); - assert(context[method].parameters.map(param => (param.in === 'path' ? param.name : null)).filter(Boolean).every(name => names.includes(name)), `${method.toUpperCase()} ${path} has parameter(s) in path that are not defined in schema`); + const pathParams = (path.match(/{[\w\-_*]+}?/g) || []).map(match => match.slice(1, -1)); + const schemaParams = context[method].parameters.map(param => (param.in === 'path' ? param.name : null)).filter(Boolean); + assert(pathParams.every(param => schemaParams.includes(param)), `${method.toUpperCase()} ${path} has path parameters specified but not defined`); }); it('should have examples when parameters are present', () => { @@ -333,11 +335,11 @@ describe('API', async () => { }); } else if (type === 'form') { response = await new Promise((resolve, reject) => { - helpers.uploadFile(url, pathLib.join(__dirname, './files/test.png'), {}, jar, csrfToken, function (err, res, body) { + helpers.uploadFile(url, pathLib.join(__dirname, './files/test.png'), {}, jar, csrfToken, function (err, res) { if (err) { return reject(err); } - resolve(body); + resolve(res); }); }); } From 6062039de204537ae3a4d4714f76163569ab4cf1 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 18 Dec 2020 11:43:29 -0500 Subject: [PATCH 27/98] fix: .flat() not defined in v10, added debug router to exclusion list --- test/api.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/api.js b/test/api.js index 8e047336b1..06fa145840 100644 --- a/test/api.js +++ b/test/api.js @@ -1,5 +1,6 @@ 'use strict'; +const _ = require('lodash'); const assert = require('assert'); const path = require('path'); const fs = require('fs'); @@ -191,14 +192,14 @@ describe('API', async () => { return null; }); - return paths.flat(); + return _.flatten(paths); }; let paths = buildPaths(webserver.app._router.stack).filter(Boolean).map(function normalize(pathObj) { pathObj.path = pathObj.path.replace(/\/:([^\\/]+)/g, '/{$1}'); return pathObj; }); - const exclusionPrefixes = ['/api/admin/plugins', '/api/compose']; + const exclusionPrefixes = ['/api/admin/plugins', '/api/compose', '/debug']; paths = paths.filter(function filterExclusions(path) { return path.method !== '_all' && !exclusionPrefixes.some(prefix => path.path.startsWith(prefix)); }); From 5f038dffbd4fbe49a043f3b564f2c62048b7cc01 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 18 Dec 2020 11:59:44 -0500 Subject: [PATCH 28/98] test: changed test a bit to see what is going on --- test/api.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/api.js b/test/api.js index 06fa145840..ed60f483c6 100644 --- a/test/api.js +++ b/test/api.js @@ -367,7 +367,7 @@ describe('API', async () => { for (const header in expectedHeaders) { if (expectedHeaders.hasOwnProperty(header)) { assert(response.headers[header.toLowerCase()]); - assert(response.headers[header.toLowerCase()] === expectedHeaders[header]); + assert.strictEqual(response.headers[header.toLowerCase()], expectedHeaders[header]); } } return; From bbd97ccb4888a678c4ff20222c2ba1cc157209dd Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 18 Dec 2020 12:28:32 -0500 Subject: [PATCH 29/98] fix: subfolder handling in tests --- test/api.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/api.js b/test/api.js index ed60f483c6..d444f0337f 100644 --- a/test/api.js +++ b/test/api.js @@ -179,6 +179,11 @@ describe('API', async () => { if (!prefix && !dispatch.route.path.startsWith('/api/')) { return null; } + + if (prefix === nconf.get('relative_path')) { + prefix = ''; + } + return { method: Object.keys(dispatch.route.methods)[0], path: (prefix || '') + dispatch.route.path, @@ -360,7 +365,8 @@ describe('API', async () => { if (http302 && response.statusCode === 302) { // Compare headers instead const expectedHeaders = Object.keys(http302.headers).reduce((memo, name) => { - memo[name] = http302.headers[name].schema.example; + const value = http302.headers[name].schema.example; + memo[name] = value.startsWith(nconf.get('relative_path')) ? value : nconf.get('relative_path') + value; return memo; }, {}); From b369dc888bff0b7dd359a99326c8d35d21dc0a2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Fri, 18 Dec 2020 14:53:21 -0500 Subject: [PATCH 30/98] fix: #9129, event is fired on socket.io --- public/src/sockets.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/src/sockets.js b/public/src/sockets.js index e19e2f08ba..722f6785af 100644 --- a/public/src/sockets.js +++ b/public/src/sockets.js @@ -44,7 +44,7 @@ socket = window.socket; socket.on('disconnect', onDisconnect); - socket.on('reconnect_failed', function () { + socket.io.on('reconnect_failed', function () { // Wait ten times the reconnection delay and then start over setTimeout(socket.connect.bind(socket), parseInt(config.reconnectionDelay, 10) * 10); }); From 713f029dc8455470afc650712b8ae33b8d04b497 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 18 Dec 2020 15:25:37 -0500 Subject: [PATCH 31/98] fix: removing ability to specify deprecated topic 'thumb' on topic creation --- src/topics/create.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/topics/create.js b/src/topics/create.js index aacee91432..c6daa389bd 100644 --- a/src/topics/create.js +++ b/src/topics/create.js @@ -35,9 +35,6 @@ module.exports = function (Topics) { postcount: 0, viewcount: 0, }; - if (data.thumb) { - topicData.thumb = data.thumb; - } const result = await plugins.hooks.fire('filter:topic.create', { topic: topicData, data: data }); topicData = result.topic; await db.setObject('topic:' + topicData.tid, topicData); From 2be396ff6e750c86fe691933f2ace1c2bf9b4b82 Mon Sep 17 00:00:00 2001 From: Peter Jaszkowiak Date: Sat, 5 Dec 2020 14:25:14 -0700 Subject: [PATCH 32/98] fix: email testing and settings change from ACP - changing email SMTP settings wouldn't apply the first time - "Send Test Email" now will report emailer errors in most cases --- public/src/admin/settings/email.js | 1 + src/emailer.js | 36 +++++++----------- src/notifications.js | 6 +-- src/socket.io/admin/email.js | 59 +++++++++++++----------------- src/socket.io/admin/user.js | 11 +++++- src/socket.io/user.js | 3 +- src/user/approval.js | 3 +- src/user/bans.js | 6 +-- src/user/create.js | 4 +- src/user/digest.js | 26 ++++++------- src/user/invite.js | 1 - src/user/profile.js | 3 +- src/user/reset.js | 2 +- test/api.js | 9 +++++ test/socket.io.js | 15 ++++++++ test/user.js | 14 +++++++ 16 files changed, 114 insertions(+), 85 deletions(-) diff --git a/public/src/admin/settings/email.js b/public/src/admin/settings/email.js index 458163f8d7..8f2f7f46ed 100644 --- a/public/src/admin/settings/email.js +++ b/public/src/admin/settings/email.js @@ -22,6 +22,7 @@ define('admin/settings/email', ['ace/ace', 'admin/settings'], function (ace) { $('button[data-action="email.test"]').off('click').on('click', function () { socket.emit('admin.email.test', { template: $('#test-email').val() }, function (err) { if (err) { + console.error(err.message); return app.alertError(err.message); } app.alertSuccess('Test Email Sent'); diff --git a/src/emailer.js b/src/emailer.js index 4b9ae4a575..7a2fcdc46d 100644 --- a/src/emailer.js +++ b/src/emailer.js @@ -37,12 +37,9 @@ Emailer.listServices = () => Object.keys(wellKnownServices); Emailer._defaultPayload = {}; const smtpSettingsChanged = (config) => { - if (!prevConfig) { - prevConfig = meta.config; - } - const settings = [ 'email:smtpTransport:enabled', + 'email:smtpTransport:pool', 'email:smtpTransport:user', 'email:smtpTransport:pass', 'email:smtpTransport:service', @@ -113,8 +110,8 @@ Emailer.getTemplates = async (config) => { }; Emailer.setupFallbackTransport = (config) => { - winston.verbose('[emailer] Setting up SMTP fallback transport'); - // Enable Gmail transport if enabled in ACP + winston.verbose('[emailer] Setting up fallback transport'); + // Enable SMTP transport if enabled in ACP if (parseInt(config['email:smtpTransport:enabled'], 10) === 1) { const smtpOptions = { pool: config['email:smtpTransport:pool'], @@ -177,6 +174,11 @@ Emailer.registerApp = (expressApp) => { Emailer.setupFallbackTransport(meta.config); buildCustomTemplates(meta.config); + // need to shallow clone the config object + // otherwise prevConfig holds reference to meta.config object, + // which is updated before the pubsub handler is called + prevConfig = { ...meta.config }; + // Update default payload if new logo is uploaded pubsub.on('config:update', (config) => { if (config) { @@ -204,8 +206,7 @@ Emailer.registerApp = (expressApp) => { Emailer.send = async (template, uid, params) => { if (!app) { - winston.warn('[emailer] App not ready!'); - return; + throw Error('[emailer] App not ready!'); } const [userData, userSettings] = await Promise.all([ @@ -214,13 +215,13 @@ Emailer.send = async (template, uid, params) => { ]); if (!userData || !userData.email) { - winston.warn('uid : ' + uid + ' has no email, not sending.'); + winston.warn(`uid : ${uid} has no email, not sending "${template}" email.`); return; } const allowedTpls = ['verify_email', 'welcome', 'registration_accepted']; if (meta.config.requireEmailConfirmation && !userData['email:confirmed'] && !allowedTpls.includes(template)) { - winston.warn('uid : ' + uid + ' has not confirmed email, not sending "' + template + '" email.'); + winston.warn(`uid : ${uid} (${userData.email}) has not confirmed email, not sending "${template}" email.`); return; } @@ -229,11 +230,7 @@ Emailer.send = async (template, uid, params) => { params.uid = uid; params.username = userData.username; params.rtl = await translator.translate('[[language:dir]]', userSettings.userLang) === 'rtl'; - try { - await Emailer.sendToEmail(template, userData.email, userSettings.userLang, params); - } catch (err) { - winston.error(err.stack); - } + await Emailer.sendToEmail(template, userData.email, userSettings.userLang, params); }; Emailer.sendToEmail = async (template, email, language, params) => { @@ -313,7 +310,7 @@ Emailer.sendToEmail = async (template, email, language, params) => { } }; -Emailer.sendViaFallback = (data, callback) => { +Emailer.sendViaFallback = async (data) => { // Some minor alterations to the data to conform to nodemailer standard data.text = data.plaintext; delete data.plaintext; @@ -323,12 +320,7 @@ Emailer.sendViaFallback = (data, callback) => { delete data.from_name; winston.verbose('[emailer] Sending email to uid ' + data.uid + ' (' + data.to + ')'); - Emailer.fallbackTransport.sendMail(data, (err) => { - if (err) { - winston.error(err.stack); - } - callback(); - }); + await Emailer.fallbackTransport.sendMail(data); }; Emailer.renderAndTranslate = async (template, params, lang) => { diff --git a/src/notifications.js b/src/notifications.js index df0429c654..b98a987cb8 100644 --- a/src/notifications.js +++ b/src/notifications.js @@ -181,8 +181,8 @@ async function pushToUids(uids, notification) { } body = posts.relativeToAbsolute(body, posts.urlRegex); body = posts.relativeToAbsolute(body, posts.imgRegex); - await async.eachLimit(uids, 3, function (uid, next) { - emailer.send('notification', uid, { + await async.eachLimit(uids, 3, async (uid) => { + await emailer.send('notification', uid, { path: notification.path, notification_url: notification.path.startsWith('http') ? notification.path : nconf.get('url') + notification.path, subject: utils.stripHTMLTags(notification.subject || '[[notifications:new_notification]]'), @@ -190,7 +190,7 @@ async function pushToUids(uids, notification) { body: body, notification: notification, showUnsubscribe: true, - }, next); + }).catch(err => winston.error('[emailer.send] ' + err.stack)); }); } diff --git a/src/socket.io/admin/email.js b/src/socket.io/admin/email.js index 04874675b9..584e5785e4 100644 --- a/src/socket.io/admin/email.js +++ b/src/socket.io/admin/email.js @@ -1,25 +1,24 @@ 'use strict'; -const async = require('async'); const userDigest = require('../../user/digest'); const userEmail = require('../../user/email'); const notifications = require('../../notifications'); const emailer = require('../../emailer'); -const utils = require('../../../public/src/utils'); +const utils = require('../../utils'); const Email = module.exports; -Email.test = function (socket, data, callback) { +Email.test = async function (socket, data) { const payload = { subject: '[[email:test-email.subject]]', }; switch (data.template) { case 'digest': - userDigest.execute({ + await userDigest.execute({ interval: 'alltime', subscribers: [socket.uid], - }, callback); + }); break; case 'banned': @@ -28,42 +27,36 @@ Email.test = function (socket, data, callback) { until: utils.toISOString(Date.now()), reason: 'Test Reason', }); - emailer.send(data.template, socket.uid, payload, callback); + await emailer.send(data.template, socket.uid, payload); break; case 'welcome': - userEmail.sendValidationEmail(socket.uid, { + await userEmail.sendValidationEmail(socket.uid, { force: 1, - }, callback); + }); break; - case 'notification': - async.waterfall([ - function (next) { - notifications.create({ - type: 'test', - bodyShort: '[[email:notif.test.short]]', - bodyLong: '[[email:notif.test.long]]', - nid: 'uid:' + socket.uid + ':test', - path: '/', - from: socket.uid, - }, next); - }, - function (notifObj, next) { - emailer.send('notification', socket.uid, { - path: notifObj.path, - subject: utils.stripHTMLTags(notifObj.subject || '[[notifications:new_notification]]'), - intro: utils.stripHTMLTags(notifObj.bodyShort), - body: notifObj.bodyLong || '', - notification: notifObj, - showUnsubscribe: true, - }, next); - }, - ], callback); - break; + case 'notification': { + const notification = await notifications.create({ + type: 'test', + bodyShort: '[[email:notif.test.short]]', + bodyLong: '[[email:notif.test.long]]', + nid: 'uid:' + socket.uid + ':test', + path: '/', + from: socket.uid, + }); + await emailer.send('notification', socket.uid, { + path: notification.path, + subject: utils.stripHTMLTags(notification.subject || '[[notifications:new_notification]]'), + intro: utils.stripHTMLTags(notification.bodyShort), + body: notification.bodyLong || '', + notification, + showUnsubscribe: true, + }); + } break; default: - emailer.send(data.template, socket.uid, payload, callback); + await emailer.send(data.template, socket.uid, payload); break; } }; diff --git a/src/socket.io/admin/user.js b/src/socket.io/admin/user.js index a0070d7d37..8e1c91a2ab 100644 --- a/src/socket.io/admin/user.js +++ b/src/socket.io/admin/user.js @@ -87,9 +87,18 @@ User.sendValidationEmail = async function (socket, uids) { throw new Error('[[error:email-confirmations-are-disabled]]'); } + const failed = []; + await async.eachLimit(uids, 50, async function (uid) { - await user.email.sendValidationEmail(uid, { force: true }); + await user.email.sendValidationEmail(uid, { force: true }).catch((err) => { + winston.error('[user.create] Validation email failed to send\n[emailer.send] ' + err.stack); + failed.push(uid); + }); }); + + if (failed.length) { + throw Error(`Email sending failed for the following uids, check server logs for more info: ${failed.join(',')}`); + } }; User.sendPasswordResetEmail = async function (socket, uids) { diff --git a/src/socket.io/user.js b/src/socket.io/user.js index ed7466a377..6e7f03321f 100644 --- a/src/socket.io/user.js +++ b/src/socket.io/user.js @@ -1,6 +1,7 @@ 'use strict'; const util = require('util'); +const winston = require('winston'); const sleep = util.promisify(setTimeout); const api = require('../api'); @@ -117,7 +118,7 @@ SocketUser.reset.commit = async function (socket, data) { username: username, date: parsedDate, subject: '[[email:reset.notify.subject]]', - }); + }).catch(err => winston.error('[emailer.send] ' + err.stack)); }; SocketUser.isFollowing = async function (socket, data) { diff --git a/src/user/approval.js b/src/user/approval.js index 507aa7ef1b..94514963e4 100644 --- a/src/user/approval.js +++ b/src/user/approval.js @@ -1,6 +1,7 @@ 'use strict'; const validator = require('validator'); +const winston = require('winston'); const cronJob = require('cron').CronJob; const db = require('../database'); @@ -78,7 +79,7 @@ module.exports = function (User) { subject: '[[email:welcome-to, ' + (meta.config.title || meta.config.browserTitle || 'NodeBB') + ']]', template: 'registration_accepted', uid: uid, - }); + }).catch(err => winston.error('[emailer.send] ' + err.stack)); const total = await db.incrObjectField('registration:queue:approval:times', 'totalTime', Math.floor((Date.now() - creation_time) / 60000)); const counter = await db.incrObjectField('registration:queue:approval:times', 'counter', 1); await db.setObjectField('registration:queue:approval:times', 'average', total / counter); diff --git a/src/user/bans.js b/src/user/bans.js index d35370cb57..617ecce3bc 100644 --- a/src/user/bans.js +++ b/src/user/bans.js @@ -53,11 +53,7 @@ module.exports = function (User) { until: until ? (new Date(until)).toUTCString().replace(/,/g, '\\,') : false, reason: reason, }; - try { - await emailer.send('banned', uid, data); - } catch (err) { - winston.error('[emailer.send] ' + err.message); - } + await emailer.send('banned', uid, data).catch(err => winston.error('[emailer.send] ' + err.stack)); return banData; }; diff --git a/src/user/create.js b/src/user/create.js index b622a8b5a3..690f7b7846 100644 --- a/src/user/create.js +++ b/src/user/create.js @@ -1,6 +1,8 @@ 'use strict'; const zxcvbn = require('zxcvbn'); +const winston = require('winston'); + const db = require('../database'); const utils = require('../utils'); const slugify = require('../slugify'); @@ -116,7 +118,7 @@ module.exports = function (User) { if (userData.email && userData.uid > 1 && meta.config.requireEmailConfirmation) { User.email.sendValidationEmail(userData.uid, { email: userData.email, - }); + }).catch(err => winston.error('[user.create] Validation email failed to send\n[emailer.send] ' + err.stack)); } if (userNameChanged) { await User.notifications.sendNameChangeNotification(userData.uid, userData.username); diff --git a/src/user/digest.js b/src/user/digest.js index 689d0ff771..4ae34abd14 100644 --- a/src/user/digest.js +++ b/src/user/digest.js @@ -125,21 +125,17 @@ Digest.send = async function (data) { emailsSent += 1; const now = new Date(); - try { - await emailer.send('digest', userObj.uid, { - subject: '[[email:digest.subject, ' + (now.getFullYear() + '/' + (now.getMonth() + 1) + '/' + now.getDate()) + ']]', - username: userObj.username, - userslug: userObj.userslug, - notifications: unreadNotifs, - recent: recentTopics, - topTopics: topTopics, - popularTopics: popularTopics, - interval: data.interval, - showUnsubscribe: true, - }); - } catch (err) { - winston.error('[user/jobs] Could not send digest email\n' + err.stack); - } + await emailer.send('digest', userObj.uid, { + subject: '[[email:digest.subject, ' + (now.getFullYear() + '/' + (now.getMonth() + 1) + '/' + now.getDate()) + ']]', + username: userObj.username, + userslug: userObj.userslug, + notifications: unreadNotifs, + recent: recentTopics, + topTopics: topTopics, + popularTopics: popularTopics, + interval: data.interval, + showUnsubscribe: true, + }).catch(err => winston.error('[user/jobs] Could not send digest email\n[emailer.send] ' + err.stack)); if (data.interval !== 'alltime') { await db.sortedSetAdd('digest:delivery', now.getTime(), userObj.uid); diff --git a/src/user/invite.js b/src/user/invite.js index 4c20eab70a..8085ae4ece 100644 --- a/src/user/invite.js +++ b/src/user/invite.js @@ -49,7 +49,6 @@ module.exports = function (User) { } const data = await prepareInvitation(uid, email, groupsToJoin); - await emailer.sendToEmail('invitation', email, meta.config.defaultLang, data); }; diff --git a/src/user/profile.js b/src/user/profile.js index 2a571d4cfc..fee66f7fe2 100644 --- a/src/user/profile.js +++ b/src/user/profile.js @@ -3,6 +3,7 @@ const async = require('async'); const validator = require('validator'); +const winston = require('winston'); const utils = require('../utils'); const slugify = require('../slugify'); @@ -246,7 +247,7 @@ module.exports = function (User) { email: newEmail, subject: '[[email:email.verify-your-email.subject]]', template: 'verify_email', - }); + }).catch(err => winston.error('[user.create] Validation email failed to send\n[emailer.send] ' + err.stack)); } } diff --git a/src/user/reset.js b/src/user/reset.js index eabf826c5a..24a3869438 100644 --- a/src/user/reset.js +++ b/src/user/reset.js @@ -55,7 +55,7 @@ UserReset.send = async function (email) { subject: '[[email:password-reset-requested]]', template: 'reset', uid: uid, - }); + }).catch(err => winston.error('[emailer.send] ' + err.stack)); }; UserReset.commit = async function (code, password) { diff --git a/test/api.js b/test/api.js index d444f0337f..4d929743c3 100644 --- a/test/api.js +++ b/test/api.js @@ -68,9 +68,13 @@ describe('API', async () => { async function dummySearchHook(data) { return [1]; } + async function dummyEmailerHook(data) { + // pretend to handle sending emails + } after(async function () { plugins.unregisterHook('core', 'filter:search.query', dummySearchHook); + plugins.unregisterHook('emailer-test', 'filter:email.send'); }); async function setupData() { @@ -145,6 +149,11 @@ describe('API', async () => { hook: 'filter:search.query', method: dummySearchHook, }); + // Attach an emailer hook so related requests do not error + plugins.registerHook('emailer-test', { + hook: 'filter:email.send', + method: dummyEmailerHook, + }); jar = await helpers.loginUser('admin', '123456'); diff --git a/test/socket.io.js b/test/socket.io.js index f041037d1d..8a4e7fa432 100644 --- a/test/socket.io.js +++ b/test/socket.io.js @@ -240,6 +240,21 @@ describe('socket.io', function () { describe('validation emails', function () { var meta = require('../src/meta'); + var plugins = require('../src/plugins'); + + async function dummyEmailerHook(data) { + // pretend to handle sending emails + } + before(function () { + // Attach an emailer hook so related requests do not error + plugins.registerHook('emailer-test', { + hook: 'filter:email.send', + method: dummyEmailerHook, + }); + }); + after(function () { + plugins.unregisterHook('emailer-test', 'filter:email.send'); + }); it('should validate emails', function (done) { socketAdmin.user.validateEmail({ uid: adminUid }, [regularUid], function (err) { diff --git a/test/user.js b/test/user.js index 253657afa2..405ba3adc0 100644 --- a/test/user.js +++ b/test/user.js @@ -25,7 +25,18 @@ describe('User', function () { var testUid; var testCid; + var plugins = require('../src/plugins'); + + async function dummyEmailerHook(data) { + // pretend to handle sending emails + } before(function (done) { + // Attach an emailer hook so related requests do not error + plugins.registerHook('emailer-test', { + hook: 'filter:email.send', + method: dummyEmailerHook, + }); + Categories.create({ name: 'Test Category', description: 'A test', @@ -39,6 +50,9 @@ describe('User', function () { done(); }); }); + after(function () { + plugins.unregisterHook('emailer-test', 'filter:email.send'); + }); beforeEach(function () { userData = { From da4f91186bf9a46abcbb65a5bfa1585f5f021c63 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 18 Dec 2020 14:57:40 -0500 Subject: [PATCH 33/98] fix: #9113, wrong path separator used in thumbs.get --- src/topics/thumbs.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/topics/thumbs.js b/src/topics/thumbs.js index e8709a46b4..a894e12810 100644 --- a/src/topics/thumbs.js +++ b/src/topics/thumbs.js @@ -37,7 +37,7 @@ Thumbs.get = async function (tids) { let response = thumbs.map((thumbSet, idx) => thumbSet.map(thumb => ({ id: tids[idx], name: path.basename(thumb), - url: thumb.startsWith('http') ? thumb : path.join(upload_url, thumb), + url: thumb.startsWith('http') ? thumb : path.posix.join(upload_url, thumb), }))); ({ thumbs: response } = await plugins.hooks.fire('filter:topics.getThumbs', { tids, thumbs: response })); From 37b35f7de0fbf0146d80b22b1ff9b012047f62ed Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Fri, 18 Dec 2020 21:39:04 +0000 Subject: [PATCH 34/98] fix(deps): update dependency nodebb-theme-persona to v10.3.12 --- install/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/package.json b/install/package.json index f24311ad37..01ce4f5f7f 100644 --- a/install/package.json +++ b/install/package.json @@ -101,7 +101,7 @@ "nodebb-plugin-spam-be-gone": "0.7.7", "nodebb-rewards-essentials": "0.1.4", "nodebb-theme-lavender": "5.0.17", - "nodebb-theme-persona": "10.3.11", + "nodebb-theme-persona": "10.3.12", "nodebb-theme-slick": "1.3.7", "nodebb-theme-vanilla": "11.3.10", "nodebb-widget-essentials": "5.0.0", From 2610dfcf56ef06e41487d0f4aa46a6354779b829 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sat, 19 Dec 2020 00:18:00 +0000 Subject: [PATCH 35/98] chore(deps): update dependency eslint to v7.16.0 --- install/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/package.json b/install/package.json index 01ce4f5f7f..f3fc069970 100644 --- a/install/package.json +++ b/install/package.json @@ -153,7 +153,7 @@ "@commitlint/cli": "11.0.0", "@commitlint/config-angular": "11.0.0", "coveralls": "3.1.0", - "eslint": "7.15.0", + "eslint": "7.16.0", "eslint-config-airbnb-base": "14.2.1", "eslint-plugin-import": "2.22.1", "grunt": "1.3.0", From 05d8b3c33936d0aa83984339b8d5c2933942f950 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 21 Dec 2020 09:58:39 -0500 Subject: [PATCH 36/98] chore: add deprecation notice to topic thumb tpl value --- src/topics/data.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/topics/data.js b/src/topics/data.js index f518a5f5c0..430236b7d5 100644 --- a/src/topics/data.js +++ b/src/topics/data.js @@ -98,6 +98,7 @@ function modifyTopic(topic, fields) { escapeTitle(topic); + // TODO: Remove in v1.17.0 if (topic.hasOwnProperty('thumb')) { topic.thumb = validator.escape(String(topic.thumb)); } From 75b1bbd09fe54115a8bef500c3de9156ad9b2d7e Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 21 Dec 2020 09:59:12 -0500 Subject: [PATCH 37/98] feat: explicitly add filter:admin/header.build hook As it is not fired during middleware.processRender --- src/middleware/admin.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/middleware/admin.js b/src/middleware/admin.js index 021ccd2785..41ad3dbc33 100644 --- a/src/middleware/admin.js +++ b/src/middleware/admin.js @@ -79,6 +79,9 @@ middleware.renderHeader = async (req, res, data) => { templateValues.template = { name: res.locals.template }; templateValues.template[res.locals.template] = true; + // Normally this should hook be automatically added by middleware.processRender(), but it seems to only be fired for page hooks, and not when called internally. + ({ templateValues } = await plugins.hooks.fire('filter:admin/header.build', { req, res, templateData: templateValues })); + return await req.app.renderAsync('admin/header', templateValues); }; From 34ccabe3ab082de7984ad4158fd3f6f2f5f79c65 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 21 Dec 2020 10:02:28 -0500 Subject: [PATCH 38/98] fix: bad assignment logic in middleware.renderHeader --- src/middleware/admin.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/middleware/admin.js b/src/middleware/admin.js index 41ad3dbc33..c30f40765e 100644 --- a/src/middleware/admin.js +++ b/src/middleware/admin.js @@ -80,7 +80,7 @@ middleware.renderHeader = async (req, res, data) => { templateValues.template[res.locals.template] = true; // Normally this should hook be automatically added by middleware.processRender(), but it seems to only be fired for page hooks, and not when called internally. - ({ templateValues } = await plugins.hooks.fire('filter:admin/header.build', { req, res, templateData: templateValues })); + ({ templateData: templateValues } = await plugins.hooks.fire('filter:admin/header.build', { req, res, templateData: templateValues })); return await req.app.renderAsync('admin/header', templateValues); }; From 2ef72a94c2ae68e85605d8e04b1e86480716f4e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 21 Dec 2020 11:19:51 -0500 Subject: [PATCH 39/98] fix: #9136, fix move topic/post timeout errors --- public/src/client/topic/move-post.js | 21 +++++++++++++-------- public/src/client/topic/move.js | 23 +++++++++++------------ 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/public/src/client/topic/move-post.js b/public/src/client/topic/move-post.js index 029891c8ca..16eb4590fb 100644 --- a/public/src/client/topic/move-post.js +++ b/public/src/client/topic/move-post.js @@ -30,12 +30,18 @@ define('forum/topic/move-post', [ postSelect.togglePostSelection(postEl, postEl.attr('data-pid')); } - $(window).off('action:axajify.end', checkMoveButtonEnable) + $(window).off('action:ajaxify.end', checkMoveButtonEnable) .on('action:ajaxify.end', checkMoveButtonEnable); moveCommit.on('click', function () { + if (!ajaxify.data.template.topic || !ajaxify.data.tid) { + return; + } moveCommit.attr('disabled', true); - + var data = { + pids: postSelect.pids.slice(), + tid: ajaxify.data.tid, + }; alerts.alert({ alert_id: 'pids_move_' + postSelect.pids.join('-'), title: '[[topic:thread_tools.move-posts]]', @@ -43,7 +49,7 @@ define('forum/topic/move-post', [ type: 'success', timeout: 10000, timeoutfn: function () { - movePosts(); + movePosts(data); }, clickfn: function (alert, params) { delete params.timeoutfn; @@ -90,15 +96,15 @@ define('forum/topic/move-post', [ checkMoveButtonEnable(); } - function movePosts() { - if (!ajaxify.data.template.topic || !ajaxify.data.tid) { + function movePosts(data) { + if (!ajaxify.data.template.topic || !data.tid) { return; } - socket.emit('posts.movePosts', { pids: postSelect.pids, tid: ajaxify.data.tid }, function (err) { + socket.emit('posts.movePosts', { pids: data.pids, tid: data.tid }, function (err) { if (err) { return app.alertError(err.message); } - postSelect.pids.forEach(function (pid) { + data.pids.forEach(function (pid) { components.get('post', 'pid', pid).fadeOut(500, function () { $(this).remove(); }); @@ -116,6 +122,5 @@ define('forum/topic/move-post', [ } } - return MovePost; }); diff --git a/public/src/client/topic/move.js b/public/src/client/topic/move.js index 706ca635f8..03c40b56d3 100644 --- a/public/src/client/topic/move.js +++ b/public/src/client/topic/move.js @@ -60,6 +60,12 @@ define('forum/topic/move', ['categorySelector', 'alerts'], function (categorySel } else if (!Move.tids) { message = '[[topic:topic_move_all_success, ' + selectedCategory.name + ']]'; } + var data = { + tids: Move.tids ? Move.tids.slice() : null, + cid: selectedCategory.cid, + currentCid: Move.currentCid, + onComplete: Move.onComplete, + }; alerts.alert({ alert_id: 'tids_move_' + (Move.tids ? Move.tids.join('-') : 'all'), title: '[[topic:thread_tools.move]]', @@ -67,7 +73,7 @@ define('forum/topic/move', ['categorySelector', 'alerts'], function (categorySel type: 'success', timeout: 10000, timeoutfn: function () { - moveTopics(); + moveTopics(data); }, clickfn: function (alert, params) { delete params.timeoutfn; @@ -77,26 +83,19 @@ define('forum/topic/move', ['categorySelector', 'alerts'], function (categorySel } } - function moveTopics() { - var data = { - tids: Move.tids, - cid: selectedCategory.cid, - currentCid: Move.currentCid, - }; - + function moveTopics(data) { $(window).trigger('action:topic.move', data); - socket.emit(Move.moveAll ? 'topics.moveAll' : 'topics.move', data, function (err) { + socket.emit(!data.tids ? 'topics.moveAll' : 'topics.move', data, function (err) { if (err) { return app.alertError(err.message); } - if (typeof Move.onComplete === 'function') { - Move.onComplete(); + if (typeof data.onComplete === 'function') { + data.onComplete(); } }); } - return Move; }); From 4ac13160b544657cfef53dc5c60d08578d36033c Mon Sep 17 00:00:00 2001 From: "Misty (Bot)" Date: Tue, 22 Dec 2020 09:09:07 +0000 Subject: [PATCH 40/98] Latest translations and fallbacks --- public/language/he/topic.json | 8 ++++---- public/language/he/user.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/public/language/he/topic.json b/public/language/he/topic.json index ba4036c164..dd50f34cae 100644 --- a/public/language/he/topic.json +++ b/public/language/he/topic.json @@ -81,11 +81,11 @@ "thread_tools.purge_confirm": "האם אתה בטוח שאתה רוצה למחוק נושא זה?", "thread_tools.merge_topics": "מזג נושאים", "thread_tools.merge": "מזג", - "topic_move_success": "נושא זה יועבר תיקף ל\"%1\". לחץ כאן כדי לבטל.", - "topic_move_multiple_success": "נושאים אלו יועברו תיקף ל\"%1\" . לחץ כאן לביטול.", - "topic_move_all_success": "כל הנושאים יועברו תיקף ל\"%1\". לחץ כאן לביטול.", + "topic_move_success": "נושא זה יועבר מיד ל\"%1\". לחץ כאן כדי לבטל.", + "topic_move_multiple_success": "נושאים אלו יועברו מיד ל\"%1\" . לחץ כאן לביטול.", + "topic_move_all_success": "כל הנושאים יועברו מיד ל\"%1\". לחץ כאן לביטול.", "topic_move_undone": "העברת הנושא בוטל", - "topic_move_posts_success": "הפוסטים יועברו תיקף ל\"%1\" . לחץ כאן לביטול.", + "topic_move_posts_success": "הפוסטים יועברו מיד ל\"%1\" . לחץ כאן לביטול.", "topic_move_posts_undone": "העברת הפוסט בוטל", "post_delete_confirm": "האם אתה בטוח שאתה רוצה למחוק פוסט זה?", "post_restore_confirm": "האם אתה בטוח שאתה רוצה לשחזר פוסט זה?", diff --git a/public/language/he/user.json b/public/language/he/user.json index 7f070c79f5..936bff3076 100644 --- a/public/language/he/user.json +++ b/public/language/he/user.json @@ -31,7 +31,7 @@ "profile": "פרופיל", "profile_views": "צפיות בפרופיל", "reputation": "מוניטין", - "bookmarks": "סימניות", + "bookmarks": "מועדפים", "watched_categories": "קטגוריות במעקב", "change_all": "שנה הכל", "watched": "נצפה", From 4919e5968dc437acd2275bc9582dee2531cc8b0d Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Tue, 22 Dec 2020 13:42:06 +0000 Subject: [PATCH 41/98] fix(deps): update dependency sharp to v0.27.0 --- install/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/package.json b/install/package.json index f3fc069970..5828f79509 100644 --- a/install/package.json +++ b/install/package.json @@ -125,7 +125,7 @@ "sanitize-html": "^2.0.0", "semver": "^7.2.1", "serve-favicon": "^2.5.0", - "sharp": "0.26.3", + "sharp": "0.27.0", "sitemap": "^6.1.0", "slideout": "1.0.1", "socket.io": "3.0.4", From e640a41a78396ff1338a30c8b68b26817fb9b987 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 21 Dec 2020 15:10:12 -0500 Subject: [PATCH 42/98] refactor(api): update group deletion calls to use write API --- public/src/admin/manage/groups.js | 13 +++---------- public/src/client/groups/details.js | 14 ++++---------- 2 files changed, 7 insertions(+), 20 deletions(-) diff --git a/public/src/admin/manage/groups.js b/public/src/admin/manage/groups.js index c61c64aaf0..b7b181776a 100644 --- a/public/src/admin/manage/groups.js +++ b/public/src/admin/manage/groups.js @@ -2,8 +2,9 @@ define('admin/manage/groups', [ 'categorySelector', + 'slugify', 'api', -], function (categorySelector, api) { +], function (categorySelector, slugify, api) { var Groups = {}; var intervalId = 0; @@ -61,15 +62,7 @@ define('admin/manage/groups', [ case 'delete': bootbox.confirm('[[admin/manage/groups:alerts.confirm-delete]]', function (confirm) { if (confirm) { - socket.emit('groups.delete', { - groupName: groupName, - }, function (err) { - if (err) { - return app.alertError(err.message); - } - - ajaxify.refresh(); - }); + api.del(`/groups/${slugify(groupName)}`, {}).then(ajaxify.refresh).catch(app.alertError); } }); break; diff --git a/public/src/client/groups/details.js b/public/src/client/groups/details.js index 4680fa7171..3e804bbc84 100644 --- a/public/src/client/groups/details.js +++ b/public/src/client/groups/details.js @@ -229,16 +229,10 @@ define('forum/groups/details', [ if (confirm) { bootbox.prompt('Please enter the name of this group in order to delete it:', function (response) { if (response === groupName) { - socket.emit('groups.delete', { - groupName: groupName, - }, function (err) { - if (!err) { - app.alertSuccess('[[groups:event.deleted, ' + utils.escapeHTML(groupName) + ']]'); - ajaxify.go('groups'); - } else { - app.alertError(err.message); - } - }); + api.del(`/groups/${slugify(groupName)}`, {}).then(() => { + app.alertSuccess('[[groups:event.deleted, ' + utils.escapeHTML(groupName) + ']]'); + ajaxify.go('groups'); + }).catch(app.alertError); } }); } From 1cd2689cf62f552468d2a6e02b38783de0dbfe13 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 21 Dec 2020 22:05:00 -0500 Subject: [PATCH 43/98] refactor(api): deprecated groups update socket in favour of API lib --- public/src/admin/manage/group.js | 40 ++++++++++++----------------- public/src/client/groups/details.js | 11 ++------ src/api/groups.js | 10 ++++++++ src/controllers/write/groups.js | 8 ++++++ src/groups/update.js | 7 +++++ src/routes/write/groups.js | 1 + src/socket.io/admin/groups.js | 5 +++- src/socket.io/groups.js | 6 ++++- 8 files changed, 54 insertions(+), 34 deletions(-) diff --git a/public/src/admin/manage/group.js b/public/src/admin/manage/group.js index 792560bf2b..e041164bbf 100644 --- a/public/src/admin/manage/group.js +++ b/public/src/admin/manage/group.js @@ -6,8 +6,9 @@ define('admin/manage/group', [ 'translator', 'categorySelector', 'groupSearch', + 'slugify', 'api', -], function (memberList, iconSelect, translator, categorySelector, groupSearch, api) { +], function (memberList, iconSelect, translator, categorySelector, groupSearch, slugify, api) { var Groups = {}; Groups.init = function () { @@ -68,27 +69,20 @@ define('admin/manage/group', [ }); $('#save').on('click', function () { - socket.emit('admin.groups.update', { - groupName: groupName, - values: { - name: $('#change-group-name').val(), - userTitle: changeGroupUserTitle.val(), - description: $('#change-group-desc').val(), - icon: groupIcon.attr('value'), - labelColor: changeGroupLabelColor.val(), - textColor: changeGroupTextColor.val(), - userTitleEnabled: $('#group-userTitleEnabled').is(':checked'), - private: $('#group-private').is(':checked'), - hidden: $('#group-hidden').is(':checked'), - memberPostCids: $('#memberPostCids').val(), - disableJoinRequests: $('#group-disableJoinRequests').is(':checked'), - disableLeave: $('#group-disableLeave').is(':checked'), - }, - }, function (err) { - if (err) { - return app.alertError(err.message); - } - + api.put(`/groups/${slugify(groupName)}`, { + name: $('#change-group-name').val(), + userTitle: changeGroupUserTitle.val(), + description: $('#change-group-desc').val(), + icon: groupIcon.attr('value'), + labelColor: changeGroupLabelColor.val(), + textColor: changeGroupTextColor.val(), + userTitleEnabled: $('#group-userTitleEnabled').is(':checked'), + private: $('#group-private').is(':checked'), + hidden: $('#group-hidden').is(':checked'), + memberPostCids: $('#memberPostCids').val(), + disableJoinRequests: $('#group-disableJoinRequests').is(':checked'), + disableLeave: $('#group-disableLeave').is(':checked'), + }).then(() => { var newName = $('#change-group-name').val(); // If the group name changed, change url @@ -97,7 +91,7 @@ define('admin/manage/group', [ } app.alertSuccess('[[admin/manage/groups:edit.save-success]]'); - }); + }).catch(app.alertError); return false; }); }; diff --git a/public/src/client/groups/details.js b/public/src/client/groups/details.js index 3e804bbc84..502af28949 100644 --- a/public/src/client/groups/details.js +++ b/public/src/client/groups/details.js @@ -203,14 +203,7 @@ define('forum/groups/details', [ } }); - socket.emit('groups.update', { - groupName: groupName, - values: settings, - }, function (err) { - if (err) { - return app.alertError(err.message); - } - + api.put(`/groups/${slugify(groupName)}`, settings).then(() => { if (settings.name) { var pathname = window.location.pathname; pathname = pathname.substr(1, pathname.lastIndexOf('/')); @@ -220,7 +213,7 @@ define('forum/groups/details', [ } app.alertSuccess('[[groups:event.updated]]'); - }); + }).catch(app.alertError); } }; diff --git a/src/api/groups.js b/src/api/groups.js index 426ec1dcde..1a0157e8e9 100644 --- a/src/api/groups.js +++ b/src/api/groups.js @@ -33,6 +33,16 @@ groupsAPI.create = async function (caller, data) { return groupData; }; +groupsAPI.update = async function (caller, data) { + const groupName = await groups.getGroupNameByGroupSlug(data.slug); + await isOwner(caller, groupName); + + delete data.slug; + await groups.update(groupName, data); + + return await groups.getGroupData(data.name || groupName); +}; + groupsAPI.delete = async function (caller, data) { const groupName = await groups.getGroupNameByGroupSlug(data.slug); await isOwner(caller, groupName); diff --git a/src/controllers/write/groups.js b/src/controllers/write/groups.js index 243d5b0cf7..0831e2ed43 100644 --- a/src/controllers/write/groups.js +++ b/src/controllers/write/groups.js @@ -15,6 +15,14 @@ Groups.create = async (req, res) => { helpers.formatApiResponse(200, res, groupObj); }; +Groups.update = async (req, res) => { + const groupObj = await api.groups.update(req, { + ...req.body, + slug: req.params.slug, + }); + helpers.formatApiResponse(200, res, groupObj); +}; + Groups.delete = async (req, res) => { await api.groups.delete(req, req.params); helpers.formatApiResponse(200, res); diff --git a/src/groups/update.js b/src/groups/update.js index 26d0cdad5c..0e34169660 100644 --- a/src/groups/update.js +++ b/src/groups/update.js @@ -24,6 +24,13 @@ module.exports = function (Groups) { values: values, })); + // Case some values as bool (if not boolean already) + ['userTitleEnabled', 'private', 'hidden', 'disableJoinRequests', 'disableLeave'].forEach((prop) => { + if (values.hasOwnProperty(prop) && typeof values[prop] !== 'boolean') { + values[prop] = !!parseInt(values[prop], 10); + } + }); + const payload = { description: values.description || '', icon: values.icon || '', diff --git a/src/routes/write/groups.js b/src/routes/write/groups.js index 64af35bf70..8a5014c6f0 100644 --- a/src/routes/write/groups.js +++ b/src/routes/write/groups.js @@ -12,6 +12,7 @@ module.exports = function () { setupApiRoute(router, 'post', '/', [...middlewares, middleware.checkRequired.bind(null, ['name'])], controllers.write.groups.create); setupApiRoute(router, 'head', '/:slug', [middleware.assert.group], controllers.write.groups.exists); + setupApiRoute(router, 'put', '/:slug', [...middlewares, middleware.assert.group], controllers.write.groups.update); setupApiRoute(router, 'delete', '/:slug', [...middlewares, middleware.assert.group], controllers.write.groups.delete); setupApiRoute(router, 'put', '/:slug/membership/:uid', [...middlewares, middleware.assert.group], controllers.write.groups.join); setupApiRoute(router, 'delete', '/:slug/membership/:uid', [...middlewares, middleware.assert.group], controllers.write.groups.leave); diff --git a/src/socket.io/admin/groups.js b/src/socket.io/admin/groups.js index ef3cb3f878..6aa609288e 100644 --- a/src/socket.io/admin/groups.js +++ b/src/socket.io/admin/groups.js @@ -43,9 +43,12 @@ Groups.leave = async function (socket, data) { }; Groups.update = async function (socket, data) { + sockets.warnDeprecated(socket, 'PUT /api/v3/groups/:slug'); if (!data) { throw new Error('[[error:invalid-data]]'); } - await groups.update(data.groupName, data.values); + const slug = await groups.getGroupField(data.groupName, 'slug'); + await api.groups.update(socket, { slug, ...data.values }); + // await groups.update(data.groupName, data.values); }; diff --git a/src/socket.io/groups.js b/src/socket.io/groups.js index 4e1152b514..73cae30dc6 100644 --- a/src/socket.io/groups.js +++ b/src/socket.io/groups.js @@ -186,8 +186,12 @@ SocketGroups.rejectInvite = async (socket, data) => { }; SocketGroups.update = async (socket, data) => { + sockets.warnDeprecated(socket, 'PUT /api/v3/groups/:slug'); await isOwner(socket, data); - await groups.update(data.groupName, data.values); + + const slug = await groups.getGroupField(data.groupName, 'slug'); + await api.groups.update(socket, { slug, ...data.values }); + // await groups.update(data.groupName, data.values); }; From 501a7b777467e9ad8b5790f1bc5e3c43a3575c1e Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 22 Dec 2020 10:26:02 -0500 Subject: [PATCH 44/98] fix: bug in api path existence test It was only checking for the path, but not ensuring that the method was also defined --- test/api.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/api.js b/test/api.js index 4d929743c3..3cfa700d60 100644 --- a/test/api.js +++ b/test/api.js @@ -235,7 +235,8 @@ describe('API', async () => { } const normalizedPath = pathObj.path.replace(/\/:([^\\/]+)/g, '/{$1}').replace(/\?/g, ''); - assert(schema.paths.hasOwnProperty(normalizedPath)); + assert(schema.paths.hasOwnProperty(normalizedPath), `${pathObj.path} is not defined in schema docs`); + assert(schema.paths[normalizedPath].hasOwnProperty(pathObj.method), `${pathObj.path} was found in schema docs, but ${pathObj.method.toUpperCase()} method is not defined`); }); }); }); From 4fc1337762da0a9e75dc873d13f654f857b02737 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 22 Dec 2020 13:18:18 -0500 Subject: [PATCH 45/98] feat(api): added schema for email unsubscribe token --- .../openapi/read/email/unsubscribe/token.yaml | 47 ++++++++++++++++++- test/api.js | 14 +++++- 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/public/openapi/read/email/unsubscribe/token.yaml b/public/openapi/read/email/unsubscribe/token.yaml index 71c02c8809..3b1c9deaf8 100644 --- a/public/openapi/read/email/unsubscribe/token.yaml +++ b/public/openapi/read/email/unsubscribe/token.yaml @@ -1,8 +1,51 @@ -# TODO: Need GET route here as well +get: + tags: + - emails + summary: Unsubscribe user from email type (user variant) + parameters: + - name: token + in: path + required: true + schema: + type: string + example: testToken + responses: + "200": + description: "" + content: + application/json: + schema: + allOf: + - type: object + properties: + payload: + type: object + properties: + uid: + type: number + template: + type: string + description: The type of email template to unsubscribe from. + enum: + - digest + - notification + type: + type: string + description: Only used if `template` is `notification`, signifies the type of notification to unsubscribe from. + nullable: true + iat: + type: number + description: Reflection of the token's "issued at" claim + required: + - uid + - template + - $ref: ../../../components/schemas/CommonProps.yaml#/CommonProps + "500": + description: "Server-side error (likely token verification failure)" post: tags: - emails - summary: Unsubscribe user from email type + summary: Unsubscribe user from email type (auto variant) parameters: - name: token in: path diff --git a/test/api.js b/test/api.js index 3cfa700d60..7d81b55c04 100644 --- a/test/api.js +++ b/test/api.js @@ -7,6 +7,7 @@ const fs = require('fs'); const SwaggerParser = require('@apidevtools/swagger-parser'); const request = require('request-promise-native'); const nconf = require('nconf'); +const jwt = require('jsonwebtoken'); const util = require('util'); const wait = util.promisify(setTimeout); @@ -34,7 +35,18 @@ describe('API', async () => { const mocks = { head: {}, - get: {}, + get: { + '/api/email/unsubscribe/{token}': [ + { + in: 'path', + name: 'token', + example: (() => jwt.sign({ + template: 'digest', + uid: 1, + }, nconf.get('secret')))(), + }, + ], + }, post: {}, put: {}, delete: { From 98550d61d7a88f933d62a39e8bd4beb1584b70a3 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 22 Dec 2020 13:18:35 -0500 Subject: [PATCH 46/98] feat(api): add schema for groups update route --- public/openapi/write/groups/slug.yaml | 37 +++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/public/openapi/write/groups/slug.yaml b/public/openapi/write/groups/slug.yaml index 28077e290c..4959da9f12 100644 --- a/public/openapi/write/groups/slug.yaml +++ b/public/openapi/write/groups/slug.yaml @@ -15,6 +15,43 @@ head: description: group found '404': description: group not found +put: + tags: + - groups + summary: update group data + parameters: + - in: path + name: slug + schema: + type: string + required: true + description: slug of the group you wish to update + example: my-test-group + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + icon: + type: string + example: fa-times + additionalProperties: + description: An object of group properties you wish to update + example: + responses: + '200': + description: group successfully updated + content: + application/json: + schema: + type: object + properties: + status: + $ref: ../../components/schemas/Status.yaml#/Status + response: + $ref: ../../components/schemas/GroupObject.yaml#/GroupDataObject delete: tags: - groups From 32e36f7b2e8f9c0de4887a3bb437cadeeec69102 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 22 Dec 2020 14:26:31 -0500 Subject: [PATCH 47/98] feat(api): group ownership API route, switch client-side to use API route --- public/openapi/write.yaml | 2 ++ public/src/admin/manage/group.js | 14 ++++---------- public/src/client/groups/details.js | 28 ++++++---------------------- src/api/groups.js | 22 ++++++++++++++++++++++ src/controllers/write/groups.js | 10 ++++++++++ src/groups/ownership.js | 7 +++---- src/routes/write/groups.js | 2 ++ src/socket.io/groups.js | 4 ++++ 8 files changed, 53 insertions(+), 36 deletions(-) diff --git a/public/openapi/write.yaml b/public/openapi/write.yaml index ab6f8753d9..2b6266d64c 100644 --- a/public/openapi/write.yaml +++ b/public/openapi/write.yaml @@ -74,6 +74,8 @@ paths: $ref: 'write/groups/slug.yaml' /groups/{slug}/membership/{uid}: $ref: 'write/groups/slug/membership/uid.yaml' + /groups/{slug}/ownership/{uid}: + $ref: 'write/groups/slug/ownership/uid.yaml' /categories/: $ref: 'write/categories.yaml' /categories/{cid}: diff --git a/public/src/admin/manage/group.js b/public/src/admin/manage/group.js index e041164bbf..5eb41bab9f 100644 --- a/public/src/admin/manage/group.js +++ b/public/src/admin/manage/group.js @@ -39,7 +39,7 @@ define('admin/manage/group', [ groupLabelPreview.css('color', changeGroupTextColor.val() || '#ffffff'); }); - setupGroupMembersMenu(groupName); + setupGroupMembersMenu(); $('#group-icon, #group-icon-label').on('click', function () { var currentIcon = groupIcon.attr('value'); @@ -96,7 +96,7 @@ define('admin/manage/group', [ }); }; - function setupGroupMembersMenu(groupName) { + function setupGroupMembersMenu() { $('[component="groups/members"]').on('click', '[data-action]', function () { var btnEl = $(this); var userRow = btnEl.parents('[data-uid]'); @@ -107,15 +107,9 @@ define('admin/manage/group', [ switch (action) { case 'toggleOwnership': - socket.emit('groups.' + (isOwner ? 'rescind' : 'grant'), { - toUid: uid, - groupName: groupName, - }, function (err) { - if (err) { - return app.alertError(err.message); - } + api[isOwner ? 'del' : 'put'](`/groups/${ajaxify.data.group.slug}/ownership/${uid}`, {}).then(() => { ownerFlagEl.toggleClass('invisible'); - }); + }).catch(app.alertError); break; case 'kick': diff --git a/public/src/client/groups/details.js b/public/src/client/groups/details.js index 502af28949..9b240fc5d3 100644 --- a/public/src/client/groups/details.js +++ b/public/src/client/groups/details.js @@ -64,16 +64,9 @@ define('forum/groups/details', [ switch (action) { case 'toggleOwnership': - socket.emit('groups.' + (isOwner ? 'rescind' : 'grant'), { - toUid: uid, - groupName: groupName, - }, function (err) { - if (!err) { - ownerFlagEl.toggleClass('invisible'); - } else { - app.alertError(err.message); - } - }); + api[isOwner ? 'del' : 'put'](`/groups/${ajaxify.data.group.slug}/ownership/${uid}`, {}).then(() => { + ownerFlagEl.toggleClass('invisible'); + }).catch(app.alertError); break; case 'kick': @@ -83,16 +76,7 @@ define('forum/groups/details', [ return; } - socket.emit('groups.kick', { - uid: uid, - groupName: groupName, - }, function (err) { - if (!err) { - userRow.slideUp().remove(); - } else { - app.alertError(err.message); - } - }); + api.del(`/groups/${ajaxify.data.group.slug}/membership/${uid}`, undefined).then(() => userRow.slideUp().remove()).catch(app.alertError); }); }); break; @@ -203,7 +187,7 @@ define('forum/groups/details', [ } }); - api.put(`/groups/${slugify(groupName)}`, settings).then(() => { + api.put(`/groups/${ajaxify.data.group.slug}`, settings).then(() => { if (settings.name) { var pathname = window.location.pathname; pathname = pathname.substr(1, pathname.lastIndexOf('/')); @@ -222,7 +206,7 @@ define('forum/groups/details', [ if (confirm) { bootbox.prompt('Please enter the name of this group in order to delete it:', function (response) { if (response === groupName) { - api.del(`/groups/${slugify(groupName)}`, {}).then(() => { + api.del(`/groups/${ajaxify.data.group.slug}`, {}).then(() => { app.alertSuccess('[[groups:event.deleted, ' + utils.escapeHTML(groupName) + ']]'); ajaxify.go('groups'); }).catch(app.alertError); diff --git a/src/api/groups.js b/src/api/groups.js index 1a0157e8e9..9f68d31afe 100644 --- a/src/api/groups.js +++ b/src/api/groups.js @@ -177,6 +177,28 @@ groupsAPI.leave = async function (caller, data) { }); }; +groupsAPI.grant = async (caller, data) => { + const groupName = await groups.getGroupNameByGroupSlug(data.slug); + await isOwner(caller, groupName); + + await groups.ownership.grant(data.uid, groupName); + logGroupEvent(caller, 'group-owner-grant', { + groupName: groupName, + targetUid: data.uid, + }); +}; + +groupsAPI.rescind = async (caller, data) => { + const groupName = await groups.getGroupNameByGroupSlug(data.slug); + await isOwner(caller, groupName); + + await groups.ownership.rescind(data.uid, groupName); + logGroupEvent(caller, 'group-owner-rescind', { + groupName: groupName, + targetUid: data.uid, + }); +}; + async function isOwner(caller, groupName) { if (typeof groupName !== 'string') { throw new Error('[[error:invalid-group-name]]'); diff --git a/src/controllers/write/groups.js b/src/controllers/write/groups.js index 0831e2ed43..f4019cb075 100644 --- a/src/controllers/write/groups.js +++ b/src/controllers/write/groups.js @@ -37,3 +37,13 @@ Groups.leave = async (req, res) => { await api.groups.leave(req, req.params); helpers.formatApiResponse(200, res); }; + +Groups.grant = async (req, res) => { + await api.groups.grant(req, req.params); + helpers.formatApiResponse(200, res); +}; + +Groups.rescind = async (req, res) => { + await api.groups.rescind(req, req.params); + helpers.formatApiResponse(200, res); +}; diff --git a/src/groups/ownership.js b/src/groups/ownership.js index 4296c307c0..9f0ce9a08f 100644 --- a/src/groups/ownership.js +++ b/src/groups/ownership.js @@ -22,16 +22,15 @@ module.exports = function (Groups) { }; Groups.ownership.grant = async function (toUid, groupName) { - // Note: No ownership checking is done here on purpose! await db.setAdd('group:' + groupName + ':owners', toUid); plugins.hooks.fire('action:group.grantOwnership', { uid: toUid, groupName: groupName }); }; Groups.ownership.rescind = async function (toUid, groupName) { - // Note: No ownership checking is done here on purpose! - // If the owners set only contains one member, error out! + // If the owners set only contains one member (and toUid is that member), error out! const numOwners = await db.setCount('group:' + groupName + ':owners'); - if (numOwners <= 1) { + const isOwner = await db.isSortedSetMember(`group:${groupName}:owners`); + if (numOwners <= 1 && isOwner) { throw new Error('[[error:group-needs-owner]]'); } await db.setRemove('group:' + groupName + ':owners', toUid); diff --git a/src/routes/write/groups.js b/src/routes/write/groups.js index 8a5014c6f0..e5adf28a40 100644 --- a/src/routes/write/groups.js +++ b/src/routes/write/groups.js @@ -16,6 +16,8 @@ module.exports = function () { setupApiRoute(router, 'delete', '/:slug', [...middlewares, middleware.assert.group], controllers.write.groups.delete); setupApiRoute(router, 'put', '/:slug/membership/:uid', [...middlewares, middleware.assert.group], controllers.write.groups.join); setupApiRoute(router, 'delete', '/:slug/membership/:uid', [...middlewares, middleware.assert.group], controllers.write.groups.leave); + setupApiRoute(router, 'put', '/:slug/ownership/:uid', [...middlewares, middleware.assert.group], controllers.write.groups.grant); + setupApiRoute(router, 'delete', '/:slug/ownership/:uid', [...middlewares, middleware.assert.group], controllers.write.groups.rescind); return router; }; diff --git a/src/socket.io/groups.js b/src/socket.io/groups.js index 73cae30dc6..8ed59e2bf6 100644 --- a/src/socket.io/groups.js +++ b/src/socket.io/groups.js @@ -79,6 +79,8 @@ async function isInvited(socket, data) { } SocketGroups.grant = async (socket, data) => { + sockets.warnDeprecated(socket, 'PUT /api/v3/groups/:slug/ownership/:uid'); + await isOwner(socket, data); await groups.ownership.grant(data.toUid, data.groupName); logGroupEvent(socket, 'group-owner-grant', { @@ -88,6 +90,8 @@ SocketGroups.grant = async (socket, data) => { }; SocketGroups.rescind = async (socket, data) => { + sockets.warnDeprecated(socket, 'DELETE /api/v3/groups/:slug/ownership/:uid'); + await isOwner(socket, data); await groups.ownership.rescind(data.toUid, data.groupName); logGroupEvent(socket, 'group-owner-rescind', { From 931105e6cbc5cce6ba4caa68a390e469b50082fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Tue, 22 Dec 2020 14:29:25 -0500 Subject: [PATCH 48/98] fix: dont show deleted posts in navigator --- src/socket.io/posts.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/socket.io/posts.js b/src/socket.io/posts.js index 27930e3824..c8e30962f4 100644 --- a/src/socket.io/posts.js +++ b/src/socket.io/posts.js @@ -86,12 +86,13 @@ SocketPosts.getPostSummaryByIndex = async function (socket, data) { return 0; } - const canRead = await privileges.posts.can('topics:read', pid, socket.uid); - if (!canRead) { + const topicPrivileges = await privileges.topics.get(data.tid, socket.uid); + if (!topicPrivileges['topics:read']) { throw new Error('[[error:no-privileges]]'); } const postsData = await posts.getPostSummaryByPids([pid], socket.uid, { stripTags: false }); + posts.modifyPostByPrivilege(postsData[0], topicPrivileges); return postsData[0]; }; From 80ee3dfbd144c1d9d85c3685b9a3092eaf74a8b6 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 22 Dec 2020 14:39:42 -0500 Subject: [PATCH 49/98] fix(api): tests --- src/socket.io/groups.js | 1 - test/groups.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/socket.io/groups.js b/src/socket.io/groups.js index 8ed59e2bf6..73f6af7d81 100644 --- a/src/socket.io/groups.js +++ b/src/socket.io/groups.js @@ -195,7 +195,6 @@ SocketGroups.update = async (socket, data) => { const slug = await groups.getGroupField(data.groupName, 'slug'); await api.groups.update(socket, { slug, ...data.values }); - // await groups.update(data.groupName, data.values); }; diff --git a/test/groups.js b/test/groups.js index 643eff6ee0..3543499b97 100644 --- a/test/groups.js +++ b/test/groups.js @@ -1189,7 +1189,7 @@ describe('Groups', function () { }, }; socketGroups.update({ uid: adminUid }, data, function (err) { - assert.equal(err.message, '[[error:no-group]]'); + assert.equal(err.message, '[[error:invalid-group-name]]'); done(); }); }); From 3959a7bd9c49e917b9b062af4371aff37bcae2c9 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 22 Dec 2020 14:43:22 -0500 Subject: [PATCH 50/98] fix(api): failing test due to missing file --- .../write/groups/slug/ownership/uid.yaml | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 public/openapi/write/groups/slug/ownership/uid.yaml diff --git a/public/openapi/write/groups/slug/ownership/uid.yaml b/public/openapi/write/groups/slug/ownership/uid.yaml new file mode 100644 index 0000000000..ba8e16558e --- /dev/null +++ b/public/openapi/write/groups/slug/ownership/uid.yaml @@ -0,0 +1,66 @@ +put: + tags: + - groups + summary: grant group ownership + description: This operation grants ownership privilege to a user. + parameters: + - in: path + name: slug + schema: + type: string + required: true + description: slug of the group you would like to grant ownership + example: test-group + - in: path + name: uid + schema: + type: number + required: true + description: uid of the user to grant ownership + example: 1 + responses: + '200': + description: ownership successfully granted + content: + application/json: + schema: + type: object + properties: + status: + $ref: ../../../../components/schemas/Status.yaml#/Status + response: + type: object + properties: {} +delete: + tags: + - groups + summary: rescind group ownership + description: 'This operation rescinds ownership privilege from a user. **Note**: Every group needs at least one owner, so if you are attempting to remove the last owner of a group, this call will fail.' + parameters: + - in: path + name: slug + schema: + type: string + required: true + description: slug of the group you would like to rescind ownership + example: test-group + - in: path + name: uid + schema: + type: number + required: true + description: uid of the user to rescind ownership from + example: 2 + responses: + '200': + description: ownership successfully rescinded + content: + application/json: + schema: + type: object + properties: + status: + $ref: ../../../../components/schemas/Status.yaml#/Status + response: + type: object + properties: {} \ No newline at end of file From da191341e8c4b06e2c531e4cfa16f4d6715f169d Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 22 Dec 2020 15:07:37 -0500 Subject: [PATCH 51/98] feat(acp): added new admin privilege for groups management --- public/language/en-GB/admin/manage/privileges.json | 1 + src/controllers/admin.js | 2 ++ src/middleware/admin.js | 2 +- src/privileges/admin.js | 6 +++++- src/views/admin/partials/menu.tpl | 4 ++-- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/public/language/en-GB/admin/manage/privileges.json b/public/language/en-GB/admin/manage/privileges.json index d90fb7893d..92749720b0 100644 --- a/public/language/en-GB/admin/manage/privileges.json +++ b/public/language/en-GB/admin/manage/privileges.json @@ -39,6 +39,7 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-groups": "Groups", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", diff --git a/src/controllers/admin.js b/src/controllers/admin.js index b6412828a5..a9fcd83eb6 100644 --- a/src/controllers/admin.js +++ b/src/controllers/admin.js @@ -42,6 +42,8 @@ adminController.routeIndex = async (req, res) => { return helpers.redirect(res, 'admin/manage/privileges'); } else if (privilegeSet['admin:users']) { return helpers.redirect(res, 'admin/manage/users'); + } else if (privilegeSet['admin:groups']) { + return helpers.redirect(res, 'admin/manage/groups'); } else if (privilegeSet['admin:settings']) { return helpers.redirect(res, 'admin/settings/general'); } diff --git a/src/middleware/admin.js b/src/middleware/admin.js index c30f40765e..da97a67d18 100644 --- a/src/middleware/admin.js +++ b/src/middleware/admin.js @@ -73,7 +73,7 @@ middleware.renderHeader = async (req, res, data) => { version: version, latestVersion: results.latestVersion, upgradeAvailable: results.latestVersion && semver.gt(results.latestVersion, version), - showManageMenu: results.privileges.superadmin || ['categories', 'privileges', 'users', 'settings'].some(priv => results.privileges[`admin:${priv}`]), + showManageMenu: results.privileges.superadmin || ['categories', 'privileges', 'users', 'groups', 'settings'].some(priv => results.privileges[`admin:${priv}`]), }; templateValues.template = { name: res.locals.template }; diff --git a/src/privileges/admin.js b/src/privileges/admin.js index 31c0b3022d..dcf8f5ff28 100644 --- a/src/privileges/admin.js +++ b/src/privileges/admin.js @@ -17,6 +17,7 @@ module.exports = function (privileges) { { name: '[[admin/manage/privileges:admin-categories]]' }, { name: '[[admin/manage/privileges:admin-privileges]]' }, { name: '[[admin/manage/privileges:admin-users]]' }, + { name: '[[admin/manage/privileges:admin-groups]]' }, { name: '[[admin/manage/privileges:admin-settings]]' }, ]; @@ -25,6 +26,7 @@ module.exports = function (privileges) { 'admin:categories', 'admin:privileges', 'admin:users', + 'admin:groups', 'admin:settings', ]; @@ -36,6 +38,7 @@ module.exports = function (privileges) { 'manage/categories': 'admin:categories', 'manage/privileges': 'admin:privileges', 'manage/users': 'admin:users', + 'manage/groups': 'admin:groups', 'extend/plugins': 'admin:settings', 'extend/widgets': 'admin:settings', 'extend/rewards': 'admin:settings', @@ -43,6 +46,7 @@ module.exports = function (privileges) { privileges.admin.routeRegexpMap = { '^manage/categories/\\d+': 'admin:categories', '^manage/privileges/(\\d+|admin)': 'admin:privileges', + '^manage/groups/.+$': 'admin:groups', '^settings/[\\w\\-]+$': 'admin:settings', '^appearance/[\\w]+$': 'admin:settings', '^plugins/[\\w\\-]+$': 'admin:settings', @@ -138,7 +142,7 @@ module.exports = function (privileges) { payload.keys = keys; // This is a hack because I can't do {labels.users.length} to echo the count in templates.js - payload.columnCount = payload.labels.users.length + 2; + payload.columnCount = payload.labels.users.length + 3; return payload; }; diff --git a/src/views/admin/partials/menu.tpl b/src/views/admin/partials/menu.tpl index 7192135e87..ba84482e4a 100644 --- a/src/views/admin/partials/menu.tpl +++ b/src/views/admin/partials/menu.tpl @@ -18,8 +18,8 @@ {{{ if user.privileges.admin:categories }}}
  • [[admin/menu:manage/categories]]
  • {{{ end }}} {{{ if user.privileges.admin:privileges }}}
  • [[admin/menu:manage/privileges]]
  • {{{ end }}} {{{ if user.privileges.admin:users }}}
  • [[admin/menu:manage/users]]
  • {{{ end }}} + {{{ if user.privileges.admin:groups }}}
  • [[admin/menu:manage/groups]]
  • {{{ end }}} {{{ if user.privileges.superadmin }}} -
  • [[admin/menu:manage/groups]]
  • [[admin/menu:manage/admins-mods]]
  • [[admin/menu:manage/registration]]
  • [[admin/menu:manage/tags]]
  • @@ -189,8 +189,8 @@ {{{ if user.privileges.admin:categories }}}
  • [[admin/menu:manage/categories]]
  • {{{ end }}} {{{ if user.privileges.admin:privileges }}}
  • [[admin/menu:manage/privileges]]
  • {{{ end }}} {{{ if user.privileges.admin:users }}}
  • [[admin/menu:manage/users]]
  • {{{ end }}} + {{{ if user.privileges.admin:groups }}}
  • [[admin/menu:manage/groups]]
  • {{{ end }}} {{{ if user.privileges.superadmin }}} -
  • [[admin/menu:manage/groups]]
  • [[admin/menu:manage/admins-mods]]
  • [[admin/menu:manage/registration]]
  • [[admin/menu:manage/tags]]
  • From c62a6b6ca8d0ad7aa099efe4385804c286ec8d62 Mon Sep 17 00:00:00 2001 From: "Misty (Bot)" Date: Wed, 23 Dec 2020 09:07:52 +0000 Subject: [PATCH 52/98] Latest translations and fallbacks --- .../language/ar/admin/manage/privileges.json | 1 + .../language/bg/admin/manage/privileges.json | 1 + .../language/bn/admin/manage/privileges.json | 1 + .../language/cs/admin/manage/privileges.json | 1 + .../language/da/admin/manage/privileges.json | 1 + .../language/de/admin/manage/privileges.json | 1 + .../language/el/admin/manage/privileges.json | 1 + .../en-US/admin/manage/privileges.json | 1 + .../en-x-pirate/admin/manage/privileges.json | 1 + .../language/es/admin/manage/privileges.json | 1 + .../language/et/admin/manage/privileges.json | 1 + .../fa-IR/admin/manage/privileges.json | 1 + .../language/fi/admin/manage/privileges.json | 1 + .../language/fr/admin/manage/privileges.json | 1 + .../language/gl/admin/manage/privileges.json | 1 + .../language/he/admin/appearance/skins.json | 14 +++++----- .../language/he/admin/development/logger.json | 16 ++++++------ public/language/he/admin/extend/rewards.json | 22 ++++++++-------- .../language/he/admin/manage/privileges.json | 1 + public/language/he/admin/menu.json | 4 +-- public/language/he/admin/settings/chat.json | 20 +++++++------- public/language/he/admin/settings/post.json | 6 ++--- public/language/he/global.json | 26 +++++++++---------- public/language/he/user.json | 4 +-- .../language/hr/admin/manage/privileges.json | 1 + .../language/hu/admin/manage/privileges.json | 1 + .../language/id/admin/manage/privileges.json | 1 + .../language/it/admin/manage/privileges.json | 1 + .../language/ja/admin/manage/privileges.json | 1 + .../language/ko/admin/manage/privileges.json | 1 + .../language/lt/admin/manage/privileges.json | 1 + .../language/lv/admin/manage/privileges.json | 1 + .../language/ms/admin/manage/privileges.json | 1 + .../language/nb/admin/manage/privileges.json | 1 + .../language/nl/admin/manage/privileges.json | 1 + .../language/pl/admin/manage/privileges.json | 1 + .../pt-BR/admin/manage/privileges.json | 1 + .../pt-PT/admin/manage/privileges.json | 1 + .../language/ro/admin/manage/privileges.json | 1 + .../language/ru/admin/manage/privileges.json | 1 + .../language/rw/admin/manage/privileges.json | 1 + .../language/sc/admin/manage/privileges.json | 1 + .../language/sk/admin/manage/privileges.json | 1 + .../language/sl/admin/manage/privileges.json | 1 + .../language/sr/admin/manage/privileges.json | 1 + .../language/sv/admin/manage/privileges.json | 1 + .../language/th/admin/manage/privileges.json | 1 + .../language/tr/admin/manage/privileges.json | 1 + .../language/uk/admin/manage/privileges.json | 1 + .../language/vi/admin/manage/privileges.json | 1 + .../zh-CN/admin/manage/privileges.json | 1 + .../zh-TW/admin/manage/privileges.json | 1 + 52 files changed, 100 insertions(+), 56 deletions(-) diff --git a/public/language/ar/admin/manage/privileges.json b/public/language/ar/admin/manage/privileges.json index d90fb7893d..92749720b0 100644 --- a/public/language/ar/admin/manage/privileges.json +++ b/public/language/ar/admin/manage/privileges.json @@ -39,6 +39,7 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-groups": "Groups", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", diff --git a/public/language/bg/admin/manage/privileges.json b/public/language/bg/admin/manage/privileges.json index 0b481eb69f..5705f00dbf 100644 --- a/public/language/bg/admin/manage/privileges.json +++ b/public/language/bg/admin/manage/privileges.json @@ -39,6 +39,7 @@ "admin-categories": "Категории", "admin-privileges": "Правомощия", "admin-users": "Потребители", + "admin-groups": "Groups", "admin-settings": "Настройки", "alert.confirm-moderate": "Наистина ли искате да дадете правомощието за модериране на тази потребителска група? Тази група е публична и всеки може свободно да се присъедини към нея.", diff --git a/public/language/bn/admin/manage/privileges.json b/public/language/bn/admin/manage/privileges.json index d90fb7893d..92749720b0 100644 --- a/public/language/bn/admin/manage/privileges.json +++ b/public/language/bn/admin/manage/privileges.json @@ -39,6 +39,7 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-groups": "Groups", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", diff --git a/public/language/cs/admin/manage/privileges.json b/public/language/cs/admin/manage/privileges.json index 5e3a34dc4b..2580806abb 100644 --- a/public/language/cs/admin/manage/privileges.json +++ b/public/language/cs/admin/manage/privileges.json @@ -39,6 +39,7 @@ "admin-categories": "Kategorie", "admin-privileges": "Oprávnění", "admin-users": "Uživatelé", + "admin-groups": "Groups", "admin-settings": "Nastavení", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", diff --git a/public/language/da/admin/manage/privileges.json b/public/language/da/admin/manage/privileges.json index d90fb7893d..92749720b0 100644 --- a/public/language/da/admin/manage/privileges.json +++ b/public/language/da/admin/manage/privileges.json @@ -39,6 +39,7 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-groups": "Groups", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", diff --git a/public/language/de/admin/manage/privileges.json b/public/language/de/admin/manage/privileges.json index 767631b11b..73a8113964 100644 --- a/public/language/de/admin/manage/privileges.json +++ b/public/language/de/admin/manage/privileges.json @@ -39,6 +39,7 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Nutzende Personen", + "admin-groups": "Groups", "admin-settings": "Einstellungen", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", diff --git a/public/language/el/admin/manage/privileges.json b/public/language/el/admin/manage/privileges.json index d90fb7893d..92749720b0 100644 --- a/public/language/el/admin/manage/privileges.json +++ b/public/language/el/admin/manage/privileges.json @@ -39,6 +39,7 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-groups": "Groups", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", diff --git a/public/language/en-US/admin/manage/privileges.json b/public/language/en-US/admin/manage/privileges.json index d90fb7893d..92749720b0 100644 --- a/public/language/en-US/admin/manage/privileges.json +++ b/public/language/en-US/admin/manage/privileges.json @@ -39,6 +39,7 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-groups": "Groups", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", diff --git a/public/language/en-x-pirate/admin/manage/privileges.json b/public/language/en-x-pirate/admin/manage/privileges.json index d90fb7893d..92749720b0 100644 --- a/public/language/en-x-pirate/admin/manage/privileges.json +++ b/public/language/en-x-pirate/admin/manage/privileges.json @@ -39,6 +39,7 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-groups": "Groups", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", diff --git a/public/language/es/admin/manage/privileges.json b/public/language/es/admin/manage/privileges.json index 118cc2d722..e551e0ecc8 100644 --- a/public/language/es/admin/manage/privileges.json +++ b/public/language/es/admin/manage/privileges.json @@ -39,6 +39,7 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-groups": "Groups", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", diff --git a/public/language/et/admin/manage/privileges.json b/public/language/et/admin/manage/privileges.json index d90fb7893d..92749720b0 100644 --- a/public/language/et/admin/manage/privileges.json +++ b/public/language/et/admin/manage/privileges.json @@ -39,6 +39,7 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-groups": "Groups", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", diff --git a/public/language/fa-IR/admin/manage/privileges.json b/public/language/fa-IR/admin/manage/privileges.json index d90fb7893d..92749720b0 100644 --- a/public/language/fa-IR/admin/manage/privileges.json +++ b/public/language/fa-IR/admin/manage/privileges.json @@ -39,6 +39,7 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-groups": "Groups", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", diff --git a/public/language/fi/admin/manage/privileges.json b/public/language/fi/admin/manage/privileges.json index d90fb7893d..92749720b0 100644 --- a/public/language/fi/admin/manage/privileges.json +++ b/public/language/fi/admin/manage/privileges.json @@ -39,6 +39,7 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-groups": "Groups", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", diff --git a/public/language/fr/admin/manage/privileges.json b/public/language/fr/admin/manage/privileges.json index 487bb04fdb..47279d3805 100644 --- a/public/language/fr/admin/manage/privileges.json +++ b/public/language/fr/admin/manage/privileges.json @@ -39,6 +39,7 @@ "admin-categories": "Catégories", "admin-privileges": "Privilèges", "admin-users": "Utilisateurs", + "admin-groups": "Groups", "admin-settings": "Paramètres", "alert.confirm-moderate": "Voulez-vous vraiment accorder le privilège de modération à ce groupe d'utilisateurs ? Ce groupe est public et tous les utilisateurs peuvent le rejoindre à volonté.", diff --git a/public/language/gl/admin/manage/privileges.json b/public/language/gl/admin/manage/privileges.json index d90fb7893d..92749720b0 100644 --- a/public/language/gl/admin/manage/privileges.json +++ b/public/language/gl/admin/manage/privileges.json @@ -39,6 +39,7 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-groups": "Groups", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", diff --git a/public/language/he/admin/appearance/skins.json b/public/language/he/admin/appearance/skins.json index 4f49fb6e2b..ea33d800bc 100644 --- a/public/language/he/admin/appearance/skins.json +++ b/public/language/he/admin/appearance/skins.json @@ -1,9 +1,9 @@ { - "loading": "טוען סקין...", - "homepage": "דף הבית", - "select-skin": "סקין נבחר", - "current-skin": "סקין נוכחי", - "skin-updated": "סקין מעודכן", - "applied-success": "העיצוב %1 הוחל בהצלחה", - "revert-success": "העיצובהוחזר לצבעים הבסיסיים" + "loading": "טוען עיצובים", + "homepage": "דף הפרוייקט", + "select-skin": "בחר עיצוב זה", + "current-skin": "עיצוב נוכחי", + "skin-updated": "עיצוב עודכן", + "applied-success": "עיצוב %1 הוחל בהצלחה", + "revert-success": "עיצוב הוחזר לצבעים בסיסיים" } \ No newline at end of file diff --git a/public/language/he/admin/development/logger.json b/public/language/he/admin/development/logger.json index 6ab9558149..4fdf500f14 100644 --- a/public/language/he/admin/development/logger.json +++ b/public/language/he/admin/development/logger.json @@ -1,12 +1,12 @@ { - "logger-settings": "Logger Settings", - "description": "By enabling the check boxes, you will receive logs to your terminal. If you specify a path, logs will then be saved to a file instead. HTTP logging is useful for collecting statistics about who, when, and what people access on your forum. In addition to logging HTTP requests, we can also log socket.io events. Socket.io logging, in combination with redis-cli monitor, can be very helpful for learning NodeBB's internals.", - "explanation": "Simply check/uncheck the logging settings to enable or disable logging on the fly. No restart needed.", - "enable-http": "Enable HTTP logging", - "enable-socket": "Enable socket.io event logging", - "file-path": "Path to log file", + "logger-settings": "הגדרות מנהל הרישום", + "description": "על-ידי הפיכת תיבות הסימון לזמינות, תקבל יומני רישום למסוף שלך. אם תציין נתיב, יומני הרישום יישמרו בקובץ במקום זאת. רישום HTTP שימושי לאיסוף נתונים סטטיסטיים אודות מי ומתי אנשים נכנסים לפורום שלך. בנוסף לרישום בקשות ה-HTTP, אנו יכולים גם לרשום אירועי Socket.io, אשר בשילוב עם מודד redis-cli, יכול להיות מאוד מועיל ללימוד הפנימיים של NodeBB.", + "explanation": "הפעל או ​בטל את סימון הגדרות הרישום בכדי לאפשר או להשבית כניסה במהירות. אין צורך בהפעלה מחדש.", + "enable-http": "הפעל רישום HTTP", + "enable-socket": "הפעל רישום אירועים ב-socket.io", + "file-path": "נתיב קובץ יומן רישום", "file-path-placeholder": "/path/to/log/file.log ::: leave blank to log to your terminal", - "control-panel": "Logger Control Panel", - "update-settings": "Update Logger Settings" + "control-panel": "לוח בקרת מנהל רישום", + "update-settings": "עדכן הגדרות מנהל רישום" } \ No newline at end of file diff --git a/public/language/he/admin/extend/rewards.json b/public/language/he/admin/extend/rewards.json index 3c4310ffef..aebf2d1986 100644 --- a/public/language/he/admin/extend/rewards.json +++ b/public/language/he/admin/extend/rewards.json @@ -1,17 +1,17 @@ { - "rewards": "פרסים", - "condition-if-users": "אם המשתמשים", - "condition-is": "הוא:", - "condition-then": "אז:", - "max-claims": "מספר הפעמים הניתן לדרוש פרס", + "rewards": "תגמולים", + "condition-if-users": "אם משתמש", + "condition-is": "Is:", + "condition-then": "תגמל ב:", + "max-claims": "מספר פעמים בה ניתן לדרוש תגמול", "zero-infinite": "הזן 0 לאינסוף", "delete": "מחק", "enable": "הפעל", - "disable": "בטל", - "control-panel": "בקרת פרסים", - "new-reward": "פרס חדש", + "disable": "השבת", + "control-panel": "בקרת תגמולים", + "new-reward": "תגמול חדש", - "alert.delete-success": "הפרס נמחק בהצלחה", - "alert.no-inputs-found": "פרס לא חוקי - לא נמצא מידע!", - "alert.save-success": "הפרסים נשמרו בהצלחה" + "alert.delete-success": "תגמול נמחק בהצלחה", + "alert.no-inputs-found": "תגמול לא חוקי - לא נמצא מידע!", + "alert.save-success": "תגמולים נשמרו בהצלחה" } \ No newline at end of file diff --git a/public/language/he/admin/manage/privileges.json b/public/language/he/admin/manage/privileges.json index fe96ec8647..c19d37d7c5 100644 --- a/public/language/he/admin/manage/privileges.json +++ b/public/language/he/admin/manage/privileges.json @@ -39,6 +39,7 @@ "admin-categories": "קטגוריות", "admin-privileges": "הרשאות", "admin-users": "משתמשים", + "admin-groups": "Groups", "admin-settings": "הגדרות", "alert.confirm-moderate": "האם אתה בטוח שברצונך להעניק הרשאות מודרטור לקבוצת משתמשים זו? הקבוצה היא ציבורית, וכל משתמש יכול להצטרף כרצונו.", diff --git a/public/language/he/admin/menu.json b/public/language/he/admin/menu.json index 7c79dd8167..ed2932db94 100644 --- a/public/language/he/admin/menu.json +++ b/public/language/he/admin/menu.json @@ -2,7 +2,7 @@ "dashboard": "לוח מחוונים", "section-general": "כללי", - "section-manage": "נהל", + "section-manage": "ניהול", "manage/categories": "קטגוריות", "manage/privileges": "הרשאות", "manage/tags": "תגיות", @@ -63,7 +63,7 @@ "advanced/logs": "רישומים", "advanced/errors": "שגיאות", "advanced/cache": "עוגיות", - "development/logger": "כותב הרישומים", + "development/logger": "מנהל הרישומים", "development/info": "מידע", "rebuild-and-restart-forum": "בנה והפעל מחדש את הפורום", diff --git a/public/language/he/admin/settings/chat.json b/public/language/he/admin/settings/chat.json index 67898611e7..23fbc77000 100644 --- a/public/language/he/admin/settings/chat.json +++ b/public/language/he/admin/settings/chat.json @@ -1,12 +1,12 @@ { - "chat-settings": "Chat Settings", - "disable": "Disable chat", - "disable-editing": "Disable chat message editing/deletion", - "disable-editing-help": "Administrators and global moderators are exempt from this restriction", - "max-length": "Maximum length of chat messages", - "max-room-size": "Maximum number of users in chat rooms", - "delay": "Time between chat messages in milliseconds", - "notification-delay": "Notification delay for chat messages. (0 for no delay)", - "restrictions.seconds-edit-after": "Number of seconds a chat message will remain editable. (0 disabled)", - "restrictions.seconds-delete-after": "Number of seconds a chat message will remain deletable. (0 disabled)" + "chat-settings": "הגדרות צ'אט", + "disable": "השבת צ'אט", + "disable-editing": "השבת עריכה/מחיקה של הודעות צ'אט", + "disable-editing-help": "מנהלים ומודרטורים גולבליים אינם נכללים בהגבלה זו", + "max-length": "אורך מקסימלי של הודעת צ'אט", + "max-room-size": "מספר המשתמשים המרבי בחדרי צ'אט", + "delay": "זמן המתנה בין הודעות צ'אט - באלפיות שניה", + "notification-delay": "עיכוב התראות להודעות צ'אט. (0 ללא עיכוב)", + "restrictions.seconds-edit-after": "מספר השניות בה ניתן לערוך הודעת צ'אט מרגע פרסומו (כתבו 0 להפוך ללא זמין)", + "restrictions.seconds-delete-after": "מספר השניות בה ניתן למחוק הודעת צ'אט מרגע פרסומו (כתבו 0 להפוך ללא זמין)" } \ No newline at end of file diff --git a/public/language/he/admin/settings/post.json b/public/language/he/admin/settings/post.json index c5e3c1838b..ecca8e24f7 100644 --- a/public/language/he/admin/settings/post.json +++ b/public/language/he/admin/settings/post.json @@ -20,9 +20,9 @@ "restrictions.seconds-between-new": "שניות בין פוסטים עבור משתמשים חדשים", "restrictions.rep-threshold": "סף המוניטין לפני ביטול המגבלות הללו", "restrictions.seconds-before-new": "שניות לפני שמשתמש חדש יוכל לפרסם את הפוסט הראשון שלו", - "restrictions.seconds-edit-after": "מספר השניות בה ניתן לערוך פוסט מרגע פרסומו (כתוב 0 בכדי להפוך ללא זמין)", - "restrictions.seconds-delete-after": "מספר השניות בה ניתן למחוק פוסט מרגע פרסומו (כתוב 0 בכדי להפוך ללא זמין)", - "restrictions.replies-no-delete": "מספר תשובות בנושא שלאחריו לא יוכל מפרסם הנושא למחקו (כתוב 0 בכדי להפוך ללא זמין)", + "restrictions.seconds-edit-after": "מספר השניות בה ניתן לערוך פוסט מרגע פרסומו (כתבו 0 להפוך ללא זמין)", + "restrictions.seconds-delete-after": "מספר השניות בה ניתן למחוק פוסט מרגע פרסומו (כתבו 0 להפוך ללא זמין)", + "restrictions.replies-no-delete": "מספר תשובות בנושא שלאחריו לא יוכל מפרסם הנושא למחקו (כתבו 0 להפוך ללא זמין)", "restrictions.min-title-length": "אורך כותרת מינימלי", "restrictions.max-title-length": "אורך כותרת מקסימלי", "restrictions.min-post-length": "אורך פוסט מינימלי", diff --git a/public/language/he/global.json b/public/language/he/global.json index 305f0018b8..1e8e110ff0 100644 --- a/public/language/he/global.json +++ b/public/language/he/global.json @@ -3,27 +3,27 @@ "search": "חיפוש", "buttons.close": "סגור", "403.title": "גישה נדחתה", - "403.message": "נראה שהגעת לעמוד שאין לך הרשאה לצפות בו", - "403.login": "נסה להתחבר.", + "403.message": "הגעתם לעמוד שאין לכם הרשאת צפייה בו", + "403.login": "נסו להתחבר.", "404.title": "לא נמצא", - "404.message": "נראה שהגעת לעמוד שלא קיים. חזור לעמוד הבית", + "404.message": "הגעתם לעמוד שאינו קיים. חזרו לעמוד הבית", "500.title": "שגיאה פנימית.", "500.message": "אופס! נראה שמשהו השתבש!", "400.title": "בקשה שגויה", - "400.message": "נראה שהלינק הזה לא תקין, בדוק ונסה שוב. אחרת, חזור לעמוד הבית.", + "400.message": "לינק לא תקין, בדקו ונסו שוב. או, חזרו לעמוד הבית.", "register": "הרשמה", "login": "התחברות", - "please_log_in": "אנא התחבר", + "please_log_in": "נא להתחבר", "logout": "יציאה", - "posting_restriction_info": "העלאת פוסטים מוגבלת למשתמשים רשומים בלבד כרגע, ", + "posting_restriction_info": "כתיבת פוסטים מאופשר למשתמשים רשומים בלבד, לחצו כאן כדי להתחבר.", "welcome_back": "ברוכים השבים", - "you_have_successfully_logged_in": "התחברת בהצלחה", + "you_have_successfully_logged_in": "התחברתם בהצלחה", "save_changes": "שמור שינויים", "save": "שמור", "close": "סגור", - "pagination": "עימוד", + "pagination": "הגדרות עמוד", "pagination.out_of": "%1 מתוך %2", - "pagination.enter_index": "יש להכניס אינדקס", + "pagination.enter_index": "הכניסו אינדקס", "header.admin": "ניהול", "header.categories": "קטגוריות", "header.recent": "פוסטים אחרונים", @@ -40,7 +40,7 @@ "header.navigation": "ניווט", "notifications.loading": "טוען התראות", "chats.loading": "טוען צ'אטים", - "motd.welcome": "ברוכים הבאים ל NodeBB, פלטפורמת הדיון של העתיד", + "motd.welcome": "ברוכים הבאים ל-NodeBB, פלטפורמות הדיון של העתיד", "previouspage": "העמוד הקודם", "nextpage": "העמוד הבא", "alert.success": "הצלחה", @@ -108,13 +108,13 @@ "uploads": "העלאות", "allowed-file-types": "פורמטי הקבצים המורשים הם %1", "unsaved-changes": "יש לך שינויים שאינם נשמרו. האם הנך בטוח שברצונך להמשיך?", - "reconnecting-message": "נראה שההתחברות שלך אל %1 אבדה, אנא המתן בזמן שהמערכת מנסה לחבר אותך מחדש", + "reconnecting-message": "החיבור ל-%1 אבד, אנא המתינו בזמן שאנו מנסים לחבר אתכם מחדש", "play": "נגן", "cookies.message": "אתר זה משתמש ב cookies על מנת לשפר את חוויות המשתמש.", "cookies.accept": "קיבלתי!", "cookies.learn_more": "למד עוד", "edited": "נערך", - "disabled": "לא מאופשר", + "disabled": "מושבת", "select": "בחר", - "user-search-prompt": "נסה כאן למציאת משתמשים" + "user-search-prompt": "הקלד כאן משהו על מנת למצוא משתמשים..." } \ No newline at end of file diff --git a/public/language/he/user.json b/public/language/he/user.json index 936bff3076..165bf72e38 100644 --- a/public/language/he/user.json +++ b/public/language/he/user.json @@ -118,11 +118,11 @@ "upvote-notif-freq.everyTen": "כל 10 הצבעות חיוביות", "upvote-notif-freq.threshold": "ב-1, 5, 10, 25, 50, 100, 150, 200...", "upvote-notif-freq.logarithmic": "ב-10, 100, 1000...", - "upvote-notif-freq.disabled": "מבוטל", + "upvote-notif-freq.disabled": "מושבת", "browsing": "הגדרות ניווט", "open_links_in_new_tab": "פתח קישורים חיצוניים בכרטיסייה חדשה", "enable_topic_searching": "הפעל חיפוש בתוך נושא", - "topic_search_help": "אם מופעל, החיפוש בתוך הנושא יעקוף את שיטת החיפוש של הדפדפן, ויאפשר לך לחפש בכל הנושא - ולא רק במה שמוצג על המסך, עם זאת בלחיצה שניה על Ctrl+5 ייפתח לך החיפוש הרגיל של הדפדפן", + "topic_search_help": "החיפוש בתוך הנושא יעקוף את שיטת החיפוש של הדפדפן, ויאפשר לך לחפש בכל הנושא - ולא רק במה שמוצג על המסך, עם זאת בלחיצה נוספת על Ctrl+5 ייפתח לך החיפוש הרגיל של הדפדפן", "update_url_with_post_index": "עדכון כתובת ה-URL עם אינדקס הפוסט בעת גלישה בנושאים", "scroll_to_my_post": "הצג את הפוסט לאחר פרסום התגובה", "follow_topics_you_reply_to": "עקוב אחר נושאים שהגבת עליהם", diff --git a/public/language/hr/admin/manage/privileges.json b/public/language/hr/admin/manage/privileges.json index d90fb7893d..92749720b0 100644 --- a/public/language/hr/admin/manage/privileges.json +++ b/public/language/hr/admin/manage/privileges.json @@ -39,6 +39,7 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-groups": "Groups", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", diff --git a/public/language/hu/admin/manage/privileges.json b/public/language/hu/admin/manage/privileges.json index d90fb7893d..92749720b0 100644 --- a/public/language/hu/admin/manage/privileges.json +++ b/public/language/hu/admin/manage/privileges.json @@ -39,6 +39,7 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-groups": "Groups", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", diff --git a/public/language/id/admin/manage/privileges.json b/public/language/id/admin/manage/privileges.json index d90fb7893d..92749720b0 100644 --- a/public/language/id/admin/manage/privileges.json +++ b/public/language/id/admin/manage/privileges.json @@ -39,6 +39,7 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-groups": "Groups", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", diff --git a/public/language/it/admin/manage/privileges.json b/public/language/it/admin/manage/privileges.json index 0b45ece233..e1d0d5b312 100644 --- a/public/language/it/admin/manage/privileges.json +++ b/public/language/it/admin/manage/privileges.json @@ -39,6 +39,7 @@ "admin-categories": "Categorie", "admin-privileges": "Privilegi", "admin-users": "Utenti", + "admin-groups": "Groups", "admin-settings": "Impostazioni", "alert.confirm-moderate": "Sei sicuro di voler concedere il privilegio di moderazione a questo gruppo di utenti? Questo gruppo è pubblico e tutti gli utenti possono iscriversi a piacimento.", diff --git a/public/language/ja/admin/manage/privileges.json b/public/language/ja/admin/manage/privileges.json index 8afef9882b..469bf95537 100644 --- a/public/language/ja/admin/manage/privileges.json +++ b/public/language/ja/admin/manage/privileges.json @@ -39,6 +39,7 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-groups": "Groups", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", diff --git a/public/language/ko/admin/manage/privileges.json b/public/language/ko/admin/manage/privileges.json index f135f9a1b8..b7fb6764a7 100644 --- a/public/language/ko/admin/manage/privileges.json +++ b/public/language/ko/admin/manage/privileges.json @@ -39,6 +39,7 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-groups": "Groups", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", diff --git a/public/language/lt/admin/manage/privileges.json b/public/language/lt/admin/manage/privileges.json index d90fb7893d..92749720b0 100644 --- a/public/language/lt/admin/manage/privileges.json +++ b/public/language/lt/admin/manage/privileges.json @@ -39,6 +39,7 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-groups": "Groups", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", diff --git a/public/language/lv/admin/manage/privileges.json b/public/language/lv/admin/manage/privileges.json index 1bd5ca057c..f83c523743 100644 --- a/public/language/lv/admin/manage/privileges.json +++ b/public/language/lv/admin/manage/privileges.json @@ -39,6 +39,7 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-groups": "Groups", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", diff --git a/public/language/ms/admin/manage/privileges.json b/public/language/ms/admin/manage/privileges.json index d90fb7893d..92749720b0 100644 --- a/public/language/ms/admin/manage/privileges.json +++ b/public/language/ms/admin/manage/privileges.json @@ -39,6 +39,7 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-groups": "Groups", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", diff --git a/public/language/nb/admin/manage/privileges.json b/public/language/nb/admin/manage/privileges.json index d90fb7893d..92749720b0 100644 --- a/public/language/nb/admin/manage/privileges.json +++ b/public/language/nb/admin/manage/privileges.json @@ -39,6 +39,7 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-groups": "Groups", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", diff --git a/public/language/nl/admin/manage/privileges.json b/public/language/nl/admin/manage/privileges.json index d90fb7893d..92749720b0 100644 --- a/public/language/nl/admin/manage/privileges.json +++ b/public/language/nl/admin/manage/privileges.json @@ -39,6 +39,7 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-groups": "Groups", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", diff --git a/public/language/pl/admin/manage/privileges.json b/public/language/pl/admin/manage/privileges.json index 4e845d4e35..e76a43981f 100644 --- a/public/language/pl/admin/manage/privileges.json +++ b/public/language/pl/admin/manage/privileges.json @@ -39,6 +39,7 @@ "admin-categories": "Kategorie", "admin-privileges": "Uprawnienia", "admin-users": "Użytkownicy", + "admin-groups": "Groups", "admin-settings": "Ustawienia", "alert.confirm-moderate": "Czy na pewno chcesz przyznać uprawnienia moderacji dla tej grupy użytkowników? Ta grupa jest publiczna i każdy użytkownik może do niej dołączyć.", diff --git a/public/language/pt-BR/admin/manage/privileges.json b/public/language/pt-BR/admin/manage/privileges.json index 8c669d01a5..2a96273dd5 100644 --- a/public/language/pt-BR/admin/manage/privileges.json +++ b/public/language/pt-BR/admin/manage/privileges.json @@ -39,6 +39,7 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-groups": "Groups", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", diff --git a/public/language/pt-PT/admin/manage/privileges.json b/public/language/pt-PT/admin/manage/privileges.json index ca35e46541..8a7eb57bdd 100644 --- a/public/language/pt-PT/admin/manage/privileges.json +++ b/public/language/pt-PT/admin/manage/privileges.json @@ -39,6 +39,7 @@ "admin-categories": "Categorias", "admin-privileges": "Privilégios", "admin-users": "Utilizadores", + "admin-groups": "Groups", "admin-settings": "Definições", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", diff --git a/public/language/ro/admin/manage/privileges.json b/public/language/ro/admin/manage/privileges.json index d90fb7893d..92749720b0 100644 --- a/public/language/ro/admin/manage/privileges.json +++ b/public/language/ro/admin/manage/privileges.json @@ -39,6 +39,7 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-groups": "Groups", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", diff --git a/public/language/ru/admin/manage/privileges.json b/public/language/ru/admin/manage/privileges.json index a09c20ab31..5e719e791c 100644 --- a/public/language/ru/admin/manage/privileges.json +++ b/public/language/ru/admin/manage/privileges.json @@ -39,6 +39,7 @@ "admin-categories": "Категории", "admin-privileges": "Права доступа", "admin-users": "Пользователи", + "admin-groups": "Groups", "admin-settings": "Настройки", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", diff --git a/public/language/rw/admin/manage/privileges.json b/public/language/rw/admin/manage/privileges.json index d90fb7893d..92749720b0 100644 --- a/public/language/rw/admin/manage/privileges.json +++ b/public/language/rw/admin/manage/privileges.json @@ -39,6 +39,7 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-groups": "Groups", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", diff --git a/public/language/sc/admin/manage/privileges.json b/public/language/sc/admin/manage/privileges.json index d90fb7893d..92749720b0 100644 --- a/public/language/sc/admin/manage/privileges.json +++ b/public/language/sc/admin/manage/privileges.json @@ -39,6 +39,7 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-groups": "Groups", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", diff --git a/public/language/sk/admin/manage/privileges.json b/public/language/sk/admin/manage/privileges.json index b787c7dd0f..c032ebf6f0 100644 --- a/public/language/sk/admin/manage/privileges.json +++ b/public/language/sk/admin/manage/privileges.json @@ -39,6 +39,7 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-groups": "Groups", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", diff --git a/public/language/sl/admin/manage/privileges.json b/public/language/sl/admin/manage/privileges.json index d90fb7893d..92749720b0 100644 --- a/public/language/sl/admin/manage/privileges.json +++ b/public/language/sl/admin/manage/privileges.json @@ -39,6 +39,7 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-groups": "Groups", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", diff --git a/public/language/sr/admin/manage/privileges.json b/public/language/sr/admin/manage/privileges.json index d90fb7893d..92749720b0 100644 --- a/public/language/sr/admin/manage/privileges.json +++ b/public/language/sr/admin/manage/privileges.json @@ -39,6 +39,7 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-groups": "Groups", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", diff --git a/public/language/sv/admin/manage/privileges.json b/public/language/sv/admin/manage/privileges.json index d90fb7893d..92749720b0 100644 --- a/public/language/sv/admin/manage/privileges.json +++ b/public/language/sv/admin/manage/privileges.json @@ -39,6 +39,7 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-groups": "Groups", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", diff --git a/public/language/th/admin/manage/privileges.json b/public/language/th/admin/manage/privileges.json index d90fb7893d..92749720b0 100644 --- a/public/language/th/admin/manage/privileges.json +++ b/public/language/th/admin/manage/privileges.json @@ -39,6 +39,7 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-groups": "Groups", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", diff --git a/public/language/tr/admin/manage/privileges.json b/public/language/tr/admin/manage/privileges.json index fd11b7201d..2509ae3845 100644 --- a/public/language/tr/admin/manage/privileges.json +++ b/public/language/tr/admin/manage/privileges.json @@ -39,6 +39,7 @@ "admin-categories": "Kategoriler", "admin-privileges": "Ayrıcalıklar", "admin-users": "Kullanıcılar", + "admin-groups": "Groups", "admin-settings": "Ayarlar", "alert.confirm-moderate": "Bu gruba yönetim ayrıcalıkları vermek istediğinize emin misiniz? Bu grup genele açık olduğundan her kullanıcı gruba katılabilir. ", diff --git a/public/language/uk/admin/manage/privileges.json b/public/language/uk/admin/manage/privileges.json index d1ddb942f3..2a7bdab902 100644 --- a/public/language/uk/admin/manage/privileges.json +++ b/public/language/uk/admin/manage/privileges.json @@ -39,6 +39,7 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-groups": "Groups", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", diff --git a/public/language/vi/admin/manage/privileges.json b/public/language/vi/admin/manage/privileges.json index d90fb7893d..92749720b0 100644 --- a/public/language/vi/admin/manage/privileges.json +++ b/public/language/vi/admin/manage/privileges.json @@ -39,6 +39,7 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-groups": "Groups", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", diff --git a/public/language/zh-CN/admin/manage/privileges.json b/public/language/zh-CN/admin/manage/privileges.json index 3d8b8bbd72..0897549b91 100644 --- a/public/language/zh-CN/admin/manage/privileges.json +++ b/public/language/zh-CN/admin/manage/privileges.json @@ -39,6 +39,7 @@ "admin-categories": "版块", "admin-privileges": "权限", "admin-users": "用户", + "admin-groups": "Groups", "admin-settings": "设置", "alert.confirm-moderate": "您确定要将审核权限授予此用户组吗?此用户组是公开的,任何用户都可以随意加入。", diff --git a/public/language/zh-TW/admin/manage/privileges.json b/public/language/zh-TW/admin/manage/privileges.json index 88d37d2f19..1714823a15 100644 --- a/public/language/zh-TW/admin/manage/privileges.json +++ b/public/language/zh-TW/admin/manage/privileges.json @@ -39,6 +39,7 @@ "admin-categories": "版面", "admin-privileges": "權限", "admin-users": "使用者", + "admin-groups": "Groups", "admin-settings": "設定", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", From 6d980d26d7c1bfa49a89cbfc9067b30bb2a1cf9c Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Wed, 23 Dec 2020 14:52:53 +0000 Subject: [PATCH 53/98] fix(deps): update dependency nodebb-plugin-composer-default to v6.5.5 --- install/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/package.json b/install/package.json index 5828f79509..e260bb27b3 100644 --- a/install/package.json +++ b/install/package.json @@ -91,7 +91,7 @@ "mousetrap": "^1.6.5", "@nodebb/bootswatch": "3.4.2", "nconf": "^0.11.0", - "nodebb-plugin-composer-default": "6.5.4", + "nodebb-plugin-composer-default": "6.5.5", "nodebb-plugin-dbsearch": "4.1.2", "nodebb-plugin-emoji": "^3.3.0", "nodebb-plugin-emoji-android": "2.0.0", From fcc1e24ad03721500a8d0922df3fbfb4aa53a673 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Wed, 23 Dec 2020 10:47:51 -0500 Subject: [PATCH 54/98] feat: rename admin middleware header hook --- src/middleware/admin.js | 9 +++++++-- src/plugins/hooks.js | 3 ++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/middleware/admin.js b/src/middleware/admin.js index da97a67d18..5bdc3a0e9c 100644 --- a/src/middleware/admin.js +++ b/src/middleware/admin.js @@ -78,9 +78,14 @@ middleware.renderHeader = async (req, res, data) => { templateValues.template = { name: res.locals.template }; templateValues.template[res.locals.template] = true; - - // Normally this should hook be automatically added by middleware.processRender(), but it seems to only be fired for page hooks, and not when called internally. + // remove @1.17.0 ({ templateData: templateValues } = await plugins.hooks.fire('filter:admin/header.build', { req, res, templateData: templateValues })); + ({ templateData: templateValues } = await plugins.hooks.fire('filter:middleware.renderAdminHeader', { + req, + res, + templateData: templateValues, + data, + })); return await req.app.renderAsync('admin/header', templateValues); }; diff --git a/src/plugins/hooks.js b/src/plugins/hooks.js index a2509b35dc..0ed353e946 100644 --- a/src/plugins/hooks.js +++ b/src/plugins/hooks.js @@ -9,7 +9,8 @@ const Hooks = {}; module.exports = Hooks; Hooks.deprecatedHooks = { - 'filter:privileges:isUserAllowedTo': 'filter:privileges:isAllowedTo', // 👋 @ 1.16.0 + 'filter:privileges:isUserAllowedTo': 'filter:privileges:isAllowedTo', // 👋 @ 1.17.0 + 'filter:admin/header.build': 'filter:middleware.renderAdminHeader', // 👋 @ 1.17.0 'filter:router.page': 'response:router.page', // 👋 @ 2.0.0 }; From f55dddb2befa6f3e02bbf1df9b6fc620c973a2a7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 23 Dec 2020 12:49:34 -0500 Subject: [PATCH 55/98] fix(deps): update dependency nodebb-widget-essentials to v5.0.1 (#9144) 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 e260bb27b3..a84d1aabb5 100644 --- a/install/package.json +++ b/install/package.json @@ -104,7 +104,7 @@ "nodebb-theme-persona": "10.3.12", "nodebb-theme-slick": "1.3.7", "nodebb-theme-vanilla": "11.3.10", - "nodebb-widget-essentials": "5.0.0", + "nodebb-widget-essentials": "5.0.1", "nodemailer": "^6.4.6", "nprogress": "0.2.0", "passport": "^0.4.1", From 3aa5beb8326ffa96c3ad4cbc151f5e6cfc65f592 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 23 Dec 2020 13:07:41 -0500 Subject: [PATCH 56/98] feat: allow multiple privileges to be defined for a given admin socket call --- src/socket.io/admin.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/socket.io/admin.js b/src/socket.io/admin.js index 631914f05d..bec2f0fae9 100644 --- a/src/socket.io/admin.js +++ b/src/socket.io/admin.js @@ -41,8 +41,9 @@ SocketAdmin.before = async function (socket, method) { } // Check admin privileges mapping (if not in mapping, deny access) - const privilege = privileges.admin.socketMap[method]; - if (privilege && await privileges.admin.can(privilege, socket.uid)) { + const privilegeSet = privileges.admin.socketMap[method].split(';'); + const hasPrivilege = (await Promise.all(privilegeSet.map(async privilege => privileges.admin.can(privilege, socket.uid)))).some(Boolean); + if (privilegeSet.length && hasPrivilege) { return; } From 5b8558e9a5a8f2febb1f9be3c67638edc582e2d7 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 23 Dec 2020 13:16:35 -0500 Subject: [PATCH 57/98] feat: allow dashes in privilege group names --- src/groups/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/groups/index.js b/src/groups/index.js index d8392623da..f98ef0e7a8 100644 --- a/src/groups/index.js +++ b/src/groups/index.js @@ -54,7 +54,7 @@ Groups.removeEphemeralGroups = function (groups) { return groups; }; -var isPrivilegeGroupRegex = /^cid:\d+:privileges:[\w:]+$/; +var isPrivilegeGroupRegex = /^cid:\d+:privileges:[\w\-:]+$/; Groups.isPrivilegeGroup = function (groupName) { return isPrivilegeGroupRegex.test(groupName); }; From fb46a8d9754efa80a06aea72697929ab1fac6dfc Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 23 Dec 2020 14:11:01 -0500 Subject: [PATCH 58/98] feat(acp): admins-mods privilege --- public/language/en-GB/admin/manage/privileges.json | 1 + src/controllers/admin.js | 2 ++ src/middleware/admin.js | 2 +- src/privileges/admin.js | 8 +++++++- src/views/admin/partials/menu.tpl | 4 ++-- 5 files changed, 13 insertions(+), 4 deletions(-) diff --git a/public/language/en-GB/admin/manage/privileges.json b/public/language/en-GB/admin/manage/privileges.json index 92749720b0..737a734f5a 100644 --- a/public/language/en-GB/admin/manage/privileges.json +++ b/public/language/en-GB/admin/manage/privileges.json @@ -39,6 +39,7 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-admins-mods": "Admins & Mods", "admin-groups": "Groups", "admin-settings": "Settings", diff --git a/src/controllers/admin.js b/src/controllers/admin.js index a9fcd83eb6..3ae851145a 100644 --- a/src/controllers/admin.js +++ b/src/controllers/admin.js @@ -44,6 +44,8 @@ adminController.routeIndex = async (req, res) => { return helpers.redirect(res, 'admin/manage/users'); } else if (privilegeSet['admin:groups']) { return helpers.redirect(res, 'admin/manage/groups'); + } else if (privilegeSet['admin:admins-mods']) { + return helpers.redirect(res, 'admin/manage/admins-mods'); } else if (privilegeSet['admin:settings']) { return helpers.redirect(res, 'admin/settings/general'); } diff --git a/src/middleware/admin.js b/src/middleware/admin.js index 5bdc3a0e9c..1a19d5c281 100644 --- a/src/middleware/admin.js +++ b/src/middleware/admin.js @@ -73,7 +73,7 @@ middleware.renderHeader = async (req, res, data) => { version: version, latestVersion: results.latestVersion, upgradeAvailable: results.latestVersion && semver.gt(results.latestVersion, version), - showManageMenu: results.privileges.superadmin || ['categories', 'privileges', 'users', 'groups', 'settings'].some(priv => results.privileges[`admin:${priv}`]), + showManageMenu: results.privileges.superadmin || ['categories', 'privileges', 'users', 'admins-mods', 'groups', 'settings'].some(priv => results.privileges[`admin:${priv}`]), }; templateValues.template = { name: res.locals.template }; diff --git a/src/privileges/admin.js b/src/privileges/admin.js index dcf8f5ff28..9b4d07a732 100644 --- a/src/privileges/admin.js +++ b/src/privileges/admin.js @@ -16,6 +16,7 @@ module.exports = function (privileges) { { name: '[[admin/manage/privileges:admin-dashboard]]' }, { name: '[[admin/manage/privileges:admin-categories]]' }, { name: '[[admin/manage/privileges:admin-privileges]]' }, + { name: '[[admin/manage/privileges:admin-admins-mods]]' }, { name: '[[admin/manage/privileges:admin-users]]' }, { name: '[[admin/manage/privileges:admin-groups]]' }, { name: '[[admin/manage/privileges:admin-settings]]' }, @@ -25,6 +26,7 @@ module.exports = function (privileges) { 'admin:dashboard', 'admin:categories', 'admin:privileges', + 'admin:admins-mods', 'admin:users', 'admin:groups', 'admin:settings', @@ -37,6 +39,7 @@ module.exports = function (privileges) { dashboard: 'admin:dashboard', 'manage/categories': 'admin:categories', 'manage/privileges': 'admin:privileges', + 'manage/admins-mods': 'admin:admins-mods', 'manage/users': 'admin:users', 'manage/groups': 'admin:groups', 'extend/plugins': 'admin:settings', @@ -65,11 +68,14 @@ module.exports = function (privileges) { 'admin.categories.copySettingsFrom': 'admin:categories', 'admin.categories.getPrivilegeSettings': 'admin:privileges', - 'admin.categories.setPrivilege': 'admin:privileges', + 'admin.categories.setPrivilege': 'admin:privileges;admin:admins-mods', 'admin.categories.copyPrivilegesToChildren': 'admin:privileges', 'admin.categories.copyPrivilegesFrom': 'admin:privileges', 'admin.categories.copyPrivilegesToAllCategories': 'admin:privileges', + 'admin.user.makeAdmins': 'admin:admins-mods', + 'admin.user.removeAdmins': 'admin:admins-mods', + 'admin.user.loadGroups': 'admin:users', 'admin.groups.join': 'admin:users', 'admin.groups.leave': 'admin:users', diff --git a/src/views/admin/partials/menu.tpl b/src/views/admin/partials/menu.tpl index ba84482e4a..ef2e3f1e16 100644 --- a/src/views/admin/partials/menu.tpl +++ b/src/views/admin/partials/menu.tpl @@ -19,8 +19,8 @@ {{{ if user.privileges.admin:privileges }}}
  • [[admin/menu:manage/privileges]]
  • {{{ end }}} {{{ if user.privileges.admin:users }}}
  • [[admin/menu:manage/users]]
  • {{{ end }}} {{{ if user.privileges.admin:groups }}}
  • [[admin/menu:manage/groups]]
  • {{{ end }}} + {{{ if user.privileges.admin:admins-mods }}}
  • [[admin/menu:manage/admins-mods]]
  • {{{ end }}} {{{ if user.privileges.superadmin }}} -
  • [[admin/menu:manage/admins-mods]]
  • [[admin/menu:manage/registration]]
  • [[admin/menu:manage/tags]]
  • [[admin/menu:manage/uploads]]
  • @@ -190,8 +190,8 @@ {{{ if user.privileges.admin:privileges }}}
  • [[admin/menu:manage/privileges]]
  • {{{ end }}} {{{ if user.privileges.admin:users }}}
  • [[admin/menu:manage/users]]
  • {{{ end }}} {{{ if user.privileges.admin:groups }}}
  • [[admin/menu:manage/groups]]
  • {{{ end }}} + {{{ if user.privileges.admin:admins-mods }}}
  • [[admin/menu:manage/admins-mods]]
  • {{{ end }}} {{{ if user.privileges.superadmin }}} -
  • [[admin/menu:manage/admins-mods]]
  • [[admin/menu:manage/registration]]
  • [[admin/menu:manage/tags]]
  • [[admin/menu:manage/uploads]]
  • From d90aa9580850b5ca955071bac1b8028f696a4328 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 23 Dec 2020 14:16:53 -0500 Subject: [PATCH 59/98] feat: add confirmation modal when assigning admin:admins-mods privilege --- public/language/en-GB/admin/manage/privileges.json | 1 + public/src/admin/manage/privileges.js | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/public/language/en-GB/admin/manage/privileges.json b/public/language/en-GB/admin/manage/privileges.json index 737a734f5a..8d9e59f86d 100644 --- a/public/language/en-GB/admin/manage/privileges.json +++ b/public/language/en-GB/admin/manage/privileges.json @@ -44,6 +44,7 @@ "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", + "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", "alert.confirm-save": "Please confirm your intention to save these privileges", "alert.saved": "Privilege changes saved and applied", "alert.confirm-discard": "Are you sure you wish to discard your privilege changes?", diff --git a/public/src/admin/manage/privileges.js b/public/src/admin/manage/privileges.js index 0fab84ed20..49757db41e 100644 --- a/public/src/admin/manage/privileges.js +++ b/public/src/admin/manage/privileges.js @@ -50,6 +50,15 @@ define('admin/manage/privileges', [ checkboxEl.prop('checked', !checkboxEl.prop('checked')); } }); + } else if (privilege.endsWith('admin:admins-mods') && state) { + bootbox.confirm('[[admin/manage/privileges:alert.confirm-admins-mods]]', function (confirm) { + if (confirm) { + wrapperEl.attr('data-delta', delta); + Privileges.exposeAssumedPrivileges(); + } else { + checkboxEl.prop('checked', !checkboxEl.prop('checked')); + } + }); } else { wrapperEl.attr('data-delta', delta); Privileges.exposeAssumedPrivileges(); From 223f0a5515f301eabd1a935547d2463f9b063da6 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 23 Dec 2020 17:44:17 -0500 Subject: [PATCH 60/98] feat(acp): admin tags privilege --- public/language/en-GB/admin/manage/privileges.json | 1 + src/controllers/admin.js | 2 ++ src/middleware/admin.js | 2 +- src/privileges/admin.js | 9 +++++++++ src/views/admin/partials/menu.tpl | 4 ++-- 5 files changed, 15 insertions(+), 3 deletions(-) diff --git a/public/language/en-GB/admin/manage/privileges.json b/public/language/en-GB/admin/manage/privileges.json index 8d9e59f86d..a6b39716fd 100644 --- a/public/language/en-GB/admin/manage/privileges.json +++ b/public/language/en-GB/admin/manage/privileges.json @@ -41,6 +41,7 @@ "admin-users": "Users", "admin-admins-mods": "Admins & Mods", "admin-groups": "Groups", + "admin-tags": "Tags", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", diff --git a/src/controllers/admin.js b/src/controllers/admin.js index 3ae851145a..d616a67a6a 100644 --- a/src/controllers/admin.js +++ b/src/controllers/admin.js @@ -46,6 +46,8 @@ adminController.routeIndex = async (req, res) => { return helpers.redirect(res, 'admin/manage/groups'); } else if (privilegeSet['admin:admins-mods']) { return helpers.redirect(res, 'admin/manage/admins-mods'); + } else if (privilegeSet['admin:tags']) { + return helpers.redirect(res, 'admin/manage/tags'); } else if (privilegeSet['admin:settings']) { return helpers.redirect(res, 'admin/settings/general'); } diff --git a/src/middleware/admin.js b/src/middleware/admin.js index 1a19d5c281..3c4cc9f9fc 100644 --- a/src/middleware/admin.js +++ b/src/middleware/admin.js @@ -73,7 +73,7 @@ middleware.renderHeader = async (req, res, data) => { version: version, latestVersion: results.latestVersion, upgradeAvailable: results.latestVersion && semver.gt(results.latestVersion, version), - showManageMenu: results.privileges.superadmin || ['categories', 'privileges', 'users', 'admins-mods', 'groups', 'settings'].some(priv => results.privileges[`admin:${priv}`]), + showManageMenu: results.privileges.superadmin || ['categories', 'privileges', 'users', 'admins-mods', 'groups', 'tags', 'settings'].some(priv => results.privileges[`admin:${priv}`]), }; templateValues.template = { name: res.locals.template }; diff --git a/src/privileges/admin.js b/src/privileges/admin.js index 9b4d07a732..95f0f27884 100644 --- a/src/privileges/admin.js +++ b/src/privileges/admin.js @@ -19,6 +19,7 @@ module.exports = function (privileges) { { name: '[[admin/manage/privileges:admin-admins-mods]]' }, { name: '[[admin/manage/privileges:admin-users]]' }, { name: '[[admin/manage/privileges:admin-groups]]' }, + { name: '[[admin/manage/privileges:admin-tags]]' }, { name: '[[admin/manage/privileges:admin-settings]]' }, ]; @@ -29,6 +30,7 @@ module.exports = function (privileges) { 'admin:admins-mods', 'admin:users', 'admin:groups', + 'admin:tags', 'admin:settings', ]; @@ -42,6 +44,8 @@ module.exports = function (privileges) { 'manage/admins-mods': 'admin:admins-mods', 'manage/users': 'admin:users', 'manage/groups': 'admin:groups', + 'manage/tags': 'admin:tags', + 'settings/tags': 'admin:tags', 'extend/plugins': 'admin:settings', 'extend/widgets': 'admin:settings', 'extend/rewards': 'admin:settings', @@ -89,6 +93,11 @@ module.exports = function (privileges) { 'admin.user.createUser': 'admin:users', 'admin.user.invite': 'admin:users', + 'admin.tags.create': 'admin:tags', + 'admin.tags.update': 'admin:tags', + 'admin.tags.rename': 'admin:tags', + 'admin.tags.deleteTags': 'admin:tags', + 'admin.getSearchDict': 'admin:settings', 'admin.config.setMultiple': 'admin:settings', 'admin.config.remove': 'admin:settings', diff --git a/src/views/admin/partials/menu.tpl b/src/views/admin/partials/menu.tpl index ef2e3f1e16..160c9ae69e 100644 --- a/src/views/admin/partials/menu.tpl +++ b/src/views/admin/partials/menu.tpl @@ -20,9 +20,9 @@ {{{ if user.privileges.admin:users }}}
  • [[admin/menu:manage/users]]
  • {{{ end }}} {{{ if user.privileges.admin:groups }}}
  • [[admin/menu:manage/groups]]
  • {{{ end }}} {{{ if user.privileges.admin:admins-mods }}}
  • [[admin/menu:manage/admins-mods]]
  • {{{ end }}} + {{{ if user.privileges.admin:tags }}}
  • [[admin/menu:manage/tags]]
  • {{{ end }}} {{{ if user.privileges.superadmin }}}
  • [[admin/menu:manage/registration]]
  • -
  • [[admin/menu:manage/tags]]
  • [[admin/menu:manage/uploads]]
  • [[admin/menu:manage/digest]]
  • @@ -191,9 +191,9 @@ {{{ if user.privileges.admin:users }}}
  • [[admin/menu:manage/users]]
  • {{{ end }}} {{{ if user.privileges.admin:groups }}}
  • [[admin/menu:manage/groups]]
  • {{{ end }}} {{{ if user.privileges.admin:admins-mods }}}
  • [[admin/menu:manage/admins-mods]]
  • {{{ end }}} + {{{ if user.privileges.admin:tags }}}
  • [[admin/menu:manage/tags]]
  • {{{ end }}} {{{ if user.privileges.superadmin }}}
  • [[admin/menu:manage/registration]]
  • -
  • [[admin/menu:manage/tags]]
  • [[admin/menu:manage/uploads]]
  • [[admin/menu:manage/digest]]
  • From 287403602415889651d2c69610036112a2c0db44 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 23 Dec 2020 17:48:54 -0500 Subject: [PATCH 61/98] fix: tests --- src/socket.io/admin.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/socket.io/admin.js b/src/socket.io/admin.js index bec2f0fae9..0cd8b3d452 100644 --- a/src/socket.io/admin.js +++ b/src/socket.io/admin.js @@ -41,7 +41,7 @@ SocketAdmin.before = async function (socket, method) { } // Check admin privileges mapping (if not in mapping, deny access) - const privilegeSet = privileges.admin.socketMap[method].split(';'); + const privilegeSet = privileges.admin.socketMap.hasOwnProperty(method) ? privileges.admin.socketMap[method].split(';') : []; const hasPrivilege = (await Promise.all(privilegeSet.map(async privilege => privileges.admin.can(privilege, socket.uid)))).some(Boolean); if (privilegeSet.length && hasPrivilege) { return; From d85e351f4399f261020fbd2aa1fe92b0e2b385f5 Mon Sep 17 00:00:00 2001 From: "Misty (Bot)" Date: Thu, 24 Dec 2020 09:08:02 +0000 Subject: [PATCH 62/98] Latest translations and fallbacks --- public/language/ar/admin/manage/privileges.json | 3 +++ public/language/bg/admin/manage/privileges.json | 3 +++ public/language/bn/admin/manage/privileges.json | 3 +++ public/language/cs/admin/manage/privileges.json | 3 +++ public/language/da/admin/manage/privileges.json | 3 +++ public/language/de/admin/manage/privileges.json | 3 +++ public/language/el/admin/manage/privileges.json | 3 +++ public/language/en-US/admin/manage/privileges.json | 3 +++ public/language/en-x-pirate/admin/manage/privileges.json | 3 +++ public/language/es/admin/manage/privileges.json | 3 +++ public/language/et/admin/manage/privileges.json | 3 +++ public/language/fa-IR/admin/manage/privileges.json | 3 +++ public/language/fi/admin/manage/privileges.json | 3 +++ public/language/fr/admin/manage/privileges.json | 3 +++ public/language/gl/admin/manage/privileges.json | 3 +++ public/language/he/admin/manage/privileges.json | 3 +++ public/language/he/global.json | 6 +++--- public/language/he/register.json | 4 ++-- public/language/hr/admin/manage/privileges.json | 3 +++ public/language/hu/admin/manage/privileges.json | 3 +++ public/language/id/admin/manage/privileges.json | 3 +++ public/language/it/admin/manage/privileges.json | 3 +++ public/language/ja/admin/manage/privileges.json | 3 +++ public/language/ko/admin/manage/privileges.json | 3 +++ public/language/lt/admin/manage/privileges.json | 3 +++ public/language/lv/admin/manage/privileges.json | 3 +++ public/language/ms/admin/manage/privileges.json | 3 +++ public/language/nb/admin/manage/privileges.json | 3 +++ public/language/nl/admin/manage/privileges.json | 3 +++ public/language/pl/admin/manage/privileges.json | 3 +++ public/language/pt-BR/admin/manage/privileges.json | 3 +++ public/language/pt-PT/admin/manage/privileges.json | 3 +++ public/language/ro/admin/manage/privileges.json | 3 +++ public/language/ru/admin/manage/privileges.json | 3 +++ public/language/rw/admin/manage/privileges.json | 3 +++ public/language/sc/admin/manage/privileges.json | 3 +++ public/language/sk/admin/manage/privileges.json | 3 +++ public/language/sl/admin/manage/privileges.json | 3 +++ public/language/sr/admin/manage/privileges.json | 3 +++ public/language/sv/admin/manage/privileges.json | 3 +++ public/language/th/admin/manage/privileges.json | 3 +++ public/language/tr/admin/manage/privileges.json | 3 +++ public/language/uk/admin/manage/privileges.json | 3 +++ public/language/vi/admin/manage/privileges.json | 3 +++ public/language/zh-CN/admin/manage/privileges.json | 3 +++ public/language/zh-TW/admin/manage/privileges.json | 3 +++ 46 files changed, 137 insertions(+), 5 deletions(-) diff --git a/public/language/ar/admin/manage/privileges.json b/public/language/ar/admin/manage/privileges.json index 92749720b0..a6b39716fd 100644 --- a/public/language/ar/admin/manage/privileges.json +++ b/public/language/ar/admin/manage/privileges.json @@ -39,10 +39,13 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-admins-mods": "Admins & Mods", "admin-groups": "Groups", + "admin-tags": "Tags", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", + "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", "alert.confirm-save": "Please confirm your intention to save these privileges", "alert.saved": "Privilege changes saved and applied", "alert.confirm-discard": "Are you sure you wish to discard your privilege changes?", diff --git a/public/language/bg/admin/manage/privileges.json b/public/language/bg/admin/manage/privileges.json index 5705f00dbf..beb89c2a07 100644 --- a/public/language/bg/admin/manage/privileges.json +++ b/public/language/bg/admin/manage/privileges.json @@ -39,10 +39,13 @@ "admin-categories": "Категории", "admin-privileges": "Правомощия", "admin-users": "Потребители", + "admin-admins-mods": "Admins & Mods", "admin-groups": "Groups", + "admin-tags": "Tags", "admin-settings": "Настройки", "alert.confirm-moderate": "Наистина ли искате да дадете правомощието за модериране на тази потребителска група? Тази група е публична и всеки може свободно да се присъедини към нея.", + "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", "alert.confirm-save": "Моля, потвърдете желанието си да запазите тези правомощия", "alert.saved": "Промените по правомощията са запазени и приложени", "alert.confirm-discard": "Наистина ли искате да отхвърлите промените по правомощията?", diff --git a/public/language/bn/admin/manage/privileges.json b/public/language/bn/admin/manage/privileges.json index 92749720b0..a6b39716fd 100644 --- a/public/language/bn/admin/manage/privileges.json +++ b/public/language/bn/admin/manage/privileges.json @@ -39,10 +39,13 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-admins-mods": "Admins & Mods", "admin-groups": "Groups", + "admin-tags": "Tags", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", + "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", "alert.confirm-save": "Please confirm your intention to save these privileges", "alert.saved": "Privilege changes saved and applied", "alert.confirm-discard": "Are you sure you wish to discard your privilege changes?", diff --git a/public/language/cs/admin/manage/privileges.json b/public/language/cs/admin/manage/privileges.json index 2580806abb..35e7a0dedd 100644 --- a/public/language/cs/admin/manage/privileges.json +++ b/public/language/cs/admin/manage/privileges.json @@ -39,10 +39,13 @@ "admin-categories": "Kategorie", "admin-privileges": "Oprávnění", "admin-users": "Uživatelé", + "admin-admins-mods": "Admins & Mods", "admin-groups": "Groups", + "admin-tags": "Tags", "admin-settings": "Nastavení", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", + "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", "alert.confirm-save": "Please confirm your intention to save these privileges", "alert.saved": "Privilege changes saved and applied", "alert.confirm-discard": "Are you sure you wish to discard your privilege changes?", diff --git a/public/language/da/admin/manage/privileges.json b/public/language/da/admin/manage/privileges.json index 92749720b0..a6b39716fd 100644 --- a/public/language/da/admin/manage/privileges.json +++ b/public/language/da/admin/manage/privileges.json @@ -39,10 +39,13 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-admins-mods": "Admins & Mods", "admin-groups": "Groups", + "admin-tags": "Tags", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", + "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", "alert.confirm-save": "Please confirm your intention to save these privileges", "alert.saved": "Privilege changes saved and applied", "alert.confirm-discard": "Are you sure you wish to discard your privilege changes?", diff --git a/public/language/de/admin/manage/privileges.json b/public/language/de/admin/manage/privileges.json index 73a8113964..c771cd2ded 100644 --- a/public/language/de/admin/manage/privileges.json +++ b/public/language/de/admin/manage/privileges.json @@ -39,10 +39,13 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Nutzende Personen", + "admin-admins-mods": "Admins & Mods", "admin-groups": "Groups", + "admin-tags": "Tags", "admin-settings": "Einstellungen", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", + "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", "alert.confirm-save": "Please confirm your intention to save these privileges", "alert.saved": "Privilege changes saved and applied", "alert.confirm-discard": "Are you sure you wish to discard your privilege changes?", diff --git a/public/language/el/admin/manage/privileges.json b/public/language/el/admin/manage/privileges.json index 92749720b0..a6b39716fd 100644 --- a/public/language/el/admin/manage/privileges.json +++ b/public/language/el/admin/manage/privileges.json @@ -39,10 +39,13 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-admins-mods": "Admins & Mods", "admin-groups": "Groups", + "admin-tags": "Tags", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", + "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", "alert.confirm-save": "Please confirm your intention to save these privileges", "alert.saved": "Privilege changes saved and applied", "alert.confirm-discard": "Are you sure you wish to discard your privilege changes?", diff --git a/public/language/en-US/admin/manage/privileges.json b/public/language/en-US/admin/manage/privileges.json index 92749720b0..a6b39716fd 100644 --- a/public/language/en-US/admin/manage/privileges.json +++ b/public/language/en-US/admin/manage/privileges.json @@ -39,10 +39,13 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-admins-mods": "Admins & Mods", "admin-groups": "Groups", + "admin-tags": "Tags", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", + "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", "alert.confirm-save": "Please confirm your intention to save these privileges", "alert.saved": "Privilege changes saved and applied", "alert.confirm-discard": "Are you sure you wish to discard your privilege changes?", diff --git a/public/language/en-x-pirate/admin/manage/privileges.json b/public/language/en-x-pirate/admin/manage/privileges.json index 92749720b0..a6b39716fd 100644 --- a/public/language/en-x-pirate/admin/manage/privileges.json +++ b/public/language/en-x-pirate/admin/manage/privileges.json @@ -39,10 +39,13 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-admins-mods": "Admins & Mods", "admin-groups": "Groups", + "admin-tags": "Tags", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", + "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", "alert.confirm-save": "Please confirm your intention to save these privileges", "alert.saved": "Privilege changes saved and applied", "alert.confirm-discard": "Are you sure you wish to discard your privilege changes?", diff --git a/public/language/es/admin/manage/privileges.json b/public/language/es/admin/manage/privileges.json index e551e0ecc8..4a2b3d375f 100644 --- a/public/language/es/admin/manage/privileges.json +++ b/public/language/es/admin/manage/privileges.json @@ -39,10 +39,13 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-admins-mods": "Admins & Mods", "admin-groups": "Groups", + "admin-tags": "Tags", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", + "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", "alert.confirm-save": "Please confirm your intention to save these privileges", "alert.saved": "Privilege changes saved and applied", "alert.confirm-discard": "Are you sure you wish to discard your privilege changes?", diff --git a/public/language/et/admin/manage/privileges.json b/public/language/et/admin/manage/privileges.json index 92749720b0..a6b39716fd 100644 --- a/public/language/et/admin/manage/privileges.json +++ b/public/language/et/admin/manage/privileges.json @@ -39,10 +39,13 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-admins-mods": "Admins & Mods", "admin-groups": "Groups", + "admin-tags": "Tags", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", + "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", "alert.confirm-save": "Please confirm your intention to save these privileges", "alert.saved": "Privilege changes saved and applied", "alert.confirm-discard": "Are you sure you wish to discard your privilege changes?", diff --git a/public/language/fa-IR/admin/manage/privileges.json b/public/language/fa-IR/admin/manage/privileges.json index 92749720b0..a6b39716fd 100644 --- a/public/language/fa-IR/admin/manage/privileges.json +++ b/public/language/fa-IR/admin/manage/privileges.json @@ -39,10 +39,13 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-admins-mods": "Admins & Mods", "admin-groups": "Groups", + "admin-tags": "Tags", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", + "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", "alert.confirm-save": "Please confirm your intention to save these privileges", "alert.saved": "Privilege changes saved and applied", "alert.confirm-discard": "Are you sure you wish to discard your privilege changes?", diff --git a/public/language/fi/admin/manage/privileges.json b/public/language/fi/admin/manage/privileges.json index 92749720b0..a6b39716fd 100644 --- a/public/language/fi/admin/manage/privileges.json +++ b/public/language/fi/admin/manage/privileges.json @@ -39,10 +39,13 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-admins-mods": "Admins & Mods", "admin-groups": "Groups", + "admin-tags": "Tags", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", + "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", "alert.confirm-save": "Please confirm your intention to save these privileges", "alert.saved": "Privilege changes saved and applied", "alert.confirm-discard": "Are you sure you wish to discard your privilege changes?", diff --git a/public/language/fr/admin/manage/privileges.json b/public/language/fr/admin/manage/privileges.json index 47279d3805..a8374f8998 100644 --- a/public/language/fr/admin/manage/privileges.json +++ b/public/language/fr/admin/manage/privileges.json @@ -39,10 +39,13 @@ "admin-categories": "Catégories", "admin-privileges": "Privilèges", "admin-users": "Utilisateurs", + "admin-admins-mods": "Admins & Mods", "admin-groups": "Groups", + "admin-tags": "Tags", "admin-settings": "Paramètres", "alert.confirm-moderate": "Voulez-vous vraiment accorder le privilège de modération à ce groupe d'utilisateurs ? Ce groupe est public et tous les utilisateurs peuvent le rejoindre à volonté.", + "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", "alert.confirm-save": "Veuillez confirmer votre intention de sauvegarder ces privilèges", "alert.saved": "Changements de privilèges enregistrés et appliqués", "alert.confirm-discard": "Êtes-vous sûr de vouloir annuler vos modifications de privilèges ?", diff --git a/public/language/gl/admin/manage/privileges.json b/public/language/gl/admin/manage/privileges.json index 92749720b0..a6b39716fd 100644 --- a/public/language/gl/admin/manage/privileges.json +++ b/public/language/gl/admin/manage/privileges.json @@ -39,10 +39,13 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-admins-mods": "Admins & Mods", "admin-groups": "Groups", + "admin-tags": "Tags", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", + "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", "alert.confirm-save": "Please confirm your intention to save these privileges", "alert.saved": "Privilege changes saved and applied", "alert.confirm-discard": "Are you sure you wish to discard your privilege changes?", diff --git a/public/language/he/admin/manage/privileges.json b/public/language/he/admin/manage/privileges.json index c19d37d7c5..a2bff34095 100644 --- a/public/language/he/admin/manage/privileges.json +++ b/public/language/he/admin/manage/privileges.json @@ -39,10 +39,13 @@ "admin-categories": "קטגוריות", "admin-privileges": "הרשאות", "admin-users": "משתמשים", + "admin-admins-mods": "Admins & Mods", "admin-groups": "Groups", + "admin-tags": "Tags", "admin-settings": "הגדרות", "alert.confirm-moderate": "האם אתה בטוח שברצונך להעניק הרשאות מודרטור לקבוצת משתמשים זו? הקבוצה היא ציבורית, וכל משתמש יכול להצטרף כרצונו.", + "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", "alert.confirm-save": "נא אשר את הגדרת ההרשאות", "alert.saved": "שינויי הרשאות נשמרו והוחלו", "alert.confirm-discard": "האם אתה בטוח שברצונך לבטל את שינויי ההרשאות שלך?", diff --git a/public/language/he/global.json b/public/language/he/global.json index 1e8e110ff0..ae63ba9ad9 100644 --- a/public/language/he/global.json +++ b/public/language/he/global.json @@ -40,14 +40,14 @@ "header.navigation": "ניווט", "notifications.loading": "טוען התראות", "chats.loading": "טוען צ'אטים", - "motd.welcome": "ברוכים הבאים ל-NodeBB, פלטפורמות הדיון של העתיד", + "motd.welcome": "ברוכים הבאים ל-NodeBB, פלטפורמות הדיון העתידני", "previouspage": "העמוד הקודם", "nextpage": "העמוד הבא", "alert.success": "הצלחה", "alert.error": "שגיאה", "alert.banned": "מורחק", - "alert.banned.message": "זה עתה נחסם חשבונך, הנך יוצא מן המערכת.", - "alert.unfollow": "אתה כבר לא עוקב אחרי %1!", + "alert.banned.message": "חשבונך נחסם, מתבצע יציאה מהמערכת.", + "alert.unfollow": "אינך עוקב יותר אחרי %1!", "alert.follow": "אתה עכשיו עוקב אחרי %1", "users": "משתמשים", "topics": "נושאים", diff --git a/public/language/he/register.json b/public/language/he/register.json index 0905771a84..463e6c6a26 100644 --- a/public/language/he/register.json +++ b/public/language/he/register.json @@ -17,12 +17,12 @@ "terms_of_use": "תנאי שימוש", "agree_to_terms_of_use": "אני מסכים לתנאי השימוש", "terms_of_use_error": "אתה מוכרח להסכים לתנאי השימוש", - "registration-added-to-queue": "הבקשה שלך להרשמה נשלחה. תקבל בקרוב מייל אישור לכתובת האימייל שהכנסת כשמנהל יאשר את הבקשה.", + "registration-added-to-queue": "בקשתך להרשמה נשלחה. במידה ובקשתך יאושר, ישלח אישור לכתובת האימייל שהכנסת.", "registration-queue-average-time": "הזמן הממוצע לאישור משתמשים הוא %1 שעות ו-%2 דקות.", "registration-queue-auto-approve-time": "חשבונך יאושר תוך %1 שעות.", "interstitial.intro": "אנו דורשים מידע נוסף לפני שנוכל ליצור עבורך את החשבון.", "interstitial.errors-found": "לא הצלחנו להשלים את הרישום שלך:", "gdpr_agree_data": "אני מסכים שפורום זה יאגור ויעבד את נתוני האישיים", "gdpr_agree_email": "אני מסכים לקבל מדי פעם מיילים מפורום זה עם סיכום נושאים מעניינים שפורסמו", - "gdpr_consent_denied": "אתה מוכרח להסכים לתנאים על מנת להרשם" + "gdpr_consent_denied": "אין אפשרות להירשם ללא אישור הסכמה על תנאים אלו." } \ No newline at end of file diff --git a/public/language/hr/admin/manage/privileges.json b/public/language/hr/admin/manage/privileges.json index 92749720b0..a6b39716fd 100644 --- a/public/language/hr/admin/manage/privileges.json +++ b/public/language/hr/admin/manage/privileges.json @@ -39,10 +39,13 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-admins-mods": "Admins & Mods", "admin-groups": "Groups", + "admin-tags": "Tags", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", + "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", "alert.confirm-save": "Please confirm your intention to save these privileges", "alert.saved": "Privilege changes saved and applied", "alert.confirm-discard": "Are you sure you wish to discard your privilege changes?", diff --git a/public/language/hu/admin/manage/privileges.json b/public/language/hu/admin/manage/privileges.json index 92749720b0..a6b39716fd 100644 --- a/public/language/hu/admin/manage/privileges.json +++ b/public/language/hu/admin/manage/privileges.json @@ -39,10 +39,13 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-admins-mods": "Admins & Mods", "admin-groups": "Groups", + "admin-tags": "Tags", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", + "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", "alert.confirm-save": "Please confirm your intention to save these privileges", "alert.saved": "Privilege changes saved and applied", "alert.confirm-discard": "Are you sure you wish to discard your privilege changes?", diff --git a/public/language/id/admin/manage/privileges.json b/public/language/id/admin/manage/privileges.json index 92749720b0..a6b39716fd 100644 --- a/public/language/id/admin/manage/privileges.json +++ b/public/language/id/admin/manage/privileges.json @@ -39,10 +39,13 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-admins-mods": "Admins & Mods", "admin-groups": "Groups", + "admin-tags": "Tags", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", + "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", "alert.confirm-save": "Please confirm your intention to save these privileges", "alert.saved": "Privilege changes saved and applied", "alert.confirm-discard": "Are you sure you wish to discard your privilege changes?", diff --git a/public/language/it/admin/manage/privileges.json b/public/language/it/admin/manage/privileges.json index e1d0d5b312..43f7591e5b 100644 --- a/public/language/it/admin/manage/privileges.json +++ b/public/language/it/admin/manage/privileges.json @@ -39,10 +39,13 @@ "admin-categories": "Categorie", "admin-privileges": "Privilegi", "admin-users": "Utenti", + "admin-admins-mods": "Admins & Mods", "admin-groups": "Groups", + "admin-tags": "Tags", "admin-settings": "Impostazioni", "alert.confirm-moderate": "Sei sicuro di voler concedere il privilegio di moderazione a questo gruppo di utenti? Questo gruppo è pubblico e tutti gli utenti possono iscriversi a piacimento.", + "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", "alert.confirm-save": "Si prega di confermare l'intenzione di salvare questi privilegi", "alert.saved": "Modifiche ai privilegi salvate e applicate", "alert.confirm-discard": "Sei sicuro di voler annullare le modifiche ai privilegi?", diff --git a/public/language/ja/admin/manage/privileges.json b/public/language/ja/admin/manage/privileges.json index 469bf95537..2e4296f089 100644 --- a/public/language/ja/admin/manage/privileges.json +++ b/public/language/ja/admin/manage/privileges.json @@ -39,10 +39,13 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-admins-mods": "Admins & Mods", "admin-groups": "Groups", + "admin-tags": "Tags", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", + "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", "alert.confirm-save": "Please confirm your intention to save these privileges", "alert.saved": "Privilege changes saved and applied", "alert.confirm-discard": "Are you sure you wish to discard your privilege changes?", diff --git a/public/language/ko/admin/manage/privileges.json b/public/language/ko/admin/manage/privileges.json index b7fb6764a7..ab386b05d7 100644 --- a/public/language/ko/admin/manage/privileges.json +++ b/public/language/ko/admin/manage/privileges.json @@ -39,10 +39,13 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-admins-mods": "Admins & Mods", "admin-groups": "Groups", + "admin-tags": "Tags", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", + "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", "alert.confirm-save": "Please confirm your intention to save these privileges", "alert.saved": "Privilege changes saved and applied", "alert.confirm-discard": "Are you sure you wish to discard your privilege changes?", diff --git a/public/language/lt/admin/manage/privileges.json b/public/language/lt/admin/manage/privileges.json index 92749720b0..a6b39716fd 100644 --- a/public/language/lt/admin/manage/privileges.json +++ b/public/language/lt/admin/manage/privileges.json @@ -39,10 +39,13 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-admins-mods": "Admins & Mods", "admin-groups": "Groups", + "admin-tags": "Tags", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", + "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", "alert.confirm-save": "Please confirm your intention to save these privileges", "alert.saved": "Privilege changes saved and applied", "alert.confirm-discard": "Are you sure you wish to discard your privilege changes?", diff --git a/public/language/lv/admin/manage/privileges.json b/public/language/lv/admin/manage/privileges.json index f83c523743..73d7c71ee3 100644 --- a/public/language/lv/admin/manage/privileges.json +++ b/public/language/lv/admin/manage/privileges.json @@ -39,10 +39,13 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-admins-mods": "Admins & Mods", "admin-groups": "Groups", + "admin-tags": "Tags", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", + "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", "alert.confirm-save": "Please confirm your intention to save these privileges", "alert.saved": "Privilege changes saved and applied", "alert.confirm-discard": "Are you sure you wish to discard your privilege changes?", diff --git a/public/language/ms/admin/manage/privileges.json b/public/language/ms/admin/manage/privileges.json index 92749720b0..a6b39716fd 100644 --- a/public/language/ms/admin/manage/privileges.json +++ b/public/language/ms/admin/manage/privileges.json @@ -39,10 +39,13 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-admins-mods": "Admins & Mods", "admin-groups": "Groups", + "admin-tags": "Tags", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", + "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", "alert.confirm-save": "Please confirm your intention to save these privileges", "alert.saved": "Privilege changes saved and applied", "alert.confirm-discard": "Are you sure you wish to discard your privilege changes?", diff --git a/public/language/nb/admin/manage/privileges.json b/public/language/nb/admin/manage/privileges.json index 92749720b0..a6b39716fd 100644 --- a/public/language/nb/admin/manage/privileges.json +++ b/public/language/nb/admin/manage/privileges.json @@ -39,10 +39,13 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-admins-mods": "Admins & Mods", "admin-groups": "Groups", + "admin-tags": "Tags", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", + "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", "alert.confirm-save": "Please confirm your intention to save these privileges", "alert.saved": "Privilege changes saved and applied", "alert.confirm-discard": "Are you sure you wish to discard your privilege changes?", diff --git a/public/language/nl/admin/manage/privileges.json b/public/language/nl/admin/manage/privileges.json index 92749720b0..a6b39716fd 100644 --- a/public/language/nl/admin/manage/privileges.json +++ b/public/language/nl/admin/manage/privileges.json @@ -39,10 +39,13 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-admins-mods": "Admins & Mods", "admin-groups": "Groups", + "admin-tags": "Tags", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", + "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", "alert.confirm-save": "Please confirm your intention to save these privileges", "alert.saved": "Privilege changes saved and applied", "alert.confirm-discard": "Are you sure you wish to discard your privilege changes?", diff --git a/public/language/pl/admin/manage/privileges.json b/public/language/pl/admin/manage/privileges.json index e76a43981f..856b6cd66b 100644 --- a/public/language/pl/admin/manage/privileges.json +++ b/public/language/pl/admin/manage/privileges.json @@ -39,10 +39,13 @@ "admin-categories": "Kategorie", "admin-privileges": "Uprawnienia", "admin-users": "Użytkownicy", + "admin-admins-mods": "Admins & Mods", "admin-groups": "Groups", + "admin-tags": "Tags", "admin-settings": "Ustawienia", "alert.confirm-moderate": "Czy na pewno chcesz przyznać uprawnienia moderacji dla tej grupy użytkowników? Ta grupa jest publiczna i każdy użytkownik może do niej dołączyć.", + "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", "alert.confirm-save": "Potwierdź zamiar zapisania uprawnień", "alert.saved": "Zapisano i zastosowano zmiany w uprawnieniach", "alert.confirm-discard": "Czy na pewno chcesz odrzucić wprowadzone zmiany w uprawnieniach?", diff --git a/public/language/pt-BR/admin/manage/privileges.json b/public/language/pt-BR/admin/manage/privileges.json index 2a96273dd5..e181f0310c 100644 --- a/public/language/pt-BR/admin/manage/privileges.json +++ b/public/language/pt-BR/admin/manage/privileges.json @@ -39,10 +39,13 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-admins-mods": "Admins & Mods", "admin-groups": "Groups", + "admin-tags": "Tags", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", + "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", "alert.confirm-save": "Please confirm your intention to save these privileges", "alert.saved": "Privilege changes saved and applied", "alert.confirm-discard": "Are you sure you wish to discard your privilege changes?", diff --git a/public/language/pt-PT/admin/manage/privileges.json b/public/language/pt-PT/admin/manage/privileges.json index 8a7eb57bdd..e6621e8a6c 100644 --- a/public/language/pt-PT/admin/manage/privileges.json +++ b/public/language/pt-PT/admin/manage/privileges.json @@ -39,10 +39,13 @@ "admin-categories": "Categorias", "admin-privileges": "Privilégios", "admin-users": "Utilizadores", + "admin-admins-mods": "Admins & Mods", "admin-groups": "Groups", + "admin-tags": "Tags", "admin-settings": "Definições", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", + "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", "alert.confirm-save": "Please confirm your intention to save these privileges", "alert.saved": "Privilege changes saved and applied", "alert.confirm-discard": "Are you sure you wish to discard your privilege changes?", diff --git a/public/language/ro/admin/manage/privileges.json b/public/language/ro/admin/manage/privileges.json index 92749720b0..a6b39716fd 100644 --- a/public/language/ro/admin/manage/privileges.json +++ b/public/language/ro/admin/manage/privileges.json @@ -39,10 +39,13 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-admins-mods": "Admins & Mods", "admin-groups": "Groups", + "admin-tags": "Tags", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", + "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", "alert.confirm-save": "Please confirm your intention to save these privileges", "alert.saved": "Privilege changes saved and applied", "alert.confirm-discard": "Are you sure you wish to discard your privilege changes?", diff --git a/public/language/ru/admin/manage/privileges.json b/public/language/ru/admin/manage/privileges.json index 5e719e791c..fb73b8749a 100644 --- a/public/language/ru/admin/manage/privileges.json +++ b/public/language/ru/admin/manage/privileges.json @@ -39,10 +39,13 @@ "admin-categories": "Категории", "admin-privileges": "Права доступа", "admin-users": "Пользователи", + "admin-admins-mods": "Admins & Mods", "admin-groups": "Groups", + "admin-tags": "Tags", "admin-settings": "Настройки", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", + "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", "alert.confirm-save": "Please confirm your intention to save these privileges", "alert.saved": "Privilege changes saved and applied", "alert.confirm-discard": "Are you sure you wish to discard your privilege changes?", diff --git a/public/language/rw/admin/manage/privileges.json b/public/language/rw/admin/manage/privileges.json index 92749720b0..a6b39716fd 100644 --- a/public/language/rw/admin/manage/privileges.json +++ b/public/language/rw/admin/manage/privileges.json @@ -39,10 +39,13 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-admins-mods": "Admins & Mods", "admin-groups": "Groups", + "admin-tags": "Tags", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", + "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", "alert.confirm-save": "Please confirm your intention to save these privileges", "alert.saved": "Privilege changes saved and applied", "alert.confirm-discard": "Are you sure you wish to discard your privilege changes?", diff --git a/public/language/sc/admin/manage/privileges.json b/public/language/sc/admin/manage/privileges.json index 92749720b0..a6b39716fd 100644 --- a/public/language/sc/admin/manage/privileges.json +++ b/public/language/sc/admin/manage/privileges.json @@ -39,10 +39,13 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-admins-mods": "Admins & Mods", "admin-groups": "Groups", + "admin-tags": "Tags", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", + "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", "alert.confirm-save": "Please confirm your intention to save these privileges", "alert.saved": "Privilege changes saved and applied", "alert.confirm-discard": "Are you sure you wish to discard your privilege changes?", diff --git a/public/language/sk/admin/manage/privileges.json b/public/language/sk/admin/manage/privileges.json index c032ebf6f0..c8a7ea4171 100644 --- a/public/language/sk/admin/manage/privileges.json +++ b/public/language/sk/admin/manage/privileges.json @@ -39,10 +39,13 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-admins-mods": "Admins & Mods", "admin-groups": "Groups", + "admin-tags": "Tags", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", + "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", "alert.confirm-save": "Please confirm your intention to save these privileges", "alert.saved": "Privilege changes saved and applied", "alert.confirm-discard": "Are you sure you wish to discard your privilege changes?", diff --git a/public/language/sl/admin/manage/privileges.json b/public/language/sl/admin/manage/privileges.json index 92749720b0..a6b39716fd 100644 --- a/public/language/sl/admin/manage/privileges.json +++ b/public/language/sl/admin/manage/privileges.json @@ -39,10 +39,13 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-admins-mods": "Admins & Mods", "admin-groups": "Groups", + "admin-tags": "Tags", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", + "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", "alert.confirm-save": "Please confirm your intention to save these privileges", "alert.saved": "Privilege changes saved and applied", "alert.confirm-discard": "Are you sure you wish to discard your privilege changes?", diff --git a/public/language/sr/admin/manage/privileges.json b/public/language/sr/admin/manage/privileges.json index 92749720b0..a6b39716fd 100644 --- a/public/language/sr/admin/manage/privileges.json +++ b/public/language/sr/admin/manage/privileges.json @@ -39,10 +39,13 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-admins-mods": "Admins & Mods", "admin-groups": "Groups", + "admin-tags": "Tags", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", + "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", "alert.confirm-save": "Please confirm your intention to save these privileges", "alert.saved": "Privilege changes saved and applied", "alert.confirm-discard": "Are you sure you wish to discard your privilege changes?", diff --git a/public/language/sv/admin/manage/privileges.json b/public/language/sv/admin/manage/privileges.json index 92749720b0..a6b39716fd 100644 --- a/public/language/sv/admin/manage/privileges.json +++ b/public/language/sv/admin/manage/privileges.json @@ -39,10 +39,13 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-admins-mods": "Admins & Mods", "admin-groups": "Groups", + "admin-tags": "Tags", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", + "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", "alert.confirm-save": "Please confirm your intention to save these privileges", "alert.saved": "Privilege changes saved and applied", "alert.confirm-discard": "Are you sure you wish to discard your privilege changes?", diff --git a/public/language/th/admin/manage/privileges.json b/public/language/th/admin/manage/privileges.json index 92749720b0..a6b39716fd 100644 --- a/public/language/th/admin/manage/privileges.json +++ b/public/language/th/admin/manage/privileges.json @@ -39,10 +39,13 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-admins-mods": "Admins & Mods", "admin-groups": "Groups", + "admin-tags": "Tags", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", + "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", "alert.confirm-save": "Please confirm your intention to save these privileges", "alert.saved": "Privilege changes saved and applied", "alert.confirm-discard": "Are you sure you wish to discard your privilege changes?", diff --git a/public/language/tr/admin/manage/privileges.json b/public/language/tr/admin/manage/privileges.json index 2509ae3845..d6e2024a65 100644 --- a/public/language/tr/admin/manage/privileges.json +++ b/public/language/tr/admin/manage/privileges.json @@ -39,10 +39,13 @@ "admin-categories": "Kategoriler", "admin-privileges": "Ayrıcalıklar", "admin-users": "Kullanıcılar", + "admin-admins-mods": "Admins & Mods", "admin-groups": "Groups", + "admin-tags": "Tags", "admin-settings": "Ayarlar", "alert.confirm-moderate": "Bu gruba yönetim ayrıcalıkları vermek istediğinize emin misiniz? Bu grup genele açık olduğundan her kullanıcı gruba katılabilir. ", + "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", "alert.confirm-save": "Lütfen ayrıcalıkları kaydetme isteğinizi onaylayınız", "alert.saved": "Ayrıcalık değişiklikleri kaydedildi ve uygulandı", "alert.confirm-discard": "Ayrıcalık değişikliklerini iptal etmek istediğinize emin misiniz?", diff --git a/public/language/uk/admin/manage/privileges.json b/public/language/uk/admin/manage/privileges.json index 2a7bdab902..fc3c530b4b 100644 --- a/public/language/uk/admin/manage/privileges.json +++ b/public/language/uk/admin/manage/privileges.json @@ -39,10 +39,13 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-admins-mods": "Admins & Mods", "admin-groups": "Groups", + "admin-tags": "Tags", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", + "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", "alert.confirm-save": "Please confirm your intention to save these privileges", "alert.saved": "Privilege changes saved and applied", "alert.confirm-discard": "Are you sure you wish to discard your privilege changes?", diff --git a/public/language/vi/admin/manage/privileges.json b/public/language/vi/admin/manage/privileges.json index 92749720b0..a6b39716fd 100644 --- a/public/language/vi/admin/manage/privileges.json +++ b/public/language/vi/admin/manage/privileges.json @@ -39,10 +39,13 @@ "admin-categories": "Categories", "admin-privileges": "Privileges", "admin-users": "Users", + "admin-admins-mods": "Admins & Mods", "admin-groups": "Groups", + "admin-tags": "Tags", "admin-settings": "Settings", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", + "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", "alert.confirm-save": "Please confirm your intention to save these privileges", "alert.saved": "Privilege changes saved and applied", "alert.confirm-discard": "Are you sure you wish to discard your privilege changes?", diff --git a/public/language/zh-CN/admin/manage/privileges.json b/public/language/zh-CN/admin/manage/privileges.json index 0897549b91..b4124dee8e 100644 --- a/public/language/zh-CN/admin/manage/privileges.json +++ b/public/language/zh-CN/admin/manage/privileges.json @@ -39,10 +39,13 @@ "admin-categories": "版块", "admin-privileges": "权限", "admin-users": "用户", + "admin-admins-mods": "Admins & Mods", "admin-groups": "Groups", + "admin-tags": "Tags", "admin-settings": "设置", "alert.confirm-moderate": "您确定要将审核权限授予此用户组吗?此用户组是公开的,任何用户都可以随意加入。", + "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", "alert.confirm-save": "请验证您保存这些权限的目的", "alert.saved": "权限修改已保存并应用", "alert.confirm-discard": "您确定要取消权限修改吗?", diff --git a/public/language/zh-TW/admin/manage/privileges.json b/public/language/zh-TW/admin/manage/privileges.json index 1714823a15..1fd71dbb3b 100644 --- a/public/language/zh-TW/admin/manage/privileges.json +++ b/public/language/zh-TW/admin/manage/privileges.json @@ -39,10 +39,13 @@ "admin-categories": "版面", "admin-privileges": "權限", "admin-users": "使用者", + "admin-admins-mods": "Admins & Mods", "admin-groups": "Groups", + "admin-tags": "Tags", "admin-settings": "設定", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", + "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", "alert.confirm-save": "Please confirm your intention to save these privileges", "alert.saved": "Privilege changes saved and applied", "alert.confirm-discard": "Are you sure you wish to discard your privilege changes?", From 1dd1d3b064362d2514fa3d12a23f0d8ca3c3c006 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Fri, 25 Dec 2020 01:39:30 +0000 Subject: [PATCH 63/98] fix(deps): update dependency nodebb-widget-essentials to v5.0.2 --- install/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/package.json b/install/package.json index a84d1aabb5..5f09356944 100644 --- a/install/package.json +++ b/install/package.json @@ -104,7 +104,7 @@ "nodebb-theme-persona": "10.3.12", "nodebb-theme-slick": "1.3.7", "nodebb-theme-vanilla": "11.3.10", - "nodebb-widget-essentials": "5.0.1", + "nodebb-widget-essentials": "5.0.2", "nodemailer": "^6.4.6", "nprogress": "0.2.0", "passport": "^0.4.1", From 183cabe90fbd5cc56ec9c6e982015a48ac526384 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Fri, 25 Dec 2020 23:19:45 -0500 Subject: [PATCH 64/98] fix: #9150, fix selector so it doesn't add img-responsive to profile pics --- public/src/client/account/profile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/src/client/account/profile.js b/public/src/client/account/profile.js index f69941e192..d75e451615 100644 --- a/public/src/client/account/profile.js +++ b/public/src/client/account/profile.js @@ -19,7 +19,7 @@ define('forum/account/profile', [ }; function processPage() { - $('[component="posts"] img:not(.not-responsive), [component="aboutme"] img:not(.not-responsive)').addClass('img-responsive'); + $('[component="posts"] [component="post/content"] img:not(.not-responsive), [component="aboutme"] img:not(.not-responsive)').addClass('img-responsive'); } function onUserStatusChange(data) { From 29fcdc4c224a63aed5d91805edd1560f35bc9081 Mon Sep 17 00:00:00 2001 From: "Misty (Bot)" Date: Sat, 26 Dec 2020 09:06:53 +0000 Subject: [PATCH 65/98] Latest translations and fallbacks --- public/language/it/admin/manage/privileges.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/public/language/it/admin/manage/privileges.json b/public/language/it/admin/manage/privileges.json index 43f7591e5b..6269fa30ae 100644 --- a/public/language/it/admin/manage/privileges.json +++ b/public/language/it/admin/manage/privileges.json @@ -39,13 +39,13 @@ "admin-categories": "Categorie", "admin-privileges": "Privilegi", "admin-users": "Utenti", - "admin-admins-mods": "Admins & Mods", - "admin-groups": "Groups", - "admin-tags": "Tags", + "admin-admins-mods": "Amministratore & Moderatori", + "admin-groups": "Gruppi", + "admin-tags": "Tag", "admin-settings": "Impostazioni", "alert.confirm-moderate": "Sei sicuro di voler concedere il privilegio di moderazione a questo gruppo di utenti? Questo gruppo è pubblico e tutti gli utenti possono iscriversi a piacimento.", - "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", + "alert.confirm-admins-mods": "Sei sicuro di voler concedere i privilegi di "Amministratori & Moderatori" a questo utente/gruppo? Gli utenti con questo privilegio possono promuovere e retrocedere altri utenti in posizioni privilegiate, compreso il super amministratore", "alert.confirm-save": "Si prega di confermare l'intenzione di salvare questi privilegi", "alert.saved": "Modifiche ai privilegi salvate e applicate", "alert.confirm-discard": "Sei sicuro di voler annullare le modifiche ai privilegi?", From f1ec4961d9a3649c2fa9d15d3082b259144da6db Mon Sep 17 00:00:00 2001 From: "Misty (Bot)" Date: Sun, 27 Dec 2020 09:06:08 +0000 Subject: [PATCH 66/98] Latest translations and fallbacks --- public/language/fr/admin/manage/privileges.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/public/language/fr/admin/manage/privileges.json b/public/language/fr/admin/manage/privileges.json index a8374f8998..2ed0770103 100644 --- a/public/language/fr/admin/manage/privileges.json +++ b/public/language/fr/admin/manage/privileges.json @@ -39,13 +39,13 @@ "admin-categories": "Catégories", "admin-privileges": "Privilèges", "admin-users": "Utilisateurs", - "admin-admins-mods": "Admins & Mods", - "admin-groups": "Groups", - "admin-tags": "Tags", + "admin-admins-mods": "Admin & Modo", + "admin-groups": "Groupes", + "admin-tags": "Mots Clés", "admin-settings": "Paramètres", "alert.confirm-moderate": "Voulez-vous vraiment accorder le privilège de modération à ce groupe d'utilisateurs ? Ce groupe est public et tous les utilisateurs peuvent le rejoindre à volonté.", - "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", + "alert.confirm-admins-mods": "Voulez-vous vraiment attribuer les droits aux & quot; d'Administrations & amp; Modérations & quot; à cet utilisateur / groupe? Les utilisateurs disposant de ce privilège peuvent promouvoir et rétrograder d'autres utilisateurs à des postes privilégiés, y compris le super administrateur", "alert.confirm-save": "Veuillez confirmer votre intention de sauvegarder ces privilèges", "alert.saved": "Changements de privilèges enregistrés et appliqués", "alert.confirm-discard": "Êtes-vous sûr de vouloir annuler vos modifications de privilèges ?", From 20c1b684ed9a4522d6c5ee69a0151be5b68c947f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Sun, 27 Dec 2020 21:18:41 -0500 Subject: [PATCH 67/98] fix: #9151, dont use service worker for posts requests --- public/src/service-worker.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/public/src/service-worker.js b/public/src/service-worker.js index 57a2243742..babbf24f19 100644 --- a/public/src/service-worker.js +++ b/public/src/service-worker.js @@ -1,6 +1,14 @@ 'use strict'; self.addEventListener('fetch', function (event) { + // This is the code that ignores post requests + // https://github.com/NodeBB/NodeBB/issues/9151 + // https://github.com/w3c/ServiceWorker/issues/1141 + // https://stackoverflow.com/questions/54448367/ajax-xmlhttprequest-progress-monitoring-doesnt-work-with-service-workers + if (event.request.method === 'POST') { + return; + } + event.respondWith(caches.match(event.request).then(function (response) { if (!response) { return fetch(event.request); From d27815a8c04ae69de0e0c27e08e02bac449a437f Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 28 Dec 2020 09:50:50 -0500 Subject: [PATCH 68/98] fix: #9149, incorrect client-side `disableChatMessageEditing` value for admins/gmods --- src/controllers/api.js | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/controllers/api.js b/src/controllers/api.js index 57e98ed0b7..80b13f961d 100644 --- a/src/controllers/api.js +++ b/src/controllers/api.js @@ -22,7 +22,7 @@ const socketioOrigins = nconf.get('socket.io:origins'); const websocketAddress = nconf.get('socket.io:address') || ''; apiController.loadConfig = async function (req) { - let config = { + const config = { relative_path, upload_url, assetBaseUrl: `${relative_path}/assets`, @@ -83,8 +83,12 @@ apiController.loadConfig = async function (req) { }; let settings = config; + let isAdminOrGlobalMod; if (req.loggedIn) { - settings = await user.getSettings(req.uid); + ([settings, isAdminOrGlobalMod] = await Promise.all([ + user.getSettings(req.uid), + user.isAdminOrGlobalMod(req.uid), + ])); } // Handle old skin configs @@ -101,8 +105,11 @@ apiController.loadConfig = async function (req) { config.categoryTopicSort = settings.categoryTopicSort || config.categoryTopicSort; config.topicSearchEnabled = settings.topicSearchEnabled || false; config.bootswatchSkin = (meta.config.disableCustomUserSkins !== 1 && settings.bootswatchSkin && settings.bootswatchSkin !== '') ? settings.bootswatchSkin : ''; - config = await plugins.hooks.fire('filter:config.get', config); - return config; + + // Overrides based on privilege + config.disableChatMessageEditing = isAdminOrGlobalMod ? false : config.disableChatMessageEditing; + + return await plugins.hooks.fire('filter:config.get', config); }; apiController.getConfig = async function (req, res) { From 895e3d939e8013b902229f644bc9c805c3ba8777 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 28 Dec 2020 10:20:52 -0500 Subject: [PATCH 69/98] fix: #9149, server-side handling of disableChatMessageEditing --- src/messaging/edit.js | 14 ++++++------- test/messaging.js | 47 +++++++++++++++++++++++++++++++++++++------ 2 files changed, 48 insertions(+), 13 deletions(-) diff --git a/src/messaging/edit.js b/src/messaging/edit.js index 3e94d55bbb..2ad14d2126 100644 --- a/src/messaging/edit.js +++ b/src/messaging/edit.js @@ -47,9 +47,11 @@ module.exports = function (Messaging) { durationConfig = 'chatDeleteDuration'; } + const isAdminOrGlobalMod = await user.isAdminOrGlobalMod(uid); + if (meta.config.disableChat) { throw new Error('[[error:chat-disabled]]'); - } else if (meta.config.disableChatMessageEditing) { + } else if (!isAdminOrGlobalMod && meta.config.disableChatMessageEditing) { throw new Error('[[error:chat-message-editing-disabled]]'); } @@ -57,19 +59,17 @@ module.exports = function (Messaging) { if (userData.banned) { throw new Error('[[error:user-banned]]'); } + const canChat = await privileges.global.can('chat', uid); if (!canChat) { throw new Error('[[error:no-privileges]]'); } - const [isAdmin, messageData] = await Promise.all([ - user.isAdministrator(uid), - Messaging.getMessageFields(messageId, ['fromuid', 'timestamp', 'system']), - ]); - - if (isAdmin && !messageData.system) { + const messageData = await Messaging.getMessageFields(messageId, ['fromuid', 'timestamp', 'system']); + if (isAdminOrGlobalMod && !messageData.system) { return; } + const chatConfigDuration = meta.config[durationConfig]; if (chatConfigDuration && Date.now() - messageData.timestamp > chatConfigDuration * 1000) { throw new Error('[[error:chat-' + type + '-duration-expired, ' + meta.config[durationConfig] + ']]'); diff --git a/test/messaging.js b/test/messaging.js index ae6f3f164c..3ebe798a98 100644 --- a/test/messaging.js +++ b/test/messaging.js @@ -597,12 +597,15 @@ describe('Messaging Library', function () { describe('edit/delete', function () { var socketModules = require('../src/socket.io/modules'); var mid; - before(function (done) { - socketModules.chats.send({ uid: fooUid }, { roomId: roomId, message: 'first chat message' }, function (err, messageData) { - assert.ifError(err); - mid = messageData.mid; - done(); - }); + let mid2; + before(async function () { + await socketModules.chats.addUserToRoom({ uid: fooUid }, { roomId: roomId, username: 'baz' }); + mid = (await socketModules.chats.send({ uid: fooUid }, { roomId: roomId, message: 'first chat message' })).mid; + mid2 = (await socketModules.chats.send({ uid: bazUid }, { roomId: roomId, message: 'second chat message' })).mid; + }); + + after(async () => { + await socketModules.chats.leave({ uid: bazUid }, roomId); }); it('should fail to edit message with invalid data', function (done) { @@ -723,6 +726,38 @@ describe('Messaging Library', function () { done(); }); }); + + describe('disabled via ACP', () => { + before(async () => { + meta.config.disableChatMessageEditing = true; + }); + + after(async () => { + meta.config.disableChatMessageEditing = false; + }); + + it('should error out for regular users', async () => { + try { + await socketModules.chats.delete({ uid: bazUid }, { messageId: mid2, roomId: roomId }); + } catch (err) { + assert.strictEqual('[[error:chat-message-editing-disabled]]', err.message); + } + }); + + it('should succeed for administrators', async () => { + await socketModules.chats.delete({ uid: fooUid }, { messageId: mid2, roomId: roomId }); + await socketModules.chats.restore({ uid: fooUid }, { messageId: mid2, roomId: roomId }); + }); + + it('should succeed for global moderators', async () => { + await Groups.join(['Global Moderators'], bazUid); + + await socketModules.chats.delete({ uid: fooUid }, { messageId: mid2, roomId: roomId }); + await socketModules.chats.restore({ uid: fooUid }, { messageId: mid2, roomId: roomId }); + + await Groups.leave(['Global Moderators'], bazUid); + }); + }); }); describe('controller', function () { From fb3f3f729f161892d8f2795c87dca73f1385b11d Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 28 Dec 2020 10:48:58 -0500 Subject: [PATCH 70/98] refactor(openapi): update TopicObject component to reference TopicObjectSlim in its schema --- .../components/schemas/TopicObject.yaml | 43 +------------------ 1 file changed, 1 insertion(+), 42 deletions(-) diff --git a/public/openapi/components/schemas/TopicObject.yaml b/public/openapi/components/schemas/TopicObject.yaml index 45114a696b..653c0d3c53 100644 --- a/public/openapi/components/schemas/TopicObject.yaml +++ b/public/openapi/components/schemas/TopicObject.yaml @@ -1,62 +1,21 @@ TopicObject: allOf: + - $ref: '#/TopicObjectSlim' - type: object properties: - tid: - type: number - description: A topic identifier - uid: - type: number - description: A user identifier - cid: - type: number - description: A category identifier - mainPid: - type: number - description: The post id of the first post in this topic (also called the "original post") title: type: string slug: type: string - timestamp: - type: number lastposttime: type: number - postcount: - type: number - viewcount: - type: number - postercount: - type: number teaserPid: oneOf: - type: number - type: string nullable: true - upvotes: - type: number - downvotes: - type: number - deleted: - type: number - locked: - type: number - pinned: - type: number - description: Whether or not this particular topic is pinned to the top of the - category - deleterUid: - type: number titleRaw: type: string - timestampISO: - type: string - description: An ISO 8601 formatted date string (complementing `timestamp`) - lastposttimeISO: - type: string - description: An ISO 8601 formatted date string (complementing `lastposttime`) - votes: - type: number thumbs: type: array items: From a555f02415a19c049b1b9ddae52edace6d4b7bff Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 28 Dec 2020 11:09:40 -0500 Subject: [PATCH 71/98] fix: inability for admins with setting privilege to save plugin settings --- src/privileges/admin.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/privileges/admin.js b/src/privileges/admin.js index 95f0f27884..004d3e862a 100644 --- a/src/privileges/admin.js +++ b/src/privileges/admin.js @@ -105,6 +105,7 @@ module.exports = function (privileges) { 'admin.themes.set': 'admin:settings', 'admin.reloadAllSessions': 'admin:settings', 'admin.settings.get': 'admin:settings', + 'admin.settings.set': 'admin:settings', }; privileges.admin.resolve = (path) => { From c1ecfd1ebf0663cd7aa0515d6c43fdfa91f6c0c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 28 Dec 2020 11:44:14 -0500 Subject: [PATCH 72/98] feat: #9135, don't try to reconnect forever --- install/data/defaults.json | 4 +++- public/language/en-GB/error.json | 1 + public/src/ajaxify.js | 1 + public/src/sockets.js | 6 ++++-- src/controllers/api.js | 4 ++-- 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/install/data/defaults.json b/install/data/defaults.json index 1f08ca4ad8..556cf055c7 100644 --- a/install/data/defaults.json +++ b/install/data/defaults.json @@ -150,5 +150,7 @@ "useCompression": 0, "updateUrlWithPostIndex": 1, "composer:showHelpTab": 1, - "composer:allowPluginHelp": 1 + "composer:allowPluginHelp": 1, + "maxReconnectionAttempts": 5, + "reconnectionDelay": 1500 } \ No newline at end of file diff --git a/public/language/en-GB/error.json b/public/language/en-GB/error.json index 63b5ebdee5..9b1fbcc789 100644 --- a/public/language/en-GB/error.json +++ b/public/language/en-GB/error.json @@ -208,6 +208,7 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "There seems to be a problem with your internet connection", + "socket-reconnect-failed": "Reconnect failed, please try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } diff --git a/public/src/ajaxify.js b/public/src/ajaxify.js index a5a6cae576..08a83597e1 100644 --- a/public/src/ajaxify.js +++ b/public/src/ajaxify.js @@ -409,6 +409,7 @@ ajaxify = window.ajaxify || {}; require(['translator', 'benchpress'], function (translator, Benchpress) { translator.translate('[[error:no-connection]]'); + translator.translate('[[error:socket-reconnect-failed]]'); Benchpress.registerLoader(ajaxify.loadTemplate); Benchpress.setGlobal('config', config); }); diff --git a/public/src/sockets.js b/public/src/sockets.js index 722f6785af..18f5204897 100644 --- a/public/src/sockets.js +++ b/public/src/sockets.js @@ -45,8 +45,10 @@ socket = window.socket; socket.on('disconnect', onDisconnect); socket.io.on('reconnect_failed', function () { - // Wait ten times the reconnection delay and then start over - setTimeout(socket.connect.bind(socket), parseInt(config.reconnectionDelay, 10) * 10); + $('#reconnect-alert').removeClass('alert-warning') + .addClass('alert-danger') + .find('p') + .translateText('[[error:socket-reconnect-failed]]'); }); socket.on('checkSession', function (uid) { diff --git a/src/controllers/api.js b/src/controllers/api.js index 80b13f961d..ac6313152f 100644 --- a/src/controllers/api.js +++ b/src/controllers/api.js @@ -49,8 +49,8 @@ apiController.loadConfig = async function (req) { socketioTransports, socketioOrigins, websocketAddress, - maxReconnectionAttempts: meta.config.maxReconnectionAttempts || 5, - reconnectionDelay: meta.config.reconnectionDelay || 1500, + maxReconnectionAttempts: meta.config.maxReconnectionAttempts, + reconnectionDelay: meta.config.reconnectionDelay, topicsPerPage: meta.config.topicsPerPage || 20, postsPerPage: meta.config.postsPerPage || 20, maximumFileSize: meta.config.maximumFileSize, From 3121215e87e8ece215663b6d5efcd1f04e63d2c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 28 Dec 2020 12:03:27 -0500 Subject: [PATCH 73/98] fix: #9127, use assets path --- public/src/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/src/app.js b/public/src/app.js index 5e87d55086..ee1178e1b5 100644 --- a/public/src/app.js +++ b/public/src/app.js @@ -786,7 +786,7 @@ app.cacheBuster = null; function registerServiceWorker() { if ('serviceWorker' in navigator) { - navigator.serviceWorker.register(config.relative_path + '/service-worker.js') + navigator.serviceWorker.register(config.relative_path + '/assets/src/service-worker.js') .then(function () { console.info('ServiceWorker registration succeeded.'); }).catch(function (err) { From 33290850ee8cc21f7888fd47fdcce25c5de8506f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 28 Dec 2020 12:10:48 -0500 Subject: [PATCH 74/98] fix: #9126, skip base64 and long values --- src/upgrades/1.16.0/migrate_thumbs.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/upgrades/1.16.0/migrate_thumbs.js b/src/upgrades/1.16.0/migrate_thumbs.js index b5ebd4c5cc..34851568e7 100644 --- a/src/upgrades/1.16.0/migrate_thumbs.js +++ b/src/upgrades/1.16.0/migrate_thumbs.js @@ -17,16 +17,18 @@ module.exports = { if (parseInt(current, 10) === 120) { await meta.configs.set('topicThumbSize', 512); } - await meta.configs.set('allowTopicsThumbnail', 1); await batch.processSortedSet('topics:tid', async function (tids) { const keys = tids.map(tid => `topic:${tid}`); - const topicThumbs = (await db.getObjectsFields(keys, ['thumb'])).map(obj => (obj.thumb ? obj.thumb.replace(nconf.get('upload_url'), '') : null)); + const topicThumbs = (await db.getObjectsFields(keys, ['thumb'])) + .map(obj => (obj.thumb ? obj.thumb.replace(nconf.get('upload_url'), '') : null)); await Promise.all(tids.map(async (tid, idx) => { const path = topicThumbs[idx]; if (path) { - await topics.thumbs.associate({ id: tid, path }); + if (path.length < 255 && !path.startsWith('data:')) { + await topics.thumbs.associate({ id: tid, path }); + } await db.deleteObjectField(keys[idx], 'thumb'); } From 1002c14aa27d0966efb4edfe6720ee3ba383e62b Mon Sep 17 00:00:00 2001 From: "Misty (Bot)" Date: Tue, 29 Dec 2020 09:08:10 +0000 Subject: [PATCH 75/98] Latest translations and fallbacks --- public/language/ar/error.json | 1 + public/language/bg/error.json | 1 + public/language/bn/error.json | 1 + public/language/cs/error.json | 1 + public/language/da/error.json | 1 + public/language/de/error.json | 1 + public/language/el/error.json | 1 + public/language/en-US/error.json | 1 + public/language/en-x-pirate/error.json | 1 + public/language/es/error.json | 1 + public/language/et/error.json | 1 + public/language/fa-IR/error.json | 1 + public/language/fi/error.json | 1 + public/language/fr/error.json | 1 + public/language/gl/error.json | 1 + public/language/he/error.json | 1 + public/language/hr/error.json | 1 + public/language/hu/error.json | 1 + public/language/id/error.json | 1 + public/language/it/error.json | 1 + public/language/ja/error.json | 1 + public/language/ko/error.json | 1 + public/language/lt/error.json | 1 + public/language/lv/error.json | 1 + public/language/ms/error.json | 1 + public/language/nb/error.json | 1 + public/language/nl/error.json | 1 + public/language/pl/error.json | 1 + public/language/pt-BR/error.json | 1 + public/language/pt-PT/error.json | 1 + public/language/ro/error.json | 1 + public/language/ru/error.json | 1 + public/language/rw/error.json | 1 + public/language/sc/error.json | 1 + public/language/sk/error.json | 1 + public/language/sl/error.json | 1 + public/language/sr/error.json | 1 + public/language/sv/error.json | 1 + public/language/th/error.json | 1 + public/language/tr/error.json | 1 + public/language/uk/error.json | 1 + public/language/vi/error.json | 1 + public/language/zh-CN/error.json | 1 + public/language/zh-TW/error.json | 1 + 44 files changed, 44 insertions(+) diff --git a/public/language/ar/error.json b/public/language/ar/error.json index 2346aded87..2fa187a44b 100644 --- a/public/language/ar/error.json +++ b/public/language/ar/error.json @@ -175,5 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "There seems to be a problem with your internet connection", + "socket-reconnect-failed": "Reconnect failed, please try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/bg/error.json b/public/language/bg/error.json index d61e536cf1..702513e558 100644 --- a/public/language/bg/error.json +++ b/public/language/bg/error.json @@ -175,5 +175,6 @@ "already-blocked": "Този потребител вече е блокиран", "already-unblocked": "Този потребител вече е отблокиран", "no-connection": "Изглежда има проблем с връзката Ви с Интернет", + "socket-reconnect-failed": "Reconnect failed, please try again later", "plugin-not-whitelisted": "Добавката не може да бъде инсталирана – само добавки, одобрени от пакетния мениджър на NodeBB могат да бъдат инсталирани чрез ACP" } \ No newline at end of file diff --git a/public/language/bn/error.json b/public/language/bn/error.json index f2b1b22551..8d462e9151 100644 --- a/public/language/bn/error.json +++ b/public/language/bn/error.json @@ -175,5 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "There seems to be a problem with your internet connection", + "socket-reconnect-failed": "Reconnect failed, please try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/cs/error.json b/public/language/cs/error.json index 1854352793..bfe99fb519 100644 --- a/public/language/cs/error.json +++ b/public/language/cs/error.json @@ -175,5 +175,6 @@ "already-blocked": "Tento uživatel již byl zablokován.", "already-unblocked": "Tento uživatel již byl odblokován", "no-connection": "Zdá se, že nastal problém s připojením k internetu", + "socket-reconnect-failed": "Reconnect failed, please try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/da/error.json b/public/language/da/error.json index b4550eb4dc..15a19fb179 100644 --- a/public/language/da/error.json +++ b/public/language/da/error.json @@ -175,5 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "There seems to be a problem with your internet connection", + "socket-reconnect-failed": "Reconnect failed, please try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/de/error.json b/public/language/de/error.json index 9a67930c82..9713a7fc85 100644 --- a/public/language/de/error.json +++ b/public/language/de/error.json @@ -175,5 +175,6 @@ "already-blocked": "Dieser Nutzer ist bereits gesperrt", "already-unblocked": "Dieser Nutzer ist bereits entsperrt", "no-connection": "Es scheint als gäbe es ein Problem mit deiner Internetverbindung", + "socket-reconnect-failed": "Reconnect failed, please try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/el/error.json b/public/language/el/error.json index 727400039b..a29e8fdb8b 100644 --- a/public/language/el/error.json +++ b/public/language/el/error.json @@ -175,5 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "There seems to be a problem with your internet connection", + "socket-reconnect-failed": "Reconnect failed, please try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/en-US/error.json b/public/language/en-US/error.json index c541eff485..2d2e5da7ab 100644 --- a/public/language/en-US/error.json +++ b/public/language/en-US/error.json @@ -175,5 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "There seems to be a problem with your internet connection", + "socket-reconnect-failed": "Reconnect failed, please try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/en-x-pirate/error.json b/public/language/en-x-pirate/error.json index c541eff485..2d2e5da7ab 100644 --- a/public/language/en-x-pirate/error.json +++ b/public/language/en-x-pirate/error.json @@ -175,5 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "There seems to be a problem with your internet connection", + "socket-reconnect-failed": "Reconnect failed, please try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/es/error.json b/public/language/es/error.json index d0ac6ac9e1..a8754acd94 100644 --- a/public/language/es/error.json +++ b/public/language/es/error.json @@ -175,5 +175,6 @@ "already-blocked": "Este usuario ya está bloqueado.", "already-unblocked": "Este usuario ya está desbloqueado.", "no-connection": "Parece haber un problema con tu conexión a internet", + "socket-reconnect-failed": "Reconnect failed, please try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/et/error.json b/public/language/et/error.json index c9c7704681..45c93607ce 100644 --- a/public/language/et/error.json +++ b/public/language/et/error.json @@ -175,5 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "There seems to be a problem with your internet connection", + "socket-reconnect-failed": "Reconnect failed, please try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/fa-IR/error.json b/public/language/fa-IR/error.json index a6ca1049a3..301da03d0c 100644 --- a/public/language/fa-IR/error.json +++ b/public/language/fa-IR/error.json @@ -175,5 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "به نظر می رسد اینترنت شما مشکل دارد", + "socket-reconnect-failed": "Reconnect failed, please try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/fi/error.json b/public/language/fi/error.json index b61ce646d6..4635abdd50 100644 --- a/public/language/fi/error.json +++ b/public/language/fi/error.json @@ -175,5 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "There seems to be a problem with your internet connection", + "socket-reconnect-failed": "Reconnect failed, please try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/fr/error.json b/public/language/fr/error.json index 17a38aa8f4..7ad55e84d5 100644 --- a/public/language/fr/error.json +++ b/public/language/fr/error.json @@ -175,5 +175,6 @@ "already-blocked": "Cet utilisateur est déjà bloqué", "already-unblocked": "Cet utilisateur est déjà débloqué", "no-connection": "Il semble y avoir un problème avec votre connexion Internet", + "socket-reconnect-failed": "La reconnexion a échoué, veuillez réessayer plus tard", "plugin-not-whitelisted": "Impossible d'installer le plug-in – seuls les plugins mis en liste blanche dans le gestionnaire de packages NodeBB peuvent être installés via l'ACP" } \ No newline at end of file diff --git a/public/language/gl/error.json b/public/language/gl/error.json index 569d9e04f0..6cf90357c6 100644 --- a/public/language/gl/error.json +++ b/public/language/gl/error.json @@ -175,5 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "There seems to be a problem with your internet connection", + "socket-reconnect-failed": "Reconnect failed, please try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/he/error.json b/public/language/he/error.json index 58eb587197..f6bb20d2bb 100644 --- a/public/language/he/error.json +++ b/public/language/he/error.json @@ -175,5 +175,6 @@ "already-blocked": "המשתמש כבר חסום", "already-unblocked": "המשתמש כבר לא חסום", "no-connection": "נראה שיש בעיות בחיבור האינטרנט שלך..", + "socket-reconnect-failed": "Reconnect failed, please try again later", "plugin-not-whitelisted": "לא ניתן להתקין את התוסף – ניתן להתקין דרך הניהול רק תוספים שנמצאים ברשימה הלבנה של מנהל החבילות של NodeBB." } \ No newline at end of file diff --git a/public/language/hr/error.json b/public/language/hr/error.json index aa8cc0b726..54320c9ed7 100644 --- a/public/language/hr/error.json +++ b/public/language/hr/error.json @@ -175,5 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "There seems to be a problem with your internet connection", + "socket-reconnect-failed": "Reconnect failed, please try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/hu/error.json b/public/language/hu/error.json index 76b74781eb..fd01c26310 100644 --- a/public/language/hu/error.json +++ b/public/language/hu/error.json @@ -175,5 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "There seems to be a problem with your internet connection", + "socket-reconnect-failed": "Reconnect failed, please try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/id/error.json b/public/language/id/error.json index 1b60c295bc..3428fa90f6 100644 --- a/public/language/id/error.json +++ b/public/language/id/error.json @@ -175,5 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "There seems to be a problem with your internet connection", + "socket-reconnect-failed": "Reconnect failed, please try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/it/error.json b/public/language/it/error.json index f5d2d3fc79..a033ba420a 100644 --- a/public/language/it/error.json +++ b/public/language/it/error.json @@ -175,5 +175,6 @@ "already-blocked": "Questo utente è già bloccato", "already-unblocked": "Questo utente è già sbloccato", "no-connection": "Sembra ci sia un problema con la tua connessione internet", + "socket-reconnect-failed": "Riconnessione non riuscita, per favore riprova più tardi", "plugin-not-whitelisted": "Impossibile installare il plug-in & solo i plugin nella whitelist del Gestione Pacchetti di NodeBB possono essere installati tramite ACP" } \ No newline at end of file diff --git a/public/language/ja/error.json b/public/language/ja/error.json index fe2b5b17c4..b9da3470f3 100644 --- a/public/language/ja/error.json +++ b/public/language/ja/error.json @@ -175,5 +175,6 @@ "already-blocked": "このユーザーは既にブロックされています", "already-unblocked": "このユーザーは既にブロック解除されています", "no-connection": "インターネット接続に問題があるようです", + "socket-reconnect-failed": "Reconnect failed, please try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/ko/error.json b/public/language/ko/error.json index 5af2bd0f25..158668f41d 100644 --- a/public/language/ko/error.json +++ b/public/language/ko/error.json @@ -175,5 +175,6 @@ "already-blocked": "이 사용자는 이미 차단 되었습니다", "already-unblocked": "이 사용자는 이미 차단 해제 되었습니다", "no-connection": "사용자님의 인터넷 연결에 문제가있는 것 같습니다", + "socket-reconnect-failed": "Reconnect failed, please try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/lt/error.json b/public/language/lt/error.json index a4404c6164..d4eeaab7b5 100644 --- a/public/language/lt/error.json +++ b/public/language/lt/error.json @@ -175,5 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "Panašu, jog yra problema su jūsų interneto prieiga", + "socket-reconnect-failed": "Reconnect failed, please try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/lv/error.json b/public/language/lv/error.json index 3c7c6b2e40..7eb664b7cd 100644 --- a/public/language/lv/error.json +++ b/public/language/lv/error.json @@ -175,5 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "Šķiet, ka pastāv problēma ar Tavu interneta savienojumu", + "socket-reconnect-failed": "Reconnect failed, please try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/ms/error.json b/public/language/ms/error.json index 02c455b8ca..aac661fea9 100644 --- a/public/language/ms/error.json +++ b/public/language/ms/error.json @@ -175,5 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "There seems to be a problem with your internet connection", + "socket-reconnect-failed": "Reconnect failed, please try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/nb/error.json b/public/language/nb/error.json index a43035886e..cd86a6b096 100644 --- a/public/language/nb/error.json +++ b/public/language/nb/error.json @@ -175,5 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "There seems to be a problem with your internet connection", + "socket-reconnect-failed": "Reconnect failed, please try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/nl/error.json b/public/language/nl/error.json index e0d5360d3a..e5ab6e3878 100644 --- a/public/language/nl/error.json +++ b/public/language/nl/error.json @@ -175,5 +175,6 @@ "already-blocked": "Deze gebruiker is al geblokkeerd", "already-unblocked": "Deze gebruiker is al gedeblokkeerd", "no-connection": "Er lijkt een probleem te zijn met je internetverbinding", + "socket-reconnect-failed": "Reconnect failed, please try again later", "plugin-not-whitelisted": "Kan plugin niet installeren – alleen plugins toegestaan door de NodeBB Package Manager kunnen via de ACP geinstalleerd worden" } \ No newline at end of file diff --git a/public/language/pl/error.json b/public/language/pl/error.json index 6fb1b1fcbf..b6eaa8349e 100644 --- a/public/language/pl/error.json +++ b/public/language/pl/error.json @@ -175,5 +175,6 @@ "already-blocked": "Ten użytkownik jest już zablokowany", "already-unblocked": "Ten użytkownik jest już odblokowany", "no-connection": "Sprawdź swoje połączenie z internetem", + "socket-reconnect-failed": "Reconnect failed, please try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/pt-BR/error.json b/public/language/pt-BR/error.json index d0e66efc6a..bc2cf4721b 100644 --- a/public/language/pt-BR/error.json +++ b/public/language/pt-BR/error.json @@ -175,5 +175,6 @@ "already-blocked": "Este usuário já foi bloqueado", "already-unblocked": "Este usuário já foi desbloqueado", "no-connection": "Parece haver um problema com a sua conexão com a internet", + "socket-reconnect-failed": "Reconnect failed, please try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/pt-PT/error.json b/public/language/pt-PT/error.json index b06df94fe9..54b236f436 100644 --- a/public/language/pt-PT/error.json +++ b/public/language/pt-PT/error.json @@ -175,5 +175,6 @@ "already-blocked": "Este utilizador já está bloqueado", "already-unblocked": "Este utilizador já está desbloqueado", "no-connection": "Parece haver um problema com a tua conexão à Internet", + "socket-reconnect-failed": "Reconnect failed, please try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/ro/error.json b/public/language/ro/error.json index b9fc0c03d8..df5981dbfa 100644 --- a/public/language/ro/error.json +++ b/public/language/ro/error.json @@ -175,5 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "There seems to be a problem with your internet connection", + "socket-reconnect-failed": "Reconnect failed, please try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/ru/error.json b/public/language/ru/error.json index 807d415728..e5e6144cb9 100644 --- a/public/language/ru/error.json +++ b/public/language/ru/error.json @@ -175,5 +175,6 @@ "already-blocked": "Этот пользователь уже заблокирован", "already-unblocked": "Этот пользователь уже разблокирован", "no-connection": "Похоже, есть проблема с вашим подключением к Интернету", + "socket-reconnect-failed": "Reconnect failed, please try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/rw/error.json b/public/language/rw/error.json index 29f41719d2..fec91faf30 100644 --- a/public/language/rw/error.json +++ b/public/language/rw/error.json @@ -175,5 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "There seems to be a problem with your internet connection", + "socket-reconnect-failed": "Reconnect failed, please try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/sc/error.json b/public/language/sc/error.json index c541eff485..2d2e5da7ab 100644 --- a/public/language/sc/error.json +++ b/public/language/sc/error.json @@ -175,5 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "There seems to be a problem with your internet connection", + "socket-reconnect-failed": "Reconnect failed, please try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/sk/error.json b/public/language/sk/error.json index 68a6990a26..90679ba3cb 100644 --- a/public/language/sk/error.json +++ b/public/language/sk/error.json @@ -175,5 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "Zdá sa, že máte problém s pripojením k internetu", + "socket-reconnect-failed": "Reconnect failed, please try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/sl/error.json b/public/language/sl/error.json index e244684a98..da04905711 100644 --- a/public/language/sl/error.json +++ b/public/language/sl/error.json @@ -175,5 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "There seems to be a problem with your internet connection", + "socket-reconnect-failed": "Reconnect failed, please try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/sr/error.json b/public/language/sr/error.json index 2f06b7476c..2cff57688c 100644 --- a/public/language/sr/error.json +++ b/public/language/sr/error.json @@ -175,5 +175,6 @@ "already-blocked": "Овај корисник је већ блокиран", "already-unblocked": "Овај корисник је већ одблокиран", "no-connection": "Изгледа да постоји проблем са вашом интернет везом", + "socket-reconnect-failed": "Reconnect failed, please try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/sv/error.json b/public/language/sv/error.json index 05a40764ab..266935efe0 100644 --- a/public/language/sv/error.json +++ b/public/language/sv/error.json @@ -175,5 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "Det verkar vara något problem med din internetanslutning", + "socket-reconnect-failed": "Reconnect failed, please try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/th/error.json b/public/language/th/error.json index 8cb12ec719..acd49d13b1 100644 --- a/public/language/th/error.json +++ b/public/language/th/error.json @@ -175,5 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "There seems to be a problem with your internet connection", + "socket-reconnect-failed": "Reconnect failed, please try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/tr/error.json b/public/language/tr/error.json index 08168b4644..6e5d704b64 100644 --- a/public/language/tr/error.json +++ b/public/language/tr/error.json @@ -175,5 +175,6 @@ "already-blocked": "Bu kullanıcı zaten engellendi", "already-unblocked": "Bu kullanıcı zaten engellenmedi", "no-connection": "İnternet bağlantınızda sorun var gibi görünüyor", + "socket-reconnect-failed": "Reconnect failed, please try again later", "plugin-not-whitelisted": "– eklentisi yüklenemedi, sadece NodeBB Paket Yöneticisi tarafından onaylanan eklentiler kontrol panelinden kurulabilir" } \ No newline at end of file diff --git a/public/language/uk/error.json b/public/language/uk/error.json index 9914ff6e53..12bae97fdf 100644 --- a/public/language/uk/error.json +++ b/public/language/uk/error.json @@ -175,5 +175,6 @@ "already-blocked": "Цей користувач вже заблокований", "already-unblocked": "Цей користувач вже розблокований", "no-connection": "Схоже, виникла проблема з вашим Інтернет-з'єднанням", + "socket-reconnect-failed": "Reconnect failed, please try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/vi/error.json b/public/language/vi/error.json index a3bdacd594..ff24aa7d04 100644 --- a/public/language/vi/error.json +++ b/public/language/vi/error.json @@ -175,5 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "Kết nối internet của bạn có vấn đề.", + "socket-reconnect-failed": "Reconnect failed, please try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/zh-CN/error.json b/public/language/zh-CN/error.json index 1265b06fe5..9df74f76cd 100644 --- a/public/language/zh-CN/error.json +++ b/public/language/zh-CN/error.json @@ -175,5 +175,6 @@ "already-blocked": "此用户已被屏蔽", "already-unblocked": "此用户已被取消屏蔽", "no-connection": "您的网络连接似乎存在问题", + "socket-reconnect-failed": "Reconnect failed, please try again later", "plugin-not-whitelisted": "无法安装插件 – 只有被NodeBB包管理器列入白名单的插件才能通过ACP安装。" } \ No newline at end of file diff --git a/public/language/zh-TW/error.json b/public/language/zh-TW/error.json index 9dae827138..7525739d98 100644 --- a/public/language/zh-TW/error.json +++ b/public/language/zh-TW/error.json @@ -175,5 +175,6 @@ "already-blocked": "此使用者已被封鎖", "already-unblocked": "此使用者已被取消封鎖", "no-connection": "您的網路連線似乎有問題", + "socket-reconnect-failed": "Reconnect failed, please try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file From e267f29584f6dd4c7b34ca9a67adc194cb8e9e2e Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 28 Dec 2020 19:38:00 -0500 Subject: [PATCH 76/98] feat(api): #9123, migrate /api/post/pid/:pid to Write API --- src/api/posts.js | 25 +++++++++++++++++++++++++ src/controllers/api.js | 27 +++------------------------ src/controllers/write/posts.js | 4 ++++ src/routes/write/posts.js | 2 ++ src/socket.io/posts.js | 6 +++--- 5 files changed, 37 insertions(+), 27 deletions(-) diff --git a/src/api/posts.js b/src/api/posts.js index ca77e1ce33..c01b377340 100644 --- a/src/api/posts.js +++ b/src/api/posts.js @@ -15,6 +15,31 @@ const websockets = require('../socket.io'); const postsAPI = module.exports; +postsAPI.get = async function (caller, data) { + const [userPrivileges, post, voted] = await Promise.all([ + privileges.posts.get([data.pid], caller.uid), + posts.getPostData(data.pid), + posts.hasVoted(data.pid, caller.uid), + ]); + if (!post) { + return null; + } + Object.assign(post, voted); + + const userPrivilege = userPrivileges[0]; + if (!userPrivilege.read || !userPrivilege['topics:read']) { + return null; + } + + post.ip = userPrivilege.isAdminOrMod ? post.ip : undefined; + const selfPost = caller.uid && caller.uid === parseInt(post.uid, 10); + if (post.deleted && !(userPrivilege.isAdminOrMod || selfPost)) { + post.content = '[[topic:post_is_deleted]]'; + } + + return post; +}; + postsAPI.edit = async function (caller, data) { if (!data || !data.pid || (meta.config.minimumPostLength !== 0 && !data.content)) { throw new Error('[[error:invalid-data]]'); diff --git a/src/controllers/api.js b/src/controllers/api.js index ac6313152f..f6748dfb81 100644 --- a/src/controllers/api.js +++ b/src/controllers/api.js @@ -5,13 +5,13 @@ const nconf = require('nconf'); const meta = require('../meta'); const user = require('../user'); -const posts = require('../posts'); const topics = require('../topics'); const categories = require('../categories'); const privileges = require('../privileges'); const plugins = require('../plugins'); const translator = require('../translator'); const languages = require('../languages'); +const api = require('../api'); const apiController = module.exports; @@ -117,29 +117,8 @@ apiController.getConfig = async function (req, res) { res.json(config); }; -apiController.getPostData = async function (pid, uid) { - const [userPrivileges, post, voted] = await Promise.all([ - privileges.posts.get([pid], uid), - posts.getPostData(pid), - posts.hasVoted(pid, uid), - ]); - if (!post) { - return null; - } - Object.assign(post, voted); - - const userPrivilege = userPrivileges[0]; - if (!userPrivilege.read || !userPrivilege['topics:read']) { - return null; - } - - post.ip = userPrivilege.isAdminOrMod ? post.ip : undefined; - const selfPost = uid && uid === parseInt(post.uid, 10); - if (post.deleted && !(userPrivilege.isAdminOrMod || selfPost)) { - post.content = '[[topic:post_is_deleted]]'; - } - return post; -}; +// TODO: Deprecate these four controllers in 1.17.0 +apiController.getPostData = async (pid, uid) => api.posts.get({ uid }, { pid }); apiController.getTopicData = async function (tid, uid) { const [userPrivileges, topic] = await Promise.all([ diff --git a/src/controllers/write/posts.js b/src/controllers/write/posts.js index 1a73cb7b1b..7988439a1f 100644 --- a/src/controllers/write/posts.js +++ b/src/controllers/write/posts.js @@ -8,6 +8,10 @@ const apiHelpers = require('../../api/helpers'); const Posts = module.exports; +Posts.get = async (req, res) => { + helpers.formatApiResponse(200, res, await api.posts.get(req, { pid: req.params.pid })); +}; + Posts.edit = async (req, res) => { const editResult = await api.posts.edit(req, { ...req.body, diff --git a/src/routes/write/posts.js b/src/routes/write/posts.js index f445873a1a..837f8def8c 100644 --- a/src/routes/write/posts.js +++ b/src/routes/write/posts.js @@ -10,6 +10,8 @@ const setupApiRoute = routeHelpers.setupApiRoute; module.exports = function () { const middlewares = [middleware.authenticate]; + setupApiRoute(router, 'get', '/:pid', [middleware.authenticateOrGuest], controllers.write.posts.get); + // There is no POST route because you POST to a topic to create a new post. Intuitive, no? setupApiRoute(router, 'put', '/:pid', [...middlewares, middleware.checkRequired.bind(null, ['content'])], controllers.write.posts.edit); setupApiRoute(router, 'delete', '/:pid', [...middlewares, middleware.assert.post], controllers.write.posts.purge); diff --git a/src/socket.io/posts.js b/src/socket.io/posts.js index c8e30962f4..e9fc770338 100644 --- a/src/socket.io/posts.js +++ b/src/socket.io/posts.js @@ -10,8 +10,7 @@ const categories = require('../categories'); const user = require('../user'); const socketHelpers = require('./helpers'); const utils = require('../utils'); - -const apiController = require('../controllers/api'); +const api = require('../api'); const sockets = require('.'); const SocketPosts = module.exports; @@ -97,7 +96,8 @@ SocketPosts.getPostSummaryByIndex = async function (socket, data) { }; SocketPosts.getPost = async function (socket, pid) { - return await apiController.getPostData(pid, socket.uid); + sockets.warnDeprecated(socket, 'GET /api/v3/posts/:pid'); + return await api.posts.get(socket, { pid }); }; SocketPosts.loadMoreBookmarks = async function (socket, data) { From cdff8d286abb3118b8eeaac3b0946b14814dcb02 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 28 Dec 2020 19:38:22 -0500 Subject: [PATCH 77/98] chore(api): add deprecation notices re: #9123 --- src/controllers/api.js | 5 +++++ src/routes/api.js | 1 + 2 files changed, 6 insertions(+) diff --git a/src/controllers/api.js b/src/controllers/api.js index f6748dfb81..432507f969 100644 --- a/src/controllers/api.js +++ b/src/controllers/api.js @@ -2,6 +2,7 @@ const validator = require('validator'); const nconf = require('nconf'); +const winston = require('winston'); const meta = require('../meta'); const user = require('../user'); @@ -152,6 +153,10 @@ apiController.getObject = async function (req, res, next) { if (!method) { return next(); } + + winston.warn('[api] This route has been deprecated and will likely be removed in v1.17.0'); + winston.warn('[api] Use GET /api/v3/(posts|topics|categories)/:id instead'); + try { const result = await method(req.params.id, req.uid); if (!result) { diff --git a/src/routes/api.js b/src/routes/api.js index 3280246626..3461a28550 100644 --- a/src/routes/api.js +++ b/src/routes/api.js @@ -19,6 +19,7 @@ module.exports = function (app, middleware, controllers) { router.get('/user/uid/:userslug/export/uploads', middleware.checkAccountPermissions, middleware.exposeUid, controllers.user.exportUploads); router.get('/user/uid/:userslug/export/profile', middleware.checkAccountPermissions, middleware.exposeUid, controllers.user.exportProfile); + // TODO: Deprecate in v1.17.0 router.get('/:type/pid/:id', middleware.authenticateOrGuest, controllers.api.getObject); router.get('/:type/tid/:id', middleware.authenticateOrGuest, controllers.api.getObject); router.get('/:type/cid/:id', middleware.authenticateOrGuest, controllers.api.getObject); From 9ecfac9b68ce5e9ece794e396dc41c16642bafd7 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 28 Dec 2020 19:52:46 -0500 Subject: [PATCH 78/98] feat(api): #9123, migrate rest of the getObject controllers to Write API --- src/api/categories.js | 13 +++++++++++++ src/api/topics.js | 13 +++++++++++++ src/controllers/api.js | 27 ++------------------------- src/socket.io/categories.js | 7 +++++-- src/socket.io/topics.js | 4 ++-- 5 files changed, 35 insertions(+), 29 deletions(-) diff --git a/src/api/categories.js b/src/api/categories.js index 673155b436..a3df7860c9 100644 --- a/src/api/categories.js +++ b/src/api/categories.js @@ -2,9 +2,22 @@ const categories = require('../categories'); const events = require('../events'); +const privileges = require('../privileges'); const categoriesAPI = module.exports; +categoriesAPI.get = async function (caller, data) { + const [userPrivileges, category] = await Promise.all([ + privileges.categories.get(data.cid, caller.uid), + categories.getCategoryData(data.cid), + ]); + if (!category || !userPrivileges.read) { + return null; + } + + return category; +}; + categoriesAPI.create = async function (caller, data) { const response = await categories.create(data); const categoryObjs = await categories.getCategories([response.cid], caller.uid); diff --git a/src/api/topics.js b/src/api/topics.js index f313321c8e..de62942370 100644 --- a/src/api/topics.js +++ b/src/api/topics.js @@ -4,6 +4,7 @@ const user = require('../user'); const topics = require('../topics'); const posts = require('../posts'); const meta = require('../meta'); +const privileges = require('../privileges'); const apiHelpers = require('./helpers'); const doTopicAction = apiHelpers.doTopicAction; @@ -13,6 +14,18 @@ const socketHelpers = require('../socket.io/helpers'); const topicsAPI = module.exports; +topicsAPI.get = async function (caller, data) { + const [userPrivileges, topic] = await Promise.all([ + privileges.topics.get(data.tid, caller.uid), + topics.getTopicData(data.tid), + ]); + if (!topic || !userPrivileges.read || !userPrivileges['topics:read'] || (topic.deleted && !userPrivileges.view_deleted)) { + return null; + } + + return topic; +}; + topicsAPI.create = async function (caller, data) { if (!data) { throw new Error('[[error:invalid-data]]'); diff --git a/src/controllers/api.js b/src/controllers/api.js index 432507f969..ae2af4fa76 100644 --- a/src/controllers/api.js +++ b/src/controllers/api.js @@ -6,9 +6,7 @@ const winston = require('winston'); const meta = require('../meta'); const user = require('../user'); -const topics = require('../topics'); const categories = require('../categories'); -const privileges = require('../privileges'); const plugins = require('../plugins'); const translator = require('../translator'); const languages = require('../languages'); @@ -120,29 +118,8 @@ apiController.getConfig = async function (req, res) { // TODO: Deprecate these four controllers in 1.17.0 apiController.getPostData = async (pid, uid) => api.posts.get({ uid }, { pid }); - -apiController.getTopicData = async function (tid, uid) { - const [userPrivileges, topic] = await Promise.all([ - privileges.topics.get(tid, uid), - topics.getTopicData(tid), - ]); - if (!topic || !userPrivileges.read || !userPrivileges['topics:read'] || (topic.deleted && !userPrivileges.view_deleted)) { - return null; - } - return topic; -}; - -apiController.getCategoryData = async function (cid, uid) { - const [userPrivileges, category] = await Promise.all([ - privileges.categories.get(cid, uid), - categories.getCategoryData(cid), - ]); - if (!category || !userPrivileges.read) { - return null; - } - return category; -}; - +apiController.getTopicData = async (tid, uid) => api.topics.get({ uid }, { tid }); +apiController.getCategoryData = async (cid, uid) => api.categories.get({ uid }, { cid }); apiController.getObject = async function (req, res, next) { const methods = { post: apiController.getPostData, diff --git a/src/socket.io/categories.js b/src/socket.io/categories.js index 7b2c35b4aa..b7aabb8174 100644 --- a/src/socket.io/categories.js +++ b/src/socket.io/categories.js @@ -4,7 +4,8 @@ const categories = require('../categories'); const privileges = require('../privileges'); const user = require('../user'); const topics = require('../topics'); -const apiController = require('../controllers/api'); +const api = require('../api'); +const sockets = require('.'); const SocketCategories = module.exports; @@ -147,7 +148,9 @@ SocketCategories.isModerator = async function (socket, cid) { }; SocketCategories.getCategory = async function (socket, cid) { - return await apiController.getCategoryData(cid, socket.uid); + sockets.warnDeprecated(socket, 'GET /api/v3/categories/:tid'); + return await api.categories.get(socket, { cid }); + // return await apiController.getCategoryData(cid, socket.uid); }; require('../promisify')(SocketCategories); diff --git a/src/socket.io/topics.js b/src/socket.io/topics.js index ecdc0e12cd..de4d356ad6 100644 --- a/src/socket.io/topics.js +++ b/src/socket.io/topics.js @@ -4,7 +4,6 @@ const api = require('../api'); const topics = require('../topics'); const user = require('../user'); const meta = require('../meta'); -const apiController = require('../controllers/api'); const privileges = require('../privileges'); const sockets = require('.'); @@ -96,7 +95,8 @@ SocketTopics.isModerator = async function (socket, tid) { }; SocketTopics.getTopic = async function (socket, tid) { - return await apiController.getTopicData(tid, socket.uid); + sockets.warnDeprecated(socket, 'GET /api/v3/topics/:tid'); + return await api.topics.get(socket, { tid }); }; require('../promisify')(SocketTopics); From 77a5adb616e09a58a7d00217106b086ca0bb0d17 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 29 Dec 2020 10:31:25 -0500 Subject: [PATCH 79/98] fix(tests): handle nested allOf blocks --- test/api.js | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/test/api.js b/test/api.js index 7d81b55c04..e4c28cfcef 100644 --- a/test/api.js +++ b/test/api.js @@ -117,7 +117,7 @@ describe('API', async () => { }], }); meta.config.allowTopicsThumbnail = 1; - meta.config.termsOfUse = 'I, for one, welcome our new test-drive overlords'; + meta.config.termsOfUse = 'I, for one, welcome our new test-driven overlords'; // Create a category const testCategory = await categories.create({ name: 'test' }); @@ -446,12 +446,20 @@ describe('API', async () => { let required = []; const additionalProperties = schema.hasOwnProperty('additionalProperties'); - if (schema.allOf) { - schema = schema.allOf.reduce((memo, obj) => { - required = required.concat(obj.required ? obj.required : Object.keys(obj.properties)); - memo = { ...memo, ...obj.properties }; - return memo; + function flattenAllOf(obj) { + return obj.reduce((memo, obj) => { + if (obj.allOf) { + obj = { properties: flattenAllOf(obj.allOf) }; + } else { + required = required.concat(obj.required ? obj.required : Object.keys(obj.properties)); + } + + return { ...memo, ...obj.properties }; }, {}); + } + + if (schema.allOf) { + schema = flattenAllOf(schema.allOf); } else if (schema.properties) { required = schema.required || Object.keys(schema.properties); schema = schema.properties; From edb8da1ef9334b75267d012b839a7e214041ee1f Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 29 Dec 2020 10:31:53 -0500 Subject: [PATCH 80/98] feat(api): closes #9123 category and topic routes migrated to Write API --- .../components/schemas/CategoryObject.yaml | 156 ++++++++++-------- .../components/schemas/TopicObject.yaml | 24 +-- public/openapi/write/categories/cid.yaml | 29 +++- public/openapi/write/posts/pid.yaml | 66 ++++++++ public/openapi/write/topics/tid.yaml | 25 +++ src/controllers/write/categories.js | 4 + src/controllers/write/topics.js | 4 + src/routes/write/categories.js | 1 + src/routes/write/topics.js | 1 + 9 files changed, 224 insertions(+), 86 deletions(-) diff --git a/public/openapi/components/schemas/CategoryObject.yaml b/public/openapi/components/schemas/CategoryObject.yaml index bbeca1cf9f..384349bc9e 100644 --- a/public/openapi/components/schemas/CategoryObject.yaml +++ b/public/openapi/components/schemas/CategoryObject.yaml @@ -1,73 +1,85 @@ CategoryObject: - type: object - properties: - cid: - type: number - description: A category identifier assigned upon category creation (this value cannot be changed) - name: - type: string - description: The category's name/title - description: - type: string - description: A variable-length description of the category (usually displayed underneath the category name) - descriptionParsed: - type: string - description: A variable-length description of the category (usually displayed underneath the category name). Unlike `description`, this value here will have been run through any parsers installed on the forum (e.g. Markdown) - icon: - type: string - description: A FontAwesome icon string - example: fa-comments-o - bgColor: - type: string - description: Theme-related, a six-character hexadecimal string representing the background colour of the category - color: - type: string - description: Theme-related, a six-character hexadecimal string representing the foreground/text colour of the category - slug: - type: string - description: An URL-safe variant of the category title. This value is automatically generated. - readOnly: true - parentCid: - type: number - description: The category identifier for the category that is the immediate ancestor of the current category - topic_count: - type: number - description: The number of topics in the category - post_count: - type: number - description: The number of posts in the category - disabled: - type: number - description: Whether or not this category is disabled. - order: - type: number - description: A number representing the category's place in the hierarchy - link: - type: string - description: If set, attempting to access the forum will go to this external link instead (theme-specific) - numRecentReplies: - type: number - description: The number of posts to render in the API response (this is mostly used at the theme level) - class: - type: string - description: Values that are appended to the `class` attribute of the category's parent/root element - imageClass: - type: string - enum: [auto, cover, contain] - description: The `background-position` of the category background image, if one is set - isSection: - type: number - minTags: - type: number - description: Minimum tags per topic in this category - maxTags: - type: number - description: Maximum tags per topic in this category - postQueue: - type: number - totalPostCount: - type: number - description: The number of posts in the category - totalTopicCount: - type: number - description: The number of topics in the category \ No newline at end of file + allOf: + - type: object + properties: + cid: + type: number + description: A category identifier assigned upon category creation (this value cannot be changed) + name: + type: string + description: The category's name/title + description: + type: string + description: A variable-length description of the category (usually displayed underneath the category name) + descriptionParsed: + type: string + description: A variable-length description of the category (usually displayed underneath the category name). Unlike `description`, this value here will have been run through any parsers installed on the forum (e.g. Markdown) + icon: + type: string + description: A FontAwesome icon string + example: fa-comments-o + bgColor: + type: string + description: Theme-related, a six-character hexadecimal string representing the background colour of the category + color: + type: string + description: Theme-related, a six-character hexadecimal string representing the foreground/text colour of the category + slug: + type: string + description: An URL-safe variant of the category title. This value is automatically generated. + readOnly: true + parentCid: + type: number + description: The category identifier for the category that is the immediate ancestor of the current category + topic_count: + type: number + description: The number of topics in the category + post_count: + type: number + description: The number of posts in the category + disabled: + type: number + description: Whether or not this category is disabled. + order: + type: number + description: A number representing the category's place in the hierarchy + link: + type: string + description: If set, attempting to access the forum will go to this external link instead (theme-specific) + numRecentReplies: + type: number + description: The number of posts to render in the API response (this is mostly used at the theme level) + class: + type: string + description: Values that are appended to the `class` attribute of the category's parent/root element + imageClass: + type: string + enum: [auto, cover, contain] + description: The `background-position` of the category background image, if one is set + isSection: + type: number + minTags: + type: number + description: Minimum tags per topic in this category + maxTags: + type: number + description: Maximum tags per topic in this category + postQueue: + type: number + totalPostCount: + type: number + description: The number of posts in the category + totalTopicCount: + type: number + description: The number of topics in the category + - type: object + description: Optional properties that may or may not be present (except for `cid`, which is always present, and is only here as a hack to pass validation) + properties: + cid: + type: number + description: A category identifier + backgroundImage: + type: string + description: Relative URL to the category's background image + required: + - cid \ No newline at end of file diff --git a/public/openapi/components/schemas/TopicObject.yaml b/public/openapi/components/schemas/TopicObject.yaml index 653c0d3c53..9c95a4a5ed 100644 --- a/public/openapi/components/schemas/TopicObject.yaml +++ b/public/openapi/components/schemas/TopicObject.yaml @@ -3,19 +3,8 @@ TopicObject: - $ref: '#/TopicObjectSlim' - type: object properties: - title: - type: string - slug: - type: string lastposttime: type: number - teaserPid: - oneOf: - - type: number - - type: string - nullable: true - titleRaw: - type: string thumbs: type: array items: @@ -218,6 +207,10 @@ TopicObjectSlim: cid: type: number description: A category identifier + title: + type: string + slug: + type: string mainPid: type: number description: The post id of the first post in this topic (also called the "original post") @@ -231,6 +224,8 @@ TopicObjectSlim: type: number deleterUid: type: number + titleRaw: + type: string locked: type: number pinned: @@ -258,4 +253,9 @@ TopicObjectSlim: downvotes: type: number votes: - type: number \ No newline at end of file + type: number + teaserPid: + oneOf: + - type: number + - type: string + nullable: true \ No newline at end of file diff --git a/public/openapi/write/categories/cid.yaml b/public/openapi/write/categories/cid.yaml index af7790088c..bcb50225a5 100644 --- a/public/openapi/write/categories/cid.yaml +++ b/public/openapi/write/categories/cid.yaml @@ -1,6 +1,31 @@ +get: + tags: + - categories + summary: get a category + description: This operation retrieves a category's data + parameters: + - in: path + name: cid + schema: + type: string + required: true + description: a valid category id + example: 2 + responses: + '200': + description: Category successfully retrieved + content: + application/json: + schema: + type: object + properties: + status: + $ref: ../../components/schemas/Status.yaml#/Status + response: + $ref: ../../components/schemas/CategoryObject.yaml#/CategoryObject put: tags: - - topics + - categories summary: update a category description: This operation updates an existing category. parameters: @@ -42,7 +67,7 @@ put: type: string delete: tags: - - topics + - categories summary: delete a category description: This operation deletes and purges a category and all of its topics and posts (careful, there is no confirmation!) parameters: diff --git a/public/openapi/write/posts/pid.yaml b/public/openapi/write/posts/pid.yaml index 2d439bd8f0..593a7acd01 100644 --- a/public/openapi/write/posts/pid.yaml +++ b/public/openapi/write/posts/pid.yaml @@ -1,3 +1,69 @@ +get: + tags: + - posts + summary: get a post + description: This operation retrieves a post's data + parameters: + - in: path + name: pid + schema: + type: string + required: true + description: a valid post id + example: 1 + responses: + '200': + description: Post successfully retrieved + content: + application/json: + schema: + type: object + properties: + status: + $ref: ../../components/schemas/Status.yaml#/Status + response: + type: object + properties: + pid: + type: number + uid: + type: number + description: A user identifier + tid: + type: number + description: A topic identifier + content: + type: string + timestamp: + type: number + flagId: + type: number + deleted: + type: number + upvotes: + type: number + downvotes: + type: number + deleterUid: + type: number + edited: + type: number + replies: + type: number + bookmarks: + type: number + votes: + type: number + timestampISO: + type: string + description: An ISO 8601 formatted date string (complementing `timestamp`) + editedISO: + type: string + description: An ISO 8601 formatted date string (complementing `timestamp`) + upvoted: + type: boolean + downvoted: + type: boolean put: tags: - posts diff --git a/public/openapi/write/topics/tid.yaml b/public/openapi/write/topics/tid.yaml index 04a5e98aee..8e68efe25a 100644 --- a/public/openapi/write/topics/tid.yaml +++ b/public/openapi/write/topics/tid.yaml @@ -1,3 +1,28 @@ +get: + tags: + - topics + summary: get a topic + description: This operation retrieves a topic's data + parameters: + - in: path + name: tid + schema: + type: string + required: true + description: a valid topic id + example: 1 + responses: + '200': + description: Topic successfully retrieved + content: + application/json: + schema: + type: object + properties: + status: + $ref: ../../components/schemas/Status.yaml#/Status + response: + $ref: ../../components/schemas/TopicObject.yaml#/TopicObjectSlim post: tags: - topics diff --git a/src/controllers/write/categories.js b/src/controllers/write/categories.js index d9bbd8ece6..17f56ab78b 100644 --- a/src/controllers/write/categories.js +++ b/src/controllers/write/categories.js @@ -15,6 +15,10 @@ const hasAdminPrivilege = async (uid) => { } }; +Categories.get = async (req, res) => { + helpers.formatApiResponse(200, res, await api.categories.get(req, req.params)); +}; + Categories.create = async (req, res) => { await hasAdminPrivilege(req.uid); diff --git a/src/controllers/write/topics.js b/src/controllers/write/topics.js index c91654530b..c20cc6a269 100644 --- a/src/controllers/write/topics.js +++ b/src/controllers/write/topics.js @@ -12,6 +12,10 @@ const uploadsController = require('../uploads'); const Topics = module.exports; +Topics.get = async (req, res) => { + helpers.formatApiResponse(200, res, await api.topics.get(req, req.params)); +}; + Topics.create = async (req, res) => { const payload = await api.topics.create(req, req.body); if (payload.queued) { diff --git a/src/routes/write/categories.js b/src/routes/write/categories.js index beb73b90b8..a87456a657 100644 --- a/src/routes/write/categories.js +++ b/src/routes/write/categories.js @@ -11,6 +11,7 @@ module.exports = function () { const middlewares = [middleware.authenticate]; setupApiRoute(router, 'post', '/', [...middlewares, middleware.checkRequired.bind(null, ['name'])], controllers.write.categories.create); + setupApiRoute(router, 'get', '/:cid', [middleware.authenticateOrGuest], controllers.write.categories.get); setupApiRoute(router, 'put', '/:cid', [...middlewares], controllers.write.categories.update); setupApiRoute(router, 'delete', '/:cid', [...middlewares], controllers.write.categories.delete); diff --git a/src/routes/write/topics.js b/src/routes/write/topics.js index 9203146588..d2c05a38bd 100644 --- a/src/routes/write/topics.js +++ b/src/routes/write/topics.js @@ -14,6 +14,7 @@ module.exports = function () { var multipartMiddleware = multipart(); setupApiRoute(router, 'post', '/', [middleware.authenticateOrGuest, middleware.checkRequired.bind(null, ['cid', 'title', 'content'])], controllers.write.topics.create); + setupApiRoute(router, 'get', '/:tid', [middleware.authenticateOrGuest], controllers.write.topics.get); setupApiRoute(router, 'post', '/:tid', [middleware.authenticateOrGuest, middleware.checkRequired.bind(null, ['content']), middleware.assert.topic], controllers.write.topics.reply); setupApiRoute(router, 'delete', '/:tid', [...middlewares], controllers.write.topics.purge); From 041d45c3d2678e2479d7db5e3036c77cce229721 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 29 Dec 2020 16:34:05 -0500 Subject: [PATCH 81/98] fix: genericise .necro-post, bump persona to latest --- install/package.json | 2 +- public/less/generics.less | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/install/package.json b/install/package.json index 5f09356944..de8dee3fce 100644 --- a/install/package.json +++ b/install/package.json @@ -101,7 +101,7 @@ "nodebb-plugin-spam-be-gone": "0.7.7", "nodebb-rewards-essentials": "0.1.4", "nodebb-theme-lavender": "5.0.17", - "nodebb-theme-persona": "10.3.12", + "nodebb-theme-persona": "10.3.14", "nodebb-theme-slick": "1.3.7", "nodebb-theme-vanilla": "11.3.10", "nodebb-widget-essentials": "5.0.2", diff --git a/public/less/generics.less b/public/less/generics.less index 254c8fe505..4e2102bd37 100644 --- a/public/less/generics.less +++ b/public/less/generics.less @@ -155,7 +155,7 @@ } } -.topic .necro-post { +.necro-post { color: rgba(127,127,127,.5); font-size: 1.5em; margin-bottom: 20px; From 189be9e0becef524ac3d548be7df6f1d48dc2c7d Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Wed, 30 Dec 2020 01:39:39 +0000 Subject: [PATCH 82/98] fix(deps): update dependency nodebb-theme-persona to v10.3.15 --- install/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/package.json b/install/package.json index de8dee3fce..79a9ca30c1 100644 --- a/install/package.json +++ b/install/package.json @@ -101,7 +101,7 @@ "nodebb-plugin-spam-be-gone": "0.7.7", "nodebb-rewards-essentials": "0.1.4", "nodebb-theme-lavender": "5.0.17", - "nodebb-theme-persona": "10.3.14", + "nodebb-theme-persona": "10.3.15", "nodebb-theme-slick": "1.3.7", "nodebb-theme-vanilla": "11.3.10", "nodebb-widget-essentials": "5.0.2", From e5edbc6fafa0c2060fe2b275f088e989a4d35ccf Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 30 Dec 2020 13:51:45 -0500 Subject: [PATCH 83/98] feat: automatically attempt socket.io reconnection on ajaxify Also, updated messaging and toaster to accurately reflect offline-mode state --- public/language/en-GB/error.json | 2 +- public/src/ajaxify.js | 4 ++++ public/src/sockets.js | 29 +++++++++++++++++++++++++---- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/public/language/en-GB/error.json b/public/language/en-GB/error.json index 9b1fbcc789..7a08489cb5 100644 --- a/public/language/en-GB/error.json +++ b/public/language/en-GB/error.json @@ -208,7 +208,7 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "There seems to be a problem with your internet connection", - "socket-reconnect-failed": "Reconnect failed, please try again later", + "socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } diff --git a/public/src/ajaxify.js b/public/src/ajaxify.js index 08a83597e1..a7c91aaffb 100644 --- a/public/src/ajaxify.js +++ b/public/src/ajaxify.js @@ -14,7 +14,10 @@ ajaxify = window.ajaxify || {}; ajaxify.currentPage = null; ajaxify.go = function (url, callback, quiet) { + // Automatically reconnect to socket and re-ajaxify on success if (!socket.connected) { + app.reconnect(); + if (ajaxify.reconnectAction) { $(window).off('action:reconnected', ajaxify.reconnectAction); } @@ -410,6 +413,7 @@ ajaxify = window.ajaxify || {}; require(['translator', 'benchpress'], function (translator, Benchpress) { translator.translate('[[error:no-connection]]'); translator.translate('[[error:socket-reconnect-failed]]'); + translator.translate(`[[global:reconnecting-message, ${config.siteTitle}]]`); Benchpress.registerLoader(ajaxify.loadTemplate); Benchpress.setGlobal('config', config); }); diff --git a/public/src/sockets.js b/public/src/sockets.js index 18f5204897..80b639b1d1 100644 --- a/public/src/sockets.js +++ b/public/src/sockets.js @@ -39,16 +39,37 @@ socket = window.socket; addHandlers(); } + window.app.reconnect = () => { + if (socket.connected) { + return; + } + + var reconnectEl = $('#reconnect'); + $('#reconnect-alert') + .removeClass('alert-danger pointer') + .addClass('alert-warning') + .find('p') + .translateText(`[[global:reconnecting-message, ${config.siteTitle}]]`); + + reconnectEl.html(''); + socket.connect(); + }; + function addHandlers() { socket.on('connect', onConnect); socket.on('disconnect', onDisconnect); socket.io.on('reconnect_failed', function () { - $('#reconnect-alert').removeClass('alert-warning') - .addClass('alert-danger') + var reconnectEl = $('#reconnect'); + reconnectEl.html(''); + + $('#reconnect-alert') + .removeClass('alert-warning') + .addClass('alert-danger pointer') .find('p') - .translateText('[[error:socket-reconnect-failed]]'); + .translateText('[[error:socket-reconnect-failed]]') + .one('click', app.reconnect); }); socket.on('checkSession', function (uid) { @@ -105,7 +126,7 @@ socket = window.socket; var reconnectAlert = $('#reconnect-alert'); reconnectEl.tooltip('destroy'); - reconnectEl.html(''); + reconnectEl.html(''); reconnectAlert.fadeOut(500); reconnecting = false; From 4d6ddf6dbf0c5a52e5ab130376fb2b4f6ae518f3 Mon Sep 17 00:00:00 2001 From: Andrew Rodrigues Date: Thu, 31 Dec 2020 00:17:10 -0500 Subject: [PATCH 84/98] feat: added note that you can now upload videos --- .github/ISSUE_TEMPLATE.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 166f0b20b1..8ef171106e 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -20,6 +20,9 @@ - **What you expected:** From d1700c40e2b5503146f70f5f3c307022c7197722 Mon Sep 17 00:00:00 2001 From: "Misty (Bot)" Date: Thu, 31 Dec 2020 09:09:40 +0000 Subject: [PATCH 85/98] Latest translations and fallbacks --- public/language/ar/error.json | 2 +- public/language/bg/error.json | 2 +- public/language/bn/error.json | 2 +- public/language/cs/error.json | 2 +- public/language/da/error.json | 2 +- public/language/de/error.json | 2 +- public/language/el/error.json | 2 +- public/language/en-US/error.json | 2 +- public/language/en-x-pirate/error.json | 2 +- public/language/es/error.json | 2 +- public/language/et/error.json | 2 +- public/language/fa-IR/error.json | 2 +- public/language/fi/error.json | 2 +- public/language/fr/error.json | 2 +- public/language/gl/error.json | 2 +- public/language/he/error.json | 2 +- public/language/hr/error.json | 2 +- public/language/hu/error.json | 2 +- public/language/id/error.json | 2 +- public/language/it/error.json | 2 +- public/language/ja/error.json | 2 +- public/language/ko/error.json | 2 +- public/language/lt/error.json | 2 +- public/language/lv/error.json | 2 +- public/language/ms/error.json | 2 +- public/language/nb/error.json | 2 +- public/language/nl/error.json | 2 +- public/language/pl/error.json | 2 +- public/language/pt-BR/error.json | 2 +- public/language/pt-PT/error.json | 2 +- public/language/ro/error.json | 2 +- public/language/ru/error.json | 2 +- public/language/rw/error.json | 2 +- public/language/sc/error.json | 2 +- public/language/sk/error.json | 2 +- public/language/sl/error.json | 2 +- public/language/sr/error.json | 2 +- public/language/sv/error.json | 2 +- public/language/th/error.json | 2 +- public/language/tr/error.json | 2 +- public/language/uk/error.json | 2 +- public/language/vi/error.json | 2 +- public/language/zh-CN/error.json | 2 +- public/language/zh-TW/error.json | 2 +- 44 files changed, 44 insertions(+), 44 deletions(-) diff --git a/public/language/ar/error.json b/public/language/ar/error.json index 2fa187a44b..078a2d95c9 100644 --- a/public/language/ar/error.json +++ b/public/language/ar/error.json @@ -175,6 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "There seems to be a problem with your internet connection", - "socket-reconnect-failed": "Reconnect failed, please try again later", + "socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/bg/error.json b/public/language/bg/error.json index 702513e558..da394e7688 100644 --- a/public/language/bg/error.json +++ b/public/language/bg/error.json @@ -175,6 +175,6 @@ "already-blocked": "Този потребител вече е блокиран", "already-unblocked": "Този потребител вече е отблокиран", "no-connection": "Изглежда има проблем с връзката Ви с Интернет", - "socket-reconnect-failed": "Reconnect failed, please try again later", + "socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later", "plugin-not-whitelisted": "Добавката не може да бъде инсталирана – само добавки, одобрени от пакетния мениджър на NodeBB могат да бъдат инсталирани чрез ACP" } \ No newline at end of file diff --git a/public/language/bn/error.json b/public/language/bn/error.json index 8d462e9151..6fd47fffaf 100644 --- a/public/language/bn/error.json +++ b/public/language/bn/error.json @@ -175,6 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "There seems to be a problem with your internet connection", - "socket-reconnect-failed": "Reconnect failed, please try again later", + "socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/cs/error.json b/public/language/cs/error.json index bfe99fb519..fbedf86258 100644 --- a/public/language/cs/error.json +++ b/public/language/cs/error.json @@ -175,6 +175,6 @@ "already-blocked": "Tento uživatel již byl zablokován.", "already-unblocked": "Tento uživatel již byl odblokován", "no-connection": "Zdá se, že nastal problém s připojením k internetu", - "socket-reconnect-failed": "Reconnect failed, please try again later", + "socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/da/error.json b/public/language/da/error.json index 15a19fb179..9f2b9c8eff 100644 --- a/public/language/da/error.json +++ b/public/language/da/error.json @@ -175,6 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "There seems to be a problem with your internet connection", - "socket-reconnect-failed": "Reconnect failed, please try again later", + "socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/de/error.json b/public/language/de/error.json index 9713a7fc85..e20858a89d 100644 --- a/public/language/de/error.json +++ b/public/language/de/error.json @@ -175,6 +175,6 @@ "already-blocked": "Dieser Nutzer ist bereits gesperrt", "already-unblocked": "Dieser Nutzer ist bereits entsperrt", "no-connection": "Es scheint als gäbe es ein Problem mit deiner Internetverbindung", - "socket-reconnect-failed": "Reconnect failed, please try again later", + "socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/el/error.json b/public/language/el/error.json index a29e8fdb8b..695f0f3d1e 100644 --- a/public/language/el/error.json +++ b/public/language/el/error.json @@ -175,6 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "There seems to be a problem with your internet connection", - "socket-reconnect-failed": "Reconnect failed, please try again later", + "socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/en-US/error.json b/public/language/en-US/error.json index 2d2e5da7ab..ebf6de4ec7 100644 --- a/public/language/en-US/error.json +++ b/public/language/en-US/error.json @@ -175,6 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "There seems to be a problem with your internet connection", - "socket-reconnect-failed": "Reconnect failed, please try again later", + "socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/en-x-pirate/error.json b/public/language/en-x-pirate/error.json index 2d2e5da7ab..ebf6de4ec7 100644 --- a/public/language/en-x-pirate/error.json +++ b/public/language/en-x-pirate/error.json @@ -175,6 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "There seems to be a problem with your internet connection", - "socket-reconnect-failed": "Reconnect failed, please try again later", + "socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/es/error.json b/public/language/es/error.json index a8754acd94..91c3a067e4 100644 --- a/public/language/es/error.json +++ b/public/language/es/error.json @@ -175,6 +175,6 @@ "already-blocked": "Este usuario ya está bloqueado.", "already-unblocked": "Este usuario ya está desbloqueado.", "no-connection": "Parece haber un problema con tu conexión a internet", - "socket-reconnect-failed": "Reconnect failed, please try again later", + "socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/et/error.json b/public/language/et/error.json index 45c93607ce..081c2623d0 100644 --- a/public/language/et/error.json +++ b/public/language/et/error.json @@ -175,6 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "There seems to be a problem with your internet connection", - "socket-reconnect-failed": "Reconnect failed, please try again later", + "socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/fa-IR/error.json b/public/language/fa-IR/error.json index 301da03d0c..5b695d287f 100644 --- a/public/language/fa-IR/error.json +++ b/public/language/fa-IR/error.json @@ -175,6 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "به نظر می رسد اینترنت شما مشکل دارد", - "socket-reconnect-failed": "Reconnect failed, please try again later", + "socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/fi/error.json b/public/language/fi/error.json index 4635abdd50..7828030e84 100644 --- a/public/language/fi/error.json +++ b/public/language/fi/error.json @@ -175,6 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "There seems to be a problem with your internet connection", - "socket-reconnect-failed": "Reconnect failed, please try again later", + "socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/fr/error.json b/public/language/fr/error.json index 7ad55e84d5..fbe86c9616 100644 --- a/public/language/fr/error.json +++ b/public/language/fr/error.json @@ -175,6 +175,6 @@ "already-blocked": "Cet utilisateur est déjà bloqué", "already-unblocked": "Cet utilisateur est déjà débloqué", "no-connection": "Il semble y avoir un problème avec votre connexion Internet", - "socket-reconnect-failed": "La reconnexion a échoué, veuillez réessayer plus tard", + "socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later", "plugin-not-whitelisted": "Impossible d'installer le plug-in – seuls les plugins mis en liste blanche dans le gestionnaire de packages NodeBB peuvent être installés via l'ACP" } \ No newline at end of file diff --git a/public/language/gl/error.json b/public/language/gl/error.json index 6cf90357c6..41356ebc76 100644 --- a/public/language/gl/error.json +++ b/public/language/gl/error.json @@ -175,6 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "There seems to be a problem with your internet connection", - "socket-reconnect-failed": "Reconnect failed, please try again later", + "socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/he/error.json b/public/language/he/error.json index f6bb20d2bb..3f07d4829d 100644 --- a/public/language/he/error.json +++ b/public/language/he/error.json @@ -175,6 +175,6 @@ "already-blocked": "המשתמש כבר חסום", "already-unblocked": "המשתמש כבר לא חסום", "no-connection": "נראה שיש בעיות בחיבור האינטרנט שלך..", - "socket-reconnect-failed": "Reconnect failed, please try again later", + "socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later", "plugin-not-whitelisted": "לא ניתן להתקין את התוסף – ניתן להתקין דרך הניהול רק תוספים שנמצאים ברשימה הלבנה של מנהל החבילות של NodeBB." } \ No newline at end of file diff --git a/public/language/hr/error.json b/public/language/hr/error.json index 54320c9ed7..3daf75a236 100644 --- a/public/language/hr/error.json +++ b/public/language/hr/error.json @@ -175,6 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "There seems to be a problem with your internet connection", - "socket-reconnect-failed": "Reconnect failed, please try again later", + "socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/hu/error.json b/public/language/hu/error.json index fd01c26310..708f3c775d 100644 --- a/public/language/hu/error.json +++ b/public/language/hu/error.json @@ -175,6 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "There seems to be a problem with your internet connection", - "socket-reconnect-failed": "Reconnect failed, please try again later", + "socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/id/error.json b/public/language/id/error.json index 3428fa90f6..0a0ac11943 100644 --- a/public/language/id/error.json +++ b/public/language/id/error.json @@ -175,6 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "There seems to be a problem with your internet connection", - "socket-reconnect-failed": "Reconnect failed, please try again later", + "socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/it/error.json b/public/language/it/error.json index a033ba420a..1218441858 100644 --- a/public/language/it/error.json +++ b/public/language/it/error.json @@ -175,6 +175,6 @@ "already-blocked": "Questo utente è già bloccato", "already-unblocked": "Questo utente è già sbloccato", "no-connection": "Sembra ci sia un problema con la tua connessione internet", - "socket-reconnect-failed": "Riconnessione non riuscita, per favore riprova più tardi", + "socket-reconnect-failed": "Impossibile raggiungere il server al momento. Clicca qui per riprovare o riprova in un secondo momento", "plugin-not-whitelisted": "Impossibile installare il plug-in & solo i plugin nella whitelist del Gestione Pacchetti di NodeBB possono essere installati tramite ACP" } \ No newline at end of file diff --git a/public/language/ja/error.json b/public/language/ja/error.json index b9da3470f3..c4eac6aeca 100644 --- a/public/language/ja/error.json +++ b/public/language/ja/error.json @@ -175,6 +175,6 @@ "already-blocked": "このユーザーは既にブロックされています", "already-unblocked": "このユーザーは既にブロック解除されています", "no-connection": "インターネット接続に問題があるようです", - "socket-reconnect-failed": "Reconnect failed, please try again later", + "socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/ko/error.json b/public/language/ko/error.json index 158668f41d..f879589785 100644 --- a/public/language/ko/error.json +++ b/public/language/ko/error.json @@ -175,6 +175,6 @@ "already-blocked": "이 사용자는 이미 차단 되었습니다", "already-unblocked": "이 사용자는 이미 차단 해제 되었습니다", "no-connection": "사용자님의 인터넷 연결에 문제가있는 것 같습니다", - "socket-reconnect-failed": "Reconnect failed, please try again later", + "socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/lt/error.json b/public/language/lt/error.json index d4eeaab7b5..3598ac9193 100644 --- a/public/language/lt/error.json +++ b/public/language/lt/error.json @@ -175,6 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "Panašu, jog yra problema su jūsų interneto prieiga", - "socket-reconnect-failed": "Reconnect failed, please try again later", + "socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/lv/error.json b/public/language/lv/error.json index 7eb664b7cd..ce80b1aee3 100644 --- a/public/language/lv/error.json +++ b/public/language/lv/error.json @@ -175,6 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "Šķiet, ka pastāv problēma ar Tavu interneta savienojumu", - "socket-reconnect-failed": "Reconnect failed, please try again later", + "socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/ms/error.json b/public/language/ms/error.json index aac661fea9..5106040b82 100644 --- a/public/language/ms/error.json +++ b/public/language/ms/error.json @@ -175,6 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "There seems to be a problem with your internet connection", - "socket-reconnect-failed": "Reconnect failed, please try again later", + "socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/nb/error.json b/public/language/nb/error.json index cd86a6b096..755ebfd9b2 100644 --- a/public/language/nb/error.json +++ b/public/language/nb/error.json @@ -175,6 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "There seems to be a problem with your internet connection", - "socket-reconnect-failed": "Reconnect failed, please try again later", + "socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/nl/error.json b/public/language/nl/error.json index e5ab6e3878..ea797e2ea1 100644 --- a/public/language/nl/error.json +++ b/public/language/nl/error.json @@ -175,6 +175,6 @@ "already-blocked": "Deze gebruiker is al geblokkeerd", "already-unblocked": "Deze gebruiker is al gedeblokkeerd", "no-connection": "Er lijkt een probleem te zijn met je internetverbinding", - "socket-reconnect-failed": "Reconnect failed, please try again later", + "socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later", "plugin-not-whitelisted": "Kan plugin niet installeren – alleen plugins toegestaan door de NodeBB Package Manager kunnen via de ACP geinstalleerd worden" } \ No newline at end of file diff --git a/public/language/pl/error.json b/public/language/pl/error.json index b6eaa8349e..b0c6f69db0 100644 --- a/public/language/pl/error.json +++ b/public/language/pl/error.json @@ -175,6 +175,6 @@ "already-blocked": "Ten użytkownik jest już zablokowany", "already-unblocked": "Ten użytkownik jest już odblokowany", "no-connection": "Sprawdź swoje połączenie z internetem", - "socket-reconnect-failed": "Reconnect failed, please try again later", + "socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/pt-BR/error.json b/public/language/pt-BR/error.json index bc2cf4721b..4f65019307 100644 --- a/public/language/pt-BR/error.json +++ b/public/language/pt-BR/error.json @@ -175,6 +175,6 @@ "already-blocked": "Este usuário já foi bloqueado", "already-unblocked": "Este usuário já foi desbloqueado", "no-connection": "Parece haver um problema com a sua conexão com a internet", - "socket-reconnect-failed": "Reconnect failed, please try again later", + "socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/pt-PT/error.json b/public/language/pt-PT/error.json index 54b236f436..ddc52e0b16 100644 --- a/public/language/pt-PT/error.json +++ b/public/language/pt-PT/error.json @@ -175,6 +175,6 @@ "already-blocked": "Este utilizador já está bloqueado", "already-unblocked": "Este utilizador já está desbloqueado", "no-connection": "Parece haver um problema com a tua conexão à Internet", - "socket-reconnect-failed": "Reconnect failed, please try again later", + "socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/ro/error.json b/public/language/ro/error.json index df5981dbfa..4b494f2870 100644 --- a/public/language/ro/error.json +++ b/public/language/ro/error.json @@ -175,6 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "There seems to be a problem with your internet connection", - "socket-reconnect-failed": "Reconnect failed, please try again later", + "socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/ru/error.json b/public/language/ru/error.json index e5e6144cb9..6cfcdda27e 100644 --- a/public/language/ru/error.json +++ b/public/language/ru/error.json @@ -175,6 +175,6 @@ "already-blocked": "Этот пользователь уже заблокирован", "already-unblocked": "Этот пользователь уже разблокирован", "no-connection": "Похоже, есть проблема с вашим подключением к Интернету", - "socket-reconnect-failed": "Reconnect failed, please try again later", + "socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/rw/error.json b/public/language/rw/error.json index fec91faf30..56efaaab99 100644 --- a/public/language/rw/error.json +++ b/public/language/rw/error.json @@ -175,6 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "There seems to be a problem with your internet connection", - "socket-reconnect-failed": "Reconnect failed, please try again later", + "socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/sc/error.json b/public/language/sc/error.json index 2d2e5da7ab..ebf6de4ec7 100644 --- a/public/language/sc/error.json +++ b/public/language/sc/error.json @@ -175,6 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "There seems to be a problem with your internet connection", - "socket-reconnect-failed": "Reconnect failed, please try again later", + "socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/sk/error.json b/public/language/sk/error.json index 90679ba3cb..b4dc7108c1 100644 --- a/public/language/sk/error.json +++ b/public/language/sk/error.json @@ -175,6 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "Zdá sa, že máte problém s pripojením k internetu", - "socket-reconnect-failed": "Reconnect failed, please try again later", + "socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/sl/error.json b/public/language/sl/error.json index da04905711..a186c812ba 100644 --- a/public/language/sl/error.json +++ b/public/language/sl/error.json @@ -175,6 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "There seems to be a problem with your internet connection", - "socket-reconnect-failed": "Reconnect failed, please try again later", + "socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/sr/error.json b/public/language/sr/error.json index 2cff57688c..d2500b60d0 100644 --- a/public/language/sr/error.json +++ b/public/language/sr/error.json @@ -175,6 +175,6 @@ "already-blocked": "Овај корисник је већ блокиран", "already-unblocked": "Овај корисник је већ одблокиран", "no-connection": "Изгледа да постоји проблем са вашом интернет везом", - "socket-reconnect-failed": "Reconnect failed, please try again later", + "socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/sv/error.json b/public/language/sv/error.json index 266935efe0..2a245b4fdf 100644 --- a/public/language/sv/error.json +++ b/public/language/sv/error.json @@ -175,6 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "Det verkar vara något problem med din internetanslutning", - "socket-reconnect-failed": "Reconnect failed, please try again later", + "socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/th/error.json b/public/language/th/error.json index acd49d13b1..f21336e4ed 100644 --- a/public/language/th/error.json +++ b/public/language/th/error.json @@ -175,6 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "There seems to be a problem with your internet connection", - "socket-reconnect-failed": "Reconnect failed, please try again later", + "socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/tr/error.json b/public/language/tr/error.json index 6e5d704b64..bff70f0089 100644 --- a/public/language/tr/error.json +++ b/public/language/tr/error.json @@ -175,6 +175,6 @@ "already-blocked": "Bu kullanıcı zaten engellendi", "already-unblocked": "Bu kullanıcı zaten engellenmedi", "no-connection": "İnternet bağlantınızda sorun var gibi görünüyor", - "socket-reconnect-failed": "Reconnect failed, please try again later", + "socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later", "plugin-not-whitelisted": "– eklentisi yüklenemedi, sadece NodeBB Paket Yöneticisi tarafından onaylanan eklentiler kontrol panelinden kurulabilir" } \ No newline at end of file diff --git a/public/language/uk/error.json b/public/language/uk/error.json index 12bae97fdf..8229219d14 100644 --- a/public/language/uk/error.json +++ b/public/language/uk/error.json @@ -175,6 +175,6 @@ "already-blocked": "Цей користувач вже заблокований", "already-unblocked": "Цей користувач вже розблокований", "no-connection": "Схоже, виникла проблема з вашим Інтернет-з'єднанням", - "socket-reconnect-failed": "Reconnect failed, please try again later", + "socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/vi/error.json b/public/language/vi/error.json index ff24aa7d04..e0f6e0c346 100644 --- a/public/language/vi/error.json +++ b/public/language/vi/error.json @@ -175,6 +175,6 @@ "already-blocked": "This user is already blocked", "already-unblocked": "This user is already unblocked", "no-connection": "Kết nối internet của bạn có vấn đề.", - "socket-reconnect-failed": "Reconnect failed, please try again later", + "socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file diff --git a/public/language/zh-CN/error.json b/public/language/zh-CN/error.json index 9df74f76cd..9d586203bf 100644 --- a/public/language/zh-CN/error.json +++ b/public/language/zh-CN/error.json @@ -175,6 +175,6 @@ "already-blocked": "此用户已被屏蔽", "already-unblocked": "此用户已被取消屏蔽", "no-connection": "您的网络连接似乎存在问题", - "socket-reconnect-failed": "Reconnect failed, please try again later", + "socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later", "plugin-not-whitelisted": "无法安装插件 – 只有被NodeBB包管理器列入白名单的插件才能通过ACP安装。" } \ No newline at end of file diff --git a/public/language/zh-TW/error.json b/public/language/zh-TW/error.json index 7525739d98..509624f544 100644 --- a/public/language/zh-TW/error.json +++ b/public/language/zh-TW/error.json @@ -175,6 +175,6 @@ "already-blocked": "此使用者已被封鎖", "already-unblocked": "此使用者已被取消封鎖", "no-connection": "您的網路連線似乎有問題", - "socket-reconnect-failed": "Reconnect failed, please try again later", + "socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later", "plugin-not-whitelisted": "Unable to install plugin – only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP" } \ No newline at end of file From 18ae7cf719fba7dab4a7461894de9de1364b87ac Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sat, 2 Jan 2021 02:53:03 +0000 Subject: [PATCH 86/98] chore(deps): update dependency eslint to v7.17.0 --- install/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/package.json b/install/package.json index 79a9ca30c1..de71577b65 100644 --- a/install/package.json +++ b/install/package.json @@ -153,7 +153,7 @@ "@commitlint/cli": "11.0.0", "@commitlint/config-angular": "11.0.0", "coveralls": "3.1.0", - "eslint": "7.16.0", + "eslint": "7.17.0", "eslint-config-airbnb-base": "14.2.1", "eslint-plugin-import": "2.22.1", "grunt": "1.3.0", From ebf1624a906e13c7da9dfb0bc4316f455541ab83 Mon Sep 17 00:00:00 2001 From: "Misty (Bot)" Date: Sat, 2 Jan 2021 09:07:42 +0000 Subject: [PATCH 87/98] Latest translations and fallbacks --- public/language/bg/admin/manage/privileges.json | 8 ++++---- public/language/bg/error.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/public/language/bg/admin/manage/privileges.json b/public/language/bg/admin/manage/privileges.json index beb89c2a07..d61fb1ad39 100644 --- a/public/language/bg/admin/manage/privileges.json +++ b/public/language/bg/admin/manage/privileges.json @@ -39,13 +39,13 @@ "admin-categories": "Категории", "admin-privileges": "Правомощия", "admin-users": "Потребители", - "admin-admins-mods": "Admins & Mods", - "admin-groups": "Groups", - "admin-tags": "Tags", + "admin-admins-mods": "Администратори и модератори", + "admin-groups": "Групи", + "admin-tags": "Етикети", "admin-settings": "Настройки", "alert.confirm-moderate": "Наистина ли искате да дадете правомощието за модериране на тази потребителска група? Тази група е публична и всеки може свободно да се присъедини към нея.", - "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", + "alert.confirm-admins-mods": "Наистина ли искате да дадете правото „Администратори и модератори“ на този потребител/група? Потребителите с това право могат да променят правомощията на други групи, включително да им дават правото на супер администратори", "alert.confirm-save": "Моля, потвърдете желанието си да запазите тези правомощия", "alert.saved": "Промените по правомощията са запазени и приложени", "alert.confirm-discard": "Наистина ли искате да отхвърлите промените по правомощията?", diff --git a/public/language/bg/error.json b/public/language/bg/error.json index da394e7688..0a6461682d 100644 --- a/public/language/bg/error.json +++ b/public/language/bg/error.json @@ -175,6 +175,6 @@ "already-blocked": "Този потребител вече е блокиран", "already-unblocked": "Този потребител вече е отблокиран", "no-connection": "Изглежда има проблем с връзката Ви с Интернет", - "socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later", + "socket-reconnect-failed": "В момента сървърът е недостъпен. Натиснете тук, за да опитате отново, или опитайте пак по-късно.", "plugin-not-whitelisted": "Добавката не може да бъде инсталирана – само добавки, одобрени от пакетния мениджър на NodeBB могат да бъдат инсталирани чрез ACP" } \ No newline at end of file From 5fcf3ea61bb8e74da6f2796e3939fe78686cbd5a Mon Sep 17 00:00:00 2001 From: "Misty (Bot)" Date: Mon, 4 Jan 2021 14:40:53 +0000 Subject: [PATCH 88/98] chore: incrementing version number - v1.16.1-beta.0 --- install/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/package.json b/install/package.json index de71577b65..367f2e01e2 100644 --- a/install/package.json +++ b/install/package.json @@ -2,7 +2,7 @@ "name": "nodebb", "license": "GPL-3.0", "description": "NodeBB Forum", - "version": "1.16.0", + "version": "1.16.1-beta.0", "homepage": "http://www.nodebb.org", "repository": { "type": "git", From 4524f825d4012c83f5693607da09a31aaf7185c4 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Mon, 4 Jan 2021 17:37:22 +0000 Subject: [PATCH 89/98] fix(deps): update dependency benchpressjs to v2.4.0 --- install/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/package.json b/install/package.json index 367f2e01e2..53a07aec5b 100644 --- a/install/package.json +++ b/install/package.json @@ -41,7 +41,7 @@ "async": "^3.2.0", "autoprefixer": "10.1.0", "bcryptjs": "2.4.3", - "benchpressjs": "2.3.0", + "benchpressjs": "2.4.0", "body-parser": "^1.19.0", "bootbox": "4.4.0", "bootstrap": "^3.4.1", From 87e333b44f3b27d440f669466977ab59c68739b2 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Mon, 4 Jan 2021 18:53:15 +0000 Subject: [PATCH 90/98] fix(deps): update dependency nodebb-theme-persona to v10.3.16 --- install/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/package.json b/install/package.json index 53a07aec5b..d9f24d68dc 100644 --- a/install/package.json +++ b/install/package.json @@ -101,7 +101,7 @@ "nodebb-plugin-spam-be-gone": "0.7.7", "nodebb-rewards-essentials": "0.1.4", "nodebb-theme-lavender": "5.0.17", - "nodebb-theme-persona": "10.3.15", + "nodebb-theme-persona": "10.3.16", "nodebb-theme-slick": "1.3.7", "nodebb-theme-vanilla": "11.3.10", "nodebb-widget-essentials": "5.0.2", From 1968bf50f19ca2bad56fda278d08426a9f538675 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 4 Jan 2021 16:58:29 -0500 Subject: [PATCH 91/98] fix: #9163, fix total connection count on ACP sockets.sockets is a JS map in 3.x --- src/socket.io/admin/rooms.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/socket.io/admin/rooms.js b/src/socket.io/admin/rooms.js index 1dc58f1c13..705d3d9e9d 100644 --- a/src/socket.io/admin/rooms.js +++ b/src/socket.io/admin/rooms.js @@ -131,10 +131,10 @@ SocketRooms.getLocalStats = function () { topics: {}, }; - if (io) { + if (io && io.sockets) { socketData.onlineGuestCount = Sockets.getCountInRoom('online_guests'); socketData.onlineRegisteredCount = SocketRooms.getOnlineUserCount(io); - socketData.socketCount = Object.keys(io.sockets.sockets).length; + socketData.socketCount = io.sockets.sockets.size; socketData.users.categories = Sockets.getCountInRoom('categories'); socketData.users.recent = Sockets.getCountInRoom('recent_topics'); socketData.users.unread = Sockets.getCountInRoom('unread_topics'); From 0d7dfeeb86c178971aba1e4a9e32a8fbe04f3795 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 4 Jan 2021 17:09:14 -0500 Subject: [PATCH 92/98] chore: increase test timeout --- test/database/sorted.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/database/sorted.js b/test/database/sorted.js index c63c653433..2670e7e791 100644 --- a/test/database/sorted.js +++ b/test/database/sorted.js @@ -334,6 +334,7 @@ describe('Sorted Set methods', function () { }); it('should work with big arrays (length > 100) ', async function () { + this.timeout(50000); const keys = []; for (let i = 0; i < 400; i++) { /* eslint-disable no-await-in-loop */ From fd045c67c92d94afc7e027bce21d898d7c3fcea0 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Tue, 5 Jan 2021 12:37:16 +0000 Subject: [PATCH 93/98] fix(deps): update socket.io packages to v3.0.5 --- install/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/package.json b/install/package.json index d9f24d68dc..ffade032c2 100644 --- a/install/package.json +++ b/install/package.json @@ -128,9 +128,9 @@ "sharp": "0.27.0", "sitemap": "^6.1.0", "slideout": "1.0.1", - "socket.io": "3.0.4", + "socket.io": "3.0.5", "socket.io-adapter-cluster": "^1.0.1", - "socket.io-client": "3.0.4", + "socket.io-client": "3.0.5", "socket.io-redis": "6.0.1", "sortablejs": "1.10.2", "spdx-license-list": "^6.1.0", From b9ba44edd7faab7a5516b34f263bba31591dca7c Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 5 Jan 2021 13:40:10 -0500 Subject: [PATCH 94/98] fix: #9166 missing relative path in topic thumbs modal and topic list --- install/package.json | 4 ++-- src/views/modals/topic-thumbs.tpl | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/install/package.json b/install/package.json index ffade032c2..4f52dabb6b 100644 --- a/install/package.json +++ b/install/package.json @@ -101,7 +101,7 @@ "nodebb-plugin-spam-be-gone": "0.7.7", "nodebb-rewards-essentials": "0.1.4", "nodebb-theme-lavender": "5.0.17", - "nodebb-theme-persona": "10.3.16", + "nodebb-theme-persona": "10.3.17", "nodebb-theme-slick": "1.3.7", "nodebb-theme-vanilla": "11.3.10", "nodebb-widget-essentials": "5.0.2", @@ -189,4 +189,4 @@ "url": "https://github.com/barisusakli" } ] -} \ No newline at end of file +} diff --git a/src/views/modals/topic-thumbs.tpl b/src/views/modals/topic-thumbs.tpl index 7ee9773836..814ea86718 100644 --- a/src/views/modals/topic-thumbs.tpl +++ b/src/views/modals/topic-thumbs.tpl @@ -5,7 +5,7 @@ {{{ each thumbs }}}
    - +

    From e445ae5a7a87416fc6bc70492614a5a167eab97a Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Wed, 6 Jan 2021 03:29:16 +0000 Subject: [PATCH 95/98] fix(deps): update dependency autoprefixer to v10.2.0 --- install/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/package.json b/install/package.json index 4f52dabb6b..52ced9f460 100644 --- a/install/package.json +++ b/install/package.json @@ -39,7 +39,7 @@ "ace-builds": "^1.4.9", "archiver": "^5.0.0", "async": "^3.2.0", - "autoprefixer": "10.1.0", + "autoprefixer": "10.2.0", "bcryptjs": "2.4.3", "benchpressjs": "2.4.0", "body-parser": "^1.19.0", From afe6d84710ffc8e3e9f93dd0b5efa1f488fd89b3 Mon Sep 17 00:00:00 2001 From: "Misty (Bot)" Date: Wed, 6 Jan 2021 09:09:57 +0000 Subject: [PATCH 96/98] Latest translations and fallbacks --- public/language/fr/error.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/language/fr/error.json b/public/language/fr/error.json index fbe86c9616..9c712fd0a7 100644 --- a/public/language/fr/error.json +++ b/public/language/fr/error.json @@ -175,6 +175,6 @@ "already-blocked": "Cet utilisateur est déjà bloqué", "already-unblocked": "Cet utilisateur est déjà débloqué", "no-connection": "Il semble y avoir un problème avec votre connexion Internet", - "socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later", + "socket-reconnect-failed": "Serveur inaccessible pour le moment. Cliquez ici pour réessayer ou réessayez plus tard", "plugin-not-whitelisted": "Impossible d'installer le plug-in – seuls les plugins mis en liste blanche dans le gestionnaire de packages NodeBB peuvent être installés via l'ACP" } \ No newline at end of file From 8c31afae7d7fcae09e99f01e6a198ddb954ce579 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Wed, 6 Jan 2021 11:52:19 -0500 Subject: [PATCH 97/98] feat: #9173, show installed plugin versions in ./nodebb plugins --- src/cli/manage.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cli/manage.js b/src/cli/manage.js index 2f541149c0..c986a2725f 100644 --- a/src/cli/manage.js +++ b/src/cli/manage.js @@ -77,7 +77,7 @@ async function listPlugins() { const active = await db.getSortedSetRange('plugins:active', 0, -1); // Merge the two sets, defer to plugins in `installed` if already present - let combined = installed.concat(active.reduce((memo, cur) => { + const combined = installed.concat(active.reduce((memo, cur) => { if (!installedList.includes(cur)) { memo.push({ id: cur, @@ -90,12 +90,12 @@ async function listPlugins() { }, [])); // Alphabetical sort - combined = combined.sort((a, b) => (a.id > b.id ? 1 : -1)); + combined.sort((a, b) => (a.id > b.id ? 1 : -1)); // Pretty output process.stdout.write('Active plugins:\n'); combined.forEach((plugin) => { - process.stdout.write('\t* ' + plugin.id + ' ('); + process.stdout.write('\t* ' + plugin.id + (plugin.version ? '@' + plugin.version : '') + ' ('); process.stdout.write(plugin.installed ? 'installed'.green : 'not installed'.red); process.stdout.write(', '); process.stdout.write(plugin.active ? 'enabled'.green : 'disabled'.yellow); From 171017c38c24fa06fee7333ccf955883e393dfab Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 6 Jan 2021 12:15:32 -0500 Subject: [PATCH 98/98] fix: #9130, remove timestamp prefix from thumbnail names in API response --- src/topics/thumbs.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/topics/thumbs.js b/src/topics/thumbs.js index a894e12810..d65b99b3c4 100644 --- a/src/topics/thumbs.js +++ b/src/topics/thumbs.js @@ -31,12 +31,16 @@ Thumbs.get = async function (tids) { return singular ? null : tids.map(() => []); } + const hasTimestampPrefix = /^\d+-/; const upload_url = nconf.get('upload_url'); const sets = tids.map(tid => `${validator.isUUID(String(tid)) ? 'draft' : 'topic'}:${tid}:thumbs`); const thumbs = await Promise.all(sets.map(set => getThumbs(set))); let response = thumbs.map((thumbSet, idx) => thumbSet.map(thumb => ({ id: tids[idx], - name: path.basename(thumb), + name: (() => { + const name = path.basename(thumb); + return hasTimestampPrefix.test(name) ? name.slice(14) : name; + })(), url: thumb.startsWith('http') ? thumb : path.posix.join(upload_url, thumb), })));