diff --git a/CHANGELOG.md b/CHANGELOG.md index eba105bc73..91e3df8def 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,304 @@ +#### v3.2.0 (2023-06-28) + +##### Chores + +* **deps:** + * update dependency lint-staged to v13.2.3 (#11762) (26af152a) + * update commitlint monorepo to v17.6.6 (#11750) (a19e7b5c) + * update dependency sass-embedded to v1.63.6 (#11742) (8a961794) + * update dependency sass-embedded to v1.63.5 (#11738) (94d1dbc7) + * update dependency eslint to v8.43.0 (#11722) (7beadb6b) + * update coverallsapp/github-action action to v2.2.0 (#11690) (9282bc58) + * update dependency eslint to v8.42.0 (#11672) (55e5467d) + * update commitlint monorepo to v17.6.5 (#11648) (3b53f415) + * update dependency jsdom to v22.1.0 (#11640) (9a5d39c0) + * update dependency smtp-server to v3.12.0 (#11628) (57e3f999) + * update dependency eslint to v8.41.0 (#11616) (70bb50cd) + * update redis docker tag to v7 (#10830) (f1e2342d) + * update dependency jquery to v3.7.0 (#11591) (12c03130) +* up harmony (94013139) +* up persona (8b4e2ca9) +* up deps (b1de9472) +* up harmony (db5016cc) +* up harmony (8f6889e0) +* up harmony (c4c06be5) +* up harmony (777c7d09) +* up deps (7f465006) +* up harmony (f9e37829) +* incrementing version number - v3.1.7 (0b4e81ab) +* update changelog for v3.1.7 (8744e412) +* up persona (7a0e5c6d) +* up harmony (be474fb4) +* up harmony (1896b486) +* up persona (09d42076) +* up harmony (cce42fec) +* up markdown (4107d6b8) +* up themes (b2f70a2e) +* up composer (21919524) +* up harmony (64441602) +* incrementing version number - v3.1.6 (b3a3b130) +* up themes (61420fdc) +* up themes (f9990cab) +* bump themes for da02361b13d064763223533368f9b71d998ecf37 (c97977f8) +* up harmony (0f84f597) +* up emoji (4b0d3940) +* up emoji (291aa58e) +* incrementing version number - v3.1.5 (ec19343a) +* up harmony (56ac610b) +* up themes (0bfe361c) +* up themes (67ab222d) +* update lang key (1cc079f3) +* up harmony (590a7237) +* up harmony (ed54c7c0) +* up harmony (91760eef) +* up emoji (7ab05f5d) +* up harmony (43887328) +* up emoji (9a3c62f7) +* up emoji (783fbfd8) +* up emoji (7a3468a1) +* up emoji (933f5a90) +* up harmony (61f4202e) +* up themes (5d089363) +* scroll up alert (ca6f43c5) +* harmony (c48f15c5) +* up themes (14c93cd5) +* up harmony (6188d8be) +* up harmony (9e416d7f) +* incrementing version number - v3.1.4 (2452783c) +* up persona (78c2e29c) +* up markdown (7f5ec0ef) +* up themes (cdfc1bfe) +* up harmony (fd80b612) +* incrementing version number - v3.1.3 (3b4e9d3f) +* incrementing version number - v3.1.2 (40fa3489) +* incrementing version number - v3.1.1 (40250733) +* incrementing version number - v3.1.0 (0cb386bd) +* incrementing version number - v3.0.1 (26f6ea49) +* incrementing version number - v3.0.0 (224e08cd) +* **i18n:** + * fallback strings for new resources: nodebb.admin-appearance-skins (7ca85c60) + * fallback strings for new resources: nodebb.admin-advanced-cache, nodebb.admin-advanced-errors, nodebb.admin-development-logger (fa8e656d) + * fallback strings for new resources: nodebb.admin-appearance-customise (0863afd0) + * fallback strings for new resources: nodebb.user (195b09d5) + * fallback strings for new resources: nodebb.admin-manage-users (934b6792) + * fallback strings for new resources: nodebb.themes-harmony (619c4ff7) + * fallback strings for new resources: nodebb.topic (639eeb44) + * fallback strings for new resources: nodebb.admin-appearance-customise, nodebb.admin-appearance-skins, nodebb.admin-appearance-themes, nodebb.admin-settings-api (d508772c) + * fallback strings for new resources: nodebb.admin-manage-groups (e53929fc) + * fallback strings for new resources: nodebb.users (1ffc9221) + * fallback strings for new resources: nodebb.modules (54a683bb) + * fallback strings for new resources: nodebb.themes-harmony (0aef9a91) + * fallback strings for new resources: nodebb.admin-settings-api (78bc71aa) + +##### New Features + +* closes #11747, add pagination to tokens page (4b11cd0d) +* add vote privs to topic (#11734) (86faed6f) +* custom skins panel in acp (556fd65d) +* add req and socket to als, closes https://github.com/NodeBB/NodeBB/pull/10304 (68ddca1e) +* #11714, add page title (1a5e18cd) +* #11714, add registrationQueue flag (3080eb7e) +* do not show the replies container in a post's footer if the only reply present is the next post (da02361b) +* add new tab to define bs variables (de68f749) +* bring back noskin option (2edfe0ef) +* add not validated/expired (5fae09f3) +* closes #11671, allow custom classes on body (e89cfd44) +* upgrade fonts (392a7d28) +* allow more params to app.newTopic/newReply (325c1955) +* update progress bar more frequently (99aaa9f1) +* closes #11630 (8c9ab01f) +* add hidden-empty utility (63ae03b4) +* translate bodyShort (ed15cbb0) +* add lang string and drag fix (db9b807c) +* center chat modal attr (4833a2b9) +* token rolling API for admins (4f524e9f) +* token editing and deletion (ce23caf7) +* api token migration, new ACP tokens list, token creation (e4888dea) +* internal utility functions for token management (creation, deletion, etc) (7b8bffd7) + +##### Bug Fixes + +* **deps:** + * update socket.io packages to v4.7.1 (#11763) (1de2d632) + * update dependency ace-builds to v1.23.1 (#11759) (53ab5931) + * update dependency esbuild to v0.18.10 (#11760) (e7f68aec) + * update dependency pg-cursor to v2.10.1 (#11757) (a814440c) + * update dependency pg to v8.11.1 (#11758) (8b56fa2f) + * update dependency esbuild to v0.18.9 (#11755) (3adc7505) + * update socket.io packages to v4.7.0 (#11744) (b25e7247) + * update dependency semver to v7.5.3 (#11745) (9d0edc36) + * update dependency esbuild to v0.18.8 (#11751) (a7243790) + * update dependency esbuild to v0.18.7 (#11749) (85e5619c) + * update dependency sass to v1.63.6 (#11743) (939e5818) + * update dependency webpack to v5.88.0 (#11741) (c45854d9) + * update dependency sanitize-html to v2.11.0 (#11740) (9d5fadad) + * update dependency esbuild to v0.18.6 (#11736) (e0c137c9) + * update dependency sass to v1.63.5 (#11737) (61e45cb3) + * update dependency ace-builds to v1.23.0 (#11739) (a53e7d21) + * update dependency esbuild to v0.18.5 (#11730) (2a56d21c) + * update dependency esbuild to v0.18.4 (#11721) (ae349004) + * update dependency commander to v11 (#11719) (d454c5c4) + * update dependency lru-cache to v10 (#11716) (b3bdb9d0) + * update dependency semver to v7.5.2 (#11718) (770021e0) + * update dependency esbuild to v0.18.3 (#11715) (9f94b56f) + * update dependency webpack to v5.87.0 (#11713) (f95929b2) + * update dependency nodebb-plugin-2factor to v7.1.3 (#11711) (c9e41e10) + * update dependency sass to v1.63.4 (#11709) (dfcef322) + * update dependency esbuild to v0.18.2 (#11705) (9521c8de) + * update dependency nodebb-plugin-2factor to v7.1.1 (#11703) (fe96d6f8) + * update dependency ace-builds to v1.22.1 (#11699) (b0d91a55) + * update dependency esbuild to v0.18.1 (#11701) (5247f763) + * update dependency nodebb-theme-harmony to v1.0.47 (#11700) (96a7add5) + * update dependency sass to v1.63.3 (#11693) (1512a37d) + * update dependency @fontsource/poppins to v5.0.3 (#11696) (1bd8f898) + * update dependency @fontsource/inter to v5.0.3 (#11695) (ddb41fbe) + * update dependency esbuild to v0.18.0 (#11698) (2b419f93) + * update dependency webpack to v5.86.0 (#11691) (744f399c) + * update dependency connect-pg-simple to v9 (#11692) (32cebaa6) + * update dependency mongodb to v5.6.0 (#11670) (1738f3c5) + * update dependency lru-cache to v9.1.2 (#11668) (09fff40c) + * update dependency nodebb-plugin-emoji to v5.1.0 (#11683) (bf76989e) + * update dependency nodebb-plugin-dbsearch to v6.1.0 (#11689) (9ef40569) + * update dependency nodebb-theme-persona to v13.1.0 (#11688) (6240a8c6) + * update dependency nodebb-plugin-composer-default to v10.2.0 (#11686) (bfdb72ca) + * update dependency nodebb-plugin-mentions to v4.2.0 (#11687) (d58c9d8c) + * update dependency nodebb-plugin-2factor to v7.1.0 (#11685) (64557680) + * update dependency nodebb-plugin-markdown to v12.1.0 (#11684) (ed2d9a96) + * update dependency nodebb-theme-lavender to v7.1.0 (#11682) (c23deb87) + * update dependency nodebb-plugin-spam-be-gone to v2.1.0 (#11681) (5d8d0946) + * update dependency nodebb-theme-harmony to v1.0.43 (#11680) (601a0363) + * update dependency webpack to v5.85.1 (#11678) (ccf3d3bc) + * update dependency nodebb-theme-harmony to v1.0.42 (#11674) (d41651dc) + * update dependency nodebb-plugin-composer-default to v10.1.9 (#11676) (929835de) + * update dependency nodebb-theme-harmony to v1.0.40 (#11669) (b5ee0247) + * pin dependencies (#11660) (1033cd1c) + * update dependency webpack to v5.85.0 (#11663) (e6344db8) + * update dependency @fontsource/inter to v5 (#11661) (e5e8debf) + * update dependency nodebb-theme-harmony to v1.0.39 (#11659) (6d4ab1d0) + * update socket.io packages to v4.6.2 (#11658) (fe10356c) + * update dependency ipaddr.js to v2.1.0 (#11653) (a13b5c40) + * update dependency nodebb-plugin-composer-default to v10.1.8 (#11656) (032425b2) + * update dependency nodebb-plugin-markdown to v12.0.5 (#11657) (a7a1cda5) + * update dependency nodebb-theme-persona to v13.0.73 (#11652) (e4c2015f) + * update dependency nodebb-plugin-composer-default to v10.1.7 (#11654) (d242bc29) + * update dependency nodemailer to v6.9.3 (#11644) (bd93ab8b) + * update dependency nodebb-theme-harmony to v1.0.38 (#11646) (f9ba518a) + * update dependency nodebb-theme-persona to v13.0.72 (#11647) (c474841e) + * update dependency nodebb-widget-essentials to v7.0.13 (#11645) (5daa733c) + * update dependency webpack to v5.84.1 (#11635) (7c333fb1) + * update dependency @popperjs/core to v2.11.8 (#11636) (14ac1206) + * update dependency winston to v3.9.0 (#11637) (45af9333) + * update dependency postcss to v8.4.24 (#11642) (1bc3c384) + * update dependency nodebb-theme-persona to v13.0.71 (#11641) (a6be0cd7) + * update dependency nodebb-plugin-composer-default to v10.1.6 (#11638) (3c9a960e) + * update dependency cron to v2.3.1 (#11633) (f5bcdc87) + * update dependency nodebb-theme-persona to v13.0.70 (#11632) (73d8ac66) + * update dependency webpack to v5.84.0 (#11631) (23532464) + * update dependency nodebb-theme-peace to v2.0.27 (#11629) (b10227dc) + * update dependency ace-builds to v1.22.0 (#11621) (70d0fc1a) + * update dependency nodebb-plugin-emoji to v5.0.16 (#11626) (21b61b37) + * update dependency webpack-merge to v5.9.0 (#11622) (5c70b428) + * update dependency nodebb-theme-peace to v2.0.25 (#11615) (f0336af4) + * update dependency webpack to v5.83.1 (#11608) (a4ae2e08) + * update dependency rimraf to v5.0.1 (#11610) (d1ccfac1) + * update dependency terser-webpack-plugin to v5.3.9 (#11611) (af00ebbe) + * update dependency ace-builds to v1.21.1 (#11607) (d26c9bf8) + * update dependency pg-cursor to v2.10.0 (#11605) (10ed33f1) + * update dependency @socket.io/redis-adapter to v8.2.1 (#11602) (49b3badb) + * update dependency ace-builds to v1.21.0 (#11603) (3d9f6f41) + * update dependency pg to v8.11.0 (#11604) (9840289c) + * update dependency semver to v7.5.1 (#11597) (18606f9c) + * update dependency esbuild to v0.17.19 (#11598) (28fb4e10) + * update dependency mongodb to v5.5.0 (#11593) (88e891fc) + * update dependency nodemailer to v6.9.2 (#11590) (3a883e3f) + * update dependency connect-redis to v7.1.0 (#11592) (97ec0c75) + * update dependency webpack to v5.82.1 (#11585) (90e53177) + * update dependency ace-builds to v1.20.0 (#11587) (326d820f) +* #11761, don't escape topic title twice (01c36f8b) +* use btn instead of icon (aefef763) +* rollback ace-builds 1.23.0 doesnt work (ea150162) +* #11756, fix unique visitor stats in acp table (e45f513b) +* dont overwrite postIndex (c21d7dbc) +* closes #11731, set postIndex on pagination (cbd98c1b) +* pinned topic ordering if parent element has non topic elements (cfedd087) +* delete events not working (281078f5) +* if reply is in different topic dont hide in parent (9ee1d7b0) +* web (6d5f6a15) +* var name (b48a6e26) +* error toast on lost connection (2c89d784) +* dont send 200 status on admin upload errors (#11707) (8ca65b0c) +* #11702, don't escape backgroundImage twice (6740a51e) +* closes #11697, remove min attribute from rep thresholds (4a6249a4) +* skins page revert (33a6b3e1) +* dont add deleted users to users:online (d0e9eb2c) +* #11679, fix username change (db3bff09) +* closes #11673, topic search for harmony (ee8cb378) +* #11664, dont create backlinks for quotes (d3c5a79d) +* wrap on events page (a5b080ea) +* app.newTopic usage (9f3bdf75) +* rtl fixes for code and long text (05460d8e) +* topic count in tools after purge closes #11651 (1974abeb) +* add loadingMore flag to chat infinite load (e0300ab7) +* #11619, fix selector (cdeaef6b) +* text area height calculation in chats (c52916de) +* textarea on error (c77b6224) +* closes #11612 (3cef2535) +* edge case in category filter (5daaa5ea) +* #11572 (29303f4a) +* browser title if options.title is undefined (adf14ea5) +* lang strings (d6ead3d4) +* #11594 (11d315df) +* relocated upgrade script, removed outdated code that called core.api for tokens (3e6dd78d) + +##### Other Changes + +* fix whitespace (4768d06b) +* fix unused (dcc2bb10) +* //github.com/NodeBB/NodeBB/issues/11612 (cf7d9334) +* up peace (0da8b506) +* use csrf_token in ws handshake (#11573) (63b859f0) +* closes #11577 (ad1ae291) + +##### Performance Improvements + +* make less db calls to load indices (0b2feb9e) + +##### Refactors + +* show both emails in user list (daa5ac85) +* move async call to promise.all (e56e44ab) +* remove script (2360d296) +* remove log (9522951f) +* remove log (32f60df1) +* use new params (2cf865e8) +* use app.parseAndTranslate (b1fbc194) +* remove return (e0149462) + +##### Reverts + +* remove emoji email css (7b8ebd0a) + +##### Tests + +* fix spec (94f06301) +* make email requirement flag changes more explicit in tests, so cases do not rely on variable values from other cases (a8399aa2) +* fix another test (c638186b) +* fix email tests @julianlam (64718d0c) +* openapi (2f94eb21) +* openapi (bc74afaf) +* remove is touch test (1ab0faa4) +* remove socket.io test (366b18e0) +* log socket.io error (3cb4edf1) +* fix another test (12e75ff4) +* fix test (c201bf45) +* add back missing tests (031ffe49) +* add missing tests (a75fd636) +* fix digest test (aff6d20f) +* add email (4bfebc81) +* digest (61563b01) +* additional tests for .roll() (6765d053) +* fix authentication tests to use new token saving utility functions (f42b636a) + #### v3.1.7 (2023-06-21) ##### Chores diff --git a/install/package.json b/install/package.json index 950e580cb0..6701232457 100644 --- a/install/package.json +++ b/install/package.json @@ -92,16 +92,16 @@ "multiparty": "4.2.3", "nconf": "0.12.0", "nodebb-plugin-2factor": "7.1.3", - "nodebb-plugin-composer-default": "10.2.3", + "nodebb-plugin-composer-default": "10.2.4", "nodebb-plugin-dbsearch": "6.1.0", - "nodebb-plugin-emoji": "5.1.2", + "nodebb-plugin-emoji": "5.1.3", "nodebb-plugin-emoji-android": "4.0.0", "nodebb-plugin-markdown": "12.1.4", "nodebb-plugin-mentions": "4.2.0", "nodebb-plugin-ntfy": "1.0.15", "nodebb-plugin-spam-be-gone": "2.1.0", "nodebb-rewards-essentials": "0.2.3", - "nodebb-theme-harmony": "1.0.64", + "nodebb-theme-harmony": "1.0.65", "nodebb-theme-lavender": "7.1.1", "nodebb-theme-peace": "2.0.32", "nodebb-theme-persona": "13.1.6", @@ -135,7 +135,7 @@ "@socket.io/redis-adapter": "8.2.1", "sortablejs": "1.15.0", "spdx-license-list": "6.6.0", - "spider-detector": "2.0.0", + "spider-detector": "2.0.1", "terser-webpack-plugin": "5.3.9", "textcomplete": "0.18.2", "textcomplete.contenteditable": "0.1.1", diff --git a/public/src/admin/settings/api.js b/public/src/admin/settings/api.js index 4420e9a300..b8c709c722 100644 --- a/public/src/admin/settings/api.js +++ b/public/src/admin/settings/api.js @@ -13,6 +13,8 @@ define('admin/settings/api', ['settings', 'clipboard', 'bootbox', 'benchpress', const copyEls = document.querySelectorAll('[data-component="acp/tokens"] [data-action="copy"]'); new clipboard(copyEls); + $('[data-action="create"]').on('click', handleTokenCreation); + handleActions(); }; @@ -28,10 +30,6 @@ define('admin/settings/api', ['settings', 'clipboard', 'bootbox', 'benchpress', const action = subselector.getAttribute('data-action'); switch (action) { - case 'create': - handleTokenCreation(); - break; - case 'edit': handleTokenUpdate(subselector); break; diff --git a/public/src/modules/navigator.js b/public/src/modules/navigator.js index 1ac9035031..98c0752312 100644 --- a/public/src/modules/navigator.js +++ b/public/src/modules/navigator.js @@ -440,7 +440,7 @@ define('navigator', [ count = value; navigator.updateTextAndProgressBar(); setThumbToIndex(index); - toggle(count > 1); + toggle(count > 0); }; navigator.show = function () { @@ -464,6 +464,7 @@ define('navigator', [ } paginationBlockEl.toggleClass('ready', flag); + paginationBlockEl.toggleClass('noreplies', count <= 1); } navigator.delayedUpdate = function () { @@ -523,7 +524,7 @@ define('navigator', [ setThumbToIndex(index); } - toggle(count > 1); + toggle(count > 0); }; navigator.getIndex = () => index; diff --git a/src/controllers/helpers.js b/src/controllers/helpers.js index 5981cdc47a..0f6b5b4a34 100644 --- a/src/controllers/helpers.js +++ b/src/controllers/helpers.js @@ -58,9 +58,10 @@ helpers.buildQueryString = function (query, key, value) { helpers.addLinkTags = function (params) { params.res.locals.linkTags = params.res.locals.linkTags || []; + const page = params.page > 1 ? `?page=${params.page}` : ''; params.res.locals.linkTags.push({ rel: 'canonical', - href: `${url}/${params.url}`, + href: `${url}/${params.url}${page}`, }); params.tags.forEach((rel) => { diff --git a/src/controllers/recent.js b/src/controllers/recent.js index 32d253edd7..878f74d6b3 100644 --- a/src/controllers/recent.js +++ b/src/controllers/recent.js @@ -91,7 +91,12 @@ recentController.getData = async function (req, url, sort) { const pageCount = Math.max(1, Math.ceil(data.topicCount / settings.topicsPerPage)); data.pagination = pagination.create(page, pageCount, req.query); - helpers.addLinkTags({ url: url, res: req.res, tags: data.pagination.rel }); + helpers.addLinkTags({ + url: url, + res: req.res, + tags: data.pagination.rel, + page: page, + }); return data; }; diff --git a/src/controllers/tags.js b/src/controllers/tags.js index 3165303445..1b1d1a98f2 100644 --- a/src/controllers/tags.js +++ b/src/controllers/tags.js @@ -61,7 +61,12 @@ tagsController.getTag = async function (req, res) { const pageCount = Math.max(1, Math.ceil(topicCount / settings.topicsPerPage)); templateData.pagination = pagination.create(page, pageCount, req.query); - helpers.addLinkTags({ url: `tags/${tag}`, res: req.res, tags: templateData.pagination.rel }); + helpers.addLinkTags({ + url: `tags/${tag}`, + res: req.res, + tags: templateData.pagination.rel, + page: page, + }); templateData['feeds:disableRSS'] = meta.config['feeds:disableRSS']; templateData.rssFeedUrl = `${nconf.get('relative_path')}/tags/${tag}.rss`; diff --git a/src/controllers/topics.js b/src/controllers/topics.js index d177da7cbc..58ca059502 100644 --- a/src/controllers/topics.js +++ b/src/controllers/topics.js @@ -72,15 +72,15 @@ topicsController.get = async function getTopic(req, res, next) { const sort = req.query.sort || settings.topicPostSort; const set = sort === 'most_votes' ? `tid:${tid}:posts:votes` : `tid:${tid}:posts`; const reverse = sort === 'newest_to_oldest' || sort === 'most_votes'; - if (settings.usePagination) { - if (!req.query.page) { - currentPage = calculatePageFromIndex(postIndex, settings); - } else { - const top = ((currentPage - 1) * settings.postsPerPage) + 1; - const bottom = top + settings.postsPerPage; - if (!req.params.post_index || (postIndex < top || postIndex > bottom)) { - postIndex = top; - } + + if (!req.query.page) { + currentPage = calculatePageFromIndex(postIndex, settings); + } + if (settings.usePagination && req.query.page) { + const top = ((currentPage - 1) * settings.postsPerPage) + 1; + const bottom = top + settings.postsPerPage; + if (!req.params.post_index || (postIndex < top || postIndex > bottom)) { + postIndex = top; } } const { start, stop } = calculateStartStop(currentPage, postIndex, settings); @@ -115,7 +115,7 @@ topicsController.get = async function getTopic(req, res, next) { await Promise.all([ buildBreadcrumbs(topicData), addOldCategory(topicData, userPrivileges), - addTags(topicData, req, res), + addTags(topicData, req, res, currentPage), incrementViewCount(req, tid), markAsRead(req, tid), analytics.increment([`pageviews:byCid:${topicData.category.cid}`]), @@ -201,7 +201,7 @@ async function addOldCategory(topicData, userPrivileges) { } } -async function addTags(topicData, req, res) { +async function addTags(topicData, req, res, currentPage) { const postIndex = parseInt(req.params.post_index, 10) || 0; const postAtIndex = topicData.posts.find(p => parseInt(p.index, 10) === parseInt(Math.max(0, postIndex - 1), 10)); let description = ''; @@ -256,10 +256,11 @@ async function addTags(topicData, req, res) { await addOGImageTags(res, topicData, postAtIndex); + const page = currentPage > 1 ? `?page=${currentPage}` : ''; res.locals.linkTags = [ { rel: 'canonical', - href: `${url}/topic/${topicData.slug}`, + href: `${url}/topic/${topicData.slug}${page}`, }, ]; diff --git a/src/controllers/unread.js b/src/controllers/unread.js index 5db883a12a..d5809d2089 100644 --- a/src/controllers/unread.js +++ b/src/controllers/unread.js @@ -51,7 +51,12 @@ unreadController.get = async function (req, res) { data.pageCount = Math.max(1, Math.ceil(data.topicCount / userSettings.topicsPerPage)); data.pagination = pagination.create(page, data.pageCount, req.query); - helpers.addLinkTags({ url: 'unread', res: req.res, tags: data.pagination.rel }); + helpers.addLinkTags({ + url: 'unread', + res: req.res, + tags: data.pagination.rel, + page: page, + }); if (userSettings.usePagination && (page < 1 || page > data.pageCount)) { req.query.page = Math.max(1, Math.min(data.pageCount, page)); diff --git a/src/groups/membership.js b/src/groups/membership.js index f37853d433..66af1c1ed1 100644 --- a/src/groups/membership.js +++ b/src/groups/membership.js @@ -25,7 +25,7 @@ module.exports = function (Groups) { Groups.isMember = async function (uid, groupName) { if (!uid || parseInt(uid, 10) <= 0 || !groupName) { - return false; + return isMemberOfEphemeralGroup(uid, groupName); } const cacheKey = `${uid}:${groupName}`; @@ -43,8 +43,8 @@ module.exports = function (Groups) { return uids.map(() => false); } - if (groupName === 'guests') { - return uids.map(uid => parseInt(uid, 10) === 0); + if (groupName === 'guests' || groupName === 'spiders') { + return uids.map(uid => isMemberOfEphemeralGroup(uid, groupName)); } const cachedData = {}; @@ -64,7 +64,7 @@ module.exports = function (Groups) { Groups.isMemberOfGroups = async function (uid, groups) { if (!uid || parseInt(uid, 10) <= 0 || !groups.length) { - return groups.map(groupName => groupName === 'guests'); + return groups.map(groupName => isMemberOfEphemeralGroup(uid, groupName)); } const cachedData = {}; const nonCachedGroups = groups.filter(groupName => filterNonCached(cachedData, uid, groupName)); @@ -82,6 +82,11 @@ module.exports = function (Groups) { return groups.map(groupName => cachedData[`${uid}:${groupName}`]); }; + function isMemberOfEphemeralGroup(uid, groupName) { + return (groupName === 'guests' && parseInt(uid, 10) === 0) || + (groupName === 'spiders' && parseInt(uid, 10) === -1); + } + function filterNonCached(cachedData, uid, groupName) { const isMember = Groups.cache.get(`${uid}:${groupName}`); const isInCache = isMember !== undefined; diff --git a/src/messaging/index.js b/src/messaging/index.js index 729ec8fedf..e532043a12 100644 --- a/src/messaging/index.js +++ b/src/messaging/index.js @@ -292,8 +292,11 @@ async function checkReputation(uid) { if (meta.config['reputation:disabled']) { return; } - const reputation = await user.getUserField(uid, 'reputation'); - if (meta.config['min:rep:chat'] > reputation) { + const [reputation, isPrivileged] = await Promise.all([ + user.getUserField(uid, 'reputation'), + user.isPrivileged(uid), + ]); + if (!isPrivileged && meta.config['min:rep:chat'] > reputation) { throw new Error(`[[error:not-enough-reputation-to-chat, ${meta.config['min:rep:chat']}]]`); } } diff --git a/src/middleware/index.js b/src/middleware/index.js index a2554b161b..f3c42d11d6 100644 --- a/src/middleware/index.js +++ b/src/middleware/index.js @@ -229,7 +229,7 @@ middleware.delayLoading = function delayLoading(req, res, next) { middleware.buildSkinAsset = helpers.try(async (req, res, next) => { // If this middleware is reached, a skin was requested, so it is built on-demand - const targetSkin = path.basename(req.originalUrl).split('.css')[0]; + const targetSkin = path.basename(req.originalUrl).split('.css')[0].replace(/-rtl$/, ''); if (!targetSkin) { return next(); } diff --git a/src/posts/votes.js b/src/posts/votes.js index dfdfad2899..bfe5e1e47f 100644 --- a/src/posts/votes.js +++ b/src/posts/votes.js @@ -142,14 +142,17 @@ module.exports = function (Posts) { async function checkVoteLimitation(pid, uid, type) { // type = 'upvote' or 'downvote' const oneDay = 86400000; - const [reputation, targetUid, votedPidsToday] = await Promise.all([ + const [reputation, isPrivileged, targetUid, votedPidsToday] = await Promise.all([ user.getUserField(uid, 'reputation'), + user.isPrivileged(uid), Posts.getPostField(pid, 'uid'), db.getSortedSetRevRangeByScore( `uid:${uid}:${type}`, 0, -1, '+inf', Date.now() - oneDay ), ]); - + if (isPrivileged) { + return; + } if (reputation < meta.config[`min:rep:${type}`]) { throw new Error(`[[error:not-enough-reputation-to-${type}, ${meta.config[`min:rep:${type}`]}]]`); } diff --git a/src/user/settings.js b/src/user/settings.js index cb982a4014..6f560e58f7 100644 --- a/src/user/settings.js +++ b/src/user/settings.js @@ -12,7 +12,9 @@ const languages = require('../languages'); module.exports = function (User) { User.getSettings = async function (uid) { if (parseInt(uid, 10) <= 0) { - return await onSettingsLoaded(0, {}); + return await onSettingsLoaded(uid, { + usePagination: parseInt(uid, 10) === -1 ? 1 : undefined, // force spiders to use pagination + }); } let settings = await db.getObject(`user:${uid}:settings`); settings = settings || {}; diff --git a/test/groups.js b/test/groups.js index bfc6965c9e..c80313e467 100644 --- a/test/groups.js +++ b/test/groups.js @@ -209,36 +209,54 @@ describe('Groups', () => { }); describe('.isMember()', () => { - it('should return boolean true when a user is in a group', (done) => { - Groups.isMember(1, 'Test', (err, isMember) => { - assert.ifError(err); - assert.strictEqual(isMember, true); - done(); - }); + it('should return boolean true when a user is in a group', async () => { + const isMember = await Groups.isMember(1, 'Test'); + assert.strictEqual(isMember, true); }); - it('should return boolean false when a user is not in a group', (done) => { - Groups.isMember(2, 'Test', (err, isMember) => { - assert.ifError(err); - assert.strictEqual(isMember, false); - done(); - }); + it('should return boolean false when a user is not in a group', async () => { + const isMember = await Groups.isMember(2, 'Test'); + assert.strictEqual(isMember, false); }); - it('should return true for uid 0 and guests group', (done) => { - Groups.isMembers([1, 0], 'guests', (err, isMembers) => { - assert.ifError(err); - assert.deepStrictEqual(isMembers, [false, true]); - done(); - }); + it('should return true for uid 0 and guests group', async () => { + const isMember = await Groups.isMember(0, 'guests'); + assert.strictEqual(isMember, true); }); - it('should return true for uid 0 and guests group', (done) => { - Groups.isMemberOfGroups(0, ['guests', 'registered-users'], (err, isMembers) => { - assert.ifError(err); - assert.deepStrictEqual(isMembers, [true, false]); - done(); - }); + it('should return false for uid 0 and spiders group', async () => { + const isMember = await Groups.isMember(0, 'spiders'); + assert.strictEqual(isMember, false); + }); + + it('should return true for uid -1 and spiders group', async () => { + const isMember = await Groups.isMember(-1, 'spiders'); + assert.strictEqual(isMember, true); + }); + + it('should return false for uid -1 and guests group', async () => { + const isMember = await Groups.isMember(-1, 'guests'); + assert.strictEqual(isMember, false); + }); + + it('should return true for uid 0, false for uid -1 with guests group', async () => { + const isMembers = await Groups.isMembers([1, 0, -1], 'guests'); + assert.deepStrictEqual(isMembers, [false, true, false]); + }); + + it('should return false for uid 0, true for uid -1 with spiders group', async () => { + const isMembers = await Groups.isMembers([1, 0, -1], 'spiders'); + assert.deepStrictEqual(isMembers, [false, false, true]); + }); + + it('should return true for uid 0 and guests group', async () => { + const isMembers = await Groups.isMemberOfGroups(0, ['guests', 'registered-users', 'spiders']); + assert.deepStrictEqual(isMembers, [true, false, false]); + }); + + it('should return true for uid -1 and spiders group', async () => { + const isMembers = await Groups.isMemberOfGroups(-1, ['guests', 'registered-users', 'spiders']); + assert.deepStrictEqual(isMembers, [false, false, true]); }); }); @@ -406,16 +424,12 @@ describe('Groups', () => { }); describe('.hide()', () => { - it('should mark the group as hidden', (done) => { - Groups.hide('foo', (err) => { - assert.ifError(err); - - Groups.get('foo', {}, (err, groupObj) => { - assert.ifError(err); - assert.strictEqual(1, groupObj.hidden); - done(); - }); - }); + it('should mark the group as hidden', async () => { + await Groups.hide('foo'); + const groupObj = await Groups.get('foo', {}); + assert.strictEqual(1, groupObj.hidden); + const isMember = await db.isSortedSetMember('groups:visible:createtime', 'foo'); + assert.strictEqual(isMember, false); }); }); @@ -798,20 +812,6 @@ describe('Groups', () => { }); }); - describe('.hide()', () => { - it('should make a group hidden', (done) => { - Groups.hide('Test', function (err) { - assert.ifError(err); - assert.equal(arguments.length, 1); - db.isSortedSetMember('groups:visible:createtime', 'Test', (err, isMember) => { - assert.ifError(err); - assert.strictEqual(isMember, false); - done(); - }); - }); - }); - }); - describe('socket methods', () => { it('should error if data is null', (done) => { socketGroups.before({ uid: 0 }, 'groups.join', null, (err) => {