Compare commits

..

31 Commits

Author SHA1 Message Date
Misty Release Bot
6976925943 chore: incrementing version number - v2.8.7 2023-03-01 15:51:30 +00:00
Barış Soner Uşaklı
791551098c fix: display 25 topics on category feed 2023-02-27 09:47:28 -05:00
Barış Soner Uşaklı
ec58700f6d fix: object destructuring overwriting type parameter
also check for valid types in generateExport
2023-02-27 09:10:53 -05:00
Barış Soner Uşaklı
8cf4a6f62e fix: alert on page load 2023-02-24 13:40:37 -05:00
Eldor
3bd9a87154 fix: show error alert if password change fails 2023-02-22 09:06:51 -05:00
gasoved
edd2fc38fc fix: update main post timestamp when rescheduling 2023-02-16 14:10:07 -05:00
Julian Lam
1b29dbb69d test: add dummy emailer hook in authentication test 2023-02-13 12:15:45 -05:00
Julian Lam
40e7b86da9 docs: update openapi spec to include info about passing in timestamps for topic creation, removing timestamp as valid request param for topic replying 2023-02-13 11:44:54 -05:00
Barış Soner Uşaklı
326b92687f fix: show admins/globalmods if content is purged 2023-02-08 17:35:38 -05:00
Barış Soner Uşaklı
e335d0f601 fix: email expiry timestamps
emailConfirmExpiry is hours and default is 24
2023-02-08 13:22:16 -05:00
Barış Soner Uşaklı
845c8013b6 fix: #11259, clean old emails when updating via admin (#11260)
when admin is changing users emails check if its avaiable and remove old email of user first
upgrade script to cleanup email:uid, email:sorted, will remove entries if user doesn't exist or doesn't have email or if entry in user hash doesn't match entry in email:uid
fix missing ! in email interstitial
fix missing await in canSendValidation,
fix broken tests
dont pass sessionId to email.remove if admin is changing/removing email
2023-02-06 10:45:01 -05:00
Julian Lam
7a5bcc2171 fix: #11257, onSuccessfulLogin called with improper uid 2023-02-03 16:01:31 -05:00
Misty Release Bot
f3306d038a chore: update changelog for v2.8.6 2023-02-03 16:39:40 +00:00
Misty Release Bot
76732140f3 chore: incrementing version number - v2.8.6 2023-02-03 16:39:40 +00:00
Barış Soner Uşaklı
bf92ee0e5f feat: add sitemap filter hooks for categories/topic pages 2023-02-03 09:46:02 -05:00
Misty Release Bot
8335f90ae0 chore(i18n): fallback strings for new resources: nodebb.error 2023-02-02 13:37:19 -05:00
nesro
202378b939 fix: #11254, return check for reroll property 2023-02-02 09:15:04 -05:00
Barış Soner Uşaklı
705cd13ad3 fix: closes #11249, notification uses displayname 2023-01-31 17:27:25 -05:00
Julian Lam
b5598a6e5d fix: wrong link to topics in acp dashboard 2023-01-30 15:05:57 -05:00
Barış Soner Uşaklı
c241baf641 feat: closes #11241, add missing error lang keys 2023-01-30 12:40:24 -05:00
Barış Soner Uşaklı
d68352cce5 lint: remove unused 2023-01-30 12:35:08 -05:00
Barış Soner Uşaklı
0713482bd4 feat: #11240, only show relevant users in flags assignee list
for user flags-> admins + all users who have `admin:users` privilege
for post flags -> admins + global mods + moderators of the category the post is in
refactor getModeratorUids function so it can be used for different privileges
2023-01-30 12:26:08 -05:00
Barış Soner Uşaklı
1d3c0e5a2b fix: https://github.com/NodeBB/NodeBB/issues/11239
parseInt uid received from req.body._uid or req.query._uid
2023-01-30 11:48:10 -05:00
Barış Soner Uşaklı
6d819b056e fix: notif filter selecte field 2023-01-30 10:43:02 -05:00
Misty Release Bot
24e58c2895 chore: update changelog for v2.8.5 2023-01-27 14:35:25 +00:00
Misty Release Bot
93ccf604db chore: incrementing version number - v2.8.5 2023-01-27 14:35:24 +00:00
Peter Jaszkowiak
f6c96948fe fix: import resolution within plugin modules (#11219)
use module.exports = require('..')
export * from '..' didn't work in some cases
2023-01-27 09:17:38 -05:00
Misty Release Bot
c13f0e2128 chore: update changelog for v2.8.4 2023-01-26 14:38:07 +00:00
Misty Release Bot
b9553613ab chore: incrementing version number - v2.8.4 2023-01-26 14:38:06 +00:00
Barış Soner Uşaklı
c3653bee60 Revert "fix: import resolution within plugin modules (#11200)"
This reverts commit 89e059a084.
2023-01-26 09:27:16 -05:00
Misty Release Bot
eb2841eed3 chore: update changelog for v2.8.3 2023-01-25 19:37:34 +00:00
76 changed files with 598 additions and 86 deletions

View File

@@ -1,3 +1,170 @@
#### v2.8.6 (2023-02-03)
##### Chores
* **i18n:** fallback strings for new resources: nodebb.error (8335f90a)
* incrementing version number - v2.8.5 (bff5ce2d)
* update changelog for v2.8.5 (24e58c28)
* incrementing version number - v2.8.4 (a46b2bbc)
* incrementing version number - v2.8.3 (c20b20a7)
* incrementing version number - v2.8.2 (050e43f8)
* incrementing version number - v2.8.1 (727f879e)
* incrementing version number - v2.8.0 (8e77673d)
* incrementing version number - v2.7.0 (96cc0617)
* incrementing version number - v2.6.1 (7e52a7a5)
* incrementing version number - v2.6.0 (e7fcf482)
* incrementing version number - v2.5.8 (dec0e7de)
* incrementing version number - v2.5.7 (5836bf4a)
* incrementing version number - v2.5.6 (c7bd7dbf)
* incrementing version number - v2.5.5 (3509ed94)
* incrementing version number - v2.5.4 (e83260ca)
* incrementing version number - v2.5.3 (7e922936)
* incrementing version number - v2.5.2 (babcd17e)
* incrementing version number - v2.5.1 (ce3aa950)
* incrementing version number - v2.5.0 (01d276cb)
* incrementing version number - v2.4.5 (dd3e1a28)
* incrementing version number - v2.4.4 (d5525c87)
* incrementing version number - v2.4.3 (9c647c6c)
* incrementing version number - v2.4.2 (3aa7b855)
* incrementing version number - v2.4.1 (60cbd148)
* incrementing version number - v2.4.0 (4834cde3)
* incrementing version number - v2.3.1 (d2425942)
* incrementing version number - v2.3.0 (046ea120)
##### New Features
* add sitemap filter hooks for categories/topic pages (bf92ee0e)
* closes #11241, add missing error lang keys (c241baf6)
* #11240, only show relevant users in flags assignee list (0713482b)
##### Bug Fixes
* #11254, return check for reroll property (202378b9)
* closes #11249, notification uses displayname (705cd13a)
* wrong link to topics in acp dashboard (b5598a6e)
* https://github.com/NodeBB/NodeBB/issues/11239 (1d3c0e5a)
* notif filter selecte field (6d819b05)
##### Other Changes
* remove unused (d68352cc)
#### v2.8.5 (2023-01-27)
##### Chores
* incrementing version number - v2.8.4 (a46b2bbc)
* update changelog for v2.8.4 (c13f0e21)
* incrementing version number - v2.8.3 (c20b20a7)
* incrementing version number - v2.8.2 (050e43f8)
* incrementing version number - v2.8.1 (727f879e)
* incrementing version number - v2.8.0 (8e77673d)
* incrementing version number - v2.7.0 (96cc0617)
* incrementing version number - v2.6.1 (7e52a7a5)
* incrementing version number - v2.6.0 (e7fcf482)
* incrementing version number - v2.5.8 (dec0e7de)
* incrementing version number - v2.5.7 (5836bf4a)
* incrementing version number - v2.5.6 (c7bd7dbf)
* incrementing version number - v2.5.5 (3509ed94)
* incrementing version number - v2.5.4 (e83260ca)
* incrementing version number - v2.5.3 (7e922936)
* incrementing version number - v2.5.2 (babcd17e)
* incrementing version number - v2.5.1 (ce3aa950)
* incrementing version number - v2.5.0 (01d276cb)
* incrementing version number - v2.4.5 (dd3e1a28)
* incrementing version number - v2.4.4 (d5525c87)
* incrementing version number - v2.4.3 (9c647c6c)
* incrementing version number - v2.4.2 (3aa7b855)
* incrementing version number - v2.4.1 (60cbd148)
* incrementing version number - v2.4.0 (4834cde3)
* incrementing version number - v2.3.1 (d2425942)
* incrementing version number - v2.3.0 (046ea120)
##### Bug Fixes
* import resolution within plugin modules (#11219) (f6c96948)
#### v2.8.4 (2023-01-26)
##### Chores
* incrementing version number - v2.8.3 (c20b20a7)
* update changelog for v2.8.3 (eb2841ee)
* incrementing version number - v2.8.2 (050e43f8)
* incrementing version number - v2.8.1 (727f879e)
* incrementing version number - v2.8.0 (8e77673d)
* incrementing version number - v2.7.0 (96cc0617)
* incrementing version number - v2.6.1 (7e52a7a5)
* incrementing version number - v2.6.0 (e7fcf482)
* incrementing version number - v2.5.8 (dec0e7de)
* incrementing version number - v2.5.7 (5836bf4a)
* incrementing version number - v2.5.6 (c7bd7dbf)
* incrementing version number - v2.5.5 (3509ed94)
* incrementing version number - v2.5.4 (e83260ca)
* incrementing version number - v2.5.3 (7e922936)
* incrementing version number - v2.5.2 (babcd17e)
* incrementing version number - v2.5.1 (ce3aa950)
* incrementing version number - v2.5.0 (01d276cb)
* incrementing version number - v2.4.5 (dd3e1a28)
* incrementing version number - v2.4.4 (d5525c87)
* incrementing version number - v2.4.3 (9c647c6c)
* incrementing version number - v2.4.2 (3aa7b855)
* incrementing version number - v2.4.1 (60cbd148)
* incrementing version number - v2.4.0 (4834cde3)
* incrementing version number - v2.3.1 (d2425942)
* incrementing version number - v2.3.0 (046ea120)
#### v2.8.3 (2023-01-25)
##### Chores
* remove extraneous lines from changelog (48c9f447)
* incrementing version number - v2.8.2 (050e43f8)
* update changelog for v2.8.2 (66aa3169)
* incrementing version number - v2.8.1 (727f879e)
* incrementing version number - v2.8.0 (8e77673d)
* incrementing version number - v2.7.0 (96cc0617)
* incrementing version number - v2.6.1 (7e52a7a5)
* incrementing version number - v2.6.0 (e7fcf482)
* incrementing version number - v2.5.8 (dec0e7de)
* incrementing version number - v2.5.7 (5836bf4a)
* incrementing version number - v2.5.6 (c7bd7dbf)
* incrementing version number - v2.5.5 (3509ed94)
* incrementing version number - v2.5.4 (e83260ca)
* incrementing version number - v2.5.3 (7e922936)
* incrementing version number - v2.5.2 (babcd17e)
* incrementing version number - v2.5.1 (ce3aa950)
* incrementing version number - v2.5.0 (01d276cb)
* incrementing version number - v2.4.5 (dd3e1a28)
* incrementing version number - v2.4.4 (d5525c87)
* incrementing version number - v2.4.3 (9c647c6c)
* incrementing version number - v2.4.2 (3aa7b855)
* incrementing version number - v2.4.1 (60cbd148)
* incrementing version number - v2.4.0 (4834cde3)
* incrementing version number - v2.3.1 (d2425942)
* incrementing version number - v2.3.0 (046ea120)
##### Bug Fixes
* import resolution within plugin modules (#11200) (89e059a0)
* #11195, allow users with admin:users privilege to delete users in acp (0bffd3d9)
* #11194, allow access to sub dashboard pages (7d04e952)
* #11136, tests, and returning the proper number of arrays (459bc523)
* #11136, only show mods of active categories when getModeratorUids is called (39e009c0)
* closes #11173, clear require cache if wrong dependency is installed (747cb1f0)
* **deps:**
* downgrade swagger-parser to v9 (00e48803)
* pinning sub dependency json-schema-ref-parser to 9.0.9 (9c250b78)
##### Reverts
* a788bd1344825ad4759e39d6e98d8bf3695bd639 (fecd84d1)
* 9c250b78b05ca2abf31a79971ed0c60ca07664ec, fix: comment out broken test for now (a788bd13)
##### Tests
* fix broken test (f295174e)
#### v2.8.2 (2023-01-13)
##### Chores

View File

@@ -2,7 +2,7 @@
"name": "nodebb",
"license": "GPL-3.0",
"description": "NodeBB Forum",
"version": "2.8.3",
"version": "2.8.7",
"homepage": "http://www.nodebb.org",
"repository": {
"type": "git",

View File

@@ -62,6 +62,7 @@
"no-user": "اسم مستخدم غير موجود",
"no-teaser": "مقتطف غير موجود",
"no-flag": "Flag does not exist",
"no-chat-room": "Chat room does not exist",
"no-privileges": "لاتملك الصلاحيات اللازمة للقيام بهذه العملية",
"category-disabled": "قائمة معطلة",
"topic-locked": "الموضوع مقفول",
@@ -156,6 +157,9 @@
"chat-deleted-already": "This chat message has already been deleted.",
"chat-restored-already": "This chat message has already been restored.",
"chat-room-does-not-exist": "Chat room does not exist.",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "لقد شاركت بالتصويت ، ألا تذكر؟",
"reputation-system-disabled": "نظام السمعة معطل",
"downvoting-disabled": "التصويتات السلبية معطلة",

View File

@@ -62,6 +62,7 @@
"no-user": "Потребителят не съществува",
"no-teaser": "Резюмето не съществува",
"no-flag": "Докладът не съществува",
"no-chat-room": "Chat room does not exist",
"no-privileges": "Нямате достатъчно права за това действие.",
"category-disabled": "Категорията е изключена",
"topic-locked": "Темата е заключена",
@@ -156,6 +157,9 @@
"chat-deleted-already": "Това съобщение вече е изтрито.",
"chat-restored-already": "Това съобщение вече е възстановено.",
"chat-room-does-not-exist": "Стаята за разговори не съществува.",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "Вече сте дали глас за тази публикация.",
"reputation-system-disabled": "Системата за репутация е изключена.",
"downvoting-disabled": "Отрицателното гласуване е изключено",

View File

@@ -62,6 +62,7 @@
"no-user": "এই নামে কোন সদস্য নেই",
"no-teaser": "টিজারটি খুজে পাওয়া যায় নি",
"no-flag": "Flag does not exist",
"no-chat-room": "Chat room does not exist",
"no-privileges": "এই কাজটির জন্য আপনার পর্যাপ্ত অধিকার নেই",
"category-disabled": "বিভাগটি নিষ্ক্রিয়",
"topic-locked": "টপিক বন্ধ",
@@ -156,6 +157,9 @@
"chat-deleted-already": "This chat message has already been deleted.",
"chat-restored-already": "This chat message has already been restored.",
"chat-room-does-not-exist": "Chat room does not exist.",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "You have already voted for this post.",
"reputation-system-disabled": "সম্মাননা ব্যাবস্থা নিস্ক্রীয় রাখা হয়েছে",
"downvoting-disabled": "ঋণাত্মক ভোট নিস্ক্রীয় রাখা হয়েছে।",

View File

@@ -62,6 +62,7 @@
"no-user": "Uživatel neexistuje",
"no-teaser": "Chyták neexistuje",
"no-flag": "Flag does not exist",
"no-chat-room": "Chat room does not exist",
"no-privileges": "Na tuto akci nemáte dostatečné oprávnění.",
"category-disabled": "Kategorie zakázána",
"topic-locked": "Téma uzamknuto",
@@ -156,6 +157,9 @@
"chat-deleted-already": "Tato konverzační zpráva již byla odstraněna.",
"chat-restored-already": "Tato konverzační zpráva již byla obnovena.",
"chat-room-does-not-exist": "Chat room does not exist.",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "Již jste v tomto příspěvku hlasoval.",
"reputation-system-disabled": "Systém reputací je zakázán.",
"downvoting-disabled": "Systém nesouhlasu je zakázán",

View File

@@ -62,6 +62,7 @@
"no-user": "Brugeren eksisterer ikke",
"no-teaser": "Teaser eksisterer ikke",
"no-flag": "Flag does not exist",
"no-chat-room": "Chat room does not exist",
"no-privileges": "Du har ikke nok rettigheder til at udføre denne handling",
"category-disabled": "Kategorien er deaktiveret",
"topic-locked": "Tråden er låst",
@@ -156,6 +157,9 @@
"chat-deleted-already": "This chat message has already been deleted.",
"chat-restored-already": "This chat message has already been restored.",
"chat-room-does-not-exist": "Chat room does not exist.",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "You have already voted for this post.",
"reputation-system-disabled": "Vurderingssystem er slået fra.",
"downvoting-disabled": "Nedvurdering er slået fra",

View File

@@ -62,6 +62,7 @@
"no-user": "Der Benutzer existiert nicht",
"no-teaser": "Zusammenfassung existiert nicht",
"no-flag": "Markierung existiert nicht",
"no-chat-room": "Chat room does not exist",
"no-privileges": "Du verfügst nicht über ausreichende Berechtigungen, um die Aktion durchzuführen.",
"category-disabled": "Kategorie ist deaktiviert",
"topic-locked": "Thema ist gesperrt",
@@ -156,6 +157,9 @@
"chat-deleted-already": "Diese Chatnachricht wurde bereits gelöscht.",
"chat-restored-already": "Diese Chatnachricht wurde bereits wiederhergestellt.",
"chat-room-does-not-exist": "Der Chatraum existiert nicht.",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "Du hast diesen Beitrag bereits bewertet.",
"reputation-system-disabled": "Das Reputationssystem ist deaktiviert.",
"downvoting-disabled": "Downvotes sind deaktiviert.",

View File

@@ -62,6 +62,7 @@
"no-user": "User does not exist",
"no-teaser": "Teaser does not exist",
"no-flag": "Flag does not exist",
"no-chat-room": "Chat room does not exist",
"no-privileges": "You do not have enough privileges for this action.",
"category-disabled": "Η κατηγορία έχει απενεργοποιηθεί",
"topic-locked": "Το θέμα έχει κλειδωθεί",
@@ -156,6 +157,9 @@
"chat-deleted-already": "This chat message has already been deleted.",
"chat-restored-already": "This chat message has already been restored.",
"chat-room-does-not-exist": "Chat room does not exist.",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "You have already voted for this post.",
"reputation-system-disabled": "Το σύστημα φήμης έχει απενεργοποιηθεί.",
"downvoting-disabled": "Η καταψήφιση έχει απενεργοποιηθεί",

View File

@@ -70,6 +70,7 @@
"no-user": "User does not exist",
"no-teaser": "Teaser does not exist",
"no-flag": "Flag does not exist",
"no-chat-room": "Chat room does not exist",
"no-privileges": "You do not have enough privileges for this action.",
"category-disabled": "Category disabled",
@@ -182,6 +183,9 @@
"chat-deleted-already": "This chat message has already been deleted.",
"chat-restored-already": "This chat message has already been restored.",
"chat-room-does-not-exist": "Chat room does not exist.",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "You have already voted for this post.",
"reputation-system-disabled": "Reputation system is disabled.",

View File

@@ -62,6 +62,7 @@
"no-user": "User does not exist",
"no-teaser": "Teaser does not exist",
"no-flag": "Flag does not exist",
"no-chat-room": "Chat room does not exist",
"no-privileges": "You do not have enough privileges for this action.",
"category-disabled": "Category disabled",
"topic-locked": "Topic Locked",
@@ -156,6 +157,9 @@
"chat-deleted-already": "This chat message has already been deleted.",
"chat-restored-already": "This chat message has already been restored.",
"chat-room-does-not-exist": "Chat room does not exist.",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "You have already voted for this post.",
"reputation-system-disabled": "Reputation system is disabled.",
"downvoting-disabled": "Downvoting is disabled",

View File

@@ -62,6 +62,7 @@
"no-user": "User does not exist",
"no-teaser": "Teaser does not exist",
"no-flag": "Flag does not exist",
"no-chat-room": "Chat room does not exist",
"no-privileges": "You do not have enough privileges for this action.",
"category-disabled": "Category disabled",
"topic-locked": "Topic Locked",
@@ -156,6 +157,9 @@
"chat-deleted-already": "This chat message has already been deleted.",
"chat-restored-already": "This chat message has already been restored.",
"chat-room-does-not-exist": "Chat room does not exist.",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "You have already voted for this post.",
"reputation-system-disabled": "Reputation system is disabled.",
"downvoting-disabled": "Downvoting is disabled",

View File

@@ -62,6 +62,7 @@
"no-user": "El usuario no existe",
"no-teaser": "El resumen no existe",
"no-flag": "Flag does not exist",
"no-chat-room": "Chat room does not exist",
"no-privileges": "No tienes suficientes privilegios para realizar esta acción.",
"category-disabled": "Categoría deshabilitada",
"topic-locked": "Tema bloqueado",
@@ -156,6 +157,9 @@
"chat-deleted-already": "Este mensaje de chat ya ha sido borrado.",
"chat-restored-already": "Este mensaje de chat ya ha sido restaurado.",
"chat-room-does-not-exist": "Chat room does not exist.",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "Ya has votado a este mensaje.",
"reputation-system-disabled": "El sistema de reputación está deshabilitado.",
"downvoting-disabled": "La votación negativa está deshabilitada.",

View File

@@ -62,6 +62,7 @@
"no-user": "Kasutajat ei eksisteeri",
"no-teaser": "Eelvaadet ei eksisteeri",
"no-flag": "Flag does not exist",
"no-chat-room": "Chat room does not exist",
"no-privileges": "Sul pole piisavalt õigusi.",
"category-disabled": "Kategooria keelatud",
"topic-locked": "Teema lukustatud",
@@ -156,6 +157,9 @@
"chat-deleted-already": "This chat message has already been deleted.",
"chat-restored-already": "This chat message has already been restored.",
"chat-room-does-not-exist": "Chat room does not exist.",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "Sa oled juba hääletanud sellel postitusel.",
"reputation-system-disabled": "Reputatsiooni süsteem ei ole aktiveeritud",
"downvoting-disabled": "Negatiivsete häälte andmine ei ole võimaldatud",

View File

@@ -62,6 +62,7 @@
"no-user": "کاربر وجود ندارد",
"no-teaser": "تیزر وجود ندارد",
"no-flag": "Flag does not exist",
"no-chat-room": "Chat room does not exist",
"no-privileges": "شما دسترسی کافی برای این کار را ندارید",
"category-disabled": "دسته غیر‌فعال شد.",
"topic-locked": "موضوع بسته شد.",
@@ -156,6 +157,9 @@
"chat-deleted-already": "این پیام قبلا حذف شده است",
"chat-restored-already": "This chat message has already been restored.",
"chat-room-does-not-exist": "Chat room does not exist.",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "شما قبلا به این پست رای داده اید.",
"reputation-system-disabled": "سیستم اعتبار غیر فعال شده است",
"downvoting-disabled": "رأی منفی غیر فعال شده است",

View File

@@ -62,6 +62,7 @@
"no-user": "Käyttäjää ei ole olemassa",
"no-teaser": "Teaser does not exist",
"no-flag": "Flag does not exist",
"no-chat-room": "Chat room does not exist",
"no-privileges": "Oikeutesi eivät riitä toiminnon suorittamiseen.",
"category-disabled": "Kategoria ei ole käytössä",
"topic-locked": "Aihe lukittu",
@@ -156,6 +157,9 @@
"chat-deleted-already": "This chat message has already been deleted.",
"chat-restored-already": "This chat message has already been restored.",
"chat-room-does-not-exist": "Chat room does not exist.",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "You have already voted for this post.",
"reputation-system-disabled": "Reputation system is disabled.",
"downvoting-disabled": "Downvoting is disabled",

View File

@@ -62,6 +62,7 @@
"no-user": "Cet utilisateur n'existe pas",
"no-teaser": "Laperçu n'existe pas",
"no-flag": "Le signalement n'existe pas",
"no-chat-room": "Chat room does not exist",
"no-privileges": "Vous n'avez pas les privilèges nécessaires pour effectuer cette action.",
"category-disabled": "Catégorie désactivée",
"topic-locked": "Sujet verrouillé",
@@ -156,6 +157,9 @@
"chat-deleted-already": "Ce message a déjà été supprimé.",
"chat-restored-already": "Ce message de discussion a déjà été restauré.",
"chat-room-does-not-exist": "Le salon de discussion n'existe pas.",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "Vous avez déjà voté pour ce message.",
"reputation-system-disabled": "Le système de réputation est désactivé",
"downvoting-disabled": "Les votes négatifs ne sont pas autorisés",

View File

@@ -62,6 +62,7 @@
"no-user": "O usuario non existe",
"no-teaser": "A vista previa do tema non existe",
"no-flag": "Flag does not exist",
"no-chat-room": "Chat room does not exist",
"no-privileges": "Non tes privilexios dabondo para ver este tema.",
"category-disabled": "Categoría deshabilitada",
"topic-locked": "Tema Pechado",
@@ -156,6 +157,9 @@
"chat-deleted-already": "This chat message has already been deleted.",
"chat-restored-already": "This chat message has already been restored.",
"chat-room-does-not-exist": "Chat room does not exist.",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "Xa votache esta mensaxe.",
"reputation-system-disabled": "O sistema de reputación está deshabilitado",
"downvoting-disabled": "Os votos negativos están deshabilitados",

View File

@@ -62,6 +62,7 @@
"no-user": "משתמש אינו קיים",
"no-teaser": "תקציר אינו קיים",
"no-flag": "דיווח לא קיים",
"no-chat-room": "Chat room does not exist",
"no-privileges": "ההרשאות שלכם אינן מספיקות לביצוע פעולה זו.",
"category-disabled": "קטגוריה לא פעילה",
"topic-locked": "נושא נעול",
@@ -156,6 +157,9 @@
"chat-deleted-already": "הודעת צ'אט זו כבר נמחקה.",
"chat-restored-already": "הודעת צ'אט זו כבר שוחזרה.",
"chat-room-does-not-exist": "חדר צ'אט אינו קיים.",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "הצבעתם כבר בנושא זה.",
"reputation-system-disabled": "מערכת המוניטין לא פעילה.",
"downvoting-disabled": "היכולת להצביע נגד מושבתת",

View File

@@ -62,6 +62,7 @@
"no-user": "Korisnik ne postoji",
"no-teaser": "Zadirkivač ne postoji",
"no-flag": "Flag does not exist",
"no-chat-room": "Chat room does not exist",
"no-privileges": "Nemate privilegije za ovu radnju.",
"category-disabled": "Kategorija onemogućena",
"topic-locked": "Tema zaključana",
@@ -156,6 +157,9 @@
"chat-deleted-already": "This chat message has already been deleted.",
"chat-restored-already": "This chat message has already been restored.",
"chat-room-does-not-exist": "Chat room does not exist.",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "Već ste glasali za ovu objavu",
"reputation-system-disabled": "Sistem reputacije onemogućen.",
"downvoting-disabled": "Oduzimanje glasova je onemogućeno",

View File

@@ -62,6 +62,7 @@
"no-user": "Nem létező felhasználó",
"no-teaser": "A bevezető nem létezik",
"no-flag": "Flag does not exist",
"no-chat-room": "Chat room does not exist",
"no-privileges": "Nincs elég jogod ehhez a művelethez.",
"category-disabled": "Kategória kikapcsolva",
"topic-locked": "Téma lezárva",
@@ -156,6 +157,9 @@
"chat-deleted-already": "Ez az üzenet már törölve lett.",
"chat-restored-already": "Ez az üzenet már vissza van állítva.",
"chat-room-does-not-exist": "Csevegő szoba nem létezik.",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "Már szavaztál erre a hozzászólásra.",
"reputation-system-disabled": "Hírnév funkció kikapcsolva.",
"downvoting-disabled": "Leszavazás funkció kikapcsolva",

View File

@@ -62,6 +62,7 @@
"no-user": "Օգտվողը գոյություն չունի",
"no-teaser": "Թիզերը գոյություն չունի",
"no-flag": "Դրոշ գոյություն չունի",
"no-chat-room": "Chat room does not exist",
"no-privileges": "Դուք չունեք բավարար արտոնություններ այս գործողության համար:",
"category-disabled": "Կատեգորիան անջատված է",
"topic-locked": "Թեման փակված է",
@@ -156,6 +157,9 @@
"chat-deleted-already": "Այս զրույցի հաղորդագրությունն արդեն ջնջված է",
"chat-restored-already": "This chat message has already been restored.",
"chat-room-does-not-exist": "Այս զրուցարանը գոյություն չունի:",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "Դուք արդեն քվեարկել եք այս գրառման օգտին:",
"reputation-system-disabled": "Վարկանիշի համակարգը անջատված է:",
"downvoting-disabled": "Դեմ քվեարկությունն անջատված է",

View File

@@ -62,6 +62,7 @@
"no-user": "Pengguna tidak ditemukan",
"no-teaser": "Teaser tidak ditemukan",
"no-flag": "Flag does not exist",
"no-chat-room": "Chat room does not exist",
"no-privileges": "Kamu tidak punya cukup izin untuk melakukan ini",
"category-disabled": "Kategori ditiadakan",
"topic-locked": "Topik dikunci",
@@ -156,6 +157,9 @@
"chat-deleted-already": "This chat message has already been deleted.",
"chat-restored-already": "This chat message has already been restored.",
"chat-room-does-not-exist": "Chat room does not exist.",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "You have already voted for this post.",
"reputation-system-disabled": "Sistem reputasi ditiadakan.",
"downvoting-disabled": "Downvoting ditiadakan",

View File

@@ -62,6 +62,7 @@
"no-user": "L'Utente non esiste",
"no-teaser": "Teaser non esiste",
"no-flag": "Segnalazione non esiste",
"no-chat-room": "Chat room does not exist",
"no-privileges": "Non hai abbastanza privilegi per questa azione.",
"category-disabled": "Categoria disabilitata",
"topic-locked": "Discussione Bloccata",
@@ -156,6 +157,9 @@
"chat-deleted-already": "Il messaggio è già stato eliminato.",
"chat-restored-already": "Questo messaggio della chat è già stato ripristinato.",
"chat-room-does-not-exist": "La stanza chat non esiste.",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "Hai già votato per questo post",
"reputation-system-disabled": "Il sistema di reputazione è disabilitato.",
"downvoting-disabled": "Votata negativamente è disabilitato",

View File

@@ -62,6 +62,7 @@
"no-user": "ユーザーは存在しません",
"no-teaser": "ティーザーが存在しません",
"no-flag": "Flag does not exist",
"no-chat-room": "Chat room does not exist",
"no-privileges": "あなたがこの行為する権利がありません。",
"category-disabled": "この板は無効された",
"topic-locked": "スレッドがロックされた",
@@ -156,6 +157,9 @@
"chat-deleted-already": "このチャットメッセージは既に削除されています",
"chat-restored-already": "このチャットメッセージは既に削除されています",
"chat-room-does-not-exist": "Chat room does not exist.",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "あなたはすでにこの投稿を評価しました。",
"reputation-system-disabled": "Reputation system is disabled.",
"downvoting-disabled": "Downvoting is disabled",

View File

@@ -62,6 +62,7 @@
"no-user": "존재하지 않는 사용자입니다.",
"no-teaser": "존재하지 않는 미리보기입니다.",
"no-flag": "Flag does not exist",
"no-chat-room": "Chat room does not exist",
"no-privileges": "이 작업을 할 수 있는 권한이 없습니다.",
"category-disabled": "카테고리가 비활성화 되었습니다.",
"topic-locked": "게시물이 잠금 상태입니다.",
@@ -156,6 +157,9 @@
"chat-deleted-already": "이미 삭제된 채팅 메시지입니다.",
"chat-restored-already": "이 채팅 메시지는 이미 복원되었습니다.",
"chat-room-does-not-exist": "채팅이 존재하지 않습니다.",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "이미 이 포스트에 투표하셨습니다.",
"reputation-system-disabled": "인지도 시스템이 비활성화되어있습니다.",
"downvoting-disabled": "비추천 기능이 비활성 상태입니다.",

View File

@@ -62,6 +62,7 @@
"no-user": "Tokio vartotojo nėra",
"no-teaser": "Anonsas neegzistuoja",
"no-flag": "Flag does not exist",
"no-chat-room": "Chat room does not exist",
"no-privileges": "Šiam veiksmui jūs neturite pakankamų privilegijų.",
"category-disabled": "Kategorija išjungta",
"topic-locked": "Tema užrakinta",
@@ -156,6 +157,9 @@
"chat-deleted-already": "Ši žinutė buvo pašalinta",
"chat-restored-already": "This chat message has already been restored.",
"chat-room-does-not-exist": "Chat room does not exist.",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "Jūs jau balsavote už šį pranešimą.",
"reputation-system-disabled": "Reputacijos sistema išjungta.",
"downvoting-disabled": "Downvoting yra išjungtas",

View File

@@ -62,6 +62,7 @@
"no-user": "Lietotājs nav atrasts",
"no-teaser": "Ievadapraksts nav atrasts",
"no-flag": "Flag does not exist",
"no-chat-room": "Chat room does not exist",
"no-privileges": "Tev nepietiek tiesības šai darbībai.",
"category-disabled": "Kategorija ir atspējota",
"topic-locked": "Temats ir slēgts",
@@ -156,6 +157,9 @@
"chat-deleted-already": "Saruna jau ir izdzēsta.",
"chat-restored-already": "This chat message has already been restored.",
"chat-room-does-not-exist": "Chat room does not exist.",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "Tu jau balsoji par šo rakstu.",
"reputation-system-disabled": "Ranga punktu sistēma ir atspējota.",
"downvoting-disabled": "Balsošana \"pret\" ir atspējota",

View File

@@ -62,6 +62,7 @@
"no-user": "Pengguna tidak wujud",
"no-teaser": "Pengusik tidak wujud",
"no-flag": "Flag does not exist",
"no-chat-room": "Chat room does not exist",
"no-privileges": "Anda tidak mempunyai cukup keistimewaan untuk perbuatan ini.",
"category-disabled": "Kategori dilumpuhkan",
"topic-locked": "Topik Dikunci",
@@ -156,6 +157,9 @@
"chat-deleted-already": "This chat message has already been deleted.",
"chat-restored-already": "This chat message has already been restored.",
"chat-room-does-not-exist": "Chat room does not exist.",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "You have already voted for this post.",
"reputation-system-disabled": "Sistem reputasi dilumpuhkan.",
"downvoting-disabled": "Undi turun dilumpuhkan",

View File

@@ -62,6 +62,7 @@
"no-user": "Bruker eksisterer ikke",
"no-teaser": "Teaseren eksisterer ikke",
"no-flag": "Flag does not exist",
"no-chat-room": "Chat room does not exist",
"no-privileges": "Du har ikke nok rettigheter til å utføre denne handlingen.",
"category-disabled": "Kategori deaktivert",
"topic-locked": "Emne låst",
@@ -156,6 +157,9 @@
"chat-deleted-already": "Denne meldingen har allerede blitt slettet.",
"chat-restored-already": "Denne meldingen har allerede blitt gjenopprettet.",
"chat-room-does-not-exist": "Dette chatterommet finnes ikke.",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "Du har allerede stemt på dette innlegget",
"reputation-system-disabled": "Omdømmesystemet er deaktivert.",
"downvoting-disabled": "Nedstemming er deaktivert",

View File

@@ -62,6 +62,7 @@
"no-user": "Gebruiker bestaat niet",
"no-teaser": "Dit voorproefje bestaat niet",
"no-flag": "Flag does not exist",
"no-chat-room": "Chat room does not exist",
"no-privileges": "Onvoldoende rechten om deze actie uit te voeren",
"category-disabled": "Categorie uitgeschakeld",
"topic-locked": "Onderwerp gesloten",
@@ -156,6 +157,9 @@
"chat-deleted-already": "Dit chat bericht is al verwijderd.",
"chat-restored-already": "Dit chat bericht is al hersteld.",
"chat-room-does-not-exist": "Chat room does not exist.",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "Je hebt al gestemd voor deze post.",
"reputation-system-disabled": "Reputatie systeem is uitgeschakeld.",
"downvoting-disabled": "Negatief stemmen is uitgeschakeld",

View File

@@ -62,6 +62,7 @@
"no-user": "Użytkownik nie istnieje",
"no-teaser": "Zwiastun nie istnieje",
"no-flag": "Flag does not exist",
"no-chat-room": "Chat room does not exist",
"no-privileges": "Nie masz przywileju wykonywania tej akcji",
"category-disabled": "Kategoria wyłączona.",
"topic-locked": "Temat zablokowany",
@@ -156,6 +157,9 @@
"chat-deleted-already": "Ten komunikat czatu jest już skasowany",
"chat-restored-already": "Ta wiadomość została już przywrócona",
"chat-room-does-not-exist": "Chat room does not exist.",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "Już zagłosowałeś na ten post",
"reputation-system-disabled": "System reputacji jest wyłączony.",
"downvoting-disabled": "Negatywna ocena postów jest wyłączona",

View File

@@ -62,6 +62,7 @@
"no-user": "O usuário não existe",
"no-teaser": "O teaser não existe",
"no-flag": "Flag does not exist",
"no-chat-room": "Chat room does not exist",
"no-privileges": "Você não possui privilégios suficientes para esta ação.",
"category-disabled": "Categoria desativada",
"topic-locked": "Tópico Trancado",
@@ -156,6 +157,9 @@
"chat-deleted-already": "Essa mensagem de chat já foi deletada",
"chat-restored-already": "Essa mensagem de chat já foi restaurada.",
"chat-room-does-not-exist": "A sala de chat não existe.",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "Você já votou neste post.",
"reputation-system-disabled": "O sistema de reputação está desabilitado.",
"downvoting-disabled": "Negativação está desabilitada",

View File

@@ -62,6 +62,7 @@
"no-user": "Utilizador não existente",
"no-teaser": "Não existe pré-visualização",
"no-flag": "Flag does not exist",
"no-chat-room": "Chat room does not exist",
"no-privileges": "Não possuis privilégios suficientes para esta ação.",
"category-disabled": "Categoria desativada",
"topic-locked": "Tópico bloqueado",
@@ -156,6 +157,9 @@
"chat-deleted-already": "Esta mensagem já foi apagada.",
"chat-restored-already": "Esta mensagem já foi restaurada.",
"chat-room-does-not-exist": "Chat room does not exist.",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "Já votaste nesta publicação.",
"reputation-system-disabled": "O sistema de reputação está desativado.",
"downvoting-disabled": "Os votos negativos estão desativados",

View File

@@ -62,6 +62,7 @@
"no-user": "Utilizatorul nu exista.",
"no-teaser": "Teaser does not exist",
"no-flag": "Flag does not exist",
"no-chat-room": "Chat room does not exist",
"no-privileges": "You do not have enough privileges for this action.",
"category-disabled": "Categorie dezactivată",
"topic-locked": "Subiect Închis",
@@ -156,6 +157,9 @@
"chat-deleted-already": "This chat message has already been deleted.",
"chat-restored-already": "This chat message has already been restored.",
"chat-room-does-not-exist": "Chat room does not exist.",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "You have already voted for this post.",
"reputation-system-disabled": "Sistemul de reputație este dezactivat.",
"downvoting-disabled": "Votarea negativă este dezactivată",

View File

@@ -62,6 +62,7 @@
"no-user": "Такого пользователя не существует",
"no-teaser": "Такого тизера не существует",
"no-flag": "Flag does not exist",
"no-chat-room": "Chat room does not exist",
"no-privileges": "У вас недостаточно прав для этого действия.",
"category-disabled": "Категория отключена",
"topic-locked": "Тема закрыта",
@@ -156,6 +157,9 @@
"chat-deleted-already": "Это сообщение чата уже удалено.",
"chat-restored-already": "Это сообщение чата уже было восстановлено.",
"chat-room-does-not-exist": "Комната чата не существует.",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "Вы уже проголосовали за это сообщение.",
"reputation-system-disabled": "Система репутации отключена.",
"downvoting-disabled": "Понижение рейтинга отключено",

View File

@@ -62,6 +62,7 @@
"no-user": "Umuntu utabaho",
"no-teaser": "Inshamake itabaho",
"no-flag": "Flag does not exist",
"no-chat-room": "Chat room does not exist",
"no-privileges": "Ntabwo uragira uburenganzira buhagije ngo wemererwe iki gikorwa",
"category-disabled": "Icyiciro cyabujijwe",
"topic-locked": "Ikiganiro Cyafungiranywe",
@@ -156,6 +157,9 @@
"chat-deleted-already": "This chat message has already been deleted.",
"chat-restored-already": "This chat message has already been restored.",
"chat-room-does-not-exist": "Chat room does not exist.",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "You have already voted for this post.",
"reputation-system-disabled": "Ibijyanye n'itangwa ry'amanota ntibyemerewe. ",
"downvoting-disabled": "Kwambura amanota ntibyemerewe",

View File

@@ -62,6 +62,7 @@
"no-user": "User does not exist",
"no-teaser": "Teaser does not exist",
"no-flag": "Flag does not exist",
"no-chat-room": "Chat room does not exist",
"no-privileges": "You do not have enough privileges for this action.",
"category-disabled": "Category disabled",
"topic-locked": "Topic Locked",
@@ -156,6 +157,9 @@
"chat-deleted-already": "This chat message has already been deleted.",
"chat-restored-already": "This chat message has already been restored.",
"chat-room-does-not-exist": "Chat room does not exist.",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "You have already voted for this post.",
"reputation-system-disabled": "Reputation system is disabled.",
"downvoting-disabled": "Downvoting is disabled",

View File

@@ -62,6 +62,7 @@
"no-user": "Užívateľ neexistuje",
"no-teaser": "Ukážka neexistuje",
"no-flag": "Flag does not exist",
"no-chat-room": "Chat room does not exist",
"no-privileges": "Na túto akciu nemáte dostatočné oprávnenia.",
"category-disabled": "Kategória je zablokovaná",
"topic-locked": "Téma je uzamknutá",
@@ -156,6 +157,9 @@
"chat-deleted-already": "Táto správa konverzácie už bola odstránená.",
"chat-restored-already": "This chat message has already been restored.",
"chat-room-does-not-exist": "Chat room does not exist.",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "Za tento príspevok ste už hlasovali.",
"reputation-system-disabled": "Systém reputácie je zablokovaný.",
"downvoting-disabled": "Hlasovanie proti je zablokované",

View File

@@ -62,6 +62,7 @@
"no-user": "Uporabnik ne obstaja.",
"no-teaser": "Predogled ne obstaja.",
"no-flag": "Flag does not exist",
"no-chat-room": "Chat room does not exist",
"no-privileges": "Nimate dovolj pravic za to dejanje.",
"category-disabled": "Kategorija je onemogočena.",
"topic-locked": "Tema je zaklenjena.",
@@ -156,6 +157,9 @@
"chat-deleted-already": "This chat message has already been deleted.",
"chat-restored-already": "This chat message has already been restored.",
"chat-room-does-not-exist": "Chat room does not exist.",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "Za to objavo ste že glasovali.",
"reputation-system-disabled": "Sistem za ugled je onemogočen.",
"downvoting-disabled": "Negativno glasovanje je onemogočeno.",

View File

@@ -62,6 +62,7 @@
"no-user": "Përdoruesi nuk ekziston",
"no-teaser": "Përmbledhja nuk ekziston",
"no-flag": "Flag does not exist",
"no-chat-room": "Chat room does not exist",
"no-privileges": "Nuk keni akses të mjaftueshem për këtë veprim.",
"category-disabled": "Kategori e çaktivizuar",
"topic-locked": "Temë e kyçur",
@@ -156,6 +157,9 @@
"chat-deleted-already": "Ky mesazh është fshirë tashmë.",
"chat-restored-already": "Ky mesazh është rikthyer tashmë.",
"chat-room-does-not-exist": "Kjo dhomë bisede nuk ekziston.",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "Ju keni votuar tashmë për këtë postim.",
"reputation-system-disabled": "Sistemi i reputacionit është i çaktivizuar.",
"downvoting-disabled": "Votimi kundër është i çaktivizuar",

View File

@@ -62,6 +62,7 @@
"no-user": "Корисник не постоји",
"no-teaser": "Исечак не постоји",
"no-flag": "Заставица не постоји",
"no-chat-room": "Chat room does not exist",
"no-privileges": "Немате довољне привилегије за обављање ове радње.",
"category-disabled": "Категорија је онемогућена",
"topic-locked": "Тема је закључана",
@@ -156,6 +157,9 @@
"chat-deleted-already": "Ова порука ћаскања је већ избрисана.",
"chat-restored-already": "Ова порука ћаскања је већ обновљена.",
"chat-room-does-not-exist": "Соба за ћаскање не постоји.",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "Већ сте гласали за ову поруку.",
"reputation-system-disabled": "Угледи су онемогућени.",
"downvoting-disabled": "Негативно гласање је онемогућено",

View File

@@ -62,6 +62,7 @@
"no-user": "Användaren finns inte",
"no-teaser": "Förhandsvisningen finns inte",
"no-flag": "Flag does not exist",
"no-chat-room": "Chat room does not exist",
"no-privileges": "Du har inte tillräckliga rättigheter för den här åtgärden.",
"category-disabled": "Kategorin inaktiverad",
"topic-locked": "Ämnet låst",
@@ -156,6 +157,9 @@
"chat-deleted-already": "Detta chattmeddelande har redan raderats.",
"chat-restored-already": "This chat message has already been restored.",
"chat-room-does-not-exist": "Chat room does not exist.",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "Du har redan röstat på det här inlägget.",
"reputation-system-disabled": "Ryktessystemet är inaktiverat.",
"downvoting-disabled": "Nedröstning är inaktiverat",

View File

@@ -62,6 +62,7 @@
"no-user": "ยังไม่มีผู้ใช้งานนี้",
"no-teaser": "ยังไม่มีทีเซอร์นี้",
"no-flag": "Flag does not exist",
"no-chat-room": "Chat room does not exist",
"no-privileges": "คุณมีสิทธิ์ไม่เพียงพอที่จะทำรายการนี้",
"category-disabled": "Category นี้ถูกปิดการใช้งานแล้ว",
"topic-locked": "กระทู้ถูกล็อก",
@@ -156,6 +157,9 @@
"chat-deleted-already": "This chat message has already been deleted.",
"chat-restored-already": "This chat message has already been restored.",
"chat-room-does-not-exist": "Chat room does not exist.",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "คุณได้โหวตโพสต์นี้แล้ว",
"reputation-system-disabled": "ระบบชื่อเสียงถูกปิดใช้งาน",
"downvoting-disabled": "\"การโหวตลง\" ถูกปิดใช้งาน",

View File

@@ -62,6 +62,7 @@
"no-user": "Kullanıcı Yok",
"no-teaser": "İleti Yok",
"no-flag": "Şikayet Yok",
"no-chat-room": "Chat room does not exist",
"no-privileges": "Bu işlemi yapmak için yeterli yetkiniz yok.",
"category-disabled": "Kategori aktif değil",
"topic-locked": "Başlık Kilitli",
@@ -156,6 +157,9 @@
"chat-deleted-already": "Bu sohbet mesajı zaten silinmiş.",
"chat-restored-already": "Bu sohbet mesajı zaten geri yüklendi.",
"chat-room-does-not-exist": "Sohbet Odası Mevcut Değil",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "Bu gönderi için zaten oy verdin.",
"reputation-system-disabled": "İtibar sistemi devre dışı.",
"downvoting-disabled": "Eksi oylama devre dışı bırakılmış. ",

View File

@@ -62,6 +62,7 @@
"no-user": "Користувач не існує",
"no-teaser": "Тизер не існує",
"no-flag": "Flag does not exist",
"no-chat-room": "Chat room does not exist",
"no-privileges": "У вас недостатньо повноважень для цієї дії. ",
"category-disabled": "Категорію відключено",
"topic-locked": "Тему заблоковано",
@@ -156,6 +157,9 @@
"chat-deleted-already": "Це повідомлення чату вже було видалено.",
"chat-restored-already": "Це чат повідомлення вже було відновлене",
"chat-room-does-not-exist": "Chat room does not exist.",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "Ви вже проголосували за цей пост.",
"reputation-system-disabled": "Система репутацій вимкнена.",
"downvoting-disabled": "Голосування проти вимкнено",

View File

@@ -62,6 +62,7 @@
"no-user": "Người dùng không tồn tại",
"no-teaser": "Đoạn giới thiệu không tồn tại",
"no-flag": "Cờ không tồn tại",
"no-chat-room": "Chat room does not exist",
"no-privileges": "Bạn không đủ quyền để thực thi hành động này",
"category-disabled": "Chuyên mục bị khóa",
"topic-locked": "Chủ đề bị khóa",
@@ -156,6 +157,9 @@
"chat-deleted-already": "Cuộc trò chuyện này đã được xóa.",
"chat-restored-already": "Tin nhắn trò chuyện này đã được khôi phục.",
"chat-room-does-not-exist": "Phòng trò chuyện không tồn tại.",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "Bạn đã bỏ phiếu cho bài viết này",
"reputation-system-disabled": "Hệ thống đánh giá uy tính đã bị vô hiệu hóa.",
"downvoting-disabled": "Phản đối đã bị tắt",

View File

@@ -62,6 +62,7 @@
"no-user": "用户不存在",
"no-teaser": "主题预览不存在",
"no-flag": "Flag does not exist",
"no-chat-room": "Chat room does not exist",
"no-privileges": "您没有权限执行此操作。",
"category-disabled": "版块已禁用",
"topic-locked": "主题已锁定",
@@ -156,6 +157,9 @@
"chat-deleted-already": "聊天消息已经被删除",
"chat-restored-already": "此聊天消息已经恢复。\n",
"chat-room-does-not-exist": "聊天室不存在。",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "您已为此帖回复投过票了。",
"reputation-system-disabled": "声望系统已禁用。",
"downvoting-disabled": "踩已被禁用",

View File

@@ -62,6 +62,7 @@
"no-user": "使用者不存在",
"no-teaser": "主題預覽不存在",
"no-flag": "Flag does not exist",
"no-chat-room": "Chat room does not exist",
"no-privileges": "您的權限不足以執行此操作。",
"category-disabled": "版面已停用",
"topic-locked": "主題已鎖定",
@@ -156,6 +157,9 @@
"chat-deleted-already": "聊天訊息已經被刪除",
"chat-restored-already": "此聊天訊息已經恢復。",
"chat-room-does-not-exist": "Chat room does not exist.",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "您已讚過此貼文回覆了。",
"reputation-system-disabled": "聲望系統已停用。",
"downvoting-disabled": "倒讚已被停用",

View File

@@ -19,6 +19,15 @@ post:
content:
type: string
example: This is the test topic's content
timestamp:
type: number
description: |
A UNIX timestamp of the topic's creation date (i.e. when it will be posted).
Specifically, this value can only be set to a value in the future if the calling user has the `topics:schedule` privilege for the passed-in category.
Otherwise, the current date and time are always assumed.
In some scenarios (e.g. forum migrations), you may want to backdate topics and posts.
Please see [this Developer FAQ topic](https://community.nodebb.org/topic/16983/how-can-i-backdate-topics-and-posts-for-migration-purposes) for more information.
example: 556084800000
tags:
type: array
items:

View File

@@ -46,8 +46,6 @@ post:
content:
type: string
example: This is a test reply
timestamp:
type: number
toPid:
type: number
required:

View File

@@ -9,15 +9,23 @@ define('admin/settings/email', ['ace/ace', 'alerts', 'admin/settings'], function
configureEmailTester();
configureEmailEditor();
handleDigestHourChange();
handleSmtpServiceChange();
$(window).on('action:admin.settingsLoaded action:admin.settingsSaved', handleDigestHourChange);
$(window).on('action:admin.settingsSaved', function () {
socket.emit('admin.user.restartJobs');
});
$('[id="email:smtpTransport:service"]').change(handleSmtpServiceChange);
$(window).off('action:admin.settingsLoaded', onSettingsLoaded)
.on('action:admin.settingsLoaded', onSettingsLoaded);
$(window).off('action:admin.settingsSaved', onSettingsSaved)
.on('action:admin.settingsSaved', onSettingsSaved);
};
function onSettingsLoaded() {
handleDigestHourChange();
handleSmtpServiceChange();
}
function onSettingsSaved() {
handleDigestHourChange();
socket.emit('admin.user.restartJobs');
}
function configureEmailTester() {
$('button[data-action="email.test"]').off('click').on('click', function () {
socket.emit('admin.email.test', { template: $('#test-email').val() }, function (err) {
@@ -106,20 +114,26 @@ define('admin/settings/email', ['ace/ace', 'alerts', 'admin/settings'], function
}
function handleSmtpServiceChange() {
const isCustom = $('[id="email:smtpTransport:service"]').val() === 'nodebb-custom-smtp';
$('[id="email:smtpTransport:custom-service"]')[isCustom ? 'slideDown' : 'slideUp'](isCustom);
const enabledEl = document.getElementById('email:smtpTransport:enabled');
if (enabledEl) {
if (!enabledEl.checked) {
enabledEl.closest('label').classList.toggle('is-checked', true);
enabledEl.checked = true;
alerts.alert({
message: '[[admin/settings/email:smtp-transport.auto-enable-toast]]',
timeout: 5000,
});
}
function toggleCustomService() {
const isCustom = $('[id="email:smtpTransport:service"]').val() === 'nodebb-custom-smtp';
$('[id="email:smtpTransport:custom-service"]')[isCustom ? 'slideDown' : 'slideUp'](isCustom);
}
toggleCustomService();
$('[id="email:smtpTransport:service"]').change(function () {
toggleCustomService();
const enabledEl = document.getElementById('email:smtpTransport:enabled');
if (enabledEl) {
if (!enabledEl.checked) {
$('label[for="email:smtpTransport:enabled"]').toggleClass('is-checked', true);
enabledEl.checked = true;
alerts.alert({
message: '[[admin/settings/email:smtp-transport.auto-enable-toast]]',
timeout: 5000,
});
}
}
});
}
return module;

View File

@@ -77,6 +77,7 @@ define('forum/account/edit/password', [
ajaxify.go('user/' + ajaxify.data.userslug + '/edit');
}
})
.catch(alerts.error)
.finally(() => {
btn.removeClass('disabled').find('i').addClass('hide');
currentPassword.val('');

View File

@@ -443,6 +443,10 @@ usersAPI.changePicture = async (caller, data) => {
};
usersAPI.generateExport = async (caller, { uid, type }) => {
const validTypes = ['profile', 'posts', 'uploads'];
if (!validTypes.includes(type)) {
throw new Error('[[error:invalid-data]]');
}
const count = await db.incrObjectField('locks', `export:${uid}${type}`);
if (count > 1) {
throw new Error('[[error:already-exporting]]');

View File

@@ -5,7 +5,6 @@ const _ = require('lodash');
const db = require('../database');
const user = require('../user');
const groups = require('../groups');
const plugins = require('../plugins');
const privileges = require('../privileges');
const cache = require('../cache');
@@ -99,39 +98,7 @@ Categories.getModerators = async function (cid) {
};
Categories.getModeratorUids = async function (cids) {
// Only check active categories
const disabled = (await Categories.getCategoriesFields(cids, ['disabled'])).map(obj => obj.disabled);
// cids = cids.filter((_, idx) => !disabled[idx]);
const groupNames = cids.reduce((memo, cid) => {
memo.push(`cid:${cid}:privileges:moderate`);
memo.push(`cid:${cid}:privileges:groups:moderate`);
return memo;
}, []);
const memberSets = await groups.getMembersOfGroups(groupNames);
// Every other set is actually a list of user groups, not uids, so convert those to members
const sets = memberSets.reduce((memo, set, idx) => {
if (idx % 2) {
memo.groupNames.push(set);
} else {
memo.uids.push(set);
}
return memo;
}, { groupNames: [], uids: [] });
const uniqGroups = _.uniq(_.flatten(sets.groupNames));
const groupUids = await groups.getMembersOfGroups(uniqGroups);
const map = _.zipObject(uniqGroups, groupUids);
const moderatorUids = cids.map((cid, index) => {
if (disabled[index]) {
return [];
}
return _.uniq(sets.uids[index].concat(_.flatten(sets.groupNames[index].map(g => map[g]))));
});
return moderatorUids;
return await privileges.categories.getUidsWithPrivilege(cids, 'moderate');
};
Categories.getCategories = async function (cids, uid) {

View File

@@ -45,10 +45,11 @@ notificationsController.get = async function (req, res, next) {
{ separator: true },
]).concat(filters.moderatorFilters);
}
const selectedFilter = allFilters.find((filterData) => {
allFilters.forEach((filterData) => {
filterData.selected = filterData.filter === filter;
return filterData.selected;
});
const selectedFilter = allFilters.find(filterData => filterData.selected);
if (!selectedFilter) {
return next();
}

View File

@@ -339,7 +339,7 @@ authenticationController.doLogin = async function (req, uid) {
return;
}
const loginAsync = util.promisify(req.login).bind(req);
await loginAsync({ uid: uid }, { keepSessionInfo: req.res.locals !== false });
await loginAsync({ uid: uid }, { keepSessionInfo: req.res.locals.reroll !== false });
await authenticationController.onSuccessfulLogin(req, uid);
};

View File

@@ -1,6 +1,9 @@
'use strict';
const _ = require('lodash');
const user = require('../user');
const groups = require('../groups');
const posts = require('../posts');
const flags = require('../flags');
const analytics = require('../analytics');
@@ -110,7 +113,6 @@ modsController.flags.detail = async function (req, res, next) {
isAdminOrGlobalMod: user.isAdminOrGlobalMod(req.uid),
moderatedCids: user.getModeratedCids(req.uid),
flagData: flags.get(req.params.flagId),
assignees: user.getAdminsandGlobalModsandModerators(),
privileges: Promise.all(['global', 'admin'].map(async type => privileges[type].get(req.uid))),
});
results.privileges = { ...results.privileges[0], ...results.privileges[1] };
@@ -119,6 +121,28 @@ modsController.flags.detail = async function (req, res, next) {
return next(); // 404
}
async function getAssignees(flagData) {
let uids = [];
const [admins, globalMods] = await Promise.all([
groups.getMembers('administrators', 0, -1),
groups.getMembers('Global Moderators', 0, -1),
]);
if (flagData.type === 'user') {
uids = await privileges.admin.getUidsWithPrivilege('admin:users');
uids = _.uniq(admins.concat(uids));
} else if (flagData.type === 'post') {
const cid = await posts.getCidByPid(flagData.targetId);
uids = _.uniq(admins.concat(globalMods));
if (cid) {
const modUids = (await privileges.categories.getUidsWithPrivilege([cid], 'moderate'))[0];
uids = _.uniq(uids.concat(modUids));
}
}
const userData = await user.getUsersData(uids);
return userData.filter(u => u && u.userslug);
}
const assignees = await getAssignees(results.flagData);
results.flagData.history = results.isAdminOrGlobalMod ? (await flags.getHistory(req.params.flagId)) : null;
if (results.flagData.type === 'user') {
@@ -128,7 +152,7 @@ modsController.flags.detail = async function (req, res, next) {
}
res.render('flags/detail', Object.assign(results.flagData, {
assignees: results.assignees,
assignees: assignees,
type_bool: ['post', 'user', 'empty'].reduce((memo, cur) => {
if (cur !== 'empty') {
memo[cur] = results.flagData.type === cur && (

View File

@@ -57,7 +57,7 @@ async function linkModules() {
// Instead of copying file, create a new file re-exporting it
// This way, imports in modules are resolved correctly
await fs.promises.writeFile(destPath, `export * from '${relPath}'`);
await fs.promises.writeFile(destPath, `module.exports = require('${relPath}');`);
}
}));
}

View File

@@ -37,7 +37,7 @@ module.exports = function (middleware) {
const loginAsync = util.promisify(req.login).bind(req);
await loginAsync(user, { keepSessionInfo: true });
await controllers.authentication.onSuccessfulLogin(req, user.uid);
req.uid = user.uid;
req.uid = parseInt(user.uid, 10);
req.loggedIn = req.uid > 0;
return true;
}

View File

@@ -211,3 +211,8 @@ privsAdmin.groupPrivileges = async function (groupName) {
const groupPrivilegeList = await privsAdmin.getGroupPrivilegeList();
return await helpers.userOrGroupPrivileges(0, groupName, groupPrivilegeList);
};
privsAdmin.getUidsWithPrivilege = async function (privilege) {
const uidsByCid = await helpers.getUidsWithPrivilege([0], privilege);
return uidsByCid[0];
};

View File

@@ -218,3 +218,7 @@ privsCategories.groupPrivileges = async function (cid, groupName) {
const groupPrivilegeList = await privsCategories.getGroupPrivilegeList();
return await helpers.userOrGroupPrivileges(cid, groupName, groupPrivilegeList);
};
privsCategories.getUidsWithPrivilege = async function (cids, privilege) {
return await helpers.getUidsWithPrivilege(cids, privilege);
};

View File

@@ -134,3 +134,8 @@ privsGlobal.groupPrivileges = async function (groupName) {
const groupPrivilegeList = await privsGlobal.getGroupPrivilegeList();
return await helpers.userOrGroupPrivileges(0, groupName, groupPrivilegeList);
};
privsGlobal.getUidsWithPrivilege = async function (privilege) {
const uidsByCid = await helpers.getUidsWithPrivilege([0], privilege);
return uidsByCid[0];
};

View File

@@ -6,6 +6,7 @@ const validator = require('validator');
const groups = require('../groups');
const user = require('../user');
const categories = require('../categories');
const plugins = require('../plugins');
const translator = require('../translator');
@@ -189,4 +190,38 @@ helpers.userOrGroupPrivileges = async function (cid, uidOrGroup, privilegeList)
return _.zipObject(privilegeList, isMembers);
};
helpers.getUidsWithPrivilege = async (cids, privilege) => {
const disabled = (await categories.getCategoriesFields(cids, ['disabled'])).map(obj => obj.disabled);
const groupNames = cids.reduce((memo, cid) => {
memo.push(`cid:${cid}:privileges:${privilege}`);
memo.push(`cid:${cid}:privileges:groups:${privilege}`);
return memo;
}, []);
const memberSets = await groups.getMembersOfGroups(groupNames);
// Every other set is actually a list of user groups, not uids, so convert those to members
const sets = memberSets.reduce((memo, set, idx) => {
if (idx % 2) {
memo.groupNames.push(set);
} else {
memo.uids.push(set);
}
return memo;
}, { groupNames: [], uids: [] });
const uniqGroups = _.uniq(_.flatten(sets.groupNames));
const groupUids = await groups.getMembersOfGroups(uniqGroups);
const map = _.zipObject(uniqGroups, groupUids);
const uidsByCid = cids.map((cid, index) => {
if (disabled[index]) {
return [];
}
return _.uniq(sets.uids[index].concat(_.flatten(sets.groupNames[index].map(g => map[g]))));
});
return uidsByCid;
};
require('../promisify')(helpers);

View File

@@ -154,7 +154,7 @@ Auth.reloadRoutes = async function (params) {
}, Auth.middleware.validateAuth, (req, res, next) => {
async.waterfall([
async.apply(req.login.bind(req), res.locals.user, { keepSessionInfo: true }),
async.apply(controllers.authentication.onSuccessfulLogin, req, req.uid),
async.apply(controllers.authentication.onSuccessfulLogin, req, res.locals.user.uid),
], (err) => {
if (err) {
return next(err);

View File

@@ -127,7 +127,7 @@ async function generateForCategory(req, res, next) {
db.getSortedSetRevIntersect({
sets: ['topics:tid', `cid:${cid}:tids:lastposttime`],
start: 0,
stop: 25,
stop: 24,
weights: [1, 0],
}),
]);

View File

@@ -79,7 +79,11 @@ sitemap.getPages = async function () {
async function getSitemapCategories() {
const cids = await categories.getCidsByPrivilege('categories:cid', 0, 'find');
return await categories.getCategoriesFields(cids, ['slug']);
const categoryData = await categories.getCategoriesFields(cids, ['slug']);
const data = await plugins.hooks.fire('filter:sitemap.getCategories', {
categories: categoryData,
});
return data.categories;
}
sitemap.getCategories = async function () {
@@ -128,7 +132,12 @@ sitemap.getTopicPage = async function (page) {
tids = await privileges.topics.filterTids('topics:read', tids, 0);
const topicData = await topics.getTopicsFields(tids, ['tid', 'title', 'slug', 'lastposttime']);
if (!topicData.length) {
const data = await plugins.hooks.fire('filter:sitemap.getCategories', {
page: page,
topics: topicData,
});
if (!data.topics.length) {
sitemap.maps.topics[page - 1] = {
sm: '',
cacheExpireTimestamp: Date.now() + (1000 * 60 * 60 * 24),
@@ -136,7 +145,7 @@ sitemap.getTopicPage = async function (page) {
return sitemap.maps.topics[page - 1].sm;
}
topicData.forEach((topic) => {
data.topics.forEach((topic) => {
if (topic) {
topicUrls.push({
url: `${nconf.get('relative_path')}/topic/${topic.slug}`,

View File

@@ -74,6 +74,6 @@ module.exports = function (SocketUser) {
await user.isAdminOrSelf(socket.uid, data.uid);
api.users.generateExport(socket, { type, ...data });
api.users.generateExport(socket, { type, uid: data.uid });
}
};

View File

@@ -60,6 +60,7 @@ Scheduled.pin = async function (tid, topicData) {
};
Scheduled.reschedule = async function ({ cid, tid, timestamp, uid }) {
const mainPid = await topics.getTopicField(tid, 'mainPid');
await Promise.all([
db.sortedSetsAdd([
'topics:scheduled',
@@ -67,6 +68,7 @@ Scheduled.reschedule = async function ({ cid, tid, timestamp, uid }) {
'topics:tid',
`cid:${cid}:uid:${uid}:tids`,
], timestamp, tid),
posts.setPostField(mainPid, 'timestamp', timestamp),
shiftPostTimes(tid, timestamp),
]);
return topics.updateLastPostTimeFromLastPid(tid);
@@ -87,14 +89,15 @@ function unpin(tid, topicData) {
}
async function sendNotifications(uids, topicsData) {
const usernames = await Promise.all(uids.map(uid => user.getUserField(uid, 'username')));
const uidToUsername = Object.fromEntries(uids.map((uid, idx) => [uid, usernames[idx]]));
const userData = await user.getUsersData(uids);
const uidToUserData = Object.fromEntries(uids.map((uid, idx) => [uid, userData[idx]]));
const postsData = await posts.getPostsData(topicsData.map(({ mainPid }) => mainPid));
const postsData = await posts.getPostsData(topicsData.map(t => t && t.mainPid));
postsData.forEach((postData, idx) => {
postData.user = {};
postData.user.username = uidToUsername[postData.uid];
postData.topic = topicsData[idx];
if (postData) {
postData.user = uidToUserData[topicsData[idx].uid];
postData.topic = topicsData[idx];
}
});
return Promise.all(topicsData.map(

View File

@@ -0,0 +1,46 @@
'use strict';
const db = require('../../database');
const batch = require('../../batch');
module.exports = {
name: 'Fix user email sorted sets',
timestamp: Date.UTC(2023, 1, 4),
method: async function () {
const { progress } = this;
const bulkRemove = [];
await batch.processSortedSet('email:uid', async (data) => {
progress.incr(data.length);
const usersData = await db.getObjects(data.map(d => `user:${d.score}`));
data.forEach((emailData, index) => {
const { score: uid, value: email } = emailData;
const userData = usersData[index];
// user no longer exists or doesn't have email set in user hash
// remove the email/uid pair from email:uid, email:sorted
if (!userData || !userData.email) {
bulkRemove.push(['email:uid', email]);
bulkRemove.push(['email:sorted', `${email.toLowerCase()}:${uid}`]);
return;
}
// user has email but doesn't match whats stored in user hash, gh#11259
if (userData.email && userData.email.toLowerCase() !== email.toLowerCase()) {
bulkRemove.push(['email:uid', email]);
bulkRemove.push(['email:sorted', `${email.toLowerCase()}:${uid}`]);
}
});
}, {
batch: 500,
withScores: true,
progress: progress,
});
await batch.processArray(bulkRemove, async (bulk) => {
await db.sortedSetRemoveBulk(bulk);
}, {
batch: 500,
});
},
};

View File

@@ -39,7 +39,7 @@ UserEmail.remove = async function (uid, sessionId) {
db.sortedSetRemove('email:uid', email.toLowerCase()),
db.sortedSetRemove('email:sorted', `${email.toLowerCase()}:${uid}`),
user.email.expireValidation(uid),
user.auth.revokeAllSessions(uid, sessionId),
sessionId ? user.auth.revokeAllSessions(uid, sessionId) : Promise.resolve(),
events.log({ type: 'email-change', email, newEmail: '' }),
]);
};
@@ -69,7 +69,7 @@ UserEmail.expireValidation = async (uid) => {
};
UserEmail.canSendValidation = async (uid, email) => {
const pending = UserEmail.isValidationPending(uid, email);
const pending = await UserEmail.isValidationPending(uid, email);
if (!pending) {
return true;
}
@@ -134,13 +134,13 @@ UserEmail.sendValidationEmail = async function (uid, options) {
await UserEmail.expireValidation(uid);
await db.set(`confirm:byUid:${uid}`, confirm_code);
await db.pexpire(`confirm:byUid:${uid}`, emailConfirmExpiry * 24 * 60 * 60 * 1000);
await db.pexpire(`confirm:byUid:${uid}`, emailConfirmExpiry * 60 * 60 * 1000);
await db.setObject(`confirm:${confirm_code}`, {
email: options.email.toLowerCase(),
uid: uid,
});
await db.pexpire(`confirm:${confirm_code}`, emailConfirmExpiry * 24 * 60 * 60 * 1000);
await db.pexpire(`confirm:${confirm_code}`, emailConfirmExpiry * 60 * 60 * 1000);
winston.verbose(`[user/email] Validation email for uid ${uid} sent to ${options.email}`);
events.log({
@@ -196,6 +196,20 @@ UserEmail.confirmByUid = async function (uid) {
throw new Error('[[error:invalid-email]]');
}
// If another uid has the same email throw error
const oldUid = await db.sortedSetScore('email:uid', currentEmail.toLowerCase());
if (oldUid && oldUid !== parseInt(uid, 10)) {
throw new Error('[[error:email-taken]]');
}
const confirmedEmails = await db.getSortedSetRangeByScore(`email:uid`, 0, -1, uid, uid);
if (confirmedEmails.length) {
// remove old email of user by uid
await db.sortedSetsRemoveRangeByScore([`email:uid`], uid, uid);
await db.sortedSetRemoveBulk(
confirmedEmails.map(email => [`email:sorted`, `${email.toLowerCase()}:${uid}`])
);
}
await Promise.all([
db.sortedSetAddBulk([
['email:uid', uid, currentEmail.toLowerCase()],

View File

@@ -42,6 +42,7 @@ Interstitials.email = async (data) => {
callback: async (userData, formData) => {
// Validate and send email confirmation
if (userData.uid) {
const isSelf = parseInt(userData.uid, 10) === parseInt(data.req.uid, 10);
const [isPasswordCorrect, canEdit, { email: current, 'email:confirmed': confirmed }, { allowed, error }] = await Promise.all([
user.isPasswordCorrect(userData.uid, formData.password, data.req.ip),
privileges.users.canEdit(data.req.uid, userData.uid),
@@ -68,13 +69,17 @@ Interstitials.email = async (data) => {
if (formData.email === current) {
if (confirmed) {
throw new Error('[[error:email-nochange]]');
} else if (await user.email.canSendValidation(userData.uid, current)) {
} else if (!await user.email.canSendValidation(userData.uid, current)) {
throw new Error(`[[error:confirm-email-already-sent, ${meta.config.emailConfirmInterval}]]`);
}
}
// Admins editing will auto-confirm, unless editing their own email
if (isAdminOrGlobalMod && userData.uid !== data.req.uid) {
if (!await user.email.available(formData.email)) {
throw new Error('[[error:email-taken]]');
}
await user.email.remove(userData.uid);
await user.setUserField(userData.uid, 'email', formData.email);
await user.email.confirmByUid(userData.uid);
} else if (canEdit) {
@@ -99,8 +104,8 @@ Interstitials.email = async (data) => {
}
if (current.length && (!hasPassword || (hasPassword && isPasswordCorrect) || isAdminOrGlobalMod)) {
// User explicitly clearing their email
await user.email.remove(userData.uid, data.req.session.id);
// User or admin explicitly clearing their email
await user.email.remove(userData.uid, isSelf ? data.req.session.id : null);
}
}
} else {

View File

@@ -17,7 +17,7 @@
{{{ end }}}
{{{ each topics }}}
<tr>
<td><a href="{config.relative_path}/topics/{../slug}">{../title}</a></td>
<td><a href="{config.relative_path}/topic/{../slug}">{../title}</a></td>
<td>[[topic:posted_by, {../user.username}]]</td>
<td><span class="timeago" data-title="{../timestampISO}"></span></td>
</tr>

View File

@@ -150,7 +150,7 @@
[[admin/settings/email:smtp-transport.gmail-warning2]]
</p>
</div>
<div class="form-group well" id="email:smtpTransport:custom-service" style="display: none">
<div class="form-group well" id="email:smtpTransport:custom-service">
<h5>Custom Service</h5>
<label for="email:smtpTransport:host">[[admin/settings/email:smtp-transport.host]]</label>

View File

@@ -12,13 +12,22 @@ const db = require('./mocks/databasemock');
const user = require('../src/user');
const utils = require('../src/utils');
const meta = require('../src/meta');
const plugins = require('../src/plugins');
const privileges = require('../src/privileges');
const helpers = require('./helpers');
describe('authentication', () => {
const jar = request.jar();
let regularUid;
const dummyEmailerHook = async (data) => {};
before((done) => {
// Attach an emailer hook so related requests do not error
plugins.hooks.register('authentication-test', {
hook: 'filter:email.send',
method: dummyEmailerHook,
});
user.create({ username: 'regular', password: 'regularpwd', email: 'regular@nodebb.org' }, (err, uid) => {
assert.ifError(err);
regularUid = uid;
@@ -27,6 +36,10 @@ describe('authentication', () => {
});
});
after(() => {
plugins.hooks.unregister('authentication-test', 'filter:email.send');
});
it('should allow login with email for uid 1', async () => {
const oldValue = meta.config.allowLoginWith;
meta.config.allowLoginWith = 'username-email';

View File

@@ -120,7 +120,7 @@ describe('email confirmation (library methods)', () => {
await user.email.sendValidationEmail(uid, {
email,
});
const ok = await user.email.canSendValidation(uid, 'test@example.com');
const ok = await user.email.canSendValidation(uid, email);
assert.strictEqual(ok, false);
});
@@ -131,7 +131,7 @@ describe('email confirmation (library methods)', () => {
email,
});
await db.pexpire(`confirm:byUid:${uid}`, 1000);
const ok = await user.email.canSendValidation(uid, 'test@example.com');
const ok = await user.email.canSendValidation(uid, email);
assert(ok);
});