diff --git a/CHANGELOG.md b/CHANGELOG.md index b1af6016e4..2c5dcba8ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,236 @@ +#### 1.13.3 (2020-05-08) + +##### Chores + +* incrementing version number - v1.13.3 (ee583e80) +* bump persona (d2bd746c) +* incrementing version number - v1.13.2 (beafd613) +* **deps:** + * update dependency coveralls to v3.1.0 (5ed4a108) + * pin dependency @apidevtools/swagger-parser to 9.0.1 (51eccef5) + * update dependency husky to v4.2.5 (30a25983) + * update dependency husky to v4.2.4 (0a650118) + * update dependency lint-staged to v10.1.3 (a9e68639) + * update dependency nyc to v15.0.1 (#8231) (a3789e28) + * update dependency lint-staged to v10.1.2 (#8235) (e1919c90) + * update dependency lint-staged to v10.1.1 (944a6f58) + * update dependency lint-staged to v10.1.0 (30bd233b) + * update dependency eslint-config-airbnb-base to v14.1.0 (811c3aee) + * update dependency jsdom to v16.2.2 (c5a7242d) + * update dependency eslint-plugin-import to v2.20.2 (b92c1600) + * update dependency lint-staged to v10.0.10 (0ad4b556) + * update dependency coveralls to v3.0.11 (14458087) + * update dependency smtp-server to v3.6.0 (22681945) + * update dependency mocha to v7.1.1 (#8215) (c5356541) + * update dependency grunt to v1.1.0 (#8214) (b0864e7c) + * update dependency husky to v4.2.3 (#8162) (776fe9d2) + * update dependency lint-staged to v10.0.8 (#8180) (13d8f6f1) + * update dependency eslint-plugin-import to v2.20.1 (#8081) (4cdb3131) + * update dependency jsdom to v16.2.1 (#8165) (fbd95a50) + * update dependency husky to v4.2.2 (#8160) (f4ed35c9) + * update dependency jsdom to v16 (#8114) (1037de02) + +##### Documentation Changes + +* updated changelog (146388aa) + +##### New Features + +* allow activating additional plugins for testing via config.json (a969c5ce) +* move plugin tests to separate file (3a23ddab) +* remove node14 for now (a72e4429) +* reduce infinite scroll area (3fcbd691) +* manifest.json improvements from #8126 (#8264) (6e5ebb61) +* show error if json is invalid (15345627) +* moved component specs into separate files (cd506557) +* added UserObject, UserObjectFull, Breadcrumb, Pagination component (64d79fe5) +* added some summary and descriptions (ae3e90d6) +* add some descriptions (442c018e) +* common schema (eade13f9) +* openapi component (1af5507a) +* add page query param to docs (9987813f) +* tag route doc (bbddaadf) +* local redoc view on development mode only (1136a369) +* added auto-generated, slimmed-down openapi 3.0 file for read api (7b155dab) +* add parent cids to body class (23571224) +* add 2 hooks for modifying privileges (d080c7b0) +* add user ip to admin/dev/info (5e91a67e) +* change option name (cba5b23e) +* add no-build to ./nodebb setup (476f6717) +* add awaitable socket.emit (4083a6e3) +* settings sorted list (#8170) (3c9689a5) +* guard against accidental ommision (79737c53) +* **openapi:** + * merging openapi-test branch into master (8387178b) + * move all commonprops out for commonprops component (65c78de6) + * added template to commonprops (2425f453) + +##### Bug Fixes + +* #8302, send string to writeFileSync (d09bd2cf) +* winston showing json object (7d081843) +* sortable topics even if only 1 pinned topic (6765de3d) +* #8298, use class name added by jQueryUI instead (dd2bc189) +* topic search shortcut for macs (f2c725c6) +* #8297, uids.length is different than topics.length (0431d75f) +* #8297 guest handles shown in category.tpl (fcb81cb8) +* only add to set if numRecentReplies>0 (16a98eaf) +* #8293, don't show error if there are no self messages (be305410) +* failing tests @julianlam (ecd622fd) +* #3321, run plugin tests for installed plugins (a6bb9f43) +* remove deprecated mocha.opts (3d0db963) +* spec (84383d39) +* #8290, if there are no filters go to ?reset=1 (9839346e) +* #8283, update gdpr link again (2d076344) +* add missing await (4f1128fd) +* #8287, dont readd user after deletion (9d153fd3) +* missing await (4d6b2ec3) +* #8286, rescind notif when its resolved/rejected (0391856d) +* #8284, parse ToS on register (0ca84bd9) +* #8283, point to official site (17d664e0) +* jquery xhtml violations (275e837b) +* #8274 Don't escape HTML in manage users (#8275) (4855f1de) +* crash in topic controller (0c7c70ed) +* crash when res.locals.linkTags is undefined (7cab2b0f) +* #8272 user link in digest email (e80b8101) +* tag of /api/unread/total (9ffdab02) +* response hook logic (5a1c6ee7) +* remove upload picture test (avatars) (6edf02d4) +* remove tests related to group covers, as route is gone (442fe65f) +* #8269, return array of topics from hook (4eafe0f0) +* remove dead picture upload code #8260 (ef52461f) +* path.resolve to logs file (5bcaf715) +* only trigger infinitescroll on scroll end (ba6d3fd3) +* wrong data returned in available.groups (c7ea84a2) +* no focus on find user modal (1b425ef1) +* accidental fp precision on flag and acp dash graphs (bcbf98aa) +* #8232, unresolvable session mismatch on register cancel (f2f6fbf1) +* pin jquery to 3.4.1, #8252 (e440d617) +* #8249, don't send move notifications for deleted posts/topics (d77036db) +* missing descriptions for common properties (7b31fb34) +* some definitions in read API spec (03739b6f) +* tagged all routes in read api spec (455d42bc) +* override ACAO header for read API spec file (240d9091) +* throw error if topic does not exist (59cf0e80) +* hookname (e93578b8) +* #8230, add hook getUserDataByUserSlug (0d1b5a7f) +* ignore case for group details route (15d6975e) +* lint (740e598a) +* lint (8e23dec8) +* #8221, fix parent selection (08031730) +* invite properly (071506eb) +* admins not seeing invite button (8f4b99a4) +* #8217, add missing lang key (0b5fac75) +* #8206 first message in chat has false `newSet` (93acd139) +* #8203, fix user invites refreshing page (2f9c7c62) +* #8202, filter non-existing users in search by uid (f07f4f8e) +* notification bodyShort truncated if there is a comma in topic title (266061c3) +* hsts max-age missing translation (b67af70d) +* call next (80f1bcad) +* try travis fix again (05bee629) +* try fixing psql on travis (bc9e92a1) +* dont let regular users see other users watched categories (cf6eadb9) +* also fix updating bookmark if sorting is newest_to_oldest (6e5de39b) +* #8188, fix bookmark if sorting is newest_to_oldest (32ada7c4) +* duplicate ID + label (ac241fb8) +* #8184 global mods unable to revoke other user sessions (f0db240a) +* return null if field does not exist (e72a29b3) +* #8179, limit length of location/website/fullname, check grouptitle (14e78667) +* tag key (32636755) +* #8175 (bc93b567) +* #8168 re-allowing slashes in homePageRoute (667608a0) +* tweak to session validation in addHeaders (eddbd868) +* only call clearCookie for logged in users (630f5d5b) +* #6422, update deleted/restored messages (06703408) +* #8163, prevent account deletion (4d0636f8) +* register (5a0c7c14) +* #8157, update recent tid when post is moved (e7495440) +* tests (b73aa84d) +* move start/stop every iteration (dd3893b1) +* #8154, move start/stop every iteration (300c04ce) +* #8154, respect stop (690bb69d) +* #8156 dont allow loading members from hidden groups (f23bc347) +* #8155, don't validate name on update if groupName didn't change (03a02e5d) +* return correct number of suggested topics (236e1e68) +* #8151, don't crash if taskbar doesn't have element (2e794801) +* logic for determining dailyStats hour vars (398f0120) +* fix daily analytics being one day off (9ecdb92f) +* remove debug line (0b9ad416) +* no decimal places for category analytics (14655f87) +* #8142, broken site if no server-side session (#8148) (d6e3f3f0) +* #8144 pluginHooks in maintenance mode middleware (0885ec68) +* **deps:** + * #8298 bump persona (158d9231) + * update dependency nodebb-plugin-composer-default to v6.3.25 (89d17647) + * update dependency jquery to v3.5.1 [security] (#8281) (a69f0b29) + * update dependency nodebb-rewards-essentials to v0.1.3 (#8289) (919034a7) + * update dependency mongodb to v3.5.7 (#8279) (25d509c4) + * actually, swagger-parser is a dev dependency (d09c6ae0) + * missing @apidevtools/swagger-parser (f1720735) + * update dependency nodebb-theme-persona to v10.1.37 (#8258) (b0c30ceb) + * update dependency archiver to v4 (28777f67) + * update dependency mongodb to v3.5.6 (#8256) (49236067) + * bump dependencies (#8239) (e68156e1) + * update dependency jsesc to v3.0.1 (#8243) (92b55ef5) + * update dependency jsesc to v3 (bb70cebb) + * update dependency pg to v8 (#8227) (ac98775f) + * update dependency validator to v13 (f497ee62) + * update dependency sharp to v0.25.2 (#8220) (dd660c87) + * bump markdown (ee6cb412) + * update dependency mongodb to v3.5.5 (#8205) (5535c50c) + * update dependency sitemap to v6 (#8198) (2052f14c) + * update dependency nodebb-plugin-composer-default to v6.3.23 (6d98d5a1) + * update dependency sharp to v0.25.1 (#8199) (21e91c91) + * update dependency nodebb-plugin-composer-default to v6.3.22 (#8193) (e01f05e3) + * update dependency nodebb-theme-slick to v1.2.29 (#8177) (9daa21ff) + * update dependency nodebb-theme-vanilla to v11.1.16 (#8178) (7d6a983b) + * update dependency nodebb-theme-persona to v10.1.35 (#8176) (3acc24b0) + * update dependency sharp to v0.24.1 (#8164) (7cc63f7d) + * update dependency mongodb to v3.5.3 (#8161) (4b907137) + * update dependency nodebb-widget-essentials to v4.1.0 (#8159) (a5f3c2a2) + * update dependency request to v2.88.2 (#8158) (7fde180a) + * update dependency redis to v3 (#8152) (ef964b11) + * update dependency rimraf to v3.0.2 (#8153) (d8efc6b6) +* **openapi:** + * v14 test fix (23a0b8c5) + * remove account and group upload routes (d342a28c) + * more fleshing out (058a15db) + * fleshed out admin routes (bae88e08) + * added some descriptions (ab4bd7e1) + * added PostsObject component (2395d2be) + * finished moving all category objects out (23dd2727) + * changed some descriptions (c939f8c6) + * added CategoryObject component (55d0a9ff) + * removed repeated breadcrumb blocks in favour of $ref (646fac1e) + * remove all repeated pagination blocks in favour of (ac579f9d) + * removed warning for category mods route (1cf62095) + * normalising the file for programmatic updates (3a5c6e07) + +##### Other Changes + +* #8298 (2e57d8ac) +* post.updatePostVoteCount (b25b51bd) +* //github.com/NodeBB/NodeBB (5e140454) +* categories.updateRecentTid (6c59683b) +* categories.updateRecentTid (51933c1f) +* router.page, dep. filter variant (0053e779) +* flags as well (5ebcdb18) +* crash when res.locals.linkTags is undefined" (fe03effe) +* //github.com/NodeBB/NodeBB (87a6ff0d) +* cnpm and pnpm (#8222) (e6a1741c) +* //github.com/NodeBB/NodeBB (7ae76477) +* openapi component" (683e5851) +* override ACAO header for read API spec file" (c82a2637) +* password.change (00e299e9) +* topic.tools.load (5aa76cdf) +* #8154, move start/stop every iteration" (4abe5eb7) + +##### Refactors + +* match core field name pinned (478ed6c1) +* getUsersCSV to use batch lib (1efb238a) +* reorganized socket.io admin modules (e1c6c3b2) + #### 1.13.2 (2020-02-05) ##### Chores diff --git a/install/data/defaults.json b/install/data/defaults.json index 86fc1075c4..797347f20f 100644 --- a/install/data/defaults.json +++ b/install/data/defaults.json @@ -81,9 +81,11 @@ "notificationType_upvote": "notification", "notificationType_new-topic": "notification", "notificationType_new-reply": "notification", + "notificationType_post-edit": "notification", "notificationType_follow": "notification", "notificationType_new-chat": "notification", "notificationType_group-invite": "notification", + "notificationType_group-request-membership": "notification", "notificationType_mention": "notification", "notificationType_new-register": "notification", "notificationType_post-queue": "notification", diff --git a/install/package.json b/install/package.json index e142fa0b70..8063b62b64 100644 --- a/install/package.json +++ b/install/package.json @@ -79,7 +79,7 @@ "mousetrap": "^1.6.5", "@nodebb/mubsub": "^1.6.0", "nconf": "^0.10.0", - "nodebb-plugin-composer-default": "6.3.25", + "nodebb-plugin-composer-default": "6.3.28", "nodebb-plugin-dbsearch": "4.0.7", "nodebb-plugin-emoji": "^3.3.0", "nodebb-plugin-emoji-android": "2.0.0", @@ -136,7 +136,7 @@ "@commitlint/cli": "8.3.5", "@commitlint/config-angular": "8.3.4", "coveralls": "3.1.0", - "eslint": "6.8.0", + "eslint": "7.0.0", "eslint-config-airbnb-base": "14.1.0", "eslint-plugin-import": "2.20.2", "grunt": "1.1.0", diff --git a/public/language/ar/notifications.json b/public/language/ar/notifications.json index 1e725aa87d..0c86cd65f8 100644 --- a/public/language/ar/notifications.json +++ b/public/language/ar/notifications.json @@ -35,6 +35,7 @@ "user_posted_to_dual": "%1 and %2 have posted replies to: %3", "user_posted_to_multiple": "%1 and %2 others have posted replies to: %3", "user_posted_topic": "%1 أنشأ موضوعًا جديدًا: %2", + "user_edited_post": "%1 has edited a post in %2", "user_started_following_you": "%1 صار يتابعك.", "user_started_following_you_dual": "%1 and %2 started following you.", "user_started_following_you_multiple": "%1 and %2 others started following you.", @@ -53,6 +54,7 @@ "notificationType_upvote": "عندما يوافقك احدهم على منشورك", "notificationType_new-topic": "When someone you follow posts a topic", "notificationType_new-reply": "When a new reply is posted in a topic you are watching", + "notificationType_post-edit": "When a post is edited in a topic you are watching", "notificationType_follow": "When someone starts following you", "notificationType_new-chat": "When you receive a chat message", "notificationType_group-invite": "When you receive a group invite", diff --git a/public/language/bg/notifications.json b/public/language/bg/notifications.json index 5bde63d158..dd195e5cb2 100644 --- a/public/language/bg/notifications.json +++ b/public/language/bg/notifications.json @@ -35,6 +35,7 @@ "user_posted_to_dual": "%1 и %2 публикуваха отговори на: %3", "user_posted_to_multiple": "%1 и %2 други публикуваха отговори на: %3", "user_posted_topic": "%1 публикува нова тема: %2", + "user_edited_post": "%1 редактира публикация в %2", "user_started_following_you": "%1 започна да Ви следва.", "user_started_following_you_dual": "%1 и %2 започнаха да Ви следват.", "user_started_following_you_multiple": "%1 и %2 започнаха да Ви следват.", @@ -53,6 +54,7 @@ "notificationType_upvote": "Когато някой гласува положително за Ваша публикация", "notificationType_new-topic": "Когато някой, когото следвате, публикува тема", "notificationType_new-reply": "Когато бъде публикуван нов отговор в тема, която следвате", + "notificationType_post-edit": "Когато бъде редактирана публикация в тема, която следите", "notificationType_follow": "Когато някой започне да Ви следва", "notificationType_new-chat": "Когато получите съобщение в разговор", "notificationType_group-invite": "Когато получите покана за група", diff --git a/public/language/bn/notifications.json b/public/language/bn/notifications.json index 2aaf5b055a..a11e62bc4c 100644 --- a/public/language/bn/notifications.json +++ b/public/language/bn/notifications.json @@ -35,6 +35,7 @@ "user_posted_to_dual": "%1 and %2 have posted replies to: %3", "user_posted_to_multiple": "%1 and %2 others have posted replies to: %3", "user_posted_topic": "%1 has posted a new topic: %2", + "user_edited_post": "%1 has edited a post in %2", "user_started_following_you": "%1 আপনাকে অনুসরন করা শুরু করেছেন।", "user_started_following_you_dual": "%1 and %2 started following you.", "user_started_following_you_multiple": "%1 and %2 others started following you.", @@ -53,6 +54,7 @@ "notificationType_upvote": "When someone upvotes your post", "notificationType_new-topic": "When someone you follow posts a topic", "notificationType_new-reply": "When a new reply is posted in a topic you are watching", + "notificationType_post-edit": "When a post is edited in a topic you are watching", "notificationType_follow": "When someone starts following you", "notificationType_new-chat": "When you receive a chat message", "notificationType_group-invite": "When you receive a group invite", diff --git a/public/language/cs/notifications.json b/public/language/cs/notifications.json index a0db549829..277095f420 100644 --- a/public/language/cs/notifications.json +++ b/public/language/cs/notifications.json @@ -35,6 +35,7 @@ "user_posted_to_dual": "%1%2 odpověděli na: %3", "user_posted_to_multiple": "%1 a %2 další/ch odpověděli na %3", "user_posted_topic": "%1 založil nové téma: %2", + "user_edited_post": "%1 has edited a post in %2", "user_started_following_you": "%1 vás začal sledovat.", "user_started_following_you_dual": "%1 a %2 vás začali sledovat.", "user_started_following_you_multiple": "%1 a %2 další/ch vás začali sledovat.", @@ -53,6 +54,7 @@ "notificationType_upvote": "Vyjádří-li někdo souhlas s vaším příspěvkem", "notificationType_new-topic": "Začne-li někdo sledovat příspěvky a téma", "notificationType_new-reply": "Bude-li přidán nový příspěvek v tématu, které sledujete", + "notificationType_post-edit": "When a post is edited in a topic you are watching", "notificationType_follow": "Začne-li vás někdo sledovat", "notificationType_new-chat": "Obdržíte-li novou konverzační zprávu", "notificationType_group-invite": "Obdržíte-li pozvání ke skupině", diff --git a/public/language/da/notifications.json b/public/language/da/notifications.json index 7544d7b047..51a44a8f0e 100644 --- a/public/language/da/notifications.json +++ b/public/language/da/notifications.json @@ -35,6 +35,7 @@ "user_posted_to_dual": "%1 og %2 har skrevet svar til: %3", "user_posted_to_multiple": "%1 og %2 andre har skrevet svar til: %3", "user_posted_topic": "%1 har oprettet en ny tråd: %2", + "user_edited_post": "%1 has edited a post in %2", "user_started_following_you": "%1 har valgt at følge dig.", "user_started_following_you_dual": "%1 og %2 har valgt at følge dig.", "user_started_following_you_multiple": "%1 og %2 har valgt at følge dig.", @@ -53,6 +54,7 @@ "notificationType_upvote": "When someone upvotes your post", "notificationType_new-topic": "When someone you follow posts a topic", "notificationType_new-reply": "When a new reply is posted in a topic you are watching", + "notificationType_post-edit": "When a post is edited in a topic you are watching", "notificationType_follow": "When someone starts following you", "notificationType_new-chat": "When you receive a chat message", "notificationType_group-invite": "When you receive a group invite", diff --git a/public/language/de/notifications.json b/public/language/de/notifications.json index 7426284d64..b9fbf859da 100644 --- a/public/language/de/notifications.json +++ b/public/language/de/notifications.json @@ -35,6 +35,7 @@ "user_posted_to_dual": "%1 und %2 haben auf %3 geantwortet.", "user_posted_to_multiple": "%1 und %2 andere Nutzer haben auf %3 geantwortet.", "user_posted_topic": "%1 hat ein neues Thema erstellt: %2", + "user_edited_post": "%1 has edited a post in %2", "user_started_following_you": "%1 folgt dir jetzt.", "user_started_following_you_dual": "%1 und %2 folgen dir jetzt.", "user_started_following_you_multiple": "%1 und %2 andere Nutzer folgen dir jetzt.", @@ -53,6 +54,7 @@ "notificationType_upvote": "Wenn jemand deinen beitrag positiv bewertet", "notificationType_new-topic": "Wenn jemand dem du folgst einen Beitrag erstellt", "notificationType_new-reply": "Wenn es eine neue Antwort auf ein Thema das du beobachtest gibt", + "notificationType_post-edit": "When a post is edited in a topic you are watching", "notificationType_follow": "Wenn dir jemand neues folgt", "notificationType_new-chat": "Wenn du eine Chat Nachricht erhältst", "notificationType_group-invite": "Wenn du eine Gruppeneinladung erhältst", diff --git a/public/language/el/notifications.json b/public/language/el/notifications.json index 8c07ad2261..fa538d29b6 100644 --- a/public/language/el/notifications.json +++ b/public/language/el/notifications.json @@ -35,6 +35,7 @@ "user_posted_to_dual": "%1 and %2 have posted replies to: %3", "user_posted_to_multiple": "%1 and %2 others have posted replies to: %3", "user_posted_topic": "%1 has posted a new topic: %2", + "user_edited_post": "%1 has edited a post in %2", "user_started_following_you": "%1 started following you.", "user_started_following_you_dual": "%1 and %2 started following you.", "user_started_following_you_multiple": "%1 and %2 others started following you.", @@ -53,6 +54,7 @@ "notificationType_upvote": "When someone upvotes your post", "notificationType_new-topic": "When someone you follow posts a topic", "notificationType_new-reply": "When a new reply is posted in a topic you are watching", + "notificationType_post-edit": "When a post is edited in a topic you are watching", "notificationType_follow": "When someone starts following you", "notificationType_new-chat": "When you receive a chat message", "notificationType_group-invite": "When you receive a group invite", diff --git a/public/language/en-GB/notifications.json b/public/language/en-GB/notifications.json index 3716089d71..7ce5515ca0 100644 --- a/public/language/en-GB/notifications.json +++ b/public/language/en-GB/notifications.json @@ -39,6 +39,7 @@ "user_posted_to_dual" : "%1 and %2 have posted replies to: %3", "user_posted_to_multiple" : "%1 and %2 others have posted replies to: %3", "user_posted_topic": "%1 has posted a new topic: %2", + "user_edited_post" : "%1 has edited a post in %2", "user_started_following_you": "%1 started following you.", "user_started_following_you_dual": "%1 and %2 started following you.", "user_started_following_you_multiple": "%1 and %2 others started following you.", @@ -59,6 +60,7 @@ "notificationType_upvote": "When someone upvotes your post", "notificationType_new-topic": "When someone you follow posts a topic", "notificationType_new-reply": "When a new reply is posted in a topic you are watching", + "notificationType_post-edit": "When a post is edited in a topic you are watching", "notificationType_follow": "When someone starts following you", "notificationType_new-chat": "When you receive a chat message", "notificationType_group-invite": "When you receive a group invite", diff --git a/public/language/en-US/notifications.json b/public/language/en-US/notifications.json index 04b6b94b2f..980fdbc964 100644 --- a/public/language/en-US/notifications.json +++ b/public/language/en-US/notifications.json @@ -35,6 +35,7 @@ "user_posted_to_dual": "%1 and %2 have posted replies to: %3", "user_posted_to_multiple": "%1 and %2 others have posted replies to: %3", "user_posted_topic": "%1 has posted a new topic: %2", + "user_edited_post": "%1 has edited a post in %2", "user_started_following_you": "%1 started following you.", "user_started_following_you_dual": "%1 and %2 started following you.", "user_started_following_you_multiple": "%1 and %2 others started following you.", @@ -53,6 +54,7 @@ "notificationType_upvote": "When someone upvotes your post", "notificationType_new-topic": "When someone you follow posts a topic", "notificationType_new-reply": "When a new reply is posted in a topic you are watching", + "notificationType_post-edit": "When a post is edited in a topic you are watching", "notificationType_follow": "When someone starts following you", "notificationType_new-chat": "When you receive a chat message", "notificationType_group-invite": "When you receive a group invite", diff --git a/public/language/en-x-pirate/notifications.json b/public/language/en-x-pirate/notifications.json index e41dc6b9e4..0d57170235 100644 --- a/public/language/en-x-pirate/notifications.json +++ b/public/language/en-x-pirate/notifications.json @@ -35,6 +35,7 @@ "user_posted_to_dual": "%1 and %2 have posted replies to: %3", "user_posted_to_multiple": "%1 and %2 others have posted replies to: %3", "user_posted_topic": "%1 has posted a new topic: %2", + "user_edited_post": "%1 has edited a post in %2", "user_started_following_you": "%1 started following you.", "user_started_following_you_dual": "%1 and %2 started following you.", "user_started_following_you_multiple": "%1 and %2 others started following you.", @@ -53,6 +54,7 @@ "notificationType_upvote": "When someone upvotes your post", "notificationType_new-topic": "When someone you follow posts a topic", "notificationType_new-reply": "When a new reply is posted in a topic you are watching", + "notificationType_post-edit": "When a post is edited in a topic you are watching", "notificationType_follow": "When someone starts following you", "notificationType_new-chat": "When you receive a chat message", "notificationType_group-invite": "When you receive a group invite", diff --git a/public/language/es/notifications.json b/public/language/es/notifications.json index a12cb1b2ba..4527ea84a3 100644 --- a/public/language/es/notifications.json +++ b/public/language/es/notifications.json @@ -35,6 +35,7 @@ "user_posted_to_dual": "%1 y %2 han respondido a %3", "user_posted_to_multiple": "%1 y otras %2 personas han respondido a: %3", "user_posted_topic": "%1 ha publicado un nuevo tema: %2", + "user_edited_post": "%1 has edited a post in %2", "user_started_following_you": "%1 comenzó a seguirte.", "user_started_following_you_dual": "%1 y %2 comenzaron a seguirte.", "user_started_following_you_multiple": "%1 y otras %2 personas comenzaron a seguirte.", @@ -53,6 +54,7 @@ "notificationType_upvote": "Cuando alguien vota positivamente en tu entrada", "notificationType_new-topic": "Cuando alguien a quien sigues comenta en un tema", "notificationType_new-reply": "Cuando hay una respuesta nueva en un tema que estás viendo", + "notificationType_post-edit": "When a post is edited in a topic you are watching", "notificationType_follow": "Cuando alguien comienza a seguirte", "notificationType_new-chat": "Cuando recibes un mensaje de chat", "notificationType_group-invite": "Cuando recibes una invitación a un grupo", diff --git a/public/language/et/notifications.json b/public/language/et/notifications.json index f884227d8e..a3377197d2 100644 --- a/public/language/et/notifications.json +++ b/public/language/et/notifications.json @@ -35,6 +35,7 @@ "user_posted_to_dual": "%1 ja %2 on postitanud vastused: %3", "user_posted_to_multiple": "%1 ja %2 teist on postitanud vastused: %3", "user_posted_topic": "%1 on postitanud uue teema: %2", + "user_edited_post": "%1 has edited a post in %2", "user_started_following_you": "%1 hakkas sind jälgima.", "user_started_following_you_dual": "%1 ja %2 hakkasid sind jälgima.", "user_started_following_you_multiple": "%1 ja %2 hakkasid sind jälgima.", @@ -53,6 +54,7 @@ "notificationType_upvote": "When someone upvotes your post", "notificationType_new-topic": "When someone you follow posts a topic", "notificationType_new-reply": "When a new reply is posted in a topic you are watching", + "notificationType_post-edit": "When a post is edited in a topic you are watching", "notificationType_follow": "When someone starts following you", "notificationType_new-chat": "When you receive a chat message", "notificationType_group-invite": "When you receive a group invite", diff --git a/public/language/fa-IR/notifications.json b/public/language/fa-IR/notifications.json index 2fc82e6cf8..c61eae7314 100644 --- a/public/language/fa-IR/notifications.json +++ b/public/language/fa-IR/notifications.json @@ -35,6 +35,7 @@ "user_posted_to_dual": "%1 و %2 پاسخ به پست دادند در: %3", "user_posted_to_multiple": "%1 و %2 نفر دیگر به پست شما پاسخ ارسال کرده‌اند در: %3", "user_posted_topic": "%1 یک موضوع جدید ارسال کرده: %2", + "user_edited_post": "%1 has edited a post in %2", "user_started_following_you": "%1 شروع به دنبال کردن شما کرده", "user_started_following_you_dual": "%1 و %2 شروع به دنبال کردن شما کرده.", "user_started_following_you_multiple": "%1 و %2 نفر دیگر شروع به دنبال کردن شما کرده.", @@ -53,6 +54,7 @@ "notificationType_upvote": "هنگامی که شخصی به پست شما رای مثبت می دهد", "notificationType_new-topic": "هنگامی که شخصی که شما دنبال می کنید موضوعی ایجاد نماید", "notificationType_new-reply": "هنگامی که پاسخ جدید در تاپیکی که شما پیگیری می کنید فرستاده می شود", + "notificationType_post-edit": "When a post is edited in a topic you are watching", "notificationType_follow": "هنگامی که کسی شما را دنبال می کند", "notificationType_new-chat": "هنگامی که شما پیام چتی دریافت می کنید", "notificationType_group-invite": "هنگامی که شما دعوتنامه گروه دریافت می کنید", diff --git a/public/language/fi/notifications.json b/public/language/fi/notifications.json index 48e6140919..0118d76edf 100644 --- a/public/language/fi/notifications.json +++ b/public/language/fi/notifications.json @@ -35,6 +35,7 @@ "user_posted_to_dual": "%1 ja %2 ovat vastanneet viestiin: %3", "user_posted_to_multiple": "%1 ja %2 muuta ovat vastanneet viestiin: %3 ", "user_posted_topic": "%1 on kirjoittanut uuden aiheen: %2", + "user_edited_post": "%1 has edited a post in %2", "user_started_following_you": "%1 alkoi seurata sinua.", "user_started_following_you_dual": "%1 ja %2 alkoivat seurata sinua", "user_started_following_you_multiple": "%1 ja %2 muuta alkoivat seurata sinua", @@ -53,6 +54,7 @@ "notificationType_upvote": "Kun joku tykkää viestistäsi", "notificationType_new-topic": "Kun joku seuraa viestejäsi aiheessa", "notificationType_new-reply": "Kun uusi vastaus on lähetetty aiheeseen, jota seuraat", + "notificationType_post-edit": "When a post is edited in a topic you are watching", "notificationType_follow": "Kun joku alkaa seurata sinua", "notificationType_new-chat": "Kun saat viestin keskusteluun", "notificationType_group-invite": "Kun saat kutsun ryhmään", diff --git a/public/language/fr/notifications.json b/public/language/fr/notifications.json index a5be7f8c4f..ff001a7886 100644 --- a/public/language/fr/notifications.json +++ b/public/language/fr/notifications.json @@ -35,6 +35,7 @@ "user_posted_to_dual": "%1 et %2 ont posté une réponse à : %3", "user_posted_to_multiple": "%1 et %2 autres ont posté une réponse à : %3", "user_posted_topic": "%1 a posté un nouveau sujet: %2.", + "user_edited_post": "%1 has edited a post in %2", "user_started_following_you": "%1 vous suit.", "user_started_following_you_dual": "%1 et %2 se sont abonnés à votre compte.", "user_started_following_you_multiple": "%1 et %2 autres se sont abonnés à votre compte.", @@ -53,6 +54,7 @@ "notificationType_upvote": "Lorsque quelqu'un a voté pour un de vos messages", "notificationType_new-topic": "Lorsque quelqu'un que vous suivez publie un sujet", "notificationType_new-reply": "Lorsqu'une nouvelle réponse est ajoutée dans un sujet que vous suivez", + "notificationType_post-edit": "When a post is edited in a topic you are watching", "notificationType_follow": "Lorsque quelqu'un commence à vous suivre", "notificationType_new-chat": "Lorsque vous recevez un message du chat ", "notificationType_group-invite": "Lorsque vous recevez une invitation d'un groupe", diff --git a/public/language/gl/notifications.json b/public/language/gl/notifications.json index 1e5e01e984..4267d4afdb 100644 --- a/public/language/gl/notifications.json +++ b/public/language/gl/notifications.json @@ -35,6 +35,7 @@ "user_posted_to_dual": "%1 e %2 responderon a %3", "user_posted_to_multiple": "%1 e outras %2 persoas responderon a: %3", "user_posted_topic": "%1 publicou un novo tema: %2", + "user_edited_post": "%1 has edited a post in %2", "user_started_following_you": "%1 comezou a seguirte.", "user_started_following_you_dual": "%1 e %2 comezaron a seguirte.", "user_started_following_you_multiple": "%1 e %2 máis comezaron a seguirte.", @@ -53,6 +54,7 @@ "notificationType_upvote": "When someone upvotes your post", "notificationType_new-topic": "When someone you follow posts a topic", "notificationType_new-reply": "When a new reply is posted in a topic you are watching", + "notificationType_post-edit": "When a post is edited in a topic you are watching", "notificationType_follow": "When someone starts following you", "notificationType_new-chat": "When you receive a chat message", "notificationType_group-invite": "When you receive a group invite", diff --git a/public/language/he/notifications.json b/public/language/he/notifications.json index 5baa8768b2..ee7bcb14d2 100644 --- a/public/language/he/notifications.json +++ b/public/language/he/notifications.json @@ -35,6 +35,7 @@ "user_posted_to_dual": "%1 ו%2 הגיבו ל: %3", "user_posted_to_multiple": "%1 ו%2 אחרים הגיבו ל: %3", "user_posted_topic": "%1 העלה נושא חדש: %2", + "user_edited_post": "%1 has edited a post in %2", "user_started_following_you": "%1 התחיל לעקוב אחריך.", "user_started_following_you_dual": "%1 ו%1 התחילו לעקוב אחריך.", "user_started_following_you_multiple": "%1 ו%2 התחילו לעקוב אחריך.", @@ -53,6 +54,7 @@ "notificationType_upvote": "כאשר מישהו מצביע בעד הפוסט שלך", "notificationType_new-topic": "כשמישהו שאתה עוקב אחריו פרסם נושא", "notificationType_new-reply": "כשתגובה חדשה מפורסמת בנושא שאתה צופה בו", + "notificationType_post-edit": "When a post is edited in a topic you are watching", "notificationType_follow": "כשמישהו מתחיל לעקוב אחריך", "notificationType_new-chat": "כשאתה מקבל הודעת צאט", "notificationType_group-invite": "כשאתה מקבל הזמנה מקבוצה", diff --git a/public/language/hr/notifications.json b/public/language/hr/notifications.json index 85e1eb3bf1..62e5a7e026 100644 --- a/public/language/hr/notifications.json +++ b/public/language/hr/notifications.json @@ -35,6 +35,7 @@ "user_posted_to_dual": "%1 i %2 ostalih su odgovorili na objavu u: %3", "user_posted_to_multiple": "%1 i %2 drugih su odgovorili na: %3", "user_posted_topic": "%1 je otvorio novu temu: %2", + "user_edited_post": "%1 has edited a post in %2", "user_started_following_you": "%1 Vas sada prati.", "user_started_following_you_dual": "%1 i %2 vas sada prate.", "user_started_following_you_multiple": "%1 i %2 ostalih vas sada prate.", @@ -53,6 +54,7 @@ "notificationType_upvote": "When someone upvotes your post", "notificationType_new-topic": "When someone you follow posts a topic", "notificationType_new-reply": "When a new reply is posted in a topic you are watching", + "notificationType_post-edit": "When a post is edited in a topic you are watching", "notificationType_follow": "When someone starts following you", "notificationType_new-chat": "When you receive a chat message", "notificationType_group-invite": "When you receive a group invite", diff --git a/public/language/hu/notifications.json b/public/language/hu/notifications.json index 540fa91141..78bf732d42 100644 --- a/public/language/hu/notifications.json +++ b/public/language/hu/notifications.json @@ -35,6 +35,7 @@ "user_posted_to_dual": "%1 és%2 választ írt neki: %3", "user_posted_to_multiple": "%1 és %2 másik választ írt neki: %3", "user_posted_topic": "%1 új témakört hozott létre: %2", + "user_edited_post": "%1 has edited a post in %2", "user_started_following_you": "%1 elkezdett követni téged.", "user_started_following_you_dual": "%1 és%2 elkezdett követni téged.", "user_started_following_you_multiple": "%1 és %2 másik elkezdett követni téged.", @@ -53,6 +54,7 @@ "notificationType_upvote": "Mikor valaki kedveli a hozzászólásod", "notificationType_new-topic": "Mikor egy követett felhasználód hozzászól", "notificationType_new-reply": "Mikor egy általad figyelt témakörre válasz érkezik", + "notificationType_post-edit": "When a post is edited in a topic you are watching", "notificationType_follow": "Mikor valaki elkezd követni téged", "notificationType_new-chat": "Mikor chat üzenetet kapsz", "notificationType_group-invite": "Mikor csoportmeghívást kapsz", diff --git a/public/language/id/notifications.json b/public/language/id/notifications.json index 505b3f8cc9..27e3026ee7 100644 --- a/public/language/id/notifications.json +++ b/public/language/id/notifications.json @@ -35,6 +35,7 @@ "user_posted_to_dual": "%1 and %2 have posted replies to: %3", "user_posted_to_multiple": "%1 and %2 others have posted replies to: %3", "user_posted_topic": "%1 telah membuat topik baru: %2", + "user_edited_post": "%1 has edited a post in %2", "user_started_following_you": "%1 mulai mengikutimu.", "user_started_following_you_dual": "%1 and %2 started following you.", "user_started_following_you_multiple": "%1 and %2 others started following you.", @@ -53,6 +54,7 @@ "notificationType_upvote": "When someone upvotes your post", "notificationType_new-topic": "When someone you follow posts a topic", "notificationType_new-reply": "When a new reply is posted in a topic you are watching", + "notificationType_post-edit": "When a post is edited in a topic you are watching", "notificationType_follow": "When someone starts following you", "notificationType_new-chat": "When you receive a chat message", "notificationType_group-invite": "When you receive a group invite", diff --git a/public/language/it/notifications.json b/public/language/it/notifications.json index a154f31c5d..12fdff7e5d 100644 --- a/public/language/it/notifications.json +++ b/public/language/it/notifications.json @@ -35,6 +35,7 @@ "user_posted_to_dual": "%1 e %2 hanno postato una risposta su: %3", "user_posted_to_multiple": "%1 ed altri %2 hanno postato una risposta su: %3", "user_posted_topic": "%1 ha postato una nuova discussione: %2", + "user_edited_post": "%1 has edited a post in %2", "user_started_following_you": "%1 ha iniziato a seguirti.", "user_started_following_you_dual": "%1 e %2 hanno iniziato a seguirti.", "user_started_following_you_multiple": "%1 ed altri %2 hanno iniziato a seguirti.", @@ -53,6 +54,7 @@ "notificationType_upvote": "Quando il tuo post riceve un Mi Piace", "notificationType_new-topic": "Quando qualcuno che segui pubblica un argomento", "notificationType_new-reply": "Quando viene pubblicata una nuova risposta in un argomento che stai seguendo", + "notificationType_post-edit": "When a post is edited in a topic you are watching", "notificationType_follow": "Quando qualcuno inizia a seguirti", "notificationType_new-chat": "Quando ricevi un messaggio in chat", "notificationType_group-invite": "Quando ricevi un invito ad un gruppo", diff --git a/public/language/ja/notifications.json b/public/language/ja/notifications.json index e22ac76f67..9f95ab0c31 100644 --- a/public/language/ja/notifications.json +++ b/public/language/ja/notifications.json @@ -35,6 +35,7 @@ "user_posted_to_dual": "%1%2 は、返信しました: %3", "user_posted_to_multiple": "%1 と %2 または他のユーザーが返信しました: %3", "user_posted_topic": "%1 が新しいスレッドを投稿しました。: %2", + "user_edited_post": "%1 has edited a post in %2", "user_started_following_you": "%1があなたをフォローしました。", "user_started_following_you_dual": "%1%2 があなたをフォローしました。", "user_started_following_you_multiple": "%1 と %2 または他のユーザーがあなたをフォローしました。", @@ -53,6 +54,7 @@ "notificationType_upvote": "誰かがあなたの投稿を評価したとき", "notificationType_new-topic": "フォロワーがスレッドを投稿したとき", "notificationType_new-reply": "あなたが見ているトピックに新しい返信が投稿されたとき", + "notificationType_post-edit": "When a post is edited in a topic you are watching", "notificationType_follow": "誰かがあなたをフォローしたとき", "notificationType_new-chat": "チャットメッセージを受信したとき", "notificationType_group-invite": "グループ招待を受けたとき", diff --git a/public/language/ko/notifications.json b/public/language/ko/notifications.json index ebe767bddc..930d3934ec 100644 --- a/public/language/ko/notifications.json +++ b/public/language/ko/notifications.json @@ -35,6 +35,7 @@ "user_posted_to_dual": "%1 님과 %2 님이 %3 에 답글을 달았습니다.", "user_posted_to_multiple": "%1 님과 %2 명의 다른 유저들이 %3 에 답글을 달았습니다.", "user_posted_topic": "%1님이 새 게시물을 작성했습니다: %2", + "user_edited_post": "%1 has edited a post in %2", "user_started_following_you": "%1님이 나를 팔로우 합니다.", "user_started_following_you_dual": "%1님과 %2님이 나를 팔로우 합니다.", "user_started_following_you_multiple": "%1님외 %2명이 나를 팔로우 합니다.", @@ -53,6 +54,7 @@ "notificationType_upvote": "누군가 사용자님의 글을 추천해 줄 때", "notificationType_new-topic": "When someone you follow posts a topic", "notificationType_new-reply": "주시중인 글에 새로운 답글이 게시되면", + "notificationType_post-edit": "When a post is edited in a topic you are watching", "notificationType_follow": "누군가 사용자님을 팔로우 하면", "notificationType_new-chat": "채팅 메시지를 받으면", "notificationType_group-invite": "그룹 초대를 받으면", diff --git a/public/language/lt/notifications.json b/public/language/lt/notifications.json index e82ca030c2..e5310eb0dd 100644 --- a/public/language/lt/notifications.json +++ b/public/language/lt/notifications.json @@ -35,6 +35,7 @@ "user_posted_to_dual": "%1 and %2 have posted replies to: %3", "user_posted_to_multiple": "%1 and %2 others have posted replies to: %3", "user_posted_topic": "%1 paskelbė naują temą: %2", + "user_edited_post": "%1 has edited a post in %2", "user_started_following_you": "%1 pradėjo sekti tave", "user_started_following_you_dual": "%1 and %2 started following you.", "user_started_following_you_multiple": "%1 and %2 others started following you.", @@ -53,6 +54,7 @@ "notificationType_upvote": "When someone upvotes your post", "notificationType_new-topic": "When someone you follow posts a topic", "notificationType_new-reply": "When a new reply is posted in a topic you are watching", + "notificationType_post-edit": "When a post is edited in a topic you are watching", "notificationType_follow": "When someone starts following you", "notificationType_new-chat": "When you receive a chat message", "notificationType_group-invite": "When you receive a group invite", diff --git a/public/language/lv/notifications.json b/public/language/lv/notifications.json index 12631ea58f..2a929402a0 100644 --- a/public/language/lv/notifications.json +++ b/public/language/lv/notifications.json @@ -35,6 +35,7 @@ "user_posted_to_dual": "%1 un %2 ir atbildējuši %3", "user_posted_to_multiple": "%1 un %2 citi ir atbildējuši %3", "user_posted_topic": "%1 ir ievietojis jaunu tematu: %2", + "user_edited_post": "%1 has edited a post in %2", "user_started_following_you": "%1 sāka Tev sekot.", "user_started_following_you_dual": "%1 un %2 sāka Tev sekot.", "user_started_following_you_multiple": "%1 un %2 citi sāka Tev sekot.", @@ -53,6 +54,7 @@ "notificationType_upvote": "Kad kāds balso \"par\" Tavu rakstu", "notificationType_new-topic": "Kad kāds, kuru Tu seko, publicē rakstu", "notificationType_new-reply": "Kad jauna atbilde tiek pievienota tematam, kuru novēro", + "notificationType_post-edit": "When a post is edited in a topic you are watching", "notificationType_follow": "Kad kāds sāk Tev sekot", "notificationType_new-chat": "Kad saņemi sarunu", "notificationType_group-invite": "Kad saņemi ielūgumu pievienoties grupai", diff --git a/public/language/ms/notifications.json b/public/language/ms/notifications.json index 80a877b632..a6142f56b7 100644 --- a/public/language/ms/notifications.json +++ b/public/language/ms/notifications.json @@ -35,6 +35,7 @@ "user_posted_to_dual": "%1 dan %2 membalas kiriman : %3", "user_posted_to_multiple": "%1 dan %2 lagu membalas kiriman: %3", "user_posted_topic": "%1 membuka topik baru : %2", + "user_edited_post": "%1 has edited a post in %2", "user_started_following_you": "%1 mula mengikut anda.", "user_started_following_you_dual": "%1 dan %2 mula mengikuti anda.", "user_started_following_you_multiple": "%1 dan %2 lagi mula mengikuti anda.", @@ -53,6 +54,7 @@ "notificationType_upvote": "When someone upvotes your post", "notificationType_new-topic": "When someone you follow posts a topic", "notificationType_new-reply": "When a new reply is posted in a topic you are watching", + "notificationType_post-edit": "When a post is edited in a topic you are watching", "notificationType_follow": "When someone starts following you", "notificationType_new-chat": "When you receive a chat message", "notificationType_group-invite": "When you receive a group invite", diff --git a/public/language/nb/notifications.json b/public/language/nb/notifications.json index 484addbd1f..04d292395c 100644 --- a/public/language/nb/notifications.json +++ b/public/language/nb/notifications.json @@ -35,6 +35,7 @@ "user_posted_to_dual": "%1 and %2 have posted replies to: %3", "user_posted_to_multiple": "%1 and %2 others have posted replies to: %3", "user_posted_topic": "%1 har skrevet et nytt emne: %2", + "user_edited_post": "%1 has edited a post in %2", "user_started_following_you": "%1 begynte å følge deg.", "user_started_following_you_dual": "%1 and %2 started following you.", "user_started_following_you_multiple": "%1 and %2 others started following you.", @@ -53,6 +54,7 @@ "notificationType_upvote": "When someone upvotes your post", "notificationType_new-topic": "When someone you follow posts a topic", "notificationType_new-reply": "When a new reply is posted in a topic you are watching", + "notificationType_post-edit": "When a post is edited in a topic you are watching", "notificationType_follow": "When someone starts following you", "notificationType_new-chat": "When you receive a chat message", "notificationType_group-invite": "When you receive a group invite", diff --git a/public/language/nl/notifications.json b/public/language/nl/notifications.json index a03b742f16..f0d5eb4da6 100644 --- a/public/language/nl/notifications.json +++ b/public/language/nl/notifications.json @@ -35,6 +35,7 @@ "user_posted_to_dual": "%1 en %2 hebben een reactie geplaatst in: %3", "user_posted_to_multiple": "%1 en %2 hebben een reactie geplaatst in: %3", "user_posted_topic": "%1 heeft een nieuw onderwerp geplaatst: %2", + "user_edited_post": "%1 has edited a post in %2", "user_started_following_you": "%1 volgt jou nu.", "user_started_following_you_dual": "%1 en %2 volgen jou nu.", "user_started_following_you_multiple": "%1 en %2 anderen volgen jou nu.", @@ -53,6 +54,7 @@ "notificationType_upvote": "Als iemand positief stemt voor je bericht", "notificationType_new-topic": "Wanneer iemand die jij volgt een onderwerp post", "notificationType_new-reply": "Als een nieuwe reactie komt op een onderwerp dat je volgt", + "notificationType_post-edit": "When a post is edited in a topic you are watching", "notificationType_follow": "Als iemand begint met jou te volgen", "notificationType_new-chat": "Als je een chat-bericht ontvangt", "notificationType_group-invite": "Als je een uitnodiging voor een groep ontvangt", diff --git a/public/language/pl/notifications.json b/public/language/pl/notifications.json index f9f1043b9b..dfb52ccf78 100644 --- a/public/language/pl/notifications.json +++ b/public/language/pl/notifications.json @@ -35,6 +35,7 @@ "user_posted_to_dual": "%1 oraz %2 dodali odpowiedzi do %3", "user_posted_to_multiple": "%1 oraz %2 innych dodali odpowiedzi do %3", "user_posted_topic": "%1 stworzył nowy temat: %2", + "user_edited_post": "%1 has edited a post in %2", "user_started_following_you": "%1 zaczął Cię obserwować.", "user_started_following_you_dual": "%1 oraz %2 zaczęli Cię obserwować.", "user_started_following_you_multiple": "%1 oraz %2 innych obserwują Cię.", @@ -53,6 +54,7 @@ "notificationType_upvote": "Kiedy ktoś zagłosuje na Twój post", "notificationType_new-topic": "Kiedy ktoś, kogo obserwujesz, utworzy temat", "notificationType_new-reply": "Kiedy ktoś doda nową odpowiedź w temacie, który obserwujesz", + "notificationType_post-edit": "When a post is edited in a topic you are watching", "notificationType_follow": "Kiedy ktoś zacznie Cię obserwować", "notificationType_new-chat": "Kiedy otrzymasz wiadomość na czacie", "notificationType_group-invite": "Kiedy otrzymasz grupowe zaproszenie", diff --git a/public/language/pt-BR/notifications.json b/public/language/pt-BR/notifications.json index fa3a8a4912..9a4df9b7b3 100644 --- a/public/language/pt-BR/notifications.json +++ b/public/language/pt-BR/notifications.json @@ -35,6 +35,7 @@ "user_posted_to_dual": "%1 e %2 postaram respostas para: %3", "user_posted_to_multiple": "%1 e %2 outros postaram respostas para: %3", "user_posted_topic": "%1 postou um novo tópico: %2", + "user_edited_post": "%1 has edited a post in %2", "user_started_following_you": "%1 começou a seguir você.", "user_started_following_you_dual": "%1 e %2 começaram a lhe acompanhar.", "user_started_following_you_multiple": "%1 e %2 outros começaram a lhe acompanhar.", @@ -53,6 +54,7 @@ "notificationType_upvote": "Quando alguém dá um voto positivo em seu post", "notificationType_new-topic": "Quando alguém que você segue posta um tópico", "notificationType_new-reply": "Quando uma nova resposta é postada em um tópico que você está acompanhando", + "notificationType_post-edit": "When a post is edited in a topic you are watching", "notificationType_follow": "Quando alguém começar a seguir você", "notificationType_new-chat": "Quando você receber uma mensagem de chat", "notificationType_group-invite": "Quando você receber um convite para um grupo", diff --git a/public/language/pt-PT/notifications.json b/public/language/pt-PT/notifications.json index 24f72dd636..edb8369ca4 100644 --- a/public/language/pt-PT/notifications.json +++ b/public/language/pt-PT/notifications.json @@ -35,6 +35,7 @@ "user_posted_to_dual": "%1 e %2 publicaram respostas a: %3", "user_posted_to_multiple": "%1 e %2 outros utilizadores publicaram respostas a: %3", "user_posted_topic": "%1 publicou um novo tópico: %2", + "user_edited_post": "%1 has edited a post in %2", "user_started_following_you": "%1 começou a seguir-te.", "user_started_following_you_dual": "%1 e %2 começaram a seguir-te.", "user_started_following_you_multiple": "%1 e %2 outros utilizadores começaram a seguir-te.", @@ -53,6 +54,7 @@ "notificationType_upvote": "Quando alguém vota positivamente numa publicação tua", "notificationType_new-topic": "Quando alguém que tu segues publica um tópico", "notificationType_new-reply": "Quando uma nova resposta é publicada num tópico que tu estás a seguir", + "notificationType_post-edit": "When a post is edited in a topic you are watching", "notificationType_follow": "Quando alguém começa a seguir-te", "notificationType_new-chat": "Quando recebes uma mensagem numa conversa", "notificationType_group-invite": "Quando recebes um convite para um grupo", diff --git a/public/language/ro/notifications.json b/public/language/ro/notifications.json index d331649a66..8de723abda 100644 --- a/public/language/ro/notifications.json +++ b/public/language/ro/notifications.json @@ -35,6 +35,7 @@ "user_posted_to_dual": "%1 and %2 have posted replies to: %3", "user_posted_to_multiple": "%1 and %2 others have posted replies to: %3", "user_posted_topic": "%1 has posted a new topic: %2", + "user_edited_post": "%1 has edited a post in %2", "user_started_following_you": "%1 a început să te urmărească.", "user_started_following_you_dual": "%1 and %2 started following you.", "user_started_following_you_multiple": "%1 and %2 others started following you.", @@ -53,6 +54,7 @@ "notificationType_upvote": "When someone upvotes your post", "notificationType_new-topic": "When someone you follow posts a topic", "notificationType_new-reply": "When a new reply is posted in a topic you are watching", + "notificationType_post-edit": "When a post is edited in a topic you are watching", "notificationType_follow": "When someone starts following you", "notificationType_new-chat": "When you receive a chat message", "notificationType_group-invite": "When you receive a group invite", diff --git a/public/language/ru/notifications.json b/public/language/ru/notifications.json index 66aebf4d63..0da0fa4925 100644 --- a/public/language/ru/notifications.json +++ b/public/language/ru/notifications.json @@ -35,6 +35,7 @@ "user_posted_to_dual": "Пользователи %1 и %2 ответили на сообщение в %3", "user_posted_to_multiple": "%1 и %2 других пользователя ответили на сообщение в %3", "user_posted_topic": "Пользователь %1 создал новую тему: %2", + "user_edited_post": "%1 has edited a post in %2", "user_started_following_you": "Пользователь %1 подписался на вас.", "user_started_following_you_dual": "Пользователи %1 и %2 подписались на вас.", "user_started_following_you_multiple": "%1 и %2 других пользователя подписались на вас.", @@ -53,6 +54,7 @@ "notificationType_upvote": "Когда кто-то проголосовал за ваше сообщение", "notificationType_new-topic": "Когда кто-то, на кого вы подписаны, создаёт новую тему", "notificationType_new-reply": "Когда в теме, за которой вы следите, появляется новое сообщение", + "notificationType_post-edit": "When a post is edited in a topic you are watching", "notificationType_follow": "Когда кто-то подписался на вас", "notificationType_new-chat": "Когда вы получаете сообщение в чат", "notificationType_group-invite": "Когда вы получаете приглашение в группу", diff --git a/public/language/rw/notifications.json b/public/language/rw/notifications.json index 9b407a2fe7..44aa3a5231 100644 --- a/public/language/rw/notifications.json +++ b/public/language/rw/notifications.json @@ -35,6 +35,7 @@ "user_posted_to_dual": "%1 and %2 have posted replies to: %3", "user_posted_to_multiple": "%1 and %2 others have posted replies to: %3", "user_posted_topic": "%1 yatangije ikiganiro gishya: %2", + "user_edited_post": "%1 has edited a post in %2", "user_started_following_you": "%1 yatangiye kugukurikira.", "user_started_following_you_dual": "%1 and %2 started following you.", "user_started_following_you_multiple": "%1 and %2 others started following you.", @@ -53,6 +54,7 @@ "notificationType_upvote": "When someone upvotes your post", "notificationType_new-topic": "When someone you follow posts a topic", "notificationType_new-reply": "When a new reply is posted in a topic you are watching", + "notificationType_post-edit": "When a post is edited in a topic you are watching", "notificationType_follow": "When someone starts following you", "notificationType_new-chat": "When you receive a chat message", "notificationType_group-invite": "When you receive a group invite", diff --git a/public/language/sc/notifications.json b/public/language/sc/notifications.json index 2eb3857af0..ec509a66f6 100644 --- a/public/language/sc/notifications.json +++ b/public/language/sc/notifications.json @@ -35,6 +35,7 @@ "user_posted_to_dual": "%1 and %2 have posted replies to: %3", "user_posted_to_multiple": "%1 and %2 others have posted replies to: %3", "user_posted_topic": "%1 has posted a new topic: %2", + "user_edited_post": "%1 has edited a post in %2", "user_started_following_you": "%1 started following you.", "user_started_following_you_dual": "%1 and %2 started following you.", "user_started_following_you_multiple": "%1 and %2 others started following you.", @@ -53,6 +54,7 @@ "notificationType_upvote": "When someone upvotes your post", "notificationType_new-topic": "When someone you follow posts a topic", "notificationType_new-reply": "When a new reply is posted in a topic you are watching", + "notificationType_post-edit": "When a post is edited in a topic you are watching", "notificationType_follow": "When someone starts following you", "notificationType_new-chat": "When you receive a chat message", "notificationType_group-invite": "When you receive a group invite", diff --git a/public/language/sk/notifications.json b/public/language/sk/notifications.json index 5e3818ca46..e4a79d54ac 100644 --- a/public/language/sk/notifications.json +++ b/public/language/sk/notifications.json @@ -35,6 +35,7 @@ "user_posted_to_dual": "%1 a %2 uverejnili odpoveď na:%3", "user_posted_to_multiple": "%1 a %2 ďalší uverejnili odpovede na:%3", "user_posted_topic": "%1 pridal novú tému: %2", + "user_edited_post": "%1 has edited a post in %2", "user_started_following_you": "%1 Vás začal sledovať.", "user_started_following_you_dual": "%1 a %2 Vás začali sledovať.", "user_started_following_you_multiple": "%1 a %2 ďalší Vás začali sledovať.", @@ -53,6 +54,7 @@ "notificationType_upvote": "Ak niekto vyjadri súhlas s vaším príspevkom", "notificationType_new-topic": "Ak začne niekto sledovať príspevky a témy", "notificationType_new-reply": "Ak bude pridaný nový príspevok v téme, ktorú sledujete", + "notificationType_post-edit": "When a post is edited in a topic you are watching", "notificationType_follow": "Ak Vás začne niekto sledovať", "notificationType_new-chat": "Ak obdržíte novú správu konverzácie", "notificationType_group-invite": "Ak obdržíte pozvanie do skupiny", diff --git a/public/language/sl/notifications.json b/public/language/sl/notifications.json index b416d1054a..a352c5250d 100644 --- a/public/language/sl/notifications.json +++ b/public/language/sl/notifications.json @@ -35,6 +35,7 @@ "user_posted_to_dual": "%1 in %2 sta objavila/-i odgovor na: %3.", "user_posted_to_multiple": "%1 in %2 drugih je objavilo odgovor na: %3.", "user_posted_topic": "%1 je odprl/-a novo temo: %2.", + "user_edited_post": "%1 has edited a post in %2", "user_started_following_you": "%1 te je začel/-a spremljati.", "user_started_following_you_dual": "%1 in %2 sta te začela/-i spremljati.", "user_started_following_you_multiple": "%1 in %2 drugih te je začelo spremljati.", @@ -53,6 +54,7 @@ "notificationType_upvote": "When someone upvotes your post", "notificationType_new-topic": "When someone you follow posts a topic", "notificationType_new-reply": "When a new reply is posted in a topic you are watching", + "notificationType_post-edit": "When a post is edited in a topic you are watching", "notificationType_follow": "When someone starts following you", "notificationType_new-chat": "When you receive a chat message", "notificationType_group-invite": "When you receive a group invite", diff --git a/public/language/sr/notifications.json b/public/language/sr/notifications.json index 2587d82844..3889746710 100644 --- a/public/language/sr/notifications.json +++ b/public/language/sr/notifications.json @@ -35,6 +35,7 @@ "user_posted_to_dual": "%1 и %2 су одговорили на: %3", "user_posted_to_multiple": "%1 и %2 других су одговорили на: %3", "user_posted_topic": "%1 је поставио нову тему: %2", + "user_edited_post": "%1 has edited a post in %2", "user_started_following_you": "%1 је почео да вас прати.", "user_started_following_you_dual": "%1 и %2 су почели да вас прате.", "user_started_following_you_multiple": "%1 и %2 других су почели да вас прате.", @@ -53,6 +54,7 @@ "notificationType_upvote": "Када неко гласа за вашу поруку", "notificationType_new-topic": "Када неко кога пратите постави тему", "notificationType_new-reply": "Када је постављен нови одговор у теми коју надгледате", + "notificationType_post-edit": "When a post is edited in a topic you are watching", "notificationType_follow": "Када неко почне да вас прати", "notificationType_new-chat": "Када примите поруку за ћаскање", "notificationType_group-invite": "Када примите позивницу за групу", diff --git a/public/language/sv/notifications.json b/public/language/sv/notifications.json index 8b1b549ad8..a77f77ed77 100644 --- a/public/language/sv/notifications.json +++ b/public/language/sv/notifications.json @@ -35,6 +35,7 @@ "user_posted_to_dual": "%1 och %2 har svarat på: %3", "user_posted_to_multiple": "%1 och %2 andra har svarat på: %3", "user_posted_topic": "%1 har skapat ett nytt ämne: %2", + "user_edited_post": "%1 has edited a post in %2", "user_started_following_you": "%1 började följa dig.", "user_started_following_you_dual": "%1 och %2 började följa dig.", "user_started_following_you_multiple": "%1 och %2 andra började följa dig.", @@ -53,6 +54,7 @@ "notificationType_upvote": "När någon röstar upp ditt inlägg", "notificationType_new-topic": "När någon du följer skapar ett ämne", "notificationType_new-reply": "När ett nytt svar skrivs inom ett ämne du följer", + "notificationType_post-edit": "When a post is edited in a topic you are watching", "notificationType_follow": "När någon börjar följa dig", "notificationType_new-chat": "När du får ett chattmeddelande", "notificationType_group-invite": "När du får en gruppinbjudan", diff --git a/public/language/th/notifications.json b/public/language/th/notifications.json index f107a25937..0bc2009f3d 100644 --- a/public/language/th/notifications.json +++ b/public/language/th/notifications.json @@ -35,6 +35,7 @@ "user_posted_to_dual": "%1และ %2ได้โพสต์คำตอบไปยัง : %3 ", "user_posted_to_multiple": "%1และคนอื่นๆอีก %2 ได้โพสต์คำตอบไปยัง : %3", "user_posted_topic": "%1ได้โพสต์กระทู้ใหม่ : %2", + "user_edited_post": "%1 has edited a post in %2", "user_started_following_you": "%1 ได้เริ่มติดตามคุณ", "user_started_following_you_dual": "%1และ%2ได้เริ่มติดตามคุณ", "user_started_following_you_multiple": "%1และคืนอื่นๆอีก %2  คนได้เริ่มติดตามคุณ", @@ -53,6 +54,7 @@ "notificationType_upvote": "เมื่อมีคนโหวตอัพให้โพสต์คุณ", "notificationType_new-topic": "เมื่อมีคนติดตามโพสต์คุณ", "notificationType_new-reply": "เมื่อมีการตอบกลับในโพสต์ที่คุณกำลังติดตาม", + "notificationType_post-edit": "When a post is edited in a topic you are watching", "notificationType_follow": "เมื่อมีคนติดตามคุณ", "notificationType_new-chat": "เมื่อคุณได้รับข้อความใหม่", "notificationType_group-invite": "เมื่อคุณได้รับเชิญเข้ากลุ่ม", diff --git a/public/language/tr/notifications.json b/public/language/tr/notifications.json index 264c08b86d..b1ec1cd9ca 100644 --- a/public/language/tr/notifications.json +++ b/public/language/tr/notifications.json @@ -35,6 +35,7 @@ "user_posted_to_dual": "%1 ve %2 şu başlıktaki gönderinize cevap verdi: %3", "user_posted_to_multiple": "%1 ve %2 kişi daha şu başlıktaki gönderinize cevap verdi: %3", "user_posted_topic": "%1 şu yeni konuyu yarattı: %2", + "user_edited_post": "%1 has edited a post in %2", "user_started_following_you": "%1 sizi takip etmeye başladı.", "user_started_following_you_dual": "%1 ve %2 sizi takip etmeye başladı.", "user_started_following_you_multiple": "%1 ve %2 kişi daha sizi takip etmeye başladı.", @@ -53,6 +54,7 @@ "notificationType_upvote": "Birisi senin iletine artı oy verdiğinde", "notificationType_new-topic": "Takip ettiğiniz birisi bir başlık gönderdiğinde", "notificationType_new-reply": "İzlediğiniz bir başlığa yeni bir ileti gönderildiğinde", + "notificationType_post-edit": "When a post is edited in a topic you are watching", "notificationType_follow": "Birisi seni takip etmeye başlayınca", "notificationType_new-chat": "Bir sohbet mesajı aldığınızda", "notificationType_group-invite": "Bir grup davetiyesi aldığınızda", diff --git a/public/language/tr/topic.json b/public/language/tr/topic.json index 0757fa4bff..8f0426603a 100644 --- a/public/language/tr/topic.json +++ b/public/language/tr/topic.json @@ -135,5 +135,5 @@ "diffs.current-revision": "mevcut revizyon", "diffs.original-revision": "orijinal revizyon", "timeago_later": "%1 sonra", - "timeago_earlier": "%1 daha öncesi" + "timeago_earlier": "%1 önce" } \ No newline at end of file diff --git a/public/language/uk/notifications.json b/public/language/uk/notifications.json index e0c4ab4f3e..05f0a1d99f 100644 --- a/public/language/uk/notifications.json +++ b/public/language/uk/notifications.json @@ -35,6 +35,7 @@ "user_posted_to_dual": "%1 та %2 запостили відповіді до: %3", "user_posted_to_multiple": "%1 та %2 інших запостили відповіді до: %3", "user_posted_topic": "%1 запостив нову тему: %2", + "user_edited_post": "%1 has edited a post in %2", "user_started_following_you": "%1 почав стежити за вами.", "user_started_following_you_dual": "%1 та %2 почали стежити за вами.", "user_started_following_you_multiple": "%1 та %2 інших почали стежити за вами.", @@ -53,6 +54,7 @@ "notificationType_upvote": "Коли хтось голосує за ваш пост", "notificationType_new-topic": "Коли хтось, кого ви читаєте, публікує тему", "notificationType_new-reply": "Коли з'являється нова відповідь у темі, за якою ви слідкуєте", + "notificationType_post-edit": "When a post is edited in a topic you are watching", "notificationType_follow": "Коли хтось починає слідкувати за вами", "notificationType_new-chat": "Коли ви отримуєте повідомлення чату", "notificationType_group-invite": "Коли ви отримуєте запрошення до групи", diff --git a/public/language/vi/notifications.json b/public/language/vi/notifications.json index a847465ea9..90dd6b3a4a 100644 --- a/public/language/vi/notifications.json +++ b/public/language/vi/notifications.json @@ -35,6 +35,7 @@ "user_posted_to_dual": "%1%2 đã trả lời: %3", "user_posted_to_multiple": "%1 và %2 người khác đã trả lời: %3", "user_posted_topic": "%1 đã gởi chủ đề mới ở %2", + "user_edited_post": "%1 has edited a post in %2", "user_started_following_you": "%1 đã theo dõi bạn.", "user_started_following_you_dual": "%1%2 đã bắt đầu theo dõi bạn.", "user_started_following_you_multiple": "%1 và %2 người khác đã bắt đầu theo dõi bạn.", @@ -53,6 +54,7 @@ "notificationType_upvote": "Khi ai đó thích bài đăng của bạn", "notificationType_new-topic": "Khi người bạn theo dõi đăng một chủ đề", "notificationType_new-reply": "Khi phản hồi được đăng trong chủ đề bạn đang quan tâm", + "notificationType_post-edit": "When a post is edited in a topic you are watching", "notificationType_follow": "Khi ai đó theo dõi bạn", "notificationType_new-chat": "Khi bạn nhận được thông điệp chat", "notificationType_group-invite": "Khi bạn nhận được lời mời gia nhập nhóm", diff --git a/public/language/zh-CN/notifications.json b/public/language/zh-CN/notifications.json index 31a7161d3d..bb1568511c 100644 --- a/public/language/zh-CN/notifications.json +++ b/public/language/zh-CN/notifications.json @@ -35,6 +35,7 @@ "user_posted_to_dual": "%1%2 回复了: %3", "user_posted_to_multiple": "%1 和 %2 个其他人回复了: %3", "user_posted_topic": "%1 发表了新主题:%2", + "user_edited_post": "%1 has edited a post in %2", "user_started_following_you": "%1关注了您。", "user_started_following_you_dual": "%1%2 关注了您。", "user_started_following_you_multiple": "%1 和 %2 个其他人关注了您。", @@ -53,6 +54,7 @@ "notificationType_upvote": "当有人顶了我的帖子时", "notificationType_new-topic": "当有人回复我的帖子时", "notificationType_new-reply": "当您正在查看的主题中有新回复时", + "notificationType_post-edit": "When a post is edited in a topic you are watching", "notificationType_follow": "当有人关注您时", "notificationType_new-chat": "当您收到聊天消息时", "notificationType_group-invite": "当您收到群组邀请时", diff --git a/public/language/zh-TW/notifications.json b/public/language/zh-TW/notifications.json index 1eef215d99..445acb6a63 100644 --- a/public/language/zh-TW/notifications.json +++ b/public/language/zh-TW/notifications.json @@ -35,6 +35,7 @@ "user_posted_to_dual": "%1%2 回覆了: %3", "user_posted_to_multiple": "%1 和 %2 個其他人回覆了: %3", "user_posted_topic": "%1 發表了新主題:%2", + "user_edited_post": "%1 has edited a post in %2", "user_started_following_you": "%1追隨了您。", "user_started_following_you_dual": "%1%2 追隨了您。", "user_started_following_you_multiple": "%1 和 %2 個其他人追隨了您。", @@ -53,6 +54,7 @@ "notificationType_upvote": "當有人點贊了我的貼文時", "notificationType_new-topic": "當有人回覆我的貼文時", "notificationType_new-reply": "當您正在查看的主題中有新回覆時", + "notificationType_post-edit": "When a post is edited in a topic you are watching", "notificationType_follow": "當有人追隨您時", "notificationType_new-chat": "當您收到聊天訊息時", "notificationType_group-invite": "當您收到群組邀請時", diff --git a/public/openapi/read.yaml b/public/openapi/read.yaml index f5ab877d9d..30f81ebc3c 100644 --- a/public/openapi/read.yaml +++ b/public/openapi/read.yaml @@ -15,15 +15,17 @@ info: ## Authentication + There are a multitude of ways to authenticate with the Read API. + ### Cookie Authentication This default authentication behaviour of this API is via cookie jar to find a valid session. A valid login session is required for API calls that pertain to operations involving a logged-in user. For example, `/api/unread` is a route showing unread topics, and is not accessible by guest users. - ### Bearer Authentcation + ### Bearer Authentication - The Write API offers bearer authentication, as administered through the administration panel. + Both the Read API and Write API offers bearer authentication, as administered through the administration panel. - * For NodeBB v1.x, this is provided by [`nodebb-plugin-write-api`](https://github.com/NodeBB/nodebb-plugin-write-api). + * For NodeBB v1.x, this is provided by [`nodebb-plugin-write-api`](https://github.com/NodeBB/nodebb-plugin-write-api). The Write API plugin needs to be installed before authentication via bearer token is enabled on routes provided by the Read API. * For NodeBB v2.x+ (in development), the Write API is available in core, and bearer authentication is available out-of-the-box ### JSON Web Token (JWT) @@ -258,14 +260,6 @@ paths: imageClass: type: string - $ref: components/schemas/CommonProps.yaml#/CommonProps - /api/admin: - get: - tags: - - admin - summary: /api/admin - responses: - "418": - description: "TODO: A proper response needs to be added. It is not really a teapot | This route is identical to /api/admin/general/dashboard. When the routes are split into separate files, replace this definition with a $ref to that route" /api/admin/general/dashboard: get: tags: @@ -578,7 +572,7 @@ paths: get: tags: - admin - summary: /api/admin/general/homepage + summary: Get homepage settings responses: "200": description: "" @@ -631,7 +625,7 @@ paths: get: tags: - admin - summary: /api/admin/manage/categories + summary: Get category management settings responses: "200": description: "" @@ -643,7 +637,7 @@ paths: get: tags: - admin - summary: /api/admin/manage/categories/{category_id} + summary: Get category settings parameters: - name: category_id in: path @@ -693,7 +687,7 @@ paths: get: tags: - admin - summary: /api/admin/manage/categories/{category_id}/analytics + summary: Get category anayltics parameters: - name: category_id in: path @@ -1079,7 +1073,7 @@ paths: get: tags: - admin - summary: /api/admin/manage/users + summary: Get users responses: "200": description: "" @@ -1115,7 +1109,7 @@ paths: get: tags: - admin - summary: /api/admin/manage/users/search + summary: Get users via search term responses: "200": description: "" @@ -1136,7 +1130,7 @@ paths: get: tags: - admin - summary: /api/admin/manage/users/latest + summary: Get latest users responses: "418": description: "TODO: A proper response needs to be added. It is not really a teapot | Replace this responses block with the block from /manage/users/latest" @@ -1144,7 +1138,7 @@ paths: get: tags: - admin - summary: /api/admin/manage/users/not-validated + summary: Get non-verified users responses: "418": description: "TODO: A proper response needs to be added. It is not really a teapot | Replace this responses block with the block from /manage/users/latest" @@ -1152,7 +1146,7 @@ paths: get: tags: - admin - summary: /api/admin/manage/users/no-posts + summary: Get users with no posts responses: "418": description: "TODO: A proper response needs to be added. It is not really a teapot | Replace this responses block with the block from /manage/users/latest" @@ -1160,7 +1154,7 @@ paths: get: tags: - admin - summary: /api/admin/manage/users/top-posters + summary: Get users with the most posts responses: "418": description: "TODO: A proper response needs to be added. It is not really a teapot | Replace this responses block with the block from /manage/users/latest" @@ -1168,7 +1162,7 @@ paths: get: tags: - admin - summary: /api/admin/manage/users/most-reputation + summary: Get users with the most reputation responses: "418": description: "TODO: A proper response needs to be added. It is not really a teapot | Replace this responses block with the block from /manage/users/latest" @@ -1176,7 +1170,7 @@ paths: get: tags: - admin - summary: /api/admin/manage/users/inactive + summary: Get inactive users responses: "418": description: "TODO: A proper response needs to be added. It is not really a teapot | Replace this responses block with the block from /manage/users/latest" @@ -1184,7 +1178,7 @@ paths: get: tags: - admin - summary: /api/admin/manage/users/flagged + summary: Get flagged users responses: "418": description: "TODO: A proper response needs to be added. It is not really a teapot | Replace this responses block with the block from /manage/users/latest" @@ -1192,7 +1186,7 @@ paths: get: tags: - admin - summary: /api/admin/manage/users/banned + summary: Get banned users responses: "418": description: "TODO: A proper response needs to be added. It is not really a teapot | Replace this responses block with the block from /manage/users/latest" @@ -1348,7 +1342,7 @@ paths: get: tags: - admin - summary: /api/admin/manage/groups + summary: Get user groups responses: "200": description: "" @@ -1441,7 +1435,7 @@ paths: get: tags: - admin - summary: /api/admin/manage/groups/{name} + summary: Get user group details parameters: - name: name in: path @@ -1636,7 +1630,7 @@ paths: get: tags: - admin - summary: /api/admin/extend/plugins + summary: Get system plugin settings responses: "200": description: "" @@ -1788,7 +1782,7 @@ paths: get: tags: - admin - summary: /api/admin/extend/widgets + summary: Get widget settings responses: "200": description: "" @@ -2162,7 +2156,7 @@ paths: get: tags: - admin - summary: /api/admin/advanced/cache + summary: Get system cache info responses: "200": description: "" @@ -2254,7 +2248,7 @@ paths: get: tags: - admin - summary: /api/admin/development/logger + summary: Get system logger settings responses: "200": description: "" @@ -2759,7 +2753,8 @@ paths: get: tags: - home - summary: /api/config + summary: Get forum settings + description: This route retrieves forum settings and user-specific settings for client-side options on the forum. responses: "200": description: "" @@ -2892,23 +2887,131 @@ paths: type: boolean enableQuickReply: type: boolean - /api/me: + /api/users: get: tags: - - shorthand - summary: /api/me + - users + summary: Get users + parameters: + - in: query + name: section + schema: + type: string + enum: ['joindate', 'online', 'sort-posts', 'sort-reputation', 'banned', 'flagged'] + required: false + description: Allows filtering of the user list via pre-defined sections + example: 'joindate' + - in: query + name: term + schema: + type: string + required: false + description: Allows for searching of user list + example: '' responses: "200": description: "" content: application/json: schema: - $ref: components/schemas/UserObject.yaml#/UserObjectFull + allOf: + - type: object + properties: + users: + type: array + items: + type: object + properties: + uid: + type: number + description: A user identifier + username: + type: string + description: A friendly name for a given user account + userslug: + type: string + description: An URL-safe variant of the username (i.e. lower-cased, spaces + removed, etc.) + picture: + nullable: true + type: string + status: + type: string + postcount: + type: number + reputation: + type: number + email:confirmed: + type: number + description: Whether the user has confirmed their email address or not + lastonline: + type: number + flags: + nullable: true + banned: + type: number + banned:expire: + type: number + joindate: + type: number + description: A UNIX timestamp representing the moment the user's account was + created + icon:text: + type: string + description: A single-letter representation of a username. This is used in the + auto-generated icon given to users without an + avatar + icon:bgColor: + type: string + description: A six-character hexadecimal colour code assigned to the user. This + value is used in conjunction with `icon:text` + for the user's auto-generated icon + example: "#f44336" + joindateISO: + type: string + lastonlineISO: + type: string + banned_until: + type: number + banned_until_readable: + type: string + administrator: + type: boolean + userCount: + type: number + title: + type: string + isAdminOrGlobalMod: + type: boolean + isAdmin: + type: boolean + isGlobalMod: + type: boolean + displayUserSearch: + type: boolean + section_joindate: + type: boolean + maximumInvites: + type: number + inviteOnly: + type: boolean + adminInviteOnly: + type: boolean + invites: + type: number + showInviteButton: + type: boolean + reputation:disabled: + type: number + - $ref: components/schemas/Pagination.yaml#/Pagination + - $ref: components/schemas/Breadcrumbs.yaml#/Breadcrumbs + - $ref: components/schemas/CommonProps.yaml#/CommonProps "/api/user/uid/{uid}": get: tags: - users - summary: /api/user/uid/{uid} + summary: Get user by uid + description: This route retrieves a user's public profile data. If the calling user is the same as the profile, then it will also return data the user elected to hide (e.g. email/fullname) parameters: - name: uid in: path @@ -2927,7 +3030,8 @@ paths: get: tags: - users - summary: /api/user/username/{username} + summary: Get user by username + description: This route retrieves a user's public profile data. If the calling user is the same as the profile, then it will also return data the user elected to hide (e.g. email/fullname) parameters: - name: username in: path @@ -2946,7 +3050,8 @@ paths: get: tags: - users - summary: /api/user/email/{email} + summary: Get user by email + description: This route retrieves a user's public profile data. If the calling user is the same as the profile, then it will also return data the user elected to hide (e.g. email/fullname) parameters: - name: email in: path @@ -2985,7 +3090,7 @@ paths: get: tags: - users - summary: /api/user/uid/{userslug}/export/uploads + summary: Export a user's uploads (.zip) parameters: - name: userslug in: path @@ -3005,7 +3110,7 @@ paths: get: tags: - users - summary: /api/user/uid/{userslug}/export/profile + summary: Export a user's profile data (.csv) parameters: - name: userslug in: path @@ -3434,456 +3539,11 @@ paths: - 1 - 2 - 3 - "/api/recent/posts/{term?}": - get: - tags: - - topics - summary: /api/recent/posts/{term?} - parameters: - - name: term? - in: path - required: true - schema: - type: string - example: day - responses: - "200": - description: "" - content: - application/json: - schema: - $ref: components/schemas/PostsObject.yaml#/PostsObject - /api/unread/total: - get: - tags: - - topics - summary: Get number of unread topics - responses: - "200": - description: "Success" - content: - text/plain: - schema: - type: number - "/api/topic/teaser/{topic_id}": - get: - tags: - - topics - summary: Get a topic's teaser post - parameters: - - name: topic_id - in: path - required: true - schema: - type: string - example: 1 - responses: - "200": - description: "A JSON object containing the teaser post for a topic" - content: - application/json: - schema: - $ref: components/schemas/PostsObject.yaml#/PostsObject - "/api/topic/pagination/{topic_id}": - get: - tags: - - topics - summary: /api/topic/pagination/{topic_id} - parameters: - - name: topic_id - in: path - required: true - schema: - type: string - example: 1 - responses: - "200": - description: "" - content: - application/json: - schema: - $ref: components/schemas/Pagination.yaml#/Pagination - /api/post/upload: - post: - tags: - - posts - summary: /api/post/upload - responses: - "200": - description: "" - content: - application/json: - schema: - type: array - items: - type: object - properties: - name: - type: string - url: - type: string - text/plain: - schema: - type: array - items: - type: object - properties: - name: - type: string - url: - type: string - "403": - description: "" - content: - application/json: - schema: - type: string - example: Forbidden - text/plain: - schema: - type: string - example: Forbidden - "500": - description: "" - content: - application/json: - schema: - type: object - properties: - path: - type: string - error: - type: string - text/plain: - schema: - type: object - properties: - path: - type: string - error: - type: string - /api/topic/thumb/upload: - post: - tags: - - topics - summary: Upload topic thumb - requestBody: - required: true - content: - multipart/form-data: - schema: - type: object - properties: - files: - type: array - items: - type: string - format: binary - required: - - files - responses: - "200": - description: "Image uploaded" - content: - application/json: - schema: - type: object - properties: - name: - type: string - description: The filename - url: - type: string - description: URL of the uploaded image for use client-side - path: - type: string - description: Path to the file in the local file system - /api/login: - get: - tags: - - authentication - summary: /api/login - responses: - "200": - description: "" - content: - application/json: - schema: - allOf: - - type: object - properties: - loginFormEntry: - type: array - items: - type: object - properties: - label: - type: string - description: A label for the added block - html: - type: string - description: HTML to render on the login page - styleName: - type: string - description: Custom identifier (value is added to `input[id]` and `label[for]`) - alternate_logins: - type: boolean - authentication: - type: array - items: - type: object - properties: - name: - type: string - url: - type: string - callbackURL: - type: string - icon: - type: string - scope: - type: string - prompt: - type: string - allowRegistration: - type: boolean - allowLoginWith: - type: string - title: - type: string - allowPasswordReset: - type: boolean - allowLocalLogin: - type: boolean - - $ref: components/schemas/Breadcrumbs.yaml#/Breadcrumbs - - $ref: components/schemas/CommonProps.yaml#/CommonProps - /api/register: - get: - tags: - - authentication - summary: /api/register - responses: - "200": - description: "" - content: - application/json: - schema: - allOf: - - type: object - properties: - register_window:spansize: - type: string - alternate_logins: - type: boolean - authentication: - type: array - items: - type: object - properties: - name: - type: string - url: - type: string - callbackURL: - type: string - icon: - type: string - scope: - type: string - prompt: - type: string - minimumUsernameLength: - type: number - maximumUsernameLength: - type: number - minimumPasswordLength: - type: number - minimumPasswordStrength: - type: number - regFormEntry: - type: array - items: - type: object - properties: - label: - type: string - html: - type: string - styleName: - type: string - 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 - /api/search: - get: - tags: - - search - summary: /api/search - responses: - "200": - description: "" - content: - application/json: - schema: - allOf: - - type: object - properties: - posts: - $ref: components/schemas/PostsObject.yaml#/PostsObject - matchCount: - type: number - pageCount: - type: number - time: - type: string - multiplePages: - type: boolean - search_query: - type: string - term: - type: string - categories: - type: array - items: - type: object - properties: - value: - oneOf: - - type: string - - type: number - text: - type: string - categoriesCount: - type: number - expandSearch: - type: boolean - showAsPosts: - type: boolean - showAsTopics: - type: boolean - title: - type: string - searchDefaultSortBy: - type: string - required: - - posts - - matchCount - - pageCount - - time - - multiplePages - - search_query - - categories - - categoriesCount - - expandSearch - - showAsPosts - - showAsTopics - - title - - searchDefaultSortBy - - $ref: components/schemas/Pagination.yaml#/Pagination - - $ref: components/schemas/Breadcrumbs.yaml#/Breadcrumbs - - $ref: components/schemas/CommonProps.yaml#/CommonProps - "/api/reset": - get: - tags: - - authentication - summary: Get user password reset (step 1) - responses: - "200": - description: "A JSON object containing the 1st step of the user password reset flow" - content: - application/json: - schema: - allOf: - - type: object - properties: - code: - type: string - nullable: true - title: - type: string - - $ref: components/schemas/Breadcrumbs.yaml#/Breadcrumbs - - $ref: components/schemas/CommonProps.yaml#/CommonProps - "/api/reset/{code}": - get: - tags: - - authentication - summary: Get user password reset (step 2) - parameters: - - name: code - in: path - required: true - schema: - type: string - example: testCode - responses: - "200": - description: "A JSON object containing the 2nd step of the user password reset flow" - content: - application/json: - schema: - allOf: - - type: object - properties: - valid: - type: boolean - code: - type: string - minimumPasswordLength: - type: number - minimumPasswordStrength: - type: number - title: - type: string - - $ref: components/schemas/Breadcrumbs.yaml#/Breadcrumbs - - $ref: components/schemas/CommonProps.yaml#/CommonProps - "/api/email/unsubscribe/{token}": - # TODO: Need GET route here as well - post: - tags: - - emails - summary: /api/email/unsubscribe/{token} - parameters: - - name: token - in: path - required: true - schema: - type: string - example: testToken - responses: - "200": - description: "Successfully unsubscribed" - "500": - description: "Server-side error (likely token verification failure)" "/api/topic/{topic_id}/{slug}/{post_index}": get: tags: - topics - summary: /api/topic/{topic_id}/{slug}/{post_index} + summary: Get topic data parameters: - name: topic_id in: path @@ -3892,12 +3552,14 @@ paths: type: string example: 1 - name: slug + description: This parameter is not required. If omitted, the request will be automatically redirected with the proper topic slug. in: path required: true schema: type: string example: test-topic - name: post_index + description: This parameter is not required. If omitted, the request will presume that you want the first post. The API response is largely unaffected by this parameter, it is used client-side (to send the user to the requested post), and changes the meta/link tags in the server-side generated HTML. in: path required: true schema: @@ -4280,946 +3942,11 @@ paths: - $ref: components/schemas/Pagination.yaml#/Pagination - $ref: components/schemas/Breadcrumbs.yaml#/Breadcrumbs - $ref: components/schemas/CommonProps.yaml#/CommonProps - "/api/topic/{topic_id}/{slug}": - get: - tags: - - topics - summary: /api/topic/{topic_id}/{slug} - parameters: - - name: topic_id - in: path - required: true - schema: - type: string - example: 1 - - name: slug - in: path - required: true - schema: - type: string - example: '' - responses: - "418": - description: "TODO: A proper response needs to be added. It is not really a teapot | Copy response from the route w/o post_index" - "/api/post/{pid}": - get: - tags: - - shorthand - summary: Access a specific post - description: This route comes in handy when all you have is the `pid`, and you want to redirect users to the canonical URL for the topic, with the appropriate topic slug and post index. - parameters: - - name: pid - in: path - required: true - schema: - type: string - example: 1 - responses: - "200": - description: "Canonical URL of topic" - content: - text/plain: - schema: - type: string - /api/flags: - get: - tags: - - flags - summary: /api/flags - responses: - "200": - description: "" - content: - application/json: - schema: - allOf: - - type: object - properties: - flags: - type: array - items: - type: object - properties: - state: - type: string - flagId: - type: number - type: - type: string - targetId: - oneOf: - - type: string - - type: number - description: - type: string - uid: - type: number - description: A user identifier - datetime: - type: number - reporter: - type: object - properties: - username: - type: string - description: A friendly name for a given user account - picture: - nullable: true - type: string - icon:bgColor: - type: string - description: A six-character hexadecimal colour code assigned to the user. This - value is used in conjunction with - `icon:text` for the user's auto-generated - icon - example: "#f44336" - icon:text: - type: string - description: A single-letter representation of a username. This is used in the - auto-generated icon given to users without - an avatar - labelClass: - type: string - target_readable: - type: string - datetimeISO: - type: string - assignee: - type: string - nullable: true - analytics: - type: array - items: - type: number - categories: - type: object - properties: {} - additionalProperties: - type: string - description: All categories will be listed here, with the `cid` as the key, and the category name as the value - hasFilter: - type: boolean - filters: - type: object - properties: - page: - type: number - perPage: - type: number - title: - type: string - - $ref: components/schemas/Pagination.yaml#/Pagination - - $ref: components/schemas/CommonProps.yaml#/CommonProps - "/api/flags/{flagId}": - get: - tags: - - flags - summary: /api/flags/{flagId} - parameters: - - name: flagId - in: path - required: true - schema: - type: string - example: 1 - responses: - "200": - description: "" - content: - application/json: - schema: - allOf: - - type: object - properties: - state: - type: string - flagId: - type: number - type: - type: string - targetId: - type: number - description: - type: string - uid: - type: number - description: A user identifier - datetime: - type: number - datetimeISO: - type: string - target_readable: - type: string - target: - type: object - properties: {} - additionalProperties: - description: Properties change depending on the target type (user, post, etc.) - assignee: - type: number - nullable: true - filters: - type: object - properties: - page: - type: number - perPage: - type: number - history: - type: array - items: - type: object - properties: - uid: - type: number - description: A user identifier - fields: - type: object - properties: - state: - type: string - datetime: - type: number - datetimeISO: - type: string - user: - type: object - properties: - username: - type: string - description: A friendly name for a given user account - userslug: - type: string - description: An URL-safe variant of the username (i.e. lower-cased, spaces - removed, etc.) - picture: - nullable: true - uid: - type: number - description: A user identifier - icon:text: - type: string - description: A single-letter representation of a username. This is used in the - auto-generated icon given to users without - an avatar - icon:bgColor: - type: string - description: A six-character hexadecimal colour code assigned to the user. This - value is used in conjunction with - `icon:text` for the user's auto-generated - icon - example: "#f44336" - notes: - type: array - items: - type: object - properties: - uid: - type: number - content: - type: string - datetime: - type: number - datetimeISO: - type: string - user: - type: object - properties: - username: - type: string - description: A friendly name for a given user account - userslug: - type: string - description: An URL-safe variant of the username (i.e. lower-cased, spaces - removed, etc.) - picture: - type: string - uid: - type: number - description: A user identifier - icon:text: - type: string - description: A single-letter representation of a username. This is used in the - auto-generated icon given to users without - an avatar - icon:bgColor: - type: string - description: A six-character hexadecimal colour code assigned to the user. This - value is used in conjunction with - `icon:text` for the user's auto-generated - icon - example: "#f44336" - reporter: - type: object - properties: - username: - type: string - description: A friendly name for a given user account - userslug: - type: string - description: An URL-safe variant of the username (i.e. lower-cased, spaces - removed, etc.) - picture: - nullable: true - reputation: - type: number - uid: - type: number - description: A user identifier - icon:text: - type: string - description: A single-letter representation of a username. This is used in the - auto-generated icon given to users without an - avatar - icon:bgColor: - type: string - description: A six-character hexadecimal colour code assigned to the user. This - value is used in conjunction with `icon:text` for - the user's auto-generated icon - example: "#f44336" - type_path: - type: string - assignees: - type: array - items: - $ref: components/schemas/UserObject.yaml#/UserObject - type_bool: - type: object - properties: - post: - type: boolean - user: - type: boolean - empty: - type: boolean - title: - type: string - categories: - type: object - additionalProperties: - type: string - - $ref: components/schemas/CommonProps.yaml#/CommonProps - /api/post-queue: - get: - tags: - - admin - summary: /api/post-queue - responses: - "200": - description: "" - content: - application/json: - schema: - allOf: - - type: object - properties: - title: - type: string - posts: - type: array - items: - allOf: - - type: object - properties: - id: - type: string - uid: - type: number - description: A user identifier - type: - type: string - data: - type: object - properties: - title: - type: string - content: - type: string - thumb: - type: string - cid: - oneOf: - - type: number - - type: string - tags: - type: array - items: {} - uid: - type: number - description: A user identifier - req: - type: object - properties: - uid: - type: number - description: A user identifier - ip: - type: string - host: - type: string - protocol: - type: string - secure: - type: boolean - url: - type: string - path: - type: string - headers: - type: object - properties: - x-real-ip: - type: string - x-forwarded-for: - type: string - x-forwarded-proto: - type: string - host: - type: string - x-nginx-proxy: - type: string - connection: - type: string - accept: - type: string - user-agent: - type: string - sec-fetch-site: - type: string - sec-fetch-mode: - type: string - referer: - type: string - accept-encoding: - type: string - accept-language: - type: string - cookie: - type: string - timestamp: - type: number - fromQueue: - type: boolean - timestampISO: - type: string - description: An ISO 8601 formatted date string (complementing `timestamp`) - rawContent: - type: string - tid: - type: number - description: A topic identifier - toPid: - nullable: true - user: - type: object - properties: - username: - type: string - description: A friendly name for a given user account - userslug: - type: string - description: An URL-safe variant of the username (i.e. lower-cased, spaces - removed, etc.) - picture: - nullable: true - type: string - uid: - type: number - description: A user identifier - icon:text: - type: string - description: A single-letter representation of a username. This is used in the - auto-generated icon given to users without - an avatar - icon:bgColor: - type: string - description: A six-character hexadecimal colour code assigned to the user. This - value is used in conjunction with - `icon:text` for the user's auto-generated - icon - example: "#f44336" - topic: - type: object - properties: - cid: - type: number - title: - type: string - titleRaw: - type: string - - $ref: components/schemas/CategoryObject.yaml#/CategoryObject - - $ref: components/schemas/Pagination.yaml#/Pagination - - $ref: components/schemas/CommonProps.yaml#/CommonProps - /api/ip-blacklist: - get: - tags: - - admin - summary: /api/ip-blacklist - responses: - "418": - description: "TODO: A proper response needs to be added. It is not really a teapot | Copy response from corresponding admin route" - /api/registration-queue: - get: - tags: - - admin - summary: /api/registration-queue - responses: - "418": - description: "TODO: A proper response needs to be added. It is not really a teapot | Copy response from corresponding admin route" - "/api/tags/{tag}": - get: - tags: - - tags - summary: /api/tags/{tag} - description: Returns a list of topics that are tagged with {tag} - parameters: - - name: tag - description: The tag used to retrieve the topics - in: path - required: true - schema: - type: string - example: test - - name: page - description: Page number used in pagination - in: query - required: false - schema: - type: number - example: '' - responses: - "200": - description: "" - content: - application/json: - schema: - allOf: - - type: object - properties: - topics: - type: array - description: An array of topics that are all tagged with {tag} - items: - 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 - teaserPid: - oneOf: - - type: number - - type: string - deleted: - type: number - locked: - type: number - pinned: - type: number - description: Whether or not this particular topic is pinned to the top of the - category - upvotes: - type: number - downvotes: - type: number - titleRaw: - type: string - timestampISO: - type: string - description: An ISO 8601 formatted date string (complementing `timestamp`) - lastposttimeISO: - type: string - votes: - type: number - category: - type: object - properties: - cid: - type: number - description: A category identifier - name: - type: string - slug: - type: string - icon: - type: string - image: - nullable: true - imageClass: - nullable: true - type: string - bgColor: - type: string - color: - type: string - disabled: - type: number - user: - type: object - properties: - uid: - type: number - description: A user identifier - username: - type: string - description: A friendly name for a given user account - userslug: - type: string - description: An URL-safe variant of the username (i.e. lower-cased, spaces - removed, etc.) - reputation: - type: number - postcount: - type: number - picture: - nullable: true - type: string - signature: - nullable: true - type: string - banned: - type: number - status: - type: string - icon:text: - type: string - description: A single-letter representation of a username. This is used in the - auto-generated icon given to users without - an avatar - icon:bgColor: - type: string - description: A six-character hexadecimal colour code assigned to the user. This - value is used in conjunction with - `icon:text` for the user's auto-generated - icon - example: "#f44336" - banned_until_readable: - type: string - fullname: - type: string - teaser: - type: object - properties: - pid: - type: number - uid: - type: number - description: A user identifier - timestamp: - type: number - tid: - type: number - description: A topic identifier - content: - type: string - timestampISO: - type: string - description: An ISO 8601 formatted date string (complementing `timestamp`) - user: - type: object - properties: - uid: - type: number - description: A user identifier - username: - type: string - description: A friendly name for a given user account - userslug: - type: string - description: An URL-safe variant of the username (i.e. lower-cased, spaces - removed, etc.) - picture: - nullable: true - type: string - icon:text: - type: string - description: A single-letter representation of a username. This is used in the - auto-generated icon given to users - without an avatar - icon:bgColor: - type: string - description: A six-character hexadecimal colour code assigned to the user. This - value is used in conjunction with - `icon:text` for the user's - auto-generated icon - example: "#f44336" - index: - type: number - tags: - type: array - items: - type: object - properties: - value: - type: string - valueEscaped: - type: string - color: - type: string - bgColor: - type: string - score: - type: number - isOwner: - type: boolean - ignored: - type: boolean - unread: - type: boolean - bookmark: - nullable: true - unreplied: - type: boolean - icons: - type: array - items: {} - index: - type: number - thumb: - type: string - isQuestion: - nullable: true - type: number - isSolved: - type: number - tag: - type: string - title: - type: string - categories: - type: array - items: - type: object - properties: - cid: - type: number - description: A category identifier - name: - type: string - level: - type: string - icon: - type: string - parentCid: - type: number - description: The category identifier for the category that is the immediate - ancestor of the current category - color: - type: string - bgColor: - type: string - selected: - type: boolean - imageClass: - type: string - rssFeedUrl: - type: string - required: - - topics - - tag - - title - - categories - - $ref: components/schemas/Pagination.yaml#/Pagination - - $ref: components/schemas/Breadcrumbs.yaml#/Breadcrumbs - - $ref: components/schemas/CommonProps.yaml#/CommonProps - /api/tags: - get: - tags: - - tags - summary: /api/tags - description: Returns a list of tags sorted by the most topics - responses: - "200": - description: "" - content: - application/json: - schema: - allOf: - - type: object - properties: - tags: - type: array - description: An array of tags sorted by the most topics - items: - type: object - properties: - value: - type: string - description: The raw tag - score: - type: number - description: Number of topics tagged by this tag - valueEscaped: - type: string - description: This is the escaped tag value, equal to validator.escape(value) - color: - type: string - bgColor: - type: string - displayTagSearch: - type: boolean - nextStart: - type: number - title: - type: string - - $ref: components/schemas/Breadcrumbs.yaml#/Breadcrumbs - - $ref: components/schemas/CommonProps.yaml#/CommonProps - /api/popular: - get: - tags: - - topics - summary: Get Popular Topics - description: Returns a list of topics sorted by most replies. In an event of a - tie breaker, the topic with the most views. Can be filtered by All Time, - Day, Week, or Month. - responses: - "200": - description: An array of topic objects sorted by most replies and views. - content: - application/json: - schema: - allOf: - - type: object - properties: - nextStart: - type: number - topicCount: - type: number - topics: - type: array - items: - $ref: components/schemas/TopicObject.yaml#/TopicObject - tids: - type: array - items: - type: number - canPost: - type: boolean - categories: - type: array - items: - type: object - properties: - cid: - type: number - description: A category identifier - name: - type: string - level: - type: string - icon: - type: string - parentCid: - type: number - description: The category identifier for the category that is the immediate - ancestor of the current category - color: - type: string - bgColor: - type: string - selected: - type: boolean - imageClass: - type: string - allCategoriesUrl: - type: string - selectedCategory: - type: object - properties: - icon: - type: string - name: - type: string - bgColor: - type: string - nullable: true - selectedCids: - type: array - items: - type: number - feeds:disableRSS: - type: number - rssFeedUrl: - type: string - title: - type: string - filters: - type: array - items: - type: object - properties: - name: - type: string - url: - type: string - selected: - type: boolean - filter: - type: string - selectedFilter: - type: object - properties: - name: - type: string - url: - type: string - selected: - type: boolean - filter: - type: string - terms: - type: array - items: - type: object - properties: - name: - type: string - url: - type: string - selected: - type: boolean - term: - type: string - selectedTerm: - type: object - properties: - name: - type: string - url: - type: string - selected: - type: boolean - term: - type: string - - $ref: components/schemas/Pagination.yaml#/Pagination - - $ref: components/schemas/Breadcrumbs.yaml#/Breadcrumbs - - $ref: components/schemas/CommonProps.yaml#/CommonProps /api/recent: get: tags: - topics - summary: Recent Topics + summary: Get recent topics description: Returns a list of topics sorted by timestamp. responses: "200": @@ -5343,152 +4070,30 @@ paths: - $ref: components/schemas/Pagination.yaml#/Pagination - $ref: components/schemas/Breadcrumbs.yaml#/Breadcrumbs - $ref: components/schemas/CommonProps.yaml#/CommonProps - /api/top: + "/api/recent/posts/{term}": get: tags: - - topics - summary: Top Topics - description: Returns a list of topics sorted by most upvotes. + - posts + summary: Get recent posts + parameters: + - name: term + in: path + required: true + schema: + type: string + example: daily responses: "200": - description: An array of topic objects sorted by most upvotes. + description: "" content: application/json: schema: - allOf: - - type: object - properties: - nextStart: - type: number - topicCount: - type: number - topics: - type: array - items: - $ref: components/schemas/TopicObject.yaml#/TopicObject - tids: - type: array - items: - type: number - canPost: - type: boolean - categories: - type: array - items: - type: object - properties: - cid: - type: number - description: A category identifier - name: - type: string - level: - type: string - icon: - type: string - parentCid: - type: number - description: The category identifier for the category that is the immediate - ancestor of the current category - color: - type: string - bgColor: - type: string - selected: - type: boolean - imageClass: - type: string - allCategoriesUrl: - type: string - selectedCategory: - type: object - properties: - cid: - type: number - description: A category identifier - name: - type: string - level: - type: string - icon: - type: string - parentCid: - type: number - description: The category identifier for the category that is the immediate - ancestor of the current category - color: - type: string - bgColor: - type: string - selected: - type: boolean - nullable: true - selectedCids: - type: array - items: - type: number - feeds:disableRSS: - type: number - rssFeedUrl: - type: string - title: - type: string - filters: - type: array - items: - type: object - properties: - name: - type: string - url: - type: string - selected: - type: boolean - filter: - type: string - selectedFilter: - type: object - properties: - name: - type: string - url: - type: string - selected: - type: boolean - filter: - type: string - terms: - type: array - items: - type: object - properties: - name: - type: string - url: - type: string - selected: - type: boolean - term: - type: string - selectedTerm: - type: object - properties: - name: - type: string - url: - type: string - selected: - type: boolean - term: - type: string - - $ref: components/schemas/Pagination.yaml#/Pagination - - $ref: components/schemas/Breadcrumbs.yaml#/Breadcrumbs - - $ref: components/schemas/CommonProps.yaml#/CommonProps + $ref: components/schemas/PostsObject.yaml#/PostsObject /api/unread: get: tags: - topics - summary: Unread Topics + summary: Get unread topics description: Returns a list of the current user's unread topics, sorted by the last post's timestamp. responses: @@ -5769,11 +4374,1495 @@ paths: - $ref: components/schemas/Pagination.yaml#/Pagination - $ref: components/schemas/Breadcrumbs.yaml#/Breadcrumbs - $ref: components/schemas/CommonProps.yaml#/CommonProps + /api/unread/total: + get: + tags: + - topics + summary: Get number of unread topics + responses: + "200": + description: "Success" + content: + text/plain: + schema: + type: number + "/api/topic/teaser/{topic_id}": + get: + tags: + - topics + summary: Get a topic's teaser post + parameters: + - name: topic_id + in: path + required: true + schema: + type: string + example: 1 + responses: + "200": + description: "A JSON object containing the teaser post for a topic" + content: + application/json: + schema: + $ref: components/schemas/PostsObject.yaml#/PostsObject + "/api/topic/pagination/{topic_id}": + get: + tags: + - topics + summary: Get topic pagination data + description: This route retrieves pagination data for a given topic. It is used mainly client-side, as it return data necessary to update a pagination block client-side. + parameters: + - name: topic_id + in: path + required: true + schema: + type: string + example: 1 + responses: + "200": + description: "" + content: + application/json: + schema: + $ref: components/schemas/Pagination.yaml#/Pagination + /api/post/upload: + post: + tags: + - posts + summary: Upload a file to a specific post + description: Provided by NodeBB core and used mainly by the composer, this route allows you to upload an image or file to a post. + responses: + "200": + description: "" + content: + application/json: + schema: + type: array + items: + type: object + properties: + name: + type: string + url: + type: string + text/plain: + schema: + type: array + items: + type: object + properties: + name: + type: string + url: + type: string + "403": + description: "" + content: + application/json: + schema: + type: string + example: Forbidden + text/plain: + schema: + type: string + example: Forbidden + "500": + description: "" + content: + application/json: + schema: + type: object + properties: + path: + type: string + error: + type: string + text/plain: + schema: + type: object + properties: + path: + type: string + error: + type: string + /api/topic/thumb/upload: + post: + tags: + - topics + summary: Upload topic thumb + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + properties: + files: + type: array + items: + type: string + format: binary + required: + - files + responses: + "200": + description: "Image uploaded" + content: + application/json: + schema: + type: object + properties: + name: + type: string + description: The filename + url: + type: string + description: URL of the uploaded image for use client-side + path: + type: string + description: Path to the file in the local file system + /api/login: + get: + tags: + - authentication + summary: /api/login + responses: + "200": + description: "" + content: + application/json: + schema: + allOf: + - type: object + properties: + loginFormEntry: + type: array + items: + type: object + properties: + label: + type: string + description: A label for the added block + html: + type: string + description: HTML to render on the login page + styleName: + type: string + description: Custom identifier (value is added to `input[id]` and `label[for]`) + alternate_logins: + type: boolean + authentication: + type: array + items: + type: object + properties: + name: + type: string + url: + type: string + callbackURL: + type: string + icon: + type: string + scope: + type: string + prompt: + type: string + allowRegistration: + type: boolean + allowLoginWith: + type: string + title: + type: string + allowPasswordReset: + type: boolean + allowLocalLogin: + type: boolean + - $ref: components/schemas/Breadcrumbs.yaml#/Breadcrumbs + - $ref: components/schemas/CommonProps.yaml#/CommonProps + /api/register: + get: + tags: + - authentication + summary: /api/register + responses: + "200": + description: "" + content: + application/json: + schema: + allOf: + - type: object + properties: + register_window:spansize: + type: string + alternate_logins: + type: boolean + authentication: + type: array + items: + type: object + properties: + name: + type: string + url: + type: string + callbackURL: + type: string + icon: + type: string + scope: + type: string + prompt: + type: string + minimumUsernameLength: + type: number + maximumUsernameLength: + type: number + minimumPasswordLength: + type: number + minimumPasswordStrength: + type: number + regFormEntry: + type: array + items: + type: object + properties: + label: + type: string + html: + type: string + styleName: + type: string + 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 + /api/search: + get: + tags: + - search + summary: Get search results + responses: + "200": + description: "" + content: + application/json: + schema: + allOf: + - type: object + properties: + posts: + $ref: components/schemas/PostsObject.yaml#/PostsObject + matchCount: + type: number + pageCount: + type: number + time: + type: string + multiplePages: + type: boolean + search_query: + type: string + term: + type: string + categories: + type: array + items: + type: object + properties: + value: + oneOf: + - type: string + - type: number + text: + type: string + categoriesCount: + type: number + expandSearch: + type: boolean + showAsPosts: + type: boolean + showAsTopics: + type: boolean + title: + type: string + searchDefaultSortBy: + type: string + required: + - posts + - matchCount + - pageCount + - time + - multiplePages + - search_query + - categories + - categoriesCount + - expandSearch + - showAsPosts + - showAsTopics + - title + - searchDefaultSortBy + - $ref: components/schemas/Pagination.yaml#/Pagination + - $ref: components/schemas/Breadcrumbs.yaml#/Breadcrumbs + - $ref: components/schemas/CommonProps.yaml#/CommonProps + "/api/reset": + get: + tags: + - authentication + summary: Get user password reset (step 1) + responses: + "200": + description: "A JSON object containing the 1st step of the user password reset flow" + content: + application/json: + schema: + allOf: + - type: object + properties: + code: + type: string + nullable: true + title: + type: string + - $ref: components/schemas/Breadcrumbs.yaml#/Breadcrumbs + - $ref: components/schemas/CommonProps.yaml#/CommonProps + "/api/reset/{code}": + get: + tags: + - authentication + summary: Get user password reset (step 2) + parameters: + - name: code + in: path + required: true + schema: + type: string + example: testCode + responses: + "200": + description: "A JSON object containing the 2nd step of the user password reset flow" + content: + application/json: + schema: + allOf: + - type: object + properties: + valid: + type: boolean + code: + type: string + minimumPasswordLength: + type: number + minimumPasswordStrength: + type: number + title: + type: string + - $ref: components/schemas/Breadcrumbs.yaml#/Breadcrumbs + - $ref: components/schemas/CommonProps.yaml#/CommonProps + "/api/email/unsubscribe/{token}": + # TODO: Need GET route here as well + post: + tags: + - emails + summary: Unsubscribe user from email type + parameters: + - name: token + in: path + required: true + schema: + type: string + example: testToken + responses: + "200": + description: "Successfully unsubscribed" + "500": + description: "Server-side error (likely token verification failure)" + "/api/post/{pid}": + get: + tags: + - shorthand + summary: Access a specific post + description: This route comes in handy when all you have is the `pid`, and you want to redirect users to the canonical URL for the topic, with the appropriate topic slug and post index. + parameters: + - name: pid + in: path + required: true + schema: + type: string + example: 1 + responses: + "200": + description: "Canonical URL of topic" + content: + text/plain: + schema: + type: string + /api/flags: + get: + tags: + - flags + summary: Get flags list + responses: + "200": + description: "" + content: + application/json: + schema: + allOf: + - type: object + properties: + flags: + type: array + items: + type: object + properties: + state: + type: string + flagId: + type: number + type: + type: string + targetId: + oneOf: + - type: string + - type: number + description: + type: string + uid: + type: number + description: A user identifier + datetime: + type: number + reporter: + type: object + properties: + username: + type: string + description: A friendly name for a given user account + picture: + nullable: true + type: string + icon:bgColor: + type: string + description: A six-character hexadecimal colour code assigned to the user. This + value is used in conjunction with + `icon:text` for the user's auto-generated + icon + example: "#f44336" + icon:text: + type: string + description: A single-letter representation of a username. This is used in the + auto-generated icon given to users without + an avatar + labelClass: + type: string + target_readable: + type: string + datetimeISO: + type: string + assignee: + type: string + nullable: true + analytics: + type: array + items: + type: number + categories: + type: object + properties: {} + additionalProperties: + type: string + description: All categories will be listed here, with the `cid` as the key, and the category name as the value + hasFilter: + type: boolean + filters: + type: object + properties: + page: + type: number + perPage: + type: number + title: + type: string + - $ref: components/schemas/Pagination.yaml#/Pagination + - $ref: components/schemas/CommonProps.yaml#/CommonProps + "/api/flags/{flagId}": + get: + tags: + - flags + summary: /api/flags/{flagId} + parameters: + - name: flagId + in: path + required: true + schema: + type: string + example: 1 + responses: + "200": + description: "" + content: + application/json: + schema: + allOf: + - type: object + properties: + state: + type: string + flagId: + type: number + type: + type: string + targetId: + type: number + description: + type: string + uid: + type: number + description: A user identifier + datetime: + type: number + datetimeISO: + type: string + target_readable: + type: string + target: + type: object + properties: {} + additionalProperties: + description: Properties change depending on the target type (user, post, etc.) + assignee: + type: number + nullable: true + filters: + type: object + properties: + page: + type: number + perPage: + type: number + history: + type: array + items: + type: object + properties: + uid: + type: number + description: A user identifier + fields: + type: object + properties: + state: + type: string + datetime: + type: number + datetimeISO: + type: string + user: + type: object + properties: + username: + type: string + description: A friendly name for a given user account + userslug: + type: string + description: An URL-safe variant of the username (i.e. lower-cased, spaces + removed, etc.) + picture: + nullable: true + uid: + type: number + description: A user identifier + icon:text: + type: string + description: A single-letter representation of a username. This is used in the + auto-generated icon given to users without + an avatar + icon:bgColor: + type: string + description: A six-character hexadecimal colour code assigned to the user. This + value is used in conjunction with + `icon:text` for the user's auto-generated + icon + example: "#f44336" + notes: + type: array + items: + type: object + properties: + uid: + type: number + content: + type: string + datetime: + type: number + datetimeISO: + type: string + user: + type: object + properties: + username: + type: string + description: A friendly name for a given user account + userslug: + type: string + description: An URL-safe variant of the username (i.e. lower-cased, spaces + removed, etc.) + picture: + type: string + uid: + type: number + description: A user identifier + icon:text: + type: string + description: A single-letter representation of a username. This is used in the + auto-generated icon given to users without + an avatar + icon:bgColor: + type: string + description: A six-character hexadecimal colour code assigned to the user. This + value is used in conjunction with + `icon:text` for the user's auto-generated + icon + example: "#f44336" + reporter: + type: object + properties: + username: + type: string + description: A friendly name for a given user account + userslug: + type: string + description: An URL-safe variant of the username (i.e. lower-cased, spaces + removed, etc.) + picture: + nullable: true + reputation: + type: number + uid: + type: number + description: A user identifier + icon:text: + type: string + description: A single-letter representation of a username. This is used in the + auto-generated icon given to users without an + avatar + icon:bgColor: + type: string + description: A six-character hexadecimal colour code assigned to the user. This + value is used in conjunction with `icon:text` for + the user's auto-generated icon + example: "#f44336" + type_path: + type: string + assignees: + type: array + items: + $ref: components/schemas/UserObject.yaml#/UserObject + type_bool: + type: object + properties: + post: + type: boolean + user: + type: boolean + empty: + type: boolean + title: + type: string + categories: + type: object + additionalProperties: + type: string + - $ref: components/schemas/CommonProps.yaml#/CommonProps + /api/post-queue: + get: + tags: + - admin + summary: Get flag data + responses: + "200": + description: "" + content: + application/json: + schema: + allOf: + - type: object + properties: + title: + type: string + posts: + type: array + items: + allOf: + - type: object + properties: + id: + type: string + uid: + type: number + description: A user identifier + type: + type: string + data: + type: object + properties: + title: + type: string + content: + type: string + thumb: + type: string + cid: + oneOf: + - type: number + - type: string + tags: + type: array + items: {} + uid: + type: number + description: A user identifier + req: + type: object + properties: + uid: + type: number + description: A user identifier + ip: + type: string + host: + type: string + protocol: + type: string + secure: + type: boolean + url: + type: string + path: + type: string + headers: + type: object + properties: + x-real-ip: + type: string + x-forwarded-for: + type: string + x-forwarded-proto: + type: string + host: + type: string + x-nginx-proxy: + type: string + connection: + type: string + accept: + type: string + user-agent: + type: string + sec-fetch-site: + type: string + sec-fetch-mode: + type: string + referer: + type: string + accept-encoding: + type: string + accept-language: + type: string + cookie: + type: string + timestamp: + type: number + fromQueue: + type: boolean + timestampISO: + type: string + description: An ISO 8601 formatted date string (complementing `timestamp`) + rawContent: + type: string + tid: + type: number + description: A topic identifier + toPid: + nullable: true + user: + type: object + properties: + username: + type: string + description: A friendly name for a given user account + userslug: + type: string + description: An URL-safe variant of the username (i.e. lower-cased, spaces + removed, etc.) + picture: + nullable: true + type: string + uid: + type: number + description: A user identifier + icon:text: + type: string + description: A single-letter representation of a username. This is used in the + auto-generated icon given to users without + an avatar + icon:bgColor: + type: string + description: A six-character hexadecimal colour code assigned to the user. This + value is used in conjunction with + `icon:text` for the user's auto-generated + icon + example: "#f44336" + topic: + type: object + properties: + cid: + type: number + title: + type: string + titleRaw: + type: string + - $ref: components/schemas/CategoryObject.yaml#/CategoryObject + - $ref: components/schemas/Pagination.yaml#/Pagination + - $ref: components/schemas/CommonProps.yaml#/CommonProps + /api/ip-blacklist: + get: + tags: + - admin + summary: Get IP blacklist settings + responses: + "418": + description: "TODO: A proper response needs to be added. It is not really a teapot | Copy response from corresponding admin route" + /api/registration-queue: + get: + tags: + - admin + summary: Get registration queue + responses: + "418": + description: "TODO: A proper response needs to be added. It is not really a teapot | Copy response from corresponding admin route" + /api/tags: + get: + tags: + - tags + summary: Get tags + description: Returns a list of tags sorted by the most topics + responses: + "200": + description: "" + content: + application/json: + schema: + allOf: + - type: object + properties: + tags: + type: array + description: An array of tags sorted by the most topics + items: + type: object + properties: + value: + type: string + description: The raw tag + score: + type: number + description: Number of topics tagged by this tag + valueEscaped: + type: string + description: This is the escaped tag value, equal to validator.escape(value) + color: + type: string + bgColor: + type: string + displayTagSearch: + type: boolean + nextStart: + type: number + title: + type: string + - $ref: components/schemas/Breadcrumbs.yaml#/Breadcrumbs + - $ref: components/schemas/CommonProps.yaml#/CommonProps + "/api/tags/{tag}": + get: + tags: + - tags + summary: Get tag data + description: Returns a list of topics that are tagged with {tag} + parameters: + - name: tag + description: The tag used to retrieve the topics + in: path + required: true + schema: + type: string + example: test + - name: page + description: Page number used in pagination + in: query + required: false + schema: + type: number + example: '' + responses: + "200": + description: "" + content: + application/json: + schema: + allOf: + - type: object + properties: + topics: + type: array + description: An array of topics that are all tagged with {tag} + items: + 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 + teaserPid: + oneOf: + - type: number + - type: string + deleted: + type: number + locked: + type: number + pinned: + type: number + description: Whether or not this particular topic is pinned to the top of the + category + upvotes: + type: number + downvotes: + type: number + titleRaw: + type: string + timestampISO: + type: string + description: An ISO 8601 formatted date string (complementing `timestamp`) + lastposttimeISO: + type: string + votes: + type: number + category: + type: object + properties: + cid: + type: number + description: A category identifier + name: + type: string + slug: + type: string + icon: + type: string + image: + nullable: true + imageClass: + nullable: true + type: string + bgColor: + type: string + color: + type: string + disabled: + type: number + user: + type: object + properties: + uid: + type: number + description: A user identifier + username: + type: string + description: A friendly name for a given user account + userslug: + type: string + description: An URL-safe variant of the username (i.e. lower-cased, spaces + removed, etc.) + reputation: + type: number + postcount: + type: number + picture: + nullable: true + type: string + signature: + nullable: true + type: string + banned: + type: number + status: + type: string + icon:text: + type: string + description: A single-letter representation of a username. This is used in the + auto-generated icon given to users without + an avatar + icon:bgColor: + type: string + description: A six-character hexadecimal colour code assigned to the user. This + value is used in conjunction with + `icon:text` for the user's auto-generated + icon + example: "#f44336" + banned_until_readable: + type: string + fullname: + type: string + teaser: + type: object + properties: + pid: + type: number + uid: + type: number + description: A user identifier + timestamp: + type: number + tid: + type: number + description: A topic identifier + content: + type: string + timestampISO: + type: string + description: An ISO 8601 formatted date string (complementing `timestamp`) + user: + type: object + properties: + uid: + type: number + description: A user identifier + username: + type: string + description: A friendly name for a given user account + userslug: + type: string + description: An URL-safe variant of the username (i.e. lower-cased, spaces + removed, etc.) + picture: + nullable: true + type: string + icon:text: + type: string + description: A single-letter representation of a username. This is used in the + auto-generated icon given to users + without an avatar + icon:bgColor: + type: string + description: A six-character hexadecimal colour code assigned to the user. This + value is used in conjunction with + `icon:text` for the user's + auto-generated icon + example: "#f44336" + index: + type: number + tags: + type: array + items: + type: object + properties: + value: + type: string + valueEscaped: + type: string + color: + type: string + bgColor: + type: string + score: + type: number + isOwner: + type: boolean + ignored: + type: boolean + unread: + type: boolean + bookmark: + nullable: true + unreplied: + type: boolean + icons: + type: array + items: {} + index: + type: number + thumb: + type: string + isQuestion: + nullable: true + type: number + isSolved: + type: number + tag: + type: string + title: + type: string + categories: + type: array + items: + type: object + properties: + cid: + type: number + description: A category identifier + name: + type: string + level: + type: string + icon: + type: string + parentCid: + type: number + description: The category identifier for the category that is the immediate + ancestor of the current category + color: + type: string + bgColor: + type: string + selected: + type: boolean + imageClass: + type: string + rssFeedUrl: + type: string + required: + - topics + - tag + - title + - categories + - $ref: components/schemas/Pagination.yaml#/Pagination + - $ref: components/schemas/Breadcrumbs.yaml#/Breadcrumbs + - $ref: components/schemas/CommonProps.yaml#/CommonProps + /api/popular: + get: + tags: + - topics + summary: Get popular topics + description: Returns a list of topics sorted by most replies. In an event of a + tie breaker, the topic with the most views. Can be filtered by All Time, + Day, Week, or Month. + responses: + "200": + description: An array of topic objects sorted by most replies and views. + content: + application/json: + schema: + allOf: + - type: object + properties: + nextStart: + type: number + topicCount: + type: number + topics: + type: array + items: + $ref: components/schemas/TopicObject.yaml#/TopicObject + tids: + type: array + items: + type: number + canPost: + type: boolean + categories: + type: array + items: + type: object + properties: + cid: + type: number + description: A category identifier + name: + type: string + level: + type: string + icon: + type: string + parentCid: + type: number + description: The category identifier for the category that is the immediate + ancestor of the current category + color: + type: string + bgColor: + type: string + selected: + type: boolean + imageClass: + type: string + allCategoriesUrl: + type: string + selectedCategory: + type: object + properties: + icon: + type: string + name: + type: string + bgColor: + type: string + nullable: true + selectedCids: + type: array + items: + type: number + feeds:disableRSS: + type: number + rssFeedUrl: + type: string + title: + type: string + filters: + type: array + items: + type: object + properties: + name: + type: string + url: + type: string + selected: + type: boolean + filter: + type: string + selectedFilter: + type: object + properties: + name: + type: string + url: + type: string + selected: + type: boolean + filter: + type: string + terms: + type: array + items: + type: object + properties: + name: + type: string + url: + type: string + selected: + type: boolean + term: + type: string + selectedTerm: + type: object + properties: + name: + type: string + url: + type: string + selected: + type: boolean + term: + type: string + - $ref: components/schemas/Pagination.yaml#/Pagination + - $ref: components/schemas/Breadcrumbs.yaml#/Breadcrumbs + - $ref: components/schemas/CommonProps.yaml#/CommonProps + /api/top: + get: + tags: + - topics + summary: Get top topics + description: Returns a list of topics sorted by most upvotes. + responses: + "200": + description: An array of topic objects sorted by most upvotes. + content: + application/json: + schema: + allOf: + - type: object + properties: + nextStart: + type: number + topicCount: + type: number + topics: + type: array + items: + $ref: components/schemas/TopicObject.yaml#/TopicObject + tids: + type: array + items: + type: number + canPost: + type: boolean + categories: + type: array + items: + type: object + properties: + cid: + type: number + description: A category identifier + name: + type: string + level: + type: string + icon: + type: string + parentCid: + type: number + description: The category identifier for the category that is the immediate + ancestor of the current category + color: + type: string + bgColor: + type: string + selected: + type: boolean + imageClass: + type: string + allCategoriesUrl: + type: string + selectedCategory: + type: object + properties: + cid: + type: number + description: A category identifier + name: + type: string + level: + type: string + icon: + type: string + parentCid: + type: number + description: The category identifier for the category that is the immediate + ancestor of the current category + color: + type: string + bgColor: + type: string + selected: + type: boolean + nullable: true + selectedCids: + type: array + items: + type: number + feeds:disableRSS: + type: number + rssFeedUrl: + type: string + title: + type: string + filters: + type: array + items: + type: object + properties: + name: + type: string + url: + type: string + selected: + type: boolean + filter: + type: string + selectedFilter: + type: object + properties: + name: + type: string + url: + type: string + selected: + type: boolean + filter: + type: string + terms: + type: array + items: + type: object + properties: + name: + type: string + url: + type: string + selected: + type: boolean + term: + type: string + selectedTerm: + type: object + properties: + name: + type: string + url: + type: string + selected: + type: boolean + term: + type: string + - $ref: components/schemas/Pagination.yaml#/Pagination + - $ref: components/schemas/Breadcrumbs.yaml#/Breadcrumbs + - $ref: components/schemas/CommonProps.yaml#/CommonProps "/api/category/{category_id}/{slug}/{topic_index}": get: tags: - categories - summary: /api/category/{category_id}/{slug}/{topic_index} + summary: Get a single category + description: This route retrieves a single category's data, along with its children and the topics created inside of the category. parameters: - name: category_id in: path @@ -5782,12 +5871,14 @@ paths: type: string example: 1 - name: slug + description: This parameter is not required. If omitted, the request will be automatically redirected with the proper category slug. in: path required: true schema: type: string example: test - name: topic_index + description: This parameter is not required. If omitted, the request will presume that you want the first post. The API response is largely unaffected by this parameter, it is used client-side (to send the user to the requested post), and changes the meta/link tags in the server-side generated HTML. in: path required: true schema: @@ -5860,32 +5951,24 @@ paths: - $ref: components/schemas/Pagination.yaml#/Pagination - $ref: components/schemas/Breadcrumbs.yaml#/Breadcrumbs - $ref: components/schemas/CommonProps.yaml#/CommonProps - "/api/category/{category_id}/{slug?}": + /api/me: get: tags: - - categories - summary: /api/category/{category_id}/{slug} - parameters: - - name: category_id - in: path - required: true - schema: - type: string - example: 1 - - name: slug - in: path - required: true - schema: - type: string - example: test + - shorthand + summary: Access your profile + description: This shorthand is useful if you want to link to pages in your own account profile, but do not want (or have) the userslug. It is also especially useful as a means to instruct users on how to do things, as you can easily redirect them to their own profile pages. responses: - "418": - description: "TODO: A proper response needs to be added. It is not really a teapot | Copy response from indexed variant" + "200": + description: "" + content: + application/json: + schema: + $ref: components/schemas/UserObject.yaml#/UserObjectFull /api/me/*: get: tags: - shorthand - summary: Access your own profile's pages + summary: Access your own profile's sub-pages description: >- This shorthand is useful if you want to link to pages in your own account profile, but do not want (or have) the `userslug`. It is also especially useful as a means to instruct users on how to do things, as you can easily redirect them to their own profile pages. @@ -6187,7 +6270,8 @@ paths: get: tags: - users - summary: /api/user/{userslug}/categories + summary: Get user's watched categories + description: This route retrieves the list of categories and their watch states parameters: - name: userslug in: path @@ -6248,7 +6332,7 @@ paths: get: tags: - users - summary: /api/user/{userslug}/posts + summary: Get a user's posts parameters: - name: userslug in: path @@ -6294,7 +6378,7 @@ paths: get: tags: - users - summary: /api/user/{userslug}/topics + summary: Get a user's topics parameters: - name: userslug in: path @@ -6342,7 +6426,7 @@ paths: get: tags: - users - summary: /api/user/{userslug}/best + summary: Get a user's best performing topics parameters: - name: userslug in: path @@ -6388,7 +6472,7 @@ paths: get: tags: - users - summary: /api/user/{userslug}/groups + summary: Get user's groups parameters: - name: userslug in: path @@ -6421,7 +6505,7 @@ paths: get: tags: - users - summary: /api/user/{userslug}/bookmarks + summary: Get user's bookmarks parameters: - name: userslug in: path @@ -6467,7 +6551,7 @@ paths: get: tags: - users - summary: /api/user/{userslug}/watched + summary: Get user's watched topics parameters: - name: userslug in: path @@ -6675,7 +6759,7 @@ paths: get: tags: - users - summary: /api/user/{userslug}/ignored + summary: Get user's ignored topics parameters: - name: userslug in: path @@ -6723,7 +6807,7 @@ paths: get: tags: - users - summary: /api/user/{userslug}/upvoted + summary: Get user's upvoted posts parameters: - name: userslug in: path @@ -6775,7 +6859,7 @@ paths: get: tags: - users - summary: Get user's downvotes + summary: Get user's downvoted posts parameters: - name: userslug in: path @@ -6827,7 +6911,7 @@ paths: get: tags: - users - summary: /api/user/{userslug}/edit + summary: Get user profile for editing parameters: - name: userslug in: path @@ -6894,7 +6978,7 @@ paths: get: tags: - users - summary: /api/user/{userslug}/edit/username + summary: Get configs for username editing parameters: - name: userslug in: path @@ -6922,7 +7006,7 @@ paths: get: tags: - users - summary: /api/user/{userslug}/edit/email + summary: Get configs for email editing parameters: - name: userslug in: path @@ -6950,7 +7034,7 @@ paths: get: tags: - users - summary: /api/user/{userslug}/edit/password + summary: Get configs for password editing parameters: - name: userslug in: path @@ -6982,7 +7066,8 @@ paths: get: tags: - users - summary: /api/user/{userslug}/info + summary: Get user moderation info + description: Administrators and Global Moderators get access to the `/info` page, which shows some backend data that is useful from a moderation point-of-view (such as IP addresses, recent bans, moderation history, etc). parameters: - name: userslug in: path @@ -7163,7 +7248,7 @@ paths: get: tags: - users - summary: /api/user/{userslug}/settings + summary: Get user's settings parameters: - name: userslug in: path @@ -7228,6 +7313,8 @@ paths: type: string notificationType_new-reply: type: string + notificationType_post-edit: + type: string sendChatNotifications: nullable: true sendPostNotifications: @@ -7495,7 +7582,7 @@ paths: get: tags: - users - summary: /api/user/{userslug}/uploads + summary: Get user's uploads parameters: - name: userslug in: path @@ -7533,7 +7620,7 @@ paths: get: tags: - users - summary: /api/user/{userslug}/consent + summary: Get user's GDPR consent settings parameters: - name: userslug in: path @@ -7568,7 +7655,7 @@ paths: get: tags: - users - summary: /api/user/{userslug}/blocks + summary: Get user's blocks parameters: - name: userslug in: path @@ -7599,7 +7686,7 @@ paths: get: tags: - users - summary: /api/user/{userslug}/sessions + summary: Get user's active sessions parameters: - name: userslug in: path @@ -7667,7 +7754,7 @@ paths: get: tags: - notifications - summary: /api/notifications + summary: Get notifications responses: "200": description: "" @@ -8092,7 +8179,8 @@ paths: get: tags: - shorthand - summary: /api/chats/{roomid} + summary: Access a chat room + description: Redirects a request to the proper chat page URL parameters: - name: roomid in: path @@ -8108,114 +8196,11 @@ paths: schema: type: string description: A relative path to the canonical URL for that chat page - /api/users: - get: - tags: - - users - summary: /api/users - responses: - "200": - description: "" - content: - application/json: - schema: - allOf: - - type: object - properties: - users: - type: array - items: - type: object - properties: - uid: - type: number - description: A user identifier - username: - type: string - description: A friendly name for a given user account - userslug: - type: string - description: An URL-safe variant of the username (i.e. lower-cased, spaces - removed, etc.) - picture: - nullable: true - type: string - status: - type: string - postcount: - type: number - reputation: - type: number - email:confirmed: - type: number - description: Whether the user has confirmed their email address or not - lastonline: - type: number - flags: - nullable: true - banned: - type: number - banned:expire: - type: number - joindate: - type: number - description: A UNIX timestamp representing the moment the user's account was - created - icon:text: - type: string - description: A single-letter representation of a username. This is used in the - auto-generated icon given to users without an - avatar - icon:bgColor: - type: string - description: A six-character hexadecimal colour code assigned to the user. This - value is used in conjunction with `icon:text` - for the user's auto-generated icon - example: "#f44336" - joindateISO: - type: string - lastonlineISO: - type: string - banned_until: - type: number - banned_until_readable: - type: string - administrator: - type: boolean - userCount: - type: number - title: - type: string - isAdminOrGlobalMod: - type: boolean - isAdmin: - type: boolean - isGlobalMod: - type: boolean - displayUserSearch: - type: boolean - section_joindate: - type: boolean - maximumInvites: - type: number - inviteOnly: - type: boolean - adminInviteOnly: - type: boolean - invites: - type: number - showInviteButton: - type: boolean - reputation:disabled: - type: number - - $ref: components/schemas/Pagination.yaml#/Pagination - - $ref: components/schemas/Breadcrumbs.yaml#/Breadcrumbs - - $ref: components/schemas/CommonProps.yaml#/CommonProps /api/groups: get: tags: - groups - summary: /api/groups + summary: Get user groups responses: "200": description: "" @@ -8322,7 +8307,7 @@ paths: get: tags: - groups - summary: /api/groups/{slug} + summary: Get user group details parameters: - name: slug in: path diff --git a/public/src/modules/topicList.js b/public/src/modules/topicList.js index bd486d5adb..553eb3a18b 100644 --- a/public/src/modules/topicList.js +++ b/public/src/modules/topicList.js @@ -199,7 +199,11 @@ define('topicList', [ }); } var categoryEl = $(this); - var cid = $(this).attr('data-cid'); + var link = categoryEl.find('a').attr('href'); + if (link && link !== '#' && link.length) { + return; + } + var cid = categoryEl.attr('data-cid'); if (ev.ctrlKey) { selectChildren(cid, !categoryEl.find('[component="category/select/icon"]').hasClass('fa-check')); } diff --git a/src/categories/index.js b/src/categories/index.js index 661275e6c4..371f71a579 100644 --- a/src/categories/index.js +++ b/src/categories/index.js @@ -175,20 +175,16 @@ function calculateTopicPostCount(category) { return; } - var postCount = category.post_count; - var topicCount = category.topic_count; - if (!Array.isArray(category.children) || !category.children.length) { - category.totalPostCount = postCount; - category.totalTopicCount = topicCount; - return; + let postCount = category.post_count; + let topicCount = category.topic_count; + if (Array.isArray(category.children)) { + category.children.forEach(function (child) { + calculateTopicPostCount(child); + postCount += parseInt(child.totalPostCount, 10) || 0; + topicCount += parseInt(child.totalTopicCount, 10) || 0; + }); } - category.children.forEach(function (child) { - calculateTopicPostCount(child); - postCount += parseInt(child.totalPostCount, 10) || 0; - topicCount += parseInt(child.totalTopicCount, 10) || 0; - }); - category.totalPostCount = postCount; category.totalTopicCount = topicCount; } @@ -240,7 +236,7 @@ Categories.getChildrenCids = async function (rootCid) { } keys = childrenCids.map(cid => 'cid:' + cid + ':children'); childrenCids.forEach(cid => allCids.push(parseInt(cid, 10))); - recursive(keys); + await recursive(keys); } const key = 'cid:' + rootCid + ':children'; const childrenCids = cache.get(key); diff --git a/src/categories/recentreplies.js b/src/categories/recentreplies.js index 4ae423f6ac..3a7e37d383 100644 --- a/src/categories/recentreplies.js +++ b/src/categories/recentreplies.js @@ -62,12 +62,24 @@ module.exports = function (Categories) { } }; - Categories.getRecentTopicReplies = async function (categoryData, uid) { + Categories.getRecentTopicReplies = async function (categoryData, uid, query) { if (!Array.isArray(categoryData) || !categoryData.length) { return; } - const categoriesToLoad = categoryData.filter(category => category && category.numRecentReplies && parseInt(category.numRecentReplies, 10) > 0); - const keys = categoriesToLoad.map(category => 'cid:' + category.cid + ':recent_tids'); + const categoriesToLoad = categoryData.filter(c => c && c.numRecentReplies && parseInt(c.numRecentReplies, 10) > 0); + let keys = []; + if (plugins.hasListeners('filter:categories.getRecentTopicReplies')) { + const result = await plugins.fireHook('filter:categories.getRecentTopicReplies', { + categories: categoriesToLoad, + uid: uid, + query: query, + keys: [], + }); + keys = result.keys; + } else { + keys = categoriesToLoad.map(c => 'cid:' + c.cid + ':recent_tids'); + } + const results = await db.getSortedSetsMembers(keys); let tids = _.uniq(_.flatten(results).filter(Boolean)); diff --git a/src/controllers/accounts/settings.js b/src/controllers/accounts/settings.js index 34ba4cd7cc..bb4d744333 100644 --- a/src/controllers/accounts/settings.js +++ b/src/controllers/accounts/settings.js @@ -108,7 +108,7 @@ settingsController.get = async function (req, res, next) { userData.disableCustomUserSkins = meta.config.disableCustomUserSkins || 0; - userData.allowUserHomePage = meta.config.allowUserHomePage || 1; + userData.allowUserHomePage = meta.config.allowUserHomePage === 1 ? 1 : 0; userData.hideFullname = meta.config.hideFullname || 0; userData.hideEmail = meta.config.hideEmail || 0; diff --git a/src/controllers/categories.js b/src/controllers/categories.js index 22f4964a74..98dae31fa5 100644 --- a/src/controllers/categories.js +++ b/src/controllers/categories.js @@ -19,7 +19,7 @@ categoriesController.list = async function (req, res) { const categoryData = await categories.getCategoriesByPrivilege('categories:cid', req.uid, 'find'); const tree = categories.getTree(categoryData, 0); - await categories.getRecentTopicReplies(categoryData, req.uid); + await categories.getRecentTopicReplies(categoryData, req.uid, req.query); const data = { title: meta.config.homePageTitle || '[[pages:home]]', diff --git a/src/controllers/category.js b/src/controllers/category.js index 4edc7324d6..befc2621cb 100644 --- a/src/controllers/category.js +++ b/src/controllers/category.js @@ -88,7 +88,7 @@ categoryController.get = async function (req, res, next) { if (categoryData.children.length) { const allCategories = []; categories.flattenCategories(allCategories, categoryData.children); - await categories.getRecentTopicReplies(allCategories, req.uid); + await categories.getRecentTopicReplies(allCategories, req.uid, req.query); } categoryData.title = translator.escape(categoryData.name); diff --git a/src/controllers/helpers.js b/src/controllers/helpers.js index 11a677116d..8ce5811cf0 100644 --- a/src/controllers/helpers.js +++ b/src/controllers/helpers.js @@ -222,7 +222,7 @@ async function getCategoryData(cids, uid, selectedCid, states) { if (selectedCid && !Array.isArray(selectedCid)) { selectedCid = [selectedCid]; } - + selectedCid = selectedCid && selectedCid.map(String); states = states || [categories.watchStates.watching, categories.watchStates.notwatching]; const [allowed, watchState, categoryData, isAdmin] = await Promise.all([ @@ -286,7 +286,7 @@ function checkVisibleChildren(c, cidToAllowed, cidToWatchState, states) { if (!c || !Array.isArray(c.children)) { return false; } - return c.children.some(c => c && ( + return c.children.some(c => c && !c.disabled && ( (cidToAllowed[c.cid] && states.includes(cidToWatchState[c.cid])) || checkVisibleChildren(c, cidToAllowed, cidToWatchState, states) )); } diff --git a/src/database/mongo/sorted/add.js b/src/database/mongo/sorted/add.js index 6dc206b953..f576a27100 100644 --- a/src/database/mongo/sorted/add.js +++ b/src/database/mongo/sorted/add.js @@ -52,7 +52,8 @@ module.exports = function (module) { return; } const isArrayOfScores = Array.isArray(scores); - if (!isArrayOfScores && !utils.isNumber(scores)) { + if ((!isArrayOfScores && !utils.isNumber(scores)) || + (isArrayOfScores && scores.map(s => utils.isNumber(s)).includes(false))) { throw new Error('[[error:invalid-score, ' + scores + ']]'); } @@ -75,6 +76,9 @@ module.exports = function (module) { } var bulk = module.client.collection('objects').initializeUnorderedBulkOp(); data.forEach(function (item) { + if (!utils.isNumber(item[1])) { + throw new Error('[[error:invalid-score, ' + item[1] + ']]'); + } bulk.find({ _key: item[0], value: String(item[2]) }).upsert().updateOne({ $set: { score: parseFloat(item[1]) } }); }); await bulk.execute(); diff --git a/src/database/postgres/sorted/add.js b/src/database/postgres/sorted/add.js index 392482dbdd..32dc2f6325 100644 --- a/src/database/postgres/sorted/add.js +++ b/src/database/postgres/sorted/add.js @@ -69,7 +69,8 @@ DO UPDATE SET "score" = EXCLUDED."score"`, return; } const isArrayOfScores = Array.isArray(scores); - if (!isArrayOfScores && !utils.isNumber(scores)) { + if ((!isArrayOfScores && !utils.isNumber(scores)) || + (isArrayOfScores && scores.map(s => utils.isNumber(s)).includes(false))) { throw new Error('[[error:invalid-score, ' + scores + ']]'); } @@ -108,6 +109,9 @@ INSERT INTO "legacy_zset" ("_key", "value", "score") const values = []; const scores = []; data.forEach(function (item) { + if (!utils.isNumber(item[1])) { + throw new Error('[[error:invalid-score, ' + item[1] + ']]'); + } keys.push(item[0]); scores.push(item[1]); values.push(item[2]); diff --git a/src/database/redis/sorted/add.js b/src/database/redis/sorted/add.js index b4f28de949..f8d5c4bd8c 100644 --- a/src/database/redis/sorted/add.js +++ b/src/database/redis/sorted/add.js @@ -42,7 +42,8 @@ module.exports = function (module) { return; } const isArrayOfScores = Array.isArray(scores); - if (!isArrayOfScores && !utils.isNumber(scores)) { + if ((!isArrayOfScores && !utils.isNumber(scores)) || + (isArrayOfScores && scores.map(s => utils.isNumber(s)).includes(false))) { throw new Error('[[error:invalid-score, ' + scores + ']]'); } @@ -65,6 +66,9 @@ module.exports = function (module) { } var batch = module.client.batch(); data.forEach(function (item) { + if (!utils.isNumber(item[1])) { + throw new Error('[[error:invalid-score, ' + item[1] + ']]'); + } batch.zadd(item[0], item[1], item[2]); }); await helpers.execBatch(batch); diff --git a/src/notifications.js b/src/notifications.js index 2159a0da9d..537a23db97 100644 --- a/src/notifications.js +++ b/src/notifications.js @@ -21,6 +21,7 @@ Notifications.baseTypes = [ 'notificationType_upvote', 'notificationType_new-topic', 'notificationType_new-reply', + 'notificationType_post-edit', 'notificationType_follow', 'notificationType_new-chat', 'notificationType_group-invite', diff --git a/src/posts/edit.js b/src/posts/edit.js index fbe3590ada..b85f54e68b 100644 --- a/src/posts/edit.js +++ b/src/posts/edit.js @@ -52,6 +52,13 @@ module.exports = function (Posts) { postData.cid = topic.cid; postData.topic = topic; + + await topics.notifyFollowers(postData, data.uid, { + type: 'post-edit', + bodyShort: translator.compile('notifications:user_edited_post', editor.username, postData.topic.title), + nid: 'edit_post:' + postData.pid + ':uid:' + data.uid, + }); + plugins.fireHook('action:post.edit', { post: _.clone(postData), data: data, uid: data.uid }); require('./cache').del(String(postData.pid)); @@ -79,6 +86,7 @@ module.exports = function (Posts) { return { tid: tid, cid: topicData.cid, + title: validator.escape(String(topicData.title)), isMainPost: false, renamed: false, }; diff --git a/src/topics/create.js b/src/topics/create.js index 88ed0b244d..884446a549 100644 --- a/src/topics/create.js +++ b/src/topics/create.js @@ -13,6 +13,7 @@ var meta = require('../meta'); var posts = require('../posts'); var privileges = require('../privileges'); var categories = require('../categories'); +const translator = require('../translator'); module.exports = function (Topics) { Topics.create = async function (data) { @@ -180,7 +181,13 @@ module.exports = function (Topics) { user.setUserField(uid, 'lastonline', Date.now()); } - Topics.notifyFollowers(postData, uid); + Topics.notifyFollowers(postData, uid, { + type: 'new-reply', + bodyShort: translator.compile('notifications:user_posted_to', postData.user.username, postData.topic.title), + nid: 'new_post:tid:' + postData.topic.tid + ':pid:' + postData.pid + ':uid:' + uid, + mergeId: 'notifications:user_posted_to|' + postData.topic.tid, + }); + analytics.increment(['posts', 'posts:byCid:' + data.cid]); plugins.fireHook('action:topic.reply', { post: _.clone(postData), data: data }); diff --git a/src/topics/follow.js b/src/topics/follow.js index 872cb92ce9..4faaa40419 100644 --- a/src/topics/follow.js +++ b/src/topics/follow.js @@ -145,47 +145,37 @@ module.exports = function (Topics) { return tids.filter((tid, index) => tid && !scores[index]); }; - Topics.notifyFollowers = async function (postData, exceptUid) { - var title; - var titleEscaped; - + Topics.notifyFollowers = async function (postData, exceptUid, notifData) { + notifData = notifData || {}; let followers = await Topics.getFollowers(postData.topic.tid); - - var index = followers.indexOf(exceptUid.toString()); + const index = followers.indexOf(String(exceptUid)); if (index !== -1) { followers.splice(index, 1); } followers = await privileges.topics.filterUids('topics:read', postData.topic.tid, followers); - if (!followers.length) { return; } - title = postData.topic.title; + let title = postData.topic.title; if (title) { title = utils.decodeHTMLEntities(title); - titleEscaped = title.replace(/%/g, '%').replace(/,/g, ','); } postData.content = posts.relativeToAbsolute(postData.content, posts.urlRegex); postData.content = posts.relativeToAbsolute(postData.content, posts.imgRegex); const notification = await notifications.create({ - type: 'new-reply', subject: title, - bodyShort: '[[notifications:user_posted_to, ' + postData.user.username + ', ' + titleEscaped + ']]', bodyLong: postData.content, pid: postData.pid, path: '/post/' + postData.pid, - nid: 'new_post:tid:' + postData.topic.tid + ':pid:' + postData.pid + ':uid:' + exceptUid, tid: postData.topic.tid, from: exceptUid, - mergeId: 'notifications:user_posted_to|' + postData.topic.tid, topicTitle: title, + ...notifData, }); - if (notification) { - notifications.push(notification, followers); - } + notifications.push(notification, followers); }; }; diff --git a/src/topics/index.js b/src/topics/index.js index 30c645f458..6a63680d89 100644 --- a/src/topics/index.js +++ b/src/topics/index.js @@ -67,7 +67,7 @@ Topics.getTopicsByTids = async function (tids, options) { const uids = _.uniq(topics.map(t => t && t.uid && t.uid.toString()).filter(v => utils.isNumber(v))); const cids = _.uniq(topics.map(t => t && t.cid && t.cid.toString()).filter(v => utils.isNumber(v))); - const guestTopics = topics.filter(t => t.uid === 0); + const guestTopics = topics.filter(t => t && t.uid === 0); async function loadGuestHandles() { return await Promise.all(guestTopics.map(topic => posts.getPostField(topic.mainPid, 'handle'))); } diff --git a/src/topics/tags.js b/src/topics/tags.js index 192d5e8a26..88a8f3a69b 100644 --- a/src/topics/tags.js +++ b/src/topics/tags.js @@ -152,12 +152,15 @@ module.exports = function (Topics) { }; Topics.getTopicTags = async function (tid) { - return await db.getSetMembers('topic:' + tid + ':tags'); + const tags = await db.getSetMembers('topic:' + tid + ':tags'); + return tags.sort(); }; Topics.getTopicsTags = async function (tids) { const keys = tids.map(tid => 'topic:' + tid + ':tags'); - return await db.getSetsMembers(keys); + const tags = await db.getSetsMembers(keys); + tags.forEach(tags => tags.sort()); + return tags; }; Topics.getTopicTagsObjects = async function (tid) { @@ -192,6 +195,31 @@ module.exports = function (Topics) { return topicTags; }; + Topics.addTags = async function (tags, tids) { + const topicData = await Topics.getTopicsFields(tids, ['timestamp']); + const sets = tids.map(tid => 'topic:' + tid + ':tags'); + for (let i = 0; i < tags.length; i++) { + /* eslint-disable no-await-in-loop */ + await Promise.all([ + db.setsAdd(sets, tags[i]), + db.sortedSetAdd('tag:' + tags[i] + ':topics', topicData.map(t => t.timestamp), tids), + ]); + await updateTagCount(tags[i]); + } + }; + + Topics.removeTags = async function (tags, tids) { + const sets = tids.map(tid => 'topic:' + tid + ':tags'); + for (let i = 0; i < tags.length; i++) { + /* eslint-disable no-await-in-loop */ + await Promise.all([ + db.setsRemove(sets, tags[i]), + db.sortedSetRemove('tag:' + tags[i] + ':topics', tids), + ]); + await updateTagCount(tags[i]); + } + }; + Topics.updateTopicTags = async function (tid, tags) { await Topics.deleteTopicTags(tid); const timestamp = await Topics.getTopicField(tid, 'timestamp'); diff --git a/src/topics/tools.js b/src/topics/tools.js index 2fdd0d445c..269538599e 100644 --- a/src/topics/tools.js +++ b/src/topics/tools.js @@ -93,7 +93,8 @@ module.exports = function (Topics) { throw new Error('[[error:no-privileges]]'); } await Topics.setTopicField(tid, 'locked', lock ? 1 : 0); - topicData.isLocked = lock; + topicData.isLocked = lock; // deprecate in v2.0 + topicData.locked = lock; plugins.fireHook('action:topic.lock', { topic: _.clone(topicData), uid: uid }); return topicData; diff --git a/src/upgrades/1.13.3/fix_users_sorted_sets.js b/src/upgrades/1.13.3/fix_users_sorted_sets.js index 3a6b947fa4..9afd97915f 100644 --- a/src/upgrades/1.13.3/fix_users_sorted_sets.js +++ b/src/upgrades/1.13.3/fix_users_sorted_sets.js @@ -44,9 +44,9 @@ module.exports = { } totalUserCount += 1; await db.sortedSetAddBulk([ - ['users:joindate', userData.joindate, uids[index]], - ['users:reputation', userData.reputation, uids[index]], - ['users:postcount', userData.postcount, uids[index]], + ['users:joindate', userData.joindate || Date.now(), uids[index]], + ['users:reputation', userData.reputation || 0, uids[index]], + ['users:postcount', userData.postcount || 0, uids[index]], ]); if (userData.hasOwnProperty('flags') && parseInt(userData.flags, 10) > 0) { await db.sortedSetAdd('users:flags', userData.flags, uids[index]); diff --git a/src/user/blocks.js b/src/user/blocks.js index 0065fd52e4..7a8423e83e 100644 --- a/src/user/blocks.js +++ b/src/user/blocks.js @@ -5,6 +5,7 @@ const LRU = require('lru-cache'); const db = require('../database'); const pubsub = require('../pubsub'); +const plugins = require('../plugins'); module.exports = function (User) { User.blocks = { @@ -63,6 +64,7 @@ module.exports = function (User) { await User.incrementUserFieldBy(uid, 'blocksCount', 1); User.blocks._cache.del(parseInt(uid, 10)); pubsub.publish('user:blocks:cache:del', parseInt(uid, 10)); + plugins.fireHook('action:user.blocks.add', { uid: uid, targetUid: targetUid }); }; User.blocks.remove = async function (targetUid, uid) { @@ -71,6 +73,7 @@ module.exports = function (User) { await User.decrementUserFieldBy(uid, 'blocksCount', 1); User.blocks._cache.del(parseInt(uid, 10)); pubsub.publish('user:blocks:cache:del', parseInt(uid, 10)); + plugins.fireHook('action:user.blocks.remove', { uid: uid, targetUid: targetUid }); }; User.blocks.applyChecks = async function (type, targetUid, uid) { @@ -115,7 +118,8 @@ module.exports = function (User) { set = set.filter(function (item) { return !blockedSet.has(parseInt(isPlain ? item : item[property], 10)); }); + const data = await plugins.fireHook('filter:user.blocks.filter', { set: set, property: property, uid: uid, blockedSet: blockedSet }); - return set; + return data.set; }; }; diff --git a/test/categories.js b/test/categories.js index a0d3f778d2..242abd9294 100644 --- a/test/categories.js +++ b/test/categories.js @@ -109,7 +109,7 @@ describe('Categories', function () { uid: 0, }, function (err, categoryData) { assert.ifError(err); - Categories.getRecentTopicReplies(categoryData, 0, function (err) { + Categories.getRecentTopicReplies(categoryData, 0, {}, function (err) { assert.ifError(err); done(); }); @@ -910,4 +910,18 @@ describe('Categories', function () { }); }); }); + + it('should return nested children categories', async function () { + const rootCategory = await Categories.create({ name: 'root' }); + const child1 = await Categories.create({ name: 'child1', parentCid: rootCategory.cid }); + const child2 = await Categories.create({ name: 'child2', parentCid: child1.cid }); + const data = await Categories.getCategoryById({ + uid: 1, + cid: rootCategory.cid, + start: 0, + stop: 19, + }); + assert.strictEqual(child1.cid, data.children[0].cid); + assert.strictEqual(child2.cid, data.children[0].children[0].cid); + }); }); diff --git a/test/database/sorted.js b/test/database/sorted.js index 73cc7714e2..922c70a9cb 100644 --- a/test/database/sorted.js +++ b/test/database/sorted.js @@ -118,6 +118,18 @@ describe('Sorted Set methods', function () { done(); }); }); + + it('should error if scores has null', async function () { + let err; + try { + await db.sortedSetsAdd(['sorted1', 'sorted2'], [1, null], 'dontadd'); + } catch (_err) { + err = _err; + } + assert.equal(err.message, '[[error:invalid-score, 1,]]'); + assert.strictEqual(await db.isSortedSetMember('sorted1', 'dontadd'), false); + assert.strictEqual(await db.isSortedSetMember('sorted2', 'dontadd'), false); + }); }); describe('sortedSetAddMulti()', function () { @@ -146,6 +158,21 @@ describe('Sorted Set methods', function () { done(); }); }); + + it('should error if score is null', async function () { + let err; + try { + await db.sortedSetAddBulk([ + ['bulk4', 0, 'dontadd'], + ['bulk5', null, 'dontadd'], + ]); + } catch (_err) { + err = _err; + } + assert.equal(err.message, '[[error:invalid-score, null]]'); + assert.strictEqual(await db.isSortedSetMember('bulk4', 'dontadd'), false); + assert.strictEqual(await db.isSortedSetMember('bulk5', 'dontadd'), false); + }); }); describe('getSortedSetRange()', function () { diff --git a/test/topics.js b/test/topics.js index 682ce72025..80380b5a3d 100644 --- a/test/topics.js +++ b/test/topics.js @@ -1753,6 +1753,19 @@ describe('Topic\'s', function () { }); }); }); + + it('should add and remove tags from topics properly', async () => { + const result = await topics.post({ uid: adminUid, tags: ['tag4', 'tag2', 'tag1', 'tag3'], title: 'tag topic', content: 'topic 1 content', cid: topic.categoryId }); + const tid = result.topicData.tid; + let tags = await topics.getTopicTags(tid); + assert.deepStrictEqual(tags, ['tag1', 'tag2', 'tag3', 'tag4']); + await topics.addTags(['tag7', 'tag6', 'tag5'], [tid]); + tags = await topics.getTopicTags(tid); + assert.deepStrictEqual(tags, ['tag1', 'tag2', 'tag3', 'tag4', 'tag5', 'tag6', 'tag7']); + await topics.removeTags(['tag1', 'tag3', 'tag5', 'tag7'], [tid]); + tags = await topics.getTopicTags(tid); + assert.deepStrictEqual(tags, ['tag2', 'tag4', 'tag6']); + }); }); describe('follow/unfollow', function () {