Compare commits

..

215 Commits

Author SHA1 Message Date
Barış Soner Uşaklı
624738359a chore: up version 2025-06-16 12:59:05 -04:00
Barış Soner Uşaklı
15c3abb6d5 fix: add sanitizesvg 2025-06-16 12:58:51 -04:00
Barış Soner Uşaklı
7ead74fe9b fix: sanitize svg when uploading site-logo, default avatar and og:image 2025-06-16 12:57:30 -04:00
Barış Soner Uşaklı
cbbdc79357 chore: up version 2025-06-09 11:21:26 -04:00
Barış Soner Uşaklı
e0d5f92b2a fix: escape, query params 2025-06-09 11:19:45 -04:00
Barış Soner Uşaklı
8228eeb468 chore: up version 2025-05-12 11:07:15 -04:00
Barış Soner Uşaklı
fc09f6c4f9 fix: escape flag filters 2025-05-12 11:00:38 -04:00
Barış Soner Uşaklı
7a26672872 fix: sql injection in sortedSetScan 2025-05-12 10:59:53 -04:00
Barış Soner Uşaklı
de820ae198 chore: up persona 2024-06-24 16:04:26 -04:00
Barış Soner Uşaklı
961a193787 chore: up persona 2024-06-24 16:02:28 -04:00
Barış Soner Uşaklı
4ab31e3f79 chore: openapi 2024-06-24 15:54:42 -04:00
Barış Soner Uşaklı
338f89deb5 backport author, and structured data fix to 2.x 2024-06-24 15:53:42 -04:00
Barış Soner Uşaklı
dc14d6a8d1 chore: up version 2023-08-22 12:49:06 -04:00
Barış Soner Uşaklı
fb43f9ae10 chore: update composer-default 2023-08-22 12:07:02 -04:00
Barış Soner Uşaklı
638e098f30 fix: #11756, fix unique visitor stats in acp table 2023-06-26 09:56:58 -04:00
Julian Lam
2514aace4e chore: bump nodebb version 2023-06-13 14:54:56 -04:00
Julian Lam
9ec7ab4afc fix: improper neutralization of user input in image wrapping code
(Backport of: 1d1639d46f)
2023-06-13 14:53:27 -04:00
Barış Soner Uşaklı
dd5ed9e507 fix: closes #11617, upgrade csrf-sync dep
adds back req.csrfToken()
2023-05-20 21:05:02 -04:00
Barış Soner Uşaklı
8bc8cf1ba0 lint 2023-05-15 12:15:48 -04:00
Barış Soner Uşaklı
62e162cf1e fix: backport ws token fix 2023-05-15 11:55:18 -04:00
psibean
a5d92da9dd Replace csurf with csrf-sync 2023-05-15 11:48:25 -04:00
Julian Lam
2bd6eea2fa fix: #11554, email requirement bypass by sending in whitespace 2023-05-02 12:01:28 -04:00
Misty Release Bot
42b9fbc91c chore: incrementing version number - v2.8.12
(cherry picked from commit 3e494a1ea0)
Signed-off-by: Misty Release Bot <deploy@nodebb.org>
2023-04-26 14:34:45 +00:00
Misty Release Bot
5c0bf7ccbe Merge commit '30b6bcfca117e667c262c0462fc5f0100e6a436c' into v2.x 2023-04-26 14:34:42 +00:00
Barış Soner Uşaklı
30b6bcfca1 fix: #11519, clear parent cache all the way to root 2023-04-26 10:13:27 -04:00
Barış Soner Uşaklı
de2669a2c6 fix: only remove deleted tag, closes #11515 2023-04-24 15:39:00 -04:00
Barış Soner Uşaklı
21fb8590e5 test: remove old comment 2023-04-23 18:59:18 -04:00
Veronikya
c931183287 fix: NodeBB#11482 thumbs Post Can not upload a thumbnail, only multip… (#11483)
* fix: NodeBB#11482 thumbs Post Can not upload a thumbnail, only multiple uploads

* Modify upload thumbnail test

* Modify upload thumbnail test +,

* Get rid of v2 uploads test

* edit times

* Modify amount of files associated post test

* edit post file amount
2023-04-15 17:56:36 -04:00
Brutus5000
ae5afdbc66 feat: name theme on error:theme-not-set-in-configuration 2023-04-11 13:42:21 -04:00
Julian Lam
5343d2a01b chore: removing superfluous changelog items 2023-04-10 21:51:12 -04:00
Misty Release Bot
2ec81eff43 chore: incrementing version number - v2.8.11
(cherry picked from commit 82f0efb14b)
Signed-off-by: Misty Release Bot <deploy@nodebb.org>
2023-04-11 01:49:11 +00:00
Misty Release Bot
f2ca93f6c6 chore: update changelog for v2.8.11 2023-04-11 01:49:11 +00:00
Misty Release Bot
82f0efb14b chore: incrementing version number - v2.8.11 2023-04-11 01:49:11 +00:00
Misty Release Bot
df08b47163 Merge commit 'c27567289f9937abd4abe6960a9b6e387cf68331' into v2.x 2023-04-11 01:49:09 +00:00
Opliko
c27567289f ci: publish to ghcr instead of docker hub 2023-04-05 14:38:47 -04:00
Julian Lam
c33730530e Revert "docs: update readme with new screenshot and updated copy for Harmony"
This reverts commit 67055006df.
2023-03-29 10:32:05 -04:00
Julian Lam
67055006df docs: update readme with new screenshot and updated copy for Harmony 2023-03-29 10:31:39 -04:00
Barış Soner Uşaklı
e0b2065802 test: update socket.io test 2023-03-28 08:15:42 -04:00
Barış Soner Uşaklı
4d2d76897a fix: don't crash on objects with toString property 2023-03-28 08:08:59 -04:00
Barış Soner Uşaklı
7397873db3 fix: fire action:user.online on user login 2023-03-27 22:16:41 -04:00
Misty Release Bot
5b7c3671c8 chore: incrementing version number - v2.8.10
(cherry picked from commit 48c1c7594d)
Signed-off-by: Misty Release Bot <deploy@nodebb.org>
2023-03-27 18:10:57 +00:00
Misty Release Bot
188ec62f9a chore: update changelog for v2.8.10 2023-03-27 18:10:57 +00:00
Misty Release Bot
48c1c7594d chore: incrementing version number - v2.8.10 2023-03-27 18:10:57 +00:00
Misty Release Bot
73ff25887c Merge commit '830f142b7aea2e597294a84d52c05aab3a3539ca' into v2.x 2023-03-27 15:12:54 +00:00
Julian Lam
830f142b7a fix: #11403, remove loader.js crash counter logic 2023-03-27 10:55:44 -04:00
Barış Soner Uşaklı
1aff9cad91 lint: fix arrow 2023-03-27 10:47:15 -04:00
Barış Soner Uşaklı
37b48b82a4 fix: don't crash if event name is not a string 2023-03-27 10:38:53 -04:00
Barış Soner Uşaklı
e9a8e19508 chore: up composer-default 2023-03-21 10:13:21 -04:00
Barış Soner Uşaklı
894f392bfc lint: whitespace 2023-03-20 11:17:05 -04:00
Barış Soner Uşaklı
c2961ad4cd fix: closes #11173, move cache clear code
if 2 deps were updated only one of them was cleared from require.cache. ie commander & lru-cache both has major version bump then only commander would be cleared from cache since it throws first
2023-03-20 11:05:48 -04:00
Misty Release Bot
57f14e419f chore: incrementing version number - v2.8.9
(cherry picked from commit fb100ac731)
Signed-off-by: Misty Release Bot <deploy@nodebb.org>
2023-03-19 16:31:26 +00:00
Misty Release Bot
18b2150edd chore: update changelog for v2.8.9 2023-03-19 16:31:26 +00:00
Misty Release Bot
fb100ac731 chore: incrementing version number - v2.8.9 2023-03-19 16:31:26 +00:00
Misty Release Bot
bb725987b3 Merge commit '73a50d17180dcd6cb42ef9cf305a480f92b4af05' into v2.x 2023-03-19 16:31:24 +00:00
Barış Soner Uşaklı
73a50d1718 chore: up cron 2023-03-19 12:21:06 -04:00
Julian Lam
93aa43f717 style: more fixes 2023-03-17 15:48:44 -04:00
Phạm Tấn Minh Tiến
9ed6961af8 fix lint 2023-03-17 15:48:44 -04:00
Phạm Tấn Minh Tiến
4b94c033c4 wrap quotes to prevent stripping leading 0 2023-03-17 15:48:44 -04:00
Barış Soner Uşaklı
9e685e657a test: openapi for thumbs 2023-03-15 15:18:05 -04:00
Barış Soner Uşaklı
767c1d1faf fix: thumb remove on windows, closes #11357 2023-03-14 15:09:12 -04:00
Barış Soner Uşaklı
a3a38e4ba3 fix: #11357 clear cache on thumb remove 2023-03-14 14:30:46 -04:00
Barış Soner Uşaklı
cfd5027245 fix: closes #11352, try/catch rss feeds 2023-03-11 16:07:02 -05:00
Barış Soner Uşaklı
56427e4f9d fix: closes #11343, don't crash if tags array is empty 2023-03-10 11:40:02 -05:00
Misty Release Bot
b331b9423b chore: incrementing version number - v2.8.8
(cherry picked from commit f5a59991fc)
Signed-off-by: Misty Release Bot <deploy@nodebb.org>
2023-03-09 14:58:29 +00:00
Misty Release Bot
c03d5db71e chore: update changelog for v2.8.8 2023-03-09 14:58:28 +00:00
Misty Release Bot
f5a59991fc chore: incrementing version number - v2.8.8 2023-03-09 14:58:28 +00:00
Misty Release Bot
e45a6de24b Merge commit '22fc8fe38fd3b3c8ba6300ca6d12d90eb9b990ca' into v2.x 2023-03-09 14:58:22 +00:00
Julian Lam
22fc8fe38f fix: stop topic navigation hotkeys from firing if in a mousetrap-enabled form element 2023-03-09 09:55:32 -05:00
Julian Lam
17d0b40efa fix: stop topic navigation hotkeys from firing if in a mousetrap-enabled form element 2023-03-09 09:44:06 -05:00
Barış Soner Uşaklı
1545223e7f fix: tag filtering when changing filter to watched topics
or changing popular time limit to month
2023-03-08 18:28:55 -05:00
Barış Soner Uşaklı
f054a4f44d fix: get cid from pid instead of passing in 2023-03-06 09:44:21 -05:00
Barış Soner Uşaklı
8c762d3228 fix: closes #11331, allow 0 length content if set to 0 in acp 2023-03-03 09:30:51 -05:00
Misty Release Bot
3f8248d673 chore: incrementing version number - v2.8.7
(cherry picked from commit 6976925943)
Signed-off-by: Misty Release Bot <deploy@nodebb.org>
2023-03-01 15:51:30 +00:00
Misty Release Bot
2ca38e7b95 chore: update changelog for v2.8.7 2023-03-01 15:51:30 +00:00
Misty Release Bot
6976925943 chore: incrementing version number - v2.8.7 2023-03-01 15:51:30 +00:00
Misty Release Bot
f4282c091b Merge commit '791551098cb4a56edbae824e45b6f0a10138695b' into v2.x 2023-03-01 15:51:22 +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
af6ce44737 chore: incrementing version number - v2.8.6
(cherry picked from commit 76732140f3)
Signed-off-by: Misty Release Bot <deploy@nodebb.org>
2023-02-03 16:39:40 +00: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
Misty Release Bot
c6681a1725 Merge commit 'bf92ee0e5fcd0b7a69bb58ec4baaf3b6225ebd6b' into v2.x 2023-02-03 16:39:38 +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
bff5ce2d79 chore: incrementing version number - v2.8.5
(cherry picked from commit 93ccf604db)
Signed-off-by: Misty Release Bot <deploy@nodebb.org>
2023-01-27 14:35:25 +00: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
Misty Release Bot
4821b21e81 Merge commit 'f6c96948fe7cee13575ab9c93af6fe7fb9d7b722' into v2.x 2023-01-27 14:35:21 +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
a46b2bbc45 chore: incrementing version number - v2.8.4
(cherry picked from commit b9553613ab)
Signed-off-by: Misty Release Bot <deploy@nodebb.org>
2023-01-26 14:38:07 +00: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
Misty Release Bot
ce924eca0d Merge commit 'c3653bee60740e410bf28808e29ffed6ab373bf9' into v2.x 2023-01-26 14:38:03 +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
c20b20a7aa chore: incrementing version number - v2.8.3
(cherry picked from commit 4c46ff42f6)
Signed-off-by: Misty Release Bot <deploy@nodebb.org>
2023-01-25 19:37:34 +00:00
Misty Release Bot
eb2841eed3 chore: update changelog for v2.8.3 2023-01-25 19:37:34 +00:00
Misty Release Bot
4c46ff42f6 chore: incrementing version number - v2.8.3 2023-01-25 19:37:33 +00:00
Misty Release Bot
82eb55d77d Merge commit '89e059a0841f4265d16b28a99ebf847dd10fa055' into v2.x 2023-01-25 19:37:31 +00:00
Peter Jaszkowiak
89e059a084 fix: import resolution within plugin modules (#11200) 2023-01-24 19:59:06 -05:00
Julian Lam
fecd84d1a0 revert: a788bd1344 2023-01-23 15:19:47 -05:00
Julian Lam
00e48803a6 fix(deps): downgrade swagger-parser to v9 2023-01-23 15:19:40 -05:00
Julian Lam
a788bd1344 revert: 9c250b78b0, fix: comment out broken test for now 2023-01-23 12:51:42 -05:00
Barış Soner Uşaklı
9f38692369 Merge branch 'master' of https://github.com/NodeBB/NodeBB 2023-01-23 11:40:22 -05:00
Barış Soner Uşaklı
0bffd3d93c fix: #11195, allow users with admin:users privilege to delete users in acp 2023-01-23 11:40:17 -05:00
Julian Lam
9c250b78b0 fix(deps): pinning sub dependency json-schema-ref-parser to 9.0.9
ref: https://github.com/APIDevTools/json-schema-ref-parser/issues/298
2023-01-23 11:28:03 -05:00
Barış Soner Uşaklı
7d04e95226 fix: #11194, allow access to sub dashboard pages 2023-01-23 11:09:19 -05:00
Julian Lam
f295174e07 test: fix broken test 2023-01-23 09:58:51 -05:00
Julian Lam
459bc52338 fix: #11136, tests, and returning the proper number of arrays 2023-01-18 15:08:35 -05:00
Julian Lam
39e009c05a fix: #11136, only show mods of active categories when getModeratorUids is called 2023-01-18 14:47:10 -05:00
Barış Soner Uşaklı
747cb1f0a1 fix: closes #11173, clear require cache if wrong dependency is installed 2023-01-18 09:40:00 -05:00
Julian Lam
48c9f4470f chore: remove extraneous lines from changelog 2023-01-13 13:38:45 -05:00
Misty Release Bot
050e43f8b4 chore: incrementing version number - v2.8.2
(cherry picked from commit 1d5eff2365)
Signed-off-by: Misty Release Bot <deploy@nodebb.org>
2023-01-13 18:38:13 +00:00
Misty Release Bot
66aa31698f chore: update changelog for v2.8.2 2023-01-13 18:38:13 +00:00
Misty Release Bot
1d5eff2365 chore: incrementing version number - v2.8.2 2023-01-13 18:38:12 +00:00
Misty Release Bot
9b6dad367d Merge commit '25ae58e8a057d9c640fbb50f675eadcdbe442aa9' into v2.x 2023-01-13 18:38:09 +00:00
Julian Lam
25ae58e8a0 fix: move call to filter:middleware.buildHeader out of parallel so that req can be overridden by plugins prior to loading config 2023-01-13 09:53:14 -05:00
Misty Release Bot
727f879e5b chore: incrementing version number - v2.8.1
(cherry picked from commit 96bdbf52b8)
Signed-off-by: Misty Release Bot <deploy@nodebb.org>
2022-12-30 20:48:48 +00:00
Misty Release Bot
d17d4ec09b chore: update changelog for v2.8.1 2022-12-30 20:48:48 +00:00
Misty Release Bot
96bdbf52b8 chore: incrementing version number - v2.8.1 2022-12-30 20:48:47 +00:00
Misty Release Bot
fe662f3a46 Merge commit '8a69e740a859cf2eb4a12a0167c1ac76a48c33db' into v2.x 2022-12-30 20:48:22 +00:00
Julian Lam
8a69e740a8 chore: fallbacks for new language string 2022-12-30 15:35:01 -05:00
Barış Soner Uşaklı
586eed1407 fix: vulnerability in socket.io nested namespaces (#11117) 2022-12-30 09:49:22 -05:00
Barış Soner Uşaklı
1ea9481af6 fix: lock post/reply similar to user.create 2022-12-25 15:05:15 -05:00
Julian Lam
bbaf26cedc chore: remove extraneous lines from changelog 2022-12-21 17:11:53 -05:00
Misty Release Bot
8e77673d39 chore: incrementing version number - v2.8.0
(cherry picked from commit 7ce758d698)
Signed-off-by: Misty Release Bot <deploy@nodebb.org>
2022-12-21 22:10:49 +00:00
Misty Release Bot
a5c2edb993 chore: update changelog for v2.8.0 2022-12-21 22:10:49 +00:00
Misty Release Bot
7ce758d698 chore: incrementing version number - v2.8.0 2022-12-21 22:10:48 +00:00
Misty Release Bot
3f950d5162 Merge commit 'ef500af8e6c618d86069cbf0be0d21e8c3f6e527' into v2.x 2022-12-21 22:10:47 +00:00
renovate[bot]
ef500af8e6 fix(deps): update dependency sharp to v0.31.3 (#11110)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-21 17:00:36 -05:00
renovate[bot]
7ab46b784a fix(deps): update dependency sanitize-html to v2.8.1 (#11109)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-21 17:00:36 -05:00
renovate[bot]
13a3faa0d1 chore(deps): update dependency jquery to v3.6.3 (#11107)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-21 17:00:36 -05:00
renovate[bot]
eb6a9c474a fix(deps): update dependency esbuild to v0.16.10 (#11104)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-21 17:00:36 -05:00
renovate[bot]
05443dbeee fix(deps): update dependency mongodb to v4.13.0 (#11105)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-21 17:00:36 -05:00
renovate[bot]
485ee1301b chore(deps): update dependency eslint to v8.30.0 (#11102)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-21 17:00:36 -05:00
renovate[bot]
18ff6caadd fix(deps): update dependency esbuild to v0.16.8 (#11101)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-21 17:00:36 -05:00
renovate[bot]
c4cc1e61df chore(deps): update dependency mocha to v10.2.0 (#11094)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-21 17:00:36 -05:00
renovate[bot]
faaf09f71c fix(deps): update dependency sanitize-html to v2.8.0 (#11098)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-21 17:00:36 -05:00
renovate[bot]
cde44587c1 fix(deps): update dependency ace-builds to v1.14.0 (#11095)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-21 17:00:36 -05:00
renovate[bot]
5dda9a5bf1 fix(deps): update dependency nodebb-plugin-2factor to v5.1.2 (#11096)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-21 17:00:36 -05:00
renovate[bot]
0a5adb416a fix(deps): update dependency postcss to v8.4.20 (#11097)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-21 17:00:35 -05:00
Opliko
de31cb1a4f feat: add force flag to plugin install in cli (#11089) 2022-12-21 17:00:35 -05:00
Julian Lam
9f531f957e refactor: flag states so that they are not hardcoded, allow plugins to add additional states, deprecated filter:flags.getFilters hook, closes #11065 2022-12-21 17:00:35 -05:00
renovate[bot]
8b209f16d1 fix(deps): update dependency compare-versions to v5.0.3 (#11092)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-21 17:00:35 -05:00
renovate[bot]
7bcfe38e60 fix(deps): update dependency html-to-text to v9.0.3 (#11093)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-21 17:00:35 -05:00
renovate[bot]
7b9bbef593 fix(deps): update dependency @socket.io/redis-adapter to v8 (#11084)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-21 17:00:35 -05:00
renovate[bot]
7b48156cc0 fix(deps): update dependency nodebb-widget-essentials to v6.0.1 (#11085)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-21 17:00:35 -05:00
renovate[bot]
65ef722e82 fix(deps): update dependency esbuild to v0.16.7 (#11086)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-21 17:00:35 -05:00
Barış Soner Uşaklı
3e8f537830 chore: up jquery 2022-12-21 17:00:35 -05:00
renovate[bot]
4f67fc1ad0 fix(deps): update dependency esbuild to v0.16.3 (#11083)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-21 17:00:34 -05:00
renovate[bot]
22493ffb4c fix(deps): update dependency esbuild to v0.15.16 (#11069)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-21 16:59:55 -05:00
Misty Release Bot
717b36129e chore(i18n): fallback strings for new resources: nodebb.admin-settings-email 2022-12-21 16:59:10 -05:00
Julian Lam
896493dbd6 fix: change hsts-maxage back to numeric input type, change API token uid input to numeric text type 2022-12-21 16:59:10 -05:00
Julian Lam
4314b8e201 Revert "fix: replace input type number with text/pattern"
This reverts commit cde4c6ac7e.

The changeset was naive in the sense that a lot of the changes inputs actually work better as type="number".
2022-12-21 16:59:10 -05:00
Misty Release Bot
4f4b48000a chore(i18n): fallback strings for new resources: nodebb.admin-settings-email 2022-12-21 16:59:10 -05:00
Julian Lam
2bc23a9526 fix: replace input type number with text/pattern 2022-12-21 16:59:10 -05:00
Julian Lam
33af2d9c18 feat: integrating basic client-side form validity checking in settings v1 and v2 2022-12-21 16:59:08 -05:00
Barış Soner Uşaklı
06f4801e57 refactor: remove debug log closes #11090 2022-12-16 09:07:17 -05:00
Julian Lam
e213dbc3b7 chore: remove extraneous lines from changelog 2022-12-14 14:37:42 -05:00
Misty Release Bot
96cc0617c5 chore: incrementing version number - v2.7.0
(cherry picked from commit 098097257d)
Signed-off-by: Misty Release Bot <deploy@nodebb.org>
2022-12-14 19:36:38 +00:00
Misty Release Bot
4701c96d9c chore: update changelog for v2.7.0 2022-12-14 19:36:38 +00:00
Misty Release Bot
ccf8739344 Merge commit '9ee8502d7a8ba41ce6ded74b1ce1fbbe180b1dda' into v2.x 2022-12-14 19:36:36 +00:00
Misty Release Bot
7e52a7a574 chore: incrementing version number - v2.6.1
(cherry picked from commit f8e947e2a7)
Signed-off-by: Misty Release Bot <deploy@nodebb.org>
2022-11-28 01:01:10 +00:00
Misty Release Bot
21d9806ca9 Merge commit '48d143921753914da45926cca6370a92ed0c46b8' into v2.x 2022-11-28 01:00:52 +00:00
Misty Release Bot
e7fcf482f3 chore: incrementing version number - v2.6.0
(cherry picked from commit 12f0541dfa)
Signed-off-by: Misty Release Bot <deploy@nodebb.org>
2022-11-23 19:04:45 +00:00
Misty Release Bot
d80c80b618 Merge commit 'c7aa4ebf47f7b87db1f5efa0c9662b21cff7b194' into v2.x 2022-11-23 19:04:37 +00:00
Misty Release Bot
dec0e7deac chore: incrementing version number - v2.5.8
(cherry picked from commit 466263172a)
Signed-off-by: Misty Release Bot <deploy@nodebb.org>
2022-11-09 18:46:09 +00:00
Misty Release Bot
c7ff98a12d Merge commit '2f9d8c350e54543f608d3d4c8e1a49bbb6cdea38' into v2.x 2022-11-09 18:42:47 +00:00
Misty Release Bot
5836bf4a05 chore: incrementing version number - v2.5.7
(cherry picked from commit dd6d104820)
Signed-off-by: Misty Release Bot <deploy@nodebb.org>
2022-10-14 15:59:58 +00:00
Misty Release Bot
a5357812c6 Merge commit 'dc4a850cacecb8c57923803363dac9bb61221bba' into v2.x 2022-10-14 15:59:56 +00:00
Misty Release Bot
c7bd7dbfe6 chore: incrementing version number - v2.5.6
(cherry picked from commit 7dc45afa4c)
Signed-off-by: Misty Release Bot <deploy@nodebb.org>
2022-10-13 14:21:02 +00:00
Misty Release Bot
ec4dadabd4 Merge commit '67efaeb4b8e03417dfc3b575f19249f18f4cb3d6' into v2.x 2022-10-13 14:21:00 +00:00
Misty Release Bot
3509ed9461 chore: incrementing version number - v2.5.5
(cherry picked from commit 58b2f10ee9)
Signed-off-by: Misty Release Bot <deploy@nodebb.org>
2022-10-11 17:07:16 +00:00
Misty Release Bot
cb8d94563a Merge commit 'b91ef6dd761d643383d1eb4f4ac3abd5e55c18e5' into v2.x 2022-10-11 17:07:09 +00:00
Misty Release Bot
e83260ca28 chore: incrementing version number - v2.5.4
(cherry picked from commit 89eb0340d1)
Signed-off-by: Misty Release Bot <deploy@nodebb.org>
2022-10-11 12:25:36 +00:00
Misty Release Bot
4bf1ce42e6 Merge commit 'ebd5dcc6d62841dbcd120351919cdf7cf59f5933' into v2.x 2022-10-11 12:25:01 +00:00
Misty Release Bot
7e922936d0 chore: incrementing version number - v2.5.3
(cherry picked from commit cf6e8101e8)
Signed-off-by: Misty Release Bot <deploy@nodebb.org>
2022-09-19 16:23:59 +00:00
Misty Release Bot
3c8ce70c74 Merge commit 'cf4f5447bb168b9bac32ac7ddbe567f273966b88' into v2.x 2022-09-19 16:23:38 +00:00
Misty Release Bot
babcd17e6c chore: incrementing version number - v2.5.2
(cherry picked from commit e351fbe89c)
Signed-off-by: Misty Release Bot <deploy@nodebb.org>
2022-09-04 14:57:03 +00:00
Misty Release Bot
ec6ffaad4e Merge commit 'b45e24139092af6c3d50851a31452b9d28953fdd' into v2.x 2022-09-04 14:54:41 +00:00
Misty Release Bot
ce3aa95053 chore: incrementing version number - v2.5.1
(cherry picked from commit 2bf475299d)
Signed-off-by: Misty Release Bot <deploy@nodebb.org>
2022-09-02 19:14:02 +00:00
Misty Release Bot
7aab01d87a Merge commit '67cb70352f994d8fab3477f0d753e0dd588bab70' into v2.x 2022-09-02 19:14:00 +00:00
Misty Release Bot
01d276cbee chore: incrementing version number - v2.5.0
(cherry picked from commit c3e19005f6)
Signed-off-by: Misty Release Bot <deploy@nodebb.org>
2022-09-01 16:14:07 +00:00
Misty Release Bot
9758b7af2c Merge commit '8fe41d92a261ee00820a2b270f67d8baf8d84461' into v2.x 2022-09-01 15:23:08 +00:00
Misty Release Bot
dd3e1a2861 chore: incrementing version number - v2.4.5
(cherry picked from commit d8b1291088)
Signed-off-by: Misty Release Bot <deploy@nodebb.org>
2022-08-22 16:14:00 +00:00
Misty Release Bot
2a97342035 Merge commit '9b96c33d5d3706f9c5795b9c07ace063f69b101d' into v2.x 2022-08-22 16:13:55 +00:00
Misty Release Bot
d5525c873b chore: incrementing version number - v2.4.4
(cherry picked from commit 24221d66e0)
Signed-off-by: Misty Release Bot <deploy@nodebb.org>
2022-08-18 13:45:27 +00:00
Misty Release Bot
e7c3634f9a Merge commit 'fc9b436f3ef9d0ef335967456b6f6890ee8560b1' into v2.x 2022-08-18 13:45:18 +00:00
Misty Release Bot
9c647c6ce2 chore: incrementing version number - v2.4.3
(cherry picked from commit be0256b26e)
Signed-off-by: Misty Release Bot <deploy@nodebb.org>
2022-08-18 02:33:19 +00:00
Misty Release Bot
52fc05edfe Merge commit '4dc7fa050f1f30888b5bd71622b68537cc032b44' into v2.x 2022-08-18 02:33:06 +00:00
Misty Release Bot
3aa7b8552a chore: incrementing version number - v2.4.2
(cherry picked from commit 1635633acd)
Signed-off-by: Misty Release Bot <deploy@nodebb.org>
2022-08-17 21:12:35 +00:00
Misty Release Bot
36523c67b8 Merge commit 'ec048a01ba9f2dbc17064427bdcafd88e7271c88' into v2.x 2022-08-17 21:12:23 +00:00
Misty Release Bot
60cbd1480d chore: incrementing version number - v2.4.1
(cherry picked from commit 7f5ff2e613)
Signed-off-by: Misty Release Bot <deploy@nodebb.org>
2022-08-14 00:18:25 +00:00
Misty Release Bot
f3e59508ae Merge commit '15ca460c8f144c3167249b135902ac59289ca2f8' into v2.x 2022-08-14 00:18:05 +00:00
Misty Release Bot
4834cde335 chore: incrementing version number - v2.4.0
(cherry picked from commit 5525442279)
Signed-off-by: Misty Release Bot <deploy@nodebb.org>
2022-08-10 20:02:19 +00:00
Misty Release Bot
01da76e1dc Merge commit '9b753d6d57b850ef5ebc50e5a3dd7b2cbe4d5a27' into v2.x 2022-08-10 20:02:08 +00:00
Misty Release Bot
d2425942a6 chore: incrementing version number - v2.3.1
(cherry picked from commit 44dd42dc89)
Signed-off-by: Misty Release Bot <deploy@nodebb.org>
2022-07-29 15:26:23 +00:00
Misty Release Bot
8d7475be7b Merge commit '89173f17cab6f6447647e5a3d8609f97c09084d1' into v2.x 2022-07-29 15:26:17 +00:00
Misty Release Bot
046ea12022 chore: incrementing version number - v2.3.0
(cherry picked from commit e616b2e16d)
Signed-off-by: Misty Release Bot <deploy@nodebb.org>
2022-07-28 18:21:07 +00:00
125 changed files with 1313 additions and 296 deletions

View File

@@ -13,13 +13,14 @@ on:
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
permissions:
contents: read
packages: write
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
fetch-depth: 0
@@ -32,14 +33,15 @@ jobs:
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Docker meta
id: meta
uses: docker/metadata-action@v4
with:
images: nodebb/docker
images: ghcr.io/${{ github.repository }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}

View File

@@ -1,3 +1,296 @@
#### v2.8.11 (2023-04-11)
##### Chores
* incrementing version number - v2.8.10 (5b7c3671)
* update changelog for v2.8.10 (188ec62f)
##### Continuous Integration
* publish to ghcr instead of docker hub (c2756728)
##### Documentation Changes
* update readme with new screenshot and updated copy for Harmony (67055006)
##### Bug Fixes
* don't crash on objects with toString property (4d2d7689)
* fire action:user.online on user login (7397873d)
##### Tests
* update socket.io test (e0b20658)
#### v2.8.10 (2023-03-27)
##### Chores
* up composer-default (e9a8e195)
* incrementing version number - v2.8.9 (57f14e41)
* update changelog for v2.8.9 (18b2150e)
##### Bug Fixes
* #11403, remove loader.js crash counter logic (830f142b)
* don't crash if event name is not a string (37b48b82)
* closes #11173, move cache clear code (c2961ad4)
##### Other Changes
* fix arrow (1aff9cad)
* whitespace (894f392b)
#### v2.8.9 (2023-03-19)
##### Chores
* up cron (73a50d17)
* incrementing version number - v2.8.8 (b331b942)
* update changelog for v2.8.8 (c03d5db7)
##### Bug Fixes
* thumb remove on windows, closes #11357 (767c1d1f)
* #11357 clear cache on thumb remove (a3a38e4b)
* closes #11352, try/catch rss feeds (cfd50272)
* closes #11343, don't crash if tags array is empty (56427e4f)
##### Code Style Changes
* more fixes (93aa43f7)
##### Tests
* openapi for thumbs (9e685e65)
#### v2.8.8 (2023-03-09)
##### Chores
* incrementing version number - v2.8.7 (3f8248d6)
* update changelog for v2.8.7 (2ca38e7b)
##### Bug Fixes
* stop topic navigation hotkeys from firing if in a mousetrap-enabled form element (22fc8fe3)
* stop topic navigation hotkeys from firing if in a mousetrap-enabled form element (17d0b40e)
* tag filtering when changing filter to watched topics (1545223e)
* get cid from pid instead of passing in (f054a4f4)
* closes #11331, allow 0 length content if set to 0 in acp (8c762d32)
#### v2.8.7 (2023-03-01)
##### Chores
* incrementing version number - v2.8.6 (af6ce447)
* update changelog for v2.8.6 (f3306d03)
##### Documentation Changes
* update openapi spec to include info about passing in timestamps for topic creation, removing timestamp as valid request param for topic replying (40e7b86d)
##### Bug Fixes
* display 25 topics on category feed (79155109)
* object destructuring overwriting type parameter (ec58700f)
* alert on page load (8cf4a6f6)
* show error alert if password change fails (3bd9a871)
* update main post timestamp when rescheduling (edd2fc38)
* show admins/globalmods if content is purged (326b9268)
* email expiry timestamps (e335d0f6)
* #11259, clean old emails when updating via admin (#11260) (845c8013)
* #11257, onSuccessfulLogin called with improper uid (7a5bcc21)
##### Tests
* add dummy emailer hook in authentication test (1b29dbb6)
#### 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)
##### 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)
##### 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)
#### 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)
##### 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
* incrementing version number - v2.8.1 (727f879e)
* update changelog for v2.8.1 (d17d4ec0)
##### Bug Fixes
* move call to `filter:middleware.buildHeader` out of parallel so that req can be overridden by plugins prior to loading config (25ae58e8)
#### v2.8.1 (2022-12-30)
##### Chores
* fallbacks for new language string (8a69e740)
* remove extraneous lines from changelog (bbaf26ce)
* incrementing version number - v2.8.0 (8e77673d)
* update changelog for v2.8.0 (a5c2edb9)
##### Bug Fixes
* vulnerability in socket.io nested namespaces (#11117) (586eed14)
* lock post/reply similar to user.create (1ea9481a)
#### v2.8.0 (2022-12-21)
##### Chores
* **deps:**
* update dependency jquery to v3.6.3 (#11107) (13a3faa0)
* update dependency eslint to v8.30.0 (#11102) (485ee130)
* update dependency mocha to v10.2.0 (#11094) (c4cc1e61)
* up jquery (3e8f5378)
* remove extraneous lines from changelog (e213dbc3)
* incrementing version number - v2.7.0 (96cc0617)
* update changelog for v2.7.0 (4701c96d)
* **i18n:**
* fallback strings for new resources: nodebb.admin-settings-email (717b3612)
* fallback strings for new resources: nodebb.admin-settings-email (4f4b4800)
##### New Features
* add force flag to plugin install in cli (#11089) (de31cb1a)
* integrating basic client-side form validity checking in settings v1 and v2 (33af2d9c)
##### Bug Fixes
* **deps:**
* update dependency sharp to v0.31.3 (#11110) (ef500af8)
* update dependency sanitize-html to v2.8.1 (#11109) (7ab46b78)
* update dependency esbuild to v0.16.10 (#11104) (eb6a9c47)
* update dependency mongodb to v4.13.0 (#11105) (05443dbe)
* update dependency esbuild to v0.16.8 (#11101) (18ff6caa)
* update dependency sanitize-html to v2.8.0 (#11098) (faaf09f7)
* update dependency ace-builds to v1.14.0 (#11095) (cde44587)
* update dependency nodebb-plugin-2factor to v5.1.2 (#11096) (5dda9a5b)
* update dependency postcss to v8.4.20 (#11097) (0a5adb41)
* update dependency compare-versions to v5.0.3 (#11092) (8b209f16)
* update dependency html-to-text to v9.0.3 (#11093) (7bcfe38e)
* update dependency @socket.io/redis-adapter to v8 (#11084) (7b9bbef5)
* update dependency nodebb-widget-essentials to v6.0.1 (#11085) (7b48156c)
* update dependency esbuild to v0.16.7 (#11086) (65ef722e)
* update dependency esbuild to v0.16.3 (#11083) (4f67fc1a)
* update dependency esbuild to v0.15.16 (#11069) (22493ffb)
* change hsts-maxage back to numeric input type, change API token uid input to numeric text type (896493db)
* replace input type number with text/pattern (2bc23a95)
##### Refactors
* flag states so that they are not hardcoded, allow plugins to add additional states, deprecated filter:flags.getFilters hook, closes #11065 (9f531f95)
* remove debug log closes #11090 (06f4801e)
#### v2.7.0 (2022-12-14)
##### Chores
* added stub file in hy (9ee8502d)
* **deps:**
* update dependency lint-staged to v13.1.0 (#11082) (693d4783)
* update dependency eslint to v8.29.0 (#11074) (eab5b754)
* update dependency lint-staged to v13.0.4 (#11064) (f947ac6d)
* **i18n:**
* fallback strings for new resources: nodebb.admin-settings-email (0e319a58)
* fallback strings for new resources: nodebb.admin-settings-email (9676b192)
##### New Features
* update transifex config (for use with new cli) (f11094cb)
* integrating basic client-side form validity checking in settings v1 and v2 (dadbcd73)
* add ./nodebb install <plugin_name> (4efc19d5)
##### Bug Fixes
* **deps:**
* update dependency nodebb-theme-lavender to v6.0.1 (#11081) (df3f1c5e)
* update dependency esbuild to v0.16.3 (#11083) (85d38158)
* update dependency html-to-text to v9 (#11075) (d8e9738d)
* update dependency ace-builds to v1.13.2 (#11080) (35be4594)
* update dependency fs-extra to v11 (#11072) (aafb7f6e)
* update dependency esbuild to v0.15.16 (#11069) (7bc4b836)
* update dependency mongodb to v4.12.1 (#11062) (e14d4abc)
* relax selectors for client-side form validation so that all form elements are checked (43e7c988)
* change hsts-maxage back to numeric input type, change API token uid input to numeric text type (db8d3a94)
* replace input type number with text/pattern (45ae31f8)
* categories.js not showing custom privileges (#10856) (8c4d6bbe)
* #11077, add admin uploads paths to priv mapping (07a02125)
##### Tests
* dont try to load admin upload routes (c2bb2b30)
#### v2.6.1 (2022-11-28)
##### Chores

View File

@@ -2,7 +2,7 @@
"name": "nodebb",
"license": "GPL-3.0",
"description": "NodeBB Forum",
"version": "2.7.0",
"version": "2.8.20",
"homepage": "http://www.nodebb.org",
"repository": {
"type": "git",
@@ -30,7 +30,7 @@
"dependencies": {
"@adactive/bootstrap-tagsinput": "0.8.2",
"@isaacs/ttlcache": "1.2.1",
"ace-builds": "1.13.2",
"ace-builds": "1.14.0",
"archiver": "5.3.1",
"async": "3.2.4",
"autoprefixer": "10.4.13",
@@ -45,7 +45,7 @@
"clipboard": "2.0.11",
"colors": "1.4.0",
"commander": "9.4.1",
"compare-versions": "5.0.1",
"compare-versions": "5.0.3",
"compression": "1.7.4",
"connect-flash": "0.1.1",
"connect-mongo": "4.6.0",
@@ -53,12 +53,12 @@
"connect-pg-simple": "8.0.0",
"connect-redis": "6.1.3",
"cookie-parser": "1.4.6",
"cron": "2.1.0",
"cron": "2.3.0",
"cropperjs": "1.5.13",
"csurf": "1.11.0",
"csrf-sync": "4.0.1",
"daemon": "1.1.0",
"diff": "5.1.0",
"esbuild": "0.16.3",
"esbuild": "0.16.10",
"express": "4.18.2",
"express-session": "1.17.3",
"express-useragent": "1.0.15",
@@ -66,9 +66,9 @@
"fs-extra": "11.1.0",
"graceful-fs": "4.2.10",
"helmet": "5.1.1",
"html-to-text": "9.0.2",
"html-to-text": "9.0.3",
"ipaddr.js": "2.0.1",
"jquery": "3.6.1",
"jquery": "3.6.3",
"jquery-deserialize": "2.0.0",
"jquery-form": "4.3.0",
"jquery-serializeobject": "1.0.0",
@@ -83,14 +83,14 @@
"material-design-lite": "1.3.0",
"mime": "3.0.0",
"mkdirp": "1.0.4",
"mongodb": "4.12.1",
"mongodb": "4.13.0",
"morgan": "1.10.0",
"mousetrap": "1.6.5",
"multiparty": "4.2.3",
"@nodebb/bootswatch": "3.4.2",
"nconf": "0.12.0",
"nodebb-plugin-2factor": "5.1.1",
"nodebb-plugin-composer-default": "9.2.4",
"nodebb-plugin-2factor": "5.1.2",
"nodebb-plugin-composer-default": "9.2.6",
"nodebb-plugin-dbsearch": "5.1.5",
"nodebb-plugin-emoji": "4.0.6",
"nodebb-plugin-emoji-android": "3.0.0",
@@ -99,10 +99,10 @@
"nodebb-plugin-spam-be-gone": "1.0.2",
"nodebb-rewards-essentials": "0.2.1",
"nodebb-theme-lavender": "6.0.1",
"nodebb-theme-persona": "12.1.12",
"nodebb-theme-persona": "12.1.18",
"nodebb-theme-slick": "2.0.2",
"nodebb-theme-vanilla": "12.1.19",
"nodebb-widget-essentials": "6.0.0",
"nodebb-widget-essentials": "6.0.1",
"nodemailer": "6.8.0",
"nprogress": "0.2.0",
"passport": "0.6.0",
@@ -110,7 +110,7 @@
"passport-local": "1.0.0",
"pg": "8.8.0",
"pg-cursor": "2.7.4",
"postcss": "8.4.19",
"postcss": "8.4.20",
"postcss-clean": "1.2.0",
"progress-webpack-plugin": "1.0.16",
"prompt": "1.3.0",
@@ -119,15 +119,15 @@
"request-promise-native": "1.0.9",
"rimraf": "3.0.2",
"rss": "1.2.2",
"sanitize-html": "2.7.3",
"sanitize-html": "2.8.1",
"semver": "7.3.8",
"serve-favicon": "2.5.0",
"sharp": "0.31.2",
"sharp": "0.31.3",
"sitemap": "7.1.1",
"slideout": "1.0.1",
"socket.io": "4.5.4",
"socket.io-client": "4.5.4",
"@socket.io/redis-adapter": "7.2.0",
"@socket.io/redis-adapter": "8.0.0",
"sortablejs": "1.15.0",
"spdx-license-list": "6.6.0",
"spider-detector": "2.0.0",
@@ -148,11 +148,11 @@
"zxcvbn": "4.4.2"
},
"devDependencies": {
"@apidevtools/swagger-parser": "10.0.3",
"@apidevtools/swagger-parser": "9.0.0",
"@commitlint/cli": "17.3.0",
"@commitlint/config-angular": "17.3.0",
"coveralls": "3.1.1",
"eslint": "8.29.0",
"eslint": "8.30.0",
"eslint-config-nodebb": "0.2.1",
"eslint-plugin-import": "2.26.0",
"grunt": "1.5.3",
@@ -160,14 +160,14 @@
"husky": "8.0.2",
"jsdom": "20.0.3",
"lint-staged": "13.1.0",
"mocha": "10.1.0",
"mocha": "10.2.0",
"mocha-lcov-reporter": "1.3.0",
"mockdate": "3.0.5",
"nyc": "15.1.0",
"smtp-server": "3.11.0"
},
"resolutions": {
"*/jquery": "3.6.1"
"*/jquery": "3.6.3"
},
"bugs": {
"url": "https://github.com/NodeBB/NodeBB/issues"
@@ -192,4 +192,4 @@
"url": "https://github.com/barisusakli"
}
]
}
}

View File

@@ -30,9 +30,7 @@ const output = logrotate({ file: outputLogFilePath, size: '1m', keep: 3, compres
const silent = nconf.get('silent') === 'false' ? false : nconf.get('silent') !== false;
let numProcs;
const workers = [];
const Loader = {
timesStarted: 0,
};
const Loader = {};
const appPath = path.join(__dirname, 'app.js');
Loader.init = function () {
@@ -57,21 +55,6 @@ Loader.displayStartupMessages = function () {
Loader.addWorkerEvents = function (worker) {
worker.on('exit', (code, signal) => {
if (code !== 0) {
if (Loader.timesStarted < numProcs * 3) {
Loader.timesStarted += 1;
if (Loader.crashTimer) {
clearTimeout(Loader.crashTimer);
}
Loader.crashTimer = setTimeout(() => {
Loader.timesStarted = 0;
}, 10000);
} else {
console.log(`${numProcs * 3} restarts in 10 seconds, most likely an error on startup. Halting.`);
process.exit();
}
}
console.log(`[cluster] Child Process (${worker.pid}) has exited (code: ${code}, signal: ${signal})`);
if (!(worker.suicide || code === 0)) {
console.log('[cluster] Spinning up another process...');

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": "الموضوع مقفول",
@@ -88,6 +89,7 @@
"category-not-selected": "Category not selected.",
"too-many-posts": "يسمح لك بالنشر مرة كل %1 ثانية - يرجى الإنتظار قبل النشر مجدداً",
"too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again",
"already-posting": "You are already posting",
"tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)",
"tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)",
"not-enough-tags": "Not enough tags. Topics must have at least %1 tag(s)",
@@ -155,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": "Темата е заключена",
@@ -88,6 +89,7 @@
"category-not-selected": "Не е избрана категория.",
"too-many-posts": "Можете да публикувате веднъж на %1 секунда/и моля, изчакайте малко, преди да опитате да публикувате отново",
"too-many-posts-newbie": "Като нов потребител, Вие можете да публикувате веднъж на %1 секунда/и, докато не натрупате %2 репутация моля, изчакайте малко, преди да опитате да публикувате отново",
"already-posting": "You are already posting",
"tag-too-short": "Моля, въведете по-дълъг етикет. Етикетите трябва да съдържат поне %1 символ(а)",
"tag-too-long": "Моля, въведете по-кратък етикет. Етикетите трябва да съдържат не повече от %1 символ(а)",
"not-enough-tags": "Недостатъчно етикети. Темите трябва да имат поне %1 етикет(а)",
@@ -155,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": "টপিক বন্ধ",
@@ -88,6 +89,7 @@
"category-not-selected": "Category not selected.",
"too-many-posts": "You can only post once every %1 second(s) - please wait before posting again",
"too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again",
"already-posting": "You are already posting",
"tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)",
"tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)",
"not-enough-tags": "Not enough tags. Topics must have at least %1 tag(s)",
@@ -155,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",
@@ -88,6 +89,7 @@
"category-not-selected": "Nebyla vybrána kategorie.",
"too-many-posts": "Můžete přispívat jednou za %1 sekund - vyčkejte tedy, než vytvoříte další příspěvek",
"too-many-posts-newbie": "Jako nový uživatel, můžete přispívat jednou za %1 sekund, dokud nezískáte pověst %2 - vyčkejte tedy, než vytvoříte další příspěvek",
"already-posting": "You are already posting",
"tag-too-short": "Zadejte delší značku. Značky by měli mít alespoň %1 znaků",
"tag-too-long": "Zadejte kratší značku. Značky nesmí být delší než %1 znaků",
"not-enough-tags": "Málo značek. Téma musí obsahovat alespoň %1 značek",
@@ -155,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",
@@ -88,6 +89,7 @@
"category-not-selected": "Category not selected.",
"too-many-posts": "Du kan højest skrive et indlæg hver %1 sekund(er) - venligst vent et øjeblik før næste indlæg",
"too-many-posts-newbie": "Som ny bruger kan du kun skrive et indlæg engang hvert %1. sekund() indtil du har optjent %2 omdømme point - venligst vent et øjeblik før næste indlæg.",
"already-posting": "You are already posting",
"tag-too-short": "Indtast et længere tag. Tags skal indeholde mindst %1 karakter(er).",
"tag-too-long": "Indtast et længere tag. Tags kan ikke være længere end %1 karakter(er).",
"not-enough-tags": "Ikke nok tags. Tråde skal have mindst %1 tag(s)",
@@ -155,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",
@@ -88,6 +89,7 @@
"category-not-selected": "Kategorie nicht ausgewählt",
"too-many-posts": "Du kannst nur einen Beitrag innerhalb von %1 Sekunden erstellen - Bitte warte bevor Du erneut einen Beitrag erstellst.",
"too-many-posts-newbie": "Als neuer Benutzer kannst du nur einmal alle %1 Sekunde(n) posten, bis du %2 Reputation erworben hast - bitte warte, bevor du erneut postest",
"already-posting": "You are already posting",
"tag-too-short": "Bitte gebe ein längeres Schlagwort ein. Schlagworte sollten mindestens %1 Zeichen enthalten.",
"tag-too-long": "Bitte gebe ein kürzeres Schlagwort ein. Schlagworte können nicht länger als %1 Zeichen sein.",
"not-enough-tags": "Nicht genügend Schlagworte. Themen müssen mindestens %1 Schlagwort(e) enthalten",
@@ -155,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": "Το θέμα έχει κλειδωθεί",
@@ -88,6 +89,7 @@
"category-not-selected": "Category not selected.",
"too-many-posts": "You can only post once every %1 second(s) - please wait before posting again",
"too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again",
"already-posting": "You are already posting",
"tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)",
"tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)",
"not-enough-tags": "Not enough tags. Topics must have at least %1 tag(s)",
@@ -155,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",
@@ -101,6 +102,7 @@
"category-not-selected": "Category not selected.",
"too-many-posts": "You can only post once every %1 second(s) - please wait before posting again",
"too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again",
"already-posting": "You are already posting",
"tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)",
"tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)",
"not-enough-tags": "Not enough tags. Topics must have at least %1 tag(s)",
@@ -181,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",
@@ -88,6 +89,7 @@
"category-not-selected": "Category not selected.",
"too-many-posts": "You can only post once every %1 second(s) - please wait before posting again",
"too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again",
"already-posting": "You are already posting",
"tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)",
"tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)",
"not-enough-tags": "Not enough tags. Topics must have at least %1 tag(s)",
@@ -155,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",
@@ -88,6 +89,7 @@
"category-not-selected": "Category not selected.",
"too-many-posts": "You can only post once every %1 second(s) - please wait before posting again",
"too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again",
"already-posting": "You are already posting",
"tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)",
"tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)",
"not-enough-tags": "Not enough tags. Topics must have at least %1 tag(s)",
@@ -155,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",
@@ -88,6 +89,7 @@
"category-not-selected": "Categoría no seleccionada.",
"too-many-posts": "Solo puedes publicar una vez cada %1 segundo(s) - por favor espere antes de volver a publicar",
"too-many-posts-newbie": "Como nuevo usuario, solo puedes publicar una vez cada %1 segundo(s) hasta hayas ganado una reputación de %2 - por favor espera antes de volver a publicar",
"already-posting": "You are already posting",
"tag-too-short": "Por favor introduce una etiqueta más larga. Las etiquetas deben contener por lo menos %1 caractere(s)",
"tag-too-long": "Por favor introduce una etiqueta más corta. Las etiquetas no pueden exceder los %1 caractere(s)",
"not-enough-tags": "Etiquetas insuficientes. El tema debe tener al menos %1 etiqueta(s).",
@@ -155,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",
@@ -88,6 +89,7 @@
"category-not-selected": "Category not selected.",
"too-many-posts": "Te saate postitada %1 sekundi tagant - palun oodake enne uue postituse tegemist.",
"too-many-posts-newbie": "Uue kasutajana saadte postitada vaid iga %1 sekundi tagant, seniks kuni olete teeninud vähemalt %2 reputatsiooni - palun oodake enne uue postituse tegemist.",
"already-posting": "You are already posting",
"tag-too-short": "Palun sisestage pikem märksõna. Märksõna pikkus peab olema vähemalt %1 tähemärk(i).",
"tag-too-long": "Palun sisestage lühem märksõna. Märksõna pikkus peab olema vähem kui %1 tähemärk(i).",
"not-enough-tags": "Liiga vähe märksõnu. Teemadel peab olemalt vähemalt %1 märksõna",
@@ -155,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": "موضوع بسته شد.",
@@ -88,6 +89,7 @@
"category-not-selected": "هیچ دسته‌بندی انتخاب نشده.",
"too-many-posts": "شما می توانید هر %1 ثانیه یک پست ایجاد کنید - لطفا قبل از ارسال پست جدید صبر کنید",
"too-many-posts-newbie": "به عنوان یک کاربر جدید ، تا زمانی که شما %2 اعتبار کسب کنید می توانید هر %1 ثانیه یک پست ایجاد کنید - لطفا قبل از ایجاد پست جدید صبر کنید .",
"already-posting": "You are already posting",
"tag-too-short": "لطفا برچسب بلندتری وارد کنید. برچسبها باید حداقل %1 کاراکتر داشته باشند.",
"tag-too-long": "لطفا برچسب کوتاه تری وارد کنید . برچسب ها نباید بیشتر از %1 کاراکتر داشته باشند",
"not-enough-tags": "تعداد برچسب ها کافی نیست. موضوع ها یابد حداقل %1 برچسب داشته باشند",
@@ -155,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",
@@ -88,6 +89,7 @@
"category-not-selected": "Category not selected.",
"too-many-posts": "You can only post once every %1 second(s) - please wait before posting again",
"too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again",
"already-posting": "You are already posting",
"tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)",
"tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)",
"not-enough-tags": "Not enough tags. Topics must have at least %1 tag(s)",
@@ -155,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é",
@@ -88,6 +89,7 @@
"category-not-selected": "Aucune catégorie sélectionnée",
"too-many-posts": "Vous ne pouvez poster que toutes les %1 seconde(s) - merci de patienter avant de publier à nouveau.",
"too-many-posts-newbie": "En tant que nouvel utilisateur, vous ne pouvez poster que toutes les %1 seconde(s) jusqu'à ce que vous obteniez une réputation de %2 - patientez avant de publier de nouveau.",
"already-posting": "You are already posting",
"tag-too-short": "Veuillez entrer un mot-clé plus long. Les mots-clés doivent contenir au moins %1 caractère(s).",
"tag-too-long": "Veuillez entrer un mot-clé plus court. Les mot-clés ne peuvent excéder %1 caractère(s).",
"not-enough-tags": "Pas assez de mots-clés. Les sujets doivent avoir au moins %1 mots-clé(s).",
@@ -155,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",
@@ -88,6 +89,7 @@
"category-not-selected": "Categoría non seleccionada",
"too-many-posts": "Só podes postear unha vez cada %1 segundo(s) - por favor agarda antes de publicar de novo.",
"too-many-posts-newbie": "Como novo usuario, só podes publicar unha vez cada %1 segundo(s) ata que acades %2 de reputación -por favor, agarda para publicar de novo.",
"already-posting": "You are already posting",
"tag-too-short": "Por favor, introduce unha etiqueta máis longa. As etiquetas deben conter %1 carácter(es) como mínimo.",
"tag-too-long": "Por favor, introduce unha etiqueta máis curta. As etiquetas non poden conter máis de %1 carácter(es).",
"not-enough-tags": "Non hai etiquetas dabondas. Os temas deben ter %1 etiqueta(s) como mínimo.",
@@ -155,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": "נושא נעול",
@@ -88,6 +89,7 @@
"category-not-selected": "לא נבחרה קטגוריה",
"too-many-posts": "ניתן לפרסם פוסט רק פעם ב-%1 שניות - אנא המתינו לפני פרסום נוסף",
"too-many-posts-newbie": "כמשתמשים חדשים, אתם יכולים לפרסם פוסט רק פעם ב-%1 שניות עד שיהיו לכם %2 נקודות מוניטין - אנא המתינו לפני פרסום נוסף",
"already-posting": "You are already posting",
"tag-too-short": "הכניסו תגית ארוכה יותר. תגיות חייבות להכיל לפחות %1 תווים",
"tag-too-long": "הכניסו תגית קצרה יותר. תגיות יכולות להיות רק עד %1 תווים",
"not-enough-tags": "אין מספיק תגיות. נושא חייב להכיל לפחות %1 תגיות",
@@ -155,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",
@@ -88,6 +89,7 @@
"category-not-selected": "Kategorija nije odabrana.",
"too-many-posts": "Možete objavljivati svakih %1 skeundi, pričekajte prije ponovne objave",
"too-many-posts-newbie": "Kao novi korisnik, možete objavljivati svakih %1 sekundi dok ne steknete reputaciju %2 - molimo pričekajte prije ponovne objave",
"already-posting": "You are already posting",
"tag-too-short": "Unesite dužu oznaku. Oznake moraju sadržavati najmanje %1 znak(ova)",
"tag-too-long": "Unesite kraću oznaku. Oznake me mogu imati više od %1 znak(ova)",
"not-enough-tags": "Nema dovoljno oznaka. Teme moraju imate bar %1 oznaku",
@@ -155,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",
@@ -88,6 +89,7 @@
"category-not-selected": "A kategória nincs kiválasztva.",
"too-many-posts": "Csak %1 másodpercenként hozhatsz létre új bejegyzést - kérlek várj egy kicsit mielőtt új bejegyzést tennél közzé",
"too-many-posts-newbie": "Új felhasználóként csak egyszer készíthetsz bejegyzést %1 másodpercen belül, amíg el nem éred a %2 szintet - kérlek várj egy kicsit mielőtt új bejegyzést tennél közzé",
"already-posting": "You are already posting",
"tag-too-short": "Kérlek hosszabb címkét adj meg. A címke legalább %1 karaktert kell, hogy tartalmazzon",
"tag-too-long": "Kérlek rövidebb címkét adj meg. A címkék nem lehetnek hosszabbak %1 karakternél",
"not-enough-tags": "Nincs elég címke. A bejegyzésnek legalább %1 címkét kell tartalmaznia",
@@ -155,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": "Թեման փակված է",
@@ -88,6 +89,7 @@
"category-not-selected": "Կատեգորիան ընտրված չէ:",
"too-many-posts": "Դուք կարող եք գրառում անել միայն յուրաքանչյուր %1 վայրկյան(եր) մեկ անգամ. խնդրում ենք սպասել նորից գրառում անելուց առաջ",
"too-many-posts-newbie": "Որպես նոր օգտատեր, դուք կարող եք հրապարակել միայն յուրաքանչյուր %1 վայրկյան(եր) մեկ անգամ, քանի դեռ չեք վաստակել %2 վարկանիշ, խնդրում ենք սպասել՝ նորից գրառում կատարելուց առաջ:",
"already-posting": "You are already posting",
"tag-too-short": "Խնդրում ենք մուտքագրել ավելի երկար թեգ: Թեգերը պետք է պարունակեն առնվազն %1 նիշ(ներ)",
"tag-too-long": "Խնդրում ենք մուտքագրել ավելի կարճ թեգ: Թեգերը չեն կարող ավելի երկար լինել, քան %1 նիշ(ներ)",
"not-enough-tags": "Ոչ բավարար թեգեր: Թեմաները պետք է ունենան առնվազն %1 թեգ(ներ)",
@@ -155,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",
@@ -88,6 +89,7 @@
"category-not-selected": "Category not selected.",
"too-many-posts": "Anda hanya dapat memposting sekali setiap %1 detik() - harap tunggu sebelum memposting lagi",
"too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again",
"already-posting": "You are already posting",
"tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)",
"tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)",
"not-enough-tags": "Not enough tags. Topics must have at least %1 tag(s)",
@@ -155,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",
@@ -88,6 +89,7 @@
"category-not-selected": "Categoria non selezionata.",
"too-many-posts": "È possibile inserire un Post ogni %1 secondi - si prega di attendere prima di postare di nuovo",
"too-many-posts-newbie": "Come nuovo utente puoi postare solamente una volta ogni %1 secondi finché non hai raggiunto un livello di reputazione %2 - per favore attendi prima di scrivere ancora",
"already-posting": "You are already posting",
"tag-too-short": "Inserisci un tag più lungo. I tag devono contenere almeno %1 caratteri.",
"tag-too-long": "Per favore inserisci un tag più corto. I tags non dovrebbero essere più lunghi di %1 caratteri",
"not-enough-tags": "Tag non sufficienti. Le discussioni devono avere almeno %1 Tag",
@@ -155,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": "スレッドがロックされた",
@@ -88,6 +89,7 @@
"category-not-selected": "カテゴリが選択されていません。",
"too-many-posts": "あなたは%1秒間に一つの投稿しか許されます少し待ってまた投稿してください",
"too-many-posts-newbie": "あなたは%2評判を得ているまで、新しいユーザーとしては、一度だけごとに%1秒を投稿することができます - 再び投稿する前にお待ちください",
"already-posting": "You are already posting",
"tag-too-short": "%1文字(s)以上でタグを入力してください。",
"tag-too-long": "%1文字(s)以内でタグを入力してください。",
"not-enough-tags": "タグが足りません。スレッドはせめて%1のタグ(s)が必要です。",
@@ -155,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": "게시물이 잠금 상태입니다.",
@@ -88,6 +89,7 @@
"category-not-selected": "선택된 카테고리가 없습니다.",
"too-many-posts": "새 게시물 작성은 %1초마다 가능합니다. 조금 천천히 작성해주세요.",
"too-many-posts-newbie": "신규 사용자는 %2만큼의 인지도를 얻기 전까지 %1초마다 게시물을 작성할 수 있습니다. 조금 천천히 작성해주세요.",
"already-posting": "You are already posting",
"tag-too-short": "태그가 너무 짧습니다. 태그는 최소 %1자 이상이어야 합니다.",
"tag-too-long": "태그가 너무 깁니다. 태그는 최대 %1자 이내로 사용 가능합니다.",
"not-enough-tags": "태그가 없거나 부족합니다. 게시물은 %1개 이상의 태그를 사용해야 합니다.",
@@ -155,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",
@@ -88,6 +89,7 @@
"category-not-selected": "Nepasirinkta kategorija.",
"too-many-posts": "Jus galite rašyti kas %1 sekunde(s) - prašome palaukti prieš rašant dar kartą",
"too-many-posts-newbie": "Kadangi esate naujas narys, jūs galite tik rašyti kas %1 sekunde(s) kol jūs pasieksite %2 reputacija - prašome palaukti prieš rašant dar kartą",
"already-posting": "You are already posting",
"tag-too-short": "Prašome įvesti ilgesnę žymą. Žyma turi sudaryti mažiausiai %1 simboli(us)",
"tag-too-long": "Prašome įvesti trumpesnę žymą. Žyma turi būti ne ilgesni negu %1 simboli(us)",
"not-enough-tags": "Neužteka žymių. Temos turi turėti mažiausiai %1 žyme(s)",
@@ -155,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",
@@ -88,6 +89,7 @@
"category-not-selected": "Kategorija nav atlasīta.",
"too-many-posts": "Var publicēt tikai vienu rakstu katras %1 sekundes - lūdzu, uzgaidi, pirms publicē vēlreiz",
"too-many-posts-newbie": "Jauni lietotāji var ievietot tikai vienu rakstu katras %1 sekundes, līdz ir nopelnīti %2 ranga punkti - lūdzu, uzgaidi, pirms publicē vēlreiz",
"already-posting": "You are already posting",
"tag-too-short": "Lūdzu, ievadi garāku birku. Birkā jāsatur vismaz %1 rakstzīmes.",
"tag-too-long": "Lūdzu, ievadi īsāku birku. Birkā nevar būt vairāk kā %1 rakstzīmes.",
"not-enough-tags": "Nav pietiekami daudz birku. Tematiem jābūt vismaz %1 birkām",
@@ -155,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",
@@ -88,6 +89,7 @@
"category-not-selected": "Category not selected.",
"too-many-posts": "Anda hanya boleh mengirim sekali setiap %1 saat() - sila tunggu sebelum kiriman seterusnya",
"too-many-posts-newbie": "Sebagai pengguna baru, anda hanya boleh mengirim sekali setiap %1 saat() sehinnga anda mendapat %2 reputasi - sila tunggu sebelum kiriman seterusnya",
"already-posting": "You are already posting",
"tag-too-short": "Sila masukkan tag yang lebih panjang. Tag mesti mengandungi sekurang-kurangnya %1 aksara()",
"tag-too-long": "Sila masukkan tag yang lebih pendek. Tag mesti mengandungi tidak lebih %1 aksara()",
"not-enough-tags": "Tag tidak mencukupi. Topik memerlukan sekurang-kurangnya %1 tag()",
@@ -155,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",
@@ -88,6 +89,7 @@
"category-not-selected": "Kategori ikke valgt",
"too-many-posts": "Du kan bare poste en gang per %1 sekund(er) vennligst vent før du poster igjen",
"too-many-posts-newbie": "Som ny bruker kan du bare poste en gang per %1. sekund(er), før du har opparbeidet %2 i omdømme vennligst vent før du poster igjen",
"already-posting": "You are already posting",
"tag-too-short": "Vennligst skriv et lengre emneord. Disse må være på minst %1 tegn",
"tag-too-long": "Vennligst skriv et kortere emneord. Disse kan ikke være lengre enn %1 tegn",
"not-enough-tags": "Ikke nok emneord. Emner må ha minst %1.",
@@ -155,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",
@@ -88,6 +89,7 @@
"category-not-selected": "Categorie niet geselecteerd ",
"too-many-posts": "Het is slechts toegestaan iedere %1 seconde(n) een bericht te plaatsen - wacht even voordat opnieuw een bericht verzonden wordt",
"too-many-posts-newbie": "Nieuwe gebruikersaccounts zoals deze zijn begrensd en mogen slechts iedere %1 seconde(n) berichten plaatsen, tot het moment dat %2 reputatie verdiend is - wacht daarom even met opnieuw een bericht te plaatsten",
"already-posting": "You are already posting",
"tag-too-short": "Geef een tag op die uit meer tekens bestaat. Tags dienen uit minimaal %1 teken(s) te bestaan.",
"tag-too-long": "Geef een kortere tag op. Tags mogen niet langer dan %1 teken(s) zijn",
"not-enough-tags": "Niet genoeg labels. Onderwerp moeten tenminste %1 label(s) hebben",
@@ -155,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",
@@ -88,6 +89,7 @@
"category-not-selected": "Nie wybrano kategorii.",
"too-many-posts": "Możesz publikować posty raz na %1 sekund poczekaj, zanim dodasz kolejny post",
"too-many-posts-newbie": "Jako nowy użytkownik możesz publikować posty raz na %1 sekund, dopóki nie zdobędziesz reputacji na poziomie %2 poczekaj, zanim dodasz kolejny post",
"already-posting": "You are already posting",
"tag-too-short": "Wprowadź dłuższy tag. Tagi muszą mieć przynajmniej %1 znak(-ów)",
"tag-too-long": "Wprowadź krótszy tag. Tagi nie mogą mieć więcej niż %1 znak(-ów)",
"not-enough-tags": "Zbyt mało tagów. Tematy muszą posiadać przynajmniej %1 tag(ów)",
@@ -155,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",
@@ -88,6 +89,7 @@
"category-not-selected": "Categoria não escolhida.",
"too-many-posts": "Você pode postar uma vez a cada %1 segundo(s) - por favor aguarde antes de postar novamente",
"too-many-posts-newbie": "Como novo usuário, você só pode postar uma vez a cada %1 segundo(s) até que você tenha, pelo menos, %2 de reputação. Por favor, aguarde antes de postar novamente.",
"already-posting": "You are already posting",
"tag-too-short": "Por favor digite uma tag maior. Tags devem conter pelo menos %1 caractere(s)",
"tag-too-long": "Por favor digite uma tag menor. Tags não podem conter mais que %1 caractere(s)",
"not-enough-tags": "Sem tags suficientes. Tópicos devem ter no mínimo %1 tag(s)",
@@ -155,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",
@@ -88,6 +89,7 @@
"category-not-selected": "Categoria não selecionada.",
"too-many-posts": "Só podes publicar a cada %1 segundo(s) - por favor espera até poderes publicar outra vez",
"too-many-posts-newbie": "Como novo utilizador, só podes publicar a cada %1 segundo(s) até teres conquistado %2 de reputação - por favor espera até poderes publicar outra vez",
"already-posting": "You are already posting",
"tag-too-short": "Por favor introduz um marcador maior. Os marcadores devem ter pelo menos %1 caracter(s)",
"tag-too-long": "Por favor introduz um marcador mais curto. Os marcadores devem ter no máximo %1 caracter(es)",
"not-enough-tags": "Não existem marcadores suficientes. Os tópicos devem ter pelo menos %1 marcador(es)",
@@ -155,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",
@@ -88,6 +89,7 @@
"category-not-selected": "Category not selected.",
"too-many-posts": "You can only post once every %1 second(s) - please wait before posting again",
"too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again",
"already-posting": "You are already posting",
"tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)",
"tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)",
"not-enough-tags": "Not enough tags. Topics must have at least %1 tag(s)",
@@ -155,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": "Тема закрыта",
@@ -88,6 +89,7 @@
"category-not-selected": "Категория не выбрана",
"too-many-posts": "Для того, чтобы разместить новое сообщение, нужно подождать %1 сек.",
"too-many-posts-newbie": "Для того, чтобы разместить новое сообщение, нужно подождать %1 сек. Это время уменьшится, как только ваша репутация вырастет до %2.",
"already-posting": "You are already posting",
"tag-too-short": "Слишком короткая метка. Минимум %1 символов.",
"tag-too-long": "Слишком длинная метка. Максимум %1 символов.",
"not-enough-tags": "Пожалуйста, добавьте метки в ваше сообщение. У темы должно быть минимум %1 меток.",
@@ -155,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",
@@ -88,6 +89,7 @@
"category-not-selected": "Category not selected.",
"too-many-posts": "Wemerewe kugira icyo ushyiraho rimwe mu masegonda (isegonda) %1. Ba utegerejeho gato kugirango wongere",
"too-many-posts-newbie": "Nk'umuntu mushya, wemerewe gushyiraho ikintu rimwe mu masegonda (isegonda) %1 kugeza igihe ugize amanota agera kuri %2. Ba utegerejeho gato kugirango wongere",
"already-posting": "You are already posting",
"tag-too-short": "Gerageza ukoreshe akamenyetso kagizwe n'inyuguti (cyangwa ibimenyetso) nibura zigera kuri %1",
"tag-too-long": "Gerageza ukoreshe akamenyetso kagizwe n'inyuguti (cyangwa ibimenyetso) zitarenze %1",
"not-enough-tags": "Nta tumenyetso turiho duhagije. Ibiganiro bigomba kugira utumenyetso (akamenyetso) nibura %1",
@@ -155,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",
@@ -88,6 +89,7 @@
"category-not-selected": "Category not selected.",
"too-many-posts": "You can only post once every %1 second(s) - please wait before posting again",
"too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again",
"already-posting": "You are already posting",
"tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)",
"tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)",
"not-enough-tags": "Not enough tags. Topics must have at least %1 tag(s)",
@@ -155,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á",
@@ -88,6 +89,7 @@
"category-not-selected": "Kategória nebola vybratá.",
"too-many-posts": "Môžete uverejniť príspevok každých %1 sekúnd(y) - prosím počkajte pred opätovným zverejnením",
"too-many-posts-newbie": "Ako nový užívateľ, môžete uverejniť príspevok raz za %1 sekúnd(y) pokiaľ nezískate %2 reputáciu - prosím, počkajte pred ďalším uverejnením",
"already-posting": "You are already posting",
"tag-too-short": "Prosím, zadajte dlhšiu značku. Značky by mali obsahovať najmenej %1 znak(ov)",
"tag-too-long": "Prosím, zadajte kratšiu značku. Značky nemôžu obsahovať viac ako %1 znak(ov)",
"not-enough-tags": "Príliš malo značiek. Témy musia mať minimálne %1 značku(y)",
@@ -155,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.",
@@ -88,6 +89,7 @@
"category-not-selected": "Category not selected.",
"too-many-posts": "Objavljate lahko na %1 s - prosimo, počakajte pred novo objavo.",
"too-many-posts-newbie": "Kot nov uporabnik lahko objavljate le na %1 s, dokler ne dosežete ugled vsaj %2 - prosimo, počakajte pred novo objavo.",
"already-posting": "You are already posting",
"tag-too-short": "Prosimo, vnesite daljšo oznako. Obvezno število znakov: vsaj %1.",
"tag-too-long": "Prosimo, vnesite krajšo oznako. Največje število znakov: %1.",
"not-enough-tags": "Ni dovolj oznak. Obvezno število oznak: %1. ",
@@ -155,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",
@@ -88,6 +89,7 @@
"category-not-selected": "Kategoria nuk është zgjedhur.",
"too-many-posts": "Mund të postoni vetëm një herë në %1 sekond(a) - ju lutemi prisni përpara se të postoni përsëri",
"too-many-posts-newbie": "Si përdorues i ri, ju mund të postoni vetëm një herë në %1 sekond(a) derisa të keni fituar %2 reputacion - ju lutemi prisni përpara se të postoni përsëri",
"already-posting": "You are already posting",
"tag-too-short": "Ju lutemi vendosni një tag më të gjatë. Tag-et duhet të përmbajnë të paktën %1 karakter(e)",
"tag-too-long": "Ju lutemi vendosni një tag më të shkurtër. Tag-et nuk mund të jenë më të gjata se %1 karakter(e)",
"not-enough-tags": "Numër jo i mjaftueshëm i tag-eve. Temat duhet të kenë të paktën %1 tag(-e)",
@@ -155,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": "Тема је закључана",
@@ -88,6 +89,7 @@
"category-not-selected": "Није одабрана категорија",
"too-many-posts": "Можете објављивати поруке само једном у %1 секунди - сачекајте пре него што покушате поново",
"too-many-posts-newbie": "Као нови корисник, можете објављивати поруке само једном у %1 секунди док не достигнете %2 углед - сачекајте пре него што покушате поново",
"already-posting": "You are already posting",
"tag-too-short": "Унесите дужу ознаку. Ознаке морају садржати најмање %1 знак(ов)а.",
"tag-too-long": "Унесите краћу ознаку. Ознаке не смеју бити дуже од %1 знак(ов)а.",
"not-enough-tags": "Нема довољно ознака. Теме морају имати најмање %1 ознаке/а.",
@@ -155,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",
@@ -88,6 +89,7 @@
"category-not-selected": "Kategori Ej vald.",
"too-many-posts": "Du måste vänta minst %1 sekund(er) mellan varje inlägg",
"too-many-posts-newbie": "Som ny användare måste du vänta %1 sekund(er) mellan varje inlägg tills dess du har %2 förtroende",
"already-posting": "You are already posting",
"tag-too-short": "Fyll i en längre tagg. Taggar måste vara minst %1 tecken långa",
"tag-too-long": "Fyll i en kortare tagg. Taggar kan ej vara längre än %1 tecken långa",
"not-enough-tags": "Otillräckligt antal taggar. Ämnen måste ha minst %1 taggar",
@@ -155,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": "กระทู้ถูกล็อก",
@@ -88,6 +89,7 @@
"category-not-selected": "ไม่มีการเลือกหมวดหมู่",
"too-many-posts": "คุณสามารถโพสต์ได้เพียงครั้งเดียวเท่านั้นในทุกๆ %1 วินาที(s) - โปรดรอสักครู่ก่อนการโพสต์อีกครั้ง",
"too-many-posts-newbie": "เนื่องด้วยการเป็นผู้ใช้งานใหม่ คุณสามารถโพสต์ได้เพียงครั้งเดียวเท่านั้นในทุกๆ %1 วินาที(s) จนกว่าคุณจะได้รับ %2 ชื่อเสียง - โปรดรอสักครู่ก่อนการโพสต์อีกครั้ง",
"already-posting": "You are already posting",
"tag-too-short": "กรุณากรอกแท็กให้ยาวขึ้น แท็กควรมีข้อความอย่างน้อย %1 ตัวอักษร(s)",
"tag-too-long": "กรุณากรอกแท็กให้สั้นลง แท็กไม่สามารถยาวกว่า %1 ตัวอักษร(s)",
"not-enough-tags": "จำนวนแท็กไม่พอ กระทู้ต้องมีอย่างน้อย %1 แท็ก(s)",
@@ -155,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",
@@ -88,6 +89,7 @@
"category-not-selected": "Kategori bulunamadı. Lütfen bir kategori seçiniz. ",
"too-many-posts": "%1 saniye içinde yalnızca bir ileti gönderebilirsiniz - lütfen tekrar ileti göndermeden önce bekleyiniz.",
"too-many-posts-newbie": "Yeni bir kullanıcı olarak, %2 saygınlık puanı kazanana kadar %1 saniye içinde bir ileti gönderebilirsiniz - lütfen tekrar ileti göndermeden önce bekleyiniz.",
"already-posting": "You are already posting",
"tag-too-short": "Lütfen daha uzun bir etiket girin. Etiketler en az %1 karakter içermelidir.",
"tag-too-long": "Lütfen daha kısa bir etiket girin. Etiketler %1 karakterden uzun olamaz.",
"not-enough-tags": "Yeterince etiket yok. Başlılar en az %1 etikete sahip olmalıdır",
@@ -155,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": "Тему заблоковано",
@@ -88,6 +89,7 @@
"category-not-selected": "Категорію не вибрано.",
"too-many-posts": "Ви не можете постити частіше %1 секунд(и) — зачекайте, будь ласка, перед повторною спробою",
"too-many-posts-newbie": "Як новий користувач, ви не можете публікувати частіше %1 секунд(и) доки не заробите %2 репутації — зачекайте, будь ласка, перед повторною спробою",
"already-posting": "You are already posting",
"tag-too-short": "Введіть, будь ласка, довший тег. Мінімальна довжина тегу %1 символ(ів)",
"tag-too-long": "Введіть, будь ласка, коротший тег. Максимальна довжина тегу %1 символ(ів)",
"not-enough-tags": "Замало тегів. Тема повинна мати щонайменше %1 тег(и)",
@@ -155,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",
@@ -88,6 +89,7 @@
"category-not-selected": "Chưa chọn category",
"too-many-posts": "Bạn chỉ có đăng bài mới mỗi %1 giây - vui lòng đợi để tiếp tục đăng bài.",
"too-many-posts-newbie": "Là người dùng mới, bạn chỉ có thể đăng %1 giây một lần cho đến khi bạn đạt được %2 danh tiếng - vui lòng đợi trước khi đăng lại",
"already-posting": "You are already posting",
"tag-too-short": "Vui lòng nhập tag dài hơn. Tag phải có tối thiểu %1 ký tự.",
"tag-too-long": "Vui lòng nhập tag ngắn hơn. Tag chỉ có thể có tối đa %1 ký tự.",
"not-enough-tags": "Không đủ thẻ. Chủ đề phải có ít nhất %1 thẻ.",
@@ -155,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": "主题已锁定",
@@ -88,6 +89,7 @@
"category-not-selected": "未选择版块。",
"too-many-posts": "发帖需要间隔 %1 秒以上 - 请稍候再发帖",
"too-many-posts-newbie": "因为您是新用户,所以限制每隔 %1 秒才能发帖一次,直到您有 %2 点声望为止 —— 请稍候再发帖",
"already-posting": "You are already posting",
"tag-too-short": "标签太短,不能少于 %1 个字符",
"tag-too-long": "标签太长,不能超过 %1 个字符",
"not-enough-tags": "没有足够的标签。主题必须至少有 %1 个标签。",
@@ -155,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": "主題已鎖定",
@@ -88,6 +89,7 @@
"category-not-selected": "未選擇版面。",
"too-many-posts": "貼文需要間隔 %1 秒以上 - 請稍候再發文",
"too-many-posts-newbie": "因為您是新使用者,所以限制每隔 %1 秒才能發文一次,直到您有 %2 點聲望為止 —— 請稍候再發文",
"already-posting": "You are already posting",
"tag-too-short": "標籤太短,不能少於 %1 個字元",
"tag-too-long": "標籤太長,不能超過 %1 個字元",
"not-enough-tags": "沒有足夠的主題標籤。主題必須至少有 %1 個標籤",
@@ -155,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

@@ -265,6 +265,9 @@ TopicObjectSlim:
name:
type: string
description: The topic thumbnail filename
path:
type: string
description: Path to topic thumbnail without upload_url prefix
url:
type: string
description: Relative path to the topic thumbnail

View File

@@ -374,6 +374,20 @@ get:
type: string
postIndex:
type: number
author:
type: object
required: [username, uid]
properties:
username:
type: string
userslug:
type: string
uid:
type: number
fullname:
type: string
displayname:
type: string
loggedInUser:
$ref: ../../components/schemas/UserObject.yaml#/UserObject
- type: object

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

@@ -31,6 +31,8 @@ get:
type: string
name:
type: string
path:
type: string
url:
type: string
description: Path to a topic thumbnail
@@ -155,6 +157,8 @@ delete:
type: string
name:
type: string
path:
type: string
url:
type: string
description: Path to a topic thumbnail

View File

@@ -38,4 +38,4 @@ put:
$ref: ../../../../components/schemas/Status.yaml#/Status
response:
type: object
properties: {}
properties: {}

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

@@ -88,7 +88,11 @@ define('forum/topic', [
});
}
mousetrap.bind('j', () => {
mousetrap.bind('j', (e) => {
if (e.target.classList.contains('mousetrap')) {
return;
}
const index = navigator.getIndex();
const count = navigator.getCount();
if (index === count) {
@@ -98,7 +102,11 @@ define('forum/topic', [
navigator.scrollToIndex(index, true, 0);
});
mousetrap.bind('k', () => {
mousetrap.bind('k', (e) => {
if (e.target.classList.contains('mousetrap')) {
return;
}
const index = navigator.getIndex();
if (index === 1) {
return;

View File

@@ -24,7 +24,7 @@ define('forum/topic/images', [], function () {
if (!$this.parent().is('a')) {
$this.wrap('<a href="' + src + '" ' +
(!srcExt && altExt ? ' download="' + altFilename + '" ' : '') +
(!srcExt && altExt ? ' download="' + utils.escapeHTML(altFilename) + '" ' : '') +
' target="_blank" rel="noopener">');
}
});

View File

@@ -41,7 +41,7 @@ define('forum/topic/postTools', [
const pid = postEl.attr('data-pid');
const index = parseInt(postEl.attr('data-index'), 10);
socket.emit('posts.loadPostTools', { pid: pid, cid: ajaxify.data.cid }, async (err, data) => {
socket.emit('posts.loadPostTools', { pid: pid }, async (err, data) => {
if (err) {
return alerts.error(err);
}

View File

@@ -518,7 +518,6 @@ define('settings', ['hooks', 'alerts'], function (hooks, alerts) {
formEl = $(formEl);
const controls = formEl.get(0).elements;
console.log(controls);
const ok = Settings.check(controls);
if (!ok) {
return;

View File

@@ -15,6 +15,9 @@ app = window.app || {};
reconnectionDelay: config.reconnectionDelay,
transports: config.socketioTransports,
path: config.relative_path + '/socket.io',
query: {
_csrf: config.csrf_token,
},
};
window.socket = io(config.websocketAddress, ioParams);

View File

@@ -237,23 +237,26 @@ Analytics.getDailyStatsForSet = async function (set, day, numDays) {
set = `analytics:${set}`;
}
const daysArr = [];
day = new Date(day);
// set the date to tomorrow, because getHourlyStatsForSet steps *backwards* 24 hours to sum up the values
day.setDate(day.getDate() + 1);
day.setHours(0, 0, 0, 0);
while (numDays > 0) {
/* eslint-disable no-await-in-loop */
async function getHourlyStats(hour) {
const dayData = await Analytics.getHourlyStatsForSet(
set,
day.getTime() - (1000 * 60 * 60 * 24 * (numDays - 1)),
hour,
24
);
daysArr.push(dayData.reduce((cur, next) => cur + next));
return dayData.reduce((cur, next) => cur + next);
}
const hours = [];
while (numDays > 0) {
hours.push(day.getTime() - (1000 * 60 * 60 * 24 * (numDays - 1)));
numDays -= 1;
}
return daysArr;
return await Promise.all(hours.map(getHourlyStats));
};
Analytics.getUnwrittenPageviews = function () {

View File

@@ -307,18 +307,17 @@ async function isPrivilegedOrSelfAndPasswordMatch(caller, data) {
async function processDeletion({ uid, method, password, caller }) {
const isTargetAdmin = await user.isAdministrator(uid);
const isSelf = parseInt(uid, 10) === parseInt(caller.uid, 10);
const isAdmin = await user.isAdministrator(caller.uid);
const hasAdminPrivilege = await privileges.admin.can('admin:users', caller.uid);
if (isSelf && meta.config.allowAccountDelete !== 1) {
throw new Error('[[error:account-deletion-disabled]]');
} else if (!isSelf && !isAdmin) {
} else if (!isSelf && !hasAdminPrivilege) {
throw new Error('[[error:no-privileges]]');
} else if (isTargetAdmin) {
throw new Error('[[error:cant-delete-admin]');
}
// Privilege checks -- only deleteAccount is available for non-admins
const hasAdminPrivilege = await privileges.admin.can('admin:users', caller.uid);
if (!hasAdminPrivilege && ['delete', 'deleteContent'].includes(method)) {
throw new Error('[[error:no-privileges]]');
}
@@ -444,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

@@ -95,11 +95,9 @@ module.exports = function (Categories) {
await privileges.categories.give(result.modPrivileges, category.cid, ['administrators', 'Global Moderators']);
await privileges.categories.give(result.guestPrivileges, category.cid, ['guests', 'spiders']);
cache.del([
'categories:cid',
`cid:${parentCid}:children`,
`cid:${parentCid}:children:all`,
]);
cache.del('categories:cid');
await clearParentCategoryCache(parentCid);
if (data.cloneFromCid && parseInt(data.cloneFromCid, 10)) {
category = await Categories.copySettingsFrom(data.cloneFromCid, category.cid, !data.parentCid);
}
@@ -112,6 +110,22 @@ module.exports = function (Categories) {
return category;
};
async function clearParentCategoryCache(parentCid) {
while (parseInt(parentCid, 10) >= 0) {
cache.del([
`cid:${parentCid}:children`,
`cid:${parentCid}:children:all`,
]);
if (parseInt(parentCid, 10) === 0) {
return;
}
// clear all the way to root
// eslint-disable-next-line no-await-in-loop
parentCid = await Categories.getCategoryField(parentCid, 'parentCid');
}
}
async function duplicateCategoriesChildren(parentCid, cid, uid) {
let children = await Categories.getChildren([cid], uid);
if (!children.length) {

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,31 +98,7 @@ Categories.getModerators = async function (cid) {
};
Categories.getModeratorUids = async function (cids) {
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) => _.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

@@ -51,6 +51,16 @@ try {
packageInstall.preserveExtraneousPlugins();
packageInstall.installAll();
// delete the module from require cache so it doesn't break rest of the upgrade
// https://github.com/NodeBB/NodeBB/issues/11173
const packages = ['nconf', 'async', 'commander', 'chalk', 'lodash', 'lru-cache'];
packages.forEach((packageName) => {
const resolvedModule = require.resolve(packageName);
if (require.cache[resolvedModule]) {
delete require.cache[resolvedModule];
}
});
const chalk = require('chalk');
console.log(`${chalk.green('OK')}\n`);
} else {
@@ -175,9 +185,10 @@ program
program
.command('install [plugin]')
.description('Launch the NodeBB web installer for configuration setup or install a plugin')
.action((plugin) => {
.option('-f, --force', 'Force plugin installation even if it may be incompatible with currently installed NodeBB version')
.action((plugin, options) => {
if (plugin) {
require('./manage').install(plugin);
require('./manage').install(plugin, options);
} else {
require('./setup').webInstall();
}

View File

@@ -14,7 +14,10 @@ const analytics = require('../analytics');
const reset = require('./reset');
const { pluginNamePattern, themeNamePattern, paths } = require('../constants');
async function install(plugin) {
async function install(plugin, options) {
if (!options) {
options = {};
}
try {
await db.init();
if (!pluginNamePattern.test(plugin)) {
@@ -31,7 +34,11 @@ async function install(plugin) {
const nbbVersion = require(paths.currentPackage).version;
const suggested = await plugins.suggest(plugin, nbbVersion);
if (!suggested.version) {
throw new Error(suggested.message);
if (!options.force) {
throw new Error(suggested.message);
}
winston.warn(`${suggested.message} Proceeding with installation anyway due to force option being provided`);
suggested.version = 'latest';
}
winston.info('Installing Plugin `%s@%s`', plugin, suggested.version);
await plugins.toggleInstall(plugin, suggested.version);

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

@@ -128,12 +128,13 @@ async function getStats() {
}
let results = await Promise.all([
getStatsForSet('ip:recent', 'uniqueIPCount'),
getStatsFromAnalytics('uniquevisitors', 'uniqueIPCount'),
getStatsFromAnalytics('logins', 'loginCount'),
getStatsForSet('users:joindate', 'userCount'),
getStatsForSet('posts:pid', 'postCount'),
getStatsForSet('topics:tid', 'topicCount'),
]);
results[0].name = '[[admin/dashboard:unique-visitors]]';
results[1].name = '[[admin/dashboard:logins]]';

View File

@@ -1,5 +1,6 @@
'use strict';
const validator = require('validator');
const db = require('../../database');
const events = require('../../events');
const pagination = require('../../pagination');
@@ -39,6 +40,12 @@ eventsController.get = async function (req, res) {
events: eventData,
pagination: pagination.create(page, pageCount, req.query),
types: types,
query: req.query,
query: {
start: validator.escape(String(req.query.start)),
end: validator.escape(String(req.query.end)),
username: validator.escape(String(req.query.username)),
group: validator.escape(String(req.query.group)),
perPage: validator.escape(String(req.query.perPage)),
},
});
};

View File

@@ -119,11 +119,49 @@ uploadsController.uploadCategoryPicture = async function (req, res, next) {
}
if (validateUpload(res, uploadedFile, allowedImageTypes)) {
if (uploadedFile.path.endsWith('.svg')) {
await sanitizeSvg(uploadedFile.path);
}
const filename = `category-${params.cid}${path.extname(uploadedFile.name)}`;
await uploadImage(filename, 'category', uploadedFile, req, res, next);
}
};
async function sanitizeSvg(filePath) {
const dirty = await fs.promises.readFile(filePath, 'utf8');
const clean = sanitizeHtml(dirty, {
allowedTags: [
'svg', 'g', 'defs', 'linearGradient', 'radialGradient', 'stop',
'circle', 'ellipse', 'polygon', 'polyline', 'path', 'rect',
'line', 'text', 'tspan', 'use', 'symbol', 'clipPath', 'mask', 'pattern',
'filter', 'feGaussianBlur', 'feOffset', 'feBlend', 'feColorMatrix', 'feMerge', 'feMergeNode',
],
allowedAttributes: {
'*': [
// Geometry
'x', 'y', 'x1', 'x2', 'y1', 'y2', 'cx', 'cy', 'r', 'rx', 'ry',
'width', 'height', 'd', 'points', 'viewBox', 'transform',
// Presentation
'fill', 'stroke', 'stroke-width', 'opacity',
'stop-color', 'stop-opacity', 'offset', 'style', 'class',
// Text
'text-anchor', 'font-size', 'font-family',
// Misc
'id', 'clip-path', 'mask', 'filter', 'gradientUnits', 'gradientTransform',
'xmlns', 'preserveAspectRatio',
],
},
parser: {
lowerCaseTags: false,
lowerCaseAttributeNames: false,
},
});
await fs.promises.writeFile(filePath, clean);
}
uploadsController.uploadFavicon = async function (req, res, next) {
const uploadedFile = req.files.files[0];
const allowedTypes = ['image/x-icon', 'image/vnd.microsoft.icon'];
@@ -184,10 +222,6 @@ uploadsController.uploadMaskableIcon = async function (req, res, next) {
}
};
uploadsController.uploadLogo = async function (req, res, next) {
await upload('site-logo', req, res, next);
};
uploadsController.uploadFile = async function (req, res, next) {
const uploadedFile = req.files.files[0];
let params;
@@ -208,6 +242,10 @@ uploadsController.uploadFile = async function (req, res, next) {
}
};
uploadsController.uploadLogo = async function (req, res, next) {
await upload('site-logo', req, res, next);
};
uploadsController.uploadDefaultAvatar = async function (req, res, next) {
await upload('avatar-default', req, res, next);
};
@@ -220,6 +258,9 @@ async function upload(name, req, res, next) {
const uploadedFile = req.files.files[0];
if (validateUpload(res, uploadedFile, allowedImageTypes)) {
if (uploadedFile.path.endsWith('.svg')) {
await sanitizeSvg(uploadedFile.path);
}
const filename = name + path.extname(uploadedFile.name);
await uploadImage(filename, 'system', uploadedFile, req, res, next);
}

View File

@@ -9,6 +9,7 @@ const categories = require('../categories');
const plugins = require('../plugins');
const translator = require('../translator');
const languages = require('../languages');
const { generateToken } = require('../middleware/csrf');
const apiController = module.exports;
@@ -64,7 +65,7 @@ apiController.loadConfig = async function (req) {
'cache-buster': meta.config['cache-buster'] || '',
topicPostSort: meta.config.topicPostSort || 'oldest_to_newest',
categoryTopicSort: meta.config.categoryTopicSort || 'newest_to_oldest',
csrf_token: req.uid >= 0 && req.csrfToken && req.csrfToken(),
csrf_token: req.uid >= 0 ? generateToken(req) : undefined,
searchEnabled: plugins.hooks.hasListeners('filter:search.query'),
searchDefaultInQuick: meta.config.searchDefaultInQuick || 'titles',
bootswatchSkin: meta.config.bootswatchSkin || '',

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);
};
@@ -383,7 +383,7 @@ authenticationController.onSuccessfulLogin = async function (req, uid) {
}),
user.auth.addSession(uid, req.sessionID),
user.updateLastOnlineTime(uid),
user.updateOnlineUsers(uid),
user.onUserOnline(uid, Date.now()),
analytics.increment('logins'),
db.incrObjectFieldBy('global', 'loginCount', 1),
]);

View File

@@ -1,6 +1,10 @@
'use strict';
const _ = require('lodash');
const validator = require('validator');
const user = require('../user');
const groups = require('../groups');
const posts = require('../posts');
const flags = require('../flags');
const analytics = require('../analytics');
@@ -38,9 +42,9 @@ modsController.flags.list = async function (req, res) {
filters = filters.reduce((memo, cur) => {
if (req.query.hasOwnProperty(cur)) {
if (typeof req.query[cur] === 'string' && req.query[cur].trim() !== '') {
memo[cur] = req.query[cur].trim();
memo[cur] = validator.escape(String(req.query[cur].trim()));
} else if (Array.isArray(req.query[cur]) && req.query[cur].length) {
memo[cur] = req.query[cur];
memo[cur] = req.query[cur].map(item => validator.escape(String(item).trim()));
}
}
@@ -110,7 +114,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 +122,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 +153,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 && (
@@ -141,6 +166,7 @@ modsController.flags.detail = async function (req, res, next) {
return memo;
}, {}),
states: Object.fromEntries(flags._states),
title: `[[pages:flag-details, ${req.params.flagId}]]`,
privileges: results.privileges,
breadcrumbs: helpers.buildBreadcrumbs([

View File

@@ -105,8 +105,8 @@ topicsController.get = async function getTopic(req, res, next) {
topicData.postIndex = postIndex;
await Promise.all([
buildBreadcrumbs(topicData),
const [author] = await Promise.all([
user.getUserFields(topicData.uid, ['username', 'userslug']),
addOldCategory(topicData, userPrivileges),
addTags(topicData, req, res),
incrementViewCount(req, tid),
@@ -114,6 +114,7 @@ topicsController.get = async function getTopic(req, res, next) {
analytics.increment([`pageviews:byCid:${topicData.category.cid}`]),
]);
topicData.author = author;
topicData.pagination = pagination.create(currentPage, pageCount, req.query);
topicData.pagination.rel.forEach((rel) => {
rel.href = `${url}/topic/${topicData.slug}${rel.href}`;

View File

@@ -2,6 +2,7 @@
const validator = require('validator');
const db = require('../../database');
const api = require('../../api');
const topics = require('../../topics');
const privileges = require('../../privileges');
@@ -17,19 +18,39 @@ Topics.get = async (req, res) => {
};
Topics.create = async (req, res) => {
const payload = await api.topics.create(req, req.body);
if (payload.queued) {
helpers.formatApiResponse(202, res, payload);
} else {
helpers.formatApiResponse(200, res, payload);
const id = await lockPosting(req, '[[error:already-posting]]');
try {
const payload = await api.topics.create(req, req.body);
if (payload.queued) {
helpers.formatApiResponse(202, res, payload);
} else {
helpers.formatApiResponse(200, res, payload);
}
} finally {
await db.deleteObjectField('locks', id);
}
};
Topics.reply = async (req, res) => {
const payload = await api.topics.reply(req, { ...req.body, tid: req.params.tid });
helpers.formatApiResponse(200, res, payload);
const id = await lockPosting(req, '[[error:already-posting]]');
try {
const payload = await api.topics.reply(req, { ...req.body, tid: req.params.tid });
helpers.formatApiResponse(200, res, payload);
} finally {
await db.deleteObjectField('locks', id);
}
};
async function lockPosting(req, error) {
const id = req.uid > 0 ? req.uid : req.sessionID;
const value = `posting${id}`;
const count = await db.incrObjectField('locks', value);
if (count > 1) {
throw new Error(error);
}
return value;
}
Topics.delete = async (req, res) => {
await api.topics.delete(req, { tids: [req.params.tid] });
helpers.formatApiResponse(200, res);

View File

@@ -26,7 +26,7 @@ module.exports = function (module) {
async function getSortedSetUnion(params) {
if (!Array.isArray(params.sets) || !params.sets.length) {
return;
return [];
}
let limit = params.stop - params.start + 1;
if (limit <= 0) {

View File

@@ -629,9 +629,9 @@ SELECT z."value",
ON o."_key" = z."_key"
AND o."type" = z."type"
WHERE o."_key" = $1::TEXT
AND z."value" LIKE '${match}'
AND z."value" LIKE $3
LIMIT $2::INTEGER`,
values: [params.key, params.limit],
values: [params.key, params.limit, match],
});
if (!params.withScores) {
return res.rows.map(r => r.value);

View File

@@ -32,6 +32,9 @@ SELECT COUNT(DISTINCT z."value") c
async function getSortedSetUnion(params) {
const { sets } = params;
if (!sets || !sets.length) {
return [];
}
const start = params.hasOwnProperty('start') ? params.start : 0;
const stop = params.hasOwnProperty('stop') ? params.stop : -1;
let weights = params.weights || [];

View File

@@ -20,15 +20,24 @@ const batch = require('./batch');
const Flags = module.exports;
Flags._constants = {
states: ['open', 'wip', 'resolved', 'rejected'],
state_class: {
open: 'info',
wip: 'warning',
resolved: 'success',
rejected: 'danger',
},
};
Flags._states = new Map([
['open', {
label: '[[flags:state-open]]',
class: 'danger',
}],
['wip', {
label: '[[flags:state-wip]]',
class: 'warning',
}],
['resolved', {
label: '[[flags:state-resolved]]',
class: 'success',
}],
['rejected', {
label: '[[flags:state-rejected]]',
class: 'secondary',
}],
]);
Flags.init = async function () {
// Query plugins for custom filter strategies and merge into core filter strategies
@@ -78,14 +87,15 @@ Flags.init = async function () {
}
},
},
states: Flags._states,
helpers: {
prepareSets: prepareSets,
},
};
try {
const data = await plugins.hooks.fire('filter:flags.getFilters', hookData);
Flags._filters = data.filters;
({ filters: Flags._filters } = await plugins.hooks.fire('filter:flags.getFilters', hookData));
({ filters: Flags._filters, states: Flags._states } = await plugins.hooks.fire('filter:flags.init', hookData));
} catch (err) {
winston.error(`[flags/init] Could not retrieve filters\n${err.stack}`);
Flags._filters = {};
@@ -197,7 +207,7 @@ Flags.list = async function (data) {
heat: reportCounts[idx],
...flagObj,
};
flagObj.labelClass = Flags._constants.state_class[flagObj.state];
flagObj.labelClass = Flags._states.get(flagObj.state).class;
return Object.assign(flagObj, {
target_readable: `${flagObj.type.charAt(0).toUpperCase() + flagObj.type.slice(1)} ${flagObj.targetId}`,
@@ -674,7 +684,7 @@ Flags.update = async function (flagId, uid, changeset) {
if (current[prop] === changeset[prop]) {
delete changeset[prop];
} else if (prop === 'state') {
if (!Flags._constants.states.includes(changeset[prop])) {
if (!Flags._states.has(changeset[prop])) {
delete changeset[prop];
} else {
tasks.push(db.sortedSetAdd(`flags:byState:${changeset[prop]}`, now, flagId));

View File

@@ -40,14 +40,24 @@ async function linkModules() {
await Promise.all(Object.keys(modules).map(async (relPath) => {
const srcPath = path.join(__dirname, '../../', modules[relPath]);
const destPath = path.join(__dirname, '../../build/public/src/modules', relPath);
const destDir = path.dirname(destPath);
const [stats] = await Promise.all([
fs.promises.stat(srcPath),
mkdirp(path.dirname(destPath)),
mkdirp(destDir),
]);
if (stats.isDirectory()) {
await file.linkDirs(srcPath, destPath, true);
} else {
await fs.promises.copyFile(srcPath, destPath);
// Get the relative path to the destination directory
const relPath = path.relative(destDir, srcPath)
// and convert to a posix path
.split(path.sep).join(path.posix.sep);
// Instead of copying file, create a new file re-exporting it
// This way, imports in modules are resolved correctly
await fs.promises.writeFile(destPath, `module.exports = require('${relPath}');`);
}
}));
}

View File

@@ -108,7 +108,7 @@ Themes.set = async (data) => {
await db.sortedSetAdd('plugins:active', numPlugins, data.id);
} else if (!activePluginsConfig.includes(data.id)) {
// This prevents changing theme when configuration doesn't include it, but allows it otherwise
winston.error('When defining active plugins in configuration, changing themes requires adding the new theme to the list of active plugins before updating it in the ACP');
winston.error(`When defining active plugins in configuration, changing themes requires adding the theme '${data.id}' to the list of active plugins before updating it in the ACP`);
throw new Error('[[error:theme-not-set-in-configuration]]');
}

26
src/middleware/csrf.js Normal file
View File

@@ -0,0 +1,26 @@
'use strict';
const { csrfSync } = require('csrf-sync');
const {
generateToken,
csrfSynchronisedProtection,
isRequestValid,
} = csrfSync({
getTokenFromRequest: (req) => {
if (req.headers['x-csrf-token']) {
return req.headers['x-csrf-token'];
} else if (req.body && req.body.csrf_token) {
return req.body.csrf_token;
} else if (req.query) {
return req.query._csrf;
}
},
size: 64,
});
module.exports = {
generateToken,
csrfSynchronisedProtection,
isRequestValid,
};

View File

@@ -34,10 +34,11 @@ middleware.buildHeader = helpers.try(async (req, res, next) => {
if (req.method === 'GET') {
await require('./index').applyCSRFasync(req, res);
}
({ req, locals: res.locals } = await plugins.hooks.fire('filter:middleware.buildHeader', { req: req, locals: res.locals }));
const [config, canLoginIfBanned] = await Promise.all([
controllers.api.loadConfig(req),
user.bans.canLoginIfBanned(req.uid),
plugins.hooks.fire('filter:middleware.buildHeader', { req: req, locals: res.locals }),
]);
if (!canLoginIfBanned && req.loggedIn) {

View File

@@ -2,11 +2,11 @@
const async = require('async');
const path = require('path');
const csrf = require('csurf');
const validator = require('validator');
const nconf = require('nconf');
const toobusy = require('toobusy-js');
const util = require('util');
const { csrfSynchronisedProtection } = require('./csrf');
const plugins = require('../plugins');
const meta = require('../meta');
@@ -34,7 +34,7 @@ middleware.regexes = {
timestampedUpload: /^\d+-.+$/,
};
const csrfMiddleware = csrf();
const csrfMiddleware = csrfSynchronisedProtection;
middleware.applyCSRF = function (req, res, next) {
if (req.uid >= 0) {
@@ -102,11 +102,20 @@ middleware.pluginHooks = helpers.try(async (req, res, next) => {
});
middleware.validateFiles = function validateFiles(req, res, next) {
if (!Array.isArray(req.files.files) || !req.files.files.length) {
if (!req.files.files) {
return next(new Error(['[[error:invalid-files]]']));
}
next();
if (Array.isArray(req.files.files) && req.files.files.length) {
return next();
}
if (typeof req.files.files === 'object') {
req.files.files = [req.files.files];
return next();
}
return next(new Error(['[[error:invalid-files]]']));
};
middleware.prepareAPI = function prepareAPI(req, res, next) {

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

@@ -33,6 +33,11 @@ Hooks._deprecated = new Map([
since: 'v2.2.0',
until: 'v3.0.0',
}],
['filter:flags.getFilters', {
new: 'filter:flags.init',
since: 'v2.7.0',
until: 'v3.0.0',
}],
]);
Hooks.internals = {

View File

@@ -66,6 +66,7 @@ privsAdmin.routeMap = {
uploadDefaultAvatar: 'admin:settings',
};
privsAdmin.routePrefixMap = {
'dashboard/': 'admin:dashboard',
'manage/categories/': 'admin:categories',
'manage/privileges/': 'admin:privileges',
'manage/groups/': 'admin:groups',
@@ -210,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

@@ -10,6 +10,7 @@ const meta = require('../meta');
const controllers = require('../controllers');
const helpers = require('../controllers/helpers');
const plugins = require('../plugins');
const { generateToken } = require('../middleware/csrf');
let loginStrategies = [];
@@ -108,7 +109,7 @@ Auth.reloadRoutes = async function (params) {
};
if (strategy.checkState !== false) {
req.session.ssoState = req.csrfToken && req.csrfToken();
req.session.ssoState = generateToken(req, true);
opts.state = req.session.ssoState;
}
@@ -154,7 +155,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

@@ -9,11 +9,12 @@ const topics = require('../topics');
const user = require('../user');
const categories = require('../categories');
const meta = require('../meta');
const helpers = require('../controllers/helpers');
const controllerHelpers = require('../controllers/helpers');
const privileges = require('../privileges');
const db = require('../database');
const utils = require('../utils');
const controllers404 = require('../controllers/404');
const routeHelpers = require('./helpers');
const terms = {
daily: 'day',
@@ -23,18 +24,18 @@ const terms = {
};
module.exports = function (app, middleware) {
app.get('/topic/:topic_id.rss', middleware.maintenanceMode, generateForTopic);
app.get('/category/:category_id.rss', middleware.maintenanceMode, generateForCategory);
app.get('/topics.rss', middleware.maintenanceMode, generateForTopics);
app.get('/recent.rss', middleware.maintenanceMode, generateForRecent);
app.get('/top.rss', middleware.maintenanceMode, generateForTop);
app.get('/top/:term.rss', middleware.maintenanceMode, generateForTop);
app.get('/popular.rss', middleware.maintenanceMode, generateForPopular);
app.get('/popular/:term.rss', middleware.maintenanceMode, generateForPopular);
app.get('/recentposts.rss', middleware.maintenanceMode, generateForRecentPosts);
app.get('/category/:category_id/recentposts.rss', middleware.maintenanceMode, generateForCategoryRecentPosts);
app.get('/user/:userslug/topics.rss', middleware.maintenanceMode, generateForUserTopics);
app.get('/tags/:tag.rss', middleware.maintenanceMode, generateForTag);
app.get('/topic/:topic_id.rss', middleware.maintenanceMode, routeHelpers.tryRoute(generateForTopic));
app.get('/category/:category_id.rss', middleware.maintenanceMode, routeHelpers.tryRoute(generateForCategory));
app.get('/topics.rss', middleware.maintenanceMode, routeHelpers.tryRoute(generateForTopics));
app.get('/recent.rss', middleware.maintenanceMode, routeHelpers.tryRoute(generateForRecent));
app.get('/top.rss', middleware.maintenanceMode, routeHelpers.tryRoute(generateForTop));
app.get('/top/:term.rss', middleware.maintenanceMode, routeHelpers.tryRoute(generateForTop));
app.get('/popular.rss', middleware.maintenanceMode, routeHelpers.tryRoute(generateForPopular));
app.get('/popular/:term.rss', middleware.maintenanceMode, routeHelpers.tryRoute(generateForPopular));
app.get('/recentposts.rss', middleware.maintenanceMode, routeHelpers.tryRoute(generateForRecentPosts));
app.get('/category/:category_id/recentposts.rss', middleware.maintenanceMode, routeHelpers.tryRoute(generateForCategoryRecentPosts));
app.get('/user/:userslug/topics.rss', middleware.maintenanceMode, routeHelpers.tryRoute(generateForUserTopics));
app.get('/tags/:tag.rss', middleware.maintenanceMode, routeHelpers.tryRoute(generateForTag));
};
async function validateTokenIfRequiresLogin(requiresLogin, cid, req, res) {
@@ -46,16 +47,16 @@ async function validateTokenIfRequiresLogin(requiresLogin, cid, req, res) {
}
if (uid <= 0 || !token) {
return helpers.notAllowed(req, res);
return controllerHelpers.notAllowed(req, res);
}
const userToken = await db.getObjectField(`user:${uid}`, 'rss_token');
if (userToken !== token) {
await user.auth.logAttempt(uid, req.ip);
return helpers.notAllowed(req, res);
return controllerHelpers.notAllowed(req, res);
}
const userPrivileges = await privileges.categories.get(cid, uid);
if (!userPrivileges.read) {
return helpers.notAllowed(req, res);
return controllerHelpers.notAllowed(req, res);
}
return true;
}
@@ -127,7 +128,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],
}),
]);
@@ -230,7 +231,7 @@ async function generateSorted(options, req, res, next) {
const { cid } = req.query;
if (cid) {
if (!await privileges.categories.can('topics:read', cid, uid)) {
return helpers.notAllowed(req, res);
return controllerHelpers.notAllowed(req, res);
}
params.cids = [cid];
}

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

@@ -34,13 +34,25 @@ Sockets.init = async function (server) {
}
}
io.use(authorize);
io.on('connection', onConnection);
const opts = {
transports: nconf.get('socket.io:transports') || ['polling', 'websocket'],
cookie: false,
allowRequest: (req, callback) => {
authorize(req, (err) => {
if (err) {
return callback(err);
}
const csrf = require('../middleware/csrf');
const isValid = csrf.isRequestValid({
session: req.session || {},
query: req._query,
headers: req.headers,
});
callback(null, isValid);
});
},
};
/*
* Restrict socket.io listener to cookie domain. If none is set, infer based on url.
@@ -62,7 +74,11 @@ Sockets.init = async function (server) {
};
function onConnection(socket) {
socket.ip = (socket.request.headers['x-forwarded-for'] || socket.request.connection.remoteAddress || '').split(',')[0];
socket.uid = socket.request.uid;
socket.ip = (
socket.request.headers['x-forwarded-for'] ||
socket.request.connection.remoteAddress || ''
).split(',')[0];
socket.request.ip = socket.ip;
logger.io_one(socket, socket.uid);
@@ -112,43 +128,49 @@ async function onMessage(socket, payload) {
return winston.warn('[socket.io] Empty payload');
}
const eventName = payload.data[0];
let eventName = payload.data[0];
const params = typeof payload.data[1] === 'function' ? {} : payload.data[1];
const callback = typeof payload.data[payload.data.length - 1] === 'function' ? payload.data[payload.data.length - 1] : function () {};
if (!eventName) {
return winston.warn('[socket.io] Empty method name');
}
const parts = eventName.toString().split('.');
const namespace = parts[0];
const methodToCall = parts.reduce((prev, cur) => {
if (prev !== null && prev[cur]) {
return prev[cur];
}
return null;
}, Namespaces);
if (!methodToCall || typeof methodToCall !== 'function') {
if (process.env.NODE_ENV === 'development') {
winston.warn(`[socket.io] Unrecognized message: ${eventName}`);
}
const escapedName = validator.escape(String(eventName));
return callback({ message: `[[error:invalid-event, ${escapedName}]]` });
}
socket.previousEvents = socket.previousEvents || [];
socket.previousEvents.push(eventName);
if (socket.previousEvents.length > 20) {
socket.previousEvents.shift();
}
if (!eventName.startsWith('admin.') && ratelimit.isFlooding(socket)) {
winston.warn(`[socket.io] Too many emits! Disconnecting uid : ${socket.uid}. Events : ${socket.previousEvents}`);
return socket.disconnect();
}
try {
if (!eventName) {
return winston.warn('[socket.io] Empty method name');
}
if (typeof eventName !== 'string') {
eventName = typeof eventName;
const escapedName = validator.escape(eventName);
return callback({ message: `[[error:invalid-event, ${escapedName}]]` });
}
const parts = eventName.split('.');
const namespace = parts[0];
const methodToCall = parts.reduce((prev, cur) => {
if (prev !== null && prev[cur] && (!prev.hasOwnProperty || prev.hasOwnProperty(cur))) {
return prev[cur];
}
return null;
}, Namespaces);
if (!methodToCall || typeof methodToCall !== 'function') {
if (process.env.NODE_ENV === 'development') {
winston.warn(`[socket.io] Unrecognized message: ${eventName}`);
}
const escapedName = validator.escape(String(eventName));
return callback({ message: `[[error:invalid-event, ${escapedName}]]` });
}
socket.previousEvents = socket.previousEvents || [];
socket.previousEvents.push(eventName);
if (socket.previousEvents.length > 20) {
socket.previousEvents.shift();
}
if (!eventName.startsWith('admin.') && ratelimit.isFlooding(socket)) {
winston.warn(`[socket.io] Too many emits! Disconnecting uid : ${socket.uid}. Events : ${socket.previousEvents}`);
return socket.disconnect();
}
await checkMaintenance(socket);
await validateSession(socket, '[[error:revalidate-failure]]');
@@ -225,9 +247,7 @@ async function validateSession(socket, errorMsg) {
const cookieParserAsync = util.promisify((req, callback) => cookieParser(req, {}, err => callback(err)));
async function authorize(socket, callback) {
const { request } = socket;
async function authorize(request, callback) {
if (!request) {
return callback(new Error('[[error:not-authorized]]'));
}
@@ -240,15 +260,13 @@ async function authorize(socket, callback) {
});
const sessionData = await getSessionAsync(sessionId);
request.session = sessionData;
let uid = 0;
if (sessionData && sessionData.passport && sessionData.passport.user) {
request.session = sessionData;
socket.uid = parseInt(sessionData.passport.user, 10);
} else {
socket.uid = 0;
uid = parseInt(sessionData.passport.user, 10);
}
request.uid = socket.uid;
callback();
request.uid = uid;
callback(null, uid);
}
Sockets.in = function (room) {

View File

@@ -14,15 +14,15 @@ const utils = require('../../utils');
module.exports = function (SocketPosts) {
SocketPosts.loadPostTools = async function (socket, data) {
if (!data || !data.pid || !data.cid) {
if (!data || !data.pid) {
throw new Error('[[error:invalid-data]]');
}
const cid = await posts.getCidByPid(data.pid);
const results = await utils.promiseParallel({
posts: posts.getPostFields(data.pid, ['deleted', 'bookmarks', 'uid', 'ip', 'flagId']),
isAdmin: user.isAdministrator(socket.uid),
isGlobalMod: user.isGlobalModerator(socket.uid),
isModerator: user.isModerator(socket.uid, data.cid),
isModerator: user.isModerator(socket.uid, cid),
canEdit: privileges.posts.canEdit(data.pid, socket.uid),
canDelete: privileges.posts.canDelete(data.pid, socket.uid),
canPurge: privileges.posts.canPurge(data.pid, socket.uid),

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 });
}
};

Some files were not shown because too many files have changed in this diff Show More