diff --git a/.codeclimate.yml b/.codeclimate.yml index 63c7d5cab1..d40cc58e75 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -10,7 +10,7 @@ checks: threshold: 500 method-lines: config: - threshold: 50 + threshold: 75 method-complexity: config: threshold: 10 diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000000..9e697f8438 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,51 @@ +name: Run Docker + +# Controls when the workflow will run +on: + push: + branches: + - 'master' + - 'v*.x' + tags: + - 'v*' + workflow_dispatch: + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + release: + runs-on: ubuntu-latest + steps: + + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v1 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + + - name: Login to Docker Hub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Docker meta + id: meta + uses: docker/metadata-action@v3 + with: + images: nodebb/docker + tags: | + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=raw,value=latest + + - name: Build and push Docker images + uses: docker/build-push-action@v2 + with: + context: . + file: ./Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 90683cf490..e8b07af4cd 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -21,7 +21,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest] - node: [12, 14] + node: [12, 14, 16] database: [mongo-dev, mongo, redis, postgres] include: # only run coverage once diff --git a/.jsbeautifyrc b/.jsbeautifyrc deleted file mode 100644 index d76e93f2d5..0000000000 --- a/.jsbeautifyrc +++ /dev/null @@ -1,17 +0,0 @@ -{ - "indent_size": 4, - "indent_char": " ", - "indent_level": 0, - "indent_with_tabs": true, - "preserve_newlines": true, - "max_preserve_newlines": 10, - "jslint_happy": true, - "brace_style": "collapse", - "keep_array_indentation": false, - "keep_function_indentation": false, - "space_before_conditional": true, - "break_chained_methods": false, - "eval_code": false, - "unescape_strings": false, - "wrap_line_length": 0 -} \ No newline at end of file diff --git a/.jshintrc b/.jshintrc deleted file mode 100644 index bbbcd96dc3..0000000000 --- a/.jshintrc +++ /dev/null @@ -1,91 +0,0 @@ -{ - // JSHint Default Configuration File (as on JSHint website) - // See http://jshint.com/docs/ for more details - - "maxerr" : 50, // {int} Maximum error before stopping - - "esversion": 6, - - // Enforcing - "bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.) - "camelcase" : false, // true: Identifiers must be in camelCase - "curly" : true, // true: Require {} for every new block or scope - "eqeqeq" : true, // true: Require triple equals (===) for comparison - "forin" : true, // true: Require filtering for..in loops with obj.hasOwnProperty() - "immed" : false, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());` - "indent" : 4, // {int} Number of spaces to use for indentation - "latedef" : false, // true: Require variables/functions to be defined before being used - "newcap" : false, // true: Require capitalization of all constructor functions e.g. `new F()` - "noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee` - "noempty" : true, // true: Prohibit use of empty blocks - "nonew" : false, // true: Prohibit use of constructors for side-effects (without assignment) - "plusplus" : false, // true: Prohibit use of `++` & `--` - "quotmark" : false, // Quotation mark consistency: - // false : do nothing (default) - // true : ensure whatever is used is consistent - // "single" : require single quotes - // "double" : require double quotes - "undef" : true, // true: Require all non-global variables to be declared (prevents global leaks) - "unused" : true, // true: Require all defined variables be used - "strict" : true, // true: Requires all functions run in ES5 Strict Mode - "trailing" : false, // true: Prohibit trailing whitespaces - "maxparams" : false, // {int} Max number of formal params allowed per function - "maxdepth" : false, // {int} Max depth of nested blocks (within functions) - "maxstatements" : false, // {int} Max number statements per function - "maxcomplexity" : false, // {int} Max cyclomatic complexity per function - "maxlen" : false, // {int} Max number of characters per line - - // Relaxing - "asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons) - "boss" : false, // true: Tolerate assignments where comparisons would be expected - "debug" : false, // true: Allow debugger statements e.g. browser breakpoints. - "eqnull" : false, // true: Tolerate use of `== null` - "es5" : false, // true: Allow ES5 syntax (ex: getters and setters) - "esnext" : false, // true: Allow ES.next (ES6) syntax (ex: `const`) - "moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features) - // (ex: `for each`, multiple try/catch, function expression…) - "evil" : false, // true: Tolerate use of `eval` and `new Function()` - "expr" : false, // true: Tolerate `ExpressionStatement` as Programs - "funcscope" : false, // true: Tolerate defining variables inside control statements" - "globalstrict" : false, // true: Allow global "use strict" (also enables 'strict') - "iterator" : false, // true: Tolerate using the `__iterator__` property - "lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block - "laxbreak" : false, // true: Tolerate possibly unsafe line breakings - "laxcomma" : false, // true: Tolerate comma-first style coding - "loopfunc" : false, // true: Tolerate functions being defined in loops - "multistr" : false, // true: Tolerate multi-line strings - "proto" : false, // true: Tolerate using the `__proto__` property - "scripturl" : false, // true: Tolerate script-targeted URLs - "smarttabs" : false, // true: Tolerate mixed tabs/spaces when used for alignment - "shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;` - "sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation - "supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;` - "validthis" : false, // true: Tolerate using this in a non-constructor function - - // Environments - "browser" : true, // Web Browser (window, document, etc) - "couch" : false, // CouchDB - "devel" : true, // Development/debugging (alert, confirm, etc) - "dojo" : false, // Dojo Toolkit - "jquery" : true, // jQuery - "mootools" : false, // MooTools - "node" : true, // Node.js - "nonstandard" : false, // Widely adopted globals (escape, unescape, etc) - "prototypejs" : false, // Prototype and Scriptaculous - "rhino" : false, // Rhino - "worker" : false, // Web Workers - "wsh" : false, // Windows Scripting Host - "yui" : false, // Yahoo User Interface - "mocha": true, - - // Legacy - "nomen" : false, // true: Prohibit dangling `_` in variables - "onevar" : false, // true: Allow only one `var` statement per function - "passfail" : false, // true: Stop on first error - "white" : false, // true: Check against strict whitespace and indentation rules - - // Custom Globals - "globals" : { - "Promise": true - } // additional predefined global variables -} \ No newline at end of file diff --git a/.tx/config b/.tx/config index 6d9ba616d4..58394ddcf9 100644 --- a/.tx/config +++ b/.tx/config @@ -1000,6 +1000,56 @@ trans.zh_CN = public/language/zh-CN/tags.json trans.zh_TW = public/language/zh-TW/tags.json type = KEYVALUEJSON +[nodebb.top] +file_filter = public/language//top.json +source_file = public/language/en-GB/top.json +source_lang = en_GB +trans.ar = public/language/ar/top.json +trans.bg = public/language/bg/top.json +trans.bn = public/language/bn/top.json +trans.cs = public/language/cs/top.json +trans.da = public/language/da/top.json +trans.de = public/language/de/top.json +trans.el = public/language/el/top.json +trans.en@pirate = public/language/en-x-pirate/top.json +trans.en_US = public/language/en-US/top.json +trans.es = public/language/es/top.json +trans.et = public/language/et/top.json +trans.fa_IR = public/language/fa-IR/top.json +trans.fi = public/language/fi/top.json +trans.fr = public/language/fr/top.json +trans.gl = public/language/gl/top.json +trans.he = public/language/he/top.json +trans.hr = public/language/hr/top.json +trans.hu = public/language/hu/top.json +trans.id = public/language/id/top.json +trans.it = public/language/it/top.json +trans.ja = public/language/ja/top.json +trans.ko = public/language/ko/top.json +trans.lt = public/language/lt/top.json +trans.lv = public/language/lv/top.json +trans.ms = public/language/ms/top.json +trans.nb = public/language/nb/top.json +trans.nl = public/language/nl/top.json +trans.pl = public/language/pl/top.json +trans.pt_BR = public/language/pt-BR/top.json +trans.pt_PT = public/language/pt-PT/top.json +trans.ro = public/language/ro/top.json +trans.ru = public/language/ru/top.json +trans.rw = public/language/rw/top.json +trans.sc = public/language/sc/top.json +trans.sk = public/language/sk/top.json +trans.sl = public/language/sl/top.json +trans.sr = public/language/sr/top.json +trans.sv = public/language/sv/top.json +trans.th = public/language/th/top.json +trans.tr = public/language/tr/top.json +trans.uk = public/language/uk/top.json +trans.vi = public/language/vi/top.json +trans.zh_CN = public/language/zh-CN/top.json +trans.zh_TW = public/language/zh-TW/top.json +type = KEYVALUEJSON + [nodebb.email] file_filter = public/language//email.json source_file = public/language/en-GB/email.json diff --git a/CHANGELOG.md b/CHANGELOG.md index de31b7fb97..e339ad70ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,400 @@ +#### v1.18.5 (2021-10-27) + +##### Breaking Changes + +* disable javascript in custom less tab (719cfc0d) + +##### Chores + +* remove .opacity() mixin as it is supported cross-browser (28efcb59) +* up themes (463b2076) +* up persona (1438f409) +* incrementing version number - v1.18.4 (945c2b0b) +* update changelog for v1.18.4 (7cbcb521) +* **deps:** + * update dependency lint-staged to v11.2.6 (8d4bb8bb) + * update dependency lint-staged to v11.2.5 (0728a994) + * update dependency lint-staged to v11.2.4 (f76a7882) + * update dependency husky to v7.0.4 (2a3e13f3) + * update dependency mocha to v9.1.3 (4784f016) + * update dependency eslint-plugin-import to v2.25.2 (3c3f45d9) + * update dependency jsdom to v18 (4b8dcd4c) + * update dependency eslint-plugin-import to v2.25.1 (7c4aebbd) + * update dependency lint-staged to v11.2.3 (288b5456) + * update dependency lint-staged to v11.2.2 (f96c8c4d) + * update dependency @commitlint/cli to v13.2.1 (52c38a1d) + * update dependency lint-staged to v11.2.1 (022e8df0) + * update dependency eslint-config-nodebb to v0.0.3 (4b92df82) +* **i18n:** + * fallback strings for new resources: nodebb.admin-settings-email, nodebb.error (9b68dc37) + * fallback strings for new resources: nodebb.admin-dashboard (ff962b5d) + * fallback strings for new resources: nodebb.admin-dashboard, nodebb.admin-menu (abe59131) + * fallback strings for new resources: nodebb.admin-manage-digest, nodebb.admin-settings-user, nodebb.user (2bed40be) + +##### Documentation Changes + +* update verbiage re: login API route (94c4f87b) + +##### New Features + +* new ACP option `emailPrompt` ... which allows administrators to disable the client-side prompt to encourage users to enter or confirm their email addresses (80ea12c1) +* show popular searches (f4cf482a) +* new plugin hook to allow plugins to reject email address on new registration or email change (6b4f35c2) +* utilities login API route now starts an actual login session, if requested (806a1e50) +* add method name to deprecation message (b91ae088) +* quote tooltip (66fca4e0) +* additional quality options for jpeg uploads, added quality and compression settings for png uploads (d22b076b) +* #8053, biweekly digest option (f7967bdf) +* core submit button dropdown (605a5381) +* added failing i18n tests (35af7634) +* confirm before deleting all events (#9875) (56d05b4e) + +##### Bug Fixes + +* **deps:** + * update dependency nodebb-theme-vanilla to v12.1.7 (#9944) (bf20965f) + * update dependency nodebb-theme-persona to v11.2.19 (#9943) (bcf85fcf) + * update dependency nodebb-rewards-essentials to v0.2.0 (7c2ecb12) + * update dependency nodebb-theme-vanilla to v12.1.6 (49b8b983) + * update dependency nodebb-theme-persona to v11.2.18 (ed0adf2c) + * update dependency nodebb-theme-persona to v11.2.17 (78661079) + * update dependency postcss to v8.3.11 (a5f4e206) + * update dependency nodebb-theme-vanilla to v12.1.5 (d74a6bd3) + * update dependency sharp to v0.29.2 (8b8fe393) + * update dependency postcss to v8.3.10 (b18a24e9) + * update dependency nodebb-theme-persona to v11.2.15 (f3c8d7da) + * update dependency nodebb-theme-persona to v11.2.14 (#9919) (5e08e67b) + * update dependency socket.io-client to v4.3.2 (deba3e27) + * update dependency socket.io to v4.3.1 (e1554f61) + * update socket.io packages (ce5a0a21) + * update dependency nodebb-plugin-spam-be-gone to v0.7.10 (600a8720) + * update dependency nodebb-plugin-composer-default to v7.0.10 (b0128f85) + * update dependency nodebb-plugin-markdown to v8.14.4 (f8f35d7e) + * update dependency nodebb-plugin-composer-default to v7.0.9 (ed874662) + * update dependency nodebb-theme-persona to v11.2.13 (1dba75e9) + * update dependency ioredis to v4.28.0 (4ff5452d) + * update dependency nodebb-theme-persona to v11.2.12 (fe9f82f6) + * update dependency ioredis to v4.27.11 (6d2e0aa9) + * update dependency nodebb-plugin-mentions to v2.14.1 (820f8cdf) + * update dependency jquery-ui to v1.13.0 (b0eb2aed) +* remove loading="lazy", fixes inf. scroll loaded images (01572785) +* windows tests (25ebbd65) +* undefined query showing in searches (6cfaea06) +* don't repeat search if on same page (89f5e06b) +* api session revoke test (0926ae6e) +* crash (da64810a) +* add missing translation (eb075c73) +* move record to controller (ee8e0480) +* profile edit fields showing translated values (63572c23) +* #9934, fix translator test (8d316d18) +* token verify (04dab1d5) +* guard against prototype pollution (1783f918) +* translator path traversal (c8b2fc46) +* there is no alltime digest, fixes translation in test email (e62948f7) +* clicking outside postContainer should close tooltip (47df62e7) +* minification regression (998b9e79) +* tooltip (fec7ebed) +* biweekly digest #8053 (9cb4de50) +* restore plugin upgrade checking logic (44687394) +* fallbacks for new langauge key (ed4ebd22) +* #9917, show topics as unread for guests (4333d217) +* clarify site settings urls vs config.json url (#9912) (6436aa65) +* clarify SMTP enable toggle (#9911) (09f198fc) +* don't overwrite reloadRequired with false (9e0ce027) +* delete translations in admin/general folder (since general was removed and relocated elsewhere) (b460e590) +* pushed missing key to tx and pulled fallbacks (21b61082) +* adding missing language namespace "top" (0f9b0b78) +* extra debug log (bd893cda) +* have renovate add `dependencies` label to its PRs (eddb9868) +* no global bootbox (#9879) (227456fb) +* #9872 update app badge with notification count if applicable (3e69bcdf) +* better nomenclature (c1149d04) +* html attributes (#9877) (3acaac4c) +* escape thumbs, allow robots meta tag (4f9717fb) +* missing translations (#9876) (7935bd9e) + +##### Performance Improvements + +* dont fs.open if plugin doesnt have language namespace (#9893) (1feb111a) + +##### Refactors + +* wider value field (c428ba80) +* dont save partial searches (c7e078d4) +* use search api for topic search (64192731) +* slowdown quick search (19ee7174) +* typo (a5287906) +* add callback to loadNotifications (f02fba29) +* simplified utilities API > login rout (506c34a8) +* log error as well (1d62bd6d) +* catch errors from buildHeader in error handler :fire: (73a9ca09) +* add missing helpers.tryRoute (d4da9840) +* shorter middleware (ee0282f5) +* meta/minifier use async/await (b2429ef0) +* remove unused var (90b81262) +* catch errors from digest (8e319a9b) +* less.render returns promise (14bc83a8) +* less.render already returns promise (6da32392) +* prompt.get already returns promise (c70eaa0a) +* no need for async/callbacks (057d1d58) +* no more :cow: (38756a0c) +* allow array of uids for blocks.is/list (a9bc6a09) +* show full url on error log (8e6bd7e9) +* var to const and let (#9885) (b0a24d6d) +* remove unused code (997fb2b3) +* remove unused colorpicker (543d8521) + +##### Reverts + +* lazy load (3d1cf168) + +##### Tests + +* fix broken openapi3 schema (7ef5214e) +* restore commented-out i18n test (fa1afbcf) +* moved topic event and topic thumb tests to subfolder for better organisation (154ffea0) +* remove escape (6c25b9db) +* possible fix to timeout (63109c07) +* increase timeout (8654a996) + +#### v1.18.4 (2021-10-06) + +##### Chores + +* up persona (f4e62fb1) +* incrementing version number - v1.18.3 (57358743) +* update changelog for v1.18.3 (f066ddb8) +* **deps:** + * update dependency lint-staged to v11.2.0 (840b49b9) + * update commitlint monorepo to v13.2.0 (aa370310) + * update dependency mocha to v9.1.2 (6385b88e) + +##### Documentation Changes + +* added link to unofficial IRC channel (c5a48b44) + +##### New Features + +* use unread icon in mobile (27e53b42) +* cli user management commands (#9848) (d1ff3d62) +* #9855, allow uid for post queue notifications (5aea6c6a) +* add userData to static:user.delete (f24b630e) +* closes #9845, sort by views (6399b428) +* duplicate `requireEmailAddress` settings block to Settings > User (a9645475) +* mongodb driver 4.x (#9832) (07adb49e) +* a useless hover effect because raisins (1a61ffc5) + +##### Bug Fixes + +* **deps:** + * update dependency mongodb to v4.1.3 (b4fc2773) + * update dependency postcss to v8.3.9 (9455e5b2) + * update dependency autoprefixer to v10.3.7 (78895d05) + * update dependency nodebb-plugin-composer-default to v7.0.8 (9215c7d1) + * update dependency ioredis to v4.27.10 (4694382c) + * update dependency nodebb-theme-persona to v11.2.9 (346e0890) + * update dependency autoprefixer to v10.3.6 (058fdca4) + * update dependency yargs to v17.2.1 (d50dd801) + * update dependency postcss to v8.3.8 (193c92e3) + * update dependency passport to ^0.5.0 (daea8a86) + * update dependency connect-pg-simple to v7 (#9785) (054f3da6) + * update dependency yargs to v17.2.0 (c78309b5) +* #9866, fire vote hooks after reputation changes (#9867) (8ad9a103) +* #9865, don't display register messages after login (96f5312d) +* dont show decimails on auto approva minutes (a0df3890) +* #9864 (e954ca10) +* delete old topic tags (a70c69fa) +* switch inf. scroll to xhr (#9854) (4404e819) +* #9828, max-width (40915105) +* handle undefined returnTo on registerAbort (ac1b9692) +* lint (ff850b24) +* psql tests (123354ca) +* psql test (f8d4ec6c) +* possible test fix for subfolder redirect (3605ac81) +* missing relative path in test (4eacfef0) +* #9834, missing null email check on new registrations, added tests (58e0a366) +* crossorigin not showing up on manifest link tag (0faa4937) +* #9827, fix reward duplication (89af00d1) + +##### Performance Improvements + +* convert promise.all to single query (#9851) (ea04aede) + +##### Refactors + +* use utils.debounce (a7668a7f) +* remove async.waterfall from remaining upgrade scripts (6b34065f) + +##### Tests + +* dashboard (4f8647a5) +* add tests for admin privileges (9fe9ab08) +* add missing tests (34798325) +* remove debug log (8cb47548) +* no need to create fake interstitial as NodeBB comes with some by default (cb69934a) + +#### v1.18.3 (2021-09-22) + +##### Chores + +* **deps:** update docker/build-push-action action to v2.7.0 (ee027719) +* incrementing version number - v1.18.2 (0a56158b) +* update changelog for v1.18.2 (27e9282a) + +##### New Features + +* move filter:topic.post hook to top of method (f194809f) +* add client-side static hook to fire immediately before any topic action (hint: delete `action` to stop default behaviour) (66eaae44) +* allow removing multiple items from list (397835a0) +* add uid to filter:user.saveSettings (7f48edc0) +* headers for global privs #9717 (#9762) (84ff1152) +* add ACP option to require email address on new registration (006fc700) + +##### Bug Fixes + +* **deps:** + * update dependency nodebb-plugin-composer-default to v7.0.7 (98554294) + * update dependency postcss to v8.3.7 (6ebe707c) + * update dependency autoprefixer to v10.3.5 (25687441) + * update dependency nodebb-plugin-composer-default to v7.0.6 (#9815) (c18678ce) + * update dependency nodebb-theme-persona to v11.2.8 (#9816) (39d73d0c) + * update dependency connect-mongo to v4.6.0 (8e886c85) + * update dependency nodebb-plugin-composer-default to v7.0.4 (8af54255) + * update dependency mongodb to v3.7.1 (9049dcd7) + * update dependency nodebb-theme-persona to v11.2.6 (506035b5) + * update dependency nodebb-theme-slick to v1.4.13 (787306a6) + * update dependency nodebb-plugin-composer-default to v7.0.3 (732b59c2) +* fixed element shifting in ACP menu that's been bothering me for 5-ish years (31975a62) +* #9822, use correct username/pwd (30f38771) +* remove unused translator (2add84a5) +* ban info test (07859f7e) +* #9819, show same time info for ban (9f0e55ad) +* show local time for ban (7a2f0ae1) +* crash (c437b336) +* remove caller from payload after hooks is done (15f9aaa6) +* bad uid reference (ce8ea6ea) +* update Topics.post and Topics.reply so that plugins can modify uid (or redirect a reply to a different topic) (7777812e) +* #9818, fix totalTime calculation (c4fc7bf9) +* missing microdata in category data (1ed62aa8) +* #9812, add topics:schedule (c0a52924) +* for subfolders (31a6d4b3) +* req.path doesn't have full url (0236ea86) +* escape moderation note before adding to dom (75ebe786) +* #9811, send bodyClass on 403 (40c9fca9) +* also launch docker workflow on release branches (944a7985) +* xss on flags page via ban reason (ba3582b8) +* up timeout for psql tests (896ff215) +* redis batch (0c4b875e) +* redis processing batch+1 items every iteration (3261edcc) +* #9560, don't save post diffs if content didn't change (8b576a37) +* #9790, get baseIndex on update for infinitescroll (6a55c027) +* #9790, fix sorting of more than one page of pinned topics (2657804c) +* privileges added by plugins (#9802) (3ecbb624) +* #9800, don't send all welcome test emails to test@example.org @julianlam (71ed50b9) +* docker - remove sha tag (b06e8dba) +* Return QEMU back, remove platforms definition (52eace4b) +* Docker workflow tweaks (#9792) (e7f4cde4) +* browsers autocompleting smtp fields when they should not (34afb747) + +##### Refactors + +* no regex (18252fb9) +* remove async.waterfall (58ac55c1) +* remove async.waterfall (222dccaf) +* remove async.waterfall (f35a0f43) +* allow plugins to replace og:image, or specify additional og:image (819917da) + +##### Code Style Changes + +* give me an A! :100: (0b4d7d1f) + +#### v1.18.2 (2021-09-08) + +##### Chores + +* **deps:** update commitlint monorepo to v13 (87ba768f) +* incrementing version number - v1.18.1 (f8f80e4f) +* update changelog for v1.18.1 (0713475d) + +##### New Features + +* a slightly less ugly rewards panel (bf0c02a7) + +##### Bug Fixes + +* dashboard graph controls (a7855c4c) +* #9767 ACP change group icon fix (580a016b) +* #9781 (#9782) (0ce4b87d) +* replace logic in isPrivilegedOrSelfAndPasswordMatch to use privileges.users.canEdit (856ba78a) +* handle missing uid in deprecated socket call (cdaea611) +* use privileges.users.canEdit for image upload priv check (e33e046f) +* errors from registerComplete (a48bbdbf) +* simplify logic for fullname and email blanking in user retrieval (getUserDataByUserSlug) (60de0844) +* lint (1e2bda13) +* manifest error (488f0978) +* #9772, regression from https://github.com/NodeBB/NodeBB/commit/70a04bc10577e90e28d66a647d38cafc3307a285 (72710b80) +* push back some deprecations, remove deprecated stuff scheduled for v1.18.0 (dd4e66e2) +* deprecate userData.showHidden as it is functionally equivalent to userData.canEdit (4ac701d7) +* focus on save button on plugin activation (46e5e17d) +* #9773, fire hooks properly for priv changes (#9774) (6869920e) +* **deps:** + * update dependency sharp to v0.29.1 (ac6cd02f) + * update dependency nodebb-plugin-dbsearch to v5.0.3 (338f90fc) + * update dependency nodebb-theme-vanilla to v12.1.3 (0b3ea5ad) + * update dependency nodebb-theme-persona to v11.2.5 (57e54d55) + +##### Refactors + +* deprecate picture update socket call, new API routes for picture update (0a41741b) + +##### Tests + +* added test for external image via new change picture API (8cbad61e) + +#### v1.18.1 (2021-09-03) + +##### Chores + +* found some hooks that don't play well docgen (ae793b4a) +* incrementing version number - v1.18.0 (1e436ae7) +* update changelog for v1.18.0 (2fd9c095) +* **deps:** update dependency mocha to v9.1.1 (64bac178) + +##### New Features + +* create folders in ACP uploads #9638 (#9750) (3df79683) +* column based view on wide priv. tables (#9699) (61f02f17) +* als (#9749) (e59d3575) +* add quick reply key (e9314842) +* add new lang key for no best posts (6e73d8c9) + +##### Bug Fixes + +* **deps:** + * update dependency autoprefixer to v10.3.4 (67b932f4) + * update dependency nodebb-theme-persona to v11.2.4 (fe18e100) + * update dependency mongodb to v3.7.0 (31a35d7f) + * update socket.io packages to v4.2.0 (f2028d70) + * update dependency ioredis to v4.27.9 (6052eb16) + * update dependency mongodb to v3.6.12 (#9761) (5fa982c1) + * update dependency nodebb-plugin-composer-default to v7.0.2 (33d51201) + * update dependency nodebb-theme-slick to v1.4.12 (1b416d7e) + * update dependency nodebb-theme-slick to v1.4.11 (65b32fa1) + * update dependency nodebb-theme-persona to v11.2.3 (6ce321e4) + * update dependency autoprefixer to v10.3.3 (91ba7cdf) + * update dependency nodebb-theme-slick to v1.4.9 (d80b378f) + * update dependency jquery-deserialize to v2.0.0 (#9744) (7f9451ce) +* determine indeterminate checkboxes (760ea9df) +* move app.alert back into the conditionals (ca9bae3a) +* only show email confirmation warning toast on pages that it applies (1bd1cc74) +* updated email confirm warning to be more positive (2d1380dd) +* automated tests are a good thing to have (6afeac37) + +##### Refactors + +* consistent jquery element var naming (fc0e655e) +* var to const (1272da65) + #### v1.18.0 (2021-08-25) ##### Breaking Changes diff --git a/Dockerfile b/Dockerfile index 28d8586a48..80dce7cfb5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,17 +1,20 @@ FROM node:lts -RUN mkdir -p /usr/src/app +RUN mkdir -p /usr/src/app && \ + chown -R node:node /usr/src/app WORKDIR /usr/src/app ARG NODE_ENV ENV NODE_ENV $NODE_ENV -COPY install/package.json /usr/src/app/package.json +COPY --chown=node:node install/package.json /usr/src/app/package.json + +USER node RUN npm install --only=prod && \ npm cache clean --force - -COPY . /usr/src/app + +COPY --chown=node:node . /usr/src/app ENV NODE_ENV=production \ daemon=false \ diff --git a/README.md b/README.md index a295e6f03a..ec072e3a3a 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,7 @@ # ![NodeBB](public/images/logo.svg) -[![Build Status](https://travis-ci.org/NodeBB/NodeBB.svg?branch=master)](https://travis-ci.org/NodeBB/NodeBB) +[![Workflow](https://github.com/NodeBB/NodeBB/actions/workflows/test.yaml/badge.svg)](https://github.com/NodeBB/NodeBB/actions/workflows/test.yaml) [![Coverage Status](https://coveralls.io/repos/github/NodeBB/NodeBB/badge.svg?branch=master)](https://coveralls.io/github/NodeBB/NodeBB?branch=master) -[![Dependency Status](https://david-dm.org/nodebb/nodebb.svg?path=install)](https://david-dm.org/nodebb/nodebb?path=install) [![Code Climate](https://codeclimate.com/github/NodeBB/NodeBB/badges/gpa.svg)](https://codeclimate.com/github/NodeBB/NodeBB) [**NodeBB Forum Software**](https://nodebb.org) is powered by Node.js and supports either Redis, MongoDB, or a PostgreSQL database. It utilizes web sockets for instant interactions and real-time notifications. NodeBB has many modern features out of the box such as social network integration and streaming discussions, while still making sure to be compatible with older browsers. @@ -15,6 +14,7 @@ Additional functionality is enabled through the use of third-party plugins. * [Help translate NodeBB](https://www.transifex.com/projects/p/nodebb/) * [NodeBB Blog](http://blog.nodebb.org) * [Premium Hosting for NodeBB](http://www.nodebb.org/ "NodeBB") +* Unofficial IRC community – channel `#nodebb` on Libera.chat * [Follow us on Twitter](http://www.twitter.com/NodeBB/ "NodeBB Twitter") * [Like us on Facebook](http://www.facebook.com/NodeBB/ "NodeBB Facebook") diff --git a/install/data/defaults.json b/install/data/defaults.json index acd8e9cf17..cd01e7644a 100644 --- a/install/data/defaults.json +++ b/install/data/defaults.json @@ -14,6 +14,7 @@ "newbiePostEditDuration": 3600, "postDeleteDuration": 0, "enablePostHistory": 1, + "topicBacklinks": 1, "postCacheSize": 10485760, "disableChat": 0, "chatEditDuration": 0, @@ -68,6 +69,7 @@ "gdpr_enabled": 1, "allowProfileImageUploads": 1, "teaserPost": "last-reply", + "showPostPreviewsOnHover": 1, "allowPrivateGroups": 1, "unreadCutoff": 2, "bookmarkThreshold": 5, @@ -136,7 +138,9 @@ "disableEmailSubscriptions": 0, "emailConfirmInterval": 10, "removeEmailNotificationImages": 0, + "sendValidationEmail": 1, "includeUnverifiedEmails": 0, + "emailPrompt": 1, "inviteExpiration": 7, "dailyDigestFreq": "off", "digestHour": 17, diff --git a/install/databases.js b/install/databases.js index 057de576c6..dbbda8fcff 100644 --- a/install/databases.js +++ b/install/databases.js @@ -3,10 +3,6 @@ const prompt = require('prompt'); const winston = require('winston'); -const util = require('util'); - -const promptGet = util.promisify((schema, callback) => prompt.get(schema, callback)); - const questions = { redis: require('../src/database/redis').questions, mongo: require('../src/database/mongo').questions, @@ -28,17 +24,17 @@ async function getDatabaseConfig(config) { if (config['redis:host'] && config['redis:port']) { return config; } - return await promptGet(questions.redis); + return await prompt.get(questions.redis); } else if (config.database === 'mongo') { if ((config['mongo:host'] && config['mongo:port']) || config['mongo:uri']) { return config; } - return await promptGet(questions.mongo); + return await prompt.get(questions.mongo); } else if (config.database === 'postgres') { if (config['postgres:host'] && config['postgres:port']) { return config; } - return await promptGet(questions.postgres); + return await prompt.get(questions.postgres); } throw new Error(`unknown database : ${config.database}`); } diff --git a/install/package.json b/install/package.json index 345e5e1add..1f8492be1d 100644 --- a/install/package.json +++ b/install/package.json @@ -2,7 +2,7 @@ "name": "nodebb", "license": "GPL-3.0", "description": "NodeBB Forum", - "version": "1.18.1", + "version": "1.18.6", "homepage": "http://www.nodebb.org", "repository": { "type": "git", @@ -32,7 +32,7 @@ "ace-builds": "^1.4.12", "archiver": "^5.2.0", "async": "^3.2.0", - "autoprefixer": "10.3.4", + "autoprefixer": "10.4.0", "bcryptjs": "2.4.3", "benchpressjs": "2.4.3", "body-parser": "^1.19.0", @@ -41,14 +41,14 @@ "chart.js": "^2.9.4", "cli-graph": "^3.2.2", "clipboard": "^2.0.6", - "colors": "^1.4.0", + "colors": "1.4.0", "commander": "^7.1.0", "compare-versions": "3.6.0", "compression": "^1.7.4", "connect-flash": "^0.1.1", - "connect-mongo": "4.5.0", + "connect-mongo": "4.6.0", "connect-multiparty": "^2.2.0", - "connect-pg-simple": "^6.2.1", + "connect-pg-simple": "^7.0.0", "connect-redis": "6.0.0", "cookie-parser": "^1.4.5", "cron": "^1.8.2", @@ -61,13 +61,13 @@ "express-useragent": "^1.0.15", "graceful-fs": "^4.2.6", "helmet": "^4.4.1", - "html-to-text": "8.0.0", + "html-to-text": "8.1.0", "ipaddr.js": "^2.0.0", "jquery": "3.6.0", "jquery-deserialize": "2.0.0", "jquery-form": "4.3.0", "jquery-serializeobject": "1.0.0", - "jquery-ui": "1.12.1", + "jquery-ui": "1.13.0", "jsesc": "3.0.2", "json2csv": "5.0.6", "jsonwebtoken": "^8.5.1", @@ -78,36 +78,36 @@ "material-design-lite": "^1.3.0", "mime": "^2.5.2", "mkdirp": "^1.0.4", - "mongodb": "3.7.0", + "mongodb": "4.1.4", "morgan": "^1.10.0", "mousetrap": "^1.6.5", "multiparty": "4.2.2", "@nodebb/bootswatch": "3.4.2", "nconf": "^0.11.2", - "nodebb-plugin-composer-default": "7.0.2", - "nodebb-plugin-dbsearch": "5.0.2", + "nodebb-plugin-composer-default": "7.0.14", + "nodebb-plugin-dbsearch": "5.1.0", "nodebb-plugin-emoji": "^3.5.0", "nodebb-plugin-emoji-android": "2.0.5", - "nodebb-plugin-markdown": "8.14.3", - "nodebb-plugin-mentions": "2.13.11", - "nodebb-plugin-spam-be-gone": "0.7.9", - "nodebb-rewards-essentials": "0.1.5", - "nodebb-theme-lavender": "5.2.1", - "nodebb-theme-persona": "11.2.4", - "nodebb-theme-slick": "1.4.12", - "nodebb-theme-vanilla": "12.1.2", + "nodebb-plugin-markdown": "8.14.4", + "nodebb-plugin-mentions": "3.0.3", + "nodebb-plugin-spam-be-gone": "0.7.11", + "nodebb-rewards-essentials": "0.2.0", + "nodebb-theme-lavender": "5.3.1", + "nodebb-theme-persona": "11.2.21", + "nodebb-theme-slick": "1.4.16", + "nodebb-theme-vanilla": "12.1.9", "nodebb-widget-essentials": "5.0.4", "nodemailer": "^6.5.0", "nprogress": "0.2.0", - "passport": "^0.4.1", + "passport": "^0.5.0", "passport-http-bearer": "^1.0.1", "passport-local": "1.0.0", "pg": "^8.7.1", "pg-cursor": "^2.7.1", - "postcss": "8.3.6", + "postcss": "8.3.11", "postcss-clean": "1.2.0", "prompt": "^1.1.0", - "ioredis": "4.27.9", + "ioredis": "4.28.0", "request": "2.88.2", "request-promise-native": "^1.0.9", "requirejs": "2.3.6", @@ -116,12 +116,12 @@ "sanitize-html": "^2.3.2", "semver": "^7.3.4", "serve-favicon": "^2.5.0", - "sharp": "0.29.0", + "sharp": "0.29.2", "sitemap": "^7.0.0", "slideout": "1.0.1", - "socket.io": "4.2.0", + "socket.io": "4.3.2", "socket.io-adapter-cluster": "^1.0.1", - "socket.io-client": "4.2.0", + "socket.io-client": "4.3.2", "@socket.io/redis-adapter": "7.0.0", "sortablejs": "1.14.0", "spdx-license-list": "^6.4.0", @@ -132,28 +132,28 @@ "tinycon": "0.6.8", "toobusy-js": "^0.5.1", "uglify-es": "^3.3.9", - "validator": "13.6.0", + "validator": "13.7.0", "visibilityjs": "2.0.2", "winston": "3.3.3", "xml": "^1.0.1", "xregexp": "^5.0.1", - "yargs": "17.1.1", + "yargs": "17.2.1", "zxcvbn": "^4.4.2" }, "devDependencies": { "@apidevtools/swagger-parser": "10.0.3", - "@commitlint/cli": "12.1.4", - "@commitlint/config-angular": "12.1.4", + "@commitlint/cli": "14.1.0", + "@commitlint/config-angular": "14.1.0", "coveralls": "3.1.1", "eslint": "7.32.0", - "eslint-config-nodebb": "0.0.2", - "eslint-plugin-import": "2.24.2", + "eslint-config-nodebb": "0.0.3", + "eslint-plugin-import": "2.25.3", "grunt": "1.4.1", "grunt-contrib-watch": "1.1.0", - "husky": "7.0.2", - "jsdom": "17.0.0", - "lint-staged": "11.1.2", - "mocha": "9.1.1", + "husky": "7.0.4", + "jsdom": "18.0.1", + "lint-staged": "11.2.6", + "mocha": "9.1.3", "mocha-lcov-reporter": "1.3.0", "mockdate": "3.0.5", "nyc": "15.1.0", @@ -182,4 +182,4 @@ "url": "https://github.com/barisusakli" } ] -} \ No newline at end of file +} diff --git a/install/web.js b/install/web.js index 2d9e310890..8d5774326b 100644 --- a/install/web.js +++ b/install/web.js @@ -7,11 +7,7 @@ const fs = require('fs'); const path = require('path'); const childProcess = require('child_process'); const less = require('less'); -const util = require('util'); -const lessRenderAsync = util.promisify( - (style, opts, cb) => less.render(String(style), opts, cb) -); const uglify = require('uglify-es'); const nconf = require('nconf'); @@ -253,7 +249,7 @@ async function compileLess() { try { const installSrc = path.join(__dirname, '../public/less/install.less'); const style = await fs.promises.readFile(installSrc); - const css = await lessRenderAsync(style, { filename: path.resolve(installSrc) }); + const css = await less.render(String(style), { filename: path.resolve(installSrc) }); await fs.promises.writeFile(path.join(__dirname, '../public/installer.css'), css.css); } catch (err) { winston.error(`Unable to compile LESS: \n${err.stack}`); diff --git a/loader.js b/loader.js index d1786eec8a..d9b4b025f2 100644 --- a/loader.js +++ b/loader.js @@ -5,7 +5,6 @@ const fs = require('fs'); const url = require('url'); const path = require('path'); const { fork } = require('child_process'); -const async = require('async'); const logrotate = require('logrotate-stream'); const mkdirp = require('mkdirp'); @@ -36,7 +35,7 @@ const Loader = { }; const appPath = path.join(__dirname, 'app.js'); -Loader.init = function (callback) { +Loader.init = function () { if (silent) { console.log = (...args) => { output.write(`${args.join(' ')}\n`); @@ -45,17 +44,15 @@ Loader.init = function (callback) { process.on('SIGHUP', Loader.restart); process.on('SIGTERM', Loader.stop); - callback(); }; -Loader.displayStartupMessages = function (callback) { +Loader.displayStartupMessages = function () { console.log(''); console.log(`NodeBB v${pkg.version} Copyright (C) 2013-2014 NodeBB Inc.`); console.log('This program comes with ABSOLUTELY NO WARRANTY.'); console.log('This is free software, and you are welcome to redistribute it under certain conditions.'); console.log('For the full license, please visit: http://www.gnu.org/copyleft/gpl.html'); console.log(''); - callback(); }; Loader.addWorkerEvents = function (worker) { @@ -107,17 +104,13 @@ Loader.addWorkerEvents = function (worker) { }); }; -Loader.start = function (callback) { +Loader.start = function () { numProcs = getPorts().length; console.log(`Clustering enabled: Spinning up ${numProcs} process(es).\n`); for (let x = 0; x < numProcs; x += 1) { forkWorker(x, x === 0); } - - if (callback) { - callback(); - } }; function forkWorker(index, isPrimary) { @@ -232,15 +225,12 @@ fs.open(pathToConfig, 'r', (err) => { fs.writeFileSync(pidFilePath, String(process.pid)); } - - async.series([ - Loader.init, - Loader.displayStartupMessages, - Loader.start, - ], (err) => { - if (err) { - console.error('[loader] Error during startup'); - throw err; - } - }); + try { + Loader.init(); + Loader.displayStartupMessages(); + Loader.start(); + } catch (err) { + console.error('[loader] Error during startup'); + throw err; + } }); diff --git a/public/.jshintrc b/public/.jshintrc index 0b65b2f3db..6e93972469 100644 --- a/public/.jshintrc +++ b/public/.jshintrc @@ -1,6 +1,8 @@ { "maxerr" : 50, // {int} Maximum error before stopping + "esversion": 9, + // Enforcing "bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.) "camelcase" : false, // true: Identifiers must be in camelCase @@ -42,7 +44,7 @@ "evil" : false, // true: Tolerate use of `eval` and `new Function()` "expr" : false, // true: Tolerate `ExpressionStatement` as Programs "funcscope" : false, // true: Tolerate defining variables inside control statements" - "globalstrict" : false, // true: Allow global "use strict" (also enables 'strict') + "globalstrict" : true, // true: Allow global "use strict" (also enables 'strict') "iterator" : false, // true: Tolerate using the `__iterator__` property "lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block "laxbreak" : false, // true: Tolerate possibly unsafe line breakings @@ -66,7 +68,6 @@ "utils": true, "overrides": true, "componentHandler": true, - "bootbox": true, "templates": true, "Visibility": true, "Tinycon": true, diff --git a/public/language/ar/admin/advanced/events.json b/public/language/ar/admin/advanced/events.json index ba5f150b4e..218d088830 100644 --- a/public/language/ar/admin/advanced/events.json +++ b/public/language/ar/admin/advanced/events.json @@ -3,6 +3,7 @@ "no-events": "لا توجد أحداث", "control-panel": "لوحة تحكم الأحداث", "delete-events": "حذف الاحداث", + "confirm-delete-all-events": "Are you sure you want to delete all logged events?", "filters": "تصفية", "filters-apply": "تطبيق التصفية", "filter-type": "نوع الحدث", diff --git a/public/language/ar/admin/dashboard.json b/public/language/ar/admin/dashboard.json index baef0d2f11..a54c39d931 100644 --- a/public/language/ar/admin/dashboard.json +++ b/public/language/ar/admin/dashboard.json @@ -56,8 +56,8 @@ "active-users.total": "المجموع", "active-users.connections": "Connections", - "anonymous-registered-users": "المجهولين مقابل المستخدمين المسجلين", - "anonymous": "مجهول", + "guest-registered-users": "Guest vs Registered Users", + "guest": "Guest", "registered": "مسجل", "user-presence": "تواجد المستخدمين", @@ -68,6 +68,7 @@ "unread": "غير مقروء", "high-presence-topics": "مواضيع ذات حضور قوي", + "popular-searches": "Popular Searches", "graphs.page-views": "مشاهدات الصفحة", "graphs.page-views-registered": "Page Views Registered", @@ -75,13 +76,14 @@ "graphs.page-views-bot": "Page Views Bot", "graphs.unique-visitors": "زوار فريدين", "graphs.registered-users": "مستخدمين مسجلين", - "graphs.anonymous-users": "مستخدمين مجهولين", + "graphs.guest-users": "Guest Users", "last-restarted-by": "Last restarted by", "no-users-browsing": "No users browsing", "back-to-dashboard": "Back to Dashboard", "details.no-users": "No users have joined within the selected timeframe", "details.no-topics": "No topics have been posted within the selected timeframe", + "details.no-searches": "No searches have been made yet", "details.no-logins": "No logins have been recorded within the selected timeframe", "details.logins-static": "NodeBB only saves session data for %1 days, and so this table below will only show the most recently active sessions", "details.logins-login-time": "Login Time" diff --git a/public/language/ar/admin/development/info.json b/public/language/ar/admin/development/info.json index 1003af1a5f..11202d9c3a 100644 --- a/public/language/ar/admin/development/info.json +++ b/public/language/ar/admin/development/info.json @@ -8,7 +8,11 @@ "nodejs": "nodejs", "online": "online", "git": "git", - "memory": "memory", + "process-memory": "process memory", + "system-memory": "system memory", + "used-memory-process": "Used memory by process", + "used-memory-os": "Used system memory", + "total-memory-os": "Total system memory", "load": "system load", "cpu-usage": "cpu usage", "uptime": "uptime", diff --git a/public/language/ar/admin/extend/rewards.json b/public/language/ar/admin/extend/rewards.json index 5383a90b33..df89d441a7 100644 --- a/public/language/ar/admin/extend/rewards.json +++ b/public/language/ar/admin/extend/rewards.json @@ -8,8 +8,6 @@ "delete": "Delete", "enable": "Enable", "disable": "Disable", - "control-panel": "Rewards Control", - "new-reward": "New Reward", "alert.delete-success": "Successfully deleted reward", "alert.no-inputs-found": "Illegal reward - no inputs found!", diff --git a/public/language/ar/admin/general/dashboard.json b/public/language/ar/admin/general/dashboard.json deleted file mode 100644 index 3a7c09f01d..0000000000 --- a/public/language/ar/admin/general/dashboard.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "forum-traffic": "Forum Traffic", - "page-views": "مشاهدات الصفحات", - "unique-visitors": "زائرين فريدين", - "new-users": "New Users", - "posts": "مشاركات", - "topics": "مواضيع", - "page-views-seven": "آخر 7 ايام", - "page-views-thirty": "آخر 30 يوماً", - "page-views-last-day": "آخر 24 ساعة", - "page-views-custom": "مدة زمنية مخصصة", - "page-views-custom-start": "بداية المدة", - "page-views-custom-end": "نهاية المده", - "page-views-custom-help": "أدخل نطاقا زمنيا لمرات مشاهدة الصفحات التي ترغب في عرضها. إذا لم يظهر منتقي التاريخ، فإن التنسيق المقبول هو YYYY-MM-DD", - "page-views-custom-error": "الرجاء إدخال نطاق تاريخ صالح بالتنسيق YYYY-MM-DD", - - "stats.yesterday": "Yesterday", - "stats.today": "Today", - "stats.last-week": "Last Week", - "stats.this-week": "This Week", - "stats.last-month": "Last Month", - "stats.this-month": "This Month", - "stats.all": "كل الوقت", - - "updates": "تحديثات", - "running-version": "المنتدى يعمل حاليا على NodeBB الإصدار%1.", - "keep-updated": "تأكد دائما من أن NodeBB يعمل على احدث إصدار للحصول على أحدث التصحيحات الأمنية وإصلاحات الأخطاء.", - "up-to-date": "

المنتدى يعمل على أحدث إصدار

", - "upgrade-available": "

A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-upgrade-available": "

This is an outdated pre-release version of NodeBB. A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-warning": "

هذه نسخة ماقبل الإصدار من NodeBB. قد تحدث أخطاء غير مقصودة.

", - "running-in-development": "المنتدى قيد التشغيل في وضع \"المطورين\". وقد تكون هناك ثغرات أمنية مفتوحة؛ من فضلك تواصل مع مسؤول نظامك.", - "latest-lookup-failed": "

Failed to look up latest available version of NodeBB

", - - "notices": "إشعارات", - "restart-not-required": "إعادة التشغيل غير مطلوب", - "restart-required": "إعادة التشغيل مطلوبة", - "search-plugin-installed": "إضافة البحث منصبة", - "search-plugin-not-installed": "إضافة البحث غير منصبة", - "search-plugin-tooltip": "نصب إضافة البحث من صفحة الإضافات البرمجية لتنشيط وظيفة البحث", - - "control-panel": "التحكم بالنظام", - "rebuild-and-restart": "Rebuild & Restart", - "restart": "Restart", - "restart-warning": "Rebuilding or Restarting your NodeBB will drop all existing connections for a few seconds.", - "restart-disabled": "Rebuilding and Restarting your NodeBB has been disabled as you do not seem to be running it via the appropriate daemon.", - "maintenance-mode": "وضع الصيانة", - "maintenance-mode-title": "انقر هنا لإعداد وضع الصيانة لـNodeBB", - "realtime-chart-updates": "التحديث الفوري للرسم البياني", - - "active-users": "المستخدمين النشطين", - "active-users.users": "الأعضاء", - "active-users.guests": "الزوار", - "active-users.total": "المجموع", - "active-users.connections": "Connections", - - "anonymous-registered-users": "المجهولين مقابل المستخدمين المسجلين", - "anonymous": "مجهول", - "registered": "مسجل", - - "user-presence": "تواجد المستخدمين", - "on-categories": "في قائمة الأقسام", - "reading-posts": "قراءة المشاركات", - "browsing-topics": "تصفح المواضيع", - "recent": "الأخيرة", - "unread": "غير مقروء", - - "high-presence-topics": "مواضيع ذات حضور قوي", - - "graphs.page-views": "مشاهدات الصفحة", - "graphs.page-views-registered": "Page Views Registered", - "graphs.page-views-guest": "Page Views Guest", - "graphs.page-views-bot": "Page Views Bot", - "graphs.unique-visitors": "زوار فريدين", - "graphs.registered-users": "مستخدمين مسجلين", - "graphs.anonymous-users": "مستخدمين مجهولين", - "last-restarted-by": "Last restarted by", - "no-users-browsing": "No users browsing" -} diff --git a/public/language/ar/admin/general/homepage.json b/public/language/ar/admin/general/homepage.json deleted file mode 100644 index 7428d59eeb..0000000000 --- a/public/language/ar/admin/general/homepage.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "home-page": "Home Page", - "description": "Choose what page is shown when users navigate to the root URL of your forum.", - "home-page-route": "Home Page Route", - "custom-route": "Custom Route", - "allow-user-home-pages": "Allow User Home Pages", - "home-page-title": "Title of the home page (default \"Home\")" -} \ No newline at end of file diff --git a/public/language/ar/admin/general/languages.json b/public/language/ar/admin/general/languages.json deleted file mode 100644 index 581e028ade..0000000000 --- a/public/language/ar/admin/general/languages.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "language-settings": "اعدادات اللغة", - "description": "تُحدد اللغة الافتراضية إعدادات اللغة لجميع المستخدمين الذين يزورون المنتدى.
يمكن للأعضاء تجاوز اللغة الافتراضية من خلال صفحة إعدادات الحساب الخاصة بهم.", - "default-language": "اللغة الافتراضية", - "auto-detect": "الكشف عن إعدادات اللغة للزوار بشكل آلي" -} \ No newline at end of file diff --git a/public/language/ar/admin/general/navigation.json b/public/language/ar/admin/general/navigation.json deleted file mode 100644 index 13dd01aae7..0000000000 --- a/public/language/ar/admin/general/navigation.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "icon": "Icon:", - "change-icon": "change", - "route": "Route:", - "tooltip": "Tooltip:", - "text": "Text:", - "text-class": "Text Class: optional", - "class": "Class: optional", - "id": "ID: optional", - - "properties": "Properties:", - "groups": "Groups:", - "open-new-window": "Open in a new window", - - "btn.delete": "Delete", - "btn.disable": "Disable", - "btn.enable": "Enable", - - "available-menu-items": "Available Menu Items", - "custom-route": "Custom Route", - "core": "core", - "plugin": "plugin" -} \ No newline at end of file diff --git a/public/language/ar/admin/general/social.json b/public/language/ar/admin/general/social.json deleted file mode 100644 index 23aedfcfaa..0000000000 --- a/public/language/ar/admin/general/social.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "post-sharing": "Post Sharing", - "info-plugins-additional": "Plugins can add additional networks for sharing posts.", - "save-success": "Successfully saved Post Sharing Networks!" -} \ No newline at end of file diff --git a/public/language/ar/admin/general/sounds.json b/public/language/ar/admin/general/sounds.json deleted file mode 100644 index 6f49e01f91..0000000000 --- a/public/language/ar/admin/general/sounds.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "notifications": "التنبيهات", - "chat-messages": "Chat Messages", - "play-sound": "Play", - "incoming-message": "Incoming Message", - "outgoing-message": "Outgoing Message", - "upload-new-sound": "Upload New Sound", - "saved": "Settings Saved" -} \ No newline at end of file diff --git a/public/language/ar/admin/manage/digest.json b/public/language/ar/admin/manage/digest.json index 8f3661698a..38c634d1f6 100644 --- a/public/language/ar/admin/manage/digest.json +++ b/public/language/ar/admin/manage/digest.json @@ -13,6 +13,7 @@ "resent-single": "Manual digest resend completed", "resent-day": "Daily digest resent", "resent-week": "Weekly digest resent", + "resent-biweek": "Bi-Weekly digest resent", "resent-month": "Monthly digest resent", "null": "Never", "manual-run": "Manual digest run:", diff --git a/public/language/ar/admin/manage/users.json b/public/language/ar/admin/manage/users.json index 75fa918253..510cd1f529 100644 --- a/public/language/ar/admin/manage/users.json +++ b/public/language/ar/admin/manage/users.json @@ -47,6 +47,7 @@ "users.uid": "uid", "users.username": "username", "users.email": "email", + "users.no-email": "(no email)", "users.ip": "IP", "users.postcount": "postcount", "users.reputation": "reputation", diff --git a/public/language/ar/admin/menu.json b/public/language/ar/admin/menu.json index e60767eab3..97d0fa4dfc 100644 --- a/public/language/ar/admin/menu.json +++ b/public/language/ar/admin/menu.json @@ -4,6 +4,7 @@ "dashboard/logins": "Logins", "dashboard/users": "Users", "dashboard/topics": "Topics", + "dashboard/searches": "Searches", "section-general": "عام", "section-manage": "إدارة", diff --git a/public/language/ar/admin/settings/email.json b/public/language/ar/admin/settings/email.json index 9bdbde26a6..65f579d28c 100644 --- a/public/language/ar/admin/settings/email.json +++ b/public/language/ar/admin/settings/email.json @@ -6,7 +6,7 @@ "from-help": "The from name to display in the email.", "smtp-transport": "SMTP Transport", - "smtp-transport.enabled": "Use an external email server to send emails", + "smtp-transport.enabled": "Enable SMTP Transport", "smtp-transport-help": "You can select from a list of well-known services or enter a custom one.", "smtp-transport.service": "Select a service", "smtp-transport.service-custom": "Custom Service", @@ -37,6 +37,10 @@ "subscriptions.hour": "Digest Hour", "subscriptions.hour-help": "Please enter a number representing the hour to send scheduled email digests (e.g. 0 for midnight, 17 for 5:00pm). Keep in mind that this is the hour according to the server itself, and may not exactly match your system clock.
The approximate server time is:
The next daily digest is scheduled to be sent ", "notifications.remove-images": "Remove images from email notifications", + "require-email-address": "Require new users to specify an email address", + "require-email-address-warning": "By default, users can opt-out of entering an email address. Enabling this option means they have to enter an email address in order to proceed with registration. It does not ensure user will enter a real email address, nor even an address they own.", "include-unverified-emails": "Send emails to recipients who have not explicitly confirmed their emails", - "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws." -} \ No newline at end of file + "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws.", + "prompt": "Prompt users to enter or confirm their emails", + "prompt-help": "If a user does not have an email set, or their email is not confirmed, a warning will be shown on screen." +} diff --git a/public/language/ar/admin/settings/general.json b/public/language/ar/admin/settings/general.json index acf06667ec..4584dc9c3e 100644 --- a/public/language/ar/admin/settings/general.json +++ b/public/language/ar/admin/settings/general.json @@ -3,9 +3,9 @@ "title": "عنوان الموقع", "title.short": "عنوان قصير", "title.short-placeholder": "ان لم تقم بكتابة عنوان مختصر, سيتم استخدام عنوان الموقع الكلي", - "title.url": "الرابط", + "title.url": "Title Link URL", "title.url-placeholder": "The URL of the site title", - "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.", + "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "title.name": "اسم المنتدي", "title.show-in-header": "Show Site Title in Header", "browser-title": "عنوان المتصفح", @@ -20,9 +20,9 @@ "logo.image": "صورة", "logo.image-placeholder": "Path to a logo to display on forum header", "logo.upload": "رفع", - "logo.url": "الرابط", + "logo.url": "Logo Link URL", "logo.url-placeholder": "The URL of the site logo", - "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.", + "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "logo.alt-text": "نص بديل", "log.alt-text-placeholder": "Alternative text for accessibility", "favicon": "صورة المفضله", @@ -47,4 +47,4 @@ "undo-timeout": "Undo Timeout", "undo-timeout-help": "Some operations such as moving topics will allow for the moderator to undo their action within a certain timeframe. Set to 0 to disable undo completely.", "topic-tools": "Topic Tools" -} \ No newline at end of file +} diff --git a/public/language/ar/admin/settings/navigation.json b/public/language/ar/admin/settings/navigation.json index 13dd01aae7..7baca85096 100644 --- a/public/language/ar/admin/settings/navigation.json +++ b/public/language/ar/admin/settings/navigation.json @@ -11,6 +11,8 @@ "properties": "Properties:", "groups": "Groups:", "open-new-window": "Open in a new window", + "dropdown": "Dropdown", + "dropdown-placeholder": "Place your dropdown menu items below, ie:
<li><a href="https://myforum.com">Link 1</a></li>", "btn.delete": "Delete", "btn.disable": "Disable", @@ -20,4 +22,4 @@ "custom-route": "Custom Route", "core": "core", "plugin": "plugin" -} \ No newline at end of file +} diff --git a/public/language/ar/admin/settings/notifications.json b/public/language/ar/admin/settings/notifications.json index da6c9680a3..c6d8b928ce 100644 --- a/public/language/ar/admin/settings/notifications.json +++ b/public/language/ar/admin/settings/notifications.json @@ -2,5 +2,6 @@ "notifications": "Notifications", "welcome-notification": "Welcome Notification", "welcome-notification-link": "Welcome Notification Link", - "welcome-notification-uid": "Welcome Notification User (UID)" + "welcome-notification-uid": "Welcome Notification User (UID)", + "post-queue-notification-uid": "Post Queue User (UID)" } \ No newline at end of file diff --git a/public/language/ar/admin/settings/post.json b/public/language/ar/admin/settings/post.json index 27493aafbd..ab8245738c 100644 --- a/public/language/ar/admin/settings/post.json +++ b/public/language/ar/admin/settings/post.json @@ -40,6 +40,7 @@ "teaser.last-post": "Last – Show the latest post, including the original post, if no replies", "teaser.last-reply": "Last – Show the latest reply, or a \"No replies\" placeholder if no replies", "teaser.first": "First", + "showPostPreviewsOnHover": "Show a preview of posts when mouse overed", "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", @@ -56,6 +57,9 @@ "composer.show-help": "Show \"Help\" tab", "composer.enable-plugin-help": "Allow plugins to add content to the help tab", "composer.custom-help": "Custom Help Text", + "backlinks": "Backlinks", + "backlinks.enabled": "Enable topic backlinks", + "backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.", "ip-tracking": "IP Tracking", "ip-tracking.each-post": "Track IP Address for each post", "enable-post-history": "Enable Post History" diff --git a/public/language/ar/admin/settings/user.json b/public/language/ar/admin/settings/user.json index bc8a176396..074655c026 100644 --- a/public/language/ar/admin/settings/user.json +++ b/public/language/ar/admin/settings/user.json @@ -71,6 +71,7 @@ "digest-freq.off": "Off", "digest-freq.daily": "Daily", "digest-freq.weekly": "Weekly", + "digest-freq.biweekly": "Bi-Weekly", "digest-freq.monthly": "Monthly", "email-chat-notifs": "Send an email if a new chat message arrives and I am not online", "email-post-notif": "Send an email when replies are made to topics I am subscribed to", diff --git a/public/language/ar/error.json b/public/language/ar/error.json index 9776fd896a..0dc18f5b59 100644 --- a/public/language/ar/error.json +++ b/public/language/ar/error.json @@ -34,8 +34,9 @@ "email-invited": "Email was already invited", "email-not-confirmed": "Posting in some categories or topics is enabled once your email is confirmed, please click here to send a confirmation email.", "email-not-confirmed-chat": "لا يمكنك الدردشة حتى تقوم بتأكيد بريدك الإلكتروني، الرجاء إضغط هنا لتأكيد بريدك اﻹلكتروني.", - "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You won't be able to post or chat until your email is confirmed.", - "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery. Please click here to enter an email.", + "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You may not be able to post in some categories or chat until your email is confirmed.", + "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery, and may be necessary for chatting and posting in some categories. Please click here to enter an email.", + "user-doesnt-have-email": "User \"%1\" does not have an email set.", "email-confirm-failed": "لم نستطع تفعيل بريدك الإلكتروني، المرجو المحاولة لاحقًا.", "confirm-email-already-sent": "لقد تم ارسال بريد التأكيد، الرجاء اﻹنتظار 1% دقائق لإعادة اﻹرسال", "sendmail-not-found": "The sendmail executable could not be found, please ensure it is installed and executable by the user running NodeBB.", @@ -103,6 +104,7 @@ "already-bookmarked": "You have already bookmarked this post", "already-unbookmarked": "You have already unbookmarked this post", "cant-ban-other-admins": "لايمكن حظر مدبر نظام آخر.", + "cant-make-banned-users-admin": "You can't make banned users admin.", "cant-remove-last-admin": "رجاءًا ، أضف مدير أخر قبل حذف صلاحيات الإدارة من حسابك.", "account-deletion-disabled": "Account deletion is disabled", "cant-delete-admin": "رجاءًا أزل صلاحيات الإدارة قبل حذف الحساب. ", diff --git a/public/language/ar/modules.json b/public/language/ar/modules.json index 12437e0624..8bd1274ece 100644 --- a/public/language/ar/modules.json +++ b/public/language/ar/modules.json @@ -54,7 +54,7 @@ "composer.formatting.strikethrough": "Strikethrough", "composer.formatting.code": "Code", "composer.formatting.link": "Link", - "composer.formatting.picture": "Picture", + "composer.formatting.picture": "Image Link", "composer.upload-picture": "Upload Image", "composer.upload-file": "Upload File", "composer.zen_mode": "Zen Mode", diff --git a/public/language/ar/top.json b/public/language/ar/top.json new file mode 100644 index 0000000000..b8a05bfa5f --- /dev/null +++ b/public/language/ar/top.json @@ -0,0 +1,4 @@ +{ + "title": "Top", + "no_top_topics": "No top topics" +} \ No newline at end of file diff --git a/public/language/ar/topic.json b/public/language/ar/topic.json index 21aa90016a..3f75bd070b 100644 --- a/public/language/ar/topic.json +++ b/public/language/ar/topic.json @@ -47,6 +47,7 @@ "restored-by": "Restored by", "moved-from-by": "Moved from %1 by", "queued-by": "Post queued for approval →", + "backlink": "Referenced by", "bookmark_instructions": "اضغط هنا للعودة لأخر مشاركة مقروءة في الموضوع", "flag-post": "Flag this post", "flag-user": "Flag this user", @@ -138,6 +139,7 @@ "composer.handle_placeholder": "Enter your name/handle here", "composer.discard": "نبذ التغييرات", "composer.submit": "حفظ", + "composer.additional-options": "Additional Options", "composer.schedule": "Schedule", "composer.replying_to": "الرد على %1", "composer.new_topic": "موضوع جديد", @@ -158,6 +160,7 @@ "newest_to_oldest": "من الأحدث إلى الأقدم", "most_votes": "Most Votes", "most_posts": "Most Posts", + "most_views": "Most Views", "stale.title": "Create new topic instead?", "stale.warning": "The topic you are replying to is quite old. Would you like to create a new topic instead, and reference this one in your reply?", "stale.create": "موضوع جديد", diff --git a/public/language/ar/user.json b/public/language/ar/user.json index fbc4d0172f..299f130052 100644 --- a/public/language/ar/user.json +++ b/public/language/ar/user.json @@ -94,6 +94,7 @@ "digest_off": "غير مفعل", "digest_daily": "يوميا", "digest_weekly": "أسبوعيًّا", + "digest_biweekly": "Bi-Weekly", "digest_monthly": "شهريًّا", "has_no_follower": "هذا المستخدم ليس لديه أية متابعين :(", "follows_no_one": "هذا المستخدم لا يتابع أحد :(", diff --git a/public/language/bg/admin/advanced/events.json b/public/language/bg/admin/advanced/events.json index 3351345964..30175ef9a8 100644 --- a/public/language/bg/admin/advanced/events.json +++ b/public/language/bg/admin/advanced/events.json @@ -3,6 +3,7 @@ "no-events": "Няма събития", "control-panel": "Контролен панел за събитията", "delete-events": "Изтриване на събитията", + "confirm-delete-all-events": "Наистина ли искате да изтриете всички събития в журнала?", "filters": "Филтри", "filters-apply": "Прилагане на филтрите", "filter-type": "Вид събитие", diff --git a/public/language/bg/admin/dashboard.json b/public/language/bg/admin/dashboard.json index 860d6c5ff7..7a9cc4416c 100644 --- a/public/language/bg/admin/dashboard.json +++ b/public/language/bg/admin/dashboard.json @@ -56,8 +56,8 @@ "active-users.total": "Общо", "active-users.connections": "Връзки", - "anonymous-registered-users": "Анонимни към регистрирани потребители", - "anonymous": "Анонимни", + "guest-registered-users": "Гости към регистрирани потребители", + "guest": "Гост", "registered": "Регистрирани", "user-presence": "Присъствие на потребителите ", @@ -68,6 +68,7 @@ "unread": "Непрочетени", "high-presence-topics": "Теми с най-голяма присъственост", + "popular-searches": "Популярни търсения", "graphs.page-views": "Преглеждания на страниците", "graphs.page-views-registered": "Преглеждания на страниците от регистрирани потребители", @@ -75,13 +76,14 @@ "graphs.page-views-bot": "Преглеждания на страниците от ботове", "graphs.unique-visitors": "Уникални посетители", "graphs.registered-users": "Регистрирани потребители", - "graphs.anonymous-users": "Анонимни потребители", + "graphs.guest-users": "Гости", "last-restarted-by": "Последно рестартиране от", "no-users-browsing": "Няма разглеждащи потребители", "back-to-dashboard": "Назад към таблото", "details.no-users": "В избрания период не са се регистрирали нови потребители", "details.no-topics": "В избрания период не са публикувани нови теми", + "details.no-searches": "Все още не са правени търсения", "details.no-logins": "В избрания период не са отчетени вписвания", "details.logins-static": "NodeBB запазва данни за сесията в продължение на %1 дни, така че в следната таблица могат да се видят само последните активни сесии", "details.logins-login-time": "Време на вписване" diff --git a/public/language/bg/admin/development/info.json b/public/language/bg/admin/development/info.json index 01e90f0a73..08f70c0692 100644 --- a/public/language/bg/admin/development/info.json +++ b/public/language/bg/admin/development/info.json @@ -8,7 +8,11 @@ "nodejs": "nodejs", "online": "на линия", "git": "git", - "memory": "памет", + "process-memory": "памет на процеса", + "system-memory": "системна памет", + "used-memory-process": "Използвана памет от процеса", + "used-memory-os": "Използвана системна памет", + "total-memory-os": "Обща системна памет", "load": "натоварване на системата", "cpu-usage": "използване на процесора", "uptime": "активно време", diff --git a/public/language/bg/admin/extend/rewards.json b/public/language/bg/admin/extend/rewards.json index 890cc63d9e..d8860e3193 100644 --- a/public/language/bg/admin/extend/rewards.json +++ b/public/language/bg/admin/extend/rewards.json @@ -8,8 +8,6 @@ "delete": "Изтриване", "enable": "Включване", "disable": "Изключване", - "control-panel": "Управление на наградите", - "new-reward": "Нова награда", "alert.delete-success": "Наградата е изтрита успешно", "alert.no-inputs-found": "Неправомерна награда — няма нищо въведено!", diff --git a/public/language/bg/admin/general/dashboard.json b/public/language/bg/admin/general/dashboard.json deleted file mode 100644 index 2e37da173c..0000000000 --- a/public/language/bg/admin/general/dashboard.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "forum-traffic": "Трафик на форума", - "page-views": "Преглеждания на страниците", - "unique-visitors": "Уникални посетители", - "new-users": "Нови потребители", - "posts": "Публикации", - "topics": "Теми", - "page-views-seven": "Последните 7 дни", - "page-views-thirty": "Последните 30 дни", - "page-views-last-day": "Последните 24 часа", - "page-views-custom": "Интервал по избор", - "page-views-custom-start": "Начална дата", - "page-views-custom-end": "Крайна дата", - "page-views-custom-help": "Въведете интервал от дати, за които искате да видите преглежданията на страниците. Ако не се появи календар за избор, можете да въведете датите във формат: ГГГГ-ММ-ДД", - "page-views-custom-error": "Моля, въведете правилен интервал от дати във формата: ГГГГ-ММ-ДД", - - "stats.yesterday": "Вчера", - "stats.today": "Днес", - "stats.last-week": "Миналата седмица", - "stats.this-week": "Тази седмица", - "stats.last-month": "Миналия месец", - "stats.this-month": "Този месец", - "stats.all": "От началото", - - "updates": "Обновления", - "running-version": "Вие използвате NodeBB версия %1.", - "keep-updated": "Стремете се винаги да използвате най-новата версия на NodeBB, за да се възползвате от последните подобрения на сигурността и поправки на проблеми.", - "up-to-date": "

Вие използвате най-новата версия

", - "upgrade-available": "

Има нова версия (версия %1). Ако имате възможност, обновете NodeBB.

", - "prerelease-upgrade-available": "

Това е остаряла предварителна версия на NodeBB. Има нова версия (версия %1). Ако имате възможност, обновете NodeBB.

", - "prerelease-warning": "

Това е версия за предварителен преглед на NodeBB. Възможно е да има неочаквани неизправности.

", - "running-in-development": "Форумът работи в режим за разработчици, така че може да бъде уязвим. Моля, свържете се със системния си администратор.", - "latest-lookup-failed": "

Не може да бъде извършена проверка за последната налична версия на NodeBB

", - - "notices": "Забележки", - "restart-not-required": "Не се изисква рестартиране", - "restart-required": "Изисква се рестартиране", - "search-plugin-installed": "Добавката за търсене е инсталирана", - "search-plugin-not-installed": "Добавката за търсене не е инсталирана", - "search-plugin-tooltip": "Инсталирайте добавка за търсене от страницата с добавките, за да включите функционалността за търсене", - - "control-panel": "Системен контрол", - "rebuild-and-restart": "Повторно изграждане и рестартиране", - "restart": "Рестартиране", - "restart-warning": "Повторното изграждане и рестартирането на NodeBB ще прекъснат всички връзки за няколко секунди.", - "restart-disabled": "Възможностите за повторно изграждане и рестартиране на NodeBB са изключени, тъй като изглежда, че NodeBB не се изпълнява чрез подходящия демон.", - "maintenance-mode": "Режим на профилактика", - "maintenance-mode-title": "Щракнете тук, за да зададете режим на профилактика на NodeBB", - "realtime-chart-updates": "Актуализации на таблиците в реално време", - - "active-users": "Дейни потребители", - "active-users.users": "Потребители", - "active-users.guests": "Гости", - "active-users.total": "Общо", - "active-users.connections": "Връзки", - - "anonymous-registered-users": "Анонимни към регистрирани потребители", - "anonymous": "Анонимни", - "registered": "Регистрирани", - - "user-presence": "Присъствие на потребителите ", - "on-categories": "В списъка с категории", - "reading-posts": "Четящи публикации", - "browsing-topics": "Разглеждащи теми", - "recent": "Скорошни", - "unread": "Непрочетени", - - "high-presence-topics": "Теми с най-голяма присъственост", - - "graphs.page-views": "Преглеждания на страниците", - "graphs.page-views-registered": "Преглеждания на страниците от регистрирани потребители", - "graphs.page-views-guest": "Преглеждания на страниците от гости", - "graphs.page-views-bot": "Преглеждания на страниците от ботове", - "graphs.unique-visitors": "Уникални посетители", - "graphs.registered-users": "Регистрирани потребители", - "graphs.anonymous-users": "Анонимни потребители", - "last-restarted-by": "Последно рестартиране от", - "no-users-browsing": "Няма разглеждащи потребители" -} diff --git a/public/language/bg/admin/general/homepage.json b/public/language/bg/admin/general/homepage.json deleted file mode 100644 index f0b6df5266..0000000000 --- a/public/language/bg/admin/general/homepage.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "home-page": "Начална страница", - "description": "Изберете коя страница да бъде показана, когато потребителите отидат на главния адрес на форума.", - "home-page-route": "Път на началната страница", - "custom-route": "Персонализиран път", - "allow-user-home-pages": "Разрешаване на потребителските начални страници", - "home-page-title": "Заглавие на началната страница (по подразбиране: „Начало“)" -} \ No newline at end of file diff --git a/public/language/bg/admin/general/languages.json b/public/language/bg/admin/general/languages.json deleted file mode 100644 index b4dbba6c3e..0000000000 --- a/public/language/bg/admin/general/languages.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "language-settings": "Езикови настройки", - "description": "Езикът по подразбиране определя езиковите настройки за всички потребители, които посещават Вашия форум.
Отделните потребители могат да сменят езика си от страницата с настройки на профила си.", - "default-language": "Език по подразбиране", - "auto-detect": "Автоматично разпознаване на езика за гостите" -} \ No newline at end of file diff --git a/public/language/bg/admin/general/navigation.json b/public/language/bg/admin/general/navigation.json deleted file mode 100644 index eee7a0e588..0000000000 --- a/public/language/bg/admin/general/navigation.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "icon": "Иконка:", - "change-icon": "промяна", - "route": "Маршрут:", - "tooltip": "Подсказка:", - "text": "Текст:", - "text-class": "Текстов клас: незадължително", - "class": "Клас: незадължително", - "id": "Идентификатор: незадължително", - - "properties": "Свойства:", - "groups": "Групи:", - "open-new-window": "Отваряне в нов прозорец", - - "btn.delete": "Изтриване", - "btn.disable": "Изключване", - "btn.enable": "Включване", - - "available-menu-items": "Налични елементи за менюто", - "custom-route": "Персонализиран маршрут", - "core": "ядро", - "plugin": "добавка" -} \ No newline at end of file diff --git a/public/language/bg/admin/general/social.json b/public/language/bg/admin/general/social.json deleted file mode 100644 index e090d929dc..0000000000 --- a/public/language/bg/admin/general/social.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "post-sharing": "Споделяне на публикации", - "info-plugins-additional": "Добавките могат да добавят допълнителни мрежи за споделяне на публикации.", - "save-success": "Мрежите за споделяне на публикации са запазени успешно!" -} \ No newline at end of file diff --git a/public/language/bg/admin/general/sounds.json b/public/language/bg/admin/general/sounds.json deleted file mode 100644 index 563c11e917..0000000000 --- a/public/language/bg/admin/general/sounds.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "notifications": "Известия", - "chat-messages": "Съобщения в разговори", - "play-sound": "Пускане", - "incoming-message": "Входящо съобщение", - "outgoing-message": "Изходящо съобщение", - "upload-new-sound": "Качване на нов звук", - "saved": "Настройките са запазени" -} \ No newline at end of file diff --git a/public/language/bg/admin/manage/digest.json b/public/language/bg/admin/manage/digest.json index 7bc2d424cf..13da1c1476 100644 --- a/public/language/bg/admin/manage/digest.json +++ b/public/language/bg/admin/manage/digest.json @@ -13,6 +13,7 @@ "resent-single": "Ръчното повторно разпращане на резюмето е завършено", "resent-day": "Дневното резюме беше изпратено повторно", "resent-week": "Седмичното резюме беше изпратено повторно", + "resent-biweek": "Двуседмичното резюме беше изпратено повторно", "resent-month": "Месечното резюме беше изпратено повторно", "null": "Никога", "manual-run": "Ръчно разпращане на резюмето:", diff --git a/public/language/bg/admin/manage/users.json b/public/language/bg/admin/manage/users.json index 50bf807bd1..778f50d203 100644 --- a/public/language/bg/admin/manage/users.json +++ b/public/language/bg/admin/manage/users.json @@ -47,6 +47,7 @@ "users.uid": "потр. ид.", "users.username": "потребителско име", "users.email": "е-поща", + "users.no-email": "(няма е-поща)", "users.ip": "IP адрес", "users.postcount": "брой публикации", "users.reputation": "репутация", diff --git a/public/language/bg/admin/menu.json b/public/language/bg/admin/menu.json index 4127603e4b..6b2b2fc2c7 100644 --- a/public/language/bg/admin/menu.json +++ b/public/language/bg/admin/menu.json @@ -4,6 +4,7 @@ "dashboard/logins": "Вписвания", "dashboard/users": "Потребители", "dashboard/topics": "Теми", + "dashboard/searches": "Търсения", "section-general": "Общи", "section-manage": "Управление", diff --git a/public/language/bg/admin/settings/email.json b/public/language/bg/admin/settings/email.json index 00d4bcba44..74d2d26ce1 100644 --- a/public/language/bg/admin/settings/email.json +++ b/public/language/bg/admin/settings/email.json @@ -6,7 +6,7 @@ "from-help": "Името на изпращача, което да бъде показано в е-писмото.", "smtp-transport": "Транспорт чрез SMTP", - "smtp-transport.enabled": "Използване на външен сървър за е-поща за изпращане на е-писма", + "smtp-transport.enabled": "Включване на транспорта чрез SMTP", "smtp-transport-help": "Можете да изберете от списък от познати услуги, или да въведете такава ръчно.", "smtp-transport.service": "Изберете услуга", "smtp-transport.service-custom": "Персонализирана услуга", @@ -37,6 +37,10 @@ "subscriptions.hour": "Време за разпращане", "subscriptions.hour-help": "Моля, въведете число, представляващо часа, в който да се разпращат е-писма с подготвеното резюме (напр.. 0 за полунощ, 17 за 5 следобед). Имайте предвид, че този час е според часовата зона на сървъра и може да не съвпада с часовника на системата Ви.
Приблизителното време на сървъра е:
Изпращането на следващия ежедневен бюлетин е планирано за ", "notifications.remove-images": "Премахване на изображенията от известията по е-поща", + "require-email-address": "Новите потребители задължително трябва да предоставят е-поща", + "require-email-address-warning": "По подразбиране потребителите могат да не въвеждат адрес на е-поща. Ако включите това, те задължително ще трябва да предоставят е-поща, за да могат да се регистрират. Това не означава, че потребителят ще въведе съществуваща е-поща, нито че тя ще е негова.", "include-unverified-emails": "Изпращане на е-писма към получатели, които не са потвърдили изрично е-пощата си", - "include-unverified-warning": "За потребителите, които имат свързана е-поща с регистрацията си, тя се смята за потвърдена. Но има ситуации, в които това не е така (например при ползване на регистрация от друга система, но и в други случаи), Включете тази настройка на собствен риск – изпращането на е-писма към непотвърдени адреси може да нарушава определени местни закони против нежеланата поща." -} \ No newline at end of file + "include-unverified-warning": "За потребителите, които имат свързана е-поща с регистрацията си, тя се смята за потвърдена. Но има ситуации, в които това не е така (например при ползване на регистрация от друга система, но и в други случаи), Включете тази настройка на собствен риск – изпращането на е-писма към непотвърдени адреси може да нарушава определени местни закони против нежеланата поща.", + "prompt": "Подсещане на потребителите да въведат или потвърдят е-пощата си", + "prompt-help": "Ако потребител няма зададена е-поща, или ако тя не е потвърдена, на екрана му ще се покаже предупредително съобщение." +} diff --git a/public/language/bg/admin/settings/general.json b/public/language/bg/admin/settings/general.json index 3ff56fb050..f573592a11 100644 --- a/public/language/bg/admin/settings/general.json +++ b/public/language/bg/admin/settings/general.json @@ -3,9 +3,9 @@ "title": "Заглавие на уеб сайта", "title.short": "Кратко заглавие", "title.short-placeholder": "Ако не е посочено кратко заглавие, ще бъде използвано заглавието на уеб сайта", - "title.url": "Адрес", - "title.url-placeholder": "Адресът на заглавието на уеб сайта", - "title.url-help": "При щракване върху заглавието, потребителите ще бъдат изпратени на този адрес. Ако бъде оставено празно, потребителите ще бъдат изпращани на началната страница на форума.", + "title.url": "Адрес за заглавието", + "title.url-placeholder": "Адресът за заглавието на уеб сайта", + "title.url-help": "Когато потребител щракне върху заглавието, той ще бъде прехвърлен към този адрес. Ако е празно, потребителят ще бъде изпратен към началната страница на форума.
Забележка: Това не е външният адрес, който се ползва в е-писмата. Той се задава от свойството url във файла config.json", "title.name": "Името на общността Ви", "title.show-in-header": "Показване на заглавието на уеб сайта в заглавната част", "browser-title": "Заглавие на браузъра", @@ -20,9 +20,9 @@ "logo.image": "Изображение", "logo.image-placeholder": "Път до логото, което да бъде показано в заглавната част на форума", "logo.upload": "Качване", - "logo.url": "Адрес", - "logo.url-placeholder": "Адресът на логото на уеб сайта", - "logo.url-help": "При щракване върху логото, потребителите ще бъдат изпратени на този адрес. Ако бъде оставено празно, потребителите ще бъдат изпращани на началната страница на форума.", + "logo.url": "Адрес за логото", + "logo.url-placeholder": "Адресът за логото на уеб сайта", + "logo.url-help": "Когато потребител щракне върху логото, той ще бъде прехвърлен към този адрес. Ако е празно, потребителят ще бъде изпратен към началната страница на форума.
Забележка: Това не е външният адрес, който се ползва в е-писмата. Той се задава от свойството url във файла config.json", "logo.alt-text": "Алтернативен текст", "log.alt-text-placeholder": "Алтернативен текст за достъпност", "favicon": "Иконка на уеб сайта", @@ -47,4 +47,4 @@ "undo-timeout": "Време за отмяна", "undo-timeout-help": "Някои действия, като например преместването на теми, могат да бъдат отменени от модератора в рамките на определено време. Задайте 0, за да забраните изцяло отменянето.", "topic-tools": "Инструменти за темите" -} \ No newline at end of file +} diff --git a/public/language/bg/admin/settings/navigation.json b/public/language/bg/admin/settings/navigation.json index eee7a0e588..34dc2112d8 100644 --- a/public/language/bg/admin/settings/navigation.json +++ b/public/language/bg/admin/settings/navigation.json @@ -11,6 +11,8 @@ "properties": "Свойства:", "groups": "Групи:", "open-new-window": "Отваряне в нов прозорец", + "dropdown": "Падащо меню", + "dropdown-placeholder": "Въведете елементите на падащото меню по-долу. Пример:
<li><a href="https://myforum.com">Връзка 1</a></li>", "btn.delete": "Изтриване", "btn.disable": "Изключване", @@ -20,4 +22,4 @@ "custom-route": "Персонализиран маршрут", "core": "ядро", "plugin": "добавка" -} \ No newline at end of file +} diff --git a/public/language/bg/admin/settings/notifications.json b/public/language/bg/admin/settings/notifications.json index d32a556040..c3831f2bc2 100644 --- a/public/language/bg/admin/settings/notifications.json +++ b/public/language/bg/admin/settings/notifications.json @@ -2,5 +2,6 @@ "notifications": "Известия", "welcome-notification": "Приветствено известие", "welcome-notification-link": "Връзка за приветственото известие", - "welcome-notification-uid": "Потр. ид. за приветственото известие" + "welcome-notification-uid": "Потр. ид. за приветственото известие", + "post-queue-notification-uid": "Потр. ид. за опашката с публикации" } \ No newline at end of file diff --git a/public/language/bg/admin/settings/post.json b/public/language/bg/admin/settings/post.json index 8f4ef2a238..3844182199 100644 --- a/public/language/bg/admin/settings/post.json +++ b/public/language/bg/admin/settings/post.json @@ -40,6 +40,7 @@ "teaser.last-post": "Последната – Показване на последната публикация, или първоначалната такава, ако няма отговори.", "teaser.last-reply": "Последната – Показване на последния отговор, или „Няма отговори“, ако все още няма такива.", "teaser.first": "Първата", + "showPostPreviewsOnHover": "Показване на кратък преглед на публикациите при посочване с мишката", "unread": "Настройки за непрочетените", "unread.cutoff": "Възраст на публикациите, след която те не се показват в непрочетените (в брой дни)", "unread.min-track-last": "Минимален брой публикации в темата, след което да започва следене на последно прочетената", @@ -56,6 +57,9 @@ "composer.show-help": "Показване на раздела „Помощ“", "composer.enable-plugin-help": "Позволяване на добавките да добавят съдържание в раздела за помощ", "composer.custom-help": "Персонализиран текст за помощ", + "backlinks": "Обратни връзки", + "backlinks.enabled": "Включване на обратните връзки в темите", + "backlinks.help": "Ако в публикацията има препратка към друга тема, там ще бъде поставена връзка към публикацията, с конкретното време.", "ip-tracking": "Записване на IP адреса", "ip-tracking.each-post": "Записване на IP адреса за всяка публикация", "enable-post-history": "Включване на историята на публикациите" diff --git a/public/language/bg/admin/settings/user.json b/public/language/bg/admin/settings/user.json index aaffcdddb1..e04b10dea6 100644 --- a/public/language/bg/admin/settings/user.json +++ b/public/language/bg/admin/settings/user.json @@ -71,6 +71,7 @@ "digest-freq.off": "Изключено", "digest-freq.daily": "Ежедневно", "digest-freq.weekly": "Ежеседмично", + "digest-freq.biweekly": "На всеки две седмици", "digest-freq.monthly": "Ежемесечно", "email-chat-notifs": "Изпращане на е-писмо, ако получа ново съобщение в разговор, а не съм на линия", "email-post-notif": "Изпращане на е-писмо, когато се появи отговор в темите, за които съм абониран(а).", diff --git a/public/language/bg/error.json b/public/language/bg/error.json index f40a8ef492..339a78d653 100644 --- a/public/language/bg/error.json +++ b/public/language/bg/error.json @@ -34,8 +34,9 @@ "email-invited": "На тази е-поща вече е била изпратена покана", "email-not-confirmed": "Публикуването в някои категории и теми ще бъде възможно едва след като е-пощата Ви бъде потвърдена. Щръкнете тук, за да Ви изпратим е-писмо за потвърждение.", "email-not-confirmed-chat": "Няма да можете да пишете в разговори, докато е-пощата Ви не бъде потвърдена. Моля, натиснете тук, за да потвърдите е-пощата си.", - "email-not-confirmed-email-sent": "Вашата е-поща все още не е потвърдена. Моля, проверете входящата си кутия за писмото за потвърждение. Няма да можете да публикувате съобщения или да пишете в разговори, докато е-пощата Ви не бъде потвърдена.", - "no-email-to-confirm": "Нямате зададена е-поща. Тя е необходима за възстановяването на акаунта в случай на проблем. Натиснете тук, за да въведете е-поща.", + "email-not-confirmed-email-sent": "Вашата е-поща все още не е потвърдена. Моля, проверете входящата си кутия за писмото за потвърждение. Възможно е да не можете да публикувате съобщения или да пишете в разговори, докато е-пощата Ви не бъде потвърдена.", + "no-email-to-confirm": "Нямате зададена е-поща. Тя е необходима за възстановяването на акаунта в случай на проблем, а може и да се изисква, за да пишете в някои категории. Натиснете тук, за да въведете е-поща.", + "user-doesnt-have-email": "Потребителят „%1“ няма зададена е-поща.", "email-confirm-failed": "Не успяхме да потвърдим е-пощата Ви. Моля, опитайте отново по-късно.", "confirm-email-already-sent": "Е-писмото за потвърждение вече е изпратено. Моля, почакайте още %1 минута/и, преди да изпратите ново.", "sendmail-not-found": "Изпълнимият файл на „sendmail“ не може да бъде намерен. Моля, уверете се, че е инсталиран и изпълним за потребителя, чрез който е пуснат NodeBB.", @@ -103,6 +104,7 @@ "already-bookmarked": "Вече имате отметка към тази публикация", "already-unbookmarked": "Вече сте премахнали отметката си от тази публикация", "cant-ban-other-admins": "Не можете да блокирате другите администратори!", + "cant-make-banned-users-admin": "Не можете да давате администраторски права на блокирани потребители.", "cant-remove-last-admin": "Вие сте единственият администратор. Добавете друг потребител като администратор, преди да премахнете себе си като администратор", "account-deletion-disabled": "Изтриването на акаунт е забранено", "cant-delete-admin": "Премахнете администраторските права от този акаунт, преди да го изтриете.", diff --git a/public/language/bg/modules.json b/public/language/bg/modules.json index e7336cedbe..cfd8578016 100644 --- a/public/language/bg/modules.json +++ b/public/language/bg/modules.json @@ -54,7 +54,7 @@ "composer.formatting.strikethrough": "Зачертан", "composer.formatting.code": "Код", "composer.formatting.link": "Връзка", - "composer.formatting.picture": "Снимка", + "composer.formatting.picture": "Връзка към изображение", "composer.upload-picture": "Качване на изображение", "composer.upload-file": "Качване на файл", "composer.zen_mode": "Режим Дзен", diff --git a/public/language/bg/top.json b/public/language/bg/top.json new file mode 100644 index 0000000000..54b8374e26 --- /dev/null +++ b/public/language/bg/top.json @@ -0,0 +1,4 @@ +{ + "title": "Най-популярни", + "no_top_topics": "Няма най-популярни теми" +} \ No newline at end of file diff --git a/public/language/bg/topic.json b/public/language/bg/topic.json index d624f68667..97fb850605 100644 --- a/public/language/bg/topic.json +++ b/public/language/bg/topic.json @@ -47,6 +47,7 @@ "restored-by": "Възстановена от", "moved-from-by": "Преместена от %1 от", "queued-by": "Публикацията е добавена в опашката за одобрение →", + "backlink": "Спомената от", "bookmark_instructions": "Щракнете тук, за да се върнете към последно прочетената публикация в тази тема.", "flag-post": "Докладване на тази публикация", "flag-user": "Докладване на този потребител", @@ -138,6 +139,7 @@ "composer.handle_placeholder": "Въведете името тук", "composer.discard": "Отхвърляне", "composer.submit": "Публикуване", + "composer.additional-options": "Допълнителни настройки", "composer.schedule": "Насрочване", "composer.replying_to": "Отговор на %1", "composer.new_topic": "Нова тема", @@ -158,6 +160,7 @@ "newest_to_oldest": "Първо най-новите", "most_votes": "Първо тези с най-много гласове", "most_posts": "Първо тези с най-много публикации", + "most_views": "Първо тези с най-много преглеждания", "stale.title": "Създаване на нова тема вместо това?", "stale.warning": "Темата, в която отговаряте, е доста стара. Искате ли вместо това да създадете нова и да направите препратка към тази в отговора си?", "stale.create": "Създаване на нова тема", diff --git a/public/language/bg/user.json b/public/language/bg/user.json index 02ebca2bcf..6026f976a2 100644 --- a/public/language/bg/user.json +++ b/public/language/bg/user.json @@ -94,6 +94,7 @@ "digest_off": "Изключено", "digest_daily": "Ежедневно", "digest_weekly": "Ежеседмично", + "digest_biweekly": "На всеки две седмици", "digest_monthly": "Ежемесечно", "has_no_follower": "Този потребител няма последователи :(", "follows_no_one": "Този потребител не следва никого :(", diff --git a/public/language/bn/admin/advanced/events.json b/public/language/bn/admin/advanced/events.json index 56d9457971..b2c2033fb5 100644 --- a/public/language/bn/admin/advanced/events.json +++ b/public/language/bn/admin/advanced/events.json @@ -3,6 +3,7 @@ "no-events": "There are no events", "control-panel": "Events Control Panel", "delete-events": "Delete Events", + "confirm-delete-all-events": "Are you sure you want to delete all logged events?", "filters": "Filters", "filters-apply": "Apply Filters", "filter-type": "Event Type", diff --git a/public/language/bn/admin/dashboard.json b/public/language/bn/admin/dashboard.json index 0de31d4917..4d39626882 100644 --- a/public/language/bn/admin/dashboard.json +++ b/public/language/bn/admin/dashboard.json @@ -56,8 +56,8 @@ "active-users.total": "Total", "active-users.connections": "Connections", - "anonymous-registered-users": "Anonymous vs Registered Users", - "anonymous": "Anonymous", + "guest-registered-users": "Guest vs Registered Users", + "guest": "Guest", "registered": "Registered", "user-presence": "User Presence", @@ -68,6 +68,7 @@ "unread": "Unread", "high-presence-topics": "High Presence Topics", + "popular-searches": "Popular Searches", "graphs.page-views": "Page Views", "graphs.page-views-registered": "Page Views Registered", @@ -75,13 +76,14 @@ "graphs.page-views-bot": "Page Views Bot", "graphs.unique-visitors": "Unique Visitors", "graphs.registered-users": "Registered Users", - "graphs.anonymous-users": "Anonymous Users", + "graphs.guest-users": "Guest Users", "last-restarted-by": "Last restarted by", "no-users-browsing": "No users browsing", "back-to-dashboard": "Back to Dashboard", "details.no-users": "No users have joined within the selected timeframe", "details.no-topics": "No topics have been posted within the selected timeframe", + "details.no-searches": "No searches have been made yet", "details.no-logins": "No logins have been recorded within the selected timeframe", "details.logins-static": "NodeBB only saves session data for %1 days, and so this table below will only show the most recently active sessions", "details.logins-login-time": "Login Time" diff --git a/public/language/bn/admin/development/info.json b/public/language/bn/admin/development/info.json index 1003af1a5f..11202d9c3a 100644 --- a/public/language/bn/admin/development/info.json +++ b/public/language/bn/admin/development/info.json @@ -8,7 +8,11 @@ "nodejs": "nodejs", "online": "online", "git": "git", - "memory": "memory", + "process-memory": "process memory", + "system-memory": "system memory", + "used-memory-process": "Used memory by process", + "used-memory-os": "Used system memory", + "total-memory-os": "Total system memory", "load": "system load", "cpu-usage": "cpu usage", "uptime": "uptime", diff --git a/public/language/bn/admin/extend/rewards.json b/public/language/bn/admin/extend/rewards.json index 5383a90b33..df89d441a7 100644 --- a/public/language/bn/admin/extend/rewards.json +++ b/public/language/bn/admin/extend/rewards.json @@ -8,8 +8,6 @@ "delete": "Delete", "enable": "Enable", "disable": "Disable", - "control-panel": "Rewards Control", - "new-reward": "New Reward", "alert.delete-success": "Successfully deleted reward", "alert.no-inputs-found": "Illegal reward - no inputs found!", diff --git a/public/language/bn/admin/general/dashboard.json b/public/language/bn/admin/general/dashboard.json deleted file mode 100644 index 37ae537786..0000000000 --- a/public/language/bn/admin/general/dashboard.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "forum-traffic": "Forum Traffic", - "page-views": "Page Views", - "unique-visitors": "Unique Visitors", - "new-users": "New Users", - "posts": "Posts", - "topics": "Topics", - "page-views-seven": "Last 7 Days", - "page-views-thirty": "Last 30 Days", - "page-views-last-day": "Last 24 hours", - "page-views-custom": "Custom Date Range", - "page-views-custom-start": "Range Start", - "page-views-custom-end": "Range End", - "page-views-custom-help": "Enter a date range of page views you would like to view. If no date picker is available, the accepted format is YYYY-MM-DD", - "page-views-custom-error": "Please enter a valid date range in the format YYYY-MM-DD", - - "stats.yesterday": "Yesterday", - "stats.today": "Today", - "stats.last-week": "Last Week", - "stats.this-week": "This Week", - "stats.last-month": "Last Month", - "stats.this-month": "This Month", - "stats.all": "All Time", - - "updates": "Updates", - "running-version": "You are running NodeBB v%1.", - "keep-updated": "Always make sure that your NodeBB is up to date for the latest security patches and bug fixes.", - "up-to-date": "

You are up-to-date

", - "upgrade-available": "

A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-upgrade-available": "

This is an outdated pre-release version of NodeBB. A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-warning": "

This is a pre-release version of NodeBB. Unintended bugs may occur.

", - "running-in-development": "Forum is running in development mode. The forum may be open to potential vulnerabilities; please contact your system administrator.", - "latest-lookup-failed": "

Failed to look up latest available version of NodeBB

", - - "notices": "Notices", - "restart-not-required": "Restart not required", - "restart-required": "Restart required", - "search-plugin-installed": "Search Plugin installed", - "search-plugin-not-installed": "Search Plugin not installed", - "search-plugin-tooltip": "Install a search plugin from the plugin page in order to activate search functionality", - - "control-panel": "System Control", - "rebuild-and-restart": "Rebuild & Restart", - "restart": "Restart", - "restart-warning": "Rebuilding or Restarting your NodeBB will drop all existing connections for a few seconds.", - "restart-disabled": "Rebuilding and Restarting your NodeBB has been disabled as you do not seem to be running it via the appropriate daemon.", - "maintenance-mode": "Maintenance Mode", - "maintenance-mode-title": "Click here to set up maintenance mode for NodeBB", - "realtime-chart-updates": "Realtime Chart Updates", - - "active-users": "Active Users", - "active-users.users": "Users", - "active-users.guests": "Guests", - "active-users.total": "Total", - "active-users.connections": "Connections", - - "anonymous-registered-users": "Anonymous vs Registered Users", - "anonymous": "Anonymous", - "registered": "Registered", - - "user-presence": "User Presence", - "on-categories": "On categories list", - "reading-posts": "Reading posts", - "browsing-topics": "Browsing topics", - "recent": "Recent", - "unread": "Unread", - - "high-presence-topics": "High Presence Topics", - - "graphs.page-views": "Page Views", - "graphs.page-views-registered": "Page Views Registered", - "graphs.page-views-guest": "Page Views Guest", - "graphs.page-views-bot": "Page Views Bot", - "graphs.unique-visitors": "Unique Visitors", - "graphs.registered-users": "Registered Users", - "graphs.anonymous-users": "Anonymous Users", - "last-restarted-by": "Last restarted by", - "no-users-browsing": "No users browsing" -} diff --git a/public/language/bn/admin/general/homepage.json b/public/language/bn/admin/general/homepage.json deleted file mode 100644 index 7428d59eeb..0000000000 --- a/public/language/bn/admin/general/homepage.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "home-page": "Home Page", - "description": "Choose what page is shown when users navigate to the root URL of your forum.", - "home-page-route": "Home Page Route", - "custom-route": "Custom Route", - "allow-user-home-pages": "Allow User Home Pages", - "home-page-title": "Title of the home page (default \"Home\")" -} \ No newline at end of file diff --git a/public/language/bn/admin/general/languages.json b/public/language/bn/admin/general/languages.json deleted file mode 100644 index bdd57849b3..0000000000 --- a/public/language/bn/admin/general/languages.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "language-settings": "Language Settings", - "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language", - "auto-detect": "Auto Detect Language Setting for Guests" -} \ No newline at end of file diff --git a/public/language/bn/admin/general/navigation.json b/public/language/bn/admin/general/navigation.json deleted file mode 100644 index 13dd01aae7..0000000000 --- a/public/language/bn/admin/general/navigation.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "icon": "Icon:", - "change-icon": "change", - "route": "Route:", - "tooltip": "Tooltip:", - "text": "Text:", - "text-class": "Text Class: optional", - "class": "Class: optional", - "id": "ID: optional", - - "properties": "Properties:", - "groups": "Groups:", - "open-new-window": "Open in a new window", - - "btn.delete": "Delete", - "btn.disable": "Disable", - "btn.enable": "Enable", - - "available-menu-items": "Available Menu Items", - "custom-route": "Custom Route", - "core": "core", - "plugin": "plugin" -} \ No newline at end of file diff --git a/public/language/bn/admin/general/social.json b/public/language/bn/admin/general/social.json deleted file mode 100644 index 23aedfcfaa..0000000000 --- a/public/language/bn/admin/general/social.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "post-sharing": "Post Sharing", - "info-plugins-additional": "Plugins can add additional networks for sharing posts.", - "save-success": "Successfully saved Post Sharing Networks!" -} \ No newline at end of file diff --git a/public/language/bn/admin/general/sounds.json b/public/language/bn/admin/general/sounds.json deleted file mode 100644 index 95ccbde0f1..0000000000 --- a/public/language/bn/admin/general/sounds.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "notifications": "Notifications", - "chat-messages": "Chat Messages", - "play-sound": "Play", - "incoming-message": "Incoming Message", - "outgoing-message": "Outgoing Message", - "upload-new-sound": "Upload New Sound", - "saved": "Settings Saved" -} \ No newline at end of file diff --git a/public/language/bn/admin/manage/digest.json b/public/language/bn/admin/manage/digest.json index 8f3661698a..38c634d1f6 100644 --- a/public/language/bn/admin/manage/digest.json +++ b/public/language/bn/admin/manage/digest.json @@ -13,6 +13,7 @@ "resent-single": "Manual digest resend completed", "resent-day": "Daily digest resent", "resent-week": "Weekly digest resent", + "resent-biweek": "Bi-Weekly digest resent", "resent-month": "Monthly digest resent", "null": "Never", "manual-run": "Manual digest run:", diff --git a/public/language/bn/admin/manage/users.json b/public/language/bn/admin/manage/users.json index 38a065b954..2a3c0c4829 100644 --- a/public/language/bn/admin/manage/users.json +++ b/public/language/bn/admin/manage/users.json @@ -47,6 +47,7 @@ "users.uid": "uid", "users.username": "username", "users.email": "email", + "users.no-email": "(no email)", "users.ip": "IP", "users.postcount": "postcount", "users.reputation": "reputation", diff --git a/public/language/bn/admin/menu.json b/public/language/bn/admin/menu.json index 1c5ea73b4f..5b22fbeb36 100644 --- a/public/language/bn/admin/menu.json +++ b/public/language/bn/admin/menu.json @@ -4,6 +4,7 @@ "dashboard/logins": "Logins", "dashboard/users": "Users", "dashboard/topics": "Topics", + "dashboard/searches": "Searches", "section-general": "General", "section-manage": "Manage", diff --git a/public/language/bn/admin/settings/email.json b/public/language/bn/admin/settings/email.json index 4dd2f2cb17..7970d5ec70 100644 --- a/public/language/bn/admin/settings/email.json +++ b/public/language/bn/admin/settings/email.json @@ -6,7 +6,7 @@ "from-help": "The from name to display in the email.", "smtp-transport": "SMTP Transport", - "smtp-transport.enabled": "Use an external email server to send emails", + "smtp-transport.enabled": "Enable SMTP Transport", "smtp-transport-help": "You can select from a list of well-known services or enter a custom one.", "smtp-transport.service": "Select a service", "smtp-transport.service-custom": "Custom Service", @@ -37,6 +37,10 @@ "subscriptions.hour": "Digest Hour", "subscriptions.hour-help": "Please enter a number representing the hour to send scheduled email digests (e.g. 0 for midnight, 17 for 5:00pm). Keep in mind that this is the hour according to the server itself, and may not exactly match your system clock.
The approximate server time is:
The next daily digest is scheduled to be sent ", "notifications.remove-images": "Remove images from email notifications", + "require-email-address": "Require new users to specify an email address", + "require-email-address-warning": "By default, users can opt-out of entering an email address. Enabling this option means they have to enter an email address in order to proceed with registration. It does not ensure user will enter a real email address, nor even an address they own.", "include-unverified-emails": "Send emails to recipients who have not explicitly confirmed their emails", - "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws." -} \ No newline at end of file + "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws.", + "prompt": "Prompt users to enter or confirm their emails", + "prompt-help": "If a user does not have an email set, or their email is not confirmed, a warning will be shown on screen." +} diff --git a/public/language/bn/admin/settings/general.json b/public/language/bn/admin/settings/general.json index be7df90870..29b939861b 100644 --- a/public/language/bn/admin/settings/general.json +++ b/public/language/bn/admin/settings/general.json @@ -3,9 +3,9 @@ "title": "Site Title", "title.short": "Short Title", "title.short-placeholder": "If no short title is specified, the site title will be used", - "title.url": "URL", + "title.url": "Title Link URL", "title.url-placeholder": "The URL of the site title", - "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.", + "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "title.name": "Your Community Name", "title.show-in-header": "Show Site Title in Header", "browser-title": "Browser Title", @@ -20,9 +20,9 @@ "logo.image": "Image", "logo.image-placeholder": "Path to a logo to display on forum header", "logo.upload": "Upload", - "logo.url": "URL", + "logo.url": "Logo Link URL", "logo.url-placeholder": "The URL of the site logo", - "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.", + "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "logo.alt-text": "Alt Text", "log.alt-text-placeholder": "Alternative text for accessibility", "favicon": "Favicon", @@ -47,4 +47,4 @@ "undo-timeout": "Undo Timeout", "undo-timeout-help": "Some operations such as moving topics will allow for the moderator to undo their action within a certain timeframe. Set to 0 to disable undo completely.", "topic-tools": "Topic Tools" -} \ No newline at end of file +} diff --git a/public/language/bn/admin/settings/navigation.json b/public/language/bn/admin/settings/navigation.json index 13dd01aae7..7baca85096 100644 --- a/public/language/bn/admin/settings/navigation.json +++ b/public/language/bn/admin/settings/navigation.json @@ -11,6 +11,8 @@ "properties": "Properties:", "groups": "Groups:", "open-new-window": "Open in a new window", + "dropdown": "Dropdown", + "dropdown-placeholder": "Place your dropdown menu items below, ie:
<li><a href="https://myforum.com">Link 1</a></li>", "btn.delete": "Delete", "btn.disable": "Disable", @@ -20,4 +22,4 @@ "custom-route": "Custom Route", "core": "core", "plugin": "plugin" -} \ No newline at end of file +} diff --git a/public/language/bn/admin/settings/notifications.json b/public/language/bn/admin/settings/notifications.json index da6c9680a3..c6d8b928ce 100644 --- a/public/language/bn/admin/settings/notifications.json +++ b/public/language/bn/admin/settings/notifications.json @@ -2,5 +2,6 @@ "notifications": "Notifications", "welcome-notification": "Welcome Notification", "welcome-notification-link": "Welcome Notification Link", - "welcome-notification-uid": "Welcome Notification User (UID)" + "welcome-notification-uid": "Welcome Notification User (UID)", + "post-queue-notification-uid": "Post Queue User (UID)" } \ No newline at end of file diff --git a/public/language/bn/admin/settings/post.json b/public/language/bn/admin/settings/post.json index 27493aafbd..ab8245738c 100644 --- a/public/language/bn/admin/settings/post.json +++ b/public/language/bn/admin/settings/post.json @@ -40,6 +40,7 @@ "teaser.last-post": "Last – Show the latest post, including the original post, if no replies", "teaser.last-reply": "Last – Show the latest reply, or a \"No replies\" placeholder if no replies", "teaser.first": "First", + "showPostPreviewsOnHover": "Show a preview of posts when mouse overed", "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", @@ -56,6 +57,9 @@ "composer.show-help": "Show \"Help\" tab", "composer.enable-plugin-help": "Allow plugins to add content to the help tab", "composer.custom-help": "Custom Help Text", + "backlinks": "Backlinks", + "backlinks.enabled": "Enable topic backlinks", + "backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.", "ip-tracking": "IP Tracking", "ip-tracking.each-post": "Track IP Address for each post", "enable-post-history": "Enable Post History" diff --git a/public/language/bn/admin/settings/user.json b/public/language/bn/admin/settings/user.json index 48be13b75e..7923bf8cbe 100644 --- a/public/language/bn/admin/settings/user.json +++ b/public/language/bn/admin/settings/user.json @@ -71,6 +71,7 @@ "digest-freq.off": "Off", "digest-freq.daily": "Daily", "digest-freq.weekly": "Weekly", + "digest-freq.biweekly": "Bi-Weekly", "digest-freq.monthly": "Monthly", "email-chat-notifs": "Send an email if a new chat message arrives and I am not online", "email-post-notif": "Send an email when replies are made to topics I am subscribed to", diff --git a/public/language/bn/error.json b/public/language/bn/error.json index f7fcdc6f29..8bc3ee96e4 100644 --- a/public/language/bn/error.json +++ b/public/language/bn/error.json @@ -34,8 +34,9 @@ "email-invited": "Email was already invited", "email-not-confirmed": "Posting in some categories or topics is enabled once your email is confirmed, please click here to send a confirmation email.", "email-not-confirmed-chat": "You are unable to chat until your email is confirmed, please click here to confirm your email.", - "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You won't be able to post or chat until your email is confirmed.", - "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery. Please click here to enter an email.", + "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You may not be able to post in some categories or chat until your email is confirmed.", + "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery, and may be necessary for chatting and posting in some categories. Please click here to enter an email.", + "user-doesnt-have-email": "User \"%1\" does not have an email set.", "email-confirm-failed": "We could not confirm your email, please try again later.", "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", "sendmail-not-found": "The sendmail executable could not be found, please ensure it is installed and executable by the user running NodeBB.", @@ -103,6 +104,7 @@ "already-bookmarked": "You have already bookmarked this post", "already-unbookmarked": "You have already unbookmarked this post", "cant-ban-other-admins": "আপনি অন্য এ্যাডমিনদের নিষিদ্ধ করতে পারেন না!", + "cant-make-banned-users-admin": "You can't make banned users admin.", "cant-remove-last-admin": "You are the only administrator. Add another user as an administrator before removing yourself as admin", "account-deletion-disabled": "Account deletion is disabled", "cant-delete-admin": "Remove administrator privileges from this account before attempting to delete it.", diff --git a/public/language/bn/modules.json b/public/language/bn/modules.json index f95d48b7a0..7e7c986d0a 100644 --- a/public/language/bn/modules.json +++ b/public/language/bn/modules.json @@ -54,7 +54,7 @@ "composer.formatting.strikethrough": "Strikethrough", "composer.formatting.code": "Code", "composer.formatting.link": "Link", - "composer.formatting.picture": "Picture", + "composer.formatting.picture": "Image Link", "composer.upload-picture": "Upload Image", "composer.upload-file": "Upload File", "composer.zen_mode": "Zen Mode", diff --git a/public/language/bn/top.json b/public/language/bn/top.json new file mode 100644 index 0000000000..b8a05bfa5f --- /dev/null +++ b/public/language/bn/top.json @@ -0,0 +1,4 @@ +{ + "title": "Top", + "no_top_topics": "No top topics" +} \ No newline at end of file diff --git a/public/language/bn/topic.json b/public/language/bn/topic.json index 57e9638bd4..0c06dce39a 100644 --- a/public/language/bn/topic.json +++ b/public/language/bn/topic.json @@ -47,6 +47,7 @@ "restored-by": "Restored by", "moved-from-by": "Moved from %1 by", "queued-by": "Post queued for approval →", + "backlink": "Referenced by", "bookmark_instructions": "Click here to return to the last read post in this thread.", "flag-post": "Flag this post", "flag-user": "Flag this user", @@ -138,6 +139,7 @@ "composer.handle_placeholder": "Enter your name/handle here", "composer.discard": "বাতিল", "composer.submit": "সাবমিট", + "composer.additional-options": "Additional Options", "composer.schedule": "Schedule", "composer.replying_to": "%1 এর উত্তরে:", "composer.new_topic": "নতুন টপিক", @@ -158,6 +160,7 @@ "newest_to_oldest": "নতুন থেকে পুরাতন", "most_votes": "Most Votes", "most_posts": "Most Posts", + "most_views": "Most Views", "stale.title": "Create new topic instead?", "stale.warning": "The topic you are replying to is quite old. Would you like to create a new topic instead, and reference this one in your reply?", "stale.create": "Create a new topic", diff --git a/public/language/bn/user.json b/public/language/bn/user.json index b224369d3f..d75cb6b85a 100644 --- a/public/language/bn/user.json +++ b/public/language/bn/user.json @@ -94,6 +94,7 @@ "digest_off": "বন্ধ", "digest_daily": "দৈনিক", "digest_weekly": "সাপ্তাহিক", + "digest_biweekly": "Bi-Weekly", "digest_monthly": "মাসিক", "has_no_follower": "এই সদস্যের কোন ফলোয়ার নেই :(", "follows_no_one": "এই সদস্য কাউকে ফলো করছেন না :(", diff --git a/public/language/cs/admin/advanced/events.json b/public/language/cs/admin/advanced/events.json index 49925c5035..327b32d858 100644 --- a/public/language/cs/admin/advanced/events.json +++ b/public/language/cs/admin/advanced/events.json @@ -3,6 +3,7 @@ "no-events": "Žádné nové události", "control-panel": "Ovládací panel událostí", "delete-events": "Odstranit události", + "confirm-delete-all-events": "Are you sure you want to delete all logged events?", "filters": "Filtry", "filters-apply": "Použít filtry", "filter-type": "Typ události", diff --git a/public/language/cs/admin/dashboard.json b/public/language/cs/admin/dashboard.json index d0197077c9..2ca82e2cce 100644 --- a/public/language/cs/admin/dashboard.json +++ b/public/language/cs/admin/dashboard.json @@ -56,8 +56,8 @@ "active-users.total": "Celkově", "active-users.connections": "Připojení", - "anonymous-registered-users": "Anonymní × registrovaní uživatelé", - "anonymous": "Anonymní", + "guest-registered-users": "Guest vs Registered Users", + "guest": "Guest", "registered": "Registrovaní", "user-presence": "Výskyt uživatele", @@ -68,6 +68,7 @@ "unread": "Nepřečtené", "high-presence-topics": "Témata s vysokou účastí", + "popular-searches": "Popular Searches", "graphs.page-views": "Zobrazení stránky", "graphs.page-views-registered": "Zobrazených stránek/registrovaní", @@ -75,13 +76,14 @@ "graphs.page-views-bot": "Zobrazených stránek/bot", "graphs.unique-visitors": "Jedineční návštěvníci", "graphs.registered-users": "Registrovaní uživatelé", - "graphs.anonymous-users": "Anonymní uživatelé", + "graphs.guest-users": "Guest Users", "last-restarted-by": "Poslední restart od", "no-users-browsing": "Nikdo si nic neprohlíží", "back-to-dashboard": "Back to Dashboard", "details.no-users": "No users have joined within the selected timeframe", "details.no-topics": "No topics have been posted within the selected timeframe", + "details.no-searches": "No searches have been made yet", "details.no-logins": "No logins have been recorded within the selected timeframe", "details.logins-static": "NodeBB only saves session data for %1 days, and so this table below will only show the most recently active sessions", "details.logins-login-time": "Login Time" diff --git a/public/language/cs/admin/development/info.json b/public/language/cs/admin/development/info.json index a70c980f85..c47e061cb2 100644 --- a/public/language/cs/admin/development/info.json +++ b/public/language/cs/admin/development/info.json @@ -8,7 +8,11 @@ "nodejs": "nodejs", "online": "připojen", "git": "git", - "memory": "paměť", + "process-memory": "process memory", + "system-memory": "system memory", + "used-memory-process": "Used memory by process", + "used-memory-os": "Used system memory", + "total-memory-os": "Total system memory", "load": "zatížení systému", "cpu-usage": "využití CPU", "uptime": "čas spuštění", diff --git a/public/language/cs/admin/extend/rewards.json b/public/language/cs/admin/extend/rewards.json index dd95f17371..9f0d26cfc3 100644 --- a/public/language/cs/admin/extend/rewards.json +++ b/public/language/cs/admin/extend/rewards.json @@ -8,8 +8,6 @@ "delete": "Odstranit", "enable": "Povolit", "disable": "Zakázat", - "control-panel": "Ovládací panel odměn", - "new-reward": "Nová odměna", "alert.delete-success": "Odměna byla úspěšně smazána", "alert.no-inputs-found": "Nepovolená odměna – nebyl nalezen žádný záznam.", diff --git a/public/language/cs/admin/general/dashboard.json b/public/language/cs/admin/general/dashboard.json deleted file mode 100644 index d7ca6fdd7c..0000000000 --- a/public/language/cs/admin/general/dashboard.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "forum-traffic": "Provoz fóra", - "page-views": "Zobrazení stránky", - "unique-visitors": "Jedineční návštěvníci", - "new-users": "Nový uživatelé", - "posts": "Příspěvky", - "topics": "Témata", - "page-views-seven": "Posledních 7 dnů", - "page-views-thirty": "Posledních 30 dní", - "page-views-last-day": "Posledních 24 hodin", - "page-views-custom": "Dle rozsahu data", - "page-views-custom-start": "Začátek rozsahu", - "page-views-custom-end": "Konec rozsahu", - "page-views-custom-help": "Zadejte rozsah data zobrazení stránek, které chcete vidět. Není-li datum nastaveno, výchozí formát je YYYY-MM-DD", - "page-views-custom-error": "Zadejte správný rozsah ve formátu YYYY-MM-DD", - - "stats.yesterday": "Včera", - "stats.today": "Dnes", - "stats.last-week": "Poslední týden", - "stats.this-week": "Tento víkend", - "stats.last-month": "Poslední měsíc", - "stats.this-month": "Tento měsíc", - "stats.all": "Všechny časy", - - "updates": "Aktualizace", - "running-version": "Fungujete na NodeBB v%1.", - "keep-updated": "Vždy udržujte NodeBB aktuální kvůli bezpečnostním záplatám a opravám.", - "up-to-date": "

Máte aktuální verzi

", - "upgrade-available": "

Nová verze (v%1) byla zveřejněna. Zvažte aktualizaci vašeho NodeBB.

", - "prerelease-upgrade-available": "

Toto je zastaralá testovací verze NodeBB. Nová verze (v%1) byla zveřejněna. Zvažte aktualizaci vaší verze NodeBB.

", - "prerelease-warning": "

Toto je zkušební verze NodeBB. Mohou se vyskytnout různé chyby.

", - "running-in-development": "Fórum běží ve vývojářském režimu a může být potencionálně zranitelné . Kontaktujte správce systému.", - "latest-lookup-failed": "

Náhled na poslední dostupnou verzi NodeBB

", - - "notices": "Oznámení", - "restart-not-required": "Restart není potřeba", - "restart-required": "Je potřeba restartovat", - "search-plugin-installed": "Rozšíření pro hledání je nainstalováno", - "search-plugin-not-installed": "Rozšíření pro hledání není nainstalováno", - "search-plugin-tooltip": "Pro aktivování funkce vyhledávání, nainstalujte rozšíření pro hledání ze stránky rozšíření.", - - "control-panel": "Ovládání systému", - "rebuild-and-restart": "Znovu sestavit a restartovat", - "restart": "Restartovat", - "restart-warning": "Znovu sestavení nebo restartování NodeBB odpojí všechna existující připojení na několik vteřin.", - "restart-disabled": "Znovu sestavení a restartování vašeho NodeBB bylo zakázáno, protože se nezdá, že byste byl/a připojena přes příslušného „daemona”.", - "maintenance-mode": "Režim údržby", - "maintenance-mode-title": "Pro nastavení režimu údržby NodeBB, klikněte zde", - "realtime-chart-updates": "Aktualizace grafů v reálném čase", - - "active-users": "Aktivní uživatelé", - "active-users.users": "Uživatelé", - "active-users.guests": "Hosté", - "active-users.total": "Celkově", - "active-users.connections": "Připojení", - - "anonymous-registered-users": "Anonymní × registrovaní uživatelé", - "anonymous": "Anonymní", - "registered": "Registrovaní", - - "user-presence": "Výskyt uživatele", - "on-categories": "V seznamu kategorii", - "reading-posts": "Čtení příspěvku", - "browsing-topics": "Prohlížení témat", - "recent": "Poslední", - "unread": "Nepřečtené", - - "high-presence-topics": "Témata s vysokou účastí", - - "graphs.page-views": "Zobrazení stránky", - "graphs.page-views-registered": "Zobrazených stránek/registrovaní", - "graphs.page-views-guest": "Zobrazených stránek/hosté", - "graphs.page-views-bot": "Zobrazených stránek/bot", - "graphs.unique-visitors": "Jedineční návštěvníci", - "graphs.registered-users": "Registrovaní uživatelé", - "graphs.anonymous-users": "Anonymní uživatelé", - "last-restarted-by": "Poslední restart od", - "no-users-browsing": "Nikdo si nic neprohlíží" -} diff --git a/public/language/cs/admin/general/homepage.json b/public/language/cs/admin/general/homepage.json deleted file mode 100644 index 3db45d23c3..0000000000 --- a/public/language/cs/admin/general/homepage.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "home-page": "Domovská stránka", - "description": "Vyberte, kterou stránku chcete zobrazit, jakmile uživatel přejde na výchozí URL vašeho fóra.", - "home-page-route": "Cesta k domovské stránce", - "custom-route": "Upravit cestu", - "allow-user-home-pages": "Povolit uživatelům domovské stránky", - "home-page-title": "Titulka domovské stránky (výchozí „Domů”)" -} \ No newline at end of file diff --git a/public/language/cs/admin/general/languages.json b/public/language/cs/admin/general/languages.json deleted file mode 100644 index 37124c7d04..0000000000 --- a/public/language/cs/admin/general/languages.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "language-settings": "Nastavení jazyka", - "description": "Výchozí jazyk určuje nastavení jazyka pro všechny uživatele navštěvující vaše fórum.
Každý uživatel si může pak nastavit výchozí jazyk na stránce nastavení účtu.", - "default-language": "Výchozí jazyk", - "auto-detect": "Automaticky detekovat nastavení jazyka pro hosty" -} \ No newline at end of file diff --git a/public/language/cs/admin/general/navigation.json b/public/language/cs/admin/general/navigation.json deleted file mode 100644 index a434257b94..0000000000 --- a/public/language/cs/admin/general/navigation.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "icon": "Ikona:", - "change-icon": "změnit", - "route": "Cesta:", - "tooltip": "Tip:", - "text": "Text:", - "text-class": "Textová třída: doporučené", - "class": "Třída: doporučené", - "id": "ID: doporučené", - - "properties": "Vlastnosti:", - "groups": "Skupiny:", - "open-new-window": "Otevřít v novém okně", - - "btn.delete": "Odstranit", - "btn.disable": "Zakázat", - "btn.enable": "Povolit", - - "available-menu-items": "Dostupné položky nabídky", - "custom-route": "Upravit cestu", - "core": "jádro", - "plugin": "rozšíření" -} \ No newline at end of file diff --git a/public/language/cs/admin/general/social.json b/public/language/cs/admin/general/social.json deleted file mode 100644 index 5645b29e42..0000000000 --- a/public/language/cs/admin/general/social.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "post-sharing": "Sdílení příspěvku", - "info-plugins-additional": "Rozšíření mohou přidat další dodatečné sítě pro sdílení příspěvků.", - "save-success": "Úspěšně uložené sítě sdílející příspěvky." -} \ No newline at end of file diff --git a/public/language/cs/admin/general/sounds.json b/public/language/cs/admin/general/sounds.json deleted file mode 100644 index d9b2796971..0000000000 --- a/public/language/cs/admin/general/sounds.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "notifications": "Upozornění", - "chat-messages": "Zprávy konverzace", - "play-sound": "Přehrát", - "incoming-message": "Příchozí zpráva", - "outgoing-message": "Odchozí zpráva", - "upload-new-sound": "Nahrát nový zvuk", - "saved": "Nastavení bylo uloženo" -} \ No newline at end of file diff --git a/public/language/cs/admin/manage/digest.json b/public/language/cs/admin/manage/digest.json index f627b0d2b0..96c7c0849b 100644 --- a/public/language/cs/admin/manage/digest.json +++ b/public/language/cs/admin/manage/digest.json @@ -13,6 +13,7 @@ "resent-single": "Manuální znovu poslání přehledu bylo dokončeno", "resent-day": "Znovu odeslat denní přehled", "resent-week": "Znovu odeslat týdenní přehled", + "resent-biweek": "Bi-Weekly digest resent", "resent-month": "Znovu odeslat měsíční přehled", "null": "Nikdy", "manual-run": "Spustit manuálně přehled:", diff --git a/public/language/cs/admin/manage/users.json b/public/language/cs/admin/manage/users.json index 4337c097f3..1389977e70 100644 --- a/public/language/cs/admin/manage/users.json +++ b/public/language/cs/admin/manage/users.json @@ -47,6 +47,7 @@ "users.uid": "uid", "users.username": "jméno", "users.email": "e-mail", + "users.no-email": "(no email)", "users.ip": "IP", "users.postcount": "počet příspěvků", "users.reputation": "reputace", diff --git a/public/language/cs/admin/menu.json b/public/language/cs/admin/menu.json index 9eee43ba3d..603d9616fe 100644 --- a/public/language/cs/admin/menu.json +++ b/public/language/cs/admin/menu.json @@ -4,6 +4,7 @@ "dashboard/logins": "Logins", "dashboard/users": "Users", "dashboard/topics": "Topics", + "dashboard/searches": "Searches", "section-general": "Všeobecné", "section-manage": "Spravovat", diff --git a/public/language/cs/admin/settings/email.json b/public/language/cs/admin/settings/email.json index 339700448d..daf0596406 100644 --- a/public/language/cs/admin/settings/email.json +++ b/public/language/cs/admin/settings/email.json @@ -6,7 +6,7 @@ "from-help": "Zobrazené jméno v e-mailu v – Od", "smtp-transport": "Přenos SMTP", - "smtp-transport.enabled": "Pro odesílání e-mailů použít externí e-mailový server ", + "smtp-transport.enabled": "Enable SMTP Transport", "smtp-transport-help": "Ze seznamu můžete vybrat známé služby nebo zadat vlastní.", "smtp-transport.service": "Vyberte službu", "smtp-transport.service-custom": "Uživatelský služba", @@ -37,6 +37,10 @@ "subscriptions.hour": "Hodina přehledu", "subscriptions.hour-help": "Zadejte číslo odpovídající hodině, kdy mají být odeslány přehledové e-maily (tj. 0 pro půlnoc, 17 pro 5:00pm). Mějte na paměti, že tato hodina závisí na hodinách samotného serveru a nemusí tak souhlasit se systémovými hodinami.
Přibližný čas serveru je: .
Další odeslání přehledů je plánováno na .", "notifications.remove-images": "Remove images from email notifications", + "require-email-address": "Require new users to specify an email address", + "require-email-address-warning": "By default, users can opt-out of entering an email address. Enabling this option means they have to enter an email address in order to proceed with registration. It does not ensure user will enter a real email address, nor even an address they own.", "include-unverified-emails": "Send emails to recipients who have not explicitly confirmed their emails", - "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws." -} \ No newline at end of file + "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws.", + "prompt": "Prompt users to enter or confirm their emails", + "prompt-help": "If a user does not have an email set, or their email is not confirmed, a warning will be shown on screen." +} diff --git a/public/language/cs/admin/settings/general.json b/public/language/cs/admin/settings/general.json index fdbcb48c1b..6ae5d5f244 100644 --- a/public/language/cs/admin/settings/general.json +++ b/public/language/cs/admin/settings/general.json @@ -3,9 +3,9 @@ "title": "Název stránky", "title.short": "Short Title", "title.short-placeholder": "If no short title is specified, the site title will be used", - "title.url": "URL", + "title.url": "Title Link URL", "title.url-placeholder": "URL názvu stránky", - "title.url-help": "Bude-li kliknuto na název, uživatel bude přesměrován na tuto adresu. Zůstane-li prázdné, uživatel bude odeslán na index fóra", + "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "title.name": "Název vaší komunity", "title.show-in-header": "Zobrazit název stránky v hlavičce", "browser-title": "Název prohlížeče", @@ -20,9 +20,9 @@ "logo.image": "Obrázek", "logo.image-placeholder": "Cesta k logu, aby mohlo být zobrazeno v hlavičce fóra", "logo.upload": "Nahrát", - "logo.url": "URL", + "logo.url": "Logo Link URL", "logo.url-placeholder": "URL loga stránky", - "logo.url-help": "Bude-li kliknuto na logo, uživatel bude přesměrován na tuto adresu. Zůstane-li prázdné, uživatel bude přesměrován na index fóra.", + "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "logo.alt-text": "Popisující text (alt)", "log.alt-text-placeholder": "Alternativní text pro přístupnost", "favicon": "Ikonka (favicon)", @@ -47,4 +47,4 @@ "undo-timeout": "Undo Timeout", "undo-timeout-help": "Some operations such as moving topics will allow for the moderator to undo their action within a certain timeframe. Set to 0 to disable undo completely.", "topic-tools": "Topic Tools" -} \ No newline at end of file +} diff --git a/public/language/cs/admin/settings/navigation.json b/public/language/cs/admin/settings/navigation.json index a434257b94..5811c99768 100644 --- a/public/language/cs/admin/settings/navigation.json +++ b/public/language/cs/admin/settings/navigation.json @@ -11,6 +11,8 @@ "properties": "Vlastnosti:", "groups": "Skupiny:", "open-new-window": "Otevřít v novém okně", + "dropdown": "Dropdown", + "dropdown-placeholder": "Place your dropdown menu items below, ie:
<li><a href="https://myforum.com">Link 1</a></li>", "btn.delete": "Odstranit", "btn.disable": "Zakázat", @@ -20,4 +22,4 @@ "custom-route": "Upravit cestu", "core": "jádro", "plugin": "rozšíření" -} \ No newline at end of file +} diff --git a/public/language/cs/admin/settings/notifications.json b/public/language/cs/admin/settings/notifications.json index 39bc83bdcb..83e73d288e 100644 --- a/public/language/cs/admin/settings/notifications.json +++ b/public/language/cs/admin/settings/notifications.json @@ -2,5 +2,6 @@ "notifications": "Oznámení", "welcome-notification": "Uvítání", "welcome-notification-link": "Odkaz na uvítání", - "welcome-notification-uid": "Uvítání uživatele (UID)" + "welcome-notification-uid": "Uvítání uživatele (UID)", + "post-queue-notification-uid": "Post Queue User (UID)" } \ No newline at end of file diff --git a/public/language/cs/admin/settings/post.json b/public/language/cs/admin/settings/post.json index af5702173a..eb89abb569 100644 --- a/public/language/cs/admin/settings/post.json +++ b/public/language/cs/admin/settings/post.json @@ -40,6 +40,7 @@ "teaser.last-post": "Poslední – zobrazení posledního příspěvku, včetně hlavního příspěvku, nejsou-li odpovědi", "teaser.last-reply": "Poslední – zobrazení poslední odpovědi, nebo nejsou-li žádné odpovědi textu „Bez odpovědi”", "teaser.first": "První", + "showPostPreviewsOnHover": "Show a preview of posts when mouse overed", "unread": "Nastavení nepřečtených", "unread.cutoff": "Dny ukončení nepřečtených", "unread.min-track-last": "Minimální počet příspěvků v tématu před posledním čtením", @@ -56,6 +57,9 @@ "composer.show-help": "Zobrazit záložku „Nápověda”", "composer.enable-plugin-help": "Povolit rozšíření přidat obsah do záložky nápovědy", "composer.custom-help": "Uživatelský text nápovědy", + "backlinks": "Backlinks", + "backlinks.enabled": "Enable topic backlinks", + "backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.", "ip-tracking": "Sledování IP", "ip-tracking.each-post": "Sledovat adresu IP u každého příspěvku", "enable-post-history": "Povolit historii příspěvku" diff --git a/public/language/cs/admin/settings/user.json b/public/language/cs/admin/settings/user.json index f63ff2a5aa..b4725da73d 100644 --- a/public/language/cs/admin/settings/user.json +++ b/public/language/cs/admin/settings/user.json @@ -71,6 +71,7 @@ "digest-freq.off": "Vypnuto", "digest-freq.daily": "Denně", "digest-freq.weekly": "Týdně", + "digest-freq.biweekly": "Bi-Weekly", "digest-freq.monthly": "Měsíčně", "email-chat-notifs": "Nejsem-li online zaslat e-mail, dorazí-li nová zpráva z chatu", "email-post-notif": "Zaslat e-mail, objeví-li se odpovědi v tématu, který sleduji", diff --git a/public/language/cs/error.json b/public/language/cs/error.json index 619fb91adf..4f156be9bc 100644 --- a/public/language/cs/error.json +++ b/public/language/cs/error.json @@ -34,8 +34,9 @@ "email-invited": "Email was already invited", "email-not-confirmed": "Posting in some categories or topics is enabled once your email is confirmed, please click here to send a confirmation email.", "email-not-confirmed-chat": "Nebude schopen konverzovat, dokud nebude váš e-mail potvrzen. Pro jeho potvrzení klikněte zde.", - "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You won't be able to post or chat until your email is confirmed.", - "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery. Please click here to enter an email.", + "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You may not be able to post in some categories or chat until your email is confirmed.", + "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery, and may be necessary for chatting and posting in some categories. Please click here to enter an email.", + "user-doesnt-have-email": "User \"%1\" does not have an email set.", "email-confirm-failed": "Nemohli jsme ověřit vaši e-mailovou adresu, zkuste to později.", "confirm-email-already-sent": "Potvrzovací e-mail byl již odeslán. Vyčkejte %1 minut/y, chcete-li odeslat další.", "sendmail-not-found": "Modul pro odeslání e-mailů nebyl nalezen. Zkontrolujte prosím, zda je nainstalován a spuštěn uživatelem, který spustil NodeBB.", @@ -103,6 +104,7 @@ "already-bookmarked": "Již jste tento příspěvek zazáložkoval", "already-unbookmarked": "Již jste u tohoto příspěvku odebral záložku", "cant-ban-other-admins": "Nemůžete zablokovat jiné správce.", + "cant-make-banned-users-admin": "You can't make banned users admin.", "cant-remove-last-admin": "Jste jediným správcem. Před vlastním odebráním oprávnění správce nejdříve přidejte jiného uživatele jako správce", "account-deletion-disabled": "Account deletion is disabled", "cant-delete-admin": "Před odstraněním účtu mu nejprve odeberte oprávnění správce.", diff --git a/public/language/cs/modules.json b/public/language/cs/modules.json index 5ac3ffbede..57bc4fcf9e 100644 --- a/public/language/cs/modules.json +++ b/public/language/cs/modules.json @@ -54,7 +54,7 @@ "composer.formatting.strikethrough": "Přeškrtnutí", "composer.formatting.code": "Kód", "composer.formatting.link": "Odkaz", - "composer.formatting.picture": "Obrázek", + "composer.formatting.picture": "Image Link", "composer.upload-picture": "Nahrát obrázek", "composer.upload-file": "Nahrát soubor", "composer.zen_mode": "Režim Zem", diff --git a/public/language/cs/top.json b/public/language/cs/top.json new file mode 100644 index 0000000000..b8a05bfa5f --- /dev/null +++ b/public/language/cs/top.json @@ -0,0 +1,4 @@ +{ + "title": "Top", + "no_top_topics": "No top topics" +} \ No newline at end of file diff --git a/public/language/cs/topic.json b/public/language/cs/topic.json index 02dea5d855..103b825415 100644 --- a/public/language/cs/topic.json +++ b/public/language/cs/topic.json @@ -47,6 +47,7 @@ "restored-by": "Restored by", "moved-from-by": "Moved from %1 by", "queued-by": "Post queued for approval →", + "backlink": "Referenced by", "bookmark_instructions": "Pro návrat k poslednímu čtenému příspěvku v tématu, klikněte zde.", "flag-post": "Flag this post", "flag-user": "Flag this user", @@ -138,6 +139,7 @@ "composer.handle_placeholder": "Enter your name/handle here", "composer.discard": "Zrušit", "composer.submit": "Odeslat", + "composer.additional-options": "Additional Options", "composer.schedule": "Schedule", "composer.replying_to": "Odpovídání na %1", "composer.new_topic": "Nové téma", @@ -158,6 +160,7 @@ "newest_to_oldest": "Od nejnovějších po nejstarší", "most_votes": "S nejvíce hlasy", "most_posts": "S nejvíce příspěvky", + "most_views": "Most Views", "stale.title": "Raději vytvořit nové téma?", "stale.warning": "Reagujete na starší téma. Nechcete raději vytvořit nové téma a na původní v něm odkázat?", "stale.create": "Vytvořit nové téma", diff --git a/public/language/cs/user.json b/public/language/cs/user.json index 941540cce8..8d4d58e821 100644 --- a/public/language/cs/user.json +++ b/public/language/cs/user.json @@ -94,6 +94,7 @@ "digest_off": "Vypnuto", "digest_daily": "Denně", "digest_weekly": "Týdně", + "digest_biweekly": "Bi-Weekly", "digest_monthly": "Měsíčně", "has_no_follower": "Tohoto uživatele nikdo nesleduje :(", "follows_no_one": "Tento uživatel nikoho nesleduje :(", diff --git a/public/language/da/admin/advanced/events.json b/public/language/da/admin/advanced/events.json index fe72f82b6a..dcdb6608c1 100644 --- a/public/language/da/admin/advanced/events.json +++ b/public/language/da/admin/advanced/events.json @@ -3,6 +3,7 @@ "no-events": "There are no events", "control-panel": "Kontrol Panel for Begivenheder", "delete-events": "Delete Events", + "confirm-delete-all-events": "Are you sure you want to delete all logged events?", "filters": "Filters", "filters-apply": "Apply Filters", "filter-type": "Event Type", diff --git a/public/language/da/admin/dashboard.json b/public/language/da/admin/dashboard.json index dbe77149a1..1d7c3df85d 100644 --- a/public/language/da/admin/dashboard.json +++ b/public/language/da/admin/dashboard.json @@ -56,8 +56,8 @@ "active-users.total": "Total", "active-users.connections": "Connections", - "anonymous-registered-users": "Anonymous vs Registered Users", - "anonymous": "Anonymous", + "guest-registered-users": "Guest vs Registered Users", + "guest": "Guest", "registered": "Registered", "user-presence": "User Presence", @@ -68,6 +68,7 @@ "unread": "Unread", "high-presence-topics": "High Presence Topics", + "popular-searches": "Popular Searches", "graphs.page-views": "Page Views", "graphs.page-views-registered": "Page Views Registered", @@ -75,13 +76,14 @@ "graphs.page-views-bot": "Page Views Bot", "graphs.unique-visitors": "Unique Visitors", "graphs.registered-users": "Registered Users", - "graphs.anonymous-users": "Anonymous Users", + "graphs.guest-users": "Guest Users", "last-restarted-by": "Last restarted by", "no-users-browsing": "No users browsing", "back-to-dashboard": "Back to Dashboard", "details.no-users": "No users have joined within the selected timeframe", "details.no-topics": "No topics have been posted within the selected timeframe", + "details.no-searches": "No searches have been made yet", "details.no-logins": "No logins have been recorded within the selected timeframe", "details.logins-static": "NodeBB only saves session data for %1 days, and so this table below will only show the most recently active sessions", "details.logins-login-time": "Login Time" diff --git a/public/language/da/admin/development/info.json b/public/language/da/admin/development/info.json index 1003af1a5f..11202d9c3a 100644 --- a/public/language/da/admin/development/info.json +++ b/public/language/da/admin/development/info.json @@ -8,7 +8,11 @@ "nodejs": "nodejs", "online": "online", "git": "git", - "memory": "memory", + "process-memory": "process memory", + "system-memory": "system memory", + "used-memory-process": "Used memory by process", + "used-memory-os": "Used system memory", + "total-memory-os": "Total system memory", "load": "system load", "cpu-usage": "cpu usage", "uptime": "uptime", diff --git a/public/language/da/admin/extend/rewards.json b/public/language/da/admin/extend/rewards.json index 5383a90b33..df89d441a7 100644 --- a/public/language/da/admin/extend/rewards.json +++ b/public/language/da/admin/extend/rewards.json @@ -8,8 +8,6 @@ "delete": "Delete", "enable": "Enable", "disable": "Disable", - "control-panel": "Rewards Control", - "new-reward": "New Reward", "alert.delete-success": "Successfully deleted reward", "alert.no-inputs-found": "Illegal reward - no inputs found!", diff --git a/public/language/da/admin/general/dashboard.json b/public/language/da/admin/general/dashboard.json deleted file mode 100644 index caf09a9f23..0000000000 --- a/public/language/da/admin/general/dashboard.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "forum-traffic": "Forum Traffik", - "page-views": "Side Visninger", - "unique-visitors": "Unikke Besøgere", - "new-users": "New Users", - "posts": "Posts", - "topics": "Topics", - "page-views-seven": "Last 7 Days", - "page-views-thirty": "Last 30 Days", - "page-views-last-day": "Last 24 hours", - "page-views-custom": "Custom Date Range", - "page-views-custom-start": "Range Start", - "page-views-custom-end": "Range End", - "page-views-custom-help": "Enter a date range of page views you would like to view. If no date picker is available, the accepted format is YYYY-MM-DD", - "page-views-custom-error": "Please enter a valid date range in the format YYYY-MM-DD", - - "stats.yesterday": "Yesterday", - "stats.today": "Today", - "stats.last-week": "Last Week", - "stats.this-week": "This Week", - "stats.last-month": "Last Month", - "stats.this-month": "This Month", - "stats.all": "All Time", - - "updates": "Opdateringer", - "running-version": "Du kører NodeBB v%1.", - "keep-updated": "Altid sikrer dig at din NodeBB er opdateret for de seneste sikkerheds og bug rettelser.", - "up-to-date": "

Du er opdateret

", - "upgrade-available": "

A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-upgrade-available": "

This is an outdated pre-release version of NodeBB. A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-warning": "

Dette er en pre-release udgave af NodeBB. Uforventede bugs kan forekomme.

", - "running-in-development": "Forum is running in development mode. The forum may be open to potential vulnerabilities; please contact your system administrator.", - "latest-lookup-failed": "

Failed to look up latest available version of NodeBB

", - - "notices": "Varsler", - "restart-not-required": "Restart not required", - "restart-required": "Restart required", - "search-plugin-installed": "Search Plugin installed", - "search-plugin-not-installed": "Search Plugin not installed", - "search-plugin-tooltip": "Install a search plugin from the plugin page in order to activate search functionality", - - "control-panel": "System Kontrol", - "rebuild-and-restart": "Rebuild & Restart", - "restart": "Restart", - "restart-warning": "Rebuilding or Restarting your NodeBB will drop all existing connections for a few seconds.", - "restart-disabled": "Rebuilding and Restarting your NodeBB has been disabled as you do not seem to be running it via the appropriate daemon.", - "maintenance-mode": "Maintenance Mode", - "maintenance-mode-title": "Click here to set up maintenance mode for NodeBB", - "realtime-chart-updates": "Realtime Chart Updates", - - "active-users": "Active Users", - "active-users.users": "Users", - "active-users.guests": "Guests", - "active-users.total": "Total", - "active-users.connections": "Connections", - - "anonymous-registered-users": "Anonymous vs Registered Users", - "anonymous": "Anonymous", - "registered": "Registered", - - "user-presence": "User Presence", - "on-categories": "On categories list", - "reading-posts": "Reading posts", - "browsing-topics": "Browsing topics", - "recent": "Recent", - "unread": "Unread", - - "high-presence-topics": "High Presence Topics", - - "graphs.page-views": "Page Views", - "graphs.page-views-registered": "Page Views Registered", - "graphs.page-views-guest": "Page Views Guest", - "graphs.page-views-bot": "Page Views Bot", - "graphs.unique-visitors": "Unique Visitors", - "graphs.registered-users": "Registered Users", - "graphs.anonymous-users": "Anonymous Users", - "last-restarted-by": "Last restarted by", - "no-users-browsing": "No users browsing" -} diff --git a/public/language/da/admin/general/homepage.json b/public/language/da/admin/general/homepage.json deleted file mode 100644 index 7428d59eeb..0000000000 --- a/public/language/da/admin/general/homepage.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "home-page": "Home Page", - "description": "Choose what page is shown when users navigate to the root URL of your forum.", - "home-page-route": "Home Page Route", - "custom-route": "Custom Route", - "allow-user-home-pages": "Allow User Home Pages", - "home-page-title": "Title of the home page (default \"Home\")" -} \ No newline at end of file diff --git a/public/language/da/admin/general/languages.json b/public/language/da/admin/general/languages.json deleted file mode 100644 index bdd57849b3..0000000000 --- a/public/language/da/admin/general/languages.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "language-settings": "Language Settings", - "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language", - "auto-detect": "Auto Detect Language Setting for Guests" -} \ No newline at end of file diff --git a/public/language/da/admin/general/navigation.json b/public/language/da/admin/general/navigation.json deleted file mode 100644 index 13dd01aae7..0000000000 --- a/public/language/da/admin/general/navigation.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "icon": "Icon:", - "change-icon": "change", - "route": "Route:", - "tooltip": "Tooltip:", - "text": "Text:", - "text-class": "Text Class: optional", - "class": "Class: optional", - "id": "ID: optional", - - "properties": "Properties:", - "groups": "Groups:", - "open-new-window": "Open in a new window", - - "btn.delete": "Delete", - "btn.disable": "Disable", - "btn.enable": "Enable", - - "available-menu-items": "Available Menu Items", - "custom-route": "Custom Route", - "core": "core", - "plugin": "plugin" -} \ No newline at end of file diff --git a/public/language/da/admin/general/social.json b/public/language/da/admin/general/social.json deleted file mode 100644 index 23aedfcfaa..0000000000 --- a/public/language/da/admin/general/social.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "post-sharing": "Post Sharing", - "info-plugins-additional": "Plugins can add additional networks for sharing posts.", - "save-success": "Successfully saved Post Sharing Networks!" -} \ No newline at end of file diff --git a/public/language/da/admin/general/sounds.json b/public/language/da/admin/general/sounds.json deleted file mode 100644 index 95ccbde0f1..0000000000 --- a/public/language/da/admin/general/sounds.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "notifications": "Notifications", - "chat-messages": "Chat Messages", - "play-sound": "Play", - "incoming-message": "Incoming Message", - "outgoing-message": "Outgoing Message", - "upload-new-sound": "Upload New Sound", - "saved": "Settings Saved" -} \ No newline at end of file diff --git a/public/language/da/admin/manage/digest.json b/public/language/da/admin/manage/digest.json index 8f3661698a..38c634d1f6 100644 --- a/public/language/da/admin/manage/digest.json +++ b/public/language/da/admin/manage/digest.json @@ -13,6 +13,7 @@ "resent-single": "Manual digest resend completed", "resent-day": "Daily digest resent", "resent-week": "Weekly digest resent", + "resent-biweek": "Bi-Weekly digest resent", "resent-month": "Monthly digest resent", "null": "Never", "manual-run": "Manual digest run:", diff --git a/public/language/da/admin/manage/users.json b/public/language/da/admin/manage/users.json index 38a065b954..2a3c0c4829 100644 --- a/public/language/da/admin/manage/users.json +++ b/public/language/da/admin/manage/users.json @@ -47,6 +47,7 @@ "users.uid": "uid", "users.username": "username", "users.email": "email", + "users.no-email": "(no email)", "users.ip": "IP", "users.postcount": "postcount", "users.reputation": "reputation", diff --git a/public/language/da/admin/menu.json b/public/language/da/admin/menu.json index 1c5ea73b4f..5b22fbeb36 100644 --- a/public/language/da/admin/menu.json +++ b/public/language/da/admin/menu.json @@ -4,6 +4,7 @@ "dashboard/logins": "Logins", "dashboard/users": "Users", "dashboard/topics": "Topics", + "dashboard/searches": "Searches", "section-general": "General", "section-manage": "Manage", diff --git a/public/language/da/admin/settings/email.json b/public/language/da/admin/settings/email.json index 9bdbde26a6..65f579d28c 100644 --- a/public/language/da/admin/settings/email.json +++ b/public/language/da/admin/settings/email.json @@ -6,7 +6,7 @@ "from-help": "The from name to display in the email.", "smtp-transport": "SMTP Transport", - "smtp-transport.enabled": "Use an external email server to send emails", + "smtp-transport.enabled": "Enable SMTP Transport", "smtp-transport-help": "You can select from a list of well-known services or enter a custom one.", "smtp-transport.service": "Select a service", "smtp-transport.service-custom": "Custom Service", @@ -37,6 +37,10 @@ "subscriptions.hour": "Digest Hour", "subscriptions.hour-help": "Please enter a number representing the hour to send scheduled email digests (e.g. 0 for midnight, 17 for 5:00pm). Keep in mind that this is the hour according to the server itself, and may not exactly match your system clock.
The approximate server time is:
The next daily digest is scheduled to be sent ", "notifications.remove-images": "Remove images from email notifications", + "require-email-address": "Require new users to specify an email address", + "require-email-address-warning": "By default, users can opt-out of entering an email address. Enabling this option means they have to enter an email address in order to proceed with registration. It does not ensure user will enter a real email address, nor even an address they own.", "include-unverified-emails": "Send emails to recipients who have not explicitly confirmed their emails", - "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws." -} \ No newline at end of file + "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws.", + "prompt": "Prompt users to enter or confirm their emails", + "prompt-help": "If a user does not have an email set, or their email is not confirmed, a warning will be shown on screen." +} diff --git a/public/language/da/admin/settings/general.json b/public/language/da/admin/settings/general.json index be7df90870..29b939861b 100644 --- a/public/language/da/admin/settings/general.json +++ b/public/language/da/admin/settings/general.json @@ -3,9 +3,9 @@ "title": "Site Title", "title.short": "Short Title", "title.short-placeholder": "If no short title is specified, the site title will be used", - "title.url": "URL", + "title.url": "Title Link URL", "title.url-placeholder": "The URL of the site title", - "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.", + "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "title.name": "Your Community Name", "title.show-in-header": "Show Site Title in Header", "browser-title": "Browser Title", @@ -20,9 +20,9 @@ "logo.image": "Image", "logo.image-placeholder": "Path to a logo to display on forum header", "logo.upload": "Upload", - "logo.url": "URL", + "logo.url": "Logo Link URL", "logo.url-placeholder": "The URL of the site logo", - "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.", + "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "logo.alt-text": "Alt Text", "log.alt-text-placeholder": "Alternative text for accessibility", "favicon": "Favicon", @@ -47,4 +47,4 @@ "undo-timeout": "Undo Timeout", "undo-timeout-help": "Some operations such as moving topics will allow for the moderator to undo their action within a certain timeframe. Set to 0 to disable undo completely.", "topic-tools": "Topic Tools" -} \ No newline at end of file +} diff --git a/public/language/da/admin/settings/navigation.json b/public/language/da/admin/settings/navigation.json index 13dd01aae7..7baca85096 100644 --- a/public/language/da/admin/settings/navigation.json +++ b/public/language/da/admin/settings/navigation.json @@ -11,6 +11,8 @@ "properties": "Properties:", "groups": "Groups:", "open-new-window": "Open in a new window", + "dropdown": "Dropdown", + "dropdown-placeholder": "Place your dropdown menu items below, ie:
<li><a href="https://myforum.com">Link 1</a></li>", "btn.delete": "Delete", "btn.disable": "Disable", @@ -20,4 +22,4 @@ "custom-route": "Custom Route", "core": "core", "plugin": "plugin" -} \ No newline at end of file +} diff --git a/public/language/da/admin/settings/notifications.json b/public/language/da/admin/settings/notifications.json index da6c9680a3..c6d8b928ce 100644 --- a/public/language/da/admin/settings/notifications.json +++ b/public/language/da/admin/settings/notifications.json @@ -2,5 +2,6 @@ "notifications": "Notifications", "welcome-notification": "Welcome Notification", "welcome-notification-link": "Welcome Notification Link", - "welcome-notification-uid": "Welcome Notification User (UID)" + "welcome-notification-uid": "Welcome Notification User (UID)", + "post-queue-notification-uid": "Post Queue User (UID)" } \ No newline at end of file diff --git a/public/language/da/admin/settings/post.json b/public/language/da/admin/settings/post.json index 27493aafbd..ab8245738c 100644 --- a/public/language/da/admin/settings/post.json +++ b/public/language/da/admin/settings/post.json @@ -40,6 +40,7 @@ "teaser.last-post": "Last – Show the latest post, including the original post, if no replies", "teaser.last-reply": "Last – Show the latest reply, or a \"No replies\" placeholder if no replies", "teaser.first": "First", + "showPostPreviewsOnHover": "Show a preview of posts when mouse overed", "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", @@ -56,6 +57,9 @@ "composer.show-help": "Show \"Help\" tab", "composer.enable-plugin-help": "Allow plugins to add content to the help tab", "composer.custom-help": "Custom Help Text", + "backlinks": "Backlinks", + "backlinks.enabled": "Enable topic backlinks", + "backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.", "ip-tracking": "IP Tracking", "ip-tracking.each-post": "Track IP Address for each post", "enable-post-history": "Enable Post History" diff --git a/public/language/da/admin/settings/user.json b/public/language/da/admin/settings/user.json index 48be13b75e..7923bf8cbe 100644 --- a/public/language/da/admin/settings/user.json +++ b/public/language/da/admin/settings/user.json @@ -71,6 +71,7 @@ "digest-freq.off": "Off", "digest-freq.daily": "Daily", "digest-freq.weekly": "Weekly", + "digest-freq.biweekly": "Bi-Weekly", "digest-freq.monthly": "Monthly", "email-chat-notifs": "Send an email if a new chat message arrives and I am not online", "email-post-notif": "Send an email when replies are made to topics I am subscribed to", diff --git a/public/language/da/error.json b/public/language/da/error.json index 58d887b026..6ee0328692 100644 --- a/public/language/da/error.json +++ b/public/language/da/error.json @@ -34,8 +34,9 @@ "email-invited": "Email was already invited", "email-not-confirmed": "Posting in some categories or topics is enabled once your email is confirmed, please click here to send a confirmation email.", "email-not-confirmed-chat": "Du kan ikke chatte før din email er bekræftet, klik her for at bekræfte din email.", - "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You won't be able to post or chat until your email is confirmed.", - "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery. Please click here to enter an email.", + "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You may not be able to post in some categories or chat until your email is confirmed.", + "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery, and may be necessary for chatting and posting in some categories. Please click here to enter an email.", + "user-doesnt-have-email": "User \"%1\" does not have an email set.", "email-confirm-failed": "Vi kunne ikke bekræfte din email, prøv igen senere.", "confirm-email-already-sent": "Bekræftelses email er allerede afsendt, vent venligt %1 minut(ter) for at sende endnu en.", "sendmail-not-found": "The sendmail executable could not be found, please ensure it is installed and executable by the user running NodeBB.", @@ -103,6 +104,7 @@ "already-bookmarked": "You have already bookmarked this post", "already-unbookmarked": "You have already unbookmarked this post", "cant-ban-other-admins": "Du kan ikke udlukke andre administatrorer!", + "cant-make-banned-users-admin": "You can't make banned users admin.", "cant-remove-last-admin": "Du er den eneste administrator. Tilføj en anden bruger som administrator før du fjerner dig selv som administrator", "account-deletion-disabled": "Account deletion is disabled", "cant-delete-admin": "Remove administrator privileges from this account before attempting to delete it.", diff --git a/public/language/da/modules.json b/public/language/da/modules.json index 590fab6db5..35d7aaa396 100644 --- a/public/language/da/modules.json +++ b/public/language/da/modules.json @@ -54,7 +54,7 @@ "composer.formatting.strikethrough": "Strikethrough", "composer.formatting.code": "Code", "composer.formatting.link": "Link", - "composer.formatting.picture": "Picture", + "composer.formatting.picture": "Image Link", "composer.upload-picture": "Upload Image", "composer.upload-file": "Upload File", "composer.zen_mode": "Zen Mode", diff --git a/public/language/da/top.json b/public/language/da/top.json new file mode 100644 index 0000000000..b8a05bfa5f --- /dev/null +++ b/public/language/da/top.json @@ -0,0 +1,4 @@ +{ + "title": "Top", + "no_top_topics": "No top topics" +} \ No newline at end of file diff --git a/public/language/da/topic.json b/public/language/da/topic.json index bb153483fc..4885532fd0 100644 --- a/public/language/da/topic.json +++ b/public/language/da/topic.json @@ -47,6 +47,7 @@ "restored-by": "Restored by", "moved-from-by": "Moved from %1 by", "queued-by": "Post queued for approval →", + "backlink": "Referenced by", "bookmark_instructions": "Klik her for at vende tilbage til den sidst læste indlæg i denne tråd.", "flag-post": "Flag this post", "flag-user": "Flag this user", @@ -138,6 +139,7 @@ "composer.handle_placeholder": "Enter your name/handle here", "composer.discard": "Fortryd", "composer.submit": "Send", + "composer.additional-options": "Additional Options", "composer.schedule": "Schedule", "composer.replying_to": "Svare til %1", "composer.new_topic": "Ny tråd", @@ -158,6 +160,7 @@ "newest_to_oldest": "Nyeste til ældste", "most_votes": "Most Votes", "most_posts": "Most Posts", + "most_views": "Most Views", "stale.title": "Opret nyt emne istedet?", "stale.warning": "Emnet du svarer på er ret gammelt. Vil du oprette et nyt emne istedet og referere dette indlæg i dit svar?", "stale.create": "Opret nyt emne", diff --git a/public/language/da/user.json b/public/language/da/user.json index 68aedf8a04..c3e6b0d94d 100644 --- a/public/language/da/user.json +++ b/public/language/da/user.json @@ -94,6 +94,7 @@ "digest_off": "Slukket", "digest_daily": "Daglig", "digest_weekly": "Ugentlig", + "digest_biweekly": "Bi-Weekly", "digest_monthly": "Månedlig", "has_no_follower": "Denne bruger har ingen følgere :(", "follows_no_one": "Denne bruger følger ikke nogen :(", diff --git a/public/language/de/admin/advanced/events.json b/public/language/de/admin/advanced/events.json index 2a7607040e..9f87f04efa 100644 --- a/public/language/de/admin/advanced/events.json +++ b/public/language/de/admin/advanced/events.json @@ -3,6 +3,7 @@ "no-events": "Es gibt keine Ereignisse", "control-panel": "Ereignis-Steuerung", "delete-events": "Ereignisse löschen", + "confirm-delete-all-events": "Bist du sicher, dass du alle gespeicherten Events löschen möchtest?", "filters": "Filter", "filters-apply": "Filter anwenden", "filter-type": "Ereignistyp", diff --git a/public/language/de/admin/advanced/logs.json b/public/language/de/admin/advanced/logs.json index 7399c68b46..e0bce077ae 100644 --- a/public/language/de/admin/advanced/logs.json +++ b/public/language/de/admin/advanced/logs.json @@ -1,7 +1,7 @@ { - "logs": "Protokoll", + "logs": "Protokolle", "control-panel": "Protokoll Steuerung", - "reload": "Protokoll neu laden", - "clear": "Protokoll leeren", - "clear-success": "Protokoll geleert" + "reload": "Protokolle neu laden", + "clear": "Protokolle löschen", + "clear-success": "Protokolle gelöscht" } \ No newline at end of file diff --git a/public/language/de/admin/dashboard.json b/public/language/de/admin/dashboard.json index 943b0fcfa5..5b4bcf4bf5 100644 --- a/public/language/de/admin/dashboard.json +++ b/public/language/de/admin/dashboard.json @@ -56,8 +56,8 @@ "active-users.total": "Gesamt", "active-users.connections": "Verbindungen", - "anonymous-registered-users": "Anonyme vs Registrierte Benutzer", - "anonymous": "Anonym", + "guest-registered-users": "Guest vs Registered Users", + "guest": "Guest", "registered": "Registriert", "user-presence": "Benutzerpräsenz", @@ -68,6 +68,7 @@ "unread": "Ungelesen", "high-presence-topics": "Meist besuchte Themen", + "popular-searches": "Popular Searches", "graphs.page-views": "Seitenaufrufe", "graphs.page-views-registered": "Registrierte Seitenaufrufe", @@ -75,13 +76,14 @@ "graphs.page-views-bot": "Seitenaufrufe von Bots", "graphs.unique-visitors": "Verschiedene Besucher", "graphs.registered-users": "Registrierte Benutzer", - "graphs.anonymous-users": "Anonyme Benutzer", + "graphs.guest-users": "Guest Users", "last-restarted-by": "Zuletzt Neugestartet von: ", "no-users-browsing": "Keine aktiven Benutzer", "back-to-dashboard": "Zurück zur Übersicht", "details.no-users": "Keine Benutzer sind im gewählten Zeitraum beigetreten", "details.no-topics": "Keine Themen wurden im gewählten Zeitraum beigetreten", + "details.no-searches": "No searches have been made yet", "details.no-logins": "Keine Logins wurden im gewählten Zeitraum festgestellt", "details.logins-static": "NodeBB speichert Sitzungsdaten nur für %1 Tage, deshalb zeigt die untere Tabelle nur die neuesten, aktiven Sitzungen", "details.logins-login-time": "Anmelde Zeit" diff --git a/public/language/de/admin/development/info.json b/public/language/de/admin/development/info.json index 8c04a3a0f0..c0b65f6781 100644 --- a/public/language/de/admin/development/info.json +++ b/public/language/de/admin/development/info.json @@ -8,7 +8,11 @@ "nodejs": "Node.js Version", "online": "Online", "git": "git", - "memory": "Speicher", + "process-memory": "process memory", + "system-memory": "system memory", + "used-memory-process": "Used memory by process", + "used-memory-os": "Used system memory", + "total-memory-os": "Total system memory", "load": "Systemlast", "cpu-usage": "CPU Benutzung", "uptime": "Uptime", diff --git a/public/language/de/admin/extend/rewards.json b/public/language/de/admin/extend/rewards.json index 38c39fcf44..7a6eef6513 100644 --- a/public/language/de/admin/extend/rewards.json +++ b/public/language/de/admin/extend/rewards.json @@ -8,8 +8,6 @@ "delete": "Entfernen", "enable": "Aktivieren", "disable": "Deaktivieren", - "control-panel": "Belohnungseinstellungen", - "new-reward": "Neue Belohnung", "alert.delete-success": "Die Belohnung wurde erfolgreich gelöscht", "alert.no-inputs-found": "Ungültige Belohnung - keine Eingaben gefunden!", diff --git a/public/language/de/admin/general/dashboard.json b/public/language/de/admin/general/dashboard.json deleted file mode 100644 index a2a2156661..0000000000 --- a/public/language/de/admin/general/dashboard.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "forum-traffic": "Forum Traffic", - "page-views": "Seitenaufrufe", - "unique-visitors": "Individuelle Besucher", - "new-users": "New Users", - "posts": "Beiträge", - "topics": "Themen", - "page-views-seven": "Letzte 7 Tage", - "page-views-thirty": "Letzte 30 Tage", - "page-views-last-day": "Letzte 24 Stunden", - "page-views-custom": "Benutzerdefinierte Tagesspanne", - "page-views-custom-start": "Spannen-Anfang", - "page-views-custom-end": "Spannen-Ende", - "page-views-custom-help": "Gib eine Zeitspanne an, in dem du die Besichtigungszahlen ansehen willst. Sollte keine Kalenderauswahl verfügbar sein ist das akzeptierte format YYYY-MM-DD", - "page-views-custom-error": "Bitte gib eine gültige Zeitspanne im Format YYYY-MM-DD an", - - "stats.yesterday": "Yesterday", - "stats.today": "Today", - "stats.last-week": "Last Week", - "stats.this-week": "This Week", - "stats.last-month": "Last Month", - "stats.this-month": "This Month", - "stats.all": "Alle", - - "updates": "Updates", - "running-version": "Es läuft NodeBB v%1.", - "keep-updated": "Stelle sicher, dass dein NodeBB immer auf dem neuesten Stand für die neuesten Sicherheits-Patches und Bug-fixes ist.", - "up-to-date": "

NodeBB Version ist aktuell

", - "upgrade-available": "

Eine neuere Version (v%1) ist erschienen. Erwäge NodeBB zu upgraden.

", - "prerelease-upgrade-available": "

Das ist eine veraltete NodeBB-Vorabversion. Eine neuere Version (v%1) ist erschienen. Erwäge NodeBB zu upgraden.

", - "prerelease-warning": "

Das ist eine pre-release Version von NodeBB. Es können ungewollte Fehler auftreten.

", - "running-in-development": "Das Forum wurde im Entwicklermodus gestartet. Das Forum könnte potenziellen Gefahren ausgeliefert sein. Bitte kontaktiere den Systemadministrator.", - "latest-lookup-failed": "

Beim nachschlagen der neuesten verfügbaren NodeBB Version ist ein Fehler aufgetreten

", - - "notices": "Hinweise", - "restart-not-required": "Kein Neustart benötigt", - "restart-required": "Neustart benötigt", - "search-plugin-installed": "Such-Plugin installiert", - "search-plugin-not-installed": "Kein Such-Plugin installiert", - "search-plugin-tooltip": "Installiere ein Such-Plugin auf der Plugin-Seite um die Such-Funktionalität zu aktivieren", - - "control-panel": "Systemsteuerung", - "rebuild-and-restart": "Regenerieren & Neustarten", - "restart": "Neustarten", - "restart-warning": "NodeBB zu regenerieren oder neuzustarten wird alle existierenden Verbindungen für ein paar Sekunden trennen.", - "restart-disabled": "Das Regenerieren und Neustarten von NodeBB wurde deaktiviert, da es nicht so aussieht als ob es über einem kompatiblem daemon läuft.", - "maintenance-mode": "Wartungsmodus", - "maintenance-mode-title": "Hier klicken um NodeBB in den Wartungsmodus zu versetzen", - "realtime-chart-updates": "Echtzeit Chartaktualisierung", - - "active-users": "Aktive Benutzer", - "active-users.users": "Benutzer", - "active-users.guests": "Gäste", - "active-users.total": "Gesamt", - "active-users.connections": "Verbindungen", - - "anonymous-registered-users": "Anonyme vs Registrierte Benutzer", - "anonymous": "Anonym", - "registered": "Registriert", - - "user-presence": "Benutzerpräsenz", - "on-categories": "Auf Kategorieübersicht", - "reading-posts": "Beiträge lesend", - "browsing-topics": "Themen durchsuchend", - "recent": "Aktuell", - "unread": "Ungelesen", - - "high-presence-topics": "Meist besuchte Themen", - - "graphs.page-views": "Seitenaufrufe", - "graphs.page-views-registered": "Registrierte Seitenaufrufe", - "graphs.page-views-guest": "Seitenaufrufe von Gästen", - "graphs.page-views-bot": "Seitenaufrufe von Bots", - "graphs.unique-visitors": "Verschiedene Besucher", - "graphs.registered-users": "Registrierte Benutzer", - "graphs.anonymous-users": "Anonyme Benutzer", - "last-restarted-by": "Zuletzt Neugestartet von: ", - "no-users-browsing": "Keine aktiven Benutzer" -} diff --git a/public/language/de/admin/general/homepage.json b/public/language/de/admin/general/homepage.json deleted file mode 100644 index 186e89ae62..0000000000 --- a/public/language/de/admin/general/homepage.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "home-page": "Startseite", - "description": "Wähle aus, welche Seite angezeigt werden soll, wenn Nutzer zur Startseite des Forums navigieren.", - "home-page-route": "Startseitenpfad", - "custom-route": "Eigener Startseitenpfad", - "allow-user-home-pages": "Benutzern eigene Startseiten erlauben", - "home-page-title": "Titel der Startseite (Standardmäßig \"Home\")" -} \ No newline at end of file diff --git a/public/language/de/admin/general/languages.json b/public/language/de/admin/general/languages.json deleted file mode 100644 index c2358ac047..0000000000 --- a/public/language/de/admin/general/languages.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "language-settings": "Spracheinstellungen", - "description": "Die Standardsprache legt die Spracheinstellungen für alle Benutzer fest, die das Forum besuchen.
Einzelne Benutzer können die Standardsprache auf der Seite in ihren Kontoeinstellungen überschreiben.", - "default-language": "Standardsprache", - "auto-detect": "Sprach-Einstellung bei Gästen automatisch ermitteln" -} \ No newline at end of file diff --git a/public/language/de/admin/general/navigation.json b/public/language/de/admin/general/navigation.json deleted file mode 100644 index 1bedf15f20..0000000000 --- a/public/language/de/admin/general/navigation.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "icon": "Icon:", - "change-icon": "ändern", - "route": "Pfad:", - "tooltip": "Tooltip:", - "text": "Text:", - "text-class": "Text Klasse: optional", - "class": "Klasse optional", - "id": "ID: optional", - - "properties": "Eigenschaften:", - "groups": "Gruppen:", - "open-new-window": "In neuem Fenster öffnen", - - "btn.delete": "Löschen", - "btn.disable": "Deaktivieren", - "btn.enable": "Aktivieren", - - "available-menu-items": "Verfügbare Menüpunkte", - "custom-route": "Benutzerdefinierter Pfad", - "core": "Kern", - "plugin": "Plugin" -} \ No newline at end of file diff --git a/public/language/de/admin/general/social.json b/public/language/de/admin/general/social.json deleted file mode 100644 index 0614aca7da..0000000000 --- a/public/language/de/admin/general/social.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "post-sharing": "Beiträge teilen", - "info-plugins-additional": "Plugins können zusätzliche soziale Netzwerke für das Teilen von Beiträgen hinzufügen.", - "save-success": "Erfolgreich gespeichert!" -} \ No newline at end of file diff --git a/public/language/de/admin/general/sounds.json b/public/language/de/admin/general/sounds.json deleted file mode 100644 index 22cbf29f14..0000000000 --- a/public/language/de/admin/general/sounds.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "notifications": "Benachrichtigungen", - "chat-messages": "Chat Nachrichten", - "play-sound": "Abspielen", - "incoming-message": "Eingehende Nachricht", - "outgoing-message": "Gesendete Nachricht", - "upload-new-sound": "Sound hochladen", - "saved": "Einstellungen gespeichert!" -} \ No newline at end of file diff --git a/public/language/de/admin/manage/digest.json b/public/language/de/admin/manage/digest.json index 5f924bc587..2f1212d11f 100644 --- a/public/language/de/admin/manage/digest.json +++ b/public/language/de/admin/manage/digest.json @@ -5,14 +5,15 @@ "user": "Benutzer", "subscription": "Subscription Type", - "last-delivery": "Last successful delivery", - "default": "System default", + "last-delivery": "Letzte erfolgreiche Zustellung", + "default": "System Standard", "default-help": "System default means the user has not explicitly overridden the global forum setting for digests, which is currently: "%1"", "resend": "Resend Digest", "resend-all-confirm": "Are you sure you wish to manually execute this digest run?", "resent-single": "Manual digest resend completed", "resent-day": "Daily digest resent", "resent-week": "Weekly digest resent", + "resent-biweek": "Bi-Weekly digest resent", "resent-month": "Monthly digest resent", "null": "Never", "manual-run": "Manual digest run:", diff --git a/public/language/de/admin/manage/users.json b/public/language/de/admin/manage/users.json index e9f51d32da..14a210328a 100644 --- a/public/language/de/admin/manage/users.json +++ b/public/language/de/admin/manage/users.json @@ -47,6 +47,7 @@ "users.uid": "UID", "users.username": "Nutzername", "users.email": "E-Mail", + "users.no-email": "(no email)", "users.ip": "IP", "users.postcount": "Anzahl der Beiträge", "users.reputation": "Ansehen", diff --git a/public/language/de/admin/menu.json b/public/language/de/admin/menu.json index 13448137e8..ec21748e22 100644 --- a/public/language/de/admin/menu.json +++ b/public/language/de/admin/menu.json @@ -4,6 +4,7 @@ "dashboard/logins": "Logins", "dashboard/users": "Users", "dashboard/topics": "Topics", + "dashboard/searches": "Searches", "section-general": "Allgemein", "section-manage": "Verwalten", diff --git a/public/language/de/admin/settings/email.json b/public/language/de/admin/settings/email.json index 4a7c6f2a55..9de1ce0df1 100644 --- a/public/language/de/admin/settings/email.json +++ b/public/language/de/admin/settings/email.json @@ -6,7 +6,7 @@ "from-help": "Der Name des Absenders, welcher in der E-Mail angezeigt werden soll.", "smtp-transport": "SMTP Konfiguration", - "smtp-transport.enabled": "Benutze einen externen Email-Server", + "smtp-transport.enabled": "Enable SMTP Transport", "smtp-transport-help": "Du kannst aus einer Liste bekannter Email-Provider auswählen, oder einen benutzerdefinierten eingeben.", "smtp-transport.service": "Wähle einen Provider", "smtp-transport.service-custom": "Benutzerdefiniert...", @@ -37,6 +37,10 @@ "subscriptions.hour": "Sende Zeit", "subscriptions.hour-help": "Bitte geben Sie eine Nummer ein, welche die Stunde repräsentiert zu welcher geplante Emails versandt werden sollen (z.B. 0 für Mitternacht, 17 für 5 Uhr Nachmittags). Beachten Sie, dass die Zeit auf der Serverzeit basiert und daher nicht umbedingt mit ihrer Systemzeit übereinstimmen muss.
Die ungefähre Serverzeit ist:
Die nächste tägliche Sendung ist um geplant", "notifications.remove-images": "Remove images from email notifications", + "require-email-address": "Require new users to specify an email address", + "require-email-address-warning": "By default, users can opt-out of entering an email address. Enabling this option means they have to enter an email address in order to proceed with registration. It does not ensure user will enter a real email address, nor even an address they own.", "include-unverified-emails": "Send emails to recipients who have not explicitly confirmed their emails", - "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws." -} \ No newline at end of file + "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws.", + "prompt": "Prompt users to enter or confirm their emails", + "prompt-help": "If a user does not have an email set, or their email is not confirmed, a warning will be shown on screen." +} diff --git a/public/language/de/admin/settings/general.json b/public/language/de/admin/settings/general.json index 7e004e3461..c7d890a33c 100644 --- a/public/language/de/admin/settings/general.json +++ b/public/language/de/admin/settings/general.json @@ -3,9 +3,9 @@ "title": "Forum Titel", "title.short": "Short Title", "title.short-placeholder": "If no short title is specified, the site title will be used", - "title.url": "URL", + "title.url": "Title Link URL", "title.url-placeholder": "Die URL des Seitentitels", - "title.url-help": "Wenn der Titel angeklickt wird, werden Benutzer zu dieser Adresse geschickt, bei leerem Feld wird die Startseite verwendet.", + "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "title.name": "Name Deiner Community", "title.show-in-header": "Titel im Header anzeigen", "browser-title": "Browser Titel", @@ -20,9 +20,9 @@ "logo.image": "Bild", "logo.image-placeholder": "Pfad zu einem Logo, welches im Header des Forums angezeigt werden soll", "logo.upload": "Hochladen", - "logo.url": "URL", + "logo.url": "Logo Link URL", "logo.url-placeholder": "Die URL des Logos", - "logo.url-help": "Wenn das Logo angeklickt wird, wird der Nutzer an diese Adresse weitergeleitet. Wenn das Feld leer gelassen wird, wird der Nutzer zur Startseite geleitet.", + "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "logo.alt-text": "Alt Text", "log.alt-text-placeholder": "Alternativer Text, falls das Bild nicht angezeigt werden kann", "favicon": "Favicon", @@ -47,4 +47,4 @@ "undo-timeout": "Undo Timeout", "undo-timeout-help": "Some operations such as moving topics will allow for the moderator to undo their action within a certain timeframe. Set to 0 to disable undo completely.", "topic-tools": "Topic Tools" -} \ No newline at end of file +} diff --git a/public/language/de/admin/settings/navigation.json b/public/language/de/admin/settings/navigation.json index 1bedf15f20..a3809cafeb 100644 --- a/public/language/de/admin/settings/navigation.json +++ b/public/language/de/admin/settings/navigation.json @@ -11,6 +11,8 @@ "properties": "Eigenschaften:", "groups": "Gruppen:", "open-new-window": "In neuem Fenster öffnen", + "dropdown": "Dropdown", + "dropdown-placeholder": "Place your dropdown menu items below, ie:
<li><a href="https://myforum.com">Link 1</a></li>", "btn.delete": "Löschen", "btn.disable": "Deaktivieren", @@ -20,4 +22,4 @@ "custom-route": "Benutzerdefinierter Pfad", "core": "Kern", "plugin": "Plugin" -} \ No newline at end of file +} diff --git a/public/language/de/admin/settings/notifications.json b/public/language/de/admin/settings/notifications.json index e66499b081..d3947744c5 100644 --- a/public/language/de/admin/settings/notifications.json +++ b/public/language/de/admin/settings/notifications.json @@ -2,5 +2,6 @@ "notifications": "Benachrichtigungen", "welcome-notification": "Wilkommensnachricht", "welcome-notification-link": "Wilkommensnachricht-Link", - "welcome-notification-uid": "Wilkommensbenachrichtigung Benutzer (UID)" + "welcome-notification-uid": "Wilkommensbenachrichtigung Benutzer (UID)", + "post-queue-notification-uid": "Post Queue User (UID)" } \ No newline at end of file diff --git a/public/language/de/admin/settings/post.json b/public/language/de/admin/settings/post.json index caa1fae9ae..9aa327bad0 100644 --- a/public/language/de/admin/settings/post.json +++ b/public/language/de/admin/settings/post.json @@ -40,6 +40,7 @@ "teaser.last-post": "Letzter - Den neuesten Beitrag anzeigen, den originalen Beitrag innbegriffen, wenn es keine Antworten gibt", "teaser.last-reply": "Letzter - Den neuesten Beitrag oder einen \"Keine Antworten\" Platzhalter, wenn es keine Antworten gibt anzeigen", "teaser.first": "Erster", + "showPostPreviewsOnHover": "Show a preview of posts when mouse overed", "unread": "Ungelesen-Einstellungen", "unread.cutoff": "Ungelesen-Limit (in Tagen)", "unread.min-track-last": "Minimale Anzahl an Beiträgen pro Thema bevor die letzte Sichtung mitgeschrieben wird", @@ -56,6 +57,9 @@ "composer.show-help": "\"Hilfe\"-Tab anzeigen", "composer.enable-plugin-help": "Plugins erlauben Inhalte dem \"Help\"-Tab hinzuzufügen", "composer.custom-help": "Benutzerdefinierter Hilfe-Text", + "backlinks": "Backlinks", + "backlinks.enabled": "Enable topic backlinks", + "backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.", "ip-tracking": "IP-Verfolgung", "ip-tracking.each-post": "IP-Adresse für jeden Beitrag speichern", "enable-post-history": "Aktiviere Beitrags-Änderungsgeschichte" diff --git a/public/language/de/admin/settings/user.json b/public/language/de/admin/settings/user.json index 11272dbd85..795bf4a4b0 100644 --- a/public/language/de/admin/settings/user.json +++ b/public/language/de/admin/settings/user.json @@ -71,6 +71,7 @@ "digest-freq.off": "Aus", "digest-freq.daily": "Täglich", "digest-freq.weekly": "Wöchentlich", + "digest-freq.biweekly": "Bi-Weekly", "digest-freq.monthly": "Monatlich", "email-chat-notifs": "Sende eine E-Mail, wenn eine neue Chat-Nachricht eingeht und ich nicht online bin", "email-post-notif": "Sende eine E-Mail wenn auf Themen die ich abonniert habe geantwortet wird", diff --git a/public/language/de/error.json b/public/language/de/error.json index 541af8c5ed..08c4e7732c 100644 --- a/public/language/de/error.json +++ b/public/language/de/error.json @@ -34,8 +34,9 @@ "email-invited": "Email was already invited", "email-not-confirmed": "Posting in some categories or topics is enabled once your email is confirmed, please click here to send a confirmation email.", "email-not-confirmed-chat": "Du kannst den Chat erst nutzen wenn deine E-Mail bestätigt wurde, bitte klicke hier, um deine E-Mail zu bestätigen.", - "email-not-confirmed-email-sent": "Deine Email-Adresse wurde noch nicht bestätigt, bitte kontrolliere dein Postfach nach einer Bestätigungsmail. Du kannst keine Beiträge erstellen oder chatten bis deine Email-Adresse bestätigt wurde.", - "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery. Please click here to enter an email.", + "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You may not be able to post in some categories or chat until your email is confirmed.", + "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery, and may be necessary for chatting and posting in some categories. Please click here to enter an email.", + "user-doesnt-have-email": "User \"%1\" does not have an email set.", "email-confirm-failed": "Wir konnten deine E-Mail-Adresse nicht bestätigen, bitte versuch es später noch einmal", "confirm-email-already-sent": "Die Bestätigungsmail wurde verschickt. Bitte warte %1 Minute(n), um eine weitere zu verschicken.", "sendmail-not-found": "Sendmail wurde nicht gefunden. Bitte stelle sicher, dass es installiert ist und durch den Benutzer unter dem NodeBB läuft ausgeführt werden kann.", @@ -103,6 +104,7 @@ "already-bookmarked": "Du hast diesen Beitrag bereits als Lesezeichen gespeichert", "already-unbookmarked": "Du hast diesen Beitrag bereits aus deinen Lesezeichen entfernt", "cant-ban-other-admins": "Du kannst andere Administratoren nicht sperren!", + "cant-make-banned-users-admin": "You can't make banned users admin.", "cant-remove-last-admin": "Du bist der einzige Administrator. Füge zuerst einen anderen Administrator hinzu, bevor du dich selbst als Administrator entfernst", "account-deletion-disabled": "Account deletion is disabled", "cant-delete-admin": "Bevor du versuchst dieses Konto zu löschen, entferne die zugehörigen Administratorrechte.", diff --git a/public/language/de/modules.json b/public/language/de/modules.json index 7e4b640c81..6bc6968388 100644 --- a/public/language/de/modules.json +++ b/public/language/de/modules.json @@ -54,7 +54,7 @@ "composer.formatting.strikethrough": "Durchstreichen", "composer.formatting.code": "Code", "composer.formatting.link": "Link", - "composer.formatting.picture": "Bild", + "composer.formatting.picture": "Image Link", "composer.upload-picture": "Bild hochladen", "composer.upload-file": "Datei hochladen", "composer.zen_mode": "Zen Modus", diff --git a/public/language/de/top.json b/public/language/de/top.json new file mode 100644 index 0000000000..b8a05bfa5f --- /dev/null +++ b/public/language/de/top.json @@ -0,0 +1,4 @@ +{ + "title": "Top", + "no_top_topics": "No top topics" +} \ No newline at end of file diff --git a/public/language/de/topic.json b/public/language/de/topic.json index 2f29147bc1..e9d4ebacab 100644 --- a/public/language/de/topic.json +++ b/public/language/de/topic.json @@ -47,6 +47,7 @@ "restored-by": "Restored by", "moved-from-by": "Moved from %1 by", "queued-by": "Post queued for approval →", + "backlink": "Referenced by", "bookmark_instructions": "Klicke hier, um zum letzten gelesenen Beitrag des Themas zurückzukehren.", "flag-post": "Diesen Post melden", "flag-user": "Diesen Benutzer melden", @@ -138,6 +139,7 @@ "composer.handle_placeholder": "Gib deinen Namen/Nick hier ein", "composer.discard": "Verwerfen", "composer.submit": "Absenden", + "composer.additional-options": "Additional Options", "composer.schedule": "Schedule", "composer.replying_to": "Antworte auf %1", "composer.new_topic": "Neues Thema", @@ -158,6 +160,7 @@ "newest_to_oldest": "Neuste zuerst", "most_votes": "Meiste Stimmen", "most_posts": "Meiste Beiträge", + "most_views": "Most Views", "stale.title": "Stattdessen ein neues Thema erstellen?", "stale.warning": "Das Thema auf das du antworten willst ist ziemlich alt. Möchtest du stattdessen ein neues Thema erstellen und auf dieses in deiner Antwort hinweisen?", "stale.create": "Ein neues Thema erstellen", diff --git a/public/language/de/user.json b/public/language/de/user.json index c9be5cc888..f37aac89fb 100644 --- a/public/language/de/user.json +++ b/public/language/de/user.json @@ -94,6 +94,7 @@ "digest_off": "Aus", "digest_daily": "Täglich", "digest_weekly": "Wöchentlich", + "digest_biweekly": "Bi-Weekly", "digest_monthly": "Monatlich", "has_no_follower": "Diesem Benutzer folgt noch niemand. :(", "follows_no_one": "Dieser Benutzer folgt noch niemandem. :(", diff --git a/public/language/el/admin/advanced/events.json b/public/language/el/admin/advanced/events.json index 56d9457971..b2c2033fb5 100644 --- a/public/language/el/admin/advanced/events.json +++ b/public/language/el/admin/advanced/events.json @@ -3,6 +3,7 @@ "no-events": "There are no events", "control-panel": "Events Control Panel", "delete-events": "Delete Events", + "confirm-delete-all-events": "Are you sure you want to delete all logged events?", "filters": "Filters", "filters-apply": "Apply Filters", "filter-type": "Event Type", diff --git a/public/language/el/admin/dashboard.json b/public/language/el/admin/dashboard.json index 0de31d4917..4d39626882 100644 --- a/public/language/el/admin/dashboard.json +++ b/public/language/el/admin/dashboard.json @@ -56,8 +56,8 @@ "active-users.total": "Total", "active-users.connections": "Connections", - "anonymous-registered-users": "Anonymous vs Registered Users", - "anonymous": "Anonymous", + "guest-registered-users": "Guest vs Registered Users", + "guest": "Guest", "registered": "Registered", "user-presence": "User Presence", @@ -68,6 +68,7 @@ "unread": "Unread", "high-presence-topics": "High Presence Topics", + "popular-searches": "Popular Searches", "graphs.page-views": "Page Views", "graphs.page-views-registered": "Page Views Registered", @@ -75,13 +76,14 @@ "graphs.page-views-bot": "Page Views Bot", "graphs.unique-visitors": "Unique Visitors", "graphs.registered-users": "Registered Users", - "graphs.anonymous-users": "Anonymous Users", + "graphs.guest-users": "Guest Users", "last-restarted-by": "Last restarted by", "no-users-browsing": "No users browsing", "back-to-dashboard": "Back to Dashboard", "details.no-users": "No users have joined within the selected timeframe", "details.no-topics": "No topics have been posted within the selected timeframe", + "details.no-searches": "No searches have been made yet", "details.no-logins": "No logins have been recorded within the selected timeframe", "details.logins-static": "NodeBB only saves session data for %1 days, and so this table below will only show the most recently active sessions", "details.logins-login-time": "Login Time" diff --git a/public/language/el/admin/development/info.json b/public/language/el/admin/development/info.json index 1003af1a5f..11202d9c3a 100644 --- a/public/language/el/admin/development/info.json +++ b/public/language/el/admin/development/info.json @@ -8,7 +8,11 @@ "nodejs": "nodejs", "online": "online", "git": "git", - "memory": "memory", + "process-memory": "process memory", + "system-memory": "system memory", + "used-memory-process": "Used memory by process", + "used-memory-os": "Used system memory", + "total-memory-os": "Total system memory", "load": "system load", "cpu-usage": "cpu usage", "uptime": "uptime", diff --git a/public/language/el/admin/extend/rewards.json b/public/language/el/admin/extend/rewards.json index 5383a90b33..df89d441a7 100644 --- a/public/language/el/admin/extend/rewards.json +++ b/public/language/el/admin/extend/rewards.json @@ -8,8 +8,6 @@ "delete": "Delete", "enable": "Enable", "disable": "Disable", - "control-panel": "Rewards Control", - "new-reward": "New Reward", "alert.delete-success": "Successfully deleted reward", "alert.no-inputs-found": "Illegal reward - no inputs found!", diff --git a/public/language/el/admin/general/dashboard.json b/public/language/el/admin/general/dashboard.json deleted file mode 100644 index 37ae537786..0000000000 --- a/public/language/el/admin/general/dashboard.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "forum-traffic": "Forum Traffic", - "page-views": "Page Views", - "unique-visitors": "Unique Visitors", - "new-users": "New Users", - "posts": "Posts", - "topics": "Topics", - "page-views-seven": "Last 7 Days", - "page-views-thirty": "Last 30 Days", - "page-views-last-day": "Last 24 hours", - "page-views-custom": "Custom Date Range", - "page-views-custom-start": "Range Start", - "page-views-custom-end": "Range End", - "page-views-custom-help": "Enter a date range of page views you would like to view. If no date picker is available, the accepted format is YYYY-MM-DD", - "page-views-custom-error": "Please enter a valid date range in the format YYYY-MM-DD", - - "stats.yesterday": "Yesterday", - "stats.today": "Today", - "stats.last-week": "Last Week", - "stats.this-week": "This Week", - "stats.last-month": "Last Month", - "stats.this-month": "This Month", - "stats.all": "All Time", - - "updates": "Updates", - "running-version": "You are running NodeBB v%1.", - "keep-updated": "Always make sure that your NodeBB is up to date for the latest security patches and bug fixes.", - "up-to-date": "

You are up-to-date

", - "upgrade-available": "

A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-upgrade-available": "

This is an outdated pre-release version of NodeBB. A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-warning": "

This is a pre-release version of NodeBB. Unintended bugs may occur.

", - "running-in-development": "Forum is running in development mode. The forum may be open to potential vulnerabilities; please contact your system administrator.", - "latest-lookup-failed": "

Failed to look up latest available version of NodeBB

", - - "notices": "Notices", - "restart-not-required": "Restart not required", - "restart-required": "Restart required", - "search-plugin-installed": "Search Plugin installed", - "search-plugin-not-installed": "Search Plugin not installed", - "search-plugin-tooltip": "Install a search plugin from the plugin page in order to activate search functionality", - - "control-panel": "System Control", - "rebuild-and-restart": "Rebuild & Restart", - "restart": "Restart", - "restart-warning": "Rebuilding or Restarting your NodeBB will drop all existing connections for a few seconds.", - "restart-disabled": "Rebuilding and Restarting your NodeBB has been disabled as you do not seem to be running it via the appropriate daemon.", - "maintenance-mode": "Maintenance Mode", - "maintenance-mode-title": "Click here to set up maintenance mode for NodeBB", - "realtime-chart-updates": "Realtime Chart Updates", - - "active-users": "Active Users", - "active-users.users": "Users", - "active-users.guests": "Guests", - "active-users.total": "Total", - "active-users.connections": "Connections", - - "anonymous-registered-users": "Anonymous vs Registered Users", - "anonymous": "Anonymous", - "registered": "Registered", - - "user-presence": "User Presence", - "on-categories": "On categories list", - "reading-posts": "Reading posts", - "browsing-topics": "Browsing topics", - "recent": "Recent", - "unread": "Unread", - - "high-presence-topics": "High Presence Topics", - - "graphs.page-views": "Page Views", - "graphs.page-views-registered": "Page Views Registered", - "graphs.page-views-guest": "Page Views Guest", - "graphs.page-views-bot": "Page Views Bot", - "graphs.unique-visitors": "Unique Visitors", - "graphs.registered-users": "Registered Users", - "graphs.anonymous-users": "Anonymous Users", - "last-restarted-by": "Last restarted by", - "no-users-browsing": "No users browsing" -} diff --git a/public/language/el/admin/general/homepage.json b/public/language/el/admin/general/homepage.json deleted file mode 100644 index 7428d59eeb..0000000000 --- a/public/language/el/admin/general/homepage.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "home-page": "Home Page", - "description": "Choose what page is shown when users navigate to the root URL of your forum.", - "home-page-route": "Home Page Route", - "custom-route": "Custom Route", - "allow-user-home-pages": "Allow User Home Pages", - "home-page-title": "Title of the home page (default \"Home\")" -} \ No newline at end of file diff --git a/public/language/el/admin/general/languages.json b/public/language/el/admin/general/languages.json deleted file mode 100644 index bdd57849b3..0000000000 --- a/public/language/el/admin/general/languages.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "language-settings": "Language Settings", - "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language", - "auto-detect": "Auto Detect Language Setting for Guests" -} \ No newline at end of file diff --git a/public/language/el/admin/general/navigation.json b/public/language/el/admin/general/navigation.json deleted file mode 100644 index 13dd01aae7..0000000000 --- a/public/language/el/admin/general/navigation.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "icon": "Icon:", - "change-icon": "change", - "route": "Route:", - "tooltip": "Tooltip:", - "text": "Text:", - "text-class": "Text Class: optional", - "class": "Class: optional", - "id": "ID: optional", - - "properties": "Properties:", - "groups": "Groups:", - "open-new-window": "Open in a new window", - - "btn.delete": "Delete", - "btn.disable": "Disable", - "btn.enable": "Enable", - - "available-menu-items": "Available Menu Items", - "custom-route": "Custom Route", - "core": "core", - "plugin": "plugin" -} \ No newline at end of file diff --git a/public/language/el/admin/general/social.json b/public/language/el/admin/general/social.json deleted file mode 100644 index 23aedfcfaa..0000000000 --- a/public/language/el/admin/general/social.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "post-sharing": "Post Sharing", - "info-plugins-additional": "Plugins can add additional networks for sharing posts.", - "save-success": "Successfully saved Post Sharing Networks!" -} \ No newline at end of file diff --git a/public/language/el/admin/general/sounds.json b/public/language/el/admin/general/sounds.json deleted file mode 100644 index 95ccbde0f1..0000000000 --- a/public/language/el/admin/general/sounds.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "notifications": "Notifications", - "chat-messages": "Chat Messages", - "play-sound": "Play", - "incoming-message": "Incoming Message", - "outgoing-message": "Outgoing Message", - "upload-new-sound": "Upload New Sound", - "saved": "Settings Saved" -} \ No newline at end of file diff --git a/public/language/el/admin/manage/digest.json b/public/language/el/admin/manage/digest.json index 8f3661698a..38c634d1f6 100644 --- a/public/language/el/admin/manage/digest.json +++ b/public/language/el/admin/manage/digest.json @@ -13,6 +13,7 @@ "resent-single": "Manual digest resend completed", "resent-day": "Daily digest resent", "resent-week": "Weekly digest resent", + "resent-biweek": "Bi-Weekly digest resent", "resent-month": "Monthly digest resent", "null": "Never", "manual-run": "Manual digest run:", diff --git a/public/language/el/admin/manage/users.json b/public/language/el/admin/manage/users.json index 38a065b954..2a3c0c4829 100644 --- a/public/language/el/admin/manage/users.json +++ b/public/language/el/admin/manage/users.json @@ -47,6 +47,7 @@ "users.uid": "uid", "users.username": "username", "users.email": "email", + "users.no-email": "(no email)", "users.ip": "IP", "users.postcount": "postcount", "users.reputation": "reputation", diff --git a/public/language/el/admin/menu.json b/public/language/el/admin/menu.json index 1c5ea73b4f..5b22fbeb36 100644 --- a/public/language/el/admin/menu.json +++ b/public/language/el/admin/menu.json @@ -4,6 +4,7 @@ "dashboard/logins": "Logins", "dashboard/users": "Users", "dashboard/topics": "Topics", + "dashboard/searches": "Searches", "section-general": "General", "section-manage": "Manage", diff --git a/public/language/el/admin/settings/email.json b/public/language/el/admin/settings/email.json index 9bdbde26a6..65f579d28c 100644 --- a/public/language/el/admin/settings/email.json +++ b/public/language/el/admin/settings/email.json @@ -6,7 +6,7 @@ "from-help": "The from name to display in the email.", "smtp-transport": "SMTP Transport", - "smtp-transport.enabled": "Use an external email server to send emails", + "smtp-transport.enabled": "Enable SMTP Transport", "smtp-transport-help": "You can select from a list of well-known services or enter a custom one.", "smtp-transport.service": "Select a service", "smtp-transport.service-custom": "Custom Service", @@ -37,6 +37,10 @@ "subscriptions.hour": "Digest Hour", "subscriptions.hour-help": "Please enter a number representing the hour to send scheduled email digests (e.g. 0 for midnight, 17 for 5:00pm). Keep in mind that this is the hour according to the server itself, and may not exactly match your system clock.
The approximate server time is:
The next daily digest is scheduled to be sent ", "notifications.remove-images": "Remove images from email notifications", + "require-email-address": "Require new users to specify an email address", + "require-email-address-warning": "By default, users can opt-out of entering an email address. Enabling this option means they have to enter an email address in order to proceed with registration. It does not ensure user will enter a real email address, nor even an address they own.", "include-unverified-emails": "Send emails to recipients who have not explicitly confirmed their emails", - "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws." -} \ No newline at end of file + "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws.", + "prompt": "Prompt users to enter or confirm their emails", + "prompt-help": "If a user does not have an email set, or their email is not confirmed, a warning will be shown on screen." +} diff --git a/public/language/el/admin/settings/general.json b/public/language/el/admin/settings/general.json index be7df90870..29b939861b 100644 --- a/public/language/el/admin/settings/general.json +++ b/public/language/el/admin/settings/general.json @@ -3,9 +3,9 @@ "title": "Site Title", "title.short": "Short Title", "title.short-placeholder": "If no short title is specified, the site title will be used", - "title.url": "URL", + "title.url": "Title Link URL", "title.url-placeholder": "The URL of the site title", - "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.", + "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "title.name": "Your Community Name", "title.show-in-header": "Show Site Title in Header", "browser-title": "Browser Title", @@ -20,9 +20,9 @@ "logo.image": "Image", "logo.image-placeholder": "Path to a logo to display on forum header", "logo.upload": "Upload", - "logo.url": "URL", + "logo.url": "Logo Link URL", "logo.url-placeholder": "The URL of the site logo", - "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.", + "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "logo.alt-text": "Alt Text", "log.alt-text-placeholder": "Alternative text for accessibility", "favicon": "Favicon", @@ -47,4 +47,4 @@ "undo-timeout": "Undo Timeout", "undo-timeout-help": "Some operations such as moving topics will allow for the moderator to undo their action within a certain timeframe. Set to 0 to disable undo completely.", "topic-tools": "Topic Tools" -} \ No newline at end of file +} diff --git a/public/language/el/admin/settings/navigation.json b/public/language/el/admin/settings/navigation.json index 13dd01aae7..7baca85096 100644 --- a/public/language/el/admin/settings/navigation.json +++ b/public/language/el/admin/settings/navigation.json @@ -11,6 +11,8 @@ "properties": "Properties:", "groups": "Groups:", "open-new-window": "Open in a new window", + "dropdown": "Dropdown", + "dropdown-placeholder": "Place your dropdown menu items below, ie:
<li><a href="https://myforum.com">Link 1</a></li>", "btn.delete": "Delete", "btn.disable": "Disable", @@ -20,4 +22,4 @@ "custom-route": "Custom Route", "core": "core", "plugin": "plugin" -} \ No newline at end of file +} diff --git a/public/language/el/admin/settings/notifications.json b/public/language/el/admin/settings/notifications.json index e2d5967239..b38c65a34c 100644 --- a/public/language/el/admin/settings/notifications.json +++ b/public/language/el/admin/settings/notifications.json @@ -2,5 +2,6 @@ "notifications": "Ειδοποιήσεις", "welcome-notification": "Ειδοποίηση καλωσορίσματος", "welcome-notification-link": "Welcome Notification Link", - "welcome-notification-uid": "Welcome Notification User (UID)" + "welcome-notification-uid": "Welcome Notification User (UID)", + "post-queue-notification-uid": "Post Queue User (UID)" } \ No newline at end of file diff --git a/public/language/el/admin/settings/post.json b/public/language/el/admin/settings/post.json index 27493aafbd..ab8245738c 100644 --- a/public/language/el/admin/settings/post.json +++ b/public/language/el/admin/settings/post.json @@ -40,6 +40,7 @@ "teaser.last-post": "Last – Show the latest post, including the original post, if no replies", "teaser.last-reply": "Last – Show the latest reply, or a \"No replies\" placeholder if no replies", "teaser.first": "First", + "showPostPreviewsOnHover": "Show a preview of posts when mouse overed", "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", @@ -56,6 +57,9 @@ "composer.show-help": "Show \"Help\" tab", "composer.enable-plugin-help": "Allow plugins to add content to the help tab", "composer.custom-help": "Custom Help Text", + "backlinks": "Backlinks", + "backlinks.enabled": "Enable topic backlinks", + "backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.", "ip-tracking": "IP Tracking", "ip-tracking.each-post": "Track IP Address for each post", "enable-post-history": "Enable Post History" diff --git a/public/language/el/admin/settings/user.json b/public/language/el/admin/settings/user.json index 48be13b75e..7923bf8cbe 100644 --- a/public/language/el/admin/settings/user.json +++ b/public/language/el/admin/settings/user.json @@ -71,6 +71,7 @@ "digest-freq.off": "Off", "digest-freq.daily": "Daily", "digest-freq.weekly": "Weekly", + "digest-freq.biweekly": "Bi-Weekly", "digest-freq.monthly": "Monthly", "email-chat-notifs": "Send an email if a new chat message arrives and I am not online", "email-post-notif": "Send an email when replies are made to topics I am subscribed to", diff --git a/public/language/el/error.json b/public/language/el/error.json index f34cf7d037..827c635748 100644 --- a/public/language/el/error.json +++ b/public/language/el/error.json @@ -34,8 +34,9 @@ "email-invited": "Email was already invited", "email-not-confirmed": "Posting in some categories or topics is enabled once your email is confirmed, please click here to send a confirmation email.", "email-not-confirmed-chat": "You are unable to chat until your email is confirmed, please click here to confirm your email.", - "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You won't be able to post or chat until your email is confirmed.", - "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery. Please click here to enter an email.", + "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You may not be able to post in some categories or chat until your email is confirmed.", + "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery, and may be necessary for chatting and posting in some categories. Please click here to enter an email.", + "user-doesnt-have-email": "User \"%1\" does not have an email set.", "email-confirm-failed": "We could not confirm your email, please try again later.", "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", "sendmail-not-found": "The sendmail executable could not be found, please ensure it is installed and executable by the user running NodeBB.", @@ -103,6 +104,7 @@ "already-bookmarked": "You have already bookmarked this post", "already-unbookmarked": "You have already unbookmarked this post", "cant-ban-other-admins": "Δεν μπορείς να αποκλείσεις άλλους διαχειριστές!", + "cant-make-banned-users-admin": "You can't make banned users admin.", "cant-remove-last-admin": "You are the only administrator. Add another user as an administrator before removing yourself as admin", "account-deletion-disabled": "Account deletion is disabled", "cant-delete-admin": "Remove administrator privileges from this account before attempting to delete it.", diff --git a/public/language/el/modules.json b/public/language/el/modules.json index 9a50ce2e45..f4dae3a98f 100644 --- a/public/language/el/modules.json +++ b/public/language/el/modules.json @@ -54,7 +54,7 @@ "composer.formatting.strikethrough": "Strikethrough", "composer.formatting.code": "Code", "composer.formatting.link": "Link", - "composer.formatting.picture": "Picture", + "composer.formatting.picture": "Image Link", "composer.upload-picture": "Upload Image", "composer.upload-file": "Upload File", "composer.zen_mode": "Zen Mode", diff --git a/public/language/el/top.json b/public/language/el/top.json new file mode 100644 index 0000000000..b8a05bfa5f --- /dev/null +++ b/public/language/el/top.json @@ -0,0 +1,4 @@ +{ + "title": "Top", + "no_top_topics": "No top topics" +} \ No newline at end of file diff --git a/public/language/el/topic.json b/public/language/el/topic.json index f7fbf49f0c..dfbb3a3ffc 100644 --- a/public/language/el/topic.json +++ b/public/language/el/topic.json @@ -47,6 +47,7 @@ "restored-by": "Restored by", "moved-from-by": "Moved from %1 by", "queued-by": "Post queued for approval →", + "backlink": "Referenced by", "bookmark_instructions": "Click here to return to the last read post in this thread.", "flag-post": "Flag this post", "flag-user": "Flag this user", @@ -138,6 +139,7 @@ "composer.handle_placeholder": "Enter your name/handle here", "composer.discard": "Πέταγμα", "composer.submit": "Υποβολή", + "composer.additional-options": "Additional Options", "composer.schedule": "Schedule", "composer.replying_to": "Απάντηση στο %1", "composer.new_topic": "Νέο Θέμα", @@ -158,6 +160,7 @@ "newest_to_oldest": "Νεότερο προς Παλαιότερο", "most_votes": "Most Votes", "most_posts": "Most Posts", + "most_views": "Most Views", "stale.title": "Create new topic instead?", "stale.warning": "The topic you are replying to is quite old. Would you like to create a new topic instead, and reference this one in your reply?", "stale.create": "Create a new topic", diff --git a/public/language/el/user.json b/public/language/el/user.json index 96686d7c6b..5e50868ee0 100644 --- a/public/language/el/user.json +++ b/public/language/el/user.json @@ -94,6 +94,7 @@ "digest_off": "Off", "digest_daily": "Ημερήσια", "digest_weekly": "Εβδομαδιαίως", + "digest_biweekly": "Bi-Weekly", "digest_monthly": "Μηνιαία", "has_no_follower": "Αυτός ο χρήστης δεν έχει κανέναν ακόλουθο :(", "follows_no_one": "Αυτός ο χρήστης δεν ακολουθεί κανέναν :(", diff --git a/public/language/en-GB/admin/advanced/events.json b/public/language/en-GB/admin/advanced/events.json index 56d9457971..b2c2033fb5 100644 --- a/public/language/en-GB/admin/advanced/events.json +++ b/public/language/en-GB/admin/advanced/events.json @@ -3,6 +3,7 @@ "no-events": "There are no events", "control-panel": "Events Control Panel", "delete-events": "Delete Events", + "confirm-delete-all-events": "Are you sure you want to delete all logged events?", "filters": "Filters", "filters-apply": "Apply Filters", "filter-type": "Event Type", diff --git a/public/language/en-GB/admin/dashboard.json b/public/language/en-GB/admin/dashboard.json index 0de31d4917..4d39626882 100644 --- a/public/language/en-GB/admin/dashboard.json +++ b/public/language/en-GB/admin/dashboard.json @@ -56,8 +56,8 @@ "active-users.total": "Total", "active-users.connections": "Connections", - "anonymous-registered-users": "Anonymous vs Registered Users", - "anonymous": "Anonymous", + "guest-registered-users": "Guest vs Registered Users", + "guest": "Guest", "registered": "Registered", "user-presence": "User Presence", @@ -68,6 +68,7 @@ "unread": "Unread", "high-presence-topics": "High Presence Topics", + "popular-searches": "Popular Searches", "graphs.page-views": "Page Views", "graphs.page-views-registered": "Page Views Registered", @@ -75,13 +76,14 @@ "graphs.page-views-bot": "Page Views Bot", "graphs.unique-visitors": "Unique Visitors", "graphs.registered-users": "Registered Users", - "graphs.anonymous-users": "Anonymous Users", + "graphs.guest-users": "Guest Users", "last-restarted-by": "Last restarted by", "no-users-browsing": "No users browsing", "back-to-dashboard": "Back to Dashboard", "details.no-users": "No users have joined within the selected timeframe", "details.no-topics": "No topics have been posted within the selected timeframe", + "details.no-searches": "No searches have been made yet", "details.no-logins": "No logins have been recorded within the selected timeframe", "details.logins-static": "NodeBB only saves session data for %1 days, and so this table below will only show the most recently active sessions", "details.logins-login-time": "Login Time" diff --git a/public/language/en-GB/admin/development/info.json b/public/language/en-GB/admin/development/info.json index 1003af1a5f..11202d9c3a 100644 --- a/public/language/en-GB/admin/development/info.json +++ b/public/language/en-GB/admin/development/info.json @@ -8,7 +8,11 @@ "nodejs": "nodejs", "online": "online", "git": "git", - "memory": "memory", + "process-memory": "process memory", + "system-memory": "system memory", + "used-memory-process": "Used memory by process", + "used-memory-os": "Used system memory", + "total-memory-os": "Total system memory", "load": "system load", "cpu-usage": "cpu usage", "uptime": "uptime", diff --git a/public/language/en-GB/admin/extend/rewards.json b/public/language/en-GB/admin/extend/rewards.json index 5383a90b33..df89d441a7 100644 --- a/public/language/en-GB/admin/extend/rewards.json +++ b/public/language/en-GB/admin/extend/rewards.json @@ -8,8 +8,6 @@ "delete": "Delete", "enable": "Enable", "disable": "Disable", - "control-panel": "Rewards Control", - "new-reward": "New Reward", "alert.delete-success": "Successfully deleted reward", "alert.no-inputs-found": "Illegal reward - no inputs found!", diff --git a/public/language/en-GB/admin/manage/digest.json b/public/language/en-GB/admin/manage/digest.json index 8f3661698a..38c634d1f6 100644 --- a/public/language/en-GB/admin/manage/digest.json +++ b/public/language/en-GB/admin/manage/digest.json @@ -13,6 +13,7 @@ "resent-single": "Manual digest resend completed", "resent-day": "Daily digest resent", "resent-week": "Weekly digest resent", + "resent-biweek": "Bi-Weekly digest resent", "resent-month": "Monthly digest resent", "null": "Never", "manual-run": "Manual digest run:", diff --git a/public/language/en-GB/admin/menu.json b/public/language/en-GB/admin/menu.json index 1c5ea73b4f..5b22fbeb36 100644 --- a/public/language/en-GB/admin/menu.json +++ b/public/language/en-GB/admin/menu.json @@ -4,6 +4,7 @@ "dashboard/logins": "Logins", "dashboard/users": "Users", "dashboard/topics": "Topics", + "dashboard/searches": "Searches", "section-general": "General", "section-manage": "Manage", diff --git a/public/language/en-GB/admin/settings/email.json b/public/language/en-GB/admin/settings/email.json index 9bdbde26a6..17c60daf69 100644 --- a/public/language/en-GB/admin/settings/email.json +++ b/public/language/en-GB/admin/settings/email.json @@ -6,7 +6,7 @@ "from-help": "The from name to display in the email.", "smtp-transport": "SMTP Transport", - "smtp-transport.enabled": "Use an external email server to send emails", + "smtp-transport.enabled": "Enable SMTP Transport", "smtp-transport-help": "You can select from a list of well-known services or enter a custom one.", "smtp-transport.service": "Select a service", "smtp-transport.service-custom": "Custom Service", @@ -37,6 +37,11 @@ "subscriptions.hour": "Digest Hour", "subscriptions.hour-help": "Please enter a number representing the hour to send scheduled email digests (e.g. 0 for midnight, 17 for 5:00pm). Keep in mind that this is the hour according to the server itself, and may not exactly match your system clock.
The approximate server time is:
The next daily digest is scheduled to be sent ", "notifications.remove-images": "Remove images from email notifications", + "require-email-address": "Require new users to specify an email address", + "require-email-address-warning": "By default, users can opt-out of entering an email address by leaving the field blank. Enabling this option means they have to enter an email address in order to proceed with registration. It does not ensure user will enter a real email address, nor even an address they own.", + "send-validation-email": "Send validation emails when an email is added or changed", "include-unverified-emails": "Send emails to recipients who have not explicitly confirmed their emails", - "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws." -} \ No newline at end of file + "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws.", + "prompt": "Prompt users to enter or confirm their emails", + "prompt-help": "If a user does not have an email set, or their email is not confirmed, a warning will be shown on screen." +} diff --git a/public/language/en-GB/admin/settings/general.json b/public/language/en-GB/admin/settings/general.json index be7df90870..29b939861b 100644 --- a/public/language/en-GB/admin/settings/general.json +++ b/public/language/en-GB/admin/settings/general.json @@ -3,9 +3,9 @@ "title": "Site Title", "title.short": "Short Title", "title.short-placeholder": "If no short title is specified, the site title will be used", - "title.url": "URL", + "title.url": "Title Link URL", "title.url-placeholder": "The URL of the site title", - "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.", + "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "title.name": "Your Community Name", "title.show-in-header": "Show Site Title in Header", "browser-title": "Browser Title", @@ -20,9 +20,9 @@ "logo.image": "Image", "logo.image-placeholder": "Path to a logo to display on forum header", "logo.upload": "Upload", - "logo.url": "URL", + "logo.url": "Logo Link URL", "logo.url-placeholder": "The URL of the site logo", - "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.", + "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "logo.alt-text": "Alt Text", "log.alt-text-placeholder": "Alternative text for accessibility", "favicon": "Favicon", @@ -47,4 +47,4 @@ "undo-timeout": "Undo Timeout", "undo-timeout-help": "Some operations such as moving topics will allow for the moderator to undo their action within a certain timeframe. Set to 0 to disable undo completely.", "topic-tools": "Topic Tools" -} \ No newline at end of file +} diff --git a/public/language/en-GB/admin/settings/navigation.json b/public/language/en-GB/admin/settings/navigation.json index 13dd01aae7..7baca85096 100644 --- a/public/language/en-GB/admin/settings/navigation.json +++ b/public/language/en-GB/admin/settings/navigation.json @@ -11,6 +11,8 @@ "properties": "Properties:", "groups": "Groups:", "open-new-window": "Open in a new window", + "dropdown": "Dropdown", + "dropdown-placeholder": "Place your dropdown menu items below, ie:
<li><a href="https://myforum.com">Link 1</a></li>", "btn.delete": "Delete", "btn.disable": "Disable", @@ -20,4 +22,4 @@ "custom-route": "Custom Route", "core": "core", "plugin": "plugin" -} \ No newline at end of file +} diff --git a/public/language/en-GB/admin/settings/notifications.json b/public/language/en-GB/admin/settings/notifications.json index da6c9680a3..c6d8b928ce 100644 --- a/public/language/en-GB/admin/settings/notifications.json +++ b/public/language/en-GB/admin/settings/notifications.json @@ -2,5 +2,6 @@ "notifications": "Notifications", "welcome-notification": "Welcome Notification", "welcome-notification-link": "Welcome Notification Link", - "welcome-notification-uid": "Welcome Notification User (UID)" + "welcome-notification-uid": "Welcome Notification User (UID)", + "post-queue-notification-uid": "Post Queue User (UID)" } \ No newline at end of file diff --git a/public/language/en-GB/admin/settings/post.json b/public/language/en-GB/admin/settings/post.json index 27493aafbd..ab8245738c 100644 --- a/public/language/en-GB/admin/settings/post.json +++ b/public/language/en-GB/admin/settings/post.json @@ -40,6 +40,7 @@ "teaser.last-post": "Last – Show the latest post, including the original post, if no replies", "teaser.last-reply": "Last – Show the latest reply, or a \"No replies\" placeholder if no replies", "teaser.first": "First", + "showPostPreviewsOnHover": "Show a preview of posts when mouse overed", "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", @@ -56,6 +57,9 @@ "composer.show-help": "Show \"Help\" tab", "composer.enable-plugin-help": "Allow plugins to add content to the help tab", "composer.custom-help": "Custom Help Text", + "backlinks": "Backlinks", + "backlinks.enabled": "Enable topic backlinks", + "backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.", "ip-tracking": "IP Tracking", "ip-tracking.each-post": "Track IP Address for each post", "enable-post-history": "Enable Post History" diff --git a/public/language/en-GB/admin/settings/user.json b/public/language/en-GB/admin/settings/user.json index 48be13b75e..7923bf8cbe 100644 --- a/public/language/en-GB/admin/settings/user.json +++ b/public/language/en-GB/admin/settings/user.json @@ -71,6 +71,7 @@ "digest-freq.off": "Off", "digest-freq.daily": "Daily", "digest-freq.weekly": "Weekly", + "digest-freq.biweekly": "Bi-Weekly", "digest-freq.monthly": "Monthly", "email-chat-notifs": "Send an email if a new chat message arrives and I am not online", "email-post-notif": "Send an email when replies are made to topics I am subscribed to", diff --git a/public/language/en-GB/error.json b/public/language/en-GB/error.json index 3b6f337165..0b5e714e26 100644 --- a/public/language/en-GB/error.json +++ b/public/language/en-GB/error.json @@ -39,8 +39,9 @@ "email-invited": "Email was already invited", "email-not-confirmed": "Posting in some categories or topics is enabled once your email is confirmed, please click here to send a confirmation email.", "email-not-confirmed-chat": "You are unable to chat until your email is confirmed, please click here to confirm your email.", - "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You won't be able to post or chat until your email is confirmed.", - "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery. Please click here to enter an email.", + "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You may not be able to post in some categories or chat until your email is confirmed.", + "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery, and may be necessary for chatting and posting in some categories. Please click here to enter an email.", + "user-doesnt-have-email": "User \"%1\" does not have an email set.", "email-confirm-failed": "We could not confirm your email, please try again later.", "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", "sendmail-not-found": "The sendmail executable could not be found, please ensure it is installed and executable by the user running NodeBB.", @@ -120,6 +121,7 @@ "already-unbookmarked": "You have already unbookmarked this post", "cant-ban-other-admins": "You can't ban other admins!", + "cant-make-banned-users-admin": "You can't make banned users admin.", "cant-remove-last-admin": "You are the only administrator. Add another user as an administrator before removing yourself as admin", "account-deletion-disabled": "Account deletion is disabled", "cant-delete-admin": "Remove administrator privileges from this account before attempting to delete it.", diff --git a/public/language/en-GB/modules.json b/public/language/en-GB/modules.json index fb16c945d7..ded95a7130 100644 --- a/public/language/en-GB/modules.json +++ b/public/language/en-GB/modules.json @@ -56,7 +56,7 @@ "composer.formatting.strikethrough": "Strikethrough", "composer.formatting.code": "Code", "composer.formatting.link": "Link", - "composer.formatting.picture": "Picture", + "composer.formatting.picture": "Image Link", "composer.upload-picture": "Upload Image", "composer.upload-file": "Upload File", "composer.zen_mode": "Zen Mode", diff --git a/public/language/en-GB/topic.json b/public/language/en-GB/topic.json index b95a86c862..ceb9b43d42 100644 --- a/public/language/en-GB/topic.json +++ b/public/language/en-GB/topic.json @@ -51,6 +51,7 @@ "restored-by": "Restored by", "moved-from-by": "Moved from %1 by", "queued-by": "Post queued for approval →", + "backlink": "Referenced by", "bookmark_instructions" : "Click here to return to the last read post in this thread.", @@ -157,6 +158,7 @@ "composer.handle_placeholder": "Enter your name/handle here", "composer.discard": "Discard", "composer.submit": "Submit", + "composer.additional-options": "Additional Options", "composer.schedule": "Schedule", "composer.replying_to": "Replying to %1", "composer.new_topic": "New Topic", @@ -180,6 +182,7 @@ "newest_to_oldest": "Newest to Oldest", "most_votes": "Most Votes", "most_posts": "Most Posts", + "most_views": "Most Views", "stale.title": "Create new topic instead?", "stale.warning": "The topic you are replying to is quite old. Would you like to create a new topic instead, and reference this one in your reply?", diff --git a/public/language/en-GB/user.json b/public/language/en-GB/user.json index 2e836559de..94dad42fda 100644 --- a/public/language/en-GB/user.json +++ b/public/language/en-GB/user.json @@ -100,6 +100,7 @@ "digest_off": "Off", "digest_daily": "Daily", "digest_weekly": "Weekly", + "digest_biweekly": "Bi-Weekly", "digest_monthly": "Monthly", "has_no_follower": "This user doesn't have any followers :(", diff --git a/public/language/en-US/admin/advanced/events.json b/public/language/en-US/admin/advanced/events.json index 56d9457971..b2c2033fb5 100644 --- a/public/language/en-US/admin/advanced/events.json +++ b/public/language/en-US/admin/advanced/events.json @@ -3,6 +3,7 @@ "no-events": "There are no events", "control-panel": "Events Control Panel", "delete-events": "Delete Events", + "confirm-delete-all-events": "Are you sure you want to delete all logged events?", "filters": "Filters", "filters-apply": "Apply Filters", "filter-type": "Event Type", diff --git a/public/language/en-US/admin/dashboard.json b/public/language/en-US/admin/dashboard.json index 0de31d4917..4d39626882 100644 --- a/public/language/en-US/admin/dashboard.json +++ b/public/language/en-US/admin/dashboard.json @@ -56,8 +56,8 @@ "active-users.total": "Total", "active-users.connections": "Connections", - "anonymous-registered-users": "Anonymous vs Registered Users", - "anonymous": "Anonymous", + "guest-registered-users": "Guest vs Registered Users", + "guest": "Guest", "registered": "Registered", "user-presence": "User Presence", @@ -68,6 +68,7 @@ "unread": "Unread", "high-presence-topics": "High Presence Topics", + "popular-searches": "Popular Searches", "graphs.page-views": "Page Views", "graphs.page-views-registered": "Page Views Registered", @@ -75,13 +76,14 @@ "graphs.page-views-bot": "Page Views Bot", "graphs.unique-visitors": "Unique Visitors", "graphs.registered-users": "Registered Users", - "graphs.anonymous-users": "Anonymous Users", + "graphs.guest-users": "Guest Users", "last-restarted-by": "Last restarted by", "no-users-browsing": "No users browsing", "back-to-dashboard": "Back to Dashboard", "details.no-users": "No users have joined within the selected timeframe", "details.no-topics": "No topics have been posted within the selected timeframe", + "details.no-searches": "No searches have been made yet", "details.no-logins": "No logins have been recorded within the selected timeframe", "details.logins-static": "NodeBB only saves session data for %1 days, and so this table below will only show the most recently active sessions", "details.logins-login-time": "Login Time" diff --git a/public/language/en-US/admin/development/info.json b/public/language/en-US/admin/development/info.json index 1003af1a5f..11202d9c3a 100644 --- a/public/language/en-US/admin/development/info.json +++ b/public/language/en-US/admin/development/info.json @@ -8,7 +8,11 @@ "nodejs": "nodejs", "online": "online", "git": "git", - "memory": "memory", + "process-memory": "process memory", + "system-memory": "system memory", + "used-memory-process": "Used memory by process", + "used-memory-os": "Used system memory", + "total-memory-os": "Total system memory", "load": "system load", "cpu-usage": "cpu usage", "uptime": "uptime", diff --git a/public/language/en-US/admin/extend/rewards.json b/public/language/en-US/admin/extend/rewards.json index 5383a90b33..df89d441a7 100644 --- a/public/language/en-US/admin/extend/rewards.json +++ b/public/language/en-US/admin/extend/rewards.json @@ -8,8 +8,6 @@ "delete": "Delete", "enable": "Enable", "disable": "Disable", - "control-panel": "Rewards Control", - "new-reward": "New Reward", "alert.delete-success": "Successfully deleted reward", "alert.no-inputs-found": "Illegal reward - no inputs found!", diff --git a/public/language/en-US/admin/general/dashboard.json b/public/language/en-US/admin/general/dashboard.json deleted file mode 100644 index 37ae537786..0000000000 --- a/public/language/en-US/admin/general/dashboard.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "forum-traffic": "Forum Traffic", - "page-views": "Page Views", - "unique-visitors": "Unique Visitors", - "new-users": "New Users", - "posts": "Posts", - "topics": "Topics", - "page-views-seven": "Last 7 Days", - "page-views-thirty": "Last 30 Days", - "page-views-last-day": "Last 24 hours", - "page-views-custom": "Custom Date Range", - "page-views-custom-start": "Range Start", - "page-views-custom-end": "Range End", - "page-views-custom-help": "Enter a date range of page views you would like to view. If no date picker is available, the accepted format is YYYY-MM-DD", - "page-views-custom-error": "Please enter a valid date range in the format YYYY-MM-DD", - - "stats.yesterday": "Yesterday", - "stats.today": "Today", - "stats.last-week": "Last Week", - "stats.this-week": "This Week", - "stats.last-month": "Last Month", - "stats.this-month": "This Month", - "stats.all": "All Time", - - "updates": "Updates", - "running-version": "You are running NodeBB v%1.", - "keep-updated": "Always make sure that your NodeBB is up to date for the latest security patches and bug fixes.", - "up-to-date": "

You are up-to-date

", - "upgrade-available": "

A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-upgrade-available": "

This is an outdated pre-release version of NodeBB. A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-warning": "

This is a pre-release version of NodeBB. Unintended bugs may occur.

", - "running-in-development": "Forum is running in development mode. The forum may be open to potential vulnerabilities; please contact your system administrator.", - "latest-lookup-failed": "

Failed to look up latest available version of NodeBB

", - - "notices": "Notices", - "restart-not-required": "Restart not required", - "restart-required": "Restart required", - "search-plugin-installed": "Search Plugin installed", - "search-plugin-not-installed": "Search Plugin not installed", - "search-plugin-tooltip": "Install a search plugin from the plugin page in order to activate search functionality", - - "control-panel": "System Control", - "rebuild-and-restart": "Rebuild & Restart", - "restart": "Restart", - "restart-warning": "Rebuilding or Restarting your NodeBB will drop all existing connections for a few seconds.", - "restart-disabled": "Rebuilding and Restarting your NodeBB has been disabled as you do not seem to be running it via the appropriate daemon.", - "maintenance-mode": "Maintenance Mode", - "maintenance-mode-title": "Click here to set up maintenance mode for NodeBB", - "realtime-chart-updates": "Realtime Chart Updates", - - "active-users": "Active Users", - "active-users.users": "Users", - "active-users.guests": "Guests", - "active-users.total": "Total", - "active-users.connections": "Connections", - - "anonymous-registered-users": "Anonymous vs Registered Users", - "anonymous": "Anonymous", - "registered": "Registered", - - "user-presence": "User Presence", - "on-categories": "On categories list", - "reading-posts": "Reading posts", - "browsing-topics": "Browsing topics", - "recent": "Recent", - "unread": "Unread", - - "high-presence-topics": "High Presence Topics", - - "graphs.page-views": "Page Views", - "graphs.page-views-registered": "Page Views Registered", - "graphs.page-views-guest": "Page Views Guest", - "graphs.page-views-bot": "Page Views Bot", - "graphs.unique-visitors": "Unique Visitors", - "graphs.registered-users": "Registered Users", - "graphs.anonymous-users": "Anonymous Users", - "last-restarted-by": "Last restarted by", - "no-users-browsing": "No users browsing" -} diff --git a/public/language/en-US/admin/general/homepage.json b/public/language/en-US/admin/general/homepage.json deleted file mode 100644 index 7428d59eeb..0000000000 --- a/public/language/en-US/admin/general/homepage.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "home-page": "Home Page", - "description": "Choose what page is shown when users navigate to the root URL of your forum.", - "home-page-route": "Home Page Route", - "custom-route": "Custom Route", - "allow-user-home-pages": "Allow User Home Pages", - "home-page-title": "Title of the home page (default \"Home\")" -} \ No newline at end of file diff --git a/public/language/en-US/admin/general/languages.json b/public/language/en-US/admin/general/languages.json deleted file mode 100644 index bdd57849b3..0000000000 --- a/public/language/en-US/admin/general/languages.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "language-settings": "Language Settings", - "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language", - "auto-detect": "Auto Detect Language Setting for Guests" -} \ No newline at end of file diff --git a/public/language/en-US/admin/general/navigation.json b/public/language/en-US/admin/general/navigation.json deleted file mode 100644 index 13dd01aae7..0000000000 --- a/public/language/en-US/admin/general/navigation.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "icon": "Icon:", - "change-icon": "change", - "route": "Route:", - "tooltip": "Tooltip:", - "text": "Text:", - "text-class": "Text Class: optional", - "class": "Class: optional", - "id": "ID: optional", - - "properties": "Properties:", - "groups": "Groups:", - "open-new-window": "Open in a new window", - - "btn.delete": "Delete", - "btn.disable": "Disable", - "btn.enable": "Enable", - - "available-menu-items": "Available Menu Items", - "custom-route": "Custom Route", - "core": "core", - "plugin": "plugin" -} \ No newline at end of file diff --git a/public/language/en-US/admin/general/social.json b/public/language/en-US/admin/general/social.json deleted file mode 100644 index 23aedfcfaa..0000000000 --- a/public/language/en-US/admin/general/social.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "post-sharing": "Post Sharing", - "info-plugins-additional": "Plugins can add additional networks for sharing posts.", - "save-success": "Successfully saved Post Sharing Networks!" -} \ No newline at end of file diff --git a/public/language/en-US/admin/general/sounds.json b/public/language/en-US/admin/general/sounds.json deleted file mode 100644 index 95ccbde0f1..0000000000 --- a/public/language/en-US/admin/general/sounds.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "notifications": "Notifications", - "chat-messages": "Chat Messages", - "play-sound": "Play", - "incoming-message": "Incoming Message", - "outgoing-message": "Outgoing Message", - "upload-new-sound": "Upload New Sound", - "saved": "Settings Saved" -} \ No newline at end of file diff --git a/public/language/en-US/admin/manage/digest.json b/public/language/en-US/admin/manage/digest.json index 8f3661698a..38c634d1f6 100644 --- a/public/language/en-US/admin/manage/digest.json +++ b/public/language/en-US/admin/manage/digest.json @@ -13,6 +13,7 @@ "resent-single": "Manual digest resend completed", "resent-day": "Daily digest resent", "resent-week": "Weekly digest resent", + "resent-biweek": "Bi-Weekly digest resent", "resent-month": "Monthly digest resent", "null": "Never", "manual-run": "Manual digest run:", diff --git a/public/language/en-US/admin/manage/users.json b/public/language/en-US/admin/manage/users.json index 38a065b954..2a3c0c4829 100644 --- a/public/language/en-US/admin/manage/users.json +++ b/public/language/en-US/admin/manage/users.json @@ -47,6 +47,7 @@ "users.uid": "uid", "users.username": "username", "users.email": "email", + "users.no-email": "(no email)", "users.ip": "IP", "users.postcount": "postcount", "users.reputation": "reputation", diff --git a/public/language/en-US/admin/menu.json b/public/language/en-US/admin/menu.json index 1c5ea73b4f..5b22fbeb36 100644 --- a/public/language/en-US/admin/menu.json +++ b/public/language/en-US/admin/menu.json @@ -4,6 +4,7 @@ "dashboard/logins": "Logins", "dashboard/users": "Users", "dashboard/topics": "Topics", + "dashboard/searches": "Searches", "section-general": "General", "section-manage": "Manage", diff --git a/public/language/en-US/admin/settings/email.json b/public/language/en-US/admin/settings/email.json index 9bdbde26a6..65f579d28c 100644 --- a/public/language/en-US/admin/settings/email.json +++ b/public/language/en-US/admin/settings/email.json @@ -6,7 +6,7 @@ "from-help": "The from name to display in the email.", "smtp-transport": "SMTP Transport", - "smtp-transport.enabled": "Use an external email server to send emails", + "smtp-transport.enabled": "Enable SMTP Transport", "smtp-transport-help": "You can select from a list of well-known services or enter a custom one.", "smtp-transport.service": "Select a service", "smtp-transport.service-custom": "Custom Service", @@ -37,6 +37,10 @@ "subscriptions.hour": "Digest Hour", "subscriptions.hour-help": "Please enter a number representing the hour to send scheduled email digests (e.g. 0 for midnight, 17 for 5:00pm). Keep in mind that this is the hour according to the server itself, and may not exactly match your system clock.
The approximate server time is:
The next daily digest is scheduled to be sent ", "notifications.remove-images": "Remove images from email notifications", + "require-email-address": "Require new users to specify an email address", + "require-email-address-warning": "By default, users can opt-out of entering an email address. Enabling this option means they have to enter an email address in order to proceed with registration. It does not ensure user will enter a real email address, nor even an address they own.", "include-unverified-emails": "Send emails to recipients who have not explicitly confirmed their emails", - "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws." -} \ No newline at end of file + "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws.", + "prompt": "Prompt users to enter or confirm their emails", + "prompt-help": "If a user does not have an email set, or their email is not confirmed, a warning will be shown on screen." +} diff --git a/public/language/en-US/admin/settings/general.json b/public/language/en-US/admin/settings/general.json index be7df90870..29b939861b 100644 --- a/public/language/en-US/admin/settings/general.json +++ b/public/language/en-US/admin/settings/general.json @@ -3,9 +3,9 @@ "title": "Site Title", "title.short": "Short Title", "title.short-placeholder": "If no short title is specified, the site title will be used", - "title.url": "URL", + "title.url": "Title Link URL", "title.url-placeholder": "The URL of the site title", - "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.", + "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "title.name": "Your Community Name", "title.show-in-header": "Show Site Title in Header", "browser-title": "Browser Title", @@ -20,9 +20,9 @@ "logo.image": "Image", "logo.image-placeholder": "Path to a logo to display on forum header", "logo.upload": "Upload", - "logo.url": "URL", + "logo.url": "Logo Link URL", "logo.url-placeholder": "The URL of the site logo", - "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.", + "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "logo.alt-text": "Alt Text", "log.alt-text-placeholder": "Alternative text for accessibility", "favicon": "Favicon", @@ -47,4 +47,4 @@ "undo-timeout": "Undo Timeout", "undo-timeout-help": "Some operations such as moving topics will allow for the moderator to undo their action within a certain timeframe. Set to 0 to disable undo completely.", "topic-tools": "Topic Tools" -} \ No newline at end of file +} diff --git a/public/language/en-US/admin/settings/navigation.json b/public/language/en-US/admin/settings/navigation.json index 13dd01aae7..7baca85096 100644 --- a/public/language/en-US/admin/settings/navigation.json +++ b/public/language/en-US/admin/settings/navigation.json @@ -11,6 +11,8 @@ "properties": "Properties:", "groups": "Groups:", "open-new-window": "Open in a new window", + "dropdown": "Dropdown", + "dropdown-placeholder": "Place your dropdown menu items below, ie:
<li><a href="https://myforum.com">Link 1</a></li>", "btn.delete": "Delete", "btn.disable": "Disable", @@ -20,4 +22,4 @@ "custom-route": "Custom Route", "core": "core", "plugin": "plugin" -} \ No newline at end of file +} diff --git a/public/language/en-US/admin/settings/notifications.json b/public/language/en-US/admin/settings/notifications.json index da6c9680a3..c6d8b928ce 100644 --- a/public/language/en-US/admin/settings/notifications.json +++ b/public/language/en-US/admin/settings/notifications.json @@ -2,5 +2,6 @@ "notifications": "Notifications", "welcome-notification": "Welcome Notification", "welcome-notification-link": "Welcome Notification Link", - "welcome-notification-uid": "Welcome Notification User (UID)" + "welcome-notification-uid": "Welcome Notification User (UID)", + "post-queue-notification-uid": "Post Queue User (UID)" } \ No newline at end of file diff --git a/public/language/en-US/admin/settings/post.json b/public/language/en-US/admin/settings/post.json index f91a065e46..b4d89f389a 100644 --- a/public/language/en-US/admin/settings/post.json +++ b/public/language/en-US/admin/settings/post.json @@ -40,6 +40,7 @@ "teaser.last-post": "Last – Show the latest post, including the original post, if no replies", "teaser.last-reply": "Last – Show the latest reply, or a \"No replies\" placeholder if no replies", "teaser.first": "First", + "showPostPreviewsOnHover": "Show a preview of posts when mouse overed", "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", @@ -56,6 +57,9 @@ "composer.show-help": "Show \"Help\" tab", "composer.enable-plugin-help": "Allow plugins to add content to the help tab", "composer.custom-help": "Custom Help Text", + "backlinks": "Backlinks", + "backlinks.enabled": "Enable topic backlinks", + "backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.", "ip-tracking": "IP Tracking", "ip-tracking.each-post": "Track IP Address for each post", "enable-post-history": "Enable Post History" diff --git a/public/language/en-US/admin/settings/user.json b/public/language/en-US/admin/settings/user.json index 48be13b75e..7923bf8cbe 100644 --- a/public/language/en-US/admin/settings/user.json +++ b/public/language/en-US/admin/settings/user.json @@ -71,6 +71,7 @@ "digest-freq.off": "Off", "digest-freq.daily": "Daily", "digest-freq.weekly": "Weekly", + "digest-freq.biweekly": "Bi-Weekly", "digest-freq.monthly": "Monthly", "email-chat-notifs": "Send an email if a new chat message arrives and I am not online", "email-post-notif": "Send an email when replies are made to topics I am subscribed to", diff --git a/public/language/en-US/error.json b/public/language/en-US/error.json index b806ec9367..6eb0983661 100644 --- a/public/language/en-US/error.json +++ b/public/language/en-US/error.json @@ -34,8 +34,9 @@ "email-invited": "Email was already invited", "email-not-confirmed": "Posting in some categories or topics is enabled once your email is confirmed, please click here to send a confirmation email.", "email-not-confirmed-chat": "You are unable to chat until your email is confirmed, please click here to confirm your email.", - "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You won't be able to post or chat until your email is confirmed.", - "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery. Please click here to enter an email.", + "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You may not be able to post in some categories or chat until your email is confirmed.", + "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery, and may be necessary for chatting and posting in some categories. Please click here to enter an email.", + "user-doesnt-have-email": "User \"%1\" does not have an email set.", "email-confirm-failed": "We could not confirm your email, please try again later.", "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", "sendmail-not-found": "The sendmail executable could not be found, please ensure it is installed and executable by the user running NodeBB.", @@ -103,6 +104,7 @@ "already-bookmarked": "You have already bookmarked this post", "already-unbookmarked": "You have already unbookmarked this post", "cant-ban-other-admins": "You can't ban other admins!", + "cant-make-banned-users-admin": "You can't make banned users admin.", "cant-remove-last-admin": "You are the only administrator. Add another user as an administrator before removing yourself as admin", "account-deletion-disabled": "Account deletion is disabled", "cant-delete-admin": "Remove administrator privileges from this account before attempting to delete it.", diff --git a/public/language/en-US/modules.json b/public/language/en-US/modules.json index 9a50ce2e45..f4dae3a98f 100644 --- a/public/language/en-US/modules.json +++ b/public/language/en-US/modules.json @@ -54,7 +54,7 @@ "composer.formatting.strikethrough": "Strikethrough", "composer.formatting.code": "Code", "composer.formatting.link": "Link", - "composer.formatting.picture": "Picture", + "composer.formatting.picture": "Image Link", "composer.upload-picture": "Upload Image", "composer.upload-file": "Upload File", "composer.zen_mode": "Zen Mode", diff --git a/public/language/en-US/topic.json b/public/language/en-US/topic.json index c0c7610a61..ca723b72f6 100644 --- a/public/language/en-US/topic.json +++ b/public/language/en-US/topic.json @@ -47,6 +47,7 @@ "restored-by": "Restored by", "moved-from-by": "Moved from %1 by", "queued-by": "Post queued for approval →", + "backlink": "Referenced by", "bookmark_instructions": "Click here to return to the last read post in this thread.", "flag-post": "Flag this post", "flag-user": "Flag this user", @@ -138,6 +139,7 @@ "composer.handle_placeholder": "Enter your name/handle here", "composer.discard": "Discard", "composer.submit": "Submit", + "composer.additional-options": "Additional Options", "composer.schedule": "Schedule", "composer.replying_to": "Replying to %1", "composer.new_topic": "New Topic", @@ -158,6 +160,7 @@ "newest_to_oldest": "Newest to Oldest", "most_votes": "Most Votes", "most_posts": "Most Posts", + "most_views": "Most Views", "stale.title": "Create new topic instead?", "stale.warning": "The topic you are replying to is quite old. Would you like to create a new topic instead, and reference this one in your reply?", "stale.create": "Create a new topic", diff --git a/public/language/en-US/user.json b/public/language/en-US/user.json index 06da8484d8..19da2100e2 100644 --- a/public/language/en-US/user.json +++ b/public/language/en-US/user.json @@ -94,6 +94,7 @@ "digest_off": "Off", "digest_daily": "Daily", "digest_weekly": "Weekly", + "digest_biweekly": "Bi-Weekly", "digest_monthly": "Monthly", "has_no_follower": "This user doesn't have any followers :(", "follows_no_one": "This user isn't following anyone :(", diff --git a/public/language/en-x-pirate/admin/advanced/events.json b/public/language/en-x-pirate/admin/advanced/events.json index 56d9457971..b2c2033fb5 100644 --- a/public/language/en-x-pirate/admin/advanced/events.json +++ b/public/language/en-x-pirate/admin/advanced/events.json @@ -3,6 +3,7 @@ "no-events": "There are no events", "control-panel": "Events Control Panel", "delete-events": "Delete Events", + "confirm-delete-all-events": "Are you sure you want to delete all logged events?", "filters": "Filters", "filters-apply": "Apply Filters", "filter-type": "Event Type", diff --git a/public/language/en-x-pirate/admin/dashboard.json b/public/language/en-x-pirate/admin/dashboard.json index 0de31d4917..4d39626882 100644 --- a/public/language/en-x-pirate/admin/dashboard.json +++ b/public/language/en-x-pirate/admin/dashboard.json @@ -56,8 +56,8 @@ "active-users.total": "Total", "active-users.connections": "Connections", - "anonymous-registered-users": "Anonymous vs Registered Users", - "anonymous": "Anonymous", + "guest-registered-users": "Guest vs Registered Users", + "guest": "Guest", "registered": "Registered", "user-presence": "User Presence", @@ -68,6 +68,7 @@ "unread": "Unread", "high-presence-topics": "High Presence Topics", + "popular-searches": "Popular Searches", "graphs.page-views": "Page Views", "graphs.page-views-registered": "Page Views Registered", @@ -75,13 +76,14 @@ "graphs.page-views-bot": "Page Views Bot", "graphs.unique-visitors": "Unique Visitors", "graphs.registered-users": "Registered Users", - "graphs.anonymous-users": "Anonymous Users", + "graphs.guest-users": "Guest Users", "last-restarted-by": "Last restarted by", "no-users-browsing": "No users browsing", "back-to-dashboard": "Back to Dashboard", "details.no-users": "No users have joined within the selected timeframe", "details.no-topics": "No topics have been posted within the selected timeframe", + "details.no-searches": "No searches have been made yet", "details.no-logins": "No logins have been recorded within the selected timeframe", "details.logins-static": "NodeBB only saves session data for %1 days, and so this table below will only show the most recently active sessions", "details.logins-login-time": "Login Time" diff --git a/public/language/en-x-pirate/admin/development/info.json b/public/language/en-x-pirate/admin/development/info.json index 1003af1a5f..11202d9c3a 100644 --- a/public/language/en-x-pirate/admin/development/info.json +++ b/public/language/en-x-pirate/admin/development/info.json @@ -8,7 +8,11 @@ "nodejs": "nodejs", "online": "online", "git": "git", - "memory": "memory", + "process-memory": "process memory", + "system-memory": "system memory", + "used-memory-process": "Used memory by process", + "used-memory-os": "Used system memory", + "total-memory-os": "Total system memory", "load": "system load", "cpu-usage": "cpu usage", "uptime": "uptime", diff --git a/public/language/en-x-pirate/admin/extend/rewards.json b/public/language/en-x-pirate/admin/extend/rewards.json index 5383a90b33..df89d441a7 100644 --- a/public/language/en-x-pirate/admin/extend/rewards.json +++ b/public/language/en-x-pirate/admin/extend/rewards.json @@ -8,8 +8,6 @@ "delete": "Delete", "enable": "Enable", "disable": "Disable", - "control-panel": "Rewards Control", - "new-reward": "New Reward", "alert.delete-success": "Successfully deleted reward", "alert.no-inputs-found": "Illegal reward - no inputs found!", diff --git a/public/language/en-x-pirate/admin/general/dashboard.json b/public/language/en-x-pirate/admin/general/dashboard.json deleted file mode 100644 index 37ae537786..0000000000 --- a/public/language/en-x-pirate/admin/general/dashboard.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "forum-traffic": "Forum Traffic", - "page-views": "Page Views", - "unique-visitors": "Unique Visitors", - "new-users": "New Users", - "posts": "Posts", - "topics": "Topics", - "page-views-seven": "Last 7 Days", - "page-views-thirty": "Last 30 Days", - "page-views-last-day": "Last 24 hours", - "page-views-custom": "Custom Date Range", - "page-views-custom-start": "Range Start", - "page-views-custom-end": "Range End", - "page-views-custom-help": "Enter a date range of page views you would like to view. If no date picker is available, the accepted format is YYYY-MM-DD", - "page-views-custom-error": "Please enter a valid date range in the format YYYY-MM-DD", - - "stats.yesterday": "Yesterday", - "stats.today": "Today", - "stats.last-week": "Last Week", - "stats.this-week": "This Week", - "stats.last-month": "Last Month", - "stats.this-month": "This Month", - "stats.all": "All Time", - - "updates": "Updates", - "running-version": "You are running NodeBB v%1.", - "keep-updated": "Always make sure that your NodeBB is up to date for the latest security patches and bug fixes.", - "up-to-date": "

You are up-to-date

", - "upgrade-available": "

A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-upgrade-available": "

This is an outdated pre-release version of NodeBB. A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-warning": "

This is a pre-release version of NodeBB. Unintended bugs may occur.

", - "running-in-development": "Forum is running in development mode. The forum may be open to potential vulnerabilities; please contact your system administrator.", - "latest-lookup-failed": "

Failed to look up latest available version of NodeBB

", - - "notices": "Notices", - "restart-not-required": "Restart not required", - "restart-required": "Restart required", - "search-plugin-installed": "Search Plugin installed", - "search-plugin-not-installed": "Search Plugin not installed", - "search-plugin-tooltip": "Install a search plugin from the plugin page in order to activate search functionality", - - "control-panel": "System Control", - "rebuild-and-restart": "Rebuild & Restart", - "restart": "Restart", - "restart-warning": "Rebuilding or Restarting your NodeBB will drop all existing connections for a few seconds.", - "restart-disabled": "Rebuilding and Restarting your NodeBB has been disabled as you do not seem to be running it via the appropriate daemon.", - "maintenance-mode": "Maintenance Mode", - "maintenance-mode-title": "Click here to set up maintenance mode for NodeBB", - "realtime-chart-updates": "Realtime Chart Updates", - - "active-users": "Active Users", - "active-users.users": "Users", - "active-users.guests": "Guests", - "active-users.total": "Total", - "active-users.connections": "Connections", - - "anonymous-registered-users": "Anonymous vs Registered Users", - "anonymous": "Anonymous", - "registered": "Registered", - - "user-presence": "User Presence", - "on-categories": "On categories list", - "reading-posts": "Reading posts", - "browsing-topics": "Browsing topics", - "recent": "Recent", - "unread": "Unread", - - "high-presence-topics": "High Presence Topics", - - "graphs.page-views": "Page Views", - "graphs.page-views-registered": "Page Views Registered", - "graphs.page-views-guest": "Page Views Guest", - "graphs.page-views-bot": "Page Views Bot", - "graphs.unique-visitors": "Unique Visitors", - "graphs.registered-users": "Registered Users", - "graphs.anonymous-users": "Anonymous Users", - "last-restarted-by": "Last restarted by", - "no-users-browsing": "No users browsing" -} diff --git a/public/language/en-x-pirate/admin/general/homepage.json b/public/language/en-x-pirate/admin/general/homepage.json deleted file mode 100644 index 7428d59eeb..0000000000 --- a/public/language/en-x-pirate/admin/general/homepage.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "home-page": "Home Page", - "description": "Choose what page is shown when users navigate to the root URL of your forum.", - "home-page-route": "Home Page Route", - "custom-route": "Custom Route", - "allow-user-home-pages": "Allow User Home Pages", - "home-page-title": "Title of the home page (default \"Home\")" -} \ No newline at end of file diff --git a/public/language/en-x-pirate/admin/general/languages.json b/public/language/en-x-pirate/admin/general/languages.json deleted file mode 100644 index bdd57849b3..0000000000 --- a/public/language/en-x-pirate/admin/general/languages.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "language-settings": "Language Settings", - "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language", - "auto-detect": "Auto Detect Language Setting for Guests" -} \ No newline at end of file diff --git a/public/language/en-x-pirate/admin/general/navigation.json b/public/language/en-x-pirate/admin/general/navigation.json deleted file mode 100644 index 13dd01aae7..0000000000 --- a/public/language/en-x-pirate/admin/general/navigation.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "icon": "Icon:", - "change-icon": "change", - "route": "Route:", - "tooltip": "Tooltip:", - "text": "Text:", - "text-class": "Text Class: optional", - "class": "Class: optional", - "id": "ID: optional", - - "properties": "Properties:", - "groups": "Groups:", - "open-new-window": "Open in a new window", - - "btn.delete": "Delete", - "btn.disable": "Disable", - "btn.enable": "Enable", - - "available-menu-items": "Available Menu Items", - "custom-route": "Custom Route", - "core": "core", - "plugin": "plugin" -} \ No newline at end of file diff --git a/public/language/en-x-pirate/admin/general/social.json b/public/language/en-x-pirate/admin/general/social.json deleted file mode 100644 index 23aedfcfaa..0000000000 --- a/public/language/en-x-pirate/admin/general/social.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "post-sharing": "Post Sharing", - "info-plugins-additional": "Plugins can add additional networks for sharing posts.", - "save-success": "Successfully saved Post Sharing Networks!" -} \ No newline at end of file diff --git a/public/language/en-x-pirate/admin/general/sounds.json b/public/language/en-x-pirate/admin/general/sounds.json deleted file mode 100644 index 95ccbde0f1..0000000000 --- a/public/language/en-x-pirate/admin/general/sounds.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "notifications": "Notifications", - "chat-messages": "Chat Messages", - "play-sound": "Play", - "incoming-message": "Incoming Message", - "outgoing-message": "Outgoing Message", - "upload-new-sound": "Upload New Sound", - "saved": "Settings Saved" -} \ No newline at end of file diff --git a/public/language/en-x-pirate/admin/manage/digest.json b/public/language/en-x-pirate/admin/manage/digest.json index 8f3661698a..38c634d1f6 100644 --- a/public/language/en-x-pirate/admin/manage/digest.json +++ b/public/language/en-x-pirate/admin/manage/digest.json @@ -13,6 +13,7 @@ "resent-single": "Manual digest resend completed", "resent-day": "Daily digest resent", "resent-week": "Weekly digest resent", + "resent-biweek": "Bi-Weekly digest resent", "resent-month": "Monthly digest resent", "null": "Never", "manual-run": "Manual digest run:", diff --git a/public/language/en-x-pirate/admin/manage/users.json b/public/language/en-x-pirate/admin/manage/users.json index 38a065b954..2a3c0c4829 100644 --- a/public/language/en-x-pirate/admin/manage/users.json +++ b/public/language/en-x-pirate/admin/manage/users.json @@ -47,6 +47,7 @@ "users.uid": "uid", "users.username": "username", "users.email": "email", + "users.no-email": "(no email)", "users.ip": "IP", "users.postcount": "postcount", "users.reputation": "reputation", diff --git a/public/language/en-x-pirate/admin/menu.json b/public/language/en-x-pirate/admin/menu.json index 1c5ea73b4f..5b22fbeb36 100644 --- a/public/language/en-x-pirate/admin/menu.json +++ b/public/language/en-x-pirate/admin/menu.json @@ -4,6 +4,7 @@ "dashboard/logins": "Logins", "dashboard/users": "Users", "dashboard/topics": "Topics", + "dashboard/searches": "Searches", "section-general": "General", "section-manage": "Manage", diff --git a/public/language/en-x-pirate/admin/settings/email.json b/public/language/en-x-pirate/admin/settings/email.json index 9bdbde26a6..65f579d28c 100644 --- a/public/language/en-x-pirate/admin/settings/email.json +++ b/public/language/en-x-pirate/admin/settings/email.json @@ -6,7 +6,7 @@ "from-help": "The from name to display in the email.", "smtp-transport": "SMTP Transport", - "smtp-transport.enabled": "Use an external email server to send emails", + "smtp-transport.enabled": "Enable SMTP Transport", "smtp-transport-help": "You can select from a list of well-known services or enter a custom one.", "smtp-transport.service": "Select a service", "smtp-transport.service-custom": "Custom Service", @@ -37,6 +37,10 @@ "subscriptions.hour": "Digest Hour", "subscriptions.hour-help": "Please enter a number representing the hour to send scheduled email digests (e.g. 0 for midnight, 17 for 5:00pm). Keep in mind that this is the hour according to the server itself, and may not exactly match your system clock.
The approximate server time is:
The next daily digest is scheduled to be sent ", "notifications.remove-images": "Remove images from email notifications", + "require-email-address": "Require new users to specify an email address", + "require-email-address-warning": "By default, users can opt-out of entering an email address. Enabling this option means they have to enter an email address in order to proceed with registration. It does not ensure user will enter a real email address, nor even an address they own.", "include-unverified-emails": "Send emails to recipients who have not explicitly confirmed their emails", - "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws." -} \ No newline at end of file + "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws.", + "prompt": "Prompt users to enter or confirm their emails", + "prompt-help": "If a user does not have an email set, or their email is not confirmed, a warning will be shown on screen." +} diff --git a/public/language/en-x-pirate/admin/settings/general.json b/public/language/en-x-pirate/admin/settings/general.json index be7df90870..29b939861b 100644 --- a/public/language/en-x-pirate/admin/settings/general.json +++ b/public/language/en-x-pirate/admin/settings/general.json @@ -3,9 +3,9 @@ "title": "Site Title", "title.short": "Short Title", "title.short-placeholder": "If no short title is specified, the site title will be used", - "title.url": "URL", + "title.url": "Title Link URL", "title.url-placeholder": "The URL of the site title", - "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.", + "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "title.name": "Your Community Name", "title.show-in-header": "Show Site Title in Header", "browser-title": "Browser Title", @@ -20,9 +20,9 @@ "logo.image": "Image", "logo.image-placeholder": "Path to a logo to display on forum header", "logo.upload": "Upload", - "logo.url": "URL", + "logo.url": "Logo Link URL", "logo.url-placeholder": "The URL of the site logo", - "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.", + "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "logo.alt-text": "Alt Text", "log.alt-text-placeholder": "Alternative text for accessibility", "favicon": "Favicon", @@ -47,4 +47,4 @@ "undo-timeout": "Undo Timeout", "undo-timeout-help": "Some operations such as moving topics will allow for the moderator to undo their action within a certain timeframe. Set to 0 to disable undo completely.", "topic-tools": "Topic Tools" -} \ No newline at end of file +} diff --git a/public/language/en-x-pirate/admin/settings/navigation.json b/public/language/en-x-pirate/admin/settings/navigation.json index 13dd01aae7..7baca85096 100644 --- a/public/language/en-x-pirate/admin/settings/navigation.json +++ b/public/language/en-x-pirate/admin/settings/navigation.json @@ -11,6 +11,8 @@ "properties": "Properties:", "groups": "Groups:", "open-new-window": "Open in a new window", + "dropdown": "Dropdown", + "dropdown-placeholder": "Place your dropdown menu items below, ie:
<li><a href="https://myforum.com">Link 1</a></li>", "btn.delete": "Delete", "btn.disable": "Disable", @@ -20,4 +22,4 @@ "custom-route": "Custom Route", "core": "core", "plugin": "plugin" -} \ No newline at end of file +} diff --git a/public/language/en-x-pirate/admin/settings/notifications.json b/public/language/en-x-pirate/admin/settings/notifications.json index da6c9680a3..c6d8b928ce 100644 --- a/public/language/en-x-pirate/admin/settings/notifications.json +++ b/public/language/en-x-pirate/admin/settings/notifications.json @@ -2,5 +2,6 @@ "notifications": "Notifications", "welcome-notification": "Welcome Notification", "welcome-notification-link": "Welcome Notification Link", - "welcome-notification-uid": "Welcome Notification User (UID)" + "welcome-notification-uid": "Welcome Notification User (UID)", + "post-queue-notification-uid": "Post Queue User (UID)" } \ No newline at end of file diff --git a/public/language/en-x-pirate/admin/settings/post.json b/public/language/en-x-pirate/admin/settings/post.json index 27493aafbd..ab8245738c 100644 --- a/public/language/en-x-pirate/admin/settings/post.json +++ b/public/language/en-x-pirate/admin/settings/post.json @@ -40,6 +40,7 @@ "teaser.last-post": "Last – Show the latest post, including the original post, if no replies", "teaser.last-reply": "Last – Show the latest reply, or a \"No replies\" placeholder if no replies", "teaser.first": "First", + "showPostPreviewsOnHover": "Show a preview of posts when mouse overed", "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", @@ -56,6 +57,9 @@ "composer.show-help": "Show \"Help\" tab", "composer.enable-plugin-help": "Allow plugins to add content to the help tab", "composer.custom-help": "Custom Help Text", + "backlinks": "Backlinks", + "backlinks.enabled": "Enable topic backlinks", + "backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.", "ip-tracking": "IP Tracking", "ip-tracking.each-post": "Track IP Address for each post", "enable-post-history": "Enable Post History" diff --git a/public/language/en-x-pirate/admin/settings/user.json b/public/language/en-x-pirate/admin/settings/user.json index 48be13b75e..7923bf8cbe 100644 --- a/public/language/en-x-pirate/admin/settings/user.json +++ b/public/language/en-x-pirate/admin/settings/user.json @@ -71,6 +71,7 @@ "digest-freq.off": "Off", "digest-freq.daily": "Daily", "digest-freq.weekly": "Weekly", + "digest-freq.biweekly": "Bi-Weekly", "digest-freq.monthly": "Monthly", "email-chat-notifs": "Send an email if a new chat message arrives and I am not online", "email-post-notif": "Send an email when replies are made to topics I am subscribed to", diff --git a/public/language/en-x-pirate/error.json b/public/language/en-x-pirate/error.json index b806ec9367..6eb0983661 100644 --- a/public/language/en-x-pirate/error.json +++ b/public/language/en-x-pirate/error.json @@ -34,8 +34,9 @@ "email-invited": "Email was already invited", "email-not-confirmed": "Posting in some categories or topics is enabled once your email is confirmed, please click here to send a confirmation email.", "email-not-confirmed-chat": "You are unable to chat until your email is confirmed, please click here to confirm your email.", - "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You won't be able to post or chat until your email is confirmed.", - "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery. Please click here to enter an email.", + "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You may not be able to post in some categories or chat until your email is confirmed.", + "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery, and may be necessary for chatting and posting in some categories. Please click here to enter an email.", + "user-doesnt-have-email": "User \"%1\" does not have an email set.", "email-confirm-failed": "We could not confirm your email, please try again later.", "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", "sendmail-not-found": "The sendmail executable could not be found, please ensure it is installed and executable by the user running NodeBB.", @@ -103,6 +104,7 @@ "already-bookmarked": "You have already bookmarked this post", "already-unbookmarked": "You have already unbookmarked this post", "cant-ban-other-admins": "You can't ban other admins!", + "cant-make-banned-users-admin": "You can't make banned users admin.", "cant-remove-last-admin": "You are the only administrator. Add another user as an administrator before removing yourself as admin", "account-deletion-disabled": "Account deletion is disabled", "cant-delete-admin": "Remove administrator privileges from this account before attempting to delete it.", diff --git a/public/language/en-x-pirate/modules.json b/public/language/en-x-pirate/modules.json index 17783a7fff..a537668b0b 100644 --- a/public/language/en-x-pirate/modules.json +++ b/public/language/en-x-pirate/modules.json @@ -54,7 +54,7 @@ "composer.formatting.strikethrough": "Strikethrough", "composer.formatting.code": "Code", "composer.formatting.link": "Link", - "composer.formatting.picture": "Picture", + "composer.formatting.picture": "Image Link", "composer.upload-picture": "Upload Image", "composer.upload-file": "Upload File", "composer.zen_mode": "Zen Mode", diff --git a/public/language/en-x-pirate/top.json b/public/language/en-x-pirate/top.json new file mode 100644 index 0000000000..b8a05bfa5f --- /dev/null +++ b/public/language/en-x-pirate/top.json @@ -0,0 +1,4 @@ +{ + "title": "Top", + "no_top_topics": "No top topics" +} \ No newline at end of file diff --git a/public/language/en-x-pirate/topic.json b/public/language/en-x-pirate/topic.json index c0c7610a61..ca723b72f6 100644 --- a/public/language/en-x-pirate/topic.json +++ b/public/language/en-x-pirate/topic.json @@ -47,6 +47,7 @@ "restored-by": "Restored by", "moved-from-by": "Moved from %1 by", "queued-by": "Post queued for approval →", + "backlink": "Referenced by", "bookmark_instructions": "Click here to return to the last read post in this thread.", "flag-post": "Flag this post", "flag-user": "Flag this user", @@ -138,6 +139,7 @@ "composer.handle_placeholder": "Enter your name/handle here", "composer.discard": "Discard", "composer.submit": "Submit", + "composer.additional-options": "Additional Options", "composer.schedule": "Schedule", "composer.replying_to": "Replying to %1", "composer.new_topic": "New Topic", @@ -158,6 +160,7 @@ "newest_to_oldest": "Newest to Oldest", "most_votes": "Most Votes", "most_posts": "Most Posts", + "most_views": "Most Views", "stale.title": "Create new topic instead?", "stale.warning": "The topic you are replying to is quite old. Would you like to create a new topic instead, and reference this one in your reply?", "stale.create": "Create a new topic", diff --git a/public/language/en-x-pirate/user.json b/public/language/en-x-pirate/user.json index 2f919dfedb..91dc6ba8f8 100644 --- a/public/language/en-x-pirate/user.json +++ b/public/language/en-x-pirate/user.json @@ -94,6 +94,7 @@ "digest_off": "Off", "digest_daily": "Daily", "digest_weekly": "Weekly", + "digest_biweekly": "Bi-Weekly", "digest_monthly": "Monthly", "has_no_follower": "This user doesn't have any followers :(", "follows_no_one": "This user isn't following anyone :(", diff --git a/public/language/es/admin/advanced/events.json b/public/language/es/admin/advanced/events.json index 860f8f87a9..ec26f7c11c 100644 --- a/public/language/es/admin/advanced/events.json +++ b/public/language/es/admin/advanced/events.json @@ -3,6 +3,7 @@ "no-events": "No hay eventos", "control-panel": "Panel de control de eventos", "delete-events": "Delete Events", + "confirm-delete-all-events": "Are you sure you want to delete all logged events?", "filters": "Filtros", "filters-apply": "Aplicar filtros", "filter-type": "Tipo de evento", diff --git a/public/language/es/admin/dashboard.json b/public/language/es/admin/dashboard.json index 7595d4a563..a1d11fab48 100644 --- a/public/language/es/admin/dashboard.json +++ b/public/language/es/admin/dashboard.json @@ -56,8 +56,8 @@ "active-users.total": "Total", "active-users.connections": "Conexiones", - "anonymous-registered-users": "Usuarios Anónimos vs Registrados", - "anonymous": "Anónimos", + "guest-registered-users": "Guest vs Registered Users", + "guest": "Guest", "registered": "Registrados", "user-presence": "Presencia del Usuario", @@ -68,6 +68,7 @@ "unread": "Sin Leer", "high-presence-topics": "Temas con Alta Presencia", + "popular-searches": "Popular Searches", "graphs.page-views": "Vista de la Pagina", "graphs.page-views-registered": "Vistas de la página registradas", @@ -75,13 +76,14 @@ "graphs.page-views-bot": "Vistas de la página Bot", "graphs.unique-visitors": "Visitantes Unicos", "graphs.registered-users": "Usuarios Registrados", - "graphs.anonymous-users": "Usuarios Anónimos", + "graphs.guest-users": "Guest Users", "last-restarted-by": "Reiniciado por última vez por", "no-users-browsing": "No hay usuarios explorando", "back-to-dashboard": "Back to Dashboard", "details.no-users": "No users have joined within the selected timeframe", "details.no-topics": "No topics have been posted within the selected timeframe", + "details.no-searches": "No searches have been made yet", "details.no-logins": "No logins have been recorded within the selected timeframe", "details.logins-static": "NodeBB only saves session data for %1 days, and so this table below will only show the most recently active sessions", "details.logins-login-time": "Login Time" diff --git a/public/language/es/admin/development/info.json b/public/language/es/admin/development/info.json index 385c3a56a9..5f934d3a40 100644 --- a/public/language/es/admin/development/info.json +++ b/public/language/es/admin/development/info.json @@ -8,7 +8,11 @@ "nodejs": "nodejs", "online": "en-linea", "git": "git", - "memory": "memoria", + "process-memory": "process memory", + "system-memory": "system memory", + "used-memory-process": "Used memory by process", + "used-memory-os": "Used system memory", + "total-memory-os": "Total system memory", "load": "carga del sistema", "cpu-usage": "uso del cpu", "uptime": "tiempo de actividad", diff --git a/public/language/es/admin/extend/rewards.json b/public/language/es/admin/extend/rewards.json index 44eb3f7108..98ded7cfa7 100644 --- a/public/language/es/admin/extend/rewards.json +++ b/public/language/es/admin/extend/rewards.json @@ -8,8 +8,6 @@ "delete": "Eliminar", "enable": "Habilitar", "disable": "Deshabilitar", - "control-panel": "Control de recompensas", - "new-reward": "Nueva Recompensa", "alert.delete-success": "Recompensa eliminada con éxito", "alert.no-inputs-found": "¡Recompensa ilegal - no se encontraron inputs!", diff --git a/public/language/es/admin/general/dashboard.json b/public/language/es/admin/general/dashboard.json deleted file mode 100644 index 71d4d82a3a..0000000000 --- a/public/language/es/admin/general/dashboard.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "forum-traffic": "Trafico del Foro", - "page-views": "Vistas de la Pagina", - "unique-visitors": "Visitantes Unicos", - "new-users": "New Users", - "posts": "Publicación", - "topics": "Temas", - "page-views-seven": "Últimos 7 Días", - "page-views-thirty": "Últimos 30 Días", - "page-views-last-day": "Últimas 24 horas", - "page-views-custom": "Rango de Fechas Personalizado", - "page-views-custom-start": "Comienzo del Rango", - "page-views-custom-end": "Final del Rango", - "page-views-custom-help": "Introduce un rango de fechas para las vistas de página que deseas ver. Si no hay ningún selector de fechas disponible, el formato aceptado es AAAA-MM-DD", - "page-views-custom-error": "Por favor, introduce un rango de fechas válido en el formato AAAA-MM-DD", - - "stats.yesterday": "Yesterday", - "stats.today": "Today", - "stats.last-week": "Last Week", - "stats.this-week": "This Week", - "stats.last-month": "Last Month", - "stats.this-month": "This Month", - "stats.all": "Todos los Tiempos", - - "updates": "Actualizaciones", - "running-version": "Estas ejecutando NodeBB v%1.", - "keep-updated": "Asegúrate que tu NodeBB este al día en los últimos parches de seguridad y actualizaciones.", - "up-to-date": "

Estásactualizado/a

", - "upgrade-available": "

Una versión nueva (v%1) ha sido publicada. Consideraactualizar NodeBB.

", - "prerelease-upgrade-available": "

Esta es una versión pre-publicación anticuada. Una versión nueva(v%1) ha sido publicada. Consideraactualizar NodeBB.

", - "prerelease-warning": "

Esta es una versión depre-lanzamiento de NodeBB. Algunas fallas pueden ocurrir.

", - "running-in-development": "Forum esta siendo ejecutado en modo de desarrollador. El foro puede estar abierto a vulnerabilidades potenciales; por favor contacta tu administrador del sistema.", - "latest-lookup-failed": "

No se pudo encontrar la última versión disponible de NodeBB

", - - "notices": "Noticias", - "restart-not-required": "No se require reiniciar.", - "restart-required": "Se requiere reiniciar", - "search-plugin-installed": "El plug-in de búsqueda esta instalado.", - "search-plugin-not-installed": "El plug-in de busqueda no esta instalado", - "search-plugin-tooltip": "Instala el plug-in de búsqueda desde la pagina de plugins para activar esta funcionalidad.", - - "control-panel": "Control del Systema", - "rebuild-and-restart": "Reconstruye & Reinicia", - "restart": "Reinicia", - "restart-warning": "Reconstruir o Reiniciar tu NodeBB cerrará todas las conexiones por unos segundos.", - "restart-disabled": "Reconstruir y Reiniciar tu NodeBB ha sido deshabilitado, ya que parece que no lo estás ejecutando desde el daemon adecuado.", - "maintenance-mode": "Modo de Mantenimiento", - "maintenance-mode-title": "Haz clic aquí para activar el modo de mantenimiento de NodeBB", - "realtime-chart-updates": "Actualizar el Grafo en Tiempo Real", - - "active-users": "Usuarios Activos", - "active-users.users": "Usuarios", - "active-users.guests": "Invitados", - "active-users.total": "Total", - "active-users.connections": "Conexiones", - - "anonymous-registered-users": "Usuarios Anónimos vs Registrados", - "anonymous": "Anónimos", - "registered": "Registrados", - - "user-presence": "Presencia del Usuario", - "on-categories": "Listado en Categorias", - "reading-posts": "Leer entradas", - "browsing-topics": "Explorar temas", - "recent": "Recientes", - "unread": "Sin Leer", - - "high-presence-topics": "Temas con Alta Presencia", - - "graphs.page-views": "Vista de la Pagina", - "graphs.page-views-registered": "Vistas de la página registradas", - "graphs.page-views-guest": "Vistas de la página visitantes", - "graphs.page-views-bot": "Vistas de la página Bot", - "graphs.unique-visitors": "Visitantes Unicos", - "graphs.registered-users": "Usuarios Registrados", - "graphs.anonymous-users": "Usuarios Anónimos", - "last-restarted-by": "Reiniciado por última vez por", - "no-users-browsing": "No hay usuarios explorando" -} diff --git a/public/language/es/admin/general/homepage.json b/public/language/es/admin/general/homepage.json deleted file mode 100644 index 178a101aa5..0000000000 --- a/public/language/es/admin/general/homepage.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "home-page": "Página Principal", - "description": "Escoge que pagina se muestra cuando los usuarios navegan en la raíz del foro.", - "home-page-route": "Ruta de la Pagina Principal", - "custom-route": "Ruta Personalizada", - "allow-user-home-pages": "Permitir Pagina de Perfil del Usuario", - "home-page-title": "Título de la página de inicio (por defecto, \"Home\" o \"Inicio\")" -} \ No newline at end of file diff --git a/public/language/es/admin/general/languages.json b/public/language/es/admin/general/languages.json deleted file mode 100644 index df6d3843e5..0000000000 --- a/public/language/es/admin/general/languages.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "language-settings": "Configuración de Idiomas", - "description": "El idioma por defecto determina la configuración del idioma usado para todos los usuarios que visiten el foro.
Los usuarios, a nivel individual, pueden sobreescribir el idioma por defecto en la página de configuración de su cuenta.", - "default-language": "Idioma por defecto", - "auto-detect": "Auto Detectar Configuración de Idioma para Visitantes" -} \ No newline at end of file diff --git a/public/language/es/admin/general/navigation.json b/public/language/es/admin/general/navigation.json deleted file mode 100644 index 22cad76ef8..0000000000 --- a/public/language/es/admin/general/navigation.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "icon": "Icono:", - "change-icon": "cambio", - "route": "Ruta:", - "tooltip": "Nota de ayuda:", - "text": "Texto:", - "text-class": "Clase de Texto: opcional", - "class": "Clase opcional", - "id": "ID: opcional", - - "properties": "Propiedades:", - "groups": "Grupos:", - "open-new-window": "Abrir en una ventana nueva", - - "btn.delete": "Borrar", - "btn.disable": "Deshabilitar", - "btn.enable": "Habilitar", - - "available-menu-items": "Items de Menú Disponibles", - "custom-route": "Ruta Personalizada:", - "core": "núcleo", - "plugin": "plugin" -} \ No newline at end of file diff --git a/public/language/es/admin/general/social.json b/public/language/es/admin/general/social.json deleted file mode 100644 index b9a67b4758..0000000000 --- a/public/language/es/admin/general/social.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "post-sharing": "Compartir entradas", - "info-plugins-additional": "Los plugins pueden añadir redes adicionales para compartir entradas/respuestas.", - "save-success": "¡Redes de Compartir Entradas salvadas con éxito!" -} \ No newline at end of file diff --git a/public/language/es/admin/general/sounds.json b/public/language/es/admin/general/sounds.json deleted file mode 100644 index 4635433b80..0000000000 --- a/public/language/es/admin/general/sounds.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "notifications": "Notificaciones", - "chat-messages": "Mensajes de Chat", - "play-sound": "Reproducir", - "incoming-message": "Mensaje Entrante", - "outgoing-message": "Mensaje Saliente", - "upload-new-sound": "Subir Sonido Nuevo", - "saved": "Configuración Guardada" -} \ No newline at end of file diff --git a/public/language/es/admin/manage/digest.json b/public/language/es/admin/manage/digest.json index 8f3661698a..38c634d1f6 100644 --- a/public/language/es/admin/manage/digest.json +++ b/public/language/es/admin/manage/digest.json @@ -13,6 +13,7 @@ "resent-single": "Manual digest resend completed", "resent-day": "Daily digest resent", "resent-week": "Weekly digest resent", + "resent-biweek": "Bi-Weekly digest resent", "resent-month": "Monthly digest resent", "null": "Never", "manual-run": "Manual digest run:", diff --git a/public/language/es/admin/manage/users.json b/public/language/es/admin/manage/users.json index e13e9eef40..219bb7e580 100644 --- a/public/language/es/admin/manage/users.json +++ b/public/language/es/admin/manage/users.json @@ -47,6 +47,7 @@ "users.uid": "uid", "users.username": "nombre de usuario", "users.email": "email", + "users.no-email": "(no email)", "users.ip": "IP", "users.postcount": "cantidad de posts", "users.reputation": "reputación", diff --git a/public/language/es/admin/menu.json b/public/language/es/admin/menu.json index 6fa9453a06..d9e2ce06b3 100644 --- a/public/language/es/admin/menu.json +++ b/public/language/es/admin/menu.json @@ -4,6 +4,7 @@ "dashboard/logins": "Logins", "dashboard/users": "Users", "dashboard/topics": "Topics", + "dashboard/searches": "Searches", "section-general": "General", "section-manage": "Administrar", diff --git a/public/language/es/admin/settings/email.json b/public/language/es/admin/settings/email.json index 9bdbde26a6..65f579d28c 100644 --- a/public/language/es/admin/settings/email.json +++ b/public/language/es/admin/settings/email.json @@ -6,7 +6,7 @@ "from-help": "The from name to display in the email.", "smtp-transport": "SMTP Transport", - "smtp-transport.enabled": "Use an external email server to send emails", + "smtp-transport.enabled": "Enable SMTP Transport", "smtp-transport-help": "You can select from a list of well-known services or enter a custom one.", "smtp-transport.service": "Select a service", "smtp-transport.service-custom": "Custom Service", @@ -37,6 +37,10 @@ "subscriptions.hour": "Digest Hour", "subscriptions.hour-help": "Please enter a number representing the hour to send scheduled email digests (e.g. 0 for midnight, 17 for 5:00pm). Keep in mind that this is the hour according to the server itself, and may not exactly match your system clock.
The approximate server time is:
The next daily digest is scheduled to be sent ", "notifications.remove-images": "Remove images from email notifications", + "require-email-address": "Require new users to specify an email address", + "require-email-address-warning": "By default, users can opt-out of entering an email address. Enabling this option means they have to enter an email address in order to proceed with registration. It does not ensure user will enter a real email address, nor even an address they own.", "include-unverified-emails": "Send emails to recipients who have not explicitly confirmed their emails", - "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws." -} \ No newline at end of file + "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws.", + "prompt": "Prompt users to enter or confirm their emails", + "prompt-help": "If a user does not have an email set, or their email is not confirmed, a warning will be shown on screen." +} diff --git a/public/language/es/admin/settings/general.json b/public/language/es/admin/settings/general.json index 4dade0a080..3ddadb3079 100644 --- a/public/language/es/admin/settings/general.json +++ b/public/language/es/admin/settings/general.json @@ -3,9 +3,9 @@ "title": "Título del Sitio", "title.short": "Short Title", "title.short-placeholder": "If no short title is specified, the site title will be used", - "title.url": "URL", + "title.url": "Title Link URL", "title.url-placeholder": "La URL del título del sitio", - "title.url-help": "Cuando se hace click en el título, enviar a los usuarios a esta dirección. Si se deja en blanco, el usuario será enviado al índice del foro.", + "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "title.name": "Nombre de tu Comunidad", "title.show-in-header": "Mostrar Título del Sitio en el Encabezado", "browser-title": "Título del Navegador", @@ -20,9 +20,9 @@ "logo.image": "Imagen", "logo.image-placeholder": "Ruta al logo que se mostrará en la cabecera del foro", "logo.upload": "Subir", - "logo.url": "URL", + "logo.url": "Logo Link URL", "logo.url-placeholder": "la URL del logo del sitio", - "logo.url-help": "Cuando se hace click en el logo, enviar los usuarios a esta dirección. Si se deja en blanco, el usuario será enviado al índice del foro.", + "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "logo.alt-text": "Texto alternativo (alt text)", "log.alt-text-placeholder": "Texto alternativo para accesibilidad", "favicon": "Favicon", @@ -47,4 +47,4 @@ "undo-timeout": "Undo Timeout", "undo-timeout-help": "Some operations such as moving topics will allow for the moderator to undo their action within a certain timeframe. Set to 0 to disable undo completely.", "topic-tools": "Topic Tools" -} \ No newline at end of file +} diff --git a/public/language/es/admin/settings/navigation.json b/public/language/es/admin/settings/navigation.json index 22cad76ef8..3b28dd115a 100644 --- a/public/language/es/admin/settings/navigation.json +++ b/public/language/es/admin/settings/navigation.json @@ -11,6 +11,8 @@ "properties": "Propiedades:", "groups": "Grupos:", "open-new-window": "Abrir en una ventana nueva", + "dropdown": "Dropdown", + "dropdown-placeholder": "Place your dropdown menu items below, ie:
<li><a href="https://myforum.com">Link 1</a></li>", "btn.delete": "Borrar", "btn.disable": "Deshabilitar", @@ -20,4 +22,4 @@ "custom-route": "Ruta Personalizada:", "core": "núcleo", "plugin": "plugin" -} \ No newline at end of file +} diff --git a/public/language/es/admin/settings/notifications.json b/public/language/es/admin/settings/notifications.json index 4c327c1085..8d2509d75b 100644 --- a/public/language/es/admin/settings/notifications.json +++ b/public/language/es/admin/settings/notifications.json @@ -2,5 +2,6 @@ "notifications": "Notificaciones", "welcome-notification": "Notificación de Bienvenida", "welcome-notification-link": "Enlace de Notificación de Bienvenida", - "welcome-notification-uid": "Usuario de Notificación de Bienvenida (UID)" + "welcome-notification-uid": "Usuario de Notificación de Bienvenida (UID)", + "post-queue-notification-uid": "Post Queue User (UID)" } \ No newline at end of file diff --git a/public/language/es/admin/settings/post.json b/public/language/es/admin/settings/post.json index 8525d35a08..eec90ff156 100644 --- a/public/language/es/admin/settings/post.json +++ b/public/language/es/admin/settings/post.json @@ -40,6 +40,7 @@ "teaser.last-post": "Último – Muestra la última entrada, incluyendo la entrada original, si no hay respuestas.", "teaser.last-reply": "Última – Muestra la última respuesta, o un texto \"No hay respuestas\" si no hay respuestas.", "teaser.first": "Primera", + "showPostPreviewsOnHover": "Show a preview of posts when mouse overed", "unread": "Configuraciones sin leer", "unread.cutoff": "Días límite sin leer", "unread.min-track-last": "Entradas mínimas en un tema antes de indicar la última leída.", @@ -56,6 +57,9 @@ "composer.show-help": "Mostrar pestaña \"Ayuda\"", "composer.enable-plugin-help": "Permitir a plugins añadir contenido a la pestaña de ayuda", "composer.custom-help": "Texto de Ayuda Personalizado", + "backlinks": "Backlinks", + "backlinks.enabled": "Enable topic backlinks", + "backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.", "ip-tracking": "Seguimiento de IP", "ip-tracking.each-post": "Seguir la IP para cada entrada/respuesta", "enable-post-history": "Activar historial de respuestas" diff --git a/public/language/es/admin/settings/user.json b/public/language/es/admin/settings/user.json index d460890735..a520898501 100644 --- a/public/language/es/admin/settings/user.json +++ b/public/language/es/admin/settings/user.json @@ -71,6 +71,7 @@ "digest-freq.off": "Apagado", "digest-freq.daily": "Diario", "digest-freq.weekly": "Semanal", + "digest-freq.biweekly": "Bi-Weekly", "digest-freq.monthly": "Mensual", "email-chat-notifs": "Enviar un correo electrónico si un mensaje de chat nuevo llega y no estoy conectado/a", "email-post-notif": "Enviar un correo electrónico cuando se hacen respuestas a temas a los que estoy suscrito/a", diff --git a/public/language/es/error.json b/public/language/es/error.json index af0ec0569b..52f3fb57af 100644 --- a/public/language/es/error.json +++ b/public/language/es/error.json @@ -34,8 +34,9 @@ "email-invited": "Email was already invited", "email-not-confirmed": "Posting in some categories or topics is enabled once your email is confirmed, please click here to send a confirmation email.", "email-not-confirmed-chat": "No puedes usar el chat hasta que confirmes tu dirección de correo electrónico, por favor haz click aquí para confirmar tu correo.", - "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You won't be able to post or chat until your email is confirmed.", - "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery. Please click here to enter an email.", + "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You may not be able to post in some categories or chat until your email is confirmed.", + "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery, and may be necessary for chatting and posting in some categories. Please click here to enter an email.", + "user-doesnt-have-email": "User \"%1\" does not have an email set.", "email-confirm-failed": "No se ha podido confirmar su email, por favor inténtelo de nuevo más tarde.", "confirm-email-already-sent": "El email de confirmación ya ha sido enviado, por favor espera %1 minuto(s) para enviar otro.", "sendmail-not-found": "El ejecutable \"sendmail\" no ha sido encontrado, por favor asegúrate de que esta instalado en tu sistema y es accesible por el usuario que ejecuta NodeBB. ", @@ -103,6 +104,7 @@ "already-bookmarked": "Ya marcaste este mensaje", "already-unbookmarked": "Ya desmarcarste este mensaje", "cant-ban-other-admins": "¡No puedes expulsar a otros administradores!", + "cant-make-banned-users-admin": "You can't make banned users admin.", "cant-remove-last-admin": "Tu eres el unico administrador. Añade otro usuario como administrador antes de eliminarte a ti mismo.", "account-deletion-disabled": "Account deletion is disabled", "cant-delete-admin": "Quitar privilegios de administrador de ésta cuenta antes de intentar borrarla", diff --git a/public/language/es/modules.json b/public/language/es/modules.json index cc274d90f0..8aae83b906 100644 --- a/public/language/es/modules.json +++ b/public/language/es/modules.json @@ -54,7 +54,7 @@ "composer.formatting.strikethrough": "Tachado", "composer.formatting.code": "Código", "composer.formatting.link": "Enlace", - "composer.formatting.picture": "Foto", + "composer.formatting.picture": "Image Link", "composer.upload-picture": "Subir foto", "composer.upload-file": "Subir archivo", "composer.zen_mode": "Modo Zen", diff --git a/public/language/es/top.json b/public/language/es/top.json new file mode 100644 index 0000000000..b8a05bfa5f --- /dev/null +++ b/public/language/es/top.json @@ -0,0 +1,4 @@ +{ + "title": "Top", + "no_top_topics": "No top topics" +} \ No newline at end of file diff --git a/public/language/es/topic.json b/public/language/es/topic.json index 092fcbd3aa..6e15a937f8 100644 --- a/public/language/es/topic.json +++ b/public/language/es/topic.json @@ -47,6 +47,7 @@ "restored-by": "Restaurado por", "moved-from-by": "Moved from %1 by", "queued-by": "Post queued for approval →", + "backlink": "Referenced by", "bookmark_instructions": "Haz click aquí para volver a tu último mensaje leído en este tema", "flag-post": "Flag this post", "flag-user": "Flag this user", @@ -138,6 +139,7 @@ "composer.handle_placeholder": "Enter your name/handle here", "composer.discard": "Descartar", "composer.submit": "Enviar", + "composer.additional-options": "Additional Options", "composer.schedule": "Schedule", "composer.replying_to": "En respuesta a %1", "composer.new_topic": "Nuevo tema", @@ -158,6 +160,7 @@ "newest_to_oldest": "Más nuevo a más antiguo", "most_votes": "Mayor número de Votos", "most_posts": "Mayor número de Posts", + "most_views": "Most Views", "stale.title": "¿Crear un nuevo hilo en su lugar?", "stale.warning": "El hilo al que estás respondiendo es muy antiguo. ¿Quieres crear un nuevo hilo en su lugar y añadir una referencia a este en tu mensaje?", "stale.create": "Crear un nuevo hilo", diff --git a/public/language/es/user.json b/public/language/es/user.json index 30c9631be4..53926b7c83 100644 --- a/public/language/es/user.json +++ b/public/language/es/user.json @@ -94,6 +94,7 @@ "digest_off": "Apagado", "digest_daily": "Diariamente", "digest_weekly": "Semanalmente", + "digest_biweekly": "Bi-Weekly", "digest_monthly": "Mensualmente", "has_no_follower": "Este usuario no tiene seguidores :(", "follows_no_one": "Este miembro no sigue a nadie :(", diff --git a/public/language/et/admin/advanced/events.json b/public/language/et/admin/advanced/events.json index 39eaf199f0..2b0df63877 100644 --- a/public/language/et/admin/advanced/events.json +++ b/public/language/et/admin/advanced/events.json @@ -3,6 +3,7 @@ "no-events": "Sündmused puuduvad", "control-panel": "Sündmuste kontrollpaneel", "delete-events": "Delete Events", + "confirm-delete-all-events": "Are you sure you want to delete all logged events?", "filters": "Filters", "filters-apply": "Apply Filters", "filter-type": "Event Type", diff --git a/public/language/et/admin/dashboard.json b/public/language/et/admin/dashboard.json index 0de31d4917..4d39626882 100644 --- a/public/language/et/admin/dashboard.json +++ b/public/language/et/admin/dashboard.json @@ -56,8 +56,8 @@ "active-users.total": "Total", "active-users.connections": "Connections", - "anonymous-registered-users": "Anonymous vs Registered Users", - "anonymous": "Anonymous", + "guest-registered-users": "Guest vs Registered Users", + "guest": "Guest", "registered": "Registered", "user-presence": "User Presence", @@ -68,6 +68,7 @@ "unread": "Unread", "high-presence-topics": "High Presence Topics", + "popular-searches": "Popular Searches", "graphs.page-views": "Page Views", "graphs.page-views-registered": "Page Views Registered", @@ -75,13 +76,14 @@ "graphs.page-views-bot": "Page Views Bot", "graphs.unique-visitors": "Unique Visitors", "graphs.registered-users": "Registered Users", - "graphs.anonymous-users": "Anonymous Users", + "graphs.guest-users": "Guest Users", "last-restarted-by": "Last restarted by", "no-users-browsing": "No users browsing", "back-to-dashboard": "Back to Dashboard", "details.no-users": "No users have joined within the selected timeframe", "details.no-topics": "No topics have been posted within the selected timeframe", + "details.no-searches": "No searches have been made yet", "details.no-logins": "No logins have been recorded within the selected timeframe", "details.logins-static": "NodeBB only saves session data for %1 days, and so this table below will only show the most recently active sessions", "details.logins-login-time": "Login Time" diff --git a/public/language/et/admin/development/info.json b/public/language/et/admin/development/info.json index 1003af1a5f..11202d9c3a 100644 --- a/public/language/et/admin/development/info.json +++ b/public/language/et/admin/development/info.json @@ -8,7 +8,11 @@ "nodejs": "nodejs", "online": "online", "git": "git", - "memory": "memory", + "process-memory": "process memory", + "system-memory": "system memory", + "used-memory-process": "Used memory by process", + "used-memory-os": "Used system memory", + "total-memory-os": "Total system memory", "load": "system load", "cpu-usage": "cpu usage", "uptime": "uptime", diff --git a/public/language/et/admin/extend/rewards.json b/public/language/et/admin/extend/rewards.json index 5383a90b33..df89d441a7 100644 --- a/public/language/et/admin/extend/rewards.json +++ b/public/language/et/admin/extend/rewards.json @@ -8,8 +8,6 @@ "delete": "Delete", "enable": "Enable", "disable": "Disable", - "control-panel": "Rewards Control", - "new-reward": "New Reward", "alert.delete-success": "Successfully deleted reward", "alert.no-inputs-found": "Illegal reward - no inputs found!", diff --git a/public/language/et/admin/general/dashboard.json b/public/language/et/admin/general/dashboard.json deleted file mode 100644 index 37ae537786..0000000000 --- a/public/language/et/admin/general/dashboard.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "forum-traffic": "Forum Traffic", - "page-views": "Page Views", - "unique-visitors": "Unique Visitors", - "new-users": "New Users", - "posts": "Posts", - "topics": "Topics", - "page-views-seven": "Last 7 Days", - "page-views-thirty": "Last 30 Days", - "page-views-last-day": "Last 24 hours", - "page-views-custom": "Custom Date Range", - "page-views-custom-start": "Range Start", - "page-views-custom-end": "Range End", - "page-views-custom-help": "Enter a date range of page views you would like to view. If no date picker is available, the accepted format is YYYY-MM-DD", - "page-views-custom-error": "Please enter a valid date range in the format YYYY-MM-DD", - - "stats.yesterday": "Yesterday", - "stats.today": "Today", - "stats.last-week": "Last Week", - "stats.this-week": "This Week", - "stats.last-month": "Last Month", - "stats.this-month": "This Month", - "stats.all": "All Time", - - "updates": "Updates", - "running-version": "You are running NodeBB v%1.", - "keep-updated": "Always make sure that your NodeBB is up to date for the latest security patches and bug fixes.", - "up-to-date": "

You are up-to-date

", - "upgrade-available": "

A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-upgrade-available": "

This is an outdated pre-release version of NodeBB. A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-warning": "

This is a pre-release version of NodeBB. Unintended bugs may occur.

", - "running-in-development": "Forum is running in development mode. The forum may be open to potential vulnerabilities; please contact your system administrator.", - "latest-lookup-failed": "

Failed to look up latest available version of NodeBB

", - - "notices": "Notices", - "restart-not-required": "Restart not required", - "restart-required": "Restart required", - "search-plugin-installed": "Search Plugin installed", - "search-plugin-not-installed": "Search Plugin not installed", - "search-plugin-tooltip": "Install a search plugin from the plugin page in order to activate search functionality", - - "control-panel": "System Control", - "rebuild-and-restart": "Rebuild & Restart", - "restart": "Restart", - "restart-warning": "Rebuilding or Restarting your NodeBB will drop all existing connections for a few seconds.", - "restart-disabled": "Rebuilding and Restarting your NodeBB has been disabled as you do not seem to be running it via the appropriate daemon.", - "maintenance-mode": "Maintenance Mode", - "maintenance-mode-title": "Click here to set up maintenance mode for NodeBB", - "realtime-chart-updates": "Realtime Chart Updates", - - "active-users": "Active Users", - "active-users.users": "Users", - "active-users.guests": "Guests", - "active-users.total": "Total", - "active-users.connections": "Connections", - - "anonymous-registered-users": "Anonymous vs Registered Users", - "anonymous": "Anonymous", - "registered": "Registered", - - "user-presence": "User Presence", - "on-categories": "On categories list", - "reading-posts": "Reading posts", - "browsing-topics": "Browsing topics", - "recent": "Recent", - "unread": "Unread", - - "high-presence-topics": "High Presence Topics", - - "graphs.page-views": "Page Views", - "graphs.page-views-registered": "Page Views Registered", - "graphs.page-views-guest": "Page Views Guest", - "graphs.page-views-bot": "Page Views Bot", - "graphs.unique-visitors": "Unique Visitors", - "graphs.registered-users": "Registered Users", - "graphs.anonymous-users": "Anonymous Users", - "last-restarted-by": "Last restarted by", - "no-users-browsing": "No users browsing" -} diff --git a/public/language/et/admin/general/homepage.json b/public/language/et/admin/general/homepage.json deleted file mode 100644 index 7428d59eeb..0000000000 --- a/public/language/et/admin/general/homepage.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "home-page": "Home Page", - "description": "Choose what page is shown when users navigate to the root URL of your forum.", - "home-page-route": "Home Page Route", - "custom-route": "Custom Route", - "allow-user-home-pages": "Allow User Home Pages", - "home-page-title": "Title of the home page (default \"Home\")" -} \ No newline at end of file diff --git a/public/language/et/admin/general/languages.json b/public/language/et/admin/general/languages.json deleted file mode 100644 index bdd57849b3..0000000000 --- a/public/language/et/admin/general/languages.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "language-settings": "Language Settings", - "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language", - "auto-detect": "Auto Detect Language Setting for Guests" -} \ No newline at end of file diff --git a/public/language/et/admin/general/navigation.json b/public/language/et/admin/general/navigation.json deleted file mode 100644 index 13dd01aae7..0000000000 --- a/public/language/et/admin/general/navigation.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "icon": "Icon:", - "change-icon": "change", - "route": "Route:", - "tooltip": "Tooltip:", - "text": "Text:", - "text-class": "Text Class: optional", - "class": "Class: optional", - "id": "ID: optional", - - "properties": "Properties:", - "groups": "Groups:", - "open-new-window": "Open in a new window", - - "btn.delete": "Delete", - "btn.disable": "Disable", - "btn.enable": "Enable", - - "available-menu-items": "Available Menu Items", - "custom-route": "Custom Route", - "core": "core", - "plugin": "plugin" -} \ No newline at end of file diff --git a/public/language/et/admin/general/social.json b/public/language/et/admin/general/social.json deleted file mode 100644 index 23aedfcfaa..0000000000 --- a/public/language/et/admin/general/social.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "post-sharing": "Post Sharing", - "info-plugins-additional": "Plugins can add additional networks for sharing posts.", - "save-success": "Successfully saved Post Sharing Networks!" -} \ No newline at end of file diff --git a/public/language/et/admin/general/sounds.json b/public/language/et/admin/general/sounds.json deleted file mode 100644 index 95ccbde0f1..0000000000 --- a/public/language/et/admin/general/sounds.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "notifications": "Notifications", - "chat-messages": "Chat Messages", - "play-sound": "Play", - "incoming-message": "Incoming Message", - "outgoing-message": "Outgoing Message", - "upload-new-sound": "Upload New Sound", - "saved": "Settings Saved" -} \ No newline at end of file diff --git a/public/language/et/admin/manage/digest.json b/public/language/et/admin/manage/digest.json index 8f3661698a..38c634d1f6 100644 --- a/public/language/et/admin/manage/digest.json +++ b/public/language/et/admin/manage/digest.json @@ -13,6 +13,7 @@ "resent-single": "Manual digest resend completed", "resent-day": "Daily digest resent", "resent-week": "Weekly digest resent", + "resent-biweek": "Bi-Weekly digest resent", "resent-month": "Monthly digest resent", "null": "Never", "manual-run": "Manual digest run:", diff --git a/public/language/et/admin/manage/users.json b/public/language/et/admin/manage/users.json index 03a6ec75b6..8cf1753398 100644 --- a/public/language/et/admin/manage/users.json +++ b/public/language/et/admin/manage/users.json @@ -47,6 +47,7 @@ "users.uid": "uid", "users.username": "Kasutajanimi", "users.email": "email", + "users.no-email": "(no email)", "users.ip": "IP", "users.postcount": "Postituste arv", "users.reputation": "Reputatsioon", diff --git a/public/language/et/admin/menu.json b/public/language/et/admin/menu.json index 1c5ea73b4f..5b22fbeb36 100644 --- a/public/language/et/admin/menu.json +++ b/public/language/et/admin/menu.json @@ -4,6 +4,7 @@ "dashboard/logins": "Logins", "dashboard/users": "Users", "dashboard/topics": "Topics", + "dashboard/searches": "Searches", "section-general": "General", "section-manage": "Manage", diff --git a/public/language/et/admin/settings/email.json b/public/language/et/admin/settings/email.json index 9bdbde26a6..65f579d28c 100644 --- a/public/language/et/admin/settings/email.json +++ b/public/language/et/admin/settings/email.json @@ -6,7 +6,7 @@ "from-help": "The from name to display in the email.", "smtp-transport": "SMTP Transport", - "smtp-transport.enabled": "Use an external email server to send emails", + "smtp-transport.enabled": "Enable SMTP Transport", "smtp-transport-help": "You can select from a list of well-known services or enter a custom one.", "smtp-transport.service": "Select a service", "smtp-transport.service-custom": "Custom Service", @@ -37,6 +37,10 @@ "subscriptions.hour": "Digest Hour", "subscriptions.hour-help": "Please enter a number representing the hour to send scheduled email digests (e.g. 0 for midnight, 17 for 5:00pm). Keep in mind that this is the hour according to the server itself, and may not exactly match your system clock.
The approximate server time is:
The next daily digest is scheduled to be sent ", "notifications.remove-images": "Remove images from email notifications", + "require-email-address": "Require new users to specify an email address", + "require-email-address-warning": "By default, users can opt-out of entering an email address. Enabling this option means they have to enter an email address in order to proceed with registration. It does not ensure user will enter a real email address, nor even an address they own.", "include-unverified-emails": "Send emails to recipients who have not explicitly confirmed their emails", - "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws." -} \ No newline at end of file + "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws.", + "prompt": "Prompt users to enter or confirm their emails", + "prompt-help": "If a user does not have an email set, or their email is not confirmed, a warning will be shown on screen." +} diff --git a/public/language/et/admin/settings/general.json b/public/language/et/admin/settings/general.json index be7df90870..29b939861b 100644 --- a/public/language/et/admin/settings/general.json +++ b/public/language/et/admin/settings/general.json @@ -3,9 +3,9 @@ "title": "Site Title", "title.short": "Short Title", "title.short-placeholder": "If no short title is specified, the site title will be used", - "title.url": "URL", + "title.url": "Title Link URL", "title.url-placeholder": "The URL of the site title", - "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.", + "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "title.name": "Your Community Name", "title.show-in-header": "Show Site Title in Header", "browser-title": "Browser Title", @@ -20,9 +20,9 @@ "logo.image": "Image", "logo.image-placeholder": "Path to a logo to display on forum header", "logo.upload": "Upload", - "logo.url": "URL", + "logo.url": "Logo Link URL", "logo.url-placeholder": "The URL of the site logo", - "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.", + "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "logo.alt-text": "Alt Text", "log.alt-text-placeholder": "Alternative text for accessibility", "favicon": "Favicon", @@ -47,4 +47,4 @@ "undo-timeout": "Undo Timeout", "undo-timeout-help": "Some operations such as moving topics will allow for the moderator to undo their action within a certain timeframe. Set to 0 to disable undo completely.", "topic-tools": "Topic Tools" -} \ No newline at end of file +} diff --git a/public/language/et/admin/settings/navigation.json b/public/language/et/admin/settings/navigation.json index 13dd01aae7..7baca85096 100644 --- a/public/language/et/admin/settings/navigation.json +++ b/public/language/et/admin/settings/navigation.json @@ -11,6 +11,8 @@ "properties": "Properties:", "groups": "Groups:", "open-new-window": "Open in a new window", + "dropdown": "Dropdown", + "dropdown-placeholder": "Place your dropdown menu items below, ie:
<li><a href="https://myforum.com">Link 1</a></li>", "btn.delete": "Delete", "btn.disable": "Disable", @@ -20,4 +22,4 @@ "custom-route": "Custom Route", "core": "core", "plugin": "plugin" -} \ No newline at end of file +} diff --git a/public/language/et/admin/settings/notifications.json b/public/language/et/admin/settings/notifications.json index da6c9680a3..c6d8b928ce 100644 --- a/public/language/et/admin/settings/notifications.json +++ b/public/language/et/admin/settings/notifications.json @@ -2,5 +2,6 @@ "notifications": "Notifications", "welcome-notification": "Welcome Notification", "welcome-notification-link": "Welcome Notification Link", - "welcome-notification-uid": "Welcome Notification User (UID)" + "welcome-notification-uid": "Welcome Notification User (UID)", + "post-queue-notification-uid": "Post Queue User (UID)" } \ No newline at end of file diff --git a/public/language/et/admin/settings/post.json b/public/language/et/admin/settings/post.json index 27493aafbd..ab8245738c 100644 --- a/public/language/et/admin/settings/post.json +++ b/public/language/et/admin/settings/post.json @@ -40,6 +40,7 @@ "teaser.last-post": "Last – Show the latest post, including the original post, if no replies", "teaser.last-reply": "Last – Show the latest reply, or a \"No replies\" placeholder if no replies", "teaser.first": "First", + "showPostPreviewsOnHover": "Show a preview of posts when mouse overed", "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", @@ -56,6 +57,9 @@ "composer.show-help": "Show \"Help\" tab", "composer.enable-plugin-help": "Allow plugins to add content to the help tab", "composer.custom-help": "Custom Help Text", + "backlinks": "Backlinks", + "backlinks.enabled": "Enable topic backlinks", + "backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.", "ip-tracking": "IP Tracking", "ip-tracking.each-post": "Track IP Address for each post", "enable-post-history": "Enable Post History" diff --git a/public/language/et/admin/settings/user.json b/public/language/et/admin/settings/user.json index 48be13b75e..7923bf8cbe 100644 --- a/public/language/et/admin/settings/user.json +++ b/public/language/et/admin/settings/user.json @@ -71,6 +71,7 @@ "digest-freq.off": "Off", "digest-freq.daily": "Daily", "digest-freq.weekly": "Weekly", + "digest-freq.biweekly": "Bi-Weekly", "digest-freq.monthly": "Monthly", "email-chat-notifs": "Send an email if a new chat message arrives and I am not online", "email-post-notif": "Send an email when replies are made to topics I am subscribed to", diff --git a/public/language/et/error.json b/public/language/et/error.json index ebf1ab7c31..ddb750b45f 100644 --- a/public/language/et/error.json +++ b/public/language/et/error.json @@ -34,8 +34,9 @@ "email-invited": "Email was already invited", "email-not-confirmed": "Posting in some categories or topics is enabled once your email is confirmed, please click here to send a confirmation email.", "email-not-confirmed-chat": "Sõnumeid ei ole võimalik enne saata kui sinu email on kinnitatud. Kinnitamiseks vajuta siia.", - "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You won't be able to post or chat until your email is confirmed.", - "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery. Please click here to enter an email.", + "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You may not be able to post in some categories or chat until your email is confirmed.", + "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery, and may be necessary for chatting and posting in some categories. Please click here to enter an email.", + "user-doesnt-have-email": "User \"%1\" does not have an email set.", "email-confirm-failed": "Meil ei õnnestunud sinu emaili kinnitada, proovi hiljem uuesti.", "confirm-email-already-sent": "Kinnituskiri on juba saadetud, palun oota %1 minut(it) uue kirja saatmiseks.", "sendmail-not-found": "Sendmail'i käivitatavat ei leitud, palun tee kindlaks, et see on installeeritud ja on käivitatav kasutaja poolt, kes käitab NodeBB't.", @@ -103,6 +104,7 @@ "already-bookmarked": "You have already bookmarked this post", "already-unbookmarked": "You have already unbookmarked this post", "cant-ban-other-admins": "Sa ei saa bannida teisi administraatoreid!", + "cant-make-banned-users-admin": "You can't make banned users admin.", "cant-remove-last-admin": "Te olete ainus administraator. Lisage keegi teine administraatoriks, enne kui eemaldate endalt administraatori.", "account-deletion-disabled": "Account deletion is disabled", "cant-delete-admin": "Eemalda sellelt kasutajalt administraatori õigused enne selle kustutamist", diff --git a/public/language/et/modules.json b/public/language/et/modules.json index 6fbb878b3a..61a8fbe145 100644 --- a/public/language/et/modules.json +++ b/public/language/et/modules.json @@ -54,7 +54,7 @@ "composer.formatting.strikethrough": "Läbitõmmatud", "composer.formatting.code": "Code", "composer.formatting.link": "Link", - "composer.formatting.picture": "Pilt", + "composer.formatting.picture": "Image Link", "composer.upload-picture": "Lae pilt üles", "composer.upload-file": "Lae fail üles", "composer.zen_mode": "Zen Mode", diff --git a/public/language/et/top.json b/public/language/et/top.json new file mode 100644 index 0000000000..b8a05bfa5f --- /dev/null +++ b/public/language/et/top.json @@ -0,0 +1,4 @@ +{ + "title": "Top", + "no_top_topics": "No top topics" +} \ No newline at end of file diff --git a/public/language/et/topic.json b/public/language/et/topic.json index db96717384..b3dd5b665a 100644 --- a/public/language/et/topic.json +++ b/public/language/et/topic.json @@ -47,6 +47,7 @@ "restored-by": "Restored by", "moved-from-by": "Moved from %1 by", "queued-by": "Post queued for approval →", + "backlink": "Referenced by", "bookmark_instructions": "Vajuta siia, et tagasi minna viimati loetud postituse juurde siin teemas.", "flag-post": "Flag this post", "flag-user": "Flag this user", @@ -138,6 +139,7 @@ "composer.handle_placeholder": "Enter your name/handle here", "composer.discard": "Katkesta", "composer.submit": "Postita", + "composer.additional-options": "Additional Options", "composer.schedule": "Schedule", "composer.replying_to": "Vastad %1'le", "composer.new_topic": "Uus teema", @@ -158,6 +160,7 @@ "newest_to_oldest": "Uuematest vanemateni", "most_votes": "Most Votes", "most_posts": "Most Posts", + "most_views": "Most Views", "stale.title": "Loo uus teema selle asemel?", "stale.warning": "Teema, millele vastad on küllaltki vana. Kas sooviksid hoopiski uue teema luua ning viidata sellele sinu vastuses?", "stale.create": "Loo uus teema/alapealkiri", diff --git a/public/language/et/user.json b/public/language/et/user.json index 7f09d7984e..d7d914d03b 100644 --- a/public/language/et/user.json +++ b/public/language/et/user.json @@ -94,6 +94,7 @@ "digest_off": "Väljas", "digest_daily": "Igapäevaselt", "digest_weekly": "Iga nädal", + "digest_biweekly": "Bi-Weekly", "digest_monthly": "Iga kuu", "has_no_follower": "Sellel kasutajal pole ühtegi jälgijat :(", "follows_no_one": "See kasutaja ei jälgi kedagi :(", diff --git a/public/language/fa-IR/admin/advanced/events.json b/public/language/fa-IR/admin/advanced/events.json index 007719946d..71fc64b667 100644 --- a/public/language/fa-IR/admin/advanced/events.json +++ b/public/language/fa-IR/admin/advanced/events.json @@ -3,6 +3,7 @@ "no-events": "رویدادی موجود نیست", "control-panel": "کنترل پنل رویداد ها", "delete-events": "Delete Events", + "confirm-delete-all-events": "Are you sure you want to delete all logged events?", "filters": "Filters", "filters-apply": "Apply Filters", "filter-type": "Event Type", diff --git a/public/language/fa-IR/admin/dashboard.json b/public/language/fa-IR/admin/dashboard.json index bf2ce96962..7952eee216 100644 --- a/public/language/fa-IR/admin/dashboard.json +++ b/public/language/fa-IR/admin/dashboard.json @@ -56,8 +56,8 @@ "active-users.total": "Total", "active-users.connections": "Connections", - "anonymous-registered-users": "Anonymous vs Registered Users", - "anonymous": "Anonymous", + "guest-registered-users": "Guest vs Registered Users", + "guest": "Guest", "registered": "Registered", "user-presence": "User Presence", @@ -68,6 +68,7 @@ "unread": "نخوانده‌ها", "high-presence-topics": "High Presence Topics", + "popular-searches": "Popular Searches", "graphs.page-views": "Page Views", "graphs.page-views-registered": "Page Views Registered", @@ -75,13 +76,14 @@ "graphs.page-views-bot": "Page Views Bot", "graphs.unique-visitors": "Unique Visitors", "graphs.registered-users": "Registered Users", - "graphs.anonymous-users": "Anonymous Users", + "graphs.guest-users": "Guest Users", "last-restarted-by": "Last restarted by", "no-users-browsing": "No users browsing", "back-to-dashboard": "Back to Dashboard", "details.no-users": "No users have joined within the selected timeframe", "details.no-topics": "No topics have been posted within the selected timeframe", + "details.no-searches": "No searches have been made yet", "details.no-logins": "No logins have been recorded within the selected timeframe", "details.logins-static": "NodeBB only saves session data for %1 days, and so this table below will only show the most recently active sessions", "details.logins-login-time": "Login Time" diff --git a/public/language/fa-IR/admin/development/info.json b/public/language/fa-IR/admin/development/info.json index 1003af1a5f..11202d9c3a 100644 --- a/public/language/fa-IR/admin/development/info.json +++ b/public/language/fa-IR/admin/development/info.json @@ -8,7 +8,11 @@ "nodejs": "nodejs", "online": "online", "git": "git", - "memory": "memory", + "process-memory": "process memory", + "system-memory": "system memory", + "used-memory-process": "Used memory by process", + "used-memory-os": "Used system memory", + "total-memory-os": "Total system memory", "load": "system load", "cpu-usage": "cpu usage", "uptime": "uptime", diff --git a/public/language/fa-IR/admin/extend/rewards.json b/public/language/fa-IR/admin/extend/rewards.json index 5383a90b33..df89d441a7 100644 --- a/public/language/fa-IR/admin/extend/rewards.json +++ b/public/language/fa-IR/admin/extend/rewards.json @@ -8,8 +8,6 @@ "delete": "Delete", "enable": "Enable", "disable": "Disable", - "control-panel": "Rewards Control", - "new-reward": "New Reward", "alert.delete-success": "Successfully deleted reward", "alert.no-inputs-found": "Illegal reward - no inputs found!", diff --git a/public/language/fa-IR/admin/general/dashboard.json b/public/language/fa-IR/admin/general/dashboard.json deleted file mode 100644 index 37ae537786..0000000000 --- a/public/language/fa-IR/admin/general/dashboard.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "forum-traffic": "Forum Traffic", - "page-views": "Page Views", - "unique-visitors": "Unique Visitors", - "new-users": "New Users", - "posts": "Posts", - "topics": "Topics", - "page-views-seven": "Last 7 Days", - "page-views-thirty": "Last 30 Days", - "page-views-last-day": "Last 24 hours", - "page-views-custom": "Custom Date Range", - "page-views-custom-start": "Range Start", - "page-views-custom-end": "Range End", - "page-views-custom-help": "Enter a date range of page views you would like to view. If no date picker is available, the accepted format is YYYY-MM-DD", - "page-views-custom-error": "Please enter a valid date range in the format YYYY-MM-DD", - - "stats.yesterday": "Yesterday", - "stats.today": "Today", - "stats.last-week": "Last Week", - "stats.this-week": "This Week", - "stats.last-month": "Last Month", - "stats.this-month": "This Month", - "stats.all": "All Time", - - "updates": "Updates", - "running-version": "You are running NodeBB v%1.", - "keep-updated": "Always make sure that your NodeBB is up to date for the latest security patches and bug fixes.", - "up-to-date": "

You are up-to-date

", - "upgrade-available": "

A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-upgrade-available": "

This is an outdated pre-release version of NodeBB. A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-warning": "

This is a pre-release version of NodeBB. Unintended bugs may occur.

", - "running-in-development": "Forum is running in development mode. The forum may be open to potential vulnerabilities; please contact your system administrator.", - "latest-lookup-failed": "

Failed to look up latest available version of NodeBB

", - - "notices": "Notices", - "restart-not-required": "Restart not required", - "restart-required": "Restart required", - "search-plugin-installed": "Search Plugin installed", - "search-plugin-not-installed": "Search Plugin not installed", - "search-plugin-tooltip": "Install a search plugin from the plugin page in order to activate search functionality", - - "control-panel": "System Control", - "rebuild-and-restart": "Rebuild & Restart", - "restart": "Restart", - "restart-warning": "Rebuilding or Restarting your NodeBB will drop all existing connections for a few seconds.", - "restart-disabled": "Rebuilding and Restarting your NodeBB has been disabled as you do not seem to be running it via the appropriate daemon.", - "maintenance-mode": "Maintenance Mode", - "maintenance-mode-title": "Click here to set up maintenance mode for NodeBB", - "realtime-chart-updates": "Realtime Chart Updates", - - "active-users": "Active Users", - "active-users.users": "Users", - "active-users.guests": "Guests", - "active-users.total": "Total", - "active-users.connections": "Connections", - - "anonymous-registered-users": "Anonymous vs Registered Users", - "anonymous": "Anonymous", - "registered": "Registered", - - "user-presence": "User Presence", - "on-categories": "On categories list", - "reading-posts": "Reading posts", - "browsing-topics": "Browsing topics", - "recent": "Recent", - "unread": "Unread", - - "high-presence-topics": "High Presence Topics", - - "graphs.page-views": "Page Views", - "graphs.page-views-registered": "Page Views Registered", - "graphs.page-views-guest": "Page Views Guest", - "graphs.page-views-bot": "Page Views Bot", - "graphs.unique-visitors": "Unique Visitors", - "graphs.registered-users": "Registered Users", - "graphs.anonymous-users": "Anonymous Users", - "last-restarted-by": "Last restarted by", - "no-users-browsing": "No users browsing" -} diff --git a/public/language/fa-IR/admin/general/homepage.json b/public/language/fa-IR/admin/general/homepage.json deleted file mode 100644 index 7428d59eeb..0000000000 --- a/public/language/fa-IR/admin/general/homepage.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "home-page": "Home Page", - "description": "Choose what page is shown when users navigate to the root URL of your forum.", - "home-page-route": "Home Page Route", - "custom-route": "Custom Route", - "allow-user-home-pages": "Allow User Home Pages", - "home-page-title": "Title of the home page (default \"Home\")" -} \ No newline at end of file diff --git a/public/language/fa-IR/admin/general/languages.json b/public/language/fa-IR/admin/general/languages.json deleted file mode 100644 index bdd57849b3..0000000000 --- a/public/language/fa-IR/admin/general/languages.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "language-settings": "Language Settings", - "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language", - "auto-detect": "Auto Detect Language Setting for Guests" -} \ No newline at end of file diff --git a/public/language/fa-IR/admin/general/navigation.json b/public/language/fa-IR/admin/general/navigation.json deleted file mode 100644 index 13dd01aae7..0000000000 --- a/public/language/fa-IR/admin/general/navigation.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "icon": "Icon:", - "change-icon": "change", - "route": "Route:", - "tooltip": "Tooltip:", - "text": "Text:", - "text-class": "Text Class: optional", - "class": "Class: optional", - "id": "ID: optional", - - "properties": "Properties:", - "groups": "Groups:", - "open-new-window": "Open in a new window", - - "btn.delete": "Delete", - "btn.disable": "Disable", - "btn.enable": "Enable", - - "available-menu-items": "Available Menu Items", - "custom-route": "Custom Route", - "core": "core", - "plugin": "plugin" -} \ No newline at end of file diff --git a/public/language/fa-IR/admin/general/social.json b/public/language/fa-IR/admin/general/social.json deleted file mode 100644 index 23aedfcfaa..0000000000 --- a/public/language/fa-IR/admin/general/social.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "post-sharing": "Post Sharing", - "info-plugins-additional": "Plugins can add additional networks for sharing posts.", - "save-success": "Successfully saved Post Sharing Networks!" -} \ No newline at end of file diff --git a/public/language/fa-IR/admin/general/sounds.json b/public/language/fa-IR/admin/general/sounds.json deleted file mode 100644 index 95ccbde0f1..0000000000 --- a/public/language/fa-IR/admin/general/sounds.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "notifications": "Notifications", - "chat-messages": "Chat Messages", - "play-sound": "Play", - "incoming-message": "Incoming Message", - "outgoing-message": "Outgoing Message", - "upload-new-sound": "Upload New Sound", - "saved": "Settings Saved" -} \ No newline at end of file diff --git a/public/language/fa-IR/admin/manage/digest.json b/public/language/fa-IR/admin/manage/digest.json index 8f3661698a..38c634d1f6 100644 --- a/public/language/fa-IR/admin/manage/digest.json +++ b/public/language/fa-IR/admin/manage/digest.json @@ -13,6 +13,7 @@ "resent-single": "Manual digest resend completed", "resent-day": "Daily digest resent", "resent-week": "Weekly digest resent", + "resent-biweek": "Bi-Weekly digest resent", "resent-month": "Monthly digest resent", "null": "Never", "manual-run": "Manual digest run:", diff --git a/public/language/fa-IR/admin/manage/users.json b/public/language/fa-IR/admin/manage/users.json index f006204a49..6ae785dcaa 100644 --- a/public/language/fa-IR/admin/manage/users.json +++ b/public/language/fa-IR/admin/manage/users.json @@ -47,6 +47,7 @@ "users.uid": "uid", "users.username": "username", "users.email": "email", + "users.no-email": "(no email)", "users.ip": "IP", "users.postcount": "postcount", "users.reputation": "reputation", diff --git a/public/language/fa-IR/admin/menu.json b/public/language/fa-IR/admin/menu.json index 9f65d917da..7fa68c6830 100644 --- a/public/language/fa-IR/admin/menu.json +++ b/public/language/fa-IR/admin/menu.json @@ -4,6 +4,7 @@ "dashboard/logins": "Logins", "dashboard/users": "Users", "dashboard/topics": "Topics", + "dashboard/searches": "Searches", "section-general": "عمومی", "section-manage": "Manage", diff --git a/public/language/fa-IR/admin/settings/email.json b/public/language/fa-IR/admin/settings/email.json index 9bdbde26a6..65f579d28c 100644 --- a/public/language/fa-IR/admin/settings/email.json +++ b/public/language/fa-IR/admin/settings/email.json @@ -6,7 +6,7 @@ "from-help": "The from name to display in the email.", "smtp-transport": "SMTP Transport", - "smtp-transport.enabled": "Use an external email server to send emails", + "smtp-transport.enabled": "Enable SMTP Transport", "smtp-transport-help": "You can select from a list of well-known services or enter a custom one.", "smtp-transport.service": "Select a service", "smtp-transport.service-custom": "Custom Service", @@ -37,6 +37,10 @@ "subscriptions.hour": "Digest Hour", "subscriptions.hour-help": "Please enter a number representing the hour to send scheduled email digests (e.g. 0 for midnight, 17 for 5:00pm). Keep in mind that this is the hour according to the server itself, and may not exactly match your system clock.
The approximate server time is:
The next daily digest is scheduled to be sent ", "notifications.remove-images": "Remove images from email notifications", + "require-email-address": "Require new users to specify an email address", + "require-email-address-warning": "By default, users can opt-out of entering an email address. Enabling this option means they have to enter an email address in order to proceed with registration. It does not ensure user will enter a real email address, nor even an address they own.", "include-unverified-emails": "Send emails to recipients who have not explicitly confirmed their emails", - "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws." -} \ No newline at end of file + "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws.", + "prompt": "Prompt users to enter or confirm their emails", + "prompt-help": "If a user does not have an email set, or their email is not confirmed, a warning will be shown on screen." +} diff --git a/public/language/fa-IR/admin/settings/general.json b/public/language/fa-IR/admin/settings/general.json index be7df90870..29b939861b 100644 --- a/public/language/fa-IR/admin/settings/general.json +++ b/public/language/fa-IR/admin/settings/general.json @@ -3,9 +3,9 @@ "title": "Site Title", "title.short": "Short Title", "title.short-placeholder": "If no short title is specified, the site title will be used", - "title.url": "URL", + "title.url": "Title Link URL", "title.url-placeholder": "The URL of the site title", - "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.", + "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "title.name": "Your Community Name", "title.show-in-header": "Show Site Title in Header", "browser-title": "Browser Title", @@ -20,9 +20,9 @@ "logo.image": "Image", "logo.image-placeholder": "Path to a logo to display on forum header", "logo.upload": "Upload", - "logo.url": "URL", + "logo.url": "Logo Link URL", "logo.url-placeholder": "The URL of the site logo", - "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.", + "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "logo.alt-text": "Alt Text", "log.alt-text-placeholder": "Alternative text for accessibility", "favicon": "Favicon", @@ -47,4 +47,4 @@ "undo-timeout": "Undo Timeout", "undo-timeout-help": "Some operations such as moving topics will allow for the moderator to undo their action within a certain timeframe. Set to 0 to disable undo completely.", "topic-tools": "Topic Tools" -} \ No newline at end of file +} diff --git a/public/language/fa-IR/admin/settings/navigation.json b/public/language/fa-IR/admin/settings/navigation.json index 13dd01aae7..7baca85096 100644 --- a/public/language/fa-IR/admin/settings/navigation.json +++ b/public/language/fa-IR/admin/settings/navigation.json @@ -11,6 +11,8 @@ "properties": "Properties:", "groups": "Groups:", "open-new-window": "Open in a new window", + "dropdown": "Dropdown", + "dropdown-placeholder": "Place your dropdown menu items below, ie:
<li><a href="https://myforum.com">Link 1</a></li>", "btn.delete": "Delete", "btn.disable": "Disable", @@ -20,4 +22,4 @@ "custom-route": "Custom Route", "core": "core", "plugin": "plugin" -} \ No newline at end of file +} diff --git a/public/language/fa-IR/admin/settings/notifications.json b/public/language/fa-IR/admin/settings/notifications.json index da6c9680a3..c6d8b928ce 100644 --- a/public/language/fa-IR/admin/settings/notifications.json +++ b/public/language/fa-IR/admin/settings/notifications.json @@ -2,5 +2,6 @@ "notifications": "Notifications", "welcome-notification": "Welcome Notification", "welcome-notification-link": "Welcome Notification Link", - "welcome-notification-uid": "Welcome Notification User (UID)" + "welcome-notification-uid": "Welcome Notification User (UID)", + "post-queue-notification-uid": "Post Queue User (UID)" } \ No newline at end of file diff --git a/public/language/fa-IR/admin/settings/post.json b/public/language/fa-IR/admin/settings/post.json index fb0a7ad95e..d07c92aa9d 100644 --- a/public/language/fa-IR/admin/settings/post.json +++ b/public/language/fa-IR/admin/settings/post.json @@ -40,6 +40,7 @@ "teaser.last-post": "Last – Show the latest post, including the original post, if no replies", "teaser.last-reply": "Last – Show the latest reply, or a \"No replies\" placeholder if no replies", "teaser.first": "First", + "showPostPreviewsOnHover": "Show a preview of posts when mouse overed", "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", @@ -56,6 +57,9 @@ "composer.show-help": "Show \"Help\" tab", "composer.enable-plugin-help": "Allow plugins to add content to the help tab", "composer.custom-help": "Custom Help Text", + "backlinks": "Backlinks", + "backlinks.enabled": "Enable topic backlinks", + "backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.", "ip-tracking": "IP Tracking", "ip-tracking.each-post": "Track IP Address for each post", "enable-post-history": "Enable Post History" diff --git a/public/language/fa-IR/admin/settings/user.json b/public/language/fa-IR/admin/settings/user.json index 48be13b75e..7923bf8cbe 100644 --- a/public/language/fa-IR/admin/settings/user.json +++ b/public/language/fa-IR/admin/settings/user.json @@ -71,6 +71,7 @@ "digest-freq.off": "Off", "digest-freq.daily": "Daily", "digest-freq.weekly": "Weekly", + "digest-freq.biweekly": "Bi-Weekly", "digest-freq.monthly": "Monthly", "email-chat-notifs": "Send an email if a new chat message arrives and I am not online", "email-post-notif": "Send an email when replies are made to topics I am subscribed to", diff --git a/public/language/fa-IR/error.json b/public/language/fa-IR/error.json index f5385f82bd..b707c9d3d9 100644 --- a/public/language/fa-IR/error.json +++ b/public/language/fa-IR/error.json @@ -34,8 +34,9 @@ "email-invited": "Email was already invited", "email-not-confirmed": "Posting in some categories or topics is enabled once your email is confirmed, please click here to send a confirmation email.", "email-not-confirmed-chat": "شما تا قبل از تایید ایمیل قادر به چت نیستید، لطفا برای تایید ایمیل خود اینجا کلیک کنید", - "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You won't be able to post or chat until your email is confirmed.", - "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery. Please click here to enter an email.", + "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You may not be able to post in some categories or chat until your email is confirmed.", + "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery, and may be necessary for chatting and posting in some categories. Please click here to enter an email.", + "user-doesnt-have-email": "User \"%1\" does not have an email set.", "email-confirm-failed": "سیستم موفق به تایید ایمیل شما نشد، لطفا بعدا دوباره سعی کنید", "confirm-email-already-sent": "ایمیل فعال‌سازی قبلا فرستاده شده، لطفا %1 دقیقه صبر کنید تا ایمیل دیگری فرستاده شود.", "sendmail-not-found": "اجازه ارسال رایانامه پیدا نشد، لطفا مطمئن شوید این قابلیت نصب شده و توسط کاربر مد نظر در نود‌بی‌بی قابل اجرا است.", @@ -103,6 +104,7 @@ "already-bookmarked": "شما قبلا این پست را نشانک کرده‌اید", "already-unbookmarked": "شما قبلا این پست را از نشانک در آوردید", "cant-ban-other-admins": "شما نمی‌توانید دیگر مدیران را محروم کنید!", + "cant-make-banned-users-admin": "You can't make banned users admin.", "cant-remove-last-admin": "شما تنها مدیر می باشید . شما باید قبل از عزل خود از مدیریت یک کاربر دیگر را مدیر کنید", "account-deletion-disabled": "Account deletion is disabled", "cant-delete-admin": "قبل از حذف این کاربر دسترسی های مدیریت را از وی بگیرید.", diff --git a/public/language/fa-IR/modules.json b/public/language/fa-IR/modules.json index a4a4f3169a..f401a6e6f2 100644 --- a/public/language/fa-IR/modules.json +++ b/public/language/fa-IR/modules.json @@ -54,7 +54,7 @@ "composer.formatting.strikethrough": "خط خورده", "composer.formatting.code": "کد", "composer.formatting.link": "پیوند", - "composer.formatting.picture": "عکس", + "composer.formatting.picture": "Image Link", "composer.upload-picture": "بارگذاری عکس", "composer.upload-file": "بارگذاری فایل", "composer.zen_mode": "حالت ذن", diff --git a/public/language/fa-IR/top.json b/public/language/fa-IR/top.json new file mode 100644 index 0000000000..b8a05bfa5f --- /dev/null +++ b/public/language/fa-IR/top.json @@ -0,0 +1,4 @@ +{ + "title": "Top", + "no_top_topics": "No top topics" +} \ No newline at end of file diff --git a/public/language/fa-IR/topic.json b/public/language/fa-IR/topic.json index 01f7d78bb2..b5e75abd3c 100644 --- a/public/language/fa-IR/topic.json +++ b/public/language/fa-IR/topic.json @@ -47,6 +47,7 @@ "restored-by": "Restored by", "moved-from-by": "Moved from %1 by", "queued-by": "Post queued for approval →", + "backlink": "Referenced by", "bookmark_instructions": "برای بازگشت به آخرین پست در این موضوع اینجا را کلیک کنید.", "flag-post": "گزارش این پست", "flag-user": "گزارش این کاربر", @@ -138,6 +139,7 @@ "composer.handle_placeholder": "Enter your name/handle here", "composer.discard": "انصراف", "composer.submit": "ارسال", + "composer.additional-options": "Additional Options", "composer.schedule": "Schedule", "composer.replying_to": "پاسخ به %1", "composer.new_topic": "موضوع تازه", @@ -158,6 +160,7 @@ "newest_to_oldest": "جدید‌ترین به قدیمی‌ترین", "most_votes": "بیشترین رای ها", "most_posts": "بیشترین پست", + "most_views": "Most Views", "stale.title": "آیا مایلید به جای آن یک موضوع جدید ایجاد کنید؟", "stale.warning": "موضوعی که شما در حال پاسخگویی به آن هستید قدیمی می باشد. آیا میلید به جای آن یک موضوع جدید ایجاد کنید و در آن به این موضوع ارجاع دهید؟", "stale.create": "ایجاد یک موضوع جدید", diff --git a/public/language/fa-IR/user.json b/public/language/fa-IR/user.json index 5de1da570c..68d9698a69 100644 --- a/public/language/fa-IR/user.json +++ b/public/language/fa-IR/user.json @@ -94,6 +94,7 @@ "digest_off": "خاموش", "digest_daily": "روزانه", "digest_weekly": "هفتگی", + "digest_biweekly": "Bi-Weekly", "digest_monthly": "ماهانه", "has_no_follower": "این کاربر هیچ دنبال‌کننده‌ای ندارد :(", "follows_no_one": "این کاربر هیچ کسی را دنبال نمی‌کند :(", diff --git a/public/language/fi/admin/advanced/events.json b/public/language/fi/admin/advanced/events.json index a2a77c4021..472c08b024 100644 --- a/public/language/fi/admin/advanced/events.json +++ b/public/language/fi/admin/advanced/events.json @@ -3,6 +3,7 @@ "no-events": "Ei tapahtumia.", "control-panel": "Tapahtumien hallintapaneeli", "delete-events": "Poista tapahtumia", + "confirm-delete-all-events": "Are you sure you want to delete all logged events?", "filters": "Suodattimet", "filters-apply": "Lisää suodattimia", "filter-type": "Tapahtuman tyyppi", diff --git a/public/language/fi/admin/dashboard.json b/public/language/fi/admin/dashboard.json index 8ce9c29ded..2fa46e3a70 100644 --- a/public/language/fi/admin/dashboard.json +++ b/public/language/fi/admin/dashboard.json @@ -56,8 +56,8 @@ "active-users.total": "Kokonaisuudessaan", "active-users.connections": "Yhteyttä", - "anonymous-registered-users": "Anonyymit vs. Rekisteröityneet käyttäjät", - "anonymous": "Anonyymiä", + "guest-registered-users": "Guest vs Registered Users", + "guest": "Guest", "registered": "Rekisteröitynyttä", "user-presence": "Käyttäjien sijainti", @@ -68,6 +68,7 @@ "unread": "Lukemattomat", "high-presence-topics": "Aiheet, joissa on eniten käyttäjiä paikalla", + "popular-searches": "Popular Searches", "graphs.page-views": "Sivulataukset", "graphs.page-views-registered": "Rekisteröityneiden käyttäjien sivulatausta", @@ -75,13 +76,14 @@ "graphs.page-views-bot": "Bottien sivulatausta", "graphs.unique-visitors": "Uniikkia vierailijaa", "graphs.registered-users": "Rekisteröitynyttä käyttäjää", - "graphs.anonymous-users": "Anonyymiä käyttäjää", + "graphs.guest-users": "Guest Users", "last-restarted-by": "Viimeksi uudelleenkäynnistetty", "no-users-browsing": "Ei käyttäjiä selaamassa", "back-to-dashboard": "Takaisin ohjausnäkymään", "details.no-users": "Ei liittyneitä käyttäjiä valitulla aikavälillä.", "details.no-topics": "Ei luotuja aiheita valitulla aikavälillä.", + "details.no-searches": "No searches have been made yet", "details.no-logins": "Ei sisäänkirjautumisia valitulla aikavälillä.", "details.logins-static": "NodeBB tallettaa istuntotiedot vain %1 päivän ajaksi, joten tämä kuvaaja näyttää vain viimeisimpänä aktiivisena olleet istunnot.", "details.logins-login-time": "Sisäänkirjautumisaika" diff --git a/public/language/fi/admin/development/info.json b/public/language/fi/admin/development/info.json index 1003af1a5f..11202d9c3a 100644 --- a/public/language/fi/admin/development/info.json +++ b/public/language/fi/admin/development/info.json @@ -8,7 +8,11 @@ "nodejs": "nodejs", "online": "online", "git": "git", - "memory": "memory", + "process-memory": "process memory", + "system-memory": "system memory", + "used-memory-process": "Used memory by process", + "used-memory-os": "Used system memory", + "total-memory-os": "Total system memory", "load": "system load", "cpu-usage": "cpu usage", "uptime": "uptime", diff --git a/public/language/fi/admin/extend/rewards.json b/public/language/fi/admin/extend/rewards.json index 5383a90b33..df89d441a7 100644 --- a/public/language/fi/admin/extend/rewards.json +++ b/public/language/fi/admin/extend/rewards.json @@ -8,8 +8,6 @@ "delete": "Delete", "enable": "Enable", "disable": "Disable", - "control-panel": "Rewards Control", - "new-reward": "New Reward", "alert.delete-success": "Successfully deleted reward", "alert.no-inputs-found": "Illegal reward - no inputs found!", diff --git a/public/language/fi/admin/general/dashboard.json b/public/language/fi/admin/general/dashboard.json deleted file mode 100644 index 37ae537786..0000000000 --- a/public/language/fi/admin/general/dashboard.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "forum-traffic": "Forum Traffic", - "page-views": "Page Views", - "unique-visitors": "Unique Visitors", - "new-users": "New Users", - "posts": "Posts", - "topics": "Topics", - "page-views-seven": "Last 7 Days", - "page-views-thirty": "Last 30 Days", - "page-views-last-day": "Last 24 hours", - "page-views-custom": "Custom Date Range", - "page-views-custom-start": "Range Start", - "page-views-custom-end": "Range End", - "page-views-custom-help": "Enter a date range of page views you would like to view. If no date picker is available, the accepted format is YYYY-MM-DD", - "page-views-custom-error": "Please enter a valid date range in the format YYYY-MM-DD", - - "stats.yesterday": "Yesterday", - "stats.today": "Today", - "stats.last-week": "Last Week", - "stats.this-week": "This Week", - "stats.last-month": "Last Month", - "stats.this-month": "This Month", - "stats.all": "All Time", - - "updates": "Updates", - "running-version": "You are running NodeBB v%1.", - "keep-updated": "Always make sure that your NodeBB is up to date for the latest security patches and bug fixes.", - "up-to-date": "

You are up-to-date

", - "upgrade-available": "

A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-upgrade-available": "

This is an outdated pre-release version of NodeBB. A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-warning": "

This is a pre-release version of NodeBB. Unintended bugs may occur.

", - "running-in-development": "Forum is running in development mode. The forum may be open to potential vulnerabilities; please contact your system administrator.", - "latest-lookup-failed": "

Failed to look up latest available version of NodeBB

", - - "notices": "Notices", - "restart-not-required": "Restart not required", - "restart-required": "Restart required", - "search-plugin-installed": "Search Plugin installed", - "search-plugin-not-installed": "Search Plugin not installed", - "search-plugin-tooltip": "Install a search plugin from the plugin page in order to activate search functionality", - - "control-panel": "System Control", - "rebuild-and-restart": "Rebuild & Restart", - "restart": "Restart", - "restart-warning": "Rebuilding or Restarting your NodeBB will drop all existing connections for a few seconds.", - "restart-disabled": "Rebuilding and Restarting your NodeBB has been disabled as you do not seem to be running it via the appropriate daemon.", - "maintenance-mode": "Maintenance Mode", - "maintenance-mode-title": "Click here to set up maintenance mode for NodeBB", - "realtime-chart-updates": "Realtime Chart Updates", - - "active-users": "Active Users", - "active-users.users": "Users", - "active-users.guests": "Guests", - "active-users.total": "Total", - "active-users.connections": "Connections", - - "anonymous-registered-users": "Anonymous vs Registered Users", - "anonymous": "Anonymous", - "registered": "Registered", - - "user-presence": "User Presence", - "on-categories": "On categories list", - "reading-posts": "Reading posts", - "browsing-topics": "Browsing topics", - "recent": "Recent", - "unread": "Unread", - - "high-presence-topics": "High Presence Topics", - - "graphs.page-views": "Page Views", - "graphs.page-views-registered": "Page Views Registered", - "graphs.page-views-guest": "Page Views Guest", - "graphs.page-views-bot": "Page Views Bot", - "graphs.unique-visitors": "Unique Visitors", - "graphs.registered-users": "Registered Users", - "graphs.anonymous-users": "Anonymous Users", - "last-restarted-by": "Last restarted by", - "no-users-browsing": "No users browsing" -} diff --git a/public/language/fi/admin/general/homepage.json b/public/language/fi/admin/general/homepage.json deleted file mode 100644 index 7428d59eeb..0000000000 --- a/public/language/fi/admin/general/homepage.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "home-page": "Home Page", - "description": "Choose what page is shown when users navigate to the root URL of your forum.", - "home-page-route": "Home Page Route", - "custom-route": "Custom Route", - "allow-user-home-pages": "Allow User Home Pages", - "home-page-title": "Title of the home page (default \"Home\")" -} \ No newline at end of file diff --git a/public/language/fi/admin/general/languages.json b/public/language/fi/admin/general/languages.json deleted file mode 100644 index bdd57849b3..0000000000 --- a/public/language/fi/admin/general/languages.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "language-settings": "Language Settings", - "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language", - "auto-detect": "Auto Detect Language Setting for Guests" -} \ No newline at end of file diff --git a/public/language/fi/admin/general/navigation.json b/public/language/fi/admin/general/navigation.json deleted file mode 100644 index 13dd01aae7..0000000000 --- a/public/language/fi/admin/general/navigation.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "icon": "Icon:", - "change-icon": "change", - "route": "Route:", - "tooltip": "Tooltip:", - "text": "Text:", - "text-class": "Text Class: optional", - "class": "Class: optional", - "id": "ID: optional", - - "properties": "Properties:", - "groups": "Groups:", - "open-new-window": "Open in a new window", - - "btn.delete": "Delete", - "btn.disable": "Disable", - "btn.enable": "Enable", - - "available-menu-items": "Available Menu Items", - "custom-route": "Custom Route", - "core": "core", - "plugin": "plugin" -} \ No newline at end of file diff --git a/public/language/fi/admin/general/social.json b/public/language/fi/admin/general/social.json deleted file mode 100644 index 2d6c8e5690..0000000000 --- a/public/language/fi/admin/general/social.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "post-sharing": "Viestin jakaminen", - "info-plugins-additional": "Plugins can add additional networks for sharing posts.", - "save-success": "Successfully saved Post Sharing Networks!" -} \ No newline at end of file diff --git a/public/language/fi/admin/general/sounds.json b/public/language/fi/admin/general/sounds.json deleted file mode 100644 index 95ccbde0f1..0000000000 --- a/public/language/fi/admin/general/sounds.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "notifications": "Notifications", - "chat-messages": "Chat Messages", - "play-sound": "Play", - "incoming-message": "Incoming Message", - "outgoing-message": "Outgoing Message", - "upload-new-sound": "Upload New Sound", - "saved": "Settings Saved" -} \ No newline at end of file diff --git a/public/language/fi/admin/manage/digest.json b/public/language/fi/admin/manage/digest.json index 8f3661698a..38c634d1f6 100644 --- a/public/language/fi/admin/manage/digest.json +++ b/public/language/fi/admin/manage/digest.json @@ -13,6 +13,7 @@ "resent-single": "Manual digest resend completed", "resent-day": "Daily digest resent", "resent-week": "Weekly digest resent", + "resent-biweek": "Bi-Weekly digest resent", "resent-month": "Monthly digest resent", "null": "Never", "manual-run": "Manual digest run:", diff --git a/public/language/fi/admin/manage/users.json b/public/language/fi/admin/manage/users.json index 38a065b954..2a3c0c4829 100644 --- a/public/language/fi/admin/manage/users.json +++ b/public/language/fi/admin/manage/users.json @@ -47,6 +47,7 @@ "users.uid": "uid", "users.username": "username", "users.email": "email", + "users.no-email": "(no email)", "users.ip": "IP", "users.postcount": "postcount", "users.reputation": "reputation", diff --git a/public/language/fi/admin/menu.json b/public/language/fi/admin/menu.json index 1c5ea73b4f..5b22fbeb36 100644 --- a/public/language/fi/admin/menu.json +++ b/public/language/fi/admin/menu.json @@ -4,6 +4,7 @@ "dashboard/logins": "Logins", "dashboard/users": "Users", "dashboard/topics": "Topics", + "dashboard/searches": "Searches", "section-general": "General", "section-manage": "Manage", diff --git a/public/language/fi/admin/settings/email.json b/public/language/fi/admin/settings/email.json index 02548f738e..2dd033e1ad 100644 --- a/public/language/fi/admin/settings/email.json +++ b/public/language/fi/admin/settings/email.json @@ -6,7 +6,7 @@ "from-help": "The from name to display in the email.", "smtp-transport": "SMTP Transport", - "smtp-transport.enabled": "Use an external email server to send emails", + "smtp-transport.enabled": "Enable SMTP Transport", "smtp-transport-help": "You can select from a list of well-known services or enter a custom one.", "smtp-transport.service": "Select a service", "smtp-transport.service-custom": "Custom Service", @@ -37,6 +37,10 @@ "subscriptions.hour": "Digest Hour", "subscriptions.hour-help": "Please enter a number representing the hour to send scheduled email digests (e.g. 0 for midnight, 17 for 5:00pm). Keep in mind that this is the hour according to the server itself, and may not exactly match your system clock.
The approximate server time is:
The next daily digest is scheduled to be sent ", "notifications.remove-images": "Remove images from email notifications", + "require-email-address": "Require new users to specify an email address", + "require-email-address-warning": "By default, users can opt-out of entering an email address. Enabling this option means they have to enter an email address in order to proceed with registration. It does not ensure user will enter a real email address, nor even an address they own.", "include-unverified-emails": "Send emails to recipients who have not explicitly confirmed their emails", - "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws." -} \ No newline at end of file + "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws.", + "prompt": "Prompt users to enter or confirm their emails", + "prompt-help": "If a user does not have an email set, or their email is not confirmed, a warning will be shown on screen." +} diff --git a/public/language/fi/admin/settings/general.json b/public/language/fi/admin/settings/general.json index be7df90870..29b939861b 100644 --- a/public/language/fi/admin/settings/general.json +++ b/public/language/fi/admin/settings/general.json @@ -3,9 +3,9 @@ "title": "Site Title", "title.short": "Short Title", "title.short-placeholder": "If no short title is specified, the site title will be used", - "title.url": "URL", + "title.url": "Title Link URL", "title.url-placeholder": "The URL of the site title", - "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.", + "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "title.name": "Your Community Name", "title.show-in-header": "Show Site Title in Header", "browser-title": "Browser Title", @@ -20,9 +20,9 @@ "logo.image": "Image", "logo.image-placeholder": "Path to a logo to display on forum header", "logo.upload": "Upload", - "logo.url": "URL", + "logo.url": "Logo Link URL", "logo.url-placeholder": "The URL of the site logo", - "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.", + "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "logo.alt-text": "Alt Text", "log.alt-text-placeholder": "Alternative text for accessibility", "favicon": "Favicon", @@ -47,4 +47,4 @@ "undo-timeout": "Undo Timeout", "undo-timeout-help": "Some operations such as moving topics will allow for the moderator to undo their action within a certain timeframe. Set to 0 to disable undo completely.", "topic-tools": "Topic Tools" -} \ No newline at end of file +} diff --git a/public/language/fi/admin/settings/navigation.json b/public/language/fi/admin/settings/navigation.json index 13dd01aae7..7baca85096 100644 --- a/public/language/fi/admin/settings/navigation.json +++ b/public/language/fi/admin/settings/navigation.json @@ -11,6 +11,8 @@ "properties": "Properties:", "groups": "Groups:", "open-new-window": "Open in a new window", + "dropdown": "Dropdown", + "dropdown-placeholder": "Place your dropdown menu items below, ie:
<li><a href="https://myforum.com">Link 1</a></li>", "btn.delete": "Delete", "btn.disable": "Disable", @@ -20,4 +22,4 @@ "custom-route": "Custom Route", "core": "core", "plugin": "plugin" -} \ No newline at end of file +} diff --git a/public/language/fi/admin/settings/notifications.json b/public/language/fi/admin/settings/notifications.json index da6c9680a3..c6d8b928ce 100644 --- a/public/language/fi/admin/settings/notifications.json +++ b/public/language/fi/admin/settings/notifications.json @@ -2,5 +2,6 @@ "notifications": "Notifications", "welcome-notification": "Welcome Notification", "welcome-notification-link": "Welcome Notification Link", - "welcome-notification-uid": "Welcome Notification User (UID)" + "welcome-notification-uid": "Welcome Notification User (UID)", + "post-queue-notification-uid": "Post Queue User (UID)" } \ No newline at end of file diff --git a/public/language/fi/admin/settings/post.json b/public/language/fi/admin/settings/post.json index 27493aafbd..ab8245738c 100644 --- a/public/language/fi/admin/settings/post.json +++ b/public/language/fi/admin/settings/post.json @@ -40,6 +40,7 @@ "teaser.last-post": "Last – Show the latest post, including the original post, if no replies", "teaser.last-reply": "Last – Show the latest reply, or a \"No replies\" placeholder if no replies", "teaser.first": "First", + "showPostPreviewsOnHover": "Show a preview of posts when mouse overed", "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", @@ -56,6 +57,9 @@ "composer.show-help": "Show \"Help\" tab", "composer.enable-plugin-help": "Allow plugins to add content to the help tab", "composer.custom-help": "Custom Help Text", + "backlinks": "Backlinks", + "backlinks.enabled": "Enable topic backlinks", + "backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.", "ip-tracking": "IP Tracking", "ip-tracking.each-post": "Track IP Address for each post", "enable-post-history": "Enable Post History" diff --git a/public/language/fi/admin/settings/user.json b/public/language/fi/admin/settings/user.json index 48be13b75e..7923bf8cbe 100644 --- a/public/language/fi/admin/settings/user.json +++ b/public/language/fi/admin/settings/user.json @@ -71,6 +71,7 @@ "digest-freq.off": "Off", "digest-freq.daily": "Daily", "digest-freq.weekly": "Weekly", + "digest-freq.biweekly": "Bi-Weekly", "digest-freq.monthly": "Monthly", "email-chat-notifs": "Send an email if a new chat message arrives and I am not online", "email-post-notif": "Send an email when replies are made to topics I am subscribed to", diff --git a/public/language/fi/error.json b/public/language/fi/error.json index 711704df4f..d379bc1081 100644 --- a/public/language/fi/error.json +++ b/public/language/fi/error.json @@ -34,8 +34,9 @@ "email-invited": "Email was already invited", "email-not-confirmed": "Posting in some categories or topics is enabled once your email is confirmed, please click here to send a confirmation email.", "email-not-confirmed-chat": "Et voi keskustella ennen kuin sähköpostiosoitteesi on vahvistettu, ole hyvä ja paina tästä vahvistaaksesi sen.", - "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You won't be able to post or chat until your email is confirmed.", - "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery. Please click here to enter an email.", + "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You may not be able to post in some categories or chat until your email is confirmed.", + "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery, and may be necessary for chatting and posting in some categories. Please click here to enter an email.", + "user-doesnt-have-email": "User \"%1\" does not have an email set.", "email-confirm-failed": "We could not confirm your email, please try again later.", "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", "sendmail-not-found": "The sendmail executable could not be found, please ensure it is installed and executable by the user running NodeBB.", @@ -103,6 +104,7 @@ "already-bookmarked": "You have already bookmarked this post", "already-unbookmarked": "You have already unbookmarked this post", "cant-ban-other-admins": "Et voi estää muita ylläpitäjiä!", + "cant-make-banned-users-admin": "You can't make banned users admin.", "cant-remove-last-admin": "You are the only administrator. Add another user as an administrator before removing yourself as admin", "account-deletion-disabled": "Account deletion is disabled", "cant-delete-admin": "Remove administrator privileges from this account before attempting to delete it.", diff --git a/public/language/fi/modules.json b/public/language/fi/modules.json index 465a3a947c..506acc6926 100644 --- a/public/language/fi/modules.json +++ b/public/language/fi/modules.json @@ -54,7 +54,7 @@ "composer.formatting.strikethrough": "Strikethrough", "composer.formatting.code": "Code", "composer.formatting.link": "Link", - "composer.formatting.picture": "Picture", + "composer.formatting.picture": "Image Link", "composer.upload-picture": "Upload Image", "composer.upload-file": "Upload File", "composer.zen_mode": "Zen Mode", diff --git a/public/language/fi/top.json b/public/language/fi/top.json new file mode 100644 index 0000000000..b8a05bfa5f --- /dev/null +++ b/public/language/fi/top.json @@ -0,0 +1,4 @@ +{ + "title": "Top", + "no_top_topics": "No top topics" +} \ No newline at end of file diff --git a/public/language/fi/topic.json b/public/language/fi/topic.json index 593bab2c90..671365475b 100644 --- a/public/language/fi/topic.json +++ b/public/language/fi/topic.json @@ -47,6 +47,7 @@ "restored-by": "Restored by", "moved-from-by": "Moved from %1 by", "queued-by": "Post queued for approval →", + "backlink": "Referenced by", "bookmark_instructions": "Klikkaa tästä palataksesi viimeisimpään luettuun viestiin tässä aiheessa", "flag-post": "Flag this post", "flag-user": "Flag this user", @@ -138,6 +139,7 @@ "composer.handle_placeholder": "Enter your name/handle here", "composer.discard": "Hylkää", "composer.submit": "Lähetä", + "composer.additional-options": "Additional Options", "composer.schedule": "Schedule", "composer.replying_to": "Vastaus viestiin %1", "composer.new_topic": "Uusi aihe", @@ -158,6 +160,7 @@ "newest_to_oldest": "Uusimmasta vanhimpaan", "most_votes": "Eniten ääniä", "most_posts": "Eniten viestejä", + "most_views": "Most Views", "stale.title": "Create new topic instead?", "stale.warning": "Aihe johon olet vastaamassa on melko vanha. Haluaisitko luoda mieluummin uuden aiheen ja viitata siitä tähän viestissäsi?", "stale.create": "Luo uusi aihe", diff --git a/public/language/fi/user.json b/public/language/fi/user.json index c6b4197fde..2fff42f480 100644 --- a/public/language/fi/user.json +++ b/public/language/fi/user.json @@ -94,6 +94,7 @@ "digest_off": "Pois päältä", "digest_daily": "Päivittäin", "digest_weekly": "Viikottain", + "digest_biweekly": "Bi-Weekly", "digest_monthly": "Kuukausittain", "has_no_follower": "Kukaan ei seuraa tätä käyttäjää :(", "follows_no_one": "Tämä käyttäjä ei seuraa ketään :(", diff --git a/public/language/fr/admin/advanced/events.json b/public/language/fr/admin/advanced/events.json index 6e7c591e35..3514894bce 100644 --- a/public/language/fr/admin/advanced/events.json +++ b/public/language/fr/admin/advanced/events.json @@ -3,6 +3,7 @@ "no-events": "Il n'y a aucun évènement.", "control-panel": "Panneau de contrôle des évènements", "delete-events": "Supprimer les évènements", + "confirm-delete-all-events": "Êtes-vous sûr de vouloir supprimer tous les événements enregistrés ?", "filters": "Filtres", "filters-apply": "Appliquer", "filter-type": "Évènements", diff --git a/public/language/fr/admin/dashboard.json b/public/language/fr/admin/dashboard.json index 6670e9d2db..7328224f86 100644 --- a/public/language/fr/admin/dashboard.json +++ b/public/language/fr/admin/dashboard.json @@ -56,8 +56,8 @@ "active-users.total": "Total", "active-users.connections": "Connexions", - "anonymous-registered-users": "Utilisateurs anonymes vs enregistrés", - "anonymous": "Anonymes", + "guest-registered-users": "Utilisateurs invités vs enregistrés", + "guest": "Invité", "registered": "Enregistrés", "user-presence": "Présence des utilisateurs", @@ -68,6 +68,7 @@ "unread": "Non lus", "high-presence-topics": "Sujets populaires", + "popular-searches": "Recherches populaires", "graphs.page-views": "Pages vues", "graphs.page-views-registered": "Membres", @@ -75,13 +76,14 @@ "graphs.page-views-bot": "Robots", "graphs.unique-visitors": "Visiteurs uniques", "graphs.registered-users": "Utilisateurs enregistrés", - "graphs.anonymous-users": "Utilisateurs anonymes", + "graphs.guest-users": "Utilisateurs invités", "last-restarted-by": "Redémarré par", "no-users-browsing": "Aucun utilisateur connecté", "back-to-dashboard": "Retour au Tableau de bord", "details.no-users": "Aucun utilisateur ne s'est joint dans le délai sélectionné", "details.no-topics": "Aucun sujet n'a été publié dans la période sélectionnée", + "details.no-searches": "Aucune recherche n'a encore été effectuée", "details.no-logins": "Aucune connexion n'a été enregistrée dans le délai sélectionné", "details.logins-static": "NodeBB n'enregistre que les données de session pendant %1 jours, et le tableau ci-dessous n'affichera donc que les dernières sessions actives", "details.logins-login-time": "Heure de connexion" diff --git a/public/language/fr/admin/development/info.json b/public/language/fr/admin/development/info.json index 479b30a80f..2b20a90ad6 100644 --- a/public/language/fr/admin/development/info.json +++ b/public/language/fr/admin/development/info.json @@ -8,7 +8,11 @@ "nodejs": "nodejs", "online": "en ligne", "git": "git", - "memory": "mémoire", + "process-memory": "mémoire de processus", + "system-memory": "mémoire système", + "used-memory-process": "Mémoire utilisée par processus", + "used-memory-os": "Mémoire système utilisée", + "total-memory-os": "Mémoire système totale", "load": "Charge du système", "cpu-usage": "Utilisation du processeur", "uptime": "disponibilité", diff --git a/public/language/fr/admin/extend/rewards.json b/public/language/fr/admin/extend/rewards.json index b1db437fc0..9fb342ce60 100644 --- a/public/language/fr/admin/extend/rewards.json +++ b/public/language/fr/admin/extend/rewards.json @@ -8,8 +8,6 @@ "delete": "Supprimer", "enable": "Activer", "disable": "Désactiver", - "control-panel": "Contrôle des récompenses", - "new-reward": "Nouvelle récompense", "alert.delete-success": "Récompense supprimée", "alert.no-inputs-found": "Récompense invalide - aucune entrée trouvée !", diff --git a/public/language/fr/admin/general/dashboard.json b/public/language/fr/admin/general/dashboard.json deleted file mode 100644 index c3157a46dd..0000000000 --- a/public/language/fr/admin/general/dashboard.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "forum-traffic": "Trafic du forum", - "page-views": "Pages vues", - "unique-visitors": "Visiteurs uniques", - "new-users": "Nouvel utilisateur", - "posts": "Messages", - "topics": "Sujets", - "page-views-seven": "7 derniers jours", - "page-views-thirty": "30 derniers jours", - "page-views-last-day": "Dernières 24 heures", - "page-views-custom": "Dates personnalisées", - "page-views-custom-start": "Début", - "page-views-custom-end": "Fin", - "page-views-custom-help": "Entrez une plage de date pour les vues que vous souhaitez afficher. Si aucun sélecteur de date n'est disponible, le format de date accepté est YYYY-MM-DD.", - "page-views-custom-error": "Veuillez entrer une plage de date valide dans le format suivant : YYYY-MM-DD", - - "stats.yesterday": "Hier", - "stats.today": "Aujourd'hui", - "stats.last-week": "Semaine dernière", - "stats.this-week": "Cette semaine", - "stats.last-month": "Mois dernier", - "stats.this-month": "Ce mois", - "stats.all": "Tous les temps", - - "updates": "Mises à jour", - "running-version": "NodeBB v%1 est actuellement installé.", - "keep-updated": "Assurez-vous que votre version de NodeBB est à jour pour les derniers patchs de sécurité et correctifs de bugs.", - "up-to-date": "

Votre version est à jour

", - "upgrade-available": "

Une nouvelle version (v%1) est disponible. Veuillez mettre à jour NodeBB.

", - "prerelease-upgrade-available": "

Votre version est dépassée. Une nouvelle version (v%1) est disponible. Veuillez mettre à jour NodeBB.

", - "prerelease-warning": "

Ceci est une version préliminaire de NodeBB. Des bugs inattendus peuvent se produire.

", - "running-in-development": "Le forum est en mode développement. Il peut être sujet à certaines vulnérabilités, veuillez contacter votre administrateur système.", - "latest-lookup-failed": "

Erreur de vérification de la dernière version disponible de NodeBB

", - - "notices": "Informations", - "restart-not-required": "Pas de redémarrage nécessaire", - "restart-required": "Redémarrage requis", - "search-plugin-installed": "Le plugin de recherche est installé", - "search-plugin-not-installed": "Le plugin de recherche n'est pas installé", - "search-plugin-tooltip": "Installer un plugin de recherche depuis la page des plugins pour activer la fonctionnalité de recherche", - - "control-panel": "Contrôle du système", - "rebuild-and-restart": "Régénérer & Redémarrer", - "restart": "Redémarrer", - "restart-warning": "Régénérer ou redémarrer NodeBB coupera toutes les connexions existantes pendant quelques secondes. ", - "restart-disabled": "La régénération et le redémarrage de votre forum ont été désactivés car vous ne semblez pas les exécuter à l'aide du serveur approprié.", - "maintenance-mode": "Mode maintenance", - "maintenance-mode-title": "Cliquez ici pour passer NodeBB en mode maintenance", - "realtime-chart-updates": "Mises à jour des graphiques en temps réel", - - "active-users": "Utilisateurs actifs", - "active-users.users": "Utilisateurs", - "active-users.guests": "Invités", - "active-users.total": "Total", - "active-users.connections": "Connexions", - - "anonymous-registered-users": "Utilisateurs anonymes vs enregistrés", - "anonymous": "Anonymes", - "registered": "Enregistrés", - - "user-presence": "Présence des utilisateurs", - "on-categories": "Sur la liste des catégories", - "reading-posts": "Lit des messages", - "browsing-topics": "Parcourt les sujets", - "recent": "Récents", - "unread": "Non lus", - - "high-presence-topics": "Sujets populaires", - - "graphs.page-views": "Pages vues", - "graphs.page-views-registered": "Membres", - "graphs.page-views-guest": "Invités", - "graphs.page-views-bot": "Robots", - "graphs.unique-visitors": "Visiteurs uniques", - "graphs.registered-users": "Utilisateurs enregistrés", - "graphs.anonymous-users": "Utilisateurs anonymes", - "last-restarted-by": "Redémarré par", - "no-users-browsing": "Aucun utilisateur connecté" -} diff --git a/public/language/fr/admin/general/homepage.json b/public/language/fr/admin/general/homepage.json deleted file mode 100644 index 3efe41fe65..0000000000 --- a/public/language/fr/admin/general/homepage.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "home-page": "Page d'accueil", - "description": "Choisissez la page affichée lorsque les utilisateurs naviguent à la racine de votre forum.", - "home-page-route": "Route de la page d'accueil", - "custom-route": "Route personnalisée", - "allow-user-home-pages": "Permettre aux utilisateurs de choisir une page d'accueil personnalisée", - "home-page-title": "Titre de la page d'accueil (par défaut \"Accueil\")" -} \ No newline at end of file diff --git a/public/language/fr/admin/general/languages.json b/public/language/fr/admin/general/languages.json deleted file mode 100644 index 51ee9f7f01..0000000000 --- a/public/language/fr/admin/general/languages.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "language-settings": "Réglages linguistiques", - "description": "La langue par défaut détermine les réglages pour tous les utilisateurs qui visitent votre forum.
Les utilisateurs peuvent ensuite modifier la langue par défaut sur leur page de réglages.", - "default-language": "Langue par défaut", - "auto-detect": "Détection automatique de la langue pour les invités" -} \ No newline at end of file diff --git a/public/language/fr/admin/general/navigation.json b/public/language/fr/admin/general/navigation.json deleted file mode 100644 index 02d6a7fbeb..0000000000 --- a/public/language/fr/admin/general/navigation.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "icon": "Icône :", - "change-icon": "changer", - "route": "Route :", - "tooltip": "Info-bulle :", - "text": "Texte :", - "text-class": "Classe de texte : optionnel", - "class": "Classe: facultatif", - "id": "ID : optionnel", - - "properties": "Propriétés :", - "groups": "Groupes:", - "open-new-window": "Ouvrir dans une nouvelle fenêtre", - - "btn.delete": "Supprimer", - "btn.disable": "Désactiver", - "btn.enable": "Activer", - - "available-menu-items": "Éléments de menu disponibles", - "custom-route": "Route personnalisée", - "core": "cœur", - "plugin": "plugin" -} \ No newline at end of file diff --git a/public/language/fr/admin/general/social.json b/public/language/fr/admin/general/social.json deleted file mode 100644 index 59e9e4e326..0000000000 --- a/public/language/fr/admin/general/social.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "post-sharing": "Partage de message", - "info-plugins-additional": "Les plugins peuvent ajouter de nouveaux réseaux pour partager des messages.", - "save-success": "Sauvegarde réussie !" -} \ No newline at end of file diff --git a/public/language/fr/admin/general/sounds.json b/public/language/fr/admin/general/sounds.json deleted file mode 100644 index 8ec037f62b..0000000000 --- a/public/language/fr/admin/general/sounds.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "notifications": "Notifications", - "chat-messages": "Discussions", - "play-sound": "Jouer", - "incoming-message": "Message entrant", - "outgoing-message": "Message sortant", - "upload-new-sound": "Envoyer un nouveau son", - "saved": "Réglages sauvegardés" -} \ No newline at end of file diff --git a/public/language/fr/admin/manage/digest.json b/public/language/fr/admin/manage/digest.json index cbe54df27a..2512debb1f 100644 --- a/public/language/fr/admin/manage/digest.json +++ b/public/language/fr/admin/manage/digest.json @@ -13,6 +13,7 @@ "resent-single": "Lettre d'activité envoyée", "resent-day": "Lettre d'activités quotidienne envoyée", "resent-week": "Lettre d'activité hebdomadaire envoyée", + "resent-biweek": "Lettre d'activité envoyée deux fois par semaine", "resent-month": "Lettre d'activité mensuel envoyé", "null": "Jamais", "manual-run": "Lancer manuellement l'envoi:", diff --git a/public/language/fr/admin/manage/privileges.json b/public/language/fr/admin/manage/privileges.json index 574a6b5040..0d6fac887d 100644 --- a/public/language/fr/admin/manage/privileges.json +++ b/public/language/fr/admin/manage/privileges.json @@ -51,13 +51,13 @@ "alert.saved": "Changements de privilèges enregistrés et appliqués", "alert.confirm-discard": "Êtes-vous sûr de vouloir annuler vos modifications de privilèges ?", "alert.discarded": "Modifications de privilèges annulés", - "alert.confirm-copyToAll": "Are you sure you wish to apply this set of %1 to all categories?", - "alert.confirm-copyToAllGroup": "Are you sure you wish to apply this group's set of %1 to all categories?", - "alert.confirm-copyToChildren": "Are you sure you wish to apply this set of %1 to all descendant (child) categories?", - "alert.confirm-copyToChildrenGroup": "Are you sure you wish to apply this group's set of %1 to all descendant (child) categories?", + "alert.confirm-copyToAll": "Voulez-vous vraiment appliquer cet ensemble de %1 à toutes les catégories?", + "alert.confirm-copyToAllGroup": "Voulez-vous vraiment appliquer l'ensemble de %1 de ce groupe à toutes les catégories?", + "alert.confirm-copyToChildren": "Voulez-vous vraiment appliquer cet ensemble de %1 à toutes les catégories incluses (enfants)?", + "alert.confirm-copyToChildrenGroup": "Voulez-vous vraiment appliquer l'ensemble de %1 de ce groupe à toutes les catégories incluses (enfants)?", "alert.no-undo": "Cette action ne peut pas être annulée.", "alert.admin-warning": "Les administrateurs obtiennent implicitement tous les privilèges", - "alert.copyPrivilegesFrom-title": "Select a category to copy from", - "alert.copyPrivilegesFrom-warning": "This will copy %1 from the selected category.", - "alert.copyPrivilegesFromGroup-warning": "This will copy this group's set of %1 from the selected category." + "alert.copyPrivilegesFrom-title": "Sélectionnez une catégorie à copier", + "alert.copyPrivilegesFrom-warning": "Cela copiera %1 de la catégorie sélectionnée.", + "alert.copyPrivilegesFromGroup-warning": "Cela copiera l'ensemble de %1 de ce groupe à partir de la catégorie sélectionnée." } \ No newline at end of file diff --git a/public/language/fr/admin/manage/uploads.json b/public/language/fr/admin/manage/uploads.json index a0a8608e07..8f1e276a6f 100644 --- a/public/language/fr/admin/manage/uploads.json +++ b/public/language/fr/admin/manage/uploads.json @@ -6,6 +6,6 @@ "size/filecount": "Taille / nombre de fichiers", "confirm-delete": "Voulez-vous vraiment supprimer ce fichier?", "filecount": "%1 fichiers", - "new-folder": "New Folder", - "name-new-folder": "Enter a name for new the folder" + "new-folder": "Nouveau Dossier", + "name-new-folder": "Entrez un nom pour le nouveau dossier" } \ No newline at end of file diff --git a/public/language/fr/admin/manage/users.json b/public/language/fr/admin/manage/users.json index 3cc2ce905e..4b98478d81 100644 --- a/public/language/fr/admin/manage/users.json +++ b/public/language/fr/admin/manage/users.json @@ -47,6 +47,7 @@ "users.uid": "uid", "users.username": "nom d'utilisateur", "users.email": "e-mail", + "users.no-email": "(pas e-mail)", "users.ip": "IP", "users.postcount": "nombre de sujets", "users.reputation": "réputation", diff --git a/public/language/fr/admin/menu.json b/public/language/fr/admin/menu.json index 3cb96440ec..196f86ce5c 100644 --- a/public/language/fr/admin/menu.json +++ b/public/language/fr/admin/menu.json @@ -4,6 +4,7 @@ "dashboard/logins": "Connexions", "dashboard/users": "Utilisateurs", "dashboard/topics": "Sujets", + "dashboard/searches": "Recherches", "section-general": "Général", "section-manage": "Gestion", diff --git a/public/language/fr/admin/settings/cookies.json b/public/language/fr/admin/settings/cookies.json index 797294c699..b337057dfc 100644 --- a/public/language/fr/admin/settings/cookies.json +++ b/public/language/fr/admin/settings/cookies.json @@ -4,7 +4,7 @@ "consent.message": "Message de notification", "consent.acceptance": "Message d'acceptation", "consent.link-text": "Texte du lien vers la politique de confidentialité", - "consent.link-url": "Policy Link URL", + "consent.link-url": "URL du lien Policy", "consent.blank-localised-default": "Laisser vide pour utiliser les textes localisés par défaut de NodeBB", "settings": "Réglages", "cookie-domain": "Domaine de session du cookie", diff --git a/public/language/fr/admin/settings/email.json b/public/language/fr/admin/settings/email.json index 9de8227d8d..56956a04ca 100644 --- a/public/language/fr/admin/settings/email.json +++ b/public/language/fr/admin/settings/email.json @@ -6,7 +6,7 @@ "from-help": "Le nom de l’expéditeur à afficher dans l'e-mail", "smtp-transport": "Protocole SMTP", - "smtp-transport.enabled": "Utiliser un server extérieur pour envoyer les emails", + "smtp-transport.enabled": "Activer l'envoi via SMTP", "smtp-transport-help": "Vous pouvez sélectionner depuis une liste de services ou entrer un service personnalisé.", "smtp-transport.service": "Sélectionner un service", "smtp-transport.service-custom": "Service personnalisé", @@ -37,6 +37,10 @@ "subscriptions.hour": "Heure d'envoi", "subscriptions.hour-help": "Veuillez entrer un nombre représentant l'heure à laquelle envoyer les lettres d'activités (c'est à dire 0 pour minuit, 17 pour 5:00 pm). Gardez à l'esprit qu'il s'agit de l'heure du serveur, et peut ne pas correspondre à votre heure locale.
L'heure du serveur est :
La prochaine lettre d'activités sera envoyée à ", "notifications.remove-images": "Supprimer les images des notifications par e-mail", + "require-email-address": "Exiger une adresse e-mail aux nouveaux utilisateurs ", + "require-email-address-warning": "Par défaut, les utilisateurs peuvent refuser de saisir une adresse e-mail. L'activation de cette option oblige de renseigner une une adresse e-mail lors de l'inscription. Ne garantit pas que l'utilisateur entrera adresse e-mail valide, ni même une adresse qu'il possède.", "include-unverified-emails": "Envoyer des mails aux destinataires qui n'ont pas explicitement confirmé leurs mails", - "include-unverified-warning": "Par défaut, les utilisateurs dont les mails sont associés à leur compte ont déjà été vérifiés, mais il existe des situations où ce n'est pas le cas (par exemple, les connexions SSO, les utilisateurs bénéficiant de droits acquis, etc.). Activez ce paramètre à vos risques et périls – l'envoi de mails à des adresses non vérifiées peut constituer une violation des lois anti-spam régionales." -} \ No newline at end of file + "include-unverified-warning": "Par défaut, les utilisateurs dont les mails sont associés à leur compte ont déjà été vérifiés, mais il existe des situations où ce n'est pas le cas (par exemple, les connexions SSO, les utilisateurs bénéficiant de droits acquis, etc.). Activez ce paramètre à vos risques et périls – l'envoi de mails à des adresses non vérifiées peut constituer une violation des lois anti-spam régionales.", + "prompt": "Inviter les utilisateurs à saisir ou à confirmer leurs emails", + "prompt-help": "Si un utilisateur n'a pas défini d'email ou si son email n'est pas confirmé, un avertissement s'affichera à l'écran." +} diff --git a/public/language/fr/admin/settings/general.json b/public/language/fr/admin/settings/general.json index 0670d17203..0f871bec8c 100644 --- a/public/language/fr/admin/settings/general.json +++ b/public/language/fr/admin/settings/general.json @@ -3,9 +3,9 @@ "title": "Titre du site", "title.short": "Titre court", "title.short-placeholder": "Si aucun titre court n'est spécifié, le titre du site sera utilisé", - "title.url": "URL", + "title.url": "URL du lien du titre", "title.url-placeholder": "URL du titre du site", - "title.url-help": "Adresse à laquelle l'utilisateur est renvoyé lors du clic sur le titre. Si ce champ est vide, l'adresse est celle de l'index du forum.", + "title.url-help": "Lorsque le titre est cliqué, il renvoi les utilisateurs à cette adresse. Si laissé vide, l'utilisateur sera envoyé à l'index du forum.
Remarque : il ne s'agit pas de l'URL externe utilisée dans les e-mails, etc. Elle est définie par la propriété url dans config.json", "title.name": "Nom de votre communauté", "title.show-in-header": "Afficher le titre du site dans l'en-tête", "browser-title": "Titre dans le navigateur", @@ -20,9 +20,9 @@ "logo.image": "Image", "logo.image-placeholder": "Chemin vers un logo à afficher dans l'en-tête du site", "logo.upload": "Télécharger", - "logo.url": "URL", + "logo.url": "URL du lien du logo", "logo.url-placeholder": "L'URL du logo du site", - "logo.url-help": "Quand ils cliquent sur le logo, envoyer les utilisateurs vers cette adresse. Si ce champ est vide, l'utilisateur sera envoyé à l'index du forum.", + "logo.url-help": "Lorsque le logo est cliqué, il renvoi les utilisateurs à cette adresse. Si laissé vide, l'utilisateur sera envoyé à l'index du forum.
Remarque : il ne s'agit pas de l'URL externe utilisée dans les e-mails, etc. Elle est définie par la propriété url dans config.json", "logo.alt-text": "Texte alternatif (alt)", "log.alt-text-placeholder": "Texte alternatif pour l'accessibilité", "favicon": "Favicon", @@ -47,4 +47,4 @@ "undo-timeout": "Annuler le délai d'attente", "undo-timeout-help": "Certaines opérations telles que le déplacement de sujets permettront au modérateur d'annuler son action dans un certain délai. Réglez sur 0 pour désactiver complètement l'annulation.", "topic-tools": "Outils pour sujets" -} \ No newline at end of file +} diff --git a/public/language/fr/admin/settings/navigation.json b/public/language/fr/admin/settings/navigation.json index 02d6a7fbeb..1305aed145 100644 --- a/public/language/fr/admin/settings/navigation.json +++ b/public/language/fr/admin/settings/navigation.json @@ -11,6 +11,8 @@ "properties": "Propriétés :", "groups": "Groupes:", "open-new-window": "Ouvrir dans une nouvelle fenêtre", + "dropdown": "Menu déroulant", + "dropdown-placeholder": "Placez vos éléments de menu déroulant ci-dessous, par exemple :
<li><a href="https://myforum.com">Link 1</a></li>", "btn.delete": "Supprimer", "btn.disable": "Désactiver", @@ -20,4 +22,4 @@ "custom-route": "Route personnalisée", "core": "cœur", "plugin": "plugin" -} \ No newline at end of file +} diff --git a/public/language/fr/admin/settings/notifications.json b/public/language/fr/admin/settings/notifications.json index 87ee6b31e0..9a0978d167 100644 --- a/public/language/fr/admin/settings/notifications.json +++ b/public/language/fr/admin/settings/notifications.json @@ -2,5 +2,6 @@ "notifications": "Notifications", "welcome-notification": "Notification de bienvenue", "welcome-notification-link": "Lien de notification de bienvenue", - "welcome-notification-uid": "Notification de bienvenue de l'utilisateur (UID)" + "welcome-notification-uid": "Notification de bienvenue de l'utilisateur (UID)", + "post-queue-notification-uid": "File d'attente d l'Utilisateur (UID)" } \ No newline at end of file diff --git a/public/language/fr/admin/settings/post.json b/public/language/fr/admin/settings/post.json index b8cbb4c803..9a546492b1 100644 --- a/public/language/fr/admin/settings/post.json +++ b/public/language/fr/admin/settings/post.json @@ -40,6 +40,7 @@ "teaser.last-post": "Dernier – Affiche le dernier message, ou celui d'origine, si il n'y a pas de réponse", "teaser.last-reply": "Dernier – Affiche le dernier message, ou \"Aucune réponse\" si il n'y a pas de réponse", "teaser.first": "Premier", + "showPostPreviewsOnHover": "Afficher un aperçu des messages au survol des liens", "unread": "Paramètres des messages non lus", "unread.cutoff": "Nombre de jours pour les messages non-lus", "unread.min-track-last": "Nombre minimum de messages dans le sujet avant de garder en mémoire le dernier message lu", @@ -56,6 +57,9 @@ "composer.show-help": "Afficher l'onglet \"Aide\"", "composer.enable-plugin-help": "Autoriser les plugins à modifier l'onglet d'aide", "composer.custom-help": "Message d'aide personnalisé", + "backlinks": "Backlinks", + "backlinks.enabled": "Activer les backlinks de sujet", + "backlinks.help": "Si un message fait référence à un autre sujet, un lien vers le message sera inséré dans le sujet référencé.", "ip-tracking": "Suivi d'IP", "ip-tracking.each-post": "Suivre l'adresse IP pour chaque message", "enable-post-history": "Activer l'historique des publications" diff --git a/public/language/fr/admin/settings/user.json b/public/language/fr/admin/settings/user.json index 57d2126740..0a736319e6 100644 --- a/public/language/fr/admin/settings/user.json +++ b/public/language/fr/admin/settings/user.json @@ -71,6 +71,7 @@ "digest-freq.off": "Désactivé", "digest-freq.daily": "Quotidien", "digest-freq.weekly": "Hebdomadaire", + "digest-freq.biweekly": "Deux fois par semaine", "digest-freq.monthly": "Mensuel", "email-chat-notifs": "Envoyer un e-mail si un nouveau message de chat arrive lorsque je ne suis pas en ligne", "email-post-notif": "Envoyer un email lors de réponses envoyées aux sujets auxquels que je suis", diff --git a/public/language/fr/error.json b/public/language/fr/error.json index 4ee0be6ce4..6f53fa2605 100644 --- a/public/language/fr/error.json +++ b/public/language/fr/error.json @@ -25,17 +25,18 @@ "invalid-event": "Événement non valide: %1", "local-login-disabled": "Le système de connexion local a été désactivé pour les comptes sans privilèges.", "csrf-invalid": "Nous ne pouvons pas vous connectez, probablement car votre session a expiré. Merci de réessayer.", - "invalid-path": "Invalid path", - "folder-exists": "Folder exists", + "invalid-path": "Chemin invalide", + "folder-exists": "Le dossier existe", "invalid-pagination-value": "Valeur de pagination invalide. Celle-ci doit être comprise entre %1 et %2.", "username-taken": "Nom d’utilisateur déjà utilisé", "email-taken": "Email déjà utilisé", "email-nochange": "Le mail saisi est déjà enregistré.", "email-invited": "Cet utilisateur a déjà été invité.", - "email-not-confirmed": "Posting in some categories or topics is enabled once your email is confirmed, please click here to send a confirmation email.", + "email-not-confirmed": "La publication dans certaines catégories ou sujets sera activée après confirmation de e-mail, veuillez cliquer ici pour envoyer un e-mail de confirmation.", "email-not-confirmed-chat": "Il ne vous est pas possible d'utiliser le chat tant que votre adresse email n'a pas été vérifiée. Veuillez cliquer ici pour confirmer votre adresse email.", "email-not-confirmed-email-sent": "Votre email n'a pas encore été confirmé, veuillez vérifier votre boîte mail. Vous ne pourrez pas poster ou discuter avant que votre email ne soit confirmé.", "no-email-to-confirm": "Votre compte n'a pas d'adresse mail définie. Un mail est nécessaire pour la récupération du compte. Veuillez cliquer ici pour entrer un courriel.", + "user-doesnt-have-email": "L'utilisateur « %1 » n'a pas d'adresse e-mail.", "email-confirm-failed": "Votre adresse email n'a pas pu être vérifiée. Veuillez ré-essayer plus tard.", "confirm-email-already-sent": "L'email de confirmation a déjà été envoyé. Veuillez attendre %1 minute(s) avant de redemander un nouvel envoi.", "sendmail-not-found": "L'application d'envoi de mail est introuvable, assurez-vous qu'elle est installée et que l'utilisateur ayant démarré NodeBB ait des droits suffisants.", @@ -103,6 +104,7 @@ "already-bookmarked": "Vous avez déjà mis un marque-page", "already-unbookmarked": "Vous avez déjà retiré un marque-page", "cant-ban-other-admins": "Vous ne pouvez pas bannir les autres administrateurs !", + "cant-make-banned-users-admin": "Vous ne pouvez pas mettre des utilisateurs bannis en administrateur.", "cant-remove-last-admin": "Vous êtes le seul administrateur. Ajoutez un autre utilisateur en tant qu'administrateur avant de vous retirer.", "account-deletion-disabled": "La suppression du compte est désactivée", "cant-delete-admin": "Veuillez retirer les droits d'administration de ce compte avant de tenter de le supprimer.", diff --git a/public/language/fr/modules.json b/public/language/fr/modules.json index 4e665f520a..d1c547a30c 100644 --- a/public/language/fr/modules.json +++ b/public/language/fr/modules.json @@ -54,7 +54,7 @@ "composer.formatting.strikethrough": "Barré", "composer.formatting.code": "Code", "composer.formatting.link": "Lien", - "composer.formatting.picture": "Image", + "composer.formatting.picture": "Lien d'image", "composer.upload-picture": "Envoyer une image", "composer.upload-file": "Envoyer un fichier", "composer.zen_mode": "Mode Zen", diff --git a/public/language/fr/top.json b/public/language/fr/top.json new file mode 100644 index 0000000000..b182f2d4e9 --- /dev/null +++ b/public/language/fr/top.json @@ -0,0 +1,4 @@ +{ + "title": "Haut", + "no_top_topics": "Aucun sujet principal" +} \ No newline at end of file diff --git a/public/language/fr/topic.json b/public/language/fr/topic.json index e93c59cb01..c3157a0148 100644 --- a/public/language/fr/topic.json +++ b/public/language/fr/topic.json @@ -21,7 +21,7 @@ "edit": "Éditer", "delete": "Supprimer", "delete-event": "Supprimer l'événement", - "delete-event-confirm": "Voulez-vous vraiment supprimer cet événement ?", + "delete-event-confirm": "Êtes-vous sûr de vouloir supprimer cet événement ?", "purge": "Supprimer définitivement", "restore": "Restaurer", "move": "Déplacer", @@ -47,6 +47,7 @@ "restored-by": "Restauré par", "moved-from-by": "Déplacé de %1 par", "queued-by": "Message en attente d'approbation →", + "backlink": "Référencé par", "bookmark_instructions": "Cliquez ici pour retourner au dernier message lu de ce fil.", "flag-post": "Signaler ce message", "flag-user": "Signaler cet utilisateur", @@ -138,6 +139,7 @@ "composer.handle_placeholder": "Entrez votre nom/identifiant ici", "composer.discard": "Abandonner", "composer.submit": "Envoyer", + "composer.additional-options": "Options additionnelles", "composer.schedule": "Planification", "composer.replying_to": "En réponse à %1", "composer.new_topic": "Nouveau sujet", @@ -158,6 +160,7 @@ "newest_to_oldest": "Du plus récent au plus ancien", "most_votes": "Les plus votés", "most_posts": "Meilleurs messages", + "most_views": "Les plus vues", "stale.title": "Créer un nouveau sujet à la place ?", "stale.warning": "Le sujet auquel vous répondez est assez ancien. Ne voudriez-vous pas créer un nouveau sujet à la place et placer une référence vers celui-ci dans votre réponse ?", "stale.create": "Créer un nouveau sujet", diff --git a/public/language/fr/user.json b/public/language/fr/user.json index 36ab11712b..df75626619 100644 --- a/public/language/fr/user.json +++ b/public/language/fr/user.json @@ -94,6 +94,7 @@ "digest_off": "Désactivé", "digest_daily": "Quotidien", "digest_weekly": "Hebdomadaire", + "digest_biweekly": "Deux fois par semaine", "digest_monthly": "Mensuel", "has_no_follower": "Personne n'est abonné à cet utilisateur :(", "follows_no_one": "Cet utilisateur n'est abonné à personne :(", diff --git a/public/language/gl/admin/advanced/events.json b/public/language/gl/admin/advanced/events.json index 56d9457971..b2c2033fb5 100644 --- a/public/language/gl/admin/advanced/events.json +++ b/public/language/gl/admin/advanced/events.json @@ -3,6 +3,7 @@ "no-events": "There are no events", "control-panel": "Events Control Panel", "delete-events": "Delete Events", + "confirm-delete-all-events": "Are you sure you want to delete all logged events?", "filters": "Filters", "filters-apply": "Apply Filters", "filter-type": "Event Type", diff --git a/public/language/gl/admin/dashboard.json b/public/language/gl/admin/dashboard.json index 0de31d4917..4d39626882 100644 --- a/public/language/gl/admin/dashboard.json +++ b/public/language/gl/admin/dashboard.json @@ -56,8 +56,8 @@ "active-users.total": "Total", "active-users.connections": "Connections", - "anonymous-registered-users": "Anonymous vs Registered Users", - "anonymous": "Anonymous", + "guest-registered-users": "Guest vs Registered Users", + "guest": "Guest", "registered": "Registered", "user-presence": "User Presence", @@ -68,6 +68,7 @@ "unread": "Unread", "high-presence-topics": "High Presence Topics", + "popular-searches": "Popular Searches", "graphs.page-views": "Page Views", "graphs.page-views-registered": "Page Views Registered", @@ -75,13 +76,14 @@ "graphs.page-views-bot": "Page Views Bot", "graphs.unique-visitors": "Unique Visitors", "graphs.registered-users": "Registered Users", - "graphs.anonymous-users": "Anonymous Users", + "graphs.guest-users": "Guest Users", "last-restarted-by": "Last restarted by", "no-users-browsing": "No users browsing", "back-to-dashboard": "Back to Dashboard", "details.no-users": "No users have joined within the selected timeframe", "details.no-topics": "No topics have been posted within the selected timeframe", + "details.no-searches": "No searches have been made yet", "details.no-logins": "No logins have been recorded within the selected timeframe", "details.logins-static": "NodeBB only saves session data for %1 days, and so this table below will only show the most recently active sessions", "details.logins-login-time": "Login Time" diff --git a/public/language/gl/admin/development/info.json b/public/language/gl/admin/development/info.json index 1003af1a5f..11202d9c3a 100644 --- a/public/language/gl/admin/development/info.json +++ b/public/language/gl/admin/development/info.json @@ -8,7 +8,11 @@ "nodejs": "nodejs", "online": "online", "git": "git", - "memory": "memory", + "process-memory": "process memory", + "system-memory": "system memory", + "used-memory-process": "Used memory by process", + "used-memory-os": "Used system memory", + "total-memory-os": "Total system memory", "load": "system load", "cpu-usage": "cpu usage", "uptime": "uptime", diff --git a/public/language/gl/admin/extend/rewards.json b/public/language/gl/admin/extend/rewards.json index 5383a90b33..df89d441a7 100644 --- a/public/language/gl/admin/extend/rewards.json +++ b/public/language/gl/admin/extend/rewards.json @@ -8,8 +8,6 @@ "delete": "Delete", "enable": "Enable", "disable": "Disable", - "control-panel": "Rewards Control", - "new-reward": "New Reward", "alert.delete-success": "Successfully deleted reward", "alert.no-inputs-found": "Illegal reward - no inputs found!", diff --git a/public/language/gl/admin/general/dashboard.json b/public/language/gl/admin/general/dashboard.json deleted file mode 100644 index 37ae537786..0000000000 --- a/public/language/gl/admin/general/dashboard.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "forum-traffic": "Forum Traffic", - "page-views": "Page Views", - "unique-visitors": "Unique Visitors", - "new-users": "New Users", - "posts": "Posts", - "topics": "Topics", - "page-views-seven": "Last 7 Days", - "page-views-thirty": "Last 30 Days", - "page-views-last-day": "Last 24 hours", - "page-views-custom": "Custom Date Range", - "page-views-custom-start": "Range Start", - "page-views-custom-end": "Range End", - "page-views-custom-help": "Enter a date range of page views you would like to view. If no date picker is available, the accepted format is YYYY-MM-DD", - "page-views-custom-error": "Please enter a valid date range in the format YYYY-MM-DD", - - "stats.yesterday": "Yesterday", - "stats.today": "Today", - "stats.last-week": "Last Week", - "stats.this-week": "This Week", - "stats.last-month": "Last Month", - "stats.this-month": "This Month", - "stats.all": "All Time", - - "updates": "Updates", - "running-version": "You are running NodeBB v%1.", - "keep-updated": "Always make sure that your NodeBB is up to date for the latest security patches and bug fixes.", - "up-to-date": "

You are up-to-date

", - "upgrade-available": "

A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-upgrade-available": "

This is an outdated pre-release version of NodeBB. A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-warning": "

This is a pre-release version of NodeBB. Unintended bugs may occur.

", - "running-in-development": "Forum is running in development mode. The forum may be open to potential vulnerabilities; please contact your system administrator.", - "latest-lookup-failed": "

Failed to look up latest available version of NodeBB

", - - "notices": "Notices", - "restart-not-required": "Restart not required", - "restart-required": "Restart required", - "search-plugin-installed": "Search Plugin installed", - "search-plugin-not-installed": "Search Plugin not installed", - "search-plugin-tooltip": "Install a search plugin from the plugin page in order to activate search functionality", - - "control-panel": "System Control", - "rebuild-and-restart": "Rebuild & Restart", - "restart": "Restart", - "restart-warning": "Rebuilding or Restarting your NodeBB will drop all existing connections for a few seconds.", - "restart-disabled": "Rebuilding and Restarting your NodeBB has been disabled as you do not seem to be running it via the appropriate daemon.", - "maintenance-mode": "Maintenance Mode", - "maintenance-mode-title": "Click here to set up maintenance mode for NodeBB", - "realtime-chart-updates": "Realtime Chart Updates", - - "active-users": "Active Users", - "active-users.users": "Users", - "active-users.guests": "Guests", - "active-users.total": "Total", - "active-users.connections": "Connections", - - "anonymous-registered-users": "Anonymous vs Registered Users", - "anonymous": "Anonymous", - "registered": "Registered", - - "user-presence": "User Presence", - "on-categories": "On categories list", - "reading-posts": "Reading posts", - "browsing-topics": "Browsing topics", - "recent": "Recent", - "unread": "Unread", - - "high-presence-topics": "High Presence Topics", - - "graphs.page-views": "Page Views", - "graphs.page-views-registered": "Page Views Registered", - "graphs.page-views-guest": "Page Views Guest", - "graphs.page-views-bot": "Page Views Bot", - "graphs.unique-visitors": "Unique Visitors", - "graphs.registered-users": "Registered Users", - "graphs.anonymous-users": "Anonymous Users", - "last-restarted-by": "Last restarted by", - "no-users-browsing": "No users browsing" -} diff --git a/public/language/gl/admin/general/homepage.json b/public/language/gl/admin/general/homepage.json deleted file mode 100644 index 7428d59eeb..0000000000 --- a/public/language/gl/admin/general/homepage.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "home-page": "Home Page", - "description": "Choose what page is shown when users navigate to the root URL of your forum.", - "home-page-route": "Home Page Route", - "custom-route": "Custom Route", - "allow-user-home-pages": "Allow User Home Pages", - "home-page-title": "Title of the home page (default \"Home\")" -} \ No newline at end of file diff --git a/public/language/gl/admin/general/languages.json b/public/language/gl/admin/general/languages.json deleted file mode 100644 index bdd57849b3..0000000000 --- a/public/language/gl/admin/general/languages.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "language-settings": "Language Settings", - "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language", - "auto-detect": "Auto Detect Language Setting for Guests" -} \ No newline at end of file diff --git a/public/language/gl/admin/general/navigation.json b/public/language/gl/admin/general/navigation.json deleted file mode 100644 index 13dd01aae7..0000000000 --- a/public/language/gl/admin/general/navigation.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "icon": "Icon:", - "change-icon": "change", - "route": "Route:", - "tooltip": "Tooltip:", - "text": "Text:", - "text-class": "Text Class: optional", - "class": "Class: optional", - "id": "ID: optional", - - "properties": "Properties:", - "groups": "Groups:", - "open-new-window": "Open in a new window", - - "btn.delete": "Delete", - "btn.disable": "Disable", - "btn.enable": "Enable", - - "available-menu-items": "Available Menu Items", - "custom-route": "Custom Route", - "core": "core", - "plugin": "plugin" -} \ No newline at end of file diff --git a/public/language/gl/admin/general/social.json b/public/language/gl/admin/general/social.json deleted file mode 100644 index 23aedfcfaa..0000000000 --- a/public/language/gl/admin/general/social.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "post-sharing": "Post Sharing", - "info-plugins-additional": "Plugins can add additional networks for sharing posts.", - "save-success": "Successfully saved Post Sharing Networks!" -} \ No newline at end of file diff --git a/public/language/gl/admin/general/sounds.json b/public/language/gl/admin/general/sounds.json deleted file mode 100644 index 95ccbde0f1..0000000000 --- a/public/language/gl/admin/general/sounds.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "notifications": "Notifications", - "chat-messages": "Chat Messages", - "play-sound": "Play", - "incoming-message": "Incoming Message", - "outgoing-message": "Outgoing Message", - "upload-new-sound": "Upload New Sound", - "saved": "Settings Saved" -} \ No newline at end of file diff --git a/public/language/gl/admin/manage/digest.json b/public/language/gl/admin/manage/digest.json index 8f3661698a..38c634d1f6 100644 --- a/public/language/gl/admin/manage/digest.json +++ b/public/language/gl/admin/manage/digest.json @@ -13,6 +13,7 @@ "resent-single": "Manual digest resend completed", "resent-day": "Daily digest resent", "resent-week": "Weekly digest resent", + "resent-biweek": "Bi-Weekly digest resent", "resent-month": "Monthly digest resent", "null": "Never", "manual-run": "Manual digest run:", diff --git a/public/language/gl/admin/manage/users.json b/public/language/gl/admin/manage/users.json index 38a065b954..2a3c0c4829 100644 --- a/public/language/gl/admin/manage/users.json +++ b/public/language/gl/admin/manage/users.json @@ -47,6 +47,7 @@ "users.uid": "uid", "users.username": "username", "users.email": "email", + "users.no-email": "(no email)", "users.ip": "IP", "users.postcount": "postcount", "users.reputation": "reputation", diff --git a/public/language/gl/admin/menu.json b/public/language/gl/admin/menu.json index 1c5ea73b4f..5b22fbeb36 100644 --- a/public/language/gl/admin/menu.json +++ b/public/language/gl/admin/menu.json @@ -4,6 +4,7 @@ "dashboard/logins": "Logins", "dashboard/users": "Users", "dashboard/topics": "Topics", + "dashboard/searches": "Searches", "section-general": "General", "section-manage": "Manage", diff --git a/public/language/gl/admin/settings/email.json b/public/language/gl/admin/settings/email.json index 9bdbde26a6..65f579d28c 100644 --- a/public/language/gl/admin/settings/email.json +++ b/public/language/gl/admin/settings/email.json @@ -6,7 +6,7 @@ "from-help": "The from name to display in the email.", "smtp-transport": "SMTP Transport", - "smtp-transport.enabled": "Use an external email server to send emails", + "smtp-transport.enabled": "Enable SMTP Transport", "smtp-transport-help": "You can select from a list of well-known services or enter a custom one.", "smtp-transport.service": "Select a service", "smtp-transport.service-custom": "Custom Service", @@ -37,6 +37,10 @@ "subscriptions.hour": "Digest Hour", "subscriptions.hour-help": "Please enter a number representing the hour to send scheduled email digests (e.g. 0 for midnight, 17 for 5:00pm). Keep in mind that this is the hour according to the server itself, and may not exactly match your system clock.
The approximate server time is:
The next daily digest is scheduled to be sent ", "notifications.remove-images": "Remove images from email notifications", + "require-email-address": "Require new users to specify an email address", + "require-email-address-warning": "By default, users can opt-out of entering an email address. Enabling this option means they have to enter an email address in order to proceed with registration. It does not ensure user will enter a real email address, nor even an address they own.", "include-unverified-emails": "Send emails to recipients who have not explicitly confirmed their emails", - "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws." -} \ No newline at end of file + "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws.", + "prompt": "Prompt users to enter or confirm their emails", + "prompt-help": "If a user does not have an email set, or their email is not confirmed, a warning will be shown on screen." +} diff --git a/public/language/gl/admin/settings/general.json b/public/language/gl/admin/settings/general.json index be7df90870..29b939861b 100644 --- a/public/language/gl/admin/settings/general.json +++ b/public/language/gl/admin/settings/general.json @@ -3,9 +3,9 @@ "title": "Site Title", "title.short": "Short Title", "title.short-placeholder": "If no short title is specified, the site title will be used", - "title.url": "URL", + "title.url": "Title Link URL", "title.url-placeholder": "The URL of the site title", - "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.", + "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "title.name": "Your Community Name", "title.show-in-header": "Show Site Title in Header", "browser-title": "Browser Title", @@ -20,9 +20,9 @@ "logo.image": "Image", "logo.image-placeholder": "Path to a logo to display on forum header", "logo.upload": "Upload", - "logo.url": "URL", + "logo.url": "Logo Link URL", "logo.url-placeholder": "The URL of the site logo", - "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.", + "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "logo.alt-text": "Alt Text", "log.alt-text-placeholder": "Alternative text for accessibility", "favicon": "Favicon", @@ -47,4 +47,4 @@ "undo-timeout": "Undo Timeout", "undo-timeout-help": "Some operations such as moving topics will allow for the moderator to undo their action within a certain timeframe. Set to 0 to disable undo completely.", "topic-tools": "Topic Tools" -} \ No newline at end of file +} diff --git a/public/language/gl/admin/settings/navigation.json b/public/language/gl/admin/settings/navigation.json index 13dd01aae7..7baca85096 100644 --- a/public/language/gl/admin/settings/navigation.json +++ b/public/language/gl/admin/settings/navigation.json @@ -11,6 +11,8 @@ "properties": "Properties:", "groups": "Groups:", "open-new-window": "Open in a new window", + "dropdown": "Dropdown", + "dropdown-placeholder": "Place your dropdown menu items below, ie:
<li><a href="https://myforum.com">Link 1</a></li>", "btn.delete": "Delete", "btn.disable": "Disable", @@ -20,4 +22,4 @@ "custom-route": "Custom Route", "core": "core", "plugin": "plugin" -} \ No newline at end of file +} diff --git a/public/language/gl/admin/settings/notifications.json b/public/language/gl/admin/settings/notifications.json index da6c9680a3..c6d8b928ce 100644 --- a/public/language/gl/admin/settings/notifications.json +++ b/public/language/gl/admin/settings/notifications.json @@ -2,5 +2,6 @@ "notifications": "Notifications", "welcome-notification": "Welcome Notification", "welcome-notification-link": "Welcome Notification Link", - "welcome-notification-uid": "Welcome Notification User (UID)" + "welcome-notification-uid": "Welcome Notification User (UID)", + "post-queue-notification-uid": "Post Queue User (UID)" } \ No newline at end of file diff --git a/public/language/gl/admin/settings/post.json b/public/language/gl/admin/settings/post.json index 27493aafbd..ab8245738c 100644 --- a/public/language/gl/admin/settings/post.json +++ b/public/language/gl/admin/settings/post.json @@ -40,6 +40,7 @@ "teaser.last-post": "Last – Show the latest post, including the original post, if no replies", "teaser.last-reply": "Last – Show the latest reply, or a \"No replies\" placeholder if no replies", "teaser.first": "First", + "showPostPreviewsOnHover": "Show a preview of posts when mouse overed", "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", @@ -56,6 +57,9 @@ "composer.show-help": "Show \"Help\" tab", "composer.enable-plugin-help": "Allow plugins to add content to the help tab", "composer.custom-help": "Custom Help Text", + "backlinks": "Backlinks", + "backlinks.enabled": "Enable topic backlinks", + "backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.", "ip-tracking": "IP Tracking", "ip-tracking.each-post": "Track IP Address for each post", "enable-post-history": "Enable Post History" diff --git a/public/language/gl/admin/settings/user.json b/public/language/gl/admin/settings/user.json index 48be13b75e..7923bf8cbe 100644 --- a/public/language/gl/admin/settings/user.json +++ b/public/language/gl/admin/settings/user.json @@ -71,6 +71,7 @@ "digest-freq.off": "Off", "digest-freq.daily": "Daily", "digest-freq.weekly": "Weekly", + "digest-freq.biweekly": "Bi-Weekly", "digest-freq.monthly": "Monthly", "email-chat-notifs": "Send an email if a new chat message arrives and I am not online", "email-post-notif": "Send an email when replies are made to topics I am subscribed to", diff --git a/public/language/gl/error.json b/public/language/gl/error.json index 1c5bc502c1..2d6e135643 100644 --- a/public/language/gl/error.json +++ b/public/language/gl/error.json @@ -34,8 +34,9 @@ "email-invited": "Email was already invited", "email-not-confirmed": "Posting in some categories or topics is enabled once your email is confirmed, please click here to send a confirmation email.", "email-not-confirmed-chat": "Non podes charlar ata que confirmes o teu correo, por favor pica aquí para confirmalo.", - "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You won't be able to post or chat until your email is confirmed.", - "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery. Please click here to enter an email.", + "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You may not be able to post in some categories or chat until your email is confirmed.", + "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery, and may be necessary for chatting and posting in some categories. Please click here to enter an email.", + "user-doesnt-have-email": "User \"%1\" does not have an email set.", "email-confirm-failed": "Non podemos confirmar o teu enderezo, por favor téntao de novo máis tarde.", "confirm-email-already-sent": "O correo de confirmación foi enviado, agarda %1 minute(s) para enviar outro.", "sendmail-not-found": "Non se atopa o executable \"sendmail\", por favor, asegúrate de que está instalado no teu sistema e que é accesible polo usuario que executa NodeBB. ", @@ -103,6 +104,7 @@ "already-bookmarked": "Xa marcaches esta mensaxe", "already-unbookmarked": "Xa desmarcaches esta mensaxe", "cant-ban-other-admins": "Non podes botar outros administradores!", + "cant-make-banned-users-admin": "You can't make banned users admin.", "cant-remove-last-admin": "Eres o único administrador. Engade outros administradores antes de quitarte a ti mesmo como administrador.", "account-deletion-disabled": "Account deletion is disabled", "cant-delete-admin": "Retirar privilexios de administrador desta conta antes de intentar borrala", diff --git a/public/language/gl/modules.json b/public/language/gl/modules.json index af391edcca..ea39947c26 100644 --- a/public/language/gl/modules.json +++ b/public/language/gl/modules.json @@ -54,7 +54,7 @@ "composer.formatting.strikethrough": "Tachado", "composer.formatting.code": "Code", "composer.formatting.link": "Ligazón", - "composer.formatting.picture": "Foto", + "composer.formatting.picture": "Image Link", "composer.upload-picture": "Subir foto", "composer.upload-file": "Subir arquivo", "composer.zen_mode": "Modo Zen", diff --git a/public/language/gl/top.json b/public/language/gl/top.json new file mode 100644 index 0000000000..b8a05bfa5f --- /dev/null +++ b/public/language/gl/top.json @@ -0,0 +1,4 @@ +{ + "title": "Top", + "no_top_topics": "No top topics" +} \ No newline at end of file diff --git a/public/language/gl/topic.json b/public/language/gl/topic.json index da2afd1bef..493bff32b8 100644 --- a/public/language/gl/topic.json +++ b/public/language/gl/topic.json @@ -47,6 +47,7 @@ "restored-by": "Restored by", "moved-from-by": "Moved from %1 by", "queued-by": "Post queued for approval →", + "backlink": "Referenced by", "bookmark_instructions": "Pica aquí para volver á última mensaxe lida neste tema ", "flag-post": "Flag this post", "flag-user": "Flag this user", @@ -138,6 +139,7 @@ "composer.handle_placeholder": "Enter your name/handle here", "composer.discard": "Descartar", "composer.submit": "Enviar", + "composer.additional-options": "Additional Options", "composer.schedule": "Schedule", "composer.replying_to": "En resposta a %1", "composer.new_topic": "Novo tema", @@ -158,6 +160,7 @@ "newest_to_oldest": "Máis novo a máis antigo", "most_votes": "Most Votes", "most_posts": "Most Posts", + "most_views": "Most Views", "stale.title": "Crear un novo tema no seu lugar?", "stale.warning": "O tema no que queres publicar é bastante vello. Queres crear un novo tema no seu lugar e incluir unha referencia a este na túa mensaxe?", "stale.create": "Crear un novo tema", diff --git a/public/language/gl/user.json b/public/language/gl/user.json index 7fb9ff2efb..7602c50bdf 100644 --- a/public/language/gl/user.json +++ b/public/language/gl/user.json @@ -94,6 +94,7 @@ "digest_off": "Desactivado", "digest_daily": "Diariamente", "digest_weekly": "Semanalmente", + "digest_biweekly": "Bi-Weekly", "digest_monthly": "Mensualmente", "has_no_follower": "Ninguén segue a este usuario :(", "follows_no_one": "Este usuario non sigue a ninguén :(", diff --git a/public/language/he/admin/advanced/events.json b/public/language/he/admin/advanced/events.json index 1e8a7c9a9f..37da405a88 100644 --- a/public/language/he/admin/advanced/events.json +++ b/public/language/he/admin/advanced/events.json @@ -3,6 +3,7 @@ "no-events": "אין ארועים", "control-panel": "בקרת ארועים\n ", "delete-events": "מחיקת ארועים", + "confirm-delete-all-events": "האם אתה בטוח שאתה רוצה למחוק את כל האירועים שנרשמו?", "filters": "מסננים", "filters-apply": "החל מסננים", "filter-type": "סוג אירוע", diff --git a/public/language/he/admin/dashboard.json b/public/language/he/admin/dashboard.json index 54a77a8173..2f6e8e6ba0 100644 --- a/public/language/he/admin/dashboard.json +++ b/public/language/he/admin/dashboard.json @@ -30,7 +30,7 @@ "upgrade-available": "

גרסה חדשה (v%1) שוחררה. שקול האם לעדכן את הפורום שלך.

", "prerelease-upgrade-available": "

זוהי גירסת קדם-הפצה מיושנת של NodeBB. גרסה חדשה (v%1) שוחרר. שקול האם לעדכן את ה-NodeBB שלך.

", "prerelease-warning": "

זוהי גירסת קדם-הפצה של NodeBB. באגים בלתי צפויים עלולים להתרחש.

", - "fallback-emailer-not-found": "Fallback emailer not found!", + "fallback-emailer-not-found": "Fallback emailer לא נמצא!", "running-in-development": "הפורום פועל במצב פיתוח. הפורום עשוי להיות חשוף לפגיעות פוטנציאליות; פנה אל מנהל המערכת שלך.", "latest-lookup-failed": "

נכשל בבדיקת זמינות גרסה חדשה של הפורום

", @@ -56,8 +56,8 @@ "active-users.total": "סך הכל", "active-users.connections": "חיבורים", - "anonymous-registered-users": "משתמשים רשומים ואורחים", - "anonymous": "אורחים", + "guest-registered-users": "Guest vs Registered Users", + "guest": "אורח", "registered": "רשומים", "user-presence": "נוכחות משתמשים", @@ -68,6 +68,7 @@ "unread": "לא נקראו", "high-presence-topics": "פוסטים עם הכי הרבה נוכחות", + "popular-searches": "חיפושים פופולריים", "graphs.page-views": "צפיות בדפים", "graphs.page-views-registered": "צפיות בדפים-רשומים", @@ -75,13 +76,14 @@ "graphs.page-views-bot": "צפיות בדפים-בוטים", "graphs.unique-visitors": "מבקרים ייחודיים", "graphs.registered-users": "משתמשים רשומים", - "graphs.anonymous-users": "אורחים", + "graphs.guest-users": "משתמשים אורחים", "last-restarted-by": "אותחל לארונה על ידי", "no-users-browsing": "אין גולשים", "back-to-dashboard": "חזרה ללוח מחוונים", "details.no-users": "אין משתמש שהצטרף במסגרת הזמן שנבחרה", "details.no-topics": "לא פורסמו נושאים במסגרת הזמן שנבחרה", + "details.no-searches": "No searches have been made yet", "details.no-logins": "לא נרשמו כניסות במסגרת הזמן שנבחרה", "details.logins-static": "NodeBB שומר נתוני הפעלה עבור %1 ימים בלבד, ולכן טבלה זו תציג רק את הכניסות הפעילות האחרונות", "details.logins-login-time": "זמן כניסה" diff --git a/public/language/he/admin/development/info.json b/public/language/he/admin/development/info.json index 5a7c675185..83bdfa2cc7 100644 --- a/public/language/he/admin/development/info.json +++ b/public/language/he/admin/development/info.json @@ -2,13 +2,17 @@ "you-are-on": "אתה נמצא ב %1:%2", "ip": "IP %1", "nodes-responded": "%1 צמתים הגיבו בתוך %2מילי שניות!", - "host": "מנחה", + "host": "host", "primary": "primary / run jobs", "pid": "pid", "nodejs": "nodejs", "online": "מקוון", - "git": "גיט", - "memory": "זיכרון", + "git": "git", + "process-memory": "process memory", + "system-memory": "system memory", + "used-memory-process": "Used memory by process", + "used-memory-os": "Used system memory", + "total-memory-os": "Total system memory", "load": "טעינת מערכת", "cpu-usage": "שימוש ב-CPU", "uptime": "משך זמן פעולת המערכת ללא השבתה", diff --git a/public/language/he/admin/extend/rewards.json b/public/language/he/admin/extend/rewards.json index 55adfcb6e2..12bc679568 100644 --- a/public/language/he/admin/extend/rewards.json +++ b/public/language/he/admin/extend/rewards.json @@ -8,8 +8,6 @@ "delete": "מחק", "enable": "הפעל", "disable": "השבת", - "control-panel": "בקרת תגמולים", - "new-reward": "תגמול חדש", "alert.delete-success": "תגמול נמחק בהצלחה", "alert.no-inputs-found": "תגמול לא חוקי - לא נמצא מידע!", diff --git a/public/language/he/admin/extend/widgets.json b/public/language/he/admin/extend/widgets.json index 40fa3a53e7..49ad01a667 100644 --- a/public/language/he/admin/extend/widgets.json +++ b/public/language/he/admin/extend/widgets.json @@ -1,30 +1,30 @@ { - "available": "יישומונים זמינים", - "explanation": "בחר יישומון מהתפריט הנפתח ואז גרור ושחרר אותו לאזור הווידג'ט של התבנית משמאל.", + "available": "וידג'טים זמינים", + "explanation": "בחר וידג'ט מהתפריט הנפתח ואז גרור ושחרר אותו באזור הווידג'ט של התבנית משמאל.", "none-installed": "לא נמצאו וידג'טים! הפעל את תוספי הוידג'טים ב תוספים בלוח הבקרה.", - "clone-from": "יישומונים משוכפלים מ", + "clone-from": "וידג'טים משוכפלים מ", "containers.available": "גורמים מכילים זמינים", - "containers.explanation": "גרור ושחרר מעל כל יישומון פעיל", - "containers.none": "אף אחד", + "containers.explanation": "גרור ושחרר מעל כל וידג'ט פעיל", + "containers.none": "None", "container.well": "Well", "container.jumbotron": "Jumbotron", "container.panel": "פאנל", "container.panel-header": "כותרת פאנל", "container.panel-body": "גוף הפאנל", - "container.alert": "התרעה", + "container.alert": "התראה", - "alert.confirm-delete": "האם אתה בטוח שאתה רוצה למחוק את הווידג'ט?", - "alert.updated": "העלאת ווידג'טים", - "alert.update-success": "הווידג'טים הועלו בהצלחה", + "alert.confirm-delete": "האם אתה בטוח שאתה רוצה למחוק את הוידג'ט?", + "alert.updated": "העלאת וידג'טים", + "alert.update-success": "הוידג'טים הועלו בהצלחה", "alert.clone-success": "הוידג'טים שוכפלו בהצלחה", "error.select-clone": "בחר דף לשכפל ממנו", "title": "כותרת", - "title.placeholder": "Title (only shown on some containers)", - "container": "Container", - "container.placeholder": "Drag and drop a container or enter HTML here.", - "show-to-groups": "הראה לקבוצות", - "hide-from-groups": "הסתר מקבוצות", - "hide-on-mobile": "הסתר במובייל" + "title.placeholder": "כותרת (מוצגת רק בגורמים מכילים מסוימים)", + "container": "גורם מכיל", + "container.placeholder": "גרור ושחרר גורם מכיל (container) או הזן HTML כאן.", + "show-to-groups": "יוצג בקבוצות", + "hide-from-groups": "יוסתר מקבוצות", + "hide-on-mobile": " הסתר במובייל" } \ No newline at end of file diff --git a/public/language/he/admin/general/dashboard.json b/public/language/he/admin/general/dashboard.json deleted file mode 100644 index 3bd39f79e8..0000000000 --- a/public/language/he/admin/general/dashboard.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "forum-traffic": "תעבורת הפורום", - "page-views": "צפיות בדפים", - "unique-visitors": "מבקרים ייחודיים", - "new-users": "New Users", - "posts": "פוסטים", - "topics": "נושאים", - "page-views-seven": "7 ימים אחרונים", - "page-views-thirty": "30 ימים אחרונים", - "page-views-last-day": "24 שעות אחרונות", - "page-views-custom": "טווח תאריך ידני", - "page-views-custom-start": "תחילת טווח", - "page-views-custom-end": "סוף טווח", - "page-views-custom-help": "הכנס טווח תאריך עבור התקופה שבה תרצה לצפות בצפיות העמודים. הפורמט הנדרש הוא YYYY-MM-DD", - "page-views-custom-error": "נא הזן טווח תאריכים תקין כלהלן YYYY-MM-DD", - - "stats.yesterday": "Yesterday", - "stats.today": "Today", - "stats.last-week": "Last Week", - "stats.this-week": "This Week", - "stats.last-month": "Last Month", - "stats.this-month": "This Month", - "stats.all": "כל הזמנים", - - "updates": "עדכונים", - "running-version": "אתה עובד עם NodeBB גרסה%1", - "keep-updated": "תמיד תוודא שמערכת NodeBB שלך עדכנית לטובת עדכוני אבטחה ותיקוני באגים", - "up-to-date": "

אתה עדכני

", - "upgrade-available": "

A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-upgrade-available": "

This is an outdated pre-release version of NodeBB. A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-warning": "

This is a pre-release version of NodeBB. Unintended bugs may occur.

", - "running-in-development": "Forum is running in development mode. The forum may be open to potential vulnerabilities; please contact your system administrator.", - "latest-lookup-failed": "

נכשל בבדיקת גרסה חדשה זמינה עבור NodeBB

", - - "notices": "הודעות", - "restart-not-required": "לא נדרש אתחול מחדש", - "restart-required": "נדרש אתחול מחדש", - "search-plugin-installed": "תוסף חיפוש הותקן", - "search-plugin-not-installed": "תוסף חיפוש לא הותקן", - "search-plugin-tooltip": "התקן את תוסף החיפוש מעמוד התוספים על מנת להפעיל את אפשרות החיפוש", - - "control-panel": "שליטה מערכתית", - "rebuild-and-restart": "בנייה מחדש ואתחול", - "restart": "אתחול", - "restart-warning": "בנייה או אתחול מערכת NodeBB תנתק את כל המשתמשים הקיימים למספר שניות", - "restart-disabled": "בניית ואתחול מערכת NodeBB לא אפשרית, מאחר ואתה לא מריץ את המערכת בדרך הנדרשת", - "maintenance-mode": "מצב תחזוקה", - "maintenance-mode-title": "לחץ כאן על מנת להכניס את NodeBB למצב תחזוקה", - "realtime-chart-updates": "עדכון זמן אמת של הגרף", - - "active-users": "משתמשים פעילים", - "active-users.users": "משתמשים", - "active-users.guests": "אורחים", - "active-users.total": "סך הכל", - "active-users.connections": "חיבורים", - - "anonymous-registered-users": "משתמשים רשומים כנגד אורחים", - "anonymous": "אורחים", - "registered": "רשום", - - "user-presence": "נוכחות משתמש", - "on-categories": "על רשימת הקטגוריות", - "reading-posts": "קריאת פוסטים", - "browsing-topics": "חיפוש נושאים", - "recent": "לאחרונה", - "unread": "לא נקראו", - - "high-presence-topics": "פוסטים עם נוכחות גבוהה", - - "graphs.page-views": "צפיות בדפים", - "graphs.page-views-registered": "צפיות בדפים של רשומים", - "graphs.page-views-guest": "צפיות בדפים של אורחים", - "graphs.page-views-bot": "צפיות של בוטים", - "graphs.unique-visitors": "מבקרים ייחודיים", - "graphs.registered-users": "משתמשים רשומים", - "graphs.anonymous-users": "משתמשים אנונימיים", - "last-restarted-by": "אותחל לארונה על ידי", - "no-users-browsing": "אין משתמשים גולשים" -} diff --git a/public/language/he/admin/general/homepage.json b/public/language/he/admin/general/homepage.json deleted file mode 100644 index 7428d59eeb..0000000000 --- a/public/language/he/admin/general/homepage.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "home-page": "Home Page", - "description": "Choose what page is shown when users navigate to the root URL of your forum.", - "home-page-route": "Home Page Route", - "custom-route": "Custom Route", - "allow-user-home-pages": "Allow User Home Pages", - "home-page-title": "Title of the home page (default \"Home\")" -} \ No newline at end of file diff --git a/public/language/he/admin/general/languages.json b/public/language/he/admin/general/languages.json deleted file mode 100644 index bdd57849b3..0000000000 --- a/public/language/he/admin/general/languages.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "language-settings": "Language Settings", - "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language", - "auto-detect": "Auto Detect Language Setting for Guests" -} \ No newline at end of file diff --git a/public/language/he/admin/general/navigation.json b/public/language/he/admin/general/navigation.json deleted file mode 100644 index 13dd01aae7..0000000000 --- a/public/language/he/admin/general/navigation.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "icon": "Icon:", - "change-icon": "change", - "route": "Route:", - "tooltip": "Tooltip:", - "text": "Text:", - "text-class": "Text Class: optional", - "class": "Class: optional", - "id": "ID: optional", - - "properties": "Properties:", - "groups": "Groups:", - "open-new-window": "Open in a new window", - - "btn.delete": "Delete", - "btn.disable": "Disable", - "btn.enable": "Enable", - - "available-menu-items": "Available Menu Items", - "custom-route": "Custom Route", - "core": "core", - "plugin": "plugin" -} \ No newline at end of file diff --git a/public/language/he/admin/general/social.json b/public/language/he/admin/general/social.json deleted file mode 100644 index 23aedfcfaa..0000000000 --- a/public/language/he/admin/general/social.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "post-sharing": "Post Sharing", - "info-plugins-additional": "Plugins can add additional networks for sharing posts.", - "save-success": "Successfully saved Post Sharing Networks!" -} \ No newline at end of file diff --git a/public/language/he/admin/general/sounds.json b/public/language/he/admin/general/sounds.json deleted file mode 100644 index 95ccbde0f1..0000000000 --- a/public/language/he/admin/general/sounds.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "notifications": "Notifications", - "chat-messages": "Chat Messages", - "play-sound": "Play", - "incoming-message": "Incoming Message", - "outgoing-message": "Outgoing Message", - "upload-new-sound": "Upload New Sound", - "saved": "Settings Saved" -} \ No newline at end of file diff --git a/public/language/he/admin/manage/categories.json b/public/language/he/admin/manage/categories.json index 546c5abc35..07eca8f15e 100644 --- a/public/language/he/admin/manage/categories.json +++ b/public/language/he/admin/manage/categories.json @@ -19,7 +19,7 @@ "category-image": "תמונת קטגוריה", "parent-category": "קטגוריית אב", "optional-parent-category": "(אופציונלי) קטגוריית הורים", - "top-level": "Top Level", + "top-level": "רמה עליונה", "parent-category-none": "(ללא)", "copy-parent": "העתק אב", "copy-settings": "העתק הגדרות מ:", @@ -32,8 +32,8 @@ "edit": "ערוך", "analytics": "נתח", "view-category": "הצג קטגוריה", - "set-order": "Set order", - "set-order-help": "Setting the order of the category will move this category to that order and update the order of other categories as necessary. Minimum order is 1 which puts the category at the top.", + "set-order": "קבע סדר", + "set-order-help": "הגדרת סדר הקטגוריה תעביר קטגוריה זו לסדר זה ותעדכן את סדר הקטגוריות האחרות לפי הצורך. מינימום קביעת סדר הוא 1 מה שמציב את הקטגוריה בראש.", "select-category": "בחר קטגוריה", "set-parent-category": "הגדר קטגוריית אב", diff --git a/public/language/he/admin/manage/digest.json b/public/language/he/admin/manage/digest.json index fbad5ebc74..78e865d394 100644 --- a/public/language/he/admin/manage/digest.json +++ b/public/language/he/admin/manage/digest.json @@ -13,6 +13,7 @@ "resent-single": "שליחת התקציר מחדש באופן ידני בוצע בהצלחה", "resent-day": "תקציר יומי נשלח", "resent-week": "תקציר שבועי נשלח", + "resent-biweek": "Bi-Weekly digest resent", "resent-month": "תקציר חודשי נשלח", "null": "אף פעם", "manual-run": "הפעל תקציר ידני", diff --git a/public/language/he/admin/manage/privileges.json b/public/language/he/admin/manage/privileges.json index 2100dbedde..819f7260ed 100644 --- a/public/language/he/admin/manage/privileges.json +++ b/public/language/he/admin/manage/privileges.json @@ -51,13 +51,13 @@ "alert.saved": "שינויי הרשאות נשמרו והוחלו", "alert.confirm-discard": "האם אתה בטוח שברצונך לבטל את שינויי ההרשאות שלך?", "alert.discarded": "שינויי ההרשאות נמחקו", - "alert.confirm-copyToAll": "Are you sure you wish to apply this set of %1 to all categories?", - "alert.confirm-copyToAllGroup": "Are you sure you wish to apply this group's set of %1 to all categories?", - "alert.confirm-copyToChildren": "Are you sure you wish to apply this set of %1 to all descendant (child) categories?", - "alert.confirm-copyToChildrenGroup": "Are you sure you wish to apply this group's set of %1 to all descendant (child) categories?", + "alert.confirm-copyToAll": "זהירות!! האם אתה בטוח שברצונך להחיל הגדרת הרשאות זו של %1 ל כל הקטגוריות?", + "alert.confirm-copyToAllGroup": "זהירות!! האם אתה בטוח שברצונך להחיל הרשאות קבוצה זו של%1 ל כל הקטגוריות?", + "alert.confirm-copyToChildren": "האם אתה בטוח שברצונך להחיל הגדרת הרשאות זו של %1 ל כל קטגוריות הצאצאים (ילדים)?", + "alert.confirm-copyToChildrenGroup": "האם אתה בטוח שברצונך להחיל הרשאות קבוצה זו של%1 ל לכל קטגוריות הצאצאים (ילדים)?", "alert.no-undo": "לא ניתן לבטל פעולה זו.", "alert.admin-warning": "מנהלים מקבלים את כל ההרשאות", - "alert.copyPrivilegesFrom-title": "Select a category to copy from", - "alert.copyPrivilegesFrom-warning": "This will copy %1 from the selected category.", - "alert.copyPrivilegesFromGroup-warning": "This will copy this group's set of %1 from the selected category." + "alert.copyPrivilegesFrom-title": "בחר קטגוריה להעתקה ממנו", + "alert.copyPrivilegesFrom-warning": "פעולה זו תעתיק %1 מהקטגוריה שנבחרה.", + "alert.copyPrivilegesFromGroup-warning": "פעולה זו תעתיק את הגדרת הקבוצה של %1 מהקטגוריה שנבחרה." } \ No newline at end of file diff --git a/public/language/he/admin/manage/uploads.json b/public/language/he/admin/manage/uploads.json index 2fa5ab4c06..0507edec6f 100644 --- a/public/language/he/admin/manage/uploads.json +++ b/public/language/he/admin/manage/uploads.json @@ -6,6 +6,6 @@ "size/filecount": "גודל / ספירת קבצים", "confirm-delete": "האם אתה בטוח שאתה רוצה למחוק קובץ זה?", "filecount": "%1 קבצים", - "new-folder": "New Folder", - "name-new-folder": "Enter a name for new the folder" + "new-folder": "תיקייה חדשה", + "name-new-folder": "הכנס שם לתיקייה החדשה" } \ No newline at end of file diff --git a/public/language/he/admin/manage/users.json b/public/language/he/admin/manage/users.json index 05b73d2b18..1eb0bb2aca 100644 --- a/public/language/he/admin/manage/users.json +++ b/public/language/he/admin/manage/users.json @@ -47,6 +47,7 @@ "users.uid": "מזהה משתמש (ID)", "users.username": "שם משתמש", "users.email": "דוא\"ל", + "users.no-email": "(אין כתובת דוא\"ל)", "users.ip": "IP", "users.postcount": "מספר פוסטים", "users.reputation": "מוניטין", diff --git a/public/language/he/admin/menu.json b/public/language/he/admin/menu.json index c4d31c775d..95890e7d15 100644 --- a/public/language/he/admin/menu.json +++ b/public/language/he/admin/menu.json @@ -4,6 +4,7 @@ "dashboard/logins": "כניסות", "dashboard/users": "משתמשים", "dashboard/topics": "נושאים", + "dashboard/searches": "חיפושים", "section-general": "כללי", "section-manage": "ניהול", diff --git a/public/language/he/admin/settings/advanced.json b/public/language/he/admin/settings/advanced.json index 2f71a62b2a..621a94adb3 100644 --- a/public/language/he/admin/settings/advanced.json +++ b/public/language/he/admin/settings/advanced.json @@ -5,14 +5,14 @@ "maintenance-mode.message": "הודעת תחזוקה", "headers": "כותרות", "headers.allow-from": "הגדר ALLOW-FROM למקם NodeBB ב- iFrame", - "headers.csp-frame-ancestors": "Set Content-Security-Policy frame-ancestors header to Place NodeBB in an iFrame", - "headers.csp-frame-ancestors-help": "'none', 'self'(default) or list of URIs to allow.", + "headers.csp-frame-ancestors": "הגדר את מדיניות האבטחה (Content-Security-Policy) עבור ההטמעה (frame-ancestors) של NodeBB בתוך Iframe", + "headers.csp-frame-ancestors-help": "בחר מילים שמורות כמו 'none' (ללא) 'self' (רק מהאתר שלי) או כתובת מלאה של אתר חיצוני", "headers.powered-by": "התאם אישית את הכותרת \"מופעל ע\"י\" הברירת מחדל של נודביבי", - "headers.acao": "Access-Control-Allow-Origin", - "headers.acao-regex": "Access-Control-Allow-Origin Regular Expression", + "headers.acao": "אתרים הרשאים לקרוא לאתר זה (Access-Control-Allow-Origin)", + "headers.acao-regex": "תבנית טקסט (Regex) עבור אתרים הרשאים לקרוא לאתר זה (Access-Control-Allow-Origin)", "headers.acao-help": "כדי למנוע גישה לכל האתרים, השאר ריק", - "headers.acao-regex-help": "Enter regular expressions here to match dynamic origins. To deny access to all sites, leave empty", - "headers.acac": "Access-Control-Allow-Credentials", + "headers.acao-regex-help": "הכנס תבנית טקסט (Regex) כאן כדי לאפשר קריאה דינאמית מאתרים חיצוניים. אם ברצונך לחסום כל גישה חיצונית, השאר ריק.", + "headers.acac": "אתרים אשר אל בקשות אליהם, יתווספו גם נתוני כניסה כגוןCookie וכו'. ( Access-Control-Allow-Credentials)", "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", "hsts": "Strict Transport Security", @@ -26,13 +26,13 @@ "traffic.enable": "Enable Traffic Management", "traffic.event-lag": "Event Loop Lag Threshold (in milliseconds)", "traffic.event-lag-help": "Lowering this value decreases wait times for page loads, but will also show the \"excessive load\" message to more users. (Restart required)", - "traffic.lag-check-interval": "Check Interval (in milliseconds)", + "traffic.lag-check-interval": "מרווח זמן בין בדיקות (במילישניות)", "traffic.lag-check-interval-help": "Lowering this value causes NodeBB to become more sensitive to spikes in load, but may also cause the check to become too sensitive. (Restart required)", - "sockets.settings": "WebSocket Settings", - "sockets.max-attempts": "Max Reconnection Attempts", + "sockets.settings": "הגדרות חיבור WebSocket", + "sockets.max-attempts": "מקסימום מספר נסיונות חיבור מחדש", "sockets.default-placeholder": "ברירת מחדל: %1", - "sockets.delay": "Reconnection Delay", + "sockets.delay": "זמן השעייה בן נסיונות חיבור מחדש", "analytics.settings": "Analytics Settings", "analytics.max-cache": "Analytics Cache Max Value", diff --git a/public/language/he/admin/settings/email.json b/public/language/he/admin/settings/email.json index a0bc61d400..7c1fa4f51b 100644 --- a/public/language/he/admin/settings/email.json +++ b/public/language/he/admin/settings/email.json @@ -6,7 +6,7 @@ "from-help": "השם 'מאת' יוצג בדוא\"ל.", "smtp-transport": "SMTP Transport", - "smtp-transport.enabled": "Use an external email server to send emails", + "smtp-transport.enabled": "Enable SMTP Transport", "smtp-transport-help": "You can select from a list of well-known services or enter a custom one.", "smtp-transport.service": "Select a service", "smtp-transport.service-custom": "Custom Service", @@ -37,6 +37,10 @@ "subscriptions.hour": "שעת תקציר", "subscriptions.hour-help": "Please enter a number representing the hour to send scheduled email digests (e.g. 0 for midnight, 17 for 5:00pm). Keep in mind that this is the hour according to the server itself, and may not exactly match your system clock.
The approximate server time is:
The next daily digest is scheduled to be sent ", "notifications.remove-images": "הסר תמונות מהודעות דוא\"ל", + "require-email-address": "דרוש ממשתמשים חדשים כתובת אימייל", + "require-email-address-warning": "By default, users can opt-out of entering an email address. Enabling this option means they have to enter an email address in order to proceed with registration. It does not ensure user will enter a real email address, nor even an address they own.", "include-unverified-emails": "Send emails to recipients who have not explicitly confirmed their emails", - "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws." -} \ No newline at end of file + "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws.", + "prompt": "Prompt users to enter or confirm their emails", + "prompt-help": "If a user does not have an email set, or their email is not confirmed, a warning will be shown on screen." +} diff --git a/public/language/he/admin/settings/general.json b/public/language/he/admin/settings/general.json index c807d5fccc..97c974a552 100644 --- a/public/language/he/admin/settings/general.json +++ b/public/language/he/admin/settings/general.json @@ -3,48 +3,48 @@ "title": "כותרת האתר", "title.short": "כותרת קצרה", "title.short-placeholder": "אם לא הוגדר כותרת קצרה, כותרת האתר ישמש ככותרת", - "title.url": "כתובת האתר", - "title.url-placeholder": "כתובת אתר זה", - "title.url-help": "בעת לחיצה על הכותרת, המשתמשים ינותבו לכתובת הזו. אם תשאיר ריק, המשתמשים ינותבו לאינדקס", - "title.name": "Your Community Name", + "title.url": "כותרת קישור URL", + "title.url-placeholder": "ה-URL של כותרת האתר", + "title.url-help": "בעת לחיצה על הכותרת, המשתמשים ינותבו לכתובת זו. באם יישאר ריק, המשתמשים יישלחו לאינדקס הפורום.
הערה: זו אינה כתובת ה- URL החיצונית המשמשת בהודעות דוא\"ל וכד'. זה נקבע על ידי ה-url המאופיין ב- config.json", + "title.name": "שם הקהילה שלך", "title.show-in-header": "הצג את כותרת האתר בכותרת העליונה", "browser-title": "כותרת הדפדפן", "browser-title-help": "אם לא צוין כותרת הדפדפן, כותרת האתר ישמש ככותרת", "title-layout": "פריסת כותרת", - "title-layout-help": "Define how the browser title will be structured ie. {pageTitle} | {browserTitle}", - "description.placeholder": "A short description about your community", - "description": "Site Description", - "keywords": "Site Keywords", - "keywords-placeholder": "Keywords describing your community, comma-separated", + "title-layout-help": "הגדר כיצד כותרת הדפדפן תהיה מובנית לדוגמא. {pageTitle} | {browserTitle}", + "description.placeholder": "תיאור קצר על הקהילה שלך", + "description": "תיאור האתר", + "keywords": "מילות מפתח של האתר", + "keywords-placeholder": "מילות מפתח המתארות את הקהילה שלך, מופרדות באמצעות פסיקים", "logo": "לוגו האתר", "logo.image": "תמונה", "logo.image-placeholder": "נתב ללוגו שיראה בכותרת הפורום", "logo.upload": "העלה", - "logo.url": "URL", + "logo.url": "קישור URL לאייקון", "logo.url-placeholder": "כתובת לוגו האתר", - "logo.url-help": "בעת לחיצה על הלוגו, המשתמשים ינותבו לכתובת הזו. אם תשאיר ריק, המשתמשים ינותבו לאינדקס", - "logo.alt-text": "Alt Text", - "log.alt-text-placeholder": "Alternative text for accessibility", - "favicon": "Favicon", + "logo.url-help": "בעת לחיצה על האייקון, המשתמשים ינותבו לכתובת זו. באם יישאר ריק, המשתמשים יישלחו לאינדקס הפורום.
הערה: זו אינה כתובת ה- URL החיצונית המשמשת בהודעות דוא\"ל וכד'. זה נקבע על ידי ה-url המאופיין ב- config.json", + "logo.alt-text": "טקסט חלופי", + "log.alt-text-placeholder": "הזן טקסט חלופי לנגישות", + "favicon": "פבאייקון - Favicon", "favicon.upload": "העלה", - "pwa": "Progressive Web App", - "touch-icon": "Touch Icon", + "pwa": "אפליקציית אינטרנט בסלולרי", + "touch-icon": "סמליל דף אינטרנט - Touch Icon", "touch-icon.upload": "העלה", - "touch-icon.help": "Recommended size and format: 512x512, PNG format only. If no touch icon is specified, NodeBB will fall back to using the favicon.", - "maskable-icon": "Maskable (Homescreen) Icon", - "maskable-icon.help": "Recommended size and format: 512x512, PNG format only. If no maskable icon is specified, NodeBB will fall back to the Touch Icon.", - "outgoing-links": "לינקים חיצוניים", - "outgoing-links.warning-page": "Use Outgoing Links Warning Page", - "search": "Search", - "search-default-in": "Search In", - "search-default-in-quick": "Quick Search In", - "search-default-sort-by": "Sort by", - "outgoing-links.whitelist": "Domains to whitelist for bypassing the warning page", - "site-colors": "Site Color Metadata", + "touch-icon.help": "סמליל דף אינטרנט מופיע כאשר מישהו מסמן את דף האינטרנט שלך או מוסיף את דף האינטרנט שלך למסך הבית שלו, גודל ותבנית מומלצים: 512x512, תבנית PNG בלבד. אם לא הוגדר סמליל דף אינטרנט, NodeBB יחזור להשתמש בסמליל הפבאייקון.", + "maskable-icon": "סמליל הניתן להסוואה (במסך הבית)", + "maskable-icon.help": "סמליל הניתן להסוואה מופיע בדף הבית של הסוללרי, זהו תמונה אטומה עם מעט ריפוד שהיישום דף הבית שלך יוכל לחתוך אחר כך לצורה ולגודל הרצוי. עדיף לא להסתמך על צורה מסוימת, מכיוון שהצורה שנבחרה בסופו של דבר יכולה להשתנות לפי סוגי מסך בית ופלטפורמה. גודל ותבנית מומלצים: 512x512, תבנית PNG בלבד. אם לא הוגדר אייקון הניתן להסוואה, NodeBB יחזור להשתמש בסמליל דף האינטרנט.", + "outgoing-links": "קישורים חיצוניים", + "outgoing-links.warning-page": "השתמש בדף האזהרה לקישורים יוצאים", + "search": "חיפוש", + "search-default-in": "חפש ב", + "search-default-in-quick": "חיפוש מהיר ב", + "search-default-sort-by": "מיין לפי", + "outgoing-links.whitelist": "תחומים לרשימה הלבנה לעקיפת דף האזהרה", + "site-colors": "מטה-נתונים של צבע אתר", "theme-color": "צבע ערכת נושא", "background-color": "צבע רקע", - "background-color-help": "Color used for splash screen background when website is installed as a PWA", - "undo-timeout": "Undo Timeout", - "undo-timeout-help": "Some operations such as moving topics will allow for the moderator to undo their action within a certain timeframe. Set to 0 to disable undo completely.", + "background-color-help": "צבע המשמש לרקע של מסך פתיחה כאשר אתר האינטרנט מותקן כ-PWA", + "undo-timeout": "פסק זמן לביטול", + "undo-timeout-help": "לפעולות מסוימות, כמו העברת נושאים, יאופשרו ביטול הפעולה במסגרת זמן מסוימת. הגדר ל- 0 כדי להשבית לחלוטין את האפשרות.", "topic-tools": "כלי נושא" -} \ No newline at end of file +} diff --git a/public/language/he/admin/settings/navigation.json b/public/language/he/admin/settings/navigation.json index b89a64c2ac..fd4d8d9509 100644 --- a/public/language/he/admin/settings/navigation.json +++ b/public/language/he/admin/settings/navigation.json @@ -11,6 +11,8 @@ "properties": "הרשאות:", "groups": "קבוצות:", "open-new-window": "פתח בחלון חדש", + "dropdown": "Dropdown", + "dropdown-placeholder": "Place your dropdown menu items below, ie:
<li><a href="https://myforum.com">Link 1</a></li>", "btn.delete": "מחק", "btn.disable": "השבת", @@ -20,4 +22,4 @@ "custom-route": "נתיב מותאם אישית", "core": "ליבה", "plugin": "תוסף" -} \ No newline at end of file +} diff --git a/public/language/he/admin/settings/notifications.json b/public/language/he/admin/settings/notifications.json index 2829c6e2ed..5e25d54217 100644 --- a/public/language/he/admin/settings/notifications.json +++ b/public/language/he/admin/settings/notifications.json @@ -2,5 +2,6 @@ "notifications": "התראות", "welcome-notification": "הודעת ברוכים הבאים", "welcome-notification-link": "קישור הודעת ברוכים הבאים", - "welcome-notification-uid": "הודעת ברוכים הבאים למשתמש (UID)" + "welcome-notification-uid": "הודעת ברוכים הבאים למשתמש (UID)", + "post-queue-notification-uid": "רשום משתמש בתור (UID)" } \ No newline at end of file diff --git a/public/language/he/admin/settings/post.json b/public/language/he/admin/settings/post.json index ecca8e24f7..e2c3681728 100644 --- a/public/language/he/admin/settings/post.json +++ b/public/language/he/admin/settings/post.json @@ -40,6 +40,7 @@ "teaser.last-post": "Last – הצג את הפוסט האחרון, כולל הפוסט המקורי, אם אין תגובות", "teaser.last-reply": "Last – הצג את התשובה האחרונה, או ציין \"ללא תשובות\" אם אין תשובות", "teaser.first": "ראשון", + "showPostPreviewsOnHover": "הצג תצוגה מקדימה בריחוף על פוסט", "unread": "הגדרות \"שלא נקראו\"", "unread.cutoff": "ימי ניתוק שלא נקראו", "unread.min-track-last": "פוסטים מינימליים בנושא לפני מעקב אחר קריאה אחרונה", @@ -56,6 +57,9 @@ "composer.show-help": "הצג כרטיסיית \"עזרה\"", "composer.enable-plugin-help": "אפשר לתוסיפים להוסיף תוכן ללשונית עזרה", "composer.custom-help": "טקסט עזרה מותאם אישית", + "backlinks": "Backlinks", + "backlinks.enabled": "Enable topic backlinks", + "backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.", "ip-tracking": "IP מעקב", "ip-tracking.each-post": "מעקב אחר כתובת IP על כל הודעה", "enable-post-history": "הפוך היסטוריית פוסטים לזמינה" diff --git a/public/language/he/admin/settings/user.json b/public/language/he/admin/settings/user.json index 374648e4ca..519cbaaa7e 100644 --- a/public/language/he/admin/settings/user.json +++ b/public/language/he/admin/settings/user.json @@ -71,6 +71,7 @@ "digest-freq.off": "כבוי", "digest-freq.daily": "יומי", "digest-freq.weekly": "שבועי", + "digest-freq.biweekly": "Bi-Weekly", "digest-freq.monthly": "חודשי", "email-chat-notifs": "שלח לי הודעה למייל כאשר הודעת צ'אט נשלחה אלי בזמן שאינני מחובר", "email-post-notif": "שלח לי הודעה למייל כאשר תגובות חדשות פורסמו לנושאים שאני עוקב אחריהם", diff --git a/public/language/he/error.json b/public/language/he/error.json index b93f4c9a23..41575db79d 100644 --- a/public/language/he/error.json +++ b/public/language/he/error.json @@ -25,19 +25,20 @@ "invalid-event": "אירוע לא תקין: %1", "local-login-disabled": "מערכת הכניסה המקומית הושבתה עבור חשבונות שאינם מורשים.", "csrf-invalid": "אין באפשרותנו לחבר אותך למערכת, מכיוון שעבר זמן רב מידי. אנא נסה שנית.", - "invalid-path": "Invalid path", - "folder-exists": "Folder exists", + "invalid-path": "נתיב שגוי", + "folder-exists": "התיקיה קיימת", "invalid-pagination-value": "ערך דף לא חוקי, חייב להיות לפחות %1 ולא מעל %2", "username-taken": "שם משתמש תפוס", - "email-taken": "כתובת אימייל תפוסה", - "email-nochange": "כתובת אימייל שהוזן זהה לאימייל שנמצא כבר", - "email-invited": "כבר נשלחה הזמנה למייל זה", - "email-not-confirmed": "Posting in some categories or topics is enabled once your email is confirmed, please click here to send a confirmation email.", + "email-taken": "כתובת דוא\"ל תפוסה", + "email-nochange": "כתובת דוא\"ל שהוזן זהה לדוא\"ל שנמצא כבר", + "email-invited": "כבר נשלחה הזמנה לדוא\"ל זה", + "email-not-confirmed": "פרסום בקטגוריות או בנושאים מסוימים מופעל רק לאחר אישור הדוא\"ל שלך, אנא לחץ כאן כדי לשלוח אימות לדוא\"ל שלך.", "email-not-confirmed-chat": "אין באפשרותך לשוחח עד שהדוא\"ל שלך יאושר, אנא לחץ כאן כדי לאשר את הדוא\"ל שלך.", - "email-not-confirmed-email-sent": "האימייל שלך עדין לא אושר. אנא בדוק בתיבת הדואר בנוגע לאישור האימייל שנשלח לך על ידנו. לא תוכל לכתוב פוסטים ולהשתמש בצ'אט לפני אימות המייל שלך.", - "no-email-to-confirm": "בחשבונך לא הוגדר אימייל. כתובת אימייל נחוץ לשחזור חשבון. אנא לחץ כאן כדי להכניס דוא\"ל.", + "email-not-confirmed-email-sent": "הדוא\"ל שלך עדין לא אושר. אנא בדוק בתיבת הדואר בנוגע לאישור הדוא\"ל שנשלח לך על ידינו. לא תוכל לכתוב פוסטים ולהשתמש בצ'אט לפני אימות הדוא\"ל שלך.", + "no-email-to-confirm": "בחשבונך לא הוגדר דוא\"ל. כתובת דוא\"ל נחוץ לשחזור חשבון. אנא לחץ כאן כדי להכניס דוא\"ל.", + "user-doesnt-have-email": "למשתמש \"%1\" לא הוגדר כתובת דוא\"ל.", "email-confirm-failed": "לא הצלחנו לאשר את הדוא\"ל שלך, תנסה שוב אחר כך", - "confirm-email-already-sent": "מייל האישור כבר נשלח, אנא המתן %1 דקות כדי לשלוח מייל נוסף.", + "confirm-email-already-sent": "דוא\"ל האישור כבר נשלח, אנא המתן %1 דקות כדי לשלוח דוא\"ל נוסף.", "sendmail-not-found": "תוכנת sendmail לא נמצאה, נא בדוק שהיא מותקת וניתנת להרצה על ידי המשתמש שמריץ את NodeBB.", "digest-not-enabled": "משתמש זה ביטל את התקצירים, או שברירת המחדל של המערכת היא לכבות תקצירים.", "username-too-short": "שם משתמש קצר מדי", @@ -103,6 +104,7 @@ "already-bookmarked": "כבר הוספת פוסט זה לרשימת המסומנים", "already-unbookmarked": "כבר הסרת פוסט זה מרשימת המסומנים", "cant-ban-other-admins": "אינך יכול לחסום מנהלים אחרים!", + "cant-make-banned-users-admin": "לא ניתן להפוך משתמשים מורחקים למנהלים.", "cant-remove-last-admin": "אתה המנהל היחיד. הוסף משתמש אחר לניהול לפני שאתה מוריד את עצמך מניהול", "account-deletion-disabled": "מחיקת החשבון מושבתת", "cant-delete-admin": "משתמש זה מוגדר כמנהל. על מנת למחוק את המשתמש, עליך להסיר קודם את גישותיו.", @@ -195,15 +197,15 @@ "plugin-not-whitelisted": "לא ניתן להתקין את התוסף – ניתן להתקין דרך הניהול רק תוספים שנמצאים ברשימה הלבנה של מנהל החבילות של NodeBB.", "topic-event-unrecognized": "אירוע הנושא '%1' לא מזוהה", "cant-set-child-as-parent": "לא ניתן להגדיר קטגוריה משנה לקטגוריית אב", - "cant-set-self-as-parent": "Can't set self as parent category", - "api.master-token-no-uid": "A master token was received without a corresponding `_uid` in the request body", - "api.400": "Something was wrong with the request payload you passed in.", + "cant-set-self-as-parent": "לא ניתן להגדיר את עצמי כקטגוריית אב", + "api.master-token-no-uid": "token ראשי התקבל ללא corresponding `_uid` בגוף הבקשה", + "api.400": "משהו לא היה בסדר עם בקשת ה-payload שהעברת.", "api.401": "לא נמצא סשן התחברות פעיל. נא להתחבר ולנסות שוב.", "api.403": "אינך מורשה לבצע את החיוג", - "api.404": "Invalid API call", - "api.426": "HTTPS is required for requests to the write api, please re-send your request via HTTPS", + "api.404": "קריאת API שגויה", + "api.426": "HTTPS נדרש לבקשות ל-API של הכתיבה, אנא שלח מחדש את בקשתך באמצעות HTTPS", "api.429": "יותר מידי בקשות, אנא נסה שוב מאוחר יותר", - "api.500": "An unexpected error was encountered while attempting to service your request.", - "api.501": "The route you are trying to call is not implemented yet, please try again tomorrow", - "api.503": "The route you are trying to call is not currently available due to a server configuration" + "api.500": "שגיאה בלתי צפויה אירעה בעת ניסיון להגיש את בקשתך.", + "api.501": "הנתיב אליו אתה מנסה לתקשר עדיין לא מיושם, אנא נסה שוב מחר", + "api.503": "הנתיב אליו אתה מנסה לתקשר אינו זמין כעת עקב תצורת שרת" } \ No newline at end of file diff --git a/public/language/he/modules.json b/public/language/he/modules.json index a360dc3c26..ec27ff0fcd 100644 --- a/public/language/he/modules.json +++ b/public/language/he/modules.json @@ -54,7 +54,7 @@ "composer.formatting.strikethrough": "קו פוסל", "composer.formatting.code": "קוד", "composer.formatting.link": "לינק", - "composer.formatting.picture": "תמונה מהרשת", + "composer.formatting.picture": "קישור תמונה", "composer.upload-picture": "העלה תמונה", "composer.upload-file": "העלה קובץ", "composer.zen_mode": "מסך מלא", diff --git a/public/language/he/notifications.json b/public/language/he/notifications.json index 93ed6c8e7a..c00e4be904 100644 --- a/public/language/he/notifications.json +++ b/public/language/he/notifications.json @@ -4,7 +4,7 @@ "see_all": "צפה בכל ההתראות", "mark_all_read": "סמן את כל ההתראות כנקראו", "back_to_home": "חזרה ל%1", - "outgoing_link": "לינק", + "outgoing_link": "קישור יוצא", "outgoing_link_message": "אתה עוזב עכשיו את %1", "continue_to": "המשך ל %1", "return_to": "חזור ל %1", diff --git a/public/language/he/top.json b/public/language/he/top.json new file mode 100644 index 0000000000..c942bb51a7 --- /dev/null +++ b/public/language/he/top.json @@ -0,0 +1,4 @@ +{ + "title": "הכי פופולארי", + "no_top_topics": "אין כותרות פופולאריות" +} \ No newline at end of file diff --git a/public/language/he/topic.json b/public/language/he/topic.json index 7c5a688bbe..5eb30f0cb8 100644 --- a/public/language/he/topic.json +++ b/public/language/he/topic.json @@ -20,8 +20,8 @@ "login-to-view": "🔒 התחבר בכדי לצפות", "edit": "עריכה", "delete": "מחק", - "delete-event": "Delete Event", - "delete-event-confirm": "Are you sure you want to delete this event?", + "delete-event": "מחיקת ארוע", + "delete-event-confirm": "האם אתה בטוח שאתה רוצה למחוק אירוע זה?", "purge": "מחק לצמיתות", "restore": "שחזר", "move": "העבר", @@ -45,8 +45,9 @@ "unpinned-by": "נעיצה הוסרה על ידי", "deleted-by": "נמחק על ידי", "restored-by": "שוחזר על ידי", - "moved-from-by": "Moved from %1 by", + "moved-from-by": "הועבר מ %1 ע\"י", "queued-by": "הפוסט ממתין לאישור →", + "backlink": "הוזכר על-ידי", "bookmark_instructions": "לחץ כאן בכדי לחזור לפוסט האחרון שקראת בנושא הזה.", "flag-post": "דווח על פוסט זה", "flag-user": "דווח על משתמש זה", @@ -138,6 +139,7 @@ "composer.handle_placeholder": "הזן את שמך / כינוי שלך כאן", "composer.discard": "ביטול", "composer.submit": "שלח", + "composer.additional-options": "אפשרויות נוספות", "composer.schedule": "תזמן", "composer.replying_to": "מגיב ל%1", "composer.new_topic": "נושא חדש", @@ -158,6 +160,7 @@ "newest_to_oldest": "מהחדש לישן", "most_votes": "הכי הרבה הצבעות", "most_posts": "הכי הרבה פוסטים", + "most_views": "הכי הרבה צפיות", "stale.title": "ליצור נושא חדש במקום זאת?", "stale.warning": "הנושא בו אתה מגיב הוא די ישן. האם ברצונך לפתוח נושא חדש, ולהזכיר נושא זה בתגובתך?", "stale.create": "צור נושא חדש", @@ -177,5 +180,5 @@ "timeago_earlier": "לפני %1 ", "first-post": "פוסט ראשון", "last-post": "פוסט אחרון", - "post-quick-reply": "Post quick reply" + "post-quick-reply": "רשום תשובה מהירה" } \ No newline at end of file diff --git a/public/language/he/user.json b/public/language/he/user.json index a8c49d6f0c..47dc53acd1 100644 --- a/public/language/he/user.json +++ b/public/language/he/user.json @@ -94,11 +94,12 @@ "digest_off": "כבוי", "digest_daily": "יומי", "digest_weekly": "שבועי", + "digest_biweekly": "דו שבועי", "digest_monthly": "חודשי", "has_no_follower": "למשתמש זה אין עוקבים :(", "follows_no_one": "משתמש זה אינו עוקב אחרי אחרים :(", "has_no_posts": "המשתמש טרם יצר פוסטים כלשהם.", - "has_no_best_posts": "This user does not have any upvoted posts yet.", + "has_no_best_posts": "למשתמש זה אין עדיין פוסטים נבחרים.", "has_no_topics": "המשתמש טרם יצר נושאים כלשהם.", "has_no_watched_topics": "המשתמש טרם צפה בנושאים כלשהם.", "has_no_ignored_topics": "המשתמש הזה טרם התעלם מנושאים.", diff --git a/public/language/hr/admin/advanced/events.json b/public/language/hr/admin/advanced/events.json index 3d741f3d17..f648110f4d 100644 --- a/public/language/hr/admin/advanced/events.json +++ b/public/language/hr/admin/advanced/events.json @@ -3,6 +3,7 @@ "no-events": "Nema događaja", "control-panel": "Kontrolna ploča događanja", "delete-events": "Delete Events", + "confirm-delete-all-events": "Are you sure you want to delete all logged events?", "filters": "Filters", "filters-apply": "Apply Filters", "filter-type": "Event Type", diff --git a/public/language/hr/admin/dashboard.json b/public/language/hr/admin/dashboard.json index 7ce75d8880..8072389569 100644 --- a/public/language/hr/admin/dashboard.json +++ b/public/language/hr/admin/dashboard.json @@ -56,8 +56,8 @@ "active-users.total": "Ukupno", "active-users.connections": "Veze", - "anonymous-registered-users": "Anonimni vs Registrirani korisnici", - "anonymous": "Anomiman", + "guest-registered-users": "Guest vs Registered Users", + "guest": "Guest", "registered": "Registriran", "user-presence": "Korisnik prisutan", @@ -68,6 +68,7 @@ "unread": "Nepročitano", "high-presence-topics": "Teme visoke prisutnosti", + "popular-searches": "Popular Searches", "graphs.page-views": "Pregled stranica", "graphs.page-views-registered": "Page Views Registered", @@ -75,13 +76,14 @@ "graphs.page-views-bot": "Page Views Bot", "graphs.unique-visitors": "Jedninstveni posjetitelji", "graphs.registered-users": "Registrirani korisnici", - "graphs.anonymous-users": "Anonimni korisnici", + "graphs.guest-users": "Guest Users", "last-restarted-by": "Last restarted by", "no-users-browsing": "No users browsing", "back-to-dashboard": "Back to Dashboard", "details.no-users": "No users have joined within the selected timeframe", "details.no-topics": "No topics have been posted within the selected timeframe", + "details.no-searches": "No searches have been made yet", "details.no-logins": "No logins have been recorded within the selected timeframe", "details.logins-static": "NodeBB only saves session data for %1 days, and so this table below will only show the most recently active sessions", "details.logins-login-time": "Login Time" diff --git a/public/language/hr/admin/development/info.json b/public/language/hr/admin/development/info.json index 888f9f053f..0eb118a954 100644 --- a/public/language/hr/admin/development/info.json +++ b/public/language/hr/admin/development/info.json @@ -8,7 +8,11 @@ "nodejs": "nodejs", "online": "Na mreži", "git": "git", - "memory": "memory", + "process-memory": "process memory", + "system-memory": "system memory", + "used-memory-process": "Used memory by process", + "used-memory-os": "Used system memory", + "total-memory-os": "Total system memory", "load": "system load", "cpu-usage": "cpu usage", "uptime": "uptime", diff --git a/public/language/hr/admin/extend/rewards.json b/public/language/hr/admin/extend/rewards.json index 5e20c3872f..d8198466a2 100644 --- a/public/language/hr/admin/extend/rewards.json +++ b/public/language/hr/admin/extend/rewards.json @@ -8,8 +8,6 @@ "delete": "Obriši", "enable": "Omogući", "disable": "onemogući", - "control-panel": "Kontrola nagrada", - "new-reward": "Nova nagrada", "alert.delete-success": "Uspješno obrisana nagrada", "alert.no-inputs-found": "Ilegalna nagrada - nije pronađen unos!", diff --git a/public/language/hr/admin/general/dashboard.json b/public/language/hr/admin/general/dashboard.json deleted file mode 100644 index e063550936..0000000000 --- a/public/language/hr/admin/general/dashboard.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "forum-traffic": "Promet foruma", - "page-views": "Broj pogleda", - "unique-visitors": "Jedinstveni posjetitelji", - "new-users": "New Users", - "posts": "Objave", - "topics": "Teme", - "page-views-seven": "Last 7 Days", - "page-views-thirty": "Last 30 Days", - "page-views-last-day": "Last 24 hours", - "page-views-custom": "Custom Date Range", - "page-views-custom-start": "Range Start", - "page-views-custom-end": "Range End", - "page-views-custom-help": "Enter a date range of page views you would like to view. If no date picker is available, the accepted format is YYYY-MM-DD", - "page-views-custom-error": "Please enter a valid date range in the format YYYY-MM-DD", - - "stats.yesterday": "Yesterday", - "stats.today": "Today", - "stats.last-week": "Last Week", - "stats.this-week": "This Week", - "stats.last-month": "Last Month", - "stats.this-month": "This Month", - "stats.all": "Sve vrijeme", - - "updates": "Nadogradnje", - "running-version": "Ovo je verzija NodeBB v%1.", - "keep-updated": "Uvijek se pobrinite da je Vaš NodeBB na najnovijoj verziji za najnovije sigurnosne mjere i popravke grešaka.", - "up-to-date": "

Vaš NodeBB je na najnovijoj verziji

", - "upgrade-available": "

A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-upgrade-available": "

This is an outdated pre-release version of NodeBB. A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-warning": "

Ovo je pre-release verzija NodeBB. Nenamjerne greške su moguće.

", - "running-in-development": "Forum je u razvojnom stanju. Forum bi mogao biti otvoren za napade; Molimo kontaktirajte vašeg sistemskog administratora", - "latest-lookup-failed": "

Failed to look up latest available version of NodeBB

", - - "notices": "Obavijest", - "restart-not-required": "Restart nije potreban", - "restart-required": "Potrebno je ponovno pokretanje", - "search-plugin-installed": "Dodatak pretrage instaliran", - "search-plugin-not-installed": "Dodatak pretrage nije instaliran", - "search-plugin-tooltip": "Instalirajte dodatak za pretragu sa stranice za upravljanje dodatcima da aktivirate mogućnost pretrage foruma.", - - "control-panel": "Kontrola sistema", - "rebuild-and-restart": "Rebuild & Restart", - "restart": "Restart", - "restart-warning": "Rebuilding or Restarting your NodeBB will drop all existing connections for a few seconds.", - "restart-disabled": "Rebuilding and Restarting your NodeBB has been disabled as you do not seem to be running it via the appropriate daemon.", - "maintenance-mode": "Održavanje", - "maintenance-mode-title": "Postavite mod za održavanje foruma", - "realtime-chart-updates": "Ažuriranja u stvarnom vremenu", - - "active-users": "Aktivni korisnici", - "active-users.users": "Korisnici", - "active-users.guests": "Gosti", - "active-users.total": "Ukupno", - "active-users.connections": "Veze", - - "anonymous-registered-users": "Anonimni vs Registrirani korisnici", - "anonymous": "Anomiman", - "registered": "Registriran", - - "user-presence": "Korisnik prisutan", - "on-categories": "Na listi kategorija", - "reading-posts": "Čita objave", - "browsing-topics": "Pretražuj teme", - "recent": "Nedavno", - "unread": "Nepročitano", - - "high-presence-topics": "Teme visoke prisutnosti", - - "graphs.page-views": "Pregled stranica", - "graphs.page-views-registered": "Page Views Registered", - "graphs.page-views-guest": "Page Views Guest", - "graphs.page-views-bot": "Page Views Bot", - "graphs.unique-visitors": "Jedninstveni posjetitelji", - "graphs.registered-users": "Registrirani korisnici", - "graphs.anonymous-users": "Anonimni korisnici", - "last-restarted-by": "Last restarted by", - "no-users-browsing": "No users browsing" -} diff --git a/public/language/hr/admin/general/homepage.json b/public/language/hr/admin/general/homepage.json deleted file mode 100644 index 4c4d323a2f..0000000000 --- a/public/language/hr/admin/general/homepage.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "home-page": "Naslovnica", - "description": "Izaberi koja stranica će se prikazivati kada korisnici navigiraju u root URL Vašeg foruma", - "home-page-route": "Putanja naslovnice", - "custom-route": "Uobičajna putanja", - "allow-user-home-pages": "Dozvoli korisničke naslovnice", - "home-page-title": "Title of the home page (default \"Home\")" -} \ No newline at end of file diff --git a/public/language/hr/admin/general/languages.json b/public/language/hr/admin/general/languages.json deleted file mode 100644 index a20b3c705d..0000000000 --- a/public/language/hr/admin/general/languages.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "language-settings": "Postavke jezika", - "description": "Zadani jezik odlučuje o postavkama jezika za sve korisnike foruma.
.Korisnici mogu sami odabrati jezik na stranici postavki jezika.", - "default-language": "Zadani jezik", - "auto-detect": "Auto Detect Language Setting for Guests" -} \ No newline at end of file diff --git a/public/language/hr/admin/general/navigation.json b/public/language/hr/admin/general/navigation.json deleted file mode 100644 index 4921e75e6c..0000000000 --- a/public/language/hr/admin/general/navigation.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "icon": "Ikona:", - "change-icon": "promjena", - "route": "Putanja:", - "tooltip": "Napomena:", - "text": "Tekst:", - "text-class": "Text Class: opcija", - "class": "Class: optional", - "id": "ID: opcionalno", - - "properties": "Postavke", - "groups": "Groups:", - "open-new-window": "Otvori u novom prozoru", - - "btn.delete": "Obriši", - "btn.disable": "Onemogući", - "btn.enable": "Omogući", - - "available-menu-items": "Dostupni artikli menija", - "custom-route": "Uobičajna putanja", - "core": "jezgra", - "plugin": "dodatak" -} \ No newline at end of file diff --git a/public/language/hr/admin/general/social.json b/public/language/hr/admin/general/social.json deleted file mode 100644 index b6f1c3ee29..0000000000 --- a/public/language/hr/admin/general/social.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "post-sharing": "Dijeljenje objave", - "info-plugins-additional": "Dodaci mogu dodati dodatne mreže za dijeljenje objava.", - "save-success": "Uspješno spremljene mreže za razmjenu objava!" -} \ No newline at end of file diff --git a/public/language/hr/admin/general/sounds.json b/public/language/hr/admin/general/sounds.json deleted file mode 100644 index 21bf8e26ff..0000000000 --- a/public/language/hr/admin/general/sounds.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "notifications": "Obavijesti", - "chat-messages": "Poruke", - "play-sound": "Pokreni", - "incoming-message": "Dolazna poruka", - "outgoing-message": "Odlazna poruka", - "upload-new-sound": "Učitaj novi zvuk", - "saved": "Postavke spremljene" -} \ No newline at end of file diff --git a/public/language/hr/admin/manage/digest.json b/public/language/hr/admin/manage/digest.json index 8f3661698a..38c634d1f6 100644 --- a/public/language/hr/admin/manage/digest.json +++ b/public/language/hr/admin/manage/digest.json @@ -13,6 +13,7 @@ "resent-single": "Manual digest resend completed", "resent-day": "Daily digest resent", "resent-week": "Weekly digest resent", + "resent-biweek": "Bi-Weekly digest resent", "resent-month": "Monthly digest resent", "null": "Never", "manual-run": "Manual digest run:", diff --git a/public/language/hr/admin/manage/users.json b/public/language/hr/admin/manage/users.json index 5561e6e901..b91a04252b 100644 --- a/public/language/hr/admin/manage/users.json +++ b/public/language/hr/admin/manage/users.json @@ -47,6 +47,7 @@ "users.uid": "uid", "users.username": "korisničko ime", "users.email": "email", + "users.no-email": "(no email)", "users.ip": "IP", "users.postcount": "postcount", "users.reputation": "reputacija", diff --git a/public/language/hr/admin/menu.json b/public/language/hr/admin/menu.json index dcb1868922..4dd18a9204 100644 --- a/public/language/hr/admin/menu.json +++ b/public/language/hr/admin/menu.json @@ -4,6 +4,7 @@ "dashboard/logins": "Logins", "dashboard/users": "Users", "dashboard/topics": "Topics", + "dashboard/searches": "Searches", "section-general": "Glavno", "section-manage": "Upravljanje", diff --git a/public/language/hr/admin/settings/email.json b/public/language/hr/admin/settings/email.json index b80c494740..48d010cef5 100644 --- a/public/language/hr/admin/settings/email.json +++ b/public/language/hr/admin/settings/email.json @@ -6,7 +6,7 @@ "from-help": "Ime prikazano u dolaznom emailu.", "smtp-transport": "SMTP Transport", - "smtp-transport.enabled": "Use an external email server to send emails", + "smtp-transport.enabled": "Enable SMTP Transport", "smtp-transport-help": "You can select from a list of well-known services or enter a custom one.", "smtp-transport.service": "Select a service", "smtp-transport.service-custom": "Custom Service", @@ -37,6 +37,10 @@ "subscriptions.hour": "Pregled Sati.", "subscriptions.hour-help": "Unesite broj koji pretstavlja vrijeme kada će se poslati pregled mailom (npr. 0 za ponoć, 17za 5 popodne).Imajte na umu da to vrijeme predstavlja vrijeme servera te ne mora predstavljati vrijeme na Vašem sistemu. Vrijeme servera je:
Sljedeći pregled će biti poslan .", "notifications.remove-images": "Remove images from email notifications", + "require-email-address": "Require new users to specify an email address", + "require-email-address-warning": "By default, users can opt-out of entering an email address. Enabling this option means they have to enter an email address in order to proceed with registration. It does not ensure user will enter a real email address, nor even an address they own.", "include-unverified-emails": "Send emails to recipients who have not explicitly confirmed their emails", - "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws." -} \ No newline at end of file + "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws.", + "prompt": "Prompt users to enter or confirm their emails", + "prompt-help": "If a user does not have an email set, or their email is not confirmed, a warning will be shown on screen." +} diff --git a/public/language/hr/admin/settings/general.json b/public/language/hr/admin/settings/general.json index c3d16f6f39..56f975324f 100644 --- a/public/language/hr/admin/settings/general.json +++ b/public/language/hr/admin/settings/general.json @@ -3,9 +3,9 @@ "title": "Naslov stranice", "title.short": "Short Title", "title.short-placeholder": "If no short title is specified, the site title will be used", - "title.url": "URL", + "title.url": "Title Link URL", "title.url-placeholder": "The URL of the site title", - "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.", + "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "title.name": "Ime Vaše zajednice", "title.show-in-header": "Prikaži naslov stranice u zaglavlju", "browser-title": "Naslov pretraživača", @@ -20,9 +20,9 @@ "logo.image": "Slika", "logo.image-placeholder": "Putanja logotipa za zaglavlje foruma", "logo.upload": "Učitaj", - "logo.url": "URL", + "logo.url": "Logo Link URL", "logo.url-placeholder": "URL loga stranice", - "logo.url-help": "U slučaju klika na logo,pošalji korisnike na ovu adresu.U slučaju praznog polja,korisnik će biti poslan na index foruma.", + "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "logo.alt-text": "Alt tekst", "log.alt-text-placeholder": "Alternativni tekst za dostupnost", "favicon": "Favicon", @@ -47,4 +47,4 @@ "undo-timeout": "Undo Timeout", "undo-timeout-help": "Some operations such as moving topics will allow for the moderator to undo their action within a certain timeframe. Set to 0 to disable undo completely.", "topic-tools": "Topic Tools" -} \ No newline at end of file +} diff --git a/public/language/hr/admin/settings/navigation.json b/public/language/hr/admin/settings/navigation.json index 4921e75e6c..00f84662dd 100644 --- a/public/language/hr/admin/settings/navigation.json +++ b/public/language/hr/admin/settings/navigation.json @@ -11,6 +11,8 @@ "properties": "Postavke", "groups": "Groups:", "open-new-window": "Otvori u novom prozoru", + "dropdown": "Dropdown", + "dropdown-placeholder": "Place your dropdown menu items below, ie:
<li><a href="https://myforum.com">Link 1</a></li>", "btn.delete": "Obriši", "btn.disable": "Onemogući", @@ -20,4 +22,4 @@ "custom-route": "Uobičajna putanja", "core": "jezgra", "plugin": "dodatak" -} \ No newline at end of file +} diff --git a/public/language/hr/admin/settings/notifications.json b/public/language/hr/admin/settings/notifications.json index d70a8007a4..9b33fa7397 100644 --- a/public/language/hr/admin/settings/notifications.json +++ b/public/language/hr/admin/settings/notifications.json @@ -2,5 +2,6 @@ "notifications": "Obavijesti", "welcome-notification": "Obavijest dobrodošlice", "welcome-notification-link": "Poveznica objave dobrodošlice", - "welcome-notification-uid": "Welcome Notification User (UID)" + "welcome-notification-uid": "Welcome Notification User (UID)", + "post-queue-notification-uid": "Post Queue User (UID)" } \ No newline at end of file diff --git a/public/language/hr/admin/settings/post.json b/public/language/hr/admin/settings/post.json index 4b6d50fc5a..21e8c03071 100644 --- a/public/language/hr/admin/settings/post.json +++ b/public/language/hr/admin/settings/post.json @@ -40,6 +40,7 @@ "teaser.last-post": "Last – Show the latest post, including the original post, if no replies", "teaser.last-reply": "Last – Show the latest reply, or a \"No replies\" placeholder if no replies", "teaser.first": "Prvi", + "showPostPreviewsOnHover": "Show a preview of posts when mouse overed", "unread": "Nepročitane postavke", "unread.cutoff": "Nepročitano dani prekinutosti", "unread.min-track-last": "Minimalni broj objava u temi prije praćenja zadnje pročitanog", @@ -56,6 +57,9 @@ "composer.show-help": "Prikaži \"Pomoć\"", "composer.enable-plugin-help": "Dozvoli dodatcima da dodaju sadržaj u \"Pomoć\"", "composer.custom-help": "Tekst \"Pomoć\"", + "backlinks": "Backlinks", + "backlinks.enabled": "Enable topic backlinks", + "backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.", "ip-tracking": "IP praćenje", "ip-tracking.each-post": "Prati IP adresu za svaku objavu", "enable-post-history": "Enable Post History" diff --git a/public/language/hr/admin/settings/user.json b/public/language/hr/admin/settings/user.json index abaddd6cf4..e2f9d018fa 100644 --- a/public/language/hr/admin/settings/user.json +++ b/public/language/hr/admin/settings/user.json @@ -71,6 +71,7 @@ "digest-freq.off": "Isključi", "digest-freq.daily": "Dnevno", "digest-freq.weekly": "Tjedno", + "digest-freq.biweekly": "Bi-Weekly", "digest-freq.monthly": "Mjesečno", "email-chat-notifs": "Pošalji email ukoliko stigne nova poruka dok nisam na mreži", "email-post-notif": "Pošalji email pri odgovoru u teme na koje pratim", diff --git a/public/language/hr/error.json b/public/language/hr/error.json index a95f997f54..df2dd79ccc 100644 --- a/public/language/hr/error.json +++ b/public/language/hr/error.json @@ -34,8 +34,9 @@ "email-invited": "Email was already invited", "email-not-confirmed": "Posting in some categories or topics is enabled once your email is confirmed, please click here to send a confirmation email.", "email-not-confirmed-chat": "Ne možete razgovarati dok Vaš email nije potvrđen. Kliknite ovdje da biste potvrdili svoj email.", - "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You won't be able to post or chat until your email is confirmed.", - "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery. Please click here to enter an email.", + "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You may not be able to post in some categories or chat until your email is confirmed.", + "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery, and may be necessary for chatting and posting in some categories. Please click here to enter an email.", + "user-doesnt-have-email": "User \"%1\" does not have an email set.", "email-confirm-failed": "Nismo u mogućnosti potvrditi Vaš email, pokušajte ponovno kasnije.", "confirm-email-already-sent": "Potvrdni email je poslan, počekajte %1 minuta za ponovni pokušaj.", "sendmail-not-found": "Sendmail nije pronađen, provjerite da li je instaliran?", @@ -103,6 +104,7 @@ "already-bookmarked": "Već ste zabilježili ovu objavu", "already-unbookmarked": "Već ste odbilježili ovu objavu", "cant-ban-other-admins": "Nemožete blokirati ostale administratore!", + "cant-make-banned-users-admin": "You can't make banned users admin.", "cant-remove-last-admin": "Vi ste jedini administrator. Dodajte korisnika kao administratora prije nego sebe odjavite kao administratora.", "account-deletion-disabled": "Account deletion is disabled", "cant-delete-admin": "Ukloni administratorske privilegije sa ovog računa prije brisanja.", diff --git a/public/language/hr/modules.json b/public/language/hr/modules.json index e56b7e30e1..ffec174d2b 100644 --- a/public/language/hr/modules.json +++ b/public/language/hr/modules.json @@ -54,7 +54,7 @@ "composer.formatting.strikethrough": "Precrtano", "composer.formatting.code": "Code", "composer.formatting.link": "Poveznica", - "composer.formatting.picture": "Slika", + "composer.formatting.picture": "Image Link", "composer.upload-picture": "Učitaj sliku", "composer.upload-file": "Učitaj datoteku", "composer.zen_mode": "Zen", diff --git a/public/language/hr/top.json b/public/language/hr/top.json new file mode 100644 index 0000000000..b8a05bfa5f --- /dev/null +++ b/public/language/hr/top.json @@ -0,0 +1,4 @@ +{ + "title": "Top", + "no_top_topics": "No top topics" +} \ No newline at end of file diff --git a/public/language/hr/topic.json b/public/language/hr/topic.json index 6c77001ccc..c4a5c879e6 100644 --- a/public/language/hr/topic.json +++ b/public/language/hr/topic.json @@ -47,6 +47,7 @@ "restored-by": "Restored by", "moved-from-by": "Moved from %1 by", "queued-by": "Post queued for approval →", + "backlink": "Referenced by", "bookmark_instructions": "Klikni ovdje za povratak na zadnji pročitani post.", "flag-post": "Flag this post", "flag-user": "Flag this user", @@ -138,6 +139,7 @@ "composer.handle_placeholder": "Enter your name/handle here", "composer.discard": "Odbaci", "composer.submit": "Podnesi", + "composer.additional-options": "Additional Options", "composer.schedule": "Schedule", "composer.replying_to": "Odgovori na %1", "composer.new_topic": "Nova tema", @@ -158,6 +160,7 @@ "newest_to_oldest": "Novije prema Starom", "most_votes": "Most Votes", "most_posts": "Most Posts", + "most_views": "Most Views", "stale.title": "Otvori novu temu?", "stale.warning": "Tema na koju odgovarate je stara. Želite li otvoriti novu temu i postaviti referencu u vašem odgovoru?", "stale.create": "Otvori novu temu", diff --git a/public/language/hr/user.json b/public/language/hr/user.json index 1d76144f1b..44e9fc7163 100644 --- a/public/language/hr/user.json +++ b/public/language/hr/user.json @@ -94,6 +94,7 @@ "digest_off": "Isključi", "digest_daily": "Dnevno", "digest_weekly": "Tjedno", + "digest_biweekly": "Bi-Weekly", "digest_monthly": "Mjesečno", "has_no_follower": "Ovaj korisnik nema pratitelja :(.", "follows_no_one": "Ovaj korisnik nikog ne prati :(", diff --git a/public/language/hu/admin/advanced/events.json b/public/language/hu/admin/advanced/events.json index 7dc7fb849b..b824e98cb9 100644 --- a/public/language/hu/admin/advanced/events.json +++ b/public/language/hu/admin/advanced/events.json @@ -3,6 +3,7 @@ "no-events": "Nem voltak események", "control-panel": "Esemény vezérlőpult", "delete-events": "Események törlése", + "confirm-delete-all-events": "Are you sure you want to delete all logged events?", "filters": "Szűrők", "filters-apply": "Szűrők érvényesítése", "filter-type": "Esemény típus", diff --git a/public/language/hu/admin/dashboard.json b/public/language/hu/admin/dashboard.json index 5d5c00b20f..e02ccff6de 100644 --- a/public/language/hu/admin/dashboard.json +++ b/public/language/hu/admin/dashboard.json @@ -56,8 +56,8 @@ "active-users.total": "Összesen", "active-users.connections": "Kapcsolatok", - "anonymous-registered-users": "Névtelen vs regisztrált felhasználók", - "anonymous": "Névtelen", + "guest-registered-users": "Guest vs Registered Users", + "guest": "Guest", "registered": "Regisztrált", "user-presence": "Felhasználói jelenlét", @@ -68,6 +68,7 @@ "unread": "Olvasatlan", "high-presence-topics": "Témakörök nagy jelenléttel", + "popular-searches": "Popular Searches", "graphs.page-views": "Oldal megtekintések", "graphs.page-views-registered": "Regisztrált látogatások", @@ -75,13 +76,14 @@ "graphs.page-views-bot": "Bot látogatások", "graphs.unique-visitors": "Egyedi látogatók", "graphs.registered-users": "Regisztrált felhasználók", - "graphs.anonymous-users": "Névtelen felhasználók", + "graphs.guest-users": "Guest Users", "last-restarted-by": "Utoljára újraindította:", "no-users-browsing": "Jelenleg nem böngész senki", "back-to-dashboard": "Vissza a vezérlőpultra", "details.no-users": "Nem csatlakozott egy felhasználó sem a kiválasztott időszakban", "details.no-topics": "Nem voltak új témakörök a kiválasztott időszakban", + "details.no-searches": "No searches have been made yet", "details.no-logins": "Nem volt bejelentkezés a kiválasztott időszakban", "details.logins-static": "A NodeBB csak %1 napig menti a munkamenet adatokat és az alábbi táblázat csak a legutóbbi aktív munkameneteket tartalmazza", "details.logins-login-time": "Bejelentkezés ideje" diff --git a/public/language/hu/admin/development/info.json b/public/language/hu/admin/development/info.json index fb32d22f96..d67e35f340 100644 --- a/public/language/hu/admin/development/info.json +++ b/public/language/hu/admin/development/info.json @@ -8,7 +8,11 @@ "nodejs": "nodejs", "online": "online", "git": "git", - "memory": "memória", + "process-memory": "process memory", + "system-memory": "system memory", + "used-memory-process": "Used memory by process", + "used-memory-os": "Used system memory", + "total-memory-os": "Total system memory", "load": "system load", "cpu-usage": "cpu usage", "uptime": "üzemidő", diff --git a/public/language/hu/admin/extend/rewards.json b/public/language/hu/admin/extend/rewards.json index 1258e3a0c6..d5fe130fe8 100644 --- a/public/language/hu/admin/extend/rewards.json +++ b/public/language/hu/admin/extend/rewards.json @@ -8,8 +8,6 @@ "delete": "Törlés", "enable": "Engedélyezés", "disable": "Tiltás", - "control-panel": "Jutalom vezérlés", - "new-reward": "Új jutalom", "alert.delete-success": "Jutalom sikeresen törölve", "alert.no-inputs-found": "Helytelen jutalom - nem található bevitel!", diff --git a/public/language/hu/admin/general/dashboard.json b/public/language/hu/admin/general/dashboard.json deleted file mode 100644 index 1eb1ab359b..0000000000 --- a/public/language/hu/admin/general/dashboard.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "forum-traffic": "Fórum forgalma", - "page-views": "Oldal megtekintések", - "unique-visitors": "Egyedi látogatók", - "new-users": "New Users", - "posts": "Hozzászólások", - "topics": "Témakörök", - "page-views-seven": "Az utóbbi 7 napban", - "page-views-thirty": "Az utóbbi 30 napban", - "page-views-last-day": "Az utóbbi 24 órában", - "page-views-custom": "Egyéni dátum tartomány", - "page-views-custom-start": "Tartomény kezdete", - "page-views-custom-end": "Tartomány vége", - "page-views-custom-help": "Adj meg egy dátum tartományt a kívánt oldal megtekintések megtekintéséhez. Ha nem áll rendelkezésre dátumválasztó, az elfogadott formátum ÉÉÉÉ-HH-NN", - "page-views-custom-error": "Kérlek, érvényes dátum tartományt adj meg ÉÉÉÉ-HH-NN formátumban.", - - "stats.yesterday": "Yesterday", - "stats.today": "Today", - "stats.last-week": "Last Week", - "stats.this-week": "This Week", - "stats.last-month": "Last Month", - "stats.this-month": "This Month", - "stats.all": "Mindenkori", - - "updates": "Frissítések", - "running-version": "Jelenleg a NodeBB v%1 verzióját futtatod.", - "keep-updated": "Mindig tégy róla, hogy a NodeBB naprakész a legfrissebb biztonsági javítások és hibajavítások végett.", - "up-to-date": "

Naprakész vagy

", - "upgrade-available": "

Kiadásra került egy új verzió (v%1). Vedd fontolóra a NodeBB frissítését.

", - "prerelease-upgrade-available": "

This is an outdated pre-release version of NodeBB. A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-warning": "

This is a pre-release version of NodeBB. Unintended bugs may occur.

", - "running-in-development": "Forum is running in development mode. The forum may be open to potential vulnerabilities; please contact your system administrator.", - "latest-lookup-failed": "

Failed to look up latest available version of NodeBB

", - - "notices": "Értesítések", - "restart-not-required": "Nem szükséges az újraindítás", - "restart-required": "Újraindítás szükséges", - "search-plugin-installed": "Kereső beépülő telepítve", - "search-plugin-not-installed": "Kereső beépülő nincs telepítve", - "search-plugin-tooltip": "Telepíts egy kereső beépülőt a beépülők oldaláról a keresési funkciók aktiválásához", - - "control-panel": "Rendszervezérlés", - "rebuild-and-restart": "Újraépítés & újraindítás", - "restart": "Újraindítás", - "restart-warning": "A NodeBB újraépítésével ill. újraindításával pár másodpercre elvész minden meglévő kapcsolat.", - "restart-disabled": "Rebuilding and Restarting your NodeBB has been disabled as you do not seem to be running it via the appropriate daemon.", - "maintenance-mode": "Karbantartási mód", - "maintenance-mode-title": "Kattints ide a NodeBB karbantartási módjának beállításához", - "realtime-chart-updates": "Valós idejű grafikon frissítések ", - - "active-users": "Aktív felhasználók", - "active-users.users": "Felhasználók", - "active-users.guests": "Vendégek", - "active-users.total": "Összesen", - "active-users.connections": "Kapcsolatok", - - "anonymous-registered-users": "Névtelen vs regisztrált felhasználók", - "anonymous": "Névtelen", - "registered": "Regisztrált", - - "user-presence": "Felhasználói jelenlét", - "on-categories": "A kategória listán", - "reading-posts": "Hozzászólásokat olvas", - "browsing-topics": "Témaköröket böngész", - "recent": "Mostanában", - "unread": "Olvasatlan", - - "high-presence-topics": "Témakörök nagy jelenléttel", - - "graphs.page-views": "Oldal megtekintések", - "graphs.page-views-registered": "Page Views Registered", - "graphs.page-views-guest": "Page Views Guest", - "graphs.page-views-bot": "Page Views Bot", - "graphs.unique-visitors": "Egyedi látogatók", - "graphs.registered-users": "Regisztrált felhasználók", - "graphs.anonymous-users": "Névtelen felhasználók", - "last-restarted-by": "Utoljára újraindította:", - "no-users-browsing": "No users browsing" -} diff --git a/public/language/hu/admin/general/homepage.json b/public/language/hu/admin/general/homepage.json deleted file mode 100644 index 679ff06af5..0000000000 --- a/public/language/hu/admin/general/homepage.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "home-page": "Kezdőlap", - "description": "Válaszd ki milyen oldal jelenjen meg, amikor a felhasználók fórumod gyökér URL címéhez navigálnak.", - "home-page-route": "Kezdőlap útvonala", - "custom-route": "Egyéni útvonal", - "allow-user-home-pages": "Felhasználói kezdőlapok engedélyezése", - "home-page-title": "A kezdőlap címe (alapértelmezés \"Kezdőlap\")" -} \ No newline at end of file diff --git a/public/language/hu/admin/general/languages.json b/public/language/hu/admin/general/languages.json deleted file mode 100644 index 63de413af4..0000000000 --- a/public/language/hu/admin/general/languages.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "language-settings": "Nyelvi beállítások", - "description": "Az alapértelmezett nyelv meghatározza a nyelvi beállításokat minden fórumot látogató számára.
Ezt az egyes felhasználók felülírhatják fiókjuk beállításaiban.", - "default-language": "Alapértelmezett nyelv", - "auto-detect": "Nyelvi beállítás automatikus észlelése vendégeknek" -} \ No newline at end of file diff --git a/public/language/hu/admin/general/navigation.json b/public/language/hu/admin/general/navigation.json deleted file mode 100644 index 0d96c33984..0000000000 --- a/public/language/hu/admin/general/navigation.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "icon": "Ikon:", - "change-icon": "módosítás", - "route": "Útvonal:", - "tooltip": "Elemleírás:", - "text": "Szöveg:", - "text-class": "Szövegosztály: nem kötelező", - "class": "Class: optional", - "id": "ID: nem kötelező", - - "properties": "Tulajdonságok:", - "groups": "Groups:", - "open-new-window": "Megnyitás új ablakban", - - "btn.delete": "Törlés", - "btn.disable": "Tiltás", - "btn.enable": "Engedélyezés", - - "available-menu-items": "Rendelkezésre álló menüelemek", - "custom-route": "Egyéni útvonal", - "core": "alapvető", - "plugin": "beépülő" -} \ No newline at end of file diff --git a/public/language/hu/admin/general/social.json b/public/language/hu/admin/general/social.json deleted file mode 100644 index ec11bf8035..0000000000 --- a/public/language/hu/admin/general/social.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "post-sharing": "Hozzászólás megosztás", - "info-plugins-additional": "Beépülőkkel további hálózatok adhatók hozzá hozzászólások megosztásához.", - "save-success": "Hozzászólás megosztási rendszerek sikeresen elmentve!" -} \ No newline at end of file diff --git a/public/language/hu/admin/general/sounds.json b/public/language/hu/admin/general/sounds.json deleted file mode 100644 index fc9943fc82..0000000000 --- a/public/language/hu/admin/general/sounds.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "notifications": "Értesítések", - "chat-messages": "Chat üzenetek", - "play-sound": "Lejátszás", - "incoming-message": "Bejövő üzenet", - "outgoing-message": "Kimenő üzenet", - "upload-new-sound": "Új hang feltöltése", - "saved": "Beállítások elmentve" -} \ No newline at end of file diff --git a/public/language/hu/admin/manage/digest.json b/public/language/hu/admin/manage/digest.json index 23f2c9bb91..dced3f927a 100644 --- a/public/language/hu/admin/manage/digest.json +++ b/public/language/hu/admin/manage/digest.json @@ -13,6 +13,7 @@ "resent-single": "Manuális összefoglaló küldés sikeres", "resent-day": "Napi összefoglaló újraküldés sikeres", "resent-week": "Heti összefoglaló újraküldés sikeres", + "resent-biweek": "Bi-Weekly digest resent", "resent-month": "Havi összefoglaló újraküldés sikeres", "null": "Soha", "manual-run": "Manuális összefoglaló futtatás:", diff --git a/public/language/hu/admin/manage/users.json b/public/language/hu/admin/manage/users.json index 6db8d91d12..71210911d2 100644 --- a/public/language/hu/admin/manage/users.json +++ b/public/language/hu/admin/manage/users.json @@ -47,6 +47,7 @@ "users.uid": "uid", "users.username": "felhasználónév", "users.email": "email", + "users.no-email": "(no email)", "users.ip": "IP", "users.postcount": "hozzászólások száma", "users.reputation": "hírnév", diff --git a/public/language/hu/admin/menu.json b/public/language/hu/admin/menu.json index e28e561edc..e2329570bb 100644 --- a/public/language/hu/admin/menu.json +++ b/public/language/hu/admin/menu.json @@ -4,6 +4,7 @@ "dashboard/logins": "Bejelentkezések", "dashboard/users": "Felhasználók", "dashboard/topics": "Témakörök", + "dashboard/searches": "Searches", "section-general": "Általános", "section-manage": "Kezelés", diff --git a/public/language/hu/admin/settings/email.json b/public/language/hu/admin/settings/email.json index d65bea6375..5206a293fb 100644 --- a/public/language/hu/admin/settings/email.json +++ b/public/language/hu/admin/settings/email.json @@ -6,7 +6,7 @@ "from-help": "Az emailben megjelenített feladói név.", "smtp-transport": "SMTP beállítások", - "smtp-transport.enabled": "Külső email szerver használata email-ek küldéséhez", + "smtp-transport.enabled": "Enable SMTP Transport", "smtp-transport-help": "Válogathatsz a jól ismert szolgáltatások listájából vagy megadhatsz sajátot.", "smtp-transport.service": "Válassz egy szolgáltatást", "smtp-transport.service-custom": "Egyedi szolgáltatás", @@ -37,6 +37,10 @@ "subscriptions.hour": "Összefoglalások küldési időpontja", "subscriptions.hour-help": "Kérjük adj meg egy számot, ami azt az órát jelenti, amikor az ütemezett összefoglalókat kiküldi a rendszer (0 az éjfél, 17 a délután 5 óra). Tartsd észben, hogy ez az időpont a szerver idejét veszi figyelembe és előfordulhat, hogy nem egyezik meg a Te gépeden jelzett idővel. A becsült szerver idő jelenleg:
A következő napi összefoglalás tervezett kiküldési ideje ", "notifications.remove-images": "Képek eltávolítása az email értesítésekből", + "require-email-address": "Require new users to specify an email address", + "require-email-address-warning": "By default, users can opt-out of entering an email address. Enabling this option means they have to enter an email address in order to proceed with registration. It does not ensure user will enter a real email address, nor even an address they own.", "include-unverified-emails": "Send emails to recipients who have not explicitly confirmed their emails", - "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws." -} \ No newline at end of file + "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws.", + "prompt": "Prompt users to enter or confirm their emails", + "prompt-help": "If a user does not have an email set, or their email is not confirmed, a warning will be shown on screen." +} diff --git a/public/language/hu/admin/settings/general.json b/public/language/hu/admin/settings/general.json index dd089c6783..46beeaf4b7 100644 --- a/public/language/hu/admin/settings/general.json +++ b/public/language/hu/admin/settings/general.json @@ -3,9 +3,9 @@ "title": "Weboldal címe", "title.short": "Rövid cím", "title.short-placeholder": "Ha nincs rövid cím beállítva, akkor a weboldal címét fogjuk használni", - "title.url": "URL", + "title.url": "Title Link URL", "title.url-placeholder": "A weboldal címre kattintáskor megnyitandó URL", - "title.url-help": "Amikor a cím szövegre kattint egy felhasználó, erre a címre irányítjuk át. Ha üres, akkor a fórum nyitóoldalára küldjük.", + "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "title.name": "A közösséged neve", "title.show-in-header": "A weboldal címének megjelenítése a fejlécben", "browser-title": "Böngésző cím", @@ -20,9 +20,9 @@ "logo.image": "Kép", "logo.image-placeholder": "A logó elérési útvonala, amit a fórum fejlécében fogunk megjeleníteni", "logo.upload": "Feltöltés", - "logo.url": "URL", + "logo.url": "Logo Link URL", "logo.url-placeholder": "A weboldal logójának URL-je", - "logo.url-help": "Amikor a logóra kattint egy felhasználó, erre a címre irányítjuk át. Ha üres, akkor a fórum nyitóoldalára küldjük.", + "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "logo.alt-text": "Alt szöveg", "log.alt-text-placeholder": "Alternatív szöveg", "favicon": "Favicon", @@ -47,4 +47,4 @@ "undo-timeout": "Undo Timeout", "undo-timeout-help": "Some operations such as moving topics will allow for the moderator to undo their action within a certain timeframe. Set to 0 to disable undo completely.", "topic-tools": "Topic Tools" -} \ No newline at end of file +} diff --git a/public/language/hu/admin/settings/navigation.json b/public/language/hu/admin/settings/navigation.json index 5c75530a87..704bea5c2a 100644 --- a/public/language/hu/admin/settings/navigation.json +++ b/public/language/hu/admin/settings/navigation.json @@ -11,6 +11,8 @@ "properties": "Tulajdonságok:", "groups": "Csoportok:", "open-new-window": "Megnyitás új ablakban", + "dropdown": "Dropdown", + "dropdown-placeholder": "Place your dropdown menu items below, ie:
<li><a href="https://myforum.com">Link 1</a></li>", "btn.delete": "Törlés", "btn.disable": "Tiltás", @@ -20,4 +22,4 @@ "custom-route": "Egyéni útvonal", "core": "alapvető", "plugin": "beépülő" -} \ No newline at end of file +} diff --git a/public/language/hu/admin/settings/notifications.json b/public/language/hu/admin/settings/notifications.json index 46379e19f5..1e19679d4a 100644 --- a/public/language/hu/admin/settings/notifications.json +++ b/public/language/hu/admin/settings/notifications.json @@ -2,5 +2,6 @@ "notifications": "Értesítések", "welcome-notification": "Üdvözlő értesítés", "welcome-notification-link": "Üdvözlő értesítés linkje", - "welcome-notification-uid": "Felhasználói üdvözlő értesítés (UID)" + "welcome-notification-uid": "Felhasználói üdvözlő értesítés (UID)", + "post-queue-notification-uid": "Post Queue User (UID)" } \ No newline at end of file diff --git a/public/language/hu/admin/settings/post.json b/public/language/hu/admin/settings/post.json index 3091efd2f2..0bdf3d228b 100644 --- a/public/language/hu/admin/settings/post.json +++ b/public/language/hu/admin/settings/post.json @@ -40,6 +40,7 @@ "teaser.last-post": "Utolsó – Utolsó hozzászólás megjelenítése, az eredeti hozzászólást is beleértve, ha nincsenek válaszok", "teaser.last-reply": "Utolsó – Utolsó válasz vagy, ha nincsenek válaszok, akkor \"Nincs válasz\" szöveg megjelenítése", "teaser.first": "Első", + "showPostPreviewsOnHover": "Show a preview of posts when mouse overed", "unread": "Olvasatlansági beállítások", "unread.cutoff": "Hány napig legyen olvasatlan egy hozzászólás", "unread.min-track-last": "Hozzászólások minimális száma egy témakörben, mielőtt a legutóbbi olvasás követése elkezdődik", @@ -56,6 +57,9 @@ "composer.show-help": "\"Segítség\" panel megjelenítése", "composer.enable-plugin-help": "Beépülők hozzáadhassanak saját tartalmaz a segítség panelhoz", "composer.custom-help": "Egyedi szöveg a segítségnél", + "backlinks": "Backlinks", + "backlinks.enabled": "Enable topic backlinks", + "backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.", "ip-tracking": "IP nyomonkövetés", "ip-tracking.each-post": "IP cím követése minden hozzászólásnál", "enable-post-history": "Hozzászólás történetiség engedélyezése" diff --git a/public/language/hu/admin/settings/user.json b/public/language/hu/admin/settings/user.json index d521d9fe3b..d0b96ca525 100644 --- a/public/language/hu/admin/settings/user.json +++ b/public/language/hu/admin/settings/user.json @@ -71,6 +71,7 @@ "digest-freq.off": "Kikapcsolt", "digest-freq.daily": "Napi", "digest-freq.weekly": "Heti", + "digest-freq.biweekly": "Bi-Weekly", "digest-freq.monthly": "Havi", "email-chat-notifs": "Email küldése, ha új csevegési üzenet érkezik miközben nem vagyok elérhető", "email-post-notif": "Email küldése, ha válasz érkezik olyan témakörhöz amire feliratkoztam", diff --git a/public/language/hu/error.json b/public/language/hu/error.json index 20ecb8cf9a..5e6a9e604d 100644 --- a/public/language/hu/error.json +++ b/public/language/hu/error.json @@ -34,8 +34,9 @@ "email-invited": "Ez az email cím már meg lett hívva", "email-not-confirmed": "Posting in some categories or topics is enabled once your email is confirmed, please click here to send a confirmation email.", "email-not-confirmed-chat": "Nem küldhetsz üzenetet amíg nem erősíted meg az email címed, kattints ide az email cím megerősítéséhez!", - "email-not-confirmed-email-sent": "Az email címed még nem lett megerősítve, kérlek ellenőrizd az email fiókodba érkező leveleket. Amíg nincs az email címed megerősítve addig nem tudsz üzeneteket küldeni valamint bejegyzést közzé tenni.", - "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery. Please click here to enter an email.", + "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You may not be able to post in some categories or chat until your email is confirmed.", + "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery, and may be necessary for chatting and posting in some categories. Please click here to enter an email.", + "user-doesnt-have-email": "User \"%1\" does not have an email set.", "email-confirm-failed": "Nem tudtuk ellenőrizni az e-mail címedet, kérlek próbálkozz később.", "confirm-email-already-sent": "A megerősítéshez szükséges email már el lett küldve, kérlek várj %1 percet az újraküldéshez.", "sendmail-not-found": "A levél küldés végrehajtása nem található, kérlek bizonyosodj meg róla, hogy telepítve van és végrehajtható a felhasználó által NodeBB-t futtatva.", @@ -103,6 +104,7 @@ "already-bookmarked": "Már elmentetted ezt a hozzászólást a könyvjelzők közé", "already-unbookmarked": "Már eltávolítottad ezt a hozzászólást a könyvjelzők közül", "cant-ban-other-admins": "Nem tilthatsz ki másik adminisztrátort!", + "cant-make-banned-users-admin": "You can't make banned users admin.", "cant-remove-last-admin": "Te vagy az egyedüli adminisztrátor. Adj hozzá egy másik felhasználót az adminisztrátori szerepkörhöz, hogy levehesd magadról az adminisztrátori rangot", "account-deletion-disabled": "Fiók törlése ki van kapcsolva", "cant-delete-admin": "Vedd el az adminisztrátori jogokat ettől a fióktól mielőtt törölni szeretnéd.", diff --git a/public/language/hu/modules.json b/public/language/hu/modules.json index 6ab23fb498..132b399d2a 100644 --- a/public/language/hu/modules.json +++ b/public/language/hu/modules.json @@ -54,7 +54,7 @@ "composer.formatting.strikethrough": "Áthúzás", "composer.formatting.code": "Code", "composer.formatting.link": "Hivatkozás", - "composer.formatting.picture": "Kép", + "composer.formatting.picture": "Image Link", "composer.upload-picture": "Kép feltöltése", "composer.upload-file": "Fájl feltöltése", "composer.zen_mode": "Zen mód", diff --git a/public/language/hu/top.json b/public/language/hu/top.json new file mode 100644 index 0000000000..b8a05bfa5f --- /dev/null +++ b/public/language/hu/top.json @@ -0,0 +1,4 @@ +{ + "title": "Top", + "no_top_topics": "No top topics" +} \ No newline at end of file diff --git a/public/language/hu/topic.json b/public/language/hu/topic.json index 2fe3a8fb93..dffb769f86 100644 --- a/public/language/hu/topic.json +++ b/public/language/hu/topic.json @@ -47,6 +47,7 @@ "restored-by": "Visszaállította", "moved-from-by": "Moved from %1 by", "queued-by": "Hozzászólás jóváhagyásra bejegyezve →", + "backlink": "Referenced by", "bookmark_instructions": "Kattints ide a beszélgetés utolsó hozzászólására ugráshoz.", "flag-post": "Jelöld meg ezt a bejegyzést", "flag-user": "Jelöld meg ezt a felhasználót", @@ -138,6 +139,7 @@ "composer.handle_placeholder": "Adj meg egy nevet/kezelőt", "composer.discard": "Elvet", "composer.submit": "Küldés", + "composer.additional-options": "Additional Options", "composer.schedule": "Időzítés", "composer.replying_to": "Válasz erre: %1", "composer.new_topic": "Új témakör", @@ -158,6 +160,7 @@ "newest_to_oldest": "Újabbak elől", "most_votes": "Legtöbb szavazat", "most_posts": "Legtöbb bejegyzés", + "most_views": "Most Views", "stale.title": "Inkább új témakör létrehozása?", "stale.warning": "A témakör, melyre válaszolsz, elég régi. Szeretnél helyette inkább új témakört létrehozni, és erre hivatkozni a válaszodban?", "stale.create": "Új témakör létrehozása", diff --git a/public/language/hu/user.json b/public/language/hu/user.json index 3d66f7b525..8eed6a7527 100644 --- a/public/language/hu/user.json +++ b/public/language/hu/user.json @@ -94,6 +94,7 @@ "digest_off": "Ki", "digest_daily": "Napi", "digest_weekly": "Heti", + "digest_biweekly": "Bi-Weekly", "digest_monthly": "Havi", "has_no_follower": "Ezt a felhasználót nem követi senki :(", "follows_no_one": "Ez a felhasználó nem követ senkit :(", diff --git a/public/language/id/admin/advanced/events.json b/public/language/id/admin/advanced/events.json index 56d9457971..b2c2033fb5 100644 --- a/public/language/id/admin/advanced/events.json +++ b/public/language/id/admin/advanced/events.json @@ -3,6 +3,7 @@ "no-events": "There are no events", "control-panel": "Events Control Panel", "delete-events": "Delete Events", + "confirm-delete-all-events": "Are you sure you want to delete all logged events?", "filters": "Filters", "filters-apply": "Apply Filters", "filter-type": "Event Type", diff --git a/public/language/id/admin/dashboard.json b/public/language/id/admin/dashboard.json index 0de31d4917..4d39626882 100644 --- a/public/language/id/admin/dashboard.json +++ b/public/language/id/admin/dashboard.json @@ -56,8 +56,8 @@ "active-users.total": "Total", "active-users.connections": "Connections", - "anonymous-registered-users": "Anonymous vs Registered Users", - "anonymous": "Anonymous", + "guest-registered-users": "Guest vs Registered Users", + "guest": "Guest", "registered": "Registered", "user-presence": "User Presence", @@ -68,6 +68,7 @@ "unread": "Unread", "high-presence-topics": "High Presence Topics", + "popular-searches": "Popular Searches", "graphs.page-views": "Page Views", "graphs.page-views-registered": "Page Views Registered", @@ -75,13 +76,14 @@ "graphs.page-views-bot": "Page Views Bot", "graphs.unique-visitors": "Unique Visitors", "graphs.registered-users": "Registered Users", - "graphs.anonymous-users": "Anonymous Users", + "graphs.guest-users": "Guest Users", "last-restarted-by": "Last restarted by", "no-users-browsing": "No users browsing", "back-to-dashboard": "Back to Dashboard", "details.no-users": "No users have joined within the selected timeframe", "details.no-topics": "No topics have been posted within the selected timeframe", + "details.no-searches": "No searches have been made yet", "details.no-logins": "No logins have been recorded within the selected timeframe", "details.logins-static": "NodeBB only saves session data for %1 days, and so this table below will only show the most recently active sessions", "details.logins-login-time": "Login Time" diff --git a/public/language/id/admin/development/info.json b/public/language/id/admin/development/info.json index 1003af1a5f..11202d9c3a 100644 --- a/public/language/id/admin/development/info.json +++ b/public/language/id/admin/development/info.json @@ -8,7 +8,11 @@ "nodejs": "nodejs", "online": "online", "git": "git", - "memory": "memory", + "process-memory": "process memory", + "system-memory": "system memory", + "used-memory-process": "Used memory by process", + "used-memory-os": "Used system memory", + "total-memory-os": "Total system memory", "load": "system load", "cpu-usage": "cpu usage", "uptime": "uptime", diff --git a/public/language/id/admin/extend/rewards.json b/public/language/id/admin/extend/rewards.json index 5383a90b33..df89d441a7 100644 --- a/public/language/id/admin/extend/rewards.json +++ b/public/language/id/admin/extend/rewards.json @@ -8,8 +8,6 @@ "delete": "Delete", "enable": "Enable", "disable": "Disable", - "control-panel": "Rewards Control", - "new-reward": "New Reward", "alert.delete-success": "Successfully deleted reward", "alert.no-inputs-found": "Illegal reward - no inputs found!", diff --git a/public/language/id/admin/general/dashboard.json b/public/language/id/admin/general/dashboard.json deleted file mode 100644 index 37ae537786..0000000000 --- a/public/language/id/admin/general/dashboard.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "forum-traffic": "Forum Traffic", - "page-views": "Page Views", - "unique-visitors": "Unique Visitors", - "new-users": "New Users", - "posts": "Posts", - "topics": "Topics", - "page-views-seven": "Last 7 Days", - "page-views-thirty": "Last 30 Days", - "page-views-last-day": "Last 24 hours", - "page-views-custom": "Custom Date Range", - "page-views-custom-start": "Range Start", - "page-views-custom-end": "Range End", - "page-views-custom-help": "Enter a date range of page views you would like to view. If no date picker is available, the accepted format is YYYY-MM-DD", - "page-views-custom-error": "Please enter a valid date range in the format YYYY-MM-DD", - - "stats.yesterday": "Yesterday", - "stats.today": "Today", - "stats.last-week": "Last Week", - "stats.this-week": "This Week", - "stats.last-month": "Last Month", - "stats.this-month": "This Month", - "stats.all": "All Time", - - "updates": "Updates", - "running-version": "You are running NodeBB v%1.", - "keep-updated": "Always make sure that your NodeBB is up to date for the latest security patches and bug fixes.", - "up-to-date": "

You are up-to-date

", - "upgrade-available": "

A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-upgrade-available": "

This is an outdated pre-release version of NodeBB. A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-warning": "

This is a pre-release version of NodeBB. Unintended bugs may occur.

", - "running-in-development": "Forum is running in development mode. The forum may be open to potential vulnerabilities; please contact your system administrator.", - "latest-lookup-failed": "

Failed to look up latest available version of NodeBB

", - - "notices": "Notices", - "restart-not-required": "Restart not required", - "restart-required": "Restart required", - "search-plugin-installed": "Search Plugin installed", - "search-plugin-not-installed": "Search Plugin not installed", - "search-plugin-tooltip": "Install a search plugin from the plugin page in order to activate search functionality", - - "control-panel": "System Control", - "rebuild-and-restart": "Rebuild & Restart", - "restart": "Restart", - "restart-warning": "Rebuilding or Restarting your NodeBB will drop all existing connections for a few seconds.", - "restart-disabled": "Rebuilding and Restarting your NodeBB has been disabled as you do not seem to be running it via the appropriate daemon.", - "maintenance-mode": "Maintenance Mode", - "maintenance-mode-title": "Click here to set up maintenance mode for NodeBB", - "realtime-chart-updates": "Realtime Chart Updates", - - "active-users": "Active Users", - "active-users.users": "Users", - "active-users.guests": "Guests", - "active-users.total": "Total", - "active-users.connections": "Connections", - - "anonymous-registered-users": "Anonymous vs Registered Users", - "anonymous": "Anonymous", - "registered": "Registered", - - "user-presence": "User Presence", - "on-categories": "On categories list", - "reading-posts": "Reading posts", - "browsing-topics": "Browsing topics", - "recent": "Recent", - "unread": "Unread", - - "high-presence-topics": "High Presence Topics", - - "graphs.page-views": "Page Views", - "graphs.page-views-registered": "Page Views Registered", - "graphs.page-views-guest": "Page Views Guest", - "graphs.page-views-bot": "Page Views Bot", - "graphs.unique-visitors": "Unique Visitors", - "graphs.registered-users": "Registered Users", - "graphs.anonymous-users": "Anonymous Users", - "last-restarted-by": "Last restarted by", - "no-users-browsing": "No users browsing" -} diff --git a/public/language/id/admin/general/homepage.json b/public/language/id/admin/general/homepage.json deleted file mode 100644 index 7428d59eeb..0000000000 --- a/public/language/id/admin/general/homepage.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "home-page": "Home Page", - "description": "Choose what page is shown when users navigate to the root URL of your forum.", - "home-page-route": "Home Page Route", - "custom-route": "Custom Route", - "allow-user-home-pages": "Allow User Home Pages", - "home-page-title": "Title of the home page (default \"Home\")" -} \ No newline at end of file diff --git a/public/language/id/admin/general/languages.json b/public/language/id/admin/general/languages.json deleted file mode 100644 index bdd57849b3..0000000000 --- a/public/language/id/admin/general/languages.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "language-settings": "Language Settings", - "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language", - "auto-detect": "Auto Detect Language Setting for Guests" -} \ No newline at end of file diff --git a/public/language/id/admin/general/navigation.json b/public/language/id/admin/general/navigation.json deleted file mode 100644 index 13dd01aae7..0000000000 --- a/public/language/id/admin/general/navigation.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "icon": "Icon:", - "change-icon": "change", - "route": "Route:", - "tooltip": "Tooltip:", - "text": "Text:", - "text-class": "Text Class: optional", - "class": "Class: optional", - "id": "ID: optional", - - "properties": "Properties:", - "groups": "Groups:", - "open-new-window": "Open in a new window", - - "btn.delete": "Delete", - "btn.disable": "Disable", - "btn.enable": "Enable", - - "available-menu-items": "Available Menu Items", - "custom-route": "Custom Route", - "core": "core", - "plugin": "plugin" -} \ No newline at end of file diff --git a/public/language/id/admin/general/social.json b/public/language/id/admin/general/social.json deleted file mode 100644 index 23aedfcfaa..0000000000 --- a/public/language/id/admin/general/social.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "post-sharing": "Post Sharing", - "info-plugins-additional": "Plugins can add additional networks for sharing posts.", - "save-success": "Successfully saved Post Sharing Networks!" -} \ No newline at end of file diff --git a/public/language/id/admin/general/sounds.json b/public/language/id/admin/general/sounds.json deleted file mode 100644 index 95ccbde0f1..0000000000 --- a/public/language/id/admin/general/sounds.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "notifications": "Notifications", - "chat-messages": "Chat Messages", - "play-sound": "Play", - "incoming-message": "Incoming Message", - "outgoing-message": "Outgoing Message", - "upload-new-sound": "Upload New Sound", - "saved": "Settings Saved" -} \ No newline at end of file diff --git a/public/language/id/admin/manage/digest.json b/public/language/id/admin/manage/digest.json index 8f3661698a..38c634d1f6 100644 --- a/public/language/id/admin/manage/digest.json +++ b/public/language/id/admin/manage/digest.json @@ -13,6 +13,7 @@ "resent-single": "Manual digest resend completed", "resent-day": "Daily digest resent", "resent-week": "Weekly digest resent", + "resent-biweek": "Bi-Weekly digest resent", "resent-month": "Monthly digest resent", "null": "Never", "manual-run": "Manual digest run:", diff --git a/public/language/id/admin/manage/users.json b/public/language/id/admin/manage/users.json index 38a065b954..2a3c0c4829 100644 --- a/public/language/id/admin/manage/users.json +++ b/public/language/id/admin/manage/users.json @@ -47,6 +47,7 @@ "users.uid": "uid", "users.username": "username", "users.email": "email", + "users.no-email": "(no email)", "users.ip": "IP", "users.postcount": "postcount", "users.reputation": "reputation", diff --git a/public/language/id/admin/menu.json b/public/language/id/admin/menu.json index 1c5ea73b4f..5b22fbeb36 100644 --- a/public/language/id/admin/menu.json +++ b/public/language/id/admin/menu.json @@ -4,6 +4,7 @@ "dashboard/logins": "Logins", "dashboard/users": "Users", "dashboard/topics": "Topics", + "dashboard/searches": "Searches", "section-general": "General", "section-manage": "Manage", diff --git a/public/language/id/admin/settings/email.json b/public/language/id/admin/settings/email.json index 9bdbde26a6..65f579d28c 100644 --- a/public/language/id/admin/settings/email.json +++ b/public/language/id/admin/settings/email.json @@ -6,7 +6,7 @@ "from-help": "The from name to display in the email.", "smtp-transport": "SMTP Transport", - "smtp-transport.enabled": "Use an external email server to send emails", + "smtp-transport.enabled": "Enable SMTP Transport", "smtp-transport-help": "You can select from a list of well-known services or enter a custom one.", "smtp-transport.service": "Select a service", "smtp-transport.service-custom": "Custom Service", @@ -37,6 +37,10 @@ "subscriptions.hour": "Digest Hour", "subscriptions.hour-help": "Please enter a number representing the hour to send scheduled email digests (e.g. 0 for midnight, 17 for 5:00pm). Keep in mind that this is the hour according to the server itself, and may not exactly match your system clock.
The approximate server time is:
The next daily digest is scheduled to be sent ", "notifications.remove-images": "Remove images from email notifications", + "require-email-address": "Require new users to specify an email address", + "require-email-address-warning": "By default, users can opt-out of entering an email address. Enabling this option means they have to enter an email address in order to proceed with registration. It does not ensure user will enter a real email address, nor even an address they own.", "include-unverified-emails": "Send emails to recipients who have not explicitly confirmed their emails", - "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws." -} \ No newline at end of file + "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws.", + "prompt": "Prompt users to enter or confirm their emails", + "prompt-help": "If a user does not have an email set, or their email is not confirmed, a warning will be shown on screen." +} diff --git a/public/language/id/admin/settings/general.json b/public/language/id/admin/settings/general.json index be7df90870..29b939861b 100644 --- a/public/language/id/admin/settings/general.json +++ b/public/language/id/admin/settings/general.json @@ -3,9 +3,9 @@ "title": "Site Title", "title.short": "Short Title", "title.short-placeholder": "If no short title is specified, the site title will be used", - "title.url": "URL", + "title.url": "Title Link URL", "title.url-placeholder": "The URL of the site title", - "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.", + "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "title.name": "Your Community Name", "title.show-in-header": "Show Site Title in Header", "browser-title": "Browser Title", @@ -20,9 +20,9 @@ "logo.image": "Image", "logo.image-placeholder": "Path to a logo to display on forum header", "logo.upload": "Upload", - "logo.url": "URL", + "logo.url": "Logo Link URL", "logo.url-placeholder": "The URL of the site logo", - "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.", + "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "logo.alt-text": "Alt Text", "log.alt-text-placeholder": "Alternative text for accessibility", "favicon": "Favicon", @@ -47,4 +47,4 @@ "undo-timeout": "Undo Timeout", "undo-timeout-help": "Some operations such as moving topics will allow for the moderator to undo their action within a certain timeframe. Set to 0 to disable undo completely.", "topic-tools": "Topic Tools" -} \ No newline at end of file +} diff --git a/public/language/id/admin/settings/navigation.json b/public/language/id/admin/settings/navigation.json index 13dd01aae7..7baca85096 100644 --- a/public/language/id/admin/settings/navigation.json +++ b/public/language/id/admin/settings/navigation.json @@ -11,6 +11,8 @@ "properties": "Properties:", "groups": "Groups:", "open-new-window": "Open in a new window", + "dropdown": "Dropdown", + "dropdown-placeholder": "Place your dropdown menu items below, ie:
<li><a href="https://myforum.com">Link 1</a></li>", "btn.delete": "Delete", "btn.disable": "Disable", @@ -20,4 +22,4 @@ "custom-route": "Custom Route", "core": "core", "plugin": "plugin" -} \ No newline at end of file +} diff --git a/public/language/id/admin/settings/notifications.json b/public/language/id/admin/settings/notifications.json index da6c9680a3..c6d8b928ce 100644 --- a/public/language/id/admin/settings/notifications.json +++ b/public/language/id/admin/settings/notifications.json @@ -2,5 +2,6 @@ "notifications": "Notifications", "welcome-notification": "Welcome Notification", "welcome-notification-link": "Welcome Notification Link", - "welcome-notification-uid": "Welcome Notification User (UID)" + "welcome-notification-uid": "Welcome Notification User (UID)", + "post-queue-notification-uid": "Post Queue User (UID)" } \ No newline at end of file diff --git a/public/language/id/admin/settings/post.json b/public/language/id/admin/settings/post.json index 27493aafbd..ab8245738c 100644 --- a/public/language/id/admin/settings/post.json +++ b/public/language/id/admin/settings/post.json @@ -40,6 +40,7 @@ "teaser.last-post": "Last – Show the latest post, including the original post, if no replies", "teaser.last-reply": "Last – Show the latest reply, or a \"No replies\" placeholder if no replies", "teaser.first": "First", + "showPostPreviewsOnHover": "Show a preview of posts when mouse overed", "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", @@ -56,6 +57,9 @@ "composer.show-help": "Show \"Help\" tab", "composer.enable-plugin-help": "Allow plugins to add content to the help tab", "composer.custom-help": "Custom Help Text", + "backlinks": "Backlinks", + "backlinks.enabled": "Enable topic backlinks", + "backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.", "ip-tracking": "IP Tracking", "ip-tracking.each-post": "Track IP Address for each post", "enable-post-history": "Enable Post History" diff --git a/public/language/id/admin/settings/user.json b/public/language/id/admin/settings/user.json index 48be13b75e..7923bf8cbe 100644 --- a/public/language/id/admin/settings/user.json +++ b/public/language/id/admin/settings/user.json @@ -71,6 +71,7 @@ "digest-freq.off": "Off", "digest-freq.daily": "Daily", "digest-freq.weekly": "Weekly", + "digest-freq.biweekly": "Bi-Weekly", "digest-freq.monthly": "Monthly", "email-chat-notifs": "Send an email if a new chat message arrives and I am not online", "email-post-notif": "Send an email when replies are made to topics I am subscribed to", diff --git a/public/language/id/error.json b/public/language/id/error.json index 3bc6e19b51..b1d2322af6 100644 --- a/public/language/id/error.json +++ b/public/language/id/error.json @@ -34,8 +34,9 @@ "email-invited": "Email was already invited", "email-not-confirmed": "Posting in some categories or topics is enabled once your email is confirmed, please click here to send a confirmation email.", "email-not-confirmed-chat": "You are unable to chat until your email is confirmed, please click here to confirm your email.", - "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You won't be able to post or chat until your email is confirmed.", - "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery. Please click here to enter an email.", + "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You may not be able to post in some categories or chat until your email is confirmed.", + "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery, and may be necessary for chatting and posting in some categories. Please click here to enter an email.", + "user-doesnt-have-email": "User \"%1\" does not have an email set.", "email-confirm-failed": "We could not confirm your email, please try again later.", "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", "sendmail-not-found": "The sendmail executable could not be found, please ensure it is installed and executable by the user running NodeBB.", @@ -103,6 +104,7 @@ "already-bookmarked": "You have already bookmarked this post", "already-unbookmarked": "You have already unbookmarked this post", "cant-ban-other-admins": "Kamu tidak dapat ban admin lainnya!", + "cant-make-banned-users-admin": "You can't make banned users admin.", "cant-remove-last-admin": "You are the only administrator. Add another user as an administrator before removing yourself as admin", "account-deletion-disabled": "Account deletion is disabled", "cant-delete-admin": "Remove administrator privileges from this account before attempting to delete it.", diff --git a/public/language/id/modules.json b/public/language/id/modules.json index 6e137c2752..fe27b6ffc6 100644 --- a/public/language/id/modules.json +++ b/public/language/id/modules.json @@ -54,7 +54,7 @@ "composer.formatting.strikethrough": "Strikethrough", "composer.formatting.code": "Code", "composer.formatting.link": "Link", - "composer.formatting.picture": "Picture", + "composer.formatting.picture": "Image Link", "composer.upload-picture": "Upload Image", "composer.upload-file": "Upload File", "composer.zen_mode": "Zen Mode", diff --git a/public/language/id/top.json b/public/language/id/top.json new file mode 100644 index 0000000000..b8a05bfa5f --- /dev/null +++ b/public/language/id/top.json @@ -0,0 +1,4 @@ +{ + "title": "Top", + "no_top_topics": "No top topics" +} \ No newline at end of file diff --git a/public/language/id/topic.json b/public/language/id/topic.json index c5d0cbdff0..8668e37499 100644 --- a/public/language/id/topic.json +++ b/public/language/id/topic.json @@ -47,6 +47,7 @@ "restored-by": "Restored by", "moved-from-by": "Moved from %1 by", "queued-by": "Post queued for approval →", + "backlink": "Referenced by", "bookmark_instructions": "Klik di sini untuk kembali ke posting yang terakhir kali dibaca pada topik ini.", "flag-post": "Flag this post", "flag-user": "Flag this user", @@ -138,6 +139,7 @@ "composer.handle_placeholder": "Enter your name/handle here", "composer.discard": "Buang", "composer.submit": "Kirim", + "composer.additional-options": "Additional Options", "composer.schedule": "Schedule", "composer.replying_to": "Membalas ke %1", "composer.new_topic": "Topik Baru", @@ -158,6 +160,7 @@ "newest_to_oldest": "Terbaru ke Terlama", "most_votes": "Most Votes", "most_posts": "Most Posts", + "most_views": "Most Views", "stale.title": "Create new topic instead?", "stale.warning": "The topic you are replying to is quite old. Would you like to create a new topic instead, and reference this one in your reply?", "stale.create": "Create a new topic", diff --git a/public/language/id/user.json b/public/language/id/user.json index 7bd0adeca9..58172e680a 100644 --- a/public/language/id/user.json +++ b/public/language/id/user.json @@ -94,6 +94,7 @@ "digest_off": "Off", "digest_daily": "Harian", "digest_weekly": "Mingguan", + "digest_biweekly": "Bi-Weekly", "digest_monthly": "Bulanan", "has_no_follower": "User ini tidak memiliki pengikut :(", "follows_no_one": "User ini tidak mengikuti seorangpun :(", diff --git a/public/language/it/admin/advanced/events.json b/public/language/it/admin/advanced/events.json index 062fb122e8..6f202cbdb1 100644 --- a/public/language/it/admin/advanced/events.json +++ b/public/language/it/admin/advanced/events.json @@ -3,6 +3,7 @@ "no-events": "Non ci sono eventi", "control-panel": "Pannello di controllo eventi", "delete-events": "Elimina eventi", + "confirm-delete-all-events": "Sei sicuro di voler eliminare tutti gli eventi registrati?", "filters": "Filtri", "filters-apply": "Applica filtri", "filter-type": "Tipo evento", diff --git a/public/language/it/admin/dashboard.json b/public/language/it/admin/dashboard.json index 121ab8e291..329c11dd7c 100644 --- a/public/language/it/admin/dashboard.json +++ b/public/language/it/admin/dashboard.json @@ -56,8 +56,8 @@ "active-users.total": "Totale", "active-users.connections": "Connessioni", - "anonymous-registered-users": "Anonimi vs Utenti Registrati", - "anonymous": "Anonimi", + "guest-registered-users": "Ospite vs Utenti Registrati", + "guest": "Ospite", "registered": "Registrati", "user-presence": "Presenza utente", @@ -68,6 +68,7 @@ "unread": "Non letto", "high-presence-topics": "Alta presenza discussioni", + "popular-searches": "Ricerche popolari", "graphs.page-views": "Pagine viste", "graphs.page-views-registered": "Pagine viste Registrati", @@ -75,13 +76,14 @@ "graphs.page-views-bot": "Pagine viste Bot", "graphs.unique-visitors": "Visitatori Unici", "graphs.registered-users": "Utenti Registrati", - "graphs.anonymous-users": "Utenti Anonimi", + "graphs.guest-users": "Utenti ospiti", "last-restarted-by": "Ultimo riavvio di", "no-users-browsing": "Nessun utente sta navigando", "back-to-dashboard": "Torna alla dashboard", "details.no-users": "Nessun utente si è iscritto nell'arco di tempo selezionato", "details.no-topics": "Nessuna discussione è stata postata nell'arco di tempo selezionato", + "details.no-searches": "Nessuna ricerca è ancora stata fatta", "details.no-logins": "Non sono stati registrati accessi nell'arco di tempo selezionato", "details.logins-static": "NodeBB salva solo i dati di sessione per %1 giorni, quindi la tabella qui sotto mostrerà solo le sessioni attive più recenti", "details.logins-login-time": "Tempo di accesso" diff --git a/public/language/it/admin/development/info.json b/public/language/it/admin/development/info.json index 2d32a277a5..28b9bf2831 100644 --- a/public/language/it/admin/development/info.json +++ b/public/language/it/admin/development/info.json @@ -8,7 +8,11 @@ "nodejs": "nodejs", "online": "online", "git": "git", - "memory": "memoria", + "process-memory": "memoria di processo", + "system-memory": "memoria di sistema", + "used-memory-process": "Memoria usata dal processo", + "used-memory-os": "Memoria di sistema usata", + "total-memory-os": "Memoria totale del sistema", "load": "carico sistema", "cpu-usage": "uso CPU", "uptime": "tempo di caricamento", diff --git a/public/language/it/admin/extend/rewards.json b/public/language/it/admin/extend/rewards.json index 1f1e6bc50f..7778999fbd 100644 --- a/public/language/it/admin/extend/rewards.json +++ b/public/language/it/admin/extend/rewards.json @@ -8,8 +8,6 @@ "delete": "Elimina", "enable": "Abilita", "disable": "Disabilita", - "control-panel": "Controllo dei premi", - "new-reward": "Nuovo premio", "alert.delete-success": "Premi eliminati con successo", "alert.no-inputs-found": "Premio illegale - immissioni non trovate!", diff --git a/public/language/it/admin/general/dashboard.json b/public/language/it/admin/general/dashboard.json deleted file mode 100644 index 5ae092ffd7..0000000000 --- a/public/language/it/admin/general/dashboard.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "forum-traffic": "Traffico Forum", - "page-views": "Pagine viste", - "unique-visitors": "Visitatori Unici", - "new-users": "Nuovi utenti", - "posts": "Post", - "topics": "Discussioni", - "page-views-seven": "Ultimi 7 giorni", - "page-views-thirty": "Ultimi 30 giorni", - "page-views-last-day": "Ultime 24 ore", - "page-views-custom": "Intervallo data personalizzato", - "page-views-custom-start": "Inizio intervallo", - "page-views-custom-end": "Fine intervallo", - "page-views-custom-help": "Immettere un intervallo di date, delle pagine viste, che si desidera visualizzare. Se non è disponibile un selezionatore di date, il formato accettato è il seguente YYYY-MM-DD", - "page-views-custom-error": "Si prega di inserire un intervallo di date valido nel formato YYYY-MM-DD", - - "stats.yesterday": "Ieri", - "stats.today": "Oggi", - "stats.last-week": "Ultima settimana", - "stats.this-week": "Questa settimana", - "stats.last-month": "Ultimo mese", - "stats.this-month": "Questo mese", - "stats.all": "Sempre", - - "updates": "Aggiornamenti", - "running-version": "Stai eseguendo NodeBB v%1.", - "keep-updated": "Assicurati sempre che il tuo NodeBB sia aggiornato con le ultime patch di sicurezza e correzioni per bug.", - "up-to-date": "

Seiaggiornato

", - "upgrade-available": "

È stata rilasciata una nuova versione (v%1). Considera di aggiornare il tuo NodeBB.

", - "prerelease-upgrade-available": "

Questa è una versione pre-release sorpassata di NodeBB. È stata rilasciata una nuova versione (v%1). Considerare di aggiornare il tuo NodeBB.

", - "prerelease-warning": "

Questa è una versione pre-release di NodeBB. Possono verificarsi bug non intenzionali.

", - "running-in-development": "Forum è in esecuzione in modalità sviluppo. Il forum potrebbe essere aperto a potenziali vulnerabilità; si prega di contattare il proprio amministratore di sistema.", - "latest-lookup-failed": "

Ricerca dell'ultima versione disponibile di NodeBB non riuscita

", - - "notices": "Annunci", - "restart-not-required": "Riavvio non richiesto", - "restart-required": "Riavvio richiesto", - "search-plugin-installed": "Ricerca Plugin installato", - "search-plugin-not-installed": "Ricerca Plugin non installato", - "search-plugin-tooltip": "Installa un plugin di ricerca dalla pagina plugin per attivare la funzionalità di ricerca", - - "control-panel": "Controllo sistema", - "rebuild-and-restart": "Riorganizza & Riavvia", - "restart": "Riavvia", - "restart-warning": "Riorganizzando o Riavviando il tuo NodeBB cadranno tutte le connessioni esistenti per alcuni secondi.", - "restart-disabled": "La Riorganizzazione e il Riavvio del tuo NodeBB sono stati disabilitati in quanto non sembra che tu lo stia eseguendo tramite il demone appropriato.", - "maintenance-mode": "Modalità Manutenzione", - "maintenance-mode-title": "Clicca qui per impostare la modalità di manutenzione per NodeBB", - "realtime-chart-updates": "Aggiornamento grafici in tempo reale", - - "active-users": "Utenti Attivi", - "active-users.users": "Utenti", - "active-users.guests": "Ospiti", - "active-users.total": "Totale", - "active-users.connections": "Connessioni", - - "anonymous-registered-users": "Anonimi vs Utenti Registrati", - "anonymous": "Anonimi", - "registered": "Registrati", - - "user-presence": "Presenza utente", - "on-categories": "Nella lista delle categorie", - "reading-posts": "Lettura post", - "browsing-topics": "Navigazione discussioni", - "recent": "Recenti", - "unread": "Non letto", - - "high-presence-topics": "Alta presenza discussioni", - - "graphs.page-views": "Pagine viste", - "graphs.page-views-registered": "Pagine viste Registrati", - "graphs.page-views-guest": "Pagine viste Ospite", - "graphs.page-views-bot": "Pagine viste Bot", - "graphs.unique-visitors": "Visitatori Unici", - "graphs.registered-users": "Utenti Registrati", - "graphs.anonymous-users": "Utenti Anonimi", - "last-restarted-by": "Ultimo riavvio di", - "no-users-browsing": "Nessun utente sta navigando" -} diff --git a/public/language/it/admin/general/homepage.json b/public/language/it/admin/general/homepage.json deleted file mode 100644 index 93c5b3e964..0000000000 --- a/public/language/it/admin/general/homepage.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "home-page": "Pagina Iniziale", - "description": "Scegliere quale pagina visualizzare quando gli utenti navigano all'URL principale del forum.", - "home-page-route": "Percorso Pagina Iniziale", - "custom-route": "Percorso personalizzato", - "allow-user-home-pages": "Consenti Pagina Iniziale Utente", - "home-page-title": "Titolo della pagina iniziale (impostazione predefinita \"Home\")" -} \ No newline at end of file diff --git a/public/language/it/admin/general/languages.json b/public/language/it/admin/general/languages.json deleted file mode 100644 index 321d12f8e4..0000000000 --- a/public/language/it/admin/general/languages.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "language-settings": "Impostazioni lingua", - "description": "La lingua predefinita determina le impostazioni della lingua per tutti gli utenti che visitano il tuo forum.
I singoli utenti possono sovrascrivere la lingua predefinita nella pagina delle impostazioni dell'account.", - "default-language": "Lingua predefinita", - "auto-detect": "Rilevazione automatica della lingua impostata per gli Ospiti" -} \ No newline at end of file diff --git a/public/language/it/admin/general/navigation.json b/public/language/it/admin/general/navigation.json deleted file mode 100644 index 04cd16e1a6..0000000000 --- a/public/language/it/admin/general/navigation.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "icon": "Icona:", - "change-icon": "modifica", - "route": "Percorso:", - "tooltip": "Suggerimento:", - "text": "Testo:", - "text-class": "Classe Testo: opzionale", - "class": "Classe: opzionale", - "id": "ID: opzionale", - - "properties": "Proprietà:", - "groups": "Gruppi:", - "open-new-window": "Apri in una nuova finestra", - - "btn.delete": "Elimina", - "btn.disable": "Disabilita", - "btn.enable": "Abilita", - - "available-menu-items": "Voci di Menu disponibili", - "custom-route": "Percorso personalizzato", - "core": "core", - "plugin": "plugin" -} \ No newline at end of file diff --git a/public/language/it/admin/general/social.json b/public/language/it/admin/general/social.json deleted file mode 100644 index 0a2eeb5181..0000000000 --- a/public/language/it/admin/general/social.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "post-sharing": "Condivisione Post", - "info-plugins-additional": "I plugin possono aggiungere reti aggiuntive per la condivisione dei post.", - "save-success": "Salvato con successo Reti Condivisione Post!" -} \ No newline at end of file diff --git a/public/language/it/admin/general/sounds.json b/public/language/it/admin/general/sounds.json deleted file mode 100644 index 0392f2954b..0000000000 --- a/public/language/it/admin/general/sounds.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "notifications": "Notifiche", - "chat-messages": "Messaggi di chat", - "play-sound": "Play", - "incoming-message": "Messaggio in arrivo", - "outgoing-message": "Messaggio in uscita", - "upload-new-sound": "Carica nuovo suono", - "saved": "Impostazioni salvate" -} \ No newline at end of file diff --git a/public/language/it/admin/manage/categories.json b/public/language/it/admin/manage/categories.json index 5166ed1b90..b46897636f 100644 --- a/public/language/it/admin/manage/categories.json +++ b/public/language/it/admin/manage/categories.json @@ -12,7 +12,7 @@ "ext-link": "Link esterni", "subcategories-per-page": "Sottocategorie per pagina", "is-section": "Tratta questa categoria come una sezione", - "post-queue": "Post in attesa", + "post-queue": "Coda post", "tag-whitelist": "Whitelist tag", "upload-image": "Caricamento Immagine", "delete-image": "Rimuove", diff --git a/public/language/it/admin/manage/digest.json b/public/language/it/admin/manage/digest.json index 7f1af991a5..9e66d7500c 100644 --- a/public/language/it/admin/manage/digest.json +++ b/public/language/it/admin/manage/digest.json @@ -13,6 +13,7 @@ "resent-single": "Invio del riepilogo manuale completato", "resent-day": "Rinvio riepilogo giornaliero", "resent-week": "Rinvio del riepilogo settimanale", + "resent-biweek": "Re invio riepilogo bisettimanale", "resent-month": "Rinvio del riepilogo mensile", "null": "Mai", "manual-run": "Esecuzione riepilogo manuale:", diff --git a/public/language/it/admin/manage/privileges.json b/public/language/it/admin/manage/privileges.json index 032e6a844a..0da351c033 100644 --- a/public/language/it/admin/manage/privileges.json +++ b/public/language/it/admin/manage/privileges.json @@ -51,13 +51,13 @@ "alert.saved": "Modifiche ai privilegi salvate e applicate", "alert.confirm-discard": "Sei sicuro di voler annullare le modifiche ai privilegi?", "alert.discarded": "Modifiche ai privilegi ignorate", - "alert.confirm-copyToAll": "Are you sure you wish to apply this set of %1 to all categories?", - "alert.confirm-copyToAllGroup": "Are you sure you wish to apply this group's set of %1 to all categories?", - "alert.confirm-copyToChildren": "Are you sure you wish to apply this set of %1 to all descendant (child) categories?", - "alert.confirm-copyToChildrenGroup": "Are you sure you wish to apply this group's set of %1 to all descendant (child) categories?", + "alert.confirm-copyToAll": "Sei sicuro di voler applicare questa serie di %1 a tutte le categorie?", + "alert.confirm-copyToAllGroup": "Sei sicuro di voler applicare questa serie di %1 del gruppo a tutte le categorie?", + "alert.confirm-copyToChildren": "Sei sicuro di voler applicare questa serie di %1 a tutte le categorie discendenti (figli)?", + "alert.confirm-copyToChildrenGroup": "Sei sicuro di voler applicare questa serie di %1 del questo gruppo a tutte le categorie discendenti (figli)?", "alert.no-undo": "Questa azione non può essere annullata.", "alert.admin-warning": "Gli amministratori ottengono implicitamente tutti i privilegi", - "alert.copyPrivilegesFrom-title": "Select a category to copy from", - "alert.copyPrivilegesFrom-warning": "This will copy %1 from the selected category.", - "alert.copyPrivilegesFromGroup-warning": "This will copy this group's set of %1 from the selected category." + "alert.copyPrivilegesFrom-title": "Seleziona una categoria da cui copiare", + "alert.copyPrivilegesFrom-warning": "Questo copierà 1% dalla categoria selezionata.", + "alert.copyPrivilegesFromGroup-warning": "Questo copierà la serie di %1 da questo gruppo dalla categoria selezionata." } \ No newline at end of file diff --git a/public/language/it/admin/manage/registration.json b/public/language/it/admin/manage/registration.json index 68e491a73d..11d0329b87 100644 --- a/public/language/it/admin/manage/registration.json +++ b/public/language/it/admin/manage/registration.json @@ -1,6 +1,6 @@ { - "queue": "Attesa", - "description": "Non ci sono utenti in attesa di registrazione.
Per abilitare questa funzione, vai in Impostazioni → Utente → Registrazione Utente e imposta Tipo Registrazione su \"Approvazione Amministratore\".", + "queue": "Coda", + "description": "Non ci sono utenti nella coda di registrazione.
Per abilitare questa funzione, vai in Impostazioni → Utente → Registrazione Utente e imposta Tipo Registrazione su \"Approvazione Amministratore\".", "list.name": "Nome", "list.email": "Email", diff --git a/public/language/it/admin/manage/uploads.json b/public/language/it/admin/manage/uploads.json index 7d12c43dd0..5d992eec52 100644 --- a/public/language/it/admin/manage/uploads.json +++ b/public/language/it/admin/manage/uploads.json @@ -6,6 +6,6 @@ "size/filecount": "Dimensione / Numero file", "confirm-delete": "Vuoi davvero cancellare questo file?", "filecount": "%1 file", - "new-folder": "New Folder", - "name-new-folder": "Enter a name for new the folder" + "new-folder": "Nuova cartella", + "name-new-folder": "Inserisci un nome per la nuova cartella" } \ No newline at end of file diff --git a/public/language/it/admin/manage/users.json b/public/language/it/admin/manage/users.json index 9d08cf4257..ad076e6a89 100644 --- a/public/language/it/admin/manage/users.json +++ b/public/language/it/admin/manage/users.json @@ -47,6 +47,7 @@ "users.uid": "id utente", "users.username": "username", "users.email": "email", + "users.no-email": "(nessuna email)", "users.ip": "IP", "users.postcount": "numero di post", "users.reputation": "reputazione", diff --git a/public/language/it/admin/menu.json b/public/language/it/admin/menu.json index 1148c9b67d..8851052e87 100644 --- a/public/language/it/admin/menu.json +++ b/public/language/it/admin/menu.json @@ -4,6 +4,7 @@ "dashboard/logins": "Accessi", "dashboard/users": "Utenti", "dashboard/topics": "Discussioni", + "dashboard/searches": "Ricerche", "section-general": "Generale", "section-manage": "Gestisci", @@ -12,8 +13,8 @@ "manage/tags": "Tabs", "manage/users": "Utenti", "manage/admins-mods": "Amministratori e Moderatori", - "manage/registration": "Attesa di registrazione", - "manage/post-queue": "Post in attesa", + "manage/registration": "Coda di registrazione", + "manage/post-queue": "Coda post", "manage/groups": "Gruppi", "manage/ip-blacklist": "Lista degli IP bloccati", "manage/uploads": "Uploads", diff --git a/public/language/it/admin/settings/email.json b/public/language/it/admin/settings/email.json index aea02f6460..2d2eae1612 100644 --- a/public/language/it/admin/settings/email.json +++ b/public/language/it/admin/settings/email.json @@ -6,7 +6,7 @@ "from-help": "Il nome da visualizzare nell'email.", "smtp-transport": "Trasporto SMTP", - "smtp-transport.enabled": "Utilizza un server di posta elettronica esterno per inviare le email", + "smtp-transport.enabled": "Abilita trasporto SMTP", "smtp-transport-help": "Puoi selezionare da un elenco di servizi noti o inserirne uno personalizzato.", "smtp-transport.service": "Seleziona un servizio", "smtp-transport.service-custom": "Servizio personalizzato", @@ -37,6 +37,10 @@ "subscriptions.hour": "Orario riepilogo", "subscriptions.hour-help": "Si prega di inserire un numero che rappresenta l'ora per l'invio dell'email programmate (es. 0per mezzanotte, 17per le 17: 00). Tieni presente che questa è l'ora secondo il server stesso, e potrebbe non combaciare esattamente al tuo orologio di sistema.
L'orario approssimativo del server è:
La prossima trasmissione giornaliera è prevista alle ", "notifications.remove-images": "Rimuovi le immagini dalle notifiche email", + "require-email-address": "Richiedere ai nuovi utenti di specificare un indirizzo email", + "require-email-address-warning": "Per impostazione predefinita, gli utenti possono rinunciare a inserire un indirizzo email. Abilitare questa opzione significa che devono inserire un indirizzo email per procedere con la registrazione. Non assicura che l'utente inserisca un indirizzo email reale, e nemmeno un indirizzo che possiede.", "include-unverified-emails": "Invia email a destinatari che non hanno confermato esplicitamente le loro email", - "include-unverified-warning": "Per impostazione predefinita, gli utenti con email associate al loro account sono già stati verificati, ma ci sono situazioni in cui ciò non è vero (ad esempio accessi SSO, vecchi utenti, ecc.). Abilita questa impostazione a tuo rischio e pericolo – l'invio di email a indirizzi non verificati può essere una violazione delle leggi regionali anti-spam." -} \ No newline at end of file + "include-unverified-warning": "Per impostazione predefinita, gli utenti con email associate al loro account sono già stati verificati, ma ci sono situazioni in cui ciò non è vero (ad esempio accessi SSO, vecchi utenti, ecc.). Abilita questa impostazione a tuo rischio e pericolo – l'invio di email a indirizzi non verificati può essere una violazione delle leggi regionali anti-spam.", + "prompt": "Chiedi agli utenti di inserire o confermare le loro email", + "prompt-help": "Se un utente non ha impostato un'email, o la sua email non è confermata, sarà mostrato un avviso sullo schermo." +} diff --git a/public/language/it/admin/settings/general.json b/public/language/it/admin/settings/general.json index 803565154e..c556adf46c 100644 --- a/public/language/it/admin/settings/general.json +++ b/public/language/it/admin/settings/general.json @@ -3,9 +3,9 @@ "title": "Titolo Sito", "title.short": "Titolo abbreviato", "title.short-placeholder": "Se non specifichi un titolo abbreviato, verrà utilizzato il titolo completo", - "title.url": "URL", + "title.url": "Link URL Titolo", "title.url-placeholder": "L'URL del titolo del sito", - "title.url-help": "Quando si clicca sul titolo, invia gli utenti a questo indirizzo. Se lasciato vuoto, l'utente sarà inviato all'indice del forum.", + "title.url-help": "Quando il titolo viene cliccato, invia gli utenti a questo indirizzo. Se lasciato vuoto, l'utente sarà inviato all'indice del forum.
Nota: Questo non è l'URL esterno usato nelle email, ecc. Questo è impostato dalla proprietà url in config.json", "title.name": "Il Nome della Comunità", "title.show-in-header": "Mostra Titolo Sito nell'Intestazione", "browser-title": "Titolo Browser", @@ -20,9 +20,9 @@ "logo.image": "Immagine", "logo.image-placeholder": "Percorso del logo da visualizzare sull'intestazione del forum", "logo.upload": "Carica", - "logo.url": "URL", + "logo.url": "Link URL Logo", "logo.url-placeholder": "L'URL del logo del sito", - "logo.url-help": "Quando si fa clic sul logo, invia gli utenti a questo indirizzo. Se lasciato vuoto, l'utente sarà inviato all'indice del forum.", + "logo.url-help": "Quando il logo viene cliccato, invia gli utenti a questo indirizzo. Se lasciato vuoto, l'utente sarà inviato all'indice del forum.
Nota: Questo non è l'URL esterno usato nelle email, ecc. Questo è impostato dalla proprietà url in config.json", "logo.alt-text": "Testo alternativo", "log.alt-text-placeholder": "Testo alternativo per l'accessibilità", "favicon": "Favicon", @@ -47,4 +47,4 @@ "undo-timeout": "Annulla timeout", "undo-timeout-help": "Alcune operazioni come lo spostamento delle discussioni permetteranno al moderatore di annullare la sua azione entro un certo periodo di tempo. Imposta a 0 per disabilitare completamente l'annullamento.", "topic-tools": "Strumenti discussione" -} \ No newline at end of file +} diff --git a/public/language/it/admin/settings/navigation.json b/public/language/it/admin/settings/navigation.json index 04cd16e1a6..75607ccce6 100644 --- a/public/language/it/admin/settings/navigation.json +++ b/public/language/it/admin/settings/navigation.json @@ -11,6 +11,8 @@ "properties": "Proprietà:", "groups": "Gruppi:", "open-new-window": "Apri in una nuova finestra", + "dropdown": "Menu a tendina", + "dropdown-placeholder": "Posiziona le voci del tuo menu a tendina in basso, ad esempio:
<li><a href="https://myforum.com">Link 1</a></li>", "btn.delete": "Elimina", "btn.disable": "Disabilita", @@ -20,4 +22,4 @@ "custom-route": "Percorso personalizzato", "core": "core", "plugin": "plugin" -} \ No newline at end of file +} diff --git a/public/language/it/admin/settings/notifications.json b/public/language/it/admin/settings/notifications.json index 6168c5510c..9574902ce0 100644 --- a/public/language/it/admin/settings/notifications.json +++ b/public/language/it/admin/settings/notifications.json @@ -2,5 +2,6 @@ "notifications": "Notifiche", "welcome-notification": "Notifica di benvenuto", "welcome-notification-link": "Collegamento a Notifica di benvenuto", - "welcome-notification-uid": "Notifica di benvenuto utente (UID)" + "welcome-notification-uid": "Notifica di benvenuto utente (UID)", + "post-queue-notification-uid": "Coda post utente (UID)" } \ No newline at end of file diff --git a/public/language/it/admin/settings/post.json b/public/language/it/admin/settings/post.json index fa1b9066dc..74c8a44199 100644 --- a/public/language/it/admin/settings/post.json +++ b/public/language/it/admin/settings/post.json @@ -7,14 +7,14 @@ "sorting.most-posts": "Più Post", "sorting.topic-default": "Ordinamento Discussione Predefinito", "length": "Lunghezza Post", - "post-queue": "Coda Post", + "post-queue": "Coda post", "restrictions": "Restrizioni pubblicazione", "restrictions-new": "Restrizioni Nuovo Utente", - "restrictions.post-queue": "Abilita post in attesa", + "restrictions.post-queue": "Abilita coda post", "restrictions.post-queue-rep-threshold": "Reputazione necessaria a superare la coda dei post", "restrictions.groups-exempt-from-post-queue": "Seleziona i gruppi che dovrebbero essere esclusi dalla coda dei post", "restrictions-new.post-queue": "Abilita le restrizioni per i nuovi utenti", - "restrictions.post-queue-help": "Abilitando la coda dei post, i post dei nuovi utenti, verranno messi in coda per l'approvazione", + "restrictions.post-queue-help": "Abilitando la coda dei post, i post dei nuovi utenti, saranno messi in coda per l'approvazione", "restrictions-new.post-queue-help": "Abilitando le restrizioni per i nuovi utenti verranno impostate le restrizioni sui post dei nuovi utenti", "restrictions.seconds-between": "Numero di secondi tra i post", "restrictions.seconds-between-new": "Numero di secondi tra i post per i nuovi utenti", @@ -40,6 +40,7 @@ "teaser.last-post": "Ultimo – Mostra l'ultimo post, incluso il post originale, se non ci sono risposte", "teaser.last-reply": "Ultimo – Mostra l'ultima risposta o un segnaposto \"Nessuna risposta\" se non risposto", "teaser.first": "Primo", + "showPostPreviewsOnHover": "Mostra un'anteprima dei post quando il mouse ci passa sopra", "unread": "Impostazioni non Lette", "unread.cutoff": "Giorni di interruzione non letti", "unread.min-track-last": "Post minimi nell'argomento prima del monitoraggio dell'ultima lettura", @@ -56,6 +57,9 @@ "composer.show-help": "Mostra la scheda \"Aiuto\"", "composer.enable-plugin-help": "Consenti ai plug-in di aggiungere contenuti alla scheda Guida", "composer.custom-help": "Testo di aiuto personalizzato", + "backlinks": "Backlink", + "backlinks.enabled": "Abilita backlink discussione", + "backlinks.help": "Se un post fa riferimento ad un altra discussione, un link al post sarà inserito nella discussione di riferimento in quel momento.", "ip-tracking": "Monitoraggio IP", "ip-tracking.each-post": "Traccia l'indirizzo IP per ogni post", "enable-post-history": "Abilita Cronologia post" diff --git a/public/language/it/admin/settings/user.json b/public/language/it/admin/settings/user.json index 55c7369480..ed79c8ebc3 100644 --- a/public/language/it/admin/settings/user.json +++ b/public/language/it/admin/settings/user.json @@ -43,7 +43,7 @@ "registration-type.admin-invite-only": "Solo invito per Amministratori", "registration-type.disabled": "Niente registrazione", "registration-type.help": "Normale: gli utenti possono registrarsi dalla pagina/di registrazione.
\nSolo invito: gli utenti possono invitare altri dalla pagina utenti.
\nSolo su invito amministratore: solo gli amministratori possono invitare altri utenti edalle pagine amministratore/gestione/utenti.
\nNessuna registrazione - Nessuna registrazione dell'utente.
", - "registration-approval-type.help": "Normale: gli utenti vengono registrati immediatamente.
\nApprovazione amministratore - Le registrazioni degli utenti vengono inserite in una coda di approvazione per amministratori.
\nApprovazione amministratore per IP - Normale per i nuovi utenti, Approvazione amministratore per indirizzi IP che dispongono già di un account.
", + "registration-approval-type.help": "Normale: gli utenti vengono registrati immediatamente.
\nApprovazione amministratore - Le registrazioni degli utenti sono inserite in una coda di approvazione per amministratori.
\nApprovazione amministratore per IP - Normale per i nuovi utenti, Approvazione amministratore per indirizzi IP che dispongono già di un account.
", "registration-queue-auto-approve-time": "Tempo di approvazione automatico", "registration-queue-auto-approve-time-help": "Ore prima che l'utente venga approvato automaticamente. 0 per disabilitare.", "registration-queue-show-average-time": "Mostra agli utenti il tempo medio necessario per approvare un nuovo utente", @@ -71,6 +71,7 @@ "digest-freq.off": "Spento", "digest-freq.daily": "Quotidiano", "digest-freq.weekly": "Settimanale", + "digest-freq.biweekly": "Bisettimanale", "digest-freq.monthly": "Mensile", "email-chat-notifs": "Manda una email se arriva un nuovo messaggio di chat e non sono online", "email-post-notif": "Manda una email quando ci sono nuove risposte a discussioni a cui sono iscritto", diff --git a/public/language/it/error.json b/public/language/it/error.json index 166ee800fa..2ce566199d 100644 --- a/public/language/it/error.json +++ b/public/language/it/error.json @@ -25,17 +25,18 @@ "invalid-event": "Evento non valido: %1", "local-login-disabled": "Il sistema di accesso locale è stato disabilitato per gli account senza privilegi.", "csrf-invalid": "Non siamo riusciti a farti accedere, probabilmente perché la sessione è scaduta. Per favore riprova.", - "invalid-path": "Invalid path", - "folder-exists": "Folder exists", + "invalid-path": "Percorso non valido", + "folder-exists": "La cartella esiste", "invalid-pagination-value": "Valore di impaginazione non valido, deve essere almeno %1 ed al massimo %2", "username-taken": "Nome utente già esistente", "email-taken": "Email già esistente", "email-nochange": "L'email inserita è la stessa dell'email già presente in archivio.", "email-invited": "L'email è già stata invitata", - "email-not-confirmed": "Posting in some categories or topics is enabled once your email is confirmed, please click here to send a confirmation email.", + "email-not-confirmed": "Sarai abilitato a postare in alcune categorie o discussioni una volta che la tua email sarà confermata, per favore clicca qui per inviare una email di conferma.", "email-not-confirmed-chat": "Non puoi chattare finché non confermi la tua email, per favore clicca qui per confermare la tua email.", - "email-not-confirmed-email-sent": "La tua email non è stata ancora confermata, controlla la tua casella di posta per l'email di conferma. Non potrai pubblicare post o chattare fino a quando la tua email non sarà confermata.", - "no-email-to-confirm": "Il tuo account non ha un'email impostata. Un'email è necessaria per il recupero dell'account. Clicca qui per inserire un'email.", + "email-not-confirmed-email-sent": "La tua email non è stata ancora confermata, controlla la tua casella di posta per l'email di conferma. Potresti non essere in grado di postare in alcune categorie o chattare fino a quando la tua email non sarà confermata.", + "no-email-to-confirm": "Il tuo account non ha un'email impostata. Un'email è necessaria per il recupero dell'account, e può essere necessaria per chattare e postare in alcune categorie. Clicca qui per inserire un'email.", + "user-doesnt-have-email": "L'utente \"%1\" non ha impostato un email.", "email-confirm-failed": "Non abbiamo potuto confermare la tua email, per favore riprovaci più tardi.", "confirm-email-already-sent": "Email di conferma già inviata, per favore attendere %1 minuto(i) per inviarne un'altra.", "sendmail-not-found": "Impossibile trovare l'eseguibile di sendmail, per favore assicurati che sia installato ed eseguibile dall'utente che esegue NodeBB.", @@ -103,6 +104,7 @@ "already-bookmarked": "Hai già aggiunto questa discussione ai preferiti.", "already-unbookmarked": "Hai già rimosso questa discussione dai preferiti", "cant-ban-other-admins": "Non puoi bannare altri amministratori!", + "cant-make-banned-users-admin": "Non puoi rendere amministratori gli utenti bannati.", "cant-remove-last-admin": "Sei l'unico Amministratore. Aggiungi un altro amministratore prima di rimuovere il tuo ruolo", "account-deletion-disabled": "L'eliminazione dell'account è disabilitata", "cant-delete-admin": "Togli i privilegi amministrativi da questo account prima di provare ad eliminarlo.", diff --git a/public/language/it/modules.json b/public/language/it/modules.json index 5b1bb16729..b081184324 100644 --- a/public/language/it/modules.json +++ b/public/language/it/modules.json @@ -54,7 +54,7 @@ "composer.formatting.strikethrough": "Barrato", "composer.formatting.code": "Codice", "composer.formatting.link": "Collegamento", - "composer.formatting.picture": "Immagine", + "composer.formatting.picture": "Link immagine", "composer.upload-picture": "Carica immagine", "composer.upload-file": "Carica file", "composer.zen_mode": "Zen Mode", diff --git a/public/language/it/notifications.json b/public/language/it/notifications.json index 0fdc2b79e5..869f711a7c 100644 --- a/public/language/it/notifications.json +++ b/public/language/it/notifications.json @@ -48,7 +48,7 @@ "posts-exported": "%1 post esportati, clicca per scaricare", "uploads-exported": "%1 caricamenti esportati, clicca per scaricare", "users-csv-exported": "Utenti esportati in CSV, clicca per scaricare", - "post-queue-accepted": "Your queued post has been accepted. Click here to see your post.", + "post-queue-accepted": "Il tuo post in coda è stato accettato. Clicca qui per vedere il tuo post.", "post-queue-rejected": "Il tuo post in coda è stato rifiutato.", "email-confirmed": "Email Confermata", "email-confirmed-message": "Grazie per aver validato la tua email. Il tuo account è ora completamente attivato.", @@ -68,8 +68,8 @@ "notificationType_group-invite": "Quando ricevi un invito ad un gruppo", "notificationType_group-leave": "Quando un utente lascia il gruppo", "notificationType_group-request-membership": "Quando qualcuno richiede di iscriversi a un gruppo di tua proprietà", - "notificationType_new-register": "Quando qualcuno è in attesa della registrazione", - "notificationType_post-queue": "Quando un nuovo post è in attesa", + "notificationType_new-register": "Quando qualcuno viene aggiunto alla coda di registrazione", + "notificationType_post-queue": "Quando un nuovo post è in coda", "notificationType_new-post-flag": "Quando un post viene segnalato", "notificationType_new-user-flag": "Quando un utente viene segnalato" } \ No newline at end of file diff --git a/public/language/it/pages.json b/public/language/it/pages.json index fe0bac5bc9..a6f5b08e1f 100644 --- a/public/language/it/pages.json +++ b/public/language/it/pages.json @@ -13,7 +13,7 @@ "moderator-tools": "Strumenti di moderazione", "flagged-content": "Contenuti Segnalati", "ip-blacklist": "Blacklist degli IP", - "post-queue": "Post in attesa", + "post-queue": "Coda post", "users/online": "Utenti Online", "users/latest": "Ultimi Utenti", "users/sort-posts": "Utenti maggiori contributori", diff --git a/public/language/it/post-queue.json b/public/language/it/post-queue.json index b052203795..2835fe4607 100644 --- a/public/language/it/post-queue.json +++ b/public/language/it/post-queue.json @@ -1,7 +1,7 @@ { - "post-queue": "Post in attesa", - "description": "Non ci sono post nella coda della post.
Per abilitare questa funzione, vai in Impostazioni → Post → Codice post e abilita Post in attesa.", + "post-queue": "Coda post", + "description": "Non ci sono post nella coda dei post.
Per abilitare questa funzione, vai in Impostazioni → Post → Coda post e abilita Coda post.", "user": "Utente", "category": "Categoria", "title": "Titolo", diff --git a/public/language/it/top.json b/public/language/it/top.json new file mode 100644 index 0000000000..5146312ddf --- /dev/null +++ b/public/language/it/top.json @@ -0,0 +1,4 @@ +{ + "title": "In alto", + "no_top_topics": "Nessuna discussione principale" +} \ No newline at end of file diff --git a/public/language/it/topic.json b/public/language/it/topic.json index 8a4a3dc03f..47a4170cf6 100644 --- a/public/language/it/topic.json +++ b/public/language/it/topic.json @@ -47,6 +47,7 @@ "restored-by": "Ripristinato da", "moved-from-by": "Spostato da %1 da", "queued-by": "Post in coda per l'approvazione →", + "backlink": "Referenziato da", "bookmark_instructions": "Clicca qui per tornare all'ultimo post letto in questa discussione.", "flag-post": "Segnala questo post", "flag-user": "Segnala questo utente", @@ -138,6 +139,7 @@ "composer.handle_placeholder": "Inserisci qui il tuo nome/nome utente ospite", "composer.discard": "Annulla", "composer.submit": "Invia", + "composer.additional-options": "Opzioni aggiuntive", "composer.schedule": "Pianifica", "composer.replying_to": "Rispondendo a %1", "composer.new_topic": "Nuova Discussione", @@ -158,6 +160,7 @@ "newest_to_oldest": "Da Nuovi a Vecchi", "most_votes": "Più Voti", "most_posts": "Più Post", + "most_views": "Più visualizzazioni", "stale.title": "Preferisci creare una nuova discussione?", "stale.warning": "Il topic al quale stai rispondendo è abbastanza vecchio. Vorresti piuttosto creare un nuovo topic in riferimento a questo nella tua risposta?", "stale.create": "Crea una nuova discussione", diff --git a/public/language/it/user.json b/public/language/it/user.json index 43b0c2655c..38592bc8bb 100644 --- a/public/language/it/user.json +++ b/public/language/it/user.json @@ -94,6 +94,7 @@ "digest_off": "Spento", "digest_daily": "Quotidiano", "digest_weekly": "Settimanale", + "digest_biweekly": "Bisettimanale", "digest_monthly": "Mensile", "has_no_follower": "Questo utente non è seguito da nessuno :(", "follows_no_one": "Questo utente non segue nessuno :(", diff --git a/public/language/ja/admin/advanced/events.json b/public/language/ja/admin/advanced/events.json index 8c0dbd27a7..e149a72c5c 100644 --- a/public/language/ja/admin/advanced/events.json +++ b/public/language/ja/admin/advanced/events.json @@ -3,6 +3,7 @@ "no-events": "イベントがありません", "control-panel": "イベントのコントロールパネル", "delete-events": "Delete Events", + "confirm-delete-all-events": "Are you sure you want to delete all logged events?", "filters": "Filters", "filters-apply": "Apply Filters", "filter-type": "Event Type", diff --git a/public/language/ja/admin/dashboard.json b/public/language/ja/admin/dashboard.json index 4e719a00cd..b21cbb4cc2 100644 --- a/public/language/ja/admin/dashboard.json +++ b/public/language/ja/admin/dashboard.json @@ -56,8 +56,8 @@ "active-users.total": "総合", "active-users.connections": "接続", - "anonymous-registered-users": "匿名 vs 登録ユーザー", - "anonymous": "匿名", + "guest-registered-users": "Guest vs Registered Users", + "guest": "Guest", "registered": "登録数", "user-presence": "ユーザープレゼンス", @@ -68,6 +68,7 @@ "unread": "未読", "high-presence-topics": "ハイプレゼンススレッド", + "popular-searches": "Popular Searches", "graphs.page-views": "ページビュー", "graphs.page-views-registered": "ページビュー登録済み", @@ -75,13 +76,14 @@ "graphs.page-views-bot": "ページビューBot", "graphs.unique-visitors": "ユニークな訪問者", "graphs.registered-users": "登録したユーザー", - "graphs.anonymous-users": "匿名ユーザー", + "graphs.guest-users": "Guest Users", "last-restarted-by": "最後に再起動された順", "no-users-browsing": "閲覧中のユーザーなし", "back-to-dashboard": "Back to Dashboard", "details.no-users": "No users have joined within the selected timeframe", "details.no-topics": "No topics have been posted within the selected timeframe", + "details.no-searches": "No searches have been made yet", "details.no-logins": "No logins have been recorded within the selected timeframe", "details.logins-static": "NodeBB only saves session data for %1 days, and so this table below will only show the most recently active sessions", "details.logins-login-time": "Login Time" diff --git a/public/language/ja/admin/development/info.json b/public/language/ja/admin/development/info.json index 8d70eb611c..f70dd00849 100644 --- a/public/language/ja/admin/development/info.json +++ b/public/language/ja/admin/development/info.json @@ -8,7 +8,11 @@ "nodejs": "nodejs", "online": "オンライン", "git": "git", - "memory": "メモリ", + "process-memory": "process memory", + "system-memory": "system memory", + "used-memory-process": "Used memory by process", + "used-memory-os": "Used system memory", + "total-memory-os": "Total system memory", "load": "system load", "cpu-usage": "cpu usage", "uptime": "稼働時間", diff --git a/public/language/ja/admin/extend/rewards.json b/public/language/ja/admin/extend/rewards.json index 3023baeba3..72ef6500b8 100644 --- a/public/language/ja/admin/extend/rewards.json +++ b/public/language/ja/admin/extend/rewards.json @@ -8,8 +8,6 @@ "delete": "削除", "enable": "有効", "disable": "無効", - "control-panel": "報酬コントロール", - "new-reward": "新しい報酬", "alert.delete-success": "報酬を削除しました", "alert.no-inputs-found": "違法報酬 - 入力が見つかりません!", diff --git a/public/language/ja/admin/general/dashboard.json b/public/language/ja/admin/general/dashboard.json deleted file mode 100644 index ab5c98c2bc..0000000000 --- a/public/language/ja/admin/general/dashboard.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "forum-traffic": "フォーラムのトラフィック", - "page-views": "ページビュー", - "unique-visitors": "ユニークな訪問者", - "new-users": "New Users", - "posts": "投稿", - "topics": "スレッド", - "page-views-seven": "過去7日間", - "page-views-thirty": "過去30日間", - "page-views-last-day": "過去24時間", - "page-views-custom": "カスタム期間", - "page-views-custom-start": "期間開始", - "page-views-custom-end": "期間終了", - "page-views-custom-help": "表示したいページビューの日付範囲を入力します。日付選択ツールが使用できない場合、受け入れ可能な形式は次のとおりです。YYYY-MM-DD", - "page-views-custom-error": "有効な期間をフォーマットで入力してくださいYYYY-MM-DD", - - "stats.yesterday": "Yesterday", - "stats.today": "Today", - "stats.last-week": "Last Week", - "stats.this-week": "This Week", - "stats.last-month": "Last Month", - "stats.this-month": "This Month", - "stats.all": "全て", - - "updates": "更新", - "running-version": "NodeBB v%1 を実行しています。", - "keep-updated": "常に最新のセキュリティパッチとバグ修正のためにNodeBBが最新であることを確認してください。", - "up-to-date": "

あなたは最新の状態です。

", - "upgrade-available": "

A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-upgrade-available": "

This is an outdated pre-release version of NodeBB. A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-warning": "

これはNodeBBのプレリリース版です。意図しないバグが発生することがあります。

", - "running-in-development": "フォーラムが開発モードで動作しています。フォーラムの動作が脆弱かもしれませんので、管理者に問い合わせてください。", - "latest-lookup-failed": "

Failed to look up latest available version of NodeBB

", - - "notices": "通知", - "restart-not-required": "再起動は必要ありません", - "restart-required": "再起動が必要です", - "search-plugin-installed": "検索プラグインのインストール", - "search-plugin-not-installed": "検索プラグインがインストールされていません", - "search-plugin-tooltip": "検索機能を有効にするには、プラグインページから検索プラグインをインストールしてください", - - "control-panel": "システムコントロール", - "rebuild-and-restart": "再構築 & 再起動", - "restart": "再起動", - "restart-warning": "NodeBBを再構築または再起動すると、数秒間既存の接続がすべて切断されます。", - "restart-disabled": "適切なデーモンを介してNodeBBを実行しているようには見えないため、NodeBBの再構築および再起動は無効になっています。", - "maintenance-mode": "メンテナンスモード", - "maintenance-mode-title": "NodeBBのメンテナンスモードを設定するには、ここをクリックしてください", - "realtime-chart-updates": "リアルタイムチャートの更新", - - "active-users": "アクティブユーザー", - "active-users.users": "ユーザー", - "active-users.guests": "ゲスト", - "active-users.total": "総合", - "active-users.connections": "接続", - - "anonymous-registered-users": "匿名 vs 登録ユーザー", - "anonymous": "匿名", - "registered": "登録数", - - "user-presence": "ユーザープレゼンス", - "on-categories": "カテゴリ一覧", - "reading-posts": "記事を読む", - "browsing-topics": "スレッドを閲覧", - "recent": "最近", - "unread": "未読", - - "high-presence-topics": "ハイプレゼンススレッド", - - "graphs.page-views": "ページビュー", - "graphs.page-views-registered": "ページビュー登録済み", - "graphs.page-views-guest": "ページビューゲスト", - "graphs.page-views-bot": "ページビューBot", - "graphs.unique-visitors": "ユニークな訪問者", - "graphs.registered-users": "登録したユーザー", - "graphs.anonymous-users": "匿名ユーザー", - "last-restarted-by": "最後に再起動された順", - "no-users-browsing": "閲覧中のユーザーなし" -} diff --git a/public/language/ja/admin/general/homepage.json b/public/language/ja/admin/general/homepage.json deleted file mode 100644 index f033b87c51..0000000000 --- a/public/language/ja/admin/general/homepage.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "home-page": "ホームページ", - "description": "ユーザーがあなたのフォーラムのルートURLに移動するときに表示されるページを選択します。", - "home-page-route": "ホームページのルート", - "custom-route": "カスタムルート", - "allow-user-home-pages": "ユーザーホームページを有効にする", - "home-page-title": "Title of the home page (default \"Home\")" -} \ No newline at end of file diff --git a/public/language/ja/admin/general/languages.json b/public/language/ja/admin/general/languages.json deleted file mode 100644 index 1d2f019640..0000000000 --- a/public/language/ja/admin/general/languages.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "language-settings": "言語設定", - "description": "デフォルトの言語は、フォーラムにアクセスしているすべてのユーザーの言語表示を決定します。
個々のユーザーは、アカウント設定ページでデフォルトの言語を上書きできます。", - "default-language": "デフォルトの言語", - "auto-detect": "ゲストの自動検出言語設定" -} \ No newline at end of file diff --git a/public/language/ja/admin/general/navigation.json b/public/language/ja/admin/general/navigation.json deleted file mode 100644 index f263418193..0000000000 --- a/public/language/ja/admin/general/navigation.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "icon": "アイコン:", - "change-icon": "変更", - "route": "ルート:", - "tooltip": "ツールチップ:", - "text": "テキスト:", - "text-class": " テキストのClass:任意", - "class": "Class: optional", - "id": "ID: 任意", - - "properties": "プロパティ:", - "groups": "Groups:", - "open-new-window": "新しいウィンドウで開く", - - "btn.delete": "削除", - "btn.disable": "無効", - "btn.enable": "有効", - - "available-menu-items": "利用可能なメニューアイテム", - "custom-route": "カスタムルート", - "core": "コア", - "plugin": "プラグイン" -} \ No newline at end of file diff --git a/public/language/ja/admin/general/social.json b/public/language/ja/admin/general/social.json deleted file mode 100644 index 211d840d69..0000000000 --- a/public/language/ja/admin/general/social.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "post-sharing": "投稿共有", - "info-plugins-additional": "プラグインは投稿を共有するために追加のネットワークを設定することができます", - "save-success": "投稿共有ネットワークを正常に保存しました!" -} \ No newline at end of file diff --git a/public/language/ja/admin/general/sounds.json b/public/language/ja/admin/general/sounds.json deleted file mode 100644 index b03597c4de..0000000000 --- a/public/language/ja/admin/general/sounds.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "notifications": "通知", - "chat-messages": "チャットメッセージ", - "play-sound": "再生", - "incoming-message": "受信メッセージ", - "outgoing-message": "送信メッセージ", - "upload-new-sound": "新しい音声のアップロード", - "saved": "設定を保存しました" -} \ No newline at end of file diff --git a/public/language/ja/admin/manage/digest.json b/public/language/ja/admin/manage/digest.json index 8f3661698a..38c634d1f6 100644 --- a/public/language/ja/admin/manage/digest.json +++ b/public/language/ja/admin/manage/digest.json @@ -13,6 +13,7 @@ "resent-single": "Manual digest resend completed", "resent-day": "Daily digest resent", "resent-week": "Weekly digest resent", + "resent-biweek": "Bi-Weekly digest resent", "resent-month": "Monthly digest resent", "null": "Never", "manual-run": "Manual digest run:", diff --git a/public/language/ja/admin/manage/users.json b/public/language/ja/admin/manage/users.json index caf2a3aac8..9d9c693e1c 100644 --- a/public/language/ja/admin/manage/users.json +++ b/public/language/ja/admin/manage/users.json @@ -47,6 +47,7 @@ "users.uid": "ユーザーID", "users.username": "ユーザー名", "users.email": "メール", + "users.no-email": "(no email)", "users.ip": "IP", "users.postcount": "投稿カウント", "users.reputation": "評価", diff --git a/public/language/ja/admin/menu.json b/public/language/ja/admin/menu.json index e7228820fb..35eec8d76c 100644 --- a/public/language/ja/admin/menu.json +++ b/public/language/ja/admin/menu.json @@ -4,6 +4,7 @@ "dashboard/logins": "Logins", "dashboard/users": "Users", "dashboard/topics": "Topics", + "dashboard/searches": "Searches", "section-general": "一般", "section-manage": "管理", diff --git a/public/language/ja/admin/settings/email.json b/public/language/ja/admin/settings/email.json index ee843db6c5..a3bf8a8616 100644 --- a/public/language/ja/admin/settings/email.json +++ b/public/language/ja/admin/settings/email.json @@ -6,7 +6,7 @@ "from-help": "メールからの名前が表示されます。", "smtp-transport": "SMTP Transport", - "smtp-transport.enabled": "Use an external email server to send emails", + "smtp-transport.enabled": "Enable SMTP Transport", "smtp-transport-help": "You can select from a list of well-known services or enter a custom one.", "smtp-transport.service": "Select a service", "smtp-transport.service-custom": "Custom Service", @@ -37,6 +37,10 @@ "subscriptions.hour": "ダイジェストアワー", "subscriptions.hour-help": "スケジュールされたメールのダイジェストを送信する時間を表す数字を入力してください(深夜は0、午後5:00は17)これはサーバー自体に基づく時間であり、システムの時計と正確に一致しない場合があります。
次の日のダイジェストは", "notifications.remove-images": "Remove images from email notifications", + "require-email-address": "Require new users to specify an email address", + "require-email-address-warning": "By default, users can opt-out of entering an email address. Enabling this option means they have to enter an email address in order to proceed with registration. It does not ensure user will enter a real email address, nor even an address they own.", "include-unverified-emails": "Send emails to recipients who have not explicitly confirmed their emails", - "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws." -} \ No newline at end of file + "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws.", + "prompt": "Prompt users to enter or confirm their emails", + "prompt-help": "If a user does not have an email set, or their email is not confirmed, a warning will be shown on screen." +} diff --git a/public/language/ja/admin/settings/general.json b/public/language/ja/admin/settings/general.json index 23a3d20057..d37f057bc1 100644 --- a/public/language/ja/admin/settings/general.json +++ b/public/language/ja/admin/settings/general.json @@ -3,9 +3,9 @@ "title": "サイトタイトル", "title.short": "Short Title", "title.short-placeholder": "If no short title is specified, the site title will be used", - "title.url": "URL", + "title.url": "Title Link URL", "title.url-placeholder": "サイトタイトルのURL", - "title.url-help": "タイトルをクリックすると、ユーザーをこのアドレスに送信します。空白のままにすると、ユーザーはフォーラムのインデックスに送信されます。", + "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "title.name": "あなたのコミュニティ名", "title.show-in-header": "ヘッダーにサイトタイトルを表示する", "browser-title": "ブラウザ", @@ -20,9 +20,9 @@ "logo.image": "画像", "logo.image-placeholder": "フォーラムのヘッダーに表示するロゴのパス", "logo.upload": "アップロード", - "logo.url": "URL", + "logo.url": "Logo Link URL", "logo.url-placeholder": "サイトロゴのURL", - "logo.url-help": "ロゴをクリックすると、ユーザーをこのアドレスに送信します。空白のままにすると、ユーザーはフォーラムのインデックスに送信されます。", + "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "logo.alt-text": "全てのテキスト:", "log.alt-text-placeholder": "アクセシビリティのための代替テキスト", "favicon": "お気に入りアイコン", @@ -47,4 +47,4 @@ "undo-timeout": "Undo Timeout", "undo-timeout-help": "Some operations such as moving topics will allow for the moderator to undo their action within a certain timeframe. Set to 0 to disable undo completely.", "topic-tools": "Topic Tools" -} \ No newline at end of file +} diff --git a/public/language/ja/admin/settings/navigation.json b/public/language/ja/admin/settings/navigation.json index f263418193..f3d7c35e87 100644 --- a/public/language/ja/admin/settings/navigation.json +++ b/public/language/ja/admin/settings/navigation.json @@ -11,6 +11,8 @@ "properties": "プロパティ:", "groups": "Groups:", "open-new-window": "新しいウィンドウで開く", + "dropdown": "Dropdown", + "dropdown-placeholder": "Place your dropdown menu items below, ie:
<li><a href="https://myforum.com">Link 1</a></li>", "btn.delete": "削除", "btn.disable": "無効", @@ -20,4 +22,4 @@ "custom-route": "カスタムルート", "core": "コア", "plugin": "プラグイン" -} \ No newline at end of file +} diff --git a/public/language/ja/admin/settings/notifications.json b/public/language/ja/admin/settings/notifications.json index d69a16d629..9be8707a2b 100644 --- a/public/language/ja/admin/settings/notifications.json +++ b/public/language/ja/admin/settings/notifications.json @@ -2,5 +2,6 @@ "notifications": "通知", "welcome-notification": "ウェルカム通知", "welcome-notification-link": "ウェルカム通知のリンク", - "welcome-notification-uid": "Welcome Notification User (UID)" + "welcome-notification-uid": "Welcome Notification User (UID)", + "post-queue-notification-uid": "Post Queue User (UID)" } \ No newline at end of file diff --git a/public/language/ja/admin/settings/post.json b/public/language/ja/admin/settings/post.json index 09cfa068b9..8c0502c8d2 100644 --- a/public/language/ja/admin/settings/post.json +++ b/public/language/ja/admin/settings/post.json @@ -40,6 +40,7 @@ "teaser.last-post": "最後&ndash;返信がない場合は、元の投稿を含む最新の投稿を表示", "teaser.last-reply": "最後&ndash;最新の返信を表示するか、返信がない場合は「返信なし」のプレースホルダを表示する", "teaser.first": "最初", + "showPostPreviewsOnHover": "Show a preview of posts when mouse overed", "unread": "未読の設定", "unread.cutoff": "未読のカットオフ日", "unread.min-track-last": "最後に読み込みを行う前に追跡するスレッドの最小投稿数", @@ -56,6 +57,9 @@ "composer.show-help": "「ヘルプ」タグを表示", "composer.enable-plugin-help": "プラグインがヘルプタブにコンテンツを追加できるようにする", "composer.custom-help": "カスタムヘルプテキスト", + "backlinks": "Backlinks", + "backlinks.enabled": "Enable topic backlinks", + "backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.", "ip-tracking": "IPトラッキング", "ip-tracking.each-post": "各投稿のトラックIPアドレス", "enable-post-history": "Enable Post History" diff --git a/public/language/ja/admin/settings/user.json b/public/language/ja/admin/settings/user.json index e79e65efe7..41762e6abf 100644 --- a/public/language/ja/admin/settings/user.json +++ b/public/language/ja/admin/settings/user.json @@ -71,6 +71,7 @@ "digest-freq.off": "オフ", "digest-freq.daily": "デイリー", "digest-freq.weekly": "ウィークリー", + "digest-freq.biweekly": "Bi-Weekly", "digest-freq.monthly": "マンスリー", "email-chat-notifs": "オンラインではない時に新しいチャットメッセージを受信した場合、通知メールを送信する。", "email-post-notif": "購読中のスレッドに返信があった場合、メールで通知する。", diff --git a/public/language/ja/error.json b/public/language/ja/error.json index a59597180b..bf4213818d 100644 --- a/public/language/ja/error.json +++ b/public/language/ja/error.json @@ -34,8 +34,9 @@ "email-invited": "Email was already invited", "email-not-confirmed": "Posting in some categories or topics is enabled once your email is confirmed, please click here to send a confirmation email.", "email-not-confirmed-chat": "チャットを行うにはメールアドレスの確認を行う必要があります。メールアドレスを確認するためにはここをクリックしてください。", - "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You won't be able to post or chat until your email is confirmed.", - "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery. Please click here to enter an email.", + "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You may not be able to post in some categories or chat until your email is confirmed.", + "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery, and may be necessary for chatting and posting in some categories. Please click here to enter an email.", + "user-doesnt-have-email": "User \"%1\" does not have an email set.", "email-confirm-failed": "メールアドレスの確認が出来ませんでした。再度お試しください。", "confirm-email-already-sent": "確認のメールは既に送信されています。再度送信するには、%1分後に再度お試しください。", "sendmail-not-found": "Sendmailの実行ファイルが見つかりませんでした。インストールされ、ユーザーによってNodeBBが実行されていることを確認してください。", @@ -103,6 +104,7 @@ "already-bookmarked": "あなたは、この投稿をすでにブックマークしています", "already-unbookmarked": "あなたは、この投稿をすでにブックマークから外しています", "cant-ban-other-admins": "ほかの管理者を停止することはできません!", + "cant-make-banned-users-admin": "You can't make banned users admin.", "cant-remove-last-admin": "あなたが唯一の管理者です。管理者としてあなた自身を削除する前に、管理者として別のユーザーを追加します。", "account-deletion-disabled": "Account deletion is disabled", "cant-delete-admin": "削除する前に、このアカウントから管理者権限を削除してください。", diff --git a/public/language/ja/modules.json b/public/language/ja/modules.json index cdfd699752..792e8a33a7 100644 --- a/public/language/ja/modules.json +++ b/public/language/ja/modules.json @@ -54,7 +54,7 @@ "composer.formatting.strikethrough": "取り消し線", "composer.formatting.code": "コード", "composer.formatting.link": "リンク", - "composer.formatting.picture": "画像", + "composer.formatting.picture": "Image Link", "composer.upload-picture": "画像をアップロード", "composer.upload-file": "ファイルをアップロード", "composer.zen_mode": "Zen モード", diff --git a/public/language/ja/top.json b/public/language/ja/top.json new file mode 100644 index 0000000000..b8a05bfa5f --- /dev/null +++ b/public/language/ja/top.json @@ -0,0 +1,4 @@ +{ + "title": "Top", + "no_top_topics": "No top topics" +} \ No newline at end of file diff --git a/public/language/ja/topic.json b/public/language/ja/topic.json index d68eebd84c..d71074fddc 100644 --- a/public/language/ja/topic.json +++ b/public/language/ja/topic.json @@ -47,6 +47,7 @@ "restored-by": "Restored by", "moved-from-by": "Moved from %1 by", "queued-by": "Post queued for approval →", + "backlink": "Referenced by", "bookmark_instructions": "ここをクリックすると、このスレッドの最後に読んでいた投稿へ移動します。", "flag-post": "Flag this post", "flag-user": "Flag this user", @@ -138,6 +139,7 @@ "composer.handle_placeholder": "Enter your name/handle here", "composer.discard": "破棄する", "composer.submit": "保存する", + "composer.additional-options": "Additional Options", "composer.schedule": "Schedule", "composer.replying_to": "%1へ返答中", "composer.new_topic": "新規スレッド", @@ -158,6 +160,7 @@ "newest_to_oldest": "新しいものから古い順", "most_votes": "最高評価", "most_posts": "最大投稿", + "most_views": "Most Views", "stale.title": "新しいスレッドを作りますか?", "stale.warning": "あなたが返信しようとしてるスレッドが古いスレッドです。新しいスレッドを作って、そしてこのスレッドが参考として入れた方を勧めます。そうしますか?", "stale.create": "新しいスレッドを作ります。", diff --git a/public/language/ja/user.json b/public/language/ja/user.json index bb65236c7c..0880b21114 100644 --- a/public/language/ja/user.json +++ b/public/language/ja/user.json @@ -94,6 +94,7 @@ "digest_off": "オフ", "digest_daily": "デイリー", "digest_weekly": "ウィークリー", + "digest_biweekly": "Bi-Weekly", "digest_monthly": "マンスリー", "has_no_follower": "フォロワーはまだいません :(", "follows_no_one": "フォロー中のユーザーはまだいません :(", diff --git a/public/language/ko/admin/advanced/events.json b/public/language/ko/admin/advanced/events.json index 4db7030eeb..3f96674ed5 100644 --- a/public/language/ko/admin/advanced/events.json +++ b/public/language/ko/admin/advanced/events.json @@ -3,6 +3,7 @@ "no-events": "이벤트가 없습니다", "control-panel": "이벤트 제어판", "delete-events": "이벤트 삭제", + "confirm-delete-all-events": "Are you sure you want to delete all logged events?", "filters": "필터", "filters-apply": "필터 적용", "filter-type": "이벤트 유형", diff --git a/public/language/ko/admin/dashboard.json b/public/language/ko/admin/dashboard.json index 64f29de725..aee85929ae 100644 --- a/public/language/ko/admin/dashboard.json +++ b/public/language/ko/admin/dashboard.json @@ -56,8 +56,8 @@ "active-users.total": "총", "active-users.connections": "연결", - "anonymous-registered-users": "익명 vs 가입한 사용자", - "anonymous": "익명", + "guest-registered-users": "Guest vs Registered Users", + "guest": "Guest", "registered": "가입한 사용자", "user-presence": "사용자 활동", @@ -68,6 +68,7 @@ "unread": "읽지 않음", "high-presence-topics": "활동량이 많은 화제", + "popular-searches": "Popular Searches", "graphs.page-views": "페이지 뷰", "graphs.page-views-registered": "가입한 사용자의 페이지 뷰", @@ -75,13 +76,14 @@ "graphs.page-views-bot": "봇의 페이지 뷰", "graphs.unique-visitors": "고유 방문자", "graphs.registered-users": "등록된 사용자", - "graphs.anonymous-users": "익명 사용자", + "graphs.guest-users": "Guest Users", "last-restarted-by": "최근 재시작 시점", "no-users-browsing": "보고있는 사용자 없음", "back-to-dashboard": "대시보드로 돌아가기", "details.no-users": "설정한 기간에 가입한 사용자 없음", "details.no-topics": "설정한 기간에 생성된 화제 없음", + "details.no-searches": "No searches have been made yet", "details.no-logins": "설정한 기간에 로그인 기록 없음", "details.logins-static": "NodeBB는 세션 정보를 %1일 동안만 저장합니다. 따라서 아래의 표는 최근 활성화된 세션 정보만을 표시합니다.", "details.logins-login-time": "로그인 시점" diff --git a/public/language/ko/admin/development/info.json b/public/language/ko/admin/development/info.json index 6b921c1d08..640110f2d0 100644 --- a/public/language/ko/admin/development/info.json +++ b/public/language/ko/admin/development/info.json @@ -8,7 +8,11 @@ "nodejs": "nodejs", "online": "온라인", "git": "git", - "memory": "메모리", + "process-memory": "process memory", + "system-memory": "system memory", + "used-memory-process": "Used memory by process", + "used-memory-os": "Used system memory", + "total-memory-os": "Total system memory", "load": "시스템 로드", "cpu-usage": "cpu 사용량", "uptime": "업타임", diff --git a/public/language/ko/admin/extend/rewards.json b/public/language/ko/admin/extend/rewards.json index ac23d6e380..be8bd8cf85 100644 --- a/public/language/ko/admin/extend/rewards.json +++ b/public/language/ko/admin/extend/rewards.json @@ -8,8 +8,6 @@ "delete": "삭제", "enable": "활성화", "disable": "비활성화", - "control-panel": "보상 제어판", - "new-reward": "새로운 보상", "alert.delete-success": "성공적으로 보상을 삭제했습니다.", "alert.no-inputs-found": "잘못된 보상 - 입력값이 없습니다!", diff --git a/public/language/ko/admin/general/dashboard.json b/public/language/ko/admin/general/dashboard.json deleted file mode 100644 index 7462d56bfa..0000000000 --- a/public/language/ko/admin/general/dashboard.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "forum-traffic": "포럼 트래픽", - "page-views": "페이지 뷰", - "unique-visitors": "순 방문자수", - "new-users": "New Users", - "posts": "포스트", - "topics": "게시물", - "page-views-seven": "지난 7일간", - "page-views-thirty": "지난 30일간", - "page-views-last-day": "지난 24시간 동안", - "page-views-custom": "사용자 설정 날짜 기간", - "page-views-custom-start": "기간 시작", - "page-views-custom-end": "기간 끝", - "page-views-custom-help": "페이지 뷰를 확인하고 싶은 기간을 입력하세요. 만약 데이트 피커를 사용할 수 없다면, YYYY-MM-DD 포맷으로 입력해주세요.", - "page-views-custom-error": "유효한 기간을 다음과 같은 포맷으로 입력하세요 YYYY-MM-DD", - - "stats.yesterday": "Yesterday", - "stats.today": "Today", - "stats.last-week": "Last Week", - "stats.this-week": "This Week", - "stats.last-month": "Last Month", - "stats.this-month": "This Month", - "stats.all": "항상", - - "updates": "업데이트", - "running-version": "NodeBB v%1 를 사용 중입니다.", - "keep-updated": "사용하시는 NodeBB의 보안 및 오류 업데이트를 항상 최신 버젼으로 유지하십시오.", - "up-to-date": "

최신 버전입니다

", - "upgrade-available": "

새로운 버전 (v%1) 이 출시 되었습니다. 사용하시는 NodeBB의 업데이트를 고려해보세요.

", - "prerelease-upgrade-available": "

사용하는 NodeBB 시험판이 오래되었습니다. 새로운 버전 (v%1) 이 출시 되었습니다. 사용하시는 NodeBB의 업데이트를 고려해보세요.

", - "prerelease-warning": "

이것은 정식 발표 전 버젼의 NodeBB 입니다. 예상치 못한 버그가 발생할 수 있습니다.

", - "running-in-development": "포럼이 개발자 모드로 실행되고 있습니다. 잠재적 취약점에 노출되어 있을 수 있으니 시스템 관리자에게 문의하십시오.", - "latest-lookup-failed": "

NodeBB의 최신 버전을 확인하는데 실패했습니다

", - - "notices": "알림", - "restart-not-required": "재시작 필요 없음", - "restart-required": "재시작 필요", - "search-plugin-installed": "설치된 플러그인 검색", - "search-plugin-not-installed": "설치되지 않은 플러그인 검색", - "search-plugin-tooltip": "검색 기능을 활성화하시려면 플러그인 페이지에서 검색 플러그인을 설치하세요.", - - "control-panel": "시스템 제어", - "rebuild-and-restart": "리빌드 & 재시작", - "restart": "재시작", - "restart-warning": "NodeBB가 리빌드 또는 재시작을 하고 있습니다. 수초 내에 연결된 모든 접속을 끊습니다.", - "restart-disabled": "정상적인 데몬으로 판단할 수 없어 리빌드와 재시작을 할 수 없습니다.", - "maintenance-mode": "점검 모드", - "maintenance-mode-title": "NodeBB 점검 모드를 설정하시려면 이곳을 클릭하세요.", - "realtime-chart-updates": "실시간 차트 업데이트", - - "active-users": "활동 중인 사용자", - "active-users.users": "사용자", - "active-users.guests": "게스트", - "active-users.total": "총", - "active-users.connections": "연결", - - "anonymous-registered-users": "익명 vs 가입한 사용자", - "anonymous": "익명", - "registered": "가입한", - - "user-presence": "사용자 활동", - "on-categories": "카테고리 목록 보는 중", - "reading-posts": "게시물 읽기", - "browsing-topics": "토픽 보기", - "recent": "최근", - "unread": "읽지 않은", - - "high-presence-topics": "활동량이 많은 토픽", - - "graphs.page-views": "페이지 뷰", - "graphs.page-views-registered": "가입한 사용자의 페이지 조회", - "graphs.page-views-guest": "손님의 페이지 조회", - "graphs.page-views-bot": "봇의 페이지 조회", - "graphs.unique-visitors": "고유 방문자", - "graphs.registered-users": "등록된 사용자", - "graphs.anonymous-users": "익명의 사용자", - "last-restarted-by": "마지막으로 재시작", - "no-users-browsing": "보고있는 사용자가 없습니다" -} diff --git a/public/language/ko/admin/general/homepage.json b/public/language/ko/admin/general/homepage.json deleted file mode 100644 index 288d1a1f89..0000000000 --- a/public/language/ko/admin/general/homepage.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "home-page": "홈페이지", - "description": "사용자가 루트 URL에 들어갔을 때 어떤 페이지가 보일지 선택하세요.", - "home-page-route": "홈페이지 경로", - "custom-route": "사용자 지정 경로", - "allow-user-home-pages": "사용자가 직접 홈페이지를 설정할 수 있게 허용", - "home-page-title": "홈페이지의 타이틀 (기본값 \"Home\")" -} \ No newline at end of file diff --git a/public/language/ko/admin/general/languages.json b/public/language/ko/admin/general/languages.json deleted file mode 100644 index 2464b7b75a..0000000000 --- a/public/language/ko/admin/general/languages.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "language-settings": "언어 설정", - "description": "기본 언어 설정은 사이트를 방문하는 모든 사용자들에게 적용됩니다.
하지만 사용자들이 직접 본인의 계정 설정 페이지에서 언어 설정을 바꿀 수 있습니다.", - "default-language": "기본 언어", - "auto-detect": "사용자의 언어 설정 자동으로 감지합니다." -} \ No newline at end of file diff --git a/public/language/ko/admin/general/navigation.json b/public/language/ko/admin/general/navigation.json deleted file mode 100644 index 9adb375c75..0000000000 --- a/public/language/ko/admin/general/navigation.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "icon": "아이콘:", - "change-icon": "변경", - "route": "경로:", - "tooltip": "툴팁:", - "text": "텍스트:", - "text-class": "텍스트 클래스: 선택 사항", - "class": "Class: optional", - "id": "ID: 선택 사항", - - "properties": "속성:", - "groups": "그룹:", - "open-new-window": "새 창에서 열기", - - "btn.delete": "삭제", - "btn.disable": "비활성화", - "btn.enable": "활성화", - - "available-menu-items": "이용 가능한 메뉴 항목", - "custom-route": "사용자 지정 경로", - "core": "핵심", - "plugin": "플러그인" -} \ No newline at end of file diff --git a/public/language/ko/admin/general/social.json b/public/language/ko/admin/general/social.json deleted file mode 100644 index 35c38ef758..0000000000 --- a/public/language/ko/admin/general/social.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "post-sharing": "포스트 공유", - "info-plugins-additional": "플러그인을 이용해서 포스트를 공유할 수 있는 네트워크를 추가할 수 있습니다.", - "save-success": "포스트 공유 네트워크 추가 완료!" -} \ No newline at end of file diff --git a/public/language/ko/admin/general/sounds.json b/public/language/ko/admin/general/sounds.json deleted file mode 100644 index 70cbb40922..0000000000 --- a/public/language/ko/admin/general/sounds.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "notifications": "알림", - "chat-messages": "채팅 메세지", - "play-sound": "재생", - "incoming-message": "수신 메시지", - "outgoing-message": "발신 메시지", - "upload-new-sound": "새로운 사운드 업로드", - "saved": "설정 저장됨" -} \ No newline at end of file diff --git a/public/language/ko/admin/manage/digest.json b/public/language/ko/admin/manage/digest.json index ace601e819..db9f7dc963 100644 --- a/public/language/ko/admin/manage/digest.json +++ b/public/language/ko/admin/manage/digest.json @@ -13,6 +13,7 @@ "resent-single": "포럼 메일 수동 전송 완료", "resent-day": "일간 포럼 메일 재전송 완료", "resent-week": "주간 포럼 메일 재전송 완료", + "resent-biweek": "Bi-Weekly digest resent", "resent-month": "월간 포럼 메일 재전송 완료", "null": "기록 없음", "manual-run": "포럼 메일 수동 전송", diff --git a/public/language/ko/admin/manage/privileges.json b/public/language/ko/admin/manage/privileges.json index 2e16cda8e3..d01ba7c79e 100644 --- a/public/language/ko/admin/manage/privileges.json +++ b/public/language/ko/admin/manage/privileges.json @@ -51,13 +51,13 @@ "alert.saved": "변경된 권한이 적용되었습니다.", "alert.confirm-discard": "권한 변경을 취소하시겠습니까?", "alert.discarded": "권한 변경이 취소되었습니다.", - "alert.confirm-copyToAll": "Are you sure you wish to apply this set of %1 to all categories?", - "alert.confirm-copyToAllGroup": "Are you sure you wish to apply this group's set of %1 to all categories?", - "alert.confirm-copyToChildren": "Are you sure you wish to apply this set of %1 to all descendant (child) categories?", - "alert.confirm-copyToChildrenGroup": "Are you sure you wish to apply this group's set of %1 to all descendant (child) categories?", + "alert.confirm-copyToAll": "%1의 설정을 모든 카테고리에 적용하시겠습니까?", + "alert.confirm-copyToAllGroup": "%1 그룹의 설정을 모든 카테고리에 적용하시겠습니까?", + "alert.confirm-copyToChildren": "%1의 설정을 모든 하위 카테고리에 적용하시겠습니까?", + "alert.confirm-copyToChildrenGroup": "%1 그룹의 설정을 모든 하위 카테고리에 적용하시겠습니까?", "alert.no-undo": "이 행동은 되돌릴 수 없습니다.", "alert.admin-warning": "관리자에게는 절대적인 권한이 부여됩니다.", - "alert.copyPrivilegesFrom-title": "Select a category to copy from", - "alert.copyPrivilegesFrom-warning": "This will copy %1 from the selected category.", - "alert.copyPrivilegesFromGroup-warning": "This will copy this group's set of %1 from the selected category." + "alert.copyPrivilegesFrom-title": "복사할 카테고리 설정", + "alert.copyPrivilegesFrom-warning": "이 작업은 %1의 설정을 선택한 카테고리에서 복사합니다.", + "alert.copyPrivilegesFromGroup-warning": "이 작업은 %1 그룹의 설정을 선택한 카테고리에서 복사합니다." } \ No newline at end of file diff --git a/public/language/ko/admin/manage/uploads.json b/public/language/ko/admin/manage/uploads.json index e321e7f567..6a5fcf9282 100644 --- a/public/language/ko/admin/manage/uploads.json +++ b/public/language/ko/admin/manage/uploads.json @@ -6,6 +6,6 @@ "size/filecount": "크기 / 파일 수", "confirm-delete": "이 파일을 정말로 삭제하시겠습니까?", "filecount": "%1 파일", - "new-folder": "New Folder", - "name-new-folder": "Enter a name for new the folder" + "new-folder": "새로운 폴더", + "name-new-folder": "폴더의 이름을 입력해주세요." } \ No newline at end of file diff --git a/public/language/ko/admin/manage/users.json b/public/language/ko/admin/manage/users.json index 656d4f928d..948440a063 100644 --- a/public/language/ko/admin/manage/users.json +++ b/public/language/ko/admin/manage/users.json @@ -1,6 +1,6 @@ { "users": "사용자", - "edit": "Actions", + "edit": "작업", "make-admin": "관리자 등록", "remove-admin": "관리자 해제", "validate-email": "이메일 인증", @@ -47,6 +47,7 @@ "users.uid": "uid", "users.username": "사용자명", "users.email": "이메일", + "users.no-email": "(no email)", "users.ip": "IP", "users.postcount": "글 개수", "users.reputation": "인지도", diff --git a/public/language/ko/admin/menu.json b/public/language/ko/admin/menu.json index 3999230bcc..b28b897736 100644 --- a/public/language/ko/admin/menu.json +++ b/public/language/ko/admin/menu.json @@ -4,6 +4,7 @@ "dashboard/logins": "로그인 기록", "dashboard/users": "사용자", "dashboard/topics": "화제", + "dashboard/searches": "Searches", "section-general": "일반", "section-manage": "관리", diff --git a/public/language/ko/admin/settings/email.json b/public/language/ko/admin/settings/email.json index 1b45f1e43b..2b6b3587dd 100644 --- a/public/language/ko/admin/settings/email.json +++ b/public/language/ko/admin/settings/email.json @@ -6,7 +6,7 @@ "from-help": "이메일에 표시할 발신자 이름", "smtp-transport": "SMTP Transport", - "smtp-transport.enabled": "외부 이메일 서버를 통해 메일 발송", + "smtp-transport.enabled": "Enable SMTP Transport", "smtp-transport-help": "자주 사용되는 서비스 목록 중에 하나를 선택하거나 직접 입력할 수 있습니다.", "smtp-transport.service": "서비스 선택", "smtp-transport.service-custom": "직접 입력", @@ -37,6 +37,10 @@ "subscriptions.hour": "포럼 메일 발송 시간", "subscriptions.hour-help": "정기 포럼 메일을 보낼 시간을 입력해주세요. (예: 0은 자정, 17은 오후 5시 입니다. 이 시간은 서버 시간 기준이며, 사용자의 시스템 시간과 일치하지 않을 수 있습니다.
서버 시간은 입니다.
다음 정기 포럼 메일은 에 발송 예정입니다.", "notifications.remove-images": "이메일 알림에서 이미지 제거", + "require-email-address": "신규 사용자에게 이메일 주소 설정 요구", + "require-email-address-warning": "기본적으로 사용자들은 이메일 주소 입력을 거부할 수 있습니다. 이 설정을 활성화하면 사용자 등록 시 이메일 주소를 입력해야 진행할 수 있습니다. 하지만 해당 이메일 주소가 유효한 주소인지는 확인하지 않습니다.", "include-unverified-emails": "전자 메일을 명시적으로 확인하지 않은 수신자에게 전자 메일 보내기", - "include-unverified-warning": "기본적으로 계정과 연결된 전자 메일이 있는 사용자는 이미 확인되었지만 그렇지 않은 경우가 있습니다(예: SSO 로그인, 약관으로부터 제외된 사용자 등). 사용자가 위험을 감수하고 이 설정을 사용하도록 설정합니다. – 확인되지 않은 주소로 이메일을 보내는 것은 지역별 스팸 방지법을 위반하는 것일 수 있습니다." -} \ No newline at end of file + "include-unverified-warning": "기본적으로 계정과 연결된 전자 메일이 있는 사용자는 이미 확인되었지만 그렇지 않은 경우가 있습니다(예: SSO 로그인, 약관으로부터 제외된 사용자 등). 사용자가 위험을 감수하고 이 설정을 사용하도록 설정합니다. – 확인되지 않은 주소로 이메일을 보내는 것은 지역별 스팸 방지법을 위반하는 것일 수 있습니다.", + "prompt": "Prompt users to enter or confirm their emails", + "prompt-help": "If a user does not have an email set, or their email is not confirmed, a warning will be shown on screen." +} diff --git a/public/language/ko/admin/settings/general.json b/public/language/ko/admin/settings/general.json index 8771eb6b3f..c20abb4195 100644 --- a/public/language/ko/admin/settings/general.json +++ b/public/language/ko/admin/settings/general.json @@ -3,9 +3,9 @@ "title": "사이트 이름", "title.short": "짧은 이름", "title.short-placeholder": "짧은 제목이 설정되지 않으면 일반 사이트 이름을 로고처럼 사용합니다.", - "title.url": "URL", + "title.url": "Title Link URL", "title.url-placeholder": "사이트 이름을 눌렀을 때 이동할 URL", - "title.url-help": "사이트 상단의 사이트 이름을 클릭하면 사용자가 이 주소로 이동합니다. 비워두면 사용자가 포럼 인덱스로 이동합니다.", + "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "title.name": "커뮤니티 이름", "title.show-in-header": "상단바에 사이트 이름 표시", "browser-title": "브라우저 타이틀", @@ -20,9 +20,9 @@ "logo.image": "사진", "logo.image-placeholder": "로고 파일 저장 위치", "logo.upload": "업로드", - "logo.url": "URL", + "logo.url": "Logo Link URL", "logo.url-placeholder": "사이트 로고 URL", - "logo.url-help": "사용자가 로고를 클릭했을 때 연결할 URL 주소. 빈칸으로 남겨두면 사이트 홈페이지로 연결됩니다.", + "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "logo.alt-text": "대체 텍스트", "log.alt-text-placeholder": "대체 텍스트", "favicon": "파비콘", @@ -35,10 +35,10 @@ "maskable-icon.help": "권장 사항: 512x512, PNG 확장자만 가능, 지정되지 않을 경우 터치 아이콘 사용", "outgoing-links": "외부 링크", "outgoing-links.warning-page": "외부 링크 경고페이지 사용", - "search": "Search", - "search-default-in": "Search In", - "search-default-in-quick": "Quick Search In", - "search-default-sort-by": "Sort by", + "search": "검색", + "search-default-in": "범위 검색", + "search-default-in-quick": "빠른 범위 검색", + "search-default-sort-by": "분류", "outgoing-links.whitelist": "경고 창이 필요 없는 외부 링크 도메인 whitelist", "site-colors": "사이트 색상 설정", "theme-color": "테마 색상", @@ -47,4 +47,4 @@ "undo-timeout": "되돌리기 시간 초과", "undo-timeout-help": "조정자는 주제 이동과 같은 일부 작업을 통해 특정 기간 내에 작업을 취소할 수 있습니다. 되돌리기를 완전히 비활성화하려면 0으로 설정합니다.", "topic-tools": "주제 도구" -} \ No newline at end of file +} diff --git a/public/language/ko/admin/settings/navigation.json b/public/language/ko/admin/settings/navigation.json index c3c613d0f5..6c6f584c78 100644 --- a/public/language/ko/admin/settings/navigation.json +++ b/public/language/ko/admin/settings/navigation.json @@ -11,6 +11,8 @@ "properties": "속성:", "groups": "그룹:", "open-new-window": "새 창에서 열기", + "dropdown": "Dropdown", + "dropdown-placeholder": "Place your dropdown menu items below, ie:
<li><a href="https://myforum.com">Link 1</a></li>", "btn.delete": "삭제", "btn.disable": "비활성화", @@ -20,4 +22,4 @@ "custom-route": "사용자 정의 경로", "core": "코어", "plugin": "플러그인" -} \ No newline at end of file +} diff --git a/public/language/ko/admin/settings/notifications.json b/public/language/ko/admin/settings/notifications.json index 1973f2e758..c9a3ebb946 100644 --- a/public/language/ko/admin/settings/notifications.json +++ b/public/language/ko/admin/settings/notifications.json @@ -2,5 +2,6 @@ "notifications": "알림", "welcome-notification": "환영 알림", "welcome-notification-link": "환영 알림 링크", - "welcome-notification-uid": "환영 알림 사용자 (UID)" + "welcome-notification-uid": "환영 알림 사용자 (UID)", + "post-queue-notification-uid": "게시 대기 중인 사용자 (UID)" } \ No newline at end of file diff --git a/public/language/ko/admin/settings/post.json b/public/language/ko/admin/settings/post.json index 3ca69a289f..a2ea9e3e67 100644 --- a/public/language/ko/admin/settings/post.json +++ b/public/language/ko/admin/settings/post.json @@ -40,6 +40,7 @@ "teaser.last-post": "최근 - 최근 작성된 포스트를 보여주고 답글이 없을 경우 포스트 본문 보여주기", "teaser.last-reply": "최근 - 최근 작성된 답글을 보여주고 답글이 없을 경우 \"답글 없음\" 표시", "teaser.first": "첫 글", + "showPostPreviewsOnHover": "Show a preview of posts when mouse overed", "unread": "읽지 않음 목록 설정", "unread.cutoff": "읽지 않음 표시 기간", "unread.min-track-last": "마지막으로 읽은 글 추적 기능을 사용할 최소 글 수", @@ -56,6 +57,9 @@ "composer.show-help": "\"도움말\" 탭 표시", "composer.enable-plugin-help": "플러그인의 도움말 탭 내용 추가 허용", "composer.custom-help": "사용자 정의 \"도움말\" 텍스트", + "backlinks": "역링크", + "backlinks.enabled": "화제 역링크 활성화", + "backlinks.help": "포스트가 다른 화제를 참조할 경우 참조한 화제에 해당 포스트의 역링크가 표시됩니다.", "ip-tracking": "IP 추적", "ip-tracking.each-post": "모든 포스트 IP 추적", "enable-post-history": "게시글 편집 기록 활성화" diff --git a/public/language/ko/admin/settings/user.json b/public/language/ko/admin/settings/user.json index 5deefd2b9e..feb1658be6 100644 --- a/public/language/ko/admin/settings/user.json +++ b/public/language/ko/admin/settings/user.json @@ -71,6 +71,7 @@ "digest-freq.off": "해제", "digest-freq.daily": "매일", "digest-freq.weekly": "매주", + "digest-freq.biweekly": "Bi-Weekly", "digest-freq.monthly": "매달", "email-chat-notifs": "오프라인일 때 채팅 메시지가 도착하면 알림 메일 보내기", "email-post-notif": "내가 관심있는 화제에 답글이 달리면 메일 보내기", diff --git a/public/language/ko/email.json b/public/language/ko/email.json index 9c910ac944..c4a36ab099 100644 --- a/public/language/ko/email.json +++ b/public/language/ko/email.json @@ -7,7 +7,7 @@ "greeting_with_name": "안녕하세요 %1님", "email.verify-your-email.subject": "사용자의 이메일을 인증해주세요.", "email.verify.text1": "이메일 주소 변경 또는 확인을 요청했습니다.", - "email.verify.text2": "For security purposes, we only change or confirm the email address on file once its ownership has been confirmed via email. If you did not request this, no action is required on your part.", + "email.verify.text2": "보안을 위해 이메일이 인증되어야만 변경이 가능합니다. 요청을 하지 않은 경우 사용자 측에서 수행할 작업이 없습니다.", "email.verify.text3": "이 전자 메일 주소를 확인하면 현재 전자 메일 주소를 %1로 바꿉니다.", "welcome.text1": "%1님 가입해주셔서 감사합니다.", "welcome.text2": "계정을 활성화하려면 등록한 메일 주소의 인증이 필요합니다.", diff --git a/public/language/ko/error.json b/public/language/ko/error.json index 5b09333f7d..bc4af29b50 100644 --- a/public/language/ko/error.json +++ b/public/language/ko/error.json @@ -25,17 +25,18 @@ "invalid-event": "올바르지 않은 이벤트: %1", "local-login-disabled": "권한이 없는 계정에서의 로컬 로그인이 비활성화 되었습니다.", "csrf-invalid": "세션이 만료되어 로그인에 실패하였습니다. 다시 시도해주세요.", - "invalid-path": "Invalid path", - "folder-exists": "Folder exists", + "invalid-path": "올바르지 않은 경로입니다.", + "folder-exists": "폴더가 이미 존재합니다.", "invalid-pagination-value": "올바르지 않은 페이지 값입니다. 최소 %1에서 최대 2% 사이로 설정해야 합니다.", "username-taken": "이미 사용 중인 사용자명입니다.", "email-taken": "이미 사용 중인 이메일입니다.", "email-nochange": "입력한 전자 메일이 이미 등록되어 있는 전자 메일과 동일합니다.", "email-invited": "해당 이메일의 사용자는 이미 초대되었습니다.", - "email-not-confirmed": "Posting in some categories or topics is enabled once your email is confirmed, please click here to send a confirmation email.", + "email-not-confirmed": "이메일 인증이 완료된 후 카테고리나 화제에 새로운 포스트를 작성할 수 있습니다. 여기를 눌러 인증 메일을 다시 발송할 수 있습니다.", "email-not-confirmed-chat": "아직 이메일이 인증되지 않아 채팅 기능을 사용할 수 없습니다. 여기를 눌러 이메일 인증을 진행하세요.", - "email-not-confirmed-email-sent": "이메일 인증이 완료되지 않았습니다. 수신함에서 인증 메일을 확인해주세요. 인증이 완료되기 전까지는 글을 작성하거나 채팅 참여가 불가능합니다.", - "no-email-to-confirm": "계정에 전자 메일 설정이 없습니다. 계정 복구를 위해 이메일이 필요합니다. 이메일을 입력하려면 여기를 클릭하십시오.", + "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You may not be able to post in some categories or chat until your email is confirmed.", + "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery, and may be necessary for chatting and posting in some categories. Please click here to enter an email.", + "user-doesnt-have-email": "User \"%1\" does not have an email set.", "email-confirm-failed": "이메일 인증이 실패하였습니다. 잠시 후에 다시 시도하세요.", "confirm-email-already-sent": "인증 메일이 이미 발송되었습니다. 다시 보내려면 %1분을 기다리세요.", "sendmail-not-found": "Sendmail 실행파일을 찾을 수 없었습니다. 관리자가 sendmail을 설치했고 실행이 가능한 상태인지 확인해 주시기 바랍니다.", @@ -103,6 +104,7 @@ "already-bookmarked": "이미 즐겨찾기에 추가한 포스트 입니다.", "already-unbookmarked": "이미 즐겨찾기를 해제한 포스트 입니다.", "cant-ban-other-admins": "다른 관리자를 차단할 수 없습니다!", + "cant-make-banned-users-admin": "You can't make banned users admin.", "cant-remove-last-admin": "당신은 유일한 관리자입니다. 관리자를 그만두기 전에 다른 사용자를 관리자로 임명하세요.", "account-deletion-disabled": "계정 삭제 기능이 비활성화 상태입니다.", "cant-delete-admin": "해당 계정을 삭제하기 전에 관리자 권한을 해제해주십시오.", diff --git a/public/language/ko/global.json b/public/language/ko/global.json index 6f78a5f9a5..1b22d7ce1d 100644 --- a/public/language/ko/global.json +++ b/public/language/ko/global.json @@ -70,7 +70,7 @@ "firstpost": "첫 포스트", "read_more": "더 보기", "more": "더 보기", - "none": "None", + "none": "없음", "posted_ago_by_guest": "비회원이 %1에 작성했습니다.", "posted_ago_by": "%2님이 %1에 작성했습니다.", "posted_ago": "%1에 작성되었습니다.", diff --git a/public/language/ko/modules.json b/public/language/ko/modules.json index 8233e20d6f..b82a889498 100644 --- a/public/language/ko/modules.json +++ b/public/language/ko/modules.json @@ -54,7 +54,7 @@ "composer.formatting.strikethrough": "취소선", "composer.formatting.code": "코드", "composer.formatting.link": "링크", - "composer.formatting.picture": "사진", + "composer.formatting.picture": "이미지 링크", "composer.upload-picture": "이미지 업로드", "composer.upload-file": "파일 업로드", "composer.zen_mode": "전체화면", diff --git a/public/language/ko/notifications.json b/public/language/ko/notifications.json index 1778a6e0e4..8fa50bb11f 100644 --- a/public/language/ko/notifications.json +++ b/public/language/ko/notifications.json @@ -14,7 +14,7 @@ "topics": "화제", "replies": "답글", "chat": "채팅", - "group-chat": "Group Chats", + "group-chat": "그룹 채팅", "follows": "팔로우", "upvote": "추천", "new-flags": "새로 들어온 신고", diff --git a/public/language/ko/register.json b/public/language/ko/register.json index 0c6547f61f..f9249dfbb5 100644 --- a/public/language/ko/register.json +++ b/public/language/ko/register.json @@ -20,9 +20,9 @@ "registration-added-to-queue": "회원가입이 요청되었습니다. 관리자의 승인 후 메일이 발송됩니다.", "registration-queue-average-time": "가입 승인에는 평균적으로 %1시간 %2분이 소요됩니다.", "registration-queue-auto-approve-time": "%1시간 내로 이 포럼에서 사용자의 계정이 완전히 활성화될 예정입니다.", - "interstitial.intro": "We'd like some additional information in order to update your account…", - "interstitial.intro-new": "We'd like some additional information before we can create your account…", - "interstitial.errors-found": "Please review the entered information:", + "interstitial.intro": "계정 정보를 수정하기 전 추가 정보가 필요합니다.", + "interstitial.intro-new": "계정을 생성하기 전 추가 정보가 필요합니다.", + "interstitial.errors-found": "입력한 정보를 다시 확인해주세요.", "gdpr_agree_data": "나는 이 웹사이트에서 개인 정보를 수집하고 처리하는데 동의합니다.", "gdpr_agree_email": "나는 이 웹사이트에서 포럼 메일 및 알림 메일을 수신하는데 동의합니다.", "gdpr_consent_denied": "사용자는 이 사이트가 사용자의 정보를 수집/처리하고 이메일을 보내는 것에 동의해야 합니다.", diff --git a/public/language/ko/top.json b/public/language/ko/top.json new file mode 100644 index 0000000000..b8a05bfa5f --- /dev/null +++ b/public/language/ko/top.json @@ -0,0 +1,4 @@ +{ + "title": "Top", + "no_top_topics": "No top topics" +} \ No newline at end of file diff --git a/public/language/ko/topic.json b/public/language/ko/topic.json index 1804eb712e..385ec0f625 100644 --- a/public/language/ko/topic.json +++ b/public/language/ko/topic.json @@ -20,8 +20,8 @@ "login-to-view": "🔒 열람을 위해 로그인", "edit": "수정", "delete": "삭제", - "delete-event": "Delete Event", - "delete-event-confirm": "Are you sure you want to delete this event?", + "delete-event": "이벤트 삭제", + "delete-event-confirm": "이 이벤트를 삭제하시겠습니까?", "purge": "완전 삭제", "restore": "복원", "move": "이동", @@ -45,8 +45,9 @@ "unpinned-by": "상단 고정을 해제한 사용자:", "deleted-by": "삭제한 사용자:", "restored-by": "복원한 사용자:", - "moved-from-by": "Moved from %1 by", + "moved-from-by": "%1에서 이동됨 -", "queued-by": "게시 승인 대기 중 →", + "backlink": "참조 -", "bookmark_instructions": "이 쓰레드에서 읽은 마지막 포스트로 이동하려면 여기를 클릭 하세요.", "flag-post": "해당 포스트 신고", "flag-user": "해당 유저 신고", @@ -138,6 +139,7 @@ "composer.handle_placeholder": "이름을 입력하세요.", "composer.discard": "취소", "composer.submit": "등록", + "composer.additional-options": "Additional Options", "composer.schedule": "예약", "composer.replying_to": "'%1'에 대한 답글", "composer.new_topic": "새 화제 생성", @@ -158,6 +160,7 @@ "newest_to_oldest": "최신순", "most_votes": "투표순", "most_posts": "포스트순", + "most_views": "조회수순", "stale.title": "새로운 화제를 생성하시겠습니까?", "stale.warning": "현재 답글을 작성 중인 화제는 오래전에 작성 되었습니다. 새로 화제를 생성하고 이 게시물을 인용하시겠습니까?", "stale.create": "새로운 화제 작성", @@ -177,5 +180,5 @@ "timeago_earlier": "%1 이전", "first-post": "첫 포스트", "last-post": "마지막 포스트", - "post-quick-reply": "Post quick reply" + "post-quick-reply": "빠른 답글" } \ No newline at end of file diff --git a/public/language/ko/user.json b/public/language/ko/user.json index 68fc02c4c7..e66a123f20 100644 --- a/public/language/ko/user.json +++ b/public/language/ko/user.json @@ -94,11 +94,12 @@ "digest_off": "해제", "digest_daily": "매일", "digest_weekly": "매주", + "digest_biweekly": "Bi-Weekly", "digest_monthly": "매월", "has_no_follower": "이 사용자는 팔로워가 없습니다 :(", "follows_no_one": "이 사용자는 아무도 팔로우하고 있지 않습니다 :(", "has_no_posts": "이 사용자가 작성한 포스트가 없습니다.", - "has_no_best_posts": "This user does not have any upvoted posts yet.", + "has_no_best_posts": "해당 유저는 아직까지 추천을 받은 포스트가 없습니다.", "has_no_topics": "이 사용자가 작성한 화제가 없습니다.", "has_no_watched_topics": "이 사용자가 관심 목록에 추가한 화제가 없습니다.", "has_no_ignored_topics": "이 사용자는 아직 무시 중인 화제가 없습니다.", @@ -182,7 +183,7 @@ "consent.export-uploads-success": "업로드한 컨텐츠를 내보내기 합니다. 완료되면 알림을 수신합니다.", "consent.export_posts": "포스트 내보내기 (.csv)", "consent.export-posts-success": "포스트를 내보내기 합니다. 완료되면 알림을 수신합니다.", - "emailUpdate.intro": "Please enter your email address below. This forum uses your email address for scheduled digest and notifications, as well as for account recovery in the event of a lost password.", - "emailUpdate.optional": "This field is optional. You are not obligated to provide your email address, but without a validated email, you will not be able to recover your account.", - "emailUpdate.change-instructions": "A confirmation email will be sent to the entered email address with a unique link. Accessing that link will confirm your ownership of the email address and it will become active on your account. At any time, you are able to update your email on file from within your account page." + "emailUpdate.intro": "아래에 이메일 주소를 입력하세요. 해당 포럼은 입력한 이메일 주소로 정기 알림 메일과 그 외의 알림을 전송하고, 계정 복구 작업에도 해당 이메일 주소를 사용합니다.", + "emailUpdate.optional": "해당 항목은 선택 입력 항목입니다. 이메일 주소를 반드시 입력할 필요는 없지만 유효한 이메일 주소를 입력하지 않을 경우 계정을 잃어버렸을 때 복구하지 못 할 수도 있습니다.", + "emailUpdate.change-instructions": "입력하신 이메일 주소로 가입 인증 메일이 발송되었습니다. 메일 내의 링크에 접속할 경우 메일 소유자를 확인하고 계정이 활성화됩니다. 활성화 후에도 계정 페이지에서 이메일 주소를 변경할 수 있습니다." } \ No newline at end of file diff --git a/public/language/lt/admin/advanced/events.json b/public/language/lt/admin/advanced/events.json index 56d9457971..b2c2033fb5 100644 --- a/public/language/lt/admin/advanced/events.json +++ b/public/language/lt/admin/advanced/events.json @@ -3,6 +3,7 @@ "no-events": "There are no events", "control-panel": "Events Control Panel", "delete-events": "Delete Events", + "confirm-delete-all-events": "Are you sure you want to delete all logged events?", "filters": "Filters", "filters-apply": "Apply Filters", "filter-type": "Event Type", diff --git a/public/language/lt/admin/dashboard.json b/public/language/lt/admin/dashboard.json index 0de31d4917..4d39626882 100644 --- a/public/language/lt/admin/dashboard.json +++ b/public/language/lt/admin/dashboard.json @@ -56,8 +56,8 @@ "active-users.total": "Total", "active-users.connections": "Connections", - "anonymous-registered-users": "Anonymous vs Registered Users", - "anonymous": "Anonymous", + "guest-registered-users": "Guest vs Registered Users", + "guest": "Guest", "registered": "Registered", "user-presence": "User Presence", @@ -68,6 +68,7 @@ "unread": "Unread", "high-presence-topics": "High Presence Topics", + "popular-searches": "Popular Searches", "graphs.page-views": "Page Views", "graphs.page-views-registered": "Page Views Registered", @@ -75,13 +76,14 @@ "graphs.page-views-bot": "Page Views Bot", "graphs.unique-visitors": "Unique Visitors", "graphs.registered-users": "Registered Users", - "graphs.anonymous-users": "Anonymous Users", + "graphs.guest-users": "Guest Users", "last-restarted-by": "Last restarted by", "no-users-browsing": "No users browsing", "back-to-dashboard": "Back to Dashboard", "details.no-users": "No users have joined within the selected timeframe", "details.no-topics": "No topics have been posted within the selected timeframe", + "details.no-searches": "No searches have been made yet", "details.no-logins": "No logins have been recorded within the selected timeframe", "details.logins-static": "NodeBB only saves session data for %1 days, and so this table below will only show the most recently active sessions", "details.logins-login-time": "Login Time" diff --git a/public/language/lt/admin/development/info.json b/public/language/lt/admin/development/info.json index 1003af1a5f..11202d9c3a 100644 --- a/public/language/lt/admin/development/info.json +++ b/public/language/lt/admin/development/info.json @@ -8,7 +8,11 @@ "nodejs": "nodejs", "online": "online", "git": "git", - "memory": "memory", + "process-memory": "process memory", + "system-memory": "system memory", + "used-memory-process": "Used memory by process", + "used-memory-os": "Used system memory", + "total-memory-os": "Total system memory", "load": "system load", "cpu-usage": "cpu usage", "uptime": "uptime", diff --git a/public/language/lt/admin/extend/rewards.json b/public/language/lt/admin/extend/rewards.json index 5383a90b33..df89d441a7 100644 --- a/public/language/lt/admin/extend/rewards.json +++ b/public/language/lt/admin/extend/rewards.json @@ -8,8 +8,6 @@ "delete": "Delete", "enable": "Enable", "disable": "Disable", - "control-panel": "Rewards Control", - "new-reward": "New Reward", "alert.delete-success": "Successfully deleted reward", "alert.no-inputs-found": "Illegal reward - no inputs found!", diff --git a/public/language/lt/admin/general/dashboard.json b/public/language/lt/admin/general/dashboard.json deleted file mode 100644 index 37ae537786..0000000000 --- a/public/language/lt/admin/general/dashboard.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "forum-traffic": "Forum Traffic", - "page-views": "Page Views", - "unique-visitors": "Unique Visitors", - "new-users": "New Users", - "posts": "Posts", - "topics": "Topics", - "page-views-seven": "Last 7 Days", - "page-views-thirty": "Last 30 Days", - "page-views-last-day": "Last 24 hours", - "page-views-custom": "Custom Date Range", - "page-views-custom-start": "Range Start", - "page-views-custom-end": "Range End", - "page-views-custom-help": "Enter a date range of page views you would like to view. If no date picker is available, the accepted format is YYYY-MM-DD", - "page-views-custom-error": "Please enter a valid date range in the format YYYY-MM-DD", - - "stats.yesterday": "Yesterday", - "stats.today": "Today", - "stats.last-week": "Last Week", - "stats.this-week": "This Week", - "stats.last-month": "Last Month", - "stats.this-month": "This Month", - "stats.all": "All Time", - - "updates": "Updates", - "running-version": "You are running NodeBB v%1.", - "keep-updated": "Always make sure that your NodeBB is up to date for the latest security patches and bug fixes.", - "up-to-date": "

You are up-to-date

", - "upgrade-available": "

A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-upgrade-available": "

This is an outdated pre-release version of NodeBB. A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-warning": "

This is a pre-release version of NodeBB. Unintended bugs may occur.

", - "running-in-development": "Forum is running in development mode. The forum may be open to potential vulnerabilities; please contact your system administrator.", - "latest-lookup-failed": "

Failed to look up latest available version of NodeBB

", - - "notices": "Notices", - "restart-not-required": "Restart not required", - "restart-required": "Restart required", - "search-plugin-installed": "Search Plugin installed", - "search-plugin-not-installed": "Search Plugin not installed", - "search-plugin-tooltip": "Install a search plugin from the plugin page in order to activate search functionality", - - "control-panel": "System Control", - "rebuild-and-restart": "Rebuild & Restart", - "restart": "Restart", - "restart-warning": "Rebuilding or Restarting your NodeBB will drop all existing connections for a few seconds.", - "restart-disabled": "Rebuilding and Restarting your NodeBB has been disabled as you do not seem to be running it via the appropriate daemon.", - "maintenance-mode": "Maintenance Mode", - "maintenance-mode-title": "Click here to set up maintenance mode for NodeBB", - "realtime-chart-updates": "Realtime Chart Updates", - - "active-users": "Active Users", - "active-users.users": "Users", - "active-users.guests": "Guests", - "active-users.total": "Total", - "active-users.connections": "Connections", - - "anonymous-registered-users": "Anonymous vs Registered Users", - "anonymous": "Anonymous", - "registered": "Registered", - - "user-presence": "User Presence", - "on-categories": "On categories list", - "reading-posts": "Reading posts", - "browsing-topics": "Browsing topics", - "recent": "Recent", - "unread": "Unread", - - "high-presence-topics": "High Presence Topics", - - "graphs.page-views": "Page Views", - "graphs.page-views-registered": "Page Views Registered", - "graphs.page-views-guest": "Page Views Guest", - "graphs.page-views-bot": "Page Views Bot", - "graphs.unique-visitors": "Unique Visitors", - "graphs.registered-users": "Registered Users", - "graphs.anonymous-users": "Anonymous Users", - "last-restarted-by": "Last restarted by", - "no-users-browsing": "No users browsing" -} diff --git a/public/language/lt/admin/general/homepage.json b/public/language/lt/admin/general/homepage.json deleted file mode 100644 index 7428d59eeb..0000000000 --- a/public/language/lt/admin/general/homepage.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "home-page": "Home Page", - "description": "Choose what page is shown when users navigate to the root URL of your forum.", - "home-page-route": "Home Page Route", - "custom-route": "Custom Route", - "allow-user-home-pages": "Allow User Home Pages", - "home-page-title": "Title of the home page (default \"Home\")" -} \ No newline at end of file diff --git a/public/language/lt/admin/general/languages.json b/public/language/lt/admin/general/languages.json deleted file mode 100644 index bdd57849b3..0000000000 --- a/public/language/lt/admin/general/languages.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "language-settings": "Language Settings", - "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language", - "auto-detect": "Auto Detect Language Setting for Guests" -} \ No newline at end of file diff --git a/public/language/lt/admin/general/navigation.json b/public/language/lt/admin/general/navigation.json deleted file mode 100644 index 13dd01aae7..0000000000 --- a/public/language/lt/admin/general/navigation.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "icon": "Icon:", - "change-icon": "change", - "route": "Route:", - "tooltip": "Tooltip:", - "text": "Text:", - "text-class": "Text Class: optional", - "class": "Class: optional", - "id": "ID: optional", - - "properties": "Properties:", - "groups": "Groups:", - "open-new-window": "Open in a new window", - - "btn.delete": "Delete", - "btn.disable": "Disable", - "btn.enable": "Enable", - - "available-menu-items": "Available Menu Items", - "custom-route": "Custom Route", - "core": "core", - "plugin": "plugin" -} \ No newline at end of file diff --git a/public/language/lt/admin/general/social.json b/public/language/lt/admin/general/social.json deleted file mode 100644 index 23aedfcfaa..0000000000 --- a/public/language/lt/admin/general/social.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "post-sharing": "Post Sharing", - "info-plugins-additional": "Plugins can add additional networks for sharing posts.", - "save-success": "Successfully saved Post Sharing Networks!" -} \ No newline at end of file diff --git a/public/language/lt/admin/general/sounds.json b/public/language/lt/admin/general/sounds.json deleted file mode 100644 index 95ccbde0f1..0000000000 --- a/public/language/lt/admin/general/sounds.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "notifications": "Notifications", - "chat-messages": "Chat Messages", - "play-sound": "Play", - "incoming-message": "Incoming Message", - "outgoing-message": "Outgoing Message", - "upload-new-sound": "Upload New Sound", - "saved": "Settings Saved" -} \ No newline at end of file diff --git a/public/language/lt/admin/manage/digest.json b/public/language/lt/admin/manage/digest.json index 8f3661698a..38c634d1f6 100644 --- a/public/language/lt/admin/manage/digest.json +++ b/public/language/lt/admin/manage/digest.json @@ -13,6 +13,7 @@ "resent-single": "Manual digest resend completed", "resent-day": "Daily digest resent", "resent-week": "Weekly digest resent", + "resent-biweek": "Bi-Weekly digest resent", "resent-month": "Monthly digest resent", "null": "Never", "manual-run": "Manual digest run:", diff --git a/public/language/lt/admin/manage/users.json b/public/language/lt/admin/manage/users.json index 38a065b954..2a3c0c4829 100644 --- a/public/language/lt/admin/manage/users.json +++ b/public/language/lt/admin/manage/users.json @@ -47,6 +47,7 @@ "users.uid": "uid", "users.username": "username", "users.email": "email", + "users.no-email": "(no email)", "users.ip": "IP", "users.postcount": "postcount", "users.reputation": "reputation", diff --git a/public/language/lt/admin/menu.json b/public/language/lt/admin/menu.json index 1c5ea73b4f..5b22fbeb36 100644 --- a/public/language/lt/admin/menu.json +++ b/public/language/lt/admin/menu.json @@ -4,6 +4,7 @@ "dashboard/logins": "Logins", "dashboard/users": "Users", "dashboard/topics": "Topics", + "dashboard/searches": "Searches", "section-general": "General", "section-manage": "Manage", diff --git a/public/language/lt/admin/settings/email.json b/public/language/lt/admin/settings/email.json index 9bdbde26a6..65f579d28c 100644 --- a/public/language/lt/admin/settings/email.json +++ b/public/language/lt/admin/settings/email.json @@ -6,7 +6,7 @@ "from-help": "The from name to display in the email.", "smtp-transport": "SMTP Transport", - "smtp-transport.enabled": "Use an external email server to send emails", + "smtp-transport.enabled": "Enable SMTP Transport", "smtp-transport-help": "You can select from a list of well-known services or enter a custom one.", "smtp-transport.service": "Select a service", "smtp-transport.service-custom": "Custom Service", @@ -37,6 +37,10 @@ "subscriptions.hour": "Digest Hour", "subscriptions.hour-help": "Please enter a number representing the hour to send scheduled email digests (e.g. 0 for midnight, 17 for 5:00pm). Keep in mind that this is the hour according to the server itself, and may not exactly match your system clock.
The approximate server time is:
The next daily digest is scheduled to be sent ", "notifications.remove-images": "Remove images from email notifications", + "require-email-address": "Require new users to specify an email address", + "require-email-address-warning": "By default, users can opt-out of entering an email address. Enabling this option means they have to enter an email address in order to proceed with registration. It does not ensure user will enter a real email address, nor even an address they own.", "include-unverified-emails": "Send emails to recipients who have not explicitly confirmed their emails", - "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws." -} \ No newline at end of file + "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws.", + "prompt": "Prompt users to enter or confirm their emails", + "prompt-help": "If a user does not have an email set, or their email is not confirmed, a warning will be shown on screen." +} diff --git a/public/language/lt/admin/settings/general.json b/public/language/lt/admin/settings/general.json index be7df90870..29b939861b 100644 --- a/public/language/lt/admin/settings/general.json +++ b/public/language/lt/admin/settings/general.json @@ -3,9 +3,9 @@ "title": "Site Title", "title.short": "Short Title", "title.short-placeholder": "If no short title is specified, the site title will be used", - "title.url": "URL", + "title.url": "Title Link URL", "title.url-placeholder": "The URL of the site title", - "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.", + "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "title.name": "Your Community Name", "title.show-in-header": "Show Site Title in Header", "browser-title": "Browser Title", @@ -20,9 +20,9 @@ "logo.image": "Image", "logo.image-placeholder": "Path to a logo to display on forum header", "logo.upload": "Upload", - "logo.url": "URL", + "logo.url": "Logo Link URL", "logo.url-placeholder": "The URL of the site logo", - "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.", + "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "logo.alt-text": "Alt Text", "log.alt-text-placeholder": "Alternative text for accessibility", "favicon": "Favicon", @@ -47,4 +47,4 @@ "undo-timeout": "Undo Timeout", "undo-timeout-help": "Some operations such as moving topics will allow for the moderator to undo their action within a certain timeframe. Set to 0 to disable undo completely.", "topic-tools": "Topic Tools" -} \ No newline at end of file +} diff --git a/public/language/lt/admin/settings/navigation.json b/public/language/lt/admin/settings/navigation.json index 13dd01aae7..7baca85096 100644 --- a/public/language/lt/admin/settings/navigation.json +++ b/public/language/lt/admin/settings/navigation.json @@ -11,6 +11,8 @@ "properties": "Properties:", "groups": "Groups:", "open-new-window": "Open in a new window", + "dropdown": "Dropdown", + "dropdown-placeholder": "Place your dropdown menu items below, ie:
<li><a href="https://myforum.com">Link 1</a></li>", "btn.delete": "Delete", "btn.disable": "Disable", @@ -20,4 +22,4 @@ "custom-route": "Custom Route", "core": "core", "plugin": "plugin" -} \ No newline at end of file +} diff --git a/public/language/lt/admin/settings/notifications.json b/public/language/lt/admin/settings/notifications.json index da6c9680a3..c6d8b928ce 100644 --- a/public/language/lt/admin/settings/notifications.json +++ b/public/language/lt/admin/settings/notifications.json @@ -2,5 +2,6 @@ "notifications": "Notifications", "welcome-notification": "Welcome Notification", "welcome-notification-link": "Welcome Notification Link", - "welcome-notification-uid": "Welcome Notification User (UID)" + "welcome-notification-uid": "Welcome Notification User (UID)", + "post-queue-notification-uid": "Post Queue User (UID)" } \ No newline at end of file diff --git a/public/language/lt/admin/settings/post.json b/public/language/lt/admin/settings/post.json index 27493aafbd..ab8245738c 100644 --- a/public/language/lt/admin/settings/post.json +++ b/public/language/lt/admin/settings/post.json @@ -40,6 +40,7 @@ "teaser.last-post": "Last – Show the latest post, including the original post, if no replies", "teaser.last-reply": "Last – Show the latest reply, or a \"No replies\" placeholder if no replies", "teaser.first": "First", + "showPostPreviewsOnHover": "Show a preview of posts when mouse overed", "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", @@ -56,6 +57,9 @@ "composer.show-help": "Show \"Help\" tab", "composer.enable-plugin-help": "Allow plugins to add content to the help tab", "composer.custom-help": "Custom Help Text", + "backlinks": "Backlinks", + "backlinks.enabled": "Enable topic backlinks", + "backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.", "ip-tracking": "IP Tracking", "ip-tracking.each-post": "Track IP Address for each post", "enable-post-history": "Enable Post History" diff --git a/public/language/lt/admin/settings/user.json b/public/language/lt/admin/settings/user.json index 48be13b75e..7923bf8cbe 100644 --- a/public/language/lt/admin/settings/user.json +++ b/public/language/lt/admin/settings/user.json @@ -71,6 +71,7 @@ "digest-freq.off": "Off", "digest-freq.daily": "Daily", "digest-freq.weekly": "Weekly", + "digest-freq.biweekly": "Bi-Weekly", "digest-freq.monthly": "Monthly", "email-chat-notifs": "Send an email if a new chat message arrives and I am not online", "email-post-notif": "Send an email when replies are made to topics I am subscribed to", diff --git a/public/language/lt/error.json b/public/language/lt/error.json index df9b8e9d42..aa1e74f66a 100644 --- a/public/language/lt/error.json +++ b/public/language/lt/error.json @@ -34,8 +34,9 @@ "email-invited": "Email was already invited", "email-not-confirmed": "Posting in some categories or topics is enabled once your email is confirmed, please click here to send a confirmation email.", "email-not-confirmed-chat": "Jūs negalite bendrauti, kol jūsų el.paštas nėra patvirtintas, prašome spausti čia kad aktyvuoti jūsų el.paštą", - "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You won't be able to post or chat until your email is confirmed.", - "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery. Please click here to enter an email.", + "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You may not be able to post in some categories or chat until your email is confirmed.", + "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery, and may be necessary for chatting and posting in some categories. Please click here to enter an email.", + "user-doesnt-have-email": "User \"%1\" does not have an email set.", "email-confirm-failed": "Negalime patvirtinti jūsų el. adreso, prašom bandyti vėliau.", "confirm-email-already-sent": "Patvirtinimo laiškas išsiųstas, prašome palaukti %1 minute(s) kad išsiųstume kita", "sendmail-not-found": "The sendmail executable could not be found, please ensure it is installed and executable by the user running NodeBB.", @@ -103,6 +104,7 @@ "already-bookmarked": "Jūs jau turite žymekelį šiam įrašui", "already-unbookmarked": "Jūs jau nuėmėte žymeklį šiam įrašui", "cant-ban-other-admins": "Jūs negalite užblokuoti kitų administratorių!", + "cant-make-banned-users-admin": "You can't make banned users admin.", "cant-remove-last-admin": "Jūs esate vienintelis administratorius. Pridėkite kitą vartotoja kaip administratorių prieš pašalindamas save", "account-deletion-disabled": "Account deletion is disabled", "cant-delete-admin": "Pašalinkite administratoriaus teises šiai paskyrai prieš bandydami ją ištrinti.", diff --git a/public/language/lt/modules.json b/public/language/lt/modules.json index 3ba39a844e..94ae834e8c 100644 --- a/public/language/lt/modules.json +++ b/public/language/lt/modules.json @@ -54,7 +54,7 @@ "composer.formatting.strikethrough": "Strikethrough", "composer.formatting.code": "Code", "composer.formatting.link": "Link", - "composer.formatting.picture": "Picture", + "composer.formatting.picture": "Image Link", "composer.upload-picture": "Upload Image", "composer.upload-file": "Upload File", "composer.zen_mode": "Zen Mode", diff --git a/public/language/lt/top.json b/public/language/lt/top.json new file mode 100644 index 0000000000..b8a05bfa5f --- /dev/null +++ b/public/language/lt/top.json @@ -0,0 +1,4 @@ +{ + "title": "Top", + "no_top_topics": "No top topics" +} \ No newline at end of file diff --git a/public/language/lt/topic.json b/public/language/lt/topic.json index 26cfa3d292..b8d8436625 100644 --- a/public/language/lt/topic.json +++ b/public/language/lt/topic.json @@ -47,6 +47,7 @@ "restored-by": "Restored by", "moved-from-by": "Moved from %1 by", "queued-by": "Post queued for approval →", + "backlink": "Referenced by", "bookmark_instructions": "Click here to return to the last read post in this thread.", "flag-post": "Flag this post", "flag-user": "Flag this user", @@ -138,6 +139,7 @@ "composer.handle_placeholder": "Enter your name/handle here", "composer.discard": "Atšaukti", "composer.submit": "Patvirtinti", + "composer.additional-options": "Additional Options", "composer.schedule": "Schedule", "composer.replying_to": "Atsakymas %1", "composer.new_topic": "Nauja tema", @@ -158,6 +160,7 @@ "newest_to_oldest": "Nuo naujausių iki seniausių", "most_votes": "Daugiausiai Balsų", "most_posts": "Daugiausiai Įrašų", + "most_views": "Most Views", "stale.title": "Create new topic instead?", "stale.warning": "The topic you are replying to is quite old. Would you like to create a new topic instead, and reference this one in your reply?", "stale.create": "Sukurti naują temą", diff --git a/public/language/lt/user.json b/public/language/lt/user.json index dcd1f0f102..dabce36492 100644 --- a/public/language/lt/user.json +++ b/public/language/lt/user.json @@ -94,6 +94,7 @@ "digest_off": "Išjungta", "digest_daily": "Kas dieną", "digest_weekly": "Kas savaitę", + "digest_biweekly": "Bi-Weekly", "digest_monthly": "Kas mėnesį", "has_no_follower": "Šis vartotojas neturi jokių sekėjų :(", "follows_no_one": "Šis vartotojas nieko neseka :(", diff --git a/public/language/lv/admin/advanced/events.json b/public/language/lv/admin/advanced/events.json index 5168d6544c..2836e7f88e 100644 --- a/public/language/lv/admin/advanced/events.json +++ b/public/language/lv/admin/advanced/events.json @@ -3,6 +3,7 @@ "no-events": "Nav notikumu", "control-panel": "Notikumu vadības panelis", "delete-events": "Delete Events", + "confirm-delete-all-events": "Are you sure you want to delete all logged events?", "filters": "Filters", "filters-apply": "Apply Filters", "filter-type": "Event Type", diff --git a/public/language/lv/admin/dashboard.json b/public/language/lv/admin/dashboard.json index 234cf4baee..076694b4ca 100644 --- a/public/language/lv/admin/dashboard.json +++ b/public/language/lv/admin/dashboard.json @@ -56,8 +56,8 @@ "active-users.total": "Kopēji", "active-users.connections": "Savienojumi", - "anonymous-registered-users": "Anonīmie lietotāji pret reģistrētiem lietotājiem", - "anonymous": "Anonīmie", + "guest-registered-users": "Guest vs Registered Users", + "guest": "Guest", "registered": "Reģistrētie", "user-presence": "Lietotāju novietojums", @@ -68,6 +68,7 @@ "unread": "Skatās nelasītos rakstus", "high-presence-topics": "Augstās klātesamības temati", + "popular-searches": "Popular Searches", "graphs.page-views": "Lapu skatījumi", "graphs.page-views-registered": "Lapu skatījumi no lietotājiem", @@ -75,13 +76,14 @@ "graphs.page-views-bot": "Lapu skatījumi no botiem", "graphs.unique-visitors": "Unikālie apmeklētāji", "graphs.registered-users": "Reģistrētie lietotāji", - "graphs.anonymous-users": "Anonīmie lietotāji", + "graphs.guest-users": "Guest Users", "last-restarted-by": "Pēdējoreiz restartējis", "no-users-browsing": "Nav pārlūkojošo lietotāju", "back-to-dashboard": "Back to Dashboard", "details.no-users": "No users have joined within the selected timeframe", "details.no-topics": "No topics have been posted within the selected timeframe", + "details.no-searches": "No searches have been made yet", "details.no-logins": "No logins have been recorded within the selected timeframe", "details.logins-static": "NodeBB only saves session data for %1 days, and so this table below will only show the most recently active sessions", "details.logins-login-time": "Login Time" diff --git a/public/language/lv/admin/development/info.json b/public/language/lv/admin/development/info.json index a8230d80e3..d07a526789 100644 --- a/public/language/lv/admin/development/info.json +++ b/public/language/lv/admin/development/info.json @@ -8,7 +8,11 @@ "nodejs": "nodejs", "online": "tiešsaistē", "git": "git", - "memory": "atmiņa", + "process-memory": "process memory", + "system-memory": "system memory", + "used-memory-process": "Used memory by process", + "used-memory-os": "Used system memory", + "total-memory-os": "Total system memory", "load": "system load", "cpu-usage": "cpu usage", "uptime": "darbspējas laiks", diff --git a/public/language/lv/admin/extend/rewards.json b/public/language/lv/admin/extend/rewards.json index ae99c8e088..97cf227477 100644 --- a/public/language/lv/admin/extend/rewards.json +++ b/public/language/lv/admin/extend/rewards.json @@ -8,8 +8,6 @@ "delete": "Izdzēst", "enable": "Iespējot", "disable": "Atspējot", - "control-panel": "Balvu vadības panelis", - "new-reward": "Jauna balva", "alert.delete-success": "Veiksmīgi izdzēsta balva", "alert.no-inputs-found": "Nederīga balva - nav ievažu!", diff --git a/public/language/lv/admin/general/dashboard.json b/public/language/lv/admin/general/dashboard.json deleted file mode 100644 index f5b18c427d..0000000000 --- a/public/language/lv/admin/general/dashboard.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "forum-traffic": "Foruma datplūsma", - "page-views": "Lapu skatījumi", - "unique-visitors": "Unikālie apmeklētāji", - "new-users": "New Users", - "posts": "Raksti", - "topics": "Temati", - "page-views-seven": "Pēdējās 7 dienās", - "page-views-thirty": "Pēdējās 30 dienās", - "page-views-last-day": "Pēdējās 24 stundās", - "page-views-custom": "Pielāgotais datumu diapazons", - "page-views-custom-start": "No", - "page-views-custom-end": "Līdz", - "page-views-custom-help": "Ievadīt datumu diapazonu, kā lapu skatījumu skaitu vēlies redzēt. Ja datumu atlasītājs nav pieejams, lietot formātu YYYY-MM-DD", - "page-views-custom-error": "Lūdzu, ievadīt derīgu datumu diapazonu formatā YYYY-MM-DD", - - "stats.yesterday": "Yesterday", - "stats.today": "Today", - "stats.last-week": "Last Week", - "stats.this-week": "This Week", - "stats.last-month": "Last Month", - "stats.this-month": "This Month", - "stats.all": "Visu laiku", - - "updates": "Atjauninājumi", - "running-version": "Ir palaists NodeBB v%1.", - "keep-updated": "Lūdzu, vienmēr pārliecināties, ka NodeBB ir atjaunināts ar jaunākajiem drošības ielāpiem un kļūdu labojumiem.", - "up-to-date": "

Šobrīd nav atjauninājumu

", - "upgrade-available": "

Ir izlaista jauna versija (v%1). Apsvērt NodeBB atjaunināšanu.

", - "prerelease-upgrade-available": "

Šī ir novecojusies pirmizlaides NodeBB versija. Jauna versija (v%1) ir bijusi izlaista. Apsvērt NodeBB atjaunināšanu.

", - "prerelease-warning": "

Ši ir pirmizlaides NodeBB versija. Neparedzētas kļūdas var rasties.

", - "running-in-development": "NodeBB darbojas attīstītāju režīmā. NodeBB var būt neaizsargāts pret iespējamiem uzbrukumiem; lūdzu, sazināties ar sistēmas administratoru.", - "latest-lookup-failed": "

Neizdevās atrast jaunāko pieejamo NodeBB versiju

", - - "notices": "Paziņojumi", - "restart-not-required": "Nav nepieciešama pārstartēšana", - "restart-required": "Nepieciešama pārstartēšana", - "search-plugin-installed": "Meklēšanas spraudnis instalēts", - "search-plugin-not-installed": "Meklēšanas spraudnis nav instalēts", - "search-plugin-tooltip": "Instalēt meklēšanas spraudni no spraudņu lapas, lai aktivizētu meklēšanu", - - "control-panel": "Sistēmas kontrole", - "rebuild-and-restart": "Pārkompilēt & pārstartēt", - "restart": "Pārstartēt", - "restart-warning": "NodeBB pārkompilēšana vai pārstartēšana pārtrauks visus esošos savienojumus uz dažām sekundēm.", - "restart-disabled": "NodeBB pārkompilēšana un pārstartēšana ir atspējota, jo, šķiet, ka tā nav bijusi palaista ar atbilstošo dēmona procesu.", - "maintenance-mode": "Uzturēšanas režīms", - "maintenance-mode-title": "Klikšķināt, lai pārietu uz NodeBB uzturēšanas režīmu", - "realtime-chart-updates": "Reālā laika grafiku atjauninājumi", - - "active-users": "Aktīvie lietotāji", - "active-users.users": "Lietotāji", - "active-users.guests": "Viesi", - "active-users.total": "Kopēji", - "active-users.connections": "Savienojumi", - - "anonymous-registered-users": "Anonīmie lietotāji pret reģistrētiem lietotājiem", - "anonymous": "Anonīmie", - "registered": "Reģistrētie", - - "user-presence": "Lietotāju novietojums", - "on-categories": "Skatās kategorijas", - "reading-posts": "Lasa rakstus", - "browsing-topics": "Pārlūko tematus", - "recent": "Skatās nesenos rakstus", - "unread": "Skatās nelasītos rakstus", - - "high-presence-topics": "Augstās klātesamības temati", - - "graphs.page-views": "Lapu skatījumi", - "graphs.page-views-registered": "Lapu skatījumi no lietotājiem", - "graphs.page-views-guest": "Lapu skatījumi no viesiem", - "graphs.page-views-bot": "Lapu skatījumi no botiem", - "graphs.unique-visitors": "Unikālie apmeklētāji", - "graphs.registered-users": "Reģistrētie lietotāji", - "graphs.anonymous-users": "Anonīmie lietotāji", - "last-restarted-by": "Pēdējoreiz restartējis", - "no-users-browsing": "Nav pārlūkojošo lietotāju" -} diff --git a/public/language/lv/admin/general/homepage.json b/public/language/lv/admin/general/homepage.json deleted file mode 100644 index 68249543f4..0000000000 --- a/public/language/lv/admin/general/homepage.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "home-page": "Sākums", - "description": "Izvēlies, kādu lapu rādīt, kad lietotājs izvēlas foruma saknes URL.", - "home-page-route": "Sākumlapas ceļš", - "custom-route": "Pielāgotais ceļš", - "allow-user-home-pages": "Atļaut lietotājiem savas mājaslapas", - "home-page-title": "Sākumlapas titulis (pēc noklusējuma \"Sākums\")" -} \ No newline at end of file diff --git a/public/language/lv/admin/general/languages.json b/public/language/lv/admin/general/languages.json deleted file mode 100644 index 5e668f9147..0000000000 --- a/public/language/lv/admin/general/languages.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "language-settings": "Valodas iestatījumi", - "description": "Noklusējuma valoda nosaka valodas iestatījumus visiem lietotājiem, kuri apmeklē forumu.
Lietotājs savā konta iestatījumu lapā var ignorēt noklusējuma valodu.", - "default-language": "Noklusējama valoda", - "auto-detect": "Viesiem automātiski izprast valodas iestatījumus" -} \ No newline at end of file diff --git a/public/language/lv/admin/general/navigation.json b/public/language/lv/admin/general/navigation.json deleted file mode 100644 index c6908195e0..0000000000 --- a/public/language/lv/admin/general/navigation.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "icon": "Ikona:", - "change-icon": "izmaiņa", - "route": "Ceļš:", - "tooltip": "Paskaidre:", - "text": "Teksts:", - "text-class": "Teksta klase: neobligāta", - "class": "Class: optional", - "id": "ID: neobligāts", - - "properties": "Īpašības:", - "groups": "Grupas:", - "open-new-window": "Rādīt jaunā logā", - - "btn.delete": "Izdzēst", - "btn.disable": "Atspējot", - "btn.enable": "Iespējot", - - "available-menu-items": "Pieejamās izvēlnes iespējas", - "custom-route": "Pielāgotais ceļš", - "core": "kodols", - "plugin": "spraudnis" -} \ No newline at end of file diff --git a/public/language/lv/admin/general/social.json b/public/language/lv/admin/general/social.json deleted file mode 100644 index 03a4c2e035..0000000000 --- a/public/language/lv/admin/general/social.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "post-sharing": "Rakstu kopīgošana", - "info-plugins-additional": "Spraudņi var pievienot papildu rakstu kopīgošanas tīklus.", - "save-success": "Rakstu kopīgošanas tīkli veiksmi saglabāti!" -} \ No newline at end of file diff --git a/public/language/lv/admin/general/sounds.json b/public/language/lv/admin/general/sounds.json deleted file mode 100644 index 421805409d..0000000000 --- a/public/language/lv/admin/general/sounds.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "notifications": "Paziņojumi", - "chat-messages": "Sarunas", - "play-sound": "Spēlēt", - "incoming-message": "Ienākošā saruna", - "outgoing-message": "Izejošā saruna", - "upload-new-sound": "Augšupielādēt jaunu skaņu", - "saved": "Iestatījumi saglabāti" -} \ No newline at end of file diff --git a/public/language/lv/admin/manage/digest.json b/public/language/lv/admin/manage/digest.json index 8f3661698a..38c634d1f6 100644 --- a/public/language/lv/admin/manage/digest.json +++ b/public/language/lv/admin/manage/digest.json @@ -13,6 +13,7 @@ "resent-single": "Manual digest resend completed", "resent-day": "Daily digest resent", "resent-week": "Weekly digest resent", + "resent-biweek": "Bi-Weekly digest resent", "resent-month": "Monthly digest resent", "null": "Never", "manual-run": "Manual digest run:", diff --git a/public/language/lv/admin/manage/users.json b/public/language/lv/admin/manage/users.json index a413d0b3d0..44619ba911 100644 --- a/public/language/lv/admin/manage/users.json +++ b/public/language/lv/admin/manage/users.json @@ -47,6 +47,7 @@ "users.uid": "uid", "users.username": "lietotājvārds", "users.email": "e-pasta adrese", + "users.no-email": "(no email)", "users.ip": "IP", "users.postcount": "raksti", "users.reputation": "ranga punkti", diff --git a/public/language/lv/admin/menu.json b/public/language/lv/admin/menu.json index bbf5d97854..c97b9b0cb3 100644 --- a/public/language/lv/admin/menu.json +++ b/public/language/lv/admin/menu.json @@ -4,6 +4,7 @@ "dashboard/logins": "Logins", "dashboard/users": "Users", "dashboard/topics": "Topics", + "dashboard/searches": "Searches", "section-general": "Vispārējie", "section-manage": "Pārvaldīt", diff --git a/public/language/lv/admin/settings/email.json b/public/language/lv/admin/settings/email.json index 0a17adb1dd..a170931724 100644 --- a/public/language/lv/admin/settings/email.json +++ b/public/language/lv/admin/settings/email.json @@ -6,7 +6,7 @@ "from-help": "Vārds vai nosaukums, ko saņēmējs redzēs kā sūtītāju.", "smtp-transport": "SMTP transports", - "smtp-transport.enabled": "Izmantot atsevišķu e-pasta serveri, lai nosūtītu e-pastus", + "smtp-transport.enabled": "Enable SMTP Transport", "smtp-transport-help": "Izvēlies no labi zināmu pakalpojumu saraksta vai ievadi pielāgotu pakalpojumu.", "smtp-transport.service": "Atlasīt servisu", "smtp-transport.service-custom": "Pielāgotais serviss", @@ -37,6 +37,10 @@ "subscriptions.hour": "Kopsavilkumu nosūtīšanas stunda", "subscriptions.hour-help": "Ievadīt skaitli, kas norāda stundu, kurā nosūtītu e-pasta rakstu apkopojumu (piemēram, 0 nozīmē pusnakts, 17 nozīmē plkst.1700). Paturēt prātā, ka šī ir stunda servera laikā, un tā var neatbilst Tavam pulkstenim. Aptuvens servera laiks ir:
Nākamais ikdienas apkopojums tiks nosūtīts ", "notifications.remove-images": "Remove images from email notifications", + "require-email-address": "Require new users to specify an email address", + "require-email-address-warning": "By default, users can opt-out of entering an email address. Enabling this option means they have to enter an email address in order to proceed with registration. It does not ensure user will enter a real email address, nor even an address they own.", "include-unverified-emails": "Send emails to recipients who have not explicitly confirmed their emails", - "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws." -} \ No newline at end of file + "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws.", + "prompt": "Prompt users to enter or confirm their emails", + "prompt-help": "If a user does not have an email set, or their email is not confirmed, a warning will be shown on screen." +} diff --git a/public/language/lv/admin/settings/general.json b/public/language/lv/admin/settings/general.json index 753fb1261d..ea143d538f 100644 --- a/public/language/lv/admin/settings/general.json +++ b/public/language/lv/admin/settings/general.json @@ -3,9 +3,9 @@ "title": "Foruma nosaukums", "title.short": "Short Title", "title.short-placeholder": "If no short title is specified, the site title will be used", - "title.url": "URL", + "title.url": "Title Link URL", "title.url-placeholder": "Foruma virsrakta URL", - "title.url-help": "Kad virsraksts tiek noklikšķināts, nosūtīt uz šo adresi. Ja ir tukšs, nosūtīt uz foruma sākumlapu.", + "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "title.name": "Foruma nosaukums", "title.show-in-header": "Rādīt foruma virsrakstu galvenē", "browser-title": "Virsraksts pārlūkā", @@ -20,9 +20,9 @@ "logo.image": "Bilde", "logo.image-placeholder": "Ceļš uz logo, ko parādītu foruma galvenē", "logo.upload": "Augšupielādēt", - "logo.url": "URL", + "logo.url": "Logo Link URL", "logo.url-placeholder": "Foruma logo URL", - "logo.url-help": "Kad logo tiek noklikšķināts, nosūtīt uz šo adresi. Ja ir tukšs, nosūtīt uz foruma sākumlapu.", + "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "logo.alt-text": "Alternatīvais teksts", "log.alt-text-placeholder": "Alternatīvais teksts pieejamībai", "favicon": "Favorīta ikona", @@ -47,4 +47,4 @@ "undo-timeout": "Undo Timeout", "undo-timeout-help": "Some operations such as moving topics will allow for the moderator to undo their action within a certain timeframe. Set to 0 to disable undo completely.", "topic-tools": "Topic Tools" -} \ No newline at end of file +} diff --git a/public/language/lv/admin/settings/navigation.json b/public/language/lv/admin/settings/navigation.json index c6908195e0..b4327584a4 100644 --- a/public/language/lv/admin/settings/navigation.json +++ b/public/language/lv/admin/settings/navigation.json @@ -11,6 +11,8 @@ "properties": "Īpašības:", "groups": "Grupas:", "open-new-window": "Rādīt jaunā logā", + "dropdown": "Dropdown", + "dropdown-placeholder": "Place your dropdown menu items below, ie:
<li><a href="https://myforum.com">Link 1</a></li>", "btn.delete": "Izdzēst", "btn.disable": "Atspējot", @@ -20,4 +22,4 @@ "custom-route": "Pielāgotais ceļš", "core": "kodols", "plugin": "spraudnis" -} \ No newline at end of file +} diff --git a/public/language/lv/admin/settings/notifications.json b/public/language/lv/admin/settings/notifications.json index bfe6bd745d..d2f44f18d6 100644 --- a/public/language/lv/admin/settings/notifications.json +++ b/public/language/lv/admin/settings/notifications.json @@ -2,5 +2,6 @@ "notifications": "Paziņojumi", "welcome-notification": "Sveicināšanas paziņojums", "welcome-notification-link": "Sveicināšanas paziņojuma saite", - "welcome-notification-uid": "Sveicināšanas paziņojuma lietotājs (UID)" + "welcome-notification-uid": "Sveicināšanas paziņojuma lietotājs (UID)", + "post-queue-notification-uid": "Post Queue User (UID)" } \ No newline at end of file diff --git a/public/language/lv/admin/settings/post.json b/public/language/lv/admin/settings/post.json index d38fda1644..a6dfd4826c 100644 --- a/public/language/lv/admin/settings/post.json +++ b/public/language/lv/admin/settings/post.json @@ -40,6 +40,7 @@ "teaser.last-post": "Pēdējo – rādīt jaunāko rakstu, ieskaitot sākotnējo rakstu, ja atbildes nav", "teaser.last-reply": "Pēdējo – rādīt jaunāko atbildi, vai \"Nav atbildes\" tekstu, ja atbildes nav", "teaser.first": "Pirmais", + "showPostPreviewsOnHover": "Show a preview of posts when mouse overed", "unread": "Nelasītie raksti", "unread.cutoff": "Nelasīto rakstu vecumu robeža", "unread.min-track-last": "Minimālais rakstu skaits tematā pirms izseko pēdējo lasīto", @@ -56,6 +57,9 @@ "composer.show-help": "Rādīt \"palīdzība\" cilni", "composer.enable-plugin-help": "Atļaut spraudņiem pievienot saturu palīdzības cilnei", "composer.custom-help": "Pielāgotais palīdzības teksts", + "backlinks": "Backlinks", + "backlinks.enabled": "Enable topic backlinks", + "backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.", "ip-tracking": "IP adrešu pierakstīšana", "ip-tracking.each-post": "Pierakstīt katra raksta IP adresi", "enable-post-history": "Iespējot rakstu vēsturi" diff --git a/public/language/lv/admin/settings/user.json b/public/language/lv/admin/settings/user.json index 58b8fbfb5f..e70445837c 100644 --- a/public/language/lv/admin/settings/user.json +++ b/public/language/lv/admin/settings/user.json @@ -71,6 +71,7 @@ "digest-freq.off": "Nav", "digest-freq.daily": "Ik dienu", "digest-freq.weekly": "Ik nedēļu", + "digest-freq.biweekly": "Bi-Weekly", "digest-freq.monthly": "Ik mēnesi", "email-chat-notifs": "Sūtīt e-pastu, ja ierodas jauna saruna un es neesmu tiešsaistē", "email-post-notif": "Sūtīt e-pastu, kad kāds raksta tematā, kuru esmu abonējis", diff --git a/public/language/lv/error.json b/public/language/lv/error.json index b198b0273b..224e8d2b7b 100644 --- a/public/language/lv/error.json +++ b/public/language/lv/error.json @@ -34,8 +34,9 @@ "email-invited": "Email was already invited", "email-not-confirmed": "Posting in some categories or topics is enabled once your email is confirmed, please click here to send a confirmation email.", "email-not-confirmed-chat": "Nevar sarunāties, kamēr Tava e-pasta adrese netiek apstiprināta, lūdzu, noklikšķini, lai apstiprinātu savu e-pasta adresi.", - "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You won't be able to post or chat until your email is confirmed.", - "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery. Please click here to enter an email.", + "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You may not be able to post in some categories or chat until your email is confirmed.", + "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery, and may be necessary for chatting and posting in some categories. Please click here to enter an email.", + "user-doesnt-have-email": "User \"%1\" does not have an email set.", "email-confirm-failed": "Mēs nevarējām apstiprināt Tavu e-pasta adresi, lūdzu, vēlāk mēģini vēlreiz.", "confirm-email-already-sent": "Apstiprinājuma e-pasts ir jau nosūtīts, lūdzu, uzgaidi %1 minūti(-es), lai nosūtītu vēl vienu.", "sendmail-not-found": "Sendmail programmu nevarēja atrast, lūdzu, pārliecinies, ka lietotājs, kas darbojas ar NodeBB, ir to instalējis un izdarījis palaižamu.", @@ -103,6 +104,7 @@ "already-bookmarked": "Tu jau esi atzīmējis šo rakstu ar grāmatzīmi", "already-unbookmarked": "Tu jau esi noņēmis grāmatzīmi no šī raksta", "cant-ban-other-admins": "Nevar bloķēt citus administratorus!", + "cant-make-banned-users-admin": "You can't make banned users admin.", "cant-remove-last-admin": "Tu esi vienīgais administrators. Pievieno vēl vienu lietotāju kā administratoru, pirms noņemi sevi kā administratoru", "account-deletion-disabled": "Account deletion is disabled", "cant-delete-admin": "Noņemi administratora tiesības no šī konta, pirms mēģināt to izdzēst.", diff --git a/public/language/lv/modules.json b/public/language/lv/modules.json index f6340bc53a..f9869bdbc8 100644 --- a/public/language/lv/modules.json +++ b/public/language/lv/modules.json @@ -54,7 +54,7 @@ "composer.formatting.strikethrough": "Svītrotā rakstā", "composer.formatting.code": "Koda gabals", "composer.formatting.link": "Saite", - "composer.formatting.picture": "Bilde", + "composer.formatting.picture": "Image Link", "composer.upload-picture": "Augšupielādēt bildi", "composer.upload-file": "Augšupielādēt failu", "composer.zen_mode": "Zen režīms", diff --git a/public/language/lv/top.json b/public/language/lv/top.json new file mode 100644 index 0000000000..b8a05bfa5f --- /dev/null +++ b/public/language/lv/top.json @@ -0,0 +1,4 @@ +{ + "title": "Top", + "no_top_topics": "No top topics" +} \ No newline at end of file diff --git a/public/language/lv/topic.json b/public/language/lv/topic.json index 2d7037ae73..a3d9f55252 100644 --- a/public/language/lv/topic.json +++ b/public/language/lv/topic.json @@ -47,6 +47,7 @@ "restored-by": "Restored by", "moved-from-by": "Moved from %1 by", "queued-by": "Post queued for approval →", + "backlink": "Referenced by", "bookmark_instructions": "Noklikšķināt, lai atgrieztos pēdējā lasītā rakstā šajā pavedienā.", "flag-post": "Flag this post", "flag-user": "Flag this user", @@ -138,6 +139,7 @@ "composer.handle_placeholder": "Enter your name/handle here", "composer.discard": "Atmest", "composer.submit": "Publicēt", + "composer.additional-options": "Additional Options", "composer.schedule": "Schedule", "composer.replying_to": "Atbild %1", "composer.new_topic": "Izveidot jaunu tematu", @@ -158,6 +160,7 @@ "newest_to_oldest": "No jaunākā līdz vecākam", "most_votes": "Pēc visvairāk balsojumu", "most_posts": "Pēc visvairāk rakstu", + "most_views": "Most Views", "stale.title": "Tā vietā izveidot jaunu tematu?", "stale.warning": "Šis temats, uz kuru atbildi, ir diezgan sens. Vai vēlies izveidot jaunu tematu un atsaukties uz šo tematu?", "stale.create": "Izveidot jaunu tematu", diff --git a/public/language/lv/user.json b/public/language/lv/user.json index ec39476be5..e1de0b47e6 100644 --- a/public/language/lv/user.json +++ b/public/language/lv/user.json @@ -94,6 +94,7 @@ "digest_off": "Izslēgts", "digest_daily": "Ik dienas", "digest_weekly": "Ik nedēļas", + "digest_biweekly": "Bi-Weekly", "digest_monthly": "Ik mēnesi", "has_no_follower": "Šim lietotājam nav nevienu sekotāju :(", "follows_no_one": "Šis lietotājs neseko nevienam :(", diff --git a/public/language/ms/admin/advanced/events.json b/public/language/ms/admin/advanced/events.json index 56d9457971..b2c2033fb5 100644 --- a/public/language/ms/admin/advanced/events.json +++ b/public/language/ms/admin/advanced/events.json @@ -3,6 +3,7 @@ "no-events": "There are no events", "control-panel": "Events Control Panel", "delete-events": "Delete Events", + "confirm-delete-all-events": "Are you sure you want to delete all logged events?", "filters": "Filters", "filters-apply": "Apply Filters", "filter-type": "Event Type", diff --git a/public/language/ms/admin/dashboard.json b/public/language/ms/admin/dashboard.json index 0de31d4917..4d39626882 100644 --- a/public/language/ms/admin/dashboard.json +++ b/public/language/ms/admin/dashboard.json @@ -56,8 +56,8 @@ "active-users.total": "Total", "active-users.connections": "Connections", - "anonymous-registered-users": "Anonymous vs Registered Users", - "anonymous": "Anonymous", + "guest-registered-users": "Guest vs Registered Users", + "guest": "Guest", "registered": "Registered", "user-presence": "User Presence", @@ -68,6 +68,7 @@ "unread": "Unread", "high-presence-topics": "High Presence Topics", + "popular-searches": "Popular Searches", "graphs.page-views": "Page Views", "graphs.page-views-registered": "Page Views Registered", @@ -75,13 +76,14 @@ "graphs.page-views-bot": "Page Views Bot", "graphs.unique-visitors": "Unique Visitors", "graphs.registered-users": "Registered Users", - "graphs.anonymous-users": "Anonymous Users", + "graphs.guest-users": "Guest Users", "last-restarted-by": "Last restarted by", "no-users-browsing": "No users browsing", "back-to-dashboard": "Back to Dashboard", "details.no-users": "No users have joined within the selected timeframe", "details.no-topics": "No topics have been posted within the selected timeframe", + "details.no-searches": "No searches have been made yet", "details.no-logins": "No logins have been recorded within the selected timeframe", "details.logins-static": "NodeBB only saves session data for %1 days, and so this table below will only show the most recently active sessions", "details.logins-login-time": "Login Time" diff --git a/public/language/ms/admin/development/info.json b/public/language/ms/admin/development/info.json index 1003af1a5f..11202d9c3a 100644 --- a/public/language/ms/admin/development/info.json +++ b/public/language/ms/admin/development/info.json @@ -8,7 +8,11 @@ "nodejs": "nodejs", "online": "online", "git": "git", - "memory": "memory", + "process-memory": "process memory", + "system-memory": "system memory", + "used-memory-process": "Used memory by process", + "used-memory-os": "Used system memory", + "total-memory-os": "Total system memory", "load": "system load", "cpu-usage": "cpu usage", "uptime": "uptime", diff --git a/public/language/ms/admin/extend/rewards.json b/public/language/ms/admin/extend/rewards.json index 5383a90b33..df89d441a7 100644 --- a/public/language/ms/admin/extend/rewards.json +++ b/public/language/ms/admin/extend/rewards.json @@ -8,8 +8,6 @@ "delete": "Delete", "enable": "Enable", "disable": "Disable", - "control-panel": "Rewards Control", - "new-reward": "New Reward", "alert.delete-success": "Successfully deleted reward", "alert.no-inputs-found": "Illegal reward - no inputs found!", diff --git a/public/language/ms/admin/general/dashboard.json b/public/language/ms/admin/general/dashboard.json deleted file mode 100644 index 37ae537786..0000000000 --- a/public/language/ms/admin/general/dashboard.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "forum-traffic": "Forum Traffic", - "page-views": "Page Views", - "unique-visitors": "Unique Visitors", - "new-users": "New Users", - "posts": "Posts", - "topics": "Topics", - "page-views-seven": "Last 7 Days", - "page-views-thirty": "Last 30 Days", - "page-views-last-day": "Last 24 hours", - "page-views-custom": "Custom Date Range", - "page-views-custom-start": "Range Start", - "page-views-custom-end": "Range End", - "page-views-custom-help": "Enter a date range of page views you would like to view. If no date picker is available, the accepted format is YYYY-MM-DD", - "page-views-custom-error": "Please enter a valid date range in the format YYYY-MM-DD", - - "stats.yesterday": "Yesterday", - "stats.today": "Today", - "stats.last-week": "Last Week", - "stats.this-week": "This Week", - "stats.last-month": "Last Month", - "stats.this-month": "This Month", - "stats.all": "All Time", - - "updates": "Updates", - "running-version": "You are running NodeBB v%1.", - "keep-updated": "Always make sure that your NodeBB is up to date for the latest security patches and bug fixes.", - "up-to-date": "

You are up-to-date

", - "upgrade-available": "

A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-upgrade-available": "

This is an outdated pre-release version of NodeBB. A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-warning": "

This is a pre-release version of NodeBB. Unintended bugs may occur.

", - "running-in-development": "Forum is running in development mode. The forum may be open to potential vulnerabilities; please contact your system administrator.", - "latest-lookup-failed": "

Failed to look up latest available version of NodeBB

", - - "notices": "Notices", - "restart-not-required": "Restart not required", - "restart-required": "Restart required", - "search-plugin-installed": "Search Plugin installed", - "search-plugin-not-installed": "Search Plugin not installed", - "search-plugin-tooltip": "Install a search plugin from the plugin page in order to activate search functionality", - - "control-panel": "System Control", - "rebuild-and-restart": "Rebuild & Restart", - "restart": "Restart", - "restart-warning": "Rebuilding or Restarting your NodeBB will drop all existing connections for a few seconds.", - "restart-disabled": "Rebuilding and Restarting your NodeBB has been disabled as you do not seem to be running it via the appropriate daemon.", - "maintenance-mode": "Maintenance Mode", - "maintenance-mode-title": "Click here to set up maintenance mode for NodeBB", - "realtime-chart-updates": "Realtime Chart Updates", - - "active-users": "Active Users", - "active-users.users": "Users", - "active-users.guests": "Guests", - "active-users.total": "Total", - "active-users.connections": "Connections", - - "anonymous-registered-users": "Anonymous vs Registered Users", - "anonymous": "Anonymous", - "registered": "Registered", - - "user-presence": "User Presence", - "on-categories": "On categories list", - "reading-posts": "Reading posts", - "browsing-topics": "Browsing topics", - "recent": "Recent", - "unread": "Unread", - - "high-presence-topics": "High Presence Topics", - - "graphs.page-views": "Page Views", - "graphs.page-views-registered": "Page Views Registered", - "graphs.page-views-guest": "Page Views Guest", - "graphs.page-views-bot": "Page Views Bot", - "graphs.unique-visitors": "Unique Visitors", - "graphs.registered-users": "Registered Users", - "graphs.anonymous-users": "Anonymous Users", - "last-restarted-by": "Last restarted by", - "no-users-browsing": "No users browsing" -} diff --git a/public/language/ms/admin/general/homepage.json b/public/language/ms/admin/general/homepage.json deleted file mode 100644 index 7428d59eeb..0000000000 --- a/public/language/ms/admin/general/homepage.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "home-page": "Home Page", - "description": "Choose what page is shown when users navigate to the root URL of your forum.", - "home-page-route": "Home Page Route", - "custom-route": "Custom Route", - "allow-user-home-pages": "Allow User Home Pages", - "home-page-title": "Title of the home page (default \"Home\")" -} \ No newline at end of file diff --git a/public/language/ms/admin/general/languages.json b/public/language/ms/admin/general/languages.json deleted file mode 100644 index bdd57849b3..0000000000 --- a/public/language/ms/admin/general/languages.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "language-settings": "Language Settings", - "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language", - "auto-detect": "Auto Detect Language Setting for Guests" -} \ No newline at end of file diff --git a/public/language/ms/admin/general/navigation.json b/public/language/ms/admin/general/navigation.json deleted file mode 100644 index 13dd01aae7..0000000000 --- a/public/language/ms/admin/general/navigation.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "icon": "Icon:", - "change-icon": "change", - "route": "Route:", - "tooltip": "Tooltip:", - "text": "Text:", - "text-class": "Text Class: optional", - "class": "Class: optional", - "id": "ID: optional", - - "properties": "Properties:", - "groups": "Groups:", - "open-new-window": "Open in a new window", - - "btn.delete": "Delete", - "btn.disable": "Disable", - "btn.enable": "Enable", - - "available-menu-items": "Available Menu Items", - "custom-route": "Custom Route", - "core": "core", - "plugin": "plugin" -} \ No newline at end of file diff --git a/public/language/ms/admin/general/social.json b/public/language/ms/admin/general/social.json deleted file mode 100644 index 23aedfcfaa..0000000000 --- a/public/language/ms/admin/general/social.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "post-sharing": "Post Sharing", - "info-plugins-additional": "Plugins can add additional networks for sharing posts.", - "save-success": "Successfully saved Post Sharing Networks!" -} \ No newline at end of file diff --git a/public/language/ms/admin/general/sounds.json b/public/language/ms/admin/general/sounds.json deleted file mode 100644 index 95ccbde0f1..0000000000 --- a/public/language/ms/admin/general/sounds.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "notifications": "Notifications", - "chat-messages": "Chat Messages", - "play-sound": "Play", - "incoming-message": "Incoming Message", - "outgoing-message": "Outgoing Message", - "upload-new-sound": "Upload New Sound", - "saved": "Settings Saved" -} \ No newline at end of file diff --git a/public/language/ms/admin/manage/digest.json b/public/language/ms/admin/manage/digest.json index 8f3661698a..38c634d1f6 100644 --- a/public/language/ms/admin/manage/digest.json +++ b/public/language/ms/admin/manage/digest.json @@ -13,6 +13,7 @@ "resent-single": "Manual digest resend completed", "resent-day": "Daily digest resent", "resent-week": "Weekly digest resent", + "resent-biweek": "Bi-Weekly digest resent", "resent-month": "Monthly digest resent", "null": "Never", "manual-run": "Manual digest run:", diff --git a/public/language/ms/admin/manage/users.json b/public/language/ms/admin/manage/users.json index 38a065b954..2a3c0c4829 100644 --- a/public/language/ms/admin/manage/users.json +++ b/public/language/ms/admin/manage/users.json @@ -47,6 +47,7 @@ "users.uid": "uid", "users.username": "username", "users.email": "email", + "users.no-email": "(no email)", "users.ip": "IP", "users.postcount": "postcount", "users.reputation": "reputation", diff --git a/public/language/ms/admin/menu.json b/public/language/ms/admin/menu.json index 1c5ea73b4f..5b22fbeb36 100644 --- a/public/language/ms/admin/menu.json +++ b/public/language/ms/admin/menu.json @@ -4,6 +4,7 @@ "dashboard/logins": "Logins", "dashboard/users": "Users", "dashboard/topics": "Topics", + "dashboard/searches": "Searches", "section-general": "General", "section-manage": "Manage", diff --git a/public/language/ms/admin/settings/email.json b/public/language/ms/admin/settings/email.json index 9bdbde26a6..65f579d28c 100644 --- a/public/language/ms/admin/settings/email.json +++ b/public/language/ms/admin/settings/email.json @@ -6,7 +6,7 @@ "from-help": "The from name to display in the email.", "smtp-transport": "SMTP Transport", - "smtp-transport.enabled": "Use an external email server to send emails", + "smtp-transport.enabled": "Enable SMTP Transport", "smtp-transport-help": "You can select from a list of well-known services or enter a custom one.", "smtp-transport.service": "Select a service", "smtp-transport.service-custom": "Custom Service", @@ -37,6 +37,10 @@ "subscriptions.hour": "Digest Hour", "subscriptions.hour-help": "Please enter a number representing the hour to send scheduled email digests (e.g. 0 for midnight, 17 for 5:00pm). Keep in mind that this is the hour according to the server itself, and may not exactly match your system clock.
The approximate server time is:
The next daily digest is scheduled to be sent ", "notifications.remove-images": "Remove images from email notifications", + "require-email-address": "Require new users to specify an email address", + "require-email-address-warning": "By default, users can opt-out of entering an email address. Enabling this option means they have to enter an email address in order to proceed with registration. It does not ensure user will enter a real email address, nor even an address they own.", "include-unverified-emails": "Send emails to recipients who have not explicitly confirmed their emails", - "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws." -} \ No newline at end of file + "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws.", + "prompt": "Prompt users to enter or confirm their emails", + "prompt-help": "If a user does not have an email set, or their email is not confirmed, a warning will be shown on screen." +} diff --git a/public/language/ms/admin/settings/general.json b/public/language/ms/admin/settings/general.json index be7df90870..29b939861b 100644 --- a/public/language/ms/admin/settings/general.json +++ b/public/language/ms/admin/settings/general.json @@ -3,9 +3,9 @@ "title": "Site Title", "title.short": "Short Title", "title.short-placeholder": "If no short title is specified, the site title will be used", - "title.url": "URL", + "title.url": "Title Link URL", "title.url-placeholder": "The URL of the site title", - "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.", + "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "title.name": "Your Community Name", "title.show-in-header": "Show Site Title in Header", "browser-title": "Browser Title", @@ -20,9 +20,9 @@ "logo.image": "Image", "logo.image-placeholder": "Path to a logo to display on forum header", "logo.upload": "Upload", - "logo.url": "URL", + "logo.url": "Logo Link URL", "logo.url-placeholder": "The URL of the site logo", - "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.", + "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "logo.alt-text": "Alt Text", "log.alt-text-placeholder": "Alternative text for accessibility", "favicon": "Favicon", @@ -47,4 +47,4 @@ "undo-timeout": "Undo Timeout", "undo-timeout-help": "Some operations such as moving topics will allow for the moderator to undo their action within a certain timeframe. Set to 0 to disable undo completely.", "topic-tools": "Topic Tools" -} \ No newline at end of file +} diff --git a/public/language/ms/admin/settings/navigation.json b/public/language/ms/admin/settings/navigation.json index 13dd01aae7..7baca85096 100644 --- a/public/language/ms/admin/settings/navigation.json +++ b/public/language/ms/admin/settings/navigation.json @@ -11,6 +11,8 @@ "properties": "Properties:", "groups": "Groups:", "open-new-window": "Open in a new window", + "dropdown": "Dropdown", + "dropdown-placeholder": "Place your dropdown menu items below, ie:
<li><a href="https://myforum.com">Link 1</a></li>", "btn.delete": "Delete", "btn.disable": "Disable", @@ -20,4 +22,4 @@ "custom-route": "Custom Route", "core": "core", "plugin": "plugin" -} \ No newline at end of file +} diff --git a/public/language/ms/admin/settings/notifications.json b/public/language/ms/admin/settings/notifications.json index da6c9680a3..c6d8b928ce 100644 --- a/public/language/ms/admin/settings/notifications.json +++ b/public/language/ms/admin/settings/notifications.json @@ -2,5 +2,6 @@ "notifications": "Notifications", "welcome-notification": "Welcome Notification", "welcome-notification-link": "Welcome Notification Link", - "welcome-notification-uid": "Welcome Notification User (UID)" + "welcome-notification-uid": "Welcome Notification User (UID)", + "post-queue-notification-uid": "Post Queue User (UID)" } \ No newline at end of file diff --git a/public/language/ms/admin/settings/post.json b/public/language/ms/admin/settings/post.json index 27493aafbd..ab8245738c 100644 --- a/public/language/ms/admin/settings/post.json +++ b/public/language/ms/admin/settings/post.json @@ -40,6 +40,7 @@ "teaser.last-post": "Last – Show the latest post, including the original post, if no replies", "teaser.last-reply": "Last – Show the latest reply, or a \"No replies\" placeholder if no replies", "teaser.first": "First", + "showPostPreviewsOnHover": "Show a preview of posts when mouse overed", "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", @@ -56,6 +57,9 @@ "composer.show-help": "Show \"Help\" tab", "composer.enable-plugin-help": "Allow plugins to add content to the help tab", "composer.custom-help": "Custom Help Text", + "backlinks": "Backlinks", + "backlinks.enabled": "Enable topic backlinks", + "backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.", "ip-tracking": "IP Tracking", "ip-tracking.each-post": "Track IP Address for each post", "enable-post-history": "Enable Post History" diff --git a/public/language/ms/admin/settings/user.json b/public/language/ms/admin/settings/user.json index 48be13b75e..7923bf8cbe 100644 --- a/public/language/ms/admin/settings/user.json +++ b/public/language/ms/admin/settings/user.json @@ -71,6 +71,7 @@ "digest-freq.off": "Off", "digest-freq.daily": "Daily", "digest-freq.weekly": "Weekly", + "digest-freq.biweekly": "Bi-Weekly", "digest-freq.monthly": "Monthly", "email-chat-notifs": "Send an email if a new chat message arrives and I am not online", "email-post-notif": "Send an email when replies are made to topics I am subscribed to", diff --git a/public/language/ms/error.json b/public/language/ms/error.json index f436510c21..d935916ebb 100644 --- a/public/language/ms/error.json +++ b/public/language/ms/error.json @@ -34,8 +34,9 @@ "email-invited": "Email was already invited", "email-not-confirmed": "Posting in some categories or topics is enabled once your email is confirmed, please click here to send a confirmation email.", "email-not-confirmed-chat": "Anda tidak dibenarkan sembang sehingga emel disahkan, sila sahkan emel anda.", - "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You won't be able to post or chat until your email is confirmed.", - "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery. Please click here to enter an email.", + "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You may not be able to post in some categories or chat until your email is confirmed.", + "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery, and may be necessary for chatting and posting in some categories. Please click here to enter an email.", + "user-doesnt-have-email": "User \"%1\" does not have an email set.", "email-confirm-failed": "Kami tidak dapat memastikan emel anda, sila cuba lagi nanti", "confirm-email-already-sent": "Pengesahan emel telah dihantar, sila tunggu %1 minit() untuk menghantar yang baru.", "sendmail-not-found": "The sendmail executable could not be found, please ensure it is installed and executable by the user running NodeBB.", @@ -103,6 +104,7 @@ "already-bookmarked": "You have already bookmarked this post", "already-unbookmarked": "You have already unbookmarked this post", "cant-ban-other-admins": "Anda tidak boleh haramkan admin / pentadbir!", + "cant-make-banned-users-admin": "You can't make banned users admin.", "cant-remove-last-admin": "Anda satu-satunya pentadbir. Tambah pentadbir lain sebelum membuang diri anda sebagai pentadbir", "account-deletion-disabled": "Account deletion is disabled", "cant-delete-admin": "Remove administrator privileges from this account before attempting to delete it.", diff --git a/public/language/ms/modules.json b/public/language/ms/modules.json index b094428889..cfabba393c 100644 --- a/public/language/ms/modules.json +++ b/public/language/ms/modules.json @@ -54,7 +54,7 @@ "composer.formatting.strikethrough": "Strikethrough", "composer.formatting.code": "Code", "composer.formatting.link": "Link", - "composer.formatting.picture": "Picture", + "composer.formatting.picture": "Image Link", "composer.upload-picture": "Upload Image", "composer.upload-file": "Upload File", "composer.zen_mode": "Zen Mode", diff --git a/public/language/ms/top.json b/public/language/ms/top.json new file mode 100644 index 0000000000..b8a05bfa5f --- /dev/null +++ b/public/language/ms/top.json @@ -0,0 +1,4 @@ +{ + "title": "Top", + "no_top_topics": "No top topics" +} \ No newline at end of file diff --git a/public/language/ms/topic.json b/public/language/ms/topic.json index 210ced10e5..04fa232cda 100644 --- a/public/language/ms/topic.json +++ b/public/language/ms/topic.json @@ -47,6 +47,7 @@ "restored-by": "Restored by", "moved-from-by": "Moved from %1 by", "queued-by": "Post queued for approval →", + "backlink": "Referenced by", "bookmark_instructions": "Click here to return to the last read post in this thread.", "flag-post": "Flag this post", "flag-user": "Flag this user", @@ -138,6 +139,7 @@ "composer.handle_placeholder": "Enter your name/handle here", "composer.discard": "Abaikan", "composer.submit": "Hantar", + "composer.additional-options": "Additional Options", "composer.schedule": "Schedule", "composer.replying_to": "Balas ke %1", "composer.new_topic": "Topik baru", @@ -158,6 +160,7 @@ "newest_to_oldest": "Baru ke Lama", "most_votes": "Most Votes", "most_posts": "Most Posts", + "most_views": "Most Views", "stale.title": "Bukan topik baru?", "stale.warning": "Topik yang anda nak balas agak lapuk. Adakah anda ingin buka topik baru dan rujukkan topik ini dalam balasan anda?", "stale.create": "Buka topik baru", diff --git a/public/language/ms/user.json b/public/language/ms/user.json index 71a56eeca3..4e36a3091f 100644 --- a/public/language/ms/user.json +++ b/public/language/ms/user.json @@ -94,6 +94,7 @@ "digest_off": "Tutup", "digest_daily": "Harian", "digest_weekly": "Mingguan", + "digest_biweekly": "Bi-Weekly", "digest_monthly": "Bulanan", "has_no_follower": "Pengguna ini tiada pengikut :(", "follows_no_one": "Pengguna ini tidak mengikuti sesiapa :(", diff --git a/public/language/nb/admin/advanced/events.json b/public/language/nb/admin/advanced/events.json index 56d9457971..b2c2033fb5 100644 --- a/public/language/nb/admin/advanced/events.json +++ b/public/language/nb/admin/advanced/events.json @@ -3,6 +3,7 @@ "no-events": "There are no events", "control-panel": "Events Control Panel", "delete-events": "Delete Events", + "confirm-delete-all-events": "Are you sure you want to delete all logged events?", "filters": "Filters", "filters-apply": "Apply Filters", "filter-type": "Event Type", diff --git a/public/language/nb/admin/dashboard.json b/public/language/nb/admin/dashboard.json index 2f744a1182..112aa9389e 100644 --- a/public/language/nb/admin/dashboard.json +++ b/public/language/nb/admin/dashboard.json @@ -56,8 +56,8 @@ "active-users.total": "Total", "active-users.connections": "Connections", - "anonymous-registered-users": "Anonymous vs Registered Users", - "anonymous": "Anonymous", + "guest-registered-users": "Guest vs Registered Users", + "guest": "Guest", "registered": "Registered", "user-presence": "User Presence", @@ -68,6 +68,7 @@ "unread": "Unread", "high-presence-topics": "Høyt synlige tråder", + "popular-searches": "Popular Searches", "graphs.page-views": "Page Views", "graphs.page-views-registered": "Page Views Registered", @@ -75,13 +76,14 @@ "graphs.page-views-bot": "Page Views Bot", "graphs.unique-visitors": "Unique Visitors", "graphs.registered-users": "Registered Users", - "graphs.anonymous-users": "Anonymous Users", + "graphs.guest-users": "Guest Users", "last-restarted-by": "Last restarted by", "no-users-browsing": "No users browsing", "back-to-dashboard": "Back to Dashboard", "details.no-users": "No users have joined within the selected timeframe", "details.no-topics": "No topics have been posted within the selected timeframe", + "details.no-searches": "No searches have been made yet", "details.no-logins": "No logins have been recorded within the selected timeframe", "details.logins-static": "NodeBB only saves session data for %1 days, and so this table below will only show the most recently active sessions", "details.logins-login-time": "Login Time" diff --git a/public/language/nb/admin/development/info.json b/public/language/nb/admin/development/info.json index 1003af1a5f..11202d9c3a 100644 --- a/public/language/nb/admin/development/info.json +++ b/public/language/nb/admin/development/info.json @@ -8,7 +8,11 @@ "nodejs": "nodejs", "online": "online", "git": "git", - "memory": "memory", + "process-memory": "process memory", + "system-memory": "system memory", + "used-memory-process": "Used memory by process", + "used-memory-os": "Used system memory", + "total-memory-os": "Total system memory", "load": "system load", "cpu-usage": "cpu usage", "uptime": "uptime", diff --git a/public/language/nb/admin/extend/rewards.json b/public/language/nb/admin/extend/rewards.json index 5383a90b33..df89d441a7 100644 --- a/public/language/nb/admin/extend/rewards.json +++ b/public/language/nb/admin/extend/rewards.json @@ -8,8 +8,6 @@ "delete": "Delete", "enable": "Enable", "disable": "Disable", - "control-panel": "Rewards Control", - "new-reward": "New Reward", "alert.delete-success": "Successfully deleted reward", "alert.no-inputs-found": "Illegal reward - no inputs found!", diff --git a/public/language/nb/admin/general/dashboard.json b/public/language/nb/admin/general/dashboard.json deleted file mode 100644 index 37ae537786..0000000000 --- a/public/language/nb/admin/general/dashboard.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "forum-traffic": "Forum Traffic", - "page-views": "Page Views", - "unique-visitors": "Unique Visitors", - "new-users": "New Users", - "posts": "Posts", - "topics": "Topics", - "page-views-seven": "Last 7 Days", - "page-views-thirty": "Last 30 Days", - "page-views-last-day": "Last 24 hours", - "page-views-custom": "Custom Date Range", - "page-views-custom-start": "Range Start", - "page-views-custom-end": "Range End", - "page-views-custom-help": "Enter a date range of page views you would like to view. If no date picker is available, the accepted format is YYYY-MM-DD", - "page-views-custom-error": "Please enter a valid date range in the format YYYY-MM-DD", - - "stats.yesterday": "Yesterday", - "stats.today": "Today", - "stats.last-week": "Last Week", - "stats.this-week": "This Week", - "stats.last-month": "Last Month", - "stats.this-month": "This Month", - "stats.all": "All Time", - - "updates": "Updates", - "running-version": "You are running NodeBB v%1.", - "keep-updated": "Always make sure that your NodeBB is up to date for the latest security patches and bug fixes.", - "up-to-date": "

You are up-to-date

", - "upgrade-available": "

A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-upgrade-available": "

This is an outdated pre-release version of NodeBB. A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-warning": "

This is a pre-release version of NodeBB. Unintended bugs may occur.

", - "running-in-development": "Forum is running in development mode. The forum may be open to potential vulnerabilities; please contact your system administrator.", - "latest-lookup-failed": "

Failed to look up latest available version of NodeBB

", - - "notices": "Notices", - "restart-not-required": "Restart not required", - "restart-required": "Restart required", - "search-plugin-installed": "Search Plugin installed", - "search-plugin-not-installed": "Search Plugin not installed", - "search-plugin-tooltip": "Install a search plugin from the plugin page in order to activate search functionality", - - "control-panel": "System Control", - "rebuild-and-restart": "Rebuild & Restart", - "restart": "Restart", - "restart-warning": "Rebuilding or Restarting your NodeBB will drop all existing connections for a few seconds.", - "restart-disabled": "Rebuilding and Restarting your NodeBB has been disabled as you do not seem to be running it via the appropriate daemon.", - "maintenance-mode": "Maintenance Mode", - "maintenance-mode-title": "Click here to set up maintenance mode for NodeBB", - "realtime-chart-updates": "Realtime Chart Updates", - - "active-users": "Active Users", - "active-users.users": "Users", - "active-users.guests": "Guests", - "active-users.total": "Total", - "active-users.connections": "Connections", - - "anonymous-registered-users": "Anonymous vs Registered Users", - "anonymous": "Anonymous", - "registered": "Registered", - - "user-presence": "User Presence", - "on-categories": "On categories list", - "reading-posts": "Reading posts", - "browsing-topics": "Browsing topics", - "recent": "Recent", - "unread": "Unread", - - "high-presence-topics": "High Presence Topics", - - "graphs.page-views": "Page Views", - "graphs.page-views-registered": "Page Views Registered", - "graphs.page-views-guest": "Page Views Guest", - "graphs.page-views-bot": "Page Views Bot", - "graphs.unique-visitors": "Unique Visitors", - "graphs.registered-users": "Registered Users", - "graphs.anonymous-users": "Anonymous Users", - "last-restarted-by": "Last restarted by", - "no-users-browsing": "No users browsing" -} diff --git a/public/language/nb/admin/general/homepage.json b/public/language/nb/admin/general/homepage.json deleted file mode 100644 index 7428d59eeb..0000000000 --- a/public/language/nb/admin/general/homepage.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "home-page": "Home Page", - "description": "Choose what page is shown when users navigate to the root URL of your forum.", - "home-page-route": "Home Page Route", - "custom-route": "Custom Route", - "allow-user-home-pages": "Allow User Home Pages", - "home-page-title": "Title of the home page (default \"Home\")" -} \ No newline at end of file diff --git a/public/language/nb/admin/general/languages.json b/public/language/nb/admin/general/languages.json deleted file mode 100644 index bdd57849b3..0000000000 --- a/public/language/nb/admin/general/languages.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "language-settings": "Language Settings", - "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language", - "auto-detect": "Auto Detect Language Setting for Guests" -} \ No newline at end of file diff --git a/public/language/nb/admin/general/navigation.json b/public/language/nb/admin/general/navigation.json deleted file mode 100644 index 13dd01aae7..0000000000 --- a/public/language/nb/admin/general/navigation.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "icon": "Icon:", - "change-icon": "change", - "route": "Route:", - "tooltip": "Tooltip:", - "text": "Text:", - "text-class": "Text Class: optional", - "class": "Class: optional", - "id": "ID: optional", - - "properties": "Properties:", - "groups": "Groups:", - "open-new-window": "Open in a new window", - - "btn.delete": "Delete", - "btn.disable": "Disable", - "btn.enable": "Enable", - - "available-menu-items": "Available Menu Items", - "custom-route": "Custom Route", - "core": "core", - "plugin": "plugin" -} \ No newline at end of file diff --git a/public/language/nb/admin/general/social.json b/public/language/nb/admin/general/social.json deleted file mode 100644 index 23aedfcfaa..0000000000 --- a/public/language/nb/admin/general/social.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "post-sharing": "Post Sharing", - "info-plugins-additional": "Plugins can add additional networks for sharing posts.", - "save-success": "Successfully saved Post Sharing Networks!" -} \ No newline at end of file diff --git a/public/language/nb/admin/general/sounds.json b/public/language/nb/admin/general/sounds.json deleted file mode 100644 index 95ccbde0f1..0000000000 --- a/public/language/nb/admin/general/sounds.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "notifications": "Notifications", - "chat-messages": "Chat Messages", - "play-sound": "Play", - "incoming-message": "Incoming Message", - "outgoing-message": "Outgoing Message", - "upload-new-sound": "Upload New Sound", - "saved": "Settings Saved" -} \ No newline at end of file diff --git a/public/language/nb/admin/manage/digest.json b/public/language/nb/admin/manage/digest.json index 8f3661698a..38c634d1f6 100644 --- a/public/language/nb/admin/manage/digest.json +++ b/public/language/nb/admin/manage/digest.json @@ -13,6 +13,7 @@ "resent-single": "Manual digest resend completed", "resent-day": "Daily digest resent", "resent-week": "Weekly digest resent", + "resent-biweek": "Bi-Weekly digest resent", "resent-month": "Monthly digest resent", "null": "Never", "manual-run": "Manual digest run:", diff --git a/public/language/nb/admin/manage/users.json b/public/language/nb/admin/manage/users.json index 0bab898f7e..091c07f1e0 100644 --- a/public/language/nb/admin/manage/users.json +++ b/public/language/nb/admin/manage/users.json @@ -47,6 +47,7 @@ "users.uid": "uid", "users.username": "username", "users.email": "email", + "users.no-email": "(no email)", "users.ip": "IP", "users.postcount": "postcount", "users.reputation": "reputation", diff --git a/public/language/nb/admin/menu.json b/public/language/nb/admin/menu.json index 1c5ea73b4f..5b22fbeb36 100644 --- a/public/language/nb/admin/menu.json +++ b/public/language/nb/admin/menu.json @@ -4,6 +4,7 @@ "dashboard/logins": "Logins", "dashboard/users": "Users", "dashboard/topics": "Topics", + "dashboard/searches": "Searches", "section-general": "General", "section-manage": "Manage", diff --git a/public/language/nb/admin/settings/email.json b/public/language/nb/admin/settings/email.json index 9bdbde26a6..65f579d28c 100644 --- a/public/language/nb/admin/settings/email.json +++ b/public/language/nb/admin/settings/email.json @@ -6,7 +6,7 @@ "from-help": "The from name to display in the email.", "smtp-transport": "SMTP Transport", - "smtp-transport.enabled": "Use an external email server to send emails", + "smtp-transport.enabled": "Enable SMTP Transport", "smtp-transport-help": "You can select from a list of well-known services or enter a custom one.", "smtp-transport.service": "Select a service", "smtp-transport.service-custom": "Custom Service", @@ -37,6 +37,10 @@ "subscriptions.hour": "Digest Hour", "subscriptions.hour-help": "Please enter a number representing the hour to send scheduled email digests (e.g. 0 for midnight, 17 for 5:00pm). Keep in mind that this is the hour according to the server itself, and may not exactly match your system clock.
The approximate server time is:
The next daily digest is scheduled to be sent ", "notifications.remove-images": "Remove images from email notifications", + "require-email-address": "Require new users to specify an email address", + "require-email-address-warning": "By default, users can opt-out of entering an email address. Enabling this option means they have to enter an email address in order to proceed with registration. It does not ensure user will enter a real email address, nor even an address they own.", "include-unverified-emails": "Send emails to recipients who have not explicitly confirmed their emails", - "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws." -} \ No newline at end of file + "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws.", + "prompt": "Prompt users to enter or confirm their emails", + "prompt-help": "If a user does not have an email set, or their email is not confirmed, a warning will be shown on screen." +} diff --git a/public/language/nb/admin/settings/general.json b/public/language/nb/admin/settings/general.json index be7df90870..29b939861b 100644 --- a/public/language/nb/admin/settings/general.json +++ b/public/language/nb/admin/settings/general.json @@ -3,9 +3,9 @@ "title": "Site Title", "title.short": "Short Title", "title.short-placeholder": "If no short title is specified, the site title will be used", - "title.url": "URL", + "title.url": "Title Link URL", "title.url-placeholder": "The URL of the site title", - "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.", + "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "title.name": "Your Community Name", "title.show-in-header": "Show Site Title in Header", "browser-title": "Browser Title", @@ -20,9 +20,9 @@ "logo.image": "Image", "logo.image-placeholder": "Path to a logo to display on forum header", "logo.upload": "Upload", - "logo.url": "URL", + "logo.url": "Logo Link URL", "logo.url-placeholder": "The URL of the site logo", - "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.", + "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "logo.alt-text": "Alt Text", "log.alt-text-placeholder": "Alternative text for accessibility", "favicon": "Favicon", @@ -47,4 +47,4 @@ "undo-timeout": "Undo Timeout", "undo-timeout-help": "Some operations such as moving topics will allow for the moderator to undo their action within a certain timeframe. Set to 0 to disable undo completely.", "topic-tools": "Topic Tools" -} \ No newline at end of file +} diff --git a/public/language/nb/admin/settings/navigation.json b/public/language/nb/admin/settings/navigation.json index 13dd01aae7..7baca85096 100644 --- a/public/language/nb/admin/settings/navigation.json +++ b/public/language/nb/admin/settings/navigation.json @@ -11,6 +11,8 @@ "properties": "Properties:", "groups": "Groups:", "open-new-window": "Open in a new window", + "dropdown": "Dropdown", + "dropdown-placeholder": "Place your dropdown menu items below, ie:
<li><a href="https://myforum.com">Link 1</a></li>", "btn.delete": "Delete", "btn.disable": "Disable", @@ -20,4 +22,4 @@ "custom-route": "Custom Route", "core": "core", "plugin": "plugin" -} \ No newline at end of file +} diff --git a/public/language/nb/admin/settings/notifications.json b/public/language/nb/admin/settings/notifications.json index da6c9680a3..c6d8b928ce 100644 --- a/public/language/nb/admin/settings/notifications.json +++ b/public/language/nb/admin/settings/notifications.json @@ -2,5 +2,6 @@ "notifications": "Notifications", "welcome-notification": "Welcome Notification", "welcome-notification-link": "Welcome Notification Link", - "welcome-notification-uid": "Welcome Notification User (UID)" + "welcome-notification-uid": "Welcome Notification User (UID)", + "post-queue-notification-uid": "Post Queue User (UID)" } \ No newline at end of file diff --git a/public/language/nb/admin/settings/post.json b/public/language/nb/admin/settings/post.json index f2b9a5e674..5e2ca5e30a 100644 --- a/public/language/nb/admin/settings/post.json +++ b/public/language/nb/admin/settings/post.json @@ -40,6 +40,7 @@ "teaser.last-post": "Last – Show the latest post, including the original post, if no replies", "teaser.last-reply": "Last – Show the latest reply, or a \"No replies\" placeholder if no replies", "teaser.first": "First", + "showPostPreviewsOnHover": "Show a preview of posts when mouse overed", "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum antall innlegg i tråd før registrering av sist lest", @@ -56,6 +57,9 @@ "composer.show-help": "Show \"Help\" tab", "composer.enable-plugin-help": "Allow plugins to add content to the help tab", "composer.custom-help": "Custom Help Text", + "backlinks": "Backlinks", + "backlinks.enabled": "Enable topic backlinks", + "backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.", "ip-tracking": "IP Tracking", "ip-tracking.each-post": "Track IP Address for each post", "enable-post-history": "Enable Post History" diff --git a/public/language/nb/admin/settings/user.json b/public/language/nb/admin/settings/user.json index 37f0b6fdfe..f218395cb3 100644 --- a/public/language/nb/admin/settings/user.json +++ b/public/language/nb/admin/settings/user.json @@ -71,6 +71,7 @@ "digest-freq.off": "Av", "digest-freq.daily": "Daglig", "digest-freq.weekly": "Ukentlig", + "digest-freq.biweekly": "Bi-Weekly", "digest-freq.monthly": "Månedlig ", "email-chat-notifs": "Send en e-post hvis jeg mottar en ny chatt-melding om jeg ikke er online. ", "email-post-notif": "Send en e-post når det kommer svar på tråder jeg abbonnerer på", diff --git a/public/language/nb/error.json b/public/language/nb/error.json index b8ad0e3b4c..c1cafd3303 100644 --- a/public/language/nb/error.json +++ b/public/language/nb/error.json @@ -34,8 +34,9 @@ "email-invited": "E-post har allerede fått invitasjon", "email-not-confirmed": "Posting in some categories or topics is enabled once your email is confirmed, please click here to send a confirmation email.", "email-not-confirmed-chat": "Du kan ikke chatte før e-posten din er bekreftet, vennligst klikk her for å bekrefte e-postadressen.", - "email-not-confirmed-email-sent": "Din e-post har enda ikke blitt bekreftet, vennligst sjekk innboksen din for bekreftelsesmailen. Du har ikke tilgang til å skrive innlegg eller chatte før e-posten din er bekreftet.", - "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery. Please click here to enter an email.", + "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You may not be able to post in some categories or chat until your email is confirmed.", + "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery, and may be necessary for chatting and posting in some categories. Please click here to enter an email.", + "user-doesnt-have-email": "User \"%1\" does not have an email set.", "email-confirm-failed": "Vi kunne ikke bekrefte e-posten din, vennligst prøv igjen senere.", "confirm-email-already-sent": "E-post for bekreftelse er allerede sendt, vennligst vent %1 minutt(er) for å sende en til.", "sendmail-not-found": "Funksjonaliteten \"sendmail\" ble ikke funnet, vennligst sjekk at den er installert og kjørbar av brukeren som kjører NodeBB.", @@ -103,6 +104,7 @@ "already-bookmarked": "Du har allerede bokmerket dette innlegget", "already-unbookmarked": "Du har allerede fjernet bokmerket fra dette innlegget", "cant-ban-other-admins": "Du kan ikke utestenge andre administratorer!", + "cant-make-banned-users-admin": "You can't make banned users admin.", "cant-remove-last-admin": "Du er den eneste administratoren. Legg til en annen bruker som administrator før du fjerner deg selv.", "account-deletion-disabled": "Kontosletting er deaktivert", "cant-delete-admin": "Fjern administratorrettigheter fra denne kontoen før du prøver å slette den.", diff --git a/public/language/nb/modules.json b/public/language/nb/modules.json index 02bf041915..1e29146fed 100644 --- a/public/language/nb/modules.json +++ b/public/language/nb/modules.json @@ -54,7 +54,7 @@ "composer.formatting.strikethrough": "Strikethrough", "composer.formatting.code": "Code", "composer.formatting.link": "Link", - "composer.formatting.picture": "Picture", + "composer.formatting.picture": "Image Link", "composer.upload-picture": "Upload Image", "composer.upload-file": "Upload File", "composer.zen_mode": "Zen Mode", diff --git a/public/language/nb/top.json b/public/language/nb/top.json new file mode 100644 index 0000000000..b8a05bfa5f --- /dev/null +++ b/public/language/nb/top.json @@ -0,0 +1,4 @@ +{ + "title": "Top", + "no_top_topics": "No top topics" +} \ No newline at end of file diff --git a/public/language/nb/topic.json b/public/language/nb/topic.json index b3f5912948..0d65c4eb3d 100644 --- a/public/language/nb/topic.json +++ b/public/language/nb/topic.json @@ -47,6 +47,7 @@ "restored-by": "Gjenopprettet av", "moved-from-by": "Moved from %1 by", "queued-by": "Innlegg i kø for godkjenning & rarr;", + "backlink": "Referenced by", "bookmark_instructions": "Klikk her for å gå tilbake til det siste innlegget i denne tråden.", "flag-post": "Flagg denne posten", "flag-user": "Flagg denne brukeren", @@ -138,6 +139,7 @@ "composer.handle_placeholder": "Skriv inn navnet ditt / signatur her", "composer.discard": "Forkast", "composer.submit": "Send", + "composer.additional-options": "Additional Options", "composer.schedule": "Timeplan", "composer.replying_to": "Svarer i %1", "composer.new_topic": "Ny tråd", @@ -158,6 +160,7 @@ "newest_to_oldest": "Nyeste til eldste", "most_votes": "Flest stemmer", "most_posts": "Flest innlegg", + "most_views": "Most Views", "stale.title": "Lag en ny tråd i stedet?", "stale.warning": "Tråden du svarer på er ganske gammel. Vil du heller lage en ny tråd og refere til denne i den?", "stale.create": "Lag en ny tråd", diff --git a/public/language/nb/user.json b/public/language/nb/user.json index a55eaab12b..9ee05cd3d9 100644 --- a/public/language/nb/user.json +++ b/public/language/nb/user.json @@ -94,6 +94,7 @@ "digest_off": "Av", "digest_daily": "Daglig", "digest_weekly": "Ukentlig", + "digest_biweekly": "Bi-Weekly", "digest_monthly": "Månedlig", "has_no_follower": "Denne brukeren har ingen følgere :(", "follows_no_one": "Denne brukeren følger ingen :(", diff --git a/public/language/nl/admin/advanced/events.json b/public/language/nl/admin/advanced/events.json index 3ec8d7f418..79eb67d04a 100644 --- a/public/language/nl/admin/advanced/events.json +++ b/public/language/nl/admin/advanced/events.json @@ -3,6 +3,7 @@ "no-events": "Er zijn geen events", "control-panel": "Events Controlepaneel", "delete-events": "Verwijder eventen", + "confirm-delete-all-events": "Are you sure you want to delete all logged events?", "filters": "Filters", "filters-apply": "Apply Filters", "filter-type": "Event Type", diff --git a/public/language/nl/admin/dashboard.json b/public/language/nl/admin/dashboard.json index bb81461688..2acaa26a6a 100644 --- a/public/language/nl/admin/dashboard.json +++ b/public/language/nl/admin/dashboard.json @@ -56,8 +56,8 @@ "active-users.total": "Total", "active-users.connections": "Connecties", - "anonymous-registered-users": "Anonymous vs Registered Users", - "anonymous": "Anonymous", + "guest-registered-users": "Guest vs Registered Users", + "guest": "Guest", "registered": "Registered", "user-presence": "User Presence", @@ -68,6 +68,7 @@ "unread": "Unread", "high-presence-topics": "High Presence Topics", + "popular-searches": "Popular Searches", "graphs.page-views": "Page Views", "graphs.page-views-registered": "Page Views Registered", @@ -75,13 +76,14 @@ "graphs.page-views-bot": "Page Views Bot", "graphs.unique-visitors": "Unique Visitors", "graphs.registered-users": "Registered Users", - "graphs.anonymous-users": "Anonymous Users", + "graphs.guest-users": "Guest Users", "last-restarted-by": "Laatst herstart door", "no-users-browsing": "No users browsing", "back-to-dashboard": "Back to Dashboard", "details.no-users": "No users have joined within the selected timeframe", "details.no-topics": "No topics have been posted within the selected timeframe", + "details.no-searches": "No searches have been made yet", "details.no-logins": "No logins have been recorded within the selected timeframe", "details.logins-static": "NodeBB only saves session data for %1 days, and so this table below will only show the most recently active sessions", "details.logins-login-time": "Login Time" diff --git a/public/language/nl/admin/development/info.json b/public/language/nl/admin/development/info.json index 1003af1a5f..11202d9c3a 100644 --- a/public/language/nl/admin/development/info.json +++ b/public/language/nl/admin/development/info.json @@ -8,7 +8,11 @@ "nodejs": "nodejs", "online": "online", "git": "git", - "memory": "memory", + "process-memory": "process memory", + "system-memory": "system memory", + "used-memory-process": "Used memory by process", + "used-memory-os": "Used system memory", + "total-memory-os": "Total system memory", "load": "system load", "cpu-usage": "cpu usage", "uptime": "uptime", diff --git a/public/language/nl/admin/extend/rewards.json b/public/language/nl/admin/extend/rewards.json index 5383a90b33..df89d441a7 100644 --- a/public/language/nl/admin/extend/rewards.json +++ b/public/language/nl/admin/extend/rewards.json @@ -8,8 +8,6 @@ "delete": "Delete", "enable": "Enable", "disable": "Disable", - "control-panel": "Rewards Control", - "new-reward": "New Reward", "alert.delete-success": "Successfully deleted reward", "alert.no-inputs-found": "Illegal reward - no inputs found!", diff --git a/public/language/nl/admin/general/dashboard.json b/public/language/nl/admin/general/dashboard.json deleted file mode 100644 index b1b895aa9f..0000000000 --- a/public/language/nl/admin/general/dashboard.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "forum-traffic": "Forum Traffic", - "page-views": "Page Views", - "unique-visitors": "Unique Visitors", - "new-users": "New Users", - "posts": "Posts", - "topics": "Topics", - "page-views-seven": "Last 7 Days", - "page-views-thirty": "Last 30 Days", - "page-views-last-day": "Last 24 hours", - "page-views-custom": "Custom Date Range", - "page-views-custom-start": "Range Start", - "page-views-custom-end": "Range End", - "page-views-custom-help": "Enter a date range of page views you would like to view. If no date picker is available, the accepted format is YYYY-MM-DD", - "page-views-custom-error": "Please enter a valid date range in the format YYYY-MM-DD", - - "stats.yesterday": "Yesterday", - "stats.today": "Today", - "stats.last-week": "Last Week", - "stats.this-week": "This Week", - "stats.last-month": "Last Month", - "stats.this-month": "This Month", - "stats.all": "All Time", - - "updates": "Updates", - "running-version": "You are running NodeBB v%1.", - "keep-updated": "Always make sure that your NodeBB is up to date for the latest security patches and bug fixes.", - "up-to-date": "

You are up-to-date

", - "upgrade-available": "

A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-upgrade-available": "

This is an outdated pre-release version of NodeBB. A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-warning": "

This is a pre-release version of NodeBB. Unintended bugs may occur.

", - "running-in-development": "Forum is running in development mode. The forum may be open to potential vulnerabilities; please contact your system administrator.", - "latest-lookup-failed": "

Failed to look up latest available version of NodeBB

", - - "notices": "Notices", - "restart-not-required": "Restart not required", - "restart-required": "Restart required", - "search-plugin-installed": "Search Plugin installed", - "search-plugin-not-installed": "Search Plugin not installed", - "search-plugin-tooltip": "Install a search plugin from the plugin page in order to activate search functionality", - - "control-panel": "System Control", - "rebuild-and-restart": "Rebuild & Restart", - "restart": "Restart", - "restart-warning": "Rebuilding or Restarting your NodeBB will drop all existing connections for a few seconds.", - "restart-disabled": "Rebuilding and Restarting your NodeBB has been disabled as you do not seem to be running it via the appropriate daemon.", - "maintenance-mode": "Maintenance Mode", - "maintenance-mode-title": "Click here to set up maintenance mode for NodeBB", - "realtime-chart-updates": "Realtime Chart Updates", - - "active-users": "Active Users", - "active-users.users": "Users", - "active-users.guests": "Guests", - "active-users.total": "Total", - "active-users.connections": "Connecties", - - "anonymous-registered-users": "Anonymous vs Registered Users", - "anonymous": "Anonymous", - "registered": "Registered", - - "user-presence": "User Presence", - "on-categories": "On categories list", - "reading-posts": "Reading posts", - "browsing-topics": "Browsing topics", - "recent": "Recent", - "unread": "Unread", - - "high-presence-topics": "High Presence Topics", - - "graphs.page-views": "Page Views", - "graphs.page-views-registered": "Page Views Registered", - "graphs.page-views-guest": "Page Views Guest", - "graphs.page-views-bot": "Page Views Bot", - "graphs.unique-visitors": "Unique Visitors", - "graphs.registered-users": "Registered Users", - "graphs.anonymous-users": "Anonymous Users", - "last-restarted-by": "Laatst herstart door", - "no-users-browsing": "No users browsing" -} diff --git a/public/language/nl/admin/general/homepage.json b/public/language/nl/admin/general/homepage.json deleted file mode 100644 index 7428d59eeb..0000000000 --- a/public/language/nl/admin/general/homepage.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "home-page": "Home Page", - "description": "Choose what page is shown when users navigate to the root URL of your forum.", - "home-page-route": "Home Page Route", - "custom-route": "Custom Route", - "allow-user-home-pages": "Allow User Home Pages", - "home-page-title": "Title of the home page (default \"Home\")" -} \ No newline at end of file diff --git a/public/language/nl/admin/general/languages.json b/public/language/nl/admin/general/languages.json deleted file mode 100644 index 66f75f9248..0000000000 --- a/public/language/nl/admin/general/languages.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "language-settings": "Taalinstellingen", - "description": "De standaard taal bepaald de taalinstellingen voor alle gebruikers die uw forum bezoeken.
Individuele gebruikers kunnen deze standaard instellingen overschrijven op hun gebruikersinstellingen pagina.", - "default-language": "Standaard taal", - "auto-detect": "Detecteer de taalinstellingen voor Gasten automatisch" -} \ No newline at end of file diff --git a/public/language/nl/admin/general/navigation.json b/public/language/nl/admin/general/navigation.json deleted file mode 100644 index 13dd01aae7..0000000000 --- a/public/language/nl/admin/general/navigation.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "icon": "Icon:", - "change-icon": "change", - "route": "Route:", - "tooltip": "Tooltip:", - "text": "Text:", - "text-class": "Text Class: optional", - "class": "Class: optional", - "id": "ID: optional", - - "properties": "Properties:", - "groups": "Groups:", - "open-new-window": "Open in a new window", - - "btn.delete": "Delete", - "btn.disable": "Disable", - "btn.enable": "Enable", - - "available-menu-items": "Available Menu Items", - "custom-route": "Custom Route", - "core": "core", - "plugin": "plugin" -} \ No newline at end of file diff --git a/public/language/nl/admin/general/social.json b/public/language/nl/admin/general/social.json deleted file mode 100644 index 85ed00c695..0000000000 --- a/public/language/nl/admin/general/social.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "post-sharing": "Berichten delen", - "info-plugins-additional": "Plug-ins kunnen extra netwerken toevoegen om berichten mee te delen.", - "save-success": "Netwerken om berichten te delen is succesvol opgeslagen!" -} \ No newline at end of file diff --git a/public/language/nl/admin/general/sounds.json b/public/language/nl/admin/general/sounds.json deleted file mode 100644 index 95ccbde0f1..0000000000 --- a/public/language/nl/admin/general/sounds.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "notifications": "Notifications", - "chat-messages": "Chat Messages", - "play-sound": "Play", - "incoming-message": "Incoming Message", - "outgoing-message": "Outgoing Message", - "upload-new-sound": "Upload New Sound", - "saved": "Settings Saved" -} \ No newline at end of file diff --git a/public/language/nl/admin/manage/digest.json b/public/language/nl/admin/manage/digest.json index 8f3661698a..38c634d1f6 100644 --- a/public/language/nl/admin/manage/digest.json +++ b/public/language/nl/admin/manage/digest.json @@ -13,6 +13,7 @@ "resent-single": "Manual digest resend completed", "resent-day": "Daily digest resent", "resent-week": "Weekly digest resent", + "resent-biweek": "Bi-Weekly digest resent", "resent-month": "Monthly digest resent", "null": "Never", "manual-run": "Manual digest run:", diff --git a/public/language/nl/admin/manage/users.json b/public/language/nl/admin/manage/users.json index 151a177843..4002ccbe34 100644 --- a/public/language/nl/admin/manage/users.json +++ b/public/language/nl/admin/manage/users.json @@ -47,6 +47,7 @@ "users.uid": "uid", "users.username": "username", "users.email": "email", + "users.no-email": "(no email)", "users.ip": "IP", "users.postcount": "postcount", "users.reputation": "reputation", diff --git a/public/language/nl/admin/menu.json b/public/language/nl/admin/menu.json index 1c5ea73b4f..5b22fbeb36 100644 --- a/public/language/nl/admin/menu.json +++ b/public/language/nl/admin/menu.json @@ -4,6 +4,7 @@ "dashboard/logins": "Logins", "dashboard/users": "Users", "dashboard/topics": "Topics", + "dashboard/searches": "Searches", "section-general": "General", "section-manage": "Manage", diff --git a/public/language/nl/admin/settings/email.json b/public/language/nl/admin/settings/email.json index d4cb9b7f6d..3588f40700 100644 --- a/public/language/nl/admin/settings/email.json +++ b/public/language/nl/admin/settings/email.json @@ -6,7 +6,7 @@ "from-help": "De from naam om te tonen in de e-mail.", "smtp-transport": "SMTP Transport", - "smtp-transport.enabled": "Gebruik een externe mail server om mails te verzenden", + "smtp-transport.enabled": "Enable SMTP Transport", "smtp-transport-help": "Je kunt een bekende dienst uit de lijst selecteren of vul een aangepaste dienst in.", "smtp-transport.service": "Selecteer een dienst", "smtp-transport.service-custom": "Custom Service", @@ -37,6 +37,10 @@ "subscriptions.hour": "Uur van Digest", "subscriptions.hour-help": "Voer het nummer in dat het uur representeerd waarop scheduled email digests worden verstuurd (bv. 0 voor middernacht, 17 voor 17:00). Neem er s.v.p. notie van dat dit het uur is van de server self, dit hoeft niet exact overeen te komen met de klok van uw systeem.
De tijd op de server is bij benadering:
De volgende dagelijkse digest staat gepland om ", "notifications.remove-images": "Remove images from email notifications", + "require-email-address": "Require new users to specify an email address", + "require-email-address-warning": "By default, users can opt-out of entering an email address. Enabling this option means they have to enter an email address in order to proceed with registration. It does not ensure user will enter a real email address, nor even an address they own.", "include-unverified-emails": "Send emails to recipients who have not explicitly confirmed their emails", - "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws." -} \ No newline at end of file + "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws.", + "prompt": "Prompt users to enter or confirm their emails", + "prompt-help": "If a user does not have an email set, or their email is not confirmed, a warning will be shown on screen." +} diff --git a/public/language/nl/admin/settings/general.json b/public/language/nl/admin/settings/general.json index 80e5a388e3..fedadad45d 100644 --- a/public/language/nl/admin/settings/general.json +++ b/public/language/nl/admin/settings/general.json @@ -3,9 +3,9 @@ "title": "Site Titel", "title.short": "Short Title", "title.short-placeholder": "If no short title is specified, the site title will be used", - "title.url": "URL", + "title.url": "Title Link URL", "title.url-placeholder": "De URL van de site titel", - "title.url-help": "Wanneer de titel word aangeklikt stuur gebruikers naar dit adress. Is het leeg gelaten dan worden gebruikers naar de forum hoofdpagina gestuurd.", + "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "title.name": "Jouw Communiy Naam", "title.show-in-header": "Toon Site Titel in Header", "browser-title": "Browser Titel", @@ -20,9 +20,9 @@ "logo.image": "Afbeelding", "logo.image-placeholder": "Pad naar een logo om te tonen op de forum header", "logo.upload": "Uploaden", - "logo.url": "URL", + "logo.url": "Logo Link URL", "logo.url-placeholder": "De URL van de site logo", - "logo.url-help": "Wanneer het logo aangeklikt is stuur gebruikers naar dit adress. Is het leeg gelaten dan worden gebruikers naar de forum hoofdpagina gestuurd.", + "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "logo.alt-text": "Alt Tekst", "log.alt-text-placeholder": "Alternatieve tekst voor toegankelijkheid", "favicon": "Favoicon", @@ -47,4 +47,4 @@ "undo-timeout": "Undo Timeout", "undo-timeout-help": "Some operations such as moving topics will allow for the moderator to undo their action within a certain timeframe. Set to 0 to disable undo completely.", "topic-tools": "Topic Tools" -} \ No newline at end of file +} diff --git a/public/language/nl/admin/settings/navigation.json b/public/language/nl/admin/settings/navigation.json index 13dd01aae7..7baca85096 100644 --- a/public/language/nl/admin/settings/navigation.json +++ b/public/language/nl/admin/settings/navigation.json @@ -11,6 +11,8 @@ "properties": "Properties:", "groups": "Groups:", "open-new-window": "Open in a new window", + "dropdown": "Dropdown", + "dropdown-placeholder": "Place your dropdown menu items below, ie:
<li><a href="https://myforum.com">Link 1</a></li>", "btn.delete": "Delete", "btn.disable": "Disable", @@ -20,4 +22,4 @@ "custom-route": "Custom Route", "core": "core", "plugin": "plugin" -} \ No newline at end of file +} diff --git a/public/language/nl/admin/settings/notifications.json b/public/language/nl/admin/settings/notifications.json index da6c9680a3..c6d8b928ce 100644 --- a/public/language/nl/admin/settings/notifications.json +++ b/public/language/nl/admin/settings/notifications.json @@ -2,5 +2,6 @@ "notifications": "Notifications", "welcome-notification": "Welcome Notification", "welcome-notification-link": "Welcome Notification Link", - "welcome-notification-uid": "Welcome Notification User (UID)" + "welcome-notification-uid": "Welcome Notification User (UID)", + "post-queue-notification-uid": "Post Queue User (UID)" } \ No newline at end of file diff --git a/public/language/nl/admin/settings/post.json b/public/language/nl/admin/settings/post.json index 27493aafbd..ab8245738c 100644 --- a/public/language/nl/admin/settings/post.json +++ b/public/language/nl/admin/settings/post.json @@ -40,6 +40,7 @@ "teaser.last-post": "Last – Show the latest post, including the original post, if no replies", "teaser.last-reply": "Last – Show the latest reply, or a \"No replies\" placeholder if no replies", "teaser.first": "First", + "showPostPreviewsOnHover": "Show a preview of posts when mouse overed", "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", @@ -56,6 +57,9 @@ "composer.show-help": "Show \"Help\" tab", "composer.enable-plugin-help": "Allow plugins to add content to the help tab", "composer.custom-help": "Custom Help Text", + "backlinks": "Backlinks", + "backlinks.enabled": "Enable topic backlinks", + "backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.", "ip-tracking": "IP Tracking", "ip-tracking.each-post": "Track IP Address for each post", "enable-post-history": "Enable Post History" diff --git a/public/language/nl/admin/settings/user.json b/public/language/nl/admin/settings/user.json index 48be13b75e..7923bf8cbe 100644 --- a/public/language/nl/admin/settings/user.json +++ b/public/language/nl/admin/settings/user.json @@ -71,6 +71,7 @@ "digest-freq.off": "Off", "digest-freq.daily": "Daily", "digest-freq.weekly": "Weekly", + "digest-freq.biweekly": "Bi-Weekly", "digest-freq.monthly": "Monthly", "email-chat-notifs": "Send an email if a new chat message arrives and I am not online", "email-post-notif": "Send an email when replies are made to topics I am subscribed to", diff --git a/public/language/nl/error.json b/public/language/nl/error.json index 79b8e2d3d8..a13dbcc830 100644 --- a/public/language/nl/error.json +++ b/public/language/nl/error.json @@ -34,8 +34,9 @@ "email-invited": "E-mail was reeds uitgenodigd", "email-not-confirmed": "Posting in some categories or topics is enabled once your email is confirmed, please click here to send a confirmation email.", "email-not-confirmed-chat": "Het gebruik van chatfunctionaliteit is pas toegestaan na validatie van het e-mailadres.", - "email-not-confirmed-email-sent": "Je e-mailadres is nog niet bevestigd, kijk of je de bevestigingsmail hebt ontvangen. Tot je e-mailadres is bevestigd kun je geen berichten plaatsen of aan chats deelnemen.", - "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery. Please click here to enter an email.", + "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You may not be able to post in some categories or chat until your email is confirmed.", + "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery, and may be necessary for chatting and posting in some categories. Please click here to enter an email.", + "user-doesnt-have-email": "User \"%1\" does not have an email set.", "email-confirm-failed": "Helaas kon het e-mailadres niet bevestigd worden, probeer het later nog eens.", "confirm-email-already-sent": "Bevestigingsmail is zojuist al verzonden, wacht alsjeblieft %1 minuut (minuten) voordat je opnieuw een bevestigingsmail aanvraagt.", "sendmail-not-found": "De sendmail executable kon niet worden gevonden, zorg ervoor dat deze is geïnstalleerd en dat de gebruiker die NodeBB draait deze kan uitvoeren.", @@ -103,6 +104,7 @@ "already-bookmarked": "Je hebt dit bericht al als favoriet toegevoegd", "already-unbookmarked": "Je hebt dit bericht al verwijderd uit je favorieten", "cant-ban-other-admins": "Het is niet toegestaan andere beheerders te verbannen!", + "cant-make-banned-users-admin": "You can't make banned users admin.", "cant-remove-last-admin": "Je bent de enige beheerder. Stel eerst een andere gebruiker als beheerder in voordat je jezelf geen beheerder meer maakt.", "account-deletion-disabled": "Account deletion is disabled", "cant-delete-admin": "Verwijder administratieve rechten van dit account voordat je probeert deze te verwijderen", diff --git a/public/language/nl/modules.json b/public/language/nl/modules.json index 1411e6e330..0c310bdd0d 100644 --- a/public/language/nl/modules.json +++ b/public/language/nl/modules.json @@ -54,7 +54,7 @@ "composer.formatting.strikethrough": "Doorhalen", "composer.formatting.code": "Code", "composer.formatting.link": "Link", - "composer.formatting.picture": "Afbeelding", + "composer.formatting.picture": "Image Link", "composer.upload-picture": "Upload afbeelding", "composer.upload-file": "Upload bestand", "composer.zen_mode": "Zen-modus", diff --git a/public/language/nl/top.json b/public/language/nl/top.json new file mode 100644 index 0000000000..b8a05bfa5f --- /dev/null +++ b/public/language/nl/top.json @@ -0,0 +1,4 @@ +{ + "title": "Top", + "no_top_topics": "No top topics" +} \ No newline at end of file diff --git a/public/language/nl/topic.json b/public/language/nl/topic.json index 3097dcf09d..ab10ab53ba 100644 --- a/public/language/nl/topic.json +++ b/public/language/nl/topic.json @@ -47,6 +47,7 @@ "restored-by": "Restored by", "moved-from-by": "Moved from %1 by", "queued-by": "Post queued for approval →", + "backlink": "Referenced by", "bookmark_instructions": "Klik hier om terug te keren naar de laatst gelezen post in deze thread.", "flag-post": "Rapporteer dit bericht", "flag-user": "Rapporteer deze gebruiker", @@ -138,6 +139,7 @@ "composer.handle_placeholder": "Voer je naam/pseudoniem hier in", "composer.discard": "Annuleren", "composer.submit": "Verzenden", + "composer.additional-options": "Additional Options", "composer.schedule": "Schedule", "composer.replying_to": "Reactie op %1", "composer.new_topic": "Nieuw onderwerp", @@ -158,6 +160,7 @@ "newest_to_oldest": "Meest recente berichten bovenaan", "most_votes": "Meeste stemmen", "most_posts": "Meeste berichten", + "most_views": "Most Views", "stale.title": "Een nieuw onderwerp maken in de plaats?", "stale.warning": "Het onderwerp waar je op antwoord is vrij oud. Zou je graag een nieuw onderwerp maken met een referentie naar dit onderwerp in je antwoord?", "stale.create": "Maak een nieuw onderwerp", diff --git a/public/language/nl/user.json b/public/language/nl/user.json index 8c23f20862..44a374fca4 100644 --- a/public/language/nl/user.json +++ b/public/language/nl/user.json @@ -94,6 +94,7 @@ "digest_off": "Uit", "digest_daily": "Dagelijks", "digest_weekly": "Wekelijks", + "digest_biweekly": "Bi-Weekly", "digest_monthly": "Maandelijks", "has_no_follower": "Deze gebruiker heeft geen volgers :(", "follows_no_one": "Deze gebruiker volgt niemand :(", diff --git a/public/language/pl/admin/admin.json b/public/language/pl/admin/admin.json index af8aa5e642..5b36c0273d 100644 --- a/public/language/pl/admin/admin.json +++ b/public/language/pl/admin/admin.json @@ -1,5 +1,5 @@ { - "alert.confirm-rebuild-and-restart": "Jesteś pewny, że chciałbyś przebudować oraz zrestartować NodeBB?", + "alert.confirm-rebuild-and-restart": "Czy na pewno chcesz przebudować oraz zrestartować NodeBB?", "alert.confirm-restart": "Czy na pewno chcesz zrestartować NodeBB?", "acp-title": "%1 | Panel administracyjny NodeBB", diff --git a/public/language/pl/admin/advanced/events.json b/public/language/pl/admin/advanced/events.json index 02df129699..9a61d40c38 100644 --- a/public/language/pl/admin/advanced/events.json +++ b/public/language/pl/admin/advanced/events.json @@ -3,6 +3,7 @@ "no-events": "Brak zdarzeń", "control-panel": "Panel zdarzeń", "delete-events": "Usuń zdarzenia", + "confirm-delete-all-events": "Czy na pewno chcesz usunąć wszystkie zapisane zdarzenia?", "filters": "Filtry", "filters-apply": "Zatwierdź filtry", "filter-type": "Typ zdarzenia", diff --git a/public/language/pl/admin/dashboard.json b/public/language/pl/admin/dashboard.json index d6c1c03b8a..ba2aed1b4d 100644 --- a/public/language/pl/admin/dashboard.json +++ b/public/language/pl/admin/dashboard.json @@ -2,7 +2,7 @@ "forum-traffic": "Ruch na forum", "page-views": "Wyświetlenia strony", "unique-visitors": "Unikalni goście", - "logins": "Logins", + "logins": "Loginy", "new-users": "Nowi użytkownicy", "posts": "Posty", "topics": "Tematy", @@ -30,7 +30,7 @@ "upgrade-available": "

Została wydana nowa wersja (v%1). Rozważ aktualizację NodeBB.

", "prerelease-upgrade-available": "

To jest nieaktualna przedpremierowa wersja NodeBB. Została wydana nowa wersja (v%1). Rozważ aktualizację NodeBB.

", "prerelease-warning": "

To jest przedpremierowa wersja NodeBB. Mogą występować błędy.

", - "fallback-emailer-not-found": "Fallback emailer not found!", + "fallback-emailer-not-found": "Nie znaleziono kopii maila!", "running-in-development": "Forum działa w trybie programistycznym i może być podatne na zagrożenia. Skontaktuj się z administratorem.", "latest-lookup-failed": "

Nie udało się odnaleźć najnowszej wersji NodeBB

", @@ -56,8 +56,8 @@ "active-users.total": "Łącznie", "active-users.connections": "Połączenia", - "anonymous-registered-users": "Użytkownicy anonimowi vs zarejestrowani", - "anonymous": "Anonimowi", + "guest-registered-users": "Guest vs Registered Users", + "guest": "Guest", "registered": "Zarejestrowani", "user-presence": "Obecność użytkownika", @@ -68,6 +68,7 @@ "unread": "Nieprzeczytane", "high-presence-topics": "Popularne tematy", + "popular-searches": "Popular Searches", "graphs.page-views": "Wyświetlenia strony", "graphs.page-views-registered": "Wyświetlenia użytkowników", @@ -75,14 +76,15 @@ "graphs.page-views-bot": "Wyświetlenia botów", "graphs.unique-visitors": "Unikalni użytkownicy", "graphs.registered-users": "Zarejestrowani użytkownicy", - "graphs.anonymous-users": "Anonimowi użytkownicy", + "graphs.guest-users": "Guest Users", "last-restarted-by": "Ostatnio restartowany przez", "no-users-browsing": "Brak przeglądających", - "back-to-dashboard": "Back to Dashboard", - "details.no-users": "No users have joined within the selected timeframe", - "details.no-topics": "No topics have been posted within the selected timeframe", - "details.no-logins": "No logins have been recorded within the selected timeframe", - "details.logins-static": "NodeBB only saves session data for %1 days, and so this table below will only show the most recently active sessions", - "details.logins-login-time": "Login Time" + "back-to-dashboard": "Powrót do Dashboardu", + "details.no-users": "Żaden użytkownik nie dołączył w wybranym okresie", + "details.no-topics": "Żadne tematy nie zostały opublikowane w wybranym okresie", + "details.no-searches": "No searches have been made yet", + "details.no-logins": "Żadne logi nie zostały zapisane w wybranym okresie", + "details.logins-static": "NodeBB zapisuje dane sesji tylko na %1 dzień, dlatego tabela poniżej zawierać będzie tylko ostatnią aktywną sesję", + "details.logins-login-time": "Czas logowania" } diff --git a/public/language/pl/admin/development/info.json b/public/language/pl/admin/development/info.json index 117a8c0c56..3cfe72b22d 100644 --- a/public/language/pl/admin/development/info.json +++ b/public/language/pl/admin/development/info.json @@ -8,7 +8,11 @@ "nodejs": "nodejs", "online": "dostępny", "git": "git", - "memory": "pamięć", + "process-memory": "process memory", + "system-memory": "system memory", + "used-memory-process": "Used memory by process", + "used-memory-os": "Used system memory", + "total-memory-os": "Total system memory", "load": "obciążenie systemu", "cpu-usage": "użycie procesora", "uptime": "czas działania", diff --git a/public/language/pl/admin/extend/plugins.json b/public/language/pl/admin/extend/plugins.json index de4b5ad0a9..fa3c3ebefc 100644 --- a/public/language/pl/admin/extend/plugins.json +++ b/public/language/pl/admin/extend/plugins.json @@ -39,7 +39,7 @@ "alert.upgraded": "Wtyczka zaktualizowana", "alert.installed": "Wtyczka zainstalowana", "alert.uninstalled": "Wtyczka odinstalowana", - "alert.activate-success": "Please rebuild and restart your NodeBB to fully activate this plugin", + "alert.activate-success": "Proszę odbudować i zrestartować NodeBB aby w pełni aktywować ten plugin", "alert.deactivate-success": "Wtyczka została dezaktywowana", "alert.upgrade-success": "Przebuduj i zrestartuj NodeBB, by w pełni zaktualizować tę wtyczkę.", "alert.install-success": "Wtyczka została zainstalowana, teraz należy ją aktywować.", diff --git a/public/language/pl/admin/extend/rewards.json b/public/language/pl/admin/extend/rewards.json index 41694a7ed8..afe04e15af 100644 --- a/public/language/pl/admin/extend/rewards.json +++ b/public/language/pl/admin/extend/rewards.json @@ -8,8 +8,6 @@ "delete": "Usuń", "enable": "Włącz", "disable": "Wyłącz", - "control-panel": "Ustawienia nagród", - "new-reward": "Nowa nagroda", "alert.delete-success": "Pomyślnie usunięto nagrodę", "alert.no-inputs-found": "Niepoprawnie dodana nagroda ", diff --git a/public/language/pl/admin/general/dashboard.json b/public/language/pl/admin/general/dashboard.json deleted file mode 100644 index 2cb8462085..0000000000 --- a/public/language/pl/admin/general/dashboard.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "forum-traffic": "Ruch na forum", - "page-views": "Wyświetlenia strony", - "unique-visitors": "Unikalni goście", - "new-users": "Nowi użytkownicy", - "posts": "Posty", - "topics": "Tematy", - "page-views-seven": "Ostatnie 7 dni", - "page-views-thirty": "Ostatnie 30 dni", - "page-views-last-day": "Ostatnie 24 godziny", - "page-views-custom": "Własny zakres dat", - "page-views-custom-start": "Początek zakresu", - "page-views-custom-end": "Koniec zakresu", - "page-views-custom-help": "Wprowadź zakres dat dla wyświetleń strony, które chcesz zobaczyć. Jeśli nie ma możliwości wyboru daty, obowiązuje format RRRR-MM-DD", - "page-views-custom-error": "Proszę wprowadzić poprawny zakres dat w formacie YYYY-MM-DD", - - "stats.yesterday": "Wczoraj", - "stats.today": "Dzisiaj", - "stats.last-week": "Poprzedni tydzień", - "stats.this-week": "Obecny tydzień", - "stats.last-month": "Poprzedni miesiąc", - "stats.this-month": "Obecny miesiąc", - "stats.all": "Cały czas", - - "updates": "Aktualizacje", - "running-version": "Forum działa dzięki NodeBB v%1", - "keep-updated": "Aktualizuj NodeBB regularnie, by zwiększać bezpieczeństwa i wprowadzać poprawki.", - "up-to-date": "

NodeBB jest aktualny

", - "upgrade-available": "

Została wydana nowa wersja (v%1). Rozważ aktualizację NodeBB.

", - "prerelease-upgrade-available": "

To jest nieaktualna przedpremierowa wersja NodeBB. Została wydana nowa wersja (v%1). Rozważ aktualizację NodeBB.

", - "prerelease-warning": "

To jest przedpremierowa wersja NodeBB. Mogą występować błędy.

", - "running-in-development": "Forum działa w trybie programistycznym i może być podatne na zagrożenia. Skontaktuj się z administratorem.", - "latest-lookup-failed": "

Nie udało się odnaleźć najnowszej wersji NodeBB

", - - "notices": "Powiadomienia", - "restart-not-required": "Restart nie jest wymagany", - "restart-required": "Wymagany restart", - "search-plugin-installed": "Wyszukiwarka jest zainstalowana", - "search-plugin-not-installed": "Wyszukiwarka nie jest zainstalowana", - "search-plugin-tooltip": "Zainstaluj wyszukiwarkę ze strony wtyczek, by aktywować funkcję wyszukiwania", - - "control-panel": "Zarządzanie systemem", - "rebuild-and-restart": "Przebudowa i restart", - "restart": "Restart", - "restart-warning": "Przebudowa i restart NodeBB zerwie na kilka sekund wszystkie aktywne połączenia. ", - "restart-disabled": "Zablokowano przebudowę i restart, ponieważ wygląda na to, że nie uruchamiasz NodeBB poprzez właściwy serwis.", - "maintenance-mode": "Tryb serwisowy", - "maintenance-mode-title": "Kliknij tutaj, by skonfigurować tryb konserwacji dla NodeBB", - "realtime-chart-updates": "Wykresy aktualizowane na żywo", - - "active-users": "Aktywni użytkownicy", - "active-users.users": "Użytkownicy", - "active-users.guests": "Goście", - "active-users.total": "Łącznie", - "active-users.connections": "Połączenia", - - "anonymous-registered-users": "Użytkownicy anonimowi vs zarejestrowani", - "anonymous": "Anonimowi", - "registered": "Zarejestrowani", - - "user-presence": "Obecność użytkownika", - "on-categories": "Na liście kategorii", - "reading-posts": "Czytanie postów", - "browsing-topics": "Przeglądanie tematów", - "recent": "Ostatnie", - "unread": "Nieprzeczytane", - - "high-presence-topics": "Popularne tematy", - - "graphs.page-views": "Wyświetlenia strony", - "graphs.page-views-registered": "Wyświetlenia użytkowników", - "graphs.page-views-guest": "Wyświetlenia gości", - "graphs.page-views-bot": "Wyświetlenia botów", - "graphs.unique-visitors": "Unikalni użytkownicy", - "graphs.registered-users": "Zarejestrowani użytkownicy", - "graphs.anonymous-users": "Anonimowi użytkownicy", - "last-restarted-by": "Ostatnio restartowany przez", - "no-users-browsing": "Brak przeglądających" -} diff --git a/public/language/pl/admin/general/homepage.json b/public/language/pl/admin/general/homepage.json deleted file mode 100644 index 0fc4160302..0000000000 --- a/public/language/pl/admin/general/homepage.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "home-page": "Strona główna", - "description": "Wybierz stronę startową dla forum", - "home-page-route": "Ścieżka strony głównej", - "custom-route": "Niestandardowa Ścieżka", - "allow-user-home-pages": "Zezwalaj na strony startowe użytkowników", - "home-page-title": "Tytuł strony głównej (domyślnie: „Strona Główna”)" -} \ No newline at end of file diff --git a/public/language/pl/admin/general/languages.json b/public/language/pl/admin/general/languages.json deleted file mode 100644 index 6fa0554e20..0000000000 --- a/public/language/pl/admin/general/languages.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "language-settings": "Ustawienia językowe", - "description": "Domyślny język określa ustawienia języka dla wszystkich użytkowników, którzy odwiedzają forum.
Użytkownicy mogą zmienić domyślny język w ustawieniach swojego konta.", - "default-language": "Domyślny język", - "auto-detect": "Automatycznie wykrywaj język gości" -} \ No newline at end of file diff --git a/public/language/pl/admin/general/navigation.json b/public/language/pl/admin/general/navigation.json deleted file mode 100644 index 42a9e7c98c..0000000000 --- a/public/language/pl/admin/general/navigation.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "icon": "Ikona:", - "change-icon": "zmień", - "route": "Ścieżka:", - "tooltip": "Tooltip:", - "text": "Tekst:", - "text-class": "Klasa tekstu opcjonalnie", - "class": "Klasa: opcjonalnie", - "id": "ID: opcjonalnie", - - "properties": "Ustawienia:", - "groups": "Grupy:", - "open-new-window": "Otwórz w nowym oknie", - - "btn.delete": "Usuń", - "btn.disable": "Wyłącz", - "btn.enable": "Włącz", - - "available-menu-items": "Dostępne obiekty menu", - "custom-route": "Niestandardowa ścieżka", - "core": "system", - "plugin": "wtyczka" -} \ No newline at end of file diff --git a/public/language/pl/admin/general/social.json b/public/language/pl/admin/general/social.json deleted file mode 100644 index e75834e540..0000000000 --- a/public/language/pl/admin/general/social.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "post-sharing": "Udostępnianie postów", - "info-plugins-additional": "Wtyczki mogą dodać dodatkowe platformy do udostępniania postów", - "save-success": "Pomyślnie zapisano!" -} \ No newline at end of file diff --git a/public/language/pl/admin/general/sounds.json b/public/language/pl/admin/general/sounds.json deleted file mode 100644 index 1ab957ffa3..0000000000 --- a/public/language/pl/admin/general/sounds.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "notifications": "Powiadomienia", - "chat-messages": "Wiadomości czatu", - "play-sound": "Odtwórz", - "incoming-message": "Przychodzące wiadomości", - "outgoing-message": "Wychodzące wiadomości", - "upload-new-sound": "Prześlij nowy dźwięk", - "saved": "Ustawienia zapisane" -} \ No newline at end of file diff --git a/public/language/pl/admin/manage/categories.json b/public/language/pl/admin/manage/categories.json index 924fa27e13..70dbbeba4b 100644 --- a/public/language/pl/admin/manage/categories.json +++ b/public/language/pl/admin/manage/categories.json @@ -10,16 +10,16 @@ "custom-class": "Niestandardowa klasa", "num-recent-replies": "# z ostatnich odpowiedzi", "ext-link": "Zewnętrzny odnośnik", - "subcategories-per-page": "Subcategories per page", + "subcategories-per-page": "Subkategorie na stronę", "is-section": "Traktuj tę kategorię jako sekcję", - "post-queue": "Post queue", + "post-queue": "Kolejka postów", "tag-whitelist": "Otaguj białą listę", "upload-image": "Prześlij obrazek", "delete-image": "Usuń", "category-image": "Obrazek kategorii", "parent-category": "Kategoria nadrzędna", "optional-parent-category": "(Opcjonalne) Kategoria nadrzędna", - "top-level": "Top Level", + "top-level": "Najwyższy poziom", "parent-category-none": "(Żadna)", "copy-parent": "Skopiuj od rodzica", "copy-settings": "Skopiuj ustawienia z", @@ -32,8 +32,8 @@ "edit": "Edytuj", "analytics": "Analityka", "view-category": "Wyświetl kategorię", - "set-order": "Set order", - "set-order-help": "Setting the order of the category will move this category to that order and update the order of other categories as necessary. Minimum order is 1 which puts the category at the top.", + "set-order": "Ustaw kolejnośc", + "set-order-help": "Ustawienie kolejności kategorii przesunie tę kategorię w żądanej kolejności i zaktualizuje kolejność zgodnie z potrzebą. Minimalna kolejność to 1, co umieści daną kategorię na górze.", "select-category": "Wybierz kategorię", "set-parent-category": "Ustaw nadrzędną kategorie", @@ -50,8 +50,8 @@ "privileges.no-users": "Brak uprawnień specyficznych dla użytkowników w tej kategorii", "privileges.section-group": "Grupa", "privileges.group-private": "Ta grupa jest prywatna", - "privileges.inheritance-exception": "This group does not inherit privileges from registered-users group", - "privileges.banned-user-inheritance": "Banned users inherit privileges from banned-users group", + "privileges.inheritance-exception": "W tej grupie nie obowiązują przywileje z grup dla zarejestrowanych użytkowników", + "privileges.banned-user-inheritance": "Przywileje z grup zablokowanych użytkowników obowiązują zablokowanych użytkowników", "privileges.search-group": "Dodaj grupę", "privileges.copy-to-children": "Skopiuj do podrzędnej", "privileges.copy-from-category": "Skopiuj z kategorii", diff --git a/public/language/pl/admin/manage/digest.json b/public/language/pl/admin/manage/digest.json index 26b1c8a39c..7ac8122adc 100644 --- a/public/language/pl/admin/manage/digest.json +++ b/public/language/pl/admin/manage/digest.json @@ -13,6 +13,7 @@ "resent-single": "Ręczne wysyłanie podsumowania zakończone", "resent-day": "Codzienne podsumowanie", "resent-week": "Tygodniowe podsumowanie", + "resent-biweek": "Bi-Weekly digest resent", "resent-month": "Miesięczne podsumowanie", "null": "Nigdy", "manual-run": "Włącz ręcznie podsumowania", diff --git a/public/language/pl/admin/manage/users.json b/public/language/pl/admin/manage/users.json index 85158e649a..783de0e01c 100644 --- a/public/language/pl/admin/manage/users.json +++ b/public/language/pl/admin/manage/users.json @@ -47,6 +47,7 @@ "users.uid": "uid", "users.username": "nazwa użytkownika", "users.email": "adres e-mail", + "users.no-email": "(no email)", "users.ip": "IP", "users.postcount": "liczba postów", "users.reputation": "reputacja", diff --git a/public/language/pl/admin/menu.json b/public/language/pl/admin/menu.json index 8c38851bf7..55223f5f0e 100644 --- a/public/language/pl/admin/menu.json +++ b/public/language/pl/admin/menu.json @@ -4,6 +4,7 @@ "dashboard/logins": "Logins", "dashboard/users": "Users", "dashboard/topics": "Topics", + "dashboard/searches": "Searches", "section-general": "Ogólne", "section-manage": "Zarządzanie", diff --git a/public/language/pl/admin/settings/email.json b/public/language/pl/admin/settings/email.json index f8ffa2b0cc..76895b0536 100644 --- a/public/language/pl/admin/settings/email.json +++ b/public/language/pl/admin/settings/email.json @@ -6,7 +6,7 @@ "from-help": "Nazwa „Od” widoczna w e-mailach", "smtp-transport": "Transport SMTP", - "smtp-transport.enabled": "Używaj zewnętrznego serwera e-mail do wysyłania e-maili.", + "smtp-transport.enabled": "Enable SMTP Transport", "smtp-transport-help": "Możesz wybrać z listy dobrze znanych usług lub wskazać usługę niestandardową.", "smtp-transport.service": "Wybierz usługę", "smtp-transport.service-custom": "Usługa niestandardowa", @@ -37,6 +37,10 @@ "subscriptions.hour": "Godzina podsumowania", "subscriptions.hour-help": "Wprowadź liczbę odpowiadającą godzinie, o której mają być wysyłane regularne e-maile z podsumowaniem (np. 0 dla północy lub 17 dla 17:00). Pamiętaj, że godzina jest godziną serwera i nie musi zgadzać się z czasem lokalnym administratora. Przybliżony czas serwera to:
Wysłanie kolejnego e-maila z podsumowaniem zaplanowano na ", "notifications.remove-images": "Remove images from email notifications", + "require-email-address": "Require new users to specify an email address", + "require-email-address-warning": "By default, users can opt-out of entering an email address. Enabling this option means they have to enter an email address in order to proceed with registration. It does not ensure user will enter a real email address, nor even an address they own.", "include-unverified-emails": "Send emails to recipients who have not explicitly confirmed their emails", - "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws." -} \ No newline at end of file + "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws.", + "prompt": "Prompt users to enter or confirm their emails", + "prompt-help": "If a user does not have an email set, or their email is not confirmed, a warning will be shown on screen." +} diff --git a/public/language/pl/admin/settings/general.json b/public/language/pl/admin/settings/general.json index aeee7c067e..04284cbb99 100644 --- a/public/language/pl/admin/settings/general.json +++ b/public/language/pl/admin/settings/general.json @@ -3,9 +3,9 @@ "title": "Tytuł strony", "title.short": "Krótki tytuł", "title.short-placeholder": "Jeśli nie wskazano krótkiego tytułu, użyty zostanie tytuł strony", - "title.url": "URL", + "title.url": "Title Link URL", "title.url-placeholder": "Adres URL strony tytułowej", - "title.url-help": "Po kliknięciu w tytuł użytkownik przejdzie pod ten adres. Jeśli pole to jest puste, użytkownik zostanie przeniesiony na stronę główną forum.", + "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "title.name": "Nazwa twojej społeczności", "title.show-in-header": "Pokazuj tytuł strony w nagłówku", "browser-title": "Tytuł karty przeglądarki", @@ -20,9 +20,9 @@ "logo.image": "Obraz", "logo.image-placeholder": "Ścieżka do logo, które ma być wyświetlane w nagłówku forum", "logo.upload": "Prześlij", - "logo.url": "Adres URL", + "logo.url": "Logo Link URL", "logo.url-placeholder": "Adres URL logo strony", - "logo.url-help": "Po kliknięciu w logo użytkownik przejdzie pod ten adres. Jeśli pole to jest puste, użytkownik zostanie przeniesiony na stronę główną forum.", + "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "logo.alt-text": "Alternatywny tekst", "log.alt-text-placeholder": "Alternatywny tekst dla dostępności", "favicon": "Favikona", @@ -47,4 +47,4 @@ "undo-timeout": "Undo Timeout", "undo-timeout-help": "Some operations such as moving topics will allow for the moderator to undo their action within a certain timeframe. Set to 0 to disable undo completely.", "topic-tools": "Topic Tools" -} \ No newline at end of file +} diff --git a/public/language/pl/admin/settings/navigation.json b/public/language/pl/admin/settings/navigation.json index 42a9e7c98c..36c88f8ca5 100644 --- a/public/language/pl/admin/settings/navigation.json +++ b/public/language/pl/admin/settings/navigation.json @@ -11,6 +11,8 @@ "properties": "Ustawienia:", "groups": "Grupy:", "open-new-window": "Otwórz w nowym oknie", + "dropdown": "Dropdown", + "dropdown-placeholder": "Place your dropdown menu items below, ie:
<li><a href="https://myforum.com">Link 1</a></li>", "btn.delete": "Usuń", "btn.disable": "Wyłącz", @@ -20,4 +22,4 @@ "custom-route": "Niestandardowa ścieżka", "core": "system", "plugin": "wtyczka" -} \ No newline at end of file +} diff --git a/public/language/pl/admin/settings/notifications.json b/public/language/pl/admin/settings/notifications.json index 2b3dad29ea..6a3a4be2b8 100644 --- a/public/language/pl/admin/settings/notifications.json +++ b/public/language/pl/admin/settings/notifications.json @@ -2,5 +2,6 @@ "notifications": "Powiadomienia", "welcome-notification": "Powiadomienie powitalne", "welcome-notification-link": "Łącze do komunikatu powitalnego", - "welcome-notification-uid": "Powiadomienie powitalne użytkownika (UID)" + "welcome-notification-uid": "Powiadomienie powitalne użytkownika (UID)", + "post-queue-notification-uid": "Post Queue User (UID)" } \ No newline at end of file diff --git a/public/language/pl/admin/settings/post.json b/public/language/pl/admin/settings/post.json index 4fc718bd4c..9374e26e45 100644 --- a/public/language/pl/admin/settings/post.json +++ b/public/language/pl/admin/settings/post.json @@ -40,6 +40,7 @@ "teaser.last-post": "Ostatni – Pokaż ostatni post, włączając pierwszy post, w razie braku odpowiedzi", "teaser.last-reply": "Ostatni – Pokaż ostatnią odpowiedź lub komunikat „Brak odpowiedzi” w razie ich braku", "teaser.first": "Pierwszy", + "showPostPreviewsOnHover": "Show a preview of posts when mouse overed", "unread": "Ustawienia nieprzeczytanych", "unread.cutoff": "Dni do odcięcia nieprzeczytanych ", "unread.min-track-last": "Minimalna liczba postów w temacie przed śledzeniem ostatnio przeczytanego", @@ -56,6 +57,9 @@ "composer.show-help": "Pokazuj zakładkę „Pomoc”", "composer.enable-plugin-help": "Zezwalaj wtyczkom na dodawanie zawartości do zakładki pomocy", "composer.custom-help": "Własny tekst pomocy", + "backlinks": "Backlinks", + "backlinks.enabled": "Enable topic backlinks", + "backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.", "ip-tracking": "Śledzenie IP", "ip-tracking.each-post": "Śledź adres IP dla każdego postu", "enable-post-history": "Włącz historię wpisu" diff --git a/public/language/pl/admin/settings/user.json b/public/language/pl/admin/settings/user.json index 8c02686702..3de21179b2 100644 --- a/public/language/pl/admin/settings/user.json +++ b/public/language/pl/admin/settings/user.json @@ -71,6 +71,7 @@ "digest-freq.off": "Wyłączone", "digest-freq.daily": "Dzienny ", "digest-freq.weekly": "Tygodniowy", + "digest-freq.biweekly": "Bi-Weekly", "digest-freq.monthly": "Miesięczny", "email-chat-notifs": "Wyślij powiadomienie email, jeśli dostanę nową wiadomość, a nie jestem on-line", "email-post-notif": "Wyślij wiadomość email, kiedy w tematach, które subskrybuję, pojawią się odpowiedzi", diff --git a/public/language/pl/error.json b/public/language/pl/error.json index 80b378c88c..90860a6ba5 100644 --- a/public/language/pl/error.json +++ b/public/language/pl/error.json @@ -34,8 +34,9 @@ "email-invited": "Email was already invited", "email-not-confirmed": "Posting in some categories or topics is enabled once your email is confirmed, please click here to send a confirmation email.", "email-not-confirmed-chat": "Nie możesz prowadzić rozmów, dopóki twój email nie zostanie potwierdzony. Kliknij tutaj, aby potwierdzić swój email.", - "email-not-confirmed-email-sent": "Twój e-mail nie został jeszcze potwierdzony, sprawdź swoją skrzynkę pocztową, aby znaleźć e-mail z potwierdzeniem. Nie będziesz mógł dodawać postów ani czatować, dopóki Twój adres e-mail nie zostanie potwierdzony.", - "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery. Please click here to enter an email.", + "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You may not be able to post in some categories or chat until your email is confirmed.", + "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery, and may be necessary for chatting and posting in some categories. Please click here to enter an email.", + "user-doesnt-have-email": "User \"%1\" does not have an email set.", "email-confirm-failed": "Nie byliśmy w stanie potwierdzić Twojego adresu e-mail. Spróbuj później.", "confirm-email-already-sent": "Email potwierdzający został już wysłany, proszę odczekaj jeszcze %1 minut(y), aby wysłać kolejny.", "sendmail-not-found": "Program sendmail nie został znaleziony, proszę upewnij się, że jest zainstalowany i możliwy do uruchomienia przez użytkownika uruchamiającego NodeBB.", @@ -103,6 +104,7 @@ "already-bookmarked": "Już dodałeś ten post do zakładek", "already-unbookmarked": "Już usunąłeś ten post z zakładek", "cant-ban-other-admins": "Nie możesz zbanować innych adminów!", + "cant-make-banned-users-admin": "You can't make banned users admin.", "cant-remove-last-admin": "Jesteś jedynym administratorem. Dodaj innego użytkownika jako administratora przed usunięciem siebie z tej grupy", "account-deletion-disabled": "Account deletion is disabled", "cant-delete-admin": "Usuń uprawnienia administratora z tego konta przed próbą jego usunięcia.", diff --git a/public/language/pl/groups.json b/public/language/pl/groups.json index 10210f90fb..70c903b8a0 100644 --- a/public/language/pl/groups.json +++ b/public/language/pl/groups.json @@ -35,7 +35,7 @@ "details.member_count": "Liczba Członków", "details.creation_date": "Data Utworzenia", "details.description": "Opis", - "details.member-post-cids": "Category IDs to display posts from", + "details.member-post-cids": "ID kategorii, z której wyświetlone są posty", "details.badge_preview": "Podgląd etykiety", "details.change_icon": "Zmień ikonę", "details.change_label_colour": "Zmień kolor etykiety", diff --git a/public/language/pl/modules.json b/public/language/pl/modules.json index 336f920573..9a3bd5980e 100644 --- a/public/language/pl/modules.json +++ b/public/language/pl/modules.json @@ -54,7 +54,7 @@ "composer.formatting.strikethrough": "Przekreślenie", "composer.formatting.code": "Kod", "composer.formatting.link": "Odnośnik", - "composer.formatting.picture": "Obraz", + "composer.formatting.picture": "Image Link", "composer.upload-picture": "Wyślij obraz", "composer.upload-file": "Wyślij plik", "composer.zen_mode": "Tryb Zen", diff --git a/public/language/pl/register.json b/public/language/pl/register.json index 18e2303972..85efebfb12 100644 --- a/public/language/pl/register.json +++ b/public/language/pl/register.json @@ -18,10 +18,10 @@ "agree_to_terms_of_use": "Zgadzam się na powyższe warunki", "terms_of_use_error": "Musisz zaakceptować warunki korzystania z serwisu", "registration-added-to-queue": "Twoja rejestracja została dodana do kolejki oczekujących na akceptację. Otrzymasz e-mail, kiedy zostanie zatwierdzona przez administratora.", - "registration-queue-average-time": "Our average time for approving memberships is %1 hours %2 minutes.", - "registration-queue-auto-approve-time": "Your membership to this forum will be fully activated in up to %1 hours.", - "interstitial.intro": "We'd like some additional information in order to update your account…", - "interstitial.intro-new": "We'd like some additional information before we can create your account…", + "registration-queue-average-time": "Nasz średni czas zatwierdzania członkostwa wynosi %1 godzin i %2 minut.", + "registration-queue-auto-approve-time": "Twoje członkostwo na tym forum zostanie w pełni aktywowane w ciągu maksymalnie %1 godzin.", + "interstitial.intro": "Do zaktualizowania Twojego konta potrzebne są dodatkowe informacje…", + "interstitial.intro-new": "Do utworzenia Twojego konta potrzebne są dodatkowe informacje.", "interstitial.errors-found": "Please review the entered information:", "gdpr_agree_data": "Wyrażam zgodę na zbieranie i przetwarzanie moich danych przez tę stronę.", "gdpr_agree_email": "Wyrażam zgodę na otrzymywanie e-maili z podsumowaniami i powiadomieniami od tej strony.", diff --git a/public/language/pl/top.json b/public/language/pl/top.json new file mode 100644 index 0000000000..6dbf85cad0 --- /dev/null +++ b/public/language/pl/top.json @@ -0,0 +1,4 @@ +{ + "title": "Najlepsze", + "no_top_topics": "Nie ma żadnych tematów w najlepszych" +} \ No newline at end of file diff --git a/public/language/pl/topic.json b/public/language/pl/topic.json index 948784b2bb..9dd1311fb4 100644 --- a/public/language/pl/topic.json +++ b/public/language/pl/topic.json @@ -20,8 +20,8 @@ "login-to-view": "Zaloguj się by zobaczyć", "edit": "Edytuj", "delete": "Usuń", - "delete-event": "Delete Event", - "delete-event-confirm": "Are you sure you want to delete this event?", + "delete-event": "Usuń zdarzenie", + "delete-event-confirm": "Czy na pewno chcesz usunąć to zdarzenie?", "purge": "Wymaż", "restore": "Przywróć", "move": "Przenieś", @@ -32,27 +32,28 @@ "tools": "Narzędzia", "locked": "Zablokowany", "pinned": "Przypięty", - "pinned-with-expiry": "Pinned until %1", + "pinned-with-expiry": "Przypięte do %1", "scheduled": "Zaplanowany", "moved": "Przeniesiony", - "moved-from": "Moved from %1", + "moved-from": "Przeniesiony z %1", "copy-ip": "Kopiuj IP", "ban-ip": "Blokuj IP", "view-history": "Historia edycji", "locked-by": "Zamknięto przez", "unlocked-by": "Odblokowano przez", "pinned-by": "Przypięto przez", - "unpinned-by": "Unpinned by", - "deleted-by": "Deleted by", - "restored-by": "Restored by", - "moved-from-by": "Moved from %1 by", + "unpinned-by": "Odpięty przez", + "deleted-by": "Usunięty przez", + "restored-by": "Przywrócony przez", + "moved-from-by": "Przeniesiony z %1 przez", "queued-by": "Post queued for approval →", + "backlink": "Referenced by", "bookmark_instructions": "Kliknij tutaj, by powrócić do ostatniego przeczytanego postu w tym temacie.", "flag-post": "Zgłoś ten post", "flag-user": "Zgłoś tego użytkownika", "already-flagged": "Już zgłoszono", "view-flag-report": "Zobacz zgłoszenie", - "resolve-flag": "Resolve Flag", + "resolve-flag": "Oznacz flagę jako rozwiązaną", "merged_message": "Ten temat został połączony z %2", "deleted_message": "Ten temat został usunięty. Mogą go zobaczyć tylko użytkownicy upoważnieni do zarządzania tematami.", "following_topic.message": "Będziesz teraz otrzymywać powiadomienia o nowych odpowiedziach w tym temacie.", @@ -94,16 +95,16 @@ "thread_tools.purge_confirm": "Na pewno chcesz wyczyścić ten temat?", "thread_tools.merge_topics": "Połącz tematy", "thread_tools.merge": "Połącz", - "topic_move_success": "This topic will be moved to \"%1\" shortly. Click here to undo.", - "topic_move_multiple_success": "These topics will be moved to \"%1\" shortly. Click here to undo.", - "topic_move_all_success": "All topics will be moved to \"%1\" shortly. Click here to undo.", - "topic_move_undone": "Topic move undone", - "topic_move_posts_success": "Posts will be moved shortly. Click here to undo.", - "topic_move_posts_undone": "Post move undone", + "topic_move_success": "Ten temat zostanie wkrótce przeniesiony do \"%1\". Naciśnij tutaj by to cofnąć.", + "topic_move_multiple_success": "Te tematy zostaną wkrótce przeniesione do \"%1\". Naciśnij tutaj by to cofnąć.", + "topic_move_all_success": "Wszystkie tematy zostaną wkrótce przeniesione do \"%1\". Naciśnij tutaj by to cofnąć.", + "topic_move_undone": "Cofnięto przenoszenie tematu", + "topic_move_posts_success": "Posty zostaną wkrótce przeniesione. Naciśnij tutaj by to cofnąć.", + "topic_move_posts_undone": "Cofnięto przenoszenie postów", "post_delete_confirm": "Czy na pewno chcesz usunąć ten post?", "post_restore_confirm": "Czy na pewno chcesz przywrócić ten post?", "post_purge_confirm": "Czy na pewno chcesz wyczyścić ten post?", - "pin-modal-expiry": "Expiration Date", + "pin-modal-expiry": "Data wygaśnięcia", "pin-modal-help": "You can optionally set an expiration date for the pinned topic(s) here. Alternatively, you can leave this field blank to have the topic stay pinned until it is manually unpinned.", "load_categories": "Ładowanie kategorii", "confirm_move": "Przenieś", @@ -117,12 +118,12 @@ "move_post": "Przenieś post", "post_moved": "Post został przeniesiony!", "fork_topic": "Skopiuj temat", - "enter-new-topic-title": "Enter new topic title", + "enter-new-topic-title": "Wpisz nowy tytuł tematu", "fork_topic_instruction": "Zaznacz posty, które chcesz skopiować", "fork_no_pids": "Nie zaznaczono żadnych postów!", - "no-posts-selected": "No posts selected!", - "x-posts-selected": "%1 post(s) selected", - "x-posts-will-be-moved-to-y": "%1 post(s) will be moved to \"%2\"", + "no-posts-selected": "Nie zaznaczono żadnych postów!", + "x-posts-selected": "Zaznaczono %1 post(-ów)", + "x-posts-will-be-moved-to-y": "%1 post(-ów) zostanie przeniesione do \"%2\"", "fork_pid_count": "Zaznaczono %1 post(-ów)", "fork_success": "Temat został skopiowany. Kliknij tutaj, aby do niego przejść.", "delete_posts_instruction": "Zaznacz posty, które chcesz usunąć/wyczyścić", @@ -131,17 +132,18 @@ "merge-options": "Opcję łączenia tematów", "merge-select-main-topic": "Wybierz główny temat", "merge-new-title-for-topic": "Nowy tytuł tematu", - "topic-id": "Topic ID", + "topic-id": "Identyfikator tematu", "move_posts_instruction": "Click the posts you want to move then enter a topic ID or go to the target topic", "change_owner_instruction": "Kliknij w posty, które chcesz przypisać do innego użytkownika", "composer.title_placeholder": "Tutaj wpisz tytuł tematu...", "composer.handle_placeholder": "Tutaj wpisz swoje imię/nazwę", "composer.discard": "Odrzuć", "composer.submit": "Utwórz", + "composer.additional-options": "Additional Options", "composer.schedule": "Schedule", "composer.replying_to": "Odpowiedź na %1", "composer.new_topic": "Nowy temat", - "composer.editing": "Editing", + "composer.editing": "Edytowanie", "composer.uploading": "wysyłanie...", "composer.thumb_url_label": "Wklej adres miniaturki tematu", "composer.thumb_title": "Dodaj miniaturkę do tego tematu", @@ -158,6 +160,7 @@ "newest_to_oldest": "Najpierw najnowsze", "most_votes": "Najwięcej głosów", "most_posts": "Najwięcej postów", + "most_views": "Najwięcej wyświetleń", "stale.title": "Stworzyć nowy temat?", "stale.warning": "Temat, na który chcesz udzielić odpowiedzi, jest dość stary. Czy nie wolisz utworzyć nowego tematu i jedynie odnieść się do tego?", "stale.create": "Stwórz nowy temat", @@ -169,13 +172,13 @@ "diffs.current-revision": "wersja obecna", "diffs.original-revision": "pierwsza zmiana", "diffs.restore": "Przywróć tę wersję", - "diffs.restore-description": "A new revision will be appended to this post's edit history after restoring.", + "diffs.restore-description": "Nowa wersja zostanie dodana do historii edycji tego postu po przywróceniu.", "diffs.post-restored": "Post został przywrócony do poprzedniej wersji", - "diffs.delete": "Delete this revision", - "diffs.deleted": "Revision deleted", + "diffs.delete": "Usuń tę wersję", + "diffs.deleted": "Wersja usunięta", "timeago_later": "%1 później", "timeago_earlier": "%1 wcześniej", - "first-post": "First post", - "last-post": "Last post", - "post-quick-reply": "Post quick reply" + "first-post": "Pierwszy post", + "last-post": "Ostatni post", + "post-quick-reply": "Wyślij szybką odpowiedź" } \ No newline at end of file diff --git a/public/language/pl/user.json b/public/language/pl/user.json index 91476b411f..7bf84c9427 100644 --- a/public/language/pl/user.json +++ b/public/language/pl/user.json @@ -94,6 +94,7 @@ "digest_off": "Wyłączone", "digest_daily": "Codziennie", "digest_weekly": "Co tydzień", + "digest_biweekly": "Bi-Weekly", "digest_monthly": "Co miesiąc", "has_no_follower": "Ten użytkownik nie ma jeszcze żadnych obserwujących", "follows_no_one": "Ten użytkownik jeszcze nikogo nie obserwuje", diff --git a/public/language/pt-BR/admin/advanced/events.json b/public/language/pt-BR/admin/advanced/events.json index a03335b8ef..6a6aa9b69d 100644 --- a/public/language/pt-BR/admin/advanced/events.json +++ b/public/language/pt-BR/admin/advanced/events.json @@ -3,6 +3,7 @@ "no-events": "Não há eventos", "control-panel": "Painel de Controle de Eventos", "delete-events": "Excluir Eventos", + "confirm-delete-all-events": "Are you sure you want to delete all logged events?", "filters": "Filtros", "filters-apply": "Aplicar Filtros", "filter-type": "Tipo de Evento", diff --git a/public/language/pt-BR/admin/dashboard.json b/public/language/pt-BR/admin/dashboard.json index 5769a80e97..e59eb05ad1 100644 --- a/public/language/pt-BR/admin/dashboard.json +++ b/public/language/pt-BR/admin/dashboard.json @@ -56,8 +56,8 @@ "active-users.total": "Total", "active-users.connections": "Conexões", - "anonymous-registered-users": "Anônimos vs Usuários Registrados", - "anonymous": "Anônimo", + "guest-registered-users": "Guest vs Registered Users", + "guest": "Guest", "registered": "Registrado", "user-presence": "Presença de Usuário", @@ -68,6 +68,7 @@ "unread": "Não-lidos", "high-presence-topics": "Tópicos de Alta Participação", + "popular-searches": "Popular Searches", "graphs.page-views": "Páginas Visualizadas", "graphs.page-views-registered": "Páginas Visualizadas por Registrados", @@ -75,13 +76,14 @@ "graphs.page-views-bot": "Páginas Visualizadas por Bot", "graphs.unique-visitors": "Visitantes Únicos", "graphs.registered-users": "Usuários Registrados", - "graphs.anonymous-users": "Usuários Anônimos", + "graphs.guest-users": "Guest Users", "last-restarted-by": "Última vez reiniciado por", "no-users-browsing": "Nenhum usuário navegando", "back-to-dashboard": "De volta ao Painel", "details.no-users": "Nenhum usuário ingressou dentro do período de tempo selecionado", "details.no-topics": "Nenhum tópico foi postado dentro do período de tempo selecionado", + "details.no-searches": "No searches have been made yet", "details.no-logins": "Nenhum login foi registrado dentro do período de tempo selecionado", "details.logins-static": "O NodeBB só salva os dados da sessão por %1 dias, então esta tabela abaixo mostrará apenas as sessões ativas mais recentemente", "details.logins-login-time": "Hora de Login" diff --git a/public/language/pt-BR/admin/development/info.json b/public/language/pt-BR/admin/development/info.json index cb67c31ed3..e1a5b6b618 100644 --- a/public/language/pt-BR/admin/development/info.json +++ b/public/language/pt-BR/admin/development/info.json @@ -8,7 +8,11 @@ "nodejs": "nodejs", "online": "online", "git": "git", - "memory": "memória", + "process-memory": "process memory", + "system-memory": "system memory", + "used-memory-process": "Used memory by process", + "used-memory-os": "Used system memory", + "total-memory-os": "Total system memory", "load": "carga do sistema", "cpu-usage": "uso da cpu", "uptime": "tempo de atividade", diff --git a/public/language/pt-BR/admin/extend/rewards.json b/public/language/pt-BR/admin/extend/rewards.json index 4b9c21f169..08b1de8119 100644 --- a/public/language/pt-BR/admin/extend/rewards.json +++ b/public/language/pt-BR/admin/extend/rewards.json @@ -8,8 +8,6 @@ "delete": "Deletar", "enable": "Ativar", "disable": "Desativar", - "control-panel": "Controle de Recompensas", - "new-reward": "Nova Recompensa", "alert.delete-success": "Recompensa excluída com sucesso", "alert.no-inputs-found": "Recompensa ilegal - nenhuma entrada encontrada!", diff --git a/public/language/pt-BR/admin/general/dashboard.json b/public/language/pt-BR/admin/general/dashboard.json deleted file mode 100644 index fc9b25d38d..0000000000 --- a/public/language/pt-BR/admin/general/dashboard.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "forum-traffic": "Tráfego do Forum", - "page-views": "Visualizações de Página", - "unique-visitors": "Visitantes Únicos", - "new-users": "New Users", - "posts": "Posts", - "topics": "Tópicos", - "page-views-seven": "Últimos 7 Dias", - "page-views-thirty": "Últimos 30 Dias", - "page-views-last-day": "Últimas 24 horas", - "page-views-custom": "Intervalo de Data Personalizado", - "page-views-custom-start": "Ínicio do Intervalo", - "page-views-custom-end": "Fim do Intervalo", - "page-views-custom-help": "Entre com um intervalo de data de visualizações de página que gostaria de ver. Se nenhum selecionador de data estiver disponível, o formato aceito é AAAA-MM-DD", - "page-views-custom-error": "Por favor, entre com um intervalo de data válido no formato AAAA-MM-DD", - - "stats.yesterday": "Yesterday", - "stats.today": "Today", - "stats.last-week": "Last Week", - "stats.this-week": "This Week", - "stats.last-month": "Last Month", - "stats.this-month": "This Month", - "stats.all": "Todos os Tempos", - - "updates": "Atualizações", - "running-version": "Você está usando o NodeBB v%1.", - "keep-updated": "Sempre se certifique de que o seu NodeBB está atualizado com os últimos patches de segurança e de correções de bugs.", - "up-to-date": "

Você está atualizado

", - "upgrade-available": "

Uma nova versão (v%1) foi lançada. Considere atualizar o seu NodeBB.

", - "prerelease-upgrade-available": "

Esta é uma versão de pré-lançamento desatualizada do NodeBB. Uma nova versão (v%1) foi lançada. Considere atualizar o seu NodeBB.

", - "prerelease-warning": "

Esta é uma versão de pré-lançamento do NodeBB. Bugs inesperados podem ocorrer.

", - "running-in-development": "O fórum está sendo executado em modo de desenvolvedor. O fórum pode estar sujeito a potenciais vulnerabilidades; por favor, entre em contato com o seu administrador de sistemas.", - "latest-lookup-failed": "

Falha ao procurar a versão mais recente disponível do NodeBB

", - - "notices": "Avisos", - "restart-not-required": "Reiniciar não é necessário", - "restart-required": "É necessário reiniciar", - "search-plugin-installed": "Plugin de Pesquisa instalado", - "search-plugin-not-installed": "Plugin de Pesquisa não instalado", - "search-plugin-tooltip": "Instale um plugin de pesquisa na página de plugins para que a funcionalidade de pesquisa seja ativada", - - "control-panel": "Controle do Sistema", - "rebuild-and-restart": "Recompilar & Reiniciar", - "restart": "Reiniciar", - "restart-warning": "Recompilar ou Reiniciar o seu NodeBB desconectará todas as conexões existentes por alguns segundos.", - "restart-disabled": "Recompilar e Reiniciar o seu NodeBB foi desativado, pois parece que você não está fazendo-o por meios apropriados.", - "maintenance-mode": "Modo de Manutenção", - "maintenance-mode-title": "Clique aqui para ativar o modo de manutenção do NodeBB", - "realtime-chart-updates": "Atualização de Gráfico em Tempo Real", - - "active-users": "Usuários Ativos", - "active-users.users": "Usuários", - "active-users.guests": "Visitantes", - "active-users.total": "Total", - "active-users.connections": "Conexões", - - "anonymous-registered-users": "Anônimos vs Usuários Registrados", - "anonymous": "Anônimo", - "registered": "Registrado", - - "user-presence": "Presença de Usuário", - "on-categories": "Na lista de categorias", - "reading-posts": "Lendo posts", - "browsing-topics": "Explorando tópicos", - "recent": "Recente", - "unread": "Não-lidos", - - "high-presence-topics": "Tópicos de Alta Participação", - - "graphs.page-views": "Páginas Visualizadas", - "graphs.page-views-registered": "Páginas Visualizadas por Registrados", - "graphs.page-views-guest": "Páginas Visualizadas por Visitantes", - "graphs.page-views-bot": "Páginas Visualizadas por Bot", - "graphs.unique-visitors": "Visitantes Únicos", - "graphs.registered-users": "Usuários Registrados", - "graphs.anonymous-users": "Usuários Anônimos", - "last-restarted-by": "Última vez reiniciado por", - "no-users-browsing": "Nenhum usuário navegando" -} diff --git a/public/language/pt-BR/admin/general/homepage.json b/public/language/pt-BR/admin/general/homepage.json deleted file mode 100644 index d2a1bb0f7e..0000000000 --- a/public/language/pt-BR/admin/general/homepage.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "home-page": "Página Inicial", - "description": "Escolha qual página será mostrada quando usuários navegarem para a URL raíz do seu fórum.", - "home-page-route": "Rota da Página Inicial", - "custom-route": "Rota Personalizada", - "allow-user-home-pages": "Permitir Páginas Iniciais do Usuário", - "home-page-title": "Título da página inicial (padrão \"Home\")" -} \ No newline at end of file diff --git a/public/language/pt-BR/admin/general/languages.json b/public/language/pt-BR/admin/general/languages.json deleted file mode 100644 index 53fa515534..0000000000 --- a/public/language/pt-BR/admin/general/languages.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "language-settings": "Configurações de Idioma", - "description": "O idioma padrão determina as configurações de idioma para todos os usuários que estiverem visitando o seu fórum.
Usuários individuais podem sobrepor o idioma padrão em sua página de configurações de conta.", - "default-language": "Idioma Padrão", - "auto-detect": "Auto Detectar Configurações de Idioma para Convidados" -} \ No newline at end of file diff --git a/public/language/pt-BR/admin/general/navigation.json b/public/language/pt-BR/admin/general/navigation.json deleted file mode 100644 index 84704c797e..0000000000 --- a/public/language/pt-BR/admin/general/navigation.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "icon": "Ícone:", - "change-icon": "modificar", - "route": "Rota:", - "tooltip": "Tooltip:", - "text": "Texto:", - "text-class": "Classe do Texto: opcional", - "class": "Classe: opcional", - "id": "ID: opcional", - - "properties": "Propriedades:", - "groups": "Grupos:", - "open-new-window": "Abrir em uma nova janela", - - "btn.delete": "Deletar", - "btn.disable": "Desativar", - "btn.enable": "Ativar", - - "available-menu-items": "Itens Disponíveis no Menu", - "custom-route": "Rota Personalizada", - "core": "core", - "plugin": "plugin" -} \ No newline at end of file diff --git a/public/language/pt-BR/admin/general/social.json b/public/language/pt-BR/admin/general/social.json deleted file mode 100644 index 3c58397604..0000000000 --- a/public/language/pt-BR/admin/general/social.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "post-sharing": "Compartilhamento de Posts", - "info-plugins-additional": "Plugins podem adicionar redes sociais adicionais para compartilhar posts.", - "save-success": "Redes de Compartilhamento de Posts salvas com êxito!" -} \ No newline at end of file diff --git a/public/language/pt-BR/admin/general/sounds.json b/public/language/pt-BR/admin/general/sounds.json deleted file mode 100644 index 9c8d09b9bc..0000000000 --- a/public/language/pt-BR/admin/general/sounds.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "notifications": "Notificações", - "chat-messages": "Mensagens de Chat", - "play-sound": "Tocar", - "incoming-message": "Ao receber mensagem", - "outgoing-message": "Ao enviar mensagem", - "upload-new-sound": "Enviar Novo Som", - "saved": "Configurações Salvas" -} \ No newline at end of file diff --git a/public/language/pt-BR/admin/manage/digest.json b/public/language/pt-BR/admin/manage/digest.json index c62371c550..2ef72c7c9e 100644 --- a/public/language/pt-BR/admin/manage/digest.json +++ b/public/language/pt-BR/admin/manage/digest.json @@ -13,6 +13,7 @@ "resent-single": "Reenvio manual de resumos concluído", "resent-day": "Resumo diário reenviado", "resent-week": "Resumo semanal reenviado", + "resent-biweek": "Bi-Weekly digest resent", "resent-month": "Resumo mensal reenviado", "null": "Nunca", "manual-run": "Execução de resumo manual:", diff --git a/public/language/pt-BR/admin/manage/users.json b/public/language/pt-BR/admin/manage/users.json index 0ee1d7655d..49f4284e34 100644 --- a/public/language/pt-BR/admin/manage/users.json +++ b/public/language/pt-BR/admin/manage/users.json @@ -47,6 +47,7 @@ "users.uid": "uid", "users.username": "nome de usuário", "users.email": "e-mail", + "users.no-email": "(no email)", "users.ip": "IP", "users.postcount": "quantidade de posts", "users.reputation": "reputação", diff --git a/public/language/pt-BR/admin/menu.json b/public/language/pt-BR/admin/menu.json index df133d872f..9e620cd716 100644 --- a/public/language/pt-BR/admin/menu.json +++ b/public/language/pt-BR/admin/menu.json @@ -4,6 +4,7 @@ "dashboard/logins": "Logins", "dashboard/users": "Usuários", "dashboard/topics": "Tópicos", + "dashboard/searches": "Searches", "section-general": "Geral", "section-manage": "Administrar", diff --git a/public/language/pt-BR/admin/settings/email.json b/public/language/pt-BR/admin/settings/email.json index a00c34d939..ca02b536d0 100644 --- a/public/language/pt-BR/admin/settings/email.json +++ b/public/language/pt-BR/admin/settings/email.json @@ -6,7 +6,7 @@ "from-help": "O nome que será mostrado em \"De\" no e-mail.", "smtp-transport": "Trasporte por SMTP", - "smtp-transport.enabled": "Utilizar um servidor de e-mail externo para enviar e-mails", + "smtp-transport.enabled": "Enable SMTP Transport", "smtp-transport-help": "Você pode escolher entre uma lista de serviços conhecidos ou adicionar um personalizado.", "smtp-transport.service": "Escolha um serviço", "smtp-transport.service-custom": "Serviço Personalizado", @@ -37,6 +37,10 @@ "subscriptions.hour": "Hora de Envio dos Resumos", "subscriptions.hour-help": "Por favor, entre um número representando a hora para enviar os resumos agendados via e-mail (por exemplo: 0 para meia-noite, 17 para 5:00pm). Tenha em mente que esta é a hora de acordo com o servidor e pode não combinar exatamente com o relógio do seu sistema.
O horário aproximado do servidor é:
O próximo resumo diário está agendado para ser enviado ", "notifications.remove-images": "Remover imagens de notificações por e-mail", + "require-email-address": "Require new users to specify an email address", + "require-email-address-warning": "By default, users can opt-out of entering an email address. Enabling this option means they have to enter an email address in order to proceed with registration. It does not ensure user will enter a real email address, nor even an address they own.", "include-unverified-emails": "Send emails to recipients who have not explicitly confirmed their emails", - "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws." -} \ No newline at end of file + "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws.", + "prompt": "Prompt users to enter or confirm their emails", + "prompt-help": "If a user does not have an email set, or their email is not confirmed, a warning will be shown on screen." +} diff --git a/public/language/pt-BR/admin/settings/general.json b/public/language/pt-BR/admin/settings/general.json index 13e2a2b391..9527613308 100644 --- a/public/language/pt-BR/admin/settings/general.json +++ b/public/language/pt-BR/admin/settings/general.json @@ -3,9 +3,9 @@ "title": "Título do Site", "title.short": "Título Curto", "title.short-placeholder": "Se nenhum título curto for especificado, o título do site será usado", - "title.url": "URL", + "title.url": "Title Link URL", "title.url-placeholder": "A URL do título do site", - "title.url-help": "Quando o título é clicado, enviar os usuários para este endereço. Se deixado em branco, o usuário será enviado para a página inicial do fórum.", + "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "title.name": "Nome da Sua Comunidade", "title.show-in-header": "Mostrar o Título do Site no Cabeçalho", "browser-title": "Título do Navegador", @@ -20,9 +20,9 @@ "logo.image": "Imagem", "logo.image-placeholder": "Caminho de URL do logotipo para mostrar no cabeçalho do fórum", "logo.upload": "Enviar", - "logo.url": "URL", + "logo.url": "Logo Link URL", "logo.url-placeholder": "A URL do logo do site", - "logo.url-help": "Quando o logotipo for clicado, enviar usuários para este endereço. Se deixado em branco, o usuário será enviado para a página inicial do fórum.", + "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "logo.alt-text": "Todo o Texto", "log.alt-text-placeholder": "Texto alternativo para acessibilidade", "favicon": "Favicon", @@ -47,4 +47,4 @@ "undo-timeout": "Undo Timeout", "undo-timeout-help": "Some operations such as moving topics will allow for the moderator to undo their action within a certain timeframe. Set to 0 to disable undo completely.", "topic-tools": "Topic Tools" -} \ No newline at end of file +} diff --git a/public/language/pt-BR/admin/settings/navigation.json b/public/language/pt-BR/admin/settings/navigation.json index 84704c797e..87f2bea84e 100644 --- a/public/language/pt-BR/admin/settings/navigation.json +++ b/public/language/pt-BR/admin/settings/navigation.json @@ -11,6 +11,8 @@ "properties": "Propriedades:", "groups": "Grupos:", "open-new-window": "Abrir em uma nova janela", + "dropdown": "Dropdown", + "dropdown-placeholder": "Place your dropdown menu items below, ie:
<li><a href="https://myforum.com">Link 1</a></li>", "btn.delete": "Deletar", "btn.disable": "Desativar", @@ -20,4 +22,4 @@ "custom-route": "Rota Personalizada", "core": "core", "plugin": "plugin" -} \ No newline at end of file +} diff --git a/public/language/pt-BR/admin/settings/notifications.json b/public/language/pt-BR/admin/settings/notifications.json index 1734b8890b..9a78c28825 100644 --- a/public/language/pt-BR/admin/settings/notifications.json +++ b/public/language/pt-BR/admin/settings/notifications.json @@ -2,5 +2,6 @@ "notifications": "Notificações", "welcome-notification": "Notificação de Boas-vindas", "welcome-notification-link": "Link da Notificação de Boas-vindas", - "welcome-notification-uid": "Usuário de Notificação de Boas-vindas (UID)" + "welcome-notification-uid": "Usuário de Notificação de Boas-vindas (UID)", + "post-queue-notification-uid": "Post Queue User (UID)" } \ No newline at end of file diff --git a/public/language/pt-BR/admin/settings/post.json b/public/language/pt-BR/admin/settings/post.json index 764ec57c0a..2f9be57f91 100644 --- a/public/language/pt-BR/admin/settings/post.json +++ b/public/language/pt-BR/admin/settings/post.json @@ -40,6 +40,7 @@ "teaser.last-post": "Último – Exibir o último post, incluindo o post original, se não houver respostas", "teaser.last-reply": "Último – Exibir a última resposta, ou um marcador \"Sem respostas\" se não houver respostas", "teaser.first": "Primeiro", + "showPostPreviewsOnHover": "Show a preview of posts when mouse overed", "unread": "Configurações de Não-Lidos", "unread.cutoff": "Data de corte de não-lidos", "unread.min-track-last": "Mínimo de posts no tópico antes de rastrear o último lido", @@ -56,6 +57,9 @@ "composer.show-help": "Mostrar aba \"Ajuda\"", "composer.enable-plugin-help": "Permitir plugins de adicionar conteúdo à aba ajuda", "composer.custom-help": "Texto de Ajuda Personalizado", + "backlinks": "Backlinks", + "backlinks.enabled": "Enable topic backlinks", + "backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.", "ip-tracking": "Rastreamento de IP", "ip-tracking.each-post": "Rastrear Endereço IP para cada post", "enable-post-history": "Ativar o Histórico de Postagem" diff --git a/public/language/pt-BR/admin/settings/user.json b/public/language/pt-BR/admin/settings/user.json index bb44efa13a..ecf98a2b12 100644 --- a/public/language/pt-BR/admin/settings/user.json +++ b/public/language/pt-BR/admin/settings/user.json @@ -71,6 +71,7 @@ "digest-freq.off": "Desligado", "digest-freq.daily": "Diário", "digest-freq.weekly": "Semanal", + "digest-freq.biweekly": "Bi-Weekly", "digest-freq.monthly": "Mensal", "email-chat-notifs": "Envie um email se uma nova mensagem de chat chegar e eu não estiver online", "email-post-notif": "Envie um email quando respostas forem dadas a tópicos que estou inscrito", diff --git a/public/language/pt-BR/error.json b/public/language/pt-BR/error.json index 42bd09c915..07b0e0ff18 100644 --- a/public/language/pt-BR/error.json +++ b/public/language/pt-BR/error.json @@ -34,8 +34,9 @@ "email-invited": "O email já foi convidado", "email-not-confirmed": "Posting in some categories or topics is enabled once your email is confirmed, please click here to send a confirmation email.", "email-not-confirmed-chat": "Você não está habilitado a conversar até que seu email seja confirmado, por favor clique aqui para confirmar seu email.", - "email-not-confirmed-email-sent": "Seu e-mail ainda não foi confirmado, verifique sua caixa de entrada para o e-mail de confirmação. Você não poderá postar ou usar o chat até que seu e-mail seja confirmado.", - "no-email-to-confirm": "Sua conta não tem um email definido. Um email é necessário para recuperação de conta. Clique aqui para definir um email.", + "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You may not be able to post in some categories or chat until your email is confirmed.", + "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery, and may be necessary for chatting and posting in some categories. Please click here to enter an email.", + "user-doesnt-have-email": "User \"%1\" does not have an email set.", "email-confirm-failed": "Nós não pudemos confirmar seu email, por gentileza tente novamente mais tarde.", "confirm-email-already-sent": "O email de confirmação já foi enviado, por favor aguarde %1 minuto(s) para enviar outro.", "sendmail-not-found": "O executável do sendmail não pôde ser encontrado. Por favor, certifique-se de que ele está instalado e que está autorizado a ser executado pelo usuário que roda o NodeBB.", @@ -103,6 +104,7 @@ "already-bookmarked": "Você já adicionou este post aos favoritos", "already-unbookmarked": "Você já removeu este post dos favoritos", "cant-ban-other-admins": "Você não pode banir outros administradores!", + "cant-make-banned-users-admin": "You can't make banned users admin.", "cant-remove-last-admin": "Você é o único administrador. Adicione outro usuário como administrador antes de remover a si mesmo como admin", "account-deletion-disabled": "A exclusão de conta está desabilitada", "cant-delete-admin": "Remova os privilégios de administrador dessa conta antes de tentar excluí-la.", diff --git a/public/language/pt-BR/modules.json b/public/language/pt-BR/modules.json index 570b9f3eb7..3f2c02c188 100644 --- a/public/language/pt-BR/modules.json +++ b/public/language/pt-BR/modules.json @@ -54,7 +54,7 @@ "composer.formatting.strikethrough": "Riscado", "composer.formatting.code": "Código", "composer.formatting.link": "Link", - "composer.formatting.picture": "Imagem", + "composer.formatting.picture": "Image Link", "composer.upload-picture": "Fazer upload de Imagem", "composer.upload-file": "Fazer upload de Arquivo", "composer.zen_mode": "Modo Zen", diff --git a/public/language/pt-BR/top.json b/public/language/pt-BR/top.json new file mode 100644 index 0000000000..b8a05bfa5f --- /dev/null +++ b/public/language/pt-BR/top.json @@ -0,0 +1,4 @@ +{ + "title": "Top", + "no_top_topics": "No top topics" +} \ No newline at end of file diff --git a/public/language/pt-BR/topic.json b/public/language/pt-BR/topic.json index 92b525a5e2..95d363dbcc 100644 --- a/public/language/pt-BR/topic.json +++ b/public/language/pt-BR/topic.json @@ -47,6 +47,7 @@ "restored-by": "Recuperado por", "moved-from-by": "Moved from %1 by", "queued-by": "Post aguardando aprovação →", + "backlink": "Referenced by", "bookmark_instructions": "Clique aqui para retornar ao último post lido neste tópico.", "flag-post": "Marque este post", "flag-user": "Marque este usuário", @@ -138,6 +139,7 @@ "composer.handle_placeholder": "Digite seu nome/usuário aqui", "composer.discard": "Descartar", "composer.submit": "Enviar", + "composer.additional-options": "Additional Options", "composer.schedule": "Agendar", "composer.replying_to": "Respondendo para %1", "composer.new_topic": "Novo Tópico", @@ -158,6 +160,7 @@ "newest_to_oldest": "Mais Recente para Mais Antigo", "most_votes": "Mais Votados", "most_posts": "Mais Postagens", + "most_views": "Most Views", "stale.title": "Criar um novo tópico ao invés disso?", "stale.warning": "O tópico que você está respondendo é bem antigo. Você gostaria de criar um novo tópico ao invés disso, e referenciá-lo em sua resposta?", "stale.create": "Criar um novo tópico", diff --git a/public/language/pt-BR/user.json b/public/language/pt-BR/user.json index 102aa0fbfe..0cae3a081e 100644 --- a/public/language/pt-BR/user.json +++ b/public/language/pt-BR/user.json @@ -94,6 +94,7 @@ "digest_off": "Desativado", "digest_daily": "Diariamente", "digest_weekly": "Semanalmente", + "digest_biweekly": "Bi-Weekly", "digest_monthly": "Mensalmente", "has_no_follower": "Este usuário não possui seguidores :(", "follows_no_one": "Este usuário não está seguindo ninguém :(", diff --git a/public/language/pt-PT/admin/advanced/events.json b/public/language/pt-PT/admin/advanced/events.json index 567cd6d09b..3063f1dbc2 100644 --- a/public/language/pt-PT/admin/advanced/events.json +++ b/public/language/pt-PT/admin/advanced/events.json @@ -3,6 +3,7 @@ "no-events": "Não existem eventos", "control-panel": "Painel de Controlo de Eventos", "delete-events": "Apagar Eventos", + "confirm-delete-all-events": "Are you sure you want to delete all logged events?", "filters": "Filtros", "filters-apply": "Aplicar Filtros", "filter-type": "Tipo de Evento", diff --git a/public/language/pt-PT/admin/dashboard.json b/public/language/pt-PT/admin/dashboard.json index 737a8ef13b..9f590b49c2 100644 --- a/public/language/pt-PT/admin/dashboard.json +++ b/public/language/pt-PT/admin/dashboard.json @@ -56,8 +56,8 @@ "active-users.total": "Total", "active-users.connections": "Conexões", - "anonymous-registered-users": "Utilizadores Anónimos vs Registados", - "anonymous": "Anónimos", + "guest-registered-users": "Guest vs Registered Users", + "guest": "Guest", "registered": "Registados", "user-presence": "Presença dos Utilizadores", @@ -68,6 +68,7 @@ "unread": "Não lidos", "high-presence-topics": " Alta Presença em Tópicos", + "popular-searches": "Popular Searches", "graphs.page-views": "Visualizações de páginas", "graphs.page-views-registered": "Visualizações de páginas por utilizadores registados", @@ -75,13 +76,14 @@ "graphs.page-views-bot": "Visualizações de páginas por bots", "graphs.unique-visitors": "Visitantes únicos", "graphs.registered-users": "Utilizadores Registados", - "graphs.anonymous-users": "Utilizadores Anónimos", + "graphs.guest-users": "Guest Users", "last-restarted-by": "Última vez reiniciado por", "no-users-browsing": "No users browsing", "back-to-dashboard": "Back to Dashboard", "details.no-users": "No users have joined within the selected timeframe", "details.no-topics": "No topics have been posted within the selected timeframe", + "details.no-searches": "No searches have been made yet", "details.no-logins": "No logins have been recorded within the selected timeframe", "details.logins-static": "NodeBB only saves session data for %1 days, and so this table below will only show the most recently active sessions", "details.logins-login-time": "Login Time" diff --git a/public/language/pt-PT/admin/development/info.json b/public/language/pt-PT/admin/development/info.json index 1bbac03c82..e88ef6e50d 100644 --- a/public/language/pt-PT/admin/development/info.json +++ b/public/language/pt-PT/admin/development/info.json @@ -8,7 +8,11 @@ "nodejs": "nodejs", "online": "online", "git": "git", - "memory": "memória", + "process-memory": "process memory", + "system-memory": "system memory", + "used-memory-process": "Used memory by process", + "used-memory-os": "Used system memory", + "total-memory-os": "Total system memory", "load": "carga do sistema", "cpu-usage": "uso cpu", "uptime": "tempo de atividade", diff --git a/public/language/pt-PT/admin/extend/rewards.json b/public/language/pt-PT/admin/extend/rewards.json index 7fab3c6679..346eb49e2a 100644 --- a/public/language/pt-PT/admin/extend/rewards.json +++ b/public/language/pt-PT/admin/extend/rewards.json @@ -8,8 +8,6 @@ "delete": "Apagar", "enable": "Ativar", "disable": "Desativar", - "control-panel": "Controlo de Recompensas", - "new-reward": "Nova Recompensa", "alert.delete-success": "Recompensa apagada com sucesso", "alert.no-inputs-found": "Recompensa ilegal - não foram encontradas entradas!", diff --git a/public/language/pt-PT/admin/general/dashboard.json b/public/language/pt-PT/admin/general/dashboard.json deleted file mode 100644 index 81b9783003..0000000000 --- a/public/language/pt-PT/admin/general/dashboard.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "forum-traffic": "Tráfego do Fórum", - "page-views": "Visualizações de páginas", - "unique-visitors": "Visitantes únicos", - "new-users": "Novos Utilizadores", - "posts": "Publicações", - "topics": "Tópicos", - "page-views-seven": "Últimos 7 Dias", - "page-views-thirty": "Últimos 30 Dias", - "page-views-last-day": "Últimas 24 horas", - "page-views-custom": "Intervalo Personalizado", - "page-views-custom-start": "Início do Intervalo", - "page-views-custom-end": "Fim do Intervalo", - "page-views-custom-help": "Insere um intervalo entre datas de visualizações de página que gostarias de visualizar. Se o selecionador de datas não estiver disponível, o formato aceitável é AAAA-MM-DD", - "page-views-custom-error": "Por favor, insere um intervalo entre datas no formato AAAA-MM-DD", - - "stats.yesterday": "Ontem", - "stats.today": "Hoje", - "stats.last-week": "Última Semana", - "stats.this-week": "Esta Semana", - "stats.last-month": "Último Mês", - "stats.this-month": "Este Mês", - "stats.all": "Desde sempre", - - "updates": "Atualizações", - "running-version": "Estás a executar NodeBB v%1.", - "keep-updated": "Cetifica-te que o teu NodeBB está sempre atualizado para teres as mais recentes correções de segurança e correções de bugs.", - "up-to-date": "

Estás atualizado

", - "upgrade-available": "

A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-upgrade-available": "

This is an outdated pre-release version of NodeBB. A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-warning": "

This is a pre-release version of NodeBB. Unintended bugs may occur.

", - "running-in-development": "Forum is running in development mode. The forum may be open to potential vulnerabilities; please contact your system administrator.", - "latest-lookup-failed": "

Ocorreu uma falha a obter a versão mais recente disponível para o NodeBB

", - - "notices": "Avisos", - "restart-not-required": "Não é necessário reiniciar", - "restart-required": "É necessário reiniciar", - "search-plugin-installed": "Plugin de pesquisa instalado", - "search-plugin-not-installed": "Plugin de pesquisa não instalado", - "search-plugin-tooltip": "Instala um plugin de pesquisa a partir da página de Plugins para conseguires ativar a funcionalidade de pesquisa", - - "control-panel": "Controlo do Sistema", - "rebuild-and-restart": "Reconstruir e Reiniciar", - "restart": "Reiniciar", - "restart-warning": "Reconstruir ou Reiniciar o teu NodeBB irá terminar todas as conexões existentes por alguns segundos.", - "restart-disabled": "Rebuilding and Restarting your NodeBB has been disabled as you do not seem to be running it via the appropriate daemon.", - "maintenance-mode": "Modo de Manutenção", - "maintenance-mode-title": "Clica aqui para configurar o modo de manutenção para o teu NodeBB", - "realtime-chart-updates": "Actualizar Gráfico em Tempo Real", - - "active-users": "Utilizadores Ativos", - "active-users.users": "Utilizadores", - "active-users.guests": "Convidados", - "active-users.total": "Total", - "active-users.connections": "Conexões", - - "anonymous-registered-users": "Utilizadores Anónimos vs Registados", - "anonymous": "Anónimos", - "registered": "Registados", - - "user-presence": "Presença dos Utilizadores", - "on-categories": "Na lista de categorias", - "reading-posts": "A ler publicações", - "browsing-topics": "A procurar tópicos", - "recent": "Recente", - "unread": "Não lidos", - - "high-presence-topics": " Alta Presença em Tópicos", - - "graphs.page-views": "Visualizações de páginas", - "graphs.page-views-registered": "Visualizações de páginas por utilizadores registados", - "graphs.page-views-guest": "Visualizações de páginas por convidados", - "graphs.page-views-bot": "Visualizações de páginas por bots", - "graphs.unique-visitors": "Visitantes únicos", - "graphs.registered-users": "Utilizadores Registados", - "graphs.anonymous-users": "Utilizadores Anónimos", - "last-restarted-by": "Última vez reiniciado por", - "no-users-browsing": "No users browsing" -} diff --git a/public/language/pt-PT/admin/general/homepage.json b/public/language/pt-PT/admin/general/homepage.json deleted file mode 100644 index e441f4d687..0000000000 --- a/public/language/pt-PT/admin/general/homepage.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "home-page": "Página Principal", - "description": "Escolhe qual página é apresentada quando os utilizadores navegam para o URL raiz do teu fórum.", - "home-page-route": "Caminho da Página Principal", - "custom-route": "Caminho personalizado", - "allow-user-home-pages": "Permitir página principal personalizada para os utilizadores", - "home-page-title": "Título da página inicial (predefinido \"Página inicial\")" -} \ No newline at end of file diff --git a/public/language/pt-PT/admin/general/languages.json b/public/language/pt-PT/admin/general/languages.json deleted file mode 100644 index 56be63bc12..0000000000 --- a/public/language/pt-PT/admin/general/languages.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "language-settings": "Definições de Idioma", - "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Idioma Predefinido", - "auto-detect": "Auto Detect Language Setting for Guests" -} \ No newline at end of file diff --git a/public/language/pt-PT/admin/general/navigation.json b/public/language/pt-PT/admin/general/navigation.json deleted file mode 100644 index 8e5ff802f9..0000000000 --- a/public/language/pt-PT/admin/general/navigation.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "icon": "Ícone:", - "change-icon": "alterar", - "route": "Caminho:", - "tooltip": "Título:", - "text": "Texto:", - "text-class": "Classe: opcional", - "class": "Classe: opcional", - "id": "ID: opcional", - - "properties": "Propriedades:", - "groups": "Grupos:", - "open-new-window": "Abrir numa nova janela", - - "btn.delete": "Apagar", - "btn.disable": "Desativar", - "btn.enable": "Ativar", - - "available-menu-items": "Itens de menu disponíveis", - "custom-route": "Caminho Personalizado", - "core": "sistema", - "plugin": "plugin" -} \ No newline at end of file diff --git a/public/language/pt-PT/admin/general/social.json b/public/language/pt-PT/admin/general/social.json deleted file mode 100644 index e856606a7b..0000000000 --- a/public/language/pt-PT/admin/general/social.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "post-sharing": "Partilhar Publicações", - "info-plugins-additional": "Os plugins podem adicionar outras redes sociais para partilhar publicações.", - "save-success": "Definições de partilhas de publicações em redes sociais guardadas com sucesso!" -} \ No newline at end of file diff --git a/public/language/pt-PT/admin/general/sounds.json b/public/language/pt-PT/admin/general/sounds.json deleted file mode 100644 index 167e6dbed4..0000000000 --- a/public/language/pt-PT/admin/general/sounds.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "notifications": "Notificações", - "chat-messages": "Mensagens de conversas", - "play-sound": "Reproduzir", - "incoming-message": "A Receber Mensagem", - "outgoing-message": "A Enviar Mensagem", - "upload-new-sound": "Enviar Novo Som", - "saved": "Definições guardadas" -} \ No newline at end of file diff --git a/public/language/pt-PT/admin/manage/digest.json b/public/language/pt-PT/admin/manage/digest.json index d1a8b0d2a4..83be67644b 100644 --- a/public/language/pt-PT/admin/manage/digest.json +++ b/public/language/pt-PT/admin/manage/digest.json @@ -13,6 +13,7 @@ "resent-single": "Reenvio do resumo manual concluído", "resent-day": "Resumo diário reenviado", "resent-week": "Resumo semanal reenviado", + "resent-biweek": "Bi-Weekly digest resent", "resent-month": "Resumo mensal reenviado", "null": "Nunca", "manual-run": "Manual digest run:", diff --git a/public/language/pt-PT/admin/manage/users.json b/public/language/pt-PT/admin/manage/users.json index 366bcf25bf..296d0a8dd9 100644 --- a/public/language/pt-PT/admin/manage/users.json +++ b/public/language/pt-PT/admin/manage/users.json @@ -47,6 +47,7 @@ "users.uid": "uid", "users.username": "nome de utilizador", "users.email": "e-mail", + "users.no-email": "(no email)", "users.ip": "IP", "users.postcount": "publicações", "users.reputation": "reputação", diff --git a/public/language/pt-PT/admin/menu.json b/public/language/pt-PT/admin/menu.json index 25d60dd92d..925af96d95 100644 --- a/public/language/pt-PT/admin/menu.json +++ b/public/language/pt-PT/admin/menu.json @@ -4,6 +4,7 @@ "dashboard/logins": "Logins", "dashboard/users": "Users", "dashboard/topics": "Topics", + "dashboard/searches": "Searches", "section-general": "Geral", "section-manage": "Gerir", diff --git a/public/language/pt-PT/admin/settings/email.json b/public/language/pt-PT/admin/settings/email.json index 2172f608f7..8e9e871abc 100644 --- a/public/language/pt-PT/admin/settings/email.json +++ b/public/language/pt-PT/admin/settings/email.json @@ -6,7 +6,7 @@ "from-help": "The from name to display in the email.", "smtp-transport": "SMTP Transport", - "smtp-transport.enabled": "Use an external email server to send emails", + "smtp-transport.enabled": "Enable SMTP Transport", "smtp-transport-help": "You can select from a list of well-known services or enter a custom one.", "smtp-transport.service": "Selecione um serviço", "smtp-transport.service-custom": "Serviço Personalizado", @@ -37,6 +37,10 @@ "subscriptions.hour": "Hora do Resumo", "subscriptions.hour-help": "Please enter a number representing the hour to send scheduled email digests (e.g. 0 for midnight, 17 for 5:00pm). Keep in mind that this is the hour according to the server itself, and may not exactly match your system clock.
The approximate server time is:
The next daily digest is scheduled to be sent ", "notifications.remove-images": "Remove images from email notifications", + "require-email-address": "Require new users to specify an email address", + "require-email-address-warning": "By default, users can opt-out of entering an email address. Enabling this option means they have to enter an email address in order to proceed with registration. It does not ensure user will enter a real email address, nor even an address they own.", "include-unverified-emails": "Send emails to recipients who have not explicitly confirmed their emails", - "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws." -} \ No newline at end of file + "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws.", + "prompt": "Prompt users to enter or confirm their emails", + "prompt-help": "If a user does not have an email set, or their email is not confirmed, a warning will be shown on screen." +} diff --git a/public/language/pt-PT/admin/settings/general.json b/public/language/pt-PT/admin/settings/general.json index 17c4855fd2..f5c13ef595 100644 --- a/public/language/pt-PT/admin/settings/general.json +++ b/public/language/pt-PT/admin/settings/general.json @@ -3,9 +3,9 @@ "title": "Título do Site", "title.short": "Título Curto", "title.short-placeholder": "If no short title is specified, the site title will be used", - "title.url": "URL", + "title.url": "Title Link URL", "title.url-placeholder": "O URL do título do site", - "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.", + "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "title.name": "Your Community Name", "title.show-in-header": "Show Site Title in Header", "browser-title": "Procurar Título", @@ -20,9 +20,9 @@ "logo.image": "Imagem", "logo.image-placeholder": "Path to a logo to display on forum header", "logo.upload": "Enviar", - "logo.url": "URL", + "logo.url": "Logo Link URL", "logo.url-placeholder": "O URL do logótipo do site", - "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.", + "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "logo.alt-text": "Texto Alternativo", "log.alt-text-placeholder": "Texto alternativo para acessibilidade", "favicon": "Favicon", @@ -47,4 +47,4 @@ "undo-timeout": "Undo Timeout", "undo-timeout-help": "Some operations such as moving topics will allow for the moderator to undo their action within a certain timeframe. Set to 0 to disable undo completely.", "topic-tools": "Topic Tools" -} \ No newline at end of file +} diff --git a/public/language/pt-PT/admin/settings/navigation.json b/public/language/pt-PT/admin/settings/navigation.json index 8e5ff802f9..ca1e541e23 100644 --- a/public/language/pt-PT/admin/settings/navigation.json +++ b/public/language/pt-PT/admin/settings/navigation.json @@ -11,6 +11,8 @@ "properties": "Propriedades:", "groups": "Grupos:", "open-new-window": "Abrir numa nova janela", + "dropdown": "Dropdown", + "dropdown-placeholder": "Place your dropdown menu items below, ie:
<li><a href="https://myforum.com">Link 1</a></li>", "btn.delete": "Apagar", "btn.disable": "Desativar", @@ -20,4 +22,4 @@ "custom-route": "Caminho Personalizado", "core": "sistema", "plugin": "plugin" -} \ No newline at end of file +} diff --git a/public/language/pt-PT/admin/settings/notifications.json b/public/language/pt-PT/admin/settings/notifications.json index 03fffd971e..0ce9737e1f 100644 --- a/public/language/pt-PT/admin/settings/notifications.json +++ b/public/language/pt-PT/admin/settings/notifications.json @@ -2,5 +2,6 @@ "notifications": "Notificações", "welcome-notification": "Notificação de Boas-vindas", "welcome-notification-link": "Link da Notificação de Boas-vindas", - "welcome-notification-uid": "Notificação de boas-vindas ao utilizador (UID)" + "welcome-notification-uid": "Notificação de boas-vindas ao utilizador (UID)", + "post-queue-notification-uid": "Post Queue User (UID)" } \ No newline at end of file diff --git a/public/language/pt-PT/admin/settings/post.json b/public/language/pt-PT/admin/settings/post.json index 44ff5a2c1d..df1b464869 100644 --- a/public/language/pt-PT/admin/settings/post.json +++ b/public/language/pt-PT/admin/settings/post.json @@ -40,6 +40,7 @@ "teaser.last-post": "Last – Show the latest post, including the original post, if no replies", "teaser.last-reply": "Last – Show the latest reply, or a \"No replies\" placeholder if no replies", "teaser.first": "First", + "showPostPreviewsOnHover": "Show a preview of posts when mouse overed", "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", @@ -56,6 +57,9 @@ "composer.show-help": "Mostrar separador \"Ajuda\"", "composer.enable-plugin-help": "Permitir aos plugins adicionarem conteúdo ao separador de ajuda", "composer.custom-help": "Texto de ajuda personalizado", + "backlinks": "Backlinks", + "backlinks.enabled": "Enable topic backlinks", + "backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.", "ip-tracking": "IP Tracking", "ip-tracking.each-post": "Track IP Address for each post", "enable-post-history": "Ativar histórico de publicações" diff --git a/public/language/pt-PT/admin/settings/user.json b/public/language/pt-PT/admin/settings/user.json index 41ebad0764..eb7ee976c1 100644 --- a/public/language/pt-PT/admin/settings/user.json +++ b/public/language/pt-PT/admin/settings/user.json @@ -71,6 +71,7 @@ "digest-freq.off": "Desligado", "digest-freq.daily": "Diariamente ", "digest-freq.weekly": "Semanalmente", + "digest-freq.biweekly": "Bi-Weekly", "digest-freq.monthly": "Mensalmente", "email-chat-notifs": "Enviar um e-mail se receber uma nova mensagem e não estiver online", "email-post-notif": "Send an email when replies are made to topics I am subscribed to", diff --git a/public/language/pt-PT/error.json b/public/language/pt-PT/error.json index 3c960870db..2750c5b577 100644 --- a/public/language/pt-PT/error.json +++ b/public/language/pt-PT/error.json @@ -34,8 +34,9 @@ "email-invited": "Email was already invited", "email-not-confirmed": "Posting in some categories or topics is enabled once your email is confirmed, please click here to send a confirmation email.", "email-not-confirmed-chat": "Não podes utilizar o chat enquanto não confirmares o teu e-mail, por favor clica aqui para confirmares o teu e-mail.", - "email-not-confirmed-email-sent": "O teu e-mail ainda não foi confirmado, por favor verifica a tua caixa de entrada para obteres o e-mail de confirmação. Não poderás publicar ou conversar até que o teu e-mail seja confirmado.", - "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery. Please click here to enter an email.", + "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You may not be able to post in some categories or chat until your email is confirmed.", + "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery, and may be necessary for chatting and posting in some categories. Please click here to enter an email.", + "user-doesnt-have-email": "User \"%1\" does not have an email set.", "email-confirm-failed": "Não conseguimos confirmar o teu e-mail, por favor tenta mais tarde.", "confirm-email-already-sent": "O e-mail de confirmação já foi enviado, por favor espera %1 minuto(s) para enviares outro.", "sendmail-not-found": "O executável sendmail não foi encontrado, por favor assegura-te que se encontra instalado e executável pelo utilizador a correr o NodeBB.", @@ -103,6 +104,7 @@ "already-bookmarked": "Já marcaste esta publicação", "already-unbookmarked": "Já desmarcaste esta publicação", "cant-ban-other-admins": "Não podes banir outros administradores!", + "cant-make-banned-users-admin": "You can't make banned users admin.", "cant-remove-last-admin": "És o único administrador. Adicionar outro utilizador como administrador antes de te removeres como administrador.", "account-deletion-disabled": "Account deletion is disabled", "cant-delete-admin": "Remove os privilégios de administrador desta conta antes de tentares apagá-la.", diff --git a/public/language/pt-PT/modules.json b/public/language/pt-PT/modules.json index 69fe9154b6..851361e0b4 100644 --- a/public/language/pt-PT/modules.json +++ b/public/language/pt-PT/modules.json @@ -54,7 +54,7 @@ "composer.formatting.strikethrough": "Riscado", "composer.formatting.code": "Código", "composer.formatting.link": "Hiperligação", - "composer.formatting.picture": "Imagem", + "composer.formatting.picture": "Image Link", "composer.upload-picture": "Enviar imagem", "composer.upload-file": "Enviar um ficheiro", "composer.zen_mode": "Modo Zen", diff --git a/public/language/pt-PT/top.json b/public/language/pt-PT/top.json new file mode 100644 index 0000000000..b8a05bfa5f --- /dev/null +++ b/public/language/pt-PT/top.json @@ -0,0 +1,4 @@ +{ + "title": "Top", + "no_top_topics": "No top topics" +} \ No newline at end of file diff --git a/public/language/pt-PT/topic.json b/public/language/pt-PT/topic.json index 395192641d..21d294c265 100644 --- a/public/language/pt-PT/topic.json +++ b/public/language/pt-PT/topic.json @@ -47,6 +47,7 @@ "restored-by": "Restored by", "moved-from-by": "Moved from %1 by", "queued-by": "Post queued for approval →", + "backlink": "Referenced by", "bookmark_instructions": "Carrega aqui para voltares à última publicação lide assunto.", "flag-post": "Flag this post", "flag-user": "Flag this user", @@ -138,6 +139,7 @@ "composer.handle_placeholder": "Enter your name/handle here", "composer.discard": "Descartar", "composer.submit": "Publicar", + "composer.additional-options": "Additional Options", "composer.schedule": "Schedule", "composer.replying_to": "Respondendo a %1", "composer.new_topic": "Novo tópico", @@ -158,6 +160,7 @@ "newest_to_oldest": "Mais recente para mais antigo", "most_votes": "Mais votos", "most_posts": "Mais publicações", + "most_views": "Most Views", "stale.title": "Em vez disso, criar novo tópico?", "stale.warning": "O tópico ao qual estás a responder é bastante antigo. Gostarias antes de criar um novo tópico e referir este na tua resposta?", "stale.create": "Criar um novo tópico", diff --git a/public/language/pt-PT/user.json b/public/language/pt-PT/user.json index 003633ab72..f7b641ea8b 100644 --- a/public/language/pt-PT/user.json +++ b/public/language/pt-PT/user.json @@ -94,6 +94,7 @@ "digest_off": "Desligado", "digest_daily": "Diariamente ", "digest_weekly": "Semanalmente", + "digest_biweekly": "Bi-Weekly", "digest_monthly": "Mensalmente", "has_no_follower": "Este utilizador não tem nenhum seguidor :(", "follows_no_one": "Este utilizador não está a seguir ninguém :(", diff --git a/public/language/ro/admin/advanced/events.json b/public/language/ro/admin/advanced/events.json index 56d9457971..b2c2033fb5 100644 --- a/public/language/ro/admin/advanced/events.json +++ b/public/language/ro/admin/advanced/events.json @@ -3,6 +3,7 @@ "no-events": "There are no events", "control-panel": "Events Control Panel", "delete-events": "Delete Events", + "confirm-delete-all-events": "Are you sure you want to delete all logged events?", "filters": "Filters", "filters-apply": "Apply Filters", "filter-type": "Event Type", diff --git a/public/language/ro/admin/dashboard.json b/public/language/ro/admin/dashboard.json index 0de31d4917..4d39626882 100644 --- a/public/language/ro/admin/dashboard.json +++ b/public/language/ro/admin/dashboard.json @@ -56,8 +56,8 @@ "active-users.total": "Total", "active-users.connections": "Connections", - "anonymous-registered-users": "Anonymous vs Registered Users", - "anonymous": "Anonymous", + "guest-registered-users": "Guest vs Registered Users", + "guest": "Guest", "registered": "Registered", "user-presence": "User Presence", @@ -68,6 +68,7 @@ "unread": "Unread", "high-presence-topics": "High Presence Topics", + "popular-searches": "Popular Searches", "graphs.page-views": "Page Views", "graphs.page-views-registered": "Page Views Registered", @@ -75,13 +76,14 @@ "graphs.page-views-bot": "Page Views Bot", "graphs.unique-visitors": "Unique Visitors", "graphs.registered-users": "Registered Users", - "graphs.anonymous-users": "Anonymous Users", + "graphs.guest-users": "Guest Users", "last-restarted-by": "Last restarted by", "no-users-browsing": "No users browsing", "back-to-dashboard": "Back to Dashboard", "details.no-users": "No users have joined within the selected timeframe", "details.no-topics": "No topics have been posted within the selected timeframe", + "details.no-searches": "No searches have been made yet", "details.no-logins": "No logins have been recorded within the selected timeframe", "details.logins-static": "NodeBB only saves session data for %1 days, and so this table below will only show the most recently active sessions", "details.logins-login-time": "Login Time" diff --git a/public/language/ro/admin/development/info.json b/public/language/ro/admin/development/info.json index 1003af1a5f..11202d9c3a 100644 --- a/public/language/ro/admin/development/info.json +++ b/public/language/ro/admin/development/info.json @@ -8,7 +8,11 @@ "nodejs": "nodejs", "online": "online", "git": "git", - "memory": "memory", + "process-memory": "process memory", + "system-memory": "system memory", + "used-memory-process": "Used memory by process", + "used-memory-os": "Used system memory", + "total-memory-os": "Total system memory", "load": "system load", "cpu-usage": "cpu usage", "uptime": "uptime", diff --git a/public/language/ro/admin/extend/rewards.json b/public/language/ro/admin/extend/rewards.json index 5383a90b33..df89d441a7 100644 --- a/public/language/ro/admin/extend/rewards.json +++ b/public/language/ro/admin/extend/rewards.json @@ -8,8 +8,6 @@ "delete": "Delete", "enable": "Enable", "disable": "Disable", - "control-panel": "Rewards Control", - "new-reward": "New Reward", "alert.delete-success": "Successfully deleted reward", "alert.no-inputs-found": "Illegal reward - no inputs found!", diff --git a/public/language/ro/admin/general/dashboard.json b/public/language/ro/admin/general/dashboard.json deleted file mode 100644 index 37ae537786..0000000000 --- a/public/language/ro/admin/general/dashboard.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "forum-traffic": "Forum Traffic", - "page-views": "Page Views", - "unique-visitors": "Unique Visitors", - "new-users": "New Users", - "posts": "Posts", - "topics": "Topics", - "page-views-seven": "Last 7 Days", - "page-views-thirty": "Last 30 Days", - "page-views-last-day": "Last 24 hours", - "page-views-custom": "Custom Date Range", - "page-views-custom-start": "Range Start", - "page-views-custom-end": "Range End", - "page-views-custom-help": "Enter a date range of page views you would like to view. If no date picker is available, the accepted format is YYYY-MM-DD", - "page-views-custom-error": "Please enter a valid date range in the format YYYY-MM-DD", - - "stats.yesterday": "Yesterday", - "stats.today": "Today", - "stats.last-week": "Last Week", - "stats.this-week": "This Week", - "stats.last-month": "Last Month", - "stats.this-month": "This Month", - "stats.all": "All Time", - - "updates": "Updates", - "running-version": "You are running NodeBB v%1.", - "keep-updated": "Always make sure that your NodeBB is up to date for the latest security patches and bug fixes.", - "up-to-date": "

You are up-to-date

", - "upgrade-available": "

A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-upgrade-available": "

This is an outdated pre-release version of NodeBB. A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-warning": "

This is a pre-release version of NodeBB. Unintended bugs may occur.

", - "running-in-development": "Forum is running in development mode. The forum may be open to potential vulnerabilities; please contact your system administrator.", - "latest-lookup-failed": "

Failed to look up latest available version of NodeBB

", - - "notices": "Notices", - "restart-not-required": "Restart not required", - "restart-required": "Restart required", - "search-plugin-installed": "Search Plugin installed", - "search-plugin-not-installed": "Search Plugin not installed", - "search-plugin-tooltip": "Install a search plugin from the plugin page in order to activate search functionality", - - "control-panel": "System Control", - "rebuild-and-restart": "Rebuild & Restart", - "restart": "Restart", - "restart-warning": "Rebuilding or Restarting your NodeBB will drop all existing connections for a few seconds.", - "restart-disabled": "Rebuilding and Restarting your NodeBB has been disabled as you do not seem to be running it via the appropriate daemon.", - "maintenance-mode": "Maintenance Mode", - "maintenance-mode-title": "Click here to set up maintenance mode for NodeBB", - "realtime-chart-updates": "Realtime Chart Updates", - - "active-users": "Active Users", - "active-users.users": "Users", - "active-users.guests": "Guests", - "active-users.total": "Total", - "active-users.connections": "Connections", - - "anonymous-registered-users": "Anonymous vs Registered Users", - "anonymous": "Anonymous", - "registered": "Registered", - - "user-presence": "User Presence", - "on-categories": "On categories list", - "reading-posts": "Reading posts", - "browsing-topics": "Browsing topics", - "recent": "Recent", - "unread": "Unread", - - "high-presence-topics": "High Presence Topics", - - "graphs.page-views": "Page Views", - "graphs.page-views-registered": "Page Views Registered", - "graphs.page-views-guest": "Page Views Guest", - "graphs.page-views-bot": "Page Views Bot", - "graphs.unique-visitors": "Unique Visitors", - "graphs.registered-users": "Registered Users", - "graphs.anonymous-users": "Anonymous Users", - "last-restarted-by": "Last restarted by", - "no-users-browsing": "No users browsing" -} diff --git a/public/language/ro/admin/general/homepage.json b/public/language/ro/admin/general/homepage.json deleted file mode 100644 index 7428d59eeb..0000000000 --- a/public/language/ro/admin/general/homepage.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "home-page": "Home Page", - "description": "Choose what page is shown when users navigate to the root URL of your forum.", - "home-page-route": "Home Page Route", - "custom-route": "Custom Route", - "allow-user-home-pages": "Allow User Home Pages", - "home-page-title": "Title of the home page (default \"Home\")" -} \ No newline at end of file diff --git a/public/language/ro/admin/general/languages.json b/public/language/ro/admin/general/languages.json deleted file mode 100644 index bdd57849b3..0000000000 --- a/public/language/ro/admin/general/languages.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "language-settings": "Language Settings", - "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language", - "auto-detect": "Auto Detect Language Setting for Guests" -} \ No newline at end of file diff --git a/public/language/ro/admin/general/navigation.json b/public/language/ro/admin/general/navigation.json deleted file mode 100644 index 13dd01aae7..0000000000 --- a/public/language/ro/admin/general/navigation.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "icon": "Icon:", - "change-icon": "change", - "route": "Route:", - "tooltip": "Tooltip:", - "text": "Text:", - "text-class": "Text Class: optional", - "class": "Class: optional", - "id": "ID: optional", - - "properties": "Properties:", - "groups": "Groups:", - "open-new-window": "Open in a new window", - - "btn.delete": "Delete", - "btn.disable": "Disable", - "btn.enable": "Enable", - - "available-menu-items": "Available Menu Items", - "custom-route": "Custom Route", - "core": "core", - "plugin": "plugin" -} \ No newline at end of file diff --git a/public/language/ro/admin/general/social.json b/public/language/ro/admin/general/social.json deleted file mode 100644 index 23aedfcfaa..0000000000 --- a/public/language/ro/admin/general/social.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "post-sharing": "Post Sharing", - "info-plugins-additional": "Plugins can add additional networks for sharing posts.", - "save-success": "Successfully saved Post Sharing Networks!" -} \ No newline at end of file diff --git a/public/language/ro/admin/general/sounds.json b/public/language/ro/admin/general/sounds.json deleted file mode 100644 index 95ccbde0f1..0000000000 --- a/public/language/ro/admin/general/sounds.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "notifications": "Notifications", - "chat-messages": "Chat Messages", - "play-sound": "Play", - "incoming-message": "Incoming Message", - "outgoing-message": "Outgoing Message", - "upload-new-sound": "Upload New Sound", - "saved": "Settings Saved" -} \ No newline at end of file diff --git a/public/language/ro/admin/manage/digest.json b/public/language/ro/admin/manage/digest.json index 8f3661698a..38c634d1f6 100644 --- a/public/language/ro/admin/manage/digest.json +++ b/public/language/ro/admin/manage/digest.json @@ -13,6 +13,7 @@ "resent-single": "Manual digest resend completed", "resent-day": "Daily digest resent", "resent-week": "Weekly digest resent", + "resent-biweek": "Bi-Weekly digest resent", "resent-month": "Monthly digest resent", "null": "Never", "manual-run": "Manual digest run:", diff --git a/public/language/ro/admin/manage/users.json b/public/language/ro/admin/manage/users.json index 38a065b954..2a3c0c4829 100644 --- a/public/language/ro/admin/manage/users.json +++ b/public/language/ro/admin/manage/users.json @@ -47,6 +47,7 @@ "users.uid": "uid", "users.username": "username", "users.email": "email", + "users.no-email": "(no email)", "users.ip": "IP", "users.postcount": "postcount", "users.reputation": "reputation", diff --git a/public/language/ro/admin/menu.json b/public/language/ro/admin/menu.json index 1c5ea73b4f..5b22fbeb36 100644 --- a/public/language/ro/admin/menu.json +++ b/public/language/ro/admin/menu.json @@ -4,6 +4,7 @@ "dashboard/logins": "Logins", "dashboard/users": "Users", "dashboard/topics": "Topics", + "dashboard/searches": "Searches", "section-general": "General", "section-manage": "Manage", diff --git a/public/language/ro/admin/settings/email.json b/public/language/ro/admin/settings/email.json index 9bdbde26a6..65f579d28c 100644 --- a/public/language/ro/admin/settings/email.json +++ b/public/language/ro/admin/settings/email.json @@ -6,7 +6,7 @@ "from-help": "The from name to display in the email.", "smtp-transport": "SMTP Transport", - "smtp-transport.enabled": "Use an external email server to send emails", + "smtp-transport.enabled": "Enable SMTP Transport", "smtp-transport-help": "You can select from a list of well-known services or enter a custom one.", "smtp-transport.service": "Select a service", "smtp-transport.service-custom": "Custom Service", @@ -37,6 +37,10 @@ "subscriptions.hour": "Digest Hour", "subscriptions.hour-help": "Please enter a number representing the hour to send scheduled email digests (e.g. 0 for midnight, 17 for 5:00pm). Keep in mind that this is the hour according to the server itself, and may not exactly match your system clock.
The approximate server time is:
The next daily digest is scheduled to be sent ", "notifications.remove-images": "Remove images from email notifications", + "require-email-address": "Require new users to specify an email address", + "require-email-address-warning": "By default, users can opt-out of entering an email address. Enabling this option means they have to enter an email address in order to proceed with registration. It does not ensure user will enter a real email address, nor even an address they own.", "include-unverified-emails": "Send emails to recipients who have not explicitly confirmed their emails", - "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws." -} \ No newline at end of file + "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws.", + "prompt": "Prompt users to enter or confirm their emails", + "prompt-help": "If a user does not have an email set, or their email is not confirmed, a warning will be shown on screen." +} diff --git a/public/language/ro/admin/settings/general.json b/public/language/ro/admin/settings/general.json index be7df90870..29b939861b 100644 --- a/public/language/ro/admin/settings/general.json +++ b/public/language/ro/admin/settings/general.json @@ -3,9 +3,9 @@ "title": "Site Title", "title.short": "Short Title", "title.short-placeholder": "If no short title is specified, the site title will be used", - "title.url": "URL", + "title.url": "Title Link URL", "title.url-placeholder": "The URL of the site title", - "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.", + "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "title.name": "Your Community Name", "title.show-in-header": "Show Site Title in Header", "browser-title": "Browser Title", @@ -20,9 +20,9 @@ "logo.image": "Image", "logo.image-placeholder": "Path to a logo to display on forum header", "logo.upload": "Upload", - "logo.url": "URL", + "logo.url": "Logo Link URL", "logo.url-placeholder": "The URL of the site logo", - "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.", + "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "logo.alt-text": "Alt Text", "log.alt-text-placeholder": "Alternative text for accessibility", "favicon": "Favicon", @@ -47,4 +47,4 @@ "undo-timeout": "Undo Timeout", "undo-timeout-help": "Some operations such as moving topics will allow for the moderator to undo their action within a certain timeframe. Set to 0 to disable undo completely.", "topic-tools": "Topic Tools" -} \ No newline at end of file +} diff --git a/public/language/ro/admin/settings/navigation.json b/public/language/ro/admin/settings/navigation.json index 13dd01aae7..7baca85096 100644 --- a/public/language/ro/admin/settings/navigation.json +++ b/public/language/ro/admin/settings/navigation.json @@ -11,6 +11,8 @@ "properties": "Properties:", "groups": "Groups:", "open-new-window": "Open in a new window", + "dropdown": "Dropdown", + "dropdown-placeholder": "Place your dropdown menu items below, ie:
<li><a href="https://myforum.com">Link 1</a></li>", "btn.delete": "Delete", "btn.disable": "Disable", @@ -20,4 +22,4 @@ "custom-route": "Custom Route", "core": "core", "plugin": "plugin" -} \ No newline at end of file +} diff --git a/public/language/ro/admin/settings/notifications.json b/public/language/ro/admin/settings/notifications.json index da6c9680a3..c6d8b928ce 100644 --- a/public/language/ro/admin/settings/notifications.json +++ b/public/language/ro/admin/settings/notifications.json @@ -2,5 +2,6 @@ "notifications": "Notifications", "welcome-notification": "Welcome Notification", "welcome-notification-link": "Welcome Notification Link", - "welcome-notification-uid": "Welcome Notification User (UID)" + "welcome-notification-uid": "Welcome Notification User (UID)", + "post-queue-notification-uid": "Post Queue User (UID)" } \ No newline at end of file diff --git a/public/language/ro/admin/settings/post.json b/public/language/ro/admin/settings/post.json index 27493aafbd..ab8245738c 100644 --- a/public/language/ro/admin/settings/post.json +++ b/public/language/ro/admin/settings/post.json @@ -40,6 +40,7 @@ "teaser.last-post": "Last – Show the latest post, including the original post, if no replies", "teaser.last-reply": "Last – Show the latest reply, or a \"No replies\" placeholder if no replies", "teaser.first": "First", + "showPostPreviewsOnHover": "Show a preview of posts when mouse overed", "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", @@ -56,6 +57,9 @@ "composer.show-help": "Show \"Help\" tab", "composer.enable-plugin-help": "Allow plugins to add content to the help tab", "composer.custom-help": "Custom Help Text", + "backlinks": "Backlinks", + "backlinks.enabled": "Enable topic backlinks", + "backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.", "ip-tracking": "IP Tracking", "ip-tracking.each-post": "Track IP Address for each post", "enable-post-history": "Enable Post History" diff --git a/public/language/ro/admin/settings/user.json b/public/language/ro/admin/settings/user.json index 48be13b75e..7923bf8cbe 100644 --- a/public/language/ro/admin/settings/user.json +++ b/public/language/ro/admin/settings/user.json @@ -71,6 +71,7 @@ "digest-freq.off": "Off", "digest-freq.daily": "Daily", "digest-freq.weekly": "Weekly", + "digest-freq.biweekly": "Bi-Weekly", "digest-freq.monthly": "Monthly", "email-chat-notifs": "Send an email if a new chat message arrives and I am not online", "email-post-notif": "Send an email when replies are made to topics I am subscribed to", diff --git a/public/language/ro/error.json b/public/language/ro/error.json index ab5ba55acd..d21bdd06c4 100644 --- a/public/language/ro/error.json +++ b/public/language/ro/error.json @@ -34,8 +34,9 @@ "email-invited": "Email was already invited", "email-not-confirmed": "Posting in some categories or topics is enabled once your email is confirmed, please click here to send a confirmation email.", "email-not-confirmed-chat": "Nu vei putea trimite mesaje daca email-ul tau nu e confirmat, click aici sa il confirmi.", - "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You won't be able to post or chat until your email is confirmed.", - "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery. Please click here to enter an email.", + "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You may not be able to post in some categories or chat until your email is confirmed.", + "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery, and may be necessary for chatting and posting in some categories. Please click here to enter an email.", + "user-doesnt-have-email": "User \"%1\" does not have an email set.", "email-confirm-failed": "Mail-ul tau nu a putut fi confirmat, te rog incearca mai tarziu.", "confirm-email-already-sent": "Email-ul de confirmare ti-a fost trimis, asteapta te rog %1 minut(e) ca sa trimiti inca unul.", "sendmail-not-found": "The sendmail executable could not be found, please ensure it is installed and executable by the user running NodeBB.", @@ -103,6 +104,7 @@ "already-bookmarked": "You have already bookmarked this post", "already-unbookmarked": "You have already unbookmarked this post", "cant-ban-other-admins": "Nu poți bana alți administratori!", + "cant-make-banned-users-admin": "You can't make banned users admin.", "cant-remove-last-admin": "You are the only administrator. Add another user as an administrator before removing yourself as admin", "account-deletion-disabled": "Account deletion is disabled", "cant-delete-admin": "Remove administrator privileges from this account before attempting to delete it.", diff --git a/public/language/ro/modules.json b/public/language/ro/modules.json index b77a9ec31e..84c4c9b8ec 100644 --- a/public/language/ro/modules.json +++ b/public/language/ro/modules.json @@ -54,7 +54,7 @@ "composer.formatting.strikethrough": "Strikethrough", "composer.formatting.code": "Code", "composer.formatting.link": "Link", - "composer.formatting.picture": "Picture", + "composer.formatting.picture": "Image Link", "composer.upload-picture": "Upload Image", "composer.upload-file": "Upload File", "composer.zen_mode": "Zen Mode", diff --git a/public/language/ro/top.json b/public/language/ro/top.json new file mode 100644 index 0000000000..b8a05bfa5f --- /dev/null +++ b/public/language/ro/top.json @@ -0,0 +1,4 @@ +{ + "title": "Top", + "no_top_topics": "No top topics" +} \ No newline at end of file diff --git a/public/language/ro/topic.json b/public/language/ro/topic.json index a8dd3ac675..e0cf667911 100644 --- a/public/language/ro/topic.json +++ b/public/language/ro/topic.json @@ -47,6 +47,7 @@ "restored-by": "Restored by", "moved-from-by": "Moved from %1 by", "queued-by": "Post queued for approval →", + "backlink": "Referenced by", "bookmark_instructions": "Click here to return to the last read post in this thread.", "flag-post": "Flag this post", "flag-user": "Flag this user", @@ -138,6 +139,7 @@ "composer.handle_placeholder": "Enter your name/handle here", "composer.discard": "Renunță", "composer.submit": "Trimite", + "composer.additional-options": "Additional Options", "composer.schedule": "Schedule", "composer.replying_to": "Îi raspunde lui %1", "composer.new_topic": "Subiect Nou", @@ -158,6 +160,7 @@ "newest_to_oldest": "Noi la Vechi", "most_votes": "Most Votes", "most_posts": "Most Posts", + "most_views": "Most Views", "stale.title": "Create new topic instead?", "stale.warning": "The topic you are replying to is quite old. Would you like to create a new topic instead, and reference this one in your reply?", "stale.create": "Create a new topic", diff --git a/public/language/ro/user.json b/public/language/ro/user.json index 7293421e86..799a2d2178 100644 --- a/public/language/ro/user.json +++ b/public/language/ro/user.json @@ -94,6 +94,7 @@ "digest_off": "Închis", "digest_daily": "Zilnic", "digest_weekly": "Săptămânal", + "digest_biweekly": "Bi-Weekly", "digest_monthly": "Lunar", "has_no_follower": "Pe acest utilizator nu îl urmărește nimeni :(", "follows_no_one": "Acest utilizator nu urmărește pe nimeni :(", diff --git a/public/language/ru/admin/advanced/events.json b/public/language/ru/admin/advanced/events.json index ffa09e2b83..a1647e7c6b 100644 --- a/public/language/ru/admin/advanced/events.json +++ b/public/language/ru/admin/advanced/events.json @@ -3,6 +3,7 @@ "no-events": "Нет событий", "control-panel": "Панель управления событиями", "delete-events": "Удалить события", + "confirm-delete-all-events": "Are you sure you want to delete all logged events?", "filters": "Фильтр", "filters-apply": "Применить фильтр", "filter-type": "Тип события", diff --git a/public/language/ru/admin/dashboard.json b/public/language/ru/admin/dashboard.json index 067bcae894..f8e093c304 100644 --- a/public/language/ru/admin/dashboard.json +++ b/public/language/ru/admin/dashboard.json @@ -56,8 +56,8 @@ "active-users.total": "Всего", "active-users.connections": "Соединений", - "anonymous-registered-users": "Анонимные / Авторизованные", - "anonymous": "Анонимные", + "guest-registered-users": "Guest vs Registered Users", + "guest": "Guest", "registered": "Авторизованные", "user-presence": "Присутствие", @@ -68,6 +68,7 @@ "unread": "Просм. непрочитанные", "high-presence-topics": "Популярные темы", + "popular-searches": "Popular Searches", "graphs.page-views": "Просмотры", "graphs.page-views-registered": "Просм. авторизованными", @@ -75,13 +76,14 @@ "graphs.page-views-bot": "Просмотров ботами", "graphs.unique-visitors": "Уникальных посетителей", "graphs.registered-users": "Авторизованных пользователей", - "graphs.anonymous-users": "Анонимных пользователей", + "graphs.guest-users": "Guest Users", "last-restarted-by": "Последний перезапуск:", "no-users-browsing": "Просмотров нет", "back-to-dashboard": "Вернуться на Панель управления", "details.no-users": "No users have joined within the selected timeframe", "details.no-topics": "No topics have been posted within the selected timeframe", + "details.no-searches": "No searches have been made yet", "details.no-logins": "No logins have been recorded within the selected timeframe", "details.logins-static": "NodeBB only saves session data for %1 days, and so this table below will only show the most recently active sessions", "details.logins-login-time": "Login Time" diff --git a/public/language/ru/admin/development/info.json b/public/language/ru/admin/development/info.json index 0087dac3a0..63d6faae67 100644 --- a/public/language/ru/admin/development/info.json +++ b/public/language/ru/admin/development/info.json @@ -8,7 +8,11 @@ "nodejs": "nodejs", "online": "онлайн", "git": "git", - "memory": "память", + "process-memory": "process memory", + "system-memory": "system memory", + "used-memory-process": "Used memory by process", + "used-memory-os": "Used system memory", + "total-memory-os": "Total system memory", "load": "системная загрузка", "cpu-usage": "загрузка процессора", "uptime": "продолжительность работы", diff --git a/public/language/ru/admin/extend/rewards.json b/public/language/ru/admin/extend/rewards.json index 616afc7178..d6cc9c4623 100644 --- a/public/language/ru/admin/extend/rewards.json +++ b/public/language/ru/admin/extend/rewards.json @@ -8,8 +8,6 @@ "delete": "Удалить", "enable": "Включить", "disable": "Выключить", - "control-panel": "Управление наградами", - "new-reward": "Новая награда", "alert.delete-success": "Награда успешно удалена", "alert.no-inputs-found": "Некорректная награда!", diff --git a/public/language/ru/admin/general/dashboard.json b/public/language/ru/admin/general/dashboard.json deleted file mode 100644 index 77ccd68c98..0000000000 --- a/public/language/ru/admin/general/dashboard.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "forum-traffic": "Трафик ", - "page-views": "Просмотров", - "unique-visitors": "Посетителей", - "new-users": "Новых пользователей", - "posts": "Сообщений", - "topics": "Тем", - "page-views-seven": "За 7 дней", - "page-views-thirty": "За 30 дней", - "page-views-last-day": "За 24 часа", - "page-views-custom": "Другой диапазон дат", - "page-views-custom-start": "Начало", - "page-views-custom-end": "Конец", - "page-views-custom-help": "Укажите начало и конец периода, за который вы хотите получить данные о просмотрах. Если выбор даты не доступен, то вы можете указать дату в формате ГГГГ-ММ-ДД ", - "page-views-custom-error": "Пожалуйста, укажите правильный диапазон дат в формате ГГГГ-ММ-ДД", - - "stats.yesterday": "Вчера", - "stats.today": "Сегодня", - "stats.last-week": "За прошл. неделю", - "stats.this-week": "За эту неделю", - "stats.last-month": "За прошл. месяц", - "stats.this-month": "За этот месяц", - "stats.all": "За всё время", - - "updates": "Обновления", - "running-version": "Вы используете NodeBB версии %1", - "keep-updated": "Пожалуйста, следите за тем, чтобы NodeBB своевременно обновлялся и получал все необходимые исправления ошибок и уязвимостей.", - "up-to-date": "

Вы используете актуальную версию

", - "upgrade-available": "

Вышла новая версия NodeBB (v%1). Хотите установить обновление?

", - "prerelease-upgrade-available": "

Вы используете устаревшую предрелизную версию NodeBB. Вышла новая (v%1). Хотите установить обновление?

", - "prerelease-warning": "

Вы используете предрелизную версию NodeBB. Вы можете столкнуться с разнообразными ошибками в её работе.

", - "running-in-development": "Форум работает в режиме для разработчиков. Это значит, что он может быть более уязвим для внешних угроз; пожалуйста, свяжитесь с вашим сисадмином.", - "latest-lookup-failed": "

Не удалось проверить наличие обновлений NodeBB

", - - "notices": "Примечания", - "restart-not-required": "Перезапуск не требуется", - "restart-required": "Требуется перезапуск", - "search-plugin-installed": "Плагин поиска установлен", - "search-plugin-not-installed": "Плагин поиска не установлен", - "search-plugin-tooltip": "Чтобы включить функцию поиска, установите соответствующий плагин на странице управления плагинами", - - "control-panel": "Управление системой", - "rebuild-and-restart": "Пересобрать и Перезапустить", - "restart": "Перезапустить", - "restart-warning": "Пересборка или перезапуск вашего NodeBB на несколько секунд оборвёт все имеющиеся соединения.", - "restart-disabled": "Пересборка и перезапуск вашего NodeBB была отключена, поскольку вы запустили форум без использования соответствующего демона.", - "maintenance-mode": "Режим техобслуживания", - "maintenance-mode-title": "Нажмите, чтобы включить и настроить режим техобслуживания", - "realtime-chart-updates": "Обновление графиков в реальном времени", - - "active-users": "Активных посетителей", - "active-users.users": "Польз.", - "active-users.guests": "Гостей", - "active-users.total": "Всего", - "active-users.connections": "Соединений", - - "anonymous-registered-users": "Анонимные / Авторизованные", - "anonymous": "Анонимные", - "registered": "Авторизованные", - - "user-presence": "Присутствие", - "on-categories": "В списке категорий", - "reading-posts": "Читают сообщения", - "browsing-topics": "Просматривают темы", - "recent": "Просм. последние темы", - "unread": "Просм. непрочитанные", - - "high-presence-topics": "Популярные темы", - - "graphs.page-views": "Просмотры", - "graphs.page-views-registered": "Просм. авторизованными", - "graphs.page-views-guest": "Просмотров гостями", - "graphs.page-views-bot": "Просмотров ботами", - "graphs.unique-visitors": "Уникальных посетителей", - "graphs.registered-users": "Авторизованных пользователей", - "graphs.anonymous-users": "Анонимных пользователей", - "last-restarted-by": "Последний перезапуск:", - "no-users-browsing": "Просмотров нет" -} diff --git a/public/language/ru/admin/general/homepage.json b/public/language/ru/admin/general/homepage.json deleted file mode 100644 index 2b6c14fe4d..0000000000 --- a/public/language/ru/admin/general/homepage.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "home-page": "Главная страница", - "description": "Выберите, какую страницу показывать по корневому URL форума.", - "home-page-route": "Маршрут для главной страницы", - "custom-route": "Другой маршрут", - "allow-user-home-pages": "Разрешить пользователям выбирать персональные главные страницы", - "home-page-title": "Заголовок домашней страницы («Главная» по умолчанию)" -} \ No newline at end of file diff --git a/public/language/ru/admin/general/languages.json b/public/language/ru/admin/general/languages.json deleted file mode 100644 index f7a6b365f9..0000000000 --- a/public/language/ru/admin/general/languages.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "language-settings": "Языковые настройки", - "description": "Язык по умолчанию определяет языковые настройки для всех посетителей форума.
Зарегистрированные пользователи могут выбрать другой язык в настройках своего профиля.", - "default-language": "Язык по умолчанию", - "auto-detect": "Автоматически определять язык для гостей" -} \ No newline at end of file diff --git a/public/language/ru/admin/general/navigation.json b/public/language/ru/admin/general/navigation.json deleted file mode 100644 index 4bcef87827..0000000000 --- a/public/language/ru/admin/general/navigation.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "icon": "Иконка:", - "change-icon": "изменить", - "route": "Маршрут:", - "tooltip": "Подсказка:", - "text": "Текст:", - "text-class": "Класс текста: опционально", - "class": "Класс: опционально", - "id": "ID: опционально", - - "properties": "Свойства:", - "groups": "Группы:", - "open-new-window": "Открывать в новом окне", - - "btn.delete": "Удалить", - "btn.disable": "Выключить", - "btn.enable": "Включить", - - "available-menu-items": "Доступные пункты меню", - "custom-route": "Произвольный маршрут", - "core": "ядро", - "plugin": "плагин" -} \ No newline at end of file diff --git a/public/language/ru/admin/general/social.json b/public/language/ru/admin/general/social.json deleted file mode 100644 index 7a4239e955..0000000000 --- a/public/language/ru/admin/general/social.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "post-sharing": "Делиться сообщениями в", - "info-plugins-additional": "Плагины могут добавить дополнительные опции для функции «поделиться сообщением»", - "save-success": "Настройки функции «поделиться сообщением» сохранены!" -} \ No newline at end of file diff --git a/public/language/ru/admin/general/sounds.json b/public/language/ru/admin/general/sounds.json deleted file mode 100644 index f84b71d629..0000000000 --- a/public/language/ru/admin/general/sounds.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "notifications": "Уведомления", - "chat-messages": "Сообщения чата", - "play-sound": "Воспроизвести", - "incoming-message": "Входящие сообщения", - "outgoing-message": "Исходящие сообщения", - "upload-new-sound": "Загрузить новый звук", - "saved": "Настройки сохранены" -} \ No newline at end of file diff --git a/public/language/ru/admin/manage/digest.json b/public/language/ru/admin/manage/digest.json index e92c46b18a..aa17069fb7 100644 --- a/public/language/ru/admin/manage/digest.json +++ b/public/language/ru/admin/manage/digest.json @@ -13,6 +13,7 @@ "resent-single": "Повтор рассылки вручную завершён", "resent-day": "Ежедневная рассылка отправлена повторно", "resent-week": "Еженедельная рассылка отправлена повторно", + "resent-biweek": "Bi-Weekly digest resent", "resent-month": "Ежемесячная рассылка отправлена повторно", "null": "Никогда", "manual-run": "Повтор вручную:", diff --git a/public/language/ru/admin/manage/users.json b/public/language/ru/admin/manage/users.json index c713895473..33642a429f 100644 --- a/public/language/ru/admin/manage/users.json +++ b/public/language/ru/admin/manage/users.json @@ -47,6 +47,7 @@ "users.uid": "ID", "users.username": "Логин", "users.email": "E-mail", + "users.no-email": "(no email)", "users.ip": "IP", "users.postcount": "Сообщения", "users.reputation": "Репутация", diff --git a/public/language/ru/admin/menu.json b/public/language/ru/admin/menu.json index 1cdfbb55d7..76f895e641 100644 --- a/public/language/ru/admin/menu.json +++ b/public/language/ru/admin/menu.json @@ -4,6 +4,7 @@ "dashboard/logins": "Авторизаций", "dashboard/users": "Пользователи", "dashboard/topics": "Темы", + "dashboard/searches": "Searches", "section-general": "Общие", "section-manage": "Управление", diff --git a/public/language/ru/admin/settings/email.json b/public/language/ru/admin/settings/email.json index e9c4b56c62..05028e66f2 100644 --- a/public/language/ru/admin/settings/email.json +++ b/public/language/ru/admin/settings/email.json @@ -6,7 +6,7 @@ "from-help": "Имя отправителя письма (или название форума).", "smtp-transport": "Сервис SMTP", - "smtp-transport.enabled": "Использовать сторонний сервис для рассылки писем", + "smtp-transport.enabled": "Enable SMTP Transport", "smtp-transport-help": "Выберите один из популярных сервисов или укажите свой почтовый сервер.", "smtp-transport.service": "Выберите сервис", "smtp-transport.service-custom": "Другой сервис", @@ -37,6 +37,10 @@ "subscriptions.hour": "Час отправки", "subscriptions.hour-help": "Введите число, соответствующее номеру часа (например, 0 для полуночи, 17 для 17:00). Имейте в виду, что время определяется по часовому поясу сервера.
Текущее время сервера:
Следующая рассылка запланирована на ", "notifications.remove-images": "Удалить изображения из уведомлений по электронной почте", + "require-email-address": "Require new users to specify an email address", + "require-email-address-warning": "By default, users can opt-out of entering an email address. Enabling this option means they have to enter an email address in order to proceed with registration. It does not ensure user will enter a real email address, nor even an address they own.", "include-unverified-emails": "Send emails to recipients who have not explicitly confirmed their emails", - "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws." -} \ No newline at end of file + "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws.", + "prompt": "Prompt users to enter or confirm their emails", + "prompt-help": "If a user does not have an email set, or their email is not confirmed, a warning will be shown on screen." +} diff --git a/public/language/ru/admin/settings/general.json b/public/language/ru/admin/settings/general.json index 7daa89aa62..06263dbb2d 100644 --- a/public/language/ru/admin/settings/general.json +++ b/public/language/ru/admin/settings/general.json @@ -3,9 +3,9 @@ "title": "Название сайта", "title.short": "Краткий заголовок", "title.short-placeholder": "Если здесь ничего не указано, будет использовано название сайта", - "title.url": "URL", + "title.url": "Title Link URL", "title.url-placeholder": "URL для названия сайта", - "title.url-help": "Когда пользователь нажмёт на название сайта, он перейдёт по этой ссылке.\nОставьте поле пустым, чтобы отправлять пользователей на главную страницу форума.", + "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "title.name": "Название вашего сообщества", "title.show-in-header": "Показывать название в шапке сайта", "browser-title": "Название для браузера", @@ -20,9 +20,9 @@ "logo.image": "Логотип в шапке сайта", "logo.image-placeholder": "Путь к файлу логотипа ", "logo.upload": "Загрузить", - "logo.url": "URL", + "logo.url": "Logo Link URL", "logo.url-placeholder": "URL для логотипа", - "logo.url-help": "Когда пользователь нажмёт на логотип, он перейдёт по этой ссылке.\nОставьте поле пустым, чтобы отправлять пользователей на главную страницу форума.", + "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "logo.alt-text": "Замещающий текст", "log.alt-text-placeholder": "Текст, который появится, если логотип не загрузится или загрузка изображений будет отключена", "favicon": "Favicon", @@ -47,4 +47,4 @@ "undo-timeout": "Undo Timeout", "undo-timeout-help": "Some operations such as moving topics will allow for the moderator to undo their action within a certain timeframe. Set to 0 to disable undo completely.", "topic-tools": "Topic Tools" -} \ No newline at end of file +} diff --git a/public/language/ru/admin/settings/navigation.json b/public/language/ru/admin/settings/navigation.json index 4bcef87827..d5ad4c9534 100644 --- a/public/language/ru/admin/settings/navigation.json +++ b/public/language/ru/admin/settings/navigation.json @@ -11,6 +11,8 @@ "properties": "Свойства:", "groups": "Группы:", "open-new-window": "Открывать в новом окне", + "dropdown": "Dropdown", + "dropdown-placeholder": "Place your dropdown menu items below, ie:
<li><a href="https://myforum.com">Link 1</a></li>", "btn.delete": "Удалить", "btn.disable": "Выключить", @@ -20,4 +22,4 @@ "custom-route": "Произвольный маршрут", "core": "ядро", "plugin": "плагин" -} \ No newline at end of file +} diff --git a/public/language/ru/admin/settings/notifications.json b/public/language/ru/admin/settings/notifications.json index e5593f26c7..d6a0478bc5 100644 --- a/public/language/ru/admin/settings/notifications.json +++ b/public/language/ru/admin/settings/notifications.json @@ -2,5 +2,6 @@ "notifications": "Уведомления", "welcome-notification": "Приветственное уведомление", "welcome-notification-link": "Ссылка в уведомлении", - "welcome-notification-uid": "UID отправителя" + "welcome-notification-uid": "UID отправителя", + "post-queue-notification-uid": "Post Queue User (UID)" } \ No newline at end of file diff --git a/public/language/ru/admin/settings/post.json b/public/language/ru/admin/settings/post.json index 4c6345eb37..a3ebd45ffc 100644 --- a/public/language/ru/admin/settings/post.json +++ b/public/language/ru/admin/settings/post.json @@ -40,6 +40,7 @@ "teaser.last-post": "Последнее – показать последнее сообщение в теме (первое, если ответов нет).", "teaser.last-reply": "Последнее – показать последнее сообщение или пометку «Ответов нет»", "teaser.first": "Первое сообщение", + "showPostPreviewsOnHover": "Show a preview of posts when mouse overed", "unread": "Настройка списка непрочитанных тем", "unread.cutoff": "Порог отсечки (в днях)", "unread.min-track-last": "Минимальное кол-во сообщений в теме, чтобы начать отслеживать непрочитанные ответы", @@ -56,6 +57,9 @@ "composer.show-help": "Показывать вкладку с подсказками", "composer.enable-plugin-help": "Разрешить плагинам добавлять подсказки на вкладку", "composer.custom-help": "Пользовательский текст для вкладки с подсказками", + "backlinks": "Backlinks", + "backlinks.enabled": "Enable topic backlinks", + "backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.", "ip-tracking": "Отслеживание IP", "ip-tracking.each-post": "Отслеживать IP для каждого сообщения", "enable-post-history": "Включить историю правок" diff --git a/public/language/ru/admin/settings/user.json b/public/language/ru/admin/settings/user.json index 264614898d..9dfd0f7d5d 100644 --- a/public/language/ru/admin/settings/user.json +++ b/public/language/ru/admin/settings/user.json @@ -71,6 +71,7 @@ "digest-freq.off": "Отключена", "digest-freq.daily": "Ежедневная", "digest-freq.weekly": "Еженедельная", + "digest-freq.biweekly": "Bi-Weekly", "digest-freq.monthly": "Ежемесячная", "email-chat-notifs": "Уведомить по электронной почте, если пришло новое сообщение в чат, а я не в сети", "email-post-notif": "Уведомить по электронной почте, если в отслеживаемой теме появилось новое сообщение", diff --git a/public/language/ru/error.json b/public/language/ru/error.json index 63b9b3f179..20a8a9dbe0 100644 --- a/public/language/ru/error.json +++ b/public/language/ru/error.json @@ -34,8 +34,9 @@ "email-invited": "Электронная почта уже была приглашена", "email-not-confirmed": "Posting in some categories or topics is enabled once your email is confirmed, please click here to send a confirmation email.", "email-not-confirmed-chat": "Вы не можете оставлять сообщения, пока ваша электронная почта не подтверждена. Отправить письмо с кодом подтверждения повторно.", - "email-not-confirmed-email-sent": "Ваш адрес электронной почты ещё не подтверждён. Пожалуйста, проверьте ваш почтовый ящик и пройдите по ссылке в письме с кодом подтверждения. Пока ваш e-mail не подтверждён, вы не можете пользоваться чатом или публиковать сообщения.", - "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery. Please click here to enter an email.", + "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You may not be able to post in some categories or chat until your email is confirmed.", + "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery, and may be necessary for chatting and posting in some categories. Please click here to enter an email.", + "user-doesnt-have-email": "User \"%1\" does not have an email set.", "email-confirm-failed": "По техническим причинам мы не можем подтвердить ваш адрес электронной почты. Приносим вам наши извинения, пожалуйста, попробуйте позже.", "confirm-email-already-sent": "Сообщение для подтверждения регистрации уже выслано на ваш адрес электронной почты. Повторная отправка возможна через %1 мин.", "sendmail-not-found": "Не можем найти sendmail, убедитесь что он установлен и управляется NodeBB.", @@ -103,6 +104,7 @@ "already-bookmarked": "Вы уже добавили это сообщение в закладки", "already-unbookmarked": "Вы уже удалили это сообщение из закладок", "cant-ban-other-admins": "Вы не можете заблокировать других администраторов!", + "cant-make-banned-users-admin": "You can't make banned users admin.", "cant-remove-last-admin": "Вы единственный администратор. Чтобы отказаться от своих полномочий, пожалуйста, назначьте администратором другого участника.", "account-deletion-disabled": "Удаление аккаунта отключено", "cant-delete-admin": "Чтобы удалить эту учётную запись, сначала надо снять с неё полномочия администратора.", diff --git a/public/language/ru/modules.json b/public/language/ru/modules.json index 3ed9b99f97..6c5e9e2bb3 100644 --- a/public/language/ru/modules.json +++ b/public/language/ru/modules.json @@ -54,7 +54,7 @@ "composer.formatting.strikethrough": "Зачеркнуть", "composer.formatting.code": "Код", "composer.formatting.link": "Ссылка", - "composer.formatting.picture": "Изображение", + "composer.formatting.picture": "Image Link", "composer.upload-picture": "Загрузить изображение", "composer.upload-file": "Загрузить файл", "composer.zen_mode": "Полноэкранный режим", diff --git a/public/language/ru/top.json b/public/language/ru/top.json new file mode 100644 index 0000000000..b8a05bfa5f --- /dev/null +++ b/public/language/ru/top.json @@ -0,0 +1,4 @@ +{ + "title": "Top", + "no_top_topics": "No top topics" +} \ No newline at end of file diff --git a/public/language/ru/topic.json b/public/language/ru/topic.json index 141f7b2d9e..87041a00e1 100644 --- a/public/language/ru/topic.json +++ b/public/language/ru/topic.json @@ -47,6 +47,7 @@ "restored-by": "Восстановлено", "moved-from-by": "Moved from %1 by", "queued-by": "Сообщение поставлено в очередь на утверждение;", + "backlink": "Referenced by", "bookmark_instructions": "Нажмите здесь, чтобы вернуться к последнему прочитанному сообщению в этой теме.", "flag-post": "Пожаловаться на это сообщение", "flag-user": "Пожаловаться на этого пользователя", @@ -138,6 +139,7 @@ "composer.handle_placeholder": "Введите ваше имя здесь", "composer.discard": "Отменить", "composer.submit": "Отправить", + "composer.additional-options": "Additional Options", "composer.schedule": "Запланировать", "composer.replying_to": "Ответ %1", "composer.new_topic": "Создать тему", @@ -158,6 +160,7 @@ "newest_to_oldest": "Сначала новые", "most_votes": "По количеству голосов", "most_posts": "По количеству сообщений", + "most_views": "Most Views", "stale.title": "Создать новую тему вместо этой?", "stale.warning": "Тема, в которую вы собираетесь написать, очень старая. Может, стоит создать новую, а про эту просто напомнить к случаю?", "stale.create": "Создать новую тему", diff --git a/public/language/ru/user.json b/public/language/ru/user.json index f7eac3da49..5642660e8b 100644 --- a/public/language/ru/user.json +++ b/public/language/ru/user.json @@ -94,6 +94,7 @@ "digest_off": "Отключена", "digest_daily": "Ежедневная", "digest_weekly": "Еженедельная", + "digest_biweekly": "Bi-Weekly", "digest_monthly": "Ежемесячная", "has_no_follower": "На этого пользователя никто не подписан :(", "follows_no_one": "Этот пользователь ни на кого не подписан :(", diff --git a/public/language/rw/admin/advanced/events.json b/public/language/rw/admin/advanced/events.json index 56d9457971..b2c2033fb5 100644 --- a/public/language/rw/admin/advanced/events.json +++ b/public/language/rw/admin/advanced/events.json @@ -3,6 +3,7 @@ "no-events": "There are no events", "control-panel": "Events Control Panel", "delete-events": "Delete Events", + "confirm-delete-all-events": "Are you sure you want to delete all logged events?", "filters": "Filters", "filters-apply": "Apply Filters", "filter-type": "Event Type", diff --git a/public/language/rw/admin/dashboard.json b/public/language/rw/admin/dashboard.json index 0de31d4917..4d39626882 100644 --- a/public/language/rw/admin/dashboard.json +++ b/public/language/rw/admin/dashboard.json @@ -56,8 +56,8 @@ "active-users.total": "Total", "active-users.connections": "Connections", - "anonymous-registered-users": "Anonymous vs Registered Users", - "anonymous": "Anonymous", + "guest-registered-users": "Guest vs Registered Users", + "guest": "Guest", "registered": "Registered", "user-presence": "User Presence", @@ -68,6 +68,7 @@ "unread": "Unread", "high-presence-topics": "High Presence Topics", + "popular-searches": "Popular Searches", "graphs.page-views": "Page Views", "graphs.page-views-registered": "Page Views Registered", @@ -75,13 +76,14 @@ "graphs.page-views-bot": "Page Views Bot", "graphs.unique-visitors": "Unique Visitors", "graphs.registered-users": "Registered Users", - "graphs.anonymous-users": "Anonymous Users", + "graphs.guest-users": "Guest Users", "last-restarted-by": "Last restarted by", "no-users-browsing": "No users browsing", "back-to-dashboard": "Back to Dashboard", "details.no-users": "No users have joined within the selected timeframe", "details.no-topics": "No topics have been posted within the selected timeframe", + "details.no-searches": "No searches have been made yet", "details.no-logins": "No logins have been recorded within the selected timeframe", "details.logins-static": "NodeBB only saves session data for %1 days, and so this table below will only show the most recently active sessions", "details.logins-login-time": "Login Time" diff --git a/public/language/rw/admin/development/info.json b/public/language/rw/admin/development/info.json index 1003af1a5f..11202d9c3a 100644 --- a/public/language/rw/admin/development/info.json +++ b/public/language/rw/admin/development/info.json @@ -8,7 +8,11 @@ "nodejs": "nodejs", "online": "online", "git": "git", - "memory": "memory", + "process-memory": "process memory", + "system-memory": "system memory", + "used-memory-process": "Used memory by process", + "used-memory-os": "Used system memory", + "total-memory-os": "Total system memory", "load": "system load", "cpu-usage": "cpu usage", "uptime": "uptime", diff --git a/public/language/rw/admin/extend/rewards.json b/public/language/rw/admin/extend/rewards.json index 5383a90b33..df89d441a7 100644 --- a/public/language/rw/admin/extend/rewards.json +++ b/public/language/rw/admin/extend/rewards.json @@ -8,8 +8,6 @@ "delete": "Delete", "enable": "Enable", "disable": "Disable", - "control-panel": "Rewards Control", - "new-reward": "New Reward", "alert.delete-success": "Successfully deleted reward", "alert.no-inputs-found": "Illegal reward - no inputs found!", diff --git a/public/language/rw/admin/general/dashboard.json b/public/language/rw/admin/general/dashboard.json deleted file mode 100644 index 37ae537786..0000000000 --- a/public/language/rw/admin/general/dashboard.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "forum-traffic": "Forum Traffic", - "page-views": "Page Views", - "unique-visitors": "Unique Visitors", - "new-users": "New Users", - "posts": "Posts", - "topics": "Topics", - "page-views-seven": "Last 7 Days", - "page-views-thirty": "Last 30 Days", - "page-views-last-day": "Last 24 hours", - "page-views-custom": "Custom Date Range", - "page-views-custom-start": "Range Start", - "page-views-custom-end": "Range End", - "page-views-custom-help": "Enter a date range of page views you would like to view. If no date picker is available, the accepted format is YYYY-MM-DD", - "page-views-custom-error": "Please enter a valid date range in the format YYYY-MM-DD", - - "stats.yesterday": "Yesterday", - "stats.today": "Today", - "stats.last-week": "Last Week", - "stats.this-week": "This Week", - "stats.last-month": "Last Month", - "stats.this-month": "This Month", - "stats.all": "All Time", - - "updates": "Updates", - "running-version": "You are running NodeBB v%1.", - "keep-updated": "Always make sure that your NodeBB is up to date for the latest security patches and bug fixes.", - "up-to-date": "

You are up-to-date

", - "upgrade-available": "

A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-upgrade-available": "

This is an outdated pre-release version of NodeBB. A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-warning": "

This is a pre-release version of NodeBB. Unintended bugs may occur.

", - "running-in-development": "Forum is running in development mode. The forum may be open to potential vulnerabilities; please contact your system administrator.", - "latest-lookup-failed": "

Failed to look up latest available version of NodeBB

", - - "notices": "Notices", - "restart-not-required": "Restart not required", - "restart-required": "Restart required", - "search-plugin-installed": "Search Plugin installed", - "search-plugin-not-installed": "Search Plugin not installed", - "search-plugin-tooltip": "Install a search plugin from the plugin page in order to activate search functionality", - - "control-panel": "System Control", - "rebuild-and-restart": "Rebuild & Restart", - "restart": "Restart", - "restart-warning": "Rebuilding or Restarting your NodeBB will drop all existing connections for a few seconds.", - "restart-disabled": "Rebuilding and Restarting your NodeBB has been disabled as you do not seem to be running it via the appropriate daemon.", - "maintenance-mode": "Maintenance Mode", - "maintenance-mode-title": "Click here to set up maintenance mode for NodeBB", - "realtime-chart-updates": "Realtime Chart Updates", - - "active-users": "Active Users", - "active-users.users": "Users", - "active-users.guests": "Guests", - "active-users.total": "Total", - "active-users.connections": "Connections", - - "anonymous-registered-users": "Anonymous vs Registered Users", - "anonymous": "Anonymous", - "registered": "Registered", - - "user-presence": "User Presence", - "on-categories": "On categories list", - "reading-posts": "Reading posts", - "browsing-topics": "Browsing topics", - "recent": "Recent", - "unread": "Unread", - - "high-presence-topics": "High Presence Topics", - - "graphs.page-views": "Page Views", - "graphs.page-views-registered": "Page Views Registered", - "graphs.page-views-guest": "Page Views Guest", - "graphs.page-views-bot": "Page Views Bot", - "graphs.unique-visitors": "Unique Visitors", - "graphs.registered-users": "Registered Users", - "graphs.anonymous-users": "Anonymous Users", - "last-restarted-by": "Last restarted by", - "no-users-browsing": "No users browsing" -} diff --git a/public/language/rw/admin/general/homepage.json b/public/language/rw/admin/general/homepage.json deleted file mode 100644 index 7428d59eeb..0000000000 --- a/public/language/rw/admin/general/homepage.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "home-page": "Home Page", - "description": "Choose what page is shown when users navigate to the root URL of your forum.", - "home-page-route": "Home Page Route", - "custom-route": "Custom Route", - "allow-user-home-pages": "Allow User Home Pages", - "home-page-title": "Title of the home page (default \"Home\")" -} \ No newline at end of file diff --git a/public/language/rw/admin/general/languages.json b/public/language/rw/admin/general/languages.json deleted file mode 100644 index bdd57849b3..0000000000 --- a/public/language/rw/admin/general/languages.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "language-settings": "Language Settings", - "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language", - "auto-detect": "Auto Detect Language Setting for Guests" -} \ No newline at end of file diff --git a/public/language/rw/admin/general/navigation.json b/public/language/rw/admin/general/navigation.json deleted file mode 100644 index 13dd01aae7..0000000000 --- a/public/language/rw/admin/general/navigation.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "icon": "Icon:", - "change-icon": "change", - "route": "Route:", - "tooltip": "Tooltip:", - "text": "Text:", - "text-class": "Text Class: optional", - "class": "Class: optional", - "id": "ID: optional", - - "properties": "Properties:", - "groups": "Groups:", - "open-new-window": "Open in a new window", - - "btn.delete": "Delete", - "btn.disable": "Disable", - "btn.enable": "Enable", - - "available-menu-items": "Available Menu Items", - "custom-route": "Custom Route", - "core": "core", - "plugin": "plugin" -} \ No newline at end of file diff --git a/public/language/rw/admin/general/social.json b/public/language/rw/admin/general/social.json deleted file mode 100644 index 23aedfcfaa..0000000000 --- a/public/language/rw/admin/general/social.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "post-sharing": "Post Sharing", - "info-plugins-additional": "Plugins can add additional networks for sharing posts.", - "save-success": "Successfully saved Post Sharing Networks!" -} \ No newline at end of file diff --git a/public/language/rw/admin/general/sounds.json b/public/language/rw/admin/general/sounds.json deleted file mode 100644 index 95ccbde0f1..0000000000 --- a/public/language/rw/admin/general/sounds.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "notifications": "Notifications", - "chat-messages": "Chat Messages", - "play-sound": "Play", - "incoming-message": "Incoming Message", - "outgoing-message": "Outgoing Message", - "upload-new-sound": "Upload New Sound", - "saved": "Settings Saved" -} \ No newline at end of file diff --git a/public/language/rw/admin/manage/digest.json b/public/language/rw/admin/manage/digest.json index 8f3661698a..38c634d1f6 100644 --- a/public/language/rw/admin/manage/digest.json +++ b/public/language/rw/admin/manage/digest.json @@ -13,6 +13,7 @@ "resent-single": "Manual digest resend completed", "resent-day": "Daily digest resent", "resent-week": "Weekly digest resent", + "resent-biweek": "Bi-Weekly digest resent", "resent-month": "Monthly digest resent", "null": "Never", "manual-run": "Manual digest run:", diff --git a/public/language/rw/admin/manage/users.json b/public/language/rw/admin/manage/users.json index 38a065b954..2a3c0c4829 100644 --- a/public/language/rw/admin/manage/users.json +++ b/public/language/rw/admin/manage/users.json @@ -47,6 +47,7 @@ "users.uid": "uid", "users.username": "username", "users.email": "email", + "users.no-email": "(no email)", "users.ip": "IP", "users.postcount": "postcount", "users.reputation": "reputation", diff --git a/public/language/rw/admin/menu.json b/public/language/rw/admin/menu.json index 1c5ea73b4f..5b22fbeb36 100644 --- a/public/language/rw/admin/menu.json +++ b/public/language/rw/admin/menu.json @@ -4,6 +4,7 @@ "dashboard/logins": "Logins", "dashboard/users": "Users", "dashboard/topics": "Topics", + "dashboard/searches": "Searches", "section-general": "General", "section-manage": "Manage", diff --git a/public/language/rw/admin/settings/email.json b/public/language/rw/admin/settings/email.json index 9bdbde26a6..65f579d28c 100644 --- a/public/language/rw/admin/settings/email.json +++ b/public/language/rw/admin/settings/email.json @@ -6,7 +6,7 @@ "from-help": "The from name to display in the email.", "smtp-transport": "SMTP Transport", - "smtp-transport.enabled": "Use an external email server to send emails", + "smtp-transport.enabled": "Enable SMTP Transport", "smtp-transport-help": "You can select from a list of well-known services or enter a custom one.", "smtp-transport.service": "Select a service", "smtp-transport.service-custom": "Custom Service", @@ -37,6 +37,10 @@ "subscriptions.hour": "Digest Hour", "subscriptions.hour-help": "Please enter a number representing the hour to send scheduled email digests (e.g. 0 for midnight, 17 for 5:00pm). Keep in mind that this is the hour according to the server itself, and may not exactly match your system clock.
The approximate server time is:
The next daily digest is scheduled to be sent ", "notifications.remove-images": "Remove images from email notifications", + "require-email-address": "Require new users to specify an email address", + "require-email-address-warning": "By default, users can opt-out of entering an email address. Enabling this option means they have to enter an email address in order to proceed with registration. It does not ensure user will enter a real email address, nor even an address they own.", "include-unverified-emails": "Send emails to recipients who have not explicitly confirmed their emails", - "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws." -} \ No newline at end of file + "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws.", + "prompt": "Prompt users to enter or confirm their emails", + "prompt-help": "If a user does not have an email set, or their email is not confirmed, a warning will be shown on screen." +} diff --git a/public/language/rw/admin/settings/general.json b/public/language/rw/admin/settings/general.json index be7df90870..29b939861b 100644 --- a/public/language/rw/admin/settings/general.json +++ b/public/language/rw/admin/settings/general.json @@ -3,9 +3,9 @@ "title": "Site Title", "title.short": "Short Title", "title.short-placeholder": "If no short title is specified, the site title will be used", - "title.url": "URL", + "title.url": "Title Link URL", "title.url-placeholder": "The URL of the site title", - "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.", + "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "title.name": "Your Community Name", "title.show-in-header": "Show Site Title in Header", "browser-title": "Browser Title", @@ -20,9 +20,9 @@ "logo.image": "Image", "logo.image-placeholder": "Path to a logo to display on forum header", "logo.upload": "Upload", - "logo.url": "URL", + "logo.url": "Logo Link URL", "logo.url-placeholder": "The URL of the site logo", - "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.", + "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "logo.alt-text": "Alt Text", "log.alt-text-placeholder": "Alternative text for accessibility", "favicon": "Favicon", @@ -47,4 +47,4 @@ "undo-timeout": "Undo Timeout", "undo-timeout-help": "Some operations such as moving topics will allow for the moderator to undo their action within a certain timeframe. Set to 0 to disable undo completely.", "topic-tools": "Topic Tools" -} \ No newline at end of file +} diff --git a/public/language/rw/admin/settings/navigation.json b/public/language/rw/admin/settings/navigation.json index 13dd01aae7..7baca85096 100644 --- a/public/language/rw/admin/settings/navigation.json +++ b/public/language/rw/admin/settings/navigation.json @@ -11,6 +11,8 @@ "properties": "Properties:", "groups": "Groups:", "open-new-window": "Open in a new window", + "dropdown": "Dropdown", + "dropdown-placeholder": "Place your dropdown menu items below, ie:
<li><a href="https://myforum.com">Link 1</a></li>", "btn.delete": "Delete", "btn.disable": "Disable", @@ -20,4 +22,4 @@ "custom-route": "Custom Route", "core": "core", "plugin": "plugin" -} \ No newline at end of file +} diff --git a/public/language/rw/admin/settings/notifications.json b/public/language/rw/admin/settings/notifications.json index da6c9680a3..c6d8b928ce 100644 --- a/public/language/rw/admin/settings/notifications.json +++ b/public/language/rw/admin/settings/notifications.json @@ -2,5 +2,6 @@ "notifications": "Notifications", "welcome-notification": "Welcome Notification", "welcome-notification-link": "Welcome Notification Link", - "welcome-notification-uid": "Welcome Notification User (UID)" + "welcome-notification-uid": "Welcome Notification User (UID)", + "post-queue-notification-uid": "Post Queue User (UID)" } \ No newline at end of file diff --git a/public/language/rw/admin/settings/post.json b/public/language/rw/admin/settings/post.json index 27493aafbd..ab8245738c 100644 --- a/public/language/rw/admin/settings/post.json +++ b/public/language/rw/admin/settings/post.json @@ -40,6 +40,7 @@ "teaser.last-post": "Last – Show the latest post, including the original post, if no replies", "teaser.last-reply": "Last – Show the latest reply, or a \"No replies\" placeholder if no replies", "teaser.first": "First", + "showPostPreviewsOnHover": "Show a preview of posts when mouse overed", "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", @@ -56,6 +57,9 @@ "composer.show-help": "Show \"Help\" tab", "composer.enable-plugin-help": "Allow plugins to add content to the help tab", "composer.custom-help": "Custom Help Text", + "backlinks": "Backlinks", + "backlinks.enabled": "Enable topic backlinks", + "backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.", "ip-tracking": "IP Tracking", "ip-tracking.each-post": "Track IP Address for each post", "enable-post-history": "Enable Post History" diff --git a/public/language/rw/admin/settings/user.json b/public/language/rw/admin/settings/user.json index 48be13b75e..7923bf8cbe 100644 --- a/public/language/rw/admin/settings/user.json +++ b/public/language/rw/admin/settings/user.json @@ -71,6 +71,7 @@ "digest-freq.off": "Off", "digest-freq.daily": "Daily", "digest-freq.weekly": "Weekly", + "digest-freq.biweekly": "Bi-Weekly", "digest-freq.monthly": "Monthly", "email-chat-notifs": "Send an email if a new chat message arrives and I am not online", "email-post-notif": "Send an email when replies are made to topics I am subscribed to", diff --git a/public/language/rw/error.json b/public/language/rw/error.json index 7b9d56fc4e..51ef2785a9 100644 --- a/public/language/rw/error.json +++ b/public/language/rw/error.json @@ -34,8 +34,9 @@ "email-invited": "Email was already invited", "email-not-confirmed": "Posting in some categories or topics is enabled once your email is confirmed, please click here to send a confirmation email.", "email-not-confirmed-chat": "Ntabwo uremererwa kuganirira mu gikari kuko email yawe itari yemezwa. Kanda hano kugirango wemeze email yawe. ", - "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You won't be able to post or chat until your email is confirmed.", - "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery. Please click here to enter an email.", + "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You may not be able to post in some categories or chat until your email is confirmed.", + "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery, and may be necessary for chatting and posting in some categories. Please click here to enter an email.", + "user-doesnt-have-email": "User \"%1\" does not have an email set.", "email-confirm-failed": "Ntabwo email yawe yabashije kwemezwa. Ongera ugerageze mu bundi buryo. ", "confirm-email-already-sent": "Email yo kwemeza yamaze koherezwa. Tegereza iminota (umunota) %1 mbere yo kohereza indi. ", "sendmail-not-found": "The sendmail executable could not be found, please ensure it is installed and executable by the user running NodeBB.", @@ -103,6 +104,7 @@ "already-bookmarked": "You have already bookmarked this post", "already-unbookmarked": "You have already unbookmarked this post", "cant-ban-other-admins": "Ntabwo wakwirukana abandi bayobozi!", + "cant-make-banned-users-admin": "You can't make banned users admin.", "cant-remove-last-admin": "Ni wowe muyobozi wenyine. Ongeramo undi muntu nk'umuyobozi mbere y'uko wikura ku buyobozi", "account-deletion-disabled": "Account deletion is disabled", "cant-delete-admin": "Remove administrator privileges from this account before attempting to delete it.", diff --git a/public/language/rw/modules.json b/public/language/rw/modules.json index 83bfc3d41d..3438a159da 100644 --- a/public/language/rw/modules.json +++ b/public/language/rw/modules.json @@ -54,7 +54,7 @@ "composer.formatting.strikethrough": "Strikethrough", "composer.formatting.code": "Code", "composer.formatting.link": "Link", - "composer.formatting.picture": "Picture", + "composer.formatting.picture": "Image Link", "composer.upload-picture": "Upload Image", "composer.upload-file": "Upload File", "composer.zen_mode": "Zen Mode", diff --git a/public/language/rw/top.json b/public/language/rw/top.json new file mode 100644 index 0000000000..b8a05bfa5f --- /dev/null +++ b/public/language/rw/top.json @@ -0,0 +1,4 @@ +{ + "title": "Top", + "no_top_topics": "No top topics" +} \ No newline at end of file diff --git a/public/language/rw/topic.json b/public/language/rw/topic.json index 925eb85ae9..69f3834b85 100644 --- a/public/language/rw/topic.json +++ b/public/language/rw/topic.json @@ -47,6 +47,7 @@ "restored-by": "Restored by", "moved-from-by": "Moved from %1 by", "queued-by": "Post queued for approval →", + "backlink": "Referenced by", "bookmark_instructions": "Click here to return to the last read post in this thread.", "flag-post": "Flag this post", "flag-user": "Flag this user", @@ -138,6 +139,7 @@ "composer.handle_placeholder": "Enter your name/handle here", "composer.discard": "Byihorere", "composer.submit": "Shyiraho", + "composer.additional-options": "Additional Options", "composer.schedule": "Schedule", "composer.replying_to": "Gusubiza %1", "composer.new_topic": "Ikiganiro Gishya", @@ -158,6 +160,7 @@ "newest_to_oldest": "Ibya Vuba Ujya ku bya Kera", "most_votes": "Most Votes", "most_posts": "Most Posts", + "most_views": "Most Views", "stale.title": "Urashaka gutangiza ahubwo ikiganiro gishya?", "stale.warning": "Ikiganiro ushaka kuvugaho cyarashaje. Wahitamo gutangiza ikiganiro gishya ariko wenda ukagaragaza kino mu gisubizo uza gushyiraho?", "stale.create": "Tangiza ikiganiro gishya", diff --git a/public/language/rw/user.json b/public/language/rw/user.json index 8235f4f6e9..4c548c42bb 100644 --- a/public/language/rw/user.json +++ b/public/language/rw/user.json @@ -94,6 +94,7 @@ "digest_off": "Birafunze", "digest_daily": "Buri Munsi", "digest_weekly": "Buri Cyumweru", + "digest_biweekly": "Bi-Weekly", "digest_monthly": "Buri Kwezi", "has_no_follower": "Uyu muntu ntabwo afite abamukurikira :(", "follows_no_one": "Uyu muntu ntabwo akurikira umuntu numwe :(", diff --git a/public/language/sc/admin/advanced/events.json b/public/language/sc/admin/advanced/events.json index 56d9457971..b2c2033fb5 100644 --- a/public/language/sc/admin/advanced/events.json +++ b/public/language/sc/admin/advanced/events.json @@ -3,6 +3,7 @@ "no-events": "There are no events", "control-panel": "Events Control Panel", "delete-events": "Delete Events", + "confirm-delete-all-events": "Are you sure you want to delete all logged events?", "filters": "Filters", "filters-apply": "Apply Filters", "filter-type": "Event Type", diff --git a/public/language/sc/admin/dashboard.json b/public/language/sc/admin/dashboard.json index 0de31d4917..4d39626882 100644 --- a/public/language/sc/admin/dashboard.json +++ b/public/language/sc/admin/dashboard.json @@ -56,8 +56,8 @@ "active-users.total": "Total", "active-users.connections": "Connections", - "anonymous-registered-users": "Anonymous vs Registered Users", - "anonymous": "Anonymous", + "guest-registered-users": "Guest vs Registered Users", + "guest": "Guest", "registered": "Registered", "user-presence": "User Presence", @@ -68,6 +68,7 @@ "unread": "Unread", "high-presence-topics": "High Presence Topics", + "popular-searches": "Popular Searches", "graphs.page-views": "Page Views", "graphs.page-views-registered": "Page Views Registered", @@ -75,13 +76,14 @@ "graphs.page-views-bot": "Page Views Bot", "graphs.unique-visitors": "Unique Visitors", "graphs.registered-users": "Registered Users", - "graphs.anonymous-users": "Anonymous Users", + "graphs.guest-users": "Guest Users", "last-restarted-by": "Last restarted by", "no-users-browsing": "No users browsing", "back-to-dashboard": "Back to Dashboard", "details.no-users": "No users have joined within the selected timeframe", "details.no-topics": "No topics have been posted within the selected timeframe", + "details.no-searches": "No searches have been made yet", "details.no-logins": "No logins have been recorded within the selected timeframe", "details.logins-static": "NodeBB only saves session data for %1 days, and so this table below will only show the most recently active sessions", "details.logins-login-time": "Login Time" diff --git a/public/language/sc/admin/development/info.json b/public/language/sc/admin/development/info.json index 1003af1a5f..11202d9c3a 100644 --- a/public/language/sc/admin/development/info.json +++ b/public/language/sc/admin/development/info.json @@ -8,7 +8,11 @@ "nodejs": "nodejs", "online": "online", "git": "git", - "memory": "memory", + "process-memory": "process memory", + "system-memory": "system memory", + "used-memory-process": "Used memory by process", + "used-memory-os": "Used system memory", + "total-memory-os": "Total system memory", "load": "system load", "cpu-usage": "cpu usage", "uptime": "uptime", diff --git a/public/language/sc/admin/extend/rewards.json b/public/language/sc/admin/extend/rewards.json index 5383a90b33..df89d441a7 100644 --- a/public/language/sc/admin/extend/rewards.json +++ b/public/language/sc/admin/extend/rewards.json @@ -8,8 +8,6 @@ "delete": "Delete", "enable": "Enable", "disable": "Disable", - "control-panel": "Rewards Control", - "new-reward": "New Reward", "alert.delete-success": "Successfully deleted reward", "alert.no-inputs-found": "Illegal reward - no inputs found!", diff --git a/public/language/sc/admin/general/dashboard.json b/public/language/sc/admin/general/dashboard.json deleted file mode 100644 index 37ae537786..0000000000 --- a/public/language/sc/admin/general/dashboard.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "forum-traffic": "Forum Traffic", - "page-views": "Page Views", - "unique-visitors": "Unique Visitors", - "new-users": "New Users", - "posts": "Posts", - "topics": "Topics", - "page-views-seven": "Last 7 Days", - "page-views-thirty": "Last 30 Days", - "page-views-last-day": "Last 24 hours", - "page-views-custom": "Custom Date Range", - "page-views-custom-start": "Range Start", - "page-views-custom-end": "Range End", - "page-views-custom-help": "Enter a date range of page views you would like to view. If no date picker is available, the accepted format is YYYY-MM-DD", - "page-views-custom-error": "Please enter a valid date range in the format YYYY-MM-DD", - - "stats.yesterday": "Yesterday", - "stats.today": "Today", - "stats.last-week": "Last Week", - "stats.this-week": "This Week", - "stats.last-month": "Last Month", - "stats.this-month": "This Month", - "stats.all": "All Time", - - "updates": "Updates", - "running-version": "You are running NodeBB v%1.", - "keep-updated": "Always make sure that your NodeBB is up to date for the latest security patches and bug fixes.", - "up-to-date": "

You are up-to-date

", - "upgrade-available": "

A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-upgrade-available": "

This is an outdated pre-release version of NodeBB. A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-warning": "

This is a pre-release version of NodeBB. Unintended bugs may occur.

", - "running-in-development": "Forum is running in development mode. The forum may be open to potential vulnerabilities; please contact your system administrator.", - "latest-lookup-failed": "

Failed to look up latest available version of NodeBB

", - - "notices": "Notices", - "restart-not-required": "Restart not required", - "restart-required": "Restart required", - "search-plugin-installed": "Search Plugin installed", - "search-plugin-not-installed": "Search Plugin not installed", - "search-plugin-tooltip": "Install a search plugin from the plugin page in order to activate search functionality", - - "control-panel": "System Control", - "rebuild-and-restart": "Rebuild & Restart", - "restart": "Restart", - "restart-warning": "Rebuilding or Restarting your NodeBB will drop all existing connections for a few seconds.", - "restart-disabled": "Rebuilding and Restarting your NodeBB has been disabled as you do not seem to be running it via the appropriate daemon.", - "maintenance-mode": "Maintenance Mode", - "maintenance-mode-title": "Click here to set up maintenance mode for NodeBB", - "realtime-chart-updates": "Realtime Chart Updates", - - "active-users": "Active Users", - "active-users.users": "Users", - "active-users.guests": "Guests", - "active-users.total": "Total", - "active-users.connections": "Connections", - - "anonymous-registered-users": "Anonymous vs Registered Users", - "anonymous": "Anonymous", - "registered": "Registered", - - "user-presence": "User Presence", - "on-categories": "On categories list", - "reading-posts": "Reading posts", - "browsing-topics": "Browsing topics", - "recent": "Recent", - "unread": "Unread", - - "high-presence-topics": "High Presence Topics", - - "graphs.page-views": "Page Views", - "graphs.page-views-registered": "Page Views Registered", - "graphs.page-views-guest": "Page Views Guest", - "graphs.page-views-bot": "Page Views Bot", - "graphs.unique-visitors": "Unique Visitors", - "graphs.registered-users": "Registered Users", - "graphs.anonymous-users": "Anonymous Users", - "last-restarted-by": "Last restarted by", - "no-users-browsing": "No users browsing" -} diff --git a/public/language/sc/admin/general/homepage.json b/public/language/sc/admin/general/homepage.json deleted file mode 100644 index 7428d59eeb..0000000000 --- a/public/language/sc/admin/general/homepage.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "home-page": "Home Page", - "description": "Choose what page is shown when users navigate to the root URL of your forum.", - "home-page-route": "Home Page Route", - "custom-route": "Custom Route", - "allow-user-home-pages": "Allow User Home Pages", - "home-page-title": "Title of the home page (default \"Home\")" -} \ No newline at end of file diff --git a/public/language/sc/admin/general/languages.json b/public/language/sc/admin/general/languages.json deleted file mode 100644 index bdd57849b3..0000000000 --- a/public/language/sc/admin/general/languages.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "language-settings": "Language Settings", - "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language", - "auto-detect": "Auto Detect Language Setting for Guests" -} \ No newline at end of file diff --git a/public/language/sc/admin/general/navigation.json b/public/language/sc/admin/general/navigation.json deleted file mode 100644 index 13dd01aae7..0000000000 --- a/public/language/sc/admin/general/navigation.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "icon": "Icon:", - "change-icon": "change", - "route": "Route:", - "tooltip": "Tooltip:", - "text": "Text:", - "text-class": "Text Class: optional", - "class": "Class: optional", - "id": "ID: optional", - - "properties": "Properties:", - "groups": "Groups:", - "open-new-window": "Open in a new window", - - "btn.delete": "Delete", - "btn.disable": "Disable", - "btn.enable": "Enable", - - "available-menu-items": "Available Menu Items", - "custom-route": "Custom Route", - "core": "core", - "plugin": "plugin" -} \ No newline at end of file diff --git a/public/language/sc/admin/general/social.json b/public/language/sc/admin/general/social.json deleted file mode 100644 index 23aedfcfaa..0000000000 --- a/public/language/sc/admin/general/social.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "post-sharing": "Post Sharing", - "info-plugins-additional": "Plugins can add additional networks for sharing posts.", - "save-success": "Successfully saved Post Sharing Networks!" -} \ No newline at end of file diff --git a/public/language/sc/admin/general/sounds.json b/public/language/sc/admin/general/sounds.json deleted file mode 100644 index 95ccbde0f1..0000000000 --- a/public/language/sc/admin/general/sounds.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "notifications": "Notifications", - "chat-messages": "Chat Messages", - "play-sound": "Play", - "incoming-message": "Incoming Message", - "outgoing-message": "Outgoing Message", - "upload-new-sound": "Upload New Sound", - "saved": "Settings Saved" -} \ No newline at end of file diff --git a/public/language/sc/admin/manage/digest.json b/public/language/sc/admin/manage/digest.json index 8f3661698a..38c634d1f6 100644 --- a/public/language/sc/admin/manage/digest.json +++ b/public/language/sc/admin/manage/digest.json @@ -13,6 +13,7 @@ "resent-single": "Manual digest resend completed", "resent-day": "Daily digest resent", "resent-week": "Weekly digest resent", + "resent-biweek": "Bi-Weekly digest resent", "resent-month": "Monthly digest resent", "null": "Never", "manual-run": "Manual digest run:", diff --git a/public/language/sc/admin/manage/users.json b/public/language/sc/admin/manage/users.json index 38a065b954..2a3c0c4829 100644 --- a/public/language/sc/admin/manage/users.json +++ b/public/language/sc/admin/manage/users.json @@ -47,6 +47,7 @@ "users.uid": "uid", "users.username": "username", "users.email": "email", + "users.no-email": "(no email)", "users.ip": "IP", "users.postcount": "postcount", "users.reputation": "reputation", diff --git a/public/language/sc/admin/menu.json b/public/language/sc/admin/menu.json index 1c5ea73b4f..5b22fbeb36 100644 --- a/public/language/sc/admin/menu.json +++ b/public/language/sc/admin/menu.json @@ -4,6 +4,7 @@ "dashboard/logins": "Logins", "dashboard/users": "Users", "dashboard/topics": "Topics", + "dashboard/searches": "Searches", "section-general": "General", "section-manage": "Manage", diff --git a/public/language/sc/admin/settings/email.json b/public/language/sc/admin/settings/email.json index 9bdbde26a6..65f579d28c 100644 --- a/public/language/sc/admin/settings/email.json +++ b/public/language/sc/admin/settings/email.json @@ -6,7 +6,7 @@ "from-help": "The from name to display in the email.", "smtp-transport": "SMTP Transport", - "smtp-transport.enabled": "Use an external email server to send emails", + "smtp-transport.enabled": "Enable SMTP Transport", "smtp-transport-help": "You can select from a list of well-known services or enter a custom one.", "smtp-transport.service": "Select a service", "smtp-transport.service-custom": "Custom Service", @@ -37,6 +37,10 @@ "subscriptions.hour": "Digest Hour", "subscriptions.hour-help": "Please enter a number representing the hour to send scheduled email digests (e.g. 0 for midnight, 17 for 5:00pm). Keep in mind that this is the hour according to the server itself, and may not exactly match your system clock.
The approximate server time is:
The next daily digest is scheduled to be sent ", "notifications.remove-images": "Remove images from email notifications", + "require-email-address": "Require new users to specify an email address", + "require-email-address-warning": "By default, users can opt-out of entering an email address. Enabling this option means they have to enter an email address in order to proceed with registration. It does not ensure user will enter a real email address, nor even an address they own.", "include-unverified-emails": "Send emails to recipients who have not explicitly confirmed their emails", - "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws." -} \ No newline at end of file + "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws.", + "prompt": "Prompt users to enter or confirm their emails", + "prompt-help": "If a user does not have an email set, or their email is not confirmed, a warning will be shown on screen." +} diff --git a/public/language/sc/admin/settings/general.json b/public/language/sc/admin/settings/general.json index be7df90870..29b939861b 100644 --- a/public/language/sc/admin/settings/general.json +++ b/public/language/sc/admin/settings/general.json @@ -3,9 +3,9 @@ "title": "Site Title", "title.short": "Short Title", "title.short-placeholder": "If no short title is specified, the site title will be used", - "title.url": "URL", + "title.url": "Title Link URL", "title.url-placeholder": "The URL of the site title", - "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.", + "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "title.name": "Your Community Name", "title.show-in-header": "Show Site Title in Header", "browser-title": "Browser Title", @@ -20,9 +20,9 @@ "logo.image": "Image", "logo.image-placeholder": "Path to a logo to display on forum header", "logo.upload": "Upload", - "logo.url": "URL", + "logo.url": "Logo Link URL", "logo.url-placeholder": "The URL of the site logo", - "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.", + "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "logo.alt-text": "Alt Text", "log.alt-text-placeholder": "Alternative text for accessibility", "favicon": "Favicon", @@ -47,4 +47,4 @@ "undo-timeout": "Undo Timeout", "undo-timeout-help": "Some operations such as moving topics will allow for the moderator to undo their action within a certain timeframe. Set to 0 to disable undo completely.", "topic-tools": "Topic Tools" -} \ No newline at end of file +} diff --git a/public/language/sc/admin/settings/navigation.json b/public/language/sc/admin/settings/navigation.json index 13dd01aae7..7baca85096 100644 --- a/public/language/sc/admin/settings/navigation.json +++ b/public/language/sc/admin/settings/navigation.json @@ -11,6 +11,8 @@ "properties": "Properties:", "groups": "Groups:", "open-new-window": "Open in a new window", + "dropdown": "Dropdown", + "dropdown-placeholder": "Place your dropdown menu items below, ie:
<li><a href="https://myforum.com">Link 1</a></li>", "btn.delete": "Delete", "btn.disable": "Disable", @@ -20,4 +22,4 @@ "custom-route": "Custom Route", "core": "core", "plugin": "plugin" -} \ No newline at end of file +} diff --git a/public/language/sc/admin/settings/notifications.json b/public/language/sc/admin/settings/notifications.json index da6c9680a3..c6d8b928ce 100644 --- a/public/language/sc/admin/settings/notifications.json +++ b/public/language/sc/admin/settings/notifications.json @@ -2,5 +2,6 @@ "notifications": "Notifications", "welcome-notification": "Welcome Notification", "welcome-notification-link": "Welcome Notification Link", - "welcome-notification-uid": "Welcome Notification User (UID)" + "welcome-notification-uid": "Welcome Notification User (UID)", + "post-queue-notification-uid": "Post Queue User (UID)" } \ No newline at end of file diff --git a/public/language/sc/admin/settings/post.json b/public/language/sc/admin/settings/post.json index 27493aafbd..ab8245738c 100644 --- a/public/language/sc/admin/settings/post.json +++ b/public/language/sc/admin/settings/post.json @@ -40,6 +40,7 @@ "teaser.last-post": "Last – Show the latest post, including the original post, if no replies", "teaser.last-reply": "Last – Show the latest reply, or a \"No replies\" placeholder if no replies", "teaser.first": "First", + "showPostPreviewsOnHover": "Show a preview of posts when mouse overed", "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", @@ -56,6 +57,9 @@ "composer.show-help": "Show \"Help\" tab", "composer.enable-plugin-help": "Allow plugins to add content to the help tab", "composer.custom-help": "Custom Help Text", + "backlinks": "Backlinks", + "backlinks.enabled": "Enable topic backlinks", + "backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.", "ip-tracking": "IP Tracking", "ip-tracking.each-post": "Track IP Address for each post", "enable-post-history": "Enable Post History" diff --git a/public/language/sc/admin/settings/user.json b/public/language/sc/admin/settings/user.json index 48be13b75e..7923bf8cbe 100644 --- a/public/language/sc/admin/settings/user.json +++ b/public/language/sc/admin/settings/user.json @@ -71,6 +71,7 @@ "digest-freq.off": "Off", "digest-freq.daily": "Daily", "digest-freq.weekly": "Weekly", + "digest-freq.biweekly": "Bi-Weekly", "digest-freq.monthly": "Monthly", "email-chat-notifs": "Send an email if a new chat message arrives and I am not online", "email-post-notif": "Send an email when replies are made to topics I am subscribed to", diff --git a/public/language/sc/error.json b/public/language/sc/error.json index b806ec9367..6eb0983661 100644 --- a/public/language/sc/error.json +++ b/public/language/sc/error.json @@ -34,8 +34,9 @@ "email-invited": "Email was already invited", "email-not-confirmed": "Posting in some categories or topics is enabled once your email is confirmed, please click here to send a confirmation email.", "email-not-confirmed-chat": "You are unable to chat until your email is confirmed, please click here to confirm your email.", - "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You won't be able to post or chat until your email is confirmed.", - "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery. Please click here to enter an email.", + "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You may not be able to post in some categories or chat until your email is confirmed.", + "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery, and may be necessary for chatting and posting in some categories. Please click here to enter an email.", + "user-doesnt-have-email": "User \"%1\" does not have an email set.", "email-confirm-failed": "We could not confirm your email, please try again later.", "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", "sendmail-not-found": "The sendmail executable could not be found, please ensure it is installed and executable by the user running NodeBB.", @@ -103,6 +104,7 @@ "already-bookmarked": "You have already bookmarked this post", "already-unbookmarked": "You have already unbookmarked this post", "cant-ban-other-admins": "You can't ban other admins!", + "cant-make-banned-users-admin": "You can't make banned users admin.", "cant-remove-last-admin": "You are the only administrator. Add another user as an administrator before removing yourself as admin", "account-deletion-disabled": "Account deletion is disabled", "cant-delete-admin": "Remove administrator privileges from this account before attempting to delete it.", diff --git a/public/language/sc/modules.json b/public/language/sc/modules.json index c356c23ef2..9b26a1f13a 100644 --- a/public/language/sc/modules.json +++ b/public/language/sc/modules.json @@ -54,7 +54,7 @@ "composer.formatting.strikethrough": "Strikethrough", "composer.formatting.code": "Code", "composer.formatting.link": "Link", - "composer.formatting.picture": "Picture", + "composer.formatting.picture": "Image Link", "composer.upload-picture": "Upload Image", "composer.upload-file": "Upload File", "composer.zen_mode": "Zen Mode", diff --git a/public/language/sc/top.json b/public/language/sc/top.json new file mode 100644 index 0000000000..b8a05bfa5f --- /dev/null +++ b/public/language/sc/top.json @@ -0,0 +1,4 @@ +{ + "title": "Top", + "no_top_topics": "No top topics" +} \ No newline at end of file diff --git a/public/language/sc/topic.json b/public/language/sc/topic.json index 16d6bebb47..0bf27e5997 100644 --- a/public/language/sc/topic.json +++ b/public/language/sc/topic.json @@ -47,6 +47,7 @@ "restored-by": "Restored by", "moved-from-by": "Moved from %1 by", "queued-by": "Post queued for approval →", + "backlink": "Referenced by", "bookmark_instructions": "Click here to return to the last read post in this thread.", "flag-post": "Flag this post", "flag-user": "Flag this user", @@ -138,6 +139,7 @@ "composer.handle_placeholder": "Enter your name/handle here", "composer.discard": "Lassa a Pèrdere", "composer.submit": "Imbia", + "composer.additional-options": "Additional Options", "composer.schedule": "Schedule", "composer.replying_to": "Replying to %1", "composer.new_topic": "Arresonada Noa", @@ -158,6 +160,7 @@ "newest_to_oldest": "Newest to Oldest", "most_votes": "Most Votes", "most_posts": "Most Posts", + "most_views": "Most Views", "stale.title": "Create new topic instead?", "stale.warning": "The topic you are replying to is quite old. Would you like to create a new topic instead, and reference this one in your reply?", "stale.create": "Create a new topic", diff --git a/public/language/sc/user.json b/public/language/sc/user.json index 75bb5d915a..47a7d04625 100644 --- a/public/language/sc/user.json +++ b/public/language/sc/user.json @@ -94,6 +94,7 @@ "digest_off": "Off", "digest_daily": "Daily", "digest_weekly": "Weekly", + "digest_biweekly": "Bi-Weekly", "digest_monthly": "Monthly", "has_no_follower": "Custu impitadore non tenet perunu sighidore :(", "follows_no_one": "Custu impitadore no est sighende nissunu :(", diff --git a/public/language/sk/admin/advanced/events.json b/public/language/sk/admin/advanced/events.json index 4e1313ab92..7f68cdf8bf 100644 --- a/public/language/sk/admin/advanced/events.json +++ b/public/language/sk/admin/advanced/events.json @@ -3,6 +3,7 @@ "no-events": "Žiadne nové udalosti", "control-panel": "Ovládací panel udalostí", "delete-events": "Delete Events", + "confirm-delete-all-events": "Are you sure you want to delete all logged events?", "filters": "Filters", "filters-apply": "Apply Filters", "filter-type": "Event Type", diff --git a/public/language/sk/admin/dashboard.json b/public/language/sk/admin/dashboard.json index 6fef088b74..b1c6670e81 100644 --- a/public/language/sk/admin/dashboard.json +++ b/public/language/sk/admin/dashboard.json @@ -56,8 +56,8 @@ "active-users.total": "Celkovo", "active-users.connections": "Pripojenia", - "anonymous-registered-users": "Anonymný vs zaregistrovaný používatelia", - "anonymous": "Anonymné", + "guest-registered-users": "Guest vs Registered Users", + "guest": "Guest", "registered": "Zaregistrovaný", "user-presence": "Výskyt používateľa", @@ -68,6 +68,7 @@ "unread": "Neprečitané", "high-presence-topics": "Témy s vysokou účasťou", + "popular-searches": "Popular Searches", "graphs.page-views": "Zobrazenia stránok", "graphs.page-views-registered": "Page Views Registered", @@ -75,13 +76,14 @@ "graphs.page-views-bot": "Page Views Bot", "graphs.unique-visitors": "Unikátny navštevníci", "graphs.registered-users": "Zarestrovaný užívatelia", - "graphs.anonymous-users": "Neznámy užívatelia", + "graphs.guest-users": "Guest Users", "last-restarted-by": "Posledná obnova od", "no-users-browsing": "Žiadni používatelia neprehliadajú", "back-to-dashboard": "Back to Dashboard", "details.no-users": "No users have joined within the selected timeframe", "details.no-topics": "No topics have been posted within the selected timeframe", + "details.no-searches": "No searches have been made yet", "details.no-logins": "No logins have been recorded within the selected timeframe", "details.logins-static": "NodeBB only saves session data for %1 days, and so this table below will only show the most recently active sessions", "details.logins-login-time": "Login Time" diff --git a/public/language/sk/admin/development/info.json b/public/language/sk/admin/development/info.json index 1fec788589..62f5cb9863 100644 --- a/public/language/sk/admin/development/info.json +++ b/public/language/sk/admin/development/info.json @@ -8,7 +8,11 @@ "nodejs": "nodejs", "online": "pripojený", "git": "git", - "memory": "pamäť", + "process-memory": "process memory", + "system-memory": "system memory", + "used-memory-process": "Used memory by process", + "used-memory-os": "Used system memory", + "total-memory-os": "Total system memory", "load": "system load", "cpu-usage": "cpu usage", "uptime": "čas spustenia", diff --git a/public/language/sk/admin/extend/rewards.json b/public/language/sk/admin/extend/rewards.json index e5a4a84acd..87a475ac61 100644 --- a/public/language/sk/admin/extend/rewards.json +++ b/public/language/sk/admin/extend/rewards.json @@ -8,8 +8,6 @@ "delete": "Odstrániť", "enable": "Povoliť", "disable": "Zakázať", - "control-panel": "Kontrola odmien", - "new-reward": "Nová odmena", "alert.delete-success": "Odmena bola úspešne vymazaná", "alert.no-inputs-found": "Nepovolená odmena - nebol nájdený žiadny záznam.", diff --git a/public/language/sk/admin/general/dashboard.json b/public/language/sk/admin/general/dashboard.json deleted file mode 100644 index fd1f988bb3..0000000000 --- a/public/language/sk/admin/general/dashboard.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "forum-traffic": "Prevádzka fóra", - "page-views": "Zobrazenia stránok", - "unique-visitors": "Jedineční návštevníci", - "new-users": "New Users", - "posts": "Príspevky", - "topics": "Témy", - "page-views-seven": "Posledných 7 dní", - "page-views-thirty": "Posledných 30 dní", - "page-views-last-day": "Posledných 24 hodín", - "page-views-custom": "Podľa rozsahu dátumu", - "page-views-custom-start": "Začiatok rozsahu", - "page-views-custom-end": "Koniec rozsahu", - "page-views-custom-help": "Zadajte rozsah obdobia zobrazenia stránok, ktoré chcete vidieť. Ak nie je obdobie nastavené, predvolený formát je YYYY-MM-DD", - "page-views-custom-error": "Zadajte správny rozsah vo formáte YYYY-MM-DD", - - "stats.yesterday": "Yesterday", - "stats.today": "Today", - "stats.last-week": "Last Week", - "stats.this-week": "This Week", - "stats.last-month": "Last Month", - "stats.this-month": "This Month", - "stats.all": "Celé obdobie", - - "updates": "Aktualizácie", - "running-version": "Fungujete na NodeBB v%1.", - "keep-updated": "Vždy udržujte NodeBB aktuálne kvôli bezpečnostným záplatám a opravám.", - "up-to-date": "

Máte aktuálnu verziu

", - "upgrade-available": "

Nová verzia (v%1) bola zverejnená. Zvážte aktualizáciu vášho NodeBB.

", - "prerelease-upgrade-available": "

Toto je zastaralá testovacia verzia NodeBB. Nová verzia (v%1) bola zverejnená. Zvážte aktualizáciu vášho NodeBB.

", - "prerelease-warning": "

Toto je skúšobná verzia NodeBB. Môžu sa vyskytnúť rôzne chyby.

", - "running-in-development": "Fórum beží vo vývojárskom režime a môže byť potenciálne zraniteľné. Kontaktujte správcu systému.", - "latest-lookup-failed": "

Chyba pri zistení poslednej dostupnej verzie NodeBB

", - - "notices": "Oznámenia", - "restart-not-required": "Reštart nie je potrebný", - "restart-required": "Je potrebný reštart", - "search-plugin-installed": "Vyhľadávací doplnok bol nainštalovaný", - "search-plugin-not-installed": "Vyhľadávací doplnok nebol nainštalovaný", - "search-plugin-tooltip": "Pre aktivácie funkcie vyhľadávania, nainštalujte rozšírenie pre hľadanie zo stránky rozšírení.", - - "control-panel": "Ovládanie systému", - "rebuild-and-restart": "Znovu zostaviť a reštartovať", - "restart": "Reštartovať", - "restart-warning": "Znovu zostavenie alebo reštartovanie NodeBB odpojí všetky existujúce pripojenia na niekoľko sekúnd.", - "restart-disabled": "Znovu zostavenie a reštartovanie vášho NodeBB bolo zablokované, pretože sa nezdá, že ste bol pripojený cez príslušného „daemona”.", - "maintenance-mode": "Režim údržby", - "maintenance-mode-title": "Pre nastavenia režimu údržby NodeBB, kliknite sem", - "realtime-chart-updates": "Aktualizácie grafov v reálnom čase", - - "active-users": "Aktívny užívatelia", - "active-users.users": "Užívatelia", - "active-users.guests": "Hostia", - "active-users.total": "Celkovo", - "active-users.connections": "Pripojenia", - - "anonymous-registered-users": "Anonymný vs zaregistrovaný používatelia", - "anonymous": "Anonymné", - "registered": "Zaregistrovaný", - - "user-presence": "Výskyt používateľa", - "on-categories": "V zozname kategórií", - "reading-posts": "Čítanie príspevkov", - "browsing-topics": "Prehľadávanie tém", - "recent": "Nedávne", - "unread": "Neprečitané", - - "high-presence-topics": "Témy s vysokou účasťou", - - "graphs.page-views": "Zobrazenia stránok", - "graphs.page-views-registered": "Page Views Registered", - "graphs.page-views-guest": "Page Views Guest", - "graphs.page-views-bot": "Page Views Bot", - "graphs.unique-visitors": "Unikátny navštevníci", - "graphs.registered-users": "Zarestrovaný užívatelia", - "graphs.anonymous-users": "Neznámy užívatelia", - "last-restarted-by": "Posledná obnova od", - "no-users-browsing": "Žiadni používatelia neprehliadajú" -} diff --git a/public/language/sk/admin/general/homepage.json b/public/language/sk/admin/general/homepage.json deleted file mode 100644 index 08e12e04ca..0000000000 --- a/public/language/sk/admin/general/homepage.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "home-page": "Domovská stránka", - "description": "Vyberte, akú stránku sa zobrazí, keď sa používatelia dostanú do koreňovej adresy URL vášho fóra.", - "home-page-route": "Cesta k domovskej stránke", - "custom-route": "Upraviť cestu", - "allow-user-home-pages": "Povoliť používateľom domovské stránky", - "home-page-title": "Titulok domovskej stránky (Predvolený „Domov”)" -} \ No newline at end of file diff --git a/public/language/sk/admin/general/languages.json b/public/language/sk/admin/general/languages.json deleted file mode 100644 index 96072b9642..0000000000 --- a/public/language/sk/admin/general/languages.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "language-settings": "Jazykové nastavenia", - "description": "Predvolený jazyk určuje nastavenie jazyka pre všetkých používateľov navštevujúcich vaše fórum.
Každý používateľ si môže potom nastaviť predvolený jazyk na stránke nastavenia účtu.", - "default-language": "Predvolený jazyk", - "auto-detect": "Automaticky rozpoznávať nastavenie jazyka pre hostí" -} \ No newline at end of file diff --git a/public/language/sk/admin/general/navigation.json b/public/language/sk/admin/general/navigation.json deleted file mode 100644 index 24125b086b..0000000000 --- a/public/language/sk/admin/general/navigation.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "icon": "Ikona:", - "change-icon": "zmeniť", - "route": "Cesta:", - "tooltip": "Tip:", - "text": "Text:", - "text-class": "Textová trieda: doporučené", - "class": "Class: optional", - "id": "ID: doporučené", - - "properties": "Vlastnosti:", - "groups": "Groups:", - "open-new-window": "Otvoriť v novom okne", - - "btn.delete": "Odstrániť", - "btn.disable": "Zakázať", - "btn.enable": "Povoliť", - - "available-menu-items": "Dostupné položky ponuky", - "custom-route": "Upraviť cestu", - "core": "jadro", - "plugin": "zásuvný modul" -} \ No newline at end of file diff --git a/public/language/sk/admin/general/social.json b/public/language/sk/admin/general/social.json deleted file mode 100644 index 0b19aa798a..0000000000 --- a/public/language/sk/admin/general/social.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "post-sharing": "Zdieľanie príspevku", - "info-plugins-additional": "Doplnky môžu pridávať ďalšie siete na zdieľanie príspevkov.", - "save-success": "Úspešne uložené siete zdieľajúce príspevky." -} \ No newline at end of file diff --git a/public/language/sk/admin/general/sounds.json b/public/language/sk/admin/general/sounds.json deleted file mode 100644 index c408efe93f..0000000000 --- a/public/language/sk/admin/general/sounds.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "notifications": "Oznámenia", - "chat-messages": "Správy konverzácie", - "play-sound": "Prehrať", - "incoming-message": "Prichádzajúca správa", - "outgoing-message": "Odchádzajúca správa", - "upload-new-sound": "Nahrať novú zvuk", - "saved": "Nastavenie bolo uložené" -} \ No newline at end of file diff --git a/public/language/sk/admin/manage/digest.json b/public/language/sk/admin/manage/digest.json index 8f3661698a..38c634d1f6 100644 --- a/public/language/sk/admin/manage/digest.json +++ b/public/language/sk/admin/manage/digest.json @@ -13,6 +13,7 @@ "resent-single": "Manual digest resend completed", "resent-day": "Daily digest resent", "resent-week": "Weekly digest resent", + "resent-biweek": "Bi-Weekly digest resent", "resent-month": "Monthly digest resent", "null": "Never", "manual-run": "Manual digest run:", diff --git a/public/language/sk/admin/manage/flags.json b/public/language/sk/admin/manage/flags.json deleted file mode 100644 index 4b11c7990d..0000000000 --- a/public/language/sk/admin/manage/flags.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "daily": "Daily flags", - "by-user": "Označené používateľom", - "by-user-search": "Vyhľadávať označené príspevky podľa používateľa", - "category": "Category", - "sort-by": "Sort By", - "sort-by.most-flags": "Najviac označené", - "sort-by.most-recent": "Most Recent", - "search": "Search", - "dismiss-all": "Dismiss All", - "none-flagged": "Žiadne označené príspevky!", - "posted-in": "Posted in %1", - "read-more": "Read More", - "flagged-x-times": "This post has been flagged %1 time(s):", - "dismiss": "Dismiss this Flag", - "delete-post": "Delete the Post", - - "alerts.confirm-delete-post": "Do you really want to delete this post?" -} \ No newline at end of file diff --git a/public/language/sk/admin/manage/users.json b/public/language/sk/admin/manage/users.json index 7b569e6ceb..5494728eae 100644 --- a/public/language/sk/admin/manage/users.json +++ b/public/language/sk/admin/manage/users.json @@ -47,6 +47,7 @@ "users.uid": "uid", "users.username": "používateľské meno", "users.email": "e-mail", + "users.no-email": "(no email)", "users.ip": "IP", "users.postcount": "počet príspevkov", "users.reputation": "reputácia", diff --git a/public/language/sk/admin/menu.json b/public/language/sk/admin/menu.json index a1f427f47c..12aa2962be 100644 --- a/public/language/sk/admin/menu.json +++ b/public/language/sk/admin/menu.json @@ -4,6 +4,7 @@ "dashboard/logins": "Logins", "dashboard/users": "Users", "dashboard/topics": "Topics", + "dashboard/searches": "Searches", "section-general": "Všeobecné", "section-manage": "Spravovať", diff --git a/public/language/sk/admin/settings/email.json b/public/language/sk/admin/settings/email.json index 1ecd846835..894bd512e3 100644 --- a/public/language/sk/admin/settings/email.json +++ b/public/language/sk/admin/settings/email.json @@ -6,7 +6,7 @@ "from-help": "Zobrazené meno v e-maily v - Od", "smtp-transport": "Prenos SMTP", - "smtp-transport.enabled": "Pre odosielanie e-mailov použiť externý e-mailový server", + "smtp-transport.enabled": "Enable SMTP Transport", "smtp-transport-help": "Zo zoznamu môžete vybrať známe služby alebo zadať vlastné.", "smtp-transport.service": "Vyberte službu", "smtp-transport.service-custom": "Používateľská služba", @@ -37,6 +37,10 @@ "subscriptions.hour": "Digest Hour", "subscriptions.hour-help": "Please enter a number representing the hour to send scheduled email digests (e.g. 0 for midnight, 17 for 5:00pm). Keep in mind that this is the hour according to the server itself, and may not exactly match your system clock.
The approximate server time is:
The next daily digest is scheduled to be sent ", "notifications.remove-images": "Remove images from email notifications", + "require-email-address": "Require new users to specify an email address", + "require-email-address-warning": "By default, users can opt-out of entering an email address. Enabling this option means they have to enter an email address in order to proceed with registration. It does not ensure user will enter a real email address, nor even an address they own.", "include-unverified-emails": "Send emails to recipients who have not explicitly confirmed their emails", - "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws." -} \ No newline at end of file + "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws.", + "prompt": "Prompt users to enter or confirm their emails", + "prompt-help": "If a user does not have an email set, or their email is not confirmed, a warning will be shown on screen." +} diff --git a/public/language/sk/admin/settings/general.json b/public/language/sk/admin/settings/general.json index 2d9a7f6a43..697f33a976 100644 --- a/public/language/sk/admin/settings/general.json +++ b/public/language/sk/admin/settings/general.json @@ -3,9 +3,9 @@ "title": "Názov stránky", "title.short": "Short Title", "title.short-placeholder": "If no short title is specified, the site title will be used", - "title.url": "URL", + "title.url": "Title Link URL", "title.url-placeholder": "URL názov stránky", - "title.url-help": "Ak bude kliknuté na názov, používateľ bude presmerovaný na túto adresu. Ak zostane prázdne, užívateľ bude odoslaný na index fóra.", + "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "title.name": "Názov vašej komunity", "title.show-in-header": "Zobraziť názov stránky v hlavičke", "browser-title": "Názov prehliadača", @@ -20,9 +20,9 @@ "logo.image": "Obrázok", "logo.image-placeholder": "Cesta k logu, aby mohlo byť zobrazené v hlavičke fóra", "logo.upload": "Nahrať", - "logo.url": "URL", + "logo.url": "Logo Link URL", "logo.url-placeholder": "URL logo stránky", - "logo.url-help": "Ak bude kliknuté na logo, používateľ bude presmerovaný na túto adresu. Ak zostane prázdne, používateľ bude presmerovaný na index fóra.", + "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "logo.alt-text": "Opisujúci text", "log.alt-text-placeholder": "Alternatívny text pre prístupnosť", "favicon": "Ikona (favicon)", @@ -47,4 +47,4 @@ "undo-timeout": "Undo Timeout", "undo-timeout-help": "Some operations such as moving topics will allow for the moderator to undo their action within a certain timeframe. Set to 0 to disable undo completely.", "topic-tools": "Topic Tools" -} \ No newline at end of file +} diff --git a/public/language/sk/admin/settings/navigation.json b/public/language/sk/admin/settings/navigation.json index 24125b086b..59aef4df0b 100644 --- a/public/language/sk/admin/settings/navigation.json +++ b/public/language/sk/admin/settings/navigation.json @@ -11,6 +11,8 @@ "properties": "Vlastnosti:", "groups": "Groups:", "open-new-window": "Otvoriť v novom okne", + "dropdown": "Dropdown", + "dropdown-placeholder": "Place your dropdown menu items below, ie:
<li><a href="https://myforum.com">Link 1</a></li>", "btn.delete": "Odstrániť", "btn.disable": "Zakázať", @@ -20,4 +22,4 @@ "custom-route": "Upraviť cestu", "core": "jadro", "plugin": "zásuvný modul" -} \ No newline at end of file +} diff --git a/public/language/sk/admin/settings/notifications.json b/public/language/sk/admin/settings/notifications.json index 188130c185..a873f10b02 100644 --- a/public/language/sk/admin/settings/notifications.json +++ b/public/language/sk/admin/settings/notifications.json @@ -2,5 +2,6 @@ "notifications": "Oznámenia", "welcome-notification": "Uvítacie oznámenie", "welcome-notification-link": "Odkaz na uvítanie", - "welcome-notification-uid": "Uvítanie používateľa (UID)" + "welcome-notification-uid": "Uvítanie používateľa (UID)", + "post-queue-notification-uid": "Post Queue User (UID)" } \ No newline at end of file diff --git a/public/language/sk/admin/settings/post.json b/public/language/sk/admin/settings/post.json index a99c0684e0..8c4f8419f0 100644 --- a/public/language/sk/admin/settings/post.json +++ b/public/language/sk/admin/settings/post.json @@ -40,6 +40,7 @@ "teaser.last-post": "Posledný - zobrazenie posledného príspevku, vrátane hlavného príspevku, ak nie sú odpovede", "teaser.last-reply": "Posledný - zobrazenie poslednej odpovede, alebo ak nie sú žiadne odpovede textu „Bez odpovede”", "teaser.first": "Prvý", + "showPostPreviewsOnHover": "Show a preview of posts when mouse overed", "unread": "Nastavenia neprečítaných", "unread.cutoff": "Dni ukončenia neprečítaných", "unread.min-track-last": "Minimálny počet príspevkov v téme pred posledným prečítaním", @@ -56,6 +57,9 @@ "composer.show-help": "Zobraziť záložku „Nápoveda”", "composer.enable-plugin-help": "Povoliť zásuvné moduly pre pridanie obsahu do záložky nápovedy", "composer.custom-help": "Používateľský text nápovedy", + "backlinks": "Backlinks", + "backlinks.enabled": "Enable topic backlinks", + "backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.", "ip-tracking": "Sledovanie IP adresy", "ip-tracking.each-post": "Sledovať IP adresu pri každom príspevku", "enable-post-history": "Povoliť históriu príspevkov" diff --git a/public/language/sk/admin/settings/user.json b/public/language/sk/admin/settings/user.json index e35c29d33f..eac0739010 100644 --- a/public/language/sk/admin/settings/user.json +++ b/public/language/sk/admin/settings/user.json @@ -71,6 +71,7 @@ "digest-freq.off": "Vypnuté", "digest-freq.daily": "Denne", "digest-freq.weekly": "Týždenne", + "digest-freq.biweekly": "Bi-Weekly", "digest-freq.monthly": "Mesačne", "email-chat-notifs": "Poslať mi e-mail, ak nie som online a dorazí mi nová správa z konverzácie", "email-post-notif": "Poslať e-mail, ak sa objaví odpoveď v téme, ktorú sledujem", diff --git a/public/language/sk/error.json b/public/language/sk/error.json index a8e8e29498..81d83d10ab 100644 --- a/public/language/sk/error.json +++ b/public/language/sk/error.json @@ -34,8 +34,9 @@ "email-invited": "Email was already invited", "email-not-confirmed": "Posting in some categories or topics is enabled once your email is confirmed, please click here to send a confirmation email.", "email-not-confirmed-chat": "Nemôžete vytvoriť konverzáciu pokiaľ Váš e-mail nebude overený. Prosím kliknite sem, pre overenie Vášho e-mailu.", - "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You won't be able to post or chat until your email is confirmed.", - "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery. Please click here to enter an email.", + "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You may not be able to post in some categories or chat until your email is confirmed.", + "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery, and may be necessary for chatting and posting in some categories. Please click here to enter an email.", + "user-doesnt-have-email": "User \"%1\" does not have an email set.", "email-confirm-failed": "Momentálne nemôžeme overiť Váš e-mail, prosím zopakujte to neskôr.", "confirm-email-already-sent": "Overovací e-mail už bol odoslaný. Prosím počkajte %1 minút(y) k odoslaniu ďalšieho.", "sendmail-not-found": "Odoslaný spúšťač nebol nájdený, prosím uistite sa že je nainštalovaný a spustiteľný užívateľom používajúcim NodeBB.", @@ -103,6 +104,7 @@ "already-bookmarked": "Tento príspevok máte už medzi záložkami", "already-unbookmarked": "Tento príspevok už nemáte medzi záložkami", "cant-ban-other-admins": "Nemôžte zablokovať iných správcov.", + "cant-make-banned-users-admin": "You can't make banned users admin.", "cant-remove-last-admin": "Momentálne ste jediný správca. Najskôr pridajte ďalšieho užívateľa za správcu predtým, ako zrušíte svoje výsady správcu", "account-deletion-disabled": "Account deletion is disabled", "cant-delete-admin": "Odobrať oprávnenie správcu z tohto účtu pred pokusom ho odstrániť.", diff --git a/public/language/sk/modules.json b/public/language/sk/modules.json index 9df58064b4..53a270124f 100644 --- a/public/language/sk/modules.json +++ b/public/language/sk/modules.json @@ -54,7 +54,7 @@ "composer.formatting.strikethrough": "Prečiarknuté", "composer.formatting.code": "Code", "composer.formatting.link": "Odkaz", - "composer.formatting.picture": "Obrázok", + "composer.formatting.picture": "Image Link", "composer.upload-picture": "Nahrať obrázok", "composer.upload-file": "Nahrať súbor", "composer.zen_mode": "Režim Zen", diff --git a/public/language/sk/top.json b/public/language/sk/top.json new file mode 100644 index 0000000000..b8a05bfa5f --- /dev/null +++ b/public/language/sk/top.json @@ -0,0 +1,4 @@ +{ + "title": "Top", + "no_top_topics": "No top topics" +} \ No newline at end of file diff --git a/public/language/sk/topic.json b/public/language/sk/topic.json index 5b12c39a1b..ba19316c05 100644 --- a/public/language/sk/topic.json +++ b/public/language/sk/topic.json @@ -47,6 +47,7 @@ "restored-by": "Restored by", "moved-from-by": "Moved from %1 by", "queued-by": "Post queued for approval →", + "backlink": "Referenced by", "bookmark_instructions": "Kliknite sem pre návrat k poslednému prečítanému príspevku vo vlákne.", "flag-post": "Flag this post", "flag-user": "Flag this user", @@ -138,6 +139,7 @@ "composer.handle_placeholder": "Enter your name/handle here", "composer.discard": "Zahodiť", "composer.submit": "Odoslať", + "composer.additional-options": "Additional Options", "composer.schedule": "Schedule", "composer.replying_to": "Odpovedať na %1", "composer.new_topic": "Nová téma", @@ -158,6 +160,7 @@ "newest_to_oldest": "Od najnovších po najstaršie", "most_votes": "S najviac hlasmi", "most_posts": "S najviac príspevkami", + "most_views": "Most Views", "stale.title": "Vytvoriť novú tému namiesto?", "stale.warning": "Téma na ktorú odpovedáte je pomerne stará. Chceli by ste vytvoriť novú tému namiesto tejto, a odkazovať na ňu vo Vašej odpovedi?", "stale.create": "Vytvoriť novú tému", diff --git a/public/language/sk/user.json b/public/language/sk/user.json index 31fda96283..3a775de1bd 100644 --- a/public/language/sk/user.json +++ b/public/language/sk/user.json @@ -94,6 +94,7 @@ "digest_off": "Vypnuté", "digest_daily": "Denne", "digest_weekly": "Týždenne", + "digest_biweekly": "Bi-Weekly", "digest_monthly": "Mesačne", "has_no_follower": "Tohto užívateľa nikto nesleduje :(", "follows_no_one": "Tento užívateľ nikoho nesleduje :(", diff --git a/public/language/sl/admin/admin.json b/public/language/sl/admin/admin.json index 36f0fdde60..c7ac493d94 100644 --- a/public/language/sl/admin/admin.json +++ b/public/language/sl/admin/admin.json @@ -1,7 +1,7 @@ { - "alert.confirm-rebuild-and-restart": "Are you sure you wish to rebuild and restart NodeBB?", - "alert.confirm-restart": "Are you sure you wish to restart NodeBB?", + "alert.confirm-rebuild-and-restart": "Ste prepričani, da želite obnoviti in ponovno zagnati NodeBB?", + "alert.confirm-restart": "Ste prepričani, da želite znova zagnati NodeBB?", - "acp-title": "%1 | NodeBB Admin Control Panel", - "settings-header-contents": "Contents" + "acp-title": "%1 | NodeBB skrbniška nadzorna plošča", + "settings-header-contents": "Vsebine" } \ No newline at end of file diff --git a/public/language/sl/admin/advanced/cache.json b/public/language/sl/admin/advanced/cache.json index 2fd7984883..a6c69b5944 100644 --- a/public/language/sl/admin/advanced/cache.json +++ b/public/language/sl/admin/advanced/cache.json @@ -1,6 +1,6 @@ { - "post-cache": "Post Cache", - "percent-full": "%1% Full", - "post-cache-size": "Post Cache Size", - "items-in-cache": "Items in Cache" + "post-cache": "Predpomnilnik objav", + "percent-full": "%1%Zasedeno", + "post-cache-size": "Velikost predpomnilnika objav", + "items-in-cache": "Elementi v predpomnilniku" } \ No newline at end of file diff --git a/public/language/sl/admin/advanced/database.json b/public/language/sl/admin/advanced/database.json index 9167b381ed..a116b29172 100644 --- a/public/language/sl/admin/advanced/database.json +++ b/public/language/sl/admin/advanced/database.json @@ -2,51 +2,51 @@ "x-b": "%1 b", "x-mb": "%1 mb", "x-gb": "%1 gb", - "uptime-seconds": "Uptime in Seconds", - "uptime-days": "Uptime in Days", + "uptime-seconds": "Čas delovanja v sekundah", + "uptime-days": "Čas delovanja v dneh", "mongo": "Mongo", - "mongo.version": "MongoDB Version", - "mongo.storage-engine": "Storage Engine", - "mongo.collections": "Collections", - "mongo.objects": "Objects", - "mongo.avg-object-size": "Avg. Object Size", - "mongo.data-size": "Data Size", - "mongo.storage-size": "Storage Size", - "mongo.index-size": "Index Size", - "mongo.file-size": "File Size", - "mongo.resident-memory": "Resident Memory", - "mongo.virtual-memory": "Virtual Memory", + "mongo.version": "MongoDB verzija", + "mongo.storage-engine": "Pogon za shranjevanje", + "mongo.collections": "Zbirke", + "mongo.objects": "Predmeti", + "mongo.avg-object-size": "Povpr. velikost predmeta", + "mongo.data-size": "Velikost podatkov", + "mongo.storage-size": "Velikost shrambe", + "mongo.index-size": "Velikost indeksa", + "mongo.file-size": "Velikost datoteke", + "mongo.resident-memory": "Stalni spomin", + "mongo.virtual-memory": "Navidezni spomin", "mongo.mapped-memory": "Mapped Memory", "mongo.bytes-in": "Bytes In", "mongo.bytes-out": "Bytes Out", - "mongo.num-requests": "Number of Requests", + "mongo.num-requests": "Število zahtev", "mongo.raw-info": "MongoDB Raw Info", - "mongo.unauthorized": "NodeBB was unable to query the MongoDB database for relevant statistics. Please ensure that the user in use by NodeBB contains the "clusterMonitor" role for the "admin" database.", + "mongo.unauthorized": "NodeBB ni mogel poizvedovati po zbirki podatkov MongoDB o ustreznih statističnih podatkih. Prepričajte se, da uporabnik, ki ga uporablja NodeBB, ima vlogo "nadzornika gruče" za "administriranje" zbirke podatkov.", "redis": "Redis", - "redis.version": "Redis Version", - "redis.keys": "Keys", - "redis.expires": "Expires", - "redis.avg-ttl": "Average TTL", - "redis.connected-clients": "Connected Clients", + "redis.version": "Redis verzija", + "redis.keys": "Ključi", + "redis.expires": "Poteče", + "redis.avg-ttl": "Povprečni TTL", + "redis.connected-clients": "Povezane stranke", "redis.connected-slaves": "Connected Slaves", - "redis.blocked-clients": "Blocked Clients", - "redis.used-memory": "Used Memory", - "redis.memory-frag-ratio": "Memory Fragmentation Ratio", - "redis.total-connections-recieved": "Total Connections Received", - "redis.total-commands-processed": "Total Commands Processed", - "redis.iops": "Instantaneous Ops. Per Second", - "redis.iinput": "Instantaneous Input Per Second", - "redis.ioutput": "Instantaneous Output Per Second", - "redis.total-input": "Total Input", - "redis.total-output": "Total Ouput", + "redis.blocked-clients": "Blokirane stranke", + "redis.used-memory": "Uporabljen pomnilnik", + "redis.memory-frag-ratio": "Razmerje razdrobljenosti pomnilnika", + "redis.total-connections-recieved": "Prejete povezave skupaj", + "redis.total-commands-processed": "Obdelani ukazi skupaj", + "redis.iops": "Takojšnje operacije na sekundo", + "redis.iinput": "Takojšnji vnos na sekundo", + "redis.ioutput": "Takojšnji izhod na sekundo", + "redis.total-input": "Vnos skupaj", + "redis.total-output": "Izhod skupaj", "redis.keyspace-hits": "Keyspace Hits", "redis.keyspace-misses": "Keyspace Misses", "redis.raw-info": "Redis Raw Info", "postgres": "Postgres", - "postgres.version": "PostgreSQL Version", + "postgres.version": "PostgreSQL verzija", "postgres.raw-info": "Postgres Raw Info" } diff --git a/public/language/sl/admin/advanced/errors.json b/public/language/sl/admin/advanced/errors.json index 546f0f1508..4e5a5ae888 100644 --- a/public/language/sl/admin/advanced/errors.json +++ b/public/language/sl/admin/advanced/errors.json @@ -1,14 +1,14 @@ { - "figure-x": "Figure %1", - "error-events-per-day": "%1 events per day", - "error.404": "404 Not Found", - "error.503": "503 Service Unavailable", - "manage-error-log": "Manage Error Log", - "export-error-log": "Export Error Log (CSV)", - "clear-error-log": "Clear Error Log", - "route": "Route", - "count": "Count", - "no-routes-not-found": "Hooray! No 404 errors!", - "clear404-confirm": "Are you sure you wish to clear the 404 error logs?", - "clear404-success": "\"404 Not Found\" errors cleared" + "figure-x": "Slika %1", + "error-events-per-day": "%1 dogodkov na dan", + "error.404": "4040 ni najdeno", + "error.503": "503 storitev ni na voljo", + "manage-error-log": "Upravljaj dnevnik napak", + "export-error-log": "Izvozi dnevnik napak (CSV)", + "clear-error-log": "Počisti dnevnik napak", + "route": "Pot", + "count": "Število", + "no-routes-not-found": "Hura! Ni napak 404! ", + "clear404-confirm": "Ste prepričani, da želite izbrisati dnevnik napak 404?", + "clear404-success": "Napake \"404 ni najdeno\" so počiščene" } \ No newline at end of file diff --git a/public/language/sl/admin/advanced/events.json b/public/language/sl/admin/advanced/events.json index 56d9457971..a06766728e 100644 --- a/public/language/sl/admin/advanced/events.json +++ b/public/language/sl/admin/advanced/events.json @@ -1,12 +1,13 @@ { - "events": "Events", - "no-events": "There are no events", - "control-panel": "Events Control Panel", - "delete-events": "Delete Events", - "filters": "Filters", - "filters-apply": "Apply Filters", - "filter-type": "Event Type", - "filter-start": "Start Date", - "filter-end": "End Date", - "filter-perPage": "Per Page" + "events": "Dogodki", + "no-events": "Ni dogodkov", + "control-panel": "Nadzorna plošča za dogodke", + "delete-events": "Izbriši dogodke", + "confirm-delete-all-events": "Ali ste prepričani, da želite izbrisati vse zabeležene dogodke?", + "filters": "Filtri", + "filters-apply": "Uveljavi filtre", + "filter-type": "Tip dogodka", + "filter-start": "Začetni datum", + "filter-end": "Končni datum", + "filter-perPage": "Na stran" } \ No newline at end of file diff --git a/public/language/sl/admin/advanced/logs.json b/public/language/sl/admin/advanced/logs.json index b9de400e1c..14e4592111 100644 --- a/public/language/sl/admin/advanced/logs.json +++ b/public/language/sl/admin/advanced/logs.json @@ -1,7 +1,7 @@ { - "logs": "Logs", - "control-panel": "Logs Control Panel", - "reload": "Reload Logs", - "clear": "Clear Logs", - "clear-success": "Logs Cleared!" + "logs": "Dnevniki", + "control-panel": "Nadzorna plošča dnevnikov", + "reload": "Ponovno naloži dnevnike", + "clear": "Počisti dnevnike", + "clear-success": "Dnevniki so počiščeni!" } \ No newline at end of file diff --git a/public/language/sl/admin/appearance/customise.json b/public/language/sl/admin/appearance/customise.json index 97abb5ede5..744b60dc2e 100644 --- a/public/language/sl/admin/appearance/customise.json +++ b/public/language/sl/admin/appearance/customise.json @@ -1,16 +1,16 @@ { - "custom-css": "Custom CSS/LESS", + "custom-css": "CSS/LESS po meri", "custom-css.description": "Enter your own CSS/LESS declarations here, which will be applied after all other styles.", - "custom-css.enable": "Enable Custom CSS/LESS", + "custom-css.enable": "Omogoči CSS/LESS po meri", - "custom-js": "Custom Javascript", - "custom-js.description": "Enter your own javascript here. It will be executed after the page is loaded completely.", - "custom-js.enable": "Enable Custom Javascript", + "custom-js": "Javascript po meri", + "custom-js.description": "Tukaj vnesite svoj javascript. Izveden bo, ko se stran popolnoma naloži.", + "custom-js.enable": "Omogoči Javascript po meri", - "custom-header": "Custom Header", - "custom-header.description": "Enter custom HTML here (ex. Meta Tags, etc.), which will be appended to the <head> section of your forum's markup. Script tags are allowed, but are discouraged, as the Custom Javascript tab is available.", - "custom-header.enable": "Enable Custom Header", + "custom-header": "Glava po meri", + "custom-header.description": "Tukaj vnesite HTML po meri (npr. meta oznake itd.), ki bo dodan v & lt; head & gt; razdelek oznak vašega foruma. Oznake skript so dovoljene, vendar niso priporočljive, saj je na voljo zavihek Javascript po meri.", + "custom-header.enable": "Omogoči glavo po meri", - "custom-css.livereload": "Enable Live Reload", - "custom-css.livereload.description": "Enable this to force all sessions on every device under your account to refresh whenever you click save" + "custom-css.livereload": "Omogoči ponovno nalaganje v živo", + "custom-css.livereload.description": "Omogočite to, da se vse seje na vsaki napravi v vašem računu osvežijo, ko kliknete shrani" } \ No newline at end of file diff --git a/public/language/sl/admin/appearance/skins.json b/public/language/sl/admin/appearance/skins.json index 4db6fbdd8a..2d29a09f0d 100644 --- a/public/language/sl/admin/appearance/skins.json +++ b/public/language/sl/admin/appearance/skins.json @@ -1,9 +1,9 @@ { - "loading": "Loading Skins...", - "homepage": "Homepage", - "select-skin": "Select Skin", - "current-skin": "Current Skin", - "skin-updated": "Skin Updated", - "applied-success": "%1 skin was succesfully applied", - "revert-success": "Skin reverted to base colours" + "loading": "Nalagam preobleke...", + "homepage": "Domača stran", + "select-skin": "Izberi preobleko", + "current-skin": "Trenutna preobleka", + "skin-updated": "Preobleka je posodobljena", + "applied-success": "%1 preobleke je bilo uspešno uveljavljene", + "revert-success": "Preobleka je povrnjena v osnovne barve" } \ No newline at end of file diff --git a/public/language/sl/admin/appearance/themes.json b/public/language/sl/admin/appearance/themes.json index 597830f379..3942ae0820 100644 --- a/public/language/sl/admin/appearance/themes.json +++ b/public/language/sl/admin/appearance/themes.json @@ -1,11 +1,11 @@ { - "checking-for-installed": "Checking for installed themes...", - "homepage": "Homepage", - "select-theme": "Select Theme", - "current-theme": "Current Theme", - "no-themes": "No installed themes found", - "revert-confirm": "Are you sure you wish to restore the default NodeBB theme?", - "theme-changed": "Theme Changed", - "revert-success": "You have successfully reverted your NodeBB back to it's default theme.", - "restart-to-activate": "Please rebuild and restart your NodeBB to fully activate this theme." + "checking-for-installed": "Iščem nameščene teme...", + "homepage": "Domača stran", + "select-theme": "Izberi temo", + "current-theme": "Trenutna tema", + "no-themes": "Ni najdenih nameščenih tem", + "revert-confirm": "Ste prepričani, da želite obnoviti privzeto NodeBB temo?", + "theme-changed": "Tema je spremenjena", + "revert-success": "Uspešno ste povrnili vaš NodeBB nazaj na privzeto temo.", + "restart-to-activate": "Za popolno aktivacijo te teme obnovite in ponovno zaženete vaš NodeB." } \ No newline at end of file diff --git a/public/language/sl/admin/dashboard.json b/public/language/sl/admin/dashboard.json index 0de31d4917..7b8d329e5c 100644 --- a/public/language/sl/admin/dashboard.json +++ b/public/language/sl/admin/dashboard.json @@ -1,88 +1,90 @@ { - "forum-traffic": "Forum Traffic", - "page-views": "Page Views", - "unique-visitors": "Unique Visitors", - "logins": "Logins", - "new-users": "New Users", - "posts": "Posts", - "topics": "Topics", - "page-views-seven": "Last 7 Days", - "page-views-thirty": "Last 30 Days", - "page-views-last-day": "Last 24 hours", - "page-views-custom": "Custom Date Range", - "page-views-custom-start": "Range Start", - "page-views-custom-end": "Range End", - "page-views-custom-help": "Enter a date range of page views you would like to view. If no date picker is available, the accepted format is YYYY-MM-DD", - "page-views-custom-error": "Please enter a valid date range in the format YYYY-MM-DD", + "forum-traffic": "Promet na forumu", + "page-views": "Ogledi strani", + "unique-visitors": "Edinstveni obiskovalci", + "logins": "Prijave", + "new-users": "Novi uporabniki", + "posts": "Objave", + "topics": "Teme", + "page-views-seven": "Zadnjih 7 dni", + "page-views-thirty": "Zadnjih 30 dni", + "page-views-last-day": "Zadnjih 24 ur", + "page-views-custom": "Časovno obdobje po meri", + "page-views-custom-start": "Začetek obdobja", + "page-views-custom-end": "Konec obdobja", + "page-views-custom-help": "Vnesite časovno obdobje ogledov strani, ki bi si jih radi ogledali. Če izbirnik datumov ni na voljo, je sprejeta oblika LLLL-MM-DD", + "page-views-custom-error": "Vnesite veljavno časovno obdobje v obliki LLLL-MM-DD", - "stats.yesterday": "Yesterday", - "stats.today": "Today", - "stats.last-week": "Last Week", - "stats.this-week": "This Week", - "stats.last-month": "Last Month", - "stats.this-month": "This Month", - "stats.all": "All Time", + "stats.yesterday": "Včeraj", + "stats.today": "Danes", + "stats.last-week": "Prejšnji teden", + "stats.this-week": "Ta teden", + "stats.last-month": "Zadnji mesec", + "stats.this-month": "Ta mesec", + "stats.all": "Celotni čas", - "updates": "Updates", - "running-version": "You are running NodeBB v%1.", - "keep-updated": "Always make sure that your NodeBB is up to date for the latest security patches and bug fixes.", - "up-to-date": "

You are up-to-date

", - "upgrade-available": "

A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-upgrade-available": "

This is an outdated pre-release version of NodeBB. A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-warning": "

This is a pre-release version of NodeBB. Unintended bugs may occur.

", - "fallback-emailer-not-found": "Fallback emailer not found!", - "running-in-development": "Forum is running in development mode. The forum may be open to potential vulnerabilities; please contact your system administrator.", - "latest-lookup-failed": "

Failed to look up latest available version of NodeBB

", + "updates": "Posodobitve", + "running-version": " Teče NodeBB v%1.", + "keep-updated": "Vedno se prepričajte, da je vaš NodeBB posodobljen za najnovejše varnostne popravke in popravke napak.", + "up-to-date": "

Ste na tekočem

", + "upgrade-available": "

Izdana je bila nova različica (v%1). Premislite o posodobitvi vašega NodeBB.

", + "prerelease-upgrade-available": "

To je zastarela predizdajna različica NodeBB. Izšla je nova različica (v%1). Premislite o posodobitvi vašega NodeBB.

", + "prerelease-warning": "

To je predizdajna različica NodeBB. Pojavijo se lahko nenameravane napake.

", + "fallback-emailer-not-found": "Povratnega e-poštnega sporočila ni mogoče najti!", + "running-in-development": "Forum teče v razvojnem načinu. Forum je lahko odprt za potencialne ranljivosti; obrnite se na skrbnika sistema.", + "latest-lookup-failed": "

Najnovejše razpoložljive različice NodeBB ni bilo mogoče najti

", - "notices": "Notices", - "restart-not-required": "Restart not required", - "restart-required": "Restart required", - "search-plugin-installed": "Search Plugin installed", - "search-plugin-not-installed": "Search Plugin not installed", - "search-plugin-tooltip": "Install a search plugin from the plugin page in order to activate search functionality", + "notices": "Opombe", + "restart-not-required": "Ponovni zagon ni potreben", + "restart-required": "Potreben je ponovni zagon", + "search-plugin-installed": "Iskalni vtičnik je nameščen", + "search-plugin-not-installed": "Iskalni vtičnik ni nameščen", + "search-plugin-tooltip": "Za aktiviranje iskalne funkcije namestite iskalni vtičnik s strani vtičnika", - "control-panel": "System Control", - "rebuild-and-restart": "Rebuild & Restart", - "restart": "Restart", - "restart-warning": "Rebuilding or Restarting your NodeBB will drop all existing connections for a few seconds.", - "restart-disabled": "Rebuilding and Restarting your NodeBB has been disabled as you do not seem to be running it via the appropriate daemon.", - "maintenance-mode": "Maintenance Mode", - "maintenance-mode-title": "Click here to set up maintenance mode for NodeBB", - "realtime-chart-updates": "Realtime Chart Updates", + "control-panel": "Nadzor sistema", + "rebuild-and-restart": "Obnovi & ponovno zaženi", + "restart": "Ponovno zaženi", + "restart-warning": "Obnova ali ponovni zagon vašega NodeBB za nekaj sekund prekine vse obstoječe povezave.", + "restart-disabled": "Obnova in ponovni zagon vašega NodeBB sta onemogočena, saj se zdi, da ga ne izvajate prek ustreznega prikritega procesa.", + "maintenance-mode": "Način vzdrževanja", + "maintenance-mode-title": "Za nastavitev načina vzdrževanja za NodeBB kliknite tukaj", + "realtime-chart-updates": "Posodobitev grafikona v realnem času", - "active-users": "Active Users", - "active-users.users": "Users", - "active-users.guests": "Guests", - "active-users.total": "Total", - "active-users.connections": "Connections", + "active-users": "Aktivni uporabniki", + "active-users.users": "Uporabniki", + "active-users.guests": "Gostje", + "active-users.total": "Skupaj", + "active-users.connections": "Povezave", - "anonymous-registered-users": "Anonymous vs Registered Users", - "anonymous": "Anonymous", - "registered": "Registered", + "guest-registered-users": "Gostujoči napram registriranim uporabnikom", + "guest": "Gost", + "registered": "Registrirani", - "user-presence": "User Presence", - "on-categories": "On categories list", - "reading-posts": "Reading posts", - "browsing-topics": "Browsing topics", - "recent": "Recent", - "unread": "Unread", + "user-presence": "Prisotnost uporabnikov", + "on-categories": "Na seznam kategorij", + "reading-posts": "Branje objav", + "browsing-topics": "Brskanje po temah", + "recent": "Nedavno", + "unread": "Neprebrano", - "high-presence-topics": "High Presence Topics", + "high-presence-topics": "Teme z visoko prisotnostjo", + "popular-searches": "Priljubljena iskanja", - "graphs.page-views": "Page Views", - "graphs.page-views-registered": "Page Views Registered", - "graphs.page-views-guest": "Page Views Guest", - "graphs.page-views-bot": "Page Views Bot", - "graphs.unique-visitors": "Unique Visitors", - "graphs.registered-users": "Registered Users", - "graphs.anonymous-users": "Anonymous Users", - "last-restarted-by": "Last restarted by", - "no-users-browsing": "No users browsing", + "graphs.page-views": "Ogledov strani", + "graphs.page-views-registered": "Ogledov strani-registrirani", + "graphs.page-views-guest": "Ogledov strani-gosti", + "graphs.page-views-bot": "Ogledov strani-robot", + "graphs.unique-visitors": "Edinstveni obiskovalci", + "graphs.registered-users": "Registrirani uporabniki", + "graphs.guest-users": "Gostujoči uporabniki", + "last-restarted-by": "Nazadnje ponovno zagnal(a)", + "no-users-browsing": "Ne brska noben uporabnik", - "back-to-dashboard": "Back to Dashboard", - "details.no-users": "No users have joined within the selected timeframe", - "details.no-topics": "No topics have been posted within the selected timeframe", - "details.no-logins": "No logins have been recorded within the selected timeframe", - "details.logins-static": "NodeBB only saves session data for %1 days, and so this table below will only show the most recently active sessions", - "details.logins-login-time": "Login Time" + "back-to-dashboard": "Nazaj na nadzorno ploščo", + "details.no-users": "V izbranem časovnem okviru se ni pridružil noben uporabnik", + "details.no-topics": "V izbranem časovnem okviru ni bila objavljena nobena tema", + "details.no-searches": "Iskanja še niso bila izvedena", + "details.no-logins": "V izbranem časovnem okviru ni bila zabeležena nobena prijava", + "details.logins-static": "NodeBB shranjuje samo podatke o sejah za %1 dni, zato bo ta spodnja tabela prikazala samo zadnje aktivne seje", + "details.logins-login-time": "Čas prijave" } diff --git a/public/language/sl/admin/development/info.json b/public/language/sl/admin/development/info.json index 1003af1a5f..4c469cc598 100644 --- a/public/language/sl/admin/development/info.json +++ b/public/language/sl/admin/development/info.json @@ -1,21 +1,25 @@ { - "you-are-on": "You are on %1:%2", + "you-are-on": "Ste na %1:%2", "ip": "IP %1", - "nodes-responded": "%1 nodes responded within %2ms!", - "host": "host", + "nodes-responded": "%1 vozlišč se je odzvalo v %2ms!", + "host": "gostitelj", "primary": "primary / run jobs", "pid": "pid", "nodejs": "nodejs", - "online": "online", + "online": "na spletu", "git": "git", - "memory": "memory", - "load": "system load", - "cpu-usage": "cpu usage", - "uptime": "uptime", + "process-memory": "process memory", + "system-memory": "system memory", + "used-memory-process": "Used memory by process", + "used-memory-os": "Used system memory", + "total-memory-os": "Total system memory", + "load": "obremenitev sistema", + "cpu-usage": "uporaba procesorja", + "uptime": "čas delovanja", - "registered": "Registered", - "sockets": "Sockets", - "guests": "Guests", + "registered": "Registrirani", + "sockets": "Vtičnice", + "guests": "Gostje", "info": "Info" } \ No newline at end of file diff --git a/public/language/sl/admin/development/logger.json b/public/language/sl/admin/development/logger.json index 6ab9558149..ce73544633 100644 --- a/public/language/sl/admin/development/logger.json +++ b/public/language/sl/admin/development/logger.json @@ -1,12 +1,12 @@ { - "logger-settings": "Logger Settings", - "description": "By enabling the check boxes, you will receive logs to your terminal. If you specify a path, logs will then be saved to a file instead. HTTP logging is useful for collecting statistics about who, when, and what people access on your forum. In addition to logging HTTP requests, we can also log socket.io events. Socket.io logging, in combination with redis-cli monitor, can be very helpful for learning NodeBB's internals.", - "explanation": "Simply check/uncheck the logging settings to enable or disable logging on the fly. No restart needed.", - "enable-http": "Enable HTTP logging", - "enable-socket": "Enable socket.io event logging", - "file-path": "Path to log file", + "logger-settings": "Nastavitve beleženja", + "description": "Če omogočite potrditvena polja, boste prejemali dnevnike na svoj terminal. Če določite pot, se bodo dnevniki namesto tega shranili v datoteko. Zapisovanje HTTP je uporabno za zbiranje statističnih podatkov o tem, kdo, kdaj in do česa ljudje dostopajo na vašem forumu. Poleg beleženja zahtev HTTP lahko beležimo tudi dogodke socket.io. Zapisovanje Socket.io v kombinaciji z monitorjem redis-cli je lahko v veliko pomoč pri učenju notranjosti NodeBB.", + "explanation": "Preprosto preverite/počistite nastavitve beleženja, če želite omogočiti ali onemogočiti sprotno beleženje. Ponovni zagon ni potreben.", + "enable-http": "Omogoči HTTP prijave", + "enable-socket": "Omogoči beleženje dogodkov socket.io", + "file-path": "Pot do datoteke dnevnika", "file-path-placeholder": "/path/to/log/file.log ::: leave blank to log to your terminal", - "control-panel": "Logger Control Panel", - "update-settings": "Update Logger Settings" + "control-panel": "Nadzorna plošča beleženja", + "update-settings": "Posodobi nastavitve beleženja" } \ No newline at end of file diff --git a/public/language/sl/admin/extend/plugins.json b/public/language/sl/admin/extend/plugins.json index f7e60c4360..648edefde3 100644 --- a/public/language/sl/admin/extend/plugins.json +++ b/public/language/sl/admin/extend/plugins.json @@ -1,57 +1,57 @@ { "trending": "Trending", - "installed": "Installed", - "active": "Active", - "inactive": "Inactive", - "out-of-date": "Out of Date", - "none-found": "No plugins found.", - "none-active": "No Active Plugins", - "find-plugins": "Find Plugins", + "installed": "Nameščeno", + "active": "Aktivno", + "inactive": "Neaktivno", + "out-of-date": "zastarelo", + "none-found": "Vtičnikov ni bilo mogoče najti.", + "none-active": "Ni aktivnih vtičnikov.", + "find-plugins": "Najdi vtičnike", - "plugin-search": "Plugin Search", - "plugin-search-placeholder": "Search for plugin...", + "plugin-search": "Iskanje vtičnikov", + "plugin-search-placeholder": "Iskanje vtičnika...", "submit-anonymous-usage": "Submit anonymous plugin usage data.", "reorder-plugins": "Re-order Plugins", "order-active": "Order Active Plugins", - "dev-interested": "Interested in writing plugins for NodeBB?", - "docs-info": "Full documentation regarding plugin authoring can be found in the NodeBB Docs Portal.", + "dev-interested": "Vas zanima pisanje vtičnikov za NodeBB?", + "docs-info": "Celotno dokumentacijo o ustvarjanju vtičnikov najdete v NodeBB dokumentnem portalu.", - "order.description": "Certain plugins work ideally when they are initialised before/after other plugins.", - "order.explanation": "Plugins load in the order specified here, from top to bottom", + "order.description": "Nekateri vtičniki delujejo idealno, če so inicializirani pred/po drugih vtičnikih.", + "order.explanation": "Vtičniki se nalagajo po vrstnem redu, ki je tukaj naveden, od zgoraj navzdol", - "plugin-item.themes": "Themes", - "plugin-item.deactivate": "Deactivate", - "plugin-item.activate": "Activate", - "plugin-item.install": "Install", - "plugin-item.uninstall": "Uninstall", - "plugin-item.settings": "Settings", - "plugin-item.installed": "Installed", - "plugin-item.latest": "Latest", - "plugin-item.upgrade": "Upgrade", - "plugin-item.more-info": "For more information:", - "plugin-item.unknown": "Unknown", - "plugin-item.unknown-explanation": "The state of this plugin could not be determined, possibly due to a misconfiguration error.", - "plugin-item.compatible": "This plugin works on NodeBB %1", - "plugin-item.not-compatible": "This plugin has no compatibility data, make sure it works before installing on your production environment.", + "plugin-item.themes": "Teme", + "plugin-item.deactivate": "Deaktiviraj", + "plugin-item.activate": "Aktiviraj", + "plugin-item.install": "Namesti", + "plugin-item.uninstall": "Odstrani", + "plugin-item.settings": "Nastavitve", + "plugin-item.installed": "Nameščeno", + "plugin-item.latest": "Najnovejše", + "plugin-item.upgrade": "Posodobi", + "plugin-item.more-info": "Za več informacij:", + "plugin-item.unknown": "Neznano", + "plugin-item.unknown-explanation": "Stanja tega vtičnika ni bilo mogoče določiti, morda zaradi napačne konfiguracije.", + "plugin-item.compatible": "Ta vtičnik deluje na NodeBB %1", + "plugin-item.not-compatible": "Ta vtičnik nima podatkov o združljivosti, preden ga namestite v svoje produkcijsko okolje, se prepričajte, da deluje.", - "alert.enabled": "Plugin Enabled", - "alert.disabled": "Plugin Disabled", - "alert.upgraded": "Plugin Upgraded", - "alert.installed": "Plugin Installed", - "alert.uninstalled": "Plugin Uninstalled", - "alert.activate-success": "Please rebuild and restart your NodeBB to fully activate this plugin", - "alert.deactivate-success": "Plugin successfully deactivated", - "alert.upgrade-success": "Please rebuild and restart your NodeBB to fully upgrade this plugin.", - "alert.install-success": "Plugin successfully installed, please activate the plugin.", - "alert.uninstall-success": "The plugin has been successfully deactivated and uninstalled.", - "alert.suggest-error": "

NodeBB could not reach the package manager, proceed with installation of latest version?

Server returned (%1): %2
", - "alert.package-manager-unreachable": "

NodeBB could not reach the package manager, an upgrade is not suggested at this time.

", - "alert.incompatible": "

Your version of NodeBB (v%1) is only cleared to upgrade to v%2 of this plugin. Please update your NodeBB if you wish to install a newer version of this plugin.

", - "alert.possibly-incompatible": "

No Compatibility Information Found

This plugin did not specify a specific version for installation given your NodeBB version. Full compatibility cannot be guaranteed, and may cause your NodeBB to no longer start properly.

In the event that NodeBB cannot boot properly:

$ ./nodebb reset plugin=\"%1\"

Continue installation of latest version of this plugin?

", - "alert.reorder": "Plugins Re-ordered", - "alert.reorder-success": "Please rebuild and restart your NodeBB to fully complete the process.", + "alert.enabled": "Vtičnik omogočen", + "alert.disabled": "Vtičnik onemogočen", + "alert.upgraded": "Vtičnik posodobljen", + "alert.installed": "Vtičnik nameščen", + "alert.uninstalled": "Vtičnik odstranjen", + "alert.activate-success": "Za popolno aktivacijo tega vtičnika obnovite in ponovno zaženete vaš NodeB.", + "alert.deactivate-success": "Vtičnik je bil uspešno deaktiviran", + "alert.upgrade-success": "Za popolno nadgradnjo tega vtičnika obnovite in ponovno zaženete vaš NodeB.", + "alert.install-success": "Vtičnik je uspešno nameščen, aktivirajte ga.", + "alert.uninstall-success": "Vtičnik je bil uspešno deaktiviran in odstranjen.", + "alert.suggest-error": "

NodeBB ni mogel doseči upravitelja paketov, naj nadaljuje z namestitvijo najnovejše različice?

Strežnik je našel (%1): %2
", + "alert.package-manager-unreachable": "

NodeBB ni mogel doseči upravitelja paketov, posodobitev v tem trenutku ni priporočena.

", + "alert.incompatible": "Vaša različica NodeBB (v%1) dovoljuje nadgradnjo tega vtičnika samo na različico v%2. Če želite namestiti novejšo različico tega vtičnika, posodobite svoj NodeBB.

", + "alert.possibly-incompatible": "
Podatke o združljivosti ni mogoče najti

Ta vtičnik ni določil posebne različice za namestitev glede na vašo različico NodeBB. Popolne združljivosti ni mogoče zagotoviti, zato se lahko vaš NodeBB ne zažene več pravilno.

V primeru, da se NodeBB ne zažene pravilno:

$ ./nodebb reset plugin=\"%1\"

Ali želite nadaljevati z namestitvijo najnovejše različice tega vtičnika?

", + "alert.reorder": "Vtičniki preurejeni", + "alert.reorder-success": "Prosimo, da za dokončanje postopka v celoti obnovite in znova zaženete NodeBB.", - "license.title": "Plugin License Information", - "license.intro": "The plugin %1 is licensed under the %2. Please read and understand the license terms prior to activating this plugin.", - "license.cta": "Do you wish to continue with activating this plugin?" + "license.title": "Informacija o licenci vtičnika", + "license.intro": "Vtičnik %1 je licenciran pod %2. Preden aktivirate ta vtičnik, preberite in razumite licenčne pogoje.", + "license.cta": "Ali želite nadaljevati z aktiviranjem tega vtičnika?" } diff --git a/public/language/sl/admin/extend/rewards.json b/public/language/sl/admin/extend/rewards.json index 5383a90b33..48c9762b23 100644 --- a/public/language/sl/admin/extend/rewards.json +++ b/public/language/sl/admin/extend/rewards.json @@ -1,17 +1,15 @@ { - "rewards": "Rewards", + "rewards": "Nagrade", "condition-if-users": "If User's", - "condition-is": "Is:", - "condition-then": "Then:", - "max-claims": "Amount of times reward is claimable", - "zero-infinite": "Enter 0 for infinite", - "delete": "Delete", - "enable": "Enable", - "disable": "Disable", - "control-panel": "Rewards Control", - "new-reward": "New Reward", + "condition-is": "Je:", + "condition-then": "Tedaj:", + "max-claims": "Kolikokrat je mogoče zahtevati nagrado", + "zero-infinite": "Vnesite 0 za neskončno", + "delete": "Izbriši", + "enable": "Omogoči", + "disable": "Onemogoči", - "alert.delete-success": "Successfully deleted reward", + "alert.delete-success": "Nagrada je uspešno izbrisana", "alert.no-inputs-found": "Illegal reward - no inputs found!", - "alert.save-success": "Successfully saved rewards" + "alert.save-success": "Nagrada je uspešno shranjena" } \ No newline at end of file diff --git a/public/language/sl/admin/extend/widgets.json b/public/language/sl/admin/extend/widgets.json index ab9bfb4cdb..6af766bf52 100644 --- a/public/language/sl/admin/extend/widgets.json +++ b/public/language/sl/admin/extend/widgets.json @@ -1,30 +1,30 @@ { - "available": "Available Widgets", - "explanation": "Select a widget from the dropdown menu and then drag and drop it into a template's widget area on the left.", - "none-installed": "No widgets found! Activate the widget essentials plugin in the plugins control panel.", - "clone-from": "Clone widgets from", - "containers.available": "Available Containers", - "containers.explanation": "Drag and drop on top of any active widget", - "containers.none": "None", + "available": "Razpoložljivi pripomočki", + "explanation": "V spustnem meniju izberite pripomoček in ga povlecite in spustite v območje gradnikov predloge na levi.", + "none-installed": "Pripomočki niso najdeni! Aktivirajte vtičnik za osnove pripomočkov na nadzorni ploščivtičnikov.", + "clone-from": "Klonirajte pripomočke iz", + "containers.available": "Razpoložljivi vsebniki", + "containers.explanation": "Povlecite in spustite na kateri koli aktivni gradnik", + "containers.none": "Brez", "container.well": "Well", "container.jumbotron": "Jumbotron", - "container.panel": "Panel", - "container.panel-header": "Panel Header", + "container.panel": "plošča", + "container.panel-header": "Glava plošče", "container.panel-body": "Panel Body", - "container.alert": "Alert", + "container.alert": "Opozorilo", - "alert.confirm-delete": "Are you sure you wish to delete this widget?", - "alert.updated": "Widgets Updated", - "alert.update-success": "Successfully updated widgets", - "alert.clone-success": "Successfully cloned widgets", + "alert.confirm-delete": "Ste prepričani, da želite izbrisati ta pripomoček?", + "alert.updated": "Pripomočki so posodobljeni", + "alert.update-success": "Pripomočki so uspešno shranjeni", + "alert.clone-success": "Pripomočki so uspešno klonirani", - "error.select-clone": "Please select a page to clone from", + "error.select-clone": "Izberite stran, s katere želite klonirati", - "title": "Title", - "title.placeholder": "Title (only shown on some containers)", - "container": "Container", + "title": "Naslov", + "title.placeholder": "Naslov (vidno le v nekaterih vsebnikih)", + "container": "Vsebnik", "container.placeholder": "Drag and drop a container or enter HTML here.", - "show-to-groups": "Show to groups", - "hide-from-groups": "Hide from groups", + "show-to-groups": "Prikaži skupinam", + "hide-from-groups": "Skrij skupinam", "hide-on-mobile": "Hide on mobile" } \ No newline at end of file diff --git a/public/language/sl/admin/general/dashboard.json b/public/language/sl/admin/general/dashboard.json deleted file mode 100644 index 37ae537786..0000000000 --- a/public/language/sl/admin/general/dashboard.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "forum-traffic": "Forum Traffic", - "page-views": "Page Views", - "unique-visitors": "Unique Visitors", - "new-users": "New Users", - "posts": "Posts", - "topics": "Topics", - "page-views-seven": "Last 7 Days", - "page-views-thirty": "Last 30 Days", - "page-views-last-day": "Last 24 hours", - "page-views-custom": "Custom Date Range", - "page-views-custom-start": "Range Start", - "page-views-custom-end": "Range End", - "page-views-custom-help": "Enter a date range of page views you would like to view. If no date picker is available, the accepted format is YYYY-MM-DD", - "page-views-custom-error": "Please enter a valid date range in the format YYYY-MM-DD", - - "stats.yesterday": "Yesterday", - "stats.today": "Today", - "stats.last-week": "Last Week", - "stats.this-week": "This Week", - "stats.last-month": "Last Month", - "stats.this-month": "This Month", - "stats.all": "All Time", - - "updates": "Updates", - "running-version": "You are running NodeBB v%1.", - "keep-updated": "Always make sure that your NodeBB is up to date for the latest security patches and bug fixes.", - "up-to-date": "

You are up-to-date

", - "upgrade-available": "

A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-upgrade-available": "

This is an outdated pre-release version of NodeBB. A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-warning": "

This is a pre-release version of NodeBB. Unintended bugs may occur.

", - "running-in-development": "Forum is running in development mode. The forum may be open to potential vulnerabilities; please contact your system administrator.", - "latest-lookup-failed": "

Failed to look up latest available version of NodeBB

", - - "notices": "Notices", - "restart-not-required": "Restart not required", - "restart-required": "Restart required", - "search-plugin-installed": "Search Plugin installed", - "search-plugin-not-installed": "Search Plugin not installed", - "search-plugin-tooltip": "Install a search plugin from the plugin page in order to activate search functionality", - - "control-panel": "System Control", - "rebuild-and-restart": "Rebuild & Restart", - "restart": "Restart", - "restart-warning": "Rebuilding or Restarting your NodeBB will drop all existing connections for a few seconds.", - "restart-disabled": "Rebuilding and Restarting your NodeBB has been disabled as you do not seem to be running it via the appropriate daemon.", - "maintenance-mode": "Maintenance Mode", - "maintenance-mode-title": "Click here to set up maintenance mode for NodeBB", - "realtime-chart-updates": "Realtime Chart Updates", - - "active-users": "Active Users", - "active-users.users": "Users", - "active-users.guests": "Guests", - "active-users.total": "Total", - "active-users.connections": "Connections", - - "anonymous-registered-users": "Anonymous vs Registered Users", - "anonymous": "Anonymous", - "registered": "Registered", - - "user-presence": "User Presence", - "on-categories": "On categories list", - "reading-posts": "Reading posts", - "browsing-topics": "Browsing topics", - "recent": "Recent", - "unread": "Unread", - - "high-presence-topics": "High Presence Topics", - - "graphs.page-views": "Page Views", - "graphs.page-views-registered": "Page Views Registered", - "graphs.page-views-guest": "Page Views Guest", - "graphs.page-views-bot": "Page Views Bot", - "graphs.unique-visitors": "Unique Visitors", - "graphs.registered-users": "Registered Users", - "graphs.anonymous-users": "Anonymous Users", - "last-restarted-by": "Last restarted by", - "no-users-browsing": "No users browsing" -} diff --git a/public/language/sl/admin/general/homepage.json b/public/language/sl/admin/general/homepage.json deleted file mode 100644 index 7428d59eeb..0000000000 --- a/public/language/sl/admin/general/homepage.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "home-page": "Home Page", - "description": "Choose what page is shown when users navigate to the root URL of your forum.", - "home-page-route": "Home Page Route", - "custom-route": "Custom Route", - "allow-user-home-pages": "Allow User Home Pages", - "home-page-title": "Title of the home page (default \"Home\")" -} \ No newline at end of file diff --git a/public/language/sl/admin/general/languages.json b/public/language/sl/admin/general/languages.json deleted file mode 100644 index bdd57849b3..0000000000 --- a/public/language/sl/admin/general/languages.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "language-settings": "Language Settings", - "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language", - "auto-detect": "Auto Detect Language Setting for Guests" -} \ No newline at end of file diff --git a/public/language/sl/admin/general/navigation.json b/public/language/sl/admin/general/navigation.json deleted file mode 100644 index 13dd01aae7..0000000000 --- a/public/language/sl/admin/general/navigation.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "icon": "Icon:", - "change-icon": "change", - "route": "Route:", - "tooltip": "Tooltip:", - "text": "Text:", - "text-class": "Text Class: optional", - "class": "Class: optional", - "id": "ID: optional", - - "properties": "Properties:", - "groups": "Groups:", - "open-new-window": "Open in a new window", - - "btn.delete": "Delete", - "btn.disable": "Disable", - "btn.enable": "Enable", - - "available-menu-items": "Available Menu Items", - "custom-route": "Custom Route", - "core": "core", - "plugin": "plugin" -} \ No newline at end of file diff --git a/public/language/sl/admin/general/social.json b/public/language/sl/admin/general/social.json deleted file mode 100644 index 23aedfcfaa..0000000000 --- a/public/language/sl/admin/general/social.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "post-sharing": "Post Sharing", - "info-plugins-additional": "Plugins can add additional networks for sharing posts.", - "save-success": "Successfully saved Post Sharing Networks!" -} \ No newline at end of file diff --git a/public/language/sl/admin/general/sounds.json b/public/language/sl/admin/general/sounds.json deleted file mode 100644 index 95ccbde0f1..0000000000 --- a/public/language/sl/admin/general/sounds.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "notifications": "Notifications", - "chat-messages": "Chat Messages", - "play-sound": "Play", - "incoming-message": "Incoming Message", - "outgoing-message": "Outgoing Message", - "upload-new-sound": "Upload New Sound", - "saved": "Settings Saved" -} \ No newline at end of file diff --git a/public/language/sl/admin/manage/admins-mods.json b/public/language/sl/admin/manage/admins-mods.json index e0f39ed5d4..7009fad3a0 100644 --- a/public/language/sl/admin/manage/admins-mods.json +++ b/public/language/sl/admin/manage/admins-mods.json @@ -1,10 +1,10 @@ { - "administrators": "Administrators", - "global-moderators": "Global Moderators", - "no-global-moderators": "No Global Moderators", - "moderators-of-category": "%1 Moderators", - "no-moderators": "No Moderators", - "add-administrator": "Add Administrator", - "add-global-moderator": "Add Global Moderator", - "add-moderator": "Add Moderator" + "administrators": "Skrbnik", + "global-moderators": "Globalni moderatorji", + "no-global-moderators": "Ni globalnih moderatorjev", + "moderators-of-category": "%1 moderator(jev)", + "no-moderators": "Ni moderatorjev", + "add-administrator": "Dodaj skrbnika", + "add-global-moderator": "Dodaj globalnega moderatorja", + "add-moderator": "Dodaj moderatorja" } \ No newline at end of file diff --git a/public/language/sl/admin/manage/categories.json b/public/language/sl/admin/manage/categories.json index ed5462e9be..d2bba96e4b 100644 --- a/public/language/sl/admin/manage/categories.json +++ b/public/language/sl/admin/manage/categories.json @@ -1,92 +1,92 @@ { - "settings": "Category Settings", - "privileges": "Privileges", + "settings": "Nastavitve kategorije", + "privileges": "Privilegiji", - "name": "Category Name", - "description": "Category Description", - "bg-color": "Background Colour", - "text-color": "Text Colour", - "bg-image-size": "Background Image Size", - "custom-class": "Custom Class", - "num-recent-replies": "# of Recent Replies", - "ext-link": "External Link", - "subcategories-per-page": "Subcategories per page", - "is-section": "Treat this category as a section", - "post-queue": "Post queue", - "tag-whitelist": "Tag Whitelist", - "upload-image": "Upload Image", - "delete-image": "Remove", - "category-image": "Category Image", - "parent-category": "Parent Category", - "optional-parent-category": "(Optional) Parent Category", - "top-level": "Top Level", - "parent-category-none": "(None)", + "name": "Ime kategorije", + "description": "Opis kategorije", + "bg-color": "Barva ozadja", + "text-color": "Barva besedila", + "bg-image-size": "Velikost slike ozadja", + "custom-class": "Razred po meri", + "num-recent-replies": "# nedavnih odgovorov", + "ext-link": "Zunanja povezava", + "subcategories-per-page": "Podkategorij na stran", + "is-section": "Obravnavaj to kategorijo kot sekcijo", + "post-queue": "Čakalna vrsta objav", + "tag-whitelist": "Bela lista oznak", + "upload-image": "Naloži sliko", + "delete-image": "Odstrani", + "category-image": "Slika kategorije", + "parent-category": "Nadrejena kategorija", + "optional-parent-category": "(Izbirno) Nadrejena kategorija", + "top-level": "Vrhnja raven", + "parent-category-none": "(Brez)", "copy-parent": "Copy Parent", - "copy-settings": "Copy Settings From", - "optional-clone-settings": "(Optional) Clone Settings From Category", + "copy-settings": "Kopiraj nastavitve iz", + "optional-clone-settings": "(Izbirno) Kloniraj nastavitve iz kategorije", "clone-children": "Clone Children Categories And Settings", - "purge": "Purge Category", + "purge": "Počisti kategorijo", - "enable": "Enable", - "disable": "Disable", - "edit": "Edit", - "analytics": "Analytics", - "view-category": "View category", - "set-order": "Set order", - "set-order-help": "Setting the order of the category will move this category to that order and update the order of other categories as necessary. Minimum order is 1 which puts the category at the top.", + "enable": "Omogoči", + "disable": "Onemogoči", + "edit": "Uredi", + "analytics": "Analitika", + "view-category": "Poglej kategorijo", + "set-order": "Nastavi vrstni red", + "set-order-help": "Če nastavite vrstni red kategorije, se bo ta kategorija premaknila in po potrebi posodobila vrstni red drugih kategorij. Najmanjša št. vrstnega reda je 1, kar kategorijo postavlja na vrh.", - "select-category": "Select Category", - "set-parent-category": "Set Parent Category", + "select-category": "Izberi kategorijo", + "set-parent-category": "Nastavi nadrejeno kategorijo", - "privileges.description": "You can configure the access control privileges for portions of the site in this section. Privileges can be granted on a per-user or a per-group basis. Select the domain of effect from the dropdown below.", - "privileges.category-selector": "Configuring privileges for ", - "privileges.warning": "Note: Privilege settings take effect immediately. It is not necessary to save the category after adjusting these settings.", - "privileges.section-viewing": "Viewing Privileges", - "privileges.section-posting": "Posting Privileges", - "privileges.section-moderation": "Moderation Privileges", - "privileges.section-other": "Other", - "privileges.section-user": "User", - "privileges.search-user": "Add User", - "privileges.no-users": "No user-specific privileges in this category.", - "privileges.section-group": "Group", - "privileges.group-private": "This group is private", - "privileges.inheritance-exception": "This group does not inherit privileges from registered-users group", - "privileges.banned-user-inheritance": "Banned users inherit privileges from banned-users group", - "privileges.search-group": "Add Group", + "privileges.description": "V tem razdelku lahko konfigurirate pravice za nadzor dostopa za dele spletnega mesta. Privilegiji se lahko podelijo uporabniku ali skupini. V spodnjem spustnem meniju izberite področje učinka.", + "privileges.category-selector": "Konfiguriranje privilegijev za", + "privileges.warning": "Opomba: Nastavitve privilegijev pričnejo učinkovati takoj. Po prilagoditvi teh nastavitev kategorije ni potrebno shraniti.", + "privileges.section-viewing": "Pravice ogleda", + "privileges.section-posting": "Pravice za objavo", + "privileges.section-moderation": "Pravice spreminjanja", + "privileges.section-other": "Drugo", + "privileges.section-user": "Uporabnik", + "privileges.search-user": "Dodaj uporabnika", + "privileges.no-users": "V tej kategoriji ni uporabniških pravic.", + "privileges.section-group": "Skupina", + "privileges.group-private": "Ta skupina je zasebna", + "privileges.inheritance-exception": "Ta skupina ne deduje pravic od skupine registriranih uporabnikov", + "privileges.banned-user-inheritance": "Prepovedani uporabniki dedujejo pravice od skupine prepovedanih uporabnikov", + "privileges.search-group": "Dodaj skupino", "privileges.copy-to-children": "Copy to Children", - "privileges.copy-from-category": "Copy from Category", - "privileges.copy-privileges-to-all-categories": "Copy to All Categories", + "privileges.copy-from-category": "Kopiraj iz kategorije", + "privileges.copy-privileges-to-all-categories": "Kopiraj v vse kategorije", "privileges.copy-group-privileges-to-children": "Copy this group's privileges to the children of this category.", - "privileges.copy-group-privileges-to-all-categories": "Copy this group's privileges to all categories.", - "privileges.copy-group-privileges-from": "Copy this group's privileges from another category.", - "privileges.inherit": "If the registered-users group is granted a specific privilege, all other groups receive an implicit privilege, even if they are not explicitly defined/checked. This implicit privilege is shown to you because all users are part of the registered-users user group, and so, privileges for additional groups need not be explicitly granted.", - "privileges.copy-success": "Privileges copied!", + "privileges.copy-group-privileges-to-all-categories": "Kopiraj pravice te skupine v vse kategorije.", + "privileges.copy-group-privileges-from": "Kopiraj pravice te skupine iz druge kategorije.", + "privileges.inherit": "Če je skupini registeriranih uporabnikov dodeljena posebna pravica, prejmejo vse druge skupine implicitno pravico, čeprav niso eksplicitno navedene/označene. Ta implicitna pravica se vam prikaže, ker so vsi uporabniki del skupine registriranih uporabnikov, zato pravic za dodatne skupine ni treba izrecno podeliti.", + "privileges.copy-success": "Pravice so kopirane!", - "analytics.back": "Back to Categories List", - "analytics.title": "Analytics for \"%1\" category", - "analytics.pageviews-hourly": "Figure 1 – Hourly page views for this category", - "analytics.pageviews-daily": "Figure 2 – Daily page views for this category", - "analytics.topics-daily": "Figure 3 – Daily topics created in this category", - "analytics.posts-daily": "Figure 4 – Daily posts made in this category", + "analytics.back": "Nazaj na seznam kategorij", + "analytics.title": "Analitika za kategorijo \"%1\"", + "analytics.pageviews-hourly": "
Slika 1 – Urni ogledi strani za to kategorijo", + "analytics.pageviews-daily": "Slika 2 – Dnevni ogledi strani za to kategorijo", + "analytics.topics-daily": "Slika 3 – Dnevno ustvarjene teme v tej kategoriji", + "analytics.posts-daily": "Slika 4 – Dnevne objave v tej kategoriji", - "alert.created": "Created", - "alert.create-success": "Category successfully created!", - "alert.none-active": "You have no active categories.", - "alert.create": "Create a Category", + "alert.created": "Ustvarjeno", + "alert.create-success": "Kategorija je uspešno ustvarjena!", + "alert.none-active": "Nimate aktivnih kategorij.", + "alert.create": "Ustvari kategorijo", "alert.confirm-purge": "

Do you really want to purge this category \"%1\"?

Warning! All topics and posts in this category will be purged!

Purging a category will remove all topics and posts, and delete the category from the database. If you want to remove a category temporarily, you'll want to \"disable\" the category instead.

", - "alert.purge-success": "Category purged!", - "alert.copy-success": "Settings Copied!", - "alert.set-parent-category": "Set Parent Category", - "alert.updated": "Updated Categories", - "alert.updated-success": "Category IDs %1 successfully updated.", - "alert.upload-image": "Upload category image", - "alert.find-user": "Find a User", - "alert.user-search": "Search for a user here...", - "alert.find-group": "Find a Group", - "alert.group-search": "Search for a group here...", - "alert.not-enough-whitelisted-tags": "Whitelisted tags are less than minimum tags, you need to create more whitelisted tags!", - "collapse-all": "Collapse All", - "expand-all": "Expand All", - "disable-on-create": "Disable on create", - "no-matches": "No matches" + "alert.purge-success": "Kategorija je počiščena!", + "alert.copy-success": "Nastavitve so kopirane!", + "alert.set-parent-category": "Nastavi nadrejeno kategorijo", + "alert.updated": "Posodobljene kategorije", + "alert.updated-success": "ID -ji kategorij %1 so uspešno posodobljeni.", + "alert.upload-image": "Naloži sliko kategorije", + "alert.find-user": "Poišči uporabnika", + "alert.user-search": "Išči uporabnika tukaj...", + "alert.find-group": "Poišči skupino", + "alert.group-search": "Išči skupino tukaj...", + "alert.not-enough-whitelisted-tags": "Oznak na beli listi je manj od dovoljene spodnje meje, na belo listo dodajte več oznak!", + "collapse-all": "Strni vse", + "expand-all": "Razširi vse", + "disable-on-create": "Onemogoči pri ustvarjanju", + "no-matches": "Ni zadetkov" } \ No newline at end of file diff --git a/public/language/sl/admin/manage/digest.json b/public/language/sl/admin/manage/digest.json index 8f3661698a..441af32d3c 100644 --- a/public/language/sl/admin/manage/digest.json +++ b/public/language/sl/admin/manage/digest.json @@ -1,20 +1,21 @@ { "lead": "A listing of digest delivery stats and times is displayed below.", - "disclaimer": "Please be advised that email delivery is not guaranteed, due to the nature of email technology. Many variables factor into whether an email sent to the recipient server is ultimately delivered into the user's inbox, including server reputation, blacklisted IP addresses, and whether DKIM/SPF/DMARC is configured.", - "disclaimer-continued": "A successful delivery means the message was sent successfully by NodeBB and acknowledged by the recipient server. It does not mean the email landed in the inbox. For best results, we recommend using a third-party email delivery service such as SendGrid.", + "disclaimer": "Upoštevajte, da dostava elektronske pošte zaradi narave tehnologije e-pošte ni zagotovljena. Številne spremenljivke vplivajo na to, ali je e-poštno sporočilo, poslano prejemniškemu strežniku, na koncu dostavljeno v mapo »Prejeto«, vključno z ugledom strežnika, naslovi IP na črnem seznamu in ali je konfiguriran DKIM/SPF/DMARC.", + "disclaimer-continued": "Uspešna dostava pomeni, da je NodeBB uspešno poslal sporočilo in ga je strežnik prejemnika potrdil. To ne pomeni, da je e-poštno sporočilo prispelo v mapo »Prejeto«. Za najboljše rezultate priporočamo uporabo storitev dostave e-pošte tretjih oseb, kot je npr SendGrid.", - "user": "User", - "subscription": "Subscription Type", - "last-delivery": "Last successful delivery", - "default": "System default", - "default-help": "System default means the user has not explicitly overridden the global forum setting for digests, which is currently: "%1"", - "resend": "Resend Digest", + "user": "Uporabnik", + "subscription": "Vrsta naročnine", + "last-delivery": "Zadnja uspešna dostava", + "default": "Privzeta nastavitev sistema", + "default-help": "Privzeta nastavitev sistema pomeni, uporabnik ni izrecno preglasil globalne nastavitve foruma za povzetke, ki je trenutno: "%1"", + "resend": "Ponovno pošlji povzetek", "resend-all-confirm": "Are you sure you wish to manually execute this digest run?", "resent-single": "Manual digest resend completed", - "resent-day": "Daily digest resent", - "resent-week": "Weekly digest resent", - "resent-month": "Monthly digest resent", - "null": "Never", + "resent-day": "Dnevni povzetek je ponovno poslan", + "resent-week": "Tedenski povzetek je ponovno poslan", + "resent-biweek": "Dvotedenski povzetek je ponovno poslan", + "resent-month": "Mesečni povzetek je ponovno poslan", + "null": "Nikoli", "manual-run": "Manual digest run:", "no-delivery-data": "No delivery data found" diff --git a/public/language/sl/admin/manage/groups.json b/public/language/sl/admin/manage/groups.json index 911fcce010..f24abd0371 100644 --- a/public/language/sl/admin/manage/groups.json +++ b/public/language/sl/admin/manage/groups.json @@ -1,44 +1,44 @@ { - "name": "Group Name", - "badge": "Badge", - "properties": "Properties", - "description": "Group Description", - "member-count": "Member Count", - "system": "System", - "hidden": "Hidden", - "private": "Private", - "edit": "Edit", - "delete": "Delete", + "name": "Ime skupine", + "badge": "Značka", + "properties": "Lastnosti", + "description": "Opis skupine", + "member-count": "Število članov", + "system": "Sistem", + "hidden": "Skrita", + "private": "Zasebna", + "edit": "Uredi", + "delete": "Izbriši", "privileges": "Privileges", "download-csv": "CSV", "search-placeholder": "Search", - "create": "Create Group", - "description-placeholder": "A short description about your group", - "create-button": "Create", + "create": "Ustvari skupino", + "description-placeholder": "Kratki opis vaše skupine", + "create-button": "Ustvari", "alerts.create-failure": "Uh-Oh

There was a problem creating your group. Please try again later!

", - "alerts.confirm-delete": "Are you sure you wish to delete this group?", + "alerts.confirm-delete": "Ste prepričani, da želite izbrisati to skupino?", - "edit.name": "Name", - "edit.description": "Description", + "edit.name": "Ime", + "edit.description": "Opis", "edit.user-title": "Title of Members", - "edit.icon": "Group Icon", - "edit.label-color": "Group Label Color", - "edit.text-color": "Group Text Color", - "edit.show-badge": "Show Badge", - "edit.private-details": "If enabled, joining of groups requires approval from a group owner.", - "edit.private-override": "Warning: Private groups is disabled at system level, which overrides this option.", - "edit.disable-join": "Disable join requests", - "edit.disable-leave": "Disallow users from leaving the group", - "edit.hidden": "Hidden", - "edit.hidden-details": "If enabled, this group will not be found in the groups listing, and users will have to be invited manually", - "edit.add-user": "Add User to Group", - "edit.add-user-search": "Search Users", - "edit.members": "Member List", - "control-panel": "Groups Control Panel", - "revert": "Revert", + "edit.icon": "Ikona skupine", + "edit.label-color": "Barva oznake skupine", + "edit.text-color": "Barva besedila skupine", + "edit.show-badge": "Prikaži značko", + "edit.private-details": "Če je omogočeno, je za pridružitev skupinam potrebna odobritev lastnika skupine.", + "edit.private-override": "Opozorilo: Zasebne skupine so onemogočene na sistemski ravni, kar preglasi to možnost.", + "edit.disable-join": "Onemogoči povabila za pridružitev", + "edit.disable-leave": "Ne dovoli uporabnikom, da zapustijo skupino", + "edit.hidden": "Skrito", + "edit.hidden-details": "Če je omogočeno, te skupine ne boste našli na seznamu skupin, uporabnike pa boste morali povabiti ročno", + "edit.add-user": "Dodaj uporabnika v skupino", + "edit.add-user-search": "Iskanje uporabnikov", + "edit.members": "Seznam članov", + "control-panel": "Nadzorna plošča skupine", + "revert": "Povrni", - "edit.no-users-found": "No Users Found", - "edit.confirm-remove-user": "Are you sure you want to remove this user?", - "edit.save-success": "Changes saved!" + "edit.no-users-found": "Uporabnikov ni bilo mogoče najti", + "edit.confirm-remove-user": "Ste prepričani, da želite odstraniti tega uporabnika?", + "edit.save-success": "Spremembe so shranjene!" } \ No newline at end of file diff --git a/public/language/sl/admin/manage/privileges.json b/public/language/sl/admin/manage/privileges.json index 8b38b368cd..de7c226776 100644 --- a/public/language/sl/admin/manage/privileges.json +++ b/public/language/sl/admin/manage/privileges.json @@ -1,19 +1,19 @@ { "global": "Global", - "admin": "Admin", + "admin": "Administrator", "group-privileges": "Group Privileges", "user-privileges": "User Privileges", "edit-privileges": "Edit Privileges", - "select-clear-all": "Select/Clear All", - "chat": "Chat", - "upload-images": "Upload Images", - "upload-files": "Upload Files", - "signature": "Signature", + "select-clear-all": "Izberi/Počisti vse", + "chat": "Klepet", + "upload-images": "Naloži slike", + "upload-files": "Naloži datoteke", + "signature": "Podpis", "ban": "Ban", - "invite": "Invite", - "search-content": "Search Content", - "search-users": "Search Users", - "search-tags": "Search Tags", + "invite": "Povabi", + "search-content": "Išči vsebino", + "search-users": "Išči uporabnike", + "search-tags": "Išči oznake", "view-users": "View Users", "view-tags": "View Tags", "view-groups": "View Groups", @@ -23,27 +23,27 @@ "find-category": "Find Category", "access-category": "Access Category", "access-topics": "Access Topics", - "create-topics": "Create Topics", - "reply-to-topics": "Reply to Topics", + "create-topics": "Ustvari teme", + "reply-to-topics": "Odgovori na teme", "schedule-topics": "Schedule Topics", - "tag-topics": "Tag Topics", - "edit-posts": "Edit Posts", - "view-edit-history": "View Edit History", - "delete-posts": "Delete Posts", + "tag-topics": "Označi teme", + "edit-posts": "Uredi objave", + "view-edit-history": "Poglej zgodovino urejanja", + "delete-posts": "Izbriši objave", "view_deleted": "View Deleted Posts", "upvote-posts": "Upvote Posts", "downvote-posts": "Downvote Posts", - "delete-topics": "Delete Topics", + "delete-topics": "Izbriši teme", "purge": "Purge", "moderate": "Moderate", "admin-dashboard": "Dashboard", - "admin-categories": "Categories", + "admin-categories": "Kategorije", "admin-privileges": "Privileges", - "admin-users": "Users", + "admin-users": "Uporabniki", "admin-admins-mods": "Admins & Mods", - "admin-groups": "Groups", - "admin-tags": "Tags", - "admin-settings": "Settings", + "admin-groups": "Skupine", + "admin-tags": "Oznake", + "admin-settings": "Nastavitve", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", "alert.confirm-admins-mods": "Are you sure you wish to grant the "Admins & Mods" privilege to this user/group? Users with this privilege are able to promote and demote other users into privileged positions, including super administrator", diff --git a/public/language/sl/admin/manage/registration.json b/public/language/sl/admin/manage/registration.json index 87d4f07d10..3cc776fd0c 100644 --- a/public/language/sl/admin/manage/registration.json +++ b/public/language/sl/admin/manage/registration.json @@ -1,6 +1,6 @@ { "queue": "Čakalna vrsta", - "description": "V čakalni vrsti za registracijo ni nobenega uporabnika.
Da omogočite to funkcionalnost, pojdite na Nastavitve → Uporabnik → Registracija Uporabnika in nastavitev Tip registracijev \"Admin Odobravanje\".", + "description": "V čakalni vrsti registracij ni nobenega uporabnika.
Da omogočite to funkcionalnost, pojdite na Nastavitve → Uporabnik → Registracija Uporabnika in nastavitev Tip registracijev \"Admin Odobravanje\".", "list.name": "Ime", "list.email": "E-pošta", diff --git a/public/language/sl/admin/manage/tags.json b/public/language/sl/admin/manage/tags.json index 01363dfda0..c2774f3284 100644 --- a/public/language/sl/admin/manage/tags.json +++ b/public/language/sl/admin/manage/tags.json @@ -1,18 +1,18 @@ { - "none": "Your forum does not have any topics with tags yet.", - "bg-color": "Background Colour", - "text-color": "Text Colour", + "none": "Na vašem forumu še ni nobene teme z oznakami.", + "bg-color": "Barva ozadja", + "text-color": "Barva besedila", "description": "Select tags by clicking or dragging, use CTRL to select multiple tags.", - "create": "Create Tag", - "modify": "Modify Tags", - "rename": "Rename Tags", - "delete": "Delete Selected Tags", - "search": "Search for tags...", - "settings": "Tags Settings", - "name": "Tag Name", + "create": "Ustvari oznako", + "modify": "Spremeni oznake", + "rename": "Preimenuj oznake", + "delete": "Izbriši izbrane oznake", + "search": "Iskanje oznak...", + "settings": "Nastavitve oznak", + "name": "Ime oznake", - "alerts.editing": "Editing tag(s)", - "alerts.confirm-delete": "Do you want to delete the selected tags?", - "alerts.update-success": "Tag Updated!", - "reset-colors": "Reset colors" + "alerts.editing": "Urejanje oznak(e)", + "alerts.confirm-delete": "Ali želite izbrisati izbrane oznake?", + "alerts.update-success": "Oznaka je posodobljena!", + "reset-colors": "Ponastavi barve" } \ No newline at end of file diff --git a/public/language/sl/admin/manage/uploads.json b/public/language/sl/admin/manage/uploads.json index 72a695ccdc..3fc7b46495 100644 --- a/public/language/sl/admin/manage/uploads.json +++ b/public/language/sl/admin/manage/uploads.json @@ -1,11 +1,11 @@ { - "upload-file": "Upload File", - "filename": "Filename", + "upload-file": "Naloži datoteko", + "filename": "Ime datoteke", "usage": "Post Usage", "orphaned": "Orphaned", - "size/filecount": "Size / Filecount", - "confirm-delete": "Do you really want to delete this file?", - "filecount": "%1 files", - "new-folder": "New Folder", - "name-new-folder": "Enter a name for new the folder" + "size/filecount": "Velikost / Število datotek", + "confirm-delete": "Ste prepričani, da želite izbrisati to datoteko?", + "filecount": "%1 datotek", + "new-folder": "Nova mapa", + "name-new-folder": "Vnesite ime nove mape" } \ No newline at end of file diff --git a/public/language/sl/admin/manage/users.json b/public/language/sl/admin/manage/users.json index 38a065b954..8fbd1bc68f 100644 --- a/public/language/sl/admin/manage/users.json +++ b/public/language/sl/admin/manage/users.json @@ -1,70 +1,71 @@ { - "users": "Users", - "edit": "Actions", - "make-admin": "Make Admin", - "remove-admin": "Remove Admin", - "validate-email": "Validate Email", - "send-validation-email": "Send Validation Email", - "password-reset-email": "Send Password Reset Email", - "force-password-reset": "Force Password Reset & Log User Out", - "ban": "Ban User(s)", - "temp-ban": "Ban User(s) Temporarily", - "unban": "Unban User(s)", - "reset-lockout": "Reset Lockout", + "users": "Uporabniki", + "edit": "Dejanja", + "make-admin": "Nastavi kot skrbnika", + "remove-admin": "Odstrani kot skrbnika", + "validate-email": "Potrdite e-poštni naslov", + "send-validation-email": "Pošljite potrditveno e-sporočilo", + "password-reset-email": "Pošljite e-poštno sporočilo za ponastavitev gesla", + "force-password-reset": "Vsilite ponastavitev gesla in odjavo uporabnika", + "ban": "Prepovejte uporabnika(e)", + "temp-ban": "Začasno prepovejte uporabnika(e)", + "unban": "Razveljavi prepoved uporabnika(ov)", + "reset-lockout": "Ponastavitev zaklepanja", "reset-flags": "Reset Flags", - "delete": "Delete User(s)", - "delete-content": "Delete User(s) Content", - "purge": "Delete User(s) and Content", - "download-csv": "Download CSV", - "manage-groups": "Manage Groups", - "add-group": "Add Group", - "invite": "Invite", - "new": "New User", - "filter-by": "Filter by", - "pills.unvalidated": "Not Validated", - "pills.validated": "Validated", - "pills.banned": "Banned", + "delete": "Izbrišiteuporabnika(e)", + "delete-content": "Izbrišite Vsebino uporabnika(ov)", + "purge": "Izbrišiteuporabnika(e) in vsebino", + "download-csv": "Prenesite CSV", + "manage-groups": "Upravljaj skupine", + "add-group": "Dodaj skupino", + "invite": "Povabi", + "new": "Nov uporabnik", + "filter-by": "Filtriraj po", + "pills.unvalidated": "Nepotrjeno", + "pills.validated": "Potrjeno", + "pills.banned": "Prepovedano", - "50-per-page": "50 per page", - "100-per-page": "100 per page", - "250-per-page": "250 per page", - "500-per-page": "500 per page", + "50-per-page": "50 na stran", + "100-per-page": "100 na stran", + "250-per-page": "250 na stran", + "500-per-page": "500 na stran", - "search.uid": "By User ID", - "search.uid-placeholder": "Enter a user ID to search", - "search.username": "By User Name", - "search.username-placeholder": "Enter a username to search", - "search.email": "By Email", - "search.email-placeholder": "Enter a email to search", - "search.ip": "By IP Address", - "search.ip-placeholder": "Enter an IP Address to search", - "search.not-found": "User not found!", + "search.uid": "Po ID uporabnika", + "search.uid-placeholder": "Za iskanje vnesite ID uporabnika", + "search.username": "Po imenu uporabnika", + "search.username-placeholder": "Za iskanje vnesite uporabniško ime", + "search.email": "Po e-poštnem naslovu", + "search.email-placeholder": "Za iskanje vnesite e-poštni naslov", + "search.ip": "Po IP naslovu", + "search.ip-placeholder": "Za iskanje vnesite IP naslov", + "search.not-found": "Uporabnika ni bilo mogoče najti!", - "inactive.3-months": "3 months", - "inactive.6-months": "6 months", - "inactive.12-months": "12 months", + "inactive.3-months": "3 mes.", + "inactive.6-months": "6 mes.", + "inactive.12-months": "12 mes.", "users.uid": "uid", - "users.username": "username", - "users.email": "email", + "users.username": "uporabniško ime", + "users.email": "e-poštni naslov", + "users.no-email": "(ni e-poštnega naslova)", "users.ip": "IP", "users.postcount": "postcount", - "users.reputation": "reputation", + "users.reputation": "ugled", "users.flags": "flags", "users.joined": "joined", "users.last-online": "last online", "users.banned": "banned", - "create.username": "User Name", - "create.email": "Email", - "create.email-placeholder": "Email of this user", - "create.password": "Password", - "create.password-confirm": "Confirm Password", + "create.username": "Ime uporabnika", + "create.email": "E-poštni naslov", + "create.email-placeholder": "E-poštni naslov tega uporabnika", + "create.password": "Geslo", + "create.password-confirm": "Potrdi geslo", "temp-ban.length": "Ban Length", "temp-ban.reason": "Reason (Optional)", - "temp-ban.hours": "Hours", - "temp-ban.days": "Days", + "temp-ban.hours": "Ur", + "temp-ban.days": "Dni", "temp-ban.explanation": "Enter the length of time for the ban. Note that a time of 0 will be a considered a permanent ban.", "alerts.confirm-ban": "Do you really want to ban this user permanently?", @@ -74,36 +75,36 @@ "alerts.unban-success": "User(s) unbanned!", "alerts.lockout-reset-success": "Lockout(s) reset!", "alerts.flag-reset-success": "Flags(s) reset!", - "alerts.no-remove-yourself-admin": "You can't remove yourself as Administrator!", - "alerts.make-admin-success": "User is now administrator.", - "alerts.confirm-remove-admin": "Do you really want to remove this administrator?", - "alerts.remove-admin-success": "User is no longer administrator.", + "alerts.no-remove-yourself-admin": "Sebe kot skrbnika ne morete odstraniti!", + "alerts.make-admin-success": "Uporabnik je sedaj skrbnik.", + "alerts.confirm-remove-admin": "Ste prepričani, da želite odstraniti tega skrbnika?", + "alerts.remove-admin-success": "Uporabnik ni več skrbnik.", "alerts.make-global-mod-success": "User is now global moderator.", "alerts.confirm-remove-global-mod": "Do you really want to remove this global moderator?", "alerts.remove-global-mod-success": "User is no longer global moderator.", "alerts.make-moderator-success": "User is now moderator.", "alerts.confirm-remove-moderator": "Do you really want to remove this moderator?", "alerts.remove-moderator-success": "User is no longer moderator.", - "alerts.confirm-validate-email": "Do you want to validate email(s) of these user(s)?", - "alerts.confirm-force-password-reset": "Are you sure you want to force the password reset and log out these user(s)?", - "alerts.validate-email-success": "Emails validated", - "alerts.validate-force-password-reset-success": "User(s) passwords have been reset and their existing sessions have been revoked.", - "alerts.password-reset-confirm": "Do you want to send password reset email(s) to these user(s)?", - "alerts.confirm-delete": "Warning!

Do you really want to delete user(s)?

This action is not reversible! Only the user account will be deleted, their posts and topics will remain.

", - "alerts.delete-success": "User(s) Deleted!", + "alerts.confirm-validate-email": "Ali želite potrditi e-poštni(e) naslov(e) tega/teh uporabnika(ov)?", + "alerts.confirm-force-password-reset": "Ali ste prepričani, da želite vsiliti ponastavitev gesla in odjaviti te(ga) uporabnika(e)?", + "alerts.validate-email-success": "E-poštni naslovi so potrjeni", + "alerts.validate-force-password-reset-success": "Gesla uporabnikov so bila ponastavljena in obstoječe seje preklicane.", + "alerts.password-reset-confirm": "Ali želite poslati e-poštno sporočilo za obnovitev gesla temu/tem uporabniku(om)?", + "alerts.confirm-delete": "Opozorilo!

Ali res želite izbrisati uporabnika(e)?

Tega dejanja ni mogoče razveljaviti! Izbrisan bo samo uporabniški račun, njegove objave in teme bodo ostale.

\n", + "alerts.delete-success": "Uporabnik(i) je/so izbrisan(i)!", "alerts.confirm-delete-content": "Warning!

Do you really want to delete these user(s) content?

This action is not reversible! The users' accounts will remain, but their posts and topics will be deleted.

", - "alerts.delete-content-success": "User(s) Content Deleted!", + "alerts.delete-content-success": "Vsebina uporabnika(ov) je izbrisana!", "alerts.confirm-purge": "Warning!

Do you really want to delete user(s) and their content?

This action is not reversible! All user data and content will be erased!

", - "alerts.create": "Create User", - "alerts.button-create": "Create", - "alerts.button-cancel": "Cancel", - "alerts.error-passwords-different": "Passwords must match!", - "alerts.error-x": "Error

%1

", - "alerts.create-success": "User created!", + "alerts.create": "Ustvari uporabnika", + "alerts.button-create": "Ustvari", + "alerts.button-cancel": "Prekliči", + "alerts.error-passwords-different": "Gesli se morata ujemati!", + "alerts.error-x": "Napaka

%1

", + "alerts.create-success": "Uporabnik je ustvarjen!", - "alerts.prompt-email": "Emails: ", - "alerts.email-sent-to": "An invitation email has been sent to %1", - "alerts.x-users-found": "%1 user(s) found, (%2 seconds)", - "export-users-started": "Exporting users as csv, this might take a while. You will receive a notification when it is complete.", - "export-users-completed": "Users exported as csv, click here to download." + "alerts.prompt-email": "E-poštni naslovi:", + "alerts.email-sent-to": "E -poštno sporočilo s povabilom je bilo poslano %1", + "alerts.x-users-found": "%1 najdenih uporabnik(ov), (%2 sekund)", + "export-users-started": "Izvoz uporabnikov kot CSV lahko traja nekaj časa. Ko bo končano, boste prejeli obvestilo.", + "export-users-completed": "Uporabniki, izvoženi kot CSV, kliknite tukaj za prenos." } \ No newline at end of file diff --git a/public/language/sl/admin/menu.json b/public/language/sl/admin/menu.json index 1c5ea73b4f..14b3e8c210 100644 --- a/public/language/sl/admin/menu.json +++ b/public/language/sl/admin/menu.json @@ -1,88 +1,89 @@ { "section-dashboard": "Dashboards", - "dashboard/overview": "Overview", - "dashboard/logins": "Logins", - "dashboard/users": "Users", - "dashboard/topics": "Topics", - "section-general": "General", + "dashboard/overview": "Pregled", + "dashboard/logins": "Prijave", + "dashboard/users": "Uporabniki", + "dashboard/topics": "Teme", + "dashboard/searches": "Iskanja", + "section-general": "Splošno", - "section-manage": "Manage", - "manage/categories": "Categories", + "section-manage": "Upravljaj", + "manage/categories": "Kategorije", "manage/privileges": "Privileges", - "manage/tags": "Tags", - "manage/users": "Users", - "manage/admins-mods": "Admins & Mods", - "manage/registration": "Registration Queue", - "manage/post-queue": "Post Queue", - "manage/groups": "Groups", - "manage/ip-blacklist": "IP Blacklist", - "manage/uploads": "Uploads", - "manage/digest": "Digests", + "manage/tags": "Oznake", + "manage/users": "Uporabniki", + "manage/admins-mods": "Skrbniki in moderatorji", + "manage/registration": "Čakalna vrsta registracij", + "manage/post-queue": "Čakalna vrsta objav", + "manage/groups": "Skupine", + "manage/ip-blacklist": "IP črna lista", + "manage/uploads": "Nalaganja", + "manage/digest": "Povzetki", - "section-settings": "Settings", - "settings/general": "General", - "settings/homepage": "Home Page", - "settings/navigation": "Navigation", - "settings/reputation": "Reputation & Flags", - "settings/email": "Email", - "settings/user": "Users", - "settings/group": "Groups", - "settings/guest": "Guests", - "settings/uploads": "Uploads", - "settings/languages": "Languages", - "settings/post": "Posts", - "settings/chat": "Chats", - "settings/pagination": "Pagination", - "settings/tags": "Tags", - "settings/notifications": "Notifications", - "settings/api": "API Access", - "settings/sounds": "Sounds", - "settings/social": "Social", - "settings/cookies": "Cookies", + "section-settings": "Nastavitve", + "settings/general": "Splošno", + "settings/homepage": "Domača stran", + "settings/navigation": "Krmarjenje", + "settings/reputation": "Ugled in zastavice", + "settings/email": "E-pošta", + "settings/user": "Uporabniki", + "settings/group": "Skupine", + "settings/guest": "Gostje", + "settings/uploads": "Nalaganja", + "settings/languages": "Jeziki", + "settings/post": "Objave", + "settings/chat": "Klepeti", + "settings/pagination": "Številčenje strani", + "settings/tags": "Oznake", + "settings/notifications": "Obvestila", + "settings/api": "API dostop", + "settings/sounds": "Zvoki", + "settings/social": "Družbeno", + "settings/cookies": "Piškotki", "settings/web-crawler": "Web Crawler", - "settings/sockets": "Sockets", - "settings/advanced": "Advanced", + "settings/sockets": "Vtičnice", + "settings/advanced": "Napredno", - "settings.page-title": "%1 Settings", + "settings.page-title": "%1 nastavitve", - "section-appearance": "Appearance", - "appearance/themes": "Themes", - "appearance/skins": "Skins", - "appearance/customise": "Custom Content (HTML/JS/CSS)", + "section-appearance": "Videz", + "appearance/themes": "Teme", + "appearance/skins": "Preobleke", + "appearance/customise": "Vsebina po meri (HTML/JS/CSS)", "section-extend": "Extend", - "extend/plugins": "Plugins", - "extend/widgets": "Widgets", - "extend/rewards": "Rewards", + "extend/plugins": "Vtičniki", + "extend/widgets": "Pripomočki", + "extend/rewards": "Nagrade", "section-social-auth": "Social Authentication", - "section-plugins": "Plugins", - "extend/plugins.install": "Install Plugins", + "section-plugins": "Vtičniki", + "extend/plugins.install": "Namesti vtičnike", - "section-advanced": "Advanced", - "advanced/database": "Database", - "advanced/events": "Events", + "section-advanced": "Napredno", + "advanced/database": "Podatkovna baza", + "advanced/events": "Dogodki", "advanced/hooks": "Hooks", - "advanced/logs": "Logs", - "advanced/errors": "Errors", - "advanced/cache": "Cache", + "advanced/logs": "Prijave", + "advanced/errors": "Napake", + "advanced/cache": "Predpomnilnik", "development/logger": "Logger", "development/info": "Info", "rebuild-and-restart-forum": "Rebuild & Restart Forum", - "restart-forum": "Restart Forum", - "logout": "Log out", + "restart-forum": "Ponovno zaženi forum", + "logout": "Odjavi se", "view-forum": "View Forum", "search.placeholder": "Press "/" to search for settings", - "search.no-results": "No results...", - "search.search-forum": "Search the forum for ", - "search.keep-typing": "Type more to see results...", - "search.start-typing": "Start typing to see results...", + "search.no-results": "Ni rezultatov...", + "search.search-forum": "Na forumu poišči ", + "search.keep-typing": "Vnesite več, da vidite rezultate...", + "search.start-typing": "Začnite tipkati, da vidite rezultate...", - "connection-lost": "Connection to %1 has been lost, attempting to reconnect...", + "connection-lost": "Povezava z %1 je bila izgubljena, poskus ponovne povezave...", - "alerts.version": "Running NodeBB v%1", - "alerts.upgrade": "Upgrade to v%1" + "alerts.version": "Teče NodeBB v%1", + "alerts.upgrade": "Nadgradi na v%1" } \ No newline at end of file diff --git a/public/language/sl/admin/settings/advanced.json b/public/language/sl/admin/settings/advanced.json index ce0782be9e..1ccc1a4752 100644 --- a/public/language/sl/admin/settings/advanced.json +++ b/public/language/sl/admin/settings/advanced.json @@ -1,9 +1,9 @@ { - "maintenance-mode": "Maintenance Mode", - "maintenance-mode.help": "When the forum is in maintenance mode, all requests will be redirected to a static holding page. Administrators are exempt from this redirection, and are able to access the site normally.", - "maintenance-mode.status": "Maintenance Mode Status Code", - "maintenance-mode.message": "Maintenance Message", - "headers": "Headers", + "maintenance-mode": "Način vzdrževanja", + "maintenance-mode.help": "Ko je forum v načinu vzdrževanja, bodo vse zahteve preusmerjene na statično stran za shranjevanje. Skrbniki so izvzeti iz te preusmeritve in lahko normalno dostopajo do spletnega mesta.", + "maintenance-mode.status": "Koda stanja načina vzdrževanja", + "maintenance-mode.message": "Sporočilo o vzdrževanju", + "headers": "Glave", "headers.allow-from": "Set ALLOW-FROM to Place NodeBB in an iFrame", "headers.csp-frame-ancestors": "Set Content-Security-Policy frame-ancestors header to Place NodeBB in an iFrame", "headers.csp-frame-ancestors-help": "'none', 'self'(default) or list of URIs to allow.", @@ -16,28 +16,28 @@ "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", "hsts": "Strict Transport Security", - "hsts.enabled": "Enabled HSTS (recommended)", + "hsts.enabled": "Omogočen HSTS (priporočeno)", "hsts.maxAge": "HSTS Max Age", - "hsts.subdomains": "Include subdomains in HSTS header", - "hsts.preload": "Allow preloading of HSTS header", - "hsts.help": "If enabled, an HSTS header will be set for this site. You can elect to include subdomains and preloading flags in your header. If in doubt, you can leave these unchecked. More information ", - "traffic-management": "Traffic Management", - "traffic.help": "NodeBB uses a module that automatically denies requests in high-traffic situations. You can tune these settings here, although the defaults are a good starting point.", - "traffic.enable": "Enable Traffic Management", + "hsts.subdomains": "V glavo HSTS vključi poddomene", + "hsts.preload": "Dovoli prednalaganje glave HSTS", + "hsts.help": "Če je omogočeno, bo za to spletno mesto nastavljena glava HSTS. Lahko se odločite za vključitev poddomen in zastavic za vnaprejšnje nalaganje v glavo. Če ste v dvomih, jih lahko pustite neoznačene. Več informacij ", + "traffic-management": "Upravljanje prometa", + "traffic.help": "NodeBB uporablja modul, ki v situacijah z velikim prometom samodejno zavrne zahteve. Tu lahko prilagajate te nastavitve, čeprav so privzete nastavitve dobro izhodišče.", + "traffic.enable": "Omogoči upravljanje prometa", "traffic.event-lag": "Event Loop Lag Threshold (in milliseconds)", - "traffic.event-lag-help": "Lowering this value decreases wait times for page loads, but will also show the \"excessive load\" message to more users. (Restart required)", + "traffic.event-lag-help": "Z znižanjem te vrednosti se skrajšajo čakalne dobe za nalaganje strani, hkrati pa bo več uporabnikom prikazano sporočilo »čezmerna obremenitev«. (Potreben ponovni zagon)", "traffic.lag-check-interval": "Check Interval (in milliseconds)", "traffic.lag-check-interval-help": "Lowering this value causes NodeBB to become more sensitive to spikes in load, but may also cause the check to become too sensitive. (Restart required)", "sockets.settings": "WebSocket Settings", "sockets.max-attempts": "Max Reconnection Attempts", - "sockets.default-placeholder": "Default: %1", + "sockets.default-placeholder": "Privzeto: %1", "sockets.delay": "Reconnection Delay", "analytics.settings": "Analytics Settings", "analytics.max-cache": "Analytics Cache Max Value", - "analytics.max-cache-help": "On high-traffic installs, the cache could be exhausted continuously if there are more concurrent active users than the Max Cache value. (Restart required)", - "compression.settings": "Compression Settings", - "compression.enable": "Enable Compression", - "compression.help": "This setting enables gzip compression. For a high-traffic website in production, the best way to put compression in place is to implement it at a reverse proxy level. You can enable it here for testing purposes." + "analytics.max-cache-help": "Pri namestitvah z velikim prometom bi se lahko predpomnilnik neprestano izčrpal, če je hkrati aktivnih uporabnikov več kot je največja vrednost predpomnilnika. (Potreben ponovni zagon)", + "compression.settings": "Nastavitve stiskanja", + "compression.enable": "Omogoči stiskanje", + "compression.help": "Ta nastavitev omogoča stiskanje GZIP. Za produkcijsko spletno mesto z velikim prometom je najboljši način za uvedbo stiskanja izvajanje na obratni ravni proxyja. Za namene testiranja ga lahko omogočite tukaj." } \ No newline at end of file diff --git a/public/language/sl/admin/settings/api.json b/public/language/sl/admin/settings/api.json index 50892925f3..01306f9cf2 100644 --- a/public/language/sl/admin/settings/api.json +++ b/public/language/sl/admin/settings/api.json @@ -1,16 +1,16 @@ { - "tokens": "Tokens", - "settings": "Settings", - "lead-text": "From this page you can configure access to the Write API in NodeBB.", - "intro": "By default, the Write API authenticates users based on their session cookie, but NodeBB also supports Bearer authentication via tokens generated via this page.", - "docs": "Click here to access the full API specification", + "tokens": "Žetoni", + "settings": "Nastavitve", + "lead-text": "Na tej strani lahko konfigurirate dostop do API-ja za pisanje v NodeBB.", + "intro": "API za pisanje privzeto preverja uporabnike na podlagi njihovega piškotka seje, vendar NodeBB podpira tudi preverjanje pristnosti nosilca prek žetonov, ustvarjenih na tej strani.", + "docs": "Kliknite tukaj za dostop do celotne specifikacije API-ja", - "require-https": "Require API usage via HTTPS only", - "require-https-caveat": "Note: Some installations involving load balancers may proxy their requests to NodeBB using HTTP, in which case this option should remain disabled.", + "require-https": "Zahtevaj uporabo API samo prek protokola HTTPS", + "require-https-caveat": "Opomba: Nekatere namestitve, ki vključujejo izravnalnike obremenitve, lahko svoje zahteve posredujejo NodeBB prek protokola HTTP, v tem primeru bi morala ta možnost ostati onemogočena.", - "uid": "User ID", - "uid-help-text": "Specify a User ID to associate with this token. If the user ID is 0, it will be considered a master token, which can assume the identity of other users based on the _uid parameter", - "description": "Description", - "no-description": "No description specified.", - "token-on-save": "Token will be generated once form is saved" + "uid": "ID uporabnika", + "uid-help-text": "Določite ID uporabnika, ki ga želite povezati s tem žetonom. Če je ID uporabnika 0, bo veljal za glavni žeton, ki lahko prevzame identiteto drugih uporabnikov na podlagi parametra _uid", + "description": "Opis", + "no-description": "Opis ni naveden.", + "token-on-save": "Žeton bo ustvarjen, ko bo obrazec shranjen" } \ No newline at end of file diff --git a/public/language/sl/admin/settings/chat.json b/public/language/sl/admin/settings/chat.json index 67898611e7..95d7342274 100644 --- a/public/language/sl/admin/settings/chat.json +++ b/public/language/sl/admin/settings/chat.json @@ -1,9 +1,9 @@ { - "chat-settings": "Chat Settings", - "disable": "Disable chat", - "disable-editing": "Disable chat message editing/deletion", + "chat-settings": "Nastavitve klepeta", + "disable": "Onemogoči klepet", + "disable-editing": "Onemogoči urejanje/brisanje sporočila klepeta", "disable-editing-help": "Administrators and global moderators are exempt from this restriction", - "max-length": "Maximum length of chat messages", + "max-length": "Največja dolžina sporočila klepeta", "max-room-size": "Maximum number of users in chat rooms", "delay": "Time between chat messages in milliseconds", "notification-delay": "Notification delay for chat messages. (0 for no delay)", diff --git a/public/language/sl/admin/settings/cookies.json b/public/language/sl/admin/settings/cookies.json index 1ffd2dced4..baea71728d 100644 --- a/public/language/sl/admin/settings/cookies.json +++ b/public/language/sl/admin/settings/cookies.json @@ -1,13 +1,13 @@ { - "eu-consent": "EU Consent", - "consent.enabled": "Enabled", + "eu-consent": "EU soglasje", + "consent.enabled": "Omogočeno", "consent.message": "Notification message", "consent.acceptance": "Acceptance message", "consent.link-text": "Policy Link Text", "consent.link-url": "Policy Link URL", "consent.blank-localised-default": "Leave blank to use NodeBB localised defaults", - "settings": "Settings", + "settings": "Nastavitve", "cookie-domain": "Session cookie domain", - "max-user-sessions": "Max active sessions per user", - "blank-default": "Leave blank for default" + "max-user-sessions": "Največ aktivnih sej na uporabnika", + "blank-default": "Za privzeto pusti prazno" } \ No newline at end of file diff --git a/public/language/sl/admin/settings/email.json b/public/language/sl/admin/settings/email.json index 9bdbde26a6..65f579d28c 100644 --- a/public/language/sl/admin/settings/email.json +++ b/public/language/sl/admin/settings/email.json @@ -6,7 +6,7 @@ "from-help": "The from name to display in the email.", "smtp-transport": "SMTP Transport", - "smtp-transport.enabled": "Use an external email server to send emails", + "smtp-transport.enabled": "Enable SMTP Transport", "smtp-transport-help": "You can select from a list of well-known services or enter a custom one.", "smtp-transport.service": "Select a service", "smtp-transport.service-custom": "Custom Service", @@ -37,6 +37,10 @@ "subscriptions.hour": "Digest Hour", "subscriptions.hour-help": "Please enter a number representing the hour to send scheduled email digests (e.g. 0 for midnight, 17 for 5:00pm). Keep in mind that this is the hour according to the server itself, and may not exactly match your system clock.
The approximate server time is:
The next daily digest is scheduled to be sent ", "notifications.remove-images": "Remove images from email notifications", + "require-email-address": "Require new users to specify an email address", + "require-email-address-warning": "By default, users can opt-out of entering an email address. Enabling this option means they have to enter an email address in order to proceed with registration. It does not ensure user will enter a real email address, nor even an address they own.", "include-unverified-emails": "Send emails to recipients who have not explicitly confirmed their emails", - "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws." -} \ No newline at end of file + "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws.", + "prompt": "Prompt users to enter or confirm their emails", + "prompt-help": "If a user does not have an email set, or their email is not confirmed, a warning will be shown on screen." +} diff --git a/public/language/sl/admin/settings/general.json b/public/language/sl/admin/settings/general.json index be7df90870..0705ada86f 100644 --- a/public/language/sl/admin/settings/general.json +++ b/public/language/sl/admin/settings/general.json @@ -1,28 +1,28 @@ { - "site-settings": "Site Settings", - "title": "Site Title", - "title.short": "Short Title", - "title.short-placeholder": "If no short title is specified, the site title will be used", - "title.url": "URL", - "title.url-placeholder": "The URL of the site title", - "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.", - "title.name": "Your Community Name", - "title.show-in-header": "Show Site Title in Header", - "browser-title": "Browser Title", - "browser-title-help": "If no browser title is specified, the site title will be used", - "title-layout": "Title Layout", + "site-settings": "Nastavitve spletnega mesta", + "title": "Naslov spletnega mesta", + "title.short": "Kratki naslov", + "title.short-placeholder": "Če kratek naslov ni naveden, bo uporabljen naslov spletnega mesta", + "title.url": "Title Link URL", + "title.url-placeholder": "URL naslova spletnega mesta", + "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", + "title.name": "Ime vaše skupnosti", + "title.show-in-header": "V glavi pokaži naslov strani", + "browser-title": "Naslov brskalnika", + "browser-title-help": "Če naslov brskalnika ni naveden, bo uporabljen naslov spletnega mesta", + "title-layout": "Postavitev naslova", "title-layout-help": "Define how the browser title will be structured ie. {pageTitle} | {browserTitle}", - "description.placeholder": "A short description about your community", - "description": "Site Description", - "keywords": "Site Keywords", - "keywords-placeholder": "Keywords describing your community, comma-separated", - "logo": "Site Logo", - "logo.image": "Image", - "logo.image-placeholder": "Path to a logo to display on forum header", + "description.placeholder": "Kratek opis vaše skupnosti", + "description": "Opis spletne strani", + "keywords": "Ključne besede spletnega mesta", + "keywords-placeholder": "Ključne besede, ki opisujejo vašo skupnost, ločene z vejicami", + "logo": "Logotip spletnega mesta", + "logo.image": "Slika", + "logo.image-placeholder": "Pot do logotipa za prikaz v glavi foruma", "logo.upload": "Upload", - "logo.url": "URL", - "logo.url-placeholder": "The URL of the site logo", - "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.", + "logo.url": "Logo Link URL", + "logo.url-placeholder": "URL logotipa spletnega mesta", + "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "logo.alt-text": "Alt Text", "log.alt-text-placeholder": "Alternative text for accessibility", "favicon": "Favicon", @@ -33,18 +33,18 @@ "touch-icon.help": "Recommended size and format: 512x512, PNG format only. If no touch icon is specified, NodeBB will fall back to using the favicon.", "maskable-icon": "Maskable (Homescreen) Icon", "maskable-icon.help": "Recommended size and format: 512x512, PNG format only. If no maskable icon is specified, NodeBB will fall back to the Touch Icon.", - "outgoing-links": "Outgoing Links", + "outgoing-links": "Odhodne povezave", "outgoing-links.warning-page": "Use Outgoing Links Warning Page", - "search": "Search", - "search-default-in": "Search In", - "search-default-in-quick": "Quick Search In", - "search-default-sort-by": "Sort by", + "search": "Išči", + "search-default-in": "Išči v", + "search-default-in-quick": "Hitro išči v", + "search-default-sort-by": "Razvrsti po", "outgoing-links.whitelist": "Domains to whitelist for bypassing the warning page", - "site-colors": "Site Color Metadata", - "theme-color": "Theme Color", - "background-color": "Background Color", - "background-color-help": "Color used for splash screen background when website is installed as a PWA", - "undo-timeout": "Undo Timeout", - "undo-timeout-help": "Some operations such as moving topics will allow for the moderator to undo their action within a certain timeframe. Set to 0 to disable undo completely.", + "site-colors": "Metapodatki o barvi spletnega mesta", + "theme-color": "Barva teme", + "background-color": "Barva ozadja", + "background-color-help": "Barva, ki se uporablja za ozadje začetnega zaslona, ​​ko je spletno mesto nameščeno kot PWA", + "undo-timeout": "Razveljavi časovno omejitev", + "undo-timeout-help": "Nekatere operacije, kot so premikanje tem, bodo moderatorju omogočile, da v določenem časovnem okviru razveljavi svoje dejanje. Nastavite na 0, da popolnoma onemogočite razveljavitev.", "topic-tools": "Topic Tools" -} \ No newline at end of file +} diff --git a/public/language/sl/admin/settings/group.json b/public/language/sl/admin/settings/group.json index f13933ea7e..33c91eb310 100644 --- a/public/language/sl/admin/settings/group.json +++ b/public/language/sl/admin/settings/group.json @@ -1,6 +1,6 @@ { - "general": "General", - "private-groups": "Private Groups", + "general": "Splošno", + "private-groups": "Zasebne skupine", "private-groups.help": "If enabled, joining of groups requires the approval of the group owner (Default: enabled)", "private-groups.warning": "Beware! If this option is disabled and you have private groups, they automatically become public.", "allow-multiple-badges": "Allow Multiple Badges", diff --git a/public/language/sl/admin/settings/guest.json b/public/language/sl/admin/settings/guest.json index 75d44f37e4..f0a17f7cfa 100644 --- a/public/language/sl/admin/settings/guest.json +++ b/public/language/sl/admin/settings/guest.json @@ -1,5 +1,5 @@ { - "settings": "Settings", + "settings": "Nastavitve", "handles.enabled": "Allow guest handles", "handles.enabled-help": "This option exposes a new field that allows guests to pick a name to associate with each post they make. If disabled, they will simply be called \"Guest\"", "topic-views.enabled": "Allow guests to increase topic view counts", diff --git a/public/language/sl/admin/settings/homepage.json b/public/language/sl/admin/settings/homepage.json index 7428d59eeb..7198443b3b 100644 --- a/public/language/sl/admin/settings/homepage.json +++ b/public/language/sl/admin/settings/homepage.json @@ -1,8 +1,8 @@ { - "home-page": "Home Page", - "description": "Choose what page is shown when users navigate to the root URL of your forum.", - "home-page-route": "Home Page Route", - "custom-route": "Custom Route", - "allow-user-home-pages": "Allow User Home Pages", - "home-page-title": "Title of the home page (default \"Home\")" + "home-page": "Domača stran", + "description": "Izberite, katera stran se prikaže, ko se uporabniki pomaknejo do korenskega URL-ja vašega foruma.", + "home-page-route": "Pot do domače strani", + "custom-route": "Pot po meri", + "allow-user-home-pages": "Dovoli domače strani uporabnikov", + "home-page-title": "Naslov domače strani (privzeto »Domača stran«)" } \ No newline at end of file diff --git a/public/language/sl/admin/settings/languages.json b/public/language/sl/admin/settings/languages.json index bdd57849b3..311b4af2e7 100644 --- a/public/language/sl/admin/settings/languages.json +++ b/public/language/sl/admin/settings/languages.json @@ -1,6 +1,6 @@ { - "language-settings": "Language Settings", - "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language", - "auto-detect": "Auto Detect Language Setting for Guests" + "language-settings": "Nastavitve jezika", + "description": "Privzeti jezik določa jezikovne nastavitve za vse uporabnike, ki obiščejo vaš forum.
Posamezni uporabniki lahko na strani z nastavitvami računa preglasijo privzeti jezik.", + "default-language": "Privzeti jezik", + "auto-detect": "Samodejna zaznava nastavitev jezika za goste" } \ No newline at end of file diff --git a/public/language/sl/admin/settings/navigation.json b/public/language/sl/admin/settings/navigation.json index 13dd01aae7..fdfe4fce31 100644 --- a/public/language/sl/admin/settings/navigation.json +++ b/public/language/sl/admin/settings/navigation.json @@ -1,23 +1,25 @@ { - "icon": "Icon:", + "icon": "Ikona:", "change-icon": "change", - "route": "Route:", + "route": "Pot:", "tooltip": "Tooltip:", - "text": "Text:", + "text": "Besedilo:", "text-class": "Text Class: optional", "class": "Class: optional", - "id": "ID: optional", + "id": "ID: izbirno", - "properties": "Properties:", - "groups": "Groups:", - "open-new-window": "Open in a new window", + "properties": "Lastnosti:", + "groups": "Skupine", + "open-new-window": "Odpri v novem oknu", + "dropdown": "Dropdown", + "dropdown-placeholder": "Place your dropdown menu items below, ie:
<li><a href="https://myforum.com">Link 1</a></li>", - "btn.delete": "Delete", - "btn.disable": "Disable", - "btn.enable": "Enable", + "btn.delete": "Izbriši", + "btn.disable": "Onemogoči", + "btn.enable": "Omogoči", - "available-menu-items": "Available Menu Items", - "custom-route": "Custom Route", - "core": "core", - "plugin": "plugin" -} \ No newline at end of file + "available-menu-items": "Razpoložljivi elementi menija", + "custom-route": "Pot po meri", + "core": "jedro", + "plugin": "vtičnik" +} diff --git a/public/language/sl/admin/settings/notifications.json b/public/language/sl/admin/settings/notifications.json index da6c9680a3..15cc81a25c 100644 --- a/public/language/sl/admin/settings/notifications.json +++ b/public/language/sl/admin/settings/notifications.json @@ -1,6 +1,7 @@ { - "notifications": "Notifications", - "welcome-notification": "Welcome Notification", - "welcome-notification-link": "Welcome Notification Link", - "welcome-notification-uid": "Welcome Notification User (UID)" + "notifications": "Obvestila", + "welcome-notification": "Obvestilo o dobrodošlici", + "welcome-notification-link": "Povezava do obvestila o dobrodošlici", + "welcome-notification-uid": "Obvestilo o dobrodošlici za uporabnika (UID)", + "post-queue-notification-uid": "Post Queue User (UID)" } \ No newline at end of file diff --git a/public/language/sl/admin/settings/pagination.json b/public/language/sl/admin/settings/pagination.json index 3bf306b2f9..fee948d2b5 100644 --- a/public/language/sl/admin/settings/pagination.json +++ b/public/language/sl/admin/settings/pagination.json @@ -1,12 +1,12 @@ { - "pagination": "Pagination Settings", - "enable": "Paginate topics and posts instead of using infinite scroll.", - "posts": "Post Pagination", - "topics": "Topic Pagination", - "posts-per-page": "Posts per Page", - "max-posts-per-page": "Maximum posts per page", - "categories": "Category Pagination", - "topics-per-page": "Topics per Page", - "max-topics-per-page": "Maximum topics per page", - "categories-per-page": "Categories per page" + "pagination": "Nastavitve številčenja", + "enable": "Namesto neskončnega drsenja uporabite številčenje tem in objav.", + "posts": "Številčenje objav", + "topics": "Številčenje tem", + "posts-per-page": "Objav na stran", + "max-posts-per-page": "Največ objav na stran", + "categories": "Številčenje kategorij", + "topics-per-page": "Tem na stran", + "max-topics-per-page": "Največ tem na stran", + "categories-per-page": "Kategorij na stran" } \ No newline at end of file diff --git a/public/language/sl/admin/settings/post.json b/public/language/sl/admin/settings/post.json index 27493aafbd..a0ff22173d 100644 --- a/public/language/sl/admin/settings/post.json +++ b/public/language/sl/admin/settings/post.json @@ -1,62 +1,66 @@ { - "sorting": "Post Sorting", - "sorting.post-default": "Default Post Sorting", - "sorting.oldest-to-newest": "Oldest to Newest", - "sorting.newest-to-oldest": "Newest to Oldest", - "sorting.most-votes": "Most Votes", - "sorting.most-posts": "Most Posts", - "sorting.topic-default": "Default Topic Sorting", - "length": "Post Length", - "post-queue": "Post Queue", - "restrictions": "Posting Restrictions", - "restrictions-new": "New User Restrictions", - "restrictions.post-queue": "Enable post queue", - "restrictions.post-queue-rep-threshold": "Reputation required to bypass post queue", - "restrictions.groups-exempt-from-post-queue": "Select groups that should be exempt from the post queue", - "restrictions-new.post-queue": "Enable new user restrictions", - "restrictions.post-queue-help": "Enabling post queue will put the posts of new users in a queue for approval", - "restrictions-new.post-queue-help": "Enabling new user restrictions will set restrictions on posts created by new users", - "restrictions.seconds-between": "Number of seconds between posts", - "restrictions.seconds-between-new": "Seconds between posts for new users", + "sorting": "Razvrščanje objav", + "sorting.post-default": "Privzeto razvrščanje objav", + "sorting.oldest-to-newest": "Najstarejše do najnovejše", + "sorting.newest-to-oldest": "Najnovejše do najstarejše", + "sorting.most-votes": "Največ glasov", + "sorting.most-posts": "Največ objav", + "sorting.topic-default": "Privzeto razvrščanje tem", + "length": "Dolžina objave", + "post-queue": "Čakalna vrsta objav", + "restrictions": "Omejitve objavljanja", + "restrictions-new": "Omejitve novega uporabnika", + "restrictions.post-queue": "Omogoči čakalno vrsto objav", + "restrictions.post-queue-rep-threshold": "Da se izogne ​​čakalni vrsti objav je potreben ugled", + "restrictions.groups-exempt-from-post-queue": "Izberite skupine, ki bi morale biti izvzete iz čakalne vrste objav", + "restrictions-new.post-queue": "Omogoči omejitve novega uporabnika", + "restrictions.post-queue-help": "Če omogočite čakalno vrsto objav, bodo objave novih uporabnikov postavljene v čakalno vrsto za odobritev", + "restrictions-new.post-queue-help": "Omogočanje omejitev za nove uporabnike bo postavilo omejitve za objave novih uporabnikov", + "restrictions.seconds-between": "Število sekund med objavami", + "restrictions.seconds-between-new": "Sekunde med objavami za novega uporabnika", "restrictions.rep-threshold": "Reputation threshold before these restrictions are lifted", - "restrictions.seconds-before-new": "Seconds before a new user can make their first post", - "restrictions.seconds-edit-after": "Number of seconds a post remains editable (set to 0 to disable)", - "restrictions.seconds-delete-after": "Number of seconds a post remains deletable (set to 0 to disable)", - "restrictions.replies-no-delete": "Number of replies after users are disallowed to delete their own topics (set to 0 to disable)", - "restrictions.min-title-length": "Minimum Title Length", - "restrictions.max-title-length": "Maximum Title Length", - "restrictions.min-post-length": "Minimum Post Length", - "restrictions.max-post-length": "Maximum Post Length", - "restrictions.days-until-stale": "Days until topic is considered stale", - "restrictions.stale-help": "If a topic is considered \"stale\", then a warning will be shown to users who attempt to reply to that topic.", - "timestamp": "Timestamp", - "timestamp.cut-off": "Date cut-off (in days)", + "restrictions.seconds-before-new": "Sekunde, preden lahko nov uporabnik objavi svojo prvo objavo", + "restrictions.seconds-edit-after": "Število sekund, ko je objavo še mogoče urejati (nastavite na 0, da onemogočite)", + "restrictions.seconds-delete-after": "Število sekund, ko je objavo še mogoče izbrisati (nastavite na 0, da onemogočite)", + "restrictions.replies-no-delete": "Število odgovorov, ko uporabnikom ni dovoljeno izbrisati lastnih tem (nastavite na 0, da onemogočite)", + "restrictions.min-title-length": "Najmanjša dolžina naslova", + "restrictions.max-title-length": "Največja dolžina naslova", + "restrictions.min-post-length": "Najmanjša dolžina objave", + "restrictions.max-post-length": "Največja dolžina objave", + "restrictions.days-until-stale": "Število ​​dni, dokler se tema ne šteje za zastarelo", + "restrictions.stale-help": "Če se tema šteje za \"zastarelo\", bo uporabnikom, ki poskušajo odgovoriti na to temo, prikazano opozorilo.", + "timestamp": "Časovni žig", + "timestamp.cut-off": "Mejni datum (v dneh)", "timestamp.cut-off-help": "Dates & times will be shown in a relative manner (e.g. \"3 hours ago\" / \"5 days ago\"), and localised into various\n\t\t\t\t\tlanguages. After a certain point, this text can be switched to display the localised date itself\n\t\t\t\t\t(e.g. 5 Nov 2016 15:30).
(Default: 30, or one month). Set to 0 to always display dates, leave blank to always display relative times.", "timestamp.necro-threshold": "Necro Threshold (in days)", "timestamp.necro-threshold-help": "A message will be shown between posts if the time between them is longer than the necro threshold. (Default: 7, or one week). Set to 0 to disable.", - "timestamp.topic-views-interval": "Increment topic views interval (in minutes)", - "timestamp.topic-views-interval-help": "Topic views will only increment once every X minutes as defined by this setting.", + "timestamp.topic-views-interval": "Povečanje intervala ogledov teme (v minutah)", + "timestamp.topic-views-interval-help": "Ogledi tem se bodo povečali le enkrat na vsakih X minut, kot je določeno s to nastavitvijo.", "teaser": "Teaser Post", - "teaser.last-post": "Last – Show the latest post, including the original post, if no replies", - "teaser.last-reply": "Last – Show the latest reply, or a \"No replies\" placeholder if no replies", - "teaser.first": "First", - "unread": "Unread Settings", + "teaser.last-post": "Zadnja – Prikaži najnovejšo objavo, vključno z izvirno, če ni odgovorov", + "teaser.last-reply": "Zadnja – Prikaži najnovejši odgovor ali \"Ni odgovorov\", če ni odgovorov", + "teaser.first": "Prvi", + "showPostPreviewsOnHover": "Show a preview of posts when mouse overed", + "unread": "Neprebrane nastavitve", "unread.cutoff": "Unread cutoff days", - "unread.min-track-last": "Minimum posts in topic before tracking last read", - "recent": "Recent Settings", + "unread.min-track-last": "Najmanjše število objav v temi pred sledenjem zadnjem branju", + "recent": "Nedavne nastavitve", "recent.max-topics": "Maximum topics on /recent", "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", - "signature": "Signature Settings", - "signature.disable": "Disable signatures", - "signature.no-links": "Disable links in signatures", - "signature.no-images": "Disable images in signatures", - "signature.max-length": "Maximum Signature Length", - "composer": "Composer Settings", + "signature": "Nastavitve podpisa", + "signature.disable": "Onemogoči podpise", + "signature.no-links": "Onemogoči povezave v podpisih", + "signature.no-images": "Onemogoči slike v podpisih", + "signature.max-length": "Največja dolžina podpisa", + "composer": "Nastavitve sestavljalnika", "composer-help": "The following settings govern the functionality and/or appearance of the post composer shown\n\t\t\t\tto users when they create new topics, or reply to existing topics.", - "composer.show-help": "Show \"Help\" tab", - "composer.enable-plugin-help": "Allow plugins to add content to the help tab", - "composer.custom-help": "Custom Help Text", - "ip-tracking": "IP Tracking", - "ip-tracking.each-post": "Track IP Address for each post", - "enable-post-history": "Enable Post History" + "composer.show-help": "Prikaži zavihek \"Pomoč\"", + "composer.enable-plugin-help": "Dovoli vtičnikom dodajanje vsebine na zavihek za pomoč", + "composer.custom-help": "Besedilo pomoči po meri", + "backlinks": "Backlinks", + "backlinks.enabled": "Enable topic backlinks", + "backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.", + "ip-tracking": "IP sledenje", + "ip-tracking.each-post": "Sledi IP naslov za vsako objavo", + "enable-post-history": "Omogoči zgodovino objav" } \ No newline at end of file diff --git a/public/language/sl/admin/settings/reputation.json b/public/language/sl/admin/settings/reputation.json index 7cfa636521..a943374890 100644 --- a/public/language/sl/admin/settings/reputation.json +++ b/public/language/sl/admin/settings/reputation.json @@ -1,22 +1,22 @@ { - "reputation": "Reputation Settings", - "disable": "Disable Reputation System", - "disable-down-voting": "Disable Down Voting", - "votes-are-public": "All Votes Are Public", + "reputation": "Nastavitve ugleda", + "disable": "Onemogoči sistem ugleda", + "disable-down-voting": "Onemogoči glasovanje proti", + "votes-are-public": "Vsi glasovi so javni", "thresholds": "Activity Thresholds", - "min-rep-downvote": "Minimum reputation to downvote posts", - "downvotes-per-day": "Downvotes per day (set to 0 for unlimited downvotes)", - "downvotes-per-user-per-day": "Downvotes per user per day (set to 0 for unlimited downvotes)", - "min-rep-flag": "Minimum reputation to flag posts", - "min-rep-website": "Minimum reputation to add \"Website\" to user profile", - "min-rep-aboutme": "Minimum reputation to add \"About me\" to user profile", - "min-rep-signature": "Minimum reputation to add \"Signature\" to user profile", - "min-rep-profile-picture": "Minimum reputation to add \"Profile Picture\" to user profile", - "min-rep-cover-picture": "Minimum reputation to add \"Cover Picture\" to user profile", + "min-rep-downvote": "Najmanjši ugled za objavo glasov proti", + "downvotes-per-day": "Glasovi proti na dan (nastavljeno na 0 za neomejeno število glasov proti)", + "downvotes-per-user-per-day": "Glasovi proti na uporabnika na dan (nastavljeno na 0 za neomejeno število glasov proti)", + "min-rep-flag": "Najmanjši ugled za označevanje objav z zastavico", + "min-rep-website": "Najmanjši ugled za dodajanje \"Spletna stran\" v uporabniški profil", + "min-rep-aboutme": "Najmanjši ugled za dodajanje \"O meni\" v uporabniški profil", + "min-rep-signature": "Najmanjši ugled za dodajanje \"Podpis\" v uporabniški profil", + "min-rep-profile-picture": "Najmanjši ugled za dodajanje \"Profilna slika\" v uporabniški profil", + "min-rep-cover-picture": "Najmanjši ugled za dodajanje \"Naslovna slika\" v uporabniški profil", "flags": "Flag Settings", - "flags.limit-per-target": "Maximum number of times something can be flagged", - "flags.limit-per-target-placeholder": "Default: 0", - "flags.limit-per-target-help": "When a post or user is flagged multiple times, each additional flag is considered a "report" and added to the original flag. Set this option to a number other than zero to limit the number of reports an item can receive.", + "flags.limit-per-target": "Največkrat, ko je mogoče nekaj označiti z zastavico", + "flags.limit-per-target-placeholder": "Privzeto: 0", + "flags.limit-per-target-help": "Ko je objava ali uporabnik večkrat označen z zastavico, se vsaka dodatna zastavica šteje za & quot;poročilo" in dodana prvotni zastavici. To možnost nastavite na število, različno od nič, da omejite število poročil, ki jih element lahko prejme.", "flags.auto-resolve-on-ban": "Automatically resolve all of a user's tickets when they are banned" } \ No newline at end of file diff --git a/public/language/sl/admin/settings/social.json b/public/language/sl/admin/settings/social.json index 23aedfcfaa..f01d398aa1 100644 --- a/public/language/sl/admin/settings/social.json +++ b/public/language/sl/admin/settings/social.json @@ -1,5 +1,5 @@ { - "post-sharing": "Post Sharing", - "info-plugins-additional": "Plugins can add additional networks for sharing posts.", - "save-success": "Successfully saved Post Sharing Networks!" + "post-sharing": "Deljenje objav", + "info-plugins-additional": "Vtičniki lahko dodajo dodatna omrežja za deljenje objav.", + "save-success": "Uspešno shranjena omrežja za deljenje objav!" } \ No newline at end of file diff --git a/public/language/sl/admin/settings/sockets.json b/public/language/sl/admin/settings/sockets.json index d04ee42fcf..a47145831d 100644 --- a/public/language/sl/admin/settings/sockets.json +++ b/public/language/sl/admin/settings/sockets.json @@ -1,6 +1,6 @@ { - "reconnection": "Reconnection Settings", - "max-attempts": "Max Reconnection Attempts", - "default-placeholder": "Default: %1", - "delay": "Reconnection Delay" + "reconnection": "Nastavitve vzpostavitve ponovne povezave", + "max-attempts": "Največ poskusov vzpostavitve ponovne povezave", + "default-placeholder": "Privzeto: %1", + "delay": "Zamuda pri vzpostavitvi ponovne povezave" } \ No newline at end of file diff --git a/public/language/sl/admin/settings/sounds.json b/public/language/sl/admin/settings/sounds.json index 95ccbde0f1..0b20dfc83f 100644 --- a/public/language/sl/admin/settings/sounds.json +++ b/public/language/sl/admin/settings/sounds.json @@ -1,9 +1,9 @@ { - "notifications": "Notifications", - "chat-messages": "Chat Messages", - "play-sound": "Play", - "incoming-message": "Incoming Message", - "outgoing-message": "Outgoing Message", - "upload-new-sound": "Upload New Sound", - "saved": "Settings Saved" + "notifications": "Obvestila", + "chat-messages": "Sporočila klepeta", + "play-sound": "Predvajaj", + "incoming-message": "Dohodno sporočilo", + "outgoing-message": "Odhodno sporočilo", + "upload-new-sound": "Naloži nov zvok", + "saved": "Nastavitve so shranjene" } \ No newline at end of file diff --git a/public/language/sl/admin/settings/tags.json b/public/language/sl/admin/settings/tags.json index 080010f6f0..3134921790 100644 --- a/public/language/sl/admin/settings/tags.json +++ b/public/language/sl/admin/settings/tags.json @@ -1,12 +1,12 @@ { - "tag": "Tag Settings", - "link-to-manage": "Manage Tags", + "tag": "Nastavitve oznak", + "link-to-manage": "Upravljaj oznake", "system-tags": "System Tags", - "system-tags-help": "Only privileged users will be able to use these tags.", - "min-per-topic": "Minimum Tags per Topic", - "max-per-topic": "Maximum Tags per Topic", - "min-length": "Minimum Tag Length", - "max-length": "Maximum Tag Length", - "related-topics": "Related Topics", - "max-related-topics": "Maximum related topics to display (if supported by theme)" + "system-tags-help": "Ze oznake bodo lahko uporabljali le privilegirani uporabniki.", + "min-per-topic": "Najmanj oznak na temo", + "max-per-topic": "Največ oznak na temo", + "min-length": "Najmanjša dolžina oznake", + "max-length": "Največja dolžina oznake", + "related-topics": "Sorodne teme", + "max-related-topics": "Največ sorodnih tem za prikaz (če jih tema podpira)" } \ No newline at end of file diff --git a/public/language/sl/admin/settings/uploads.json b/public/language/sl/admin/settings/uploads.json index 449ed22522..f03378516f 100644 --- a/public/language/sl/admin/settings/uploads.json +++ b/public/language/sl/admin/settings/uploads.json @@ -1,7 +1,7 @@ { - "posts": "Posts", - "allow-files": "Allow users to upload regular files", - "private": "Make uploaded files private", + "posts": "Objave", + "allow-files": "Dovoli uporabnikom nalaganje navadnih datotek", + "private": "Naložene datoteke označi kot zasebne", "strip-exif-data": "Strip EXIF Data", "private-extensions": "File extensions to make private", "private-uploads-extensions-help": "Enter comma-separated list of file extensions to make private here (e.g. pdf,xls,doc). An empty list means all files are private.", @@ -19,7 +19,7 @@ "reject-image-height-help": "Images taller than this value will be rejected.", "allow-topic-thumbnails": "Allow users to upload topic thumbnails", "topic-thumb-size": "Topic Thumb Size", - "allowed-file-extensions": "Allowed File Extensions", + "allowed-file-extensions": "Dovoljene pripone datoteke", "allowed-file-extensions-help": "Enter comma-separated list of file extensions here (e.g. pdf,xls,doc). An empty list means all extensions are allowed.", "upload-limit-threshold": "Rate limit user uploads to:", "upload-limit-threshold-per-minute": "Per %1 Minute", @@ -28,7 +28,7 @@ "allow-profile-image-uploads": "Allow users to upload profile images", "convert-profile-image-png": "Convert profile image uploads to PNG", "default-avatar": "Custom Default Avatar", - "upload": "Upload", + "upload": "Naloži", "profile-image-dimension": "Profile Image Dimension", "profile-image-dimension-help": "(in pixels, default: 128 pixels)", "max-profile-image-size": "Maximum Profile Image File Size", diff --git a/public/language/sl/admin/settings/user.json b/public/language/sl/admin/settings/user.json index 48be13b75e..d680faf1ad 100644 --- a/public/language/sl/admin/settings/user.json +++ b/public/language/sl/admin/settings/user.json @@ -1,84 +1,85 @@ { - "authentication": "Authentication", - "require-email-confirmation": "Require Email Confirmation", - "email-confirm-interval": "User may not resend a confirmation email until", + "authentication": "Preverjanje pristnosti", + "require-email-confirmation": "Zahtevaj potrditev po e-pošti", + "email-confirm-interval": "Uporabnik morda ne bo mogel znova poslati potrditvenega e-poštnega sporočila, dokler", "email-confirm-email2": "minutes have elapsed", - "allow-login-with": "Allow login with", - "allow-login-with.username-email": "Username or Email", - "allow-login-with.username": "Username Only", - "allow-login-with.email": "Email Only", - "account-settings": "Account Settings", - "gdpr_enabled": "Enable GDPR consent collection", + "allow-login-with": "Dovoli prijavo z", + "allow-login-with.username-email": "Uporabniško ime ali e-poštni naslov", + "allow-login-with.username": "Samo uporabniško ime", + "allow-login-with.email": "Samo e-poštni naslov", + "account-settings": "Nastavitve računa", + "gdpr_enabled": "Omogoči zbiranje GDPR soglasij", "gdpr_enabled_help": "When enabled, all new registrants will be required to explicitly give consent for data collection and usage under the General Data Protection Regulation (GDPR). Note: Enabling GDPR does not force pre-existing users to provide consent. To do so, you will need to install the GDPR plugin.", - "disable-username-changes": "Disable username changes", - "disable-email-changes": "Disable email changes", - "disable-password-changes": "Disable password changes", - "allow-account-deletion": "Allow account deletion", - "hide-fullname": "Hide fullname from users", - "hide-email": "Hide email from users", - "show-fullname-as-displayname": "Show user's full name as their display name if available", - "themes": "Themes", - "disable-user-skins": "Prevent users from choosing a custom skin", - "account-protection": "Account Protection", - "admin-relogin-duration": "Admin relogin duration (minutes)", + "disable-username-changes": "Onemogoči spreminjanje uporabniškega imena", + "disable-email-changes": "Onemogoči spreminjanje e-poštnega naslova", + "disable-password-changes": "Onemogoči spreminjanje gesla", + "allow-account-deletion": "Dovoli brisanje računa", + "hide-fullname": "Skrij polno ime pred uporabniki", + "hide-email": "Skrij e-poštni naslov pred uporabniki", + "show-fullname-as-displayname": "Prikaži uporabnikovo polno ime kot njegovo prikazno ime, če je na voljo", + "themes": "Teme", + "disable-user-skins": "Prepreči uporabnikom izbiro preobleke po meri", + "account-protection": "Zaščita računa", + "admin-relogin-duration": "Trajanje ponovne prijave skrbnika (v minutah)", "admin-relogin-duration-help": "After a set amount of time accessing the admin section will require re-login, set to 0 to disable", - "login-attempts": "Login attempts per hour", + "login-attempts": "Število poskusov prijave na uro", "login-attempts-help": "If login attempts to a user's account exceeds this threshold, that account will be locked for a pre-configured amount of time", - "lockout-duration": "Account Lockout Duration (minutes)", - "login-days": "Days to remember user login sessions", - "password-expiry-days": "Force password reset after a set number of days", - "session-time": "Session Time", - "session-time-days": "Days", - "session-time-seconds": "Seconds", + "lockout-duration": "Trajanje zaklepanja računa (v minutah)", + "login-days": "Dnevi za zapomnitev sej za prijavo uporabnikov", + "password-expiry-days": "Vsilite ponastavitev gesla po nastavljenem številu dni", + "session-time": "Čas seje", + "session-time-days": "Dni", + "session-time-seconds": "Sekund", "session-time-help": "These values are used to govern how long a user stays logged in when they check "Remember Me" on login. Note that only one of these values will be used. If there is no seconds value we fall back to days. If there is no days value we default to 14 days.", - "online-cutoff": "Minutes after user is considered inactive", + "online-cutoff": "Minut po tem, ko je uporabnik neaktiven", "online-cutoff-help": "If user performs no actions for this duration, they are considered inactive and they do not receive realtime updates.", - "registration": "User Registration", - "registration-type": "Registration Type", - "registration-approval-type": "Registration Approval Type", - "registration-type.normal": "Normal", + "registration": "Registracija uporabnika", + "registration-type": "Vrsta registracije", + "registration-approval-type": "Vrsta odobritve registracije", + "registration-type.normal": "Običajno", "registration-type.admin-approval": "Admin Approval", "registration-type.admin-approval-ip": "Admin Approval for IPs", - "registration-type.invite-only": "Invite Only", + "registration-type.invite-only": "Samo povabilo", "registration-type.admin-invite-only": "Admin Invite Only", "registration-type.disabled": "No registration", "registration-type.help": "Normal - Users can register from the /register page.
\nInvite Only - Users can invite others from the users page.
\nAdmin Invite Only - Only administrators can invite others from users and admin/manage/users pages.
\nNo registration - No user registration.
", "registration-approval-type.help": "Normal - Users are registered immediately.
\nAdmin Approval - User registrations are placed in an approval queue for administrators.
\nAdmin Approval for IPs - Normal for new users, Admin Approval for IP addresses that already have an account.
", "registration-queue-auto-approve-time": "Automatic Approval Time", - "registration-queue-auto-approve-time-help": "Hours before user is approved automatically. 0 to disable.", + "registration-queue-auto-approve-time-help": "Ure do samodejne potrditve uporabnika. 0 da onemogočite.", "registration-queue-show-average-time": "Show users average time it takes to approve a new user", - "registration.max-invites": "Maximum Invitations per User", - "max-invites": "Maximum Invitations per User", + "registration.max-invites": "Največje število povabil na uporabnika", + "max-invites": "Največje število povabil na uporabnika", "max-invites-help": "0 for no restriction. Admins get infinite invitations
Only applicable for \"Invite Only\"", "invite-expiration": "Invite expiration", - "invite-expiration-help": "# of days invitations expire in.", - "min-username-length": "Minimum Username Length", - "max-username-length": "Maximum Username Length", - "min-password-length": "Minimum Password Length", - "min-password-strength": "Minimum Password Strength", - "max-about-me-length": "Maximum About Me Length", + "invite-expiration-help": "Število dni, v katerih poteče povabilo.", + "min-username-length": "Najmanjša dolžina uporabniškega imena", + "max-username-length": "Največja dolžina uporabniškega imena", + "min-password-length": "Najmanjša dolžina gesla", + "min-password-strength": "Najmanjša moč gesla", + "max-about-me-length": "Največja dolžina O meni", "terms-of-use": "Forum Terms of Use (Leave blank to disable)", "user-search": "User Search", "user-search-results-per-page": "Number of results to display", "default-user-settings": "Default User Settings", - "show-email": "Show email", - "show-fullname": "Show fullname", - "restrict-chat": "Only allow chat messages from users I follow", - "outgoing-new-tab": "Open outgoing links in new tab", - "topic-search": "Enable In-Topic Searching", - "update-url-with-post-index": "Update url with post index while browsing topics", + "show-email": "Pokaži e-poštni naslov", + "show-fullname": "Pokaži polno ime", + "restrict-chat": "V klepetu dovoli samo sporočila uporabnikov, ki jih spremljam", + "outgoing-new-tab": "Zunanje povezave odpri na novem zavihku", + "topic-search": "Omogoči iskanje v temi", + "update-url-with-post-index": "Med brskanjem po temah posodobite URL z indeksom objav", "digest-freq": "Subscribe to Digest", "digest-freq.off": "Off", - "digest-freq.daily": "Daily", - "digest-freq.weekly": "Weekly", - "digest-freq.monthly": "Monthly", + "digest-freq.daily": "Dnevno", + "digest-freq.weekly": "Tedensko", + "digest-freq.biweekly": "Bi-Weekly", + "digest-freq.monthly": "Mesečno", "email-chat-notifs": "Send an email if a new chat message arrives and I am not online", "email-post-notif": "Send an email when replies are made to topics I am subscribed to", - "follow-created-topics": "Follow topics you create", - "follow-replied-topics": "Follow topics that you reply to", - "default-notification-settings": "Default notification settings", + "follow-created-topics": "Spremljanj teme, ki si jih ustvaril", + "follow-replied-topics": "Spremljanj teme, na katere si odgovoril", + "default-notification-settings": "Privzete nastavitve obveščanja", "categoryWatchState": "Default category watch state", - "categoryWatchState.watching": "Watching", - "categoryWatchState.notwatching": "Not Watching", - "categoryWatchState.ignoring": "Ignoring" + "categoryWatchState.watching": "Spremljano", + "categoryWatchState.notwatching": "Ni spremljano", + "categoryWatchState.ignoring": "Prezrto" } diff --git a/public/language/sl/category.json b/public/language/sl/category.json index 6a7fda2a16..e96e8805f9 100644 --- a/public/language/sl/category.json +++ b/public/language/sl/category.json @@ -10,9 +10,9 @@ "watch": "Spremljaj.", "ignore": "Prezri.", "watching": "Spremljano", - "not-watching": "Not Watching", + "not-watching": "Ni spremljano", "ignoring": "Prezrto", - "watching.description": "Show topics in unread and recent", + "watching.description": "Prikaži teme v nedavno in nazadnje", "not-watching.description": "Do not show topics in unread, show in recent", "ignoring.description": "Do not show topics in unread and recent", "watching.message": "You are now watching updates from this category and all subcategories", diff --git a/public/language/sl/error.json b/public/language/sl/error.json index ff9b1be6a6..e87f4d0f4e 100644 --- a/public/language/sl/error.json +++ b/public/language/sl/error.json @@ -34,8 +34,9 @@ "email-invited": "Email was already invited", "email-not-confirmed": "Posting in some categories or topics is enabled once your email is confirmed, please click here to send a confirmation email.", "email-not-confirmed-chat": "Ne morete klepetati, dokler ne potrdite svojega e-poštnega naslova. Prosimo, kliknite tu za potrditev e-poštnega naslova.", - "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You won't be able to post or chat until your email is confirmed.", - "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery. Please click here to enter an email.", + "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You may not be able to post in some categories or chat until your email is confirmed.", + "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery, and may be necessary for chatting and posting in some categories. Please click here to enter an email.", + "user-doesnt-have-email": "User \"%1\" does not have an email set.", "email-confirm-failed": "Potrditev vašega e-poštnega naslova ni uspela. Prosimo, poskusite ponovno pozneje.", "confirm-email-already-sent": "Potrditveno e-sporočilo je že bilo poslano. Prosimo, počakajte %1 min za ponovno pošiljanje.", "sendmail-not-found": "Ne najdem izvršljive datoteke za pošiljanje e-pošte. Prepričajte se, da je ta nameščena in izvršljiva prek uporabnika, ki izvaja NodeBB.", @@ -103,6 +104,7 @@ "already-bookmarked": "You have already bookmarked this post", "already-unbookmarked": "You have already unbookmarked this post", "cant-ban-other-admins": "Ne morete izločati drugih skrbnikov!", + "cant-make-banned-users-admin": "You can't make banned users admin.", "cant-remove-last-admin": "Ste edini skrbnik. Preden se odstranite, dodajte novega skrbnika.", "account-deletion-disabled": "Account deletion is disabled", "cant-delete-admin": "Pred brisanjem tega računa morate odstraniti skrbniške pravice.", diff --git a/public/language/sl/flags.json b/public/language/sl/flags.json index 5bd46b6b1d..be4fdfcc04 100644 --- a/public/language/sl/flags.json +++ b/public/language/sl/flags.json @@ -5,7 +5,7 @@ "no-flags": "Hooray! No flags found.", "assignee": "Assignee", "update": "Update", - "updated": "Updated", + "updated": "Posodobljeno", "resolved": "Resolved", "target-purged": "The content this flag referred to has been purged and is no longer available.", @@ -13,61 +13,61 @@ "quick-filters": "Quick Filters", "filter-active": "There are one or more filters active in this list of flags", "filter-reset": "Remove Filters", - "filters": "Filter Options", + "filters": "Možnosti filtra", "filter-reporterId": "Reporter UID", "filter-targetUid": "Flagged UID", "filter-type": "Flag Type", "filter-type-all": "All Content", - "filter-type-post": "Post", - "filter-type-user": "User", + "filter-type-post": "Objava", + "filter-type-user": "Uporabnik", "filter-state": "State", "filter-assignee": "Assignee UID", - "filter-cid": "Category", - "filter-quick-mine": "Assigned to me", - "filter-cid-all": "All categories", - "apply-filters": "Apply Filters", - "more-filters": "More Filters", - "fewer-filters": "Fewer Filters", + "filter-cid": "Kategorija", + "filter-quick-mine": "Dodeljeno meni", + "filter-cid-all": "Vse kategorije", + "apply-filters": "Uveljavi filtre", + "more-filters": "Več filtrov", + "fewer-filters": "Manj filtrov", - "quick-actions": "Quick Actions", + "quick-actions": "Hitra dejanja", "flagged-user": "Flagged User", - "view-profile": "View Profile", - "start-new-chat": "Start New Chat", + "view-profile": "Poglej profil", + "start-new-chat": "Začni nov klepet", "go-to-target": "View Flag Target", - "assign-to-me": "Assign To Me", - "delete-post": "Delete Post", + "assign-to-me": "Dodeli meni", + "delete-post": "Izbriši objavo", "purge-post": "Purge Post", - "restore-post": "Restore Post", + "restore-post": "Obnovi objavo", - "user-view": "View Profile", - "user-edit": "Edit Profile", + "user-view": "Poglej profil", + "user-edit": "Uredi profil", "notes": "Flag Notes", - "add-note": "Add Note", - "no-notes": "No shared notes.", + "add-note": "Dodaj opombo", + "no-notes": "Ni deljenih opomb.", "delete-note-confirm": "Are you sure you want to delete this flag note?", - "note-added": "Note Added", - "note-deleted": "Note Deleted", + "note-added": "Opomba dodana", + "note-deleted": "Opomba izbrisana", "history": "Account & Flag History", "no-history": "No flag history.", "state-all": "All states", - "state-open": "New/Open", + "state-open": "Nov/Odpri", "state-wip": "Work in Progress", "state-resolved": "Resolved", - "state-rejected": "Rejected", - "no-assignee": "Not Assigned", + "state-rejected": "Zavrnjeno", + "no-assignee": "Ni dodeljeno", - "sort": "Sort by", + "sort": "Razvrsti po", "sort-newest": "Newest first", "sort-oldest": "Oldest first", "sort-reports": "Most reports", "sort-all": "All flag types...", "sort-posts-only": "Posts only...", - "sort-downvotes": "Most downvotes", - "sort-upvotes": "Most upvotes", - "sort-replies": "Most replies", + "sort-downvotes": "Največ glasov proti", + "sort-upvotes": "Največ glasov za", + "sort-replies": "Največ odgovorov", "modal-title": "Report Content", "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", diff --git a/public/language/sl/ip-blacklist.json b/public/language/sl/ip-blacklist.json index 588fbd62b6..35baff0c52 100644 --- a/public/language/sl/ip-blacklist.json +++ b/public/language/sl/ip-blacklist.json @@ -1,19 +1,19 @@ { - "lead": "Configure your IP blacklist here.", - "description": "Occasionally, a user account ban is not enough of a deterrant. Other times, restricting access to the forum to a specific IP or a range of IPs is the best way to protect a forum. In these scenarios, you can add troublesome IP addresses or entire CIDR blocks to this blacklist, and they will be prevented from logging in to or registering a new account.", - "active-rules": "Active Rules", - "validate": "Validate Blacklist", - "apply": "Apply Blacklist", - "hints": "Syntax Hints", - "hint-1": "Define a single IP addresses per line. You can add IP blocks as long as they follow the CIDR format (e.g. 192.168.100.0/22).", - "hint-2": "You can add in comments by starting lines with the # symbol.", + "lead": "Tu nastavite svoj črni seznam IP.", + "description": "Včasih prepoved uporabniškega računa ni dovolj odvračilna. V drugih primerih je najboljši način za zaščito foruma omejitev dostopa do foruma za določen IP ali vrsto IP-jev. V teh scenarijih lahko na ta črni seznam dodate problematične naslove IP ali celotne bloke CIDR, pri čemer se jim prepreči prijava ali registracija novega računa.", + "active-rules": "Aktivna pravila", + "validate": "Potrdi črno listo", + "apply": "Uveljavi črno listo", + "hints": "namigi za sintakso", + "hint-1": "Določite posamezne naslove IP na vrstico. Bloke IP lahko dodate, dokler sledijo formatu CIDR (e.g. 192.168.100.0/22).", + "hint-2": "Komentarje lahko dodajate tako, da vrstice začnete z znakom #.", - "validate.x-valid": "%1 out of %2 rule(s) valid.", - "validate.x-invalid": "The following %1 rules are invalid:", + "validate.x-valid": "%1 od %2 pravil je neveljavnih.", + "validate.x-invalid": "Naslednjih %1 pravil je neveljavnih:", - "alerts.applied-success": "Blacklist Applied", + "alerts.applied-success": "Črna lista je uveljavljena", - "analytics.blacklist-hourly": "Figure 1 – Blacklist hits per hour", - "analytics.blacklist-daily": "Figure 2 – Blacklist hits per day", - "ip-banned": "IP banned" + "analytics.blacklist-hourly": "Slika 1 – Zadetki na črni listi na uro", + "analytics.blacklist-daily": "Slika 2 – Zadetki na črni listi na dan", + "ip-banned": "Prepovedan IP" } \ No newline at end of file diff --git a/public/language/sl/modules.json b/public/language/sl/modules.json index 3032028b52..b236194486 100644 --- a/public/language/sl/modules.json +++ b/public/language/sl/modules.json @@ -13,28 +13,28 @@ "chat.recent-chats": "Zadnji klepeti", "chat.contacts": "Stiki", "chat.message-history": "Zgodovina klepeta", - "chat.message-deleted": "Message Deleted", - "chat.options": "Chat options", + "chat.message-deleted": "Sporočilo izbrisano", + "chat.options": "Možnosti klepeta", "chat.pop-out": "Klepet v novem oknu", - "chat.minimize": "Minimize", - "chat.maximize": "Povečaj", + "chat.minimize": "Minimiziraj", + "chat.maximize": "Maksimiraj", "chat.seven_days": "7 dni", "chat.thirty_days": "30 dni", "chat.three_months": "3 meseci", "chat.delete_message_confirm": "Ali ste prepričani, da želite izbrisati to sporočilo?", - "chat.retrieving-users": "Retrieving users...", - "chat.manage-room": "Manage Chat Room", + "chat.retrieving-users": "Pridobivanje uporabnikov...", + "chat.manage-room": "Upravljaj sobo klepeta", "chat.add-user-help": "Search for users here. When selected, the user will be added to the chat. The new user will not be able to see chat messages written before they were added to the conversation. Only room owners () may remove users from chat rooms.", "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", "chat.rename-room": "Rename Room", "chat.rename-placeholder": "Enter your room name here", "chat.rename-help": "The room name set here will be viewable by all participants in the room.", - "chat.leave": "Leave Chat", - "chat.leave-prompt": "Are you sure you wish to leave this chat?", - "chat.leave-help": "Leaving this chat will remove you from future correspondence in this chat. If you are re-added in the future, you will not see any chat history from prior to your re-joining.", + "chat.leave": "Zapusti klepet", + "chat.leave-prompt": "Ste prepričani, da želite zapustiti ta klepet?", + "chat.leave-help": "Če zapustite ta klepet boste izključeni iz prihodnje korespondence v tem klepetu. Če boste v prihodnosti v klepet znova dodani, ne boste videli zgodovine klepeta pred ponovno pridružitvijo.", "chat.in-room": "In this room", "chat.kick": "Kick", - "chat.show-ip": "Show IP", + "chat.show-ip": "Pokaži IP", "chat.owner": "Room Owner", "chat.system.user-join": "%1 has joined the room", "chat.system.user-leave": "%1 has left the room", @@ -54,17 +54,17 @@ "composer.formatting.strikethrough": "Prečrtano", "composer.formatting.code": "Code", "composer.formatting.link": "Povezava", - "composer.formatting.picture": "Slika", + "composer.formatting.picture": "Image Link", "composer.upload-picture": "Prenesi sliko", "composer.upload-file": "Prenesi datoteko", "composer.zen_mode": "Zen način", - "composer.select_category": "Select a category", - "composer.textarea.placeholder": "Enter your post content here, drag and drop images", + "composer.select_category": "Izberi kategorijo", + "composer.textarea.placeholder": "Tukaj vnesite vsebino objave, povlecite in spustite slike", "composer.schedule-for": "Schedule topic for", - "composer.schedule-date": "Date", - "composer.schedule-time": "Time", + "composer.schedule-date": "Datum", + "composer.schedule-time": "Čas", "composer.cancel-scheduling": "Cancel Scheduling", - "composer.set-schedule-date": "Set Date", + "composer.set-schedule-date": "Nastavi datum", "bootbox.ok": "V redu", "bootbox.cancel": "Prekliči", "bootbox.confirm": "Potrdi", diff --git a/public/language/sl/post-queue.json b/public/language/sl/post-queue.json index bfaa367870..648f761751 100644 --- a/public/language/sl/post-queue.json +++ b/public/language/sl/post-queue.json @@ -1,18 +1,18 @@ { - "post-queue": "Post Queue", + "post-queue": "Čakalna vrsta objav", "description": "There are no posts in the post queue.
To enable this feature, go to Settings → Post → Post Queue and enable Post Queue.", - "user": "User", - "category": "Category", - "title": "Title", - "content": "Content", + "user": "Uporabnik", + "category": "Kategorija", + "title": "Naslov", + "content": "Vsebina", "posted": "Posted", - "reply-to": "Reply to \"%1\"", - "content-editable": "Click on content to edit", - "category-editable": "Click on category to edit", - "title-editable": "Click on title to edit", - "reply": "Reply", - "topic": "Topic", - "accept": "Accept", - "reject": "Reject" + "reply-to": "Odgovor na %1", + "content-editable": "Za urejanje kliknite na vsebino", + "category-editable": "Za urejanje kliknite na kategorijo", + "title-editable": "Za urejanje kliknite na naslov", + "reply": "Odgovori", + "topic": "Tema", + "accept": "Sprejmi", + "reject": "Zavrni" } \ No newline at end of file diff --git a/public/language/sl/top.json b/public/language/sl/top.json new file mode 100644 index 0000000000..b8a05bfa5f --- /dev/null +++ b/public/language/sl/top.json @@ -0,0 +1,4 @@ +{ + "title": "Top", + "no_top_topics": "No top topics" +} \ No newline at end of file diff --git a/public/language/sl/topic.json b/public/language/sl/topic.json index 2987a75dfb..79e903581e 100644 --- a/public/language/sl/topic.json +++ b/public/language/sl/topic.json @@ -1,6 +1,6 @@ { "topic": "Tema", - "title": "Title", + "title": "Naslov", "no_topics_found": "Ni najdenih tem!", "no_posts_found": "Ni najdenih objav!", "post_is_deleted": "Ta objava je izbrisana!", @@ -12,41 +12,42 @@ "notify_me": "Bodi obveščen o novih odgovorih na to temo", "quote": "Citiraj", "reply": "Odgovori", - "replies_to_this_post": "%1 Replies", - "one_reply_to_this_post": "1 Reply", - "last_reply_time": "Last reply", + "replies_to_this_post": "Št. odogvorov: %1", + "one_reply_to_this_post": "1 odgovor", + "last_reply_time": "Zadnji odgovor", "reply-as-topic": "Odgovori s temo", "guest-login-reply": "Prijavi se za odgovor", "login-to-view": "🔒 Log in to view", "edit": "Uredi", "delete": "Izbriši", - "delete-event": "Delete Event", - "delete-event-confirm": "Are you sure you want to delete this event?", + "delete-event": "Izbriši dogodek", + "delete-event-confirm": "Ste prepričani, da želite izbrisati ta dogodek?", "purge": "Očisti", - "restore": "Razveljavi", + "restore": "Obnovi", "move": "Premakni", - "change-owner": "Change Owner", + "change-owner": "Spremeni lastnika", "fork": "Razcepi", "link": "Povezava", "share": "Deli", "tools": "Orodja", "locked": "Zaklenjeno", "pinned": "Pripeto", - "pinned-with-expiry": "Pinned until %1", + "pinned-with-expiry": "Pripeto do %1", "scheduled": "Scheduled", "moved": "Premaknjeno", "moved-from": "Moved from %1", - "copy-ip": "Copy IP", - "ban-ip": "Ban IP", - "view-history": "Edit History", - "locked-by": "Locked by", - "unlocked-by": "Unlocked by", - "pinned-by": "Pinned by", - "unpinned-by": "Unpinned by", - "deleted-by": "Deleted by", - "restored-by": "Restored by", + "copy-ip": "Kopiraj IP", + "ban-ip": "Prepovej IP", + "view-history": "Uredi zgodovino", + "locked-by": "Zaklenil/a", + "unlocked-by": "Odklenil/a", + "pinned-by": "Pripel/a", + "unpinned-by": "Odpel/a", + "deleted-by": "Izbrisal/a", + "restored-by": "Povrnil/a", "moved-from-by": "Moved from %1 by", "queued-by": "Post queued for approval →", + "backlink": "Referenced by", "bookmark_instructions": "Klikni tukaj za vrnitev na zadnje prebrano objavo v tej niti", "flag-post": "Flag this post", "flag-user": "Flag this user", @@ -82,8 +83,8 @@ "thread_tools.move": "Premakni temo", "thread_tools.move-posts": "Premakni objave", "thread_tools.move_all": "Premakni vse", - "thread_tools.change_owner": "Change Owner", - "thread_tools.select_category": "Select Category", + "thread_tools.change_owner": "Spremeni lastnika", + "thread_tools.select_category": "Izberi kategorijo", "thread_tools.fork": "Razcepi temo", "thread_tools.delete": "Izbriši temo", "thread_tools.delete-posts": "Izbriši objave", @@ -92,23 +93,23 @@ "thread_tools.restore_confirm": "Ste prepričani, da želite obnoviti to temo?", "thread_tools.purge": "Očisti temo", "thread_tools.purge_confirm": "Ste prepričani, da želite očistiti to temo?", - "thread_tools.merge_topics": "Merge Topics", - "thread_tools.merge": "Merge", - "topic_move_success": "This topic will be moved to \"%1\" shortly. Click here to undo.", - "topic_move_multiple_success": "These topics will be moved to \"%1\" shortly. Click here to undo.", - "topic_move_all_success": "All topics will be moved to \"%1\" shortly. Click here to undo.", - "topic_move_undone": "Topic move undone", - "topic_move_posts_success": "Posts will be moved shortly. Click here to undo.", - "topic_move_posts_undone": "Post move undone", + "thread_tools.merge_topics": "Združi teme", + "thread_tools.merge": "Združi", + "topic_move_success": "Ta tema bo kmalu premaknjena v \"%1\". Kliknite tukaj, če želite razveljaviti.", + "topic_move_multiple_success": "Te teme bodo kmalu premaknjene v \"%1\". Kliknite tukaj, če želite razveljaviti.", + "topic_move_all_success": "Vse teme bodo kmalu premaknjene v \"%1\". Kliknite tukaj, če želite razveljaviti.", + "topic_move_undone": "Premik teme razveljavljen", + "topic_move_posts_success": "Objave bodo kmalu premaknjene. Kliknite tukaj, če želite razveljaviti.", + "topic_move_posts_undone": "Premik objav razveljavljen", "post_delete_confirm": "Ste prepričani, da želite izbrisati to objavo?", - "post_restore_confirm": "Ste prepričani, da želite razveljaviti to objavo?", + "post_restore_confirm": "Ste prepričani, da želite obnoviti to objavo?", "post_purge_confirm": "Ste prepričani, da želite očistiti to objavo?", - "pin-modal-expiry": "Expiration Date", + "pin-modal-expiry": "Datum poteka veljavnosti", "pin-modal-help": "You can optionally set an expiration date for the pinned topic(s) here. Alternatively, you can leave this field blank to have the topic stay pinned until it is manually unpinned.", "load_categories": "Nalagam kategorije", "confirm_move": "Premakni", "confirm_fork": "Razcepi", - "bookmark": "Bookmark", + "bookmark": "Zaznamek", "bookmarks": "Zaznamki", "bookmarks.has_no_bookmarks": "Zaznamovali še niste nobenih objav.", "loading_more_posts": "Nalagam več objav", @@ -117,31 +118,32 @@ "move_post": "Premakni objavo", "post_moved": "Objava premaknjena!", "fork_topic": "Razcepi temo", - "enter-new-topic-title": "Enter new topic title", + "enter-new-topic-title": "Vnesite nov naslov teme", "fork_topic_instruction": "Klikni na objavo, ki o želiš odcepiti", "fork_no_pids": "Ni izbranih objav!", - "no-posts-selected": "No posts selected!", - "x-posts-selected": "%1 post(s) selected", + "no-posts-selected": "Ni izbranih objav!", + "x-posts-selected": "Izbranih objav: %1 ", "x-posts-will-be-moved-to-y": "%1 post(s) will be moved to \"%2\"", "fork_pid_count": "Izbranih objav: %1 ", "fork_success": "Uspešno ste razcepili temo! Klikni tu za ogled te teme.", "delete_posts_instruction": "Kliknite na teme, ki jih želite izbrisati/očistiti ", - "merge_topics_instruction": "Click the topics you want to merge or search for them", - "merge-topic-list-title": "List of topics to be merged", - "merge-options": "Merge options", - "merge-select-main-topic": "Select the main topic", - "merge-new-title-for-topic": "New title for topic", - "topic-id": "Topic ID", - "move_posts_instruction": "Click the posts you want to move then enter a topic ID or go to the target topic", - "change_owner_instruction": "Click the posts you want to assign to another user", + "merge_topics_instruction": "Kliknite teme, ki jih želite združiti, ali jih poiščite", + "merge-topic-list-title": "Seznam tem za združevanje", + "merge-options": "Možnosti združevanja", + "merge-select-main-topic": "Izberi glavno temo", + "merge-new-title-for-topic": "Nov naslov teme", + "topic-id": "ID teme", + "move_posts_instruction": "Kliknite objave, ki jih želite premakniti, nato vnesite ID teme ali pojdite na ciljno temo", + "change_owner_instruction": "Kliknite objave, ki jih želite dodeliti drugemu uporabniku", "composer.title_placeholder": "Vpiši naslov teme...", "composer.handle_placeholder": "Enter your name/handle here", "composer.discard": "Zavrzi", "composer.submit": "Pošlji", + "composer.additional-options": "Additional Options", "composer.schedule": "Schedule", "composer.replying_to": "Odgovor na %1", "composer.new_topic": "Nova tema", - "composer.editing": "Editing", + "composer.editing": "Urejanje", "composer.uploading": "nalagam...", "composer.thumb_url_label": "Prilepite URL sličice teme", "composer.thumb_title": "Dodajte sličico tej temi", @@ -158,24 +160,25 @@ "newest_to_oldest": "Od novejšega do starejšega", "most_votes": "Največ glasov", "most_posts": "Največ objav", + "most_views": "Največ ogledov", "stale.title": "Raje ustvari novo temo?", "stale.warning": "Tema na katero odgovarjaš je precej stara. A ne bi raje ustvaril novo temo namesto te, z sklicem na to v tvojem odgovoru?", "stale.create": "Ustvari novo temo", "stale.reply_anyway": "Vseeno odgovori na to temo", "link_back": "Odg: [%1](%2)", - "diffs.title": "Post Edit History", + "diffs.title": "Zgodovina urejanja objav", "diffs.description": "This post has %1 revisions. Click one of the revisions below to see the post content at that point in time.", "diffs.no-revisions-description": "This post has %1 revisions.", - "diffs.current-revision": "current revision", - "diffs.original-revision": "original revision", - "diffs.restore": "Restore this revision", - "diffs.restore-description": "A new revision will be appended to this post's edit history after restoring.", - "diffs.post-restored": "Post successfully restored to earlier revision", - "diffs.delete": "Delete this revision", - "diffs.deleted": "Revision deleted", + "diffs.current-revision": "trenutna različica", + "diffs.original-revision": "izvirna različica", + "diffs.restore": "Obnovi to različico", + "diffs.restore-description": "Po obnovitvi bo v zgodovino urejanj te objave dodana nova različica.", + "diffs.post-restored": "Objava je bila uspešno obnovljena na prejšnjo različico", + "diffs.delete": "Izbriši to različico", + "diffs.deleted": "Različica izbrisana", "timeago_later": "%1 later", "timeago_earlier": "%1 earlier", - "first-post": "First post", - "last-post": "Last post", - "post-quick-reply": "Post quick reply" + "first-post": "Prva objava", + "last-post": "Zadnja obava", + "post-quick-reply": "Objavi hitri odgovor" } \ No newline at end of file diff --git a/public/language/sl/user.json b/public/language/sl/user.json index 23dbb21939..1b3a56a24c 100644 --- a/public/language/sl/user.json +++ b/public/language/sl/user.json @@ -1,27 +1,27 @@ { "banned": "Izločen", "offline": "Odjavljeni", - "deleted": "Deleted", + "deleted": "Izbrisano", "username": "Uporabniško ime", "joindate": "Datum pridružitve", "postcount": "Število objav", "email": "E-pošta", "confirm_email": "Potrdi e-poštni naslov", "account_info": "Podatki računa", - "admin_actions_label": "Administrative Actions", + "admin_actions_label": "Skrbniška dejanja", "ban_account": "Izločen račun", "ban_account_confirm": "Ali želiš izločiti uporabnika?", "unban_account": "Ponovno vključi račun", "delete_account": "Izbriši račun", - "delete_account_as_admin": "Delete Account", - "delete_content": "Delete Account Content", - "delete_all": "Delete Account and Content", - "delete_account_confirm": "Are you sure you want to anonymize your posts and delete your account?
This action is irreversible and you will not be able to recover any of your data

Enter your password to confirm that you wish to destroy this account.", - "delete_this_account_confirm": "Are you sure you want to delete this account while leaving its contents behind?
This action is irreversible, posts will be anonymized, and you will not be able to restore post associations with the deleted account

", - "delete_account_content_confirm": "Are you sure you want to delete this account's content (posts/topics/uploads)?
This action is irreversible and you will not be able to recover any data

", - "delete_all_confirm": "Are you sure you want to delete this account and all of its content (posts/topics/uploads)?
This action is irreversible and you will not be able to recover any data

", + "delete_account_as_admin": "Izbriši račun", + "delete_content": "Izbriši vsebino računa", + "delete_all": "Izbriši račun in vsebino", + "delete_account_confirm": "Ste prepričani, da želite anonimizirati vašo objavo in izbrisati vaš račun?
To dejanje je nepovratno in podatkov ne boste mogli obnoviti.

Vnesite svoje geslo za potrditev, da želite uničiti ta račun.", + "delete_this_account_confirm": "Ali ste prepričani, da želite izbrisati ta račun, pri tem pa pustiti vsebino za seboj?
To dejanje je nepovratno, objave bodo anonimizirane in povezav objav z izbrisanim računom ne boste mogli obnoviti.

", + "delete_account_content_confirm": "Ali ste prepričani, da želite izbrisati vsebino tega računa (objave/teme/nalaganja)?
To dejanje je nepovratno in podatkov ne boste mogli obnoviti

", + "delete_all_confirm": "Ali ste prepričani, da želite izbrisati ta račun in vso njegovo vsebino (objave/teme/nalaganja)?
To dejanje je nepovratno in podatkov ne boste mogli obnoviti

", "account-deleted": "Račun je izbrisan", - "account-content-deleted": "Account content deleted", + "account-content-deleted": "Vsebina računa je izbrisana", "fullname": "Ime in priimek", "website": "Spletna stran", "location": "Lokacija", @@ -33,23 +33,23 @@ "reputation": "Naziv", "bookmarks": "Zaznamki", "watched_categories": "Spremljane kategorije", - "change_all": "Change All", + "change_all": "Spremeni vse", "watched": "Spremljano", - "ignored": "Ignored", - "default-category-watch-state": "Default category watch state", + "ignored": "Prezrto", + "default-category-watch-state": "Privzeto stanje spremljanja kategorij", "followers": "Spremljevalci", "following": "Spremljano", "blocks": "Blocks", "block_toggle": "Toggle Block", - "block_user": "Block User", - "unblock_user": "Unblock User", + "block_user": "Blokiraj uporabnika", + "unblock_user": "Odblokiraj uporabnika", "aboutme": "O meni", "signature": "Podpis", "birthday": "Rojstni datum", "chat": "Klepet", - "chat_with": "Continue chat with %1", - "new_chat_with": "Start new chat with %1", - "flag-profile": "Flag Profile", + "chat_with": "Nadaljuj klepet z %1", + "new_chat_with": "Prični nov klepet z %1", + "flag-profile": "Označi profil z zastavico", "follow": "Spremljaj", "unfollow": "Ne spremljaj", "more": "Več", @@ -57,7 +57,7 @@ "change_picture": "Spremeni sliko", "change_username": "Spremeni uporabniško ime", "change_email": "Spremeni e-poštni naslov", - "email_same_as_password": "Please enter your current password to continue – you've entered your new email again", + "email_same_as_password": "Za nadaljevanje vnesite svoje trenutno geslo & ndash; ponovno ste vnesli svoj novi e-poštni naslov", "edit": "Uredi", "edit-profile": "Uredi profil", "default_picture": "Privzeta ikona", @@ -76,15 +76,15 @@ "username_taken_workaround": "Predlagano uporabniško ime je že zasedeno, zato smo ga rahlo spremenili. Sedaj vas poznamo kot %1", "password_same_as_username": "Vaše geslo je enako kot vaše uporabniško ime, prosim izberite drugačno geslo.", "password_same_as_email": "Vaše geslo je enako kot vaše e-poštni naslov, prosim izberite drugačno geslo.", - "weak_password": "Weak password.", + "weak_password": "Šibko geslo.", "upload_picture": "Naloži fotografijo", "upload_a_picture": "Naloži fotografijo", "remove_uploaded_picture": "Odstrani preneseno sliko ", "upload_cover_picture": "Prenesi fotografijo naslovnice", - "remove_cover_picture_confirm": "Are you sure you want to remove the cover picture?", - "crop_picture": "Crop picture", - "upload_cropped_picture": "Crop and upload", - "avatar-background-colour": "Avatar background colour", + "remove_cover_picture_confirm": "Ste prepričani, da želite odstraniti naslovno sliko?", + "crop_picture": "Obreži sliko", + "upload_cropped_picture": "Obreži in naloži", + "avatar-background-colour": "Slika ozadja avatarja", "settings": "Nastavitve", "show_email": "Pokaži moj e-poštni naslov.", "show_fullname": "Pokaži moj ime in priimek.", @@ -94,53 +94,54 @@ "digest_off": "Izključi", "digest_daily": "Dnevno", "digest_weekly": "Tedensko", + "digest_biweekly": "Bi-Weekly", "digest_monthly": "Mesečno", "has_no_follower": "Uporabniku nihče ne sledi :(", "follows_no_one": "Uporabnik nikomur ne sledi :(", "has_no_posts": "Uporabnik še ni nič objavil.", - "has_no_best_posts": "This user does not have any upvoted posts yet.", + "has_no_best_posts": "Ta uporabnik še nima nobenih objav z glasovi za.", "has_no_topics": "Uporabnik še ni objavil nobene teme.", "has_no_watched_topics": "Uporabnik še ne spremlja nobene teme.", - "has_no_ignored_topics": "This user hasn't ignored any topics yet.", + "has_no_ignored_topics": "Ta uporabnik še nima nobenih prezrtih tem.", "has_no_upvoted_posts": "Uporabnik še ni glasoval za nobeno objavo.", "has_no_downvoted_posts": "Uporabnik še ni glasoval proti nobeni objavi.", "has_no_voted_posts": "Uporabnik nima glasovanih objav", - "has_no_blocks": "You have blocked no users.", + "has_no_blocks": "Nimate blokiranih uporabnikov.", "email_hidden": "Skrit e-poštni naslov", "hidden": "skrit", "paginate_description": "Uporabi oštevilčenje strani namesto neskončnega drsenja", "topics_per_page": "Število tem na stran", "posts_per_page": "Število objav na stran", - "max_items_per_page": "Maximum %1", - "acp_language": "Admin Page Language", - "notifications": "Notifications", - "upvote-notif-freq": "Upvote Notification Frequency", - "upvote-notif-freq.all": "All Upvotes", - "upvote-notif-freq.first": "First Per Post", - "upvote-notif-freq.everyTen": "Every Ten Upvotes", + "max_items_per_page": "Največ %1", + "acp_language": "Jezik skrbniških strani", + "notifications": "Obvestila", + "upvote-notif-freq": "Pogostost obveščanja o glasovih za", + "upvote-notif-freq.all": "Vsi glasovi za", + "upvote-notif-freq.first": "Prvi na objavo", + "upvote-notif-freq.everyTen": "Vsakih 10 glasov za", "upvote-notif-freq.threshold": "On 1, 5, 10, 25, 50, 100, 150, 200...", "upvote-notif-freq.logarithmic": "On 10, 100, 1000...", - "upvote-notif-freq.disabled": "Disabled", + "upvote-notif-freq.disabled": "Onemogočeno", "browsing": "Preglej nastavitve", "open_links_in_new_tab": "Zunanje povezave odpri v novem zavihku", "enable_topic_searching": "Omogoči iskanje znotraj teme", "topic_search_help": "Če omogočite, bo iskanje prepisalo brskalnikove prevzete nastavitve in vam omogočilo iskanje skozi celotno temo.", "update_url_with_post_index": "Update url with post index while browsing topics", - "scroll_to_my_post": "After posting a reply, show the new post", + "scroll_to_my_post": "Po objavi odgovora prikaži novo objavo", "follow_topics_you_reply_to": "Spremljanj teme, na katere si odgovoril", "follow_topics_you_create": "Spremljanj teme, ki si jih ustvaril", "grouptitle": "Naslov skupine", - "group-order-help": "Select a group and use the arrows to order titles", + "group-order-help": "Izberi skupino in uporabi puščice za razvrstitev naslovov", "no-group-title": "Skupina nima imena", "select-skin": "Izberi preobleko", "select-homepage": "Izberi domačo stran", "homepage": "Domača stran", - "homepage_description": "Select a page to use as the forum homepage or 'None' to use the default homepage.", - "custom_route": "Custom Homepage Route", + "homepage_description": "Izberite stran, ki jo želite uporabiti kot domačo stran foruma, ali 'Brez', če želite uporabiti privzeto domačo stran.", + "custom_route": "Po do domače strani po meri", "custom_route_help": "Enter a route name here, without any preceding slash (e.g. \"recent\" or \"category/2/general-discussion\")", "sso.title": "Storitev enotne prijave ", "sso.associated": "Povezan z", - "sso.not-associated": "Click here to associate with", + "sso.not-associated": "Kliknite tu da povežete z", "sso.dissociate": "Dissociate", "sso.dissociate-confirm-title": "Confirm Dissociation", "sso.dissociate-confirm": "Are you sure you wish to dissociate your account from %1?", @@ -151,16 +152,16 @@ "info.banned-until": "Banned until %1", "info.banned-expiry": "Expiry", "info.banned-permanently": "Banned permanently", - "info.banned-reason-label": "Reason", - "info.banned-no-reason": "No reason given.", - "info.username-history": "Username History", - "info.email-history": "Email History", + "info.banned-reason-label": "Razlog", + "info.banned-no-reason": "Razlog ni podan.", + "info.username-history": "Zgodovina uporabniškega imena", + "info.email-history": "Zgodovina e-poštnega naslova", "info.moderation-note": "Moderation Note", "info.moderation-note.success": "Moderation note saved", - "info.moderation-note.add": "Add note", + "info.moderation-note.add": "Dodaj opombo", "sessions.description": "This page allows you to view any active sessions on this forum and revoke them if necessary. You can revoke your own session by logging out of your account.", "consent.title": "Your Rights & Consent", - "consent.lead": "This community forum collects and processes your personal information.", + "consent.lead": "Ta forum skupnosti zbira in obdeluje vaše osebne podatke.", "consent.intro": "We use this information strictly to personalise your experience in this community, as well as to associate the posts you make to your user account. During the registration step you were asked to provide a username and email address, you can also optionally provide additional information to complete your user profile on this website.

We retain this information for the life of your user account, and you are able to withdraw consent at any time by deleting your account. At any time you may request a copy of your contribution to this website, via your Rights & Consent page.

If you have any questions or concerns, we encourage you to reach out to this forum's administrative team.", "consent.email_intro": "Occasionally, we may send emails to your registered email address in order to provide updates and/or to notify you of new activity that is pertinent to you. You can customise the frequency of the community digest (including disabling it outright), as well as select which types of notifications to receive via email, via your user settings page.", "consent.digest_frequency": "Unless explicitly changed in your user settings, this community delivers email digests every %1.", @@ -170,17 +171,17 @@ "consent.give": "Give consent", "consent.right_of_access": "You have the Right of Access", "consent.right_of_access_description": "You have the right to access any data collected by this website upon request. You can retrieve a copy of this data by clicking the appropriate button below.", - "consent.right_to_rectification": "You have the Right to Rectification", + "consent.right_to_rectification": "Imate pravico do popravka.", "consent.right_to_rectification_description": "You have the right to change or update any inaccurate data provided to us. Your profile can be updated by editing your profile, and post content can always be edited. If this is not the case, please contact this site's administrative team.", - "consent.right_to_erasure": "You have the Right to Erasure", + "consent.right_to_erasure": "Imate pravico do izbrisa.", "consent.right_to_erasure_description": "At any time, you are able to revoke your consent to data collection and/or processing by deleting your account. Your individual profile can be deleted, although your posted content will remain. If you wish to delete both your account and your content, please contact the administrative team for this website.", - "consent.right_to_data_portability": "You have the Right to Data Portability", + "consent.right_to_data_portability": "Imate pravico do prenosa podatkov", "consent.right_to_data_portability_description": "You may request from us a machine-readable export of any collected data about you and your account. You can do so by clicking the appropriate button below.", - "consent.export_profile": "Export Profile (.json)", + "consent.export_profile": "Izvozi profil (.json)", "consent.export-profile-success": "Exporting profile, you will get a notification when it is complete.", - "consent.export_uploads": "Export Uploaded Content (.zip)", + "consent.export_uploads": "Izvozi naloženo vsebino (.zip)", "consent.export-uploads-success": "Exporting uploads, you will get a notification when it is complete.", - "consent.export_posts": "Export Posts (.csv)", + "consent.export_posts": "Izvozi objave (.csv)", "consent.export-posts-success": "Exporting posts, you will get a notification when it is complete.", "emailUpdate.intro": "Please enter your email address below. This forum uses your email address for scheduled digest and notifications, as well as for account recovery in the event of a lost password.", "emailUpdate.optional": "This field is optional. You are not obligated to provide your email address, but without a validated email, you will not be able to recover your account.", diff --git a/public/language/sr/admin/advanced/events.json b/public/language/sr/admin/advanced/events.json index 56d9457971..b2c2033fb5 100644 --- a/public/language/sr/admin/advanced/events.json +++ b/public/language/sr/admin/advanced/events.json @@ -3,6 +3,7 @@ "no-events": "There are no events", "control-panel": "Events Control Panel", "delete-events": "Delete Events", + "confirm-delete-all-events": "Are you sure you want to delete all logged events?", "filters": "Filters", "filters-apply": "Apply Filters", "filter-type": "Event Type", diff --git a/public/language/sr/admin/dashboard.json b/public/language/sr/admin/dashboard.json index 0de31d4917..4d39626882 100644 --- a/public/language/sr/admin/dashboard.json +++ b/public/language/sr/admin/dashboard.json @@ -56,8 +56,8 @@ "active-users.total": "Total", "active-users.connections": "Connections", - "anonymous-registered-users": "Anonymous vs Registered Users", - "anonymous": "Anonymous", + "guest-registered-users": "Guest vs Registered Users", + "guest": "Guest", "registered": "Registered", "user-presence": "User Presence", @@ -68,6 +68,7 @@ "unread": "Unread", "high-presence-topics": "High Presence Topics", + "popular-searches": "Popular Searches", "graphs.page-views": "Page Views", "graphs.page-views-registered": "Page Views Registered", @@ -75,13 +76,14 @@ "graphs.page-views-bot": "Page Views Bot", "graphs.unique-visitors": "Unique Visitors", "graphs.registered-users": "Registered Users", - "graphs.anonymous-users": "Anonymous Users", + "graphs.guest-users": "Guest Users", "last-restarted-by": "Last restarted by", "no-users-browsing": "No users browsing", "back-to-dashboard": "Back to Dashboard", "details.no-users": "No users have joined within the selected timeframe", "details.no-topics": "No topics have been posted within the selected timeframe", + "details.no-searches": "No searches have been made yet", "details.no-logins": "No logins have been recorded within the selected timeframe", "details.logins-static": "NodeBB only saves session data for %1 days, and so this table below will only show the most recently active sessions", "details.logins-login-time": "Login Time" diff --git a/public/language/sr/admin/development/info.json b/public/language/sr/admin/development/info.json index 1003af1a5f..11202d9c3a 100644 --- a/public/language/sr/admin/development/info.json +++ b/public/language/sr/admin/development/info.json @@ -8,7 +8,11 @@ "nodejs": "nodejs", "online": "online", "git": "git", - "memory": "memory", + "process-memory": "process memory", + "system-memory": "system memory", + "used-memory-process": "Used memory by process", + "used-memory-os": "Used system memory", + "total-memory-os": "Total system memory", "load": "system load", "cpu-usage": "cpu usage", "uptime": "uptime", diff --git a/public/language/sr/admin/extend/rewards.json b/public/language/sr/admin/extend/rewards.json index 5383a90b33..df89d441a7 100644 --- a/public/language/sr/admin/extend/rewards.json +++ b/public/language/sr/admin/extend/rewards.json @@ -8,8 +8,6 @@ "delete": "Delete", "enable": "Enable", "disable": "Disable", - "control-panel": "Rewards Control", - "new-reward": "New Reward", "alert.delete-success": "Successfully deleted reward", "alert.no-inputs-found": "Illegal reward - no inputs found!", diff --git a/public/language/sr/admin/general/dashboard.json b/public/language/sr/admin/general/dashboard.json deleted file mode 100644 index 37ae537786..0000000000 --- a/public/language/sr/admin/general/dashboard.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "forum-traffic": "Forum Traffic", - "page-views": "Page Views", - "unique-visitors": "Unique Visitors", - "new-users": "New Users", - "posts": "Posts", - "topics": "Topics", - "page-views-seven": "Last 7 Days", - "page-views-thirty": "Last 30 Days", - "page-views-last-day": "Last 24 hours", - "page-views-custom": "Custom Date Range", - "page-views-custom-start": "Range Start", - "page-views-custom-end": "Range End", - "page-views-custom-help": "Enter a date range of page views you would like to view. If no date picker is available, the accepted format is YYYY-MM-DD", - "page-views-custom-error": "Please enter a valid date range in the format YYYY-MM-DD", - - "stats.yesterday": "Yesterday", - "stats.today": "Today", - "stats.last-week": "Last Week", - "stats.this-week": "This Week", - "stats.last-month": "Last Month", - "stats.this-month": "This Month", - "stats.all": "All Time", - - "updates": "Updates", - "running-version": "You are running NodeBB v%1.", - "keep-updated": "Always make sure that your NodeBB is up to date for the latest security patches and bug fixes.", - "up-to-date": "

You are up-to-date

", - "upgrade-available": "

A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-upgrade-available": "

This is an outdated pre-release version of NodeBB. A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-warning": "

This is a pre-release version of NodeBB. Unintended bugs may occur.

", - "running-in-development": "Forum is running in development mode. The forum may be open to potential vulnerabilities; please contact your system administrator.", - "latest-lookup-failed": "

Failed to look up latest available version of NodeBB

", - - "notices": "Notices", - "restart-not-required": "Restart not required", - "restart-required": "Restart required", - "search-plugin-installed": "Search Plugin installed", - "search-plugin-not-installed": "Search Plugin not installed", - "search-plugin-tooltip": "Install a search plugin from the plugin page in order to activate search functionality", - - "control-panel": "System Control", - "rebuild-and-restart": "Rebuild & Restart", - "restart": "Restart", - "restart-warning": "Rebuilding or Restarting your NodeBB will drop all existing connections for a few seconds.", - "restart-disabled": "Rebuilding and Restarting your NodeBB has been disabled as you do not seem to be running it via the appropriate daemon.", - "maintenance-mode": "Maintenance Mode", - "maintenance-mode-title": "Click here to set up maintenance mode for NodeBB", - "realtime-chart-updates": "Realtime Chart Updates", - - "active-users": "Active Users", - "active-users.users": "Users", - "active-users.guests": "Guests", - "active-users.total": "Total", - "active-users.connections": "Connections", - - "anonymous-registered-users": "Anonymous vs Registered Users", - "anonymous": "Anonymous", - "registered": "Registered", - - "user-presence": "User Presence", - "on-categories": "On categories list", - "reading-posts": "Reading posts", - "browsing-topics": "Browsing topics", - "recent": "Recent", - "unread": "Unread", - - "high-presence-topics": "High Presence Topics", - - "graphs.page-views": "Page Views", - "graphs.page-views-registered": "Page Views Registered", - "graphs.page-views-guest": "Page Views Guest", - "graphs.page-views-bot": "Page Views Bot", - "graphs.unique-visitors": "Unique Visitors", - "graphs.registered-users": "Registered Users", - "graphs.anonymous-users": "Anonymous Users", - "last-restarted-by": "Last restarted by", - "no-users-browsing": "No users browsing" -} diff --git a/public/language/sr/admin/general/homepage.json b/public/language/sr/admin/general/homepage.json deleted file mode 100644 index 7428d59eeb..0000000000 --- a/public/language/sr/admin/general/homepage.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "home-page": "Home Page", - "description": "Choose what page is shown when users navigate to the root URL of your forum.", - "home-page-route": "Home Page Route", - "custom-route": "Custom Route", - "allow-user-home-pages": "Allow User Home Pages", - "home-page-title": "Title of the home page (default \"Home\")" -} \ No newline at end of file diff --git a/public/language/sr/admin/general/languages.json b/public/language/sr/admin/general/languages.json deleted file mode 100644 index bdd57849b3..0000000000 --- a/public/language/sr/admin/general/languages.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "language-settings": "Language Settings", - "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language", - "auto-detect": "Auto Detect Language Setting for Guests" -} \ No newline at end of file diff --git a/public/language/sr/admin/general/navigation.json b/public/language/sr/admin/general/navigation.json deleted file mode 100644 index 13dd01aae7..0000000000 --- a/public/language/sr/admin/general/navigation.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "icon": "Icon:", - "change-icon": "change", - "route": "Route:", - "tooltip": "Tooltip:", - "text": "Text:", - "text-class": "Text Class: optional", - "class": "Class: optional", - "id": "ID: optional", - - "properties": "Properties:", - "groups": "Groups:", - "open-new-window": "Open in a new window", - - "btn.delete": "Delete", - "btn.disable": "Disable", - "btn.enable": "Enable", - - "available-menu-items": "Available Menu Items", - "custom-route": "Custom Route", - "core": "core", - "plugin": "plugin" -} \ No newline at end of file diff --git a/public/language/sr/admin/general/social.json b/public/language/sr/admin/general/social.json deleted file mode 100644 index 23aedfcfaa..0000000000 --- a/public/language/sr/admin/general/social.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "post-sharing": "Post Sharing", - "info-plugins-additional": "Plugins can add additional networks for sharing posts.", - "save-success": "Successfully saved Post Sharing Networks!" -} \ No newline at end of file diff --git a/public/language/sr/admin/general/sounds.json b/public/language/sr/admin/general/sounds.json deleted file mode 100644 index 95ccbde0f1..0000000000 --- a/public/language/sr/admin/general/sounds.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "notifications": "Notifications", - "chat-messages": "Chat Messages", - "play-sound": "Play", - "incoming-message": "Incoming Message", - "outgoing-message": "Outgoing Message", - "upload-new-sound": "Upload New Sound", - "saved": "Settings Saved" -} \ No newline at end of file diff --git a/public/language/sr/admin/manage/digest.json b/public/language/sr/admin/manage/digest.json index 8f3661698a..38c634d1f6 100644 --- a/public/language/sr/admin/manage/digest.json +++ b/public/language/sr/admin/manage/digest.json @@ -13,6 +13,7 @@ "resent-single": "Manual digest resend completed", "resent-day": "Daily digest resent", "resent-week": "Weekly digest resent", + "resent-biweek": "Bi-Weekly digest resent", "resent-month": "Monthly digest resent", "null": "Never", "manual-run": "Manual digest run:", diff --git a/public/language/sr/admin/manage/users.json b/public/language/sr/admin/manage/users.json index 38a065b954..2a3c0c4829 100644 --- a/public/language/sr/admin/manage/users.json +++ b/public/language/sr/admin/manage/users.json @@ -47,6 +47,7 @@ "users.uid": "uid", "users.username": "username", "users.email": "email", + "users.no-email": "(no email)", "users.ip": "IP", "users.postcount": "postcount", "users.reputation": "reputation", diff --git a/public/language/sr/admin/menu.json b/public/language/sr/admin/menu.json index 9294dcad89..1b369a0fcd 100644 --- a/public/language/sr/admin/menu.json +++ b/public/language/sr/admin/menu.json @@ -4,6 +4,7 @@ "dashboard/logins": "Logins", "dashboard/users": "Users", "dashboard/topics": "Topics", + "dashboard/searches": "Searches", "section-general": "Uopšteno", "section-manage": "Menadžment", diff --git a/public/language/sr/admin/settings/email.json b/public/language/sr/admin/settings/email.json index 4b69f6e351..f0a4584fb4 100644 --- a/public/language/sr/admin/settings/email.json +++ b/public/language/sr/admin/settings/email.json @@ -6,7 +6,7 @@ "from-help": "The from name to display in the email.", "smtp-transport": "SMTP Transport", - "smtp-transport.enabled": "Use an external email server to send emails", + "smtp-transport.enabled": "Enable SMTP Transport", "smtp-transport-help": "You can select from a list of well-known services or enter a custom one.", "smtp-transport.service": "Select a service", "smtp-transport.service-custom": "Custom Service", @@ -37,6 +37,10 @@ "subscriptions.hour": "Digest Hour", "subscriptions.hour-help": "Molim unesite broj koji označava satnicu kada da pošalje zakazani sažeti email (nrp. 0 za ponoć, 17 za 5:00 pm). Uzmite u obzir da će se slanje događati po satnici samog servara, i da vrlo verovatno se ne poklapa sa satnicom vašeg sistema.
Trenutno vreme servera je:
Sledeći dnevni sažeti email zakazan je za slanje u ", "notifications.remove-images": "Remove images from email notifications", + "require-email-address": "Require new users to specify an email address", + "require-email-address-warning": "By default, users can opt-out of entering an email address. Enabling this option means they have to enter an email address in order to proceed with registration. It does not ensure user will enter a real email address, nor even an address they own.", "include-unverified-emails": "Send emails to recipients who have not explicitly confirmed their emails", - "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws." -} \ No newline at end of file + "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws.", + "prompt": "Prompt users to enter or confirm their emails", + "prompt-help": "If a user does not have an email set, or their email is not confirmed, a warning will be shown on screen." +} diff --git a/public/language/sr/admin/settings/general.json b/public/language/sr/admin/settings/general.json index be7df90870..29b939861b 100644 --- a/public/language/sr/admin/settings/general.json +++ b/public/language/sr/admin/settings/general.json @@ -3,9 +3,9 @@ "title": "Site Title", "title.short": "Short Title", "title.short-placeholder": "If no short title is specified, the site title will be used", - "title.url": "URL", + "title.url": "Title Link URL", "title.url-placeholder": "The URL of the site title", - "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.", + "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "title.name": "Your Community Name", "title.show-in-header": "Show Site Title in Header", "browser-title": "Browser Title", @@ -20,9 +20,9 @@ "logo.image": "Image", "logo.image-placeholder": "Path to a logo to display on forum header", "logo.upload": "Upload", - "logo.url": "URL", + "logo.url": "Logo Link URL", "logo.url-placeholder": "The URL of the site logo", - "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.", + "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "logo.alt-text": "Alt Text", "log.alt-text-placeholder": "Alternative text for accessibility", "favicon": "Favicon", @@ -47,4 +47,4 @@ "undo-timeout": "Undo Timeout", "undo-timeout-help": "Some operations such as moving topics will allow for the moderator to undo their action within a certain timeframe. Set to 0 to disable undo completely.", "topic-tools": "Topic Tools" -} \ No newline at end of file +} diff --git a/public/language/sr/admin/settings/navigation.json b/public/language/sr/admin/settings/navigation.json index 13dd01aae7..7baca85096 100644 --- a/public/language/sr/admin/settings/navigation.json +++ b/public/language/sr/admin/settings/navigation.json @@ -11,6 +11,8 @@ "properties": "Properties:", "groups": "Groups:", "open-new-window": "Open in a new window", + "dropdown": "Dropdown", + "dropdown-placeholder": "Place your dropdown menu items below, ie:
<li><a href="https://myforum.com">Link 1</a></li>", "btn.delete": "Delete", "btn.disable": "Disable", @@ -20,4 +22,4 @@ "custom-route": "Custom Route", "core": "core", "plugin": "plugin" -} \ No newline at end of file +} diff --git a/public/language/sr/admin/settings/notifications.json b/public/language/sr/admin/settings/notifications.json index da6c9680a3..c6d8b928ce 100644 --- a/public/language/sr/admin/settings/notifications.json +++ b/public/language/sr/admin/settings/notifications.json @@ -2,5 +2,6 @@ "notifications": "Notifications", "welcome-notification": "Welcome Notification", "welcome-notification-link": "Welcome Notification Link", - "welcome-notification-uid": "Welcome Notification User (UID)" + "welcome-notification-uid": "Welcome Notification User (UID)", + "post-queue-notification-uid": "Post Queue User (UID)" } \ No newline at end of file diff --git a/public/language/sr/admin/settings/post.json b/public/language/sr/admin/settings/post.json index f3ae9fccb5..beba2775b7 100644 --- a/public/language/sr/admin/settings/post.json +++ b/public/language/sr/admin/settings/post.json @@ -40,6 +40,7 @@ "teaser.last-post": "Poslednji &ndashč Pokazuje poslednji post, uključujući originalni post, ako nema odgovora", "teaser.last-reply": "Poslednji &ndashč Pokaži najnoviji odgovor, ili ako \"Nema odgovora\" placeholder ako nema odgovora", "teaser.first": "Prvi", + "showPostPreviewsOnHover": "Show a preview of posts when mouse overed", "unread": "Nepročitana podešavanja", "unread.cutoff": "Nepročitano tokom prekinutih dana", "unread.min-track-last": "Minimum postova u temi, pre praćenja poslednjeg pročitanog", @@ -56,6 +57,9 @@ "composer.show-help": "Prikaži tab \"Pomoć\"", "composer.enable-plugin-help": "Dozvoli plugin-ovima da dodaju sadržaj na tab-u \"pomoć\"", "composer.custom-help": "Prilagođen tekst za pomoć", + "backlinks": "Backlinks", + "backlinks.enabled": "Enable topic backlinks", + "backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.", "ip-tracking": "Praćenje IP adrese", "ip-tracking.each-post": "Prati IP Adresu za svaki post", "enable-post-history": "Enable Post History" diff --git a/public/language/sr/admin/settings/user.json b/public/language/sr/admin/settings/user.json index d4d6e440cc..a0327e470c 100644 --- a/public/language/sr/admin/settings/user.json +++ b/public/language/sr/admin/settings/user.json @@ -71,6 +71,7 @@ "digest-freq.off": "Isključeno", "digest-freq.daily": "Dnevno", "digest-freq.weekly": "Nedeljno", + "digest-freq.biweekly": "Bi-Weekly", "digest-freq.monthly": "Mesečno", "email-chat-notifs": "Pošalji email ako nova chat poruka stigne dok nisam online", "email-post-notif": "Pošalji email kada odgovori su načinjeni u temu u kojoj sam ja pretplaćen", diff --git a/public/language/sr/error.json b/public/language/sr/error.json index b07e597d71..d5a1e83818 100644 --- a/public/language/sr/error.json +++ b/public/language/sr/error.json @@ -34,8 +34,9 @@ "email-invited": "Е-пошта је већ позвана", "email-not-confirmed": "Posting in some categories or topics is enabled once your email is confirmed, please click here to send a confirmation email.", "email-not-confirmed-chat": "Није вам дозвољено да ћаскате док не потврдите вашу е-пошту, кликните овде да то учините.", - "email-not-confirmed-email-sent": "Ваша е-пошта још увек није потврђена, проверите ваше пријемно сандуче. Неће вам бити дозвољено да шаљете поруке или ћаскате док не потврдите е-пошту.", - "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery. Please click here to enter an email.", + "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You may not be able to post in some categories or chat until your email is confirmed.", + "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery, and may be necessary for chatting and posting in some categories. Please click here to enter an email.", + "user-doesnt-have-email": "User \"%1\" does not have an email set.", "email-confirm-failed": "Потврда е-поште није успела, молимо вас да покушате касније.", "confirm-email-already-sent": "Е-порука за потврду је већ послата, молимо вас да сачекате %1 минут(а) да бисте послали други.", "sendmail-not-found": "Програм за слање поште није пронађен, проверите да ли је инсталиран и покренут од стране корисника NodeBB.", @@ -103,6 +104,7 @@ "already-bookmarked": "Већ сте додали ову поруку у обележиваче", "already-unbookmarked": "Већ сте одстранили ову поруку из обележивача", "cant-ban-other-admins": "Не можете забранити друге администраторе!", + "cant-make-banned-users-admin": "You can't make banned users admin.", "cant-remove-last-admin": "Ви сте једини администратор. Додајте другог корисника као администратора пре него што уклоните себе као администратора.", "account-deletion-disabled": "Брисање налога је онемогућено", "cant-delete-admin": "Уклоните администраторске привилегије овом налогу пре него што покушате да га избришете.", diff --git a/public/language/sr/modules.json b/public/language/sr/modules.json index 436407a0db..2478273950 100644 --- a/public/language/sr/modules.json +++ b/public/language/sr/modules.json @@ -54,7 +54,7 @@ "composer.formatting.strikethrough": "Прецртано", "composer.formatting.code": "Код", "composer.formatting.link": "Веза", - "composer.formatting.picture": "Слика", + "composer.formatting.picture": "Image Link", "composer.upload-picture": "Отпреми слику", "composer.upload-file": "Отпреми датотеку", "composer.zen_mode": "Цео екран", diff --git a/public/language/sr/top.json b/public/language/sr/top.json new file mode 100644 index 0000000000..b8a05bfa5f --- /dev/null +++ b/public/language/sr/top.json @@ -0,0 +1,4 @@ +{ + "title": "Top", + "no_top_topics": "No top topics" +} \ No newline at end of file diff --git a/public/language/sr/topic.json b/public/language/sr/topic.json index f2265717a1..c95851bc0d 100644 --- a/public/language/sr/topic.json +++ b/public/language/sr/topic.json @@ -47,6 +47,7 @@ "restored-by": "Вратио", "moved-from-by": "Moved from %1 by", "queued-by": "Порука је на чекању за одобрење →", + "backlink": "Referenced by", "bookmark_instructions": "Кликните овде за повратак на последњу прочитану поруку у овој теми.", "flag-post": "Обележи заставицом поруку", "flag-user": "Обележи заставицом корисника", @@ -138,6 +139,7 @@ "composer.handle_placeholder": "Унесите ваше име/идентитет овде", "composer.discard": "Одбаци", "composer.submit": "Пошаљи", + "composer.additional-options": "Additional Options", "composer.schedule": "Испланирај", "composer.replying_to": "Писање одговора на %1", "composer.new_topic": "Нова тема", @@ -158,6 +160,7 @@ "newest_to_oldest": "Од новијих ка старијим", "most_votes": "Највише гласова", "most_posts": "Највише порука", + "most_views": "Most Views", "stale.title": "Креирати нову тему уместо тога?", "stale.warning": "Тема у којој желите да одговорите је сувише стара. Да ли желите да уместо тога креирате нову тему и упутите на ову у вашем одговору?", "stale.create": "Креирај нову тему", diff --git a/public/language/sr/user.json b/public/language/sr/user.json index 66b9e95715..270180a713 100644 --- a/public/language/sr/user.json +++ b/public/language/sr/user.json @@ -94,6 +94,7 @@ "digest_off": "Искључено", "digest_daily": "Дневно", "digest_weekly": "Седмично", + "digest_biweekly": "Bi-Weekly", "digest_monthly": "Месечно", "has_no_follower": "Овај корисник нема пратиоце :(", "follows_no_one": "Овај корисник не прати никога :(", diff --git a/public/language/sv/admin/advanced/events.json b/public/language/sv/admin/advanced/events.json index 56d9457971..b2c2033fb5 100644 --- a/public/language/sv/admin/advanced/events.json +++ b/public/language/sv/admin/advanced/events.json @@ -3,6 +3,7 @@ "no-events": "There are no events", "control-panel": "Events Control Panel", "delete-events": "Delete Events", + "confirm-delete-all-events": "Are you sure you want to delete all logged events?", "filters": "Filters", "filters-apply": "Apply Filters", "filter-type": "Event Type", diff --git a/public/language/sv/admin/dashboard.json b/public/language/sv/admin/dashboard.json index 0de31d4917..4d39626882 100644 --- a/public/language/sv/admin/dashboard.json +++ b/public/language/sv/admin/dashboard.json @@ -56,8 +56,8 @@ "active-users.total": "Total", "active-users.connections": "Connections", - "anonymous-registered-users": "Anonymous vs Registered Users", - "anonymous": "Anonymous", + "guest-registered-users": "Guest vs Registered Users", + "guest": "Guest", "registered": "Registered", "user-presence": "User Presence", @@ -68,6 +68,7 @@ "unread": "Unread", "high-presence-topics": "High Presence Topics", + "popular-searches": "Popular Searches", "graphs.page-views": "Page Views", "graphs.page-views-registered": "Page Views Registered", @@ -75,13 +76,14 @@ "graphs.page-views-bot": "Page Views Bot", "graphs.unique-visitors": "Unique Visitors", "graphs.registered-users": "Registered Users", - "graphs.anonymous-users": "Anonymous Users", + "graphs.guest-users": "Guest Users", "last-restarted-by": "Last restarted by", "no-users-browsing": "No users browsing", "back-to-dashboard": "Back to Dashboard", "details.no-users": "No users have joined within the selected timeframe", "details.no-topics": "No topics have been posted within the selected timeframe", + "details.no-searches": "No searches have been made yet", "details.no-logins": "No logins have been recorded within the selected timeframe", "details.logins-static": "NodeBB only saves session data for %1 days, and so this table below will only show the most recently active sessions", "details.logins-login-time": "Login Time" diff --git a/public/language/sv/admin/development/info.json b/public/language/sv/admin/development/info.json index 1003af1a5f..11202d9c3a 100644 --- a/public/language/sv/admin/development/info.json +++ b/public/language/sv/admin/development/info.json @@ -8,7 +8,11 @@ "nodejs": "nodejs", "online": "online", "git": "git", - "memory": "memory", + "process-memory": "process memory", + "system-memory": "system memory", + "used-memory-process": "Used memory by process", + "used-memory-os": "Used system memory", + "total-memory-os": "Total system memory", "load": "system load", "cpu-usage": "cpu usage", "uptime": "uptime", diff --git a/public/language/sv/admin/extend/rewards.json b/public/language/sv/admin/extend/rewards.json index 5383a90b33..df89d441a7 100644 --- a/public/language/sv/admin/extend/rewards.json +++ b/public/language/sv/admin/extend/rewards.json @@ -8,8 +8,6 @@ "delete": "Delete", "enable": "Enable", "disable": "Disable", - "control-panel": "Rewards Control", - "new-reward": "New Reward", "alert.delete-success": "Successfully deleted reward", "alert.no-inputs-found": "Illegal reward - no inputs found!", diff --git a/public/language/sv/admin/general/dashboard.json b/public/language/sv/admin/general/dashboard.json deleted file mode 100644 index 37ae537786..0000000000 --- a/public/language/sv/admin/general/dashboard.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "forum-traffic": "Forum Traffic", - "page-views": "Page Views", - "unique-visitors": "Unique Visitors", - "new-users": "New Users", - "posts": "Posts", - "topics": "Topics", - "page-views-seven": "Last 7 Days", - "page-views-thirty": "Last 30 Days", - "page-views-last-day": "Last 24 hours", - "page-views-custom": "Custom Date Range", - "page-views-custom-start": "Range Start", - "page-views-custom-end": "Range End", - "page-views-custom-help": "Enter a date range of page views you would like to view. If no date picker is available, the accepted format is YYYY-MM-DD", - "page-views-custom-error": "Please enter a valid date range in the format YYYY-MM-DD", - - "stats.yesterday": "Yesterday", - "stats.today": "Today", - "stats.last-week": "Last Week", - "stats.this-week": "This Week", - "stats.last-month": "Last Month", - "stats.this-month": "This Month", - "stats.all": "All Time", - - "updates": "Updates", - "running-version": "You are running NodeBB v%1.", - "keep-updated": "Always make sure that your NodeBB is up to date for the latest security patches and bug fixes.", - "up-to-date": "

You are up-to-date

", - "upgrade-available": "

A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-upgrade-available": "

This is an outdated pre-release version of NodeBB. A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-warning": "

This is a pre-release version of NodeBB. Unintended bugs may occur.

", - "running-in-development": "Forum is running in development mode. The forum may be open to potential vulnerabilities; please contact your system administrator.", - "latest-lookup-failed": "

Failed to look up latest available version of NodeBB

", - - "notices": "Notices", - "restart-not-required": "Restart not required", - "restart-required": "Restart required", - "search-plugin-installed": "Search Plugin installed", - "search-plugin-not-installed": "Search Plugin not installed", - "search-plugin-tooltip": "Install a search plugin from the plugin page in order to activate search functionality", - - "control-panel": "System Control", - "rebuild-and-restart": "Rebuild & Restart", - "restart": "Restart", - "restart-warning": "Rebuilding or Restarting your NodeBB will drop all existing connections for a few seconds.", - "restart-disabled": "Rebuilding and Restarting your NodeBB has been disabled as you do not seem to be running it via the appropriate daemon.", - "maintenance-mode": "Maintenance Mode", - "maintenance-mode-title": "Click here to set up maintenance mode for NodeBB", - "realtime-chart-updates": "Realtime Chart Updates", - - "active-users": "Active Users", - "active-users.users": "Users", - "active-users.guests": "Guests", - "active-users.total": "Total", - "active-users.connections": "Connections", - - "anonymous-registered-users": "Anonymous vs Registered Users", - "anonymous": "Anonymous", - "registered": "Registered", - - "user-presence": "User Presence", - "on-categories": "On categories list", - "reading-posts": "Reading posts", - "browsing-topics": "Browsing topics", - "recent": "Recent", - "unread": "Unread", - - "high-presence-topics": "High Presence Topics", - - "graphs.page-views": "Page Views", - "graphs.page-views-registered": "Page Views Registered", - "graphs.page-views-guest": "Page Views Guest", - "graphs.page-views-bot": "Page Views Bot", - "graphs.unique-visitors": "Unique Visitors", - "graphs.registered-users": "Registered Users", - "graphs.anonymous-users": "Anonymous Users", - "last-restarted-by": "Last restarted by", - "no-users-browsing": "No users browsing" -} diff --git a/public/language/sv/admin/general/homepage.json b/public/language/sv/admin/general/homepage.json deleted file mode 100644 index 7428d59eeb..0000000000 --- a/public/language/sv/admin/general/homepage.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "home-page": "Home Page", - "description": "Choose what page is shown when users navigate to the root URL of your forum.", - "home-page-route": "Home Page Route", - "custom-route": "Custom Route", - "allow-user-home-pages": "Allow User Home Pages", - "home-page-title": "Title of the home page (default \"Home\")" -} \ No newline at end of file diff --git a/public/language/sv/admin/general/languages.json b/public/language/sv/admin/general/languages.json deleted file mode 100644 index bdd57849b3..0000000000 --- a/public/language/sv/admin/general/languages.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "language-settings": "Language Settings", - "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language", - "auto-detect": "Auto Detect Language Setting for Guests" -} \ No newline at end of file diff --git a/public/language/sv/admin/general/navigation.json b/public/language/sv/admin/general/navigation.json deleted file mode 100644 index 13dd01aae7..0000000000 --- a/public/language/sv/admin/general/navigation.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "icon": "Icon:", - "change-icon": "change", - "route": "Route:", - "tooltip": "Tooltip:", - "text": "Text:", - "text-class": "Text Class: optional", - "class": "Class: optional", - "id": "ID: optional", - - "properties": "Properties:", - "groups": "Groups:", - "open-new-window": "Open in a new window", - - "btn.delete": "Delete", - "btn.disable": "Disable", - "btn.enable": "Enable", - - "available-menu-items": "Available Menu Items", - "custom-route": "Custom Route", - "core": "core", - "plugin": "plugin" -} \ No newline at end of file diff --git a/public/language/sv/admin/general/social.json b/public/language/sv/admin/general/social.json deleted file mode 100644 index 23aedfcfaa..0000000000 --- a/public/language/sv/admin/general/social.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "post-sharing": "Post Sharing", - "info-plugins-additional": "Plugins can add additional networks for sharing posts.", - "save-success": "Successfully saved Post Sharing Networks!" -} \ No newline at end of file diff --git a/public/language/sv/admin/general/sounds.json b/public/language/sv/admin/general/sounds.json deleted file mode 100644 index 95ccbde0f1..0000000000 --- a/public/language/sv/admin/general/sounds.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "notifications": "Notifications", - "chat-messages": "Chat Messages", - "play-sound": "Play", - "incoming-message": "Incoming Message", - "outgoing-message": "Outgoing Message", - "upload-new-sound": "Upload New Sound", - "saved": "Settings Saved" -} \ No newline at end of file diff --git a/public/language/sv/admin/manage/digest.json b/public/language/sv/admin/manage/digest.json index 8f3661698a..38c634d1f6 100644 --- a/public/language/sv/admin/manage/digest.json +++ b/public/language/sv/admin/manage/digest.json @@ -13,6 +13,7 @@ "resent-single": "Manual digest resend completed", "resent-day": "Daily digest resent", "resent-week": "Weekly digest resent", + "resent-biweek": "Bi-Weekly digest resent", "resent-month": "Monthly digest resent", "null": "Never", "manual-run": "Manual digest run:", diff --git a/public/language/sv/admin/manage/users.json b/public/language/sv/admin/manage/users.json index 38a065b954..2a3c0c4829 100644 --- a/public/language/sv/admin/manage/users.json +++ b/public/language/sv/admin/manage/users.json @@ -47,6 +47,7 @@ "users.uid": "uid", "users.username": "username", "users.email": "email", + "users.no-email": "(no email)", "users.ip": "IP", "users.postcount": "postcount", "users.reputation": "reputation", diff --git a/public/language/sv/admin/menu.json b/public/language/sv/admin/menu.json index 1c5ea73b4f..5b22fbeb36 100644 --- a/public/language/sv/admin/menu.json +++ b/public/language/sv/admin/menu.json @@ -4,6 +4,7 @@ "dashboard/logins": "Logins", "dashboard/users": "Users", "dashboard/topics": "Topics", + "dashboard/searches": "Searches", "section-general": "General", "section-manage": "Manage", diff --git a/public/language/sv/admin/settings/email.json b/public/language/sv/admin/settings/email.json index 206cd068d8..381ab387df 100644 --- a/public/language/sv/admin/settings/email.json +++ b/public/language/sv/admin/settings/email.json @@ -6,7 +6,7 @@ "from-help": "The from name to display in the email.", "smtp-transport": "SMTP Transport", - "smtp-transport.enabled": "Use an external email server to send emails", + "smtp-transport.enabled": "Enable SMTP Transport", "smtp-transport-help": "You can select from a list of well-known services or enter a custom one.", "smtp-transport.service": "Select a service", "smtp-transport.service-custom": "Custom Service", @@ -37,6 +37,10 @@ "subscriptions.hour": "Digest Hour", "subscriptions.hour-help": "Please enter a number representing the hour to send scheduled email digests (e.g. 0 for midnight, 17 for 5:00pm). Keep in mind that this is the hour according to the server itself, and may not exactly match your system clock.
The approximate server time is:
The next daily digest is scheduled to be sent ", "notifications.remove-images": "Remove images from email notifications", + "require-email-address": "Require new users to specify an email address", + "require-email-address-warning": "By default, users can opt-out of entering an email address. Enabling this option means they have to enter an email address in order to proceed with registration. It does not ensure user will enter a real email address, nor even an address they own.", "include-unverified-emails": "Send emails to recipients who have not explicitly confirmed their emails", - "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws." -} \ No newline at end of file + "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws.", + "prompt": "Prompt users to enter or confirm their emails", + "prompt-help": "If a user does not have an email set, or their email is not confirmed, a warning will be shown on screen." +} diff --git a/public/language/sv/admin/settings/general.json b/public/language/sv/admin/settings/general.json index be7df90870..29b939861b 100644 --- a/public/language/sv/admin/settings/general.json +++ b/public/language/sv/admin/settings/general.json @@ -3,9 +3,9 @@ "title": "Site Title", "title.short": "Short Title", "title.short-placeholder": "If no short title is specified, the site title will be used", - "title.url": "URL", + "title.url": "Title Link URL", "title.url-placeholder": "The URL of the site title", - "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.", + "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "title.name": "Your Community Name", "title.show-in-header": "Show Site Title in Header", "browser-title": "Browser Title", @@ -20,9 +20,9 @@ "logo.image": "Image", "logo.image-placeholder": "Path to a logo to display on forum header", "logo.upload": "Upload", - "logo.url": "URL", + "logo.url": "Logo Link URL", "logo.url-placeholder": "The URL of the site logo", - "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.", + "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "logo.alt-text": "Alt Text", "log.alt-text-placeholder": "Alternative text for accessibility", "favicon": "Favicon", @@ -47,4 +47,4 @@ "undo-timeout": "Undo Timeout", "undo-timeout-help": "Some operations such as moving topics will allow for the moderator to undo their action within a certain timeframe. Set to 0 to disable undo completely.", "topic-tools": "Topic Tools" -} \ No newline at end of file +} diff --git a/public/language/sv/admin/settings/navigation.json b/public/language/sv/admin/settings/navigation.json index 13dd01aae7..7baca85096 100644 --- a/public/language/sv/admin/settings/navigation.json +++ b/public/language/sv/admin/settings/navigation.json @@ -11,6 +11,8 @@ "properties": "Properties:", "groups": "Groups:", "open-new-window": "Open in a new window", + "dropdown": "Dropdown", + "dropdown-placeholder": "Place your dropdown menu items below, ie:
<li><a href="https://myforum.com">Link 1</a></li>", "btn.delete": "Delete", "btn.disable": "Disable", @@ -20,4 +22,4 @@ "custom-route": "Custom Route", "core": "core", "plugin": "plugin" -} \ No newline at end of file +} diff --git a/public/language/sv/admin/settings/notifications.json b/public/language/sv/admin/settings/notifications.json index da6c9680a3..c6d8b928ce 100644 --- a/public/language/sv/admin/settings/notifications.json +++ b/public/language/sv/admin/settings/notifications.json @@ -2,5 +2,6 @@ "notifications": "Notifications", "welcome-notification": "Welcome Notification", "welcome-notification-link": "Welcome Notification Link", - "welcome-notification-uid": "Welcome Notification User (UID)" + "welcome-notification-uid": "Welcome Notification User (UID)", + "post-queue-notification-uid": "Post Queue User (UID)" } \ No newline at end of file diff --git a/public/language/sv/admin/settings/post.json b/public/language/sv/admin/settings/post.json index 27493aafbd..ab8245738c 100644 --- a/public/language/sv/admin/settings/post.json +++ b/public/language/sv/admin/settings/post.json @@ -40,6 +40,7 @@ "teaser.last-post": "Last – Show the latest post, including the original post, if no replies", "teaser.last-reply": "Last – Show the latest reply, or a \"No replies\" placeholder if no replies", "teaser.first": "First", + "showPostPreviewsOnHover": "Show a preview of posts when mouse overed", "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", @@ -56,6 +57,9 @@ "composer.show-help": "Show \"Help\" tab", "composer.enable-plugin-help": "Allow plugins to add content to the help tab", "composer.custom-help": "Custom Help Text", + "backlinks": "Backlinks", + "backlinks.enabled": "Enable topic backlinks", + "backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.", "ip-tracking": "IP Tracking", "ip-tracking.each-post": "Track IP Address for each post", "enable-post-history": "Enable Post History" diff --git a/public/language/sv/admin/settings/user.json b/public/language/sv/admin/settings/user.json index 48be13b75e..7923bf8cbe 100644 --- a/public/language/sv/admin/settings/user.json +++ b/public/language/sv/admin/settings/user.json @@ -71,6 +71,7 @@ "digest-freq.off": "Off", "digest-freq.daily": "Daily", "digest-freq.weekly": "Weekly", + "digest-freq.biweekly": "Bi-Weekly", "digest-freq.monthly": "Monthly", "email-chat-notifs": "Send an email if a new chat message arrives and I am not online", "email-post-notif": "Send an email when replies are made to topics I am subscribed to", diff --git a/public/language/sv/error.json b/public/language/sv/error.json index f109a020bf..5b65ecb344 100644 --- a/public/language/sv/error.json +++ b/public/language/sv/error.json @@ -34,8 +34,9 @@ "email-invited": "Email was already invited", "email-not-confirmed": "Posting in some categories or topics is enabled once your email is confirmed, please click here to send a confirmation email.", "email-not-confirmed-chat": "Du kan ej använda chatten förrän din epostadress har blivit bekräftad, var god klicka här för att bekräfta din epostadress.", - "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You won't be able to post or chat until your email is confirmed.", - "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery. Please click here to enter an email.", + "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You may not be able to post in some categories or chat until your email is confirmed.", + "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery, and may be necessary for chatting and posting in some categories. Please click here to enter an email.", + "user-doesnt-have-email": "User \"%1\" does not have an email set.", "email-confirm-failed": "Vi kunde ej bekräfta din epostadress, var god försök igen senare.", "confirm-email-already-sent": "Bekräftningsbrev redan skickat, var god vänta %1 minut(er) innan du skickar ett nytt.", "sendmail-not-found": "Kunde inte hitta Sendmail, vänligen se till att den är installerad och får köras av den användare som kör NodeBB.", @@ -103,6 +104,7 @@ "already-bookmarked": "Du har redan bokmärkt det här inlägget", "already-unbookmarked": "Du har redan tagit bort bokmärket för det här inlägget", "cant-ban-other-admins": "Du kan inte bannlysa andra administratörer!", + "cant-make-banned-users-admin": "You can't make banned users admin.", "cant-remove-last-admin": "Du är den enda administratören. Lägg till en annan användare som administratör innan du tar bort dig själv.", "account-deletion-disabled": "Account deletion is disabled", "cant-delete-admin": "Ta bort administratörsbehörighet från detta konto innan du försöker ta bort den.", diff --git a/public/language/sv/modules.json b/public/language/sv/modules.json index 66bb0a23a3..628c4de96b 100644 --- a/public/language/sv/modules.json +++ b/public/language/sv/modules.json @@ -54,7 +54,7 @@ "composer.formatting.strikethrough": "Genomstrykning", "composer.formatting.code": "Kod", "composer.formatting.link": "Länk", - "composer.formatting.picture": "Bild", + "composer.formatting.picture": "Image Link", "composer.upload-picture": "Ladda upp bild", "composer.upload-file": "Ladda upp fil", "composer.zen_mode": "Zen Mode", diff --git a/public/language/sv/top.json b/public/language/sv/top.json new file mode 100644 index 0000000000..b8a05bfa5f --- /dev/null +++ b/public/language/sv/top.json @@ -0,0 +1,4 @@ +{ + "title": "Top", + "no_top_topics": "No top topics" +} \ No newline at end of file diff --git a/public/language/sv/topic.json b/public/language/sv/topic.json index e29fc805c1..cf335a343c 100644 --- a/public/language/sv/topic.json +++ b/public/language/sv/topic.json @@ -47,6 +47,7 @@ "restored-by": "Restored by", "moved-from-by": "Moved from %1 by", "queued-by": "Post queued for approval →", + "backlink": "Referenced by", "bookmark_instructions": "Klicka här för att återgå till senast lästa inlägg i detta ämne.", "flag-post": "Flag this post", "flag-user": "Flag this user", @@ -138,6 +139,7 @@ "composer.handle_placeholder": "Enter your name/handle here", "composer.discard": "Avbryt", "composer.submit": "Skicka", + "composer.additional-options": "Additional Options", "composer.schedule": "Schedule", "composer.replying_to": "Svarar till %1", "composer.new_topic": "Nytt ämne", @@ -158,6 +160,7 @@ "newest_to_oldest": "Nyaste till äldst", "most_votes": "Flest röster", "most_posts": "Flest inlägg", + "most_views": "Most Views", "stale.title": "Skapa nytt ämne istället?", "stale.warning": "Ämnet du svarar på är ganska gammalt. Vill du skapa ett nytt ämne istället och inkludera en referens till det här ämnet i ditt inlägg?", "stale.create": "Skapa nytt ämne", diff --git a/public/language/sv/user.json b/public/language/sv/user.json index 9fd68192ea..955318aae7 100644 --- a/public/language/sv/user.json +++ b/public/language/sv/user.json @@ -94,6 +94,7 @@ "digest_off": "Avslagen", "digest_daily": "Dagligen", "digest_weekly": "Veckovis", + "digest_biweekly": "Bi-Weekly", "digest_monthly": "Månadsvis", "has_no_follower": "Denna användare har inga följare :(", "follows_no_one": "Denna användare följer ingen :(", diff --git a/public/language/th/admin/advanced/events.json b/public/language/th/admin/advanced/events.json index d37ba45659..1cd0c87bb1 100644 --- a/public/language/th/admin/advanced/events.json +++ b/public/language/th/admin/advanced/events.json @@ -3,6 +3,7 @@ "no-events": "ไม่มีอีเวนท์", "control-panel": "แผงควบคุมอีเวนท์", "delete-events": "Delete Events", + "confirm-delete-all-events": "Are you sure you want to delete all logged events?", "filters": "Filters", "filters-apply": "Apply Filters", "filter-type": "Event Type", diff --git a/public/language/th/admin/dashboard.json b/public/language/th/admin/dashboard.json index 0de31d4917..4d39626882 100644 --- a/public/language/th/admin/dashboard.json +++ b/public/language/th/admin/dashboard.json @@ -56,8 +56,8 @@ "active-users.total": "Total", "active-users.connections": "Connections", - "anonymous-registered-users": "Anonymous vs Registered Users", - "anonymous": "Anonymous", + "guest-registered-users": "Guest vs Registered Users", + "guest": "Guest", "registered": "Registered", "user-presence": "User Presence", @@ -68,6 +68,7 @@ "unread": "Unread", "high-presence-topics": "High Presence Topics", + "popular-searches": "Popular Searches", "graphs.page-views": "Page Views", "graphs.page-views-registered": "Page Views Registered", @@ -75,13 +76,14 @@ "graphs.page-views-bot": "Page Views Bot", "graphs.unique-visitors": "Unique Visitors", "graphs.registered-users": "Registered Users", - "graphs.anonymous-users": "Anonymous Users", + "graphs.guest-users": "Guest Users", "last-restarted-by": "Last restarted by", "no-users-browsing": "No users browsing", "back-to-dashboard": "Back to Dashboard", "details.no-users": "No users have joined within the selected timeframe", "details.no-topics": "No topics have been posted within the selected timeframe", + "details.no-searches": "No searches have been made yet", "details.no-logins": "No logins have been recorded within the selected timeframe", "details.logins-static": "NodeBB only saves session data for %1 days, and so this table below will only show the most recently active sessions", "details.logins-login-time": "Login Time" diff --git a/public/language/th/admin/development/info.json b/public/language/th/admin/development/info.json index f19273f0f9..504759aa23 100644 --- a/public/language/th/admin/development/info.json +++ b/public/language/th/admin/development/info.json @@ -8,7 +8,11 @@ "nodejs": "nodejs", "online": "online", "git": "git", - "memory": "หน่วยความจำ", + "process-memory": "process memory", + "system-memory": "system memory", + "used-memory-process": "Used memory by process", + "used-memory-os": "Used system memory", + "total-memory-os": "Total system memory", "load": "system load", "cpu-usage": "cpu usage", "uptime": "ระยะเวลาการทำงาน", diff --git a/public/language/th/admin/extend/rewards.json b/public/language/th/admin/extend/rewards.json index 5383a90b33..df89d441a7 100644 --- a/public/language/th/admin/extend/rewards.json +++ b/public/language/th/admin/extend/rewards.json @@ -8,8 +8,6 @@ "delete": "Delete", "enable": "Enable", "disable": "Disable", - "control-panel": "Rewards Control", - "new-reward": "New Reward", "alert.delete-success": "Successfully deleted reward", "alert.no-inputs-found": "Illegal reward - no inputs found!", diff --git a/public/language/th/admin/general/dashboard.json b/public/language/th/admin/general/dashboard.json deleted file mode 100644 index 37ae537786..0000000000 --- a/public/language/th/admin/general/dashboard.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "forum-traffic": "Forum Traffic", - "page-views": "Page Views", - "unique-visitors": "Unique Visitors", - "new-users": "New Users", - "posts": "Posts", - "topics": "Topics", - "page-views-seven": "Last 7 Days", - "page-views-thirty": "Last 30 Days", - "page-views-last-day": "Last 24 hours", - "page-views-custom": "Custom Date Range", - "page-views-custom-start": "Range Start", - "page-views-custom-end": "Range End", - "page-views-custom-help": "Enter a date range of page views you would like to view. If no date picker is available, the accepted format is YYYY-MM-DD", - "page-views-custom-error": "Please enter a valid date range in the format YYYY-MM-DD", - - "stats.yesterday": "Yesterday", - "stats.today": "Today", - "stats.last-week": "Last Week", - "stats.this-week": "This Week", - "stats.last-month": "Last Month", - "stats.this-month": "This Month", - "stats.all": "All Time", - - "updates": "Updates", - "running-version": "You are running NodeBB v%1.", - "keep-updated": "Always make sure that your NodeBB is up to date for the latest security patches and bug fixes.", - "up-to-date": "

You are up-to-date

", - "upgrade-available": "

A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-upgrade-available": "

This is an outdated pre-release version of NodeBB. A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-warning": "

This is a pre-release version of NodeBB. Unintended bugs may occur.

", - "running-in-development": "Forum is running in development mode. The forum may be open to potential vulnerabilities; please contact your system administrator.", - "latest-lookup-failed": "

Failed to look up latest available version of NodeBB

", - - "notices": "Notices", - "restart-not-required": "Restart not required", - "restart-required": "Restart required", - "search-plugin-installed": "Search Plugin installed", - "search-plugin-not-installed": "Search Plugin not installed", - "search-plugin-tooltip": "Install a search plugin from the plugin page in order to activate search functionality", - - "control-panel": "System Control", - "rebuild-and-restart": "Rebuild & Restart", - "restart": "Restart", - "restart-warning": "Rebuilding or Restarting your NodeBB will drop all existing connections for a few seconds.", - "restart-disabled": "Rebuilding and Restarting your NodeBB has been disabled as you do not seem to be running it via the appropriate daemon.", - "maintenance-mode": "Maintenance Mode", - "maintenance-mode-title": "Click here to set up maintenance mode for NodeBB", - "realtime-chart-updates": "Realtime Chart Updates", - - "active-users": "Active Users", - "active-users.users": "Users", - "active-users.guests": "Guests", - "active-users.total": "Total", - "active-users.connections": "Connections", - - "anonymous-registered-users": "Anonymous vs Registered Users", - "anonymous": "Anonymous", - "registered": "Registered", - - "user-presence": "User Presence", - "on-categories": "On categories list", - "reading-posts": "Reading posts", - "browsing-topics": "Browsing topics", - "recent": "Recent", - "unread": "Unread", - - "high-presence-topics": "High Presence Topics", - - "graphs.page-views": "Page Views", - "graphs.page-views-registered": "Page Views Registered", - "graphs.page-views-guest": "Page Views Guest", - "graphs.page-views-bot": "Page Views Bot", - "graphs.unique-visitors": "Unique Visitors", - "graphs.registered-users": "Registered Users", - "graphs.anonymous-users": "Anonymous Users", - "last-restarted-by": "Last restarted by", - "no-users-browsing": "No users browsing" -} diff --git a/public/language/th/admin/general/homepage.json b/public/language/th/admin/general/homepage.json deleted file mode 100644 index 48f9ebe23a..0000000000 --- a/public/language/th/admin/general/homepage.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "home-page": "หน้าแรก", - "description": "เลือกหน้าเว็บที่จะแสดงเมื่อผู้ใช้ไปที่ URL หลักของฟอรัม", - "home-page-route": "เส้นทางหน้าแรก", - "custom-route": "เส้นทางที่กำหนดเอง", - "allow-user-home-pages": "อนุญาตหน้าแรกของผู้ใช้", - "home-page-title": "Title ของหน้าแรก (ค่าเริ่มต้น \"Home\")" -} \ No newline at end of file diff --git a/public/language/th/admin/general/languages.json b/public/language/th/admin/general/languages.json deleted file mode 100644 index bdd57849b3..0000000000 --- a/public/language/th/admin/general/languages.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "language-settings": "Language Settings", - "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language", - "auto-detect": "Auto Detect Language Setting for Guests" -} \ No newline at end of file diff --git a/public/language/th/admin/general/navigation.json b/public/language/th/admin/general/navigation.json deleted file mode 100644 index 13dd01aae7..0000000000 --- a/public/language/th/admin/general/navigation.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "icon": "Icon:", - "change-icon": "change", - "route": "Route:", - "tooltip": "Tooltip:", - "text": "Text:", - "text-class": "Text Class: optional", - "class": "Class: optional", - "id": "ID: optional", - - "properties": "Properties:", - "groups": "Groups:", - "open-new-window": "Open in a new window", - - "btn.delete": "Delete", - "btn.disable": "Disable", - "btn.enable": "Enable", - - "available-menu-items": "Available Menu Items", - "custom-route": "Custom Route", - "core": "core", - "plugin": "plugin" -} \ No newline at end of file diff --git a/public/language/th/admin/general/social.json b/public/language/th/admin/general/social.json deleted file mode 100644 index 7d6a9a8c83..0000000000 --- a/public/language/th/admin/general/social.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "post-sharing": "การแชร์กระทู้", - "info-plugins-additional": "ส่วนเสริมสามารถเพิ่มการเชือมต่อโซเชียลมิเดียเพื่อแชร์กระทู้", - "save-success": "การบันทึกการโพสแชร์เนื้อหาเสร็จสมบูรณ์!" -} \ No newline at end of file diff --git a/public/language/th/admin/general/sounds.json b/public/language/th/admin/general/sounds.json deleted file mode 100644 index 2be53c2cf0..0000000000 --- a/public/language/th/admin/general/sounds.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "notifications": "การแจ้งเตือน", - "chat-messages": "ข้อความแชท", - "play-sound": "เล่น", - "incoming-message": "ข้อความเข้า", - "outgoing-message": "ข้อความออก", - "upload-new-sound": "อัปโหลดเสียงใหม่", - "saved": "การตั้งค่าได้ถูกบันทึกแล้ว" -} \ No newline at end of file diff --git a/public/language/th/admin/manage/digest.json b/public/language/th/admin/manage/digest.json index 8f3661698a..38c634d1f6 100644 --- a/public/language/th/admin/manage/digest.json +++ b/public/language/th/admin/manage/digest.json @@ -13,6 +13,7 @@ "resent-single": "Manual digest resend completed", "resent-day": "Daily digest resent", "resent-week": "Weekly digest resent", + "resent-biweek": "Bi-Weekly digest resent", "resent-month": "Monthly digest resent", "null": "Never", "manual-run": "Manual digest run:", diff --git a/public/language/th/admin/manage/users.json b/public/language/th/admin/manage/users.json index c5fc0e13b9..9a09edd168 100644 --- a/public/language/th/admin/manage/users.json +++ b/public/language/th/admin/manage/users.json @@ -47,6 +47,7 @@ "users.uid": "uid", "users.username": "ชื่อผู้ใช้", "users.email": "อีเมล", + "users.no-email": "(no email)", "users.ip": "IP", "users.postcount": "จำนวนกระทู้", "users.reputation": "ชื่อเสียง", diff --git a/public/language/th/admin/menu.json b/public/language/th/admin/menu.json index 1c5ea73b4f..5b22fbeb36 100644 --- a/public/language/th/admin/menu.json +++ b/public/language/th/admin/menu.json @@ -4,6 +4,7 @@ "dashboard/logins": "Logins", "dashboard/users": "Users", "dashboard/topics": "Topics", + "dashboard/searches": "Searches", "section-general": "General", "section-manage": "Manage", diff --git a/public/language/th/admin/settings/email.json b/public/language/th/admin/settings/email.json index 9bdbde26a6..65f579d28c 100644 --- a/public/language/th/admin/settings/email.json +++ b/public/language/th/admin/settings/email.json @@ -6,7 +6,7 @@ "from-help": "The from name to display in the email.", "smtp-transport": "SMTP Transport", - "smtp-transport.enabled": "Use an external email server to send emails", + "smtp-transport.enabled": "Enable SMTP Transport", "smtp-transport-help": "You can select from a list of well-known services or enter a custom one.", "smtp-transport.service": "Select a service", "smtp-transport.service-custom": "Custom Service", @@ -37,6 +37,10 @@ "subscriptions.hour": "Digest Hour", "subscriptions.hour-help": "Please enter a number representing the hour to send scheduled email digests (e.g. 0 for midnight, 17 for 5:00pm). Keep in mind that this is the hour according to the server itself, and may not exactly match your system clock.
The approximate server time is:
The next daily digest is scheduled to be sent ", "notifications.remove-images": "Remove images from email notifications", + "require-email-address": "Require new users to specify an email address", + "require-email-address-warning": "By default, users can opt-out of entering an email address. Enabling this option means they have to enter an email address in order to proceed with registration. It does not ensure user will enter a real email address, nor even an address they own.", "include-unverified-emails": "Send emails to recipients who have not explicitly confirmed their emails", - "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws." -} \ No newline at end of file + "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws.", + "prompt": "Prompt users to enter or confirm their emails", + "prompt-help": "If a user does not have an email set, or their email is not confirmed, a warning will be shown on screen." +} diff --git a/public/language/th/admin/settings/general.json b/public/language/th/admin/settings/general.json index be7df90870..29b939861b 100644 --- a/public/language/th/admin/settings/general.json +++ b/public/language/th/admin/settings/general.json @@ -3,9 +3,9 @@ "title": "Site Title", "title.short": "Short Title", "title.short-placeholder": "If no short title is specified, the site title will be used", - "title.url": "URL", + "title.url": "Title Link URL", "title.url-placeholder": "The URL of the site title", - "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.", + "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "title.name": "Your Community Name", "title.show-in-header": "Show Site Title in Header", "browser-title": "Browser Title", @@ -20,9 +20,9 @@ "logo.image": "Image", "logo.image-placeholder": "Path to a logo to display on forum header", "logo.upload": "Upload", - "logo.url": "URL", + "logo.url": "Logo Link URL", "logo.url-placeholder": "The URL of the site logo", - "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.", + "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "logo.alt-text": "Alt Text", "log.alt-text-placeholder": "Alternative text for accessibility", "favicon": "Favicon", @@ -47,4 +47,4 @@ "undo-timeout": "Undo Timeout", "undo-timeout-help": "Some operations such as moving topics will allow for the moderator to undo their action within a certain timeframe. Set to 0 to disable undo completely.", "topic-tools": "Topic Tools" -} \ No newline at end of file +} diff --git a/public/language/th/admin/settings/navigation.json b/public/language/th/admin/settings/navigation.json index 13dd01aae7..7baca85096 100644 --- a/public/language/th/admin/settings/navigation.json +++ b/public/language/th/admin/settings/navigation.json @@ -11,6 +11,8 @@ "properties": "Properties:", "groups": "Groups:", "open-new-window": "Open in a new window", + "dropdown": "Dropdown", + "dropdown-placeholder": "Place your dropdown menu items below, ie:
<li><a href="https://myforum.com">Link 1</a></li>", "btn.delete": "Delete", "btn.disable": "Disable", @@ -20,4 +22,4 @@ "custom-route": "Custom Route", "core": "core", "plugin": "plugin" -} \ No newline at end of file +} diff --git a/public/language/th/admin/settings/notifications.json b/public/language/th/admin/settings/notifications.json index c2f5b8b9b9..0d8e4ec3c1 100644 --- a/public/language/th/admin/settings/notifications.json +++ b/public/language/th/admin/settings/notifications.json @@ -2,5 +2,6 @@ "notifications": "การแจ้งเตือน", "welcome-notification": "การยินดีต้อนรับแจ้งเตือน", "welcome-notification-link": "ลิงค์การยินดีต้อนรับแจ้งเตือน", - "welcome-notification-uid": "Welcome Notification User (UID)" + "welcome-notification-uid": "Welcome Notification User (UID)", + "post-queue-notification-uid": "Post Queue User (UID)" } \ No newline at end of file diff --git a/public/language/th/admin/settings/post.json b/public/language/th/admin/settings/post.json index 27493aafbd..ab8245738c 100644 --- a/public/language/th/admin/settings/post.json +++ b/public/language/th/admin/settings/post.json @@ -40,6 +40,7 @@ "teaser.last-post": "Last – Show the latest post, including the original post, if no replies", "teaser.last-reply": "Last – Show the latest reply, or a \"No replies\" placeholder if no replies", "teaser.first": "First", + "showPostPreviewsOnHover": "Show a preview of posts when mouse overed", "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", @@ -56,6 +57,9 @@ "composer.show-help": "Show \"Help\" tab", "composer.enable-plugin-help": "Allow plugins to add content to the help tab", "composer.custom-help": "Custom Help Text", + "backlinks": "Backlinks", + "backlinks.enabled": "Enable topic backlinks", + "backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.", "ip-tracking": "IP Tracking", "ip-tracking.each-post": "Track IP Address for each post", "enable-post-history": "Enable Post History" diff --git a/public/language/th/admin/settings/user.json b/public/language/th/admin/settings/user.json index 04814386d3..a6c888499c 100644 --- a/public/language/th/admin/settings/user.json +++ b/public/language/th/admin/settings/user.json @@ -71,6 +71,7 @@ "digest-freq.off": "Off", "digest-freq.daily": "ทุกวัน", "digest-freq.weekly": "ทุกอาทิตย์", + "digest-freq.biweekly": "Bi-Weekly", "digest-freq.monthly": "ทุกเดือน", "email-chat-notifs": "Send an email if a new chat message arrives and I am not online", "email-post-notif": "Send an email when replies are made to topics I am subscribed to", diff --git a/public/language/th/error.json b/public/language/th/error.json index e7537f01dd..17adfbe6b9 100644 --- a/public/language/th/error.json +++ b/public/language/th/error.json @@ -34,8 +34,9 @@ "email-invited": "Email was already invited", "email-not-confirmed": "Posting in some categories or topics is enabled once your email is confirmed, please click here to send a confirmation email.", "email-not-confirmed-chat": "คุณไม่สามารถแชทได้จนกว่าอีเมล์ของคุณจะได้รับการยืนยัน กรุณาคลิกที่นี่เพื่อยืนยันอีกมเมล์ของคุณ", - "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You won't be able to post or chat until your email is confirmed.", - "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery. Please click here to enter an email.", + "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You may not be able to post in some categories or chat until your email is confirmed.", + "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery, and may be necessary for chatting and posting in some categories. Please click here to enter an email.", + "user-doesnt-have-email": "User \"%1\" does not have an email set.", "email-confirm-failed": "เราไม่สามารถยืนยันอีเมลของคุณ ณ ขณะนี้ กรุณาลองใหม่อีกครั้งภายหลัง", "confirm-email-already-sent": "อีเมล์ยืนยันตัวตนถูกส่งไปยังคุณเรียบร้อยแล้ว กรุณารอ %1 นาที(s) ก่อนการตัดสินใจส่งอีกครั้ง", "sendmail-not-found": "ไม่พบการประมวลผลสำหรับการส่งอีเมล์ กรุณาตรวจสอบให้แน่ใจว่าได้มีการติดตั้งโปรแกรมการประมวลผลแล้วโดยผู้ใช้ที่กำลังใช้ NodeBB", @@ -103,6 +104,7 @@ "already-bookmarked": "คุณได้ติดบุ๊กมาร์คของโพสต์นี้แล้ว", "already-unbookmarked": "คุณได้ลบบุ๊กมาร์คของโพสต์นี้แล้ว", "cant-ban-other-admins": "คุณแบนแอดมินไม่ได้!!!", + "cant-make-banned-users-admin": "You can't make banned users admin.", "cant-remove-last-admin": "คุณเป็นแอดมินเพียงคนเดียว กรุณาเพิ่มผู้ใช้คนอื่นเป็นแอดมิน ก่อนการลบตัวเองออกจากแอดมิน", "account-deletion-disabled": "Account deletion is disabled", "cant-delete-admin": "ลบสิทธิพิเศษของแอดมินจากบัญชีผู้ใช้นี้ ก่อนทำการลบ", diff --git a/public/language/th/modules.json b/public/language/th/modules.json index 7e66c6fbbc..7c8d925289 100644 --- a/public/language/th/modules.json +++ b/public/language/th/modules.json @@ -54,7 +54,7 @@ "composer.formatting.strikethrough": "ขีดเส้นใต้", "composer.formatting.code": "Code", "composer.formatting.link": "ลิ้งค์", - "composer.formatting.picture": "รูปภาพ", + "composer.formatting.picture": "Image Link", "composer.upload-picture": "อัพโหลดรูปภาพ", "composer.upload-file": "อัพโหลดไฟล์", "composer.zen_mode": "เซ็นโหมด", diff --git a/public/language/th/top.json b/public/language/th/top.json new file mode 100644 index 0000000000..b8a05bfa5f --- /dev/null +++ b/public/language/th/top.json @@ -0,0 +1,4 @@ +{ + "title": "Top", + "no_top_topics": "No top topics" +} \ No newline at end of file diff --git a/public/language/th/topic.json b/public/language/th/topic.json index 94810b78fc..c7fc1707e3 100644 --- a/public/language/th/topic.json +++ b/public/language/th/topic.json @@ -47,6 +47,7 @@ "restored-by": "Restored by", "moved-from-by": "Moved from %1 by", "queued-by": "Post queued for approval →", + "backlink": "Referenced by", "bookmark_instructions": "คลิกที่นี่เพื่อกลับไปยังโพสต์ล่าสุดในหัวข้อนี้", "flag-post": "Flag this post", "flag-user": "Flag this user", @@ -138,6 +139,7 @@ "composer.handle_placeholder": "Enter your name/handle here", "composer.discard": "ยกเลิก", "composer.submit": "ส่ง", + "composer.additional-options": "Additional Options", "composer.schedule": "Schedule", "composer.replying_to": "ตอบไปยัง %1", "composer.new_topic": "กระทู้ใหม่", @@ -158,6 +160,7 @@ "newest_to_oldest": "ใหม่สุดไปยังเก่าสุด", "most_votes": "Most Votes", "most_posts": "Most Posts", + "most_views": "Most Views", "stale.title": "ตั้งกระทู้ใหม่แทนไหม?", "stale.warning": "กระทู้ที่คุณกำลังตอบเก่าไปหน่อยนะ อยากจะลองตั้งกระทู้ใหม่แทนไหมล่ะ? แล้วก็อ้างอิงกระทู้นี้ไปยังคำตอบของคุณ", "stale.create": "ตั้งกระทู้ใหม่", diff --git a/public/language/th/user.json b/public/language/th/user.json index 5a2b910b58..78759eadb0 100644 --- a/public/language/th/user.json +++ b/public/language/th/user.json @@ -94,6 +94,7 @@ "digest_off": "ปิด", "digest_daily": "รายวัน", "digest_weekly": "รายสัปดาห์", + "digest_biweekly": "Bi-Weekly", "digest_monthly": "รายเดือน", "has_no_follower": "ผู้ใช้รายนี้ไม่มีใครติดตาม :(", "follows_no_one": "ผู้ใช้รายนี้ไม่ติดตามใคร :(", diff --git a/public/language/tr/admin/advanced/events.json b/public/language/tr/admin/advanced/events.json index 6f42b7c4d2..14bdd8788c 100644 --- a/public/language/tr/admin/advanced/events.json +++ b/public/language/tr/admin/advanced/events.json @@ -3,6 +3,7 @@ "no-events": "Aktivite yok", "control-panel": "Aktivite Kontrol Paneli", "delete-events": "Aktiviteyi Sil", + "confirm-delete-all-events": "Kaydedilen tüm etkinlikleri silmek istediğinizden emin misiniz?", "filters": "Filtreler", "filters-apply": "Filtreleri Uygula", "filter-type": "Aktivite türü", diff --git a/public/language/tr/admin/dashboard.json b/public/language/tr/admin/dashboard.json index 8634d80734..f7bdad37b3 100644 --- a/public/language/tr/admin/dashboard.json +++ b/public/language/tr/admin/dashboard.json @@ -56,8 +56,8 @@ "active-users.total": "Genel Toplam", "active-users.connections": "Bağlantılar", - "anonymous-registered-users": "Anonim vs Kayıtlı Kullanıcılar", - "anonymous": "Anonim", + "guest-registered-users": "Misafir ve Kayıtlı Kullanıcılar", + "guest": "Misafir", "registered": "Kayıtlı", "user-presence": "Kullanıcı Durumları", @@ -68,6 +68,7 @@ "unread": "Okunmamış Konular Sayfasında", "high-presence-topics": "Öne Çıkan Başlıklar", + "popular-searches": "Popüler Aramalar", "graphs.page-views": "Sayfa Gösterimi", "graphs.page-views-registered": "Kayıtlı Kullanıcıların Sayfa Gösterimi", @@ -75,13 +76,14 @@ "graphs.page-views-bot": "Bot Sayfa Gösterimi", "graphs.unique-visitors": "Benzersiz Ziyaretçiler", "graphs.registered-users": "Kayıtlı Kullanıcılar", - "graphs.anonymous-users": "Anonim Kullanıcılar", + "graphs.guest-users": "Misafir Kullanıcılar", "last-restarted-by": "Son yeniden başlatma bilgisi", "no-users-browsing": "İnceleyen kullanıcı yok", "back-to-dashboard": "Yönetim Paneline geri dön", "details.no-users": "Seçilen zaman aralığında herhangi bir kullanıcı üye olmadı.", "details.no-topics": "Seçilen zaman aralığında herhangi bir başlık oluşturulmadı. ", + "details.no-searches": "Henüz arama yapılmadı", "details.no-logins": "Seçilen zaman aralığında herhangi bir giriş yapılmadı.", "details.logins-static": "NodeBB oturum kayıtlarını sadece %1 gün tutar, o nedenle aşağıdaki tablo sadece en yakın aktif oturumları listeler", "details.logins-login-time": "Giriş zamanı" diff --git a/public/language/tr/admin/development/info.json b/public/language/tr/admin/development/info.json index 2feb725487..4f80b51f8d 100644 --- a/public/language/tr/admin/development/info.json +++ b/public/language/tr/admin/development/info.json @@ -8,7 +8,11 @@ "nodejs": "nodejs", "online": "çevrimiçi", "git": "git", - "memory": "hafıza", + "process-memory": "process memory", + "system-memory": "system memory", + "used-memory-process": "Used memory by process", + "used-memory-os": "Used system memory", + "total-memory-os": "Total system memory", "load": "sistem yüklemesi", "cpu-usage": "cpu kullanımı", "uptime": "kesintisiz çalışma süresi", diff --git a/public/language/tr/admin/extend/rewards.json b/public/language/tr/admin/extend/rewards.json index ff2dca964a..282c929757 100644 --- a/public/language/tr/admin/extend/rewards.json +++ b/public/language/tr/admin/extend/rewards.json @@ -8,8 +8,6 @@ "delete": "Sil", "enable": "Etkinleştir", "disable": "Etkinsizleştir", - "control-panel": "Ödül Kontrol Paneli", - "new-reward": "Yeni Ödül Ekle", "alert.delete-success": "Ödül başarıyla silindi", "alert.no-inputs-found": "Usulsüz ödül - girdi bulunamadı!", diff --git a/public/language/tr/admin/general/dashboard.json b/public/language/tr/admin/general/dashboard.json deleted file mode 100644 index 9cadcf0391..0000000000 --- a/public/language/tr/admin/general/dashboard.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "forum-traffic": "Forum Trafiği", - "page-views": "Sayfa Gösterim Sayısı", - "unique-visitors": "Tekil Ziyaretçiler", - "new-users": "Yeni Kullanıcılar", - "posts": "İletiler", - "topics": "Başlıklar", - "page-views-seven": "Son 7 Gün", - "page-views-thirty": "Son 30 Gün", - "page-views-last-day": "Son 24 saat", - "page-views-custom": "Özel Tarih Aralığı", - "page-views-custom-start": "Başlangıç Tarihi", - "page-views-custom-end": "Bitiş Tarihi", - "page-views-custom-help": "İncelemek istediğiniz sayfa gösterim sayıları için bir tarih aralığı girin. Tarih seçeceğiniz panel görünmezse, kabul edilebilir format YYYY-AA-GG'dir.", - "page-views-custom-error": "Lütfen tarih aralığını geçerli formatta girin YYYY-MM-DD", - - "stats.yesterday": "Dün", - "stats.today": "Bugün", - "stats.last-week": "Geçen Hafta", - "stats.this-week": "Bu Hafta", - "stats.last-month": "Geçen Ay", - "stats.this-month": "Bu Ay", - "stats.all": "Tüm Zamanlar", - - "updates": "Güncellemeler", - "running-version": "NodeBB v%1 çalışıyor.", - "keep-updated": "En son güvenlik değişiklikleri ve hata düzeltmeleri için NodeBB'nin güncel olduğundan emin olun.", - "up-to-date": "

Sürümünüzgüncel

", - "upgrade-available": "

Yeni bir sürüm (v% 1) yayımlandı. NodeBB yükseltmeyi göz önünde bulundurun

", - "prerelease-upgrade-available": "

Bu, NodeBB'nin eski bir sürümü. Yeni bir sürüm (v% 1) yayımlandı. NodeBB’nizi yükseltmeyi düşünün.

", - "prerelease-warning": "

Bu, NodeBB'nin bir önsürüm versiyonudur. İstenmeyen hatalar oluşabilir.

", - "running-in-development": "Forum, geliştirici modunda çalışıyor. Forum, potansiyel güvenlik açıklarına açık olabilir; lütfen sistem yöneticinize başvurun.", - "latest-lookup-failed": "

En güncel kullanılabilecek NodeBB sürümü görüntülenemedi

", - - "notices": "Bildirimler", - "restart-not-required": "Yeniden başlatma gerekmiyor", - "restart-required": "Yeniden başlatma gerekiyor", - "search-plugin-installed": "Arama Eklentisi yüklendi", - "search-plugin-not-installed": "Arama Eklentisi yüklenmedi", - "search-plugin-tooltip": "Arama işlevselliğini etkinleştirmek için eklenti sayfasından bir arama eklentisi kurun", - - "control-panel": "Sistem Kontrol Paneli", - "rebuild-and-restart": "Yeniden oluştur & Yeniden Başlat", - "restart": "Yeniden Başlat", - "restart-warning": "NodeBB'yi yeniden oluşturmak (yapılandırmak) veya yeniden başlatmak, mevcut tüm bağlantıları birkaç saniye için sonlandırır.", - "restart-disabled": "NodeBB'nizi yeniden oluşturma ve yeniden başlatma devre dışı bırakıldı.", - "maintenance-mode": "Bakım Modu", - "maintenance-mode-title": "NodeBB için bakım modunu ayarlamak için buraya tıklayın", - "realtime-chart-updates": "Gerçek Zamanlı Grafik Güncellemeleri", - - "active-users": "Aktif Kullanıcılar", - "active-users.users": "Kullanıcılar", - "active-users.guests": "Ziyaretçiler", - "active-users.total": "Genel Toplam", - "active-users.connections": "Bağlantılar", - - "anonymous-registered-users": "Anonim vs Kayıtlı Kullanıcılar", - "anonymous": "Anonim", - "registered": "Kayıtlı", - - "user-presence": "Kullanıcı Durumları", - "on-categories": "Kategoriler Listesinde", - "reading-posts": "İleti Okuyor", - "browsing-topics": "Konuları İnceliyor", - "recent": "Yeni Konular Sayfasında", - "unread": "Okunmamış Konular Sayfasında", - - "high-presence-topics": "Öne Çıkan Başlıklar", - - "graphs.page-views": "Sayfa Gösterimi", - "graphs.page-views-registered": "Kayıtlı Kullanıcıların Sayfa Gösterimi", - "graphs.page-views-guest": "Ziyaretçilerin Sayfa Gösterimi", - "graphs.page-views-bot": "Bot Sayfa Gösterimi", - "graphs.unique-visitors": "Benzersiz Ziyaretçiler", - "graphs.registered-users": "Kayıtlı Kullanıcılar", - "graphs.anonymous-users": "Anonim Kullanıcılar", - "last-restarted-by": "Son yeniden başlatma bilgisi", - "no-users-browsing": "İnceleyen kullanıcı yok" -} diff --git a/public/language/tr/admin/general/homepage.json b/public/language/tr/admin/general/homepage.json deleted file mode 100644 index 3c3b08f9bd..0000000000 --- a/public/language/tr/admin/general/homepage.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "home-page": "Ana Sayfa", - "description": "Kullanıcıların, forumunuzun kök bağlantısına gittiğinde hangi sayfanın görüntüleneceğini seçin.", - "home-page-route": "Ana Sayfa Yolu", - "custom-route": "Özel Yol", - "allow-user-home-pages": "Kullanıcılara ana sayfalarını özelleştirmeleri için izin ver", - "home-page-title": "Ana sayfanın başlığı (varsayılan \"Ana Sayfa\")" -} \ No newline at end of file diff --git a/public/language/tr/admin/general/languages.json b/public/language/tr/admin/general/languages.json deleted file mode 100644 index 5ca8e3ec08..0000000000 --- a/public/language/tr/admin/general/languages.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "language-settings": "Dil Ayarları", - "description": "Varsayılan dil, forumunuzu ziyaret eden tüm kullanıcılar için dil ayarlarını belirler.
Kullanıcılar, bireysel olarak hesap ayarları sayfasında varsayılan dili geçersiz kılabilir.", - "default-language": "Varsayılan Dil", - "auto-detect": "Ziyaretçiler için dili otomatik tespit et" -} \ No newline at end of file diff --git a/public/language/tr/admin/general/navigation.json b/public/language/tr/admin/general/navigation.json deleted file mode 100644 index d961c240de..0000000000 --- a/public/language/tr/admin/general/navigation.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "icon": "İkon:", - "change-icon": "değiştir", - "route": "Yol:", - "tooltip": "Araç ipucu: ", - "text": "Yazı:", - "text-class": "Metin Sınıfı: opsiyonel", - "class": "Sınıf: opsiyonel", - "id": "ID: opsiyonel", - - "properties": "Özellikler:", - "groups": "Gruplar", - "open-new-window": "Yeni pencerede aç", - - "btn.delete": "Sil", - "btn.disable": "Etkinsizleştir", - "btn.enable": "Etkinleştir", - - "available-menu-items": "Kullanılabilir Menü Öğeleri", - "custom-route": "Özel Yol", - "core": "çekirdek", - "plugin": "eklenti" -} \ No newline at end of file diff --git a/public/language/tr/admin/general/social.json b/public/language/tr/admin/general/social.json deleted file mode 100644 index 5b186a82c4..0000000000 --- a/public/language/tr/admin/general/social.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "post-sharing": "İleti Paylaşımı", - "info-plugins-additional": "Eklentiler, paylaşımda bulunmak için ek sosyal ağlar ekleyebilir.", - "save-success": "İleti Paylaşım Ağları başarıyla kaydedildi!" -} \ No newline at end of file diff --git a/public/language/tr/admin/general/sounds.json b/public/language/tr/admin/general/sounds.json deleted file mode 100644 index e1d8a3c4a7..0000000000 --- a/public/language/tr/admin/general/sounds.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "notifications": "Bildirimler", - "chat-messages": "Sohbet Mesajları", - "play-sound": "Oynat", - "incoming-message": "Gelen İleti", - "outgoing-message": "Giden İleti", - "upload-new-sound": "Yeni Ses Yükle", - "saved": "Ayarlar Kaydedildi" -} \ No newline at end of file diff --git a/public/language/tr/admin/manage/categories.json b/public/language/tr/admin/manage/categories.json index 5d09d269e6..c15eb8d545 100644 --- a/public/language/tr/admin/manage/categories.json +++ b/public/language/tr/admin/manage/categories.json @@ -33,7 +33,7 @@ "analytics": "Analiz", "view-category": "Kategori Görüntüle", "set-order": "Bir sıra ayarla", - "set-order-help": "Setting the order of the category will move this category to that order and update the order of other categories as necessary. Minimum order is 1 which puts the category at the top.", + "set-order-help": "Kategorinin sırasını ayarlamak, bu kategoriyi o sıraya taşıyacak ve diğer kategorilerin sırasını güncelleyecektir. Kategoriyi en üste taşımak için 1 girin.", "select-category": "Kategori Seç", "set-parent-category": "Ana Kategori Ayarla", diff --git a/public/language/tr/admin/manage/digest.json b/public/language/tr/admin/manage/digest.json index 712494d3a0..5caee118ff 100644 --- a/public/language/tr/admin/manage/digest.json +++ b/public/language/tr/admin/manage/digest.json @@ -13,6 +13,7 @@ "resent-single": "El ile özet gönderimi tamamlandı", "resent-day": "Günlük özet yeniden gönderildi", "resent-week": "Haftalık özet yeniden gönderildi", + "resent-biweek": "İki Haftalık özeti yeniden gönder", "resent-month": "Aylık özet yeniden gönderildi", "null": "Hiçbir zaman", "manual-run": "El ile özet gönderimi:", diff --git a/public/language/tr/admin/manage/privileges.json b/public/language/tr/admin/manage/privileges.json index a915eb8d45..e641167ad8 100644 --- a/public/language/tr/admin/manage/privileges.json +++ b/public/language/tr/admin/manage/privileges.json @@ -25,7 +25,7 @@ "access-topics": "Başlıklara Eriş", "create-topics": "Başlık Oluştur", "reply-to-topics": "Başlığı Cevapla", - "schedule-topics": "Schedule Topics", + "schedule-topics": "Konuları Planla", "tag-topics": "Başlığı etiketle", "edit-posts": "İletiyi düzenle", "view-edit-history": "Düzenleme Geçmişini Görüntüle", @@ -51,13 +51,13 @@ "alert.saved": "Ayrıcalık değişiklikleri kaydedildi ve uygulandı", "alert.confirm-discard": "Ayrıcalık değişikliklerini iptal etmek istediğinize emin misiniz?", "alert.discarded": "Ayrıcalık değişiklikleri iptal edildi", - "alert.confirm-copyToAll": "Are you sure you wish to apply this set of %1 to all categories?", - "alert.confirm-copyToAllGroup": "Are you sure you wish to apply this group's set of %1 to all categories?", - "alert.confirm-copyToChildren": "Are you sure you wish to apply this set of %1 to all descendant (child) categories?", - "alert.confirm-copyToChildrenGroup": "Are you sure you wish to apply this group's set of %1 to all descendant (child) categories?", + "alert.confirm-copyToAll": "Bu %1 kategorisini tüm kategorilere uygulamak istediğinizden emin misiniz? ", + "alert.confirm-copyToAllGroup": "Bu grubun %1 kümesini tüm kategorilere uygulamak istediğinizden emin misiniz?", + "alert.confirm-copyToChildren": "Bu %1 kümesini tüm alt (alt) kategorilere uygulamak istediğinizden emin misiniz?", + "alert.confirm-copyToChildrenGroup": "Bu grubun %1 kümesini tüm alt (alt) kategorilere uygulamak istediğinizden emin misiniz?", "alert.no-undo": "Bu işlem geri alınamaz.", "alert.admin-warning": "Yöneticiler dolaylı olarak tüm ayrıcalıklara sahiptirler", - "alert.copyPrivilegesFrom-title": "Select a category to copy from", - "alert.copyPrivilegesFrom-warning": "This will copy %1 from the selected category.", - "alert.copyPrivilegesFromGroup-warning": "This will copy this group's set of %1 from the selected category." + "alert.copyPrivilegesFrom-title": "Kopyalamak için bir kategori seçin", + "alert.copyPrivilegesFrom-warning": "Seçilen kategoriden %1 kopyalayacaktır.", + "alert.copyPrivilegesFromGroup-warning": "Bu, seçilen kategoriden bu grubun %1 kümesini kopyalayacaktır." } \ No newline at end of file diff --git a/public/language/tr/admin/manage/uploads.json b/public/language/tr/admin/manage/uploads.json index 60fbb950ba..c3cd6a4574 100644 --- a/public/language/tr/admin/manage/uploads.json +++ b/public/language/tr/admin/manage/uploads.json @@ -6,6 +6,6 @@ "size/filecount": "Boyut / Dosya sayısı", "confirm-delete": "Bu dosyayı silmek istediğinden emin misin?", "filecount": "%1 dosya", - "new-folder": "New Folder", - "name-new-folder": "Enter a name for new the folder" + "new-folder": "Yeni Dosya", + "name-new-folder": "Yeni klasör için bir ad girin" } \ No newline at end of file diff --git a/public/language/tr/admin/manage/users.json b/public/language/tr/admin/manage/users.json index 308c7320cc..aa8b784cb6 100644 --- a/public/language/tr/admin/manage/users.json +++ b/public/language/tr/admin/manage/users.json @@ -1,6 +1,6 @@ { "users": "Kullanıcılar", - "edit": "Actions", + "edit": "Hareketler", "make-admin": "Yönetici Yap", "remove-admin": "Yöneticiliği Sil", "validate-email": "E-postayı Doğrula", @@ -47,6 +47,7 @@ "users.uid": "benzersiz id", "users.username": "kullanıcı adı", "users.email": "e-posta", + "users.no-email": "(e-mail yok)", "users.ip": "IP", "users.postcount": "ileti sayısı", "users.reputation": "itibar", diff --git a/public/language/tr/admin/menu.json b/public/language/tr/admin/menu.json index c9ce2dc287..821acc2ed1 100644 --- a/public/language/tr/admin/menu.json +++ b/public/language/tr/admin/menu.json @@ -4,6 +4,7 @@ "dashboard/logins": "Girişler", "dashboard/users": "Kullanıcılar", "dashboard/topics": "Başlıklar", + "dashboard/searches": "Aramalar", "section-general": "Genel", "section-manage": "Yönet", diff --git a/public/language/tr/admin/settings/email.json b/public/language/tr/admin/settings/email.json index 073c41320e..0dffd74edf 100644 --- a/public/language/tr/admin/settings/email.json +++ b/public/language/tr/admin/settings/email.json @@ -6,7 +6,7 @@ "from-help": "The from name to display in the email.", "smtp-transport": "SMTP Transport", - "smtp-transport.enabled": "Use an external email server to send emails", + "smtp-transport.enabled": "SMTP Aktarımını Etkinleştir", "smtp-transport-help": "You can select from a list of well-known services or enter a custom one.", "smtp-transport.service": "Select a service", "smtp-transport.service-custom": "Özel Servis", @@ -37,6 +37,10 @@ "subscriptions.hour": "Digest Hour", "subscriptions.hour-help": "Please enter a number representing the hour to send scheduled email digests (e.g. 0 for midnight, 17 for 5:00pm). Keep in mind that this is the hour according to the server itself, and may not exactly match your system clock.
The approximate server time is:
The next daily digest is scheduled to be sent ", "notifications.remove-images": "Görselleri e-posta bildirimlerinden kaldır", - "include-unverified-emails": "Send emails to recipients who have not explicitly confirmed their emails", - "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws." -} \ No newline at end of file + "require-email-address": "Yeni kullanıcıların bir e-posta adresi belirtmesini gerektir", + "require-email-address-warning": "Varsayılan olarak kullanıcıların bir e-posta adresi girmesi devre dışıdır. Bu seçeneğin etkinleştirilmesi, kayıt işlemine devam etmek için bir e-posta adresi girmeleri gerektiği anlamına gelir. Elbette bu kullanıcının gerçek bir e-posta adresi veya sahip olduğu bir adresi girmelerini sağlamaz.", + "include-unverified-emails": "E-postalarını onaylamayan alıcılara onay e-postası gönderin", + "include-unverified-warning": "Varsayılan olarak, hesaplarıyla ilişkili e-postaları olan kullanıcılar (Sosyal Login) zaten doğrulanmıştır, ancak durumun böyle olmadığı durumlar vardır (ör. Riski size ait olmak üzere bu ayarı etkinleştirin – doğrulanmamış adreslere e-posta göndermek, bölgesel istenmeyen posta önleme yasalarının ihlali olabilir.", + "prompt": "Kullanıcılardan e-postalarını girmelerini veya onaylamalarını isteyin", + "prompt-help": "Bir kullanıcının e-posta seti yoksa veya e-postası onaylanmadıysa ekranda bir uyarı gösterilir." +} diff --git a/public/language/tr/admin/settings/general.json b/public/language/tr/admin/settings/general.json index d0b248f56a..6bf2e17cdf 100644 --- a/public/language/tr/admin/settings/general.json +++ b/public/language/tr/admin/settings/general.json @@ -3,9 +3,9 @@ "title": "Site Başlığı", "title.short": "Başlık Kısaltması", "title.short-placeholder": "Eğer başlık kısaltması girilmediyse \"Site Başlığı\" kullanılacak", - "title.url": "Bağlantı", + "title.url": "Başlık Bağlantı URL'si", "title.url-placeholder": "Site başlığının URL adresi", - "title.url-help": "Başlık tıklandığında, kullanıcıları bu adrese gönder. Boş bırakılırsa, kullanıcı forum dizinine gönderilir.", + "title.url-help": "Başlık tıklandığında kullanıcıları bu adrese gönderin. Boş bırakılırsa, kullanıcı forum dizinine gönderilecektir. Not: Bu, e-postalarda vb. kullanılan harici URL değildir. Bu, config.json'daki url özelliği tarafından belirlenir.", "title.name": "Topluluk İsmi", "title.show-in-header": "Site Konusunu Başlık'ta Göster", "browser-title": "Tarayıcı Başlığı", @@ -20,9 +20,9 @@ "logo.image": "Görsel", "logo.image-placeholder": "Forum başlığında görüntülenecek bir logo yolu", "logo.upload": "Yükle", - "logo.url": "URL", + "logo.url": "Logonun Linki", "logo.url-placeholder": "Site Logo URL'si", - "logo.url-help": "Logo tıklandığında, kullanıcıları bu adrese gönder. Boş bırakılırsa, kullanıcı forum dizinine gönderilir.", + "logo.url-help": "Logoya tıklandığında kullanıcıları bu adrese gönderin. Boş bırakılırsa, kullanıcı forum dizinine gönderilecektir. Not: Bu, e-postalarda vb. kullanılan harici URL değildir. Bu, config.json'daki url özelliği tarafından belirlenir.", "logo.alt-text": "Alt Yazı", "log.alt-text-placeholder": "Erişilebilirlik için alternatif metin", "favicon": "Favicon", @@ -35,16 +35,16 @@ "maskable-icon.help": "Önerilen boyut ve format: 512x512, PNG formatı. If no maskable icon is specified, NodeBB will fall back to the Touch Icon.", "outgoing-links": "Harici Bağlantılar", "outgoing-links.warning-page": "Dışarı giden bağlantılar için uyarı sayfası kullan", - "search": "Search", - "search-default-in": "Search In", - "search-default-in-quick": "Quick Search In", - "search-default-sort-by": "Sort by", + "search": "Arama", + "search-default-in": "Araştır", + "search-default-in-quick": "Hızlı Arama", + "search-default-sort-by": "Göre sırala", "outgoing-links.whitelist": "Uyarı sayfasını atlamak için beyaz listeye eklenecek alan-adları", "site-colors": "Site Renk Metaverisi", "theme-color": "Tema rengi", "background-color": "Arkaplan rengi", "background-color-help": "Site PWA olarak kurulduğunda ekran arkaplanı olarak kullanılacak renk", - "undo-timeout": "Undo Timeout", - "undo-timeout-help": "Some operations such as moving topics will allow for the moderator to undo their action within a certain timeframe. Set to 0 to disable undo completely.", - "topic-tools": "Topic Tools" -} \ No newline at end of file + "undo-timeout": "Zaman Aşımını Geri Al", + "undo-timeout-help": "Konu taşıma gibi bazı işlemler, moderatörün belirli bir zaman dilimi içinde eylemlerini geri almasına olanak tanır. Tamamen geri almayı devre dışı bırakmak için 0'a ayarlayın.", + "topic-tools": "Konu Araçları" +} diff --git a/public/language/tr/admin/settings/navigation.json b/public/language/tr/admin/settings/navigation.json index d961c240de..ea0e2794e3 100644 --- a/public/language/tr/admin/settings/navigation.json +++ b/public/language/tr/admin/settings/navigation.json @@ -11,6 +11,8 @@ "properties": "Özellikler:", "groups": "Gruplar", "open-new-window": "Yeni pencerede aç", + "dropdown": "Dropdown", + "dropdown-placeholder": "Place your dropdown menu items below, ie:
<li><a href="https://myforum.com">Link 1</a></li>", "btn.delete": "Sil", "btn.disable": "Etkinsizleştir", @@ -20,4 +22,4 @@ "custom-route": "Özel Yol", "core": "çekirdek", "plugin": "eklenti" -} \ No newline at end of file +} diff --git a/public/language/tr/admin/settings/notifications.json b/public/language/tr/admin/settings/notifications.json index 6aa0a327c9..bf9502ff3d 100644 --- a/public/language/tr/admin/settings/notifications.json +++ b/public/language/tr/admin/settings/notifications.json @@ -2,5 +2,6 @@ "notifications": "Bildirimler", "welcome-notification": "Hoş Geldin Bildirimi", "welcome-notification-link": "Hoş Geldin Bildirimi Bağlantısı", - "welcome-notification-uid": "Kullanıcı Hoş Geldiniz Bildirimi (UID)" + "welcome-notification-uid": "Kullanıcı Hoş Geldiniz Bildirimi (UID)", + "post-queue-notification-uid": "İletisi kuyruğa alınan kullanıcı (UID)" } \ No newline at end of file diff --git a/public/language/tr/admin/settings/post.json b/public/language/tr/admin/settings/post.json index 8122ec2fd1..8428df7f38 100644 --- a/public/language/tr/admin/settings/post.json +++ b/public/language/tr/admin/settings/post.json @@ -40,6 +40,7 @@ "teaser.last-post": "Son – cevap yoksa orijinal gönderi de dahil olmak üzere en son gönderiyi gösterir.", "teaser.last-reply": "Son – cevap yoksa en son yanıtı veya \"Yanıt yok\" yertutucusunu gösterir.", "teaser.first": "İlk", + "showPostPreviewsOnHover": "Show a preview of posts when mouse overed", "unread": "Okunmamış Ayarları", "unread.cutoff": "Okunmamış gün sınırı", "unread.min-track-last": "Son okumayı takip etmeden önce konuya yapılan asgari gönderim", @@ -51,11 +52,14 @@ "signature.no-links": "İmzalarda linkleri devre dışı bırak", "signature.no-images": "İmzalarda resimleri devre dışı bırak", "signature.max-length": "Maksimum İmza Uzunluğu", - "composer": "Yazar Ayarları", + "composer": "Editör Ayarları", "composer-help": "Aşağıdaki ayarlar, yeni konular oluşturduklarında veya mevcut konulara cevap verdiklerinde kullanıcıların \n\t\t\t\tyazı alanının işlevselliğini ve / veya görünümünü yönetmelerini sağlar.", "composer.show-help": "\"Yardım\" sekmesini göster", "composer.enable-plugin-help": "Eklentilerin yardım sekmesine içerik eklemesine izin ver", "composer.custom-help": "Özel Yardım Metni", + "backlinks": "Geri bağlantılar", + "backlinks.enabled": "Konu geri bağlantılarını etkinleştir", + "backlinks.help": "Bir gönderi başka bir konuya atıfta bulunuyorsa, o anda referans verilen konuya gönderiye geri bir bağlantı eklenir.", "ip-tracking": "IP İzleme", "ip-tracking.each-post": "Her ileti için IP Adresini takip et", "enable-post-history": "Gönderi Geçmişini Etkinleştir" diff --git a/public/language/tr/admin/settings/reputation.json b/public/language/tr/admin/settings/reputation.json index 0109e0de78..cf5c2af702 100644 --- a/public/language/tr/admin/settings/reputation.json +++ b/public/language/tr/admin/settings/reputation.json @@ -17,6 +17,6 @@ "flags": "Şikayet Ayarları", "flags.limit-per-target": "Maksimum şikayet edilme sayısı", "flags.limit-per-target-placeholder": "Varsayılan: 0", - "flags.limit-per-target-help": "When a post or user is flagged multiple times, each additional flag is considered a "report" and added to the original flag. Set this option to a number other than zero to limit the number of reports an item can receive.", + "flags.limit-per-target-help": "Bir gönderi veya kullanıcı birden çok kez işaretlendiğinde, her ek işaret bir \"rapor\" olarak kabul edilir ve orijinal bayrağa eklenir. Bir öğenin alabileceği rapor sayısını sınırlamak için bu seçeneği sıfırdan farklı bir sayıya ayarlayın.", "flags.auto-resolve-on-ban": "Bir kullanıcı forumdan yasaklandığında otomatik olarak şikayetlerini çözülmüş say" } \ No newline at end of file diff --git a/public/language/tr/admin/settings/user.json b/public/language/tr/admin/settings/user.json index 35ef948bf5..ed09675de3 100644 --- a/public/language/tr/admin/settings/user.json +++ b/public/language/tr/admin/settings/user.json @@ -71,6 +71,7 @@ "digest-freq.off": "Kapalı", "digest-freq.daily": "Günlük", "digest-freq.weekly": "Haftalık", + "digest-freq.biweekly": "İki haftada bir", "digest-freq.monthly": "Aylık", "email-chat-notifs": "Çevrimiçi değilken gelen mesajları e-posta olarak gönder", "email-post-notif": "Abone olduğum konulara cevap gelince bana e-posta gönder", diff --git a/public/language/tr/email.json b/public/language/tr/email.json index d91faa6217..e7d587826a 100644 --- a/public/language/tr/email.json +++ b/public/language/tr/email.json @@ -6,9 +6,9 @@ "greeting_no_name": "Merhaba", "greeting_with_name": "Merhaba %1", "email.verify-your-email.subject": "Lütfen e-posta adresinizi doğrulayın", - "email.verify.text1": "You've requested that we change or confirm your email address", - "email.verify.text2": "For security purposes, we only change or confirm the email address on file once its ownership has been confirmed via email. If you did not request this, no action is required on your part.", - "email.verify.text3": "Once you confirm this email address, we will replace your current email address with this one (%1).", + "email.verify.text1": "E-posta adresinizi değiştirmemizi veya onaylamamızı istediniz", + "email.verify.text2": "Güvenlik amacıyla, kayıtlı e-posta adresini yalnızca sahipliği e-posta yoluyla onaylandıktan sonra değiştirir veya onaylarız. Bunu talep etmediyseniz, herhangi bir işlem yapmanız gerekmez.", + "email.verify.text3": "Bu e-posta adresini onayladığınızda, mevcut e-posta adresinizi bununla (%1) değiştireceğiz.", "welcome.text1": "Kaydolduğunuz için teşekkürler!", "welcome.text2": "Hesabınızı aktif hale getirmek için, kaydolduğunuz e-posta adresinin size ait olduğunu onaylamamız gerekiyor.", "welcome.text3": "Yönetici kayıt olma isteğinizi kabul etti. Kullanıcı adı/şifre ile giriş yapabilirsiniz.", diff --git a/public/language/tr/error.json b/public/language/tr/error.json index 18fea0e934..18c718f5b1 100644 --- a/public/language/tr/error.json +++ b/public/language/tr/error.json @@ -25,17 +25,18 @@ "invalid-event": "Geçersiz Aktivite: %1", "local-login-disabled": "Ayrıcalıklı-olmayan hesaplar için yerel giriş sistemi devre dışı bırakıldı.", "csrf-invalid": "Büyük olasılıkla süresi dolmuş oturum nedeniyle girişinizi geçersiz kıldık. Lütfen tekrar deneyiniz.", - "invalid-path": "Invalid path", - "folder-exists": "Folder exists", + "invalid-path": "Geçersiz yol", + "folder-exists": "Dosya mevcut", "invalid-pagination-value": "Geçersiz sayfa numarası girdiniz, en az %1 ve en fazla %2 olabilir", "username-taken": "Kullanıcı İsmi Alınmış", "email-taken": "E-posta Alınmış", - "email-nochange": "The email entered is the same as the email already on file.", + "email-nochange": "Girdiğiniz e-posta var olan e-posta ile aynı", "email-invited": "E-posta halihazırda davet edilmiş", - "email-not-confirmed": "Posting in some categories or topics is enabled once your email is confirmed, please click here to send a confirmation email.", + "email-not-confirmed": "E-postanız onaylandıktan sonra bazı kategorilerde veya konularda gönderiler etkinleştirilir, lütfen bir onay e-postası göndermek için burayı tıklayın.", "email-not-confirmed-chat": "E-postanız onaylanana kadar sohbet edemezsiniz, onaylamak için lütfen buraya tıklayın.", - "email-not-confirmed-email-sent": "E-posta adresiniz henüz onaylanmamış, lütfen onay e-postası için gelen kutunuzu kontrol ediniz. E-posta adresinizi onaylayana kadar foruma ileti gönderemeyeceksiniz veya sohbet edemeyeceksiniz!", - "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery. Please click here to enter an email.", + "email-not-confirmed-email-sent": "E-postanız henüz onaylanmadı, lütfen onay e-postası için gelen kutunuzu kontrol edin. E-postanız onaylanana kadar bazı kategorilerde gönderi paylaşamayabilir veya sohbet edemeyebilirsiniz.", + "no-email-to-confirm": "Hesabınızda bir e-posta grubu yok. Hesap kurtarma için bir e-posta gereklidir ve bazı kategorilerde sohbet etmek ve gönderi paylaşmak için gerekli olabilir. Bir e-posta girmek için lütfen burayı tıklayın.", + "user-doesnt-have-email": "\"%1\" kullanıcısı bir e-posta belirlememiş.", "email-confirm-failed": "E-posta adresinizi doğrulayamıyoruz. Lütfen daha sonra tekrar deneyin.", "confirm-email-already-sent": "E-posta onayı zaten gönderilmiş, yeni bir onay göndermek için lütfen %1 dakika bekleyin.", "sendmail-not-found": "Sendmail yürütülemedi, lüften indirildiğinden ve NodeBB kullanıcısı tarafından uygulanabilir olduğundan emin olun.", @@ -88,7 +89,7 @@ "not-enough-tags": "Yeterince etiket yok. Başlılar en az %1 etikete sahip olmalıdır", "too-many-tags": "Etiket sayısı çok fazla. Başlıklar en fazla %1 etikete sahip olabilir", "cant-use-system-tag": "Bu sistem etiketini kullanamazsınız.", - "cant-remove-system-tag": "You can not remove this system tag.", + "cant-remove-system-tag": "Bu sistem etiketini kaldıramazsınız.", "still-uploading": "Lütfen yüklemelerin bitmesini bekleyin.", "file-too-big": "İzin verilen en büyük dosya boyutu %1 kb - lütfen daha küçük bir dosya yükleyin", "guest-upload-disabled": "Ziyaretçilerin yükleme yapması devre dışı bırakıldı", @@ -103,6 +104,7 @@ "already-bookmarked": "Bu iletiyi yer imine ekledin", "already-unbookmarked": "Bu iletiyi yer iminden çıkardın", "cant-ban-other-admins": "Başka yöneticileri yasaklayamazsınız!", + "cant-make-banned-users-admin": "Yasaklanmış üyeleri yönetici yapamazsınız.", "cant-remove-last-admin": "Tek yönetici sizsiniz. Kendinizi adminlikten çıkarmadan önce başka bir kullanıcıyı admin olarak ekleyiniz", "account-deletion-disabled": "Hesap silme devre dışı bırakılmış", "cant-delete-admin": "Öncelikle yönetici izinlerini kaldırman gerekiyor.", @@ -178,10 +180,10 @@ "cant-kick-self": "Kendinizi gruptan atamazsınız.", "no-users-selected": "Seçili kullanıcı(lar) bulunamadı", "invalid-home-page-route": "Geçersiz anasayfa yolu", - "invalid-session": "Invalid Session", - "invalid-session-text": "It looks like your login session is no longer active. Please refresh this page.", - "session-mismatch": "Session Mismatch", - "session-mismatch-text": "It looks like your login session no longer matches with the server. Please refresh this page.", + "invalid-session": "Geçersiz Oturum", + "invalid-session-text": "Giriş oturumunuz aktif görünmüyor. Lütfen sayfayı yenileyiniz.", + "session-mismatch": "Oturum Uyuşmazlığı", + "session-mismatch-text": "Giriş oturumunuz sunucu ile eşleşmiyor. Lütfen sayfayı yenileyiniz.", "no-topics-selected": "Hiçbir başlık seçilmedi!", "cant-move-to-same-topic": "İletiyi aynı başlığa taşıyamazsın!", "cant-move-topic-to-same-category": "Başlığı bulunduğu kategoriye taşıyamazsınız!", @@ -196,14 +198,14 @@ "topic-event-unrecognized": "Konu aktivitesi '%1' tanımlanamadı", "cant-set-child-as-parent": "Alt-kategoriyi üst kategori olarak ayarlayamazsınız!", "cant-set-self-as-parent": "Kendisini üst kategori olarak ayarlayamazsınız!", - "api.master-token-no-uid": "A master token was received without a corresponding `_uid` in the request body", - "api.400": "Something was wrong with the request payload you passed in.", - "api.401": "A valid login session was not found. Please log in and try again.", - "api.403": "You are not authorised to make this call", - "api.404": "Invalid API call", - "api.426": "HTTPS is required for requests to the write api, please re-send your request via HTTPS", - "api.429": "You have made too many requests, please try again later", - "api.500": "An unexpected error was encountered while attempting to service your request.", - "api.501": "The route you are trying to call is not implemented yet, please try again tomorrow", - "api.503": "The route you are trying to call is not currently available due to a server configuration" + "api.master-token-no-uid": "İsteğe karşılık gelen bir \"_uid\" olmadan bir ana belirteç alındı", + "api.400": "İlettiğiniz istekle ilgili bir sorun vardı.", + "api.401": "Geçerli bir giriş oturumu bulunamadı. Lütfen yeniden giriş yapıp tekrar deneyin.", + "api.403": "Bu aramayı yapmak için yetkiniz yok", + "api.404": "Geçersiz API çağrısı", + "api.426": "Yazma API'sine yapılan istekler için HTTPS gereklidir, lütfen isteğinizi HTTPS aracılığıyla yeniden gönderin", + "api.429": "Fazla sayıda istekte bulundunuz, lütfen daha sonra tekrar deneyiniz.", + "api.500": "İsteğinizi gerçekleştirmeye çalışırken beklenmeyen bir hata ile karşılaşıldı.", + "api.501": "Aramaya çalıştığınız rota henüz uygulanmadı, lütfen yarın tekrar deneyin", + "api.503": "Aramaya çalıştığınız rota sunucu yapılandırması nedeniyle şu anda kullanılamıyor" } \ No newline at end of file diff --git a/public/language/tr/global.json b/public/language/tr/global.json index b8c2bbaff9..e8bab24f0d 100644 --- a/public/language/tr/global.json +++ b/public/language/tr/global.json @@ -70,7 +70,7 @@ "firstpost": "İlk ileti", "read_more": "daha fazla oku", "more": "Daha Fazla", - "none": "None", + "none": "Hiçbiri", "posted_ago_by_guest": "Ziyaretçi tarafından %1 yayımlandı", "posted_ago_by": "%2 tarafından %1 yayımlandı", "posted_ago": "%1 yayımlandı", diff --git a/public/language/tr/modules.json b/public/language/tr/modules.json index 563ef8c187..a35259de4b 100644 --- a/public/language/tr/modules.json +++ b/public/language/tr/modules.json @@ -54,7 +54,7 @@ "composer.formatting.strikethrough": "Üstüçizili", "composer.formatting.code": "Kod", "composer.formatting.link": "Bağlantı", - "composer.formatting.picture": "Görsel", + "composer.formatting.picture": "Görsel Linki", "composer.upload-picture": "Görsel Yükle", "composer.upload-file": "Dosya Yükle", "composer.zen_mode": "Tam ekran modu", diff --git a/public/language/tr/notifications.json b/public/language/tr/notifications.json index df1dc6ad41..efa0ce04c8 100644 --- a/public/language/tr/notifications.json +++ b/public/language/tr/notifications.json @@ -14,7 +14,7 @@ "topics": "Konular", "replies": "Yanıtlar", "chat": "Sohbetler", - "group-chat": "Group Chats", + "group-chat": "Grup Sohbetleri", "follows": "Takip Edilenler", "upvote": "Artı Oylananlar", "new-flags": "Yeni Şikayetler", diff --git a/public/language/tr/register.json b/public/language/tr/register.json index 242ac0af82..0d81d81ce0 100644 --- a/public/language/tr/register.json +++ b/public/language/tr/register.json @@ -20,9 +20,9 @@ "registration-added-to-queue": "Kayıt olma isteğiniz kabul listesine eklenmiştir. Yönetici tarafından kabul edildiğinizde e-posta alacaksınız.", "registration-queue-average-time": "Üyelik onayı için bekleyeceğiniz ortalama süre: %1 saat %2 dakika.", "registration-queue-auto-approve-time": "Forum üyeliğiniz %1 saat içerisinde tamamen aktifleştirilecektir. ", - "interstitial.intro": "We'd like some additional information in order to update your account…", - "interstitial.intro-new": "We'd like some additional information before we can create your account…", - "interstitial.errors-found": "Please review the entered information:", + "interstitial.intro": "Hesabınızı güncellemek için bazı ek bilgiler istiyoruz…", + "interstitial.intro-new": "Hesabınızı oluşturabilmemiz için önce bazı ek bilgiler istiyoruz…", + "interstitial.errors-found": "Lütfen girilen bilgileri inceleyin:", "gdpr_agree_data": "Bu web sitesinde kişisel bilgilerimin toplanmasını ve işlenmesini kabul ediyorum.", "gdpr_agree_email": "Bu web sitesinden özet ve bildirim e-postası almaya izin veriyorum.", "gdpr_consent_denied": "Bilgilerinizi toplamak/işlemek ve size e-posta göndermek için bu siteye onay vermelisiniz.", diff --git a/public/language/tr/top.json b/public/language/tr/top.json new file mode 100644 index 0000000000..012cabb42d --- /dev/null +++ b/public/language/tr/top.json @@ -0,0 +1,4 @@ +{ + "title": "Zirve", + "no_top_topics": "Zirve Konu Yok" +} \ No newline at end of file diff --git a/public/language/tr/topic.json b/public/language/tr/topic.json index 10c0c7322c..0fbcb45d5c 100644 --- a/public/language/tr/topic.json +++ b/public/language/tr/topic.json @@ -20,8 +20,8 @@ "login-to-view": "🔒 Görüntülemek için giriş yap!", "edit": "Düzenle", "delete": "Sil", - "delete-event": "Delete Event", - "delete-event-confirm": "Are you sure you want to delete this event?", + "delete-event": "Etkinliği Sil", + "delete-event-confirm": "Bu etkinliği silmek istediğinizden emin misiniz?", "purge": "Temizle", "restore": "Geri Getir", "move": "Taşı", @@ -45,8 +45,9 @@ "unpinned-by": "Sabitlenme kaldırıldı", "deleted-by": "Silindi", "restored-by": "Tekrar Yüklendi", - "moved-from-by": "Moved from %1 by", + "moved-from-by": "%1 'den taşındı", "queued-by": "İleti onay için sıraya alındı →", + "backlink": "başvurulan", "bookmark_instructions": "Bu konuda en son kaldığın yere dönmek için tıkla.", "flag-post": "Bu iletiyi şikayet et", "flag-user": "Bu kullanıcıyı şikayet et", @@ -138,6 +139,7 @@ "composer.handle_placeholder": "Kullanıcı adınızı buraya girin", "composer.discard": "Vazgeç", "composer.submit": "Gönder", + "composer.additional-options": "Ekstra seçenekler", "composer.schedule": "Konu Zamanla", "composer.replying_to": "Yanıtlanan Başlık: %1", "composer.new_topic": "Yeni Başlık", @@ -158,6 +160,7 @@ "newest_to_oldest": "En yeniden en eskiye", "most_votes": "En çok oylanan", "most_posts": "En çok ileti yazılan", + "most_views": "Çok Görüntülenen", "stale.title": "Bunun yerine yeni bir başlık oluşturun?", "stale.warning": "Yanıtlamak istediğiniz başlık oldukça eski. Bu başlığa referans oluşturacak yeni bir başlık oluşturmak ister misiniz?", "stale.create": "Yeni bir başlık oluştur", @@ -177,5 +180,5 @@ "timeago_earlier": "%1 önce", "first-post": "İlk ileti", "last-post": "Son ileti", - "post-quick-reply": "Post quick reply" + "post-quick-reply": "Hızlı yanıt gönder" } \ No newline at end of file diff --git a/public/language/tr/user.json b/public/language/tr/user.json index 284bb5d18e..dd6fef03b0 100644 --- a/public/language/tr/user.json +++ b/public/language/tr/user.json @@ -94,11 +94,12 @@ "digest_off": "Kapalı", "digest_daily": "Günlük", "digest_weekly": "Haftalık", + "digest_biweekly": "İki haftada bir", "digest_monthly": "Aylık", "has_no_follower": "Bu kullanıcının hiç takipçisi yok :(", "follows_no_one": "Bu kullanıcı kimseyi takip etmiyor :(", "has_no_posts": "Bu kullanıcı henüz herhangi bir ileti yazmamış :(", - "has_no_best_posts": "This user does not have any upvoted posts yet.", + "has_no_best_posts": "Bu kullanıcının herhangi bir gönderisi henüz olumlu oy almadı.", "has_no_topics": "Bu kullanıcı henüz hiçbir başlık açmamış :(", "has_no_watched_topics": "Bu kullanıcı henüz hiçbir başlığı takip etmiyor :(", "has_no_ignored_topics": "Bu kullanıcı henüz hiçbir başlığı yok saymamış.", @@ -182,7 +183,7 @@ "consent.export-uploads-success": "Yüklemeler aktarılmak üzere hazırlanıyor, işlem tamamlandığında bildirim alacaksınız!", "consent.export_posts": "Gönderileri Dışa Aktar (.csv)", "consent.export-posts-success": "İletiler aktarılmak üzere hazırlanıyor, işlem tamamlandığında bildirim alacaksınız!", - "emailUpdate.intro": "Please enter your email address below. This forum uses your email address for scheduled digest and notifications, as well as for account recovery in the event of a lost password.", - "emailUpdate.optional": "This field is optional. You are not obligated to provide your email address, but without a validated email, you will not be able to recover your account.", - "emailUpdate.change-instructions": "A confirmation email will be sent to the entered email address with a unique link. Accessing that link will confirm your ownership of the email address and it will become active on your account. At any time, you are able to update your email on file from within your account page." + "emailUpdate.intro": "Lütfen e-posta adresinizi aşağıya girin. Bu forum, e-posta adresinizi planlanmış özet ve bildirimler ile parolanın kaybolması durumunda hesap kurtarma için kullanır.", + "emailUpdate.optional": "Bu alan isteğe bağlıdır. E-posta adresinizi vermek zorunda değilsiniz, ancak doğrulanmış bir e-posta olmadan hesabınızı kurtaramazsınız.", + "emailUpdate.change-instructions": "Girilen e-posta adresine kişiye özel bir bağlantı içeren bir onay e-postası gönderilecektir. Bu bağlantıya erişmek, e-posta adresinin sahibi olduğunuzu onaylayacak ve hesabınızda etkin hale gelecektir. İstediğiniz zaman, hesap sayfanızdan kayıtlı e-postanızı güncelleyebilirsiniz." } \ No newline at end of file diff --git a/public/language/uk/admin/advanced/events.json b/public/language/uk/admin/advanced/events.json index 5298722da0..997d177a47 100644 --- a/public/language/uk/admin/advanced/events.json +++ b/public/language/uk/admin/advanced/events.json @@ -3,6 +3,7 @@ "no-events": "Подій немає", "control-panel": "Панель керування подіями", "delete-events": "Delete Events", + "confirm-delete-all-events": "Are you sure you want to delete all logged events?", "filters": "Filters", "filters-apply": "Apply Filters", "filter-type": "Event Type", diff --git a/public/language/uk/admin/dashboard.json b/public/language/uk/admin/dashboard.json index 02416fb058..62eee08e51 100644 --- a/public/language/uk/admin/dashboard.json +++ b/public/language/uk/admin/dashboard.json @@ -56,8 +56,8 @@ "active-users.total": "Разом", "active-users.connections": "З'єднання", - "anonymous-registered-users": "Аноніми проти Зареєстрованих", - "anonymous": "Аноніми", + "guest-registered-users": "Guest vs Registered Users", + "guest": "Guest", "registered": "Зареєстровані", "user-presence": "Присутність користувача", @@ -68,6 +68,7 @@ "unread": "Непрочитані", "high-presence-topics": "Теми з високою присутністю", + "popular-searches": "Popular Searches", "graphs.page-views": "Перегляди сторінок", "graphs.page-views-registered": "Page Views Registered", @@ -75,13 +76,14 @@ "graphs.page-views-bot": "Page Views Bot", "graphs.unique-visitors": "Унікальні відвідувачі", "graphs.registered-users": "Зареєстровані користувачі", - "graphs.anonymous-users": "Анонімні користувачі", + "graphs.guest-users": "Guest Users", "last-restarted-by": "Останнє перезавантаження", "no-users-browsing": "Немає користувачів онлайн", "back-to-dashboard": "Back to Dashboard", "details.no-users": "No users have joined within the selected timeframe", "details.no-topics": "No topics have been posted within the selected timeframe", + "details.no-searches": "No searches have been made yet", "details.no-logins": "No logins have been recorded within the selected timeframe", "details.logins-static": "NodeBB only saves session data for %1 days, and so this table below will only show the most recently active sessions", "details.logins-login-time": "Login Time" diff --git a/public/language/uk/admin/development/info.json b/public/language/uk/admin/development/info.json index ed7b89737e..c28dc7bb38 100644 --- a/public/language/uk/admin/development/info.json +++ b/public/language/uk/admin/development/info.json @@ -8,7 +8,11 @@ "nodejs": "nodejs", "online": "online", "git": "git", - "memory": "пам'ять", + "process-memory": "process memory", + "system-memory": "system memory", + "used-memory-process": "Used memory by process", + "used-memory-os": "Used system memory", + "total-memory-os": "Total system memory", "load": "system load", "cpu-usage": "cpu usage", "uptime": "uptime", diff --git a/public/language/uk/admin/extend/rewards.json b/public/language/uk/admin/extend/rewards.json index a3ba8e3b5c..47a0dd459d 100644 --- a/public/language/uk/admin/extend/rewards.json +++ b/public/language/uk/admin/extend/rewards.json @@ -8,8 +8,6 @@ "delete": "Видалити", "enable": "Увімкнути", "disable": "Вимкнути", - "control-panel": "Керування нагородами", - "new-reward": "Нова нагорода", "alert.delete-success": "Нагороду успішно видалено", "alert.no-inputs-found": "Невірна нагорода — поля пусті!", diff --git a/public/language/uk/admin/general/dashboard.json b/public/language/uk/admin/general/dashboard.json deleted file mode 100644 index e3096f077f..0000000000 --- a/public/language/uk/admin/general/dashboard.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "forum-traffic": "Трафік форуму", - "page-views": "Перегляди сторінок", - "unique-visitors": "Унікальні відвідувачі", - "new-users": "New Users", - "posts": "Пости", - "topics": "Теми", - "page-views-seven": "Останні 7 Днів", - "page-views-thirty": "Останні 30 Днів", - "page-views-last-day": "Останні 24 Години", - "page-views-custom": "Заданий Період", - "page-views-custom-start": "Початок Періоду", - "page-views-custom-end": "Кінець Періоду", - "page-views-custom-help": "Вкажіть календарний період, за який ви хочете побачити переглянуті сторінки. Якщо ви не можете використати селектор дат, допустимий формат дати YYYY-MM-DD", - "page-views-custom-error": "Будь-ласка вкажіть календарний період у форматі YYYY-MM-DD", - - "stats.yesterday": "Yesterday", - "stats.today": "Today", - "stats.last-week": "Last Week", - "stats.this-week": "This Week", - "stats.last-month": "Last Month", - "stats.this-month": "This Month", - "stats.all": "Увесь час", - - "updates": "Оновлень", - "running-version": "У вас працює NodeBB v%1.", - "keep-updated": "Регулярно перевіряйте, що ваш NodeBB знаходиться в актуальному стані, щоб мати останні патчі та виправлення.", - "up-to-date": "

Ваша версія актуальна

", - "upgrade-available": "

Було випущено нову версію (v%1). Подумайте про оновлення вашого NodeBB.

", - "prerelease-upgrade-available": "

Це застаріла до-релізна версія NodeBB. Було випущено нову версію (v%1). Подумайте про оновлення вашого NodeBB.

", - "prerelease-warning": "

Це пре-релізна версія NodeBB. Можуть виникати неочікувані помилки.

", - "running-in-development": "Форум працює в режимі розробки. Форум потенційно може бути незахищеним, будь-ласка повідомте вашого системного адміністратора.", - "latest-lookup-failed": "

Помилка при спробі перевірки останньої версії NodeBB

", - - "notices": "Сповіщення", - "restart-not-required": "Перезавантаження не потрібне", - "restart-required": "Потрібне перезавантаження", - "search-plugin-installed": "Пошуковий плагін встановлено", - "search-plugin-not-installed": "Пошуковий плагін не встановлено", - "search-plugin-tooltip": "Встановіть пошуковий плагін зі сторінки плагінів, що активувати пошуковий функціонал", - - "control-panel": "Керування системою", - "rebuild-and-restart": "Перебудувати & Перезавантажити", - "restart": "Перезавантажити", - "restart-warning": "Перебудування або перезапуск вашого NodeBB призведе до втрати всіх існуючих з'єднань протягом декількох секунд.", - "restart-disabled": "Перебудування та перезапуск вашого NodeBB вимкнено, оскільки ви, здається, не запускаєте його через відповідний демон.", - "maintenance-mode": "Режим обслуговування", - "maintenance-mode-title": "Натисніть тут, щоб налаштувати режим обслуговування NodeBB", - "realtime-chart-updates": "Оновлення графіків в реальному часі", - - "active-users": "Активні користувачі", - "active-users.users": "Користувачі", - "active-users.guests": "Гості", - "active-users.total": "Разом", - "active-users.connections": "З'єднання", - - "anonymous-registered-users": "Аноніми проти Зареєстрованих", - "anonymous": "Аноніми", - "registered": "Зареєстровані", - - "user-presence": "Присутність користувача", - "on-categories": "На списку категорій", - "reading-posts": "Читають пости", - "browsing-topics": "Переглядають теми", - "recent": "Недавні", - "unread": "Непрочитані", - - "high-presence-topics": "Теми з високою присутністю", - - "graphs.page-views": "Перегляди сторінок", - "graphs.page-views-registered": "Page Views Registered", - "graphs.page-views-guest": "Page Views Guest", - "graphs.page-views-bot": "Page Views Bot", - "graphs.unique-visitors": "Унікальні відвідувачі", - "graphs.registered-users": "Зареєстровані користувачі", - "graphs.anonymous-users": "Анонімні користувачі", - "last-restarted-by": "Останнє перезавантаження", - "no-users-browsing": "Немає користувачів онлайн" -} diff --git a/public/language/uk/admin/general/homepage.json b/public/language/uk/admin/general/homepage.json deleted file mode 100644 index f0b146ca8f..0000000000 --- a/public/language/uk/admin/general/homepage.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "home-page": "Головна сторінка", - "description": "Вкажіть яку сторінку показувати коли користувач переходить на корньовий URL форуму.", - "home-page-route": "Шлях головної сторінки", - "custom-route": "Користувацький шлях", - "allow-user-home-pages": "Дозволити користувачам власні сторінки", - "home-page-title": "Назва домашньої сторінки (за замовчуванням \"Домашня сторінка\")" -} \ No newline at end of file diff --git a/public/language/uk/admin/general/languages.json b/public/language/uk/admin/general/languages.json deleted file mode 100644 index 7f2118d887..0000000000 --- a/public/language/uk/admin/general/languages.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "language-settings": "Налаштування мов", - "description": "Мова за замовчуванням задає мову для всіх користувачів, що відвідують форум.
Кожен користувач може перевизначити мову в своїх налаштуваннях акаунта.", - "default-language": "Мова за замовчуванням", - "auto-detect": "Автоматично визначати мову для гостей" -} \ No newline at end of file diff --git a/public/language/uk/admin/general/navigation.json b/public/language/uk/admin/general/navigation.json deleted file mode 100644 index 7e80dd4304..0000000000 --- a/public/language/uk/admin/general/navigation.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "icon": "Іконка:", - "change-icon": "змінити", - "route": "Шлях:", - "tooltip": "Підказка:", - "text": "Текст:", - "text-class": "Класс тексту: необов'язковий", - "class": "Class: optional", - "id": "ID: необов'язковий", - - "properties": "Властивості:", - "groups": "Groups:", - "open-new-window": "Відкривати у новому вікні", - - "btn.delete": "Видалити", - "btn.disable": "Вимкнути", - "btn.enable": "Увімкнути", - - "available-menu-items": "Доступні пункти меню", - "custom-route": "Користувацький шлях", - "core": "ядро", - "plugin": "плагін" -} \ No newline at end of file diff --git a/public/language/uk/admin/general/social.json b/public/language/uk/admin/general/social.json deleted file mode 100644 index ce739d8b13..0000000000 --- a/public/language/uk/admin/general/social.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "post-sharing": "Поширення постів", - "info-plugins-additional": "Плагіни можуть доповнювати набір доступних мереж для поширення постів", - "save-success": "Набір мереж для поширення постів успішно збережено!" -} \ No newline at end of file diff --git a/public/language/uk/admin/general/sounds.json b/public/language/uk/admin/general/sounds.json deleted file mode 100644 index 17214deba0..0000000000 --- a/public/language/uk/admin/general/sounds.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "notifications": "Сповіщення", - "chat-messages": "Повідомлення чату", - "play-sound": "Грати", - "incoming-message": "Вхідне повідомлення", - "outgoing-message": "Вихідне повідомлення", - "upload-new-sound": "Завантажити новий звук", - "saved": "Налаштування зберережні" -} \ No newline at end of file diff --git a/public/language/uk/admin/manage/digest.json b/public/language/uk/admin/manage/digest.json index 8f3661698a..38c634d1f6 100644 --- a/public/language/uk/admin/manage/digest.json +++ b/public/language/uk/admin/manage/digest.json @@ -13,6 +13,7 @@ "resent-single": "Manual digest resend completed", "resent-day": "Daily digest resent", "resent-week": "Weekly digest resent", + "resent-biweek": "Bi-Weekly digest resent", "resent-month": "Monthly digest resent", "null": "Never", "manual-run": "Manual digest run:", diff --git a/public/language/uk/admin/manage/users.json b/public/language/uk/admin/manage/users.json index 7fd325c7d2..afef4cee35 100644 --- a/public/language/uk/admin/manage/users.json +++ b/public/language/uk/admin/manage/users.json @@ -47,6 +47,7 @@ "users.uid": "uid", "users.username": "ім'я", "users.email": "email", + "users.no-email": "(no email)", "users.ip": "IP", "users.postcount": "постів", "users.reputation": "репутація", diff --git a/public/language/uk/admin/menu.json b/public/language/uk/admin/menu.json index 4da4abf0fc..7dd7c875fb 100644 --- a/public/language/uk/admin/menu.json +++ b/public/language/uk/admin/menu.json @@ -4,6 +4,7 @@ "dashboard/logins": "Logins", "dashboard/users": "Users", "dashboard/topics": "Topics", + "dashboard/searches": "Searches", "section-general": "Загальні", "section-manage": "Керування", diff --git a/public/language/uk/admin/settings/email.json b/public/language/uk/admin/settings/email.json index 2f453ca312..912ca7528a 100644 --- a/public/language/uk/admin/settings/email.json +++ b/public/language/uk/admin/settings/email.json @@ -6,7 +6,7 @@ "from-help": "Ім'я відправника, що буде показано в електронних листах", "smtp-transport": "Протокол SMTP", - "smtp-transport.enabled": "Використовувати зовнішній поштовий сервер для відправлення повідомлень", + "smtp-transport.enabled": "Enable SMTP Transport", "smtp-transport-help": "Ви можете обрати поштовий сервіс зі списку або використати ваш власний сервіс.", "smtp-transport.service": "Оберіть сервіс", "smtp-transport.service-custom": "Власний Сервіс", @@ -37,6 +37,10 @@ "subscriptions.hour": "Година дайджесту", "subscriptions.hour-help": "Вкажіть, будь ласка, годину о котрій кожного дня буде надсилатися дайджест (наприклад 0 — це північ, а 17 — п'ята година вечора). Зверніть увагу, що година визначається згідно налаштувань сервера і може не співпадати з часом вашого комп'ютера.
Приблизний час сервера:
Наступний дайджест заплановано до відправки ", "notifications.remove-images": "Remove images from email notifications", + "require-email-address": "Require new users to specify an email address", + "require-email-address-warning": "By default, users can opt-out of entering an email address. Enabling this option means they have to enter an email address in order to proceed with registration. It does not ensure user will enter a real email address, nor even an address they own.", "include-unverified-emails": "Send emails to recipients who have not explicitly confirmed their emails", - "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws." -} \ No newline at end of file + "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws.", + "prompt": "Prompt users to enter or confirm their emails", + "prompt-help": "If a user does not have an email set, or their email is not confirmed, a warning will be shown on screen." +} diff --git a/public/language/uk/admin/settings/general.json b/public/language/uk/admin/settings/general.json index 27ffb53f48..964027ac2b 100644 --- a/public/language/uk/admin/settings/general.json +++ b/public/language/uk/admin/settings/general.json @@ -3,9 +3,9 @@ "title": "Назва сайту", "title.short": "Short Title", "title.short-placeholder": "If no short title is specified, the site title will be used", - "title.url": "URL", + "title.url": "Title Link URL", "title.url-placeholder": "URL заголовку сайту", - "title.url-help": "По кліку на заголовок, спрямовувати користувача за цією адресою. Якщо залишити порожнім, користувач буде спрямований в корінь форуму.", + "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "title.name": "Назва вашої спільноти", "title.show-in-header": "Показувати заголовок сайту в шапці", "browser-title": "Заголовок браузера", @@ -20,9 +20,9 @@ "logo.image": "Зображення", "logo.image-placeholder": "Шлях до логотипу для відображення в шапці форуму", "logo.upload": "Завантажити", - "logo.url": "URL", + "logo.url": "Logo Link URL", "logo.url-placeholder": "URL логотипу сайту", - "logo.url-help": "По кліку на логотип, направляти користувача за цією адресою. Якщо залишити пустим, користувач буде направлений в корінь сайту.", + "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "logo.alt-text": "Текст alt", "log.alt-text-placeholder": "Альтернативний текст для доступності", "favicon": "Фавіконка", @@ -47,4 +47,4 @@ "undo-timeout": "Undo Timeout", "undo-timeout-help": "Some operations such as moving topics will allow for the moderator to undo their action within a certain timeframe. Set to 0 to disable undo completely.", "topic-tools": "Topic Tools" -} \ No newline at end of file +} diff --git a/public/language/uk/admin/settings/navigation.json b/public/language/uk/admin/settings/navigation.json index 7e80dd4304..8d6f6fad19 100644 --- a/public/language/uk/admin/settings/navigation.json +++ b/public/language/uk/admin/settings/navigation.json @@ -11,6 +11,8 @@ "properties": "Властивості:", "groups": "Groups:", "open-new-window": "Відкривати у новому вікні", + "dropdown": "Dropdown", + "dropdown-placeholder": "Place your dropdown menu items below, ie:
<li><a href="https://myforum.com">Link 1</a></li>", "btn.delete": "Видалити", "btn.disable": "Вимкнути", @@ -20,4 +22,4 @@ "custom-route": "Користувацький шлях", "core": "ядро", "plugin": "плагін" -} \ No newline at end of file +} diff --git a/public/language/uk/admin/settings/notifications.json b/public/language/uk/admin/settings/notifications.json index 55f71dae63..2322bf6836 100644 --- a/public/language/uk/admin/settings/notifications.json +++ b/public/language/uk/admin/settings/notifications.json @@ -2,5 +2,6 @@ "notifications": "Сповіщення", "welcome-notification": "Сповіщення \"Ласкаво просимо\"", "welcome-notification-link": "Посилання для сповіщення \"Ласкаво просимо\"", - "welcome-notification-uid": "Сповіщення \"Ласкаво просимо\" для користувача (UID)" + "welcome-notification-uid": "Сповіщення \"Ласкаво просимо\" для користувача (UID)", + "post-queue-notification-uid": "Post Queue User (UID)" } \ No newline at end of file diff --git a/public/language/uk/admin/settings/post.json b/public/language/uk/admin/settings/post.json index f59375f68f..ba23e93c33 100644 --- a/public/language/uk/admin/settings/post.json +++ b/public/language/uk/admin/settings/post.json @@ -40,6 +40,7 @@ "teaser.last-post": "Останній — показувати останній пост або перший, якщо немає відповідей", "teaser.last-reply": "Останній — показувати останній пост або \"Немає відповідей\", якщо немає відповідей", "teaser.first": "Перший", + "showPostPreviewsOnHover": "Show a preview of posts when mouse overed", "unread": "Налаштування непрочитаних", "unread.cutoff": "За скільки днів показувати непрочитані", "unread.min-track-last": "Мінімальна кількість постів у темі перш ніж відслідковувати останні прочитані", @@ -56,6 +57,9 @@ "composer.show-help": "Показувати вкладку \"Довідка\"", "composer.enable-plugin-help": "Дозволити плагінам додавати зміст довідки", "composer.custom-help": "Користувацький текст довідки", + "backlinks": "Backlinks", + "backlinks.enabled": "Enable topic backlinks", + "backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.", "ip-tracking": "Відстеження IP", "ip-tracking.each-post": "Відстежувати IP адреси для кожного посту", "enable-post-history": "Увімкнути Історію Постів" diff --git a/public/language/uk/admin/settings/user.json b/public/language/uk/admin/settings/user.json index 7594e7cee7..e2036b2820 100644 --- a/public/language/uk/admin/settings/user.json +++ b/public/language/uk/admin/settings/user.json @@ -71,6 +71,7 @@ "digest-freq.off": "Ніколи", "digest-freq.daily": "Щоденно", "digest-freq.weekly": "Щотижнево", + "digest-freq.biweekly": "Bi-Weekly", "digest-freq.monthly": "Щомісячно", "email-chat-notifs": "Надсилати листа, коли я не в мережі, якщо приходить чат повідомлення", "email-post-notif": "Надсилати листа, коли в темах на які я підписаний з'являються відповіді", diff --git a/public/language/uk/error.json b/public/language/uk/error.json index c70a32ab1e..eae362e14b 100644 --- a/public/language/uk/error.json +++ b/public/language/uk/error.json @@ -34,8 +34,9 @@ "email-invited": "Email was already invited", "email-not-confirmed": "Posting in some categories or topics is enabled once your email is confirmed, please click here to send a confirmation email.", "email-not-confirmed-chat": "Ви не можете користуватися чатом поки ваша електронна пошта не буде підтверджена, натисніть тут, щоб це зробити.", - "email-not-confirmed-email-sent": "Ваша електронна адреса ще не була підтверджена, будь-ласка перевірте свою поштову скриньку. Ви не зможете постити або чатитись до того як ваш емейл підтверджено.", - "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery. Please click here to enter an email.", + "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You may not be able to post in some categories or chat until your email is confirmed.", + "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery, and may be necessary for chatting and posting in some categories. Please click here to enter an email.", + "user-doesnt-have-email": "User \"%1\" does not have an email set.", "email-confirm-failed": "Ми не можемо підтвердити вашу електронну пошту, будь ласка, спробуйте пізніше.", "confirm-email-already-sent": "Підтвердження по електронній пошті вже було надіслано, зачекайте, будь ласка, %1 хвилин(и), щоб відправити ще одне. ", "sendmail-not-found": "Виконуваний файл sendmail не знайдено, переконайтесь, будь ласка, що його встановлено та що він виконується власником процесу NodeBB.", @@ -103,6 +104,7 @@ "already-bookmarked": "Ви вже додали цей пост собі в закладки", "already-unbookmarked": "Ви вже видалили цей пост із закладок", "cant-ban-other-admins": "Ви не можете банити інших адмінів!", + "cant-make-banned-users-admin": "You can't make banned users admin.", "cant-remove-last-admin": "Ви єдиний адміністратор. Додайте іншого користувача в якості адміністратора перш ніж знімати з себе ці обов'язки.", "account-deletion-disabled": "Account deletion is disabled", "cant-delete-admin": "Зніміть обов'язки адміністратора з цього акаунту перш ніж видаляти його.", diff --git a/public/language/uk/flags.json b/public/language/uk/flags.json new file mode 100644 index 0000000000..44502005fc --- /dev/null +++ b/public/language/uk/flags.json @@ -0,0 +1,85 @@ +{ + "state": "Стан", + "reports": "Reports", + "first-reported": "First Reported", + "no-flags": "Ура! Скарг немає.", + "assignee": "Виконавець", + "update": "Оновлення", + "updated": "Оновлено", + "resolved": "Resolved", + "target-purged": "Зміст на який подана ця скарга було стерто і він більше недоступний.", + + "graph-label": "Щоденні прапорці", + "quick-filters": "Швидкі фільтри", + "filter-active": "У цьому списку скарг активовано один або більше фільтрів", + "filter-reset": "Видалити фільтри", + "filters": "Параметри фільтру", + "filter-reporterId": "UID скаржника", + "filter-targetUid": "UID оскаржуваного", + "filter-type": "Тип скарги", + "filter-type-all": "Увесь зміст", + "filter-type-post": "Пост", + "filter-type-user": "Користувач", + "filter-state": "Стан", + "filter-assignee": "UID виконавця", + "filter-cid": "Категорія", + "filter-quick-mine": "Призначені мені", + "filter-cid-all": "Всі категорії", + "apply-filters": "Примінити фільтри", + "more-filters": "More Filters", + "fewer-filters": "Fewer Filters", + + "quick-actions": "Quick Actions", + "flagged-user": "Користувач зі скаргою", + "view-profile": "Переглянути профіль", + "start-new-chat": "Почати новий чат", + "go-to-target": "Переглянути ціль скарги", + "assign-to-me": "Assign To Me", + "delete-post": "Delete Post", + "purge-post": "Purge Post", + "restore-post": "Restore Post", + + "user-view": "Переглянути профіль", + "user-edit": "Редагувати профіль", + + "notes": "Коментарі до скарги", + "add-note": "Додати коментар", + "no-notes": "Немає загальних коментарів.", + "delete-note-confirm": "Are you sure you want to delete this flag note?", + "note-added": "Коментар додано", + "note-deleted": "Note Deleted", + + "history": "Account & Flag History", + "no-history": "Немає історії скарг.", + + "state-all": "Всі стани", + "state-open": "Нова/Відкрита", + "state-wip": "У роботі", + "state-resolved": "Вирішена", + "state-rejected": "Відхилена", + "no-assignee": "Не призначена", + + "sort": "Sort by", + "sort-newest": "Newest first", + "sort-oldest": "Oldest first", + "sort-reports": "Most reports", + "sort-all": "All flag types...", + "sort-posts-only": "Posts only...", + "sort-downvotes": "Most downvotes", + "sort-upvotes": "Most upvotes", + "sort-replies": "Most replies", + + "modal-title": "Report Content", + "modal-body": "Будь ласка, вкажіть причину скарги на %1 %2 або використайте одну з відповідних швидких кнопок.", + "modal-reason-spam": "Спам", + "modal-reason-offensive": "Образа", + "modal-reason-other": "Інше (зазначте нижче)", + "modal-reason-custom": "Причина скарги на цей вміст...", + "modal-submit": "Надіслати скаргу", + "modal-submit-success": "Скарга на цей зміст надіслана модератору.", + + "bulk-actions": "Bulk Actions", + "bulk-resolve": "Resolve Flag(s)", + "bulk-success": "%1 flags updated", + "flagged-timeago-readable": "Flagged (%2)" +} \ No newline at end of file diff --git a/public/language/uk/modules.json b/public/language/uk/modules.json index 44bdbf848b..ef5bff16fe 100644 --- a/public/language/uk/modules.json +++ b/public/language/uk/modules.json @@ -54,7 +54,7 @@ "composer.formatting.strikethrough": "Закреслений", "composer.formatting.code": "Код", "composer.formatting.link": "Посилання", - "composer.formatting.picture": "Зображення", + "composer.formatting.picture": "Image Link", "composer.upload-picture": "Завантажити зображення", "composer.upload-file": "Завантажити файл", "composer.zen_mode": "Режим Дзен", diff --git a/public/language/uk/top.json b/public/language/uk/top.json new file mode 100644 index 0000000000..b8a05bfa5f --- /dev/null +++ b/public/language/uk/top.json @@ -0,0 +1,4 @@ +{ + "title": "Top", + "no_top_topics": "No top topics" +} \ No newline at end of file diff --git a/public/language/uk/topic.json b/public/language/uk/topic.json index aa7b3e1d9e..8fcb386cd0 100644 --- a/public/language/uk/topic.json +++ b/public/language/uk/topic.json @@ -47,6 +47,7 @@ "restored-by": "Restored by", "moved-from-by": "Moved from %1 by", "queued-by": "Post queued for approval →", + "backlink": "Referenced by", "bookmark_instructions": "Натисніть тут, щоб повернутися до останнього прочитаного посту у цій темі.", "flag-post": "Flag this post", "flag-user": "Flag this user", @@ -138,6 +139,7 @@ "composer.handle_placeholder": "Enter your name/handle here", "composer.discard": "Скасувати", "composer.submit": "Надіслати", + "composer.additional-options": "Additional Options", "composer.schedule": "Schedule", "composer.replying_to": "Відповідь для %1", "composer.new_topic": "Cтворити тему", @@ -158,6 +160,7 @@ "newest_to_oldest": "Нові > Старі", "most_votes": "Найбільше Голосів", "most_posts": "Найбільше Постів", + "most_views": "Most Views", "stale.title": "Створити натомість нову тему?", "stale.warning": "Тема на котру ви відповідаєте досить стара. Не бажаєте натомість створити новую тему і зіслатися на цю у вашій відповіді?", "stale.create": "Так, створити нову тему", diff --git a/public/language/uk/user.json b/public/language/uk/user.json index 3e19831fed..4b3c3b5512 100644 --- a/public/language/uk/user.json +++ b/public/language/uk/user.json @@ -94,6 +94,7 @@ "digest_off": "Ніколи", "digest_daily": "Щоденно", "digest_weekly": "Щотижнево", + "digest_biweekly": "Bi-Weekly", "digest_monthly": "Щомісячно", "has_no_follower": "Цей користувач не має відстежувачів :(", "follows_no_one": "Цей користувач нікого не відстежує :(", diff --git a/public/language/vi/admin/advanced/events.json b/public/language/vi/admin/advanced/events.json index f51fd591d0..1ac68d471b 100644 --- a/public/language/vi/admin/advanced/events.json +++ b/public/language/vi/admin/advanced/events.json @@ -3,6 +3,7 @@ "no-events": "Không có sự kiện", "control-panel": "Bảng Điều Khiển Sự Kiện", "delete-events": "Xóa Sự Kiện", + "confirm-delete-all-events": "Bạn có chắc chắn muốn xóa tất cả các sự kiện đã ghi không?", "filters": "Bộ lọc", "filters-apply": "Áp Dụng Bộ Lọc", "filter-type": "Loại Sự Kiện", diff --git a/public/language/vi/admin/dashboard.json b/public/language/vi/admin/dashboard.json index 9b81d59690..7ab9f47374 100644 --- a/public/language/vi/admin/dashboard.json +++ b/public/language/vi/admin/dashboard.json @@ -56,8 +56,8 @@ "active-users.total": "Tổng", "active-users.connections": "Kết nối", - "anonymous-registered-users": "Người Dùng Ẩn Danh và Đã Đăng Ký", - "anonymous": "Ẩn Danh", + "guest-registered-users": "Khách vs Người dùng đã đăng ký", + "guest": "Khách", "registered": "Đã đăng ký", "user-presence": "Người Dùng Có Mặt", @@ -68,6 +68,7 @@ "unread": "Chưa đọc", "high-presence-topics": "Chủ Đề Hiện Diện Cao", + "popular-searches": "Tìm kiếm Phổ biến", "graphs.page-views": "Xem Trang", "graphs.page-views-registered": "Đã Đăng Ký Xem Trang", @@ -75,13 +76,14 @@ "graphs.page-views-bot": "Bot Xem Trang", "graphs.unique-visitors": "Khách Truy Cập Duy Nhất", "graphs.registered-users": "Thành Viên Chính Thức", - "graphs.anonymous-users": "Người Dùng Ẩn Danh", + "graphs.guest-users": "Người dùng khách", "last-restarted-by": "Khởi động lại lần cuối bởi", "no-users-browsing": "Người không xem bài", "back-to-dashboard": "Quay lại Bảng điều khiển", "details.no-users": "Không có người dùng nào tham gia trong khung thời gian đã chọn", "details.no-topics": "Không có chủ đề nào được đăng trong khung thời gian đã chọn", + "details.no-searches": "Chưa có tìm kiếm nào", "details.no-logins": "Không có thông tin đăng nhập nào được ghi lại trong khung thời gian đã chọn", "details.logins-static": "NodeBB chỉ lưu dữ liệu phiên trong %1 ngày và do đó, bảng này bên dưới sẽ chỉ hiển thị các phiên hoạt động gần đây nhất", "details.logins-login-time": "Thời gian đăng nhập" diff --git a/public/language/vi/admin/development/info.json b/public/language/vi/admin/development/info.json index 3b14058737..4a76411c21 100644 --- a/public/language/vi/admin/development/info.json +++ b/public/language/vi/admin/development/info.json @@ -8,7 +8,11 @@ "nodejs": "nodejs", "online": "trực tuyến", "git": "git", - "memory": "bộ nhớ", + "process-memory": "xử lý bộ nhớ", + "system-memory": "bộ nhớ hệ thống", + "used-memory-process": "Đã sử dụng bộ nhớ theo quy trình", + "used-memory-os": "Bộ nhớ hệ thống đã sử dụng", + "total-memory-os": "Tổng bộ nhớ hệ thống", "load": "tải hệ thống", "cpu-usage": "sử dụng cpu", "uptime": "thời gian hoạt động", diff --git a/public/language/vi/admin/extend/rewards.json b/public/language/vi/admin/extend/rewards.json index 89c006fe45..d7a803ae6a 100644 --- a/public/language/vi/admin/extend/rewards.json +++ b/public/language/vi/admin/extend/rewards.json @@ -8,8 +8,6 @@ "delete": "Xóa", "enable": "Bật", "disable": "Tắt", - "control-panel": "Kiểm Soát Phần Thưởng", - "new-reward": "Phần Thưởng Mới", "alert.delete-success": "Đã xóa thành công phần thưởng", "alert.no-inputs-found": "Phần thưởng không hợp lệ - không tìm thấy đầu vào!", diff --git a/public/language/vi/admin/general/dashboard.json b/public/language/vi/admin/general/dashboard.json deleted file mode 100644 index 0cbc260f5e..0000000000 --- a/public/language/vi/admin/general/dashboard.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "forum-traffic": "Lưu lượng truy cập", - "page-views": "Lượt xem trang", - "unique-visitors": "Khách truy cập duy nhất", - "new-users": "Người dùng mới", - "posts": "Bài viết", - "topics": "Chủ đề", - "page-views-seven": "7 ngày trước", - "page-views-thirty": "30 ngày trước", - "page-views-last-day": "24 giờ trước", - "page-views-custom": "Tùy chỉnh phạm vi ngày", - "page-views-custom-start": "Phạm vi bắt đầu", - "page-views-custom-end": "Phạm vi kết thúc", - "page-views-custom-help": "Nhập phạm vi ngày của lượt xem trang bạn muốn xem. Nếu không có bộ chọn ngày, định dạng được chấp nhận là YYYY-MM-DD", - "page-views-custom-error": "Vui lòng nhập một phạm vi ngày hợp lệ trong định dạng YYYY-MM-DD", - - "stats.yesterday": "Hôm qua", - "stats.today": "Hôm nay", - "stats.last-week": "Tuần trước", - "stats.this-week": "Tuần này", - "stats.last-month": "Tháng trước", - "stats.this-month": "Tháng này", - "stats.all": "Mọi lúc", - - "updates": "Cập nhật", - "running-version": "Bạn đang chạy NodeBB v%1.", - "keep-updated": "Luôn đảm bảo rằng NodeBB của bạn được cập nhật cho các bản vá bảo mật và sửa lỗi mới nhất.", - "up-to-date": "

Bạn đang bản mới nhất

", - "upgrade-available": "

Phiên bản mới (v%1) đã được phát hành. Xem xét nâng cấp NodeBB của bạn.

", - "prerelease-upgrade-available": "

This is an outdated pre-release version of NodeBB. A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-warning": "

This is a pre-release version of NodeBB. Unintended bugs may occur.

", - "running-in-development": "Forum is running in development mode. The forum may be open to potential vulnerabilities; please contact your system administrator.", - "latest-lookup-failed": "

Failed to look up latest available version of NodeBB

", - - "notices": "Notices", - "restart-not-required": "Restart not required", - "restart-required": "Restart required", - "search-plugin-installed": "Search Plugin installed", - "search-plugin-not-installed": "Search Plugin not installed", - "search-plugin-tooltip": "Install a search plugin from the plugin page in order to activate search functionality", - - "control-panel": "Điều khiển hệ thống", - "rebuild-and-restart": "Rebuild & Restart", - "restart": "Restart", - "restart-warning": "Rebuilding or Restarting your NodeBB will drop all existing connections for a few seconds.", - "restart-disabled": "Rebuilding and Restarting your NodeBB has been disabled as you do not seem to be running it via the appropriate daemon.", - "maintenance-mode": "Maintenance Mode", - "maintenance-mode-title": "Click here to set up maintenance mode for NodeBB", - "realtime-chart-updates": "Realtime Chart Updates", - - "active-users": "Active Users", - "active-users.users": "Users", - "active-users.guests": "Guests", - "active-users.total": "Total", - "active-users.connections": "Connections", - - "anonymous-registered-users": "Anonymous vs Registered Users", - "anonymous": "Anonymous", - "registered": "Registered", - - "user-presence": "User Presence", - "on-categories": "On categories list", - "reading-posts": "Reading posts", - "browsing-topics": "Browsing topics", - "recent": "Recent", - "unread": "Unread", - - "high-presence-topics": "High Presence Topics", - - "graphs.page-views": "Page Views", - "graphs.page-views-registered": "Page Views Registered", - "graphs.page-views-guest": "Page Views Guest", - "graphs.page-views-bot": "Page Views Bot", - "graphs.unique-visitors": "Unique Visitors", - "graphs.registered-users": "Registered Users", - "graphs.anonymous-users": "Anonymous Users", - "last-restarted-by": "Last restarted by", - "no-users-browsing": "No users browsing" -} diff --git a/public/language/vi/admin/general/homepage.json b/public/language/vi/admin/general/homepage.json deleted file mode 100644 index 7428d59eeb..0000000000 --- a/public/language/vi/admin/general/homepage.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "home-page": "Home Page", - "description": "Choose what page is shown when users navigate to the root URL of your forum.", - "home-page-route": "Home Page Route", - "custom-route": "Custom Route", - "allow-user-home-pages": "Allow User Home Pages", - "home-page-title": "Title of the home page (default \"Home\")" -} \ No newline at end of file diff --git a/public/language/vi/admin/general/languages.json b/public/language/vi/admin/general/languages.json deleted file mode 100644 index fc78a8345a..0000000000 --- a/public/language/vi/admin/general/languages.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "language-settings": "Cài đặt ngôn ngữ", - "description": "Ngôn ngữ mặc định xác định cài đặt ngôn ngữ cho tất cả người dùng đang truy cập diễn đàn của bạn.
Người dùng cá nhân có thể ghi đè ngôn ngữ mặc định trên trang cài đặt tài khoản của họ", - "default-language": "Ngôn ngữ mặc định", - "auto-detect": "Tự động phát hiện cài đặt ngôn ngữ cho khách" -} \ No newline at end of file diff --git a/public/language/vi/admin/general/navigation.json b/public/language/vi/admin/general/navigation.json deleted file mode 100644 index 13dd01aae7..0000000000 --- a/public/language/vi/admin/general/navigation.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "icon": "Icon:", - "change-icon": "change", - "route": "Route:", - "tooltip": "Tooltip:", - "text": "Text:", - "text-class": "Text Class: optional", - "class": "Class: optional", - "id": "ID: optional", - - "properties": "Properties:", - "groups": "Groups:", - "open-new-window": "Open in a new window", - - "btn.delete": "Delete", - "btn.disable": "Disable", - "btn.enable": "Enable", - - "available-menu-items": "Available Menu Items", - "custom-route": "Custom Route", - "core": "core", - "plugin": "plugin" -} \ No newline at end of file diff --git a/public/language/vi/admin/general/social.json b/public/language/vi/admin/general/social.json deleted file mode 100644 index f9dcde47ad..0000000000 --- a/public/language/vi/admin/general/social.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "post-sharing": "Chia sẻ bài viết", - "info-plugins-additional": "Plugin có thể thêm các mạng bổ sung để chia sẻ bài viết.", - "save-success": "Mạng chia sẻ bài đã lưu thành công!" -} \ No newline at end of file diff --git a/public/language/vi/admin/general/sounds.json b/public/language/vi/admin/general/sounds.json deleted file mode 100644 index 95ccbde0f1..0000000000 --- a/public/language/vi/admin/general/sounds.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "notifications": "Notifications", - "chat-messages": "Chat Messages", - "play-sound": "Play", - "incoming-message": "Incoming Message", - "outgoing-message": "Outgoing Message", - "upload-new-sound": "Upload New Sound", - "saved": "Settings Saved" -} \ No newline at end of file diff --git a/public/language/vi/admin/manage/digest.json b/public/language/vi/admin/manage/digest.json index f852e6b586..80010b7fb1 100644 --- a/public/language/vi/admin/manage/digest.json +++ b/public/language/vi/admin/manage/digest.json @@ -13,6 +13,7 @@ "resent-single": "Đã hoàn tất gửi lại thông báo thủ công", "resent-day": "Đã gửi lại thông báo hàng ngày", "resent-week": "Đã gửi lại thông báo hàng tuần", + "resent-biweek": "Gửi lại thông báo hai tuần một lần", "resent-month": "Đã gửi lại thông báo hàng tháng", "null": "Không", "manual-run": "Chạy thông báo thủ công:", diff --git a/public/language/vi/admin/manage/privileges.json b/public/language/vi/admin/manage/privileges.json index 15ae622bc9..a41a2edd82 100644 --- a/public/language/vi/admin/manage/privileges.json +++ b/public/language/vi/admin/manage/privileges.json @@ -59,5 +59,5 @@ "alert.admin-warning": "Quản trị viên ngầm có tất cả các đặc quyền", "alert.copyPrivilegesFrom-title": "Chọn một danh mục để sao chép từ", "alert.copyPrivilegesFrom-warning": "Điều này sẽ sao chép %1 từ danh mục đã chọn.", - "alert.copyPrivilegesFromGroup-warning": "This will copy this group's set of %1 from the selected category." + "alert.copyPrivilegesFromGroup-warning": "Thao tác này sẽ sao chép cài đặt %1 của nhóm này từ danh mục đã chọn." } \ No newline at end of file diff --git a/public/language/vi/admin/manage/users.json b/public/language/vi/admin/manage/users.json index de73acdc38..2b9507036e 100644 --- a/public/language/vi/admin/manage/users.json +++ b/public/language/vi/admin/manage/users.json @@ -47,6 +47,7 @@ "users.uid": "uid", "users.username": "tên đăng nhập", "users.email": "thư điện tử", + "users.no-email": "(không có email)", "users.ip": "IP", "users.postcount": "số lượng bài viết", "users.reputation": "uy tín", diff --git a/public/language/vi/admin/menu.json b/public/language/vi/admin/menu.json index c983024553..d44cc4d246 100644 --- a/public/language/vi/admin/menu.json +++ b/public/language/vi/admin/menu.json @@ -4,6 +4,7 @@ "dashboard/logins": "Đăng nhập", "dashboard/users": "Người dùng", "dashboard/topics": "Chủ đề", + "dashboard/searches": "Tìm kiếm", "section-general": "Chung", "section-manage": "Quản lý", diff --git a/public/language/vi/admin/settings/email.json b/public/language/vi/admin/settings/email.json index ef49bcc0ca..07682dcf26 100644 --- a/public/language/vi/admin/settings/email.json +++ b/public/language/vi/admin/settings/email.json @@ -6,7 +6,7 @@ "from-help": "Tên người gửi hiển thị trong email.", "smtp-transport": "Truyền Tải SMTP", - "smtp-transport.enabled": "Sử dụng máy chủ email bên ngoài để gửi email", + "smtp-transport.enabled": "Bật truyền tải SMTP", "smtp-transport-help": "Bạn có thể chọn từ danh sách các dịch vụ nổi bật hoặc nhập một dịch vụ tùy chỉnh.", "smtp-transport.service": "Chọn một dịch vụ", "smtp-transport.service-custom": "Tùy chỉnh dịch vụ ", @@ -37,6 +37,10 @@ "subscriptions.hour": "Giờ Thông Báo", "subscriptions.hour-help": "Vui lòng nhập một số đại diện cho giờ để gửi thông báo email đã lên lịch (VD: 0 cho nửa đêm, 17 cho 5h chiều). Hãy nhớ rằng đây là giờ theo chính máy chủ và có thể không khớp chính xác với đồng hồ hệ thống của bạn.
Thời gian máy chủ gần đúng là:
Thông báo hàng ngày kế tiếp được lên lịch để gửi ", "notifications.remove-images": "Xóa hình ảnh khỏi thông báo email", + "require-email-address": "Bắt buộc người dùng mới phải điền địa chỉ email", + "require-email-address-warning": "Mặc định, người dùng có thể chọn không nhập địa chỉ email. Bật tùy chọn này nghĩa là họ buộc phải nhập địa chỉ email để đăng ký. Việc này không chắc người dùng sẽ nhập địa chỉ email thực, hoặc không phải địa chỉ mà họ sở hữu.", "include-unverified-emails": "Gửi email đến những người nhận chưa xác nhận rõ ràng email của họ", - "include-unverified-warning": "Theo mặc định, người dùng có email được liên kết với tài khoản của họ đã được xác minh, nhưng có những trường hợp không phải như vậy (ví dụ: đăng nhập SSO, người dùng phổ thông, v.v.). Bạn tự chịu rủi ro khi bật cài đặt này – gửi email đến các địa chỉ chưa được xác minh có thể vi phạm luật chống thư rác trong khu vực." -} \ No newline at end of file + "include-unverified-warning": "Theo mặc định, người dùng có email được liên kết với tài khoản của họ đã được xác minh, nhưng có những trường hợp không phải như vậy (ví dụ: đăng nhập SSO, người dùng phổ thông, v.v.). Bạn tự chịu rủi ro khi bật cài đặt này – gửi email đến các địa chỉ chưa được xác minh có thể vi phạm luật chống thư rác trong khu vực.", + "prompt": "Nhắc người dùng nhập hoặc xác nhận email của họ", + "prompt-help": "Nếu người dùng chưa cung cấp email hoặc email của họ chưa được xác nhận, một cảnh báo sẽ được hiển thị trên màn hình." +} diff --git a/public/language/vi/admin/settings/general.json b/public/language/vi/admin/settings/general.json index d4f3073728..7034716c95 100644 --- a/public/language/vi/admin/settings/general.json +++ b/public/language/vi/admin/settings/general.json @@ -3,9 +3,9 @@ "title": "Tiêu Đề Trang Web", "title.short": "Tiêu Đề Ngắn", "title.short-placeholder": "Nếu không có tiêu đề ngắn nào được chỉ định, tiêu đề trang web sẽ được sử dụng", - "title.url": "URL", + "title.url": "Liên kết URL Tiêu đề", "title.url-placeholder": "URL của tiêu đề trang web", - "title.url-help": "Khi tiêu đề được nhấn, hãy đưa người dùng đến địa chỉ này. Nếu để trống, người dùng sẽ được chuyển đến trang chủ diễn đàn.", + "title.url-help": "Khi tiêu đề được nhấp vào, hãy đưa người dùng đến địa chỉ này. Nếu để trống, người dùng sẽ được chuyển đến chỉ mục diễn đàn.
Lưu ý: Đây không phải là URL bên ngoài được sử dụng trong email, v.v. Nó được đặt bởi thuộc tính url trong config.json", "title.name": "Tên Cộng Đồng Của Bạn", "title.show-in-header": "Hiển Thị Tiêu Đề Trang Ở Phần Đầu", "browser-title": "Tiêu Đề Trình Duyệt", @@ -20,9 +20,9 @@ "logo.image": "Ảnh", "logo.image-placeholder": "Đường dẫn đến biểu trưng để hiển thị phần đầu diễn đàn", "logo.upload": "Tải lên", - "logo.url": "URL", + "logo.url": "Liên kết URL Logo", "logo.url-placeholder": "URL biểu trưng trang web", - "logo.url-help": "Khi nhấp vào logo, ​​hãy đưa người dùng đến địa chỉ này. Nếu để trống, người dùng sẽ được chuyển đến trang chủ diễn đàn.", + "logo.url-help": "Khi nhấp vào logo, ​​hãy đưa người dùng đến địa chỉ này. Nếu để trống, người dùng sẽ được chuyển đến chỉ mục diễn đàn.
Lưu ý: Đây không phải là URL bên ngoài được sử dụng trong email, v.v. Nó được đặt bởi thuộc tính url trong config.json", "logo.alt-text": "Văn Bản Thay Thế", "log.alt-text-placeholder": "Văn bản thay thế cho khả năng tiếp cận", "favicon": "Biểu tượng ưa thích", @@ -47,4 +47,4 @@ "undo-timeout": "Hoàn tác thời gian chờ", "undo-timeout-help": "Một số thao tác như chuyển chủ đề sẽ cho phép người kiểm duyệt hoàn tác hành động của họ trong một khung thời gian nhất định. Đặt thành 0 để tắt hoàn toàn hoàn tác.", "topic-tools": "Công cụ chủ đề" -} \ No newline at end of file +} diff --git a/public/language/vi/admin/settings/navigation.json b/public/language/vi/admin/settings/navigation.json index 8f47a0d402..22ce457fb4 100644 --- a/public/language/vi/admin/settings/navigation.json +++ b/public/language/vi/admin/settings/navigation.json @@ -11,6 +11,8 @@ "properties": "Thuộc tính:", "groups": "Nhóm:", "open-new-window": "Mở trong một cửa sổ mới", + "dropdown": "Thả xuống", + "dropdown-placeholder": "Đặt các mục menu thả xuống của bạn bên dưới, tức là:
<li><a href="https://myforum.com">Link 1</a></li>", "btn.delete": "Xóa", "btn.disable": "Tắt", @@ -20,4 +22,4 @@ "custom-route": "Tùy Chỉnh Liên Kết", "core": "lõi", "plugin": "plugin" -} \ No newline at end of file +} diff --git a/public/language/vi/admin/settings/notifications.json b/public/language/vi/admin/settings/notifications.json index 407bc55bc3..c7055b3c21 100644 --- a/public/language/vi/admin/settings/notifications.json +++ b/public/language/vi/admin/settings/notifications.json @@ -2,5 +2,6 @@ "notifications": "Thông báo", "welcome-notification": "Thông Báo Chào Mừng", "welcome-notification-link": "Liên Kết Thông Báo Chào Mừng", - "welcome-notification-uid": "Thông Báo Chào Mừng Người Dùng (UID)" + "welcome-notification-uid": "Thông Báo Chào Mừng Người Dùng (UID)", + "post-queue-notification-uid": "Hàng Đợi Người Dùng Đăng Bài (UID)" } \ No newline at end of file diff --git a/public/language/vi/admin/settings/post.json b/public/language/vi/admin/settings/post.json index 1a9a24f454..db7602bd43 100644 --- a/public/language/vi/admin/settings/post.json +++ b/public/language/vi/admin/settings/post.json @@ -40,6 +40,7 @@ "teaser.last-post": "Gần đây – Hiển thị bài đăng mới nhất, bao gồm cả bài gốc, nếu không có câu trả lời", "teaser.last-reply": "Cuối cùng - Hiển thị câu trả lời mới nhất hoặc trình giữ chỗ \"Không trả lời\" nếu không có câu trả lời", "teaser.first": "Đầu tiên", + "showPostPreviewsOnHover": "Hiển thị bản xem trước của các bài đăng khi di chuột qua", "unread": "Cài Đặt Chưa Đọc", "unread.cutoff": "Số ngày giới hạn chưa đọc", "unread.min-track-last": "Số bài viết tối thiểu trong chủ đề trước khi theo dõi lần đọc cuối cùng", @@ -56,6 +57,9 @@ "composer.show-help": "Hiển thị tab \"Trợ giúp\"", "composer.enable-plugin-help": "Cho phép các plugin thêm nội dung vào tab trợ giúp", "composer.custom-help": "Văn Bản Trợ Giúp Tùy Chỉnh", + "backlinks": "Liên kết ngược", + "backlinks.enabled": "Bật liên kết ngược chủ đề", + "backlinks.help": "Nếu một bài đăng tham chiếu đến chủ đề khác, một liên kết quay lại bài đăng sẽ được chèn vào chủ đề được tham chiếu tại thời điểm đó.", "ip-tracking": "Theo dõi IP", "ip-tracking.each-post": "Theo dõi Địa chỉ IP mỗi bài đăng", "enable-post-history": "Bật Lịch Sử Bài Đăng" diff --git a/public/language/vi/admin/settings/user.json b/public/language/vi/admin/settings/user.json index d8e44700b4..9074776f44 100644 --- a/public/language/vi/admin/settings/user.json +++ b/public/language/vi/admin/settings/user.json @@ -71,6 +71,7 @@ "digest-freq.off": "Tắt", "digest-freq.daily": "Hàng ngày", "digest-freq.weekly": "Hàng tuần", + "digest-freq.biweekly": "Hai tuần một lần", "digest-freq.monthly": "Hàng tháng", "email-chat-notifs": "Gửi email nếu có tin nhắn trò chuyện mới và tôi không trực tuyến", "email-post-notif": "Gửi email khi có trả lời ở các chủ đề tôi đã đăng ký", diff --git a/public/language/vi/error.json b/public/language/vi/error.json index 48d63b3474..8da98da748 100644 --- a/public/language/vi/error.json +++ b/public/language/vi/error.json @@ -34,8 +34,9 @@ "email-invited": "Email đã được mời", "email-not-confirmed": "Đăng trong một số danh mục hoặc chủ đề được bật sau khi email của bạn được xác nhận, vui lòng nhấp vào đây để gửi email xác nhận.", "email-not-confirmed-chat": "Bạn không thể trò chuyện cho đến khi email của bạn được xác nhận, vui lòng nhấp vào đây để xác nhận email của bạn.", - "email-not-confirmed-email-sent": "Email của bạn vẫn chưa được xác nhận, vui lòng kiểm tra hộp thư đến của bạn. Bạn sẽ không thể đăng hoặc trò chuyện cho đến khi email được xác nhận.", - "no-email-to-confirm": "Tài khoản của bạn không có email. Email là cần thiết để khôi phục tài khoản. Vui lòng bấm vào đây để nhập email.", + "email-not-confirmed-email-sent": "Email của bạn vẫn chưa được xác nhận, vui lòng kiểm tra hộp thư đến của bạn để biết email xác nhận. Bạn có thể không đăng được trong một số danh mục hoặc trò chuyện cho đến khi email của bạn được xác nhận.", + "no-email-to-confirm": "Tài khoản của bạn chưa có email. Email cần dùng lúc khôi phục tài khoản và có thể cần để trò chuyện và đăng bài trong một số danh mục. Vui lòng bấm vào đây để nhập email.", + "user-doesnt-have-email": "Người dùng \"%1\" chưa đặt email.", "email-confirm-failed": "Chúng tôi không thể xác nhận email của bạn, vui lòng thử lại sau.", "confirm-email-already-sent": "Email xác nhận đã được gửi, vui lòng chờ %1 phút để yêu cầu gửi lại.", "sendmail-not-found": "Không tìm thấy lệnh thực thi \"sendmail\", hãy chắc chắn nó đã được cài đặt và thực thi bởi người quản trị đang vận hành NodeBB", @@ -103,6 +104,7 @@ "already-bookmarked": "Bạn đã đánh dấu trang chủ đề này rồi", "already-unbookmarked": "Bạn đã hủy đánh dấu trang chủ đề này rồi", "cant-ban-other-admins": "Bạn không thể cấm quản trị viên khác!", + "cant-make-banned-users-admin": "Bạn không thể đặt người dùng bị cấm làm quản trị viên.", "cant-remove-last-admin": "Bạn là quản trị viên duy nhất. Hãy cho thành viên khác làm quản trị viên trước khi huỷ bỏ quyền quản trị của bạn.", "account-deletion-disabled": "Tính năng xóa tài khoản đã bị tắt", "cant-delete-admin": "Gỡ bỏ đặc quyền quản trị viên khỏi tài khoản này trước khi cố gắng xóa nó.", diff --git a/public/language/vi/modules.json b/public/language/vi/modules.json index 3fbf9c8f69..5f910b23b1 100644 --- a/public/language/vi/modules.json +++ b/public/language/vi/modules.json @@ -54,7 +54,7 @@ "composer.formatting.strikethrough": "Gạch ngang", "composer.formatting.code": "Mã", "composer.formatting.link": "Liên kết", - "composer.formatting.picture": "Hình ảnh", + "composer.formatting.picture": "Liên Kết Ảnh", "composer.upload-picture": "Tải ảnh lên", "composer.upload-file": "Tải Lên Tệp", "composer.zen_mode": "Chế Độ Zen", diff --git a/public/language/vi/top.json b/public/language/vi/top.json new file mode 100644 index 0000000000..18d3e3238b --- /dev/null +++ b/public/language/vi/top.json @@ -0,0 +1,4 @@ +{ + "title": "Hàng Đầu", + "no_top_topics": "Không có chủ đề hàng đầu" +} \ No newline at end of file diff --git a/public/language/vi/topic.json b/public/language/vi/topic.json index 1b476f4c30..bce14caa6a 100644 --- a/public/language/vi/topic.json +++ b/public/language/vi/topic.json @@ -47,6 +47,7 @@ "restored-by": "Được khôi phục bởi", "moved-from-by": "Đã chuyển từ %1 bởi", "queued-by": "Bài đăng được xếp hàng chờ duyệt →", + "backlink": "Được giới thiệu bởi", "bookmark_instructions": "Nhấn vào đây để trở lại bài viết đã đọc cuối cùng trong chủ đề này.", "flag-post": "Gắn cờ bài đăng này", "flag-user": "Gắn cờ người dùng này", @@ -138,6 +139,7 @@ "composer.handle_placeholder": "Nhập tên/xử lý của bạn ở đây", "composer.discard": "Huỷ bỏ", "composer.submit": "Gửi", + "composer.additional-options": "Tùy chọn bổ sung", "composer.schedule": "Lên lịch", "composer.replying_to": "Đang trả lời %1", "composer.new_topic": "Chủ đề mới", @@ -158,6 +160,7 @@ "newest_to_oldest": "Mới đến cũ", "most_votes": "Nhiều Bình Chọn", "most_posts": "Nhiều Bài Đăng", + "most_views": "Xem Nhiều", "stale.title": "Tạo chủ đề mới thay thế?", "stale.warning": "Chủ đề bạn đang trả lời đã khá cũ. Bạn có muốn tạo chủ đề mới, và liên kết với chủ đề hiện tại trong bài viết trả lời của bạn?", "stale.create": "Tạo chủ đề mới", diff --git a/public/language/vi/user.json b/public/language/vi/user.json index d30e2d126e..235c199efb 100644 --- a/public/language/vi/user.json +++ b/public/language/vi/user.json @@ -94,6 +94,7 @@ "digest_off": "Tắt", "digest_daily": "Hàng ngày", "digest_weekly": "Hàng tuần", + "digest_biweekly": "Hai tuần một lần", "digest_monthly": "Hàng tháng", "has_no_follower": "Người dùng này không có ai theo dõi :(", "follows_no_one": "Người dùng này không theo dõi ai :(", diff --git a/public/language/zh-CN/admin/advanced/events.json b/public/language/zh-CN/admin/advanced/events.json index 48ba6e3a6f..e49a2510fe 100644 --- a/public/language/zh-CN/admin/advanced/events.json +++ b/public/language/zh-CN/admin/advanced/events.json @@ -3,6 +3,7 @@ "no-events": "暂无事件", "control-panel": "事件控制面板", "delete-events": "删除事件", + "confirm-delete-all-events": "Are you sure you want to delete all logged events?", "filters": "过滤器", "filters-apply": "应用过滤器", "filter-type": "事件类型", diff --git a/public/language/zh-CN/admin/dashboard.json b/public/language/zh-CN/admin/dashboard.json index 427fdb412a..28a008851f 100644 --- a/public/language/zh-CN/admin/dashboard.json +++ b/public/language/zh-CN/admin/dashboard.json @@ -56,8 +56,8 @@ "active-users.total": "全部", "active-users.connections": "连接", - "anonymous-registered-users": "匿名 vs 注册用户", - "anonymous": "匿名", + "guest-registered-users": "Guest vs Registered Users", + "guest": "Guest", "registered": "已注册", "user-presence": "用户光临", @@ -68,6 +68,7 @@ "unread": "未读", "high-presence-topics": "热门话题", + "popular-searches": "Popular Searches", "graphs.page-views": "页面浏览量", "graphs.page-views-registered": "注册用户页面浏览量", @@ -75,13 +76,14 @@ "graphs.page-views-bot": "爬虫页面浏览量", "graphs.unique-visitors": "单一访客", "graphs.registered-users": "已注册用户", - "graphs.anonymous-users": "匿名用户", + "graphs.guest-users": "Guest Users", "last-restarted-by": "上次重启管理员/时间", "no-users-browsing": "没有用户正在浏览", "back-to-dashboard": "返回控制面板", "details.no-users": "选定的时间内没有用户加入", "details.no-topics": "选定的时间内没有发布主题", + "details.no-searches": "No searches have been made yet", "details.no-logins": "选定的时间内没有登录记录", "details.logins-static": "NodeBB只保留%1天登录数据,下列表格显示最近活动的登录。", "details.logins-login-time": "登录时间" diff --git a/public/language/zh-CN/admin/development/info.json b/public/language/zh-CN/admin/development/info.json index 4cf78389ba..8d4e8b4678 100644 --- a/public/language/zh-CN/admin/development/info.json +++ b/public/language/zh-CN/admin/development/info.json @@ -8,7 +8,11 @@ "nodejs": "nodejs", "online": "在线", "git": "git", - "memory": "内存", + "process-memory": "process memory", + "system-memory": "system memory", + "used-memory-process": "Used memory by process", + "used-memory-os": "Used system memory", + "total-memory-os": "Total system memory", "load": "系统负载", "cpu-usage": "CPU 使用情况", "uptime": "运行时间", diff --git a/public/language/zh-CN/admin/extend/rewards.json b/public/language/zh-CN/admin/extend/rewards.json index 12a4336ef0..7646b33976 100644 --- a/public/language/zh-CN/admin/extend/rewards.json +++ b/public/language/zh-CN/admin/extend/rewards.json @@ -8,8 +8,6 @@ "delete": "删除", "enable": "启用", "disable": "禁用", - "control-panel": "奖励控制", - "new-reward": "新奖励", "alert.delete-success": "已成功删除奖励", "alert.no-inputs-found": "非法奖励 - 输入为空!", diff --git a/public/language/zh-CN/admin/general/dashboard.json b/public/language/zh-CN/admin/general/dashboard.json deleted file mode 100644 index 0feb4970cd..0000000000 --- a/public/language/zh-CN/admin/general/dashboard.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "forum-traffic": "论坛流量", - "page-views": "页面浏览量", - "unique-visitors": "单一访客", - "new-users": "新用户", - "posts": "发帖", - "topics": "主题", - "page-views-seven": "最近7天", - "page-views-thirty": "最近30天", - "page-views-last-day": "最近24小时", - "page-views-custom": "自定义日期范围", - "page-views-custom-start": "范围开始", - "page-views-custom-end": "范围结束", - "page-views-custom-help": "输入您要查看的网页浏览日期范围。 如果没有日期选择器可用,则接受的格式是 YYYY-MM-DD", - "page-views-custom-error": "请输入 YYYY-MM-DD格式的有效日期范围 ", - - "stats.yesterday": "昨天", - "stats.today": "今天", - "stats.last-week": "上一周", - "stats.this-week": "本周", - "stats.last-month": "上一月", - "stats.this-month": "本月", - "stats.all": "总计", - - "updates": "更新", - "running-version": "您正在运行 NodeBB v%1 .", - "keep-updated": "请确保您已及时更新 NodeBB 以获得最新的安全补丁与 Bug 修复。", - "up-to-date": "

正在使用 最新版本

", - "upgrade-available": "

新的版本 (v%1) 已经发布。建议您 升级 NodeBB

", - "prerelease-upgrade-available": "

这是一个已经过期的预发布版本的 NodeBB,新的版本 (v%1) 已经发布。建议您 升级 NodeBB

", - "prerelease-warning": "

正在使用测试版 NodeBB。可能会出现意外的 Bug。

", - "running-in-development": "论坛正处于开发模式,这可能使其暴露于潜在的危险之中;请联系您的系统管理员。", - "latest-lookup-failed": "

无法查找 NodeBB 的最新可用版本

", - - "notices": "提醒", - "restart-not-required": "不需要重启", - "restart-required": "需要重启", - "search-plugin-installed": "已安装搜索插件", - "search-plugin-not-installed": "未安装搜索插件", - "search-plugin-tooltip": "在插件页面安装搜索插件来激活搜索功能", - - "control-panel": "系统控制", - "rebuild-and-restart": "部署 & 重启", - "restart": "重启", - "restart-warning": "重载或重启 NodeBB 会丢失数秒内全部的连接。", - "restart-disabled": "重建和重新启动NodeBB已被禁用,因为您似乎没有通过适当的守护进程运行它。", - "maintenance-mode": "维护模式", - "maintenance-mode-title": "点击此处设置 NodeBB 的维护模式", - "realtime-chart-updates": "实时图表更新", - - "active-users": "活跃用户", - "active-users.users": "用户", - "active-users.guests": "游客", - "active-users.total": "全部", - "active-users.connections": "连接", - - "anonymous-registered-users": "匿名 vs 注册用户", - "anonymous": "匿名", - "registered": "已注册", - - "user-presence": "用户光临", - "on-categories": "在版块列表", - "reading-posts": "读帖子", - "browsing-topics": "浏览话题", - "recent": "最近", - "unread": "未读", - - "high-presence-topics": "热门话题", - - "graphs.page-views": "页面浏览量", - "graphs.page-views-registered": "注册用户页面浏览量", - "graphs.page-views-guest": "游客页面浏览量", - "graphs.page-views-bot": "爬虫页面浏览量", - "graphs.unique-visitors": "单一访客", - "graphs.registered-users": "已注册用户", - "graphs.anonymous-users": "匿名用户", - "last-restarted-by": "上次重启管理员/时间", - "no-users-browsing": "没有用户正在浏览" -} diff --git a/public/language/zh-CN/admin/general/homepage.json b/public/language/zh-CN/admin/general/homepage.json deleted file mode 100644 index 8864e4eb34..0000000000 --- a/public/language/zh-CN/admin/general/homepage.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "home-page": "主页", - "description": "请选择用户到达根 URL 时所显示的页面。", - "home-page-route": "主页路由", - "custom-route": "自定义路由", - "allow-user-home-pages": "允许用户主页", - "home-page-title": "首页标题(默认“Home”)" -} \ No newline at end of file diff --git a/public/language/zh-CN/admin/general/languages.json b/public/language/zh-CN/admin/general/languages.json deleted file mode 100644 index b8cb60203e..0000000000 --- a/public/language/zh-CN/admin/general/languages.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "language-settings": "语言设置", - "description": "默认语言会决定所有用户的语言设定。
单一用户可以各自在帐户设置中覆盖此项设定。", - "default-language": "默认语言", - "auto-detect": "自动检测游客的语言设置" -} \ No newline at end of file diff --git a/public/language/zh-CN/admin/general/navigation.json b/public/language/zh-CN/admin/general/navigation.json deleted file mode 100644 index f7f9003ed1..0000000000 --- a/public/language/zh-CN/admin/general/navigation.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "icon": "图标:", - "change-icon": "更改", - "route": "路由:", - "tooltip": "提示:", - "text": "文本:", - "text-class": "文本类:可选", - "class": "类: 可选", - "id": "ID:可选", - - "properties": "属性:", - "groups": "群组:", - "open-new-window": "在新窗口中打开", - - "btn.delete": "删除", - "btn.disable": "禁用", - "btn.enable": "启用", - - "available-menu-items": "可用的菜单项目", - "custom-route": "自定义路由", - "core": "核心", - "plugin": "插件" -} \ No newline at end of file diff --git a/public/language/zh-CN/admin/general/social.json b/public/language/zh-CN/admin/general/social.json deleted file mode 100644 index 0882ee95e9..0000000000 --- a/public/language/zh-CN/admin/general/social.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "post-sharing": "帖子分享", - "info-plugins-additional": "插件可以增加可选的用于分享帖子的网络。", - "save-success": "已成功保存帖子分享网络。" -} \ No newline at end of file diff --git a/public/language/zh-CN/admin/general/sounds.json b/public/language/zh-CN/admin/general/sounds.json deleted file mode 100644 index d330e309ac..0000000000 --- a/public/language/zh-CN/admin/general/sounds.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "notifications": "通知", - "chat-messages": "聊天信息", - "play-sound": "播放", - "incoming-message": "收到的消息", - "outgoing-message": "发出的消息", - "upload-new-sound": "上传新的声音", - "saved": "设置已保存" -} \ No newline at end of file diff --git a/public/language/zh-CN/admin/manage/digest.json b/public/language/zh-CN/admin/manage/digest.json index 7c1f27c795..3621e92cff 100644 --- a/public/language/zh-CN/admin/manage/digest.json +++ b/public/language/zh-CN/admin/manage/digest.json @@ -13,6 +13,7 @@ "resent-single": "摘要重发操作完成", "resent-day": "已发送每日摘要", "resent-week": "已发送每周摘要", + "resent-biweek": "Bi-Weekly digest resent", "resent-month": "已发送每月摘要", "null": "从不", "manual-run": "手动运行摘要:", diff --git a/public/language/zh-CN/admin/manage/users.json b/public/language/zh-CN/admin/manage/users.json index 2958f2f4eb..2aec55959e 100644 --- a/public/language/zh-CN/admin/manage/users.json +++ b/public/language/zh-CN/admin/manage/users.json @@ -47,6 +47,7 @@ "users.uid": "UID", "users.username": "用户名", "users.email": "电子邮件", + "users.no-email": "(no email)", "users.ip": "IP", "users.postcount": "发帖数", "users.reputation": "声望", diff --git a/public/language/zh-CN/admin/menu.json b/public/language/zh-CN/admin/menu.json index 283c64c2f6..cc413ef8b3 100644 --- a/public/language/zh-CN/admin/menu.json +++ b/public/language/zh-CN/admin/menu.json @@ -4,6 +4,7 @@ "dashboard/logins": "登录", "dashboard/users": "用户", "dashboard/topics": "主题", + "dashboard/searches": "Searches", "section-general": "基本", "section-manage": "管理", diff --git a/public/language/zh-CN/admin/settings/email.json b/public/language/zh-CN/admin/settings/email.json index ba294937fa..5afd0ca782 100644 --- a/public/language/zh-CN/admin/settings/email.json +++ b/public/language/zh-CN/admin/settings/email.json @@ -6,7 +6,7 @@ "from-help": "用于邮件中显示的发送者", "smtp-transport": "SMTP 通信", - "smtp-transport.enabled": "使用一个外部电子邮箱系统来发送邮件", + "smtp-transport.enabled": "Enable SMTP Transport", "smtp-transport-help": "您可以从列表中选取一个已知的服务或自定义。", "smtp-transport.service": "选择服务", "smtp-transport.service-custom": "自定义", @@ -37,6 +37,10 @@ "subscriptions.hour": "摘要小时", "subscriptions.hour-help": "请输入一个代表小时的数字来发送计划的电子邮件摘要 (例如,对于午夜,0,对于下午5:00,17)。 请记住,这是根据服务器本身的时间,可能与您的系统时钟不完全匹配。
服务器的大致时间为:
下一个每日摘要被计划在发送", "notifications.remove-images": "Remove images from email notifications", + "require-email-address": "Require new users to specify an email address", + "require-email-address-warning": "By default, users can opt-out of entering an email address. Enabling this option means they have to enter an email address in order to proceed with registration. It does not ensure user will enter a real email address, nor even an address they own.", "include-unverified-emails": "Send emails to recipients who have not explicitly confirmed their emails", - "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws." -} \ No newline at end of file + "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws.", + "prompt": "Prompt users to enter or confirm their emails", + "prompt-help": "If a user does not have an email set, or their email is not confirmed, a warning will be shown on screen." +} diff --git a/public/language/zh-CN/admin/settings/general.json b/public/language/zh-CN/admin/settings/general.json index f9dda72c8c..7cc3e0a53b 100644 --- a/public/language/zh-CN/admin/settings/general.json +++ b/public/language/zh-CN/admin/settings/general.json @@ -3,9 +3,9 @@ "title": "站点标题", "title.short": "短标题", "title.short-placeholder": "如果没有指定短标题,将会使用站点标题", - "title.url": "网址", + "title.url": "Title Link URL", "title.url-placeholder": "网站标题链接", - "title.url-help": "当标题被点击,用户将跳转到该地址。如果留空,用户将跳转到论坛首页。", + "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "title.name": "您的社区名称", "title.show-in-header": "在顶部显示站点标题", "browser-title": "浏览器标题", @@ -20,9 +20,9 @@ "logo.image": "图像", "logo.image-placeholder": "要在论坛标题上显示的 Logo 的路径", "logo.upload": "上传", - "logo.url": "网址", + "logo.url": "Logo Link URL", "logo.url-placeholder": "站点 Logo 链接", - "logo.url-help": "当 Logo 被点击时,将用户跳转到此地址。如果留空,用户将被跳转到论坛首页。", + "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "logo.alt-text": "替代文本", "log.alt-text-placeholder": "辅助功能的替代文本", "favicon": "站点图标", @@ -47,4 +47,4 @@ "undo-timeout": "Undo Timeout", "undo-timeout-help": "Some operations such as moving topics will allow for the moderator to undo their action within a certain timeframe. Set to 0 to disable undo completely.", "topic-tools": "Topic Tools" -} \ No newline at end of file +} diff --git a/public/language/zh-CN/admin/settings/navigation.json b/public/language/zh-CN/admin/settings/navigation.json index f7f9003ed1..968f8362ba 100644 --- a/public/language/zh-CN/admin/settings/navigation.json +++ b/public/language/zh-CN/admin/settings/navigation.json @@ -11,6 +11,8 @@ "properties": "属性:", "groups": "群组:", "open-new-window": "在新窗口中打开", + "dropdown": "Dropdown", + "dropdown-placeholder": "Place your dropdown menu items below, ie:
<li><a href="https://myforum.com">Link 1</a></li>", "btn.delete": "删除", "btn.disable": "禁用", @@ -20,4 +22,4 @@ "custom-route": "自定义路由", "core": "核心", "plugin": "插件" -} \ No newline at end of file +} diff --git a/public/language/zh-CN/admin/settings/notifications.json b/public/language/zh-CN/admin/settings/notifications.json index bd4ee2967f..147dc97f23 100644 --- a/public/language/zh-CN/admin/settings/notifications.json +++ b/public/language/zh-CN/admin/settings/notifications.json @@ -2,5 +2,6 @@ "notifications": "通知", "welcome-notification": "欢迎通知", "welcome-notification-link": "欢迎通知链接", - "welcome-notification-uid": "用户欢迎通知 (UID)" + "welcome-notification-uid": "用户欢迎通知 (UID)", + "post-queue-notification-uid": "Post Queue User (UID)" } \ No newline at end of file diff --git a/public/language/zh-CN/admin/settings/post.json b/public/language/zh-CN/admin/settings/post.json index c1b58d8595..2d66c6b6ac 100644 --- a/public/language/zh-CN/admin/settings/post.json +++ b/public/language/zh-CN/admin/settings/post.json @@ -40,6 +40,7 @@ "teaser.last-post": "最后– 显示最新的帖子,包括原帖,如果没有回复", "teaser.last-reply": "最后– 显示最新回复,如果没有回复,则显示“无回复”占位符", "teaser.first": "第一", + "showPostPreviewsOnHover": "Show a preview of posts when mouse overed", "unread": "未读设置", "unread.cutoff": "未读截止时间(天)", "unread.min-track-last": "跟踪最后阅读之前的主题最小帖子", @@ -56,6 +57,9 @@ "composer.show-help": "显示“帮助”选项卡", "composer.enable-plugin-help": "允许插件添加内容到帮助选项卡", "composer.custom-help": "自定义帮助文本", + "backlinks": "Backlinks", + "backlinks.enabled": "Enable topic backlinks", + "backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.", "ip-tracking": "IP 跟踪", "ip-tracking.each-post": "跟踪每个帖子的 IP 地址", "enable-post-history": "启用回复历史" diff --git a/public/language/zh-CN/admin/settings/user.json b/public/language/zh-CN/admin/settings/user.json index 78ee3b04fe..baae37bbfc 100644 --- a/public/language/zh-CN/admin/settings/user.json +++ b/public/language/zh-CN/admin/settings/user.json @@ -71,6 +71,7 @@ "digest-freq.off": "关闭", "digest-freq.daily": "每日", "digest-freq.weekly": "每周", + "digest-freq.biweekly": "Bi-Weekly", "digest-freq.monthly": "每月", "email-chat-notifs": "当我不在线并收到新的聊天消息时,给我发送邮件通知", "email-post-notif": "当我订阅的主题有新回复时,给我发送邮件通知", diff --git a/public/language/zh-CN/error.json b/public/language/zh-CN/error.json index b4b57239ec..1c087addf7 100644 --- a/public/language/zh-CN/error.json +++ b/public/language/zh-CN/error.json @@ -34,8 +34,9 @@ "email-invited": "已通过电子邮件进行邀请", "email-not-confirmed": "Posting in some categories or topics is enabled once your email is confirmed, please click here to send a confirmation email.", "email-not-confirmed-chat": "您的电子邮箱尚未确认,无法聊天,请点击这里确认您的电子邮箱。", - "email-not-confirmed-email-sent": "您的电子邮件账户尚未确认,请检查您的收件箱。在电子邮件帐户被确认前您不能发帖和聊天。", - "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery. Please click here to enter an email.", + "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You may not be able to post in some categories or chat until your email is confirmed.", + "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery, and may be necessary for chatting and posting in some categories. Please click here to enter an email.", + "user-doesnt-have-email": "User \"%1\" does not have an email set.", "email-confirm-failed": "我们无法确认您的电子邮箱,请重试", "confirm-email-already-sent": "确认邮件已发出,如需重新发送请等待 %1 分钟后再试。", "sendmail-not-found": "无法找到 sendmail 可执行程序,请确保 sendmail 已经安装并可被运行 NodeBB 的用户执行", @@ -103,6 +104,7 @@ "already-bookmarked": "您已将此贴存为了书签", "already-unbookmarked": "您已取消了此贴的书签", "cant-ban-other-admins": "您不能封禁其他管理员!", + "cant-make-banned-users-admin": "You can't make banned users admin.", "cant-remove-last-admin": "您是唯一的管理员。在删除您的管理员权限前,请添加另一个管理员。", "account-deletion-disabled": "Account deletion is disabled", "cant-delete-admin": "在删除该账号之前,请先移除其管理权限。", diff --git a/public/language/zh-CN/modules.json b/public/language/zh-CN/modules.json index 7f24fbeab9..790e1563e4 100644 --- a/public/language/zh-CN/modules.json +++ b/public/language/zh-CN/modules.json @@ -54,7 +54,7 @@ "composer.formatting.strikethrough": "删除线", "composer.formatting.code": "代码", "composer.formatting.link": "链接", - "composer.formatting.picture": "图片", + "composer.formatting.picture": "Image Link", "composer.upload-picture": "上传图片", "composer.upload-file": "上传文件", "composer.zen_mode": "无干扰模式", diff --git a/public/language/zh-CN/top.json b/public/language/zh-CN/top.json new file mode 100644 index 0000000000..fb0e71e2e6 --- /dev/null +++ b/public/language/zh-CN/top.json @@ -0,0 +1,4 @@ +{ + "title": "置顶", + "no_top_topics": "没有置顶主题" +} \ No newline at end of file diff --git a/public/language/zh-CN/topic.json b/public/language/zh-CN/topic.json index 6491cd14ba..9d8a28c992 100644 --- a/public/language/zh-CN/topic.json +++ b/public/language/zh-CN/topic.json @@ -20,8 +20,8 @@ "login-to-view": "🔒登录查看", "edit": "编辑", "delete": "删除", - "delete-event": "Delete Event", - "delete-event-confirm": "Are you sure you want to delete this event?", + "delete-event": "删除元素", + "delete-event-confirm": "您确定要删除此元素吗?", "purge": "清除", "restore": "恢复", "move": "移动", @@ -39,14 +39,15 @@ "copy-ip": "复制IP", "ban-ip": "封禁IP", "view-history": "编辑历史", - "locked-by": "Locked by", - "unlocked-by": "Unlocked by", + "locked-by": "锁定自", + "unlocked-by": "解锁自", "pinned-by": "Pinned by", "unpinned-by": "Unpinned by", "deleted-by": "Deleted by", "restored-by": "Restored by", "moved-from-by": "Moved from %1 by", "queued-by": "Post queued for approval →", + "backlink": "Referenced by", "bookmark_instructions": "点击阅读本主题帖中的最新回复", "flag-post": "举报此帖", "flag-user": "举报此用户", @@ -138,6 +139,7 @@ "composer.handle_placeholder": "在这里输入您的姓名/昵称", "composer.discard": "撤销", "composer.submit": "提交", + "composer.additional-options": "Additional Options", "composer.schedule": "Schedule", "composer.replying_to": "正在回复 %1", "composer.new_topic": "新主题", @@ -158,6 +160,7 @@ "newest_to_oldest": "从新到旧", "most_votes": "最多赞同", "most_posts": "回复最多", + "most_views": "Most Views", "stale.title": "接受建议并创建新主题?", "stale.warning": "您回复的主题已经很古老了,是否发布新主题并引用此主题的内容?", "stale.create": "创建新主题", diff --git a/public/language/zh-CN/user.json b/public/language/zh-CN/user.json index ca479e9dd3..335df13c7b 100644 --- a/public/language/zh-CN/user.json +++ b/public/language/zh-CN/user.json @@ -94,6 +94,7 @@ "digest_off": "关闭", "digest_daily": "每天", "digest_weekly": "每周", + "digest_biweekly": "Bi-Weekly", "digest_monthly": "每月", "has_no_follower": "此用户还没有粉丝 :(", "follows_no_one": "此用户尚未关注任何人 :(", diff --git a/public/language/zh-TW/admin/advanced/events.json b/public/language/zh-TW/admin/advanced/events.json index d964ae59a8..5ecc44a954 100644 --- a/public/language/zh-TW/admin/advanced/events.json +++ b/public/language/zh-TW/admin/advanced/events.json @@ -3,6 +3,7 @@ "no-events": "暫無事件", "control-panel": "事件控制面板", "delete-events": "刪除事件", + "confirm-delete-all-events": "Are you sure you want to delete all logged events?", "filters": "過濾器", "filters-apply": "應用過濾器", "filter-type": "事件類型", diff --git a/public/language/zh-TW/admin/dashboard.json b/public/language/zh-TW/admin/dashboard.json index 90adfc831a..4b9601493e 100644 --- a/public/language/zh-TW/admin/dashboard.json +++ b/public/language/zh-TW/admin/dashboard.json @@ -56,8 +56,8 @@ "active-users.total": "全部", "active-users.connections": "連線", - "anonymous-registered-users": "匿名 vs 註冊使用者", - "anonymous": "匿名", + "guest-registered-users": "Guest vs Registered Users", + "guest": "Guest", "registered": "已註冊", "user-presence": "使用者光臨", @@ -68,6 +68,7 @@ "unread": "未讀", "high-presence-topics": "熱門主題", + "popular-searches": "Popular Searches", "graphs.page-views": "頁面瀏覽量", "graphs.page-views-registered": "註冊使用者頁面瀏覽量", @@ -75,13 +76,14 @@ "graphs.page-views-bot": "爬蟲頁面瀏覽量", "graphs.unique-visitors": "不重複訪客", "graphs.registered-users": "已註冊使用者", - "graphs.anonymous-users": "匿名使用者", + "graphs.guest-users": "Guest Users", "last-restarted-by": "上次重啟管理員/時間", "no-users-browsing": "沒有使用者正在瀏覽", "back-to-dashboard": "回到儀表板", "details.no-users": "沒有使用者有在選定的時間內註冊", "details.no-topics": "沒有新增的主題在選定的時間內", + "details.no-searches": "No searches have been made yet", "details.no-logins": "No logins have been recorded within the selected timeframe", "details.logins-static": "NodeBB only saves session data for %1 days, and so this table below will only show the most recently active sessions", "details.logins-login-time": "登入時間" diff --git a/public/language/zh-TW/admin/development/info.json b/public/language/zh-TW/admin/development/info.json index fc6c50e514..9b2d42dcfd 100644 --- a/public/language/zh-TW/admin/development/info.json +++ b/public/language/zh-TW/admin/development/info.json @@ -8,7 +8,11 @@ "nodejs": "nodejs", "online": "在線", "git": "git", - "memory": "記憶體", + "process-memory": "process memory", + "system-memory": "system memory", + "used-memory-process": "Used memory by process", + "used-memory-os": "Used system memory", + "total-memory-os": "Total system memory", "load": "系統負載", "cpu-usage": "CPU 使用情況", "uptime": "運行時間", diff --git a/public/language/zh-TW/admin/extend/rewards.json b/public/language/zh-TW/admin/extend/rewards.json index 19ef749362..924bc85f71 100644 --- a/public/language/zh-TW/admin/extend/rewards.json +++ b/public/language/zh-TW/admin/extend/rewards.json @@ -8,8 +8,6 @@ "delete": "刪除", "enable": "啟用", "disable": "禁用", - "control-panel": "獎勵控制", - "new-reward": "新獎勵", "alert.delete-success": "已成功刪除獎勵", "alert.no-inputs-found": "非法獎勵 - 輸入為空!", diff --git a/public/language/zh-TW/admin/general/dashboard.json b/public/language/zh-TW/admin/general/dashboard.json deleted file mode 100644 index 82b1238d28..0000000000 --- a/public/language/zh-TW/admin/general/dashboard.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "forum-traffic": "論壇流量", - "page-views": "頁面瀏覽量", - "unique-visitors": "不重複訪客", - "new-users": "新使用者", - "posts": "貼文", - "topics": "主題", - "page-views-seven": "最近7天", - "page-views-thirty": "最近30天", - "page-views-last-day": "最近24小時", - "page-views-custom": "自定義日期範圍", - "page-views-custom-start": "範圍開始", - "page-views-custom-end": "範圍結束", - "page-views-custom-help": "輸入您要查看的網頁瀏覽日期範圍。 如果沒有日期選擇器可用,則接受的格式是 YYYY-MM-DD", - "page-views-custom-error": "請輸入 YYYY-MM-DD格式的有效日期範圍 ", - - "stats.yesterday": "昨天", - "stats.today": "今天", - "stats.last-week": "上一週", - "stats.this-week": "本週", - "stats.last-month": "上一月", - "stats.this-month": "本月", - "stats.all": "總計", - - "updates": "更新", - "running-version": "您正在運行 NodeBB v%1 .", - "keep-updated": "請確保您已及時更新 NodeBB 以獲得最新的安全修補程式與 Bug 修復。", - "up-to-date": "

正在使用 最新版本

", - "upgrade-available": "

新的版本 (v%1) 已經發布。建議您 升級 NodeBB

", - "prerelease-upgrade-available": "

這是一個已經過期的預發佈版本的 NodeBB,新的版本 (v%1) 已經發布。建議您 升級 NodeBB

", - "prerelease-warning": "

正在使用測試版 NodeBB。可能會出現意外的 Bug。

", - "running-in-development": "論壇正處於開發模式,這可能使其暴露於潛在的危險之中;請聯繫您的系統管理員。", - "latest-lookup-failed": "

無法找到 NodeBB 的最新可用版本

", - - "notices": "提醒", - "restart-not-required": "不需要重啟", - "restart-required": "需要重啟", - "search-plugin-installed": "已安裝搜尋外掛", - "search-plugin-not-installed": "未安裝搜尋外掛", - "search-plugin-tooltip": "在外掛頁面安裝搜尋外掛來啟用搜尋功能", - - "control-panel": "系統控制", - "rebuild-and-restart": "重建 & 重啟", - "restart": "重啟", - "restart-warning": "重載或重啟 NodeBB 會丟失數秒內全部的連接。", - "restart-disabled": "重建和重新啟動NodeBB已被禁用,因為您似乎沒有通過適當的守護進程運行它。", - "maintenance-mode": "維護模式", - "maintenance-mode-title": "點擊此處設置 NodeBB 的維護模式", - "realtime-chart-updates": "即時圖表更新", - - "active-users": "活躍使用者", - "active-users.users": "使用者", - "active-users.guests": "訪客", - "active-users.total": "全部", - "active-users.connections": "連線", - - "anonymous-registered-users": "匿名 vs 註冊使用者", - "anonymous": "匿名", - "registered": "已註冊", - - "user-presence": "使用者光臨", - "on-categories": "在版面列表", - "reading-posts": "閱讀貼文", - "browsing-topics": "瀏覽主題", - "recent": "最近", - "unread": "未讀", - - "high-presence-topics": "熱門主題", - - "graphs.page-views": "頁面瀏覽量", - "graphs.page-views-registered": "註冊使用者頁面瀏覽量", - "graphs.page-views-guest": "訪客頁面瀏覽量", - "graphs.page-views-bot": "爬蟲頁面瀏覽量", - "graphs.unique-visitors": "不重複訪客", - "graphs.registered-users": "已註冊使用者", - "graphs.anonymous-users": "匿名使用者", - "last-restarted-by": "上次重啟管理員/時間", - "no-users-browsing": "沒有使用者正在瀏覽" -} diff --git a/public/language/zh-TW/admin/general/homepage.json b/public/language/zh-TW/admin/general/homepage.json deleted file mode 100644 index 28e579ad56..0000000000 --- a/public/language/zh-TW/admin/general/homepage.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "home-page": "首頁", - "description": "請選擇使用者到達根 URL 時所顯示的頁面。", - "home-page-route": "首頁路徑", - "custom-route": "自訂路徑", - "allow-user-home-pages": "允許使用者自訂首頁", - "home-page-title": "首頁標題(預設為“Home”)" -} \ No newline at end of file diff --git a/public/language/zh-TW/admin/general/languages.json b/public/language/zh-TW/admin/general/languages.json deleted file mode 100644 index c8f7db09de..0000000000 --- a/public/language/zh-TW/admin/general/languages.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "language-settings": "語言設定", - "description": "預設語言會決定所有使用者的語言設定。
單一使用者可以各自在帳戶設定中覆蓋此項設定。", - "default-language": "預設語言", - "auto-detect": "自動檢測訪客的語言設定" -} \ No newline at end of file diff --git a/public/language/zh-TW/admin/general/navigation.json b/public/language/zh-TW/admin/general/navigation.json deleted file mode 100644 index 15ac71b9a0..0000000000 --- a/public/language/zh-TW/admin/general/navigation.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "icon": "圖示:", - "change-icon": "更改", - "route": "路徑:", - "tooltip": "提示:", - "text": "文字:", - "text-class": "文字類別:可選", - "class": "類: 可選", - "id": "ID:可選", - - "properties": "屬性:", - "groups": "群組:", - "open-new-window": "在新窗口中打開", - - "btn.delete": "刪除", - "btn.disable": "禁用", - "btn.enable": "啟用", - - "available-menu-items": "可用的選單項目", - "custom-route": "自訂路徑", - "core": "核心", - "plugin": "外掛" -} \ No newline at end of file diff --git a/public/language/zh-TW/admin/general/social.json b/public/language/zh-TW/admin/general/social.json deleted file mode 100644 index aa16e3b8f5..0000000000 --- a/public/language/zh-TW/admin/general/social.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "post-sharing": "貼文分享", - "info-plugins-additional": "外掛可以增加額外用於分享貼文的社群媒體。", - "save-success": "已成功儲存貼文分享社群媒體。" -} \ No newline at end of file diff --git a/public/language/zh-TW/admin/general/sounds.json b/public/language/zh-TW/admin/general/sounds.json deleted file mode 100644 index e30202df06..0000000000 --- a/public/language/zh-TW/admin/general/sounds.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "notifications": "通知", - "chat-messages": "聊天訊息", - "play-sound": "播放", - "incoming-message": "收到的訊息", - "outgoing-message": "發出的訊息", - "upload-new-sound": "上傳新的音檔", - "saved": "設定已儲存" -} \ No newline at end of file diff --git a/public/language/zh-TW/admin/manage/digest.json b/public/language/zh-TW/admin/manage/digest.json index 85796e9f16..368cab446c 100644 --- a/public/language/zh-TW/admin/manage/digest.json +++ b/public/language/zh-TW/admin/manage/digest.json @@ -13,6 +13,7 @@ "resent-single": "摘要重發操作完成", "resent-day": "已發送每日摘要", "resent-week": "已發送每週摘要", + "resent-biweek": "Bi-Weekly digest resent", "resent-month": "已發送每月摘要", "null": "從不", "manual-run": "手動執行摘要:", diff --git a/public/language/zh-TW/admin/manage/users.json b/public/language/zh-TW/admin/manage/users.json index 43b4b84d09..c755feaf71 100644 --- a/public/language/zh-TW/admin/manage/users.json +++ b/public/language/zh-TW/admin/manage/users.json @@ -47,6 +47,7 @@ "users.uid": "UID", "users.username": "使用者名", "users.email": "電子郵件", + "users.no-email": "(no email)", "users.ip": "IP", "users.postcount": "發文數", "users.reputation": "聲望", diff --git a/public/language/zh-TW/admin/menu.json b/public/language/zh-TW/admin/menu.json index 77c408d4be..22213c257f 100644 --- a/public/language/zh-TW/admin/menu.json +++ b/public/language/zh-TW/admin/menu.json @@ -4,6 +4,7 @@ "dashboard/logins": "Logins", "dashboard/users": "Users", "dashboard/topics": "Topics", + "dashboard/searches": "Searches", "section-general": "基本", "section-manage": "管理", diff --git a/public/language/zh-TW/admin/settings/email.json b/public/language/zh-TW/admin/settings/email.json index 0939908f2e..bddc247003 100644 --- a/public/language/zh-TW/admin/settings/email.json +++ b/public/language/zh-TW/admin/settings/email.json @@ -6,7 +6,7 @@ "from-help": "用於郵件中顯示的發送人", "smtp-transport": "SMTP 通信", - "smtp-transport.enabled": "使用一個外部電子郵件系統來發送郵件", + "smtp-transport.enabled": "Enable SMTP Transport", "smtp-transport-help": "您可以從列表中選取一個已知的服務或自訂。", "smtp-transport.service": "選擇服務", "smtp-transport.service-custom": "自訂", @@ -37,6 +37,10 @@ "subscriptions.hour": "摘要小時", "subscriptions.hour-help": "請輸入一個代表小時的數字來發送排程的電子郵件摘要 (例如,對於午夜,0,對於下午5:00,17)。 請記住,這是根據伺服器本身的時間,可能與您的系統時鐘不完全符合。
伺服器的大致時間為:
下一個每日摘要被排程在發送", "notifications.remove-images": "Remove images from email notifications", + "require-email-address": "Require new users to specify an email address", + "require-email-address-warning": "By default, users can opt-out of entering an email address. Enabling this option means they have to enter an email address in order to proceed with registration. It does not ensure user will enter a real email address, nor even an address they own.", "include-unverified-emails": "Send emails to recipients who have not explicitly confirmed their emails", - "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws." -} \ No newline at end of file + "include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). Enable this setting at your own risk – sending emails to unverified addresses may be a violation of regional anti-spam laws.", + "prompt": "Prompt users to enter or confirm their emails", + "prompt-help": "If a user does not have an email set, or their email is not confirmed, a warning will be shown on screen." +} diff --git a/public/language/zh-TW/admin/settings/general.json b/public/language/zh-TW/admin/settings/general.json index 6ed79255b4..28f7187fee 100644 --- a/public/language/zh-TW/admin/settings/general.json +++ b/public/language/zh-TW/admin/settings/general.json @@ -3,9 +3,9 @@ "title": "網站標題", "title.short": "短標題", "title.short-placeholder": "如短標題為指定則會使用網站標題", - "title.url": "網址", + "title.url": "Title Link URL", "title.url-placeholder": "網站標題連結", - "title.url-help": "當標題被點擊,使用者將轉導到該地址。如果留白,使用者將跳轉到論壇首頁。", + "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "title.name": "您的社區名稱", "title.show-in-header": "在頂部顯示網站標題", "browser-title": "瀏覽器標題", @@ -20,9 +20,9 @@ "logo.image": "圖檔", "logo.image-placeholder": "要在論壇標題上顯示的 Logo 的路徑", "logo.upload": "上傳", - "logo.url": "網址", + "logo.url": "Logo Link URL", "logo.url-placeholder": "網站 Logo 連結", - "logo.url-help": "當 Logo 被點擊時,將使用者轉導到此地址。如果留白,使用者將被轉導到論壇首頁。", + "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "logo.alt-text": "替代文字", "log.alt-text-placeholder": "輔助功能的替代文字", "favicon": "網站圖示", @@ -47,4 +47,4 @@ "undo-timeout": "Undo Timeout", "undo-timeout-help": "Some operations such as moving topics will allow for the moderator to undo their action within a certain timeframe. Set to 0 to disable undo completely.", "topic-tools": "Topic Tools" -} \ No newline at end of file +} diff --git a/public/language/zh-TW/admin/settings/navigation.json b/public/language/zh-TW/admin/settings/navigation.json index 15ac71b9a0..de20547ab9 100644 --- a/public/language/zh-TW/admin/settings/navigation.json +++ b/public/language/zh-TW/admin/settings/navigation.json @@ -11,6 +11,8 @@ "properties": "屬性:", "groups": "群組:", "open-new-window": "在新窗口中打開", + "dropdown": "Dropdown", + "dropdown-placeholder": "Place your dropdown menu items below, ie:
<li><a href="https://myforum.com">Link 1</a></li>", "btn.delete": "刪除", "btn.disable": "禁用", @@ -20,4 +22,4 @@ "custom-route": "自訂路徑", "core": "核心", "plugin": "外掛" -} \ No newline at end of file +} diff --git a/public/language/zh-TW/admin/settings/notifications.json b/public/language/zh-TW/admin/settings/notifications.json index 34a1ae3d31..991f4e19d6 100644 --- a/public/language/zh-TW/admin/settings/notifications.json +++ b/public/language/zh-TW/admin/settings/notifications.json @@ -2,5 +2,6 @@ "notifications": "通知", "welcome-notification": "歡迎通知", "welcome-notification-link": "歡迎通知連結", - "welcome-notification-uid": "歡迎通知使用者 (UID)" + "welcome-notification-uid": "歡迎通知使用者 (UID)", + "post-queue-notification-uid": "Post Queue User (UID)" } \ No newline at end of file diff --git a/public/language/zh-TW/admin/settings/post.json b/public/language/zh-TW/admin/settings/post.json index eccfaf3af7..057bfe0e6b 100644 --- a/public/language/zh-TW/admin/settings/post.json +++ b/public/language/zh-TW/admin/settings/post.json @@ -40,6 +40,7 @@ "teaser.last-post": "最後– 顯示最新的貼文,包括原帖,如果沒有回覆", "teaser.last-reply": "最後– 顯示最新回覆,如果沒有回覆,則顯示“無回覆”佔位符", "teaser.first": "第一", + "showPostPreviewsOnHover": "Show a preview of posts when mouse overed", "unread": "未讀設定", "unread.cutoff": "未讀截止時間(天)", "unread.min-track-last": "跟蹤最後閱讀之前的主題最小貼文", @@ -56,6 +57,9 @@ "composer.show-help": "顯示“幫助”選項卡", "composer.enable-plugin-help": "允許外掛添加內容到幫助選項卡", "composer.custom-help": "自訂幫助文字", + "backlinks": "Backlinks", + "backlinks.enabled": "Enable topic backlinks", + "backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.", "ip-tracking": "IP 跟蹤", "ip-tracking.each-post": "跟蹤每個貼文的 IP 地址", "enable-post-history": "啟用回覆歷史" diff --git a/public/language/zh-TW/admin/settings/user.json b/public/language/zh-TW/admin/settings/user.json index b01a691a39..2e73dd1945 100644 --- a/public/language/zh-TW/admin/settings/user.json +++ b/public/language/zh-TW/admin/settings/user.json @@ -71,6 +71,7 @@ "digest-freq.off": "關閉", "digest-freq.daily": "每日", "digest-freq.weekly": "每週", + "digest-freq.biweekly": "Bi-Weekly", "digest-freq.monthly": "每月", "email-chat-notifs": "當我不在線並收到新的聊天訊息時,給我發送電郵通知", "email-post-notif": "當我訂閱的主題有新回覆時,給我發送電郵通知", diff --git a/public/language/zh-TW/category.json b/public/language/zh-TW/category.json index 66cdd37f8c..1d5770ad7f 100644 --- a/public/language/zh-TW/category.json +++ b/public/language/zh-TW/category.json @@ -19,5 +19,5 @@ "notwatching.message": "您未關注了此版面和全部子版面的動態。", "ignoring.message": "您忽略了此版面和全部子版面的動態。", "watched-categories": "已關注的版面", - "x-more-categories": "%1 more categories" + "x-more-categories": "還有 %1 個版面" } \ No newline at end of file diff --git a/public/language/zh-TW/email.json b/public/language/zh-TW/email.json index 971cb7a6b7..769259fb67 100644 --- a/public/language/zh-TW/email.json +++ b/public/language/zh-TW/email.json @@ -6,9 +6,9 @@ "greeting_no_name": "您好", "greeting_with_name": "%1,您好", "email.verify-your-email.subject": "請驗證你的電子信箱", - "email.verify.text1": "You've requested that we change or confirm your email address", - "email.verify.text2": "For security purposes, we only change or confirm the email address on file once its ownership has been confirmed via email. If you did not request this, no action is required on your part.", - "email.verify.text3": "Once you confirm this email address, we will replace your current email address with this one (%1).", + "email.verify.text1": "您要求我們更改或者驗證您的電子信箱地址", + "email.verify.text2": "為了安全起見,我們只會在透過電子郵件確認過電子信箱所有權後才會更改登錄的信箱地址。 假如您沒有提出過這個要求, 您不用進行任何動作。", + "email.verify.text3": "一旦您確認此信箱地址,我們將使用此地址取代您目前的信箱地址(%1)。", "welcome.text1": "感謝您註冊 %1 帳戶!", "welcome.text2": "在您驗證您綁定的郵件地址之後,您的帳戶才能啟用。", "welcome.text3": "管理員批准了您的註冊申請,現在您可以登入您的帳戶了。", @@ -23,8 +23,8 @@ "reset.notify.text1": "您在 %1 上的密碼已經成功修改。", "reset.notify.text2": "如果您沒有授權此操作,請立即聯繫管理員。", "digest.latest_topics": "來自 %1 的最新主題", - "digest.top-topics": "Top topics from %1", - "digest.popular-topics": "Popular topics from %1", + "digest.top-topics": "來自 %1 的置頂主題", + "digest.popular-topics": "來自 %1 的熱門主題", "digest.cta": "點擊這裡訪問 %1", "digest.unsub.info": "根據您的訂閱設定,為您發送此摘要。", "digest.day": "天", diff --git a/public/language/zh-TW/error.json b/public/language/zh-TW/error.json index b44267318d..00e6cc398c 100644 --- a/public/language/zh-TW/error.json +++ b/public/language/zh-TW/error.json @@ -34,8 +34,9 @@ "email-invited": "Email was already invited", "email-not-confirmed": "Posting in some categories or topics is enabled once your email is confirmed, please click here to send a confirmation email.", "email-not-confirmed-chat": "您的電子信箱尚未確認,無法聊天,請點擊這裡確認您的電子信箱。", - "email-not-confirmed-email-sent": "您的電子信箱尚未確認,請檢查您的收件匣。在電子信箱被確認前您不能貼文和聊天。", - "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery. Please click here to enter an email.", + "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You may not be able to post in some categories or chat until your email is confirmed.", + "no-email-to-confirm": "Your account does not have an email set. An email is necessary for account recovery, and may be necessary for chatting and posting in some categories. Please click here to enter an email.", + "user-doesnt-have-email": "User \"%1\" does not have an email set.", "email-confirm-failed": "我們無法確認您的電子信箱,請重試", "confirm-email-already-sent": "確認郵件已發出,如需重新發送請等待 %1 分鐘後再試。", "sendmail-not-found": "無法找到 sendmail 可執行檔,請確保 sendmail 已經安裝並可被運行 NodeBB 的系統帳戶執行", @@ -103,6 +104,7 @@ "already-bookmarked": "您已將此貼文存為了書籤", "already-unbookmarked": "您已移除了此貼文的書籤", "cant-ban-other-admins": "您不能封鎖其他管理員!", + "cant-make-banned-users-admin": "You can't make banned users admin.", "cant-remove-last-admin": "您是唯一的管理員。在刪除您的管理員權限前,請增加另一個管理員。", "account-deletion-disabled": "Account deletion is disabled", "cant-delete-admin": "在刪除該帳戶之前,請先移除其管理權限。", diff --git a/public/language/zh-TW/modules.json b/public/language/zh-TW/modules.json index 3049796eab..0c8ac551ba 100644 --- a/public/language/zh-TW/modules.json +++ b/public/language/zh-TW/modules.json @@ -54,7 +54,7 @@ "composer.formatting.strikethrough": "刪除線", "composer.formatting.code": "程式碼", "composer.formatting.link": "連結", - "composer.formatting.picture": "圖片", + "composer.formatting.picture": "Image Link", "composer.upload-picture": "上傳圖片", "composer.upload-file": "上傳檔案", "composer.zen_mode": "無干擾模式", diff --git a/public/language/zh-TW/top.json b/public/language/zh-TW/top.json new file mode 100644 index 0000000000..62fae776f8 --- /dev/null +++ b/public/language/zh-TW/top.json @@ -0,0 +1,4 @@ +{ + "title": "Top", + "no_top_topics": "無置頂主題" +} \ No newline at end of file diff --git a/public/language/zh-TW/topic.json b/public/language/zh-TW/topic.json index 1b7844dca8..c967c2b415 100644 --- a/public/language/zh-TW/topic.json +++ b/public/language/zh-TW/topic.json @@ -1,6 +1,6 @@ { "topic": "主題", - "title": "Title", + "title": "標題", "no_topics_found": "沒有找到主題!", "no_posts_found": "沒有找到回覆!", "post_is_deleted": "此回覆已被刪除!", @@ -47,6 +47,7 @@ "restored-by": "Restored by", "moved-from-by": "Moved from %1 by", "queued-by": "Post queued for approval →", + "backlink": "Referenced by", "bookmark_instructions": "點擊閱讀本主題貼文中的最新回覆", "flag-post": "Flag this post", "flag-user": "Flag this user", @@ -138,6 +139,7 @@ "composer.handle_placeholder": "在此輸入您的名稱/代稱", "composer.discard": "撤銷", "composer.submit": "提交", + "composer.additional-options": "Additional Options", "composer.schedule": "Schedule", "composer.replying_to": "正在回覆 %1", "composer.new_topic": "新主題", @@ -158,6 +160,7 @@ "newest_to_oldest": "從新到舊", "most_votes": "最多點贊", "most_posts": "回覆最多", + "most_views": "Most Views", "stale.title": "接受建議並建立新主題?", "stale.warning": "您回覆的主題已經很古老了,是否發佈新主題並引用此主題的內容?", "stale.create": "建立新主題", diff --git a/public/language/zh-TW/user.json b/public/language/zh-TW/user.json index 836434b84d..23924c8386 100644 --- a/public/language/zh-TW/user.json +++ b/public/language/zh-TW/user.json @@ -8,20 +8,20 @@ "email": "電子信箱", "confirm_email": "確認電子信箱", "account_info": "帳戶訊息", - "admin_actions_label": "Administrative Actions", + "admin_actions_label": "管理行動", "ban_account": "禁用帳戶", "ban_account_confirm": "您確定要禁用此帳戶、嗎?", "unban_account": "解禁帳戶", "delete_account": "刪除帳戶", - "delete_account_as_admin": "Delete Account", - "delete_content": "Delete Account Content", + "delete_account_as_admin": "刪除 帳戶", + "delete_content": "刪除帳戶 內容", "delete_all": "Delete Account and Content", "delete_account_confirm": "Are you sure you want to anonymize your posts and delete your account?
This action is irreversible and you will not be able to recover any of your data

Enter your password to confirm that you wish to destroy this account.", "delete_this_account_confirm": "Are you sure you want to delete this account while leaving its contents behind?
This action is irreversible, posts will be anonymized, and you will not be able to restore post associations with the deleted account

", "delete_account_content_confirm": "Are you sure you want to delete this account's content (posts/topics/uploads)?
This action is irreversible and you will not be able to recover any data

", "delete_all_confirm": "Are you sure you want to delete this account and all of its content (posts/topics/uploads)?
This action is irreversible and you will not be able to recover any data

", "account-deleted": "帳戶已刪除", - "account-content-deleted": "Account content deleted", + "account-content-deleted": "帳戶內容已刪除", "fullname": "姓名", "website": "網站", "location": "位置", @@ -94,6 +94,7 @@ "digest_off": "關閉", "digest_daily": "每天", "digest_weekly": "每週", + "digest_biweekly": "Bi-Weekly", "digest_monthly": "每月", "has_no_follower": "此使用者還沒有追隨者 :(", "follows_no_one": "此使用者尚未追隨任何人 :(", diff --git a/public/language/zh-TW/users.json b/public/language/zh-TW/users.json index 07e076f86c..6af6963b18 100644 --- a/public/language/zh-TW/users.json +++ b/public/language/zh-TW/users.json @@ -11,7 +11,7 @@ "online-only": "只看在線", "invite": "邀請註冊", "prompt-email": "郵件:", - "groups-to-join": "Groups to be joined when invite is accepted:", + "groups-to-join": "同意邀請後要加入的群組:", "invitation-email-sent": "已發送邀請給 %1", "user_list": "使用者列表", "recent_topics": "最新主題", diff --git a/public/less/admin/extend/rewards.less b/public/less/admin/extend/rewards.less index 2dc84b5bc3..e90eb3e717 100644 --- a/public/less/admin/extend/rewards.less +++ b/public/less/admin/extend/rewards.less @@ -16,6 +16,39 @@ > li { border-bottom: 1px solid #ddd; margin-bottom: 20px; + &:last-child { + border-bottom: 0; + } + } + } + + .rewards { width: 100%; } + + .well { + border-radius: 2px; + border-width: 2px; + color: #333; + + &.if-block { + border-color: @brand-primary; + max-width: 33%; + } + &.this-block { + border-color: @brand-warning; + max-width: 33%; + } + &.then-block { + border-color: @brand-success; + max-width: 33%; + } + &.reward-block { + border-color: @brand-success; + background-color: lighten(@brand-success, 15%); + color: #fff; + a, select, input { color: #fff; } + select > option { color: #333; } + width: 100%; + min-height: 110px; } } } \ No newline at end of file diff --git a/public/less/admin/general/dashboard.less b/public/less/admin/general/dashboard.less index e42c1ef8ac..f7e3f8ff72 100644 --- a/public/less/admin/general/dashboard.less +++ b/public/less/admin/general/dashboard.less @@ -124,7 +124,7 @@ border-color: rgba(151,187,205,1); background-color: rgba(151,187,205,0.2); } - &.anonymous { + &.guest { border-color: #46BFBD; background-color: #5AD3D1; } diff --git a/public/less/admin/header.less b/public/less/admin/header.less index 969a9d7ca5..585ac2fe37 100644 --- a/public/less/admin/header.less +++ b/public/less/admin/header.less @@ -98,10 +98,16 @@ > li { float: left; margin-right: 30px; + padding-bottom: 10px; + border-bottom: 4px solid transparent; + transition: border-color 150ms linear; + + &:hover { + border-color: darken(@brand-primary, 20%); + } &.active { - border-bottom: 4px solid @brand-primary; - padding-bottom: 10px; + border-color: @brand-primary; } > a { diff --git a/public/less/admin/manage/groups.less b/public/less/admin/manage/groups.less index dda50c5c54..2183cc748c 100644 --- a/public/less/admin/manage/groups.less +++ b/public/less/admin/manage/groups.less @@ -35,4 +35,18 @@ [component="category/list"] li { cursor: pointer; } + + .fa-nbb-none { + border: 1px dotted black; + height: 35px; + width: 35px; + } + + .fa-icons .fa-nbb-none { + vertical-align: -6px; + } + + #group-icon-preview.fa-nbb-none { + display: none; + } } \ No newline at end of file diff --git a/public/less/generics.less b/public/less/generics.less index 81ffd073ca..d7cd2b3ec3 100644 --- a/public/less/generics.less +++ b/public/less/generics.less @@ -39,7 +39,7 @@ &.disabled { background-color: #888!important; - .opacity(0.5); + opacity: 0.5; } } } diff --git a/public/less/mixins.less b/public/less/mixins.less index 57a54ea32b..04259bb089 100644 --- a/public/less/mixins.less +++ b/public/less/mixins.less @@ -32,13 +32,6 @@ } } -.opacity(@opacity: 1) { - -moz-opacity: @opacity; - opacity: @opacity; - -ms-filter: ~`"progid:DXImageTransform.Microsoft.Alpha(opacity=(" + "@{opacity}" * 100 + "))"`; - filter: ~`"alpha(opacity = (" + "@{opacity}" * 100 + "))"`; -} - .border-radius (@radius: 5px) { -webkit-border-radius: @radius; -moz-border-radius: @radius; diff --git a/public/openapi/read.yaml b/public/openapi/read.yaml index 6605079d47..2adc245b18 100644 --- a/public/openapi/read.yaml +++ b/public/openapi/read.yaml @@ -78,6 +78,8 @@ paths: $ref: 'read/admin/dashboard/users.yaml' /api/admin/dashboard/topics: $ref: 'read/admin/dashboard/topics.yaml' + /api/admin/dashboard/searches: + $ref: 'read/admin/dashboard/searches.yaml' "/api/admin/settings/{term}": $ref: 'read/admin/settings/term.yaml' /api/admin/settings/languages: @@ -178,12 +180,14 @@ paths: $ref: 'read/user/username/username.yaml' "/api/user/email/{email}": $ref: 'read/user/email/email.yaml' - "/api/user/uid/{userslug}/export/posts": - $ref: 'read/user/uid/userslug/export/posts.yaml' - "/api/user/uid/{userslug}/export/uploads": - $ref: 'read/user/uid/userslug/export/uploads.yaml' - "/api/user/uid/{userslug}/export/profile": - $ref: 'read/user/uid/userslug/export/profile.yaml' + "/api/user/{userslug}/export/posts": + $ref: 'read/user/userslug/export/posts.yaml' + "/api/user/{userslug}/export/uploads": + $ref: 'read/user/userslug/export/uploads.yaml' + "/api/user/{userslug}/export/profile": + $ref: 'read/user/userslug/export/profile.yaml' + "/api/user/uid/{userslug}/export/{type}": + $ref: 'read/user/uid/userslug/export/type.yaml' /api/categories: $ref: 'read/categories.yaml' "/api/categories/{cid}/moderators": diff --git a/public/openapi/read/admin/dashboard/searches.yaml b/public/openapi/read/admin/dashboard/searches.yaml new file mode 100644 index 0000000000..7097c8bcb8 --- /dev/null +++ b/public/openapi/read/admin/dashboard/searches.yaml @@ -0,0 +1,25 @@ +get: + tags: + - admin + summary: Get detailed user registration analytics + responses: + "200": + description: A JSON object containing popular searches. + content: + application/json: + schema: + allOf: + - type: object + properties: + searches: + type: array + items: + type: object + properties: + value: + type: string + description: The string that was searched + score: + type: number + description: Number of times this string has been searched + - $ref: ../../../components/schemas/CommonProps.yaml#/CommonProps \ No newline at end of file diff --git a/public/openapi/read/user/uid/userslug/export/type.yaml b/public/openapi/read/user/uid/userslug/export/type.yaml new file mode 100644 index 0000000000..e0ea7d93d1 --- /dev/null +++ b/public/openapi/read/user/uid/userslug/export/type.yaml @@ -0,0 +1,19 @@ +get: + tags: + - deprecated + summary: Export a user's posts/profile/uploads (.csv) + parameters: + - name: userslug + in: path + required: true + schema: + type: string + example: admin + responses: + "302": + description: A redirect to the new URL format (without the `/uid` prefix) + headers: + Location: + schema: + type: string + example: /api/user/admin/export/posts \ No newline at end of file diff --git a/public/openapi/read/user/uid/userslug/export/posts.yaml b/public/openapi/read/user/userslug/export/posts.yaml similarity index 100% rename from public/openapi/read/user/uid/userslug/export/posts.yaml rename to public/openapi/read/user/userslug/export/posts.yaml diff --git a/public/openapi/read/user/uid/userslug/export/profile.yaml b/public/openapi/read/user/userslug/export/profile.yaml similarity index 100% rename from public/openapi/read/user/uid/userslug/export/profile.yaml rename to public/openapi/read/user/userslug/export/profile.yaml diff --git a/public/openapi/read/user/uid/userslug/export/uploads.yaml b/public/openapi/read/user/userslug/export/uploads.yaml similarity index 100% rename from public/openapi/read/user/uid/userslug/export/uploads.yaml rename to public/openapi/read/user/userslug/export/uploads.yaml diff --git a/public/openapi/write.yaml b/public/openapi/write.yaml index 8ac39fe982..734b64ff24 100644 --- a/public/openapi/write.yaml +++ b/public/openapi/write.yaml @@ -52,6 +52,8 @@ paths: $ref: 'write/users.yaml' /users/{uid}: $ref: 'write/users/uid.yaml' + /users/{uid}/picture: + $ref: 'write/users/uid/picture.yaml' /users/{uid}/content: $ref: 'write/users/uid/content.yaml' /users/{uid}/account: diff --git a/public/openapi/write/login.yaml b/public/openapi/write/login.yaml index 43ba2f8cb9..001cd5da01 100644 --- a/public/openapi/write/login.yaml +++ b/public/openapi/write/login.yaml @@ -4,6 +4,8 @@ post: summary: verify login credentials description: | This route accepts a username/password or email/password pair (dependent on forum settings), returning a standard user object if credentials are validated successfully. + This route also initializes a standard login session and returns a valid cookie that can be used in subsequent API calls as though it were a browser session. + **Note**: Cookie-based sessions require a CSRF token for non-`GET` routes. requestBody: content: application/json: diff --git a/public/openapi/write/users/uid/picture.yaml b/public/openapi/write/users/uid/picture.yaml new file mode 100644 index 0000000000..d6498a0af2 --- /dev/null +++ b/public/openapi/write/users/uid/picture.yaml @@ -0,0 +1,43 @@ +put: + tags: + - users + summary: update user picture or icon background colour + parameters: + - in: path + name: uid + schema: + type: integer + required: true + description: uid of the user + example: 1 + requestBody: + content: + application/json: + schema: + type: object + properties: + type: + type: string + description: The source of the picture + enum: ['default', 'uploaded', 'external'] + example: default + url: + type: string + description: Only used for `external` type, specifies the source of the external image to use as avatar + example: '' + bgColor: + type: string + description: A hexadecimal colour representation + example: '#ff0000' + responses: + '200': + description: successfully updated user picture + content: + application/json: + schema: + type: object + properties: + status: + $ref: ../../../components/schemas/Status.yaml#/Status + response: + type: object \ No newline at end of file diff --git a/public/src/admin/admin.js b/public/src/admin/admin.js index bdc6afb35f..a91572c009 100644 --- a/public/src/admin/admin.js +++ b/public/src/admin/admin.js @@ -1,8 +1,8 @@ 'use strict'; (function () { - var logoutTimer = 0; - var logoutMessage; + let logoutTimer = 0; + let logoutMessage; function startLogoutTimer() { if (app.config.adminReloginDuration <= 0) { return; @@ -20,12 +20,14 @@ } logoutTimer = setTimeout(function () { - bootbox.alert({ - closeButton: false, - message: logoutMessage, - callback: function () { - window.location.reload(); - }, + require(['bootbox'], function (bootbox) { + bootbox.alert({ + closeButton: false, + message: logoutMessage, + callback: function () { + window.location.reload(); + }, + }); }); }, 3600000); } @@ -52,7 +54,9 @@ } $('[component="logout"]').on('click', function () { - app.logout(); + require(['logout'], function (logout) { + logout(); + }); return false; }); @@ -92,11 +96,11 @@ } url = [config.relative_path, url].join('/'); - var fallback; + let fallback; $('#main-menu li').removeClass('active'); $('#main-menu a').removeClass('active').filter('[href="' + url + '"]').each(function () { - var menu = $(this); + const menu = $(this); if (menu.parent().attr('data-link')) { return; } @@ -107,13 +111,13 @@ fallback = menu.text(); }); - var mainTitle; - var pageTitle; + let mainTitle; + let pageTitle; if (/admin\/plugins\//.test(url)) { mainTitle = fallback; pageTitle = '[[admin/menu:section-plugins]] > ' + mainTitle; } else { - var matches = url.match(/admin\/(.+?)\/(.+?)$/); + const matches = url.match(/admin\/(.+?)\/(.+?)$/); if (matches) { mainTitle = '[[admin/menu:' + matches[1] + '/' + matches[2] + ']]'; pageTitle = '[[admin/menu:section-' + @@ -141,31 +145,35 @@ function setupRestartLinks() { $('.rebuild-and-restart').off('click').on('click', function () { - bootbox.confirm('[[admin/admin:alert.confirm-rebuild-and-restart]]', function (confirm) { - if (confirm) { - require(['admin/modules/instance'], function (instance) { - instance.rebuildAndRestart(); - }); - } + require(['bootbox'], function (bootbox) { + bootbox.confirm('[[admin/admin:alert.confirm-rebuild-and-restart]]', function (confirm) { + if (confirm) { + require(['admin/modules/instance'], function (instance) { + instance.rebuildAndRestart(); + }); + } + }); }); }); $('.restart').off('click').on('click', function () { - bootbox.confirm('[[admin/admin:alert.confirm-restart]]', function (confirm) { - if (confirm) { - require(['admin/modules/instance'], function (instance) { - instance.restart(); - }); - } + require(['bootbox'], function (bootbox) { + bootbox.confirm('[[admin/admin:alert.confirm-restart]]', function (confirm) { + if (confirm) { + require(['admin/modules/instance'], function (instance) { + instance.restart(); + }); + } + }); }); }); } function configureSlidemenu() { require(['slideout'], function (Slideout) { - var env = utils.findBootstrapEnvironment(); + let env = utils.findBootstrapEnvironment(); - var slideout = new Slideout({ + const slideout = new Slideout({ panel: document.getElementById('panel'), menu: document.getElementById('menu'), padding: 256, diff --git a/public/src/admin/advanced/cache.js b/public/src/admin/advanced/cache.js index 7a97a09e8d..7bb1cbff7c 100644 --- a/public/src/admin/advanced/cache.js +++ b/public/src/admin/advanced/cache.js @@ -1,14 +1,14 @@ 'use strict'; define('admin/advanced/cache', function () { - var Cache = {}; + const Cache = {}; Cache.init = function () { require(['admin/settings'], function (Settings) { Settings.prepare(); }); $('.clear').on('click', function () { - var name = $(this).attr('data-name'); + const name = $(this).attr('data-name'); socket.emit('admin.cache.clear', { name: name }, function (err) { if (err) { return app.alertError(err.message); @@ -18,9 +18,9 @@ define('admin/advanced/cache', function () { }); $('.checkbox').on('change', function () { - var input = $(this).find('input'); - var flag = input.is(':checked'); - var name = $(this).attr('data-name'); + const input = $(this).find('input'); + const flag = input.is(':checked'); + const name = $(this).attr('data-name'); socket.emit('admin.cache.toggle', { name: name, enabled: flag }, function (err) { if (err) { return app.alertError(err.message); diff --git a/public/src/admin/advanced/errors.js b/public/src/admin/advanced/errors.js index 6356d5690b..ac9e7db071 100644 --- a/public/src/admin/advanced/errors.js +++ b/public/src/admin/advanced/errors.js @@ -1,8 +1,8 @@ 'use strict'; -define('admin/advanced/errors', ['Chart'], function (Chart) { - var Errors = {}; +define('admin/advanced/errors', ['bootbox', 'Chart'], function (bootbox, Chart) { + const Errors = {}; Errors.init = function () { Errors.setupCharts(); @@ -26,9 +26,9 @@ define('admin/advanced/errors', ['Chart'], function (Chart) { }; Errors.setupCharts = function () { - var notFoundCanvas = document.getElementById('not-found'); - var tooBusyCanvas = document.getElementById('toobusy'); - var dailyLabels = utils.getDaysArray(); + const notFoundCanvas = document.getElementById('not-found'); + const tooBusyCanvas = document.getElementById('toobusy'); + let dailyLabels = utils.getDaysArray(); dailyLabels = dailyLabels.slice(-7); @@ -36,7 +36,7 @@ define('admin/advanced/errors', ['Chart'], function (Chart) { Chart.defaults.global.tooltips.enabled = false; } - var data = { + const data = { 'not-found': { labels: dailyLabels, datasets: [ diff --git a/public/src/admin/advanced/events.js b/public/src/admin/advanced/events.js index 147ad193e5..0ebc945a80 100644 --- a/public/src/admin/advanced/events.js +++ b/public/src/admin/advanced/events.js @@ -1,27 +1,31 @@ 'use strict'; -define('admin/advanced/events', function () { - var Events = {}; +define('admin/advanced/events', ['bootbox'], function (bootbox) { + const Events = {}; Events.init = function () { $('[data-action="clear"]').on('click', function () { - socket.emit('admin.deleteAllEvents', function (err) { - if (err) { - return app.alertError(err.message); + bootbox.confirm('[[admin/advanced/events:confirm-delete-all-events]]', (confirm) => { + if (confirm) { + socket.emit('admin.deleteAllEvents', function (err) { + if (err) { + return app.alertError(err.message); + } + $('.events-list').empty(); + }); } - $('.events-list').empty(); }); }); $('.delete-event').on('click', function () { - var parentEl = $(this).parents('[data-eid]'); - var eid = parentEl.attr('data-eid'); + const $parentEl = $(this).parents('[data-eid]'); + const eid = $parentEl.attr('data-eid'); socket.emit('admin.deleteEvents', [eid], function (err) { if (err) { return app.alertError(err.message); } - parentEl.remove(); + $parentEl.remove(); }); }); @@ -31,8 +35,8 @@ define('admin/advanced/events', function () { Events.refresh = function (event) { event.preventDefault(); - var formEl = $('#filters'); - ajaxify.go('admin/advanced/events?' + formEl.serialize()); + const $formEl = $('#filters'); + ajaxify.go('admin/advanced/events?' + $formEl.serialize()); }; return Events; diff --git a/public/src/admin/advanced/logs.js b/public/src/admin/advanced/logs.js index 42aebba080..6a236b49e3 100644 --- a/public/src/admin/advanced/logs.js +++ b/public/src/admin/advanced/logs.js @@ -2,17 +2,17 @@ define('admin/advanced/logs', function () { - var Logs = {}; + const Logs = {}; Logs.init = function () { - var logsEl = $('.logs pre'); + const logsEl = $('.logs pre'); logsEl.scrollTop(logsEl.prop('scrollHeight')); // Affix menu $('.affix').affix(); $('.logs').find('button[data-action]').on('click', function () { - var btnEl = $(this); - var action = btnEl.attr('data-action'); + const btnEl = $(this); + const action = btnEl.attr('data-action'); switch (action) { case 'reload': diff --git a/public/src/admin/appearance/customise.js b/public/src/admin/appearance/customise.js index 70393a50ae..d0a95050fd 100644 --- a/public/src/admin/appearance/customise.js +++ b/public/src/admin/appearance/customise.js @@ -1,7 +1,7 @@ 'use strict'; define('admin/appearance/customise', ['admin/settings', 'ace/ace'], function (Settings, ace) { - var Customise = {}; + const Customise = {}; Customise.init = function () { Settings.prepare(function () { @@ -9,9 +9,9 @@ define('admin/appearance/customise', ['admin/settings', 'ace/ace'], function (Se $('#customJS').text($('#customJS-holder').val()); $('#customHTML').text($('#customHTML-holder').val()); - var customCSS = ace.edit('customCSS'); - var customJS = ace.edit('customJS'); - var customHTML = ace.edit('customHTML'); + const customCSS = ace.edit('customCSS'); + const customJS = ace.edit('customJS'); + const customHTML = ace.edit('customHTML'); customCSS.setTheme('ace/theme/twilight'); customCSS.getSession().setMode('ace/mode/less'); diff --git a/public/src/admin/appearance/skins.js b/public/src/admin/appearance/skins.js index 157d227ba6..653742bd7d 100644 --- a/public/src/admin/appearance/skins.js +++ b/public/src/admin/appearance/skins.js @@ -2,7 +2,7 @@ define('admin/appearance/skins', ['translator'], function (translator) { - var Skins = {}; + const Skins = {}; Skins.init = function () { // Populate skins from Bootswatch API @@ -12,19 +12,19 @@ define('admin/appearance/skins', ['translator'], function (translator) { }).done(Skins.render); $('#skins').on('click', function (e) { - var target = $(e.target); + let target = $(e.target); if (!target.attr('data-action')) { target = target.parents('[data-action]'); } - var action = target.attr('data-action'); + const action = target.attr('data-action'); if (action && action === 'use') { - var parentEl = target.parents('[data-theme]'); - var themeType = parentEl.attr('data-type'); - var cssSrc = parentEl.attr('data-css'); - var themeId = parentEl.attr('data-theme'); + const parentEl = target.parents('[data-theme]'); + const themeType = parentEl.attr('data-type'); + const cssSrc = parentEl.attr('data-css'); + const themeId = parentEl.attr('data-theme'); socket.emit('admin.themes.set', { @@ -50,7 +50,7 @@ define('admin/appearance/skins', ['translator'], function (translator) { }; Skins.render = function (bootswatch) { - var themeContainer = $('#bootstrap_themes'); + const themeContainer = $('#bootstrap_themes'); app.parseAndTranslate('admin/partials/theme_list', { themes: bootswatch.themes.map(function (theme) { @@ -70,7 +70,7 @@ define('admin/appearance/skins', ['translator'], function (translator) { themeContainer.html(html); if (config['theme:src']) { - var skin = config['theme:src'] + const skin = config['theme:src'] .match(/latest\/(\S+)\/bootstrap.min.css/)[1] .replace(/(^|\s)([a-z])/g, function (m, p1, p2) { return p1 + p2.toUpperCase(); }); @@ -82,8 +82,8 @@ define('admin/appearance/skins', ['translator'], function (translator) { function highlightSelectedTheme(themeId) { translator.translate('[[admin/appearance/skins:select-skin]] || [[admin/appearance/skins:current-skin]]', function (text) { text = text.split(' || '); - var select = text[0]; - var current = text[1]; + const select = text[0]; + const current = text[1]; $('[data-theme]') .removeClass('selected') diff --git a/public/src/admin/appearance/themes.js b/public/src/admin/appearance/themes.js index 94510fd248..7a27d94c9d 100644 --- a/public/src/admin/appearance/themes.js +++ b/public/src/admin/appearance/themes.js @@ -1,19 +1,19 @@ 'use strict'; -define('admin/appearance/themes', ['translator'], function (translator) { - var Themes = {}; +define('admin/appearance/themes', ['bootbox', 'translator'], function (bootbox, translator) { + const Themes = {}; Themes.init = function () { $('#installed_themes').on('click', function (e) { - var target = $(e.target); - var action = target.attr('data-action'); + const target = $(e.target); + const action = target.attr('data-action'); if (action && action === 'use') { - var parentEl = target.parents('[data-theme]'); - var themeType = parentEl.attr('data-type'); - var cssSrc = parentEl.attr('data-css'); - var themeId = parentEl.attr('data-theme'); + const parentEl = target.parents('[data-theme]'); + const themeType = parentEl.attr('data-type'); + const cssSrc = parentEl.attr('data-css'); + const themeId = parentEl.attr('data-theme'); socket.emit('admin.themes.set', { type: themeType, @@ -70,7 +70,7 @@ define('admin/appearance/themes', ['translator'], function (translator) { return app.alertError(err.message); } - var instListEl = $('#installed_themes'); + const instListEl = $('#installed_themes'); if (!themes.length) { instListEl.append($('
  • ').addClass('no-themes').translateHtml('[[admin/appearance/themes:no-themes]]')); @@ -88,8 +88,8 @@ define('admin/appearance/themes', ['translator'], function (translator) { function highlightSelectedTheme(themeId) { translator.translate('[[admin/appearance/themes:select-theme]] || [[admin/appearance/themes:current-theme]]', function (text) { text = text.split(' || '); - var select = text[0]; - var current = text[1]; + const select = text[0]; + const current = text[1]; $('[data-theme]') .removeClass('selected') diff --git a/public/src/admin/dashboard.js b/public/src/admin/dashboard.js index 548e1fe996..645023b563 100644 --- a/public/src/admin/dashboard.js +++ b/public/src/admin/dashboard.js @@ -1,29 +1,29 @@ 'use strict'; -define('admin/dashboard', ['Chart', 'translator', 'benchpress'], function (Chart, translator, Benchpress) { - var Admin = {}; - var intervals = { +define('admin/dashboard', ['Chart', 'translator', 'benchpress', 'bootbox'], function (Chart, translator, Benchpress, bootbox) { + const Admin = {}; + const intervals = { rooms: false, graphs: false, }; - var isMobile = false; - var graphData = { + let isMobile = false; + const graphData = { rooms: {}, traffic: {}, }; - var currentGraph = { + const currentGraph = { units: 'hours', until: undefined, }; - var DEFAULTS = { + const DEFAULTS = { roomInterval: 10000, graphInterval: 15000, realtimeInterval: 1500, }; - var usedTopicColors = []; + const usedTopicColors = []; $(window).on('action:ajaxify.start', function () { clearInterval(intervals.rooms); @@ -62,7 +62,7 @@ define('admin/dashboard', ['Chart', 'translator', 'benchpress'], function (Chart graphData.rooms = data; - var html = '
    ' + + const html = '
    ' + '' + data.onlineRegisteredCount + '' + '
    [[admin/dashboard:active-users.users]]
    ' + '
    ' + @@ -86,38 +86,38 @@ define('admin/dashboard', ['Chart', 'translator', 'benchpress'], function (Chart $('#active-users').translateHtml(html); }; - var graphs = { + const graphs = { traffic: null, registered: null, presence: null, topics: null, }; - var topicColors = ['#bf616a', '#5B90BF', '#d08770', '#ebcb8b', '#a3be8c', '#96b5b4', '#8fa1b3', '#b48ead', '#ab7967', '#46BFBD']; + const topicColors = ['#bf616a', '#5B90BF', '#d08770', '#ebcb8b', '#a3be8c', '#96b5b4', '#8fa1b3', '#b48ead', '#ab7967', '#46BFBD']; /* eslint-disable */ // from chartjs.org function lighten(col, amt) { - var usePound = false; + let usePound = false; if (col[0] === '#') { col = col.slice(1); usePound = true; } - var num = parseInt(col, 16); + const num = parseInt(col, 16); - var r = (num >> 16) + amt; + let r = (num >> 16) + amt; if (r > 255) r = 255; else if (r < 0) r = 0; - var b = ((num >> 8) & 0x00FF) + amt; + let b = ((num >> 8) & 0x00FF) + amt; if (b > 255) b = 255; else if (b < 0) b = 0; - var g = (num & 0x0000FF) + amt; + let g = (num & 0x0000FF) + amt; if (g > 255) g = 255; else if (g < 0) g = 0; @@ -128,21 +128,21 @@ define('admin/dashboard', ['Chart', 'translator', 'benchpress'], function (Chart function setupGraphs(callback) { callback = callback || function () {}; - var trafficCanvas = document.getElementById('analytics-traffic'); - var registeredCanvas = document.getElementById('analytics-registered'); - var presenceCanvas = document.getElementById('analytics-presence'); - var topicsCanvas = document.getElementById('analytics-topics'); - var trafficCtx = trafficCanvas.getContext('2d'); - var registeredCtx = registeredCanvas.getContext('2d'); - var presenceCtx = presenceCanvas.getContext('2d'); - var topicsCtx = topicsCanvas.getContext('2d'); - var trafficLabels = utils.getHoursArray(); + const trafficCanvas = document.getElementById('analytics-traffic'); + const registeredCanvas = document.getElementById('analytics-registered'); + const presenceCanvas = document.getElementById('analytics-presence'); + const topicsCanvas = document.getElementById('analytics-topics'); + const trafficCtx = trafficCanvas.getContext('2d'); + const registeredCtx = registeredCanvas.getContext('2d'); + const presenceCtx = presenceCanvas.getContext('2d'); + const topicsCtx = topicsCanvas.getContext('2d'); + const trafficLabels = utils.getHoursArray(); if (isMobile) { Chart.defaults.global.tooltips.enabled = false; } - var t = translator.Translator.create(); + const t = translator.Translator.create(); Promise.all([ t.translateKey('admin/dashboard:graphs.page-views', []), t.translateKey('admin/dashboard:graphs.page-views-registered', []), @@ -150,14 +150,14 @@ define('admin/dashboard', ['Chart', 'translator', 'benchpress'], function (Chart t.translateKey('admin/dashboard:graphs.page-views-bot', []), t.translateKey('admin/dashboard:graphs.unique-visitors', []), t.translateKey('admin/dashboard:graphs.registered-users', []), - t.translateKey('admin/dashboard:graphs.anonymous-users', []), + t.translateKey('admin/dashboard:graphs.guest-users', []), t.translateKey('admin/dashboard:on-categories', []), t.translateKey('admin/dashboard:reading-posts', []), t.translateKey('admin/dashboard:browsing-topics', []), t.translateKey('admin/dashboard:recent', []), t.translateKey('admin/dashboard:unread', []), ]).then(function (translations) { - var data = { + const data = { labels: trafficLabels, datasets: [ { @@ -323,8 +323,8 @@ define('admin/dashboard', ['Chart', 'translator', 'benchpress'], function (Chart adjustPieCharts(); $('[data-action="updateGraph"]:not([data-units="custom"])').on('click', function () { - var until = new Date(); - var amount = $(this).attr('data-amount'); + let until = new Date(); + const amount = $(this).attr('data-amount'); if ($(this).attr('data-units') === 'days') { until.setHours(0, 0, 0, 0); } @@ -339,10 +339,10 @@ define('admin/dashboard', ['Chart', 'translator', 'benchpress'], function (Chart }); $('[data-action="updateGraph"][data-units="custom"]').on('click', function () { - var targetEl = $(this); + const targetEl = $(this); Benchpress.render('admin/partials/pageviews-range-select', {}).then(function (html) { - var modal = bootbox.dialog({ + const modal = bootbox.dialog({ title: '[[admin/dashboard:page-views-custom]]', message: html, buttons: { @@ -353,10 +353,10 @@ define('admin/dashboard', ['Chart', 'translator', 'benchpress'], function (Chart }, }, }).on('shown.bs.modal', function () { - var date = new Date(); - var today = date.toISOString().substr(0, 10); + const date = new Date(); + const today = date.toISOString().substr(0, 10); date.setDate(date.getDate() - 1); - var yesterday = date.toISOString().substr(0, 10); + const yesterday = date.toISOString().substr(0, 10); modal.find('#startRange').val(targetEl.attr('data-startRange') || yesterday); modal.find('#endRange').val(targetEl.attr('data-endRange') || today); @@ -364,8 +364,8 @@ define('admin/dashboard', ['Chart', 'translator', 'benchpress'], function (Chart function submit() { // NEED TO ADD VALIDATION HERE FOR YYYY-MM-DD - var formData = modal.find('form').serializeObject(); - var validRegexp = /\d{4}-\d{2}-\d{2}/; + const formData = modal.find('form').serializeObject(); + const validRegexp = /\d{4}-\d{2}-\d{2}/; // Input validation if (!formData.startRange && !formData.endRange) { @@ -378,10 +378,10 @@ define('admin/dashboard', ['Chart', 'translator', 'benchpress'], function (Chart return false; } - var until = new Date(formData.endRange); + let until = new Date(formData.endRange); until.setDate(until.getDate() + 1); until = until.getTime(); - var amount = (until - new Date(formData.startRange).getTime()) / (1000 * 60 * 60 * 24); + const amount = (until - new Date(formData.startRange).getTime()) / (1000 * 60 * 60 * 24); updateTrafficGraph('days', until, amount); @@ -401,7 +401,7 @@ define('admin/dashboard', ['Chart', 'translator', 'benchpress'], function (Chart function adjustPieCharts() { $('.pie-chart.legend-up').each(function () { - var $this = $(this); + const $this = $(this); if ($this.width() < 320) { $this.addClass('compact'); @@ -459,8 +459,8 @@ define('admin/dashboard', ['Chart', 'translator', 'benchpress'], function (Chart currentGraph.amount = amount; // Update the View as JSON button url - var apiEl = $('#view-as-json'); - var newHref = $.param({ + const apiEl = $('#view-as-json'); + const newHref = $.param({ units: units || 'hours', until: until, count: amount, @@ -469,11 +469,11 @@ define('admin/dashboard', ['Chart', 'translator', 'benchpress'], function (Chart }); } - function updateRegisteredGraph(registered, anonymous) { + function updateRegisteredGraph(registered, guest) { $('#analytics-legend .registered').parent().find('.count').text(registered); - $('#analytics-legend .anonymous').parent().find('.count').text(anonymous); + $('#analytics-legend .guest').parent().find('.count').text(guest); graphs.registered.data.datasets[0].data[0] = registered; - graphs.registered.data.datasets[0].data[1] = anonymous; + graphs.registered.data.datasets[0].data[1] = guest; graphs.registered.update(); } @@ -517,10 +517,10 @@ define('admin/dashboard', ['Chart', 'translator', 'benchpress'], function (Chart }); function buildTopicsLegend() { - var html = ''; + let html = ''; topics.forEach(function (t, i) { - var link = t.tid ? ' ' + t.title + '' : t.title; - var label = t.count === '0' ? t.title : link; + const link = t.tid ? ' ' + t.title + '' : t.title; + const label = t.count === '0' ? t.title : link; html += '
  • ' + '
    ' + @@ -536,7 +536,7 @@ define('admin/dashboard', ['Chart', 'translator', 'benchpress'], function (Chart function setupRealtimeButton() { $('#toggle-realtime .fa').on('click', function () { - var $this = $(this); + const $this = $(this); if ($this.hasClass('fa-toggle-on')) { $this.removeClass('fa-toggle-on').addClass('fa-toggle-off'); $this.parent().find('strong').html('OFF'); @@ -565,11 +565,11 @@ define('admin/dashboard', ['Chart', 'translator', 'benchpress'], function (Chart } function setupFullscreen() { - var container = document.getElementById('analytics-panel'); - var $container = $(container); - var btn = $container.find('.fa-expand'); - var fsMethod; - var exitMethod; + const container = document.getElementById('analytics-panel'); + const $container = $(container); + const btn = $container.find('.fa-expand'); + let fsMethod; + let exitMethod; if (container.requestFullscreen) { fsMethod = 'requestFullscreen'; diff --git a/public/src/admin/extend/plugins.js b/public/src/admin/extend/plugins.js index 064b2fa79e..36ff1fdb1d 100644 --- a/public/src/admin/extend/plugins.js +++ b/public/src/admin/extend/plugins.js @@ -4,13 +4,14 @@ define('admin/extend/plugins', [ 'translator', 'benchpress', + 'bootbox', 'jquery-ui/widgets/sortable', -], function (translator, Benchpress) { - var Plugins = {}; +], function (translator, Benchpress, bootbox) { + const Plugins = {}; Plugins.init = function () { - var pluginsList = $('.plugins'); - var numPlugins = pluginsList[0].querySelectorAll('li').length; - var pluginID; + const pluginsList = $('.plugins'); + const numPlugins = pluginsList[0].querySelectorAll('li').length; + let pluginID; if (!numPlugins) { translator.translate('
  • [[admin/extend/plugins:none-found]]

  • ', function (html) { @@ -23,11 +24,11 @@ define('admin/extend/plugins', [ searchInputEl.value = ''; pluginsList.on('click', 'button[data-action="toggleActive"]', function () { - var pluginEl = $(this).parents('li'); + const pluginEl = $(this).parents('li'); pluginID = pluginEl.attr('data-plugin-id'); - var btn = $('[id="' + pluginID + '"] [data-action="toggleActive"]'); + const btn = $('[id="' + pluginID + '"] [data-action="toggleActive"]'); - var pluginData = ajaxify.data.installed[pluginEl.attr('data-plugin-index')]; + const pluginData = ajaxify.data.installed[pluginEl.attr('data-plugin-index')]; function toggleActivate() { socket.emit('admin.plugins.toggleActive', pluginID, function (err, status) { @@ -79,6 +80,12 @@ define('admin/extend/plugins', [ callback: toggleActivate, }, }, + onShown: function () { + const saveEl = this.querySelector('button.btn-primary'); + if (saveEl) { + saveEl.focus(); + } + }, }); }); } else { @@ -87,7 +94,7 @@ define('admin/extend/plugins', [ }); pluginsList.on('click', 'button[data-action="toggleInstall"]', function () { - var btn = $(this); + const btn = $(this); btn.attr('disabled', true); pluginID = $(this).parents('li').attr('data-plugin-id'); @@ -124,8 +131,8 @@ define('admin/extend/plugins', [ }); pluginsList.on('click', 'button[data-action="upgrade"]', function () { - var btn = $(this); - var parent = btn.parents('li'); + const btn = $(this); + const parent = btn.parents('li'); pluginID = parent.attr('data-plugin-id'); Plugins.suggest(pluginID, function (err, payload) { @@ -134,7 +141,7 @@ define('admin/extend/plugins', [ } require(['compare-versions'], function (compareVersions) { - var currentVersion = parent.find('.currentVersion').text(); + const currentVersion = parent.find('.currentVersion').text(); if (payload.version !== 'latest' && compareVersions.compare(payload.version, currentVersion, '>')) { upgrade(pluginID, btn, payload.version); } else if (payload.version === 'latest') { @@ -149,9 +156,9 @@ define('admin/extend/plugins', [ }); $(searchInputEl).on('input propertychange', function () { - var term = $(this).val(); + const term = $(this).val(); $('.plugins li').each(function () { - var pluginId = $(this).attr('data-plugin-id'); + const pluginId = $(this).attr('data-plugin-id'); $(this).toggleClass('hide', pluginId && pluginId.indexOf(term) === -1); }); }); @@ -172,7 +179,7 @@ define('admin/extend/plugins', [ if (err) { return app.alertError(err); } - var html = ''; + let html = ''; activePlugins.forEach(function (plugin) { html += '
  • ' + plugin + '
  • '; }); @@ -182,24 +189,24 @@ define('admin/extend/plugins', [ }); return; } - var list = $('#order-active-plugins-modal .plugin-list'); + const list = $('#order-active-plugins-modal .plugin-list'); list.html(html).sortable(); list.find('.fa-chevron-up').on('click', function () { - var item = $(this).parents('li'); + const item = $(this).parents('li'); item.prev().before(item); }); list.find('.fa-chevron-down').on('click', function () { - var item = $(this).parents('li'); + const item = $(this).parents('li'); item.next().after(item); }); }); }); $('#save-plugin-order').on('click', function () { - var plugins = $('#order-active-plugins-modal .plugin-list').children(); - var data = []; + const plugins = $('#order-active-plugins-modal .plugin-list').children(); + const data = []; plugins.each(function (index, el) { data.push({ name: $(el).text(), order: index }); }); @@ -245,7 +252,7 @@ define('admin/extend/plugins', [ if (err) { return app.alertError(err.message); } - var parent = btn.parents('li'); + const parent = btn.parents('li'); parent.find('.fa-exclamation-triangle').remove(); parent.find('.currentVersion').text(version); btn.remove(); @@ -267,7 +274,7 @@ define('admin/extend/plugins', [ } Plugins.toggleInstall = function (pluginID, version, callback) { - var btn = $('li[data-plugin-id="' + pluginID + '"] button[data-action="toggleInstall"]'); + const btn = $('li[data-plugin-id="' + pluginID + '"] button[data-action="toggleInstall"]'); btn.find('i').attr('class', 'fa fa-refresh fa-spin'); socket.emit('admin.plugins.toggleInstall', { @@ -296,7 +303,7 @@ define('admin/extend/plugins', [ }; Plugins.suggest = function (pluginId, callback) { - var nbbVersion = app.config.version.match(/^\d+\.\d+\.\d+/); + const nbbVersion = app.config.version.match(/^\d+\.\d+\.\d+/); $.ajax((app.config.registry || 'https://packages.nodebb.org') + '/api/v1/suggest', { type: 'GET', data: { diff --git a/public/src/admin/extend/rewards.js b/public/src/admin/extend/rewards.js index 3850b24017..2a07c4601f 100644 --- a/public/src/admin/extend/rewards.js +++ b/public/src/admin/extend/rewards.js @@ -2,13 +2,13 @@ define('admin/extend/rewards', [], function () { - var rewards = {}; + const rewards = {}; - var available; - var active; - var conditions; - var conditionals; + let available; + let active; + let conditions; + let conditionals; rewards.init = function () { available = ajaxify.data.rewards; @@ -25,8 +25,8 @@ define('admin/extend/rewards', [], function () { update($(this)); }) .on('click', '.delete', function () { - var parent = $(this).parents('[data-id]'); - var id = parent.attr('data-id'); + const parent = $(this).parents('[data-id]'); + const id = parent.attr('data-id'); socket.emit('admin.rewards.delete', { id: id }, function (err) { if (err) { @@ -40,8 +40,8 @@ define('admin/extend/rewards', [], function () { return false; }) .on('click', '.toggle', function () { - var btn = $(this); - var disabled = btn.hasClass('btn-success'); + const btn = $(this); + const disabled = btn.hasClass('btn-success'); btn.toggleClass('btn-warning').toggleClass('btn-success').translateHtml('[[admin/extend/rewards:' + (disabled ? 'disable' : 'enable') + ']]'); // send disable api call return false; @@ -72,12 +72,12 @@ define('admin/extend/rewards', [], function () { } function selectReward(el) { - var parent = el.parents('[data-rid]'); - var div = parent.find('.inputs'); - var inputs; - var html = ''; + const parent = el.parents('[data-rid]'); + const div = parent.find('.inputs'); + let inputs; + let html = ''; - for (var reward in available) { + for (const reward in available) { if (available.hasOwnProperty(reward)) { if (available[reward].rid === el.attr('data-selected')) { inputs = available[reward].inputs; @@ -95,13 +95,13 @@ define('admin/extend/rewards', [], function () { html += '
    '; @@ -112,10 +112,10 @@ define('admin/extend/rewards', [], function () { function populateInputs() { $('[data-rid]').each(function (i) { - var div = $(this).find('.inputs'); - var rewards = active[i].rewards; + const div = $(this).find('.inputs'); + const rewards = active[i].rewards; - for (var reward in rewards) { + for (const reward in rewards) { if (rewards.hasOwnProperty(reward)) { div.find('[name="' + reward + '"]').val(rewards[reward]); } @@ -124,9 +124,9 @@ define('admin/extend/rewards', [], function () { } function newReward() { - var ul = $('#active'); + const ul = $('#active'); - var data = { + const data = { active: [{ disabled: true, value: '', @@ -146,12 +146,12 @@ define('admin/extend/rewards', [], function () { } function saveRewards() { - var activeRewards = []; + const activeRewards = []; $('#active li').each(function () { - var data = { rewards: {} }; - var main = $(this).find('form.main').serializeArray(); - var rewards = $(this).find('form.rewards').serializeArray(); + const data = { rewards: {} }; + const main = $(this).find('form.main').serializeArray(); + const rewards = $(this).find('form.rewards').serializeArray(); main.forEach(function (obj) { data[obj.name] = obj.value; @@ -167,11 +167,17 @@ define('admin/extend/rewards', [], function () { activeRewards.push(data); }); - socket.emit('admin.rewards.save', activeRewards, function (err) { + socket.emit('admin.rewards.save', activeRewards, function (err, result) { if (err) { app.alertError(err.message); } else { app.alertSuccess('[[admin/extend/rewards:alert.save-success]]'); + // newly added rewards are missing data-id, update to prevent rewards getting duplicated + $('#active li').each(function (index) { + if (!$(this).attr('data-id')) { + $(this).attr('data-id', result[index].id); + } + }); } }); } diff --git a/public/src/admin/extend/widgets.js b/public/src/admin/extend/widgets.js index 5660cc8f05..aad32fe7e1 100644 --- a/public/src/admin/extend/widgets.js +++ b/public/src/admin/extend/widgets.js @@ -2,18 +2,19 @@ define('admin/extend/widgets', [ + 'bootbox', 'jquery-ui/widgets/sortable', 'jquery-ui/widgets/draggable', 'jquery-ui/widgets/droppable', 'jquery-ui/widgets/datepicker', -], function () { - var Widgets = {}; +], function (bootbox) { + const Widgets = {}; Widgets.init = function () { $('#widgets .nav-pills .dropdown-menu a').on('click', function (ev) { - var $this = $(this); + const $this = $(this); $('#widgets .tab-pane').removeClass('active'); - var templateName = $this.attr('data-template'); + const templateName = $this.attr('data-template'); $('#widgets .tab-pane[data-template="' + templateName + '"]').addClass('active'); $('#widgets .selected-template').text(templateName); $('#widgets .nav-pills .dropdown').trigger('click'); @@ -46,7 +47,7 @@ define('admin/extend/widgets', [ $('#widgets .available-containers .containers > [data-container-html]') .draggable({ helper: function (e) { - var target = $(e.target); + let target = $(e.target); target = target.attr('data-container-html') ? target : target.parents('[data-container-html]'); return target.clone().addClass('block').width(target.width()).css('opacity', '0.5'); @@ -64,7 +65,7 @@ define('admin/extend/widgets', [ }, connectWith: 'div', }).on('click', '.delete-widget', function () { - var panel = $(this).parents('.widget-panel'); + const panel = $(this).parents('.widget-panel'); bootbox.confirm('[[admin/extend/widgets:alert.confirm-delete]]', function (confirm) { if (confirm) { @@ -80,20 +81,20 @@ define('admin/extend/widgets', [ $('#save').on('click', saveWidgets); function saveWidgets() { - var saveData = []; + const saveData = []; $('#widgets [data-template][data-location]').each(function (i, el) { el = $(el); - var template = el.attr('data-template'); - var location = el.attr('data-location'); - var area = el.children('.widget-area'); - var widgets = []; + const template = el.attr('data-template'); + const location = el.attr('data-location'); + const area = el.children('.widget-area'); + const widgets = []; area.find('.widget-panel[data-widget]').each(function () { - var widgetData = {}; - var data = $(this).find('form').serializeArray(); + const widgetData = {}; + const data = $(this).find('form').serializeArray(); - for (var d in data) { + for (const d in data) { if (data.hasOwnProperty(d)) { if (data[d].name) { if (widgetData[data[d].name]) { @@ -139,10 +140,10 @@ define('admin/extend/widgets', [ } $('.color-selector').on('click', '.btn', function () { - var btn = $(this); - var selector = btn.parents('.color-selector'); - var container = selector.parents('[data-container-html]'); - var classList = []; + const btn = $(this); + const selector = btn.parents('.color-selector'); + const container = selector.parents('[data-container-html]'); + const classList = []; selector.children().each(function () { classList.push($(this).attr('data-class')); @@ -158,7 +159,7 @@ define('admin/extend/widgets', [ } function createDatePicker(el) { - var currentYear = new Date().getFullYear(); + const currentYear = new Date().getFullYear(); el.find('.date-selector').datepicker({ changeMonth: true, changeYear: true, @@ -172,7 +173,7 @@ define('admin/extend/widgets', [ .droppable({ accept: '[data-container-html]', drop: function (event, ui) { - var el = $(this); + const el = $(this); el.find('.panel-body .container-html').val(ui.draggable.attr('data-container-html')); el.find('.panel-body').removeClass('hidden'); @@ -189,13 +190,13 @@ define('admin/extend/widgets', [ function loadWidgetData() { function populateWidget(widget, data) { if (data.title) { - var title = widget.find('.panel-heading strong'); + const title = widget.find('.panel-heading strong'); title.text(title.text() + ' - ' + data.title); } widget.find('input, textarea, select').each(function () { - var input = $(this); - var value = data[input.attr('name')]; + const input = $(this); + const value = data[input.attr('name')]; if (input.attr('type') === 'checkbox') { input.prop('checked', !!value).trigger('change'); @@ -208,17 +209,17 @@ define('admin/extend/widgets', [ } $.get(config.relative_path + '/api/admin/extend/widgets', function (data) { - var areas = data.areas; + const areas = data.areas; - for (var i = 0; i < areas.length; i += 1) { - var area = areas[i]; - var widgetArea = $('#widgets .area[data-template="' + area.template + '"][data-location="' + area.location + '"]').find('.widget-area'); + for (let i = 0; i < areas.length; i += 1) { + const area = areas[i]; + const widgetArea = $('#widgets .area[data-template="' + area.template + '"][data-location="' + area.location + '"]').find('.widget-area'); widgetArea.html(''); - for (var k = 0; k < area.data.length; k += 1) { - var widgetData = area.data[k]; - var widgetEl = $('.available-widgets [data-widget="' + widgetData.widget + '"]').clone(true).removeClass('hide'); + for (let k = 0; k < area.data.length; k += 1) { + const widgetData = area.data[k]; + const widgetEl = $('.available-widgets [data-widget="' + widgetData.widget + '"]').clone(true).removeClass('hide'); widgetArea.append(populateWidget(widgetEl, widgetData.data)); appendToggle(widgetEl); @@ -231,44 +232,44 @@ define('admin/extend/widgets', [ } function setupCloneButton() { - var clone = $('[component="clone"]'); - var cloneBtn = $('[component="clone/button"]'); + const clone = $('[component="clone"]'); + const cloneBtn = $('[component="clone/button"]'); clone.find('.dropdown-menu li').on('click', function () { - var template = $(this).find('a').text(); + const template = $(this).find('a').text(); cloneBtn.translateHtml('[[admin/extend/widgets:clone-from]] ' + template + ''); cloneBtn.attr('data-template', template); }); cloneBtn.on('click', function () { - var template = cloneBtn.attr('data-template'); + const template = cloneBtn.attr('data-template'); if (!template) { return app.alertError('[[admin/extend/widgets:error.select-clone]]'); } - var currentTemplate = $('#active-widgets .active.tab-pane[data-template] .area'); - var templateToClone = $('#active-widgets .tab-pane[data-template="' + template + '"] .area'); + const currentTemplate = $('#active-widgets .active.tab-pane[data-template] .area'); + const templateToClone = $('#active-widgets .tab-pane[data-template="' + template + '"] .area'); - var currentAreas = currentTemplate.map(function () { + const currentAreas = currentTemplate.map(function () { return $(this).attr('data-location'); }).get(); - var areasToClone = templateToClone.map(function () { - var location = $(this).attr('data-location'); + const areasToClone = templateToClone.map(function () { + const location = $(this).attr('data-location'); return currentAreas.indexOf(location) !== -1 ? location : undefined; }).get().filter(function (i) { return i; }); function clone(location) { $('#active-widgets .tab-pane[data-template="' + template + '"] [data-location="' + location + '"]').each(function () { $(this).find('[data-widget]').each(function () { - var widget = $(this).clone(true); + const widget = $(this).clone(true); $('#active-widgets .active.tab-pane[data-template]:not([data-template="global"]) [data-location="' + location + '"] .widget-area').append(widget); }); }); } - for (var i = 0, ii = areasToClone.length; i < ii; i++) { - var location = areasToClone[i]; + for (let i = 0, ii = areasToClone.length; i < ii; i++) { + const location = areasToClone[i]; clone(location); } diff --git a/public/src/admin/manage/admins-mods.js b/public/src/admin/manage/admins-mods.js index 2bceceb15e..0a8ec2dc62 100644 --- a/public/src/admin/manage/admins-mods.js +++ b/public/src/admin/manage/admins-mods.js @@ -3,7 +3,7 @@ define('admin/manage/admins-mods', [ 'autocomplete', 'api', 'bootbox', 'categorySelector', ], function (autocomplete, api, bootbox, categorySelector) { - var AdminsMods = {}; + const AdminsMods = {}; AdminsMods.init = function () { autocomplete.user($('#admin-search'), function (ev, ui) { @@ -25,8 +25,8 @@ define('admin/manage/admins-mods', [ }); $('.administrator-area').on('click', '.remove-user-icon', function () { - var userCard = $(this).parents('[data-uid]'); - var uid = userCard.attr('data-uid'); + const userCard = $(this).parents('[data-uid]'); + const uid = userCard.attr('data-uid'); if (parseInt(uid, 10) === parseInt(app.user.uid, 10)) { return app.alertError('[[admin/manage/users:alerts.no-remove-yourself-admin]]'); } @@ -60,8 +60,8 @@ define('admin/manage/admins-mods', [ }); $('.global-moderator-area').on('click', '.remove-user-icon', function () { - var userCard = $(this).parents('[data-uid]'); - var uid = userCard.attr('data-uid'); + const userCard = $(this).parents('[data-uid]'); + const uid = userCard.attr('data-uid'); bootbox.confirm('[[admin/manage/users:alerts.confirm-remove-global-mod]]', function (confirm) { if (confirm) { @@ -85,8 +85,8 @@ define('admin/manage/admins-mods', [ }); autocomplete.user($('.moderator-search'), function (ev, ui) { - var input = $(ev.target); - var cid = $(ev.target).attr('data-cid'); + const input = $(ev.target); + const cid = $(ev.target).attr('data-cid'); socket.emit('admin.categories.setPrivilege', { cid: cid, privilege: ajaxify.data.allPrivileges, @@ -111,10 +111,10 @@ define('admin/manage/admins-mods', [ }); $('.moderator-area').on('click', '.remove-user-icon', function () { - var moderatorArea = $(this).parents('[data-cid]'); - var cid = moderatorArea.attr('data-cid'); - var userCard = $(this).parents('[data-uid]'); - var uid = userCard.attr('data-uid'); + const moderatorArea = $(this).parents('[data-cid]'); + const cid = moderatorArea.attr('data-cid'); + const userCard = $(this).parents('[data-uid]'); + const uid = userCard.attr('data-uid'); bootbox.confirm('[[admin/manage/users:alerts.confirm-remove-moderator]]', function (confirm) { if (confirm) { diff --git a/public/src/admin/manage/categories.js b/public/src/admin/manage/categories.js index c6d1307a4d..23c85892a4 100644 --- a/public/src/admin/manage/categories.js +++ b/public/src/admin/manage/categories.js @@ -8,9 +8,9 @@ define('admin/manage/categories', [ 'Sortable', 'bootbox', ], function (translator, Benchpress, categorySelector, api, Sortable, bootbox) { - var Categories = {}; - var newCategoryId = -1; - var sortables; + const Categories = {}; + let newCategoryId = -1; + let sortables; Categories.init = function () { categorySelector.init($('.category [component="category-selector"]'), { @@ -26,12 +26,12 @@ define('admin/manage/categories', [ // Enable/Disable toggle events $('.categories').on('click', '.category-tools [data-action="toggle"]', function () { - var $this = $(this); - var cid = $this.attr('data-disable-cid'); - var parentEl = $this.parents('li[data-cid="' + cid + '"]'); - var disabled = parentEl.hasClass('disabled'); - var childrenEls = parentEl.find('li[data-cid]'); - var childrenCids = childrenEls.map(function () { + const $this = $(this); + const cid = $this.attr('data-disable-cid'); + const parentEl = $this.parents('li[data-cid="' + cid + '"]'); + const disabled = parentEl.hasClass('disabled'); + const childrenEls = parentEl.find('li[data-cid]'); + const childrenCids = childrenEls.map(function () { return $(this).attr('data-cid'); }).get(); @@ -39,15 +39,15 @@ define('admin/manage/categories', [ }); $('.categories').on('click', '.toggle', function () { - var el = $(this); + const el = $(this); el.find('i').toggleClass('fa-minus').toggleClass('fa-plus'); el.closest('[data-cid]').find('> ul[data-cid]').toggleClass('hidden'); }); $('.categories').on('click', '.set-order', function () { - var cid = $(this).attr('data-cid'); - var order = $(this).attr('data-order'); - var modal = bootbox.dialog({ + const cid = $(this).attr('data-cid'); + const order = $(this).attr('data-order'); + const modal = bootbox.dialog({ title: '[[admin/manage/categories:set-order]]', message: '

    [[admin/manage/categories:set-order-help]]

    ', show: true, @@ -56,9 +56,9 @@ define('admin/manage/categories', [ label: '[[modules:bootbox.confirm]]', className: 'btn-primary', callback: function () { - var val = modal.find('input').val(); + const val = modal.find('input').val(); if (val && cid) { - var modified = {}; + const modified = {}; modified[cid] = { order: Math.max(1, parseInt(val, 10)) }; api.put('/categories/' + cid, modified[cid]).then(function () { ajaxify.refresh(); @@ -81,7 +81,7 @@ define('admin/manage/categories', [ }); function toggleAll(expand) { - var el = $('.categories .toggle'); + const el = $('.categories .toggle'); el.find('i').toggleClass('fa-minus', expand).toggleClass('fa-plus', !expand); el.closest('[data-cid]').find('> ul[data-cid]').toggleClass('hidden', !expand); } @@ -89,7 +89,7 @@ define('admin/manage/categories', [ Categories.throwCreateModal = function () { Benchpress.render('admin/partials/categories/create', {}).then(function (html) { - var modal = bootbox.dialog({ + const modal = bootbox.dialog({ title: '[[admin/manage/categories:alert.create]]', message: html, buttons: { @@ -100,7 +100,7 @@ define('admin/manage/categories', [ }, }, }); - var options = { + const options = { localCategories: [ { cid: 0, @@ -109,10 +109,10 @@ define('admin/manage/categories', [ }, ], }; - var parentSelector = categorySelector.init(modal.find('#parentCidGroup [component="category-selector"]'), options); - var cloneFromSelector = categorySelector.init(modal.find('#cloneFromCidGroup [component="category-selector"]'), options); + const parentSelector = categorySelector.init(modal.find('#parentCidGroup [component="category-selector"]'), options); + const cloneFromSelector = categorySelector.init(modal.find('#cloneFromCidGroup [component="category-selector"]'), options); function submit() { - var formData = modal.find('form').serializeObject(); + const formData = modal.find('form').serializeObject(); formData.description = ''; formData.icon = 'fa-comments'; formData.uid = app.user.uid; @@ -125,8 +125,8 @@ define('admin/manage/categories', [ } $('#cloneChildren').on('change', function () { - var check = $(this); - var parentSelect = modal.find('#parentCidGroup [component="category-selector"] .dropdown-toggle'); + const check = $(this); + const parentSelect = modal.find('#parentCidGroup [component="category-selector"] .dropdown-toggle'); if (check.prop('checked')) { parentSelect.attr('disabled', 'disabled'); @@ -159,7 +159,7 @@ define('admin/manage/categories', [ }; Categories.render = function (categories) { - var container = $('.categories'); + const container = $('.categories'); if (!categories || !categories.length) { translator.translate('[[admin/manage/categories:alert.none-active]]', function (text) { @@ -190,15 +190,15 @@ define('admin/manage/categories', [ } function itemDragDidEnd(e) { - var isCategoryUpdate = parseInt(newCategoryId, 10) !== -1; + const isCategoryUpdate = parseInt(newCategoryId, 10) !== -1; // Update needed? if ((e.newIndex != null && parseInt(e.oldIndex, 10) !== parseInt(e.newIndex, 10)) || isCategoryUpdate) { - var cid = e.item.dataset.cid; - var modified = {}; + const cid = e.item.dataset.cid; + const modified = {}; // on page 1 baseIndex is 0, on page n baseIndex is (n - 1) * ajaxify.data.categoriesPerPage // this makes sure order is correct when drag & drop is used on pages > 1 - var baseIndex = (ajaxify.data.pagination.currentPage - 1) * ajaxify.data.categoriesPerPage; + const baseIndex = (ajaxify.data.pagination.currentPage - 1) * ajaxify.data.categoriesPerPage; modified[cid] = { order: baseIndex + e.newIndex + 1, }; @@ -208,7 +208,7 @@ define('admin/manage/categories', [ } newCategoryId = -1; - api.put('/categories/' + cid, modified[cid]); + api.put('/categories/' + cid, modified[cid]).catch(app.alertError); } } @@ -222,7 +222,7 @@ define('admin/manage/categories', [ */ function renderList(categories, container, parentId) { // Translate category names if needed - var count = 0; + let count = 0; categories.forEach(function (category, idx, parent) { translator.translate(category.name, function (translated) { if (category.name !== translated) { @@ -248,7 +248,7 @@ define('admin/manage/categories', [ container.append(html); // Handle and children categories in this level have - for (var x = 0, numCategories = categories.length; x < numCategories; x += 1) { + for (let x = 0, numCategories = categories.length; x < numCategories; x += 1) { renderList(categories[x].children, $('li[data-cid="' + categories[x].cid + '"]'), categories[x].cid); } diff --git a/public/src/admin/manage/category-analytics.js b/public/src/admin/manage/category-analytics.js index 089f34ae08..6a283ef43f 100644 --- a/public/src/admin/manage/category-analytics.js +++ b/public/src/admin/manage/category-analytics.js @@ -2,17 +2,17 @@ define('admin/manage/category-analytics', ['Chart'], function (Chart) { - var CategoryAnalytics = {}; + const CategoryAnalytics = {}; CategoryAnalytics.init = function () { - var hourlyCanvas = document.getElementById('pageviews:hourly'); - var dailyCanvas = document.getElementById('pageviews:daily'); - var topicsCanvas = document.getElementById('topics:daily'); - var postsCanvas = document.getElementById('posts:daily'); - var hourlyLabels = utils.getHoursArray().map(function (text, idx) { + const hourlyCanvas = document.getElementById('pageviews:hourly'); + const dailyCanvas = document.getElementById('pageviews:daily'); + const topicsCanvas = document.getElementById('topics:daily'); + const postsCanvas = document.getElementById('posts:daily'); + const hourlyLabels = utils.getHoursArray().map(function (text, idx) { return idx % 3 ? '' : text; }); - var dailyLabels = utils.getDaysArray().map(function (text, idx) { + const dailyLabels = utils.getDaysArray().map(function (text, idx) { return idx % 3 ? '' : text; }); @@ -20,7 +20,7 @@ define('admin/manage/category-analytics', ['Chart'], function (Chart) { Chart.defaults.global.tooltips.enabled = false; } - var data = { + const data = { 'pageviews:hourly': { labels: hourlyLabels, datasets: [ diff --git a/public/src/admin/manage/category.js b/public/src/admin/manage/category.js index 1873d4de7a..10f665ecab 100644 --- a/public/src/admin/manage/category.js +++ b/public/src/admin/manage/category.js @@ -6,13 +6,14 @@ define('admin/manage/category', [ 'categorySelector', 'benchpress', 'api', -], function (uploader, iconSelect, categorySelector, Benchpress, api) { - var Category = {}; - var updateHash = {}; + 'bootbox', +], function (uploader, iconSelect, categorySelector, Benchpress, api, bootbox) { + const Category = {}; + let updateHash = {}; Category.init = function () { $('#category-settings select').each(function () { - var $this = $(this); + const $this = $(this); $this.val($this.attr('data-value')); }); @@ -34,8 +35,8 @@ define('admin/manage/category', [ }); $('[data-name="bgColor"], [data-name="color"]').on('input', function () { - var $inputEl = $(this); - var previewEl = $inputEl.parents('[data-cid]').find('.category-preview'); + const $inputEl = $(this); + const previewEl = $inputEl.parents('[data-cid]').find('.category-preview'); if ($inputEl.attr('data-name') === 'bgColor') { previewEl.css('background-color', $inputEl.val()); } else if ($inputEl.attr('data-name') === 'color') { @@ -46,12 +47,12 @@ define('admin/manage/category', [ }); $('#save').on('click', function () { - var tags = $('#tag-whitelist').val() ? $('#tag-whitelist').val().split(',') : []; + const tags = $('#tag-whitelist').val() ? $('#tag-whitelist').val().split(',') : []; if (tags.length && tags.length < parseInt($('#cid-min-tags').val(), 10)) { return app.alertError('[[admin/manage/categories:alert.not-enough-whitelisted-tags]]'); } - var cid = ajaxify.data.category.cid; + const cid = ajaxify.data.category.cid; api.put('/categories/' + cid, updateHash).then((res) => { app.flags._unsaved = false; app.alert({ @@ -73,7 +74,7 @@ define('admin/manage/category', [ name: ajaxify.data.category.name, topic_count: ajaxify.data.category.topic_count, }).then(function (html) { - var modal = bootbox.dialog({ + const modal = bootbox.dialog({ title: '[[admin/manage/categories:purge]]', message: html, size: 'large', @@ -84,13 +85,13 @@ define('admin/manage/category', [ callback: function () { modal.find('.modal-footer button').prop('disabled', true); - var intervalId = setInterval(function () { + const intervalId = setInterval(function () { socket.emit('categories.getTopicCount', ajaxify.data.category.cid, function (err, count) { if (err) { return app.alertError(err); } - var percent = 0; + let percent = 0; if (ajaxify.data.category.topic_count > 0) { percent = Math.max(0, (1 - (count / ajaxify.data.category.topic_count))) * 100; } @@ -118,8 +119,8 @@ define('admin/manage/category', [ $('.copy-settings').on('click', function () { Benchpress.render('admin/partials/categories/copy-settings', {}).then(function (html) { - var selectedCid; - var modal = bootbox.dialog({ + let selectedCid; + const modal = bootbox.dialog({ title: '[[modules:composer.select_category]]', message: html, buttons: { @@ -164,8 +165,8 @@ define('admin/manage/category', [ }); $('.upload-button').on('click', function () { - var inputEl = $(this); - var cid = inputEl.attr('data-cid'); + const inputEl = $(this); + const cid = inputEl.attr('data-cid'); uploader.show({ title: '[[admin/manage/categories:alert.upload-image]]', @@ -173,7 +174,7 @@ define('admin/manage/category', [ params: { cid: cid }, }, function (imageUrlOnServer) { $('#category-image').val(imageUrlOnServer); - var previewBox = inputEl.parent().parent().siblings('.category-preview'); + const previewBox = inputEl.parent().parent().siblings('.category-preview'); previewBox.css('background', 'url(' + imageUrlOnServer + '?' + new Date().getTime() + ')'); modified($('#category-image')); @@ -188,8 +189,8 @@ define('admin/manage/category', [ $('.delete-image').on('click', function (e) { e.preventDefault(); - var inputEl = $('#category-image'); - var previewBox = $('.category-preview'); + const inputEl = $('#category-image'); + const previewBox = $('.category-preview'); inputEl.val(''); previewBox.css('background-image', ''); @@ -216,8 +217,8 @@ define('admin/manage/category', [ }).catch(app.alertError); }); $('button[data-action="toggle"]').on('click', function () { - var $this = $(this); - var disabled = $this.attr('data-disabled') === '1'; + const $this = $(this); + const disabled = $this.attr('data-disabled') === '1'; api.put('/categories/' + ajaxify.data.category.cid, { disabled: disabled ? 0 : 1, }).then(() => { @@ -229,14 +230,14 @@ define('admin/manage/category', [ }; function modified(el) { - var value; + let value; if ($(el).is(':checkbox')) { value = $(el).is(':checked') ? 1 : 0; } else { value = $(el).val(); } - var dataName = $(el).attr('data-name'); - var fields = dataName.match(/[^\][.]+/g); + const dataName = $(el).attr('data-name'); + const fields = dataName.match(/[^\][.]+/g); function setNestedFields(obj, index) { if (index === fields.length) { @@ -262,7 +263,7 @@ define('admin/manage/category', [ } function handleTags() { - var tagEl = $('#tag-whitelist'); + const tagEl = $('#tag-whitelist'); tagEl.tagsinput({ confirmKeys: [13, 44], trimValue: true, @@ -280,7 +281,7 @@ define('admin/manage/category', [ Category.launchParentSelector = function () { categorySelector.modal({ onSubmit: function (selectedCategory) { - var parentCid = selectedCategory.cid; + const parentCid = selectedCategory.cid; if (!parentCid) { return; } @@ -289,7 +290,7 @@ define('admin/manage/category', [ }).then(() => { api.get(`/categories/${parentCid}`, {}).then(function (parent) { if (parent && parent.icon && parent.name) { - var buttonHtml = ' ' + parent.name; + const buttonHtml = ' ' + parent.name; $('button[data-action="changeParent"]').html(buttonHtml).parent().removeClass('hide'); } }); diff --git a/public/src/admin/manage/digest.js b/public/src/admin/manage/digest.js index 3b07918277..86b19e6576 100644 --- a/public/src/admin/manage/digest.js +++ b/public/src/admin/manage/digest.js @@ -1,16 +1,16 @@ 'use strict'; -define('admin/manage/digest', function () { - var Digest = {}; +define('admin/manage/digest', ['bootbox'], function (bootbox) { + const Digest = {}; Digest.init = function () { $('table').on('click', '[data-action]', function () { - var action = this.getAttribute('data-action'); - var uid = this.getAttribute('data-uid'); + const action = this.getAttribute('data-action'); + const uid = this.getAttribute('data-uid'); if (action.startsWith('resend-')) { - var interval = action.slice(7); + const interval = action.slice(7); bootbox.confirm('[[admin/manage/digest:resend-all-confirm]]', function (ok) { if (ok) { Digest.send(action, undefined, function (err) { diff --git a/public/src/admin/manage/group.js b/public/src/admin/manage/group.js index c7fb51d6ae..8e7ea9133e 100644 --- a/public/src/admin/manage/group.js +++ b/public/src/admin/manage/group.js @@ -8,18 +8,19 @@ define('admin/manage/group', [ 'groupSearch', 'slugify', 'api', -], function (memberList, iconSelect, translator, categorySelector, groupSearch, slugify, api) { - var Groups = {}; + 'bootbox', +], function (memberList, iconSelect, translator, categorySelector, groupSearch, slugify, api, bootbox) { + const Groups = {}; Groups.init = function () { - var groupIcon = $('#group-icon'); - var changeGroupUserTitle = $('#change-group-user-title'); - var changeGroupLabelColor = $('#change-group-label-color'); - var changeGroupTextColor = $('#change-group-text-color'); - var groupLabelPreview = $('#group-label-preview'); - var groupLabelPreviewText = $('#group-label-preview-text'); + const groupIcon = $('#group-icon'); + const changeGroupUserTitle = $('#change-group-user-title'); + const changeGroupLabelColor = $('#change-group-label-color'); + const changeGroupTextColor = $('#change-group-text-color'); + const groupLabelPreview = $('#group-label-preview'); + const groupLabelPreviewText = $('#group-label-preview-text'); - var groupName = ajaxify.data.group.name; + const groupName = ajaxify.data.group.name; $('#group-selector').on('change', function () { ajaxify.go('admin/manage/groups/' + $(this).val() + window.location.hash); @@ -42,9 +43,9 @@ define('admin/manage/group', [ setupGroupMembersMenu(); $('#group-icon, #group-icon-label').on('click', function () { - var currentIcon = groupIcon.attr('value'); + const currentIcon = groupIcon.attr('value'); iconSelect.init(groupIcon, function () { - var newIcon = groupIcon.attr('value'); + let newIcon = groupIcon.attr('value'); if (newIcon === currentIcon) { return; } @@ -64,9 +65,9 @@ define('admin/manage/group', [ showLinks: true, }); - var cidSelector = categorySelector.init($('.member-post-cids-selector [component="category-selector"]'), { + const cidSelector = categorySelector.init($('.member-post-cids-selector [component="category-selector"]'), { onSelect: function (selectedCategory) { - var cids = ($('#memberPostCids').val() || '').split(',').map(cid => parseInt(cid, 10)); + let cids = ($('#memberPostCids').val() || '').split(',').map(cid => parseInt(cid, 10)); cids.push(selectedCategory.cid); cids = cids.filter((cid, index, array) => array.indexOf(cid) === index); $('#memberPostCids').val(cids.join(',')); @@ -96,7 +97,7 @@ define('admin/manage/group', [ disableJoinRequests: $('#group-disableJoinRequests').is(':checked'), disableLeave: $('#group-disableLeave').is(':checked'), }).then(() => { - var newName = $('#change-group-name').val(); + const newName = $('#change-group-name').val(); // If the group name changed, change url if (groupName !== newName) { @@ -111,12 +112,12 @@ define('admin/manage/group', [ function setupGroupMembersMenu() { $('[component="groups/members"]').on('click', '[data-action]', function () { - var btnEl = $(this); - var userRow = btnEl.parents('[data-uid]'); - var ownerFlagEl = userRow.find('.member-name .user-owner-icon'); - var isOwner = !ownerFlagEl.hasClass('invisible'); - var uid = userRow.attr('data-uid'); - var action = btnEl.attr('data-action'); + const btnEl = $(this); + const userRow = btnEl.parents('[data-uid]'); + const ownerFlagEl = userRow.find('.member-name .user-owner-icon'); + const isOwner = !ownerFlagEl.hasClass('invisible'); + const uid = userRow.attr('data-uid'); + const action = btnEl.attr('data-action'); switch (action) { case 'toggleOwnership': @@ -143,7 +144,7 @@ define('admin/manage/group', [ function navigateToCategory(cid) { if (cid) { - var url = 'admin/manage/privileges/' + cid + '?group=' + ajaxify.data.group.nameEncoded; + const url = 'admin/manage/privileges/' + cid + '?group=' + ajaxify.data.group.nameEncoded; if (app.flags && app.flags._unsaved === true) { translator.translate('[[global:unsaved-changes]]', function (text) { bootbox.confirm(text, function (navigate) { diff --git a/public/src/admin/manage/groups.js b/public/src/admin/manage/groups.js index 96fd8b63fc..10867bede5 100644 --- a/public/src/admin/manage/groups.js +++ b/public/src/admin/manage/groups.js @@ -4,16 +4,15 @@ define('admin/manage/groups', [ 'categorySelector', 'slugify', 'api', -], function (categorySelector, slugify, api) { - var Groups = {}; - - var intervalId = 0; + 'bootbox', +], function (categorySelector, slugify, api, bootbox) { + const Groups = {}; Groups.init = function () { - var createModal = $('#create-modal'); - var createGroupName = $('#create-group-name'); - var createModalGo = $('#create-modal-go'); - var createModalError = $('#create-modal-error'); + const createModal = $('#create-modal'); + const createGroupName = $('#create-group-name'); + const createModalGo = $('#create-modal-go'); + const createModalError = $('#create-modal-error'); handleSearch(); @@ -31,7 +30,7 @@ define('admin/manage/groups', [ }); createModalGo.on('click', function () { - var submitObj = { + const submitObj = { name: createGroupName.val(), description: $('#create-group-desc').val(), private: $('#create-group-private').is(':checked') ? 1 : 0, @@ -54,9 +53,9 @@ define('admin/manage/groups', [ }); $('.groups-list').on('click', '[data-action]', function () { - var el = $(this); - var action = el.attr('data-action'); - var groupName = el.parents('tr[data-groupname]').attr('data-groupname'); + const el = $(this); + const action = el.attr('data-action'); + const groupName = el.parents('tr[data-groupname]').attr('data-groupname'); switch (action) { case 'delete': @@ -74,7 +73,7 @@ define('admin/manage/groups', [ function enableCategorySelectors() { $('.groups-list [component="category-selector"]').each(function () { - var nameEncoded = $(this).parents('[data-name-encoded]').attr('data-name-encoded'); + const nameEncoded = $(this).parents('[data-name-encoded]').attr('data-name-encoded'); categorySelector.init($(this), { onSelect: function (selectedCategory) { ajaxify.go('admin/manage/privileges/' + selectedCategory.cid + '?group=' + nameEncoded); @@ -85,14 +84,14 @@ define('admin/manage/groups', [ } function handleSearch() { - var queryEl = $('#group-search'); + const queryEl = $('#group-search'); function doSearch() { if (!queryEl.val()) { return ajaxify.refresh(); } $('.pagination').addClass('hide'); - var groupsEl = $('.groups-list'); + const groupsEl = $('.groups-list'); socket.emit('groups.search', { query: queryEl.val(), options: { @@ -114,13 +113,7 @@ define('admin/manage/groups', [ }); } - queryEl.on('keyup', function () { - if (intervalId) { - clearTimeout(intervalId); - intervalId = 0; - } - intervalId = setTimeout(doSearch, 200); - }); + queryEl.on('keyup', utils.debounce(doSearch, 200)); } diff --git a/public/src/admin/manage/privileges.js b/public/src/admin/manage/privileges.js index e45f967475..afb9c45a6b 100644 --- a/public/src/admin/manage/privileges.js +++ b/public/src/admin/manage/privileges.js @@ -183,7 +183,8 @@ define('admin/manage/privileges', [ api.get(`/categories/${cid}/privileges`, {}).then((privileges) => { ajaxify.data.privileges = { ...ajaxify.data.privileges, ...privileges }; const tpl = parseInt(cid, 10) ? 'admin/partials/privileges/category' : 'admin/partials/privileges/global'; - app.parseAndTranslate(tpl, { privileges }).then((html) => { + const isAdminPriv = ajaxify.currentPage.endsWith('admin/manage/privileges/admin'); + app.parseAndTranslate(tpl, { privileges, isAdminPriv }).then((html) => { // Get currently selected filters const btnIndices = $('.privilege-filters button.btn-warning').map((idx, el) => $(el).index()).get(); $('.privilege-table-container').html(html); diff --git a/public/src/admin/manage/registration.js b/public/src/admin/manage/registration.js index 08f8b6722b..90f0387f87 100644 --- a/public/src/admin/manage/registration.js +++ b/public/src/admin/manage/registration.js @@ -1,15 +1,15 @@ 'use strict'; -define('admin/manage/registration', function () { - var Registration = {}; +define('admin/manage/registration', ['bootbox'], function (bootbox) { + const Registration = {}; Registration.init = function () { $('.users-list').on('click', '[data-action]', function () { - var parent = $(this).parents('[data-username]'); - var action = $(this).attr('data-action'); - var username = parent.attr('data-username'); - var method = action === 'accept' ? 'user.acceptRegistration' : 'user.rejectRegistration'; + const parent = $(this).parents('[data-username]'); + const action = $(this).attr('data-action'); + const username = parent.attr('data-username'); + const method = action === 'accept' ? 'user.acceptRegistration' : 'user.rejectRegistration'; socket.emit(method, { username: username }, function (err) { if (err) { @@ -21,16 +21,16 @@ define('admin/manage/registration', function () { }); $('.invites-list').on('click', '[data-action]', function () { - var parent = $(this).parents('[data-invitation-mail][data-invited-by]'); - var email = parent.attr('data-invitation-mail'); - var invitedBy = parent.attr('data-invited-by'); - var action = $(this).attr('data-action'); - var method = 'user.deleteInvitation'; + const parent = $(this).parents('[data-invitation-mail][data-invited-by]'); + const email = parent.attr('data-invitation-mail'); + const invitedBy = parent.attr('data-invited-by'); + const action = $(this).attr('data-action'); + const method = 'user.deleteInvitation'; - var removeRow = function () { - var nextRow = parent.next(); - var thisRowinvitedBy = parent.find('.invited-by'); - var nextRowInvitedBy = nextRow.find('.invited-by'); + const removeRow = function () { + const nextRow = parent.next(); + const thisRowinvitedBy = parent.find('.invited-by'); + const nextRowInvitedBy = nextRow.find('.invited-by'); if (nextRowInvitedBy.html() !== undefined && nextRowInvitedBy.html().length < 2) { nextRowInvitedBy.html(thisRowinvitedBy.html()); } diff --git a/public/src/admin/manage/tags.js b/public/src/admin/manage/tags.js index 6cb4ad96f6..836c8f7be9 100644 --- a/public/src/admin/manage/tags.js +++ b/public/src/admin/manage/tags.js @@ -2,11 +2,11 @@ define('admin/manage/tags', [ + 'bootbox', 'forum/infinitescroll', 'admin/modules/selectable', -], function (infinitescroll, selectable) { - var Tags = {}; - var timeoutId = 0; +], function (bootbox, infinitescroll, selectable) { + const Tags = {}; Tags.init = function () { selectable.enable('.tag-management', '.tag-row'); @@ -18,9 +18,9 @@ define('admin/manage/tags', [ }; function handleCreate() { - var createModal = $('#create-modal'); - var createTagName = $('#create-tag-name'); - var createModalGo = $('#create-modal-go'); + const createModal = $('#create-modal'); + const createTagName = $('#create-tag-name'); + const createModalGo = $('#create-modal-go'); createModal.on('keypress', function (e) { if (e.keyCode === 13) { @@ -53,42 +53,33 @@ define('admin/manage/tags', [ } function handleSearch() { - $('#tag-search').on('input propertychange', function () { - if (timeoutId) { - clearTimeout(timeoutId); - timeoutId = 0; - } - - timeoutId = setTimeout(function () { - socket.emit('topics.searchAndLoadTags', { - query: $('#tag-search').val(), - }, function (err, result) { - if (err) { - return app.alertError(err.message); - } - - app.parseAndTranslate('admin/manage/tags', 'tags', { - tags: result.tags, - }, function (html) { - $('.tag-list').html(html); - utils.makeNumbersHumanReadable(html.find('.human-readable-number')); - timeoutId = 0; + $('#tag-search').on('input propertychange', utils.debounce(function () { + socket.emit('topics.searchAndLoadTags', { + query: $('#tag-search').val(), + }, function (err, result) { + if (err) { + return app.alertError(err.message); + } - selectable.enable('.tag-management', '.tag-row'); - }); + app.parseAndTranslate('admin/manage/tags', 'tags', { + tags: result.tags, + }, function (html) { + $('.tag-list').html(html); + utils.makeNumbersHumanReadable(html.find('.human-readable-number')); + selectable.enable('.tag-management', '.tag-row'); }); - }, 250); - }); + }); + }, 250)); } function handleRename() { $('#rename').on('click', function () { - var tagsToModify = $('.tag-row.ui-selected'); + const tagsToModify = $('.tag-row.ui-selected'); if (!tagsToModify.length) { return; } - var modal = bootbox.dialog({ + const modal = bootbox.dialog({ title: '[[admin/manage/tags:alerts.editing]]', message: $('.rename-modal').html(), buttons: { @@ -96,7 +87,7 @@ define('admin/manage/tags', [ label: 'Save', className: 'btn-primary save', callback: function () { - var data = []; + const data = []; tagsToModify.each(function (idx, tag) { tag = $(tag); data.push({ @@ -121,7 +112,7 @@ define('admin/manage/tags', [ function handleDeleteSelected() { $('#deleteSelected').on('click', function () { - var tagsToDelete = $('.tag-row.ui-selected'); + const tagsToDelete = $('.tag-row.ui-selected'); if (!tagsToDelete.length) { return; } @@ -130,7 +121,7 @@ define('admin/manage/tags', [ if (!confirm) { return; } - var tags = []; + const tags = []; tagsToDelete.each(function (index, el) { tags.push($(el).attr('data-tag')); }); diff --git a/public/src/admin/manage/uploads.js b/public/src/admin/manage/uploads.js index 6ed59d2f77..ef19b1aaf8 100644 --- a/public/src/admin/manage/uploads.js +++ b/public/src/admin/manage/uploads.js @@ -1,7 +1,7 @@ 'use strict'; define('admin/manage/uploads', ['api', 'bootbox', 'uploader'], function (api, bootbox, uploader) { - var Uploads = {}; + const Uploads = {}; Uploads.init = function () { $('#upload').on('click', function () { @@ -15,7 +15,7 @@ define('admin/manage/uploads', ['api', 'bootbox', 'uploader'], function (api, bo }); $('.delete').on('click', function () { - var file = $(this).parents('[data-path]'); + const file = $(this).parents('[data-path]'); bootbox.confirm('[[admin/manage/uploads:confirm-delete]]', function (ok) { if (!ok) { return; diff --git a/public/src/admin/manage/users.js b/public/src/admin/manage/users.js index 5675f5b0aa..7c6543fa75 100644 --- a/public/src/admin/manage/users.js +++ b/public/src/admin/manage/users.js @@ -3,13 +3,13 @@ define('admin/manage/users', [ 'translator', 'benchpress', 'autocomplete', 'api', 'slugify', 'bootbox', 'accounts/invite', ], function (translator, Benchpress, autocomplete, api, slugify, bootbox, AccountInvite) { - var Users = {}; + const Users = {}; Users.init = function () { $('#results-per-page').val(ajaxify.data.resultsPerPage).on('change', function () { - var query = utils.params(); + const query = utils.params(); query.resultsPerPage = $('#results-per-page').val(); - var qs = buildSearchQuery(query); + const qs = buildSearchQuery(query); ajaxify.go(window.location.pathname + '?' + qs); }); @@ -42,7 +42,7 @@ define('admin/manage/users', [ }); function getSelectedUids() { - var uids = []; + const uids = []; $('.users-table [component="user/select/single"]').each(function () { if ($(this).is(':checked')) { @@ -99,7 +99,7 @@ define('admin/manage/users', [ }); $('.manage-groups').on('click', function () { - var uids = getSelectedUids(); + const uids = getSelectedUids(); if (!uids.length) { app.alertError('[[error:no-users-selected]]'); return false; @@ -109,14 +109,14 @@ define('admin/manage/users', [ return app.alertError(err); } Benchpress.render('admin/partials/manage_user_groups', data).then(function (html) { - var modal = bootbox.dialog({ + const modal = bootbox.dialog({ message: html, title: '[[admin/manage/users:manage-groups]]', onEscape: true, }); modal.on('shown.bs.modal', function () { autocomplete.group(modal.find('.group-search'), function (ev, ui) { - var uid = $(ev.target).attr('data-uid'); + const uid = $(ev.target).attr('data-uid'); api.put('/groups/' + ui.item.group.slug + '/membership/' + uid, undefined).then(() => { ui.item.group.nameEscaped = translator.escape(ui.item.group.displayName); app.parseAndTranslate('admin/partials/manage_user_groups', { users: [{ groups: [ui.item.group] }] }, function (html) { @@ -129,9 +129,9 @@ define('admin/manage/users', [ modal.modal('hide'); }); modal.on('click', '.remove-group-icon', function () { - var groupCard = $(this).parents('[data-group-name]'); - var groupName = groupCard.attr('data-group-name'); - var uid = $(this).parents('[data-uid]').attr('data-uid'); + const groupCard = $(this).parents('[data-group-name]'); + const groupName = groupCard.attr('data-group-name'); + const uid = $(this).parents('[data-uid]').attr('data-uid'); api.del('/groups/' + slugify(groupName) + '/membership/' + uid).then(() => { groupCard.remove(); }).catch(app.alertError); @@ -142,7 +142,7 @@ define('admin/manage/users', [ }); $('.ban-user').on('click', function () { - var uids = getSelectedUids(); + const uids = getSelectedUids(); if (!uids.length) { app.alertError('[[error:no-users-selected]]'); return false; // specifically to keep the menu open @@ -160,7 +160,7 @@ define('admin/manage/users', [ }); $('.ban-user-temporary').on('click', function () { - var uids = getSelectedUids(); + const uids = getSelectedUids(); if (!uids.length) { app.alertError('[[error:no-users-selected]]'); return false; // specifically to keep the menu open @@ -180,11 +180,11 @@ define('admin/manage/users', [ submit: { label: '[[admin/manage/users:alerts.button-ban-x, ' + uids.length + ']]', callback: function () { - var formData = $('.ban-modal form').serializeArray().reduce(function (data, cur) { + const formData = $('.ban-modal form').serializeArray().reduce(function (data, cur) { data[cur.name] = cur.value; return data; }, {}); - var until = formData.length > 0 ? ( + const until = formData.length > 0 ? ( Date.now() + (formData.length * 1000 * 60 * 60 * (parseInt(formData.unit, 10) ? 24 : 1)) ) : 0; @@ -204,7 +204,7 @@ define('admin/manage/users', [ }); $('.unban-user').on('click', function () { - var uids = getSelectedUids(); + const uids = getSelectedUids(); if (!uids.length) { app.alertError('[[error:no-users-selected]]'); return false; // specifically to keep the menu open @@ -218,7 +218,7 @@ define('admin/manage/users', [ }); $('.reset-lockout').on('click', function () { - var uids = getSelectedUids(); + const uids = getSelectedUids(); if (!uids.length) { return; } @@ -227,7 +227,7 @@ define('admin/manage/users', [ }); $('.validate-email').on('click', function () { - var uids = getSelectedUids(); + const uids = getSelectedUids(); if (!uids.length) { return; } @@ -249,7 +249,7 @@ define('admin/manage/users', [ }); $('.send-validation-email').on('click', function () { - var uids = getSelectedUids(); + const uids = getSelectedUids(); if (!uids.length) { return; } @@ -262,7 +262,7 @@ define('admin/manage/users', [ }); $('.password-reset-email').on('click', function () { - var uids = getSelectedUids(); + const uids = getSelectedUids(); if (!uids.length) { return; } @@ -275,7 +275,7 @@ define('admin/manage/users', [ }); $('.force-password-reset').on('click', function () { - var uids = getSelectedUids(); + const uids = getSelectedUids(); if (!uids.length) { return; } @@ -304,7 +304,7 @@ define('admin/manage/users', [ tableEl.addEventListener('change', (e) => { const subselector = e.target.closest('[component="user/select/single"]') || e.target.closest('[component="user/select/all"]'); if (subselector) { - var uids = getSelectedUids(); + const uids = getSelectedUids(); if (uids.length) { actionBtn.removeAttribute('disabled'); } else { @@ -314,7 +314,7 @@ define('admin/manage/users', [ }); function handleDelete(confirmMsg, path) { - var uids = getSelectedUids(); + const uids = getSelectedUids(); if (!uids.length) { return; } @@ -347,7 +347,7 @@ define('admin/manage/users', [ function handleUserCreate() { $('[data-action="create"]').on('click', function () { Benchpress.render('admin/partials/create_user_modal', {}).then(function (html) { - var modal = bootbox.dialog({ + const modal = bootbox.dialog({ message: html, title: '[[admin/manage/users:alerts.create]]', onEscape: true, @@ -375,19 +375,19 @@ define('admin/manage/users', [ } function createUser() { - var modal = this; - var username = document.getElementById('create-user-name').value; - var email = document.getElementById('create-user-email').value; - var password = document.getElementById('create-user-password').value; - var passwordAgain = document.getElementById('create-user-password-again').value; + const modal = this; + const username = document.getElementById('create-user-name').value; + const email = document.getElementById('create-user-email').value; + const password = document.getElementById('create-user-password').value; + const passwordAgain = document.getElementById('create-user-password-again').value; - var errorEl = $('#create-modal-error'); + const errorEl = $('#create-modal-error'); if (password !== passwordAgain) { return errorEl.translateHtml('[[admin/manage/users:alerts.error-x, [[admin/manage/users:alerts.error-passwords-different]]]]').removeClass('hide'); } - var user = { + const user = { username: username, email: email, password: password, @@ -412,7 +412,6 @@ define('admin/manage/users', [ }; function handleSearch() { - var timeoutId = 0; function doSearch() { $('.fa-spinner').removeClass('hidden'); loadSearchPage({ @@ -421,25 +420,17 @@ define('admin/manage/users', [ page: 1, }); } - $('#user-search').on('keyup', function () { - if (timeoutId !== 0) { - clearTimeout(timeoutId); - timeoutId = 0; - } - timeoutId = setTimeout(doSearch, 250); - }); - $('#user-search-by').on('change', function () { - doSearch(); - }); + $('#user-search').on('keyup', utils.debounce(doSearch, 250)); + $('#user-search-by').on('change', doSearch); } function loadSearchPage(query) { - var params = utils.params(); + const params = utils.params(); params.searchBy = query.searchBy; params.query = query.query; params.page = query.page; params.sortBy = params.sortBy || 'lastonline'; - var qs = decodeURIComponent($.param(params)); + const qs = decodeURIComponent($.param(params)); $.get(config.relative_path + '/api/admin/manage/users?' + qs, function (data) { renderSearchResults(data); const url = config.relative_path + '/admin/manage/users?' + qs; @@ -497,12 +488,12 @@ define('admin/manage/users', [ function handleSort() { $('.users-table thead th').on('click', function () { - var $this = $(this); - var sortBy = $this.attr('data-sort'); + const $this = $(this); + const sortBy = $this.attr('data-sort'); if (!sortBy) { return; } - var params = utils.params(); + const params = utils.params(); params.sortBy = sortBy; if (ajaxify.data.sortBy === sortBy) { params.sortDirection = ajaxify.data.reverse ? 'asc' : 'desc'; @@ -510,13 +501,13 @@ define('admin/manage/users', [ params.sortDirection = 'desc'; } - var qs = buildSearchQuery(params); + const qs = buildSearchQuery(params); ajaxify.go('admin/manage/users?' + qs); }); } function getFilters() { - var filters = []; + const filters = []; $('#filter-by').find('[data-filter-by]').each(function () { if ($(this).find('.fa-check').length) { filters.push($(this).attr('data-filter-by')); @@ -526,16 +517,16 @@ define('admin/manage/users', [ } function handleFilter() { - var currentFilters = getFilters(); + let currentFilters = getFilters(); $('#filter-by').on('click', 'li', function () { - var $this = $(this); + const $this = $(this); $this.find('i').toggleClass('fa-check', !$this.find('i').hasClass('fa-check')); return false; }); $('#filter-by').on('hidden.bs.dropdown', function () { - var filters = getFilters(); - var changed = filters.length !== currentFilters.length; + const filters = getFilters(); + let changed = filters.length !== currentFilters.length; if (filters.length === currentFilters.length) { filters.forEach(function (filter, i) { if (filter !== currentFilters[i]) { @@ -545,9 +536,9 @@ define('admin/manage/users', [ } currentFilters = getFilters(); if (changed) { - var params = utils.params(); + const params = utils.params(); params.filters = filters; - var qs = buildSearchQuery(params); + const qs = buildSearchQuery(params); ajaxify.go('admin/manage/users?' + qs); } }); diff --git a/public/src/admin/modules/colorpicker.js b/public/src/admin/modules/colorpicker.js index 6000c416d6..3b64164647 100644 --- a/public/src/admin/modules/colorpicker.js +++ b/public/src/admin/modules/colorpicker.js @@ -1,12 +1,12 @@ 'use strict'; - +// TODO: no longer used remove in 1.19.0 define('admin/modules/colorpicker', function () { - var colorpicker = {}; + const colorpicker = {}; colorpicker.enable = function (inputEl, callback) { (inputEl instanceof jQuery ? inputEl : $(inputEl)).each(function () { - var $this = $(this); + const $this = $(this); $this.ColorPicker({ color: $this.val() || '#000', diff --git a/public/src/admin/modules/dashboard-line-graph.js b/public/src/admin/modules/dashboard-line-graph.js index ac53497ce7..38f75e62cc 100644 --- a/public/src/admin/modules/dashboard-line-graph.js +++ b/public/src/admin/modules/dashboard-line-graph.js @@ -1,6 +1,6 @@ 'use strict'; -define('admin/modules/dashboard-line-graph', ['Chart', 'translator', 'benchpress', 'api', 'hooks'], function (Chart, translator, Benchpress, api, hooks) { +define('admin/modules/dashboard-line-graph', ['Chart', 'translator', 'benchpress', 'api', 'hooks', 'bootbox'], function (Chart, translator, Benchpress, api, hooks, bootbox) { const Graph = { _current: null, }; @@ -18,7 +18,7 @@ define('admin/modules/dashboard-line-graph', ['Chart', 'translator', 'benchpress Graph.handleUpdateControls({ set }); - var t = translator.Translator.create(); + const t = translator.Translator.create(); return new Promise((resolve) => { t.translateKey(`admin/menu:${ajaxify.data.template.name.replace('admin/', '')}`, []).then((key) => { const data = { @@ -81,8 +81,8 @@ define('admin/modules/dashboard-line-graph', ['Chart', 'translator', 'benchpress Graph.handleUpdateControls = ({ set }) => { $('[data-action="updateGraph"]:not([data-units="custom"])').on('click', function () { - var until = new Date(); - var amount = $(this).attr('data-amount'); + let until = new Date(); + const amount = $(this).attr('data-amount'); if ($(this).attr('data-units') === 'days') { until.setHours(0, 0, 0, 0); } @@ -97,10 +97,10 @@ define('admin/modules/dashboard-line-graph', ['Chart', 'translator', 'benchpress }); $('[data-action="updateGraph"][data-units="custom"]').on('click', function () { - var targetEl = $(this); + const targetEl = $(this); Benchpress.render('admin/partials/pageviews-range-select', {}).then(function (html) { - var modal = bootbox.dialog({ + const modal = bootbox.dialog({ title: '[[admin/dashboard:page-views-custom]]', message: html, buttons: { @@ -111,10 +111,10 @@ define('admin/modules/dashboard-line-graph', ['Chart', 'translator', 'benchpress }, }, }).on('shown.bs.modal', function () { - var date = new Date(); - var today = date.toISOString().substr(0, 10); + const date = new Date(); + const today = date.toISOString().substr(0, 10); date.setDate(date.getDate() - 1); - var yesterday = date.toISOString().substr(0, 10); + const yesterday = date.toISOString().substr(0, 10); modal.find('#startRange').val(targetEl.attr('data-startRange') || yesterday); modal.find('#endRange').val(targetEl.attr('data-endRange') || today); @@ -122,8 +122,8 @@ define('admin/modules/dashboard-line-graph', ['Chart', 'translator', 'benchpress function submit() { // NEED TO ADD VALIDATION HERE FOR YYYY-MM-DD - var formData = modal.find('form').serializeObject(); - var validRegexp = /\d{4}-\d{2}-\d{2}/; + const formData = modal.find('form').serializeObject(); + const validRegexp = /\d{4}-\d{2}-\d{2}/; // Input validation if (!formData.startRange && !formData.endRange) { @@ -136,10 +136,10 @@ define('admin/modules/dashboard-line-graph', ['Chart', 'translator', 'benchpress return false; } - var until = new Date(formData.endRange); + let until = new Date(formData.endRange); until.setDate(until.getDate() + 1); until = until.getTime(); - var amount = (until - new Date(formData.startRange).getTime()) / (1000 * 60 * 60 * 24); + const amount = (until - new Date(formData.startRange).getTime()) / (1000 * 60 * 60 * 24); Graph.update(set, 'days', until, amount); @@ -175,8 +175,8 @@ define('admin/modules/dashboard-line-graph', ['Chart', 'translator', 'benchpress Graph._current.update(); // Update address bar and "View as JSON" button url - var apiEl = $('#view-as-json'); - var newHref = $.param({ + const apiEl = $('#view-as-json'); + const newHref = $.param({ units: units || 'hours', until: until, count: amount, diff --git a/public/src/admin/modules/instance.js b/public/src/admin/modules/instance.js index bdb2aad21b..996e381229 100644 --- a/public/src/admin/modules/instance.js +++ b/public/src/admin/modules/instance.js @@ -6,7 +6,7 @@ define('admin/modules/instance', [ // the client can't fetch the template file, resulting in an error config.relative_path + '/assets/templates/alert.js', ], function () { - var instance = {}; + const instance = {}; instance.rebuildAndRestart = function (callback) { app.alert({ diff --git a/public/src/admin/modules/search.js b/public/src/admin/modules/search.js index e2ea2a4c3d..1bb62ad2cc 100644 --- a/public/src/admin/modules/search.js +++ b/public/src/admin/modules/search.js @@ -1,18 +1,18 @@ 'use strict'; define('admin/modules/search', ['mousetrap'], function (mousetrap) { - var search = {}; + const search = {}; function find(dict, term) { - var html = dict.filter(function (elem) { + const html = dict.filter(function (elem) { return elem.translations.toLowerCase().includes(term); }).map(function (params) { - var namespace = params.namespace; - var translations = params.translations; - var title = params.title; - var escaped = utils.escapeRegexChars(term); + const namespace = params.namespace; + const translations = params.translations; + let title = params.title; + const escaped = utils.escapeRegexChars(term); - var results = translations + const results = translations // remove all lines without a match .replace(new RegExp('^(?:(?!' + escaped + ').)*$', 'gmi'), '') // remove lines that only match the title @@ -60,9 +60,9 @@ define('admin/modules/search', ['mousetrap'], function (mousetrap) { }; function setupACPSearch(dict) { - var dropdown = $('#acp-search .dropdown'); - var menu = $('#acp-search .dropdown-menu'); - var input = $('#acp-search input'); + const dropdown = $('#acp-search .dropdown'); + const menu = $('#acp-search .dropdown-menu'); + const input = $('#acp-search input'); if (!config.searchEnabled) { menu.addClass('search-disabled'); @@ -73,11 +73,11 @@ define('admin/modules/search', ['mousetrap'], function (mousetrap) { }); $('#acp-search').parents('form').on('submit', function (ev) { - var selected = menu.find('li.result > a.focus').attr('href'); + let selected = menu.find('li.result > a.focus').attr('href'); if (!selected.length) { selected = menu.find('li.result > a').first().attr('href'); } - var href = selected || config.relative_path + '/search?in=titlesposts&term=' + escape(input.val()); + const href = selected || config.relative_path + '/search?in=titlesposts&term=' + escape(input.val()); ajaxify.go(href.replace(/^\//, '')); @@ -96,7 +96,7 @@ define('admin/modules/search', ['mousetrap'], function (mousetrap) { }); mousetrap(input[0]).bind(['up', 'down'], function (ev, key) { - var next; + let next; if (key === 'up') { next = menu.find('li.result > a.focus').removeClass('focus').parent().prev('.result') .children(); @@ -122,10 +122,10 @@ define('admin/modules/search', ['mousetrap'], function (mousetrap) { ev.preventDefault(); }); - var prevValue; + let prevValue; input.on('keyup focus', function () { - var value = input.val().toLowerCase(); + const value = input.val().toLowerCase(); if (value === prevValue) { return; @@ -134,8 +134,8 @@ define('admin/modules/search', ['mousetrap'], function (mousetrap) { menu.children('.result').remove(); - var len = /\W/.test(value) ? 3 : value.length; - var results; + const len = /\W/.test(value) ? 3 : value.length; + let results; menu.toggleClass('state-start-typing', len === 0); menu.toggleClass('state-keep-typing', len > 0 && len < 3); diff --git a/public/src/admin/modules/selectable.js b/public/src/admin/modules/selectable.js index 34949a4e10..470462d558 100644 --- a/public/src/admin/modules/selectable.js +++ b/public/src/admin/modules/selectable.js @@ -4,7 +4,7 @@ define('admin/modules/selectable', [ 'jquery-ui/widgets/selectable', ], function () { - var selectable = {}; + const selectable = {}; selectable.enable = function (containerEl, targets) { $(containerEl).selectable({ diff --git a/public/src/admin/settings.js b/public/src/admin/settings.js index 5b5dee579e..c7166305a0 100644 --- a/public/src/admin/settings.js +++ b/public/src/admin/settings.js @@ -2,21 +2,21 @@ define('admin/settings', ['uploader', 'mousetrap', 'hooks'], function (uploader, mousetrap, hooks) { - var Settings = {}; + const Settings = {}; Settings.populateTOC = function () { - var headers = $('.settings-header'); + const headers = $('.settings-header'); if (headers.length > 1) { headers.each(function () { - var header = $(this).text(); - var anchor = header.toLowerCase().replace(/ /g, '-').trim(); + const header = $(this).text(); + const anchor = header.toLowerCase().replace(/ /g, '-').trim(); $(this).prepend(''); $('.section-content ul').append('
  • ' + header + '
  • '); }); - var scrollTo = $('a[name="' + window.location.hash.replace('#', '') + '"]'); + const scrollTo = $('a[name="' + window.location.hash.replace('#', '') + '"]'); if (scrollTo.length) { $('html, body').animate({ scrollTop: (scrollTo.offset().top) + 'px', @@ -29,28 +29,28 @@ define('admin/settings', ['uploader', 'mousetrap', 'hooks'], function (uploader, Settings.prepare = function (callback) { // Populate the fields on the page from the config - var fields = $('#content [data-field]'); - var numFields = fields.length; - var saveBtn = $('#save'); - var revertBtn = $('#revert'); - var x; - var key; - var inputType; - var field; + const fields = $('#content [data-field]'); + const numFields = fields.length; + const saveBtn = $('#save'); + const revertBtn = $('#revert'); + let x; + let key; + let inputType; + let field; // Handle unsaved changes fields.on('change', function () { app.flags = app.flags || {}; app.flags._unsaved = true; }); - var defaultInputs = ['text', 'hidden', 'password', 'textarea', 'number']; + const defaultInputs = ['text', 'hidden', 'password', 'textarea', 'number']; for (x = 0; x < numFields; x += 1) { field = fields.eq(x); key = field.attr('data-field'); inputType = field.attr('type'); if (app.config.hasOwnProperty(key)) { if (field.is('input') && inputType === 'checkbox') { - var checked = parseInt(app.config[key], 10) === 1; + const checked = parseInt(app.config[key], 10) === 1; field.prop('checked', checked); field.parents('.mdl-switch').toggleClass('is-checked', checked); } else if (field.is('textarea') || field.is('select') || (field.is('input') && defaultInputs.indexOf(inputType) !== -1)) { @@ -117,7 +117,7 @@ define('admin/settings', ['uploader', 'mousetrap', 'hooks'], function (uploader, function handleUploads() { $('#content input[data-action="upload"]').each(function () { - var uploadBtn = $(this); + const uploadBtn = $(this); uploadBtn.on('click', function () { uploader.show({ title: uploadBtn.attr('data-title'), @@ -146,13 +146,13 @@ define('admin/settings', ['uploader', 'mousetrap', 'hooks'], function (uploader, }; function saveFields(fields, callback) { - var data = {}; + const data = {}; fields.each(function () { - var field = $(this); - var key = field.attr('data-field'); - var value; - var inputType; + const field = $(this); + const key = field.attr('data-field'); + let value; + let inputType; if (field.is('input')) { inputType = field.attr('type'); @@ -181,7 +181,7 @@ define('admin/settings', ['uploader', 'mousetrap', 'hooks'], function (uploader, return callback(err); } - for (var field in data) { + for (const field in data) { if (data.hasOwnProperty(field)) { app.config[field] = data[field]; } diff --git a/public/src/admin/settings/api.js b/public/src/admin/settings/api.js index e2a755ee6c..6f70813a76 100644 --- a/public/src/admin/settings/api.js +++ b/public/src/admin/settings/api.js @@ -1,7 +1,7 @@ 'use strict'; define('admin/settings/api', ['settings'], function (settings) { - var ACP = {}; + const ACP = {}; ACP.init = function () { settings.load('core.api', $('.core-api-settings')); diff --git a/public/src/admin/settings/cookies.js b/public/src/admin/settings/cookies.js index 0a2e8c7243..886fc7ad99 100644 --- a/public/src/admin/settings/cookies.js +++ b/public/src/admin/settings/cookies.js @@ -1,14 +1,9 @@ 'use strict'; - -define('admin/settings/cookies', [ - 'admin/modules/colorpicker', -], function (colorpicker) { - var Module = {}; +define('admin/settings/cookies', function () { + const Module = {}; Module.init = function () { - colorpicker.enable($('[data-colorpicker="1"]')); - $('#delete-all-sessions').on('click', function () { socket.emit('admin.deleteAllSessions', function (err) { if (err) { diff --git a/public/src/admin/settings/email.js b/public/src/admin/settings/email.js index 8f2f7f46ed..83a6137e77 100644 --- a/public/src/admin/settings/email.js +++ b/public/src/admin/settings/email.js @@ -2,8 +2,8 @@ define('admin/settings/email', ['ace/ace', 'admin/settings'], function (ace) { - var module = {}; - var emailEditor; + const module = {}; + let emailEditor; module.init = function () { configureEmailTester(); @@ -40,14 +40,14 @@ define('admin/settings/email', ['ace/ace', 'admin/settings'], function (ace) { emailEditor.getSession().setMode('ace/mode/html'); emailEditor.on('change', function () { - var emailPath = $('#email-editor-selector').val(); - var original; + const emailPath = $('#email-editor-selector').val(); + let original; ajaxify.data.emails.forEach(function (email) { if (email.path === emailPath) { original = email.original; } }); - var newEmail = emailEditor.getValue(); + const newEmail = emailEditor.getValue(); $('#email-editor-holder').val(newEmail !== original ? newEmail : ''); }); @@ -75,7 +75,7 @@ define('admin/settings/email', ['ace/ace', 'admin/settings'], function (ace) { } function handleDigestHourChange() { - var hour = parseInt($('#digestHour').val(), 10); + let hour = parseInt($('#digestHour').val(), 10); if (isNaN(hour)) { hour = 17; @@ -88,8 +88,8 @@ define('admin/settings/email', ['ace/ace', 'admin/settings'], function (ace) { return app.alertError(err.message); } - var date = new Date(now.timestamp); - var offset = (new Date().getTimezoneOffset() - now.offset) / 60; + const date = new Date(now.timestamp); + const offset = (new Date().getTimezoneOffset() - now.offset) / 60; date.setHours(date.getHours() + offset); $('#serverTime').text(date.toLocaleTimeString()); @@ -106,7 +106,7 @@ define('admin/settings/email', ['ace/ace', 'admin/settings'], function (ace) { } function handleSmtpServiceChange() { - var isCustom = $('[id="email:smtpTransport:service"]').val() === 'nodebb-custom-smtp'; + const isCustom = $('[id="email:smtpTransport:service"]').val() === 'nodebb-custom-smtp'; $('[id="email:smtpTransport:custom-service"]')[isCustom ? 'slideDown' : 'slideUp'](isCustom); } diff --git a/public/src/admin/settings/general.js b/public/src/admin/settings/general.js index bf88ded896..805f9f1c8f 100644 --- a/public/src/admin/settings/general.js +++ b/public/src/admin/settings/general.js @@ -2,7 +2,7 @@ define('admin/settings/general', ['admin/settings'], function () { - var Module = {}; + const Module = {}; Module.init = function () { $('button[data-action="removeLogo"]').on('click', function () { diff --git a/public/src/admin/settings/homepage.js b/public/src/admin/settings/homepage.js index 87c845921e..96447d9bbb 100644 --- a/public/src/admin/settings/homepage.js +++ b/public/src/admin/settings/homepage.js @@ -10,7 +10,7 @@ define('admin/settings/homepage', ['admin/settings'], function () { } } - var Homepage = {}; + const Homepage = {}; Homepage.init = function () { $('[data-field="homePageRoute"]').on('change', toggleCustomRoute); diff --git a/public/src/admin/settings/navigation.js b/public/src/admin/settings/navigation.js index 563709ad0b..a2fef0a0d3 100644 --- a/public/src/admin/settings/navigation.js +++ b/public/src/admin/settings/navigation.js @@ -9,8 +9,8 @@ define('admin/settings/navigation', [ 'jquery-ui/widgets/droppable', 'jquery-ui/widgets/sortable', ], function (translator, iconSelect, Benchpress) { - var navigation = {}; - var available; + const navigation = {}; + let available; navigation.init = function () { available = ajaxify.data.available; @@ -27,10 +27,10 @@ define('admin/settings/navigation', [ }); $('#enabled').on('click', '.iconPicker', function () { - var iconEl = $(this).find('i'); + const iconEl = $(this).find('i'); iconSelect.init(iconEl, function (el) { - var newIconClass = el.attr('value'); - var index = iconEl.parents('[data-index]').attr('data-index'); + const newIconClass = el.attr('value'); + const index = iconEl.parents('[data-index]').attr('data-index'); $('#active-navigation [data-index="' + index + '"] i').attr('class', 'fa fa-fw ' + newIconClass); iconEl.siblings('[name="iconClass"]').val(newIconClass); iconEl.siblings('.change-icon-link').toggleClass('hidden', !!newIconClass); @@ -47,11 +47,11 @@ define('admin/settings/navigation', [ }; function onSelect() { - var clickedIndex = $(this).attr('data-index'); + const clickedIndex = $(this).attr('data-index'); $('#active-navigation li').removeClass('active'); $(this).addClass('active'); - var detailsForm = $('#enabled').children('[data-index="' + clickedIndex + '"]'); + const detailsForm = $('#enabled').children('[data-index="' + clickedIndex + '"]'); $('#enabled li').addClass('hidden'); if (detailsForm.length) { @@ -61,9 +61,9 @@ define('admin/settings/navigation', [ } function drop(ev, ui) { - var id = ui.helper.attr('data-id'); - var el = $('#active-navigation [data-id="' + id + '"]'); - var data = id === 'custom' ? { iconClass: 'fa-navicon', groups: available[0].groups } : available[id]; + const id = ui.helper.attr('data-id'); + const el = $('#active-navigation [data-id="' + id + '"]'); + const data = id === 'custom' ? { iconClass: 'fa-navicon', groups: available[0].groups } : available[id]; data.enabled = false; data.index = (parseInt($('#enabled').children().last().attr('data-index'), 10) || 0) + 1; @@ -87,18 +87,18 @@ define('admin/settings/navigation', [ } function save() { - var nav = []; + const nav = []; - var indices = []; + const indices = []; $('#active-navigation li').each(function () { indices.push($(this).attr('data-index')); }); indices.forEach(function (index) { - var el = $('#enabled').children('[data-index="' + index + '"]'); - var form = el.find('form').serializeArray(); - var data = {}; - var properties = {}; + const el = $('#enabled').children('[data-index="' + index + '"]'); + const form = el.find('form').serializeArray(); + const data = {}; + const properties = {}; form.forEach(function (input) { if (input.name.slice(0, 9) === 'property:' && input.value === 'on') { @@ -117,7 +117,7 @@ define('admin/settings/navigation', [ data.properties = {}; - for (var prop in properties) { + for (const prop in properties) { if (properties.hasOwnProperty(prop)) { data.properties[prop] = properties[prop]; } @@ -136,15 +136,15 @@ define('admin/settings/navigation', [ } function remove() { - var index = $(this).parents('[data-index]').attr('data-index'); + const index = $(this).parents('[data-index]').attr('data-index'); $('#active-navigation [data-index="' + index + '"]').remove(); $('#enabled [data-index="' + index + '"]').remove(); return false; } function toggle() { - var btn = $(this); - var disabled = btn.hasClass('btn-success'); + const btn = $(this); + const disabled = btn.hasClass('btn-success'); translator.translate(disabled ? '[[admin/settings/navigation:btn.disable]]' : '[[admin/settings/navigation:btn.enable]]', function (html) { btn.toggleClass('btn-warning').toggleClass('btn-success').html(html); btn.parents('li').find('[name="enabled"]').val(disabled ? 'on' : ''); diff --git a/public/src/admin/settings/notifications.js b/public/src/admin/settings/notifications.js index e58dc4c628..9235478482 100644 --- a/public/src/admin/settings/notifications.js +++ b/public/src/admin/settings/notifications.js @@ -3,10 +3,10 @@ define('admin/settings/notifications', [ 'autocomplete', ], function (autocomplete) { - var Notifications = {}; + const Notifications = {}; Notifications.init = function () { - var searchInput = $('[data-field="welcomeUid"]'); + const searchInput = $('[data-field="welcomeUid"]'); autocomplete.user(searchInput, function (event, selected) { setTimeout(function () { searchInput.val(selected.item.user.uid); diff --git a/public/src/admin/settings/social.js b/public/src/admin/settings/social.js index 294357eb84..32ff8d7840 100644 --- a/public/src/admin/settings/social.js +++ b/public/src/admin/settings/social.js @@ -2,11 +2,11 @@ define('admin/settings/social', [], function () { - var social = {}; + const social = {}; social.init = function () { $('#save').on('click', function () { - var networks = []; + const networks = []; $('#postSharingNetworks input[type="checkbox"]').each(function () { if ($(this).prop('checked')) { networks.push($(this).attr('id')); diff --git a/public/src/ajaxify.js b/public/src/ajaxify.js index ec8b3fc1c6..451ed50534 100644 --- a/public/src/ajaxify.js +++ b/public/src/ajaxify.js @@ -4,17 +4,17 @@ ajaxify = window.ajaxify || {}; (function () { - var apiXHR = null; - var ajaxifyTimer; + let apiXHR = null; + let ajaxifyTimer; - var retry = true; - var previousBodyClass = ''; + let retry = true; + let previousBodyClass = ''; ajaxify.loading = false; ajaxify.count = 0; ajaxify.currentPage = null; - var hooks; + let hooks; require(['hooks'], function (_hooks) { hooks = _hooks; }); @@ -113,7 +113,7 @@ ajaxify = window.ajaxify || {}; // this function is called just once from footer on page load ajaxify.coldLoad = function () { - var url = ajaxify.start(window.location.pathname.slice(1) + window.location.search + window.location.hash); + const url = ajaxify.start(window.location.pathname.slice(1) + window.location.search + window.location.hash); ajaxify.updateHistory(url, true); ajaxify.end(url, ajaxify.data.template.name); hooks.fire('action:ajaxify.coldLoad'); @@ -125,8 +125,8 @@ ajaxify = window.ajaxify || {}; ajaxify.handleRedirects = function (url) { url = ajaxify.removeRelativePath(url.replace(/^\/|\/$/g, '')).toLowerCase(); - var isClientToAdmin = url.startsWith('admin') && window.location.pathname.indexOf(config.relative_path + '/admin') !== 0; - var isAdminToClient = !url.startsWith('admin') && window.location.pathname.indexOf(config.relative_path + '/admin') === 0; + const isClientToAdmin = url.startsWith('admin') && window.location.pathname.indexOf(config.relative_path + '/admin') !== 0; + const isAdminToClient = !url.startsWith('admin') && window.location.pathname.indexOf(config.relative_path + '/admin') === 0; if (isClientToAdmin || isAdminToClient) { window.open(config.relative_path + '/' + url, '_top'); @@ -138,7 +138,7 @@ ajaxify = window.ajaxify || {}; ajaxify.start = function (url) { url = ajaxify.removeRelativePath(url.replace(/^\/|\/$/g, '')); - var payload = { + const payload = { url: url, }; @@ -159,13 +159,13 @@ ajaxify = window.ajaxify || {}; }; function onAjaxError(err, url, callback, quiet) { - var data = err.data; - var textStatus = err.textStatus; + const data = err.data; + const textStatus = err.textStatus; ajaxify.loading = false; if (data) { - var status = parseInt(data.status, 10); + let status = parseInt(data.status, 10); if (status === 403 || status === 404 || status === 500 || status === 502 || status === 503) { if (status === 502 && retry) { retry = false; @@ -240,7 +240,7 @@ ajaxify = window.ajaxify || {}; // Allow translation strings in title on ajaxify (#5927) title = translator.unescape(title); - var data = { title: title }; + const data = { title: title }; hooks.fire('action:ajaxify.updateTitle', data); translator.translate(data.title, function (translated) { window.document.title = $('
    ').html(translated).text(); @@ -249,16 +249,16 @@ ajaxify = window.ajaxify || {}; } function updateTags() { - var metaWhitelist = ['title', 'description', /og:.+/, /article:.+/].map(function (val) { + const metaWhitelist = ['title', 'description', /og:.+/, /article:.+/, 'robots'].map(function (val) { return new RegExp(val); }); - var linkWhitelist = ['canonical', 'alternate', 'up']; + const linkWhitelist = ['canonical', 'alternate', 'up']; // Delete the old meta tags Array.prototype.slice .call(document.querySelectorAll('head meta')) .filter(function (el) { - var name = el.getAttribute('property') || el.getAttribute('name'); + const name = el.getAttribute('property') || el.getAttribute('name'); return metaWhitelist.some(function (exp) { return !!exp.test(name); }); @@ -270,7 +270,7 @@ ajaxify = window.ajaxify || {}; // Add new meta tags ajaxify.data._header.tags.meta .filter(function (tagObj) { - var name = tagObj.name || tagObj.property; + const name = tagObj.name || tagObj.property; return metaWhitelist.some(function (exp) { return !!exp.test(name); }); @@ -278,7 +278,7 @@ ajaxify = window.ajaxify || {}; if (tagObj.content) { tagObj.content = await translator.translate(tagObj.content); } - var metaEl = document.createElement('meta'); + const metaEl = document.createElement('meta'); Object.keys(tagObj).forEach(function (prop) { metaEl.setAttribute(prop, tagObj[prop]); }); @@ -290,7 +290,7 @@ ajaxify = window.ajaxify || {}; Array.prototype.slice .call(document.querySelectorAll('head link')) .filter(function (el) { - var name = el.getAttribute('rel'); + const name = el.getAttribute('rel'); return linkWhitelist.some(function (item) { return item === name; }); @@ -307,7 +307,7 @@ ajaxify = window.ajaxify || {}; }); }) .forEach(function (tagObj) { - var linkEl = document.createElement('link'); + const linkEl = document.createElement('link'); Object.keys(tagObj).forEach(function (prop) { linkEl.setAttribute(prop, tagObj[prop]); }); @@ -343,7 +343,7 @@ ajaxify = window.ajaxify || {}; }; ajaxify.parseData = function () { - var dataEl = $('#ajaxify-data'); + const dataEl = $('#ajaxify-data'); if (dataEl.length) { ajaxify.data = JSON.parse(dataEl.text()); dataEl.remove(); @@ -362,7 +362,7 @@ ajaxify = window.ajaxify || {}; }; ajaxify.loadScript = function (tpl_url, callback) { - var location = !app.inAdmin ? 'forum/' : ''; + let location = !app.inAdmin ? 'forum/' : ''; if (tpl_url.startsWith('admin')) { location = ''; @@ -376,7 +376,7 @@ ajaxify = window.ajaxify || {}; hooks.fire('action:script.load', data); hooks.fire('filter:script.load', data).then((data) => { // Require and parse modules - var outstanding = data.scripts.length; + let outstanding = data.scripts.length; data.scripts.map(function (script) { if (typeof script === 'function') { @@ -462,7 +462,7 @@ ajaxify = window.ajaxify || {}; }; ajaxify.loadTemplate = function (template, callback) { - require([config.assetBaseUrl + '/templates/' + template + '.js'], callback, function (err) { + require([config.asset_base_url + '/templates/' + template + '.js'], callback, function (err) { console.error('Unable to load template: ' + template); throw err; }); @@ -477,8 +477,8 @@ ajaxify = window.ajaxify || {}; }); }()); -$(function () { - var hooks; +$(document).ready(function () { + let hooks; require(['hooks'], function (_hooks) { hooks = _hooks; }); @@ -504,26 +504,26 @@ $(function () { // eslint-disable-next-line no-script-url return href === undefined || href === '' || href === 'javascript:;'; } - var location = document.location || window.location; - var rootUrl = location.protocol + '//' + (location.hostname || location.host) + (location.port ? ':' + location.port : ''); - var contentEl = document.getElementById('content'); + const location = document.location || window.location; + const rootUrl = location.protocol + '//' + (location.hostname || location.host) + (location.port ? ':' + location.port : ''); + const contentEl = document.getElementById('content'); // Enhancing all anchors to ajaxify... $(document.body).on('click', 'a', function (e) { - var _self = this; + const _self = this; if (this.target !== '' || (this.protocol !== 'http:' && this.protocol !== 'https:')) { return; } - var $this = $(this); - var href = $this.attr('href'); - var internalLink = utils.isInternalURI(this, window.location, config.relative_path); + const $this = $(this); + const href = $this.attr('href'); + const internalLink = utils.isInternalURI(this, window.location, config.relative_path); const rootAndPath = new RegExp(`^${rootUrl}${config.relative_path}/?`); - var process = function () { + const process = function () { if (!e.ctrlKey && !e.shiftKey && !e.metaKey && e.which === 1) { if (internalLink) { - var pathname = this.href.replace(rootAndPath, ''); + const pathname = this.href.replace(rootAndPath, ''); // Special handling for urls with hashes if (window.location.pathname === this.pathname && this.hash.length) { @@ -537,13 +537,13 @@ $(function () { } } else if (window.location.pathname !== config.relative_path + '/outgoing') { if (config.openOutgoingLinksInNewTab && $.contains(contentEl, this)) { - var externalTab = window.open(); + const externalTab = window.open(); externalTab.opener = null; externalTab.location = this.href; e.preventDefault(); } else if (config.useOutgoingLinksPage) { - var safeUrls = config.outgoingLinksWhitelist.trim().split(/[\s,]+/g).filter(Boolean); - var href = this.href; + const safeUrls = config.outgoingLinksWhitelist.trim().split(/[\s,]+/g).filter(Boolean); + const href = this.href; if (!safeUrls.length || !safeUrls.some(function (url) { return href.indexOf(url) !== -1; })) { ajaxify.go('outgoing?url=' + encodeURIComponent(href)); e.preventDefault(); @@ -587,11 +587,13 @@ $(function () { return; } - bootbox.confirm('[[global:unsaved-changes]]', function (navigate) { - if (navigate) { - app.flags._unsaved = false; - process.call(_self); - } + require(['bootbox'], function (bootbox) { + bootbox.confirm('[[global:unsaved-changes]]', function (navigate) { + if (navigate) { + app.flags._unsaved = false; + process.call(_self); + } + }); }); return e.preventDefault(); } diff --git a/public/src/app.js b/public/src/app.js index 694fed6dd5..6154c8b323 100644 --- a/public/src/app.js +++ b/public/src/app.js @@ -7,22 +7,13 @@ app.isFocused = true; app.currentRoom = null; app.widgets = {}; app.flags = {}; -app.cacheBuster = null; (function () { - var appLoaded = false; - var params = utils.params(); - var showWelcomeMessage = !!params.loggedin; - var registerMessage = params.register; - var isTouchDevice = utils.isTouchDevice(); + let appLoaded = false; + const isTouchDevice = utils.isTouchDevice(); app.cacheBuster = config['cache-buster']; - var hooks; - require(['hooks'], function (_hooks) { - hooks = _hooks; - }); - $(document).ready(function () { ajaxify.parseData(); app.load(); @@ -46,10 +37,10 @@ app.cacheBuster = null; * e.g. New Topic/Reply, post tools */ if (document.body) { - var earlyQueue = []; // once we can ES6, use Set instead - var earlyClick = function (ev) { - var btnEl = ev.target.closest('button'); - var anchorEl = ev.target.closest('a'); + let earlyQueue = []; // once we can ES6, use Set instead + const earlyClick = function (ev) { + let btnEl = ev.target.closest('button'); + const anchorEl = ev.target.closest('a'); if (!btnEl && anchorEl && (anchorEl.getAttribute('data-ajaxify') === 'false' || anchorEl.href === '#')) { btnEl = anchorEl; } @@ -76,28 +67,15 @@ app.cacheBuster = null; app.handleEarlyClicks(); app.load = function () { - handleStatusChange(); - - if (config.searchEnabled) { - app.handleSearch(); - } - $('body').on('click', '#new_topic', function (e) { e.preventDefault(); app.newTopic(); }); - $('#header-menu .container').on('click', '[component="user/logout"]', function () { - app.logout(); - return false; - }); - Visibility.change(function (event, state) { app.isFocused = state === 'visible'; }); - createHeaderTooltips(); - app.showCookieWarning(); registerServiceWorker(); require([ @@ -105,24 +83,26 @@ app.cacheBuster = null; 'helpers', 'forum/pagination', 'translator', + 'messages', + 'search', 'forum/unread', - 'forum/header/notifications', - 'forum/header/chat', + 'forum/header', + 'hooks', 'timeago/jquery.timeago', - ], function (taskbar, helpers, pagination, translator, unread, notifications, chat) { - notifications.prepareDOM(); - chat.prepareDOM(); + ], function (taskbar, helpers, pagination, translator, messages, search, unread, header, hooks) { + header.prepareDOM(); translator.prepareDOM(); taskbar.init(); helpers.register(); pagination.init(); + search.init(); if (app.user.uid > 0) { unread.initUnreadTopics(); } function finishLoad() { hooks.fire('action:app.load'); - app.showMessages(); + messages.show(); appLoaded = true; } overrides.overrideTimeago(); @@ -135,10 +115,9 @@ app.cacheBuster = null; }; app.require = async (modules) => { // allows you to await require.js modules - let single = false; - if (!Array.isArray(modules)) { + const single = !Array.isArray(modules); + if (single) { modules = [modules]; - single = true; } return new Promise((resolve, reject) => { @@ -149,104 +128,44 @@ app.cacheBuster = null; }; app.logout = function (redirect) { - redirect = redirect === undefined ? true : redirect; - hooks.fire('action:app.logout'); - - $.ajax(config.relative_path + '/logout', { - type: 'POST', - headers: { - 'x-csrf-token': config.csrf_token, - }, - beforeSend: function () { - app.flags._logout = true; - }, - success: function (data) { - hooks.fire('action:app.loggedOut', data); - if (redirect) { - if (data.next) { - window.location.href = data.next; - } else { - window.location.reload(); - } - } - }, + console.warn('[deprecated] app.logout is deprecated, please use logout module directly'); + require(['logout'], function (logout) { + logout(redirect); }); - return false; }; app.alert = function (params) { + console.warn('[deprecated] app.alert is deprecated, please use alerts.alert'); require(['alerts'], function (alerts) { alerts.alert(params); }); }; app.removeAlert = function (id) { + console.warn('[deprecated] app.removeAlert is deprecated, please use alerts.remove'); require(['alerts'], function (alerts) { alerts.remove(id); }); }; app.alertSuccess = function (message, timeout) { - app.alert({ - alert_id: utils.generateUUID(), - title: '[[global:alert.success]]', - message: message, - type: 'success', - timeout: timeout || 5000, + console.warn('[deprecated] app.alertSuccess is deprecated, please use alerts.success'); + require(['alerts'], function (alerts) { + alerts.success(message, timeout); }); }; app.alertError = function (message, timeout) { - message = (message && message.message) || message; - - if (message === '[[error:revalidate-failure]]') { - socket.disconnect(); - app.reconnect(); - return; - } - - app.alert({ - alert_id: utils.generateUUID(), - title: '[[global:alert.error]]', - message: message, - type: 'danger', - timeout: timeout || 10000, - }); - }; - - app.handleInvalidSession = function () { - socket.disconnect(); - app.logout(false); - bootbox.alert({ - title: '[[error:invalid-session]]', - message: '[[error:invalid-session-text]]', - closeButton: false, - callback: function () { - window.location.reload(); - }, - }); - }; - - app.handleSessionMismatch = () => { - if (app.flags._login || app.flags._logout) { - return; - } - - socket.disconnect(); - bootbox.alert({ - title: '[[error:session-mismatch]]', - message: '[[error:session-mismatch-text]]', - closeButton: false, - callback: function () { - window.location.reload(); - }, + console.warn('[deprecated] app.alertError is deprecated, please use alerts.error'); + require(['alerts'], function (alerts) { + alerts.error(message, timeout); }); }; app.enterRoom = function (room, callback) { callback = callback || function () { }; if (socket && app.user.uid && app.currentRoom !== room) { - var previousRoom = app.currentRoom; + const previousRoom = app.currentRoom; app.currentRoom = room; socket.emit('meta.rooms.enter', { enter: room, @@ -265,7 +184,7 @@ app.cacheBuster = null; if (!socket || config.maintenanceMode) { return; } - var previousRoom = app.currentRoom; + const previousRoom = app.currentRoom; app.currentRoom = ''; socket.emit('meta.rooms.leaveCurrent', function (err) { if (err) { @@ -279,9 +198,12 @@ app.cacheBuster = null; $('#main-nav li') .removeClass('active') .find('a') - .filter(function (i, x) { - return window.location.pathname === x.pathname || - window.location.pathname.startsWith(x.pathname + '/'); + .filter(function (i, a) { + return $(a).attr('href') !== '#' && window.location.hostname === a.hostname && + ( + window.location.pathname === a.pathname || + window.location.pathname.startsWith(a.pathname + '/') + ); }) .parent() .addClass('active'); @@ -324,121 +246,17 @@ app.cacheBuster = null; app.createStatusTooltips(); }; - app.showMessages = function () { - var messages = { - login: { - format: 'alert', - title: '[[global:welcome_back]] ' + app.user.username + '!', - message: '[[global:you_have_successfully_logged_in]]', - }, - register: { - format: 'modal', - }, - }; - - function showAlert(type, message) { - switch (messages[type].format) { - case 'alert': - app.alert({ - type: 'success', - title: messages[type].title, - message: messages[type].message, - timeout: 5000, - }); - break; - - case 'modal': - require(['bootbox'], function (bootbox) { - bootbox.alert({ - title: messages[type].title, - message: message || messages[type].message, - }); - }); - break; - } - } - - if (showWelcomeMessage) { - showWelcomeMessage = false; - $(document).ready(function () { - showAlert('login'); - }); - } - if (registerMessage) { - $(document).ready(function () { - showAlert('register', utils.escapeHTML(decodeURIComponent(registerMessage))); - registerMessage = false; - }); - } - }; - app.openChat = function (roomId, uid) { - if (!app.user.uid) { - return app.alertError('[[error:not-logged-in]]'); - } - + console.warn('[deprecated] app.openChat is deprecated, please use chat.openChat'); require(['chat'], function (chat) { - function loadAndCenter(chatModal) { - chat.load(chatModal.attr('data-uuid')); - chat.center(chatModal); - chat.focusInput(chatModal); - } - - if (chat.modalExists(roomId)) { - loadAndCenter(chat.getModal(roomId)); - } else { - socket.emit('modules.chats.loadRoom', { roomId: roomId, uid: uid || app.user.uid }, function (err, roomData) { - if (err) { - return app.alertError(err.message); - } - roomData.users = roomData.users.filter(function (user) { - return user && parseInt(user.uid, 10) !== parseInt(app.user.uid, 10); - }); - roomData.uid = uid || app.user.uid; - roomData.isSelf = true; - chat.createModal(roomData, loadAndCenter); - }); - } + chat.openChat(roomId, uid); }); }; app.newChat = function (touid, callback) { - function createChat() { - socket.emit('modules.chats.newRoom', { touid: touid }, function (err, roomId) { - if (err) { - return app.alertError(err.message); - } - - if (!ajaxify.data.template.chats) { - app.openChat(roomId); - } else { - ajaxify.go('chats/' + roomId); - } - - callback(false, roomId); - }); - } - - callback = callback || function () { }; - if (!app.user.uid) { - return app.alertError('[[error:not-logged-in]]'); - } - - if (parseInt(touid, 10) === parseInt(app.user.uid, 10)) { - return app.alertError('[[error:cant-chat-with-yourself]]'); - } - socket.emit('modules.chats.isDnD', touid, function (err, isDnD) { - if (err) { - return app.alertError(err.message); - } - if (!isDnD) { - return createChat(); - } - bootbox.confirm('[[modules:chat.confirm-chat-with-dnd-user]]', function (ok) { - if (ok) { - createChat(); - } - }); + console.warn('[deprecated] app.newChat is deprecated, please use chat.newChat'); + require(['chat'], function (chat) { + chat.newChat(touid, callback); }); }; @@ -449,255 +267,27 @@ app.cacheBuster = null; }); }; - function createHeaderTooltips() { - var env = utils.findBootstrapEnvironment(); - if (env === 'xs' || env === 'sm' || isTouchDevice) { - return; - } - $('#header-menu li a[title]').each(function () { - $(this).tooltip({ - placement: 'bottom', - trigger: 'hover', - title: $(this).attr('title'), - }); - }); - - - $('#search-form').tooltip({ - placement: 'bottom', - trigger: 'hover', - title: $('#search-button i').attr('title'), - }); - - - $('#user_dropdown').tooltip({ - placement: 'bottom', - trigger: 'hover', - title: $('#user_dropdown').attr('title'), - }); - } - app.enableTopicSearch = function (options) { - if (!config.searchEnabled || !app.user.privileges['search:content']) { - return; - } - /* eslint-disable-next-line */ - var searchOptions = Object.assign({ in: config.searchDefaultInQuick || 'titles' }, options.searchOptions); - var quickSearchResults = options.searchElements.resultEl; - var inputEl = options.searchElements.inputEl; - var searchTimeoutId = 0; - var oldValue = inputEl.val(); - var filterCategoryEl = quickSearchResults.find('.filter-category'); - - function updateCategoryFilterName() { - if (ajaxify.data.template.category) { - require(['translator'], function (translator) { - translator.translate('[[search:search-in-category, ' + ajaxify.data.name + ']]', function (translated) { - var name = $('
    ').html(translated).text(); - filterCategoryEl.find('.name').text(name); - }); - }); - } - filterCategoryEl.toggleClass('hidden', !ajaxify.data.template.category); - } - - function doSearch() { - require(['search'], function (search) { - /* eslint-disable-next-line */ - options.searchOptions = Object.assign({}, searchOptions); - options.searchOptions.term = inputEl.val(); - updateCategoryFilterName(); - - if (ajaxify.data.template.category) { - if (filterCategoryEl.find('input[type="checkbox"]').is(':checked')) { - options.searchOptions.categories = [ajaxify.data.cid]; - options.searchOptions.searchChildren = true; - } - } - - quickSearchResults.removeClass('hidden').find('.quick-search-results-container').html(''); - quickSearchResults.find('.loading-indicator').removeClass('hidden'); - hooks.fire('action:search.quick.start', options); - options.searchOptions.searchOnly = 1; - search.api(options.searchOptions, function (data) { - quickSearchResults.find('.loading-indicator').addClass('hidden'); - if (options.hideOnNoMatches && !data.posts.length) { - return quickSearchResults.addClass('hidden').find('.quick-search-results-container').html(''); - } - data.posts.forEach(function (p) { - var text = $('
    ' + p.content + '
    ').text(); - var start = Math.max(0, text.toLowerCase().indexOf(inputEl.val().toLowerCase()) - 40); - p.snippet = utils.escapeHTML((start > 0 ? '...' : '') + - text.slice(start, start + 80) + - (text.length - start > 80 ? '...' : '')); - }); - app.parseAndTranslate('partials/quick-search-results', data, function (html) { - if (html.length) { - html.find('.timeago').timeago(); - } - quickSearchResults.toggleClass('hidden', !html.length || !inputEl.is(':focus')) - .find('.quick-search-results-container') - .html(html.length ? html : ''); - var highlightEls = quickSearchResults.find( - '.quick-search-results .quick-search-title, .quick-search-results .snippet' - ); - search.highlightMatches(options.searchOptions.term, highlightEls); - hooks.fire('action:search.quick.complete', { - data: data, - options: options, - }); - }); - }); - }); - } - - quickSearchResults.find('.filter-category input[type="checkbox"]').on('change', function () { - inputEl.focus(); - doSearch(); - }); - - inputEl.off('keyup').on('keyup', function () { - if (searchTimeoutId) { - clearTimeout(searchTimeoutId); - searchTimeoutId = 0; - } - searchTimeoutId = setTimeout(function () { - if (inputEl.val().length < 3) { - quickSearchResults.addClass('hidden'); - oldValue = inputEl.val(); - return; - } - if (inputEl.val() === oldValue) { - return; - } - oldValue = inputEl.val(); - if (!inputEl.is(':focus')) { - return quickSearchResults.addClass('hidden'); - } - doSearch(); - }, 250); - }); - - var mousedownOnResults = false; - quickSearchResults.on('mousedown', function () { - $(window).one('mouseup', function () { - quickSearchResults.addClass('hidden'); - }); - mousedownOnResults = true; - }); - inputEl.on('blur', function () { - if (!inputEl.is(':focus') && !mousedownOnResults && !quickSearchResults.hasClass('hidden')) { - quickSearchResults.addClass('hidden'); - } - }); - - inputEl.on('focus', function () { - mousedownOnResults = false; - oldValue = inputEl.val(); - if (inputEl.val() && quickSearchResults.find('#quick-search-results').children().length) { - updateCategoryFilterName(); - doSearch(); - inputEl[0].setSelectionRange(0, inputEl.val().length); - } - }); - - inputEl.off('refresh').on('refresh', function () { - doSearch(); + console.warn('[deprecated] app.enableTopicSearch is deprecated, please use search.enableQuickSearch(options)'); + require(['search'], function (search) { + search.enableQuickSearch(options); }); }; app.handleSearch = function (searchOptions) { - searchOptions = searchOptions || { in: config.searchDefaultInQuick || 'titles' }; - var searchButton = $('#search-button'); - var searchFields = $('#search-fields'); - var searchInput = $('#search-fields input'); - var quickSearchContainer = $('#quick-search-container'); - - $('#search-form .advanced-search-link').off('mousedown').on('mousedown', function () { - ajaxify.go('/search'); - }); - - $('#search-form').off('submit').on('submit', function () { - searchInput.blur(); - }); - searchInput.off('blur').on('blur', dismissSearch); - searchInput.off('focus'); - - var searchElements = { - inputEl: searchInput, - resultEl: quickSearchContainer, - }; - - app.enableTopicSearch({ - searchOptions: searchOptions, - searchElements: searchElements, - }); - - function dismissSearch() { - setTimeout(function () { - if (!searchInput.is(':focus')) { - searchFields.addClass('hidden'); - searchButton.removeClass('hidden'); - } - }, 200); - } - - searchButton.off('click').on('click', function (e) { - if (!config.loggedIn && !app.user.privileges['search:content']) { - app.alert({ - message: '[[error:search-requires-login]]', - timeout: 3000, - }); - ajaxify.go('login'); - return false; - } - e.stopPropagation(); - - app.prepareSearch(); - return false; - }); - - $('#search-form').off('submit').on('submit', function () { - var input = $(this).find('input'); - require(['search'], function (search) { - var data = search.getSearchPreferences(); - data.term = input.val(); - hooks.fire('action:search.submit', { - searchOptions: data, - searchElements: searchElements, - }); - search.query(data, function () { - input.val(''); - }); - }); - return false; + console.warn('[deprecated] app.handleSearch is deprecated, please use search.init(options)'); + require(['search'], function (search) { + search.init(searchOptions); }); }; app.prepareSearch = function () { - $('#search-fields').removeClass('hidden'); - $('#search-button').addClass('hidden'); - $('#search-fields input').focus(); + console.warn('[deprecated] app.prepareSearch is deprecated, please use search.showAndFocusInput()'); + require(['search'], function (search) { + search.showAndFocusInput(); + }); }; - function handleStatusChange() { - $('[component="header/usercontrol"] [data-status]').off('click').on('click', function (e) { - var status = $(this).attr('data-status'); - socket.emit('user.setStatus', status, function (err) { - if (err) { - return app.alertError(err.message); - } - $('[data-uid="' + app.user.uid + '"] [component="user/status"], [component="header/profilelink"] [component="user/status"]') - .removeClass('away online dnd offline') - .addClass(status); - $('[component="header/usercontrol"] [data-status]').each(function () { - $(this).find('span').toggleClass('bold', $(this).attr('data-status') === status); - }); - app.user.status = status; - }); - e.preventDefault(); - }); - } app.updateUserStatus = function (el, status) { if (!el.length) { @@ -715,9 +305,11 @@ app.cacheBuster = null; }; app.newTopic = function (cid, tags) { - hooks.fire('action:composer.topic.new', { - cid: cid || ajaxify.data.cid || 0, - tags: tags || (ajaxify.data.tag ? [ajaxify.data.tag] : []), + require(['hooks'], function (hooks) { + hooks.fire('action:composer.topic.new', { + cid: cid || ajaxify.data.cid || 0, + tags: tags || (ajaxify.data.tag ? [ajaxify.data.tag] : []), + }); }); }; @@ -736,63 +328,6 @@ app.cacheBuster = null; }); }; - app.showEmailConfirmWarning = async (err) => { - const storage = await app.require('storage'); - - let showModal = false; - switch (ajaxify.data.template.name) { - case 'recent': { - showModal = !ajaxify.data.canPost; - break; - } - - case 'category': { - showModal = !ajaxify.data.privileges['topics:create']; - break; - } - - case 'topic': { - showModal = !ajaxify.data.privileges['topics:reply']; - } - } - - if (!showModal || !app.user.uid || parseInt(storage.getItem('email-confirm-dismiss'), 10) === 1) { - return; - } - var msg = { - alert_id: 'email_confirm', - type: 'warning', - timeout: 0, - closefn: () => { - storage.setItem('email-confirm-dismiss', 1); - }, - }; - - if (!app.user.email) { - msg.message = '[[error:no-email-to-confirm]]'; - msg.clickfn = function () { - app.removeAlert('email_confirm'); - ajaxify.go('user/' + app.user.userslug + '/edit/email'); - }; - app.alert(msg); - } else if (!app.user['email:confirmed'] && !app.user.isEmailConfirmSent) { - msg.message = err ? err.message : '[[error:email-not-confirmed]]'; - msg.clickfn = function () { - app.removeAlert('email_confirm'); - socket.emit('user.emailConfirm', {}, function (err) { - if (err) { - return app.alertError(err.message); - } - app.alertSuccess('[[notifications:email-confirm-sent]]'); - }); - }; - app.alert(msg); - } else if (!app.user['email:confirmed'] && app.user.isEmailConfirmSent) { - msg.message = '[[error:email-not-confirmed-email-sent]]'; - app.alert(msg); - } - }; - app.parseAndTranslate = function (template, blockName, data, callback) { if (typeof blockName !== 'string') { callback = data; @@ -817,39 +352,6 @@ app.cacheBuster = null; }); }; - app.showCookieWarning = function () { - require(['translator', 'storage'], function (translator, storage) { - if (!config.cookies.enabled || !navigator.cookieEnabled) { - // Skip warning if cookie consent subsystem disabled (obviously), or cookies not in use - return; - } else if (window.location.pathname.startsWith(config.relative_path + '/admin')) { - // No need to show cookie consent warning in ACP - return; - } else if (storage.getItem('cookieconsent') === '1') { - return; - } - - config.cookies.message = translator.unescape(config.cookies.message); - config.cookies.dismiss = translator.unescape(config.cookies.dismiss); - config.cookies.link = translator.unescape(config.cookies.link); - config.cookies.link_url = translator.unescape(config.cookies.link_url); - - app.parseAndTranslate('partials/cookie-consent', config.cookies, function (html) { - $(document.body).append(html); - $(document.body).addClass('cookie-consent-open'); - - var warningEl = $('.cookie-consent'); - var dismissEl = warningEl.find('button'); - dismissEl.on('click', function () { - // Save consent cookie and remove warning element - storage.setItem('cookieconsent', '1'); - warningEl.remove(); - $(document.body).removeClass('cookie-consent-open'); - }); - }); - }); - }; - function registerServiceWorker() { // Do not register for Safari browsers if (!ajaxify.data._locals.useragent.isSafari && 'serviceWorker' in navigator) { diff --git a/public/src/client/account/best.js b/public/src/client/account/best.js index ebd61760bc..b8327a3e39 100644 --- a/public/src/client/account/best.js +++ b/public/src/client/account/best.js @@ -2,7 +2,7 @@ define('forum/account/best', ['forum/account/header', 'forum/account/posts'], function (header, posts) { - var Best = {}; + const Best = {}; Best.init = function () { header.init(); diff --git a/public/src/client/account/blocks.js b/public/src/client/account/blocks.js index 0469cf957d..98bd96a8ff 100644 --- a/public/src/client/account/blocks.js +++ b/public/src/client/account/blocks.js @@ -5,13 +5,13 @@ define('forum/account/blocks', [ 'api', 'hooks', ], function (header, api, hooks) { - var Blocks = {}; + const Blocks = {}; Blocks.init = function () { header.init(); $('#user-search').on('keyup', function () { - var username = this.value; + const username = this.value; api.get('/api/users', { query: username, @@ -36,7 +36,7 @@ define('forum/account/blocks', [ }); $('.block-edit').on('click', '[data-action="toggle"]', function () { - var uid = parseInt(this.getAttribute('data-uid'), 10); + const uid = parseInt(this.getAttribute('data-uid'), 10); socket.emit('user.toggleBlock', { blockeeUid: uid, blockerUid: ajaxify.data.uid, diff --git a/public/src/client/account/bookmarks.js b/public/src/client/account/bookmarks.js index 56f9983e9f..4959a24d73 100644 --- a/public/src/client/account/bookmarks.js +++ b/public/src/client/account/bookmarks.js @@ -2,7 +2,7 @@ define('forum/account/bookmarks', ['forum/account/header', 'forum/account/posts'], function (header, posts) { - var Bookmarks = {}; + const Bookmarks = {}; Bookmarks.init = function () { header.init(); diff --git a/public/src/client/account/categories.js b/public/src/client/account/categories.js index 1522af929a..49a52b68cd 100644 --- a/public/src/client/account/categories.js +++ b/public/src/client/account/categories.js @@ -2,7 +2,7 @@ define('forum/account/categories', ['forum/account/header'], function (header) { - var Categories = {}; + const Categories = {}; Categories.init = function () { header.init(); @@ -12,8 +12,8 @@ define('forum/account/categories', ['forum/account/header'], function (header) { }); $('[component="category/watch/all"]').find('[component="category/watching"], [component="category/ignoring"], [component="category/notwatching"]').on('click', function () { - var cids = []; - var state = $(this).attr('data-state'); + const cids = []; + const state = $(this).attr('data-state'); $('[data-parent-cid="0"]').each(function (index, el) { cids.push($(el).attr('data-cid')); }); @@ -28,10 +28,10 @@ define('forum/account/categories', ['forum/account/header'], function (header) { }; function handleIgnoreWatch(cid) { - var category = $('[data-cid="' + cid + '"]'); + const category = $('[data-cid="' + cid + '"]'); category.find('[component="category/watching"], [component="category/ignoring"], [component="category/notwatching"]').on('click', function () { - var $this = $(this); - var state = $this.attr('data-state'); + const $this = $(this); + const state = $this.attr('data-state'); socket.emit('categories.setWatchState', { cid: cid, state: state, uid: ajaxify.data.uid }, function (err, modified_cids) { if (err) { @@ -46,7 +46,7 @@ define('forum/account/categories', ['forum/account/header'], function (header) { function updateDropdowns(modified_cids, state) { modified_cids.forEach(function (cid) { - var category = $('[data-cid="' + cid + '"]'); + const category = $('[data-cid="' + cid + '"]'); category.find('[component="category/watching/menu"]').toggleClass('hidden', state !== 'watching'); category.find('[component="category/watching/check"]').toggleClass('fa-check', state === 'watching'); diff --git a/public/src/client/account/consent.js b/public/src/client/account/consent.js index f53bc365b0..01e97bb4cf 100644 --- a/public/src/client/account/consent.js +++ b/public/src/client/account/consent.js @@ -2,7 +2,7 @@ define('forum/account/consent', ['forum/account/header'], function (header) { - var Consent = {}; + const Consent = {}; Consent.init = function () { header.init(); diff --git a/public/src/client/account/downvoted.js b/public/src/client/account/downvoted.js index 86252758d8..4ebf742237 100644 --- a/public/src/client/account/downvoted.js +++ b/public/src/client/account/downvoted.js @@ -2,7 +2,7 @@ define('forum/account/downvoted', ['forum/account/header', 'forum/account/posts'], function (header, posts) { - var Downvoted = {}; + const Downvoted = {}; Downvoted.init = function () { header.init(); diff --git a/public/src/client/account/edit.js b/public/src/client/account/edit.js index 5ccccd0730..6c69eb6dba 100644 --- a/public/src/client/account/edit.js +++ b/public/src/client/account/edit.js @@ -6,8 +6,9 @@ define('forum/account/edit', [ 'translator', 'api', 'hooks', -], function (header, picture, translator, api, hooks) { - var AccountEdit = {}; + 'bootbox', +], function (header, picture, translator, api, hooks, bootbox) { + const AccountEdit = {}; AccountEdit.init = function () { header.init(); @@ -63,12 +64,12 @@ define('forum/account/edit', [ function handleAccountDelete() { $('#deleteAccountBtn').on('click', function () { translator.translate('[[user:delete_account_confirm]]', function (translated) { - var modal = bootbox.confirm(translated + '

    ', function (confirm) { + const modal = bootbox.confirm(translated + '

    ', function (confirm) { if (!confirm) { return; } - var confirmBtn = modal.find('.btn-primary'); + const confirmBtn = modal.find('.btn-primary'); confirmBtn.html(''); confirmBtn.prop('disabled', true); @@ -88,7 +89,9 @@ define('forum/account/edit', [ } confirmBtn.html(''); - app.logout(); + require(['logout'], function (logout) { + logout(); + }); }); return false; @@ -104,7 +107,7 @@ define('forum/account/edit', [ function handleEmailConfirm() { $('#confirm-email').on('click', function () { - var btn = $(this).attr('disabled', true); + const btn = $(this).attr('disabled', true); socket.emit('user.emailConfirm', {}, function (err) { btn.removeAttr('disabled'); if (err) { @@ -120,7 +123,7 @@ define('forum/account/edit', [ } function updateSignature() { - var el = $('#signature'); + const el = $('#signature'); $('#signatureCharCountLeft').html(getCharsLeft(el, ajaxify.data.maximumSignatureLength)); el.on('keyup change', function () { @@ -129,7 +132,7 @@ define('forum/account/edit', [ } function updateAboutMe() { - var el = $('#aboutme'); + const el = $('#aboutme'); $('#aboutMeCharCountLeft').html(getCharsLeft(el, ajaxify.data.maximumAboutMeLength)); el.on('keyup change', function () { @@ -139,11 +142,11 @@ define('forum/account/edit', [ function handleGroupSort() { function move(direction) { - var selected = $('#groupTitle').val(); + const selected = $('#groupTitle').val(); if (!ajaxify.data.allowMultipleBadges || (Array.isArray(selected) && selected.length > 1)) { return; } - var el = $('#groupTitle').find(':selected'); + const el = $('#groupTitle').find(':selected'); if (el.length && el.val()) { if (direction > 0) { el.insertAfter(el.next()); diff --git a/public/src/client/account/edit/email.js b/public/src/client/account/edit/email.js index 824b7aaf5e..96d40877ff 100644 --- a/public/src/client/account/edit/email.js +++ b/public/src/client/account/edit/email.js @@ -1,14 +1,14 @@ 'use strict'; define('forum/account/edit/email', ['forum/account/header', 'api'], function (header, api) { - var AccountEditEmail = {}; + const AccountEditEmail = {}; AccountEditEmail.init = function () { header.init(); $('#submitBtn').on('click', function () { - var curPasswordEl = $('#inputCurrentPassword'); - var userData = { + const curPasswordEl = $('#inputCurrentPassword'); + const userData = { uid: $('#inputUID').val(), email: $('#inputNewEmail').val(), password: curPasswordEl.val(), @@ -23,7 +23,7 @@ define('forum/account/edit/email', ['forum/account/header', 'api'], function (he return app.alertError('[[user:email_same_as_password]]'); } - var btn = $(this); + const btn = $(this); btn.addClass('disabled').find('i').removeClass('hide'); api.put('/users/' + userData.uid, userData).then((res) => { diff --git a/public/src/client/account/edit/password.js b/public/src/client/account/edit/password.js index 99b8cdde71..1e9257790a 100644 --- a/public/src/client/account/edit/password.js +++ b/public/src/client/account/edit/password.js @@ -3,7 +3,7 @@ define('forum/account/edit/password', [ 'forum/account/header', 'translator', 'zxcvbn', 'api', ], function (header, translator, zxcvbn, api) { - var AccountEditPassword = {}; + const AccountEditPassword = {}; AccountEditPassword.init = function () { header.init(); @@ -12,16 +12,16 @@ define('forum/account/edit/password', [ }; function handlePasswordChange() { - var currentPassword = $('#inputCurrentPassword'); - var password_notify = $('#password-notify'); - var password_confirm_notify = $('#password-confirm-notify'); - var password = $('#inputNewPassword'); - var password_confirm = $('#inputNewPasswordAgain'); - var passwordvalid = false; - var passwordsmatch = false; + const currentPassword = $('#inputCurrentPassword'); + const password_notify = $('#password-notify'); + const password_confirm_notify = $('#password-confirm-notify'); + const password = $('#inputNewPassword'); + const password_confirm = $('#inputNewPasswordAgain'); + let passwordvalid = false; + let passwordsmatch = false; function onPasswordChanged() { - var passwordStrength = zxcvbn(password.val()); + const passwordStrength = zxcvbn(password.val()); passwordvalid = false; if (password.val().length < ajaxify.data.minimumPasswordLength) { showError(password_notify, '[[reset_password:password_too_short]]'); @@ -65,7 +65,7 @@ define('forum/account/edit/password', [ onPasswordChanged(); onPasswordConfirmChanged(); - var btn = $(this); + const btn = $(this); if (passwordvalid && passwordsmatch) { btn.addClass('disabled').find('i').removeClass('hide'); api.put('/users/' + ajaxify.data.theirid + '/password', { diff --git a/public/src/client/account/edit/username.js b/public/src/client/account/edit/username.js index 487b7872ee..a854a4b520 100644 --- a/public/src/client/account/edit/username.js +++ b/public/src/client/account/edit/username.js @@ -3,13 +3,13 @@ define('forum/account/edit/username', [ 'forum/account/header', 'api', 'slugify', ], function (header, api, slugify) { - var AccountEditUsername = {}; + const AccountEditUsername = {}; AccountEditUsername.init = function () { header.init(); $('#submitBtn').on('click', function updateUsername() { - var userData = { + const userData = { uid: $('#inputUID').val(), username: $('#inputNewUsername').val(), password: $('#inputCurrentPassword').val(), @@ -23,11 +23,11 @@ define('forum/account/edit/username', [ return app.alertError('[[user:username_same_as_password]]'); } - var btn = $(this); + const btn = $(this); btn.addClass('disabled').find('i').removeClass('hide'); api.put('/users/' + userData.uid, userData).then((response) => { - var userslug = slugify(userData.username); + const userslug = slugify(userData.username); if (userData.username && userslug && parseInt(userData.uid, 10) === parseInt(app.user.uid, 10)) { $('[component="header/profilelink"]').attr('href', config.relative_path + '/user/' + userslug); $('[component="header/profilelink/edit"]').attr('href', config.relative_path + '/user/' + userslug + '/edit'); diff --git a/public/src/client/account/followers.js b/public/src/client/account/followers.js index bae7343262..89b67561d8 100644 --- a/public/src/client/account/followers.js +++ b/public/src/client/account/followers.js @@ -2,7 +2,7 @@ define('forum/account/followers', ['forum/account/header'], function (header) { - var Followers = {}; + const Followers = {}; Followers.init = function () { header.init(); diff --git a/public/src/client/account/following.js b/public/src/client/account/following.js index 5881ae1bec..8bc5c92614 100644 --- a/public/src/client/account/following.js +++ b/public/src/client/account/following.js @@ -2,7 +2,7 @@ define('forum/account/following', ['forum/account/header'], function (header) { - var Following = {}; + const Following = {}; Following.init = function () { header.init(); diff --git a/public/src/client/account/groups.js b/public/src/client/account/groups.js index ba3fb3ce7c..17c78b1aa4 100644 --- a/public/src/client/account/groups.js +++ b/public/src/client/account/groups.js @@ -2,15 +2,15 @@ define('forum/account/groups', ['forum/account/header'], function (header) { - var AccountTopics = {}; + const AccountTopics = {}; AccountTopics.init = function () { header.init(); - var groupsEl = $('#groups-list'); + const groupsEl = $('#groups-list'); groupsEl.on('click', '.list-cover', function () { - var groupSlug = $(this).parents('[data-slug]').attr('data-slug'); + const groupSlug = $(this).parents('[data-slug]').attr('data-slug'); ajaxify.go('groups/' + groupSlug); }); diff --git a/public/src/client/account/header.js b/public/src/client/account/header.js index d7d04ebdd5..5bb46becaf 100644 --- a/public/src/client/account/header.js +++ b/public/src/client/account/header.js @@ -9,9 +9,10 @@ define('forum/account/header', [ 'benchpress', 'accounts/delete', 'api', -], function (coverPhoto, pictureCropper, components, translator, Benchpress, AccountsDelete, api) { - var AccountHeader = {}; - var isAdminOrSelfOrGlobalMod; + 'bootbox', +], function (coverPhoto, pictureCropper, components, translator, Benchpress, AccountsDelete, api, bootbox) { + const AccountHeader = {}; + let isAdminOrSelfOrGlobalMod; AccountHeader.init = function () { isAdminOrSelfOrGlobalMod = ajaxify.data.isAdmin || ajaxify.data.isSelf || ajaxify.data.isGlobalModerator; @@ -31,21 +32,19 @@ define('forum/account/header', [ toggleFollow('unfollow'); }); - components.get('account/chat').on('click', function () { - socket.emit('modules.chats.hasPrivateChat', ajaxify.data.uid, function (err, roomId) { - if (err) { - return app.alertError(err.message); - } - if (roomId) { - app.openChat(roomId); - } else { - app.newChat(ajaxify.data.uid); - } - }); + components.get('account/chat').on('click', async function () { + const roomId = await socket.emit('modules.chats.hasPrivateChat', ajaxify.data.uid); + const chat = await app.require('chat'); + if (roomId) { + chat.openChat(roomId); + } else { + chat.newChat(ajaxify.data.uid); + } }); - components.get('account/new-chat').on('click', function () { - app.newChat(ajaxify.data.uid, function () { + components.get('account/new-chat').on('click', async function () { + const chat = await app.require('chat'); + chat.newChat(ajaxify.data.uid, function () { components.get('account/chat').parent().removeClass('hidden'); }); }); @@ -77,7 +76,7 @@ define('forum/account/header', [ function selectActivePill() { $('.account-sub-links li').removeClass('active').each(function () { - var href = $(this).find('a').attr('href'); + const href = $(this).find('a').attr('href'); if (decodeURIComponent(href) === decodeURIComponent(window.location.pathname)) { $(this).addClass('active'); @@ -145,12 +144,12 @@ define('forum/account/header', [ submit: { label: '[[user:ban_account]]', callback: function () { - var formData = $('.ban-modal form').serializeArray().reduce(function (data, cur) { + const formData = $('.ban-modal form').serializeArray().reduce(function (data, cur) { data[cur.name] = cur.value; return data; }, {}); - var until = formData.length > 0 ? ( + const until = formData.length > 0 ? ( Date.now() + (formData.length * 1000 * 60 * 60 * (parseInt(formData.unit, 10) ? 24 : 1)) ) : 0; @@ -187,7 +186,7 @@ define('forum/account/header', [ } function toggleBlockAccount() { - var targetEl = this; + const targetEl = this; socket.emit('user.toggleBlock', { blockeeUid: ajaxify.data.uid, blockerUid: app.user.uid, diff --git a/public/src/client/account/ignored.js b/public/src/client/account/ignored.js index 0664325d8e..802985e141 100644 --- a/public/src/client/account/ignored.js +++ b/public/src/client/account/ignored.js @@ -1,7 +1,7 @@ 'use strict'; define('forum/account/ignored', ['forum/account/header', 'forum/account/topics'], function (header, topics) { - var AccountIgnored = {}; + const AccountIgnored = {}; AccountIgnored.init = function () { header.init(); diff --git a/public/src/client/account/info.js b/public/src/client/account/info.js index 6e57439bff..9a653fb582 100644 --- a/public/src/client/account/info.js +++ b/public/src/client/account/info.js @@ -2,7 +2,7 @@ define('forum/account/info', ['forum/account/header', 'components', 'forum/account/sessions'], function (header, components, sessions) { - var Info = {}; + const Info = {}; Info.init = function () { header.init(); @@ -12,16 +12,16 @@ define('forum/account/info', ['forum/account/header', 'components', 'forum/accou function handleModerationNote() { $('[component="account/save-moderation-note"]').on('click', function () { - var note = $('[component="account/moderation-note"]').val(); + const note = $('[component="account/moderation-note"]').val(); socket.emit('user.setModerationNote', { uid: ajaxify.data.uid, note: note }, function (err) { if (err) { return app.alertError(err.message); } $('[component="account/moderation-note"]').val(''); app.alertSuccess('[[user:info.moderation-note.success]]'); - var timestamp = Date.now(); - var data = [{ - note: note, + const timestamp = Date.now(); + const data = [{ + note: utils.escapeHTML(note), user: app.user, timestamp: timestamp, timestampISO: utils.toISOString(timestamp), diff --git a/public/src/client/account/posts.js b/public/src/client/account/posts.js index dbf1a6f7d8..27442e21a5 100644 --- a/public/src/client/account/posts.js +++ b/public/src/client/account/posts.js @@ -2,10 +2,10 @@ define('forum/account/posts', ['forum/account/header', 'forum/infinitescroll', 'hooks'], function (header, infinitescroll, hooks) { - var AccountPosts = {}; + const AccountPosts = {}; - var template; - var page = 1; + let template; + let page = 1; AccountPosts.init = function () { header.init(); @@ -27,7 +27,7 @@ define('forum/account/posts', ['forum/account/header', 'forum/infinitescroll', ' if (direction < 0) { return; } - var params = utils.params(); + const params = utils.params(); page += 1; params.page = page; diff --git a/public/src/client/account/profile.js b/public/src/client/account/profile.js index 421c108113..63024e4d04 100644 --- a/public/src/client/account/profile.js +++ b/public/src/client/account/profile.js @@ -5,7 +5,7 @@ define('forum/account/profile', [ 'forum/account/header', 'bootbox', ], function (header, bootbox) { - var Account = {}; + const Account = {}; Account.init = function () { header.init(); diff --git a/public/src/client/account/sessions.js b/public/src/client/account/sessions.js index f9dab2419f..13414bda6a 100644 --- a/public/src/client/account/sessions.js +++ b/public/src/client/account/sessions.js @@ -2,7 +2,7 @@ define('forum/account/sessions', ['forum/account/header', 'components', 'api'], function (header, components, api) { - var Sessions = {}; + const Sessions = {}; Sessions.init = function () { header.init(); @@ -11,8 +11,8 @@ define('forum/account/sessions', ['forum/account/header', 'components', 'api'], Sessions.prepareSessionRevocation = function () { components.get('user/sessions').on('click', '[data-action]', function () { - var parentEl = $(this).parents('[data-uuid]'); - var uuid = parentEl.attr('data-uuid'); + const parentEl = $(this).parents('[data-uuid]'); + const uuid = parentEl.attr('data-uuid'); if (uuid) { // This is done via DELETE because a user shouldn't be able to @@ -21,7 +21,7 @@ define('forum/account/sessions', ['forum/account/header', 'components', 'api'], parentEl.remove(); }).catch((err) => { try { - var errorObj = JSON.parse(err.responseText); + const errorObj = JSON.parse(err.responseText); if (errorObj.loggedIn === false) { window.location.href = config.relative_path + '/login?error=' + errorObj.title; } diff --git a/public/src/client/account/settings.js b/public/src/client/account/settings.js index 7994e552de..995fbbd4b6 100644 --- a/public/src/client/account/settings.js +++ b/public/src/client/account/settings.js @@ -2,7 +2,7 @@ define('forum/account/settings', ['forum/account/header', 'components', 'translator', 'api'], function (header, components, translator, api) { - var AccountSettings = {}; + const AccountSettings = {}; // If page skin is changed but not saved, switch the skin back $(window).on('action:ajaxify.start', function () { @@ -15,7 +15,7 @@ define('forum/account/settings', ['forum/account/header', 'components', 'transla header.init(); $('#submitBtn').on('click', function () { - var settings = loadSettings(); + const settings = loadSettings(); if (settings.homePageRoute === 'custom' && settings.homePageCustom) { $.get(config.relative_path + '/' + settings.homePageCustom, function () { @@ -42,11 +42,11 @@ define('forum/account/settings', ['forum/account/header', 'components', 'transla }; function loadSettings() { - var settings = {}; + const settings = {}; $('.account').find('input, textarea, select').each(function (id, input) { input = $(input); - var setting = input.attr('data-property'); + const setting = input.attr('data-property'); if (input.is('select')) { settings[setting] = input.val(); return; @@ -68,8 +68,8 @@ define('forum/account/settings', ['forum/account/header', 'components', 'transla function saveSettings(settings) { api.put(`/users/${ajaxify.data.uid}/settings`, { settings }).then((newSettings) => { app.alertSuccess('[[success:settings-saved]]'); - var languageChanged = false; - for (var key in newSettings) { + let languageChanged = false; + for (const key in newSettings) { if (newSettings.hasOwnProperty(key)) { if (key === 'userLang' && config.userLang !== newSettings.userLang) { languageChanged = true; @@ -82,7 +82,7 @@ define('forum/account/settings', ['forum/account/header', 'components', 'transla if (languageChanged && parseInt(app.user.uid, 10) === parseInt(ajaxify.data.theirid, 10)) { translator.translate('[[language:dir]]', config.userLang, function (translated) { - var htmlEl = $('html'); + const htmlEl = $('html'); htmlEl.attr('data-dir', translated); htmlEl.css('direction', translated); }); @@ -105,20 +105,20 @@ define('forum/account/settings', ['forum/account/header', 'components', 'transla } function reskin(skinName) { - var clientEl = Array.prototype.filter.call(document.querySelectorAll('link[rel="stylesheet"]'), function (el) { + const clientEl = Array.prototype.filter.call(document.querySelectorAll('link[rel="stylesheet"]'), function (el) { return el.href.indexOf(config.relative_path + '/assets/client') !== -1; })[0] || null; if (!clientEl) { return; } - var currentSkinClassName = $('body').attr('class').split(/\s+/).filter(function (className) { + const currentSkinClassName = $('body').attr('class').split(/\s+/).filter(function (className) { return className.startsWith('skin-'); }); if (!currentSkinClassName[0]) { return; } - var currentSkin = currentSkinClassName[0].slice(5); + let currentSkin = currentSkinClassName[0].slice(5); currentSkin = currentSkin !== 'noskin' ? currentSkin : ''; // Stop execution if skin didn't change @@ -126,7 +126,7 @@ define('forum/account/settings', ['forum/account/header', 'components', 'transla return; } - var linkEl = document.createElement('link'); + const linkEl = document.createElement('link'); linkEl.rel = 'stylesheet'; linkEl.type = 'text/css'; linkEl.href = config.relative_path + '/assets/client' + (skinName ? '-' + skinName : '') + '.css'; diff --git a/public/src/client/account/topics.js b/public/src/client/account/topics.js index de005271d9..cba3358275 100644 --- a/public/src/client/account/topics.js +++ b/public/src/client/account/topics.js @@ -6,10 +6,10 @@ define('forum/account/topics', [ 'forum/infinitescroll', 'hooks', ], function (header, infinitescroll, hooks) { - var AccountTopics = {}; + const AccountTopics = {}; - var template; - var page = 1; + let template; + let page = 1; AccountTopics.init = function () { header.init(); @@ -29,7 +29,7 @@ define('forum/account/topics', [ if (direction < 0) { return; } - var params = utils.params(); + const params = utils.params(); page += 1; params.page = page; diff --git a/public/src/client/account/uploads.js b/public/src/client/account/uploads.js index 222e30e9a0..51fdb8a614 100644 --- a/public/src/client/account/uploads.js +++ b/public/src/client/account/uploads.js @@ -1,14 +1,14 @@ 'use strict'; define('forum/account/uploads', ['forum/account/header'], function (header) { - var AccountUploads = {}; + const AccountUploads = {}; AccountUploads.init = function () { header.init(); $('[data-action="delete"]').on('click', function () { - var el = $(this).parents('[data-name]'); - var name = el.attr('data-name'); + const el = $(this).parents('[data-name]'); + const name = el.attr('data-name'); socket.emit('user.deleteUpload', { name: name, uid: ajaxify.data.uid }, function (err) { if (err) { diff --git a/public/src/client/account/upvoted.js b/public/src/client/account/upvoted.js index fb14f58111..274cdfb025 100644 --- a/public/src/client/account/upvoted.js +++ b/public/src/client/account/upvoted.js @@ -2,7 +2,7 @@ define('forum/account/upvoted', ['forum/account/header', 'forum/account/posts'], function (header, posts) { - var Upvoted = {}; + const Upvoted = {}; Upvoted.init = function () { header.init(); diff --git a/public/src/client/account/watched.js b/public/src/client/account/watched.js index a2c22f90f7..15861122c0 100644 --- a/public/src/client/account/watched.js +++ b/public/src/client/account/watched.js @@ -2,7 +2,7 @@ define('forum/account/watched', ['forum/account/header', 'forum/account/topics'], function (header, topics) { - var AccountWatched = {}; + const AccountWatched = {}; AccountWatched.init = function () { header.init(); diff --git a/public/src/client/categories.js b/public/src/client/categories.js index c7a05e7af5..44025e8b8c 100644 --- a/public/src/client/categories.js +++ b/public/src/client/categories.js @@ -2,7 +2,7 @@ define('forum/categories', ['components', 'categorySelector', 'hooks'], function (components, categorySelector, hooks) { - var categories = {}; + const categories = {}; $(window).on('action:ajaxify.start', function (ev, data) { if (ajaxify.currentPage !== data.url) { @@ -34,8 +34,8 @@ define('forum/categories', ['components', 'categorySelector', 'hooks'], function }; function renderNewPost(cid, post) { - var category = components.get('categories/category', 'cid', cid); - var numRecentReplies = category.attr('data-numRecentReplies'); + const category = components.get('categories/category', 'cid', cid); + const numRecentReplies = category.attr('data-numRecentReplies'); if (!numRecentReplies || !parseInt(numRecentReplies, 10)) { return; } @@ -43,7 +43,7 @@ define('forum/categories', ['components', 'categorySelector', 'hooks'], function return; } - var recentPosts = category.find('[component="category/posts"]'); + const recentPosts = category.find('[component="category/posts"]'); app.parseAndTranslate('partials/categories/lastpost', 'posts', { posts: [post] }, function (html) { html.find('.post-content img:not(.not-responsive)').addClass('img-responsive'); diff --git a/public/src/client/category.js b/public/src/client/category.js index 43c828a4af..da8a6296cf 100644 --- a/public/src/client/category.js +++ b/public/src/client/category.js @@ -9,7 +9,7 @@ define('forum/category', [ 'categorySelector', 'hooks', ], function (infinitescroll, share, navigator, topicList, sort, categorySelector, hooks) { - var Category = {}; + const Category = {}; $(window).on('action:ajaxify.start', function (ev, data) { if (!String(data.url).startsWith('category/')) { @@ -18,7 +18,7 @@ define('forum/category', [ }); Category.init = function () { - var cid = ajaxify.data.cid; + const cid = ajaxify.data.cid; app.enterRoom('category_' + cid); @@ -48,14 +48,12 @@ define('forum/category', [ }, }); - app.showEmailConfirmWarning(); - hooks.fire('action:topics.loaded', { topics: ajaxify.data.topics }); hooks.fire('action:category.loaded', { cid: ajaxify.data.cid }); }; function handleScrollToTopicIndex() { - var topicIndex = ajaxify.data.topicIndex; + let topicIndex = ajaxify.data.topicIndex; if (topicIndex && utils.isNumber(topicIndex)) { topicIndex = Math.max(0, parseInt(topicIndex, 10)); if (topicIndex && window.location.search.indexOf('page=') === -1) { @@ -66,8 +64,8 @@ define('forum/category', [ function handleIgnoreWatch(cid) { $('[component="category/watching"], [component="category/ignoring"], [component="category/notwatching"]').on('click', function () { - var $this = $(this); - var state = $this.attr('data-state'); + const $this = $(this); + const state = $this.attr('data-state'); socket.emit('categories.setWatchState', { cid: cid, state: state }, function (err) { if (err) { @@ -90,7 +88,7 @@ define('forum/category', [ function handleLoadMoreSubcategories() { $('[component="category/load-more-subcategories"]').on('click', function () { - var btn = $(this); + const btn = $(this); socket.emit('categories.loadMoreSubCategories', { cid: ajaxify.data.cid, start: ajaxify.data.nextSubCategoryStart, @@ -109,7 +107,8 @@ define('forum/category', [ app.createUserTooltips(html); ajaxify.data.nextSubCategoryStart += ajaxify.data.subCategoriesPerPage; ajaxify.data.subCategoriesLeft -= data.length; - btn.translateText('[[category:x-more-categories, ' + ajaxify.data.subCategoriesLeft + ']]'); + btn.toggleClass('hidden', ajaxify.data.subCategoriesLeft <= 0) + .translateText('[[category:x-more-categories, ' + ajaxify.data.subCategoriesLeft + ']]'); }); }); return false; @@ -138,7 +137,7 @@ define('forum/category', [ callback = callback || function () {}; hooks.fire('action:category.loading'); - var params = utils.params(); + const params = utils.params(); infinitescroll.loadMore('categories.loadMore', { cid: ajaxify.data.cid, after: after, diff --git a/public/src/client/category/tools.js b/public/src/client/category/tools.js index 0a10cec5be..84e2829d0c 100644 --- a/public/src/client/category/tools.js +++ b/public/src/client/category/tools.js @@ -10,7 +10,7 @@ define('forum/category/tools', [ 'api', 'bootbox', ], function (topicSelect, threadTools, components, translator, api, bootbox) { - var CategoryTools = {}; + const CategoryTools = {}; CategoryTools.init = function () { topicSelect.init(updateDropdownOptions); @@ -54,7 +54,7 @@ define('forum/category/tools', [ // todo: should also use categoryCommand, but no write api call exists for this yet components.get('topic/mark-unread-for-all').on('click', function () { - var tids = topicSelect.getSelectedTids(); + const tids = topicSelect.getSelectedTids(); if (!tids.length) { return app.alertError('[[error:no-topics-selected]]'); } @@ -73,7 +73,7 @@ define('forum/category/tools', [ components.get('topic/move').on('click', function () { require(['forum/topic/move'], function (move) { - var tids = topicSelect.getSelectedTids(); + const tids = topicSelect.getSelectedTids(); if (!tids.length) { return app.alertError('[[error:no-topics-selected]]'); @@ -85,7 +85,7 @@ define('forum/category/tools', [ }); components.get('topic/move-all').on('click', function () { - var cid = ajaxify.data.cid; + const cid = ajaxify.data.cid; if (!ajaxify.data.template.category) { return app.alertError('[[error:invalid-data]]'); } @@ -101,7 +101,7 @@ define('forum/category/tools', [ }); components.get('topic/merge').on('click', function () { - var tids = topicSelect.getSelectedTids(); + const tids = topicSelect.getSelectedTids(); require(['forum/topic/merge'], function (merge) { merge.init(function () { if (tids.length) { @@ -185,11 +185,11 @@ define('forum/category/tools', [ } function updateDropdownOptions() { - var tids = topicSelect.getSelectedTids(); - var isAnyDeleted = isAny(isTopicDeleted, tids); - var areAllDeleted = areAll(isTopicDeleted, tids); - var isAnyPinned = isAny(isTopicPinned, tids); - var isAnyLocked = isAny(isTopicLocked, tids); + const tids = topicSelect.getSelectedTids(); + const isAnyDeleted = isAny(isTopicDeleted, tids); + const areAllDeleted = areAll(isTopicDeleted, tids); + const isAnyPinned = isAny(isTopicPinned, tids); + const isAnyLocked = isAny(isTopicLocked, tids); const isAnyScheduled = isAny(isTopicScheduled, tids); const areAllScheduled = areAll(isTopicScheduled, tids); @@ -207,7 +207,7 @@ define('forum/category/tools', [ } function isAny(method, tids) { - for (var i = 0; i < tids.length; i += 1) { + for (let i = 0; i < tids.length; i += 1) { if (method(tids[i])) { return true; } @@ -216,7 +216,7 @@ define('forum/category/tools', [ } function areAll(method, tids) { - for (var i = 0; i < tids.length; i += 1) { + for (let i = 0; i < tids.length; i += 1) { if (!method(tids[i])) { return false; } @@ -245,20 +245,20 @@ define('forum/category/tools', [ } function setDeleteState(data) { - var topic = getTopicEl(data.tid); + const topic = getTopicEl(data.tid); topic.toggleClass('deleted', data.isDeleted); topic.find('[component="topic/locked"]').toggleClass('hide', !data.isDeleted); } function setPinnedState(data) { - var topic = getTopicEl(data.tid); + const topic = getTopicEl(data.tid); topic.toggleClass('pinned', data.isPinned); topic.find('[component="topic/pinned"]').toggleClass('hide', !data.isPinned); ajaxify.refresh(); } function setLockedState(data) { - var topic = getTopicEl(data.tid); + const topic = getTopicEl(data.tid); topic.toggleClass('locked', data.isLocked); topic.find('[component="topic/locked"]').toggleClass('hide', !data.isLocked); } @@ -275,31 +275,24 @@ define('forum/category/tools', [ if (!ajaxify.data.topics || !ajaxify.data.template.category) { return; } - var numPinned = ajaxify.data.topics.reduce(function (memo, topic) { - memo = topic.pinned ? memo += 1 : memo; - return memo; - }, 0); - + const numPinned = ajaxify.data.topics.filter(topic => topic.pinned).length; if ((!app.user.isAdmin && !app.user.isMod) || numPinned < 2) { return; } app.loadJQueryUI(function () { - var topicListEl = $('[component="category"]').filter(function (i, e) { + const topicListEl = $('[component="category"]').filter(function (i, e) { return !$(e).parents('[widget-area],[data-widget-area]').length; }); topicListEl.sortable({ handle: '[component="topic/pinned"]', items: '[component="category/topic"].pinned', - update: function () { - var data = []; - - var pinnedTopics = topicListEl.find('[component="category/topic"].pinned'); - pinnedTopics.each(function (index, element) { - data.push({ tid: $(element).attr('data-tid'), order: pinnedTopics.length - index - 1 }); - }); - - socket.emit('topics.orderPinnedTopics', data, function (err) { + update: function (ev, ui) { + const baseIndex = parseInt(topicListEl.find('[component="category/topic"].pinned').first().attr('data-index'), 10); + socket.emit('topics.orderPinnedTopics', { + tid: ui.item.attr('data-tid'), + order: baseIndex + ui.item.index() - 1, + }, function (err) { if (err) { return app.alertError(err.message); } diff --git a/public/src/client/chats.js b/public/src/client/chats.js index 0389834e76..c3fc8ff560 100644 --- a/public/src/client/chats.js +++ b/public/src/client/chats.js @@ -8,18 +8,23 @@ define('forum/chats', [ 'forum/chats/recent', 'forum/chats/search', 'forum/chats/messages', - 'benchpress', 'composer/autocomplete', 'hooks', -], function (components, translator, mousetrap, recentChats, search, messages, Benchpress, autocomplete, hooks) { - var Chats = { + 'bootbox', + 'chat', +], function ( + components, translator, mousetrap, + recentChats, search, messages, + autocomplete, hooks, bootbox, chatModule +) { + const Chats = { initialised: false, }; - var newMessage = false; + let newMessage = false; Chats.init = function () { - var env = utils.findBootstrapEnvironment(); + const env = utils.findBootstrapEnvironment(); if (!Chats.initialised) { Chats.addSocketListeners(); @@ -30,6 +35,7 @@ define('forum/chats', [ Chats.addEventListeners(); Chats.resizeMainWindow(); + Chats.setActive(); if (env === 'md' || env === 'lg') { Chats.addHotkeys(); @@ -69,8 +75,8 @@ define('forum/chats', [ Chats.addIPHandler = function (container) { container.on('click', '.chat-ip-button', function () { - var ipEl = $(this).parent(); - var mid = ipEl.parents('[data-mid]').attr('data-mid'); + const ipEl = $(this).parent(); + const mid = ipEl.parents('[data-mid]').attr('data-mid'); socket.emit('modules.chats.getIP', mid, function (err, ip) { if (err) { return app.alertError(err); @@ -82,16 +88,16 @@ define('forum/chats', [ Chats.addPopoutHandler = function () { $('[data-action="pop-out"]').on('click', function () { - var text = components.get('chat/input').val(); - var roomId = ajaxify.data.roomId; + const text = components.get('chat/input').val(); + const roomId = ajaxify.data.roomId; if (app.previousUrl && app.previousUrl.match(/chats/)) { ajaxify.go('user/' + ajaxify.data.userslug + '/chats', function () { - app.openChat(roomId, ajaxify.data.uid); + chatModule.openChat(roomId, ajaxify.data.uid); }, true); } else { window.history.go(-1); - app.openChat(roomId, ajaxify.data.uid); + chatModule.openChat(roomId, ajaxify.data.uid); } $(window).one('action:chat.loaded', function () { @@ -101,19 +107,19 @@ define('forum/chats', [ }; Chats.addScrollHandler = function (roomId, uid, el) { - var loading = false; + let loading = false; el.off('scroll').on('scroll', function () { messages.toggleScrollUpAlert(el); if (loading) { return; } - var top = (el[0].scrollHeight - el.height()) * 0.1; + const top = (el[0].scrollHeight - el.height()) * 0.1; if (el.scrollTop() >= top) { return; } loading = true; - var start = parseInt(el.children('[data-mid]').length, 10); + const start = parseInt(el.children('[data-mid]').length, 10); socket.emit('modules.chats.getMessages', { roomId: roomId, uid: uid, @@ -134,8 +140,8 @@ define('forum/chats', [ return; } messages.parseMessage(data, function (html) { - var currentScrollTop = el.scrollTop(); - var previousHeight = el[0].scrollHeight; + const currentScrollTop = el.scrollTop(); + const previousHeight = el[0].scrollHeight; html = $(html); el.prepend(html); html.find('.timeago').timeago(); @@ -156,7 +162,7 @@ define('forum/chats', [ }; Chats.addCharactersLeftHandler = function (parent) { - var element = parent.find('[component="chat/input"]'); + const element = parent.find('[component="chat/input"]'); element.on('change keyup paste', function () { messages.updateRemainingLength(parent); }); @@ -164,15 +170,15 @@ define('forum/chats', [ Chats.addActionHandlers = function (element, roomId) { element.on('click', '[data-action]', function () { - var messageId = $(this).parents('[data-mid]').attr('data-mid'); - var action = this.getAttribute('data-action'); + const messageId = $(this).parents('[data-mid]').attr('data-mid'); + const action = this.getAttribute('data-action'); switch (action) { - case 'edit': - var inputEl = $('[data-roomid="' + roomId + '"] [component="chat/input"]'); + case 'edit': { + const inputEl = $('[data-roomid="' + roomId + '"] [component="chat/input"]'); messages.prepEdit(inputEl, messageId, roomId); break; - + } case 'delete': messages.delete(messageId, roomId); break; @@ -186,16 +192,16 @@ define('forum/chats', [ Chats.addHotkeys = function () { mousetrap.bind('ctrl+up', function () { - var activeContact = $('.chats-list .bg-info'); - var prev = activeContact.prev(); + const activeContact = $('.chats-list .bg-info'); + const prev = activeContact.prev(); if (prev.length) { Chats.switchChat(prev.attr('data-roomid')); } }); mousetrap.bind('ctrl+down', function () { - var activeContact = $('.chats-list .bg-info'); - var next = activeContact.next(); + const activeContact = $('.chats-list .bg-info'); + const next = activeContact.next(); if (next.length) { Chats.switchChat(next.attr('data-roomid')); @@ -204,12 +210,12 @@ define('forum/chats', [ mousetrap.bind('up', function (e) { if (e.target === components.get('chat/input').get(0)) { // Retrieve message id from messages list - var message = components.get('chat/messages').find('.chat-message[data-self="1"]').last(); + const message = components.get('chat/messages').find('.chat-message[data-self="1"]').last(); if (!message.length) { return; } - var lastMid = message.attr('data-mid'); - var inputEl = components.get('chat/input'); + const lastMid = message.attr('data-mid'); + const inputEl = components.get('chat/input'); messages.prepEdit(inputEl, lastMid, ajaxify.data.roomId); } @@ -217,7 +223,7 @@ define('forum/chats', [ }; Chats.addMemberHandler = function (roomId, buttonEl) { - var modal; + let modal; buttonEl.on('click', function () { app.parseAndTranslate('partials/modals/manage_room', {}, function (html) { @@ -231,8 +237,8 @@ define('forum/chats', [ Chats.refreshParticipantsList(roomId, modal); Chats.addKickHandler(roomId, modal); - var searchInput = modal.find('input'); - var errorEl = modal.find('.text-danger'); + const searchInput = modal.find('input'); + const errorEl = modal.find('.text-danger'); require(['autocomplete', 'translator'], function (autocomplete, translator) { autocomplete.user(searchInput, function (event, selected) { errorEl.text(''); @@ -257,7 +263,7 @@ define('forum/chats', [ Chats.addKickHandler = function (roomId, modal) { modal.on('click', '[data-action="kick"]', function () { - var uid = parseInt(this.getAttribute('data-uid'), 10); + const uid = parseInt(this.getAttribute('data-uid'), 10); socket.emit('modules.chats.removeUserFromRoom', { roomId: roomId, @@ -286,11 +292,9 @@ define('forum/chats', [ } // Return user to chats page. If modal, close modal. - var modal = buttonEl.parents('.chat-modal'); + const modal = buttonEl.parents('.chat-modal'); if (modal.length) { - require(['chat'], function (chatLib) { - chatLib.close(modal); - }); + chatModule.close(modal); } else { ajaxify.go('chats'); } @@ -303,7 +307,7 @@ define('forum/chats', [ Chats.refreshParticipantsList = function (roomId, modal) { socket.emit('modules.chats.getUsersInRoom', { roomId: roomId }, function (err, users) { - var listEl = modal.find('.list-group'); + const listEl = modal.find('.list-group'); if (err) { return translator.translate('[[error:invalid-data]]', function (translated) { @@ -320,7 +324,7 @@ define('forum/chats', [ }; Chats.addRenameHandler = function (roomId, buttonEl, roomName) { - var modal; + let modal; buttonEl.on('click', function () { app.parseAndTranslate('partials/modals/rename_room', { @@ -372,7 +376,7 @@ define('forum/chats', [ return; } - var data = { + const data = { element: element, strategies: [], options: { @@ -392,7 +396,7 @@ define('forum/chats', [ }; Chats.leave = function (el) { - var roomId = el.attr('data-roomid'); + const roomId = el.attr('data-roomid'); socket.emit('modules.chats.leave', roomId, function (err) { if (err) { return app.alertError(err.message); @@ -402,12 +406,11 @@ define('forum/chats', [ } else { el.remove(); } - require(['chat'], function (chat) { - var modal = chat.getModal(roomId); - if (modal.length) { - chat.close(modal); - } - }); + + const modal = chatModule.getModal(roomId); + if (modal.length) { + chatModule.close(modal); + } }); }; @@ -417,7 +420,7 @@ define('forum/chats', [ roomid = ''; } - var url = 'user/' + ajaxify.data.userslug + '/chats/' + roomid + window.location.search; + const url = 'user/' + ajaxify.data.userslug + '/chats/' + roomid + window.location.search; if (self.fetch) { fetch(config.relative_path + '/api/' + url, { credentials: 'include' }) .then(function (response) { @@ -452,7 +455,7 @@ define('forum/chats', [ }; Chats.addGlobalEventListeners = function () { - $(window).on('resize', Chats.resizeMainWindow); + $(window).on('resize', utils.throttle(Chats.resizeMainWindow, 100)); $(window).on('mousemove keypress click', function () { if (newMessage && ajaxify.data.roomId) { socket.emit('modules.chats.markRead', ajaxify.data.roomId); @@ -469,12 +472,12 @@ define('forum/chats', [ messages.appendChatMessage($('.expanded-chat .chat-content'), data.message); } else if (ajaxify.data.template.chats) { - var roomEl = $('[data-roomid=' + data.roomId + ']'); + const roomEl = $('[data-roomid=' + data.roomId + ']'); if (roomEl.length > 0) { roomEl.addClass('unread'); } else { - var recentEl = components.get('chat/recent'); + const recentEl = components.get('chat/recent'); app.parseAndTranslate('partials/chats/recent_room', { rooms: { roomId: data.roomId, @@ -496,8 +499,8 @@ define('forum/chats', [ messages.addSocketListeners(); socket.on('event:chats.roomRename', function (data) { - var roomEl = components.get('chat/recent/room', data.roomId); - var titleEl = roomEl.find('[component="chat/title"]'); + const roomEl = components.get('chat/recent/room', data.roomId); + const titleEl = roomEl.find('[component="chat/title"]'); ajaxify.data.roomName = data.newName; titleEl.text(data.newName); @@ -505,17 +508,15 @@ define('forum/chats', [ }; Chats.resizeMainWindow = function () { - var viewportHeight = $(window).height(); - var mainWrapper = components.get('chat/main-wrapper'); - var navWrapper = components.get('chat/nav-wrapper'); - var fromTop = 0; + const viewportHeight = $(window).height(); + const mainWrapper = components.get('chat/main-wrapper'); + const navWrapper = components.get('chat/nav-wrapper'); + let fromTop = 0; if (mainWrapper.length && navWrapper.length) { fromTop = mainWrapper.offset().top || navWrapper.offset().top; } $('.chats-full').height(viewportHeight - fromTop - 1); - - Chats.setActive(); }; Chats.setActive = function () { diff --git a/public/src/client/chats/messages.js b/public/src/client/chats/messages.js index 29ab113e9b..7c11c1a479 100644 --- a/public/src/client/chats/messages.js +++ b/public/src/client/chats/messages.js @@ -1,12 +1,14 @@ 'use strict'; -define('forum/chats/messages', ['components', 'translator', 'benchpress', 'hooks'], function (components, translator, Benchpress, hooks) { - var messages = {}; +define('forum/chats/messages', [ + 'components', 'translator', 'benchpress', 'hooks', 'bootbox', 'messages', +], function (components, translator, Benchpress, hooks, bootbox, messagesModule) { + const messages = {}; messages.sendMessage = function (roomId, inputEl) { - var msg = inputEl.val(); - var mid = inputEl.attr('data-mid'); + const msg = inputEl.val(); + const mid = inputEl.attr('data-mid'); if (!msg.trim().length) { return; @@ -30,7 +32,7 @@ define('forum/chats/messages', ['components', 'translator', 'benchpress', 'hooks inputEl.val(msg); messages.updateRemainingLength(inputEl.parent()); if (err.message === '[[error:email-not-confirmed-chat]]') { - return app.showEmailConfirmWarning(err); + return messagesModule.showEmailConfirmWarning(err.message); } return app.alert({ @@ -59,7 +61,7 @@ define('forum/chats/messages', ['components', 'translator', 'benchpress', 'hooks }; messages.updateRemainingLength = function (parent) { - var element = parent.find('[component="chat/input"]'); + const element = parent.find('[component="chat/input"]'); parent.find('[component="chat/message/length"]').text(element.val().length); parent.find('[component="chat/message/remaining"]').text(config.maximumChatMessageLength - element.val().length); hooks.fire('action:chat.updateRemainingLength', { @@ -68,8 +70,8 @@ define('forum/chats/messages', ['components', 'translator', 'benchpress', 'hooks }; messages.appendChatMessage = function (chatContentEl, data) { - var lastSpeaker = parseInt(chatContentEl.find('.chat-message').last().attr('data-uid'), 10); - var lasttimestamp = parseInt(chatContentEl.find('.chat-message').last().attr('data-timestamp'), 10); + const lastSpeaker = parseInt(chatContentEl.find('.chat-message').last().attr('data-uid'), 10); + const lasttimestamp = parseInt(chatContentEl.find('.chat-message').last().attr('data-timestamp'), 10); if (!Array.isArray(data)) { data.newSet = lastSpeaker !== parseInt(data.fromuid, 10) || parseInt(data.timestamp, 10) > parseInt(lasttimestamp, 10) + (1000 * 60 * 3); @@ -81,8 +83,8 @@ define('forum/chats/messages', ['components', 'translator', 'benchpress', 'hooks }; function onMessagesParsed(chatContentEl, html) { - var newMessage = $(html); - var isAtBottom = messages.isAtBottom(chatContentEl); + const newMessage = $(html); + const isAtBottom = messages.isAtBottom(chatContentEl); newMessage.appendTo(chatContentEl); newMessage.find('.timeago').timeago(); newMessage.find('img:not(.not-responsive)').addClass('img-responsive'); @@ -114,7 +116,7 @@ define('forum/chats/messages', ['components', 'translator', 'benchpress', 'hooks messages.isAtBottom = function (containerEl, threshold) { if (containerEl.length) { - var distanceToBottom = containerEl[0].scrollHeight - ( + const distanceToBottom = containerEl[0].scrollHeight - ( containerEl.outerHeight() + containerEl.scrollTop() ); return distanceToBottom < (threshold || 100); @@ -131,7 +133,7 @@ define('forum/chats/messages', ['components', 'translator', 'benchpress', 'hooks }; messages.toggleScrollUpAlert = function (containerEl) { - var isAtBottom = messages.isAtBottom(containerEl, 300); + const isAtBottom = messages.isAtBottom(containerEl, 300); containerEl.parent() .find('[component="chat/messages/scroll-up-alert"]') .toggleClass('hidden', isAtBottom); @@ -171,10 +173,10 @@ define('forum/chats/messages', ['components', 'translator', 'benchpress', 'hooks function onChatMessageEdited(data) { data.messages.forEach(function (message) { - var self = parseInt(message.fromuid, 10) === parseInt(app.user.uid, 10); + const self = parseInt(message.fromuid, 10) === parseInt(app.user.uid, 10); message.self = self ? 1 : 0; messages.parseMessage(message, function (html) { - var body = components.get('chat/message', message.messageId); + const body = components.get('chat/message', message.messageId); if (body.length) { body.replaceWith(html); components.get('chat/message', message.messageId).find('.timeago').timeago(); diff --git a/public/src/client/chats/recent.js b/public/src/client/chats/recent.js index 32156cfc8f..de69baf45e 100644 --- a/public/src/client/chats/recent.js +++ b/public/src/client/chats/recent.js @@ -2,7 +2,7 @@ define('forum/chats/recent', function () { - var recent = {}; + const recent = {}; recent.init = function () { require(['forum/chats'], function (Chats) { @@ -11,8 +11,8 @@ define('forum/chats/recent', function () { }); $('[component="chat/recent"]').on('scroll', function () { - var $this = $(this); - var bottom = ($this[0].scrollHeight - $this.height()) * 0.9; + const $this = $(this); + const bottom = ($this[0].scrollHeight - $this.height()) * 0.9; if ($this.scrollTop() > bottom) { loadMoreRecentChats(); } @@ -21,7 +21,7 @@ define('forum/chats/recent', function () { }; function loadMoreRecentChats() { - var recentChats = $('[component="chat/recent"]'); + const recentChats = $('[component="chat/recent"]'); if (recentChats.attr('loading')) { return; } diff --git a/public/src/client/chats/search.js b/public/src/client/chats/search.js index 3103a22fab..fac37f908e 100644 --- a/public/src/client/chats/search.js +++ b/public/src/client/chats/search.js @@ -2,23 +2,14 @@ define('forum/chats/search', ['components', 'api'], function (components, api) { - var search = {}; + const search = {}; search.init = function () { - var timeoutId = 0; - - components.get('chat/search').on('keyup', function () { - if (timeoutId) { - clearTimeout(timeoutId); - timeoutId = 0; - } - - timeoutId = setTimeout(doSearch, 250); - }); + components.get('chat/search').on('keyup', utils.debounce(doSearch, 250)); }; function doSearch() { - var username = components.get('chat/search').val(); + const username = components.get('chat/search').val(); if (!username) { return $('[component="chat/search/list"]').empty(); } @@ -32,7 +23,7 @@ define('forum/chats/search', ['components', 'api'], function (components, api) { } function displayResults(data) { - var chatsListEl = $('[component="chat/search/list"]'); + const chatsListEl = $('[component="chat/search/list"]'); chatsListEl.empty(); data.users = data.users.filter(function (user) { @@ -44,7 +35,7 @@ define('forum/chats/search', ['components', 'api'], function (components, api) { } data.users.forEach(function (userObj) { - var chatEl = displayUser(chatsListEl, userObj); + const chatEl = displayUser(chatsListEl, userObj); onUserClick(chatEl, userObj); }); @@ -59,7 +50,7 @@ define('forum/chats/search', ['components', 'api'], function (components, api) { ' ' + userObj.username; } - var chatEl = $('
  • ') + const chatEl = $('
  • ') .attr('data-uid', userObj.uid) .appendTo(chatsListEl); @@ -78,7 +69,9 @@ define('forum/chats/search', ['components', 'api'], function (components, api) { chats.switchChat(roomId); }); } else { - app.newChat(userObj.uid); + require(['chat'], function (chat) { + chat.newChat(userObj.uid); + }); } }); }); diff --git a/public/src/client/compose.js b/public/src/client/compose.js index a7890679a5..2c0267c374 100644 --- a/public/src/client/compose.js +++ b/public/src/client/compose.js @@ -2,10 +2,10 @@ define('forum/compose', ['hooks'], function (hooks) { - var Compose = {}; + const Compose = {}; Compose.init = function () { - var container = $('.composer'); + const container = $('.composer'); if (container.length) { hooks.fire('action:composer.enhance', { diff --git a/public/src/client/flags/detail.js b/public/src/client/flags/detail.js index b43c7bcc5d..aa8ef07c21 100644 --- a/public/src/client/flags/detail.js +++ b/public/src/client/flags/detail.js @@ -1,7 +1,9 @@ 'use strict'; -define('forum/flags/detail', ['forum/flags/list', 'components', 'translator', 'benchpress', 'forum/account/header', 'accounts/delete', 'api'], function (FlagsList, components, translator, Benchpress, AccountHeader, AccountsDelete, api) { - var Detail = {}; +define('forum/flags/detail', [ + 'components', 'translator', 'benchpress', 'forum/account/header', 'accounts/delete', 'api', 'bootbox', +], function (components, translator, Benchpress, AccountHeader, AccountsDelete, api, bootbox) { + const Detail = {}; Detail.init = function () { // Update attributes @@ -9,9 +11,9 @@ define('forum/flags/detail', ['forum/flags/list', 'components', 'translator', 'b $('#assignee').val(ajaxify.data.assignee).removeAttr('disabled'); $('#content > div').on('click', '[data-action]', function () { - var action = this.getAttribute('data-action'); - var uid = $(this).parents('[data-uid]').attr('data-uid'); - var noteEl = document.getElementById('note'); + const action = this.getAttribute('data-action'); + const uid = $(this).parents('[data-uid]').attr('data-uid'); + const noteEl = document.getElementById('note'); switch (action) { case 'assign': @@ -45,8 +47,8 @@ define('forum/flags/detail', ['forum/flags/list', 'components', 'translator', 'b }).catch(app.alertError); break; - case 'delete-note': - var datetime = parseInt(this.closest('[data-datetime]').getAttribute('data-datetime'), 10); + case 'delete-note': { + const datetime = parseInt(this.closest('[data-datetime]').getAttribute('data-datetime'), 10); bootbox.confirm('[[flags:delete-note-confirm]]', function (ok) { if (ok) { api.delete(`/flags/${ajaxify.data.flagId}/notes/${datetime}`, {}).then((payload) => { @@ -57,9 +59,11 @@ define('forum/flags/detail', ['forum/flags/list', 'components', 'translator', 'b } }); break; - + } case 'chat': - app.newChat(uid); + require(['chat'], function (chat) { + chat.newChat(uid); + }); break; case 'ban': @@ -90,15 +94,15 @@ define('forum/flags/detail', ['forum/flags/list', 'components', 'translator', 'b postAction('restore', ajaxify.data.target.pid, ajaxify.data.target.tid); break; - case 'prepare-edit': - var selectedNoteEl = this.closest('[data-index]'); - var index = selectedNoteEl.getAttribute('data-index'); - var textareaEl = document.getElementById('note'); + case 'prepare-edit': { + const selectedNoteEl = this.closest('[data-index]'); + const index = selectedNoteEl.getAttribute('data-index'); + const textareaEl = document.getElementById('note'); textareaEl.value = ajaxify.data.notes[index].content; textareaEl.setAttribute('data-datetime', ajaxify.data.notes[index].datetime); - var siblings = selectedNoteEl.parentElement.children; - for (var el in siblings) { + const siblings = selectedNoteEl.parentElement.children; + for (const el in siblings) { if (siblings.hasOwnProperty(el)) { siblings[el].classList.remove('editing'); } @@ -106,6 +110,7 @@ define('forum/flags/detail', ['forum/flags/list', 'components', 'translator', 'b selectedNoteEl.classList.add('editing'); textareaEl.focus(); break; + } } }); }; @@ -136,7 +141,7 @@ define('forum/flags/detail', ['forum/flags/list', 'components', 'translator', 'b Benchpress.render('flags/detail', { notes: notes, }, 'notes').then(function (html) { - var wrapperEl = components.get('flag/notes'); + const wrapperEl = components.get('flag/notes'); wrapperEl.empty(); wrapperEl.html(html); wrapperEl.find('span.timeago').timeago(); @@ -148,7 +153,7 @@ define('forum/flags/detail', ['forum/flags/list', 'components', 'translator', 'b app.parseAndTranslate('flags/detail', 'history', { history: history, }, function (html) { - var wrapperEl = components.get('flag/history'); + const wrapperEl = components.get('flag/history'); wrapperEl.empty(); wrapperEl.html(html); wrapperEl.find('span.timeago').timeago(); diff --git a/public/src/client/flags/list.js b/public/src/client/flags/list.js index a46dbade1c..2e3000c2d2 100644 --- a/public/src/client/flags/list.js +++ b/public/src/client/flags/list.js @@ -1,9 +1,9 @@ 'use strict'; define('forum/flags/list', ['components', 'Chart', 'categoryFilter', 'autocomplete', 'api'], function (components, Chart, categoryFilter, autocomplete, api) { - var Flags = {}; + const Flags = {}; - var selectedCids; + let selectedCids; Flags.init = function () { Flags.enableFilterForm(); @@ -30,7 +30,7 @@ define('forum/flags/list', ['components', 'Chart', 'categoryFilter', 'autocomple return; } - var flagId = this.getAttribute('data-flag-id'); + const flagId = this.getAttribute('data-flag-id'); ajaxify.go('flags/' + flagId); }); @@ -75,13 +75,13 @@ define('forum/flags/list', ['components', 'Chart', 'categoryFilter', 'autocomple }; Flags.enableCheckboxes = function () { - var flagsList = document.querySelector('[component="flags/list"]'); - var checkboxes = flagsList.querySelectorAll('[data-flag-id] input[type="checkbox"]'); - var bulkEl = document.querySelector('[component="flags/bulk-actions"] button'); - var lastClicked; + const flagsList = document.querySelector('[component="flags/list"]'); + const checkboxes = flagsList.querySelectorAll('[data-flag-id] input[type="checkbox"]'); + const bulkEl = document.querySelector('[component="flags/bulk-actions"] button'); + let lastClicked; document.querySelector('[data-action="toggle-all"]').addEventListener('click', function () { - var state = this.checked; + const state = this.checked; checkboxes.forEach(function (el) { el.checked = state; @@ -90,15 +90,15 @@ define('forum/flags/list', ['components', 'Chart', 'categoryFilter', 'autocomple }); flagsList.addEventListener('click', function (e) { - var subselector = e.target.closest('input[type="checkbox"]'); + const subselector = e.target.closest('input[type="checkbox"]'); if (subselector) { // Stop checkbox clicks from going into the flag details e.stopImmediatePropagation(); if (lastClicked && e.shiftKey && lastClicked !== subselector) { // Select all the checkboxes in between - var state = subselector.checked; - var started = false; + const state = subselector.checked; + let started = false; checkboxes.forEach(function (el) { if ([subselector, lastClicked].some(function (ref) { @@ -130,16 +130,16 @@ define('forum/flags/list', ['components', 'Chart', 'categoryFilter', 'autocomple Flags.handleBulkActions = function () { document.querySelector('[component="flags/bulk-actions"]').addEventListener('click', function (e) { - var subselector = e.target.closest('[data-action]'); + const subselector = e.target.closest('[data-action]'); if (subselector) { - var action = subselector.getAttribute('data-action'); - var flagIds = Flags.getSelected(); - var promises = []; + const action = subselector.getAttribute('data-action'); + const flagIds = Flags.getSelected(); + const promises = []; // TODO: this can be better done with flagIds.map to return promises flagIds.forEach(function (flagId) { promises.push(new Promise(function (resolve, reject) { - var handler = function (err) { + const handler = function (err) { if (err) { reject(err); } @@ -164,10 +164,10 @@ define('forum/flags/list', ['components', 'Chart', 'categoryFilter', 'autocomple }); Promise.allSettled(promises).then(function (results) { - var fulfilled = results.filter(function (res) { + const fulfilled = results.filter(function (res) { return res.status === 'fulfilled'; }).length; - var errors = results.filter(function (res) { + const errors = results.filter(function (res) { return res.status === 'rejected'; }); if (fulfilled) { @@ -184,8 +184,8 @@ define('forum/flags/list', ['components', 'Chart', 'categoryFilter', 'autocomple }; Flags.getSelected = function () { - var checkboxes = document.querySelectorAll('[component="flags/list"] [data-flag-id] input[type="checkbox"]'); - var payload = []; + const checkboxes = document.querySelectorAll('[component="flags/list"] [data-flag-id] input[type="checkbox"]'); + const payload = []; checkboxes.forEach(function (el) { if (el.checked) { payload.push(el.closest('[data-flag-id]').getAttribute('data-flag-id')); @@ -196,15 +196,15 @@ define('forum/flags/list', ['components', 'Chart', 'categoryFilter', 'autocomple }; Flags.handleGraphs = function () { - var dailyCanvas = document.getElementById('flags:daily'); - var dailyLabels = utils.getDaysArray().map(function (text, idx) { + const dailyCanvas = document.getElementById('flags:daily'); + const dailyLabels = utils.getDaysArray().map(function (text, idx) { return idx % 3 ? '' : text; }); if (utils.isMobile()) { Chart.defaults.global.tooltips.enabled = false; } - var data = { + const data = { 'flags:daily': { labels: dailyLabels, datasets: [ diff --git a/public/src/client/groups/details.js b/public/src/client/groups/details.js index 531c62532e..b0ed482c0c 100644 --- a/public/src/client/groups/details.js +++ b/public/src/client/groups/details.js @@ -10,6 +10,7 @@ define('forum/groups/details', [ 'api', 'slugify', 'categorySelector', + 'bootbox', ], function ( memberList, iconSelect, @@ -19,13 +20,14 @@ define('forum/groups/details', [ translator, api, slugify, - categorySelector + categorySelector, + bootbox ) { - var Details = {}; - var groupName; + const Details = {}; + let groupName; Details.init = function () { - var detailsPage = components.get('groups/container'); + const detailsPage = components.get('groups/container'); groupName = ajaxify.data.group.name; @@ -66,12 +68,12 @@ define('forum/groups/details', [ components.get('groups/activity').find('.content img:not(.not-responsive)').addClass('img-responsive'); detailsPage.on('click', '[data-action]', function () { - var btnEl = $(this); - var userRow = btnEl.parents('[data-uid]'); - var ownerFlagEl = userRow.find('.member-name > i'); - var isOwner = !ownerFlagEl.hasClass('invisible'); - var uid = userRow.attr('data-uid'); - var action = btnEl.attr('data-action'); + const btnEl = $(this); + const userRow = btnEl.parents('[data-uid]'); + const ownerFlagEl = userRow.find('.member-name > i'); + const isOwner = !ownerFlagEl.hasClass('invisible'); + const uid = userRow.attr('data-uid'); + const action = btnEl.attr('data-action'); switch (action) { case 'toggleOwnership': @@ -133,16 +135,16 @@ define('forum/groups/details', [ }; Details.prepareSettings = function () { - var settingsFormEl = components.get('groups/settings'); - var labelColorValueEl = settingsFormEl.find('[name="labelColor"]'); - var textColorValueEl = settingsFormEl.find('[name="textColor"]'); - var iconBtn = settingsFormEl.find('[data-action="icon-select"]'); - var previewEl = settingsFormEl.find('.label'); - var previewElText = settingsFormEl.find('.label-text'); - var previewIcon = previewEl.find('i'); - var userTitleEl = settingsFormEl.find('[name="userTitle"]'); - var userTitleEnabledEl = settingsFormEl.find('[name="userTitleEnabled"]'); - var iconValueEl = settingsFormEl.find('[name="icon"]'); + const settingsFormEl = components.get('groups/settings'); + const labelColorValueEl = settingsFormEl.find('[name="labelColor"]'); + const textColorValueEl = settingsFormEl.find('[name="textColor"]'); + const iconBtn = settingsFormEl.find('[data-action="icon-select"]'); + const previewEl = settingsFormEl.find('.label'); + const previewElText = settingsFormEl.find('.label-text'); + const previewIcon = previewEl.find('i'); + const userTitleEl = settingsFormEl.find('[name="userTitle"]'); + const userTitleEnabledEl = settingsFormEl.find('[name="userTitleEnabled"]'); + const iconValueEl = settingsFormEl.find('[name="icon"]'); labelColorValueEl.on('input', function () { previewEl.css('background-color', labelColorValueEl.val()); @@ -166,7 +168,7 @@ define('forum/groups/details', [ // Disable user title customisation options if the the user title itself is disabled userTitleEnabledEl.on('change', function () { - var customOpts = components.get('groups/userTitleOption'); + const customOpts = components.get('groups/userTitleOption'); if (this.checked) { customOpts.removeAttr('disabled'); @@ -177,9 +179,9 @@ define('forum/groups/details', [ } }); - var cidSelector = categorySelector.init($('.member-post-cids-selector [component="category-selector"]'), { + const cidSelector = categorySelector.init($('.member-post-cids-selector [component="category-selector"]'), { onSelect: function (selectedCategory) { - var cids = ($('#memberPostCids').val() || '').split(',').map(cid => parseInt(cid, 10)); + let cids = ($('#memberPostCids').val() || '').split(',').map(cid => parseInt(cid, 10)); cids.push(selectedCategory.cid); cids = cids.filter((cid, index, array) => array.indexOf(cid) === index); $('#memberPostCids').val(cids.join(',')); @@ -189,11 +191,11 @@ define('forum/groups/details', [ }; Details.update = function () { - var settingsFormEl = components.get('groups/settings'); - var checkboxes = settingsFormEl.find('input[type="checkbox"][name]'); + const settingsFormEl = components.get('groups/settings'); + const checkboxes = settingsFormEl.find('input[type="checkbox"][name]'); if (settingsFormEl.length) { - var settings = settingsFormEl.serializeObject(); + const settings = settingsFormEl.serializeObject(); // serializeObject doesnt return array for multi selects if only one item is selected if (!Array.isArray(settings.memberPostCids)) { @@ -210,7 +212,7 @@ define('forum/groups/details', [ api.put(`/groups/${ajaxify.data.group.slug}`, settings).then(() => { if (settings.name) { - var pathname = window.location.pathname; + let pathname = window.location.pathname; pathname = pathname.substr(1, pathname.lastIndexOf('/')); ajaxify.go(pathname + slugify(settings.name)); } else { @@ -242,7 +244,7 @@ define('forum/groups/details', [ return; } - var searchInput = $('[component="groups/members/invite"]'); + const searchInput = $('[component="groups/members/invite"]'); require(['autocomplete'], function (autocomplete) { autocomplete.user(searchInput, function (event, selected) { socket.emit('groups.issueInvite', { @@ -258,7 +260,7 @@ define('forum/groups/details', [ }); $('[component="groups/members/bulk-invite-button"]').on('click', function () { - var usernames = $('[component="groups/members/bulk-invite"]').val(); + const usernames = $('[component="groups/members/bulk-invite"]').val(); if (!usernames) { return false; } diff --git a/public/src/client/groups/list.js b/public/src/client/groups/list.js index abfae6716d..d7645668e9 100644 --- a/public/src/client/groups/list.js +++ b/public/src/client/groups/list.js @@ -1,9 +1,9 @@ 'use strict'; define('forum/groups/list', [ - 'forum/infinitescroll', 'benchpress', 'api', -], function (infinitescroll, Benchpress, api) { - var Groups = {}; + 'forum/infinitescroll', 'benchpress', 'api', 'bootbox', +], function (infinitescroll, Benchpress, api, bootbox) { + const Groups = {}; Groups.init = function () { infinitescroll.init(Groups.loadMoreGroups); @@ -20,7 +20,7 @@ define('forum/groups/list', [ } }); }); - var params = utils.params(); + const params = utils.params(); $('#search-sort').val(params.sort || 'alpha'); // Group searching @@ -58,9 +58,9 @@ define('forum/groups/list', [ }; Groups.search = function () { - var groupsEl = $('#groups-list'); - var queryEl = $('#search-text'); - var sortEl = $('#search-sort'); + const groupsEl = $('#groups-list'); + const queryEl = $('#search-text'); + const sortEl = $('#search-sort'); socket.emit('groups.search', { query: queryEl.val(), diff --git a/public/src/client/groups/memberlist.js b/public/src/client/groups/memberlist.js index 679f270cbc..61f2c53334 100644 --- a/public/src/client/groups/memberlist.js +++ b/public/src/client/groups/memberlist.js @@ -1,10 +1,10 @@ 'use strict'; -define('forum/groups/memberlist', ['api'], function (api) { - var MemberList = {}; - var searchInterval; - var groupName; - var templateName; +define('forum/groups/memberlist', ['api', 'bootbox'], function (api, bootbox) { + const MemberList = {}; + let searchInterval; + let groupName; + let templateName; MemberList.init = function (_templateName) { templateName = _templateName || 'groups/details'; @@ -18,14 +18,14 @@ define('forum/groups/memberlist', ['api'], function (api) { function handleMemberAdd() { $('[component="groups/members/add"]').on('click', function () { app.parseAndTranslate('admin/partials/groups/add-members', {}, function (html) { - var foundUsers = []; - var modal = bootbox.dialog({ + const foundUsers = []; + const modal = bootbox.dialog({ title: '[[groups:details.add-member]]', message: html, buttons: { ok: { callback: function () { - var users = []; + const users = []; modal.find('[data-uid][data-selected]').each(function (index, el) { users.push(foundUsers[$(el).attr('data-uid')]); }); @@ -37,7 +37,7 @@ define('forum/groups/memberlist', ['api'], function (api) { }, }); modal.on('click', '[data-username]', function () { - var isSelected = $(this).attr('data-selected') === '1'; + const isSelected = $(this).attr('data-selected') === '1'; if (isSelected) { $(this).removeAttr('data-selected'); } else { @@ -75,7 +75,7 @@ define('forum/groups/memberlist', ['api'], function (api) { }); callback(); } - var uids = users.map(function (user) { return user.uid; }); + const uids = users.map(function (user) { return user.uid; }); if (groupName === 'administrators') { socket.emit('admin.user.makeAdmins', uids, function (err) { if (err) { @@ -90,7 +90,7 @@ define('forum/groups/memberlist', ['api'], function (api) { function handleMemberSearch() { $('[component="groups/members/search"]').on('keyup', function () { - var query = $(this).val(); + const query = $(this).val(); if (searchInterval) { clearInterval(searchInterval); searchInterval = 0; @@ -112,8 +112,8 @@ define('forum/groups/memberlist', ['api'], function (api) { function handleMemberInfiniteScroll() { $('[component="groups/members"] tbody').on('scroll', function () { - var $this = $(this); - var bottom = ($this[0].scrollHeight - $this.innerHeight()) * 0.9; + const $this = $(this); + const bottom = ($this[0].scrollHeight - $this.innerHeight()) * 0.9; if ($this.scrollTop() > bottom && !$('[component="groups/members/search"]').val()) { loadMoreMembers(); @@ -122,7 +122,7 @@ define('forum/groups/memberlist', ['api'], function (api) { } function loadMoreMembers() { - var members = $('[component="groups/members"]'); + const members = $('[component="groups/members"]'); if (members.attr('loading')) { return; } diff --git a/public/src/client/header.js b/public/src/client/header.js new file mode 100644 index 0000000000..c6dfe9edb2 --- /dev/null +++ b/public/src/client/header.js @@ -0,0 +1,71 @@ +'use strict'; + +define('forum/header', ['forum/header/notifications', 'forum/header/chat'], function (notifications, chat) { + const module = {}; + + module.prepareDOM = function () { + notifications.prepareDOM(); + chat.prepareDOM(); + handleStatusChange(); + createHeaderTooltips(); + handleLogout(); + }; + + function handleStatusChange() { + $('[component="header/usercontrol"] [data-status]').off('click').on('click', function (e) { + const status = $(this).attr('data-status'); + socket.emit('user.setStatus', status, function (err) { + if (err) { + return app.alertError(err.message); + } + $('[data-uid="' + app.user.uid + '"] [component="user/status"], [component="header/profilelink"] [component="user/status"]') + .removeClass('away online dnd offline') + .addClass(status); + $('[component="header/usercontrol"] [data-status]').each(function () { + $(this).find('span').toggleClass('bold', $(this).attr('data-status') === status); + }); + app.user.status = status; + }); + e.preventDefault(); + }); + } + + function createHeaderTooltips() { + const env = utils.findBootstrapEnvironment(); + if (env === 'xs' || env === 'sm' || utils.isTouchDevice()) { + return; + } + $('#header-menu li a[title]').each(function () { + $(this).tooltip({ + placement: 'bottom', + trigger: 'hover', + title: $(this).attr('title'), + }); + }); + + + $('#search-form').tooltip({ + placement: 'bottom', + trigger: 'hover', + title: $('#search-button i').attr('title'), + }); + + + $('#user_dropdown').tooltip({ + placement: 'bottom', + trigger: 'hover', + title: $('#user_dropdown').attr('title'), + }); + } + + function handleLogout() { + $('#header-menu .container').on('click', '[component="user/logout"]', function () { + require(['logout'], function (logout) { + logout(); + }); + return false; + }); + } + + return module; +}); diff --git a/public/src/client/header/chat.js b/public/src/client/header/chat.js index 188bd4386d..8f87e2c513 100644 --- a/public/src/client/header/chat.js +++ b/public/src/client/header/chat.js @@ -1,11 +1,11 @@ 'use strict'; define('forum/header/chat', ['components'], function (components) { - var chat = {}; + const chat = {}; chat.prepareDOM = function () { - var chatsToggleEl = components.get('chat/dropdown'); - var chatsListEl = components.get('chat/list'); + const chatsToggleEl = components.get('chat/dropdown'); + const chatsListEl = components.get('chat/list'); chatsToggleEl.on('click', function () { if (chatsToggleEl.parent().hasClass('open')) { diff --git a/public/src/client/header/notifications.js b/public/src/client/header/notifications.js index b30535f0fd..024cc5960f 100644 --- a/public/src/client/header/notifications.js +++ b/public/src/client/header/notifications.js @@ -1,12 +1,12 @@ 'use strict'; define('forum/header/notifications', ['components'], function (components) { - var notifications = {}; + const notifications = {}; notifications.prepareDOM = function () { - var notifContainer = components.get('notifications'); - var notifTrigger = notifContainer.children('a'); - var notifList = components.get('notifications/list'); + const notifContainer = components.get('notifications'); + const notifTrigger = notifContainer.children('a'); + const notifList = components.get('notifications/list'); notifTrigger.on('click', function (e) { e.preventDefault(); diff --git a/public/src/client/infinitescroll.js b/public/src/client/infinitescroll.js index c8aebb413e..e734cf455e 100644 --- a/public/src/client/infinitescroll.js +++ b/public/src/client/infinitescroll.js @@ -2,12 +2,12 @@ define('forum/infinitescroll', ['hooks'], function (hooks) { - var scroll = {}; - var callback; - var previousScrollTop = 0; - var loadingMore = false; - var container; - var scrollTimeout = 0; + const scroll = {}; + let callback; + let previousScrollTop = 0; + let loadingMore = false; + let container; + let scrollTimeout = 0; scroll.init = function (el, cb) { const $body = $('body'); @@ -37,20 +37,20 @@ define('forum/infinitescroll', ['hooks'], function (hooks) { } function onScroll() { - var bsEnv = utils.findBootstrapEnvironment(); - var mobileComposerOpen = (bsEnv === 'xs' || bsEnv === 'sm') && $('html').hasClass('composing'); + const bsEnv = utils.findBootstrapEnvironment(); + const mobileComposerOpen = (bsEnv === 'xs' || bsEnv === 'sm') && $('html').hasClass('composing'); if (loadingMore || mobileComposerOpen) { return; } - var currentScrollTop = $(window).scrollTop(); - var wh = $(window).height(); - var viewportHeight = container.height() - wh; - var offsetTop = container.offset() ? container.offset().top : 0; - var scrollPercent = 100 * (currentScrollTop - offsetTop) / (viewportHeight <= 0 ? wh : viewportHeight); + const currentScrollTop = $(window).scrollTop(); + const wh = $(window).height(); + const viewportHeight = container.height() - wh; + const offsetTop = container.offset() ? container.offset().top : 0; + const scrollPercent = 100 * (currentScrollTop - offsetTop) / (viewportHeight <= 0 ? wh : viewportHeight); - var top = 15; - var bottom = 85; - var direction = currentScrollTop > previousScrollTop ? 1 : -1; + const top = 15; + const bottom = 85; + const direction = currentScrollTop > previousScrollTop ? 1 : -1; if (scrollPercent < top && currentScrollTop < previousScrollTop) { callback(direction); @@ -69,7 +69,7 @@ define('forum/infinitescroll', ['hooks'], function (hooks) { } loadingMore = true; - var hookData = { method: method, data: data }; + const hookData = { method: method, data: data }; hooks.fire('action:infinitescroll.loadmore', hookData); socket.emit(hookData.method, hookData.data, function (err, data) { @@ -88,8 +88,8 @@ define('forum/infinitescroll', ['hooks'], function (hooks) { return; } loadingMore = true; - var url = config.relative_path + '/api' + location.pathname.replace(new RegExp('^' + config.relative_path), ''); - var hookData = { url: url, data: data }; + const url = config.relative_path + '/api' + location.pathname.replace(new RegExp('^' + config.relative_path), ''); + const hookData = { url: url, data: data }; hooks.fire('action:infinitescroll.loadmore.xhr', hookData); $.get(url, data, function (data) { @@ -107,10 +107,10 @@ define('forum/infinitescroll', ['hooks'], function (hooks) { return; } - var removeCount = els.length - count; + const removeCount = els.length - count; if (direction > 0) { - var height = $(document).height(); - var scrollTop = $(window).scrollTop(); + const height = $(document).height(); + const scrollTop = $(window).scrollTop(); els.slice(0, removeCount).remove(); diff --git a/public/src/client/ip-blacklist.js b/public/src/client/ip-blacklist.js index cf4a7c12b9..624fe70ee5 100644 --- a/public/src/client/ip-blacklist.js +++ b/public/src/client/ip-blacklist.js @@ -1,11 +1,11 @@ 'use strict'; -define('forum/ip-blacklist', ['Chart', 'benchpress'], function (Chart, Benchpress) { - var Blacklist = {}; +define('forum/ip-blacklist', ['Chart', 'benchpress', 'bootbox'], function (Chart, Benchpress, bootbox) { + const Blacklist = {}; Blacklist.init = function () { - var blacklist = $('#blacklist-rules'); + const blacklist = $('#blacklist-rules'); blacklist.on('keyup', function () { $('#blacklist-rules-holder').val(blacklist.val()); @@ -42,12 +42,12 @@ define('forum/ip-blacklist', ['Chart', 'benchpress'], function (Chart, Benchpres }; Blacklist.setupAnalytics = function () { - var hourlyCanvas = document.getElementById('blacklist:hourly'); - var dailyCanvas = document.getElementById('blacklist:daily'); - var hourlyLabels = utils.getHoursArray().map(function (text, idx) { + const hourlyCanvas = document.getElementById('blacklist:hourly'); + const dailyCanvas = document.getElementById('blacklist:daily'); + const hourlyLabels = utils.getHoursArray().map(function (text, idx) { return idx % 3 ? '' : text; }); - var dailyLabels = utils.getDaysArray().slice(-7).map(function (text, idx) { + const dailyLabels = utils.getDaysArray().slice(-7).map(function (text, idx) { return idx % 3 ? '' : text; }); @@ -55,7 +55,7 @@ define('forum/ip-blacklist', ['Chart', 'benchpress'], function (Chart, Benchpres Chart.defaults.global.tooltips.enabled = false; } - var data = { + const data = { 'blacklist:hourly': { labels: hourlyLabels, datasets: [ diff --git a/public/src/client/login.js b/public/src/client/login.js index 0c59cdf2cc..9b130a4939 100644 --- a/public/src/client/login.js +++ b/public/src/client/login.js @@ -1,15 +1,15 @@ 'use strict'; -define('forum/login', ['hooks', 'jquery-form'], function (hooks) { - var Login = { +define('forum/login', ['hooks', 'translator', 'jquery-form'], function (hooks, translator) { + const Login = { _capsState: false, }; Login.init = function () { - var errorEl = $('#login-error-notify'); - var submitEl = $('#login'); - var formEl = $('#login-form'); + const errorEl = $('#login-error-notify'); + const submitEl = $('#login'); + const formEl = $('#login-form'); submitEl.on('click', function (e) { e.preventDefault(); @@ -36,25 +36,31 @@ define('forum/login', ['hooks', 'jquery-form'], function (hooks) { }, success: function (data) { hooks.fire('action:app.loggedIn', data); - var pathname = utils.urlToLocation(data.next).pathname; - var params = utils.params({ url: data.next }); + const pathname = utils.urlToLocation(data.next).pathname; + const params = utils.params({ url: data.next }); params.loggedin = true; - var qs = decodeURIComponent($.param(params)); + delete params.register; // clear register message incase it exists + const qs = decodeURIComponent($.param(params)); window.location.href = pathname + '?' + qs; }, error: function (data) { + let message = data.responseText; + const errInfo = data.responseJSON; if (data.status === 403 && data.responseText === 'Forbidden') { window.location.href = config.relative_path + '/login?error=csrf-invalid'; - } else { - errorEl.find('p').translateText(data.responseText); - errorEl.show(); - submitEl.removeClass('disabled'); - - // Select the entire password if that field has focus - if ($('#password:focus').length) { - $('#password').select(); - } + } else if (errInfo && errInfo.hasOwnProperty('banned_until')) { + message = errInfo.banned_until ? + translator.compile('error:user-banned-reason-until', (new Date(errInfo.banned_until).toLocaleString()), errInfo.reason) : + '[[error:user-banned-reason, ' + errInfo.reason + ']]'; + } + errorEl.find('p').translateText(message); + errorEl.show(); + submitEl.removeClass('disabled'); + + // Select the entire password if that field has focus + if ($('#password:focus').length) { + $('#password').select(); } }, }); diff --git a/public/src/client/notifications.js b/public/src/client/notifications.js index 97e7bb3129..e13da8f707 100644 --- a/public/src/client/notifications.js +++ b/public/src/client/notifications.js @@ -2,12 +2,12 @@ define('forum/notifications', ['components'], function (components) { - var Notifications = {}; + const Notifications = {}; Notifications.init = function () { - var listEl = $('.notifications-list'); + const listEl = $('.notifications-list'); listEl.on('click', '[component="notifications/item/link"]', function () { - var nid = $(this).parents('[data-nid]').attr('data-nid'); + const nid = $(this).parents('[data-nid]').attr('data-nid'); socket.emit('notifications.markRead', nid, function (err) { if (err) { return app.alertError(err); diff --git a/public/src/client/pagination.js b/public/src/client/pagination.js index 543f7534b7..8055708a90 100644 --- a/public/src/client/pagination.js +++ b/public/src/client/pagination.js @@ -1,8 +1,8 @@ 'use strict'; -define('forum/pagination', function () { - var pagination = {}; +define('forum/pagination', ['bootbox'], function (bootbox) { + const pagination = {}; pagination.init = function () { $('body').on('click', '[component="pagination/select-page"]', function () { @@ -20,10 +20,10 @@ define('forum/pagination', function () { return; } - var query = utils.params(); + const query = utils.params(); query.page = page; - var url = window.location.pathname + '?' + $.param(query); + const url = window.location.pathname + '?' + $.param(query); ajaxify.go(url, callback); }; diff --git a/public/src/client/popular.js b/public/src/client/popular.js index df9a656411..0ce846b784 100644 --- a/public/src/client/popular.js +++ b/public/src/client/popular.js @@ -2,7 +2,7 @@ define('forum/popular', ['topicList'], function (topicList) { - var Popular = {}; + const Popular = {}; Popular.init = function () { app.enterRoom('popular_topics'); diff --git a/public/src/client/post-queue.js b/public/src/client/post-queue.js index 6551fb031b..37934b0ea8 100644 --- a/public/src/client/post-queue.js +++ b/public/src/client/post-queue.js @@ -4,7 +4,7 @@ define('forum/post-queue', [ 'categoryFilter', 'categorySelector', 'api', ], function (categoryFilter, categorySelector, api) { - var PostQueue = {}; + const PostQueue = {}; PostQueue.init = function () { $('[data-toggle="tooltip"]').tooltip(); @@ -14,10 +14,10 @@ define('forum/post-queue', [ }); $('.posts-list').on('click', '[data-action]', function () { - var parent = $(this).parents('[data-id]'); - var action = $(this).attr('data-action'); - var id = parent.attr('data-id'); - var listContainer = parent.get(0).parentNode; + const parent = $(this).parents('[data-id]'); + const action = $(this).attr('data-action'); + const id = parent.attr('data-id'); + const listContainer = parent.get(0).parentNode; if (!['accept', 'reject'].some(function (valid) { return action === valid; @@ -42,8 +42,8 @@ define('forum/post-queue', [ handleContentEdit('.topic-title', '.topic-title-editable', 'input'); $('.posts-list').on('click', '.topic-category[data-editable]', function () { - var $this = $(this); - var id = $this.parents('[data-id]').attr('data-id'); + const $this = $(this); + const id = $this.parents('[data-id]').attr('data-id'); categorySelector.modal({ onSubmit: function (selectedCategory) { Promise.all([ @@ -53,7 +53,7 @@ define('forum/post-queue', [ cid: selectedCategory.cid, }), ]).then(function (result) { - var category = result[0]; + const category = result[0]; app.parseAndTranslate('post-queue', 'posts', { posts: [{ category: category, @@ -79,8 +79,8 @@ define('forum/post-queue', [ function handleContentEdit(displayClass, editableClass, inputSelector) { $('.posts-list').on('click', displayClass, function () { - var el = $(this); - var inputEl = el.parent().find(editableClass); + const el = $(this); + const inputEl = el.parent().find(editableClass); if (inputEl.length) { el.addClass('hidden'); inputEl.removeClass('hidden').find(inputSelector).focus(); @@ -88,10 +88,10 @@ define('forum/post-queue', [ }); $('.posts-list').on('blur', editableClass + ' ' + inputSelector, function () { - var textarea = $(this); - var preview = textarea.parent().parent().find(displayClass); - var id = textarea.parents('[data-id]').attr('data-id'); - var titleEdit = displayClass === '.topic-title'; + const textarea = $(this); + const preview = textarea.parent().parent().find(displayClass); + const id = textarea.parents('[data-id]').attr('data-id'); + const titleEdit = displayClass === '.topic-title'; socket.emit('posts.editQueuedContent', { id: id, diff --git a/public/src/client/recent.js b/public/src/client/recent.js index 93e3593039..9d0a9904a2 100644 --- a/public/src/client/recent.js +++ b/public/src/client/recent.js @@ -1,13 +1,12 @@ 'use strict'; define('forum/recent', ['topicList'], function (topicList) { - var Recent = {}; + const Recent = {}; Recent.init = function () { app.enterRoom('recent_topics'); topicList.init('recent'); - app.showEmailConfirmWarning(); }; return Recent; diff --git a/public/src/client/register.js b/public/src/client/register.js index 068ba9a6ed..758f402feb 100644 --- a/public/src/client/register.js +++ b/public/src/client/register.js @@ -2,23 +2,23 @@ define('forum/register', [ - 'translator', 'zxcvbn', 'slugify', 'api', 'forum/login', 'jquery-form', -], function (translator, zxcvbn, slugify, api, Login) { - var Register = {}; - var validationError = false; - var successIcon = ''; + 'translator', 'zxcvbn', 'slugify', 'api', 'bootbox', 'forum/login', 'jquery-form', +], function (translator, zxcvbn, slugify, api, bootbox, Login) { + const Register = {}; + let validationError = false; + const successIcon = ''; Register.init = function () { - var username = $('#username'); - var password = $('#password'); - var password_confirm = $('#password-confirm'); - var register = $('#register'); + const username = $('#username'); + const password = $('#password'); + const password_confirm = $('#password-confirm'); + const register = $('#register'); handleLanguageOverride(); $('#content #noscript').val('false'); - var query = utils.params(); + const query = utils.params(); if (query.token) { $('#token').val(query.token); } @@ -57,8 +57,8 @@ define('forum/register', [ Login.capsLockCheck(document.querySelector('#password'), document.querySelector('#caps-lock-warning')); register.on('click', function (e) { - var registerBtn = $(this); - var errorEl = $('#register-error-notify'); + const registerBtn = $(this); + const errorEl = $('#register-error-notify'); errorEl.addClass('hidden'); e.preventDefault(); validateForm(function () { @@ -78,11 +78,11 @@ define('forum/register', [ return; } if (data.next) { - var pathname = utils.urlToLocation(data.next).pathname; + const pathname = utils.urlToLocation(data.next).pathname; - var params = utils.params({ url: data.next }); + const params = utils.params({ url: data.next }); params.registered = true; - var qs = decodeURIComponent($.param(params)); + const qs = decodeURIComponent($.param(params)); window.location.href = pathname + '?' + qs; } else if (data.message) { @@ -114,8 +114,8 @@ define('forum/register', [ function validateUsername(username, callback) { callback = callback || function () {}; - var username_notify = $('#username-notify'); - var userslug = slugify(username); + const username_notify = $('#username-notify'); + const userslug = slugify(username); if (username.length < ajaxify.data.minimumUsernameLength || userslug.length < ajaxify.data.minimumUsernameLength) { showError(username_notify, '[[error:username-too-short]]'); } else if (username.length > ajaxify.data.maximumUsernameLength) { @@ -139,9 +139,9 @@ define('forum/register', [ } function validatePassword(password, password_confirm) { - var password_notify = $('#password-notify'); - var password_confirm_notify = $('#password-confirm-notify'); - var passwordStrength = zxcvbn(password); + const password_notify = $('#password-notify'); + const password_confirm_notify = $('#password-confirm-notify'); + const passwordStrength = zxcvbn(password); if (password.length < ajaxify.data.minimumPasswordLength) { showError(password_notify, '[[reset_password:password_too_short]]'); @@ -163,8 +163,8 @@ define('forum/register', [ } function validatePasswordConfirm(password, password_confirm) { - var password_notify = $('#password-notify'); - var password_confirm_notify = $('#password-confirm-notify'); + const password_notify = $('#password-notify'); + const password_confirm_notify = $('#password-confirm-notify'); if (!password || password_notify.hasClass('alert-error')) { return; @@ -200,8 +200,8 @@ define('forum/register', [ function handleLanguageOverride() { if (!app.user.uid && config.defaultLang !== config.userLang) { - var formEl = $('[component="register/local"]'); - var langEl = $(''); + const formEl = $('[component="register/local"]'); + const langEl = $(''); formEl.append(langEl); } diff --git a/public/src/client/reset.js b/public/src/client/reset.js index 06bda6307a..0c2f4d6066 100644 --- a/public/src/client/reset.js +++ b/public/src/client/reset.js @@ -2,12 +2,12 @@ define('forum/reset', function () { - var ResetPassword = {}; + const ResetPassword = {}; ResetPassword.init = function () { - var inputEl = $('#email'); - var errorEl = $('#error'); - var successEl = $('#success'); + const inputEl = $('#email'); + const errorEl = $('#error'); + const successEl = $('#success'); $('#reset').on('click', function () { if (inputEl.val() && inputEl.val().indexOf('@') !== -1) { diff --git a/public/src/client/reset_code.js b/public/src/client/reset_code.js index 5a27b16978..16333e8c3f 100644 --- a/public/src/client/reset_code.js +++ b/public/src/client/reset_code.js @@ -2,17 +2,17 @@ define('forum/reset_code', ['zxcvbn'], function (zxcvbn) { - var ResetCode = {}; + const ResetCode = {}; ResetCode.init = function () { - var reset_code = ajaxify.data.code; + const reset_code = ajaxify.data.code; - var resetEl = $('#reset'); - var password = $('#password'); - var repeat = $('#repeat'); + const resetEl = $('#reset'); + const password = $('#password'); + const repeat = $('#repeat'); resetEl.on('click', function () { - var strength = zxcvbn(password.val()); + const strength = zxcvbn(password.val()); if (password.val().length < ajaxify.data.minimumPasswordLength) { $('#notice').removeClass('hidden'); $('#notice strong').translateText('[[reset_password:password_too_short]]'); diff --git a/public/src/client/search.js b/public/src/client/search.js index 9412acc6d8..ac0d1ccfb1 100644 --- a/public/src/client/search.js +++ b/public/src/client/search.js @@ -7,12 +7,12 @@ define('forum/search', [ 'storage', 'hooks', ], function (searchModule, autocomplete, storage, hooks) { - var Search = {}; + const Search = {}; Search.init = function () { - var searchQuery = $('#results').attr('data-search-query'); + const searchQuery = $('#results').attr('data-search-query'); - var searchIn = $('#search-in'); + const searchIn = $('#search-in'); searchIn.on('change', function () { updateFormItemVisiblity(searchIn.val()); @@ -36,8 +36,8 @@ define('forum/search', [ }; function getSearchDataFromDOM() { - var form = $('#advanced-search'); - var searchData = { + const form = $('#advanced-search'); + const searchData = { in: $('#search-in').val(), }; searchData.term = $('#search-input').val(); @@ -65,17 +65,17 @@ define('forum/search', [ } function updateFormItemVisiblity(searchIn) { - var hide = searchIn.indexOf('posts') === -1 && searchIn.indexOf('titles') === -1; + const hide = searchIn.indexOf('posts') === -1 && searchIn.indexOf('titles') === -1; $('.post-search-item').toggleClass('hide', hide); } function fillOutForm() { - var params = utils.params({ + const params = utils.params({ disableToType: true, }); - var searchData = searchModule.getSearchPreferences(); - var formData = utils.merge(searchData, params); + const searchData = searchModule.getSearchPreferences(); + const formData = utils.merge(searchData, params); if (formData) { if (ajaxify.data.term) { @@ -127,8 +127,8 @@ define('forum/search', [ $('#post-sort-direction').val(formData.sortDirection || 'desc'); if (formData.showAs) { - var isTopic = formData.showAs === 'topics'; - var isPost = formData.showAs === 'posts'; + const isTopic = formData.showAs === 'topics'; + const isPost = formData.showAs === 'posts'; $('#show-as-topics').prop('checked', isTopic).parent().toggleClass('active', isTopic); $('#show-as-posts').prop('checked', isPost).parent().toggleClass('active', isPost); } @@ -148,7 +148,7 @@ define('forum/search', [ $('#clear-preferences').on('click', function () { storage.removeItem('search-preferences'); - var query = $('#search-input').val(); + const query = $('#search-input').val(); $('#advanced-search')[0].reset(); $('#search-input').val(query); app.alertSuccess('[[search:search-preferences-cleared]]'); @@ -157,7 +157,7 @@ define('forum/search', [ } function enableAutoComplete() { - var userEl = $('#posted-by-user'); + const userEl = $('#posted-by-user'); userEl.tagsinput({ confirmKeys: [13, 44], trimValue: true, @@ -166,7 +166,7 @@ define('forum/search', [ autocomplete.user(userEl.siblings('.bootstrap-tagsinput').find('input')); } - var tagEl = $('#has-tags'); + const tagEl = $('#has-tags'); tagEl.tagsinput({ confirmKeys: [13, 44], trimValue: true, diff --git a/public/src/client/tag.js b/public/src/client/tag.js index a23690ba68..4d4a048a2c 100644 --- a/public/src/client/tag.js +++ b/public/src/client/tag.js @@ -1,22 +1,12 @@ 'use strict'; - -define('forum/tag', ['topicList', 'forum/infinitescroll'], function (topicList, infinitescroll) { - var Tag = {}; +define('forum/tag', ['topicList', 'forum/infinitescroll'], function (topicList) { + const Tag = {}; Tag.init = function () { app.enterRoom('tags'); - topicList.init('tag', loadMoreTopics); - - function loadMoreTopics(after, direction, callback) { - infinitescroll.loadMore('topics.loadMoreFromSet', { - set: 'tag:' + ajaxify.data.tag + ':topics', - after: after, - direction: direction, - count: config.topicsPerPage, - }, callback); - } + topicList.init('tag'); }; return Tag; diff --git a/public/src/client/tags.js b/public/src/client/tags.js index cb096401de..b5f88d05b0 100644 --- a/public/src/client/tags.js +++ b/public/src/client/tags.js @@ -2,33 +2,23 @@ define('forum/tags', ['forum/infinitescroll'], function (infinitescroll) { - var Tags = {}; - var timeoutId = 0; + const Tags = {}; Tags.init = function () { app.enterRoom('tags'); $('#tag-search').focus(); - $('#tag-search').on('input propertychange', function () { - if (timeoutId) { - clearTimeout(timeoutId); - timeoutId = 0; - } - + $('#tag-search').on('input propertychange', utils.debounce(function () { if (!$('#tag-search').val().length) { return resetSearch(); } - timeoutId = setTimeout(function () { - socket.emit('topics.searchAndLoadTags', { query: $('#tag-search').val() }, function (err, results) { - if (err) { - return app.alertError(err.message); - } - onTagsLoaded(results.tags, true, function () { - timeoutId = 0; - }); - }); - }, 250); - }); + socket.emit('topics.searchAndLoadTags', { query: $('#tag-search').val() }, function (err, results) { + if (err) { + return app.alertError(err.message); + } + onTagsLoaded(results.tags, true); + }); + }, 250)); infinitescroll.init(Tags.loadMoreTags); }; diff --git a/public/src/client/top.js b/public/src/client/top.js index 9ad2e7b886..b2eb44e99f 100644 --- a/public/src/client/top.js +++ b/public/src/client/top.js @@ -1,7 +1,7 @@ 'use strict'; define('forum/top', ['topicList'], function (topicList) { - var Top = {}; + const Top = {}; Top.init = function () { app.enterRoom('top_topics'); diff --git a/public/src/client/topic.js b/public/src/client/topic.js index adf46848e5..47dfdba236 100644 --- a/public/src/client/topic.js +++ b/public/src/client/topic.js @@ -7,42 +7,32 @@ define('forum/topic', [ 'forum/topic/postTools', 'forum/topic/events', 'forum/topic/posts', - 'forum/topic/images', 'navigator', 'sort', 'components', 'storage', 'hooks', + 'api', ], function ( infinitescroll, threadTools, postTools, - events, posts, images, navigator, sort, - components, storage, hooks + events, posts, navigator, sort, + components, storage, hooks, api ) { - var Topic = {}; - var currentUrl = ''; + const Topic = {}; + let currentUrl = ''; $(window).on('action:ajaxify.start', function (ev, data) { - if (Topic.replaceURLTimeout) { - clearTimeout(Topic.replaceURLTimeout); - Topic.replaceURLTimeout = 0; - } events.removeListeners(); if (!String(data.url).startsWith('topic/')) { navigator.disable(); components.get('navbar/title').find('span').text('').hide(); app.removeAlert('bookmark'); - - require(['search'], function (search) { - if (search.topicDOM.active) { - search.topicDOM.end(); - } - }); } }); Topic.init = function () { - var tid = ajaxify.data.tid; + const tid = ajaxify.data.tid; currentUrl = ajaxify.currentPage; hooks.fire('action:topic.loading'); @@ -50,7 +40,7 @@ define('forum/topic', [ posts.onTopicPageLoad(components.get('post')); - navigator.init('[component="post"]', ajaxify.data.postcount, Topic.toTop, Topic.toBottom, Topic.navigatorCallback); + navigator.init('[component="post"]', ajaxify.data.postcount, Topic.toTop, Topic.toBottom, utils.debounce(Topic.navigatorCallback, 500)); postTools.init(tid); threadTools.init(tid, $('.topic')); @@ -66,42 +56,25 @@ define('forum/topic', [ addParentHandler(); addDropupHandler(); addRepliesHandler(); - - + addPostsPreviewHandler(); handleBookmark(tid); - $(window).on('scroll', updateTopicTitle); + $(window).on('scroll', utils.debounce(updateTopicTitle, 250)); handleTopicSearch(); - app.showEmailConfirmWarning(); hooks.fire('action:topic.loaded', ajaxify.data); }; function handleTopicSearch() { - $('.topic-search').off('click') - .on('click', '.prev', function () { - require(['search'], function (search) { - search.topicDOM.prev(); - }); - }) - .on('click', '.next', function () { - require(['search'], function (search) { - search.topicDOM.next(); - }); - }); - if (config.topicSearchEnabled) { - require(['mousetrap'], function (mousetrap) { + require(['mousetrap', 'search'], function (mousetrap, search) { mousetrap.bind(['command+f', 'ctrl+f'], function (e) { - var match = ajaxify.currentPage.match(/^topic\/([\d]+)/); - var tid; - if (match) { + if (ajaxify.data.template.topic) { e.preventDefault(); - tid = match[1]; - $('#search-fields input').val('in:topic-' + tid + ' '); - app.prepareSearch(); + $('#search-fields input').val('in:topic-' + ajaxify.data.tid + ' '); + search.showAndFocusInput(); } }); }); @@ -124,13 +97,13 @@ define('forum/topic', [ function handleBookmark(tid) { if (window.location.hash) { - var el = $(utils.escapeHTML(window.location.hash)); + const el = $(utils.escapeHTML(window.location.hash)); if (el.length) { return navigator.scrollToElement(el, true, 0); } } - var bookmark = ajaxify.data.bookmark || storage.getItem('topic:' + tid + ':bookmark'); - var postIndex = ajaxify.data.postIndex; + const bookmark = ajaxify.data.bookmark || storage.getItem('topic:' + tid + ':bookmark'); + const postIndex = ajaxify.data.postIndex; if (postIndex > 1) { if (components.get('post/anchor', postIndex - 1).length) { @@ -160,19 +133,19 @@ define('forum/topic', [ function addBlockQuoteHandler() { components.get('topic').on('click', 'blockquote .toggle', function () { - var blockQuote = $(this).parent('blockquote'); - var toggle = $(this); + const blockQuote = $(this).parent('blockquote'); + const toggle = $(this); blockQuote.toggleClass('uncollapsed'); - var collapsed = !blockQuote.hasClass('uncollapsed'); + const collapsed = !blockQuote.hasClass('uncollapsed'); toggle.toggleClass('fa-angle-down', collapsed).toggleClass('fa-angle-up', !collapsed); }); } function addParentHandler() { components.get('topic').on('click', '[component="post/parent"]', function (e) { - var toPid = $(this).attr('data-topid'); + const toPid = $(this).attr('data-topid'); - var toPost = $('[component="topic"]>[component="post"][data-pid="' + toPid + '"]'); + const toPost = $('[component="topic"]>[component="post"][data-pid="' + toPid + '"]'); if (toPost.length) { e.preventDefault(); navigator.scrollToIndex(toPost.attr('data-index'), true); @@ -183,26 +156,86 @@ define('forum/topic', [ function addDropupHandler() { // Locate all dropdowns - var target = $('#content .dropdown-menu').parent(); + const target = $('#content .dropdown-menu').parent(); // Toggle dropup if past 50% of screen $(target).on('show.bs.dropdown', function () { - var dropUp = this.getBoundingClientRect().top > ($(window).height() / 2); + const dropUp = this.getBoundingClientRect().top > ($(window).height() / 2); $(this).toggleClass('dropup', dropUp); }); } function addRepliesHandler() { $('[component="topic"]').on('click', '[component="post/reply-count"]', function () { - var btn = $(this); + const btn = $(this); require(['forum/topic/replies'], function (replies) { replies.init(btn); }); }); } + function addPostsPreviewHandler() { + if (!ajaxify.data.showPostPreviewsOnHover || utils.isMobile()) { + return; + } + let timeoutId = 0; + const postCache = {}; + $(window).one('action:ajaxify.start', function () { + clearTimeout(timeoutId); + $('#post-tooltip').remove(); + }); + $('[component="topic"]').on('mouseenter', '[component="post"] a, [component="topic/event"] a', async function () { + const link = $(this); + + async function renderPost(pid) { + const postData = postCache[pid] || await socket.emit('posts.getPostSummaryByPid', { pid: pid }); + if (postData) { + postCache[pid] = postData; + const tooltip = await app.parseAndTranslate('partials/topic/post-preview', { post: postData }); + tooltip.hide().find('.timeago').timeago(); + tooltip.appendTo($('body')).fadeIn(300); + const postContent = link.parents('[component="topic"]').find('[component="post/content"]').first(); + const postRect = postContent.offset(); + const postWidth = postContent.width(); + const linkRect = link.offset(); + tooltip.css({ + top: linkRect.top + 30, + left: postRect.left, + width: postWidth, + }); + } + } + + const href = link.attr('href'); + const validHref = href && href !== '#'; + const pathname = utils.urlToLocation(href).pathname; + $('#post-tooltip').remove(); + const postMatch = validHref && pathname && pathname.match(/\/post\/([\d]+)/); + const topicMatch = validHref && pathname && pathname.match(/\/topic\/([\d]+)/); + if (postMatch) { + const pid = postMatch[1]; + if (parseInt(link.parents('[component="post"]').attr('data-pid'), 10) === parseInt(pid, 10)) { + return; // dont render self post + } + + timeoutId = setTimeout(async () => { + renderPost(pid); + }, 300); + } else if (topicMatch) { + timeoutId = setTimeout(async () => { + const tid = topicMatch[1]; + const topicData = await api.get('/topics/' + tid, {}); + renderPost(topicData.mainPid); + }, 300); + } + }).on('mouseleave', '[component="post"] a, [component="topic/event"] a', function () { + clearTimeout(timeoutId); + $('#post-tooltip').remove(); + }); + } + function updateTopicTitle() { - var span = components.get('navbar/title').find('span'); + const span = components.get('navbar/title').find('span'); if ($(window).scrollTop() > 50 && span.hasClass('hidden')) { span.html(ajaxify.data.title).removeClass('hidden'); } else if ($(window).scrollTop() <= 50 && !span.hasClass('hidden')) { @@ -214,47 +247,37 @@ define('forum/topic', [ } Topic.navigatorCallback = function (index, elementCount) { - var path = ajaxify.removeRelativePath(window.location.pathname.slice(1)); - if (!path.startsWith('topic')) { - return; - } - - if (navigator.scrollActive) { + if (!ajaxify.data.template.topic || navigator.scrollActive) { return; } - var newUrl = 'topic/' + ajaxify.data.slug + (index > 1 ? ('/' + index) : ''); + const newUrl = 'topic/' + ajaxify.data.slug + (index > 1 ? ('/' + index) : ''); if (newUrl !== currentUrl) { - if (Topic.replaceURLTimeout) { - clearTimeout(Topic.replaceURLTimeout); - Topic.replaceURLTimeout = 0; - } currentUrl = newUrl; - Topic.replaceURLTimeout = setTimeout(function () { - if (index >= elementCount && app.user.uid) { - socket.emit('topics.markAsRead', [ajaxify.data.tid]); - } - updateUserBookmark(index); + if (index >= elementCount && app.user.uid) { + socket.emit('topics.markAsRead', [ajaxify.data.tid]); + } - Topic.replaceURLTimeout = 0; - if (ajaxify.data.updateUrlWithPostIndex && history.replaceState) { - var search = window.location.search || ''; - if (!config.usePagination) { - search = (search && !/^\?page=\d+$/.test(search) ? search : ''); - } + updateUserBookmark(index); - history.replaceState({ - url: newUrl + search, - }, null, window.location.protocol + '//' + window.location.host + config.relative_path + '/' + newUrl + search); + Topic.replaceURLTimeout = 0; + if (ajaxify.data.updateUrlWithPostIndex && history.replaceState) { + let search = window.location.search || ''; + if (!config.usePagination) { + search = (search && !/^\?page=\d+$/.test(search) ? search : ''); } - }, 500); + + history.replaceState({ + url: newUrl + search, + }, null, window.location.protocol + '//' + window.location.host + config.relative_path + '/' + newUrl + search); + } } }; function updateUserBookmark(index) { - var bookmarkKey = 'topic:' + ajaxify.data.tid + ':bookmark'; - var currentBookmark = ajaxify.data.bookmark || storage.getItem(bookmarkKey); + const bookmarkKey = 'topic:' + ajaxify.data.tid + ':bookmark'; + const currentBookmark = ajaxify.data.bookmark || storage.getItem(bookmarkKey); if (config.topicPostSort === 'newest_to_oldest') { index = Math.max(1, ajaxify.data.postcount - index + 2); } diff --git a/public/src/client/topic/change-owner.js b/public/src/client/topic/change-owner.js index 04fda698f5..f11a75099c 100644 --- a/public/src/client/topic/change-owner.js +++ b/public/src/client/topic/change-owner.js @@ -6,11 +6,11 @@ define('forum/topic/change-owner', [ 'postSelect', 'autocomplete', ], function (components, postSelect, autocomplete) { - var ChangeOwner = {}; + const ChangeOwner = {}; - var modal; - var commit; - var toUid = 0; + let modal; + let commit; + let toUid = 0; ChangeOwner.init = function (postEl) { if (modal) { return; diff --git a/public/src/client/topic/delete-posts.js b/public/src/client/topic/delete-posts.js index 76cd4ed9c2..b08697c18a 100644 --- a/public/src/client/topic/delete-posts.js +++ b/public/src/client/topic/delete-posts.js @@ -2,11 +2,11 @@ define('forum/topic/delete-posts', ['components', 'postSelect'], function (components, postSelect) { - var DeletePosts = {}; - var modal; - var deleteBtn; - var purgeBtn; - var tid; + const DeletePosts = {}; + let modal; + let deleteBtn; + let purgeBtn; + let tid; DeletePosts.init = function () { tid = ajaxify.data.tid; diff --git a/public/src/client/topic/events.js b/public/src/client/topic/events.js index e3ae6d82f1..fe08787232 100644 --- a/public/src/client/topic/events.js +++ b/public/src/client/topic/events.js @@ -12,9 +12,9 @@ define('forum/topic/events', [ 'benchpress', 'hooks', ], function (postTools, threadTools, posts, images, components, translator, Benchpress, hooks) { - var Events = {}; + const Events = {}; - var events = { + const events = { 'event:user_status_change': onUserStatusChange, 'event:voted': updatePostVotesAndUserReputation, 'event:bookmarked': updateBookmarkCount, @@ -50,7 +50,7 @@ define('forum/topic/events', [ Events.init = function () { Events.removeListeners(); - for (var eventName in events) { + for (const eventName in events) { if (events.hasOwnProperty(eventName)) { socket.on(eventName, events[eventName]); } @@ -58,7 +58,7 @@ define('forum/topic/events', [ }; Events.removeListeners = function () { - for (var eventName in events) { + for (const eventName in events) { if (events.hasOwnProperty(eventName)) { socket.removeListener(eventName, events[eventName]); } @@ -70,10 +70,10 @@ define('forum/topic/events', [ } function updatePostVotesAndUserReputation(data) { - var votes = $('[data-pid="' + data.post.pid + '"] [component="post/vote-count"]').filter(function (index, el) { + const votes = $('[data-pid="' + data.post.pid + '"] [component="post/vote-count"]').filter(function (index, el) { return parseInt($(el).closest('[data-pid]').attr('data-pid'), 10) === parseInt(data.post.pid, 10); }); - var reputationElements = $('.reputation[data-uid="' + data.post.uid + '"]'); + const reputationElements = $('.reputation[data-uid="' + data.post.uid + '"]'); votes.html(data.post.votes).attr('data-votes', data.post.votes); reputationElements.html(data.user.reputation).attr('data-reputation', data.user.reputation); } @@ -104,16 +104,16 @@ define('forum/topic/events', [ if (!data || !data.post || parseInt(data.post.tid, 10) !== parseInt(ajaxify.data.tid, 10)) { return; } - var editedPostEl = components.get('post/content', data.post.pid).filter(function (index, el) { + const editedPostEl = components.get('post/content', data.post.pid).filter(function (index, el) { return parseInt($(el).closest('[data-pid]').attr('data-pid'), 10) === parseInt(data.post.pid, 10); }); - var editorEl = $('[data-pid="' + data.post.pid + '"] [component="post/editor"]').filter(function (index, el) { + const editorEl = $('[data-pid="' + data.post.pid + '"] [component="post/editor"]').filter(function (index, el) { return parseInt($(el).closest('[data-pid]').attr('data-pid'), 10) === parseInt(data.post.pid, 10); }); - var topicTitle = components.get('topic/title'); - var navbarTitle = components.get('navbar/title').find('span'); - var breadCrumb = components.get('breadcrumb/current'); + const topicTitle = components.get('topic/title'); + const navbarTitle = components.get('navbar/title').find('span'); + const breadCrumb = components.get('breadcrumb/current'); if (data.topic.rescheduled) { return ajaxify.go('topic/' + data.topic.slug, null, true); @@ -121,7 +121,7 @@ define('forum/topic/events', [ if (topicTitle.length && data.topic.title && data.topic.renamed) { ajaxify.data.title = data.topic.title; - var newUrl = 'topic/' + data.topic.slug + (window.location.search ? window.location.search : ''); + const newUrl = 'topic/' + data.topic.slug + (window.location.search ? window.location.search : ''); history.replaceState({ url: newUrl }, null, window.location.protocol + '//' + window.location.host + config.relative_path + '/' + newUrl); topicTitle.fadeOut(250, function () { @@ -143,7 +143,7 @@ define('forum/topic/events', [ posts.addBlockquoteEllipses(editedPostEl.parent()); editedPostEl.fadeIn(250); - var editData = { + const editData = { editor: data.editor, editedISO: utils.toISOString(data.post.edited), }; @@ -160,7 +160,7 @@ define('forum/topic/events', [ if (data.topic.tags && tagsUpdated(data.topic.tags)) { Benchpress.render('partials/topic/tags', { tags: data.topic.tags }).then(function (html) { - var tags = $('.tags'); + const tags = $('.tags'); tags.fadeOut(250, function () { tags.html(html).fadeIn(250); @@ -176,7 +176,7 @@ define('forum/topic/events', [ return true; } - for (var i = 0; i < tags.length; i += 1) { + for (let i = 0; i < tags.length; i += 1) { if (!$('.tags .tag-item[data-tag="' + tags[i].value + '"]').length) { return true; } @@ -200,14 +200,14 @@ define('forum/topic/events', [ } function togglePostDeleteState(data) { - var postEl = components.get('post', 'pid', data.pid); + const postEl = components.get('post', 'pid', data.pid); if (!postEl.length) { return; } postEl.toggleClass('deleted'); - var isDeleted = postEl.hasClass('deleted'); + const isDeleted = postEl.hasClass('deleted'); postTools.toggle(data.pid, isDeleted); if (!ajaxify.data.privileges.isAdminOrMod && parseInt(data.uid, 10) !== parseInt(app.user.uid, 10)) { @@ -221,7 +221,7 @@ define('forum/topic/events', [ } function togglePostBookmark(data) { - var el = $('[data-pid="' + data.post.pid + '"] [component="post/bookmark"]').filter(function (index, el) { + const el = $('[data-pid="' + data.post.pid + '"] [component="post/bookmark"]').filter(function (index, el) { return parseInt($(el).closest('[data-pid]').attr('data-pid'), 10) === parseInt(data.post.pid, 10); }); if (!el.length) { @@ -235,7 +235,7 @@ define('forum/topic/events', [ } function togglePostVote(data) { - var post = $('[data-pid="' + data.post.pid + '"]'); + const post = $('[data-pid="' + data.post.pid + '"]'); post.find('[component="post/upvote"]').filter(function (index, el) { return parseInt($(el).closest('[data-pid]').attr('data-pid'), 10) === parseInt(data.post.pid, 10); }).toggleClass('upvoted', data.upvote); @@ -245,7 +245,7 @@ define('forum/topic/events', [ } function onNewNotification(data) { - var tid = ajaxify.data.tid; + const tid = ajaxify.data.tid; if (data && data.tid && parseInt(data.tid, 10) === parseInt(tid, 10)) { socket.emit('topics.markTopicNotificationsRead', [tid]); } diff --git a/public/src/client/topic/fork.js b/public/src/client/topic/fork.js index 3b540e58e7..ba4e220d2c 100644 --- a/public/src/client/topic/fork.js +++ b/public/src/client/topic/fork.js @@ -2,10 +2,10 @@ define('forum/topic/fork', ['components', 'postSelect'], function (components, postSelect) { - var Fork = {}; - var forkModal; - var forkCommit; - var fromTid; + const Fork = {}; + let forkModal; + let forkCommit; + let fromTid; Fork.init = function () { fromTid = ajaxify.data.tid; diff --git a/public/src/client/topic/images.js b/public/src/client/topic/images.js index c1dc5866db..a04b22a9e9 100644 --- a/public/src/client/topic/images.js +++ b/public/src/client/topic/images.js @@ -2,14 +2,14 @@ define('forum/topic/images', [], function () { - var Images = {}; + const Images = {}; Images.wrapImagesInLinks = function (posts) { posts.find('[component="post/content"] img:not(.emoji)').each(function () { - var $this = $(this); - var src = $this.attr('src') || ''; - var alt = $this.attr('alt') || ''; - var suffixRegex = /-resized(\.[\w]+)?$/; + const $this = $(this); + let src = $this.attr('src') || ''; + const alt = $this.attr('alt') || ''; + const suffixRegex = /-resized(\.[\w]+)?$/; if (src === 'about:blank') { return; @@ -18,9 +18,9 @@ define('forum/topic/images', [], function () { if (utils.isRelativeUrl(src) && suffixRegex.test(src)) { src = src.replace(suffixRegex, '$1'); } - var srcExt = src.split('.').slice(1).pop(); - var altFilename = alt.split('/').pop(); - var altExt = altFilename.split('.').slice(1).pop(); + const srcExt = src.split('.').slice(1).pop(); + const altFilename = alt.split('/').pop(); + const altExt = altFilename.split('.').slice(1).pop(); if (!$this.parent().is('a')) { $this.wrap(' 1) { message = '[[topic:topic_move_multiple_success, ' + selectedCategory.name + ']]'; } else if (!Move.tids) { message = '[[topic:topic_move_all_success, ' + selectedCategory.name + ']]'; } - var data = { + const data = { tids: Move.tids ? Move.tids.slice() : null, cid: selectedCategory.cid, currentCid: Move.currentCid, diff --git a/public/src/client/topic/postTools.js b/public/src/client/topic/postTools.js index dfc9f6ce72..36c752fa9f 100644 --- a/public/src/client/topic/postTools.js +++ b/public/src/client/topic/postTools.js @@ -11,9 +11,9 @@ define('forum/topic/postTools', [ 'bootbox', 'hooks', ], function (share, navigator, components, translator, votes, api, bootbox, hooks) { - var PostTools = {}; + const PostTools = {}; - var staleReplyAnyway = false; + let staleReplyAnyway = false; PostTools.init = function (tid) { staleReplyAnyway = false; @@ -31,14 +31,14 @@ define('forum/topic/postTools', [ function renderMenu() { $('[component="topic"]').on('show.bs.dropdown', '.moderator-tools', function () { - var $this = $(this); - var dropdownMenu = $this.find('.dropdown-menu'); + const $this = $(this); + const dropdownMenu = $this.find('.dropdown-menu'); if (dropdownMenu.html()) { return; } - var postEl = $this.parents('[data-pid]'); - var pid = postEl.attr('data-pid'); - var index = parseInt(postEl.attr('data-index'), 10); + const postEl = $this.parents('[data-pid]'); + const pid = postEl.attr('data-pid'); + const index = parseInt(postEl.attr('data-index'), 10); socket.emit('posts.loadPostTools', { pid: pid, cid: ajaxify.data.cid }, function (err, data) { if (err) { @@ -58,7 +58,7 @@ define('forum/topic/postTools', [ } PostTools.toggle = function (pid, isDeleted) { - var postEl = components.get('post', 'pid', pid); + const postEl = components.get('post', 'pid', pid); postEl.find('[component="post/quote"], [component="post/bookmark"], [component="post/reply"], [component="post/flag"], [component="user/chat"]') .toggleClass('hidden', isDeleted); @@ -75,14 +75,16 @@ define('forum/topic/postTools', [ }; PostTools.updatePostCount = function (postCount) { - var postCountEl = components.get('topic/post-count'); + const postCountEl = components.get('topic/post-count'); postCountEl.html(postCount).attr('title', postCount); utils.makeNumbersHumanReadable(postCountEl); navigator.setCount(postCount); }; function addPostHandlers(tid) { - var postContainer = components.get('topic'); + const postContainer = components.get('topic'); + + handleSelectionTooltip(); postContainer.on('click', '[component="post/quote"]', function () { onQuoteClicked($(this), tid); @@ -123,7 +125,7 @@ define('forum/topic/postTools', [ }); postContainer.on('click', '[component="post/flag"]', function () { - var pid = getData($(this), 'data-pid'); + const pid = getData($(this), 'data-pid'); require(['flags'], function (flags) { flags.showFlagModal({ type: 'post', @@ -133,7 +135,7 @@ define('forum/topic/postTools', [ }); postContainer.on('click', '[component="post/flagUser"]', function () { - var uid = getData($(this), 'data-uid'); + const uid = getData($(this), 'data-uid'); require(['flags'], function (flags) { flags.showFlagModal({ type: 'user', @@ -143,17 +145,17 @@ define('forum/topic/postTools', [ }); postContainer.on('click', '[component="post/flagResolve"]', function () { - var flagId = $(this).attr('data-flagId'); + const flagId = $(this).attr('data-flagId'); require(['flags'], function (flags) { flags.resolve(flagId); }); }); postContainer.on('click', '[component="post/edit"]', function () { - var btn = $(this); + const btn = $(this); - var timestamp = parseInt(getData(btn, 'data-timestamp'), 10); - var postEditDuration = parseInt(ajaxify.data.postEditDuration, 10); + const timestamp = parseInt(getData(btn, 'data-timestamp'), 10); + const postEditDuration = parseInt(ajaxify.data.postEditDuration, 10); if (checkDuration(postEditDuration, timestamp, 'post-edit-duration-expired')) { hooks.fire('action:composer.post.edit', { @@ -164,7 +166,7 @@ define('forum/topic/postTools', [ if (config.enablePostHistory && ajaxify.data.privileges['posts:history']) { postContainer.on('click', '[component="post/view-history"], [component="post/edit-indicator"]', function () { - var btn = $(this); + const btn = $(this); require(['forum/topic/diffs'], function (diffs) { diffs.open(getData(btn, 'data-pid')); }); @@ -172,9 +174,9 @@ define('forum/topic/postTools', [ } postContainer.on('click', '[component="post/delete"]', function () { - var btn = $(this); - var timestamp = parseInt(getData(btn, 'data-timestamp'), 10); - var postDeleteDuration = parseInt(ajaxify.data.postDeleteDuration, 10); + const btn = $(this); + const timestamp = parseInt(getData(btn, 'data-timestamp'), 10); + const postDeleteDuration = parseInt(ajaxify.data.postDeleteDuration, 10); if (checkDuration(postDeleteDuration, timestamp, 'post-delete-duration-expired')) { togglePostDelete($(this)); } @@ -182,11 +184,11 @@ define('forum/topic/postTools', [ function checkDuration(duration, postTimestamp, languageKey) { if (!ajaxify.data.privileges.isAdminOrMod && duration && Date.now() - postTimestamp > duration * 1000) { - var numDays = Math.floor(duration / 86400); - var numHours = Math.floor((duration % 86400) / 3600); - var numMinutes = Math.floor(((duration % 86400) % 3600) / 60); - var numSeconds = ((duration % 86400) % 3600) % 60; - var msg = '[[error:' + languageKey + ', ' + duration + ']]'; + const numDays = Math.floor(duration / 86400); + const numHours = Math.floor((duration % 86400) / 3600); + const numMinutes = Math.floor(((duration % 86400) % 3600) / 60); + const numSeconds = ((duration % 86400) % 3600) % 60; + let msg = '[[error:' + languageKey + ', ' + duration + ']]'; if (numDays) { if (numHours) { msg = '[[error:' + languageKey + '-days-hours, ' + numDays + ', ' + numHours + ']]'; @@ -221,21 +223,21 @@ define('forum/topic/postTools', [ }); postContainer.on('click', '[component="post/move"]', function () { - var btn = $(this); + const btn = $(this); require(['forum/topic/move-post'], function (movePost) { movePost.init(btn.parents('[data-pid]')); }); }); postContainer.on('click', '[component="post/change-owner"]', function () { - var btn = $(this); + const btn = $(this); require(['forum/topic/change-owner'], function (changeOwner) { changeOwner.init(btn.parents('[data-pid]')); }); }); postContainer.on('click', '[component="post/ban-ip"]', function () { - var ip = $(this).attr('data-ip'); + const ip = $(this).attr('data-ip'); socket.emit('blacklist.addRule', ip, function (err) { if (err) { return app.alertError(err.message); @@ -250,16 +252,16 @@ define('forum/topic/postTools', [ } function onReplyClicked(button, tid) { - var selectedNode = getSelectedNode(); + const selectedNode = getSelectedNode(); showStaleWarning(function () { - var username = getUserSlug(button); + let username = getUserSlug(button); if (getData(button, 'data-uid') === '0' || !getData(button, 'data-userslug')) { username = ''; } - var toPid = button.is('[component="post/reply"]') ? getData(button, 'data-pid') : null; - var isQuoteToPid = !toPid || !selectedNode.pid || toPid === selectedNode.pid; + const toPid = button.is('[component="post/reply"]') ? getData(button, 'data-pid') : null; + const isQuoteToPid = !toPid || !selectedNode.pid || toPid === selectedNode.pid; if (selectedNode.text && isQuoteToPid) { username = username || selectedNode.username; @@ -283,11 +285,11 @@ define('forum/topic/postTools', [ } function onQuoteClicked(button, tid) { - var selectedNode = getSelectedNode(); + const selectedNode = getSelectedNode(); showStaleWarning(function () { - var username = getUserSlug(button); - var toPid = getData(button, 'data-pid'); + const username = getUserSlug(button); + const toPid = getData(button, 'data-pid'); function quote(text) { hooks.fire('action:composer.addQuote', { @@ -313,12 +315,12 @@ define('forum/topic/postTools', [ } function getSelectedNode() { - var selectedText = ''; - var selectedPid; - var username = ''; - var selection = window.getSelection ? window.getSelection() : document.selection.createRange(); - var postContents = $('[component="post"] [component="post/content"]'); - var content; + let selectedText = ''; + let selectedPid; + let username = ''; + const selection = window.getSelection ? window.getSelection() : document.selection.createRange(); + const postContents = $('[component="post"] [component="post/content"]'); + let content; postContents.each(function (index, el) { if (selection && selection.containsNode && el && selection.containsNode(el, true)) { content = el; @@ -326,9 +328,9 @@ define('forum/topic/postTools', [ }); if (content) { - var bounds = document.createRange(); + const bounds = document.createRange(); bounds.selectNodeContents(content); - var range = selection.getRangeAt(0).cloneRange(); + const range = selection.getRangeAt(0).cloneRange(); if (range.compareBoundaryPoints(Range.START_TO_START, bounds) < 0) { range.setStart(bounds.startContainer, bounds.startOffset); } @@ -337,7 +339,7 @@ define('forum/topic/postTools', [ } bounds.detach(); selectedText = range.toString(); - var postEl = $(content).parents('[component="post"]'); + const postEl = $(content).parents('[component="post"]'); selectedPid = postEl.attr('data-pid'); username = getUserSlug($(content)); range.detach(); @@ -346,13 +348,13 @@ define('forum/topic/postTools', [ } function bookmarkPost(button, pid) { - var method = button.attr('data-bookmarked') === 'false' ? 'put' : 'del'; + const method = button.attr('data-bookmarked') === 'false' ? 'put' : 'del'; api[method](`/posts/${pid}/bookmark`, undefined, function (err) { if (err) { return app.alertError(err); } - var type = method === 'put' ? 'bookmark' : 'unbookmark'; + const type = method === 'put' ? 'bookmark' : 'unbookmark'; hooks.fire(`action:post.${type}`, { pid: pid }); }); return false; @@ -363,8 +365,8 @@ define('forum/topic/postTools', [ } function getUserSlug(button) { - var slug = ''; - var post = button.parents('[data-pid]'); + let slug = ''; + const post = button.parents('[data-pid]'); if (button.attr('component') === 'topic/reply') { return slug; @@ -388,9 +390,9 @@ define('forum/topic/postTools', [ } function togglePostDelete(button) { - var pid = getData(button, 'data-pid'); - var postEl = components.get('post', 'pid', pid); - var action = !postEl.hasClass('deleted') ? 'delete' : 'restore'; + const pid = getData(button, 'data-pid'); + const postEl = components.get('post', 'pid', pid); + const action = !postEl.hasClass('deleted') ? 'delete' : 'restore'; postAction(action, pid); } @@ -399,7 +401,12 @@ define('forum/topic/postTools', [ postAction('purge', getData(button, 'data-pid')); } - function postAction(action, pid) { + async function postAction(action, pid) { + ({ action } = await hooks.fire(`static:post.${action}`, { action, pid })); + if (!action) { + return; + } + bootbox.confirm('[[topic:post_' + action + '_confirm]]', function (confirm) { if (!confirm) { return; @@ -412,20 +419,21 @@ define('forum/topic/postTools', [ } function openChat(button) { - var post = button.parents('[data-pid]'); - - app.newChat(post.attr('data-uid')); + const post = button.parents('[data-pid]'); + require(['chat'], function (chat) { + chat.newChat(post.attr('data-uid')); + }); button.parents('.btn-group').find('.dropdown-toggle').click(); return false; } function showStaleWarning(callback) { - var staleThreshold = Math.min(Date.now() - (1000 * 60 * 60 * 24 * ajaxify.data.topicStaleDays), 8640000000000000); + const staleThreshold = Math.min(Date.now() - (1000 * 60 * 60 * 24 * ajaxify.data.topicStaleDays), 8640000000000000); if (staleReplyAnyway || ajaxify.data.lastposttime >= staleThreshold) { return callback(); } - var warning = bootbox.dialog({ + const warning = bootbox.dialog({ title: '[[topic:stale.title]]', message: '[[topic:stale.warning]]', buttons: { @@ -456,5 +464,63 @@ define('forum/topic/postTools', [ warning.modal(); } + function handleSelectionTooltip() { + hooks.onPage('action:posts.loaded', delayedTooltip); + + $(document).off('mouseup', delayedTooltip).on('mouseup', delayedTooltip); + $(document).off('selectionchange', selectionChange).on('selectionchange', selectionChange); + } + + let selectionEmpty = true; + function selectionChange() { + selectionEmpty = window.getSelection().toString() === ''; + if (selectionEmpty) { + $('[component="selection/tooltip"]').addClass('hidden'); + } + } + + function delayedTooltip() { + setTimeout(async function () { + let selectionTooltip = $('[component="selection/tooltip"]'); + selectionTooltip.addClass('hidden'); + const selection = window.getSelection(); + if (selection.focusNode && selection.type === 'Range' && ajaxify.data.template.topic && !selectionEmpty) { + const focusNode = $(selection.focusNode); + const anchorNode = $(selection.anchorNode); + const firstPid = anchorNode.parents('[data-pid]').attr('data-pid'); + const lastPid = focusNode.parents('[data-pid]').attr('data-pid'); + if (firstPid !== lastPid || !focusNode.parents('[component="post/content"]').length || !anchorNode.parents('[component="post/content"]').length) { + return; + } + const postEl = focusNode.parents('[data-pid]'); + const selectionRange = selection.getRangeAt(0); + if (!postEl.length || selectionRange.collapsed) { + return; + } + const rects = selectionRange.getClientRects(); + const lastRect = rects[rects.length - 1]; + + if (!selectionTooltip.length) { + selectionTooltip = await app.parseAndTranslate('partials/topic/selection-tooltip', ajaxify.data); + selectionTooltip.addClass('hidden').appendTo('body'); + } + selectionTooltip.off('click').on('click', '[component="selection/tooltip/quote"]', function () { + selectionTooltip.addClass('hidden'); + onQuoteClicked(postEl.find('[component="post/quote"]'), ajaxify.data.tid); + }); + selectionTooltip.removeClass('hidden'); + $(window).one('action:ajaxify.start', function () { + selectionTooltip.remove(); + $(document).off('selectionchange', selectionChange); + }); + const tooltipWidth = selectionTooltip.outerWidth(true); + selectionTooltip.css({ + top: lastRect.bottom + $(window).scrollTop(), + left: tooltipWidth > lastRect.width ? lastRect.left : lastRect.left + lastRect.width - tooltipWidth, + }); + } + }, 0); + } + return PostTools; }); diff --git a/public/src/client/topic/posts.js b/public/src/client/topic/posts.js index 7545e2199e..498604840a 100644 --- a/public/src/client/topic/posts.js +++ b/public/src/client/topic/posts.js @@ -12,7 +12,7 @@ define('forum/topic/posts', [ 'hooks', 'helpers', ], function (pagination, infinitescroll, postTools, images, navigator, components, translator, hooks, helpers) { - var Posts = { }; + const Posts = { }; Posts.onNewPost = function (data) { if ( @@ -68,8 +68,8 @@ define('forum/topic/posts', [ }; function updatePostCounts(posts) { - for (var i = 0; i < posts.length; i += 1) { - var cmp = components.get('user/postcount', posts[i].uid); + for (let i = 0; i < posts.length; i += 1) { + const cmp = components.get('user/postcount', posts[i].uid); cmp.html(parseInt(cmp.attr('data-postcount'), 10) + 1); utils.addCommasToNumbers(cmp); } @@ -79,7 +79,7 @@ define('forum/topic/posts', [ if (config.topicPostSort === 'newest_to_oldest') { posts[0].index = 1; components.get('post').not('[data-index=0]').each(function () { - var newIndex = parseInt($(this).attr('data-index'), 10) + 1; + const newIndex = parseInt($(this).attr('data-index'), 10) + 1; $(this).attr('data-index', newIndex); }); } @@ -90,18 +90,18 @@ define('forum/topic/posts', [ scrollToPostIfSelf(data.posts[0]); } - var posts = data.posts; + const posts = data.posts; ajaxify.data.pagination.pageCount = Math.max(1, Math.ceil(posts[0].topic.postcount / config.postsPerPage)); - var direction = config.topicPostSort === 'oldest_to_newest' || config.topicPostSort === 'most_votes' ? 1 : -1; + const direction = config.topicPostSort === 'oldest_to_newest' || config.topicPostSort === 'most_votes' ? 1 : -1; - var isPostVisible = ( + const isPostVisible = ( ajaxify.data.pagination.currentPage === ajaxify.data.pagination.pageCount && direction === 1 ) || (ajaxify.data.pagination.currentPage === 1 && direction === -1); if (isPostVisible) { - var repliesSelector = $('[component="post"]:not([data-index=0]), [component="topic/event"]'); + const repliesSelector = $('[component="post"]:not([data-index=0]), [component="topic/event"]'); createNewPosts(data, repliesSelector, direction, false, scrollToPost); } else if (ajaxify.data.scrollToMyPost && parseInt(posts[0].uid, 10) === parseInt(app.user.uid, 10)) { // https://github.com/NodeBB/NodeBB/issues/5004#issuecomment-247157441 @@ -122,9 +122,9 @@ define('forum/topic/posts', [ } function onNewPostInfiniteScroll(data) { - var direction = (config.topicPostSort === 'oldest_to_newest' || config.topicPostSort === 'most_votes') ? 1 : -1; + const direction = (config.topicPostSort === 'oldest_to_newest' || config.topicPostSort === 'most_votes') ? 1 : -1; - var isPreviousPostAdded = $('[component="post"][data-index="' + (data.posts[0].index - 1) + '"]').length; + const isPreviousPostAdded = $('[component="post"][data-index="' + (data.posts[0].index - 1) + '"]').length; if (!isPreviousPostAdded && (!data.posts[0].selfPost || !ajaxify.data.scrollToMyPost)) { return; } @@ -132,7 +132,7 @@ define('forum/topic/posts', [ if (!isPreviousPostAdded && data.posts[0].selfPost) { return ajaxify.go('post/' + data.posts[0].pid); } - var repliesSelector = $('[component="post"]:not([data-index=0]), [component="topic/event"]'); + const repliesSelector = $('[component="topic"]>[component="post"]:not([data-index=0]), [component="topic"]>[component="topic/event"]'); createNewPosts(data, repliesSelector, direction, false, function (html) { if (html) { html.addClass('new'); @@ -154,10 +154,10 @@ define('forum/topic/posts', [ } function removeAlreadyAddedPosts() { - var newPosts = $('[component="post"].new'); + const newPosts = $('[component="post"].new'); if (newPosts.length === data.posts.length) { - var allSamePids = true; + let allSamePids = true; newPosts.each(function (index, el) { if (parseInt($(el).attr('data-pid'), 10) !== parseInt(data.posts[index].pid, 10)) { allSamePids = false; @@ -175,7 +175,7 @@ define('forum/topic/posts', [ if (newPosts.length && data.posts.length > 1) { data.posts.forEach(function (post) { - var p = components.get('post', 'pid', post.pid); + const p = components.get('post', 'pid', post.pid); if (p.hasClass('new')) { p.remove(); } @@ -193,8 +193,8 @@ define('forum/topic/posts', [ return callback(); } - var after; - var before; + let after; + let before; if (direction > 0 && repliesSelector.length) { after = repliesSelector.last(); @@ -206,16 +206,17 @@ define('forum/topic/posts', [ app.parseAndTranslate('topic', 'posts', Object.assign({}, ajaxify.data, data), function (html) { html = html.filter(function () { - var pid = $(this).attr('data-pid'); - return pid && $('[component="post"][data-pid="' + pid + '"]').length === 0; + const pid = $(this).attr('data-pid'); + const isPost = $(this).is('[component="post"]'); + return !isPost || (pid && $('[component="post"][data-pid="' + pid + '"]').length === 0); }); if (after) { html.insertAfter(after); } else if (before) { // Save document height and position for future reference (about 5 lines down) - var height = $(document).height(); - var scrollTop = $(window).scrollTop(); + const height = $(document).height(); + const scrollTop = $(window).scrollTop(); html.insertBefore(before); @@ -242,16 +243,16 @@ define('forum/topic/posts', [ return; } - var replies = components.get('topic').find(components.get('post').not('[data-index=0]').not('.new')); - var afterEl = direction > 0 ? replies.last() : replies.first(); - var after = parseInt(afterEl.attr('data-index'), 10) || 0; + const replies = components.get('topic').find(components.get('post').not('[data-index=0]').not('.new')); + const afterEl = direction > 0 ? replies.last() : replies.first(); + const after = parseInt(afterEl.attr('data-index'), 10) || 0; - var tid = ajaxify.data.tid; + const tid = ajaxify.data.tid; if (!utils.isNumber(tid) || !utils.isNumber(after) || (direction < 0 && components.get('post', 'index', 0).length)) { return; } - var indicatorEl = $('.loading-indicator'); + const indicatorEl = $('.loading-indicator'); if (!indicatorEl.is(':animated')) { indicatorEl.fadeIn(); } @@ -266,7 +267,7 @@ define('forum/topic/posts', [ indicatorEl.fadeOut(); if (data && data.posts && data.posts.length) { - var repliesSelector = $('[component="post"]:not([data-index=0]):not(.new), [component="topic/event"]'); + const repliesSelector = $('[component="post"]:not([data-index=0]):not(.new), [component="topic/event"]'); createNewPosts(data, repliesSelector, direction, true, done); } else { navigator.update(); @@ -294,15 +295,15 @@ define('forum/topic/posts', [ }; function addNecroPostMessage(callback) { - var necroThreshold = ajaxify.data.necroThreshold * 24 * 60 * 60 * 1000; + const necroThreshold = ajaxify.data.necroThreshold * 24 * 60 * 60 * 1000; if (!necroThreshold || (config.topicPostSort !== 'newest_to_oldest' && config.topicPostSort !== 'oldest_to_newest')) { return callback && callback(); } - var postEls = $('[component="post"]').toArray(); + const postEls = $('[component="post"]').toArray(); Promise.all(postEls.map(function (post) { post = $(post); - var prev = post.prev('[component="post"]'); + const prev = post.prev('[component="post"]'); if (post.is(':has(.necro-post)') || !prev.length) { return; } @@ -310,20 +311,20 @@ define('forum/topic/posts', [ return; } - var diff = post.attr('data-timestamp') - prev.attr('data-timestamp'); + const diff = post.attr('data-timestamp') - prev.attr('data-timestamp'); return new Promise(function (resolve) { if (Math.abs(diff) >= necroThreshold) { - var suffixAgo = $.timeago.settings.strings.suffixAgo; - var prefixAgo = $.timeago.settings.strings.prefixAgo; - var suffixFromNow = $.timeago.settings.strings.suffixFromNow; - var prefixFromNow = $.timeago.settings.strings.prefixFromNow; + const suffixAgo = $.timeago.settings.strings.suffixAgo; + const prefixAgo = $.timeago.settings.strings.prefixAgo; + const suffixFromNow = $.timeago.settings.strings.suffixFromNow; + const prefixFromNow = $.timeago.settings.strings.prefixFromNow; $.timeago.settings.strings.suffixAgo = ''; $.timeago.settings.strings.prefixAgo = ''; $.timeago.settings.strings.suffixFromNow = ''; $.timeago.settings.strings.prefixFromNow = ''; - var translationText = (diff > 0 ? '[[topic:timeago_later,' : '[[topic:timeago_earlier,') + $.timeago.inWords(diff) + ']]'; + const translationText = (diff > 0 ? '[[topic:timeago_later,' : '[[topic:timeago_earlier,') + $.timeago.inWords(diff) + ']]'; $.timeago.settings.strings.suffixAgo = suffixAgo; $.timeago.settings.strings.prefixAgo = prefixAgo; @@ -350,7 +351,7 @@ define('forum/topic/posts', [ } // Replace all requests for uploaded images/files with a login link - var loginEl = document.createElement('a'); + const loginEl = document.createElement('a'); loginEl.className = 'login-required'; loginEl.href = config.relative_path + '/login'; @@ -378,9 +379,9 @@ define('forum/topic/posts', [ }; Posts.showBottomPostBar = function () { - var mainPost = components.get('post', 'index', 0); - var placeHolder = $('.post-bar-placeholder'); - var posts = $('[component="post"]'); + const mainPost = components.get('post', 'index', 0); + const placeHolder = $('.post-bar-placeholder'); + const posts = $('[component="post"]'); if (!!mainPost.length && posts.length > 1 && $('.post-bar').length < 2 && placeHolder.length) { $('.post-bar').clone().insertAfter(placeHolder); placeHolder.remove(); @@ -398,9 +399,9 @@ define('forum/topic/posts', [ } Posts.addBlockquoteEllipses = function (posts) { - var blockquotes = posts.find('[component="post/content"] > blockquote > blockquote'); + const blockquotes = posts.find('[component="post/content"] > blockquote > blockquote'); blockquotes.each(function () { - var $this = $(this); + const $this = $(this); if ($this.find(':hidden:not(br)').length && !$this.find('.toggle').length) { $this.append(''); } diff --git a/public/src/client/topic/replies.js b/public/src/client/topic/replies.js index bc017d25f6..51a4064f0b 100644 --- a/public/src/client/topic/replies.js +++ b/public/src/client/topic/replies.js @@ -2,14 +2,14 @@ define('forum/topic/replies', ['navigator', 'components', 'forum/topic/posts', 'hooks'], function (navigator, components, posts, hooks) { - var Replies = {}; + const Replies = {}; Replies.init = function (button) { - var post = button.closest('[data-pid]'); - var pid = post.data('pid'); - var open = button.find('[component="post/replies/open"]'); - var loading = button.find('[component="post/replies/loading"]'); - var close = button.find('[component="post/replies/close"]'); + const post = button.closest('[data-pid]'); + const pid = post.data('pid'); + const open = button.find('[component="post/replies/open"]'); + const loading = button.find('[component="post/replies/loading"]'); + const close = button.find('[component="post/replies/close"]'); if (open.is(':not(.hidden)') && loading.is('.hidden')) { open.addClass('hidden'); @@ -25,7 +25,7 @@ define('forum/topic/replies', ['navigator', 'components', 'forum/topic/posts', ' close.removeClass('hidden'); posts.modifyPostsByPrivileges(data); - var tplData = { + const tplData = { posts: data, privileges: ajaxify.data.privileges, 'downvote:disabled': ajaxify.data['downvote:disabled'], @@ -34,7 +34,7 @@ define('forum/topic/replies', ['navigator', 'components', 'forum/topic/posts', ' hideReplies: config.hasOwnProperty('showNestedReplies') ? !config.showNestedReplies : true, }; app.parseAndTranslate('topic', 'posts', tplData, function (html) { - var repliesEl = $('
    ', { component: 'post/replies' }).html(html).hide(); + const repliesEl = $('
    ', { component: 'post/replies' }).html(html).hide(); if (button.attr('data-target-component')) { post.find('[component="' + button.attr('data-target-component') + '"]').html(repliesEl); } else { @@ -57,14 +57,14 @@ define('forum/topic/replies', ['navigator', 'components', 'forum/topic/posts', ' }; Replies.onNewPost = function (data) { - var post = data.posts[0]; + const post = data.posts[0]; if (!post) { return; } incrementCount(post, 1); data.hideReplies = config.hasOwnProperty('showNestedReplies') ? !config.showNestedReplies : true; app.parseAndTranslate('topic', 'posts', data, function (html) { - var replies = $('[component="post"][data-pid="' + post.toPid + '"] [component="post/replies"]').first(); + const replies = $('[component="post"][data-pid="' + post.toPid + '"] [component="post/replies"]').first(); if (replies.length) { if (config.topicPostSort === 'newest_to_oldest') { replies.prepend(html); @@ -81,11 +81,11 @@ define('forum/topic/replies', ['navigator', 'components', 'forum/topic/posts', ' }; function incrementCount(post, inc) { - var replyCount = $('[component="post"][data-pid="' + post.toPid + '"]').find('[component="post/reply-count"]').first(); - var countEl = replyCount.find('[component="post/reply-count/text"]'); - var avatars = replyCount.find('[component="post/reply-count/avatars"]'); - var count = Math.max(0, parseInt(countEl.attr('data-replies'), 10) + inc); - var timestamp = replyCount.find('.timeago').attr('title', post.timestampISO); + const replyCount = $('[component="post"][data-pid="' + post.toPid + '"]').find('[component="post/reply-count"]').first(); + const countEl = replyCount.find('[component="post/reply-count/text"]'); + const avatars = replyCount.find('[component="post/reply-count/avatars"]'); + const count = Math.max(0, parseInt(countEl.attr('data-replies'), 10) + inc); + const timestamp = replyCount.find('.timeago').attr('title', post.timestampISO); countEl.attr('data-replies', count); replyCount.toggleClass('hidden', count <= 0); diff --git a/public/src/client/topic/threadTools.js b/public/src/client/topic/threadTools.js index 0dcf444c2a..e627a51643 100644 --- a/public/src/client/topic/threadTools.js +++ b/public/src/client/topic/threadTools.js @@ -8,8 +8,9 @@ define('forum/topic/threadTools', [ 'forum/topic/posts', 'api', 'hooks', -], function (components, translator, handleBack, posts, api, hooks) { - var ThreadTools = {}; + 'bootbox', +], function (components, translator, handleBack, posts, api, hooks, bootbox) { + const ThreadTools = {}; ThreadTools.init = function (tid, topicContainer) { renderMenu(topicContainer); @@ -85,7 +86,7 @@ define('forum/topic/threadTools', [ }); topicContainer.on('click', '[component="topic/mark-unread-for-all"]', function () { - var btn = $(this); + const btn = $(this); socket.emit('topics.markAsUnreadForAll', [tid], function (err) { if (err) { return app.alertError(err.message); @@ -134,7 +135,7 @@ define('forum/topic/threadTools', [ function changeWatching(type, state = 1) { const method = state ? 'put' : 'del'; api[method](`/topics/${tid}/${type}`, {}, () => { - var message = ''; + let message = ''; if (type === 'follow') { message = state ? '[[topic:following_topic.message]]' : '[[topic:not_following_topic.message]]'; } else if (type === 'ignore') { @@ -172,8 +173,8 @@ define('forum/topic/threadTools', [ function renderMenu(container) { container.on('show.bs.dropdown', '.thread-tools', function () { - var $this = $(this); - var dropdownMenu = $this.find('.dropdown-menu'); + const $this = $(this); + const dropdownMenu = $this.find('.dropdown-menu'); if (dropdownMenu.html()) { return; } @@ -264,17 +265,17 @@ define('forum/topic/threadTools', [ }; ThreadTools.setLockedState = function (data) { - var threadEl = components.get('topic'); + const threadEl = components.get('topic'); if (parseInt(data.tid, 10) !== parseInt(threadEl.attr('data-tid'), 10)) { return; } - var isLocked = data.isLocked && !ajaxify.data.privileges.isAdminOrMod; + const isLocked = data.isLocked && !ajaxify.data.privileges.isAdminOrMod; components.get('topic/lock').toggleClass('hidden', data.isLocked).parent().attr('hidden', data.isLocked ? '' : null); components.get('topic/unlock').toggleClass('hidden', !data.isLocked).parent().attr('hidden', !data.isLocked ? '' : null); - var hideReply = !!((data.isLocked || ajaxify.data.deleted) && !ajaxify.data.privileges.isAdminOrMod); + const hideReply = !!((data.isLocked || ajaxify.data.deleted) && !ajaxify.data.privileges.isAdminOrMod); components.get('topic/reply/container').toggleClass('hidden', hideReply); components.get('topic/reply/locked').toggleClass('hidden', ajaxify.data.privileges.isAdminOrMod || !data.isLocked || ajaxify.data.deleted); @@ -284,7 +285,7 @@ define('forum/topic/threadTools', [ threadEl.find('[component="post"][data-uid="' + app.user.uid + '"].deleted [component="post/tools"]').toggleClass('hidden', isLocked); - $('.topic-header [component="topic/locked"]').toggleClass('hidden', !data.isLocked); + $('[component="topic/labels"] [component="topic/locked"]').toggleClass('hidden', !data.isLocked); $('[component="post/tools"] .dropdown-menu').html(''); ajaxify.data.locked = data.isLocked; @@ -292,7 +293,7 @@ define('forum/topic/threadTools', [ }; ThreadTools.setDeleteState = function (data) { - var threadEl = components.get('topic'); + const threadEl = components.get('topic'); if (parseInt(data.tid, 10) !== parseInt(threadEl.attr('data-tid'), 10)) { return; } @@ -312,7 +313,7 @@ define('forum/topic/threadTools', [ html.find('.timeago').timeago(); }); } - var hideReply = data.isDelete && !ajaxify.data.privileges.isAdminOrMod; + const hideReply = data.isDelete && !ajaxify.data.privileges.isAdminOrMod; components.get('topic/reply/container').toggleClass('hidden', hideReply); components.get('topic/reply/locked').toggleClass('hidden', ajaxify.data.privileges.isAdminOrMod || !ajaxify.data.locked || data.isDelete); @@ -326,14 +327,14 @@ define('forum/topic/threadTools', [ ThreadTools.setPinnedState = function (data) { - var threadEl = components.get('topic'); + const threadEl = components.get('topic'); if (parseInt(data.tid, 10) !== parseInt(threadEl.attr('data-tid'), 10)) { return; } components.get('topic/pin').toggleClass('hidden', data.pinned).parent().attr('hidden', data.pinned ? '' : null); components.get('topic/unpin').toggleClass('hidden', !data.pinned).parent().attr('hidden', !data.pinned ? '' : null); - var icon = $('.topic-header [component="topic/pinned"]'); + const icon = $('[component="topic/labels"] [component="topic/pinned"]'); icon.toggleClass('hidden', !data.pinned); if (data.pinned) { icon.translateAttr('title', ( @@ -348,7 +349,7 @@ define('forum/topic/threadTools', [ }; function setFollowState(state) { - var titles = { + const titles = { follow: '[[topic:watching]]', unfollow: '[[topic:not-watching]]', ignore: '[[topic:ignoring]]', @@ -359,7 +360,7 @@ define('forum/topic/threadTools', [ .tooltip('fixTitle'); }); - var menu = components.get('topic/following/menu'); + let menu = components.get('topic/following/menu'); menu.toggleClass('hidden', state !== 'follow'); components.get('topic/following/check').toggleClass('fa-check', state === 'follow'); diff --git a/public/src/client/topic/votes.js b/public/src/client/topic/votes.js index 797d27dee4..8c895e6ccb 100644 --- a/public/src/client/topic/votes.js +++ b/public/src/client/topic/votes.js @@ -2,9 +2,9 @@ define('forum/topic/votes', [ - 'components', 'translator', 'benchpress', 'api', 'hooks', -], function (components, translator, Benchpress, api, hooks) { - var Votes = {}; + 'components', 'translator', 'benchpress', 'api', 'hooks', 'bootbox', +], function (components, translator, Benchpress, api, hooks, bootbox) { + const Votes = {}; Votes.addVoteHandler = function () { components.get('topic').on('mouseenter', '[data-pid] [component="post/vote-count"]', loadDataAndCreateTooltip); @@ -13,10 +13,10 @@ define('forum/topic/votes', [ function loadDataAndCreateTooltip(e) { e.stopPropagation(); - var $this = $(this); - var el = $this.parent(); + const $this = $(this); + const el = $this.parent(); el.find('.tooltip').css('display', 'none'); - var pid = el.parents('[data-pid]').attr('data-pid'); + const pid = el.parents('[data-pid]').attr('data-pid'); socket.emit('posts.getUpvoters', [pid], function (err, data) { if (err) { @@ -35,7 +35,7 @@ define('forum/topic/votes', [ el.attr('title', title).tooltip('fixTitle').tooltip('show'); el.parent().find('.tooltip').css('display', ''); } - var usernames = data.usernames + let usernames = data.usernames .filter(name => name !== '[[global:former_user]]'); if (!usernames.length) { return; @@ -54,11 +54,11 @@ define('forum/topic/votes', [ Votes.toggleVote = function (button, className, delta) { - var post = button.closest('[data-pid]'); - var currentState = post.find(className).length; + const post = button.closest('[data-pid]'); + const currentState = post.find(className).length; const method = currentState ? 'del' : 'put'; - var pid = post.attr('data-pid'); + const pid = post.attr('data-pid'); api[method](`/posts/${pid}/vote`, { delta: delta, }, function (err) { @@ -92,7 +92,7 @@ define('forum/topic/votes', [ } app.parseAndTranslate('partials/modals/votes_modal', data, function (html) { - var dialog = bootbox.dialog({ + const dialog = bootbox.dialog({ title: '[[global:voters]]', message: html, className: 'vote-modal', diff --git a/public/src/client/unread.js b/public/src/client/unread.js index 6af50e0472..43983abbac 100644 --- a/public/src/client/unread.js +++ b/public/src/client/unread.js @@ -4,9 +4,9 @@ define('forum/unread', [ 'topicSelect', 'components', 'topicList', 'categorySelector', ], function (topicSelect, components, topicList, categorySelector) { - var Unread = {}; + const Unread = {}; - var watchStates = { + const watchStates = { ignoring: 1, notwatching: 2, watching: 3, @@ -39,7 +39,7 @@ define('forum/unread', [ } function markSelectedRead() { - var tids = topicSelect.getSelectedTids(); + const tids = topicSelect.getSelectedTids(); if (!tids.length) { return; } @@ -54,13 +54,13 @@ define('forum/unread', [ function markCategoryRead(cid) { function getCategoryTids(cid) { - var tids = []; + const tids = []; components.get('category/topic', 'cid', cid).each(function () { tids.push($(this).attr('data-tid')); }); return tids; } - var tids = getCategoryTids(cid); + const tids = getCategoryTids(cid); socket.emit('topics.markCategoryTopicsRead', cid, function (err) { if (err) { @@ -71,7 +71,7 @@ define('forum/unread', [ }); } - var selector = categorySelector.init($('[component="category-selector"]'), { + const selector = categorySelector.init($('[component="category-selector"]'), { onSelect: function (category) { selector.selectCategory(0); if (category.cid === 'all') { @@ -110,7 +110,7 @@ define('forum/unread', [ } function removeTids(tids) { - for (var i = 0; i < tids.length; i += 1) { + for (let i = 0; i < tids.length; i += 1) { components.get('category/topic', 'tid', tids[i]).remove(); } } @@ -126,18 +126,18 @@ define('forum/unread', [ } Unread.initUnreadTopics = function () { - var unreadTopics = app.user.unreadData; + const unreadTopics = app.user.unreadData; function onNewPost(data) { if (data && data.posts && data.posts.length && unreadTopics) { - var post = data.posts[0]; + const post = data.posts[0]; if (parseInt(post.uid, 10) === parseInt(app.user.uid, 10) || (!post.topic.isFollowing && post.categoryWatchState !== watchStates.watching) ) { return; } - var tid = post.topic.tid; + const tid = post.topic.tid; if (!unreadTopics[''][tid] || !unreadTopics.new[tid] || !unreadTopics.watched[tid] || !unreadTopics.unreplied[tid]) { markTopicsUnread(tid); @@ -147,12 +147,12 @@ define('forum/unread', [ increaseUnreadCount(''); unreadTopics[''][tid] = true; } - var isNewTopic = post.isMain && parseInt(post.uid, 10) !== parseInt(app.user.uid, 10); + const isNewTopic = post.isMain && parseInt(post.uid, 10) !== parseInt(app.user.uid, 10); if (isNewTopic && !unreadTopics.new[tid]) { increaseUnreadCount('new'); unreadTopics.new[tid] = true; } - var isUnreplied = parseInt(post.topic.postcount, 10) <= 1; + const isUnreplied = parseInt(post.topic.postcount, 10) <= 1; if (isUnreplied && !unreadTopics.unreplied[tid]) { increaseUnreadCount('unreplied'); unreadTopics.unreplied[tid] = true; @@ -166,8 +166,8 @@ define('forum/unread', [ } function increaseUnreadCount(filter) { - var unreadUrl = '/unread' + (filter ? '?filter=' + filter : ''); - var newCount = 1 + parseInt($('a[href="' + config.relative_path + unreadUrl + '"].navigation-link i').attr('data-content'), 10); + const unreadUrl = '/unread' + (filter ? '?filter=' + filter : ''); + const newCount = 1 + parseInt($('a[href="' + config.relative_path + unreadUrl + '"].navigation-link i').attr('data-content'), 10); updateUnreadTopicCount(unreadUrl, newCount); } diff --git a/public/src/client/users.js b/public/src/client/users.js index 50ecab2ac1..2b732c7632 100644 --- a/public/src/client/users.js +++ b/public/src/client/users.js @@ -4,22 +4,14 @@ define('forum/users', [ 'translator', 'benchpress', 'api', 'accounts/invite', ], function (translator, Benchpress, api, AccountInvite) { - var Users = {}; + const Users = {}; - var searchTimeoutID = 0; - var searchResultCount = 0; - - $(window).on('action:ajaxify.start', function () { - if (searchTimeoutID) { - clearTimeout(searchTimeoutID); - searchTimeoutID = 0; - } - }); + let searchResultCount = 0; Users.init = function () { app.enterRoom('user_list'); - var section = utils.params().section ? ('?section=' + utils.params().section) : ''; + const section = utils.params().section ? ('?section=' + utils.params().section) : ''; $('.nav-pills li').removeClass('active').find('a[href="' + window.location.pathname + section + '"]').parent() .addClass('active'); @@ -33,28 +25,19 @@ define('forum/users', [ Users.handleSearch = function (params) { searchResultCount = params && params.resultCount; - searchTimeoutID = 0; - - $('#search-user').on('keyup', function () { - if (searchTimeoutID) { - clearTimeout(searchTimeoutID); - searchTimeoutID = 0; - } - - searchTimeoutID = setTimeout(doSearch, 250); - }); - - $('.search select, .search input[type="checkbox"]').on('change', function () { - doSearch(); - }); + $('#search-user').on('keyup', utils.debounce(doSearch, 250)); + $('.search select, .search input[type="checkbox"]').on('change', doSearch); }; function doSearch() { + if (!ajaxify.data.template.users) { + return; + } $('[component="user/search/icon"]').removeClass('fa-search').addClass('fa-spinner fa-spin'); - var username = $('#search-user').val(); - var activeSection = getActiveSection(); + const username = $('#search-user').val(); + const activeSection = getActiveSection(); - var query = { + const query = { section: activeSection, page: 1, }; @@ -65,7 +48,7 @@ define('forum/users', [ query.query = username; query.sortBy = getSortBy(); - var filters = []; + const filters = []; if ($('.search .online-only').is(':checked') || (activeSection === 'online')) { filters.push('online'); } @@ -83,8 +66,8 @@ define('forum/users', [ } function getSortBy() { - var sortBy; - var activeSection = getActiveSection(); + let sortBy; + const activeSection = getActiveSection(); if (activeSection === 'sort-posts') { sortBy = 'postcount'; } else if (activeSection === 'sort-reputation') { @@ -120,7 +103,7 @@ define('forum/users', [ } function onUserStatusChange(data) { - var section = getActiveSection(); + const section = getActiveSection(); if ((section.startsWith('online') || section.startsWith('users'))) { updateUser(data); diff --git a/public/src/installer/install.js b/public/src/installer/install.js index a4ac88a763..48b1d1eba7 100644 --- a/public/src/installer/install.js +++ b/public/src/installer/install.js @@ -25,12 +25,12 @@ $('document').ready(function () { function setupInputs() { $('form').on('focus', '.form-control', function () { - var parent = $(this).parents('.input-row'); + const parent = $(this).parents('.input-row'); $('.input-row.active').removeClass('active'); parent.addClass('active').removeClass('error'); - var help = parent.find('.help-text'); + const help = parent.find('.help-text'); help.html(help.attr('data-help')); }); @@ -56,9 +56,9 @@ $('document').ready(function () { } function activate(type, el) { - var field = el.val(); - var parent = el.parents('.input-row'); - var help = parent.children('.help-text'); + const field = el.val(); + const parent = el.parents('.input-row'); + const help = parent.children('.help-text'); function validateUsername(field) { if (!utils.isUserNameValid(field) || !slugify(field)) { @@ -123,8 +123,8 @@ $('document').ready(function () { function launchForum() { $('#launch .working').removeClass('hide'); $.post('/launch', function () { - var successCount = 0; - var url = $('#launch').attr('data-url'); + let successCount = 0; + const url = $('#launch').attr('data-url'); setInterval(function () { $.get(url + '/admin').done(function () { if (successCount >= 5) { diff --git a/public/src/modules/accounts/delete.js b/public/src/modules/accounts/delete.js index 1b79634de0..cff044cfae 100644 --- a/public/src/modules/accounts/delete.js +++ b/public/src/modules/accounts/delete.js @@ -1,7 +1,7 @@ 'use strict'; -define('accounts/delete', ['api', 'bootbox'], function (api) { - var Delete = {}; +define('accounts/delete', ['api', 'bootbox'], function (api, bootbox) { + const Delete = {}; Delete.account = function (uid, callback) { executeAction( diff --git a/public/src/modules/accounts/invite.js b/public/src/modules/accounts/invite.js index 621c84bcba..937ab6401f 100644 --- a/public/src/modules/accounts/invite.js +++ b/public/src/modules/accounts/invite.js @@ -36,10 +36,10 @@ define('accounts/invite', ['api', 'benchpress', 'bootbox'], function (api, Bench }; Invite.send = function () { - var $emails = $('#invite-modal-emails'); - var $groups = $('#invite-modal-groups'); + const $emails = $('#invite-modal-emails'); + const $groups = $('#invite-modal-groups'); - var data = { + const data = { emails: $emails.val() .split(',') .map(m => m.trim()) diff --git a/public/src/modules/accounts/picture.js b/public/src/modules/accounts/picture.js index a97e0fb891..7b62c96867 100644 --- a/public/src/modules/accounts/picture.js +++ b/public/src/modules/accounts/picture.js @@ -2,7 +2,9 @@ define('accounts/picture', [ 'pictureCropper', -], (pictureCropper) => { + 'api', + 'bootbox', +], (pictureCropper, api, bootbox) => { const Picture = {}; Picture.openChangeModal = () => { @@ -14,7 +16,7 @@ define('accounts/picture', [ } // boolean to signify whether an uploaded picture is present in the pictures list - var uploaded = pictures.reduce(function (memo, cur) { + const uploaded = pictures.reduce(function (memo, cur) { return memo || cur.type === 'uploaded'; }, false); @@ -33,7 +35,7 @@ define('accounts/picture', [ 'icon:bgColor': ajaxify.data['icon:bgColor'], }, }, function (html) { - var modal = bootbox.dialog({ + const modal = bootbox.dialog({ className: 'picture-switcher', title: '[[user:change_picture]]', message: html, @@ -86,17 +88,13 @@ define('accounts/picture', [ } function saveSelection() { - var type = modal.find('.list-group-item.active').attr('data-type'); + const type = modal.find('.list-group-item.active').attr('data-type'); const iconBgColor = document.querySelector('.modal.picture-switcher input[type="radio"]:checked').value || 'transparent'; - changeUserPicture(type, iconBgColor, function (err) { - if (err) { - return app.alertError(err.message); - } - + changeUserPicture(type, iconBgColor).then(() => { Picture.updateHeader(type === 'default' ? '' : modal.find('.list-group-item.active img').attr('src'), iconBgColor); ajaxify.refresh(); - }); + }).catch(app.alertError); } function onCloseModal() { @@ -176,7 +174,7 @@ define('accounts/picture', [ uploadModal.modal('show'); uploadModal.find('.upload-btn').on('click', function () { - var url = uploadModal.find('#uploadFromUrl').val(); + const url = uploadModal.find('#uploadFromUrl').val(); if (!url) { return false; } @@ -212,12 +210,8 @@ define('accounts/picture', [ }); } - function changeUserPicture(type, bgColor, callback) { - socket.emit('user.changePicture', { - type, - bgColor, - uid: ajaxify.data.theirid, - }, callback); + function changeUserPicture(type, bgColor) { + return api.put(`/users/${ajaxify.data.theirid}/picture`, { type, bgColor }); } return Picture; diff --git a/public/src/modules/alerts.js b/public/src/modules/alerts.js index d9cffeb114..da0fc006e7 100644 --- a/public/src/modules/alerts.js +++ b/public/src/modules/alerts.js @@ -2,7 +2,7 @@ define('alerts', ['translator', 'components', 'hooks'], function (translator, components, hooks) { - var module = {}; + const module = {}; module.alert = function (params) { params.alert_id = 'alert_button_' + (params.alert_id ? params.alert_id : new Date().getTime()); @@ -10,7 +10,7 @@ define('alerts', ['translator', 'components', 'hooks'], function (translator, co params.message = params.message ? params.message.trim() : ''; params.type = params.type || 'info'; - var alert = $('#' + params.alert_id); + const alert = $('#' + params.alert_id); if (alert.length) { updateAlert(alert, params); } else { @@ -18,9 +18,41 @@ define('alerts', ['translator', 'components', 'hooks'], function (translator, co } }; + module.success = function (message, timeout) { + module.alert({ + alert_id: utils.generateUUID(), + title: '[[global:alert.success]]', + message: message, + type: 'success', + timeout: timeout || 5000, + }); + }; + + module.error = function (message, timeout) { + message = (message && message.message) || message; + + if (message === '[[error:revalidate-failure]]') { + socket.disconnect(); + app.reconnect(); + return; + } + + module.alert({ + alert_id: utils.generateUUID(), + title: '[[global:alert.error]]', + message: message, + type: 'danger', + timeout: timeout || 10000, + }); + }; + + module.remove = function (id) { + $('#alert_button_' + id).remove(); + }; + function createNew(params) { app.parseAndTranslate('alert', params, function (html) { - var alert = $('#' + params.alert_id); + let alert = $('#' + params.alert_id); if (alert.length) { return updateAlert(alert, params); } @@ -56,10 +88,6 @@ define('alerts', ['translator', 'components', 'hooks'], function (translator, co }); } - module.remove = function (id) { - $('#alert_button_' + id).remove(); - }; - function updateAlert(alert, params) { alert.find('strong').html(params.title); alert.find('p').html(params.message); @@ -98,9 +126,9 @@ define('alerts', ['translator', 'components', 'hooks'], function (translator, co } function startTimeout(alert, params) { - var timeout = params.timeout; + const timeout = params.timeout; - var timeoutId = setTimeout(function () { + const timeoutId = setTimeout(function () { fadeOut(alert); if (typeof params.timeoutfn === 'function') { diff --git a/public/src/modules/autocomplete.js b/public/src/modules/autocomplete.js index 7aa1d7744e..3fb216edb7 100644 --- a/public/src/modules/autocomplete.js +++ b/public/src/modules/autocomplete.js @@ -3,7 +3,7 @@ define('autocomplete', ['api'], function (api) { - var module = {}; + const module = {}; module.user = function (input, params, onselect) { if (typeof params === 'function') { @@ -29,8 +29,8 @@ define('autocomplete', ['api'], function (api) { } if (result && result.users) { - var names = result.users.map(function (user) { - var username = $('
    ').html(user.username).text(); + const names = result.users.map(function (user) { + const username = $('
    ').html(user.username).text(); return user && { label: username, value: username, @@ -75,7 +75,7 @@ define('autocomplete', ['api'], function (api) { return app.alertError(err.message); } if (results && results.length) { - var names = results.map(function (group) { + const names = results.map(function (group) { return group && { label: group.name, value: group.name, @@ -121,7 +121,7 @@ define('autocomplete', ['api'], function (api) { function handleOnSelect(input, onselect, event, ui) { onselect = onselect || function () { }; - var e = jQuery.Event('keypress'); + const e = jQuery.Event('keypress'); e.which = 13; e.keyCode = 13; setTimeout(function () { diff --git a/public/src/modules/categoryFilter.js b/public/src/modules/categoryFilter.js index 25b9a80a96..b1593a5dec 100644 --- a/public/src/modules/categoryFilter.js +++ b/public/src/modules/categoryFilter.js @@ -1,7 +1,7 @@ 'use strict'; define('categoryFilter', ['categorySearch', 'api', 'hooks'], function (categorySearch, api, hooks) { - var categoryFilter = {}; + const categoryFilter = {}; categoryFilter.init = function (el, options) { if (!el || !el.length) { @@ -15,8 +15,8 @@ define('categoryFilter', ['categorySearch', 'api', 'hooks'], function (categoryS categorySearch.init(el, options); - var selectedCids = []; - var initialCids = []; + let selectedCids = []; + let initialCids = []; if (Array.isArray(options.selectedCids)) { selectedCids = options.selectedCids.map(cid => parseInt(cid, 10)); } else if (Array.isArray(ajaxify.data.selectedCids)) { @@ -25,7 +25,7 @@ define('categoryFilter', ['categorySearch', 'api', 'hooks'], function (categoryS initialCids = selectedCids.slice(); el.on('hidden.bs.dropdown', function () { - var changed = initialCids.length !== selectedCids.length; + let changed = initialCids.length !== selectedCids.length; initialCids.forEach(function (cid, index) { if (cid !== selectedCids[index]) { changed = true; @@ -39,8 +39,8 @@ define('categoryFilter', ['categorySearch', 'api', 'hooks'], function (categoryS return; } if (changed) { - var url = window.location.pathname; - var currentParams = utils.params(); + let url = window.location.pathname; + const currentParams = utils.params(); if (selectedCids.length) { currentParams.cid = selectedCids; url += '?' + decodeURIComponent($.param(currentParams)); @@ -50,14 +50,14 @@ define('categoryFilter', ['categorySearch', 'api', 'hooks'], function (categoryS }); el.on('click', '[component="category/list"] [data-cid]', function () { - var listEl = el.find('[component="category/list"]'); - var categoryEl = $(this); - var link = categoryEl.find('a').attr('href'); + const listEl = el.find('[component="category/list"]'); + const categoryEl = $(this); + const link = categoryEl.find('a').attr('href'); if (link && link !== '#' && link.length) { return; } - var cid = parseInt(categoryEl.attr('data-cid'), 10); - var icon = categoryEl.find('[component="category/select/icon"]'); + const cid = parseInt(categoryEl.attr('data-cid'), 10); + const icon = categoryEl.find('[component="category/select/icon"]'); if (selectedCids.includes(cid)) { selectedCids.splice(selectedCids.indexOf(cid), 1); diff --git a/public/src/modules/categorySearch.js b/public/src/modules/categorySearch.js index cf9a42bad4..f6453da65a 100644 --- a/public/src/modules/categorySearch.js +++ b/public/src/modules/categorySearch.js @@ -1,26 +1,26 @@ 'use strict'; define('categorySearch', function () { - var categorySearch = {}; + const categorySearch = {}; categorySearch.init = function (el, options) { - var categoriesList = null; + let categoriesList = null; options = options || {}; options.privilege = options.privilege || 'topics:read'; options.states = options.states || ['watching', 'notwatching', 'ignoring']; - var localCategories = []; + let localCategories = []; if (Array.isArray(options.localCategories)) { localCategories = options.localCategories.map(c => ({ ...c })); } options.selectedCids = options.selectedCids || ajaxify.data.selectedCids || []; - var searchEl = el.find('[component="category-selector-search"]'); + const searchEl = el.find('[component="category-selector-search"]'); if (!searchEl.length) { return; } - var toggleVisibility = searchEl.parent('[component="category/dropdown"]').length > 0 || + const toggleVisibility = searchEl.parent('[component="category/dropdown"]').length > 0 || searchEl.parent('[component="category-selector"]').length > 0; el.on('show.bs.dropdown', function () { @@ -30,7 +30,7 @@ define('categorySearch', function () { } function doSearch() { - var val = searchEl.find('input').val(); + const val = searchEl.find('input').val(); if (val.length > 1 || (!val && !categoriesList)) { loadList(val, function (categories) { categoriesList = categoriesList || categories; diff --git a/public/src/modules/categorySelector.js b/public/src/modules/categorySelector.js index f7de882276..c0a36931a3 100644 --- a/public/src/modules/categorySelector.js +++ b/public/src/modules/categorySelector.js @@ -3,14 +3,14 @@ define('categorySelector', [ 'categorySearch', 'bootbox', 'hooks', ], function (categorySearch, bootbox, hooks) { - var categorySelector = {}; + const categorySelector = {}; categorySelector.init = function (el, options) { if (!el || !el.length) { return; } options = options || {}; - var onSelect = options.onSelect || function () {}; + const onSelect = options.onSelect || function () {}; options.states = options.states || ['watching', 'notwatching', 'ignoring']; options.template = 'partials/category-selector'; @@ -18,21 +18,21 @@ define('categorySelector', [ categorySearch.init(el, options); - var selector = { + const selector = { el: el, selectedCategory: null, }; el.on('click', '[data-cid]', function () { - var categoryEl = $(this); + const categoryEl = $(this); if (categoryEl.hasClass('disabled')) { return false; } selector.selectCategory(categoryEl.attr('data-cid')); onSelect(selector.selectedCategory); }); - + const defaultSelectHtml = selector.el.find('[component="category-selector-selected"]').html(); selector.selectCategory = function (cid) { - var categoryEl = selector.el.find('[data-cid="' + cid + '"]'); + const categoryEl = selector.el.find('[data-cid="' + cid + '"]'); selector.selectedCategory = { cid: cid, name: categoryEl.attr('data-name'), @@ -43,8 +43,8 @@ define('categorySelector', [ categoryEl.find('[component="category-markup"]').html() ); } else { - selector.el.find('[component="category-selector-selected"]').translateHtml( - options.selectCategoryLabel || '[[topic:thread_tools.select_category]]' + selector.el.find('[component="category-selector-selected"]').html( + defaultSelectHtml ); } }; @@ -62,7 +62,7 @@ define('categorySelector', [ options.onSelect = options.onSelect || function () {}; options.onSubmit = options.onSubmit || function () {}; app.parseAndTranslate('admin/partials/categories/select-category', { message: options.message }, function (html) { - var modal = bootbox.dialog({ + const modal = bootbox.dialog({ title: options.title || '[[modules:composer.select_category]]', message: html, buttons: { @@ -74,7 +74,7 @@ define('categorySelector', [ }, }); - var selector = categorySelector.init(modal.find('[component="category-selector"]'), options); + const selector = categorySelector.init(modal.find('[component="category-selector"]'), options); function submit(ev) { ev.preventDefault(); if (selector.selectedCategory) { diff --git a/public/src/modules/chat.js b/public/src/modules/chat.js index ff703a85a9..d57386040a 100644 --- a/public/src/modules/chat.js +++ b/public/src/modules/chat.js @@ -1,13 +1,79 @@ 'use strict'; define('chat', [ - 'components', - 'taskbar', - 'translator', - 'hooks', -], function (components, taskbar, translator, hooks) { - var module = {}; - var newMessage = false; + 'components', 'taskbar', 'translator', 'hooks', 'bootbox', +], function (components, taskbar, translator, hooks, bootbox) { + const module = {}; + let newMessage = false; + + module.openChat = function (roomId, uid) { + if (!app.user.uid) { + return app.alertError('[[error:not-logged-in]]'); + } + + function loadAndCenter(chatModal) { + module.load(chatModal.attr('data-uuid')); + module.center(chatModal); + module.focusInput(chatModal); + } + + if (module.modalExists(roomId)) { + loadAndCenter(module.getModal(roomId)); + } else { + socket.emit('modules.chats.loadRoom', { roomId: roomId, uid: uid || app.user.uid }, function (err, roomData) { + if (err) { + return app.alertError(err.message); + } + roomData.users = roomData.users.filter(function (user) { + return user && parseInt(user.uid, 10) !== parseInt(app.user.uid, 10); + }); + roomData.uid = uid || app.user.uid; + roomData.isSelf = true; + module.createModal(roomData, loadAndCenter); + }); + } + }; + + module.newChat = function (touid, callback) { + function createChat() { + socket.emit('modules.chats.newRoom', { touid: touid }, function (err, roomId) { + if (err) { + return app.alertError(err.message); + } + + if (!ajaxify.data.template.chats) { + module.openChat(roomId); + } else { + ajaxify.go('chats/' + roomId); + } + + callback(null, roomId); + }); + } + + callback = callback || function () { }; + if (!app.user.uid) { + return app.alertError('[[error:not-logged-in]]'); + } + + if (parseInt(touid, 10) === parseInt(app.user.uid, 10)) { + return app.alertError('[[error:cant-chat-with-yourself]]'); + } + socket.emit('modules.chats.isDnD', touid, function (err, isDnD) { + if (err) { + return app.alertError(err.message); + } + if (!isDnD) { + return createChat(); + } + + bootbox.confirm('[[modules:chat.confirm-chat-with-dnd-user]]', function (ok) { + if (ok) { + createChat(); + } + }); + }); + }; module.loadChatsDropdown = function (chatsListEl) { socket.emit('modules.chats.getRecentChats', { @@ -18,12 +84,12 @@ define('chat', [ return app.alertError(err.message); } - var rooms = data.rooms.filter(function (room) { + const rooms = data.rooms.filter(function (room) { return room.teaser; }); translator.toggleTimeagoShorthand(function () { - for (var i = 0; i < rooms.length; i += 1) { + for (let i = 0; i < rooms.length; i += 1) { rooms[i].teaser.timeago = $.timeago(new Date(parseInt(rooms[i].teaser.timestamp, 10))); } translator.toggleTimeagoShorthand(); @@ -35,9 +101,9 @@ define('chat', [ if ($(ev.target).parents('.user-link').length) { return; } - var roomId = $(this).attr('data-roomid'); + const roomId = $(this).attr('data-roomid'); if (!ajaxify.currentPage.match(/^chats\//)) { - app.openChat(roomId); + module.openChat(roomId); } else { ajaxify.go('user/' + app.user.userslug + '/chats/' + roomId); } @@ -57,7 +123,7 @@ define('chat', [ module.onChatMessageReceived = function (data) { - var isSelf = data.self === 1; + const isSelf = data.self === 1; data.message.self = data.self; newMessage = data.self === 0; @@ -83,9 +149,9 @@ define('chat', [ }; function addMessageToModal(data) { - var modal = module.getModal(data.roomId); - var username = data.message.fromUser.username; - var isSelf = data.self === 1; + const modal = module.getModal(data.roomId); + const username = data.message.fromUser.username; + const isSelf = data.self === 1; require(['forum/chats/messages'], function (ChatsMessages) { // don't add if already added if (!modal.find('[data-mid="' + data.message.messageId + '"]').length) { @@ -113,13 +179,13 @@ define('chat', [ } module.onUserStatusChange = function (data) { - var modal = module.getModal(data.uid); + const modal = module.getModal(data.uid); app.updateUserStatus(modal.find('[component="user/status"]'), data.status); }; module.onRoomRename = function (data) { - var newTitle = $('
    ').html(data.newName).text(); - var modal = module.getModal(data.roomId); + const newTitle = $('
    ').html(data.newName).text(); + const modal = module.getModal(data.roomId); modal.find('[component="chat/room/name"]').text(newTitle); taskbar.update('chat', modal.attr('data-uuid'), { title: newTitle, @@ -144,8 +210,8 @@ define('chat', [ if (module.modalExists(data.roomId)) { return callback(module.getModal(data.roomId)); } - var uuid = utils.generateUUID(); - var dragged = false; + const uuid = utils.generateUUID(); + let dragged = false; chatModal.attr('id', 'chat-modal-' + data.roomId); chatModal.attr('data-roomid', data.roomId); @@ -190,7 +256,7 @@ define('chat', [ }); function gotoChats() { - var text = components.get('chat/input').val(); + const text = components.get('chat/input').val(); $(window).one('action:ajaxify.end', function () { components.get('chat/input').val(text); }); @@ -202,11 +268,11 @@ define('chat', [ chatModal.find('.modal-header').on('dblclick', gotoChats); chatModal.find('button[data-action="maximize"]').on('click', gotoChats); chatModal.find('button[data-action="minimize"]').on('click', function () { - var uuid = chatModal.attr('data-uuid'); + const uuid = chatModal.attr('data-uuid'); module.minimize(uuid); }); - chatModal.on('click', ':not(.close)', function () { + chatModal.on('mouseup', function () { taskbar.updateActive(chatModal.attr('data-uuid')); if (dragged) { @@ -265,7 +331,7 @@ define('chat', [ }; module.close = function (chatModal) { - var uuid = chatModal.attr('data-uuid'); + const uuid = chatModal.attr('data-uuid'); clearInterval(chatModal.attr('intervalId')); chatModal.attr('intervalId', 0); chatModal.remove(); @@ -284,12 +350,12 @@ define('chat', [ // TODO: see taskbar.js:44 module.closeByUUID = function (uuid) { - var chatModal = $('.chat-modal[data-uuid="' + uuid + '"]'); + const chatModal = $('.chat-modal[data-uuid="' + uuid + '"]'); module.close(chatModal); }; module.center = function (chatModal) { - var hideAfter = false; + let hideAfter = false; if (chatModal.hasClass('hide')) { chatModal.removeClass('hide'); hideAfter = true; @@ -305,14 +371,14 @@ define('chat', [ module.load = function (uuid) { require(['forum/chats/messages'], function (ChatsMessages) { - var chatModal = $('.chat-modal[data-uuid="' + uuid + '"]'); + const chatModal = $('.chat-modal[data-uuid="' + uuid + '"]'); chatModal.removeClass('hide'); taskbar.updateActive(uuid); ChatsMessages.scrollToBottom(chatModal.find('.chat-content')); module.focusInput(chatModal); socket.emit('modules.chats.markRead', chatModal.attr('data-roomid')); - var env = utils.findBootstrapEnvironment(); + const env = utils.findBootstrapEnvironment(); if (env === 'xs' || env === 'sm') { module.enableMobileBehaviour(chatModal); } @@ -322,7 +388,7 @@ define('chat', [ module.enableMobileBehaviour = function (modalEl) { app.toggleNavbar(false); modalEl.attr('data-mobile', '1'); - var messagesEl = modalEl.find('.modal-body'); + const messagesEl = modalEl.find('.modal-body'); messagesEl.css('height', module.calculateChatListHeight(modalEl)); function resize() { messagesEl.css('height', module.calculateChatListHeight(modalEl)); @@ -348,7 +414,7 @@ define('chat', [ }; module.minimize = function (uuid) { - var chatModal = $('.chat-modal[data-uuid="' + uuid + '"]'); + const chatModal = $('.chat-modal[data-uuid="' + uuid + '"]'); chatModal.addClass('hide'); taskbar.minimize('chat', uuid); clearInterval(chatModal.attr('intervalId')); diff --git a/public/src/modules/components.js b/public/src/modules/components.js index 618ee81ee8..2ebd952f91 100644 --- a/public/src/modules/components.js +++ b/public/src/modules/components.js @@ -1,7 +1,7 @@ 'use strict'; define('components', function () { - var components = {}; + const components = {}; components.core = { 'topic/teaser': function (tid) { @@ -61,7 +61,7 @@ define('components', function () { }; components.get = function () { - var args = Array.prototype.slice.call(arguments, 1); + const args = Array.prototype.slice.call(arguments, 1); if (components.core[arguments[0]] && args.length) { return components.core[arguments[0]].apply(this, args); diff --git a/public/src/modules/coverPhoto.js b/public/src/modules/coverPhoto.js index 8cae013101..7c1a1e21e6 100644 --- a/public/src/modules/coverPhoto.js +++ b/public/src/modules/coverPhoto.js @@ -4,7 +4,7 @@ define('coverPhoto', [ 'vendor/jquery/draggable-background/backgroundDraggable', ], function () { - var coverPhoto = { + const coverPhoto = { coverEl: null, saveFn: null, }; @@ -37,8 +37,8 @@ define('coverPhoto', [ e.stopPropagation(); e.preventDefault(); - var files = e.originalEvent.dataTransfer.files; - var reader = new FileReader(); + const files = e.originalEvent.dataTransfer.files; + const reader = new FileReader(); if (files.length && files[0].type.match('image.*')) { reader.onload = function (e) { diff --git a/public/src/modules/flags.js b/public/src/modules/flags.js index fb6a171d9a..0e9fa13928 100644 --- a/public/src/modules/flags.js +++ b/public/src/modules/flags.js @@ -2,10 +2,10 @@ define('flags', ['hooks', 'components', 'api'], function (hooks, components, api) { - var Flag = {}; - var flagModal; - var flagCommit; - var flagReason; + const Flag = {}; + let flagModal; + let flagCommit; + let flagReason; Flag.showFlagModal = function (data) { app.parseAndTranslate('partials/modals/flag_modal', data, function (html) { @@ -30,8 +30,8 @@ define('flags', ['hooks', 'components', 'api'], function (hooks, components, api }); flagCommit.on('click', function () { - var selected = $('input[name="flag-reason"]:checked'); - var reason = selected.val(); + const selected = $('input[name="flag-reason"]:checked'); + let reason = selected.val(); if (selected.attr('id') === 'flag-reason-other') { reason = flagReason.val(); } @@ -66,7 +66,7 @@ define('flags', ['hooks', 'components', 'api'], function (hooks, components, api if (!type || !id || !reason) { return; } - var data = { type: type, id: id, reason: reason }; + const data = { type: type, id: id, reason: reason }; api.post('/flags', data, function (err, flagId) { if (err) { return app.alertError(err.message); @@ -75,7 +75,7 @@ define('flags', ['hooks', 'components', 'api'], function (hooks, components, api flagModal.modal('hide'); app.alertSuccess('[[flags:modal-submit-success]]'); if (type === 'post') { - var postEl = components.get('post', 'pid', id); + const postEl = components.get('post', 'pid', id); postEl.find('[component="post/flag"]').addClass('hidden').parent().attr('hidden', ''); postEl.find('[component="post/already-flagged"]').removeClass('hidden').parent().attr('hidden', null); } diff --git a/public/src/modules/groupSearch.js b/public/src/modules/groupSearch.js index 6a650ac2b0..b77e559757 100644 --- a/public/src/modules/groupSearch.js +++ b/public/src/modules/groupSearch.js @@ -1,26 +1,26 @@ 'use strict'; define('groupSearch', function () { - var groupSearch = {}; + const groupSearch = {}; groupSearch.init = function (el) { if (utils.isTouchDevice()) { return; } - var searchEl = el.find('[component="group-selector-search"]'); + const searchEl = el.find('[component="group-selector-search"]'); if (!searchEl.length) { return; } - var toggleVisibility = searchEl.parent('[component="group-selector"]').length > 0; + const toggleVisibility = searchEl.parent('[component="group-selector"]').length > 0; - var groupEls = el.find('[component="group-list"] [data-name]'); + const groupEls = el.find('[component="group-list"] [data-name]'); el.on('show.bs.dropdown', function () { function updateList() { - var val = searchEl.find('input').val().toLowerCase(); - var noMatch = true; + const val = searchEl.find('input').val().toLowerCase(); + let noMatch = true; groupEls.each(function () { - var liEl = $(this); - var isMatch = liEl.attr('data-name').toLowerCase().indexOf(val) !== -1; + const liEl = $(this); + const isMatch = liEl.attr('data-name').toLowerCase().indexOf(val) !== -1; if (noMatch && isMatch) { noMatch = false; } diff --git a/public/src/modules/handleBack.js b/public/src/modules/handleBack.js index e16546b705..2df8baceda 100644 --- a/public/src/modules/handleBack.js +++ b/public/src/modules/handleBack.js @@ -6,8 +6,8 @@ define('handleBack', [ 'navigator', 'forum/pagination', ], function (components, storage, navigator, pagination) { - var handleBack = {}; - var loadTopicsMethod; + const handleBack = {}; + let loadTopicsMethod; handleBack.init = function (_loadTopicsMethod) { loadTopicsMethod = _loadTopicsMethod; @@ -19,8 +19,8 @@ define('handleBack', [ function saveClickedIndex() { $('[component="category"]').on('click', '[component="topic/header"]', function () { - var clickedIndex = $(this).parents('[data-index]').attr('data-index'); - var windowScrollTop = $(window).scrollTop(); + const clickedIndex = $(this).parents('[data-index]').attr('data-index'); + const windowScrollTop = $(window).scrollTop(); $('[component="category/topic"]').each(function (index, el) { if ($(el).offset().top - windowScrollTop > 0) { storage.setItem('category:bookmark', $(el).attr('data-index')); @@ -33,15 +33,15 @@ define('handleBack', [ } function onBackClicked(isMarkedUnread) { - var highlightUnread = isMarkedUnread && ajaxify.data.template.unread; + const highlightUnread = isMarkedUnread && ajaxify.data.template.unread; if ( ajaxify.data.template.category || ajaxify.data.template.recent || ajaxify.data.template.popular || highlightUnread ) { - var bookmarkIndex = storage.getItem('category:bookmark'); - var clickedIndex = storage.getItem('category:bookmark:clicked'); + let bookmarkIndex = storage.getItem('category:bookmark'); + let clickedIndex = storage.getItem('category:bookmark:clicked'); storage.removeItem('category:bookmark'); storage.removeItem('category:bookmark:clicked'); @@ -53,7 +53,7 @@ define('handleBack', [ clickedIndex = Math.max(0, parseInt(clickedIndex, 10) || 0); if (config.usePagination) { - var page = Math.ceil((parseInt(bookmarkIndex, 10) + 1) / config.topicsPerPage); + const page = Math.ceil((parseInt(bookmarkIndex, 10) + 1) / config.topicsPerPage); if (parseInt(page, 10) !== ajaxify.data.pagination.currentPage) { pagination.loadPage(page, function () { handleBack.scrollToTopic(bookmarkIndex, clickedIndex); @@ -76,7 +76,7 @@ define('handleBack', [ } handleBack.highlightTopic = function (topicIndex) { - var highlight = components.get('category/topic', 'index', topicIndex); + const highlight = components.get('category/topic', 'index', topicIndex); if (highlight.length && !highlight.hasClass('highlight')) { highlight.addClass('highlight'); @@ -91,10 +91,10 @@ define('handleBack', [ return; } - var scrollTo = components.get('category/topic', 'index', bookmarkIndex); + const scrollTo = components.get('category/topic', 'index', bookmarkIndex); if (scrollTo.length) { - var offset = storage.getItem('category:bookmark:offset'); + const offset = storage.getItem('category:bookmark:offset'); storage.removeItem('category:bookmark:offset'); $(window).scrollTop(scrollTo.offset().top - offset); handleBack.highlightTopic(clickedIndex); diff --git a/public/src/modules/helpers.js b/public/src/modules/helpers.js index 33cc8a6323..34781ba92d 100644 --- a/public/src/modules/helpers.js +++ b/public/src/modules/helpers.js @@ -2,7 +2,7 @@ (function (factory) { if (typeof module === 'object' && module.exports) { - var relative_path = require('nconf').get('relative_path'); + const relative_path = require('nconf').get('relative_path'); module.exports = factory(require('../utils'), require('benchpressjs'), relative_path); } else if (typeof define === 'function' && define.amd) { define('helpers', ['benchpress'], function (Benchpress) { @@ -13,7 +13,7 @@ Benchpress.setGlobal('true', true); Benchpress.setGlobal('false', false); - var helpers = { + const helpers = { displayMenuItem, buildMetaTag, buildLinkTag, @@ -41,7 +41,7 @@ } function displayMenuItem(data, index) { - var item = data.navigation[index]; + const item = data.navigation[index]; if (!item) { return false; } @@ -62,18 +62,18 @@ } function buildMetaTag(tag) { - var name = tag.name ? 'name="' + tag.name + '" ' : ''; - var property = tag.property ? 'property="' + tag.property + '" ' : ''; - var content = tag.content ? 'content="' + tag.content.replace(/\n/g, ' ') + '" ' : ''; + const name = tag.name ? 'name="' + tag.name + '" ' : ''; + const property = tag.property ? 'property="' + tag.property + '" ' : ''; + const content = tag.content ? 'content="' + tag.content.replace(/\n/g, ' ') + '" ' : ''; return '\n\t'; } function buildLinkTag(tag) { - const attributes = ['link', 'rel', 'as', 'type', 'href', 'sizes', 'title']; - const [link, rel, as, type, href, sizes, title] = attributes.map(attr => (tag[attr] ? `${attr}="${tag[attr]}" ` : '')); + const attributes = ['link', 'rel', 'as', 'type', 'href', 'sizes', 'title', 'crossorigin']; + const [link, rel, as, type, href, sizes, title, crossorigin] = attributes.map(attr => (tag[attr] ? `${attr}="${tag[attr]}" ` : '')); - return '\n\t'; + return '\n\t'; } function stringify(obj) { @@ -94,7 +94,7 @@ if (!category) { return ''; } - var style = []; + const style = []; if (category.bgColor) { style.push('background-color: ' + category.bgColor); @@ -115,13 +115,13 @@ } function generateChildrenCategories(category) { - var html = ''; + let html = ''; if (!category || !category.children || !category.children.length) { return html; } category.children.forEach(function (child) { if (child && !child.isSection) { - var link = child.link ? child.link : (relative_path + '/category/' + child.slug); + const link = child.link ? child.link : (relative_path + '/category/' + child.slug); html += '' + ' - ${event.href ? `
    ${event.text}` : event.text}  `; @@ -264,7 +264,7 @@ } function userAgentIcons(data) { - var icons = ''; + let icons = ''; switch (data.platform) { case 'Linux': @@ -330,13 +330,13 @@ userObj = this; } - var attributes = [ + const attributes = [ 'alt="' + userObj.username + '"', 'title="' + userObj.username + '"', 'data-uid="' + userObj.uid + '"', 'loading="lazy"', ]; - var styles = []; + const styles = []; classNames = classNames || ''; // Validate sizes, handle integers, otherwise fall back to `avatar-sm` diff --git a/public/src/modules/hooks.js b/public/src/modules/hooks.js index c474d9b15f..f324bc706e 100644 --- a/public/src/modules/hooks.js +++ b/public/src/modules/hooks.js @@ -4,6 +4,7 @@ define('hooks', [], () => { const Hooks = { loaded: {}, temporary: new Set(), + runOnce: new Set(), deprecated: { 'action:script.load': 'filter:script.load', // 👋 @ 1.18.0 'action:category.loaded': 'action:topics.loaded', // 👋 @ 1.19.0 @@ -32,6 +33,10 @@ define('hooks', [], () => { console.debug(`[hooks] Registered ${hookName}`, method); }; Hooks.on = Hooks.register; + Hooks.one = (hookName, method) => { + Hooks.register(hookName, method); + Hooks.runOnce.add({ hookName, method }); + }; // registerPage/onPage takes care of unregistering the listener on ajaxify Hooks.registerPage = (hookName, method) => { @@ -110,17 +115,27 @@ define('hooks', [], () => { Hooks.fire = (hookName, data) => { const type = hookName.split(':').shift(); - + let result; switch (type) { case 'filter': - return _fireFilterHook(hookName, data); + result = _fireFilterHook(hookName, data); + break; case 'action': - return _fireActionHook(hookName, data); + result = _fireActionHook(hookName, data); + break; case 'static': - return _fireStaticHook(hookName, data); + result = _fireStaticHook(hookName, data); + break; } + Hooks.runOnce.forEach((pair) => { + if (pair.hookName === hookName) { + Hooks.unregister(hookName, pair.method); + Hooks.runOnce.delete(pair); + } + }); + return result; }; return Hooks; diff --git a/public/src/modules/iconSelect.js b/public/src/modules/iconSelect.js index 17530cd05d..7bbbfb7fa9 100644 --- a/public/src/modules/iconSelect.js +++ b/public/src/modules/iconSelect.js @@ -1,13 +1,13 @@ 'use strict'; -define('iconSelect', ['benchpress'], function (Benchpress) { - var iconSelect = {}; +define('iconSelect', ['benchpress', 'bootbox'], function (Benchpress, bootbox) { + const iconSelect = {}; iconSelect.init = function (el, onModified) { onModified = onModified || function () {}; - var doubleSize = el.hasClass('fa-2x'); - var selected = el.attr('class').replace('fa-2x', '').replace('fa', '').replace(/\s+/g, ''); + const doubleSize = el.hasClass('fa-2x'); + let selected = el.attr('class').replace('fa-2x', '').replace('fa', '').replace(/\s+/g, ''); $('#icons .selected').removeClass('selected'); @@ -23,7 +23,7 @@ define('iconSelect', ['benchpress'], function (Benchpress) { html = $(html); html.find('.fa-icons').prepend($('')); - var picker = bootbox.dialog({ + const picker = bootbox.dialog({ onEscape: true, backdrop: true, show: false, @@ -45,10 +45,10 @@ define('iconSelect', ['benchpress'], function (Benchpress) { label: 'Select', className: 'btn-primary', callback: function () { - var iconClass = $('.bootbox .selected').attr('class'); - var categoryIconClass = $('
    ').addClass(iconClass).removeClass('fa').removeClass('selected') + const iconClass = $('.bootbox .selected').attr('class'); + const categoryIconClass = $('
    ').addClass(iconClass).removeClass('fa').removeClass('selected') .attr('class'); - var searchElVal = picker.find('input').val(); + const searchElVal = picker.find('input').val(); if (categoryIconClass) { el.attr('class', 'fa ' + (doubleSize ? 'fa-2x ' : '') + categoryIconClass); @@ -67,8 +67,8 @@ define('iconSelect', ['benchpress'], function (Benchpress) { }); picker.on('show.bs.modal', function () { - var modalEl = $(this); - var searchEl = modalEl.find('input'); + const modalEl = $(this); + const searchEl = modalEl.find('input'); if (selected) { modalEl.find('.' + selected).addClass('selected'); @@ -77,10 +77,10 @@ define('iconSelect', ['benchpress'], function (Benchpress) { }).modal('show'); picker.on('shown.bs.modal', function () { - var modalEl = $(this); - var searchEl = modalEl.find('input'); - var icons = modalEl.find('.fa-icons i'); - var submitEl = modalEl.find('button.btn-primary'); + const modalEl = $(this); + const searchEl = modalEl.find('input'); + const icons = modalEl.find('.fa-icons i'); + const submitEl = modalEl.find('button.btn-primary'); function changeSelection(newSelection) { modalEl.find('i.selected').removeClass('selected'); diff --git a/public/src/modules/logout.js b/public/src/modules/logout.js new file mode 100644 index 0000000000..400d5c25e1 --- /dev/null +++ b/public/src/modules/logout.js @@ -0,0 +1,28 @@ +'use strict'; + +define('logout', ['hooks'], function (hooks) { + return function logout(redirect) { + redirect = redirect === undefined ? true : redirect; + hooks.fire('action:app.logout'); + + $.ajax(config.relative_path + '/logout', { + type: 'POST', + headers: { + 'x-csrf-token': config.csrf_token, + }, + beforeSend: function () { + app.flags._logout = true; + }, + success: function (data) { + hooks.fire('action:app.loggedOut', data); + if (redirect) { + if (data.next) { + window.location.href = data.next; + } else { + window.location.reload(); + } + } + }, + }); + }; +}); diff --git a/public/src/modules/messages.js b/public/src/modules/messages.js new file mode 100644 index 0000000000..0c1cfe2e71 --- /dev/null +++ b/public/src/modules/messages.js @@ -0,0 +1,122 @@ +'use strict'; + +define('messages', ['bootbox', 'translator', 'storage', 'alerts'], function (bootbox, translator, storage, alerts) { + const messages = {}; + + let showWelcomeMessage; + let registerMessage; + + messages.show = function () { + showQueryStringMessages(); + showCookieWarning(); + messages.showEmailConfirmWarning(); + }; + + messages.showEmailConfirmWarning = function (message) { + if (!config.emailPrompt || !app.user.uid || parseInt(storage.getItem('email-confirm-dismiss'), 10) === 1) { + return; + } + const msg = { + alert_id: 'email_confirm', + type: 'warning', + timeout: 0, + closefn: () => { + storage.setItem('email-confirm-dismiss', 1); + }, + }; + + if (!app.user.email) { + msg.message = '[[error:no-email-to-confirm]]'; + msg.clickfn = function () { + app.removeAlert('email_confirm'); + ajaxify.go('user/' + app.user.userslug + '/edit/email'); + }; + app.alert(msg); + } else if (!app.user['email:confirmed'] && !app.user.isEmailConfirmSent) { + msg.message = message || '[[error:email-not-confirmed]]'; + msg.clickfn = function () { + app.removeAlert('email_confirm'); + socket.emit('user.emailConfirm', {}, function (err) { + if (err) { + return app.alertError(err.message); + } + app.alertSuccess('[[notifications:email-confirm-sent]]'); + }); + }; + app.alert(msg); + } else if (!app.user['email:confirmed'] && app.user.isEmailConfirmSent) { + msg.message = '[[error:email-not-confirmed-email-sent]]'; + app.alert(msg); + } + }; + + function showCookieWarning() { + if (!config.cookies.enabled || !navigator.cookieEnabled || app.inAdmin || storage.getItem('cookieconsent') === '1') { + return; + } + + config.cookies.message = translator.unescape(config.cookies.message); + config.cookies.dismiss = translator.unescape(config.cookies.dismiss); + config.cookies.link = translator.unescape(config.cookies.link); + config.cookies.link_url = translator.unescape(config.cookies.link_url); + + app.parseAndTranslate('partials/cookie-consent', config.cookies, function (html) { + $(document.body).append(html); + $(document.body).addClass('cookie-consent-open'); + + const warningEl = $('.cookie-consent'); + const dismissEl = warningEl.find('button'); + dismissEl.on('click', function () { + // Save consent cookie and remove warning element + storage.setItem('cookieconsent', '1'); + warningEl.remove(); + $(document.body).removeClass('cookie-consent-open'); + }); + }); + } + + function showQueryStringMessages() { + const params = utils.params(); + showWelcomeMessage = !!params.loggedin; + registerMessage = params.register; + + if (showWelcomeMessage) { + alerts.alert({ + type: 'success', + title: '[[global:welcome_back]] ' + app.user.username + '!', + message: '[[global:you_have_successfully_logged_in]]', + timeout: 5000, + }); + } + + if (registerMessage) { + bootbox.alert({ + message: utils.escapeHTML(decodeURIComponent(registerMessage)), + }); + } + } + + messages.showInvalidSession = function () { + bootbox.alert({ + title: '[[error:invalid-session]]', + message: '[[error:invalid-session-text]]', + closeButton: false, + callback: function () { + window.location.reload(); + }, + }); + }; + + messages.showSessionMismatch = function () { + bootbox.alert({ + title: '[[error:session-mismatch]]', + message: '[[error:session-mismatch-text]]', + closeButton: false, + callback: function () { + window.location.reload(); + }, + }); + }; + + return messages; +}); diff --git a/public/src/modules/navigator.js b/public/src/modules/navigator.js index 12922616d6..58e8a69c2d 100644 --- a/public/src/modules/navigator.js +++ b/public/src/modules/navigator.js @@ -1,29 +1,29 @@ 'use strict'; define('navigator', ['forum/pagination', 'components', 'hooks'], function (pagination, components, hooks) { - var navigator = {}; - var index = 0; - var count = 0; - var navigatorUpdateTimeoutId; - - var renderPostIntervalId; - var touchX; - var touchY; - var renderPostIndex; - var isNavigating = false; - var firstMove = true; + const navigator = {}; + let index = 0; + let count = 0; + let navigatorUpdateTimeoutId; + + let renderPostIntervalId; + let touchX; + let touchY; + let renderPostIndex; + let isNavigating = false; + let firstMove = true; navigator.scrollActive = false; - var paginationBlockEl = $('.pagination-block'); - var paginationTextEl = paginationBlockEl.find('.pagination-text'); - var paginationBlockMeterEl = paginationBlockEl.find('meter'); - var paginationBlockProgressEl = paginationBlockEl.find('.progress-bar'); - var thumb; - var thumbText; - var thumbIcon; - var thumbIconHeight; - var thumbIconHalfHeight; + let paginationBlockEl = $('.pagination-block'); + let paginationTextEl = paginationBlockEl.find('.pagination-text'); + let paginationBlockMeterEl = paginationBlockEl.find('meter'); + let paginationBlockProgressEl = paginationBlockEl.find('.progress-bar'); + let thumb; + let thumbText; + let thumbIcon; + let thumbIconHeight; + let thumbIconHalfHeight; $(window).on('action:ajaxify.start', function () { $(window).off('keydown', onKeyDown); @@ -66,14 +66,14 @@ define('navigator', ['forum/pagination', 'components', 'hooks'], function (pagin paginationBlockEl.find('input').on('keydown', function (e) { if (e.which === 13) { - var input = $(this); + const input = $(this); if (!utils.isNumber(input.val())) { input.val(''); return; } - var index = parseInt(input.val(), 10); - var url = generateUrl(index); + const index = parseInt(input.val(), 10); + const url = generateUrl(index); input.val(''); $('.pagination-block .dropdown-toggle').trigger('click'); ajaxify.go(url); @@ -91,8 +91,8 @@ define('navigator', ['forum/pagination', 'components', 'hooks'], function (pagin }; function clampTop(newTop) { - var parent = thumb.parent(); - var parentOffset = parent.offset(); + const parent = thumb.parent(); + const parentOffset = parent.offset(); if (newTop < parentOffset.top) { newTop = parentOffset.top; } else if (newTop > parentOffset.top + parent.height() - thumbIconHeight) { @@ -105,15 +105,15 @@ define('navigator', ['forum/pagination', 'components', 'hooks'], function (pagin if (!thumb.length || thumb.is(':hidden')) { return; } - var parent = thumb.parent(); - var parentOffset = parent.offset(); - var percent = (index - 1) / ajaxify.data.postcount; + const parent = thumb.parent(); + const parentOffset = parent.offset(); + let percent = (index - 1) / ajaxify.data.postcount; if (index === count) { percent = 1; } - var newTop = clampTop(parentOffset.top + ((parent.height() - thumbIconHeight) * percent)); + const newTop = clampTop(parentOffset.top + ((parent.height() - thumbIconHeight) * percent)); - var offset = { top: newTop, left: thumb.offset().left }; + const offset = { top: newTop, left: thumb.offset().left }; thumb.offset(offset); thumbText.text(index + '/' + ajaxify.data.postcount); renderPost(index); @@ -124,24 +124,24 @@ define('navigator', ['forum/pagination', 'components', 'hooks'], function (pagin return; } - var parent = thumb.parent(); + const parent = thumb.parent(); parent.on('click', function (ev) { if ($(ev.target).hasClass('scroller-container')) { - var index = calculateIndexFromY(ev.pageY); + const index = calculateIndexFromY(ev.pageY); navigator.scrollToIndex(index - 1, true, 0); return false; } }); function calculateIndexFromY(y) { - var newTop = clampTop(y - thumbIconHalfHeight); - var parentOffset = parent.offset(); - var percent = (newTop - parentOffset.top) / (parent.height() - thumbIconHeight); + const newTop = clampTop(y - thumbIconHalfHeight); + const parentOffset = parent.offset(); + const percent = (newTop - parentOffset.top) / (parent.height() - thumbIconHeight); index = Math.max(1, Math.ceil(ajaxify.data.postcount * percent)); return index > ajaxify.data.postcount ? ajaxify.data.count : index; } - var mouseDragging = false; + let mouseDragging = false; hooks.on('action:ajaxify.end', function () { renderPostIndex = null; }); @@ -167,9 +167,9 @@ define('navigator', ['forum/pagination', 'components', 'hooks'], function (pagin } function mousemove(ev) { - var newTop = clampTop(ev.pageY - thumbIconHalfHeight); + const newTop = clampTop(ev.pageY - thumbIconHalfHeight); thumb.offset({ top: newTop, left: thumb.offset().left }); - var index = calculateIndexFromY(ev.pageY); + const index = calculateIndexFromY(ev.pageY); navigator.updateTextAndProgressBar(); thumbText.text(index + '/' + ajaxify.data.postcount); if (firstMove) { @@ -198,10 +198,10 @@ define('navigator', ['forum/pagination', 'components', 'hooks'], function (pagin }); thumb.on('touchmove', function (ev) { - var windowWidth = $(window).width(); - var windowHeight = $(window).height(); - var deltaX = Math.abs(touchX - Math.min(windowWidth, Math.max(0, ev.touches[0].clientX))); - var deltaY = Math.abs(touchY - Math.min(windowHeight, Math.max(0, ev.touches[0].clientY))); + const windowWidth = $(window).width(); + const windowHeight = $(window).height(); + const deltaX = Math.abs(touchX - Math.min(windowWidth, Math.max(0, ev.touches[0].clientX))); + const deltaY = Math.abs(touchY - Math.min(windowHeight, Math.max(0, ev.touches[0].clientY))); touchX = Math.min(windowWidth, Math.max(0, ev.touches[0].clientX)); touchY = Math.min(windowHeight, Math.max(0, ev.touches[0].clientY)); @@ -213,9 +213,9 @@ define('navigator', ['forum/pagination', 'components', 'hooks'], function (pagin if (isNavigating && ev.cancelable) { ev.preventDefault(); ev.stopPropagation(); - var newTop = clampTop(touchY + $(window).scrollTop() - thumbIconHalfHeight); + const newTop = clampTop(touchY + $(window).scrollTop() - thumbIconHalfHeight); thumb.offset({ top: newTop, left: thumb.offset().left }); - var index = calculateIndexFromY(touchY + $(window).scrollTop()); + const index = calculateIndexFromY(touchY + $(window).scrollTop()); navigator.updateTextAndProgressBar(); thumbText.text(index + '/' + ajaxify.data.postcount); if (firstMove) { @@ -286,8 +286,8 @@ define('navigator', ['forum/pagination', 'components', 'hooks'], function (pagin } function generateUrl(index) { - var pathname = window.location.pathname.replace(config.relative_path, ''); - var parts = pathname.split('/'); + const pathname = window.location.pathname.replace(config.relative_path, ''); + const parts = pathname.split('/'); return parts[1] + '/' + parts[2] + '/' + parts[3] + (index ? '/' + index : ''); } @@ -315,7 +315,7 @@ define('navigator', ['forum/pagination', 'components', 'hooks'], function (pagin }; function toggle(flag) { - var path = ajaxify.removeRelativePath(window.location.pathname.slice(1)); + const path = ajaxify.removeRelativePath(window.location.pathname.slice(1)); if (flag && (!path.startsWith('topic') && !path.startsWith('category'))) { return; } @@ -338,22 +338,22 @@ define('navigator', ['forum/pagination', 'components', 'hooks'], function (pagin a spot where a user is expecting to begin reading. */ threshold = typeof threshold === 'number' ? threshold : undefined; - var newIndex = index; - var els = $(navigator.selector); + let newIndex = index; + const els = $(navigator.selector); if (els.length) { newIndex = parseInt(els.first().attr('data-index'), 10) + 1; } - var scrollTop = $(window).scrollTop(); - var windowHeight = $(window).height(); - var documentHeight = $(document).height(); - var middleOfViewport = scrollTop + (windowHeight / 2); - var previousDistance = Number.MAX_VALUE; + const scrollTop = $(window).scrollTop(); + const windowHeight = $(window).height(); + const documentHeight = $(document).height(); + const middleOfViewport = scrollTop + (windowHeight / 2); + let previousDistance = Number.MAX_VALUE; els.each(function () { - var $this = $(this); - var elIndex = parseInt($this.attr('data-index'), 10); + const $this = $(this); + const elIndex = parseInt($this.attr('data-index'), 10); if (elIndex >= 0) { - var distanceToMiddle = Math.abs(middleOfViewport - ($this.offset().top + ($this.outerHeight(true) / 2))); + const distanceToMiddle = Math.abs(middleOfViewport - ($this.offset().top + ($this.outerHeight(true) / 2))); if (distanceToMiddle > previousDistance) { return false; } @@ -365,8 +365,8 @@ define('navigator', ['forum/pagination', 'components', 'hooks'], function (pagin } }); - var atTop = scrollTop === 0 && parseInt(els.first().attr('data-index'), 10) === 0; - var nearBottom = scrollTop + windowHeight > documentHeight - 100 && parseInt(els.last().attr('data-index'), 10) === count - 1; + const atTop = scrollTop === 0 && parseInt(els.first().attr('data-index'), 10) === 0; + const nearBottom = scrollTop + windowHeight > documentHeight - 100 && parseInt(els.last().attr('data-index'), 10) === count - 1; if (atTop) { newIndex = 1; @@ -379,9 +379,9 @@ define('navigator', ['forum/pagination', 'components', 'hooks'], function (pagin if (atTop) { threshold = 0; } else { - var anchorEl = components.get('post/anchor', index - 1); + const anchorEl = components.get('post/anchor', index - 1); if (anchorEl.length) { - var anchorRect = anchorEl.get(0).getBoundingClientRect(); + const anchorRect = anchorEl.get(0).getBoundingClientRect(); threshold = anchorRect.top; } } @@ -406,16 +406,16 @@ define('navigator', ['forum/pagination', 'components', 'hooks'], function (pagin } index = index > count ? count : index; paginationTextEl.translateHtml('[[global:pagination.out_of, ' + index + ', ' + count + ']]'); - var fraction = (index - 1) / (count - 1 || 1); + const fraction = (index - 1) / (count - 1 || 1); paginationBlockMeterEl.val(fraction); paginationBlockProgressEl.width((fraction * 100) + '%'); }; navigator.scrollUp = function () { - var $window = $(window); + const $window = $(window); if (config.usePagination) { - var atTop = $window.scrollTop() <= 0; + const atTop = $window.scrollTop() <= 0; if (atTop) { return pagination.previousPage(function () { $('body,html').scrollTop($(document).height() - $window.height()); @@ -428,10 +428,10 @@ define('navigator', ['forum/pagination', 'components', 'hooks'], function (pagin }; navigator.scrollDown = function () { - var $window = $(window); + const $window = $(window); if (config.usePagination) { - var atBottom = $window.scrollTop() >= $(document).height() - $window.height(); + const atBottom = $window.scrollTop() >= $(document).height() - $window.height(); if (atBottom) { return pagination.nextPage(); } @@ -463,8 +463,8 @@ define('navigator', ['forum/pagination', 'components', 'hooks'], function (pagin }; navigator.scrollToIndex = function (index, highlight, duration) { - var inTopic = !!components.get('topic').length; - var inCategory = !!components.get('category').length; + const inTopic = !!components.get('topic').length; + const inCategory = !!components.get('category').length; if (!utils.isNumber(index) || (!inTopic && !inCategory)) { return; @@ -490,9 +490,9 @@ define('navigator', ['forum/pagination', 'components', 'hooks'], function (pagin return; } - var scrollMethod = inTopic ? navigator.scrollToPostIndex : navigator.scrollToTopicIndex; + const scrollMethod = inTopic ? navigator.scrollToPostIndex : navigator.scrollToTopicIndex; - var page = 1 + Math.floor(index / config.postsPerPage); + const page = 1 + Math.floor(index / config.postsPerPage); if (parseInt(page, 10) !== ajaxify.data.pagination.currentPage) { pagination.loadPage(page, function () { scrollMethod(index, highlight, duration); @@ -503,12 +503,12 @@ define('navigator', ['forum/pagination', 'components', 'hooks'], function (pagin }; navigator.scrollToPostIndex = function (postIndex, highlight, duration) { - var scrollTo = components.get('post', 'index', postIndex); + const scrollTo = components.get('post', 'index', postIndex); navigator.scrollToElement(scrollTo, highlight, duration); }; navigator.scrollToTopicIndex = function (topicIndex, highlight, duration) { - var scrollTo = $('[component="category/topic"][data-index="' + topicIndex + '"]'); + const scrollTo = $('[component="category/topic"][data-index="' + topicIndex + '"]'); navigator.scrollToElement(scrollTo, highlight, duration); }; @@ -518,23 +518,23 @@ define('navigator', ['forum/pagination', 'components', 'hooks'], function (pagin return; } - var postHeight = scrollTo.outerHeight(true); - var navbarHeight = components.get('navbar').outerHeight(true); - var topicHeaderHeight = $('.topic-header').outerHeight(true) || 0; - var viewportHeight = $(window).height(); + const postHeight = scrollTo.outerHeight(true); + const navbarHeight = components.get('navbar').outerHeight(true); + const topicHeaderHeight = $('.topic-header').outerHeight(true) || 0; + const viewportHeight = $(window).height(); // Temporarily disable navigator update on scroll $(window).off('scroll', navigator.delayedUpdate); duration = duration !== undefined ? duration : 400; navigator.scrollActive = true; - var done = false; + let done = false; function animateScroll() { function reenableScroll() { // Re-enable onScroll behaviour $(window).on('scroll', navigator.delayedUpdate); - var scrollToRect = scrollTo.get(0).getBoundingClientRect(); + const scrollToRect = scrollTo.get(0).getBoundingClientRect(); navigator.update(scrollToRect.top); } function onAnimateComplete() { @@ -550,7 +550,7 @@ define('navigator', ['forum/pagination', 'components', 'hooks'], function (pagin $('html').scrollTop($('html').scrollTop() - 1); } - var scrollTop = 0; + let scrollTop = 0; if (postHeight < viewportHeight - navbarHeight - topicHeaderHeight) { scrollTop = scrollTo.offset().top - (viewportHeight / 2) + (postHeight / 2); } else { diff --git a/public/src/modules/notifications.js b/public/src/modules/notifications.js index d2b578c19e..cb8ed86b6b 100644 --- a/public/src/modules/notifications.js +++ b/public/src/modules/notifications.js @@ -9,13 +9,13 @@ define('notifications', [ 'tinycon', 'hooks', ], function (translator, components, navigator, Benchpress, Tinycon, hooks) { - var Notifications = {}; + const Notifications = {}; - var unreadNotifs = {}; + let unreadNotifs = {}; const _addShortTimeagoString = ({ notifications: notifs }) => new Promise((resolve) => { translator.toggleTimeagoShorthand(function () { - for (var i = 0; i < notifs.length; i += 1) { + for (let i = 0; i < notifs.length; i += 1) { notifs[i].timeago = $.timeago(new Date(parseInt(notifs[i].datetime, 10))); } translator.toggleTimeagoShorthand(); @@ -24,13 +24,14 @@ define('notifications', [ }); hooks.on('filter:notifications.load', _addShortTimeagoString); - Notifications.loadNotifications = function (notifList) { + Notifications.loadNotifications = function (notifList, callback) { + callback = callback || function () {}; socket.emit('notifications.get', null, function (err, data) { if (err) { return app.alertError(err.message); } - var notifs = data.unread.concat(data.read).sort(function (a, b) { + const notifs = data.unread.concat(data.read).sort(function (a, b) { return parseInt(a.datetime, 10) > parseInt(b.datetime, 10) ? -1 : 1; }); @@ -38,26 +39,26 @@ define('notifications', [ app.parseAndTranslate('partials/notifications_list', { notifications }, function (html) { notifList.html(html); notifList.off('click').on('click', '[data-nid]', function (ev) { - var notifEl = $(this); + const notifEl = $(this); if (scrollToPostIndexIfOnPage(notifEl)) { ev.stopPropagation(); ev.preventDefault(); components.get('notifications/list').dropdown('toggle'); } - var unread = notifEl.hasClass('unread'); + const unread = notifEl.hasClass('unread'); if (!unread) { return; } - var nid = notifEl.attr('data-nid'); + const nid = notifEl.attr('data-nid'); markNotification(nid, true); }); components.get('notifications').on('click', '.mark-all-read', Notifications.markAllRead); notifList.on('click', '.mark-read', function () { - var liEl = $(this).parent(); - var unread = liEl.hasClass('unread'); - var nid = liEl.attr('data-nid'); + const liEl = $(this).parent(); + const unread = liEl.hasClass('unread'); + const nid = liEl.attr('data-nid'); markNotification(nid, unread, function () { liEl.toggleClass('unread'); }); @@ -68,6 +69,7 @@ define('notifications', [ notifications: notifs, list: notifList, }); + callback(); }); }); }); @@ -108,9 +110,9 @@ define('notifications', [ function scrollToPostIndexIfOnPage(notifEl) { // Scroll to index if already in topic (gh#5873) - var pid = notifEl.attr('data-pid'); - var path = notifEl.attr('data-path'); - var postEl = components.get('post', 'pid', pid); + const pid = notifEl.attr('data-pid'); + const path = notifEl.attr('data-path'); + const postEl = components.get('post', 'pid', pid); if (path.startsWith(config.relative_path + '/post/') && pid && postEl.length && ajaxify.data.template.topic) { navigator.scrollToIndex(postEl.attr('data-index'), true); return true; @@ -119,7 +121,7 @@ define('notifications', [ } Notifications.updateNotifCount = function (count) { - var notifIcon = components.get('notifications/icon'); + const notifIcon = components.get('notifications/icon'); count = Math.max(0, count); if (count > 0) { notifIcon.removeClass('fa-bell-o').addClass('fa-bell'); @@ -130,7 +132,7 @@ define('notifications', [ notifIcon.toggleClass('unread-count', count > 0); notifIcon.attr('data-content', count > 99 ? '99+' : count); - var payload = { + const payload = { count: count, updateFavicon: true, }; @@ -139,6 +141,10 @@ define('notifications', [ if (payload.updateFavicon) { Tinycon.setBubble(count > 99 ? '99+' : count); } + + if (navigator.setAppBadge) { // feature detection + navigator.setAppBadge(count); + } }; Notifications.markAllRead = function () { diff --git a/public/src/modules/pictureCropper.js b/public/src/modules/pictureCropper.js index f9033cd89b..cfb01b4654 100644 --- a/public/src/modules/pictureCropper.js +++ b/public/src/modules/pictureCropper.js @@ -1,10 +1,10 @@ 'use strict'; define('pictureCropper', ['cropper'], function (Cropper) { - var module = {}; + const module = {}; module.show = function (data, callback) { - var fileSize = data.hasOwnProperty('fileSize') && data.fileSize !== undefined ? parseInt(data.fileSize, 10) : false; + const fileSize = data.hasOwnProperty('fileSize') && data.fileSize !== undefined ? parseInt(data.fileSize, 10) : false; app.parseAndTranslate('partials/modals/upload_file_modal', { showHelp: data.hasOwnProperty('showHelp') && data.showHelp !== undefined ? data.showHelp : true, fileSize: fileSize, @@ -18,7 +18,7 @@ define('pictureCropper', ['cropper'], function (Cropper) { uploadModal.remove(); }); - var uploadForm = uploadModal.find('#uploadForm'); + const uploadForm = uploadModal.find('#uploadForm'); if (data.route) { uploadForm.attr('action', data.route); } @@ -42,11 +42,11 @@ define('pictureCropper', ['cropper'], function (Cropper) { }).modal('show'); // Set cropper image max-height based on viewport - var cropBoxHeight = parseInt($(window).height() / 2, 10); - var img = document.getElementById('cropped-image'); + const cropBoxHeight = parseInt($(window).height() / 2, 10); + const img = document.getElementById('cropped-image'); $(img).css('max-height', cropBoxHeight); - var cropperTool = new Cropper(img, { + let cropperTool = new Cropper(img, { aspectRatio: data.aspectRatio, autoCropArea: 1, viewMode: 1, @@ -71,8 +71,8 @@ define('pictureCropper', ['cropper'], function (Cropper) { } if (data.restrictImageDimension) { - var origDimension = (img.width < img.height) ? img.width : img.height; - var dimension = (origDimension > data.imageDimension) ? data.imageDimension : origDimension; + const origDimension = (img.width < img.height) ? img.width : img.height; + const dimension = (origDimension > data.imageDimension) ? data.imageDimension : origDimension; cropperTool.setCropBoxData({ width: dimension, height: dimension, @@ -80,13 +80,13 @@ define('pictureCropper', ['cropper'], function (Cropper) { } cropperModal.find('.rotate').on('click', function () { - var degrees = this.getAttribute('data-degrees'); + const degrees = this.getAttribute('data-degrees'); cropperTool.rotate(degrees); }); cropperModal.find('.flip').on('click', function () { - var option = this.getAttribute('data-option'); - var method = this.getAttribute('data-method'); + const option = this.getAttribute('data-option'); + const method = this.getAttribute('data-method'); if (method === 'scaleX') { cropperTool.scaleX(option); } else { @@ -101,7 +101,7 @@ define('pictureCropper', ['cropper'], function (Cropper) { cropperModal.find('.crop-btn').on('click', function () { $(this).addClass('disabled'); - var imageData = checkCORS(cropperTool, data); + const imageData = checkCORS(cropperTool, data); if (!imageData) { return; } @@ -144,15 +144,15 @@ define('pictureCropper', ['cropper'], function (Cropper) { }; function socketUpload(params, callback) { - var socketData = {}; + const socketData = {}; socketData[params.data.paramName] = params.data.paramValue; socketData.method = params.data.socketMethod; socketData.size = params.imageData.length; socketData.progress = 0; - var chunkSize = 100000; + const chunkSize = 100000; function doUpload() { - var chunk = params.imageData.slice(socketData.progress, socketData.progress + chunkSize); + const chunk = params.imageData.slice(socketData.progress, socketData.progress + chunkSize); socket.emit('uploads.upload', { chunk: chunk, params: socketData, @@ -174,13 +174,13 @@ define('pictureCropper', ['cropper'], function (Cropper) { } function checkCORS(cropperTool, data) { - var imageData; + let imageData; try { imageData = data.imageType ? cropperTool.getCroppedCanvas().toDataURL(data.imageType) : cropperTool.getCroppedCanvas().toDataURL(); } catch (err) { - var corsErrors = [ + const corsErrors = [ 'The operation is insecure.', 'Failed to execute \'toDataURL\' on \'HTMLCanvasElement\': Tainted canvases may not be exported.', ]; @@ -201,13 +201,13 @@ define('pictureCropper', ['cropper'], function (Cropper) { } data.uploadModal.find('#alert-' + type).translateText(message).removeClass('hide'); } - var fileInput = data.uploadModal.find('#fileInput'); + const fileInput = data.uploadModal.find('#fileInput'); if (!fileInput.val()) { return showAlert('error', '[[uploads:select-file-to-upload]]'); } - var file = fileInput[0].files[0]; - var fileSize = data.hasOwnProperty('fileSize') && data.fileSize !== undefined ? parseInt(data.fileSize, 10) : false; + const file = fileInput[0].files[0]; + const fileSize = data.hasOwnProperty('fileSize') && data.fileSize !== undefined ? parseInt(data.fileSize, 10) : false; if (fileSize && file.size > fileSize * 1024) { return showAlert('error', '[[error:file-too-big, ' + fileSize + ']]'); } @@ -219,9 +219,9 @@ define('pictureCropper', ['cropper'], function (Cropper) { return; } - var reader = new FileReader(); + const reader = new FileReader(); reader.addEventListener('load', function () { - var imageUrl = reader.result; + const imageUrl = reader.result; data.uploadModal.modal('hide'); diff --git a/public/src/modules/postSelect.js b/public/src/modules/postSelect.js index d2ca3ff81c..893d7c6142 100644 --- a/public/src/modules/postSelect.js +++ b/public/src/modules/postSelect.js @@ -2,12 +2,12 @@ define('postSelect', ['components'], function (components) { - var PostSelect = {}; - var onSelect; + const PostSelect = {}; + let onSelect; PostSelect.pids = []; - var allowMainPostSelect = false; + let allowMainPostSelect = false; PostSelect.init = function (_onSelect, options) { PostSelect.pids.length = 0; @@ -20,8 +20,8 @@ define('postSelect', ['components'], function (components) { function onPostClicked(ev) { ev.stopPropagation(); - var pidClicked = $(this).attr('data-pid'); - var postEls = $('[component="topic"] [data-pid="' + pidClicked + '"]'); + const pidClicked = $(this).attr('data-pid'); + const postEls = $('[component="topic"] [data-pid="' + pidClicked + '"]'); if (!allowMainPostSelect && parseInt($(this).attr('data-index'), 10) === 0) { return; } @@ -39,7 +39,7 @@ define('postSelect', ['components'], function (components) { PostSelect.togglePostSelection = function (postEls, pid) { if (pid) { - var index = PostSelect.pids.indexOf(pid); + const index = PostSelect.pids.indexOf(pid); if (index === -1) { PostSelect.pids.push(pid); postEls.toggleClass('bg-success', true); diff --git a/public/src/modules/scrollStop.js b/public/src/modules/scrollStop.js index 5e056f5fd9..c6134feefa 100644 --- a/public/src/modules/scrollStop.js +++ b/public/src/modules/scrollStop.js @@ -10,13 +10,13 @@ */ define('scrollStop', function () { - var Module = {}; + const Module = {}; Module.apply = function (element) { $(element).on('mousewheel', function (e) { - var scrollTop = this.scrollTop; - var scrollHeight = this.scrollHeight; - var elementHeight = Math.round(this.getBoundingClientRect().height); + const scrollTop = this.scrollTop; + const scrollHeight = this.scrollHeight; + const elementHeight = Math.round(this.getBoundingClientRect().height); if ( (e.originalEvent.deltaY < 0 && scrollTop === 0) || // scroll up diff --git a/public/src/modules/search.js b/public/src/modules/search.js index 536bebbe5d..1249435c69 100644 --- a/public/src/modules/search.js +++ b/public/src/modules/search.js @@ -1,32 +1,229 @@ 'use strict'; - -define('search', ['navigator', 'translator', 'storage', 'hooks'], function (nav, translator, storage, hooks) { - var Search = { +define('search', ['translator', 'storage', 'hooks'], function (translator, storage, hooks) { + const Search = { current: {}, }; - Search.query = function (data, callback) { - // Detect if a tid was specified - var topicSearch = data.term.match(/^in:topic-([\d]+) /); - callback = callback || function () {}; - if (!topicSearch) { - ajaxify.go('search?' + createQueryString(data)); - callback(); - } else { - var cleanedTerm = data.term.replace(topicSearch[0], ''); - var tid = topicSearch[1]; - - if (cleanedTerm.length > 0) { - Search.queryTopic(tid, cleanedTerm, callback); + Search.init = function (searchOptions) { + if (!config.searchEnabled) { + return; + } + + searchOptions = searchOptions || { in: config.searchDefaultInQuick || 'titles' }; + const searchButton = $('#search-button'); + const searchFields = $('#search-fields'); + const searchInput = $('#search-fields input'); + const quickSearchContainer = $('#quick-search-container'); + + $('#search-form .advanced-search-link').off('mousedown').on('mousedown', function () { + ajaxify.go('/search'); + }); + + $('#search-form').off('submit').on('submit', function () { + searchInput.blur(); + }); + searchInput.off('blur').on('blur', function dismissSearch() { + setTimeout(function () { + if (!searchInput.is(':focus')) { + searchFields.addClass('hidden'); + searchButton.removeClass('hidden'); + } + }, 200); + }); + searchInput.off('focus'); + + const searchElements = { + inputEl: searchInput, + resultEl: quickSearchContainer, + }; + + Search.enableQuickSearch({ + searchOptions: searchOptions, + searchElements: searchElements, + }); + + searchButton.off('click').on('click', function (e) { + if (!config.loggedIn && !app.user.privileges['search:content']) { + app.alert({ + message: '[[error:search-requires-login]]', + timeout: 3000, + }); + ajaxify.go('login'); + return false; } + e.stopPropagation(); + + Search.showAndFocusInput(); + return false; + }); + + $('#search-form').off('submit').on('submit', function () { + const input = $(this).find('input'); + const data = Search.getSearchPreferences(); + data.term = input.val(); + hooks.fire('action:search.submit', { + searchOptions: data, + searchElements: searchElements, + }); + Search.query(data, function () { + input.val(''); + }); + + return false; + }); + }; + + Search.enableQuickSearch = function (options) { + if (!config.searchEnabled || !app.user.privileges['search:content']) { + return; } + + const searchOptions = Object.assign({ in: config.searchDefaultInQuick || 'titles' }, options.searchOptions); + const quickSearchResults = options.searchElements.resultEl; + const inputEl = options.searchElements.inputEl; + let oldValue = inputEl.val(); + const filterCategoryEl = quickSearchResults.find('.filter-category'); + + function updateCategoryFilterName() { + if (ajaxify.data.template.category) { + translator.translate('[[search:search-in-category, ' + ajaxify.data.name + ']]', function (translated) { + const name = $('
    ').html(translated).text(); + filterCategoryEl.find('.name').text(name); + }); + } + filterCategoryEl.toggleClass('hidden', !ajaxify.data.template.category); + } + + function doSearch() { + options.searchOptions = Object.assign({}, searchOptions); + options.searchOptions.term = inputEl.val(); + updateCategoryFilterName(); + + if (ajaxify.data.template.category) { + if (filterCategoryEl.find('input[type="checkbox"]').is(':checked')) { + options.searchOptions.categories = [ajaxify.data.cid]; + options.searchOptions.searchChildren = true; + } + } + + quickSearchResults.removeClass('hidden').find('.quick-search-results-container').html(''); + quickSearchResults.find('.loading-indicator').removeClass('hidden'); + hooks.fire('action:search.quick.start', options); + options.searchOptions.searchOnly = 1; + Search.api(options.searchOptions, function (data) { + quickSearchResults.find('.loading-indicator').addClass('hidden'); + if (options.hideOnNoMatches && !data.posts.length) { + return quickSearchResults.addClass('hidden').find('.quick-search-results-container').html(''); + } + data.posts.forEach(function (p) { + const text = $('
    ' + p.content + '
    ').text(); + const query = inputEl.val().toLowerCase().replace(/^in:topic-\d+/, ''); + const start = Math.max(0, text.toLowerCase().indexOf(query) - 40); + p.snippet = utils.escapeHTML((start > 0 ? '...' : '') + + text.slice(start, start + 80) + + (text.length - start > 80 ? '...' : '')); + }); + app.parseAndTranslate('partials/quick-search-results', data, function (html) { + if (html.length) { + html.find('.timeago').timeago(); + } + quickSearchResults.toggleClass('hidden', !html.length || !inputEl.is(':focus')) + .find('.quick-search-results-container') + .html(html.length ? html : ''); + const highlightEls = quickSearchResults.find( + '.quick-search-results .quick-search-title, .quick-search-results .snippet' + ); + Search.highlightMatches(options.searchOptions.term, highlightEls); + hooks.fire('action:search.quick.complete', { + data: data, + options: options, + }); + }); + }); + } + + quickSearchResults.find('.filter-category input[type="checkbox"]').on('change', function () { + inputEl.focus(); + doSearch(); + }); + + inputEl.off('keyup').on('keyup', utils.debounce(function () { + if (inputEl.val().length < 3) { + quickSearchResults.addClass('hidden'); + oldValue = inputEl.val(); + return; + } + if (inputEl.val() === oldValue) { + return; + } + oldValue = inputEl.val(); + if (!inputEl.is(':focus')) { + return quickSearchResults.addClass('hidden'); + } + doSearch(); + }, 500)); + + let mousedownOnResults = false; + quickSearchResults.on('mousedown', function () { + $(window).one('mouseup', function () { + quickSearchResults.addClass('hidden'); + }); + mousedownOnResults = true; + }); + inputEl.on('blur', function () { + if (!inputEl.is(':focus') && !mousedownOnResults && !quickSearchResults.hasClass('hidden')) { + quickSearchResults.addClass('hidden'); + } + }); + + let ajaxified = false; + hooks.on('action:ajaxify.end', function () { + if (!ajaxify.isCold()) { + ajaxified = true; + } + }); + + inputEl.on('focus', function () { + mousedownOnResults = false; + const query = inputEl.val(); + oldValue = query; + if (query && quickSearchResults.find('#quick-search-results').children().length) { + updateCategoryFilterName(); + if (ajaxified) { + doSearch(); + ajaxified = false; + } else { + quickSearchResults.removeClass('hidden'); + } + inputEl[0].setSelectionRange( + query.startsWith('in:topic') ? query.indexOf(' ') + 1 : 0, + query.length + ); + } + }); + + inputEl.off('refresh').on('refresh', function () { + doSearch(); + }); + }; + + Search.showAndFocusInput = function () { + $('#search-fields').removeClass('hidden'); + $('#search-button').addClass('hidden'); + $('#search-fields input').focus(); + }; + + Search.query = function (data, callback) { + callback = callback || function () {}; + ajaxify.go('search?' + createQueryString(data)); + callback(); }; Search.api = function (data, callback) { - var apiURL = config.relative_path + '/api/search?' + createQueryString(data); + const apiURL = config.relative_path + '/api/search?' + createQueryString(data); data.searchOnly = undefined; - var searchURL = config.relative_path + '/search?' + createQueryString(data); + const searchURL = config.relative_path + '/search?' + createQueryString(data); $.get(apiURL, function (result) { result.url = searchURL; callback(result); @@ -34,16 +231,16 @@ define('search', ['navigator', 'translator', 'storage', 'hooks'], function (nav, }; function createQueryString(data) { - var searchIn = data.in || 'titles'; - var postedBy = data.by || ''; - var term = data.term.replace(/^[ ?#]*/, ''); + const searchIn = data.in || 'titles'; + const postedBy = data.by || ''; + let term = data.term.replace(/^[ ?#]*/, ''); try { term = encodeURIComponent(term); } catch (e) { return app.alertError('[[error:invalid-search-term]]'); } - var query = { + const query = { term: term, in: searchIn, }; @@ -111,14 +308,14 @@ define('search', ['navigator', 'translator', 'storage', 'hooks'], function (nav, return; } searchQuery = utils.escapeHTML(searchQuery.replace(/^"/, '').replace(/"$/, '').trim()); - var regexStr = searchQuery.split(' ') + const regexStr = searchQuery.split(' ') .map(function (word) { return utils.escapeRegexChars(word); }) .join('|'); - var regex = new RegExp('(' + regexStr + ')', 'gi'); + const regex = new RegExp('(' + regexStr + ')', 'gi'); els.each(function () { - var result = $(this); - var nested = []; + const result = $(this); + const nested = []; result.find('*').each(function () { $(this).after(''); @@ -139,102 +336,5 @@ define('search', ['navigator', 'translator', 'storage', 'hooks'], function (nav, $('.search-result-text').find('img:not(.not-responsive)').addClass('img-responsive'); }; - Search.queryTopic = function (tid, term) { - socket.emit('topics.search', { - tid: tid, - term: term, - }, function (err, pids) { - if (err) { - return app.alertError(err.message); - } - - if (Array.isArray(pids)) { - // Sort pids numerically & store - Search.current = { - results: pids.sort(function (a, b) { - return a - b; - }), - tid: tid, - term: term, - }; - - Search.checkPagePresence(tid, function () { - Search.topicDOM.update(0); - }); - } - }); - }; - - Search.checkPagePresence = function (tid, callback) { - if (parseInt(ajaxify.data.tid, 10) !== parseInt(tid, 10)) { - ajaxify.go('topic/' + tid, callback); - } else { - callback(); - } - }; - - Search.topicDOM = { - active: false, - }; - - Search.topicDOM.prev = function () { - Search.topicDOM.update((Search.current.index === 0) ? Search.current.results.length - 1 : Search.current.index - 1); - }; - - Search.topicDOM.next = function () { - Search.topicDOM.update((Search.current.index === Search.current.results.length - 1) ? 0 : Search.current.index + 1); - }; - - Search.topicDOM.update = function (index) { - var topicSearchEl = $('.topic-search'); - Search.current.index = index; - - Search.topicDOM.start(); - - if (Search.current.results.length > 0) { - topicSearchEl.find('.count').html((index + 1) + ' / ' + Search.current.results.length); - topicSearchEl.find('.prev, .next').removeAttr('disabled'); - var data = { - pid: Search.current.results[index], - tid: Search.current.tid, - topicPostSort: config.topicPostSort, - }; - socket.emit('posts.getPidIndex', data, function (err, postIndex) { - if (err) { - return app.alertError(err.message); - } - - nav.scrollToIndex(postIndex, true); - }); - } else { - translator.translate('[[search:no-matches]]', function (text) { - topicSearchEl.find('.count').html(text); - }); - topicSearchEl.removeClass('hidden').find('.prev, .next').attr('disabled', 'disabled'); - } - }; - - Search.topicDOM.start = function () { - $('.topic-search').removeClass('hidden'); - if (!Search.topicDOM.active) { - Search.topicDOM.active = true; - - // Bind to esc - require(['mousetrap'], function (mousetrap) { - mousetrap.bind('esc', Search.topicDOM.end); - }); - } - }; - - Search.topicDOM.end = function () { - $('.topic-search').addClass('hidden').find('.prev, .next').attr('disabled', 'disabled'); - Search.topicDOM.active = false; - - // Unbind esc - require(['mousetrap'], function (mousetrap) { - mousetrap.unbind('esc', Search.topicDOM.end); - }); - }; - return Search; }); diff --git a/public/src/modules/settings.js b/public/src/modules/settings.js index dde0a41e22..0d6258c537 100644 --- a/public/src/modules/settings.js +++ b/public/src/modules/settings.js @@ -2,7 +2,7 @@ define('settings', ['hooks'], function (hooks) { - var DEFAULT_PLUGINS = [ + const DEFAULT_PLUGINS = [ 'settings/checkbox', 'settings/number', 'settings/textarea', @@ -13,10 +13,12 @@ define('settings', ['hooks'], function (hooks) { 'settings/sorted-list', ]; - var Settings; - var onReady = []; - var waitingJobs = 0; - var helper; + // eslint-disable-next-line prefer-const + let Settings; + let onReady = []; + let waitingJobs = 0; + // eslint-disable-next-line prefer-const + let helper; /** Returns the hook of given name that matches the given type or element. @@ -24,23 +26,22 @@ define('settings', ['hooks'], function (hooks) { @param name The name of the hook. */ function getHook(type, name) { - var hook; - var plugin; if (typeof type !== 'string') { type = $(type); type = type.data('type') || type.attr('type') || type.prop('tagName'); } - plugin = Settings.plugins[type.toLowerCase()]; + const plugin = Settings.plugins[type.toLowerCase()]; if (plugin == null) { return; } - hook = plugin[name]; + const hook = plugin[name]; if (typeof hook === 'function') { return hook; } return null; } + // eslint-disable-next-line prefer-const helper = { /** @returns Object A deep clone of the given object. @@ -59,8 +60,8 @@ define('settings', ['hooks'], function (hooks) { @returns HTMLElement The created element. */ createElement: function (tagName, data, text) { - var element = document.createElement(tagName); - for (var k in data) { + const element = document.createElement(tagName); + for (const k in data) { if (data.hasOwnProperty(k)) { element.setAttribute(k, data[k]); } @@ -75,7 +76,7 @@ define('settings', ['hooks'], function (hooks) { @param element The element to initialize. */ initElement: function (element) { - var hook = getHook(element, 'init'); + const hook = getHook(element, 'init'); if (hook != null) { hook.call(Settings, $(element)); } @@ -85,7 +86,7 @@ define('settings', ['hooks'], function (hooks) { @param element The element to destruct. */ destructElement: function (element) { - var hook = getHook(element, 'destruct'); + const hook = getHook(element, 'destruct'); if (hook != null) { hook.call(Settings, $(element)); } @@ -98,8 +99,8 @@ define('settings', ['hooks'], function (hooks) { @returns JQuery The created element. */ createElementOfType: function (type, tagName, data) { - var element; - var hook = getHook(type, 'create'); + let element; + const hook = getHook(type, 'create'); if (hook != null) { element = $(hook.call(Settings, type, tagName, data)); } else { @@ -123,12 +124,12 @@ define('settings', ['hooks'], function (hooks) { @returns Array The filtered and/or modified Array. */ cleanArray: function (array, trim, empty) { - var cleaned = []; + const cleaned = []; if (!trim && empty) { return array; } - for (var i = 0; i < array.length; i += 1) { - var value = array[i]; + for (let i = 0; i < array.length; i += 1) { + let value = array[i]; if (trim) { if (value === !!value) { value = +value; @@ -155,18 +156,18 @@ define('settings', ['hooks'], function (hooks) { @returns Object The value of the element. */ readValue: function (element) { - var empty = !helper.isFalse(element.data('empty')); - var trim = !helper.isFalse(element.data('trim')); - var split = element.data('split'); - var hook = getHook(element, 'get'); - var value; + let empty = !helper.isFalse(element.data('empty')); + const trim = !helper.isFalse(element.data('trim')); + const split = element.data('split'); + const hook = getHook(element, 'get'); + let value; if (hook != null) { return hook.call(Settings, element, trim, empty); } if (split != null) { empty = helper.isTrue(element.data('empty')); // default empty-value is false for arrays value = element.val(); - var array = (value != null && value.split(split || ',')) || []; + const array = (value != null && value.split(split || ',')) || []; return helper.cleanArray(array, trim, empty); } value = element.val(); @@ -184,8 +185,8 @@ define('settings', ['hooks'], function (hooks) { @param value The value to set. */ fillField: function (element, value) { - var hook = getHook(element, 'set'); - var trim = element.data('trim'); + const hook = getHook(element, 'set'); + let trim = element.data('trim'); trim = trim !== 'false' && +trim !== 0; if (hook != null) { return hook.call(Settings, element, value, trim); @@ -219,14 +220,14 @@ define('settings', ['hooks'], function (hooks) { initFields: function (wrapper) { $('[data-key]', wrapper).each(function (ignored, field) { field = $(field); - var hook = getHook(field, 'init'); - var keyParts = field.data('key').split('.'); - var value = Settings.get(); + const hook = getHook(field, 'init'); + const keyParts = field.data('key').split('.'); + let value = Settings.get(); if (hook != null) { hook.call(Settings, field); } - for (var i = 0; i < keyParts.length; i += 1) { - var part = keyParts[i]; + for (let i = 0; i < keyParts.length; i += 1) { + const part = keyParts[i]; if (part && value != null) { value = value[part]; } @@ -254,7 +255,7 @@ define('settings', ['hooks'], function (hooks) { if (waitingJobs > 0) { waitingJobs -= amount; if (waitingJobs <= 0) { - for (var i = 0; i < onReady.length; i += 1) { + for (let i = 0; i < onReady.length; i += 1) { onReady[i](); } onReady = []; @@ -273,7 +274,7 @@ define('settings', ['hooks'], function (hooks) { } }, serializeForm: function (formEl) { - var values = formEl.serializeObject(); + const values = formEl.serializeObject(); // "Fix" checkbox values, so that unchecked options are not omitted formEl.find('input[type="checkbox"]').each(function (idx, inputEl) { @@ -341,7 +342,7 @@ define('settings', ['hooks'], function (hooks) { }, }; - + // eslint-disable-next-line prefer-const Settings = { helper: helper, plugins: {}, @@ -371,8 +372,8 @@ define('settings', ['hooks'], function (hooks) { if (typeof service.use === 'function') { service.use.call(Settings); } - for (var i = 0; i < types.length; i += 1) { - var type = types[i].toLowerCase(); + for (let i = 0; i < types.length; i += 1) { + const type = types[i].toLowerCase(); if (Settings.plugins[type] == null) { Settings.plugins[type] = service; } @@ -429,20 +430,20 @@ define('settings', ['hooks'], function (hooks) { @param notify Whether to send notification when settings got saved. */ persist: function (hash, wrapper, callback, notify) { - var notSaved = []; - var fields = $('[data-key]', wrapper || 'form').toArray(); + const notSaved = []; + const fields = $('[data-key]', wrapper || 'form').toArray(); if (notify == null) { notify = true; } - for (var i = 0; i < fields.length; i += 1) { - var field = $(fields[i]); - var value = helper.readValue(field); - var parentCfg = Settings.get(); - var keyParts = field.data('key').split('.'); - var lastKey = keyParts[keyParts.length - 1]; + for (let i = 0; i < fields.length; i += 1) { + const field = $(fields[i]); + const value = helper.readValue(field); + let parentCfg = Settings.get(); + const keyParts = field.data('key').split('.'); + const lastKey = keyParts[keyParts.length - 1]; if (keyParts.length > 1) { - for (var j = 0; j < keyParts.length - 1; j += 1) { - var part = keyParts[j]; + for (let j = 0; j < keyParts.length - 1; j += 1) { + const part = keyParts[j]; if (part && parentCfg != null) { parentCfg = parentCfg[part]; } @@ -470,7 +471,7 @@ define('settings', ['hooks'], function (hooks) { }, load: function (hash, formEl, callback) { callback = callback || function () {}; - var call = formEl.attr('data-socket-get'); + const call = formEl.attr('data-socket-get'); socket.emit(call || 'admin.settings.get', { hash: hash, @@ -480,7 +481,7 @@ define('settings', ['hooks'], function (hooks) { } // multipe selects are saved as json arrays, parse them here $(formEl).find('select[multiple]').each(function (idx, selectEl) { - var key = $(selectEl).attr('name'); + const key = $(selectEl).attr('name'); if (key && values.hasOwnProperty(key)) { try { values[key] = JSON.parse(values[key]); @@ -511,7 +512,7 @@ define('settings', ['hooks'], function (hooks) { app.flags._unsaved = true; }); - var saveEl = document.getElementById('save'); + const saveEl = document.getElementById('save'); if (saveEl) { require(['mousetrap'], function (mousetrap) { mousetrap.bind('ctrl+s', function (ev) { @@ -528,10 +529,10 @@ define('settings', ['hooks'], function (hooks) { formEl = $(formEl); if (formEl.length) { - var values = helper.serializeForm(formEl); + const values = helper.serializeForm(formEl); helper.whenReady(function () { - var list = formEl.find('[data-sorted-list]'); + const list = formEl.find('[data-sorted-list]'); if (list.length) { list.each((idx, item) => { getHook(item, 'set').call(Settings, $(item), values); @@ -539,7 +540,7 @@ define('settings', ['hooks'], function (hooks) { } }); - var call = formEl.attr('data-socket-set'); + const call = formEl.attr('data-socket-set'); socket.emit(call || 'admin.settings.set', { hash: hash, values: values, @@ -573,7 +574,7 @@ define('settings', ['hooks'], function (hooks) { helper.registerReadyJobs(1); require(DEFAULT_PLUGINS, function () { - for (var i = 0; i < arguments.length; i += 1) { + for (let i = 0; i < arguments.length; i += 1) { Settings.registerPlugin(arguments[i]); } helper.beforeReadyJobsDecreased(); diff --git a/public/src/modules/settings/array.js b/public/src/modules/settings/array.js index 9b51aee582..6c1ee1395e 100644 --- a/public/src/modules/settings/array.js +++ b/public/src/modules/settings/array.js @@ -1,8 +1,7 @@ 'use strict'; define('settings/array', function () { - var SettingsArray; - var helper = null; + let helper = null; /** Creates a new button that removes itself and the given elements on click. @@ -11,7 +10,7 @@ define('settings/array', function () { @returns JQuery The created remove-button. */ function createRemoveButton(elements) { - var rm = $(helper.createElement('button', { + const rm = $(helper.createElement('button', { class: 'btn btn-xs btn-primary remove', title: 'Remove Item', }, '-')); @@ -41,14 +40,14 @@ define('settings/array', function () { */ function addArrayChildElement(field, key, attributes, value, separator, insertCb) { attributes = helper.deepClone(attributes); - var type = attributes['data-type'] || attributes.type || 'text'; - var element = $(helper.createElementOfType(type, attributes.tagName, attributes)); + const type = attributes['data-type'] || attributes.type || 'text'; + const element = $(helper.createElementOfType(type, attributes.tagName, attributes)); element.attr('data-parent', '_' + key); delete attributes['data-type']; delete attributes.tagName; - for (var name in attributes) { + for (const name in attributes) { if (attributes.hasOwnProperty(name)) { - var val = attributes[name]; + const val = attributes[name]; if (name.search('data-') === 0) { element.data(name.substring(5), val); } else if (name.search('prop-') === 0) { @@ -74,9 +73,9 @@ define('settings/array', function () { @param separator The separator to forward to {@link addArrayChildElement}. */ function addAddButton(element, key, attributes, separator) { - var addSpace = $(document.createTextNode(' ')); - var newValue = element.data('new') || ''; - var add = $(helper.createElement('button', { + const addSpace = $(document.createTextNode(' ')); + const newValue = element.data('new') || ''; + const add = $(helper.createElement('button', { class: 'btn btn-sm btn-primary add', title: 'Expand Array', }, '+')); @@ -91,7 +90,7 @@ define('settings/array', function () { } - SettingsArray = { + const SettingsArray = { types: ['array', 'div'], use: function () { helper = this.helper; @@ -100,9 +99,9 @@ define('settings/array', function () { return helper.createElement(tagName || 'div'); }, set: function (element, value) { - var attributes = element.data('attributes'); - var key = element.data('key') || element.data('parent'); - var separator = element.data('split') || ', '; + let attributes = element.data('attributes'); + const key = element.data('key') || element.data('parent'); + let separator = element.data('split') || ', '; separator = (function () { try { return $(separator); @@ -117,7 +116,7 @@ define('settings/array', function () { if (!(value instanceof Array)) { value = []; } - for (var i = 0; i < value.length; i += 1) { + for (let i = 0; i < value.length; i += 1) { addArrayChildElement(element, key, attributes, value[i], separator.clone(), function (el) { element.append(el); }); @@ -125,13 +124,13 @@ define('settings/array', function () { addAddButton(element, key, attributes, separator); }, get: function (element, trim, empty) { - var key = element.data('key') || element.data('parent'); - var children = $('[data-parent="_' + key + '"]', element); - var values = []; + const key = element.data('key') || element.data('parent'); + const children = $('[data-parent="_' + key + '"]', element); + const values = []; children.each(function (i, child) { child = $(child); - var val = helper.readValue(child); - var empty = helper.isTrue(child.data('empty')); + const val = helper.readValue(child); + const empty = helper.isTrue(child.data('empty')); if (empty || (val !== undefined && (val == null || val.length !== 0))) { return values.push(val); } diff --git a/public/src/modules/settings/checkbox.js b/public/src/modules/settings/checkbox.js index d64212d4e0..8657eeda9f 100644 --- a/public/src/modules/settings/checkbox.js +++ b/public/src/modules/settings/checkbox.js @@ -1,10 +1,9 @@ 'use strict'; define('settings/checkbox', function () { - var Settings = null; - var SettingsCheckbox; + let Settings = null; - SettingsCheckbox = { + const SettingsCheckbox = { types: ['checkbox'], use: function () { Settings = this; @@ -19,7 +18,7 @@ define('settings/checkbox', function () { element.closest('.mdl-switch').toggleClass('is-checked', element.is(':checked')); }, get: function (element, trim, empty) { - var value = element.prop('checked'); + const value = element.prop('checked'); if (value == null) { return; } diff --git a/public/src/modules/settings/key.js b/public/src/modules/settings/key.js index fd0b41e926..8148ab58d7 100644 --- a/public/src/modules/settings/key.js +++ b/public/src/modules/settings/key.js @@ -1,11 +1,10 @@ 'use strict'; define('settings/key', function () { - var SettingsKey; - var helper = null; - var lastKey = null; - var oldKey = null; - var keyMap = Object.freeze({ + let helper = null; + let lastKey = null; + let oldKey = null; + const keyMap = Object.freeze({ 0: '', 8: 'Backspace', 9: 'Tab', @@ -42,13 +41,13 @@ define('settings/key', function () { @returns Key | null The Key-Object the focused element should be set to. */ function getKey(event) { - var anyModChange = ( + const anyModChange = ( event.ctrlKey !== lastKey.c || event.altKey !== lastKey.a || event.shiftKey !== lastKey.s || event.metaKey !== lastKey.m ); - var modChange = ( + const modChange = ( event.ctrlKey + event.altKey + event.shiftKey + @@ -58,7 +57,7 @@ define('settings/key', function () { lastKey.s - lastKey.m ); - var key = new Key(); + const key = new Key(); key.c = event.ctrlKey; key.a = event.altKey; key.s = event.shiftKey; @@ -104,7 +103,7 @@ define('settings/key', function () { @returns String The string to identify the given key-object the given way. */ function getKeyString(key, human, short, separator) { - var str = ''; + let str = ''; if (!(key instanceof Key)) { return str; } @@ -130,7 +129,7 @@ define('settings/key', function () { str += (short ? 'M' : 'Meta') + separator; } - var out; + let out; if (human) { out = key.char; } else if (key.code) { @@ -149,11 +148,11 @@ define('settings/key', function () { if (str instanceof Key) { return str; } - var key = new Key(); - var sep = /([^CtrlAShifMea#\d]+)(?:#|\d)/.exec(str); - var parts = sep != null ? str.split(sep[1]) : [str]; - for (var i = 0; i < parts.length; i += 1) { - var part = parts[i]; + const key = new Key(); + const sep = /([^CtrlAShifMea#\d]+)(?:#|\d)/.exec(str); + const parts = sep != null ? str.split(sep[1]) : [str]; + for (let i = 0; i < parts.length; i += 1) { + const part = parts[i]; switch (part) { case 'C': case 'Ctrl': @@ -171,28 +170,20 @@ define('settings/key', function () { case 'Meta': key.m = true; break; - default: - var num = /\d+/.exec(part); + default: { + const num = /\d+/.exec(part); if (num != null) { key.code = num[0]; } key.char = convertKeyCodeToChar(key.code); + } } } return key; } - function handleEvent(element, event) { - event = event || window.event; - event.which = event.which || event.keyCode || event.key; - var key = getKey(event); - if (key != null) { - SettingsKey.set(element, key); - } - } - - SettingsKey = { + const SettingsKey = { types: ['key'], use: function () { helper = this.helper; @@ -210,7 +201,7 @@ define('settings/key', function () { return element; }, set: function (element, value) { - var key = getKeyFromString(value || ''); + const key = getKeyFromString(value || ''); element.data('keyData', key); if (key.code) { element.removeClass('alert-danger'); @@ -220,9 +211,9 @@ define('settings/key', function () { element.val(getKeyString(key, true, false, ' + ')); }, get: function (element, trim, empty) { - var key = element.data('keyData'); - var separator = element.data('split') || element.data('separator') || '+'; - var short = !helper.isFalse(element.data('short')); + const key = element.data('keyData'); + const separator = element.data('split') || element.data('separator') || '+'; + const short = !helper.isFalse(element.data('short')); if (trim) { if (empty || (key != null && key.char)) { return getKeyString(key, false, short, separator); @@ -233,5 +224,14 @@ define('settings/key', function () { }, }; + function handleEvent(element, event) { + event = event || window.event; + event.which = event.which || event.keyCode || event.key; + const key = getKey(event); + if (key != null) { + SettingsKey.set(element, key); + } + } + return SettingsKey; }); diff --git a/public/src/modules/settings/number.js b/public/src/modules/settings/number.js index 12ff231076..c3eea1c8c0 100644 --- a/public/src/modules/settings/number.js +++ b/public/src/modules/settings/number.js @@ -4,7 +4,7 @@ define('settings/number', function () { return { types: ['number'], get: function (element, trim, empty) { - var value = element.val(); + const value = element.val(); if (!empty) { if (value) { return +value; diff --git a/public/src/modules/settings/object.js b/public/src/modules/settings/object.js index 92c91a041f..b6a7398bbc 100644 --- a/public/src/modules/settings/object.js +++ b/public/src/modules/settings/object.js @@ -1,8 +1,7 @@ 'use strict'; define('settings/object', function () { - var SettingsObject; - var helper = null; + let helper = null; /** Creates a new child-element of given property with given data and calls given callback with elements to add. @@ -15,22 +14,20 @@ define('settings/object', function () { @param insertCb The callback to insert the elements. */ function addObjectPropertyElement(field, key, attributes, prop, value, separator, insertCb) { - var prepend = attributes['data-prepend']; - var append = attributes['data-append']; - var type; - var element; + const prepend = attributes['data-prepend']; + const append = attributes['data-append']; delete attributes['data-prepend']; delete attributes['data-append']; attributes = helper.deepClone(attributes); - type = attributes['data-type'] || attributes.type || 'text'; - element = $(helper.createElementOfType(type, attributes.tagName, attributes)); + const type = attributes['data-type'] || attributes.type || 'text'; + const element = $(helper.createElementOfType(type, attributes.tagName, attributes)); element.attr('data-parent', '_' + key); element.attr('data-prop', prop); delete attributes['data-type']; delete attributes.tagName; - for (var name in attributes) { + for (const name in attributes) { if (attributes.hasOwnProperty(name)) { - var val = attributes[name]; + const val = attributes[name]; if (name.search('data-') === 0) { element.data(name.substring(5), val); } else if (name.search('prop-') === 0) { @@ -53,7 +50,7 @@ define('settings/object', function () { } } - SettingsObject = { + const SettingsObject = { types: ['object'], use: function () { helper = this.helper; @@ -62,12 +59,12 @@ define('settings/object', function () { return helper.createElement(tagName || 'div'); }, set: function (element, value) { - var properties = element.data('attributes') || element.data('properties'); - var key = element.data('key') || element.data('parent'); - var separator = element.data('split') || ', '; - var propertyIndex; - var propertyName; - var attributes; + const properties = element.data('attributes') || element.data('properties'); + const key = element.data('key') || element.data('parent'); + let separator = element.data('split') || ', '; + let propertyIndex; + let propertyName; + let attributes; separator = (function () { try { return $(separator); @@ -104,14 +101,14 @@ define('settings/object', function () { } }, get: function (element, trim, empty) { - var key = element.data('key') || element.data('parent'); - var properties = $('[data-parent="_' + key + '"]', element); - var value = {}; + const key = element.data('key') || element.data('parent'); + const properties = $('[data-parent="_' + key + '"]', element); + const value = {}; properties.each(function (i, property) { property = $(property); - var val = helper.readValue(property); - var prop = property.data('prop'); - var empty = helper.isTrue(property.data('empty')); + const val = helper.readValue(property); + const prop = property.data('prop'); + const empty = helper.isTrue(property.data('empty')); if (empty || (val !== undefined && (val == null || val.length !== 0))) { value[prop] = val; return val; diff --git a/public/src/modules/settings/select.js b/public/src/modules/settings/select.js index 1b28774871..27c13c2831 100644 --- a/public/src/modules/settings/select.js +++ b/public/src/modules/settings/select.js @@ -1,33 +1,32 @@ 'use strict'; define('settings/select', function () { - var Settings = null; - var SettingsSelect; + let Settings = null; function addOptions(element, options) { - for (var i = 0; i < options.length; i += 1) { - var optionData = options[i]; - var value = optionData.text || optionData.value; + for (let i = 0; i < options.length; i += 1) { + const optionData = options[i]; + const value = optionData.text || optionData.value; delete optionData.text; element.append($(Settings.helper.createElement('option', optionData)).text(value)); } } - SettingsSelect = { + const SettingsSelect = { types: ['select'], use: function () { Settings = this; }, create: function (ignore, ignored, data) { - var element = $(Settings.helper.createElement('select')); + const element = $(Settings.helper.createElement('select')); // prevent data-options from being attached to DOM addOptions(element, data['data-options']); delete data['data-options']; return element; }, init: function (element) { - var options = element.data('options'); + const options = element.data('options'); if (options != null) { addOptions(element, options); } @@ -36,7 +35,7 @@ define('settings/select', function () { element.val(value || ''); }, get: function (element, ignored, empty) { - var value = element.val(); + const value = element.val(); if (empty || value) { return value; } diff --git a/public/src/modules/settings/sorted-list.js b/public/src/modules/settings/sorted-list.js index 806c6b148f..367999161e 100644 --- a/public/src/modules/settings/sorted-list.js +++ b/public/src/modules/settings/sorted-list.js @@ -2,53 +2,53 @@ define('settings/sorted-list', [ 'benchpress', + 'bootbox', 'hooks', 'jquery-ui/widgets/sortable', -], function (benchpress, hooks) { - var SortedList; - var Settings; +], function (benchpress, bootbox, hooks) { + let Settings; - SortedList = { + const SortedList = { types: ['sorted-list'], use: function () { Settings = this; }, set: function ($container, values) { - var key = $container.attr('data-sorted-list'); + const key = $container.attr('data-sorted-list'); values[key] = []; $container.find('[data-type="item"]').each(function (idx, item) { - var itemUUID = $(item).attr('data-sorted-list-uuid'); + const itemUUID = $(item).attr('data-sorted-list-uuid'); - var formData = Settings.helper.serializeForm($('[data-sorted-list-object="' + key + '"][data-sorted-list-uuid="' + itemUUID + '"]')); + const formData = Settings.helper.serializeForm($('[data-sorted-list-object="' + key + '"][data-sorted-list-uuid="' + itemUUID + '"]')); stripTags(formData); values[key].push(formData); }); }, get: function ($container, hash) { - var $list = $container.find('[data-type="list"]'); - var key = $container.attr('data-sorted-list'); - var formTpl = $container.attr('data-form-template'); + const $list = $container.find('[data-type="list"]'); + const key = $container.attr('data-sorted-list'); + const formTpl = $container.attr('data-form-template'); benchpress.render(formTpl, {}).then(function (formHtml) { - var addBtn = $('[data-sorted-list="' + key + '"] [data-type="add"]'); + const addBtn = $('[data-sorted-list="' + key + '"] [data-type="add"]'); addBtn.on('click', function () { - var modal = bootbox.confirm(formHtml, function (save) { + const modal = bootbox.confirm(formHtml, function (save) { if (save) { SortedList.addItem(modal.find('form').children(), $container); } }); }); - var call = $container.parents('form').attr('data-socket-get'); - var list = ajaxify.data[call ? hash : 'settings'][key]; + const call = $container.parents('form').attr('data-socket-get'); + const list = ajaxify.data[call ? hash : 'settings'][key]; if (Array.isArray(list) && typeof list[0] !== 'string') { list.forEach(function (item) { - var itemUUID = utils.generateUUID(); - var form = $(formHtml).deserialize(item); + const itemUUID = utils.generateUUID(); + const form = $(formHtml).deserialize(item); form.attr('data-sorted-list-uuid', itemUUID); form.attr('data-sorted-list-object', key); $('#content').append(form.hide()); @@ -76,36 +76,36 @@ define('settings/sorted-list', [ }; function setupRemoveButton($container, itemUUID) { - var removeBtn = $container.find('[data-sorted-list-uuid="' + itemUUID + '"] [data-type="remove"]'); + const removeBtn = $container.find('[data-sorted-list-uuid="' + itemUUID + '"] [data-type="remove"]'); removeBtn.on('click', function () { $('[data-sorted-list-uuid="' + itemUUID + '"]').remove(); }); } function setupEditButton($container, itemUUID) { - var $list = $container.find('[data-type="list"]'); - var key = $container.attr('data-sorted-list'); - var itemTpl = $container.attr('data-item-template'); - var editBtn = $('[data-sorted-list-uuid="' + itemUUID + '"] [data-type="edit"]'); + const $list = $container.find('[data-type="list"]'); + const key = $container.attr('data-sorted-list'); + const itemTpl = $container.attr('data-item-template'); + const editBtn = $('[data-sorted-list-uuid="' + itemUUID + '"] [data-type="edit"]'); editBtn.on('click', function () { - var form = $('[data-sorted-list-uuid="' + itemUUID + '"][data-sorted-list-object="' + key + '"]').clone(true).show(); + const form = $('[data-sorted-list-uuid="' + itemUUID + '"][data-sorted-list-object="' + key + '"]').clone(true).show(); - var modal = bootbox.confirm(form, function (save) { + const modal = bootbox.confirm(form, function (save) { if (save) { - var form = $('
    '); + const form = $('
    '); form.append(modal.find('form').children()); $('#content').find('[data-sorted-list-uuid="' + itemUUID + '"][data-sorted-list-object="' + key + '"]').remove(); $('#content').append(form.hide()); - var data = Settings.helper.serializeForm(form); + const data = Settings.helper.serializeForm(form); stripTags(data); app.parseAndTranslate(itemTpl, data, function (itemHtml) { itemHtml = $(itemHtml); - var oldItem = $list.find('[data-sorted-list-uuid="' + itemUUID + '"]'); + const oldItem = $list.find('[data-sorted-list-uuid="' + itemUUID + '"]'); oldItem.after(itemHtml); oldItem.remove(); itemHtml.attr('data-sorted-list-uuid', itemUUID); @@ -119,8 +119,8 @@ define('settings/sorted-list', [ } function parse($container, itemUUID, data) { - var $list = $container.find('[data-type="list"]'); - var itemTpl = $container.attr('data-item-template'); + const $list = $container.find('[data-type="list"]'); + const itemTpl = $container.attr('data-item-template'); stripTags(data); diff --git a/public/src/modules/settings/textarea.js b/public/src/modules/settings/textarea.js index cc36e90762..9c6938fef3 100644 --- a/public/src/modules/settings/textarea.js +++ b/public/src/modules/settings/textarea.js @@ -1,10 +1,9 @@ 'use strict'; define('settings/textarea', function () { - var Settings = null; - var SettingsArea; + let Settings = null; - SettingsArea = { + const SettingsArea = { types: ['textarea'], use: function () { Settings = this; @@ -19,7 +18,7 @@ define('settings/textarea', function () { element.val(value || ''); }, get: function (element, trim, empty) { - var value = element.val(); + let value = element.val(); if (trim) { if (value == null) { value = undefined; diff --git a/public/src/modules/share.js b/public/src/modules/share.js index 946135e986..7d414086cb 100644 --- a/public/src/modules/share.js +++ b/public/src/modules/share.js @@ -2,10 +2,10 @@ define('share', ['hooks'], function (hooks) { - var module = {}; + const module = {}; module.addShareHandlers = function (name) { - var baseUrl = window.location.protocol + '//' + window.location.host; + const baseUrl = window.location.protocol + '//' + window.location.host; function openShare(url, urlToPost, width, height) { window.open(url + encodeURIComponent(baseUrl + config.relative_path + urlToPost), '_blank', 'width=' + width + ',height=' + height + ',scrollbars=no,status=no'); @@ -17,7 +17,7 @@ define('share', ['hooks'], function (hooks) { } $('#content').off('shown.bs.dropdown', '.share-dropdown').on('shown.bs.dropdown', '.share-dropdown', function () { - var postLink = $(this).find('.post-link'); + const postLink = $(this).find('.post-link'); postLink.val(baseUrl + getPostUrl($(this))); // without the setTimeout can't select the text in the input @@ -51,7 +51,7 @@ define('share', ['hooks'], function (hooks) { } function getPostUrl(clickedElement) { - var pid = parseInt(clickedElement.parents('[data-pid]').attr('data-pid'), 10); + const pid = parseInt(clickedElement.parents('[data-pid]').attr('data-pid'), 10); return '/post' + (pid ? '/' + (pid) : ''); } diff --git a/public/src/modules/slugify.js b/public/src/modules/slugify.js index 56b72bbe4a..3046ed2b94 100644 --- a/public/src/modules/slugify.js +++ b/public/src/modules/slugify.js @@ -10,14 +10,14 @@ window.slugify = factory(XRegExp); } }(function (XRegExp) { - var invalidUnicodeChars = XRegExp('[^\\p{L}\\s\\d\\-_]', 'g'); - var invalidLatinChars = /[^\w\s\d\-_]/g; - var trimRegex = /^\s+|\s+$/g; - var collapseWhitespace = /\s+/g; - var collapseDash = /-+/g; - var trimTrailingDash = /-$/g; - var trimLeadingDash = /^-/g; - var isLatin = /^[\w\d\s.,\-@]+$/; + const invalidUnicodeChars = XRegExp('[^\\p{L}\\s\\d\\-_]', 'g'); + const invalidLatinChars = /[^\w\s\d\-_]/g; + const trimRegex = /^\s+|\s+$/g; + const collapseWhitespace = /\s+/g; + const collapseDash = /-+/g; + const trimTrailingDash = /-$/g; + const trimLeadingDash = /^-/g; + const isLatin = /^[\w\d\s.,\-@]+$/; // http://dense13.com/blog/2009/05/03/converting-string-to-slug-javascript/ return function slugify(str, preserveCase) { diff --git a/public/src/modules/sort.js b/public/src/modules/sort.js index de83eb49d1..783b7185ae 100644 --- a/public/src/modules/sort.js +++ b/public/src/modules/sort.js @@ -2,12 +2,12 @@ define('sort', ['components', 'api'], function (components, api) { - var module = {}; + const module = {}; module.handleSort = function (field, gotoOnSave) { - var threadSort = components.get('thread/sort'); + const threadSort = components.get('thread/sort'); threadSort.find('i').removeClass('fa-check'); - var currentSetting = threadSort.find('a[data-sort="' + config[field] + '"]'); + const currentSetting = threadSort.find('a[data-sort="' + config[field] + '"]'); currentSetting.find('i').addClass('fa-check'); $('body') @@ -15,10 +15,10 @@ define('sort', ['components', 'api'], function (components, api) { .on('click', '[component="thread/sort"] a', function () { function refresh(newSetting, params) { config[field] = newSetting; - var qs = decodeURIComponent($.param(params)); + const qs = decodeURIComponent($.param(params)); ajaxify.go(gotoOnSave + (qs ? '?' + qs : '')); } - var newSetting = $(this).attr('data-sort'); + const newSetting = $(this).attr('data-sort'); if (app.user.uid) { const payload = { settings: {} }; payload.settings[field] = newSetting; @@ -26,7 +26,7 @@ define('sort', ['components', 'api'], function (components, api) { refresh(newSetting, utils.params()); }); } else { - var urlParams = utils.params(); + const urlParams = utils.params(); urlParams.sort = newSetting; refresh(newSetting, urlParams); } diff --git a/public/src/modules/storage.js b/public/src/modules/storage.js index bccaabcf6b..90db00049e 100644 --- a/public/src/modules/storage.js +++ b/public/src/modules/storage.js @@ -47,8 +47,8 @@ define('storage', function () { }); } - var storage; - var item = Date.now().toString(); + let storage; + const item = Date.now().toString(); try { storage = window.localStorage; diff --git a/public/src/modules/taskbar.js b/public/src/modules/taskbar.js index 1c8b2165bb..ce811bc046 100644 --- a/public/src/modules/taskbar.js +++ b/public/src/modules/taskbar.js @@ -2,10 +2,10 @@ define('taskbar', ['benchpress', 'translator', 'hooks'], function (Benchpress, translator, hooks) { - var taskbar = {}; + const taskbar = {}; taskbar.init = function () { - var self = this; + const self = this; Benchpress.render('modules/taskbar', {}).then(function (html) { self.taskbar = $(html); @@ -13,9 +13,9 @@ define('taskbar', ['benchpress', 'translator', 'hooks'], function (Benchpress, t $(document.body).append(self.taskbar); self.taskbar.on('click', 'li', function () { - var $btn = $(this); - var module = $btn.attr('data-module'); - var uuid = $btn.attr('data-uuid'); + const $btn = $(this); + const module = $btn.attr('data-module'); + const uuid = $btn.attr('data-uuid'); require([module], function (module) { if (!$btn.hasClass('active')) { @@ -41,8 +41,8 @@ define('taskbar', ['benchpress', 'translator', 'hooks'], function (Benchpress, t taskbar.close = function (module, uuid) { // Sends signal to the appropriate module's .close() fn (if present) - var btnEl = taskbar.tasklist.find('[data-module="' + module + '"][data-uuid="' + uuid + '"]'); - var fnName = 'close'; + const btnEl = taskbar.tasklist.find('[data-module="' + module + '"][data-uuid="' + uuid + '"]'); + let fnName = 'close'; // TODO: Refactor chat module to not take uuid in close instead of by jQuery element if (module === 'chat') { @@ -60,7 +60,7 @@ define('taskbar', ['benchpress', 'translator', 'hooks'], function (Benchpress, t taskbar.closeAll = function (module) { // module is optional - var selector = '[data-uuid]'; + let selector = '[data-uuid]'; if (module) { selector = '[data-module="' + module + '"]' + selector; @@ -72,7 +72,7 @@ define('taskbar', ['benchpress', 'translator', 'hooks'], function (Benchpress, t }; taskbar.discard = function (module, uuid) { - var btnEl = taskbar.tasklist.find('[data-module="' + module + '"][data-uuid="' + uuid + '"]'); + const btnEl = taskbar.tasklist.find('[data-module="' + module + '"][data-uuid="' + uuid + '"]'); btnEl.remove(); update(); @@ -80,9 +80,9 @@ define('taskbar', ['benchpress', 'translator', 'hooks'], function (Benchpress, t taskbar.push = function (module, uuid, options, callback) { callback = callback || function () {}; - var element = taskbar.tasklist.find('li[data-uuid="' + uuid + '"]'); + const element = taskbar.tasklist.find('li[data-uuid="' + uuid + '"]'); - var data = { + const data = { module: module, uuid: uuid, options: options, @@ -99,7 +99,7 @@ define('taskbar', ['benchpress', 'translator', 'hooks'], function (Benchpress, t }; taskbar.get = function (module) { - var items = $('[data-module="' + module + '"]').map(function (idx, el) { + const items = $('[data-module="' + module + '"]').map(function (idx, el) { return $(el).data(); }); @@ -107,12 +107,12 @@ define('taskbar', ['benchpress', 'translator', 'hooks'], function (Benchpress, t }; taskbar.minimize = function (module, uuid) { - var btnEl = taskbar.tasklist.find('[data-module="' + module + '"][data-uuid="' + uuid + '"]'); + const btnEl = taskbar.tasklist.find('[data-module="' + module + '"][data-uuid="' + uuid + '"]'); btnEl.toggleClass('active', false); }; taskbar.toggleNew = function (uuid, state, silent) { - var btnEl = taskbar.tasklist.find('[data-uuid="' + uuid + '"]'); + const btnEl = taskbar.tasklist.find('[data-uuid="' + uuid + '"]'); btnEl.toggleClass('new', state); if (!silent) { @@ -121,7 +121,7 @@ define('taskbar', ['benchpress', 'translator', 'hooks'], function (Benchpress, t }; taskbar.updateActive = function (uuid) { - var tasks = taskbar.tasklist.find('li'); + const tasks = taskbar.tasklist.find('li'); tasks.removeClass('active'); tasks.filter('[data-uuid="' + uuid + '"]').addClass('active'); @@ -130,12 +130,12 @@ define('taskbar', ['benchpress', 'translator', 'hooks'], function (Benchpress, t }; taskbar.isActive = function (uuid) { - var taskBtn = taskbar.tasklist.find('li[data-uuid="' + uuid + '"]'); + const taskBtn = taskbar.tasklist.find('li[data-uuid="' + uuid + '"]'); return taskBtn.hasClass('active'); }; function update() { - var tasks = taskbar.tasklist.find('li'); + const tasks = taskbar.tasklist.find('li'); if (tasks.length > 0) { taskbar.taskbar.attr('data-active', '1'); @@ -150,9 +150,9 @@ define('taskbar', ['benchpress', 'translator', 'hooks'], function (Benchpress, t function createTaskbarItem(data, callback) { translator.translate(data.options.title, function (taskTitle) { - var title = $('
    ').text(taskTitle || 'NodeBB Task').html(); + const title = $('
    ').text(taskTitle || 'NodeBB Task').html(); - var taskbarEl = $('
  • ') + const taskbarEl = $('
  • ') .addClass(data.options.className) .html('' + (data.options.icon ? ' ' : '') + @@ -180,7 +180,7 @@ define('taskbar', ['benchpress', 'translator', 'hooks'], function (Benchpress, t }); } - var processUpdate = function (element, key, value) { + const processUpdate = function (element, key, value) { switch (key) { case 'title': element.find('[component="taskbar/title"]').text(value); @@ -198,11 +198,11 @@ define('taskbar', ['benchpress', 'translator', 'hooks'], function (Benchpress, t }; taskbar.update = function (module, uuid, options) { - var element = taskbar.tasklist.find('[data-module="' + module + '"][data-uuid="' + uuid + '"]'); + const element = taskbar.tasklist.find('[data-module="' + module + '"][data-uuid="' + uuid + '"]'); if (!element.length) { return; } - var data = element.data(); + const data = element.data(); Object.keys(options).forEach(function (key) { data[key] = options[key]; diff --git a/public/src/modules/topicList.js b/public/src/modules/topicList.js index b85e04aef9..1ce4016364 100644 --- a/public/src/modules/topicList.js +++ b/public/src/modules/topicList.js @@ -8,21 +8,14 @@ define('topicList', [ 'forum/category/tools', 'hooks', ], function (infinitescroll, handleBack, topicSelect, categoryFilter, categoryTools, hooks) { - var TopicList = {}; - var templateName = ''; - - var tplToSort = { - recent: 'recent', - unread: 'unread', - popular: 'posts', - top: 'votes', - }; + const TopicList = {}; + let templateName = ''; - var newTopicCount = 0; - var newPostCount = 0; + let newTopicCount = 0; + let newPostCount = 0; - var loadTopicsCallback; - var topicListEl; + let loadTopicsCallback; + let topicListEl; const scheduledTopics = []; @@ -40,7 +33,7 @@ define('topicList', [ categoryTools.init(); TopicList.watchForNewPosts(); - var states = ['watching']; + const states = ['watching']; if (ajaxify.data.selectedFilter && ajaxify.data.selectedFilter.filter === 'watched') { states.push('notwatching', 'ignoring'); } else if (template !== 'unread') { @@ -120,7 +113,7 @@ define('topicList', [ } function onNewPost(data) { - var post = data.posts[0]; + const post = data.posts[0]; if (!post || !post.topic || post.topic.isFollowing) { return; } @@ -148,7 +141,7 @@ define('topicList', [ } function updateAlertText() { - var text = ''; + let text = ''; if (newTopicCount === 0) { if (newPostCount === 1) { @@ -184,9 +177,9 @@ define('topicList', [ if (!topicListEl.length || !topicListEl.children().length) { return; } - var topics = topicListEl.find('[component="category/topic"]'); - var afterEl = direction > 0 ? topics.last() : topics.first(); - var after = (parseInt(afterEl.attr('data-index'), 10) || 0) + (direction > 0 ? 1 : 0); + const topics = topicListEl.find('[component="category/topic"]'); + const afterEl = direction > 0 ? topics.last() : topics.first(); + const after = (parseInt(afterEl.attr('data-index'), 10) || 0) + (direction > 0 ? 1 : 0); if (!utils.isNumber(after) || (after === 0 && topicListEl.find('[component="category/topic"][data-index="0"]').length)) { return; @@ -197,21 +190,15 @@ define('topicList', [ }); }; + function calculateNextPage(after, direction) { + return Math.floor(after / config.topicsPerPage) + (direction > 0 ? 1 : 0); + } + function loadTopicsAfter(after, direction, callback) { callback = callback || function () {}; - var query = utils.params(); - infinitescroll.loadMore('topics.loadMoreSortedTopics', { - after: after, - direction: direction, - sort: tplToSort[templateName], - count: config.topicsPerPage, - cid: query.cid, - tags: query.tags, - query: query, - term: ajaxify.data.selectedTerm && ajaxify.data.selectedTerm.term, - filter: ajaxify.data.selectedFilter.filter, - set: topicListEl.attr('data-set') ? topicListEl.attr('data-set') : 'topics:recent', - }, callback); + const query = utils.params(); + query.page = calculateNextPage(after, direction); + infinitescroll.loadMoreXhr(query, callback); } function filterTopicsOnDom(topics) { @@ -232,9 +219,9 @@ define('topicList', [ return callback(); } - var after; - var before; - var topicEls = topicListEl.find('[component="category/topic"]'); + let after; + let before; + const topicEls = topicListEl.find('[component="category/topic"]'); if (direction > 0 && topics.length) { after = topicEls.last(); @@ -242,7 +229,7 @@ define('topicList', [ before = topicEls.first(); } - var tplData = { + const tplData = { topics: topics, showSelect: showSelect, template: { @@ -260,8 +247,8 @@ define('topicList', [ if (after && after.length) { html.insertAfter(after); } else if (before && before.length) { - var height = $(document).height(); - var scrollTop = $(window).scrollTop(); + const height = $(document).height(); + const scrollTop = $(window).scrollTop(); html.insertBefore(before); diff --git a/public/src/modules/topicSelect.js b/public/src/modules/topicSelect.js index 3040b36e98..526dd530bc 100644 --- a/public/src/modules/topicSelect.js +++ b/public/src/modules/topicSelect.js @@ -2,10 +2,10 @@ define('topicSelect', ['components'], function (components) { - var TopicSelect = {}; - var lastSelected; + const TopicSelect = {}; + let lastSelected; - var topicsContainer; + let topicsContainer; TopicSelect.init = function (onSelect) { topicsContainer = $('[component="category"]'); @@ -14,7 +14,7 @@ define('topicSelect', ['components'], function (components) { }); topicsContainer.on('click', '[component="topic/select"]', function (ev) { - var select = $(this); + const select = $(this); if (ev.shiftKey) { selectRange($(this).parents('[component="category/topic"]').attr('data-tid')); @@ -22,7 +22,7 @@ define('topicSelect', ['components'], function (components) { return false; } - var isSelected = select.parents('[data-tid]').hasClass('selected'); + const isSelected = select.parents('[data-tid]').hasClass('selected'); toggleSelect(select, !isSelected); lastSelected = select; if (typeof onSelect === 'function') { @@ -38,7 +38,7 @@ define('topicSelect', ['components'], function (components) { } TopicSelect.getSelectedTids = function () { - var tids = []; + const tids = []; if (!topicsContainer) { return tids; } @@ -60,22 +60,22 @@ define('topicSelect', ['components'], function (components) { lastSelected = $('[component="category/topic"]').first().find('[component="topic/select"]'); } - var isClickedSelected = components.get('category/topic', 'tid', clickedTid).hasClass('selected'); + const isClickedSelected = components.get('category/topic', 'tid', clickedTid).hasClass('selected'); - var clickedIndex = getIndex(clickedTid); - var lastIndex = getIndex(lastSelected.parents('[component="category/topic"]').attr('data-tid')); + const clickedIndex = getIndex(clickedTid); + const lastIndex = getIndex(lastSelected.parents('[component="category/topic"]').attr('data-tid')); selectIndexRange(clickedIndex, lastIndex, !isClickedSelected); } function selectIndexRange(start, end, isSelected) { if (start > end) { - var tmp = start; + const tmp = start; start = end; end = tmp; } - for (var i = start; i <= end; i += 1) { - var topic = $('[component="category/topic"]').eq(i); + for (let i = start; i <= end; i += 1) { + const topic = $('[component="category/topic"]').eq(i); toggleSelect(topic.find('[component="topic/select"]'), isSelected); } } diff --git a/public/src/modules/translator.js b/public/src/modules/translator.js index 2205d9b64b..eb40cf658f 100644 --- a/public/src/modules/translator.js +++ b/public/src/modules/translator.js @@ -3,7 +3,7 @@ (function (factory) { function loadClient(language, namespace) { return new Promise(function (resolve, reject) { - jQuery.getJSON([config.assetBaseUrl, 'language', language, namespace].join('/') + '.json?' + config['cache-buster'], function (data) { + jQuery.getJSON([config.asset_base_url, 'language', language, namespace].join('/') + '.json?' + config['cache-buster'], function (data) { const payload = { language: language, namespace: namespace, @@ -18,7 +18,7 @@ }); }); } - var warn = function () { console.warn.apply(console, arguments); }; + let warn = function () { console.warn.apply(console, arguments); }; if (typeof define === 'function' && define.amd) { // AMD. Register as a named module define('translator', [], function () { @@ -27,20 +27,21 @@ } else if (typeof module === 'object' && module.exports) { // Node (function () { - var languages = require('../../../src/languages'); - if (global.env === 'development') { - var winston = require('winston'); + const winston = require('winston'); warn = function (a) { winston.warn(a); }; } - module.exports = factory(require('../utils'), languages.get, warn); + module.exports = factory(require('../utils'), function (lang, namespace) { + const languages = require('../../../src/languages'); + return languages.get(lang, namespace); + }, warn); }()); } }(function (utils, load, warn) { - var assign = Object.assign || jQuery.extend; + const assign = Object.assign || jQuery.extend; function escapeHTML(str) { return utils.escapeHTML(utils.decodeHTMLEntities( @@ -50,25 +51,25 @@ )); } - var Translator = (function () { + const Translator = (function () { /** * Construct a new Translator object * @param {string} language - Language code for this translator instance * @exports translator.Translator */ function Translator(language) { - var self = this; + const self = this; if (!language) { throw new TypeError('Parameter `language` must be a language string. Received ' + language + (language === '' ? '(empty string)' : '')); } self.modules = Object.keys(Translator.moduleFactories).map(function (namespace) { - var factory = Translator.moduleFactories[namespace]; + const factory = Translator.moduleFactories[namespace]; return [namespace, factory(language)]; }).reduce(function (prev, elem) { - var namespace = elem[0]; - var module = elem[1]; + const namespace = elem[0]; + const module = elem[1]; prev[namespace] = module; return prev; @@ -87,31 +88,31 @@ */ Translator.prototype.translate = function translate(str) { // regex for valid text in namespace / key - var validText = 'a-zA-Z0-9\\-_.\\/'; - var validTextRegex = new RegExp('[' + validText + ']'); - var invalidTextRegex = new RegExp('[^' + validText + '\\]]'); + const validText = 'a-zA-Z0-9\\-_.\\/'; + const validTextRegex = new RegExp('[' + validText + ']'); + const invalidTextRegex = new RegExp('[^' + validText + '\\]]'); // current cursor position - var cursor = 0; + let cursor = 0; // last break of the input string - var lastBreak = 0; + let lastBreak = 0; // length of the input string - var len = str.length; + const len = str.length; // array to hold the promises for the translations // and the strings of untranslated text in between - var toTranslate = []; + const toTranslate = []; // to store the state of if we're currently in a top-level token for later - var inToken = false; + let inToken = false; // split a translator string into an array of tokens // but don't split by commas inside other translator strings function split(text) { - var len = text.length; - var arr = []; - var i = 0; - var brk = 0; - var level = 0; + const len = text.length; + const arr = []; + let i = 0; + let brk = 0; + let level = 0; while (i + 2 <= len) { if (text[i] === '[' && text[i + 1] === '[') { @@ -152,14 +153,14 @@ inToken = true; // the current level of nesting of the translation strings - var level = 0; - var char0; - var char1; + let level = 0; + let char0; + let char1; // validating the current string is actually a translation - var textBeforeColonFound = false; - var colonFound = false; - var textAfterColonFound = false; - var commaAfterNameFound = false; + let textBeforeColonFound = false; + let colonFound = false; + let textAfterColonFound = false; + let commaAfterNameFound = false; while (cursor + 2 <= len) { char0 = str[cursor]; @@ -204,14 +205,14 @@ // if we're at the base level, then this is the end if (level === 0) { // so grab the name and args - var currentSlice = str.slice(lastBreak, cursor); - var result = split(currentSlice); - var name = result[0]; - var args = result.slice(1); + const currentSlice = str.slice(lastBreak, cursor); + const result = split(currentSlice); + const name = result[0]; + const args = result.slice(1); // make a backup based on the raw string of the token // if there are arguments to the token - var backup = ''; + let backup = ''; if (args && args.length) { backup = this.translate(currentSlice); } @@ -241,7 +242,7 @@ } // ending string of source - var last = str.slice(lastBreak); + let last = str.slice(lastBreak); // if we were mid-token, treat it as invalid if (inToken) { @@ -265,11 +266,11 @@ * @returns {Promise} */ Translator.prototype.translateKey = function translateKey(name, args, backup) { - var self = this; + const self = this; - var result = name.split(':', 2); - var namespace = result[0]; - var key = result[1]; + const result = name.split(':', 2); + const namespace = result[0]; + const key = result[1]; if (self.modules[namespace]) { return Promise.resolve(self.modules[namespace](key, args)); @@ -284,7 +285,7 @@ return Promise.resolve('[[' + namespace + ']]'); } - var translation = this.getTranslation(namespace, key); + const translation = this.getTranslation(namespace, key); return translation.then(function (translated) { // check if the translation is missing first if (!translated) { @@ -292,14 +293,14 @@ return backup || key; } - var argsToTranslate = args.map(function (arg) { + const argsToTranslate = args.map(function (arg) { return self.translate(escapeHTML(arg)); }); return Promise.all(argsToTranslate).then(function (translatedArgs) { - var out = translated; + let out = translated; translatedArgs.forEach(function (arg, i) { - var escaped = arg.replace(/%(?=\d)/g, '%').replace(/\\,/g, ','); + let escaped = arg.replace(/%(?=\d)/g, '%').replace(/\\,/g, ','); // fix double escaped translation keys, see https://github.com/NodeBB/NodeBB/issues/9206 escaped = escaped.replace(/&lsqb;/g, '[') .replace(/&rsqb;/g, ']'); @@ -317,7 +318,7 @@ * @returns {Promise<{ [key: string]: string } | string>} */ Translator.prototype.getTranslation = function getTranslation(namespace, key) { - var translation; + let translation; if (!namespace) { warn('[translator] Parameter `namespace` is ' + namespace + (namespace === '' ? '(empty string)' : '')); translation = Promise.resolve({}); @@ -361,13 +362,13 @@ * @returns {Node[]} */ function descendantTextNodes(node) { - var textNodes = []; + const textNodes = []; function helper(node) { if (node.nodeType === 3) { textNodes.push(node); } else { - for (var i = 0, c = node.childNodes, l = c.length; i < l; i += 1) { + for (let i = 0, c = node.childNodes, l = c.length; i < l; i += 1) { helper(c[i]); } } @@ -386,18 +387,18 @@ Translator.prototype.translateInPlace = function translateInPlace(element, attributes) { attributes = attributes || ['placeholder', 'title']; - var nodes = descendantTextNodes(element); - var text = nodes.map(function (node) { + const nodes = descendantTextNodes(element); + const text = nodes.map(function (node) { return utils.escapeHTML(node.nodeValue); }).join(' || '); - var attrNodes = attributes.reduce(function (prev, attr) { - var tuples = Array.prototype.map.call(element.querySelectorAll('[' + attr + '*="[["]'), function (el) { + const attrNodes = attributes.reduce(function (prev, attr) { + const tuples = Array.prototype.map.call(element.querySelectorAll('[' + attr + '*="[["]'), function (el) { return [attr, el]; }); return prev.concat(tuples); }, []); - var attrText = attrNodes.map(function (node) { + const attrText = attrNodes.map(function (node) { return node[1].getAttribute(node[0]); }).join(' || '); @@ -405,8 +406,8 @@ this.translate(text), this.translate(attrText), ]).then(function (ref) { - var translated = ref[0]; - var translatedAttrs = ref[1]; + const translated = ref[0]; + const translatedAttrs = ref[1]; if (translated) { translated.split(' || ').forEach(function (html, i) { $(nodes[i]).replaceWith(html); @@ -425,12 +426,12 @@ * @returns {string} */ Translator.getLanguage = function getLanguage() { - var lang; + let lang; if (typeof window === 'object' && window.config && window.utils) { lang = utils.params().lang || config.userLang || config.defaultLang || 'en-GB'; } else { - var meta = require('../../../src/meta'); + const meta = require('../../../src/meta'); lang = meta.config && meta.config.defaultLang ? meta.config.defaultLang : 'en-GB'; } @@ -463,7 +464,7 @@ Translator.moduleFactories[namespace] = factory; Object.keys(Translator.cache).forEach(function (key) { - var translator = Translator.cache[key]; + const translator = Translator.cache[key]; translator.modules[namespace] = factory(translator.lang); }); }; @@ -476,12 +477,12 @@ * @returns {string} */ Translator.removePatterns = function removePatterns(text) { - var len = text.length; - var cursor = 0; - var lastBreak = 0; - var level = 0; - var out = ''; - var sub; + const len = text.length; + let cursor = 0; + let lastBreak = 0; + let level = 0; + let out = ''; + let sub; while (cursor < len) { sub = text.slice(cursor, cursor + 2); @@ -532,7 +533,7 @@ * @param {...string} arg - Optional argument for the pattern */ Translator.compile = function compile() { - var args = Array.prototype.slice.call(arguments, 0).map(function (text) { + const args = Array.prototype.slice.call(arguments, 0).map(function (text) { // escape commas and percent signs in arguments return String(text).replace(/%/g, '%').replace(/,/g, ','); }); @@ -546,7 +547,7 @@ /** * @exports translator */ - var adaptor = { + const adaptor = { /** * The Translator class */ @@ -581,8 +582,8 @@ translate: function translate(text, language, callback) { // TODO: deprecate? - var cb = callback; - var lang = language; + let cb = callback; + let lang = language; if (typeof language === 'function') { cb = language; lang = null; @@ -632,7 +633,7 @@ toggleTimeagoShorthand: function toggleTimeagoShorthand(callback) { /* eslint "prefer-object-spread": "off" */ function toggle() { - var tmp = assign({}, jQuery.timeago.settings.strings); + const tmp = assign({}, jQuery.timeago.settings.strings); jQuery.timeago.settings.strings = assign({}, adaptor.timeagoShort); adaptor.timeagoShort = assign({}, tmp); if (typeof callback === 'function') { @@ -641,12 +642,12 @@ } if (!adaptor.timeagoShort) { - var languageCode = utils.userLangToTimeagoCode(config.userLang); + let languageCode = utils.userLangToTimeagoCode(config.userLang); if (!config.timeagoCodes.includes(languageCode + '-short')) { languageCode = 'en'; } - var originalSettings = assign({}, jQuery.timeago.settings.strings); + const originalSettings = assign({}, jQuery.timeago.settings.strings); adaptor.switchTimeagoLanguage(languageCode + '-short', function () { adaptor.timeagoShort = assign({}, jQuery.timeago.settings.strings); jQuery.timeago.settings.strings = assign({}, originalSettings); @@ -661,7 +662,7 @@ // Delete the cached shorthand strings if present delete adaptor.timeagoShort; - var stringsModule = 'timeago/locales/jquery.timeago.' + langCode; + const stringsModule = 'timeago/locales/jquery.timeago.' + langCode; // without undef, requirejs won't load the strings a second time require.undef(stringsModule); require([stringsModule], function () { diff --git a/public/src/modules/uploader.js b/public/src/modules/uploader.js index e97371e42d..fb50e431ba 100644 --- a/public/src/modules/uploader.js +++ b/public/src/modules/uploader.js @@ -2,10 +2,10 @@ define('uploader', ['jquery-form'], function () { - var module = {}; + const module = {}; module.show = function (data, callback) { - var fileSize = data.hasOwnProperty('fileSize') && data.fileSize !== undefined ? parseInt(data.fileSize, 10) : false; + const fileSize = data.hasOwnProperty('fileSize') && data.fileSize !== undefined ? parseInt(data.fileSize, 10) : false; app.parseAndTranslate('partials/modals/upload_file_modal', { showHelp: data.hasOwnProperty('showHelp') && data.showHelp !== undefined ? data.showHelp : true, fileSize: fileSize, @@ -19,7 +19,7 @@ define('uploader', ['jquery-form'], function () { uploadModal.remove(); }); - var uploadForm = uploadModal.find('#uploadForm'); + const uploadForm = uploadModal.find('#uploadForm'); uploadForm.attr('action', data.route); uploadForm.find('#params').val(JSON.stringify(data.params)); @@ -45,7 +45,7 @@ define('uploader', ['jquery-form'], function () { uploadModal.find('#upload-progress-bar').css('width', '0%'); uploadModal.find('#upload-progress-box').show().removeClass('hide'); - var fileInput = uploadModal.find('#fileInput'); + const fileInput = uploadModal.find('#fileInput'); if (!fileInput.val()) { return showAlert(uploadModal, 'error', '[[uploads:select-file-to-upload]]'); } diff --git a/public/src/overrides.js b/public/src/overrides.js index 95288cf038..123d814059 100644 --- a/public/src/overrides.js +++ b/public/src/overrides.js @@ -7,14 +7,14 @@ if (typeof window !== 'undefined') { (function ($) { require(['translator'], function (translator) { $.fn.getCursorPosition = function () { - var el = $(this).get(0); - var pos = 0; + const el = $(this).get(0); + let pos = 0; if ('selectionStart' in el) { pos = el.selectionStart; } else if ('selection' in document) { el.focus(); - var Sel = document.selection.createRange(); - var SelLength = document.selection.createRange().text.length; + const Sel = document.selection.createRange(); + const SelLength = document.selection.createRange().text.length; Sel.moveStart('character', -el.value.length); pos = Sel.text.length - SelLength; } @@ -30,7 +30,7 @@ if (typeof window !== 'undefined') { this.focus(); this.setSelectionRange(start, end); } else if (this.createTextRange) { - var range = this.createTextRange(); + const range = this.createTextRange(); range.collapse(true); range.moveEnd('character', end); range.moveStart('character', start); @@ -45,7 +45,7 @@ if (typeof window !== 'undefined') { $(this).focus(); if (this.setSelectionRange) { - var len = $(this).val().length * 2; + const len = $(this).val().length * 2; this.setSelectionRange(len, len); } else { $(this).val($(this).val()); @@ -68,7 +68,7 @@ if (typeof window !== 'undefined') { $.fn.translateAttr = function (attr, str) { return this.each(function () { - var el = $(this); + const el = $(this); translator.translate(str, function (translated) { el.attr(attr, translated); }); @@ -77,7 +77,7 @@ if (typeof window !== 'undefined') { function translate(elements, type, str) { return elements.each(function () { - var el = $(this); + const el = $(this); translator.translate(str, function (translated) { el[type](translated); }); @@ -90,7 +90,7 @@ if (typeof window !== 'undefined') { // FIX FOR #1245 - https://github.com/NodeBB/NodeBB/issues/1245 // from http://stackoverflow.com/questions/15931962/bootstrap-dropdown-disappear-with-right-click-on-firefox // obtain a reference to the original handler - var _clearMenus = $._data(document, 'events').click.filter(function (el) { + let _clearMenus = $._data(document, 'events').click.filter(function (el) { return el.namespace === 'bs.data-api.dropdown' && el.selector === undefined; }); @@ -108,7 +108,7 @@ if (typeof window !== 'undefined') { } }); }()); - var timeagoFn; + let timeagoFn; overrides.overrideTimeago = function () { if (!timeagoFn) { timeagoFn = $.fn.timeago; @@ -121,24 +121,24 @@ if (typeof window !== 'undefined') { } $.timeago.settings.allowFuture = true; - var userLang = config.userLang.replace('_', '-'); - var options = { year: 'numeric', month: 'short', day: 'numeric', hour: 'numeric', minute: 'numeric' }; - var formatFn = function (date) { + const userLang = config.userLang.replace('_', '-'); + const options = { year: 'numeric', month: 'short', day: 'numeric', hour: 'numeric', minute: 'numeric' }; + let formatFn = function (date) { return date.toLocaleString(userLang, options); }; try { if (typeof Intl !== 'undefined') { - var dtFormat = new Intl.DateTimeFormat(userLang, options); + const dtFormat = new Intl.DateTimeFormat(userLang, options); formatFn = dtFormat.format; } } catch (err) { console.error(err); } - var iso; - var date; + let iso; + let date; $.fn.timeago = function () { - var els = $(this); + const els = $(this); // Convert "old" format to new format (#5108) els.each(function () { iso = this.getAttribute('title'); diff --git a/public/src/sockets.js b/public/src/sockets.js index 24fe64e747..5403717009 100644 --- a/public/src/sockets.js +++ b/public/src/sockets.js @@ -5,9 +5,9 @@ app = window.app || {}; socket = window.socket; (function () { - var reconnecting = false; + let reconnecting = false; - var ioParams = { + const ioParams = { reconnectionAttempts: config.maxReconnectionAttempts, reconnectionDelay: config.reconnectionDelay, transports: config.socketioTransports, @@ -16,7 +16,7 @@ socket = window.socket; socket = io(config.websocketAddress, ioParams); - var oEmit = socket.emit; + const oEmit = socket.emit; socket.emit = function (event, data, callback) { if (typeof data === 'function') { callback = data; @@ -35,7 +35,7 @@ socket = window.socket; }); }; - var hooks; + let hooks; require(['hooks'], function (_hooks) { hooks = _hooks; if (parseInt(app.user.uid, 10) >= 0) { @@ -48,7 +48,7 @@ socket = window.socket; return; } - var reconnectEl = $('#reconnect'); + const reconnectEl = $('#reconnect'); $('#reconnect-alert') .removeClass('alert-danger pointer') .addClass('alert-warning') @@ -65,7 +65,7 @@ socket = window.socket; socket.on('disconnect', onDisconnect); socket.io.on('reconnect_failed', function () { - var reconnectEl = $('#reconnect'); + const reconnectEl = $('#reconnect'); reconnectEl.html(''); $('#reconnect-alert') @@ -80,11 +80,11 @@ socket = window.socket; socket.on('checkSession', function (uid) { if (parseInt(uid, 10) !== parseInt(app.user.uid, 10)) { - app.handleSessionMismatch(); + handleSessionMismatch(); } }); socket.on('event:invalid_session', () => { - app.handleInvalidSession(); + handleInvalidSession(); }); socket.on('setHostname', function (hostname) { @@ -94,7 +94,9 @@ socket = window.socket; socket.on('event:banned', onEventBanned); socket.on('event:unbanned', onEventUnbanned); socket.on('event:logout', function () { - app.logout(); + require(['logout'], function (logout) { + logout(); + }); }); socket.on('event:alert', function (params) { app.alert(params); @@ -126,14 +128,33 @@ socket = window.socket; }); } + function handleInvalidSession() { + socket.disconnect(); + require(['messages', 'logout'], function (messages, logout) { + logout(false); + messages.showInvalidSession(); + }); + } + + function handleSessionMismatch() { + if (app.flags._login || app.flags._logout) { + return; + } + + socket.disconnect(); + require(['messages'], function (messages) { + messages.showSessionMismatch(); + }); + } + function onConnect() { if (!reconnecting) { hooks.fire('action:connected'); } if (reconnecting) { - var reconnectEl = $('#reconnect'); - var reconnectAlert = $('#reconnect-alert'); + const reconnectEl = $('#reconnect'); + const reconnectAlert = $('#reconnect-alert'); reconnectEl.tooltip('destroy'); reconnectEl.html(''); @@ -153,43 +174,17 @@ socket = window.socket; } function reJoinCurrentRoom() { - var url_parts = window.location.pathname.slice(config.relative_path.length).split('/').slice(1); - var room; - - switch (url_parts[0]) { - case 'user': - room = 'user/' + (ajaxify.data ? ajaxify.data.theirid : 0); - break; - case 'topic': - room = 'topic_' + url_parts[1]; - break; - case 'category': - room = 'category_' + url_parts[1]; - break; - case 'recent': - room = 'recent_topics'; - break; - case 'unread': - room = 'unread_topics'; - break; - case 'popular': - room = 'popular_topics'; - break; - case 'admin': - room = 'admin'; - break; - case 'categories': - room = 'categories'; - break; + if (app.currentRoom) { + const current = app.currentRoom; + app.currentRoom = ''; + app.enterRoom(current); } - app.currentRoom = ''; - app.enterRoom(room); } function onReconnecting() { reconnecting = true; - var reconnectEl = $('#reconnect'); - var reconnectAlert = $('#reconnect-alert'); + const reconnectEl = $('#reconnect'); + const reconnectAlert = $('#reconnect-alert'); if (!reconnectEl.hasClass('active')) { reconnectEl.html(''); @@ -212,26 +207,33 @@ socket = window.socket; } function onEventBanned(data) { - var message = data.until ? '[[error:user-banned-reason-until, ' + utils.toISOString(data.until) + ', ' + data.reason + ']]' : '[[error:user-banned-reason, ' + data.reason + ']]'; - - bootbox.alert({ - title: '[[error:user-banned]]', - message: message, - closeButton: false, - callback: function () { - window.location.href = config.relative_path + '/'; - }, + require(['bootbox', 'translator'], function (bootbox, translator) { + const message = data.until ? + translator.compile('error:user-banned-reason-until', (new Date(data.until).toLocaleString()), data.reason) : + '[[error:user-banned-reason, ' + data.reason + ']]'; + translator.translate(message, function (message) { + bootbox.alert({ + title: '[[error:user-banned]]', + message: message, + closeButton: false, + callback: function () { + window.location.href = config.relative_path + '/'; + }, + }); + }); }); } function onEventUnbanned() { - bootbox.alert({ - title: '[[global:alert.unbanned]]', - message: '[[global:alert.unbanned.message]]', - closeButton: false, - callback: function () { - window.location.href = config.relative_path + '/'; - }, + require(['bootbox'], function (bootbox) { + bootbox.alert({ + title: '[[global:alert.unbanned]]', + message: '[[global:alert.unbanned.message]]', + closeButton: false, + callback: function () { + window.location.href = config.relative_path + '/'; + }, + }); }); } diff --git a/public/src/utils.js b/public/src/utils.js index fa9864ac5c..684dd6b3ea 100644 --- a/public/src/utils.js +++ b/public/src/utils.js @@ -2,7 +2,7 @@ (function (factory) { if (typeof module === 'object' && module.exports) { - var winston = require('winston'); + const winston = require('winston'); module.exports = factory(require('xregexp'), winston); @@ -11,7 +11,7 @@ }; process.elapsedTimeSince = function (start) { - var diff = process.hrtime(start); + const diff = process.hrtime(start); return (diff[0] * 1e3) + (diff[1] / 1e6); }; } else { @@ -19,10 +19,10 @@ } // eslint-disable-next-line }(function (XRegExp, console) { - var freeze = Object.freeze || function (obj) { return obj; }; + const freeze = Object.freeze || function (obj) { return obj; }; // add default escape function for escaping HTML entities - var escapeCharMap = freeze({ + const escapeCharMap = freeze({ '&': '&', '<': '<', '>': '>', @@ -34,9 +34,9 @@ function replaceChar(c) { return escapeCharMap[c]; } - var escapeChars = /[&<>"'`=]/g; + const escapeChars = /[&<>"'`=]/g; - var HTMLEntities = freeze({ + const HTMLEntities = freeze({ amp: '&', gt: '>', lt: '<', @@ -292,12 +292,12 @@ 'diams;': 9830, }); - var utils = { + const utils = { generateUUID: function () { /* eslint-disable no-bitwise */ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { - var r = Math.random() * 16 | 0; - var v = c === 'x' ? r : ((r & 0x3) | 0x8); + const r = Math.random() * 16 | 0; + const v = c === 'x' ? r : ((r & 0x3) | 0x8); return v.toString(16); }); /* eslint-enable no-bitwise */ @@ -312,8 +312,8 @@ return String.fromCharCode(parseInt(hex, 16)); }) .replace(/&([^;\W]+;?)/g, function (m, e) { - var ee = e.replace(/;$/, ''); - var target = HTMLEntities[e] || (e.match(/;$/) && HTMLEntities[ee]); + const ee = e.replace(/;$/, ''); + const target = HTMLEntities[e] || (e.match(/;$/) && HTMLEntities[ee]); if (typeof target === 'number') { return String.fromCharCode(target); @@ -326,7 +326,7 @@ }, // https://github.com/jprichardson/string.js/blob/master/lib/string.js stripHTMLTags: function (str, tags) { - var pattern = (tags || ['']).join('|'); + const pattern = (tags || ['']).join('|'); return String(str).replace(new RegExp('<(\\/)?(' + (pattern || '[^\\s>]+') + ')(\\s+[^<>]*?)?\\s*(\\/)?>', 'gi'), ''); }, @@ -340,7 +340,7 @@ tag = tag.replace(/\u202E/gi, ''); tag = tag.replace(/[,/#!$%^*;:{}=_`<>'"~()?|]/g, ''); tag = tag.substr(0, maxLength || 15).trim(); - var matches = tag.match(/^[.-]*(.+?)[.-]*$/); + const matches = tag.match(/^[.-]*(.+?)[.-]*$/); if (matches && matches.length > 1) { tag = matches[1]; } @@ -373,7 +373,7 @@ return utils.languageKeyRegex.test(input); }, userLangToTimeagoCode: function (userLang) { - var mapping = { + const mapping = { 'en-GB': 'en', 'en-US': 'en', 'fa-IR': 'fa', @@ -384,13 +384,13 @@ }, // shallow objects merge merge: function () { - var result = {}; - var obj; - var keys; - for (var i = 0; i < arguments.length; i += 1) { + const result = {}; + let obj; + let keys; + for (let i = 0; i < arguments.length; i += 1) { obj = arguments[i] || {}; keys = Object.keys(obj); - for (var j = 0; j < keys.length; j += 1) { + for (let j = 0; j < keys.length; j += 1) { result[keys[j]] = obj[keys[j]]; } } @@ -441,11 +441,11 @@ }, promiseParallel: function (obj) { - var keys = Object.keys(obj); + const keys = Object.keys(obj); return Promise.all( keys.map(function (k) { return obj[k]; }) ).then(function (results) { - var data = {}; + const data = {}; keys.forEach(function (k, i) { data[k] = results[i]; }); @@ -476,7 +476,7 @@ }, makeNumberHumanReadable: function (num) { - var n = parseInt(num, 10); + const n = parseInt(num, 10); if (!n) { return num; } @@ -524,7 +524,7 @@ 'map', 'mark', 'menu', 'meta', 'meter', 'nav', 'noframes', 'noscript', 'object', 'ol', 'optgroup', 'option', 'output', 'p', 'param', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'script', 'section', 'select', 'small', 'source', 'span', 'strike', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'textarea', 'tfoot', - 'th', 'thead', 'time', 'title', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr'], + 'th', 'thead', 'time', 'title', 'tr', 'track', 'tt', 'u', 'ul', 'const', 'video', 'wbr'], stripTags: ['abbr', 'acronym', 'address', 'applet', 'area', 'article', 'aside', 'audio', 'base', 'basefont', 'bdi', 'bdo', 'big', 'blink', 'body', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', @@ -534,7 +534,7 @@ 'map', 'mark', 'marquee', 'menu', 'meta', 'meter', 'nav', 'noframes', 'noscript', 'object', 'ol', 'optgroup', 'option', 'output', 'param', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'script', 'section', 'select', 'source', 'span', 'strike', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'textarea', 'tfoot', - 'th', 'thead', 'time', 'title', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr'], + 'th', 'thead', 'time', 'title', 'tr', 'track', 'tt', 'u', 'ul', 'const', 'video', 'wbr'], escapeRegexChars: function (text) { return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); @@ -553,7 +553,7 @@ isAndroidBrowser: function () { // http://stackoverflow.com/questions/9286355/how-to-detect-only-the-native-android-browser - var nua = navigator.userAgent; + const nua = navigator.userAgent; return ((nua.indexOf('Mozilla/5.0') > -1 && nua.indexOf('Android ') > -1 && nua.indexOf('AppleWebKit') > -1) && !(nua.indexOf('Chrome') > -1)); }, @@ -563,13 +563,13 @@ findBootstrapEnvironment: function () { // http://stackoverflow.com/questions/14441456/how-to-detect-which-device-view-youre-on-using-twitter-bootstrap-api - var envs = ['xs', 'sm', 'md', 'lg']; - var $el = $('
    '); + const envs = ['xs', 'sm', 'md', 'lg']; + const $el = $('
    '); $el.appendTo($('body')); - for (var i = envs.length - 1; i >= 0; i -= 1) { - var env = envs[i]; + for (let i = envs.length - 1; i >= 0; i -= 1) { + const env = envs[i]; $el.addClass('hidden-' + env); if ($el.is(':hidden')) { @@ -580,18 +580,18 @@ }, isMobile: function () { - var env = utils.findBootstrapEnvironment(); + const env = utils.findBootstrapEnvironment(); return ['xs', 'sm'].some(function (targetEnv) { return targetEnv === env; }); }, getHoursArray: function () { - var currentHour = new Date().getHours(); - var labels = []; + const currentHour = new Date().getHours(); + const labels = []; - for (var i = currentHour, ii = currentHour - 24; i > ii; i -= 1) { - var hour = i < 0 ? 24 + i : i; + for (let i = currentHour, ii = currentHour - 24; i > ii; i -= 1) { + const hour = i < 0 ? 24 + i : i; labels.push(hour + ':00'); } @@ -599,12 +599,12 @@ }, getDaysArray: function (from, amount) { - var currentDay = new Date(parseInt(from, 10) || Date.now()).getTime(); - var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; - var labels = []; - var tmpDate; + const currentDay = new Date(parseInt(from, 10) || Date.now()).getTime(); + const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; + const labels = []; + let tmpDate; - for (var x = (amount || 30) - 1; x >= 0; x -= 1) { + for (let x = (amount || 30) - 1; x >= 0; x -= 1) { tmpDate = new Date(currentDay - (1000 * 60 * 60 * 24 * x)); labels.push(months[tmpDate.getMonth()] + ' ' + tmpDate.getDate()); } @@ -619,7 +619,7 @@ el = el[0]; } - var rect = el.getBoundingClientRect(); + const rect = el.getBoundingClientRect(); return ( rect.top >= 0 && @@ -631,26 +631,26 @@ // get all the url params in a single key/value hash params: function (options) { - var hash = {}; + const hash = {}; options = options || {}; options.skipToType = options.skipToType || {}; - var searchStr = window.location.search; + let searchStr = window.location.search; if (options.hasOwnProperty('url')) { if (options.url) { - var a = utils.urlToLocation(options.url); + const a = utils.urlToLocation(options.url); searchStr = a ? a.search : ''; } else { searchStr = ''; } } - var params = searchStr.substring(1).split('&'); + const params = searchStr.substring(1).split('&'); params.forEach(function (param) { - var val = param.split('='); - var key = decodeURI(val[0]); - var value = ( + const val = param.split('='); + let key = decodeURI(val[0]); + const value = ( options.disableToType || options.skipToType[key] ? decodeURI(val[1]) : utils.toType(decodeURI(val[1])) ); @@ -677,7 +677,7 @@ }, urlToLocation: function (url) { - var a = document.createElement('a'); + const a = document.createElement('a'); a.href = url; return a; }, @@ -685,11 +685,11 @@ // return boolean if string 'true' or string 'false', or if a parsable string which is a number // also supports JSON object and/or arrays parsing toType: function (str) { - var type = typeof str; + const type = typeof str; if (type !== 'string') { return str; } - var nb = parseFloat(str); + const nb = parseFloat(str); if (!isNaN(nb) && isFinite(str)) { return nb; } @@ -719,15 +719,15 @@ if (props == null) { return undefined; } - var i = props.indexOf('.'); + const i = props.indexOf('.'); if (i === -1) { if (value !== undefined) { obj[props] = value; } return obj[props]; } - var prop = props.slice(0, i); - var newProps = props.slice(i + 1); + const prop = props.slice(0, i); + const newProps = props.slice(i + 1); if (props !== undefined && !(obj[prop] instanceof Object)) { obj[prop] = {}; @@ -753,17 +753,17 @@ debounce: function (func, wait, immediate) { // modified from https://davidwalsh.name/javascript-debounce-function - var timeout; + let timeout; return function () { - var context = this; - var args = arguments; - var later = function () { + const context = this; + const args = arguments; + const later = function () { timeout = null; if (!immediate) { func.apply(context, args); } }; - var callNow = immediate && !timeout; + const callNow = immediate && !timeout; clearTimeout(timeout); timeout = setTimeout(later, wait); if (callNow) { @@ -771,6 +771,26 @@ } }; }, + throttle: function (func, wait, immediate) { + let timeout; + return function () { + const context = this; + const args = arguments; + const later = function () { + timeout = null; + if (!immediate) { + func.apply(context, args); + } + }; + const callNow = immediate && !timeout; + if (!timeout) { + timeout = setTimeout(later, wait); + } + if (callNow) { + func.apply(context, args); + } + }; + }, }; return utils; diff --git a/public/src/widgets.js b/public/src/widgets.js index a23155e56e..95eaf2f888 100644 --- a/public/src/widgets.js +++ b/public/src/widgets.js @@ -8,16 +8,16 @@ return; } - var locations = Object.keys(ajaxify.data.widgets); + const locations = Object.keys(ajaxify.data.widgets); locations.forEach(function (location) { - var area = $('#content [widget-area="' + location + '"],#content [data-widget-area="' + location + '"]').eq(0); + let area = $('#content [widget-area="' + location + '"],#content [data-widget-area="' + location + '"]').eq(0); if (area.length) { return; } - var widgetsAtLocation = ajaxify.data.widgets[location] || []; - var html = ''; + const widgetsAtLocation = ajaxify.data.widgets[location] || []; + let html = ''; widgetsAtLocation.forEach(function (widget) { html += widget.html; diff --git a/renovate.json b/renovate.json index 2e0f3667f9..e880233256 100644 --- a/renovate.json +++ b/renovate.json @@ -2,10 +2,12 @@ "extends": [ "config:base" ], + "labels": ["dependencies"], "packageRules": [ { "updateTypes": ["minor", "patch", "pin", "digest"], - "automerge": true + "automerge": true, + "excludePackageNames": ["colors"] } ] } diff --git a/src/api/categories.js b/src/api/categories.js index 3486df1ad5..3c2e6a96ec 100644 --- a/src/api/categories.js +++ b/src/api/categories.js @@ -65,10 +65,27 @@ categoriesAPI.setPrivilege = async (caller, data) => { if (!userExists && !groupExists) { throw new Error('[[error:no-user-or-group]]'); } - - await privileges.categories[data.set ? 'give' : 'rescind']( - Array.isArray(data.privilege) ? data.privilege : [data.privilege], data.cid, data.member - ); + const privs = Array.isArray(data.privilege) ? data.privilege : [data.privilege]; + const type = data.set ? 'give' : 'rescind'; + if (!privs.length) { + throw new Error('[[error:invalid-data]]'); + } + if (parseInt(data.cid, 10) === 0) { + const adminPrivList = await privileges.admin.getPrivilegeList(); + const adminPrivs = privs.filter(priv => adminPrivList.includes(priv)); + if (adminPrivs.length) { + await privileges.admin[type](adminPrivs, data.member); + } + const globalPrivList = await privileges.global.getPrivilegeList(); + const globalPrivs = privs.filter(priv => globalPrivList.includes(priv)); + if (globalPrivs.length) { + await privileges.global[type](globalPrivs, data.member); + } + } else { + const categoryPrivList = await privileges.categories.getPrivilegeList(); + const categoryPrivs = privs.filter(priv => categoryPrivList.includes(priv)); + await privileges.categories[type](categoryPrivs, data.cid, data.member); + } await events.log({ uid: caller.uid, diff --git a/src/api/flags.js b/src/api/flags.js index 19a8887260..235030c9df 100644 --- a/src/api/flags.js +++ b/src/api/flags.js @@ -51,7 +51,7 @@ flagsApi.appendNote = async (caller, data) => { } } catch (e) { // Okay if not does not exist in database - if (!e.message === '[[error:invalid-data]]') { + if (e.message !== '[[error:invalid-data]]') { throw e; } } diff --git a/src/api/users.js b/src/api/users.js index b7deaed080..cbd64e7fd5 100644 --- a/src/api/users.js +++ b/src/api/users.js @@ -227,14 +227,9 @@ usersAPI.unban = async function (caller, data) { async function isPrivilegedOrSelfAndPasswordMatch(caller, data) { const { uid } = caller; const isSelf = parseInt(uid, 10) === parseInt(data.uid, 10); + const canEdit = await privileges.users.canEdit(uid, data.uid); - const [isAdmin, isTargetAdmin, isGlobalMod] = await Promise.all([ - user.isAdministrator(uid), - user.isAdministrator(data.uid), - user.isGlobalModerator(uid), - ]); - - if ((isTargetAdmin && !isAdmin) || (!isSelf && !(isAdmin || isGlobalMod))) { + if (!canEdit) { throw new Error('[[error:no-privileges]]'); } const [hasPassword, passwordMatch] = await Promise.all([ @@ -341,3 +336,44 @@ usersAPI.search = async function (caller, data) { filters: filters, }); }; + +usersAPI.changePicture = async (caller, data) => { + if (!data) { + throw new Error('[[error:invalid-data]]'); + } + + const { type, url } = data; + let picture = ''; + + await user.checkMinReputation(caller.uid, data.uid, 'min:rep:profile-picture'); + const canEdit = await privileges.users.canEdit(caller.uid, data.uid); + if (!canEdit) { + throw new Error('[[error:no-privileges]]'); + } + + if (type === 'default') { + picture = ''; + } else if (type === 'uploaded') { + picture = await user.getUserField(data.uid, 'uploadedpicture'); + } else if (type === 'external' && url) { + picture = validator.escape(url); + } else { + const returnData = await plugins.hooks.fire('filter:user.getPicture', { + uid: caller.uid, + type: type, + picture: undefined, + }); + picture = returnData && returnData.picture; + } + + const validBackgrounds = await user.getIconBackgrounds(caller.uid); + if (!validBackgrounds.includes(data.bgColor)) { + data.bgColor = validBackgrounds[0]; + } + + await user.updateProfile(caller.uid, { + uid: data.uid, + picture: picture, + 'icon:bgColor': data.bgColor, + }, ['picture', 'icon:bgColor']); +}; diff --git a/src/batch.js b/src/batch.js index dbce61f432..8442def721 100644 --- a/src/batch.js +++ b/src/batch.js @@ -33,7 +33,7 @@ exports.processSortedSet = async function (setKey, process, options) { options.doneIf = typeof options.doneIf === 'function' ? options.doneIf : function () {}; let start = 0; - let stop = options.batch; + let stop = options.batch - 1; if (process && process.constructor && process.constructor.name !== 'AsyncFunction') { process = util.promisify(process); @@ -47,8 +47,8 @@ exports.processSortedSet = async function (setKey, process, options) { } await process(ids); - start += utils.isNumber(options.alwaysStartAt) ? options.alwaysStartAt : options.batch + 1; - stop = start + options.batch; + start += utils.isNumber(options.alwaysStartAt) ? options.alwaysStartAt : options.batch; + stop = start + options.batch - 1; if (options.interval) { await sleep(options.interval); diff --git a/src/categories/create.js b/src/categories/create.js index 7b787a666e..c72fce2bd4 100644 --- a/src/categories/create.js +++ b/src/categories/create.js @@ -203,9 +203,10 @@ module.exports = function (Categories) { group = group || ''; let privsToCopy; if (group) { - privsToCopy = privileges.categories.groupPrivilegeList.slice(...filter); + const groupPrivilegeList = await privileges.categories.getGroupPrivilegeList(); + privsToCopy = groupPrivilegeList.slice(...filter); } else { - const privs = privileges.categories.privilegeList.slice(); + const privs = await privileges.categories.getPrivilegeList(); const halfIdx = privs.length / 2; privsToCopy = privs.slice(0, halfIdx).slice(...filter).concat(privs.slice(halfIdx).slice(...filter)); } diff --git a/src/categories/delete.js b/src/categories/delete.js index dc3cba36a5..ff491a5dbc 100644 --- a/src/categories/delete.js +++ b/src/categories/delete.js @@ -39,6 +39,7 @@ module.exports = function (Categories) { `cid:${cid}:tids:pinned`, `cid:${cid}:tids:posts`, `cid:${cid}:tids:votes`, + `cid:${cid}:tids:views`, `cid:${cid}:tids:lastposttime`, `cid:${cid}:recent_tids`, `cid:${cid}:pids`, @@ -48,7 +49,8 @@ module.exports = function (Categories) { `cid:${cid}:tag:whitelist`, `category:${cid}`, ]); - await groups.destroy(privileges.categories.privilegeList.map(privilege => `cid:${cid}:privileges:${privilege}`)); + const privilegeList = await privileges.categories.getPrivilegeList(); + await groups.destroy(privilegeList.map(privilege => `cid:${cid}:privileges:${privilege}`)); } async function removeFromParent(cid) { diff --git a/src/categories/search.js b/src/categories/search.js index 5a19b9fab0..a4f079e2fd 100644 --- a/src/categories/search.js +++ b/src/categories/search.js @@ -18,6 +18,7 @@ module.exports = function (Categories) { let cids = await findCids(query, data.hardCap); const result = await plugins.hooks.fire('filter:categories.search', { + data: data, cids: cids, uid: uid, }); diff --git a/src/categories/topics.js b/src/categories/topics.js index 381776738d..fdba6ab59d 100644 --- a/src/categories/topics.js +++ b/src/categories/topics.js @@ -100,6 +100,8 @@ module.exports = function (Categories) { set = `cid:${cid}:tids:posts`; } else if (sort === 'most_votes') { set = `cid:${cid}:tids:votes`; + } else if (sort === 'most_views') { + set = `cid:${cid}:tids:views`; } if (data.tag) { @@ -123,7 +125,7 @@ module.exports = function (Categories) { Categories.getSortedSetRangeDirection = async function (sort) { sort = sort || 'newest_to_oldest'; - const direction = sort === 'newest_to_oldest' || sort === 'most_posts' || sort === 'most_votes' ? 'highest-to-lowest' : 'lowest-to-highest'; + const direction = ['newest_to_oldest', 'most_posts', 'most_votes', 'most_views'].includes(sort) ? 'highest-to-lowest' : 'lowest-to-highest'; const result = await plugins.hooks.fire('filter:categories.getSortedSetRangeDirection', { sort: sort, direction: direction, diff --git a/src/categories/update.js b/src/categories/update.js index cdca138635..04650a4c46 100644 --- a/src/categories/update.js +++ b/src/categories/update.js @@ -71,6 +71,9 @@ module.exports = function (Categories) { } const categoryData = await Categories.getCategoryFields(cid, ['parentCid', 'order']); const oldParent = categoryData.parentCid; + if (oldParent === newParent) { + return; + } await Promise.all([ db.sortedSetRemove(`cid:${oldParent}:children`, cid), db.sortedSetAdd(`cid:${newParent}:children`, categoryData.order, cid), diff --git a/src/cli/colors.js b/src/cli/colors.js index 35f30376ae..4ba0012a40 100644 --- a/src/cli/colors.js +++ b/src/cli/colors.js @@ -8,17 +8,13 @@ const { Command, Help } = require('commander'); const colors = [ // depth = 0, top-level command - { - command: 'yellow', - option: 'cyan', - arg: 'magenta', - }, + { command: 'yellow', option: 'cyan', arg: 'magenta' }, // depth = 1, second-level commands - { - command: 'green', - option: 'blue', - arg: 'red', - }, + { command: 'green', option: 'blue', arg: 'red' }, + // depth = 2, third-level commands + { command: 'yellow', option: 'cyan', arg: 'magenta' }, + // depth = 3 fourth-level commands + { command: 'green', option: 'blue', arg: 'red' }, ]; function humanReadableArgName(arg) { diff --git a/src/cli/index.js b/src/cli/index.js index e69910cf1f..b7ef494f36 100644 --- a/src/cli/index.js +++ b/src/cli/index.js @@ -253,6 +253,10 @@ resetCommand }); }); +// user +program + .addCommand(require('./user')()); + // upgrades program .command('upgrade [scripts...]') diff --git a/src/cli/upgrade-plugins.js b/src/cli/upgrade-plugins.js index b6cd6ac2a7..dfa2d38d0c 100644 --- a/src/cli/upgrade-plugins.js +++ b/src/cli/upgrade-plugins.js @@ -7,7 +7,6 @@ const semver = require('semver'); const fs = require('fs'); const path = require('path'); const nconf = require('nconf'); -const util = require('util'); const { paths, pluginNamePattern } = require('../constants'); @@ -91,8 +90,8 @@ async function getSuggestedModules(nbbVersion, toCheck) { async function checkPlugins() { process.stdout.write('Checking installed plugins and themes for updates... '); const [plugins, nbbVersion] = await Promise.all([ - getInstalledPlugins, - getCurrentVersion, + getInstalledPlugins(), + getCurrentVersion(), ]); const toCheck = Object.keys(plugins); @@ -138,9 +137,8 @@ async function upgradePlugins() { prompt.message = ''; prompt.delimiter = ''; - const promptGet = util.promisify((schema, callback) => prompt.get(schema, callback)); prompt.start(); - const result = await promptGet({ + const result = await prompt.get({ name: 'upgrade', description: '\nProceed with upgrade (y|n)?'.reset, type: 'string', diff --git a/src/cli/user.js b/src/cli/user.js new file mode 100644 index 0000000000..eda3502133 --- /dev/null +++ b/src/cli/user.js @@ -0,0 +1,310 @@ +'use strict'; + +const { Command, Option } = require('commander'); + +module.exports = () => { + const userCmd = new Command('user') + .description('Manage users') + .arguments('[command]'); + + userCmd.configureHelp(require('./colors')); + const userCommands = UserCommands(); + + userCmd + .command('info') + .description('Display user info by uid/username/userslug.') + .option('-i, --uid ', 'Retrieve user by uid') + .option('-u, --username ', 'Retrieve user by username') + .option('-s, --userslug ', 'Retrieve user by userslug') + .action((...args) => execute(userCommands.info, args)); + userCmd + .command('create') + .description('Create a new user.') + .arguments('') + .option('-p, --password ', 'Set a new password. (Auto-generates if omitted)') + .option('-e, --email ', 'Associate with an email.') + .action((...args) => execute(userCommands.create, args)); + userCmd + .command('reset') + .description('Reset a user\'s password or send a password reset email.') + .arguments('') + .option('-p, --password ', 'Set a new password. (Auto-generates if passed empty)', false) + .option('-s, --send-reset-email', 'Send a password reset email.', false) + .action((...args) => execute(userCommands.reset, args)); + userCmd + .command('delete') + .description('Delete user(s) and/or their content') + .arguments('') + .addOption( + new Option('-t, --type [operation]', 'Delete user content ([purge]), leave content ([account]), or delete content only ([content])') + .choices(['purge', 'account', 'content']).default('purge') + ) + .action((...args) => execute(userCommands.deleteUser, args)); + + const make = userCmd.command('make') + .description('Make user(s) admin, global mod, moderator or a regular user.') + .arguments('[command]'); + + make.command('admin') + .description('Make user(s) an admin') + .arguments('') + .action((...args) => execute(userCommands.makeAdmin, args)); + make.command('global-mod') + .description('Make user(s) a global moderator') + .arguments('') + .action((...args) => execute(userCommands.makeGlobalMod, args)); + make.command('mod') + .description('Make uid(s) of user(s) moderator of given category IDs (cids)') + .arguments('') + .requiredOption('-c, --cid ', 'ID(s) of categories to make the user a moderator of') + .action((...args) => execute(userCommands.makeMod, args)); + make.command('regular') + .description('Make user(s) a non-privileged user') + .arguments('') + .action((...args) => execute(userCommands.makeRegular, args)); + + return userCmd; +}; + +let db; +let user; +let groups; +let privileges; +let privHelpers; +let utils; +let winston; + +async function init() { + db = require('../database'); + await db.init(); + + user = require('../user'); + groups = require('../groups'); + privileges = require('../privileges'); + privHelpers = require('../privileges/helpers'); + utils = require('../utils'); + winston = require('winston'); +} + +async function execute(cmd, args) { + await init(); + try { + await cmd(...args); + } catch (err) { + const userError = err.name === 'UserError'; + winston.error(`[userCmd/${cmd.name}] ${userError ? `${err.message}` : 'Command failed.'}`, userError ? '' : err); + process.exit(1); + } + + process.exit(); +} + +function UserCmdHelpers() { + async function getAdminUidOrFail() { + const adminUid = (await db.getSortedSetMembers('group:administrators:members')).reverse()[0]; + if (!adminUid) { + const err = new Error('An admin account does not exists to execute the operation.'); + err.name = 'UserError'; + throw err; + } + return adminUid; + } + + async function setupApp() { + const nconf = require('nconf'); + const Benchpress = require('benchpressjs'); + + const meta = require('../meta'); + await meta.configs.init(); + + const webserver = require('../webserver'); + const viewsDir = nconf.get('views_dir'); + + webserver.app.engine('tpl', (filepath, data, next) => { + filepath = filepath.replace(/\.tpl$/, '.js'); + + Benchpress.__express(filepath, data, next); + }); + webserver.app.set('view engine', 'tpl'); + webserver.app.set('views', viewsDir); + + const emailer = require('../emailer'); + emailer.registerApp(webserver.app); + } + + const argParsers = { + intParse: (value, varName) => { + const parsedValue = parseInt(value, 10); + if (isNaN(parsedValue)) { + const err = new Error(`"${varName}" expected to be a number.`); + err.name = 'UserError'; + throw err; + } + return parsedValue; + }, + intArrayParse: (values, varName) => values.map(value => argParsers.intParse(value, varName)), + }; + + return { + argParsers, + getAdminUidOrFail, + setupApp, + }; +} + +function UserCommands() { + const { argParsers, getAdminUidOrFail, setupApp } = UserCmdHelpers(); + + async function info({ uid, username, userslug }) { + if (!uid && !username && !userslug) { + return winston.error('[userCmd/info] At least one option has to be passed (--uid, --username or --userslug).'); + } + + if (uid) { + uid = argParsers.intParse(uid, 'uid'); + } else if (username) { + uid = await user.getUidByUsername(username); + } else { + uid = await user.getUidByUserslug(userslug); + } + + const userData = await user.getUserData(uid); + winston.info('[userCmd/info] User info retrieved:'); + console.log(userData); + } + + async function create(username, { password, email }) { + let pwGenerated = false; + if (password === undefined) { + password = utils.generateUUID().slice(0, 8); + pwGenerated = true; + } + + const userExists = await user.getUidByUsername(username); + if (userExists) { + return winston.error(`[userCmd/create] A user with username '${username}' already exists`); + } + + const uid = await user.create({ + username, + password, + email, + }); + + winston.info(`[userCmd/create] User '${username}'${password ? '' : ' without a password'} has been created with uid: ${uid}.\ +${pwGenerated ? ` Generated password: ${password}` : ''}`); + } + + async function reset(uid, { password, sendResetEmail }) { + uid = argParsers.intParse(uid, 'uid'); + + if (password === false && sendResetEmail === false) { + return winston.error('[userCmd/reset] At least one option has to be passed (--password or --send-reset-email).'); + } + + const userExists = await user.exists(uid); + if (!userExists) { + return winston.error(`[userCmd/reset] A user with given uid does not exists.`); + } + + let pwGenerated = false; + if (password === '') { + password = utils.generateUUID().slice(0, 8); + pwGenerated = true; + } + + const adminUid = await getAdminUidOrFail(); + + if (password) { + await user.changePassword(adminUid, { + newPassword: password, + uid, + }); + winston.info(`[userCmd/reset] ${password ? 'User password changed.' : ''}${pwGenerated ? ` Generated password: ${password}` : ''}`); + } + + if (sendResetEmail) { + const userEmail = await user.getUserField(uid, 'email'); + if (!userEmail) { + return winston.error('User doesn\'t have an email address to send reset email.'); + } + await setupApp(); + await user.reset.send(userEmail); + winston.info('[userCmd/reset] Password reset email has been sent.'); + } + } + + async function deleteUser(uids, { type }) { + uids = argParsers.intArrayParse(uids, 'uids'); + + const userExists = await user.exists(uids); + if (!userExists || userExists.some(r => r === false)) { + return winston.error(`[userCmd/reset] A user with given uid does not exists.`); + } + + await db.initSessionStore(); + const adminUid = await getAdminUidOrFail(); + + switch (type) { + case 'purge': + await Promise.all(uids.map(uid => user.delete(adminUid, uid))); + winston.info(`[userCmd/delete] User(s) with their content has been deleted.`); + break; + case 'account': + await Promise.all(uids.map(uid => user.deleteAccount(uid))); + winston.info(`[userCmd/delete] User(s) has been deleted, their content left intact.`); + break; + case 'content': + await Promise.all(uids.map(uid => user.deleteContent(adminUid, uid))); + winston.info(`[userCmd/delete] User(s)' content has been deleted.`); + break; + } + } + + async function makeAdmin(uids) { + uids = argParsers.intArrayParse(uids, 'uids'); + await Promise.all(uids.map(uid => groups.join('administrators', uid))); + + winston.info('[userCmd/make/admin] User(s) added as administrators.'); + } + + async function makeGlobalMod(uids) { + uids = argParsers.intArrayParse(uids, 'uids'); + await Promise.all(uids.map(uid => groups.join('Global Moderators', uid))); + + winston.info('[userCmd/make/globalMod] User(s) added as global moderators.'); + } + + async function makeMod(uids, { cid: cids }) { + uids = argParsers.intArrayParse(uids, 'uids'); + cids = argParsers.intArrayParse(cids, 'cids'); + + const categoryPrivList = await privileges.categories.getPrivilegeList(); + await privHelpers.giveOrRescind(groups.join, categoryPrivList, cids, uids); + + winston.info('[userCmd/make/mod] User(s) added as moderators to given categories.'); + } + + async function makeRegular(uids) { + uids = argParsers.intArrayParse(uids, 'uids'); + + await Promise.all(uids.map(uid => groups.leave(['administrators', 'Global Moderators'], uid))); + + const categoryPrivList = await privileges.categories.getPrivilegeList(); + const cids = await db.getSortedSetRevRange('categories:cid', 0, -1); + await privHelpers.giveOrRescind(groups.leave, categoryPrivList, cids, uids); + + winston.info('[userCmd/make/regular] User(s) made regular/non-privileged.'); + } + + return { + info, + create, + reset, + deleteUser, + makeAdmin, + makeGlobalMod, + makeMod, + makeRegular, + }; +} diff --git a/src/controllers/accounts/helpers.js b/src/controllers/accounts/helpers.js index 359355da87..21e3e16908 100644 --- a/src/controllers/accounts/helpers.js +++ b/src/controllers/accounts/helpers.js @@ -43,13 +43,13 @@ helpers.getUserDataByUserSlug = async function (userslug, callerUID, query = {}) userData.emailClass = 'hide'; - if (!isAdmin && !isGlobalModerator && !isSelf && (!userSettings.showemail || meta.config.hideEmail)) { + if (!results.canEdit && (!userSettings.showemail || meta.config.hideEmail)) { userData.email = ''; } else if (!userSettings.showemail) { userData.emailClass = ''; } - if (!isAdmin && !isGlobalModerator && !isSelf && (!userSettings.showfullname || meta.config.hideFullname)) { + if (!results.canEdit && (!userSettings.showfullname || meta.config.hideFullname)) { userData.fullname = ''; } @@ -78,7 +78,7 @@ helpers.getUserDataByUserSlug = async function (userslug, callerUID, query = {}) userData.isSelf = isSelf; userData.isFollowing = results.isFollowing; userData.hasPrivateChat = results.hasPrivateChat; - userData.showHidden = isSelf || isAdmin || (isGlobalModerator && !results.isTargetAdmin); + userData.showHidden = results.canEdit; // remove in v1.19.0 userData.groups = Array.isArray(results.groups) && results.groups.length ? results.groups[0] : []; userData.disableSignatures = meta.config.disableSignatures === 1; userData['reputation:disabled'] = meta.config['reputation:disabled'] === 1; @@ -95,13 +95,13 @@ helpers.getUserDataByUserSlug = async function (userslug, callerUID, query = {}) userData.sso = results.sso.associations; userData.banned = Boolean(userData.banned); - userData.website = validator.escape(String(userData.website || '')); + userData.website = escape(userData.website); userData.websiteLink = !userData.website.startsWith('http') ? `http://${userData.website}` : userData.website; userData.websiteName = userData.website.replace(validator.escape('http://'), '').replace(validator.escape('https://'), ''); - userData.fullname = validator.escape(String(userData.fullname || '')); - userData.location = validator.escape(String(userData.location || '')); - userData.signature = validator.escape(String(userData.signature || '')); + userData.fullname = escape(userData.fullname); + userData.location = escape(userData.location); + userData.signature = escape(userData.signature); userData.birthday = validator.escape(String(userData.birthday || '')); userData.moderationNote = validator.escape(String(userData.moderationNote || '')); @@ -125,6 +125,10 @@ helpers.getUserDataByUserSlug = async function (userslug, callerUID, query = {}) return hookData.userData; }; +function escape(value) { + return translator.escape(validator.escape(String(value || ''))); +} + async function getAllData(uid, callerUID) { return await utils.promiseParallel({ userData: user.getUserData(uid), @@ -234,6 +238,7 @@ async function parseAboutMe(userData) { } userData.aboutme = validator.escape(String(userData.aboutme || '')); const parsed = await plugins.hooks.fire('filter:parse.aboutme', userData.aboutme); + userData.aboutme = translator.escape(userData.aboutme); userData.aboutmeParsed = translator.escape(parsed); } diff --git a/src/controllers/accounts/posts.js b/src/controllers/accounts/posts.js index ab213609bf..b266a2b185 100644 --- a/src/controllers/accounts/posts.js +++ b/src/controllers/accounts/posts.js @@ -140,7 +140,7 @@ postsController.getTopics = async function (req, res, next) { await getPostsFromUserSet('account/topics', req, res, next); }; -async function getPostsFromUserSet(template, req, res, callback) { +async function getPostsFromUserSet(template, req, res, next) { const data = templateToData[template]; const page = Math.max(1, parseInt(req.query.page, 10) || 1); @@ -150,7 +150,7 @@ async function getPostsFromUserSet(template, req, res, callback) { ]); if (!userData) { - return callback(); + return next(); } const itemsPerPage = data.type === 'topics' ? settings.topicsPerPage : settings.postsPerPage; const start = (page - 1) * itemsPerPage; diff --git a/src/controllers/accounts/settings.js b/src/controllers/accounts/settings.js index 848f62b893..cc79c10880 100644 --- a/src/controllers/accounts/settings.js +++ b/src/controllers/accounts/settings.js @@ -53,6 +53,7 @@ settingsController.get = async function (req, res, next) { { value: 'off', name: '[[user:digest_off]]', selected: userData.settings.dailyDigestFreq === 'off' }, { value: 'day', name: '[[user:digest_daily]]', selected: userData.settings.dailyDigestFreq === 'day' }, { value: 'week', name: '[[user:digest_weekly]]', selected: userData.settings.dailyDigestFreq === 'week' }, + { value: 'biweek', name: '[[user:digest_biweekly]]', selected: userData.settings.dailyDigestFreq === 'biweek' }, { value: 'month', name: '[[user:digest_monthly]]', selected: userData.settings.dailyDigestFreq === 'month' }, ]; @@ -141,17 +142,11 @@ const doUnsubscribe = async (payload) => { }; settingsController.unsubscribe = async (req, res) => { - let payload; try { - payload = await jwtVerifyAsync(req.params.token); + const payload = await jwtVerifyAsync(req.params.token); if (!payload || !unsubscribable.includes(payload.template)) { return; } - } catch (err) { - throw new Error(err); - } - - try { await doUnsubscribe(payload); res.render('unsubscribe', { payload: payload, diff --git a/src/controllers/admin/admins-mods.js b/src/controllers/admin/admins-mods.js index 7aaf7558e6..be5c79d1d5 100644 --- a/src/controllers/admin/admins-mods.js +++ b/src/controllers/admin/admins-mods.js @@ -19,10 +19,11 @@ AdminsMods.get = async function (req, res, next) { if (!selectedCategory) { return next(); } - const [admins, globalMods, moderators] = await Promise.all([ + const [admins, globalMods, moderators, categoryPrivList] = await Promise.all([ groups.get('administrators', { uid: req.uid }), groups.get('Global Moderators', { uid: req.uid }), getModeratorsOfCategories(selectedCategory), + privileges.categories.getUserPrivilegeList(), ]); res.render('admin/manage/admins-mods', { @@ -30,7 +31,7 @@ AdminsMods.get = async function (req, res, next) { globalMods: globalMods, categoryMods: [moderators], selectedCategory: selectedCategory, - allPrivileges: privileges.categories.userPrivilegeList, + allPrivileges: categoryPrivList, }); }; diff --git a/src/controllers/admin/dashboard.js b/src/controllers/admin/dashboard.js index bf47cb9f58..d16d2dbaa6 100644 --- a/src/controllers/admin/dashboard.js +++ b/src/controllers/admin/dashboard.js @@ -4,6 +4,7 @@ const nconf = require('nconf'); const semver = require('semver'); const winston = require('winston'); const _ = require('lodash'); +const validator = require('validator'); const versions = require('../../admin/versions'); const db = require('../../database'); @@ -18,12 +19,13 @@ const emailer = require('../../emailer'); const dashboardController = module.exports; dashboardController.get = async function (req, res) { - const [stats, notices, latestVersion, lastrestart, isAdmin] = await Promise.all([ + const [stats, notices, latestVersion, lastrestart, isAdmin, popularSearches] = await Promise.all([ getStats(), getNotices(), getLatestVersion(), getLastRestart(), user.isAdministrator(req.uid), + getPopularSearches(), ]); const version = nconf.get('version'); @@ -38,6 +40,7 @@ dashboardController.get = async function (req, res) { canRestart: !!process.send, lastrestart: lastrestart, showSystemControls: isAdmin, + popularSearches: popularSearches, }); }; @@ -238,6 +241,11 @@ async function getLastRestart() { return lastrestart; } +async function getPopularSearches() { + const searches = await db.getSortedSetRevRangeWithScores('searches:all', 0, 9); + return searches.map(s => ({ value: validator.escape(String(s.value)), score: s.score })); +} + dashboardController.getLogins = async (req, res) => { let stats = await getStats(); stats = stats.filter(stat => stat.name === '[[admin/dashboard:logins]]').map(({ ...stat }) => { @@ -327,3 +335,10 @@ dashboardController.getTopics = async (req, res) => { topics: topicData, }); }; + +dashboardController.getSearches = async (req, res) => { + const searches = await db.getSortedSetRevRangeWithScores('searches:all', 0, 99); + res.render('admin/dashboard/searches', { + searches: searches.map(s => ({ value: validator.escape(String(s.value)), score: s.score })), + }); +}; diff --git a/src/controllers/admin/events.js b/src/controllers/admin/events.js index 1ca791ac16..f077972aca 100644 --- a/src/controllers/admin/events.js +++ b/src/controllers/admin/events.js @@ -20,15 +20,17 @@ eventsController.get = async function (req, res) { const currentFilter = req.query.type || ''; - const [eventCount, eventData] = await Promise.all([ + const [eventCount, eventData, counts] = await Promise.all([ db.sortedSetCount(`events:time${currentFilter ? `:${currentFilter}` : ''}`, from || '-inf', to), events.getEvents(currentFilter, start, stop, from || '-inf', to), + db.sortedSetsCard([''].concat(events.types).map(type => `events:time${type ? `:${type}` : ''}`)), ]); - const types = [''].concat(events.types).map(type => ({ + const types = [''].concat(events.types).map((type, index) => ({ value: type, name: type || 'all', selected: type === currentFilter, + count: counts[index], })); const pageCount = Math.max(1, Math.ceil(eventCount / itemsPerPage)); diff --git a/src/controllers/admin/info.js b/src/controllers/admin/info.js index 60558f2ad6..efd126579e 100644 --- a/src/controllers/admin/info.js +++ b/src/controllers/admin/info.js @@ -11,6 +11,8 @@ const rooms = require('../../socket.io/admin/rooms'); const infoController = module.exports; let info = {}; +let previousUsage = process.cpuUsage(); +let usageStartDate = Date.now(); infoController.get = function (req, res) { info = {}; @@ -69,7 +71,7 @@ async function getNodeInfo() { version: process.version, memoryUsage: process.memoryUsage(), uptime: process.uptime(), - cpuUsage: process.cpuUsage(), + cpuUsage: getCpuUsage(), }, os: { hostname: os.hostname(), @@ -88,14 +90,12 @@ async function getNodeInfo() { jobsDisabled: nconf.get('jobsDisabled'), }, }; - data.process.cpuUsage.user /= 1000000; - data.process.cpuUsage.user = data.process.cpuUsage.user.toFixed(2); - data.process.cpuUsage.system /= 1000000; - data.process.cpuUsage.system = data.process.cpuUsage.system.toFixed(2); - data.process.memoryUsage.humanReadable = (data.process.memoryUsage.rss / (1024 * 1024)).toFixed(2); + + data.process.memoryUsage.humanReadable = (data.process.memoryUsage.rss / (1024 * 1024 * 1024)).toFixed(3); data.process.uptimeHumanReadable = humanReadableUptime(data.process.uptime); - data.os.freemem = (data.os.freemem / 1000000).toFixed(2); - data.os.totalmem = (data.os.totalmem / 1000000).toFixed(2); + data.os.freemem = (data.os.freemem / (1024 * 1024 * 1024)).toFixed(2); + data.os.totalmem = (data.os.totalmem / (1024 * 1024 * 1024)).toFixed(2); + data.os.usedmem = (data.os.totalmem - data.os.freemem).toFixed(2); const [stats, gitInfo] = await Promise.all([ rooms.getLocalStats(), getGitInfo(), @@ -105,6 +105,16 @@ async function getNodeInfo() { return data; } +function getCpuUsage() { + const newUsage = process.cpuUsage(); + const diff = (newUsage.user + newUsage.system) - (previousUsage.user + previousUsage.system); + const now = Date.now(); + const result = diff / ((now - usageStartDate) * 1000) * 100; + previousUsage = newUsage; + usageStartDate = now; + return result.toFixed(2); +} + function humanReadableUptime(seconds) { if (seconds < 60) { return `${Math.floor(seconds)}s`; diff --git a/src/controllers/admin/privileges.js b/src/controllers/admin/privileges.js index fbcd504a53..c3ab23b9d6 100644 --- a/src/controllers/admin/privileges.js +++ b/src/controllers/admin/privileges.js @@ -44,8 +44,9 @@ privilegesController.get = async function (req, res) { res.render('admin/manage/privileges', { privileges: privilegesData, categories: categoriesData, - selectedCategory: selectedCategory, - cid: cid, - group: group, + selectedCategory, + cid, + group, + isAdminPriv, }); }; diff --git a/src/controllers/admin/settings.js b/src/controllers/admin/settings.js index 41166e6d1c..d4abecd479 100644 --- a/src/controllers/admin/settings.js +++ b/src/controllers/admin/settings.js @@ -1,5 +1,7 @@ 'use strict'; +const validator = require('validator'); + const meta = require('../../meta'); const emailer = require('../../emailer'); const notifications = require('../../notifications'); @@ -72,6 +74,7 @@ settingsController.navigation = async function (req, res) { enabled.selected = index === 0; enabled.title = translator.escape(enabled.title); enabled.text = translator.escape(enabled.text); + enabled.dropdownContent = translator.escape(validator.escape(String(enabled.dropdownContent || ''))); enabled.groups = admin.groups.map(group => ({ displayName: group.displayName, selected: enabled.groups.includes(group.name), diff --git a/src/controllers/api.js b/src/controllers/api.js index 3e4e2fa216..3032fc80ff 100644 --- a/src/controllers/api.js +++ b/src/controllers/api.js @@ -14,6 +14,7 @@ const apiController = module.exports; const relative_path = nconf.get('relative_path'); const upload_url = nconf.get('upload_url'); +const asset_base_url = nconf.get('asset_base_url'); const socketioTransports = nconf.get('socket.io:transports') || ['polling', 'websocket']; const socketioOrigins = nconf.get('socket.io:origins'); const websocketAddress = nconf.get('socket.io:address') || ''; @@ -22,7 +23,8 @@ apiController.loadConfig = async function (req) { const config = { relative_path, upload_url, - assetBaseUrl: `${relative_path}/assets`, + asset_base_url, + assetBaseUrl: asset_base_url, // deprecate in 1.20.x siteTitle: validator.escape(String(meta.config.title || meta.config.browserTitle || 'NodeBB')), browserTitle: validator.escape(String(meta.config.browserTitle || meta.config.title || 'NodeBB')), titleLayout: (meta.config.titleLayout || '{pageTitle} | {browserTitle}').replace(/{/g, '{').replace(/}/g, '}'), @@ -80,6 +82,7 @@ apiController.loadConfig = async function (req) { size: meta.config.topicThumbSize, }, iconBackgrounds: await user.getIconBackgrounds(req.uid), + emailPrompt: meta.config.emailPrompt, }; let settings = config; diff --git a/src/controllers/authentication.js b/src/controllers/authentication.js index 132c0b33f7..e970f895cd 100644 --- a/src/controllers/authentication.js +++ b/src/controllers/authentication.js @@ -14,7 +14,6 @@ const user = require('../user'); const plugins = require('../plugins'); const utils = require('../utils'); const slugify = require('../slugify'); -const translator = require('../translator'); const helpers = require('./helpers'); const privileges = require('../privileges'); const sockets = require('../socket.io'); @@ -129,7 +128,7 @@ async function addToApprovalQueue(req, userData) { if (meta.config.showAverageApprovalTime) { const average_time = await db.getObjectField('registration:queue:approval:times', 'average'); if (average_time > 0) { - message += ` [[register:registration-queue-average-time, ${Math.floor(average_time / 60)}, ${average_time % 60}]]`; + message += ` [[register:registration-queue-average-time, ${Math.floor(average_time / 60)}, ${Math.floor(average_time % 60)}]]`; } } if (meta.config.autoApproveTime > 0) { @@ -138,16 +137,14 @@ async function addToApprovalQueue(req, userData) { return { message: message }; } -authenticationController.registerComplete = function (req, res, next) { - // For the interstitials that respond, execute the callback with the form body - plugins.hooks.fire('filter:register.interstitial', { - req, - userData: req.session.registration, - interstitials: [], - }, async (err, data) => { - if (err) { - return next(err); - } +authenticationController.registerComplete = async function (req, res) { + try { + // For the interstitials that respond, execute the callback with the form body + const data = await plugins.hooks.fire('filter:register.interstitial', { + req, + userData: req.session.registration, + interstitials: [], + }); const callbacks = data.interstitials.reduce((memo, cur) => { if (cur.hasOwnProperty('callback') && typeof cur.callback === 'function') { @@ -165,20 +162,17 @@ authenticationController.registerComplete = function (req, res, next) { return memo; }, []); - const done = function (err, data) { + const done = function (data) { delete req.session.registration; - if (err) { - return res.redirect(`${nconf.get('relative_path')}/?register=${encodeURIComponent(err.message)}`); - } - - if (!err && data && data.message) { - return res.redirect(`${nconf.get('relative_path')}/?register=${encodeURIComponent(data.message)}`); + const relative_path = nconf.get('relative_path'); + if (data && data.message) { + return res.redirect(`${relative_path}/?register=${encodeURIComponent(data.message)}`); } if (req.session.returnTo) { - res.redirect(nconf.get('relative_path') + req.session.returnTo); + res.redirect(relative_path + req.session.returnTo.replace(new RegExp(`^${relative_path}`), '')); } else { - res.redirect(`${nconf.get('relative_path')}/`); + res.redirect(`${relative_path}/`); } }; @@ -199,8 +193,7 @@ authenticationController.registerComplete = function (req, res, next) { if (!data) { return winston.warn('[register] Interstitial callbacks processed with no errors, but one or more interstitials remain. This is likely an issue with one of the interstitials not properly handling a null case or invalid value.'); } - - done(); + done(data); } else { // Update user hash, clear registration data in session const payload = req.session.registration; @@ -217,14 +210,17 @@ authenticationController.registerComplete = function (req, res, next) { await user.setUserFields(uid, payload); done(); } - }); + } catch (err) { + delete req.session.registration; + res.redirect(`${nconf.get('relative_path')}/?register=${encodeURIComponent(err.message)}`); + } }; authenticationController.registerAbort = function (req, res) { if (req.uid) { // Clear interstitial data and continue on... delete req.session.registration; - res.redirect(nconf.get('relative_path') + req.session.returnTo); + res.redirect(nconf.get('relative_path') + (req.session.returnTo || '/')); } else { // End the session and redirect to home req.session.destroy(() => { @@ -263,7 +259,7 @@ authenticationController.login = async (req, res, next) => { } } if (isEmailLogin || isUsernameLogin) { - (res.locals.continueLogin || continueLogin)(strategy, req, res, next); + continueLogin(strategy, req, res, next); } else { errorHandler(req, res, `[[error:wrong-login-type-${loginWith}]]`, 400); } @@ -276,7 +272,7 @@ function continueLogin(strategy, req, res, next) { passport.authenticate(strategy, async (err, userData, info) => { if (err) { plugins.hooks.fire('action:login.continue', { req, strategy, userData, error: err }); - return helpers.noScriptErrors(req, res, err.message, 403); + return helpers.noScriptErrors(req, res, err.data || err.message, 403); } if (!userData) { @@ -307,9 +303,7 @@ function continueLogin(strategy, req, res, next) { req.session.passwordExpired = true; const code = await user.reset.generate(userData.uid); - res.status(200).send({ - next: `${nconf.get('relative_path')}/reset/${code}`, - }); + (res.locals.redirectAfterLogin || redirectAfterLogin)(req, res, `${nconf.get('relative_path')}/reset/${code}`); } else { delete req.query.lang; await authenticationController.doLogin(req, userData.uid); @@ -323,17 +317,21 @@ function continueLogin(strategy, req, res, next) { destination = `${nconf.get('relative_path')}/`; } - if (req.body.noscript === 'true') { - res.redirect(`${destination}?loggedin`); - } else { - res.status(200).send({ - next: destination, - }); - } + (res.locals.redirectAfterLogin || redirectAfterLogin)(req, res, destination); } })(req, res, next); } +function redirectAfterLogin(req, res, destination) { + if (req.body.noscript === 'true') { + res.redirect(`${destination}?loggedin`); + } else { + res.status(200).send({ + next: destination, + }); + } +} + authenticationController.doLogin = async function (req, uid) { if (!uid) { return; @@ -436,8 +434,7 @@ authenticationController.localLogin = async function (req, username, password, n userData.isAdminOrGlobalMod = isAdminOrGlobalMod; if (!canLoginIfBanned) { - const banMesage = await getBanInfo(uid); - return next(new Error(banMesage)); + return next(await getBanError(uid)); } // Doing this after the ban check, because user's privileges might change after a ban expires @@ -496,19 +493,19 @@ authenticationController.logout = async function (req, res, next) { } }; -async function getBanInfo(uid) { +async function getBanError(uid) { try { const banInfo = await user.getLatestBanInfo(uid); if (!banInfo.reason) { - banInfo.reason = await translator.translate('[[user:info.banned-no-reason]]'); + banInfo.reason = '[[user:info.banned-no-reason]]'; } - return banInfo.banned_until ? - `[[error:user-banned-reason-until, ${banInfo.banned_until_readable}, ${banInfo.reason}]]` : - `[[error:user-banned-reason, ${banInfo.reason}]]`; + const err = new Error(banInfo.reason); + err.data = banInfo; + return err; } catch (err) { if (err.message === 'no-ban-info') { - return '[[error:user-banned]]'; + return new Error('[[error:user-banned]]'); } throw err; } diff --git a/src/controllers/errors.js b/src/controllers/errors.js index eace35c6c0..58f12d9f98 100644 --- a/src/controllers/errors.js +++ b/src/controllers/errors.js @@ -38,7 +38,7 @@ exports.handleURIErrors = async function handleURIErrors(err, req, res, next) { // this needs to have four arguments or express treats it as `(req, res, next)` // don't remove `next`! -exports.handleErrors = function handleErrors(err, req, res, next) { // eslint-disable-line no-unused-vars +exports.handleErrors = async function handleErrors(err, req, res, next) { // eslint-disable-line no-unused-vars const cases = { EBADCSRFTOKEN: function () { winston.error(`${req.path}\n${err.message}`); @@ -49,6 +49,9 @@ exports.handleErrors = function handleErrors(err, req, res, next) { // eslint-di }, }; const defaultHandler = async function () { + if (res.headersSent) { + return; + } // Display NodeBB error page const status = parseInt(err.status, 10); if ((status === 302 || status === 308) && err.path) { @@ -66,7 +69,7 @@ exports.handleErrors = function handleErrors(err, req, res, next) { // eslint-di return helpers.formatApiResponse(status, res, err); } - winston.error(`${req.path}\n${err.stack}`); + winston.error(`${req.originalUrl}\n${err.stack}`); res.status(status || 500); const data = { path: validator.escape(path), @@ -80,20 +83,29 @@ exports.handleErrors = function handleErrors(err, req, res, next) { // eslint-di res.render('500', data); } }; - - plugins.hooks.fire('filter:error.handle', { - cases: cases, - }, (_err, data) => { - if (_err) { - // Assume defaults - winston.warn(`[errors/handle] Unable to retrieve plugin handlers for errors: ${_err.message}`); - data.cases = cases; - } - + const data = await getErrorHandlers(cases); + try { if (data.cases.hasOwnProperty(err.code)) { data.cases[err.code](err, req, res, defaultHandler); } else { - defaultHandler(); + await defaultHandler(); } - }); + } catch (_err) { + winston.error(`${req.originalUrl}\n${_err.stack}`); + if (!res.headersSent) { + res.status(500).send(_err.message); + } + } }; + +async function getErrorHandlers(cases) { + try { + return await plugins.hooks.fire('filter:error.handle', { + cases: cases, + }); + } catch (err) { + // Assume defaults + winston.warn(`[errors/handle] Unable to retrieve plugin handlers for errors: ${err.message}`); + return { cases }; + } +} diff --git a/src/controllers/helpers.js b/src/controllers/helpers.js index 9e54c27e71..e53edae33a 100644 --- a/src/controllers/helpers.js +++ b/src/controllers/helpers.js @@ -12,6 +12,7 @@ const privileges = require('../privileges'); const categories = require('../categories'); const plugins = require('../plugins'); const meta = require('../meta'); +const middlewareHelpers = require('../middleware/helpers'); const helpers = module.exports; @@ -20,7 +21,10 @@ const url = nconf.get('url'); helpers.noScriptErrors = async function (req, res, error, httpStatus) { if (req.body.noscript !== 'true') { - return res.status(httpStatus).send(error); + if (typeof error === 'string') { + return res.status(httpStatus).send(error); + } + return res.status(httpStatus).json(error); } const middleware = require('../middleware'); const httpStatusString = httpStatus.toString(); @@ -119,9 +123,24 @@ helpers.buildTerms = function (url, term, query) { helpers.notAllowed = async function (req, res, error) { ({ error } = await plugins.hooks.fire('filter:helpers.notAllowed', { req, res, error })); + await plugins.hooks.fire('response:helpers.notAllowed', { req, res, error }); + if (res.headersSent) { + return; + } + if (req.loggedIn || req.uid === -1) { if (res.locals.isAPI) { - helpers.formatApiResponse(403, res, error); + if (req.originalUrl.startsWith(`${relative_path}/api/v3`)) { + helpers.formatApiResponse(403, res, error); + } else { + res.status(403).json({ + path: req.path.replace(/^\/api/, ''), + loggedIn: req.loggedIn, + error: error, + title: '[[global:403.title]]', + bodyClass: middlewareHelpers.buildBodyClass(req, res), + }); + } } else { const middleware = require('../middleware'); await middleware.buildHeaderAsync(req, res); diff --git a/src/controllers/search.js b/src/controllers/search.js index 0dbdff2dd0..004fe67e3b 100644 --- a/src/controllers/search.js +++ b/src/controllers/search.js @@ -3,6 +3,7 @@ const validator = require('validator'); +const db = require('../database'); const meta = require('../meta'); const plugins = require('../plugins'); const search = require('../search'); @@ -67,6 +68,7 @@ searchController.search = async function (req, res, next) { const [searchData, categoriesData] = await Promise.all([ search.search(data), buildCategories(req.uid, searchOnly), + recordSearch(data), ]); searchData.pagination = pagination.create(page, searchData.pageCount, req.query); @@ -95,6 +97,30 @@ searchController.search = async function (req, res, next) { res.render('search', searchData); }; +const searches = {}; + +async function recordSearch(data) { + const { query, searchIn } = data; + if (query) { + const cleanedQuery = String(query).trim().toLowerCase().substr(0, 255); + if (['titles', 'titlesposts', 'posts'].includes(searchIn) && cleanedQuery.length > 2) { + searches[data.uid] = searches[data.uid] || { timeoutId: 0, queries: [] }; + searches[data.uid].queries.push(cleanedQuery); + if (searches[data.uid].timeoutId) { + clearTimeout(searches[data.uid].timeoutId); + } + searches[data.uid].timeoutId = setTimeout(async () => { + const copy = searches[data.uid].queries.slice(); + const filtered = searches[data.uid].queries.filter( + q => !copy.find(query => query.startsWith(q) && query.length > q.length) + ); + await Promise.all(filtered.map(query => db.sortedSetIncrBy('searches:all', 1, query))); + delete searches[data.uid]; + }, 5000); + } + } +} + async function buildCategories(uid, searchOnly) { if (searchOnly) { return []; diff --git a/src/controllers/topics.js b/src/controllers/topics.js index 1d3d0d48d2..8b542b8ade 100644 --- a/src/controllers/topics.js +++ b/src/controllers/topics.js @@ -20,14 +20,14 @@ const url = nconf.get('url'); const relative_path = nconf.get('relative_path'); const upload_url = nconf.get('upload_url'); -topicsController.get = async function getTopic(req, res, callback) { +topicsController.get = async function getTopic(req, res, next) { const tid = req.params.topic_id; if ( (req.params.post_index && !utils.isNumber(req.params.post_index) && req.params.post_index !== 'unread') || !utils.isNumber(tid) ) { - return callback(); + return next(); } let postIndex = parseInt(req.params.post_index, 10) || 1; const [ @@ -51,7 +51,7 @@ topicsController.get = async function getTopic(req, res, callback) { invalidPagination || (topicData.scheduled && !userPrivileges.view_scheduled) ) { - return callback(); + return next(); } if (!userPrivileges['topics:read'] || (!topicData.scheduled && topicData.deleted && !userPrivileges.view_deleted)) { @@ -96,6 +96,7 @@ topicsController.get = async function getTopic(req, res, callback) { topicData.updateUrlWithPostIndex = settings.updateUrlWithPostIndex; topicData.allowMultipleBadges = meta.config.allowMultipleBadges === 1; topicData.privateUploads = meta.config.privateUploads === 1; + topicData.showPostPreviewsOnHover = meta.config.showPostPreviewsOnHover === 1; topicData.rssFeedUrl = `${relative_path}/topic/${topicData.tid}.rss`; if (req.loggedIn) { topicData.rssFeedUrl += `?uid=${req.uid}&token=${rssToken}`; @@ -334,12 +335,12 @@ topicsController.teaser = async function (req, res, next) { res.json(postData[0]); }; -topicsController.pagination = async function (req, res, callback) { +topicsController.pagination = async function (req, res, next) { const tid = req.params.topic_id; const currentPage = parseInt(req.query.page, 10) || 1; if (!utils.isNumber(tid)) { - return callback(); + return next(); } const [userPrivileges, settings, topic] = await Promise.all([ @@ -349,7 +350,7 @@ topicsController.pagination = async function (req, res, callback) { ]); if (!topic) { - return callback(); + return next(); } if (!userPrivileges.read || !privileges.topics.canViewDeletedScheduled(topic, userPrivileges)) { diff --git a/src/controllers/unread.js b/src/controllers/unread.js index 55d0d7c008..26a8c5675c 100644 --- a/src/controllers/unread.js +++ b/src/controllers/unread.js @@ -49,6 +49,7 @@ unreadController.get = async function (req, res) { data.selectedCategory = categoryData.selectedCategory; data.selectedCids = categoryData.selectedCids; data.selectCategoryLabel = '[[unread:mark_as_read]]'; + data.selectCategoryIcon = 'fa-inbox'; if (req.originalUrl.startsWith(`${nconf.get('relative_path')}/api/unread`) || req.originalUrl.startsWith(`${nconf.get('relative_path')}/unread`)) { data.title = '[[pages:unread]]'; data.breadcrumbs = helpers.buildBreadcrumbs([{ text: '[[unread:title]]' }]); diff --git a/src/controllers/write/users.js b/src/controllers/write/users.js index 43aa2c0ccc..2469ae4b2a 100644 --- a/src/controllers/write/users.js +++ b/src/controllers/write/users.js @@ -76,6 +76,11 @@ Users.deleteMany = async (req, res) => { helpers.formatApiResponse(200, res); }; +Users.changePicture = async (req, res) => { + await api.users.changePicture(req, { ...req.body, uid: req.params.uid }); + helpers.formatApiResponse(200, res); +}; + Users.updateSettings = async (req, res) => { const settings = await api.users.updateSettings(req, { ...req.body, uid: req.params.uid }); helpers.formatApiResponse(200, res, settings); diff --git a/src/controllers/write/utilities.js b/src/controllers/write/utilities.js index c0d275f55a..27df1b2ad7 100644 --- a/src/controllers/write/utilities.js +++ b/src/controllers/write/utilities.js @@ -2,7 +2,6 @@ const user = require('../../user'); const authenticationController = require('../authentication'); -const slugify = require('../../slugify'); const helpers = require('../helpers'); const Utilities = module.exports; @@ -22,26 +21,9 @@ Utilities.ping.post = (req, res) => { }; Utilities.login = (req, res) => { - res.locals.continueLogin = async (strategy, req, res) => { - const { username, password } = req.body; - - const userslug = slugify(username); - const uid = await user.getUidByUserslug(userslug); - let ok = false; - try { - ok = await user.isPasswordCorrect(uid, password, req.ip); - } catch (err) { - if (err.message === '[[error:account-locked]]') { - return helpers.formatApiResponse(429, res, err); - } - } - - if (ok) { - const userData = await user.getUsers([uid], uid); - helpers.formatApiResponse(200, res, userData.pop()); - } else { - helpers.formatApiResponse(403, res); - } + res.locals.redirectAfterLogin = async (req, res) => { + const userData = (await user.getUsers([req.uid], req.uid)).pop(); + helpers.formatApiResponse(200, res, userData); }; res.locals.noScriptErrors = (req, res, err, statusCode) => { helpers.formatApiResponse(statusCode, res, new Error(err)); diff --git a/src/database/mongo/connection.js b/src/database/mongo/connection.js index 4986a5058c..2f5d64e714 100644 --- a/src/database/mongo/connection.js +++ b/src/database/mongo/connection.js @@ -12,7 +12,7 @@ connection.getConnectionString = function (mongo) { let usernamePassword = ''; const uri = mongo.uri || ''; if (mongo.username && mongo.password) { - usernamePassword = `${nconf.get('mongo:username')}:${encodeURIComponent(nconf.get('mongo:password'))}@`; + usernamePassword = `${mongo.username}:${encodeURIComponent(mongo.password)}@`; } else if (!uri.includes('@') || !uri.slice(uri.indexOf('://') + 3, uri.indexOf('@'))) { winston.warn('You have no mongo username/password setup!'); } @@ -44,10 +44,9 @@ connection.getConnectionString = function (mongo) { connection.getConnectionOptions = function (mongo) { mongo = mongo || nconf.get('mongo'); const connOptions = { - poolSize: 10, + maxPoolSize: 10, + minPoolSize: 3, connectTimeoutMS: 90000, - useNewUrlParser: true, - useUnifiedTopology: true, }; return _.merge(connOptions, mongo.options || {}); diff --git a/src/database/mongo/hash.js b/src/database/mongo/hash.js index 0829bbf8af..67daab7b16 100644 --- a/src/database/mongo/hash.js +++ b/src/database/mongo/hash.js @@ -14,6 +14,9 @@ module.exports = function (module) { } const writeData = helpers.serializeData(data); + if (!Object.keys(writeData).length) { + return; + } try { if (isArray) { const bulk = module.client.collection('objects').initializeUnorderedBulkOp(); @@ -39,9 +42,18 @@ module.exports = function (module) { const writeData = data.map(helpers.serializeData); try { - const bulk = module.client.collection('objects').initializeUnorderedBulkOp(); - keys.forEach((key, i) => bulk.find({ _key: key }).upsert().updateOne({ $set: writeData[i] })); - await bulk.execute(); + let bulk; + keys.forEach((key, i) => { + if (Object.keys(writeData[i]).length) { + if (!bulk) { + bulk = module.client.collection('objects').initializeUnorderedBulkOp(); + } + bulk.find({ _key: key }).upsert().updateOne({ $set: writeData[i] }); + } + }); + if (bulk) { + await bulk.execute(); + } } catch (err) { if (err && err.message.startsWith('E11000 duplicate key error')) { return await module.setObjectBulk(keys, data); diff --git a/src/database/mongo/list.js b/src/database/mongo/list.js index 740d9ffd5e..e23b86bee4 100644 --- a/src/database/mongo/list.js +++ b/src/database/mongo/list.js @@ -54,9 +54,18 @@ module.exports = function (module) { if (!key) { return; } - value = helpers.valueToString(value); + const isArray = Array.isArray(value); + if (isArray) { + value = value.map(helpers.valueToString); + } else { + value = helpers.valueToString(value); + } - await module.client.collection('objects').updateOne({ _key: key }, { $pull: { array: value } }); + await module.client.collection('objects').updateOne({ + _key: key, + }, { + $pull: { array: isArray ? { $in: value } : value }, + }); }; module.listTrim = async function (key, start, stop) { diff --git a/src/database/postgres/hash.js b/src/database/postgres/hash.js index 1a733a3518..015154aa42 100644 --- a/src/database/postgres/hash.js +++ b/src/database/postgres/hash.js @@ -11,10 +11,25 @@ module.exports = function (module) { if (data.hasOwnProperty('')) { delete data['']; } - + if (!Object.keys(data).length) { + return; + } await module.transaction(async (client) => { const dataString = JSON.stringify(data); - async function setOne(key) { + + if (Array.isArray(key)) { + await helpers.ensureLegacyObjectsType(client, key, 'hash'); + await client.query({ + name: 'setObjectKeys', + text: ` + INSERT INTO "legacy_hash" ("_key", "data") + SELECT k, $2::TEXT::JSONB + FROM UNNEST($1::TEXT[]) vs(k) + ON CONFLICT ("_key") + DO UPDATE SET "data" = "legacy_hash"."data" || $2::TEXT::JSONB`, + values: [key, dataString], + }); + } else { await helpers.ensureLegacyObjectType(client, key, 'hash'); await client.query({ name: 'setObject', @@ -26,11 +41,6 @@ module.exports = function (module) { values: [key, dataString], }); } - if (Array.isArray(key)) { - await Promise.all(key.map(k => setOne(k))); - } else { - await setOne(key); - } }); }; @@ -38,8 +48,36 @@ module.exports = function (module) { if (!keys.length || !data.length) { return; } - // TODO: single query? - await Promise.all(keys.map((k, i) => module.setObject(k, data[i]))); + await module.transaction(async (client) => { + keys = keys.slice(); + data = data.filter((d, i) => { + if (d.hasOwnProperty('')) { + delete d['']; + } + const keep = !!Object.keys(d).length; + if (!keep) { + keys.splice(i, 1); + } + return keep; + }); + + if (!keys.length) { + return; + } + + await helpers.ensureLegacyObjectsType(client, keys, 'hash'); + const dataStrings = data.map(JSON.stringify); + await client.query({ + name: 'setObjectBulk', + text: ` + INSERT INTO "legacy_hash" ("_key", "data") + SELECT k, d + FROM UNNEST($1::TEXT[], $2::TEXT::JSONB[]) vs(k, d) + ON CONFLICT ("_key") + DO UPDATE SET "data" = "legacy_hash"."data" || EXCLUDED.data`, + values: [keys, dataStrings], + }); + }); }; module.setObjectField = async function (key, field, value) { @@ -49,7 +87,9 @@ module.exports = function (module) { await module.transaction(async (client) => { const valueString = JSON.stringify(value); - async function setOne(key) { + if (Array.isArray(key)) { + await module.setObject(key, { [field]: value }); + } else { await helpers.ensureLegacyObjectType(client, key, 'hash'); await client.query({ name: 'setObjectField', @@ -61,12 +101,6 @@ module.exports = function (module) { values: [key, field, valueString], }); } - - if (Array.isArray(key)) { - await Promise.all(key.map(k => setOne(k))); - } else { - await setOne(key); - } }); }; @@ -267,7 +301,19 @@ SELECT (h."data" ? $2::TEXT AND h."data"->>$2::TEXT IS NOT NULL) b if (!key || (Array.isArray(key) && !key.length) || !Array.isArray(fields) || !fields.length) { return; } - async function delKey(key, fields) { + + if (Array.isArray(key)) { + await module.pool.query({ + name: 'deleteObjectFieldsKeys', + text: ` + UPDATE "legacy_hash" + SET "data" = COALESCE((SELECT jsonb_object_agg("key", "value") + FROM jsonb_each("data") + WHERE "key" <> ALL ($2::TEXT[])), '{}') + WHERE "_key" = ANY($1::TEXT[])`, + values: [key, fields], + }); + } else { await module.pool.query({ name: 'deleteObjectFields', text: ` @@ -279,11 +325,6 @@ SELECT (h."data" ? $2::TEXT AND h."data"->>$2::TEXT IS NOT NULL) b values: [key, fields], }); } - if (Array.isArray(key)) { - await Promise.all(key.map(k => delKey(k, fields))); - } else { - await delKey(key, fields); - } }; module.incrObjectField = async function (key, field) { diff --git a/src/database/postgres/list.js b/src/database/postgres/list.js index 1f771cb719..885adb3825 100644 --- a/src/database/postgres/list.js +++ b/src/database/postgres/list.js @@ -9,28 +9,18 @@ module.exports = function (module) { } await module.transaction(async (client) => { - async function doPrepend(value) { - await client.query({ - name: 'listPrepend', - text: ` - INSERT INTO "legacy_list" ("_key", "array") - VALUES ($1::TEXT, ARRAY[$2::TEXT]) - ON CONFLICT ("_key") - DO UPDATE SET "array" = ARRAY[$2::TEXT] || "legacy_list"."array"`, - values: [key, value], - }); - } - await helpers.ensureLegacyObjectType(client, key, 'list'); - if (Array.isArray(value)) { - // TODO: perf make single query - for (const v of value) { - // eslint-disable-next-line - await doPrepend(v); - } - return; - } - await doPrepend(value); + value = Array.isArray(value) ? value : [value]; + value.reverse(); + await client.query({ + name: 'listPrependValues', + text: ` +INSERT INTO "legacy_list" ("_key", "array") +VALUES ($1::TEXT, $2::TEXT[]) +ON CONFLICT ("_key") +DO UPDATE SET "array" = EXCLUDED.array || "legacy_list"."array"`, + values: [key, value], + }); }); }; @@ -39,27 +29,18 @@ module.exports = function (module) { return; } await module.transaction(async (client) => { - async function doAppend(value) { - await client.query({ - name: 'listAppend', - text: ` - INSERT INTO "legacy_list" ("_key", "array") - VALUES ($1::TEXT, ARRAY[$2::TEXT]) - ON CONFLICT ("_key") - DO UPDATE SET "array" = "legacy_list"."array" || ARRAY[$2::TEXT]`, - values: [key, value], - }); - } + value = Array.isArray(value) ? value : [value]; + await helpers.ensureLegacyObjectType(client, key, 'list'); - if (Array.isArray(value)) { - // TODO: perf make single query - for (const v of value) { - // eslint-disable-next-line - await doAppend(v); - } - return; - } - await doAppend(value); + await client.query({ + name: 'listAppend', + text: ` +INSERT INTO "legacy_list" ("_key", "array") +VALUES ($1::TEXT, $2::TEXT[]) +ON CONFLICT ("_key") +DO UPDATE SET "array" = "legacy_list"."array" || EXCLUDED.array`, + values: [key, value], + }); }); }; @@ -94,7 +75,11 @@ RETURNING A."array"[array_length(A."array", 1)] v`, if (!key) { return; } - + // TODO: remove all values with one query + if (Array.isArray(value)) { + await Promise.all(value.map(v => module.listRemoveAll(key, v))); + return; + } await module.pool.query({ name: 'listRemoveAll', text: ` diff --git a/src/database/postgres/sorted/remove.js b/src/database/postgres/sorted/remove.js index 54bed38505..2b90dd8bc2 100644 --- a/src/database/postgres/sorted/remove.js +++ b/src/database/postgres/sorted/remove.js @@ -71,25 +71,21 @@ DELETE FROM "legacy_zset" }; module.sortedSetRemoveBulk = async function (data) { - // const keys = []; - // const values = []; - - // data.forEach(function (item) { - // keys.push(item[0]); - // values.push(item[1]); - // }); - - const promises = data.map(item => module.sortedSetRemove(item[0], item[1])); - await Promise.all(promises); + if (!Array.isArray(data) || !data.length) { + return; + } + const keys = data.map(d => d[0]); + const values = data.map(d => d[1]); - // TODO - // await query({ - // name: 'sortedSetRemoveBulk', - // text: ` - // DELETE FROM "legacy_zset" - // SELECT k, v - // FROM UNNEST($1::TEXT[], $2::TEXT[]) vs(k, v)`, - // values: [keys, values], - // }); + await module.pool.query({ + name: 'sortedSetRemoveBulk', + text: ` + DELETE FROM "legacy_zset" + WHERE (_key, value) IN ( + SELECT k, v + FROM UNNEST($1::TEXT[], $2::TEXT[]) vs(k, v) + )`, + values: [keys, values], + }); }; }; diff --git a/src/database/redis/hash.js b/src/database/redis/hash.js index 103cbc4a81..966a36eddd 100644 --- a/src/database/redis/hash.js +++ b/src/database/redis/hash.js @@ -41,7 +41,11 @@ module.exports = function (module) { return; } const batch = module.client.batch(); - keys.forEach((k, i) => batch.hmset(k, data[i])); + keys.forEach((k, i) => { + if (Object.keys(data[i]).length) { + batch.hmset(k, data[i]); + } + }); await helpers.execBatch(batch); cache.del(keys); }; diff --git a/src/database/redis/list.js b/src/database/redis/list.js index bbd48ebddc..101ef178e3 100644 --- a/src/database/redis/list.js +++ b/src/database/redis/list.js @@ -1,6 +1,8 @@ 'use strict'; module.exports = function (module) { + const helpers = require('./helpers'); + module.listPrepend = async function (key, value) { if (!key) { return; @@ -26,7 +28,13 @@ module.exports = function (module) { if (!key) { return; } - await module.client.lrem(key, 0, value); + if (Array.isArray(value)) { + const batch = module.client.batch(); + value.forEach(value => batch.lrem(key, 0, value)); + await helpers.execBatch(batch); + } else { + await module.client.lrem(key, 0, value); + } }; module.listTrim = async function (key, start, stop) { diff --git a/src/emailer.js b/src/emailer.js index 0d6e309b0a..754b794c05 100644 --- a/src/emailer.js +++ b/src/emailer.js @@ -324,7 +324,7 @@ Emailer.sendToEmail = async (template, email, language, params) => { !Plugins.hooks.hasListeners('static:email.send'); try { if (Plugins.hooks.hasListeners('filter:email.send')) { - // Deprecated, remove in v1.18.0 + // Deprecated, remove in v1.19.0 await Plugins.hooks.fire('filter:email.send', data); } else if (Plugins.hooks.hasListeners('static:email.send')) { await Plugins.hooks.fire('static:email.send', data); diff --git a/src/flags.js b/src/flags.js index f3da5cb5f2..e071bf6fa9 100644 --- a/src/flags.js +++ b/src/flags.js @@ -794,7 +794,7 @@ async function mergeBanHistory(history, targetUid, uids) { meta: [ { key: '[[user:banned]]', - value: cur.reason, + value: validator.escape(String(cur.reason)), labelClass: 'danger', }, { diff --git a/src/image.js b/src/image.js index 998bd8dd92..d1bb1de23e 100644 --- a/src/image.js +++ b/src/image.js @@ -52,8 +52,24 @@ image.resizeImage = async function (data) { sharpImage.rotate(); // auto-orients based on exif data sharpImage.resize(data.hasOwnProperty('width') ? data.width : null, data.hasOwnProperty('height') ? data.height : null); - if (data.quality && metadata.format === 'jpeg') { - sharpImage.jpeg({ quality: data.quality }); + if (data.quality) { + switch (metadata.format) { + case 'jpeg': { + sharpImage.jpeg({ + quality: data.quality, + mozjpeg: true, + }); + break; + } + + case 'png': { + sharpImage.png({ + quality: data.quality, + compressionLevel: 9, + }); + break; + } + } } await sharpImage.toFile(data.target || data.path); diff --git a/src/install.js b/src/install.js index c94e80d32c..98339c1314 100644 --- a/src/install.js +++ b/src/install.js @@ -7,9 +7,6 @@ const prompt = require('prompt'); const winston = require('winston'); const nconf = require('nconf'); const _ = require('lodash'); -const util = require('util'); - -const promptGet = util.promisify((schema, callback) => prompt.get(schema, callback)); const utils = require('./utils'); @@ -147,7 +144,7 @@ async function setupConfig() { } }); } else { - config = await promptGet(questions.main); + config = await prompt.get(questions.main); } await configureDatabases(config); await completeConfigSetup(config); @@ -298,7 +295,7 @@ async function createAdmin() { async function retryPassword(originalResults) { // Ask only the password questions - const results = await promptGet(passwordQuestions); + const results = await prompt.get(passwordQuestions); // Update the original data with newly collected password originalResults.password = results.password; @@ -312,7 +309,7 @@ async function createAdmin() { questions = questions.concat(passwordQuestions); if (!install.values) { - const results = await promptGet(questions); + const results = await prompt.get(questions); return await success(results); } // If automated setup did not provide a user password, generate one, diff --git a/src/languages.js b/src/languages.js index 200ec2a191..d307a439df 100644 --- a/src/languages.js +++ b/src/languages.js @@ -13,7 +13,11 @@ const files = fs.readdirSync(path.join(paths.nodeModules, '/timeago/locales')); Languages.timeagoCodes = files.filter(f => f.startsWith('jquery.timeago')).map(f => f.split('.')[2]); Languages.get = async function (language, namespace) { - const data = await fs.promises.readFile(path.join(languagesPath, language, `${namespace}.json`), 'utf8'); + const pathToLanguageFile = path.join(languagesPath, language, `${namespace}.json`); + if (!pathToLanguageFile.startsWith(languagesPath)) { + throw new Error('[[error:invalid-path]]'); + } + const data = await fs.promises.readFile(pathToLanguageFile, 'utf8'); const parsed = JSON.parse(data) || {}; const result = await plugins.hooks.fire('filter:languages.get', { language, diff --git a/src/messaging/rooms.js b/src/messaging/rooms.js index 09aa643fbe..6193885adb 100644 --- a/src/messaging/rooms.js +++ b/src/messaging/rooms.js @@ -69,22 +69,27 @@ module.exports = function (Messaging) { Messaging.getUserCountInRoom = async roomId => db.sortedSetCard(`chat:room:${roomId}:uids`); - Messaging.isRoomOwner = async (uid, roomId) => { - const owner = await db.getObjectField(`chat:room:${roomId}`, 'owner'); - return parseInt(uid, 10) === parseInt(owner, 10); + Messaging.isRoomOwner = async (uid, roomId, knownOwner) => { + const owner = (knownOwner == null) ? await db.getObjectField(`chat:room:${roomId}`, 'owner') : knownOwner; + const isOwner = parseInt(uid, 10) === parseInt(owner, 10); + + const payload = await plugins.hooks.fire('filter:messaging.isRoomOwner', { uid, roomId, owner, isOwner }); + return payload.isOwner; }; Messaging.addUsersToRoom = async function (uid, uids, roomId) { - const now = Date.now(); - const timestamps = uids.map(() => now); const inRoom = await Messaging.isUserInRoom(uid, roomId); - if (!inRoom) { + const payload = await plugins.hooks.fire('filter:messaging.addUsersToRoom', { uid, uids, roomId, inRoom }); + + if (!payload.inRoom) { throw new Error('[[error:cant-add-users-to-chat-room]]'); } - await db.sortedSetAdd(`chat:room:${roomId}:uids`, timestamps, uids); - await updateGroupChatField([roomId]); - await Promise.all(uids.map(uid => Messaging.addSystemMessage('user-join', uid, roomId))); + const now = Date.now(); + const timestamps = payload.uids.map(() => now); + await db.sortedSetAdd(`chat:room:${payload.roomId}:uids`, timestamps, payload.uids); + await updateGroupChatField([payload.roomId]); + await Promise.all(payload.uids.map(uid => Messaging.addSystemMessage('user-join', uid, payload.roomId))); }; Messaging.removeUsersFromRoom = async (uid, uids, roomId) => { @@ -92,15 +97,16 @@ module.exports = function (Messaging) { Messaging.isRoomOwner(uid, roomId), Messaging.getUserCountInRoom(roomId), ]); + const payload = await plugins.hooks.fire('filter:messaging.removeUsersFromRoom', { uid, uids, roomId, isOwner, userCount }); - if (!isOwner) { + if (!payload.isOwner) { throw new Error('[[error:cant-remove-users-from-chat-room]]'); } - if (userCount === 2) { + if (payload.userCount === 2) { throw new Error('[[error:cant-remove-last-user]]'); } - await Messaging.leaveRoom(uids, roomId); + await Messaging.leaveRoom(payload.uids, payload.roomId); }; Messaging.isGroupChat = async function (roomId) { @@ -170,10 +176,10 @@ module.exports = function (Messaging) { db.getObjectField(`chat:room:${roomId}`, 'owner'), ]); - return users.map((user) => { - user.isOwner = parseInt(user.uid, 10) === parseInt(ownerId, 10); + return Promise.all(users.map(async (user) => { + user.isOwner = await Messaging.isRoomOwner(user.uid, roomId, ownerId); return user; - }); + })); }; Messaging.renameRoom = async function (uid, roomId, newName) { @@ -220,7 +226,7 @@ module.exports = function (Messaging) { return null; } - const [roomData, canReply, users, messages, isAdminOrGlobalMod] = await Promise.all([ + const [room, canReply, users, messages, isAdminOrGlobalMod] = await Promise.all([ Messaging.getRoomData(data.roomId), Messaging.canReply(data.roomId, uid), Messaging.getUsersInRoom(data.roomId, 0, -1), @@ -233,9 +239,8 @@ module.exports = function (Messaging) { user.isAdminOrGlobalMod(uid), ]); - const room = roomData; room.messages = messages; - room.isOwner = parseInt(room.owner, 10) === parseInt(uid, 10); + room.isOwner = await Messaging.isRoomOwner(uid, room.roomId, room.owner); room.users = users.filter(user => user && parseInt(user.uid, 10) && parseInt(user.uid, 10) !== parseInt(uid, 10)); room.canReply = canReply; room.groupChat = room.hasOwnProperty('groupChat') ? room.groupChat : users.length > 2; @@ -245,6 +250,7 @@ module.exports = function (Messaging) { room.showUserInput = !room.maximumUsersInChatRoom || room.maximumUsersInChatRoom > 2; room.isAdminOrGlobalMod = isAdminOrGlobalMod; - return room; + const payload = await plugins.hooks.fire('filter:messaging.loadRoom', { uid, data, room }); + return payload.room; }; }; diff --git a/src/meta/configs.js b/src/meta/configs.js index be0e1eebcc..75a013f71b 100644 --- a/src/meta/configs.js +++ b/src/meta/configs.js @@ -4,7 +4,6 @@ const nconf = require('nconf'); const path = require('path'); const winston = require('winston'); -const util = require('util'); const db = require('../database'); const pubsub = require('../pubsub'); @@ -237,22 +236,15 @@ function ensureInteger(data, field, min) { } } -function lessRender(string, callback) { - const less = require('less'); - less.render(string, { - compress: true, - javascriptEnabled: true, - }, callback); -} - -const lessRenderAsync = util.promisify(lessRender); - async function saveRenderedCss(data) { if (!data.customCSS) { return; } - - const lessObject = await lessRenderAsync(data.customCSS); + const less = require('less'); + const lessObject = await less.render(data.customCSS, { + compress: true, + javascriptEnabled: false, + }); data.renderedCustomCSS = lessObject.css; } diff --git a/src/meta/css.js b/src/meta/css.js index 3e5bfc14f5..ea347d4af9 100644 --- a/src/meta/css.js +++ b/src/meta/css.js @@ -123,12 +123,12 @@ async function getBundleMetadata(target) { } const [lessImports, cssImports, acpLessImports] = await Promise.all([ - moo(plugins.lessFiles, '\n@import ".', '.less'), - moo(plugins.cssFiles, '\n@import (inline) ".', '.css'), - target === 'client' ? '' : moo(plugins.acpLessFiles, '\n@import ".', '.less'), + filterGetImports(plugins.lessFiles, '\n@import ".', '.less'), + filterGetImports(plugins.cssFiles, '\n@import (inline) ".', '.css'), + target === 'client' ? '' : filterGetImports(plugins.acpLessFiles, '\n@import ".', '.less'), ]); - async function moo(files, prefix, extension) { + async function filterGetImports(files, prefix, extension) { const filteredFiles = await filterMissingFiles(files); return await getImports(filteredFiles, prefix, extension); } diff --git a/src/meta/js.js b/src/meta/js.js index 05ecd482f3..6901932869 100644 --- a/src/meta/js.js +++ b/src/meta/js.js @@ -41,6 +41,7 @@ JS.scripts = { // files listed below are only available client-side, or are bundled in to reduce # of network requests on cold load rjs: [ + 'public/src/client/header.js', 'public/src/client/header/chat.js', 'public/src/client/header/notifications.js', 'public/src/client/infinitescroll.js', @@ -74,6 +75,8 @@ JS.scripts = { 'public/src/modules/helpers.js', 'public/src/modules/storage.js', 'public/src/modules/handleBack.js', + 'public/src/modules/messages.js', + 'public/src/modules/search.js', ], admin: [ @@ -89,7 +92,16 @@ JS.scripts = { 'cropper.js': 'node_modules/cropperjs/dist/cropper.min.js', 'jquery-ui': 'node_modules/jquery-ui/ui', 'zxcvbn.js': 'node_modules/zxcvbn/dist/zxcvbn.js', - ace: 'node_modules/ace-builds/src-min', + + // only get ace files required by acp + 'ace/ace.js': 'node_modules/ace-builds/src-min/ace.js', + 'ace/mode-less.js': 'node_modules/ace-builds/src-min/mode-less.js', + 'ace/mode-javascript.js': 'node_modules/ace-builds/src-min/mode-javascript.js', + 'ace/mode-html.js': 'node_modules/ace-builds/src-min/mode-html.js', + 'ace/theme-twilight.js': 'node_modules/ace-builds/src-min/theme-twilight.js', + 'ace/worker-javascript.js': 'node_modules/ace-builds/src-min/worker-javascript.js', + 'ace/worker-html.js': 'node_modules/ace-builds/src-min/worker-html.js', + 'clipboard.js': 'node_modules/clipboard/dist/clipboard.min.js', 'tinycon.js': 'node_modules/tinycon/tinycon.js', 'slideout.js': 'node_modules/slideout/dist/slideout.min.js', @@ -327,8 +339,10 @@ JS.buildBundle = async function (target, fork) { await requirejsOptimize(target); const files = await getBundleScriptList(target); + const srcPath = path.join(__dirname, `../../build/public/rjs-bundle-${target}.js`); files.push({ - srcPath: path.join(__dirname, `../../build/public/rjs-bundle-${target}.js`), + srcPath: srcPath, + filename: path.relative(basePath, srcPath).replace(/\\/g, '/'), }); const minify = process.env.NODE_ENV !== 'development'; diff --git a/src/meta/languages.js b/src/meta/languages.js index 36611f1b9a..4d845cc2e1 100644 --- a/src/meta/languages.js +++ b/src/meta/languages.js @@ -99,7 +99,12 @@ async function buildNamespaceLanguage(lang, namespace, plugins) { } async function addPlugin(translations, pluginData, lang, namespace) { - const pluginLanguages = path.join(paths.nodeModules, pluginData.id, pluginData.languages); + // if plugin doesn't have this namespace no need to continue + if (pluginData.languageData && !pluginData.languageData.namespaces.includes(namespace)) { + return; + } + + const pathToPluginLanguageFolder = path.join(paths.nodeModules, pluginData.id, pluginData.languages); const defaultLang = pluginData.defaultLang || 'en-GB'; // for each plugin, fallback in this order: @@ -116,7 +121,7 @@ async function addPlugin(translations, pluginData, lang, namespace) { for (const language of langs) { /* eslint-disable no-await-in-loop */ - await assignFileToTranslations(translations, path.join(pluginLanguages, language, `${namespace}.json`)); + await assignFileToTranslations(translations, path.join(pathToPluginLanguageFolder, language, `${namespace}.json`)); } } diff --git a/src/meta/minifier.js b/src/meta/minifier.js index 4c1af4bab7..cfb740755f 100644 --- a/src/meta/minifier.js +++ b/src/meta/minifier.js @@ -73,36 +73,37 @@ function removeChild(proc) { } } -function forkAction(action, callback) { - const proc = getChild(); - - proc.on('message', (message) => { - freeChild(proc); - - if (message.type === 'error') { - return callback(message.message); - } +function forkAction(action) { + return new Promise((resolve, reject) => { + const proc = getChild(); + proc.on('message', (message) => { + freeChild(proc); + + if (message.type === 'error') { + return reject(new Error(message.message)); + } - if (message.type === 'end') { - callback(null, message.result); - } - }); - proc.on('error', (err) => { - proc.kill(); - removeChild(proc); - callback(err); - }); + if (message.type === 'end') { + resolve(message.result); + } + }); + proc.on('error', (err) => { + proc.kill(); + removeChild(proc); + reject(err); + }); - proc.send({ - type: 'action', - action: action, + proc.send({ + type: 'action', + action: action, + }); }); } const actions = {}; if (process.env.minifier_child) { - process.on('message', (message) => { + process.on('message', async (message) => { if (message.type === 'action') { const { action } = message; if (typeof actions[action.act] !== 'function') { @@ -112,121 +113,81 @@ if (process.env.minifier_child) { }); return; } - - actions[action.act](action, (err, result) => { - if (err) { - process.send({ - type: 'error', - message: err.stack || err.message || 'unknown error', - }); - return; - } - + try { + const result = await actions[action.act](action); process.send({ type: 'end', result: result, }); - }); + } catch (err) { + process.send({ + type: 'error', + message: err.stack || err.message || 'unknown error', + }); + } } }); } -function executeAction(action, fork, callback) { +async function executeAction(action, fork) { if (fork && (pool.length - free.length) < Minifier.maxThreads) { - forkAction(action, callback); - } else { - if (typeof actions[action.act] !== 'function') { - return callback(Error('Unknown action')); - } - actions[action.act](action, callback); + return await forkAction(action); } + if (typeof actions[action.act] !== 'function') { + throw new Error('Unknown action'); + } + return await actions[action.act](action); } -function concat(data, callback) { +actions.concat = async function concat(data) { if (data.files && data.files.length) { - async.mapLimit(data.files, 1000, (ref, next) => { - fs.readFile(ref.srcPath, 'utf8', (err, file) => { - if (err) { - return next(err); - } - - next(null, file); - }); - }, (err, files) => { - if (err) { - return callback(err); - } - - const output = files.join('\n;'); - fs.writeFile(data.destPath, output, callback); - }); - - return; + const files = await async.mapLimit(data.files, 1000, async ref => await fs.promises.readFile(ref.srcPath, 'utf8')); + const output = files.join('\n;'); + await fs.promises.writeFile(data.destPath, output); } +}; - callback(); -} -actions.concat = concat; - -function minifyJS_batch(data, callback) { - async.eachLimit(data.files, 100, (fileObj, next) => { - fs.readFile(fileObj.srcPath, 'utf8', (err, source) => { - if (err) { - return next(err); - } - - const filesToMinify = [ - { - srcPath: fileObj.srcPath, - filename: fileObj.filename, - source: source, - }, - ]; - minifyAndSave({ - files: filesToMinify, - destPath: fileObj.destPath, - filename: fileObj.filename, - }, next); - }); - }, callback); -} -actions.minifyJS_batch = minifyJS_batch; - -function minifyJS(data, callback) { - async.mapLimit(data.files, 1000, (fileObj, next) => { - fs.readFile(fileObj.srcPath, 'utf8', (err, source) => { - if (err) { - return next(err); - } - - next(null, { +actions.minifyJS_batch = async function minifyJS_batch(data) { + await async.eachLimit(data.files, 100, async (fileObj) => { + const source = await fs.promises.readFile(fileObj.srcPath, 'utf8'); + const filesToMinify = [ + { srcPath: fileObj.srcPath, filename: fileObj.filename, source: source, - }); - }); - }, (err, filesToMinify) => { - if (err) { - return callback(err); - } + }, + ]; - minifyAndSave({ + await minifyAndSave({ files: filesToMinify, - destPath: data.destPath, - filename: data.filename, - }, callback); + destPath: fileObj.destPath, + filename: fileObj.filename, + }); }); -} -actions.minifyJS = minifyJS; +}; -function minifyAndSave(data, callback) { +actions.minifyJS = async function minifyJS(data) { + const filesToMinify = await async.mapLimit(data.files, 1000, async (fileObj) => { + const source = await fs.promises.readFile(fileObj.srcPath, 'utf8'); + return { + srcPath: fileObj.srcPath, + filename: fileObj.filename, + source: source, + }; + }); + await minifyAndSave({ + files: filesToMinify, + destPath: data.destPath, + filename: data.filename, + }); +}; + +async function minifyAndSave(data) { const scripts = {}; data.files.forEach((ref) => { - if (!ref) { - return; + if (ref && ref.filename && ref.source) { + scripts[ref.filename] = ref.source; } - - scripts[ref.filename] = ref.source; }); const minified = uglify.minify(scripts, { @@ -239,66 +200,57 @@ function minifyAndSave(data, callback) { }); if (minified.error) { - return callback({ stack: `Error minifying ${minified.error.filename}\n${minified.error.stack}` }); + throw new Error(`Error minifying ${minified.error.filename}\n${minified.error.stack}`); } - - async.parallel([ - async.apply(fs.writeFile, data.destPath, minified.code), - async.apply(fs.writeFile, `${data.destPath}.map`, minified.map), - ], callback); + await Promise.all([ + fs.promises.writeFile(data.destPath, minified.code), + fs.promises.writeFile(`${data.destPath}.map`, minified.map), + ]); } Minifier.js = {}; -Minifier.js.bundle = function (data, minify, fork, callback) { - executeAction({ +Minifier.js.bundle = async function (data, minify, fork) { + return await executeAction({ act: minify ? 'minifyJS' : 'concat', files: data.files, filename: data.filename, destPath: data.destPath, - }, fork, callback); + }, fork); }; -Minifier.js.minifyBatch = function (scripts, fork, callback) { - executeAction({ +Minifier.js.minifyBatch = async function (scripts, fork) { + return await executeAction({ act: 'minifyJS_batch', files: scripts, - }, fork, callback); + }, fork); }; -function buildCSS(data, callback) { - less.render(data.source, { +actions.buildCSS = async function buildCSS(data) { + const lessOutput = await less.render(data.source, { paths: data.paths, - javascriptEnabled: true, - }, (err, lessOutput) => { - if (err) { - // display less parser errors properly - return callback(new Error(String(err))); - } + javascriptEnabled: false, + }); - postcss(data.minify ? [ - autoprefixer, - clean({ - processImportFrom: ['local'], - }), - ] : [autoprefixer]).process(lessOutput.css, { - from: undefined, - }).then((result) => { - process.nextTick(callback, null, { code: result.css }); - }).catch((err) => { - process.nextTick(callback, err); - }); + const postcssArgs = [autoprefixer]; + if (data.minify) { + postcssArgs.push(clean({ + processImportFrom: ['local'], + })); + } + const result = await postcss(postcssArgs).process(lessOutput.css, { + from: undefined, }); -} -actions.buildCSS = buildCSS; + return { code: result.css }; +}; Minifier.css = {}; -Minifier.css.bundle = function (source, paths, minify, fork, callback) { - executeAction({ +Minifier.css.bundle = async function (source, paths, minify, fork) { + return await executeAction({ act: 'buildCSS', source: source, paths: paths, minify: minify, - }, fork, callback); + }, fork); }; require('../promisify')(exports); diff --git a/src/meta/settings.js b/src/meta/settings.js index 63e7fe47f2..5df3a2f8b9 100644 --- a/src/meta/settings.js +++ b/src/meta/settings.js @@ -93,7 +93,9 @@ Settings.set = async function (hash, values, quiet) { }); pubsub.publish(`action:settings.set.${hash}`, values); - Meta.reloadRequired = !quiet; + if (!Meta.reloadRequired && !quiet) { + Meta.reloadRequired = true; + } }; Settings.setOne = async function (hash, field, value) { diff --git a/src/meta/tags.js b/src/meta/tags.js index daa4d4808c..4e5aeb6815 100644 --- a/src/meta/tags.js +++ b/src/meta/tags.js @@ -66,6 +66,7 @@ Tags.parse = async (req, data, meta, link) => { }, { rel: 'manifest', href: `${relative_path}/manifest.webmanifest`, + crossorigin: `use-credentials`, }]; if (plugins.hooks.hasListeners('filter:search.query')) { @@ -163,7 +164,7 @@ Tags.parse = async (req, data, meta, link) => { return tag; }); - addSiteOGImage(meta); + await addSiteOGImage(meta); addIfNotExists(meta, 'property', 'og:title', Meta.config.title || 'NodeBB'); const ogUrl = url + (req.originalUrl !== '/' ? stripRelativePath(req.originalUrl) : ''); @@ -210,49 +211,59 @@ function stripRelativePath(url) { return url; } -function addSiteOGImage(meta) { +async function addSiteOGImage(meta) { const key = Meta.config['og:image'] ? 'og:image' : 'brand:logo'; let ogImage = stripRelativePath(Meta.config[key] || ''); if (ogImage && !ogImage.startsWith('http')) { ogImage = url + ogImage; } - if (ogImage) { - meta.push({ - property: 'og:image', - content: ogImage, - noEscape: true, - }, { - property: 'og:image:url', - content: ogImage, - noEscape: true, - }); + const { images } = await plugins.hooks.fire('filter:meta.addSiteOGImage', { + images: [{ + url: ogImage || `${url}/assets/images/logo@3x.png`, + width: ogImage ? Meta.config[`${key}:width`] : 963, + height: ogImage ? Meta.config[`${key}:height`] : 225, + }], + }); - if (Meta.config[`${key}:width`] && Meta.config[`${key}:height`]) { - meta.push({ - property: 'og:image:width', - content: String(Meta.config[`${key}:width`]), - }, { - property: 'og:image:height', - content: String(Meta.config[`${key}:height`]), - }); + const properties = ['url', 'secure_url', 'type', 'width', 'height', 'alt']; + images.forEach((image) => { + for (const property of properties) { + if (image.hasOwnProperty(property)) { + switch (property) { + case 'url': { + meta.push({ + property: 'og:image', + content: image.url, + noEscape: true, + }, { + property: 'og:image:url', + content: image.url, + noEscape: true, + }); + break; + } + + case 'secure_url': { + meta.push({ + property: `og:${property}`, + content: image[property], + noEscape: true, + }); + break; + } + + case 'type': + case 'alt': + case 'width': + case 'height': { + meta.push({ + property: `og:image:${property}`, + content: String(image[property]), + }); + } + } + } } - } else { - // Push fallback logo - meta.push({ - property: 'og:image', - content: `${url}/assets/images/logo@3x.png`, - noEscape: true, - }, { - property: 'og:image:url', - content: `${url}/assets/images/logo@3x.png`, - noEscape: true, - }, { - property: 'og:image:width', - content: '963', - }, { - property: 'og:image:height', - content: '225', - }); - } + }); } diff --git a/src/middleware/admin.js b/src/middleware/admin.js index 8fe54e6a10..67408905ca 100644 --- a/src/middleware/admin.js +++ b/src/middleware/admin.js @@ -40,6 +40,7 @@ middleware.renderHeader = async (req, res, data) => { configs: meta.configs.list(), latestVersion: getLatestVersion(), privileges: privileges.admin.get(req.uid), + tags: meta.tags.parse(req, {}, [], []), }); const { userData } = results; @@ -61,6 +62,8 @@ middleware.renderHeader = async (req, res, data) => { configJSON: jsesc(JSON.stringify(res.locals.config), { isScriptContext: true }), relative_path: res.locals.config.relative_path, adminConfigJSON: encodeURIComponent(JSON.stringify(results.configs)), + metaTags: results.tags.meta, + linkTags: results.tags.link, user: userData, userJSON: jsesc(JSON.stringify(userData), { isScriptContext: true }), plugins: results.custom_header.plugins, diff --git a/src/middleware/helpers.js b/src/middleware/helpers.js index bbf766895f..5c85989fc0 100644 --- a/src/middleware/helpers.js +++ b/src/middleware/helpers.js @@ -32,6 +32,7 @@ helpers.buildBodyClass = function (req, res, templateData = {}) { try { p = slugify(decodeURIComponent(p)); } catch (err) { + winston.error(`Error decoding URI: ${p}`); winston.error(err.stack); p = ''; } diff --git a/src/middleware/user.js b/src/middleware/user.js index 4adff804d5..b9f195477e 100644 --- a/src/middleware/user.js +++ b/src/middleware/user.js @@ -82,7 +82,7 @@ module.exports = function (middleware) { return !res.headersSent; } - // TODO: Remove in v1.18.0 + // TODO: Remove in v1.19.0 middleware.authenticate = helpers.try(async (req, res, next) => { winston.warn(`[middleware] middleware.authenticate has been deprecated, page and API routes are now automatically authenticated via setup(Page|API)Route. Use middleware.authenticateRequest (if not using route helper) and middleware.ensureLoggedIn instead. (request path: ${req.path})`); if (!await authenticate(req, res)) { @@ -101,7 +101,7 @@ module.exports = function (middleware) { next(); }); - // TODO: Remove in v1.18.0 + // TODO: Remove in v1.19.0 middleware.authenticateOrGuest = (req, res, next) => { winston.warn(`[middleware] middleware.authenticateOrGuest has been renamed, use middleware.authenticateRequest instead. (request path: ${req.path})`); middleware.authenticateRequest(req, res, next); diff --git a/src/password.js b/src/password.js index 727528a3a8..d1b6fc2df5 100644 --- a/src/password.js +++ b/src/password.js @@ -14,6 +14,10 @@ function forkChild(message, callback) { child.on('message', (msg) => { callback(msg.err ? new Error(msg.err) : null, msg.result); }); + child.on('error', (err) => { + console.error(err.stack); + callback(err); + }); child.send(message); } diff --git a/src/plugins/data.js b/src/plugins/data.js index e66bd74e9a..8eb1c79452 100644 --- a/src/plugins/data.js +++ b/src/plugins/data.js @@ -3,6 +3,7 @@ const fs = require('fs'); const path = require('path'); const winston = require('winston'); +const _ = require('lodash'); const db = require('../database'); const file = require('../file'); @@ -245,9 +246,8 @@ Data.getLanguageData = async function getLanguageData(pluginData) { languages.push(language); namespaces.push(namespace); }); - return { - languages, - namespaces, + languages: _.uniq(languages), + namespaces: _.uniq(namespaces), }; }; diff --git a/src/plugins/hooks.js b/src/plugins/hooks.js index d46a1fcdb0..050da0a628 100644 --- a/src/plugins/hooks.js +++ b/src/plugins/hooks.js @@ -8,7 +8,7 @@ const utils = require('../utils'); const Hooks = module.exports; Hooks.deprecatedHooks = { - 'filter:email.send': 'static:email.send', // 👋 @ 1.18.0 + 'filter:email.send': 'static:email.send', // 👋 @ 1.19.0 'filter:router.page': 'response:router.page', // 👋 @ 2.0.0 }; @@ -96,9 +96,11 @@ Hooks.fire = async function (hook, params) { winston.warn(`[plugins] Unknown hookType: ${hookType}, hook : ${hook}`); return; } + let deleteCaller = false; if (params && typeof params === 'object' && !params.hasOwnProperty('caller')) { const als = require('../als'); params.caller = als.getStore(); + deleteCaller = true; } const result = await hookTypeToMethod[hookType](hook, hookList, params); @@ -107,6 +109,9 @@ Hooks.fire = async function (hook, params) { Hooks.fire('action:plugins.firehook', payload); } if (result !== undefined) { + if (deleteCaller && result && result.caller) { + delete result.caller; + } return result; } }; diff --git a/src/plugins/index.js b/src/plugins/index.js index a7e8560dd5..04675f1a45 100644 --- a/src/plugins/index.js +++ b/src/plugins/index.js @@ -1,7 +1,6 @@ 'use strict'; const fs = require('fs'); -const os = require('os'); const path = require('path'); const winston = require('winston'); const semver = require('semver'); @@ -25,23 +24,6 @@ require('./usage')(Plugins); Plugins.data = require('./data'); Plugins.hooks = require('./hooks'); -// Backwards compatibility for hooks, remove in v1.18.0 -const _deprecate = async function (...args) { - const oldMethod = args.shift(); - const newMethod = args.shift(); - const method = args.shift(); - const stack = new Error().stack.toString().split(os.EOL); - const context = stack[stack.findIndex(line => line.startsWith(' at Object.wrapperCallback')) + 1]; - winston.warn(`[plugins/hooks] ${oldMethod} has been deprecated, call ${newMethod} instead.`); - winston.warn(`[plugins/hooks] ${context}`); - return method.apply(Plugins.hooks, args); -}; -Plugins.registerHook = _deprecate.bind(null, 'Plugins.registerHook', 'Plugins.hooks.register', Plugins.hooks.register); -Plugins.unregisterHook = _deprecate.bind(null, 'Plugins.unregisterHook', 'Plugins.hooks.unregister', Plugins.hooks.unregister); -Plugins.fireHook = _deprecate.bind(null, 'Plugins.fireHook', 'Plugins.hooks.fire', Plugins.hooks.fire); -Plugins.hasListeners = _deprecate.bind(null, 'Plugins.hasListeners', 'Plugins.hooks.hasListeners', Plugins.hooks.hasListeners); -// end - Plugins.getPluginPaths = Plugins.data.getPluginPaths; Plugins.loadPluginInfo = Plugins.data.loadPluginInfo; diff --git a/src/plugins/load.js b/src/plugins/load.js index 1ee74b5b24..99ee26df3d 100644 --- a/src/plugins/load.js +++ b/src/plugins/load.js @@ -63,6 +63,7 @@ module.exports = function (Plugins) { if (results.languageData) { Plugins.languageData.languages = _.union(Plugins.languageData.languages, results.languageData.languages); Plugins.languageData.namespaces = _.union(Plugins.languageData.namespaces, results.languageData.namespaces); + pluginData.languageData = results.languageData; } Plugins.pluginsData[pluginData.id] = pluginData; } diff --git a/src/posts/edit.js b/src/posts/edit.js index 93fd3090b9..8de8f9da1a 100644 --- a/src/posts/edit.js +++ b/src/posts/edit.js @@ -53,8 +53,8 @@ module.exports = function (Posts) { ]); await Posts.setPostFields(data.pid, result.post); - - if (meta.config.enablePostHistory === 1) { + const contentChanged = data.content !== oldContent; + if (meta.config.enablePostHistory === 1 && contentChanged) { await Posts.diffs.save({ pid: data.pid, uid: data.uid, @@ -72,13 +72,14 @@ module.exports = function (Posts) { returnPostData.cid = topic.cid; returnPostData.topic = topic; returnPostData.editedISO = utils.toISOString(editPostData.edited); - returnPostData.changed = oldContent !== data.content; + returnPostData.changed = contentChanged; await topics.notifyFollowers(returnPostData, data.uid, { type: 'post-edit', bodyShort: translator.compile('notifications:user_edited_post', editor.username, topic.title), nid: `edit_post:${data.pid}:uid:${data.uid}`, }); + await topics.syncBacklinks(returnPostData); plugins.hooks.fire('action:post.edit', { post: _.clone(returnPostData), data: data, uid: data.uid }); diff --git a/src/posts/uploads.js b/src/posts/uploads.js index 9cc827d6a5..cfab10671b 100644 --- a/src/posts/uploads.js +++ b/src/posts/uploads.js @@ -41,7 +41,8 @@ module.exports = function (Posts) { if (isMainPost) { const tid = await Posts.getPostField(pid, 'tid'); let thumbs = await topics.thumbs.get(tid); - thumbs = thumbs.map(thumb => thumb.url.replace(path.join(nconf.get('relative_path'), nconf.get('upload_url'), 'files/'), '')).filter(path => !validator.isURL(path, { + const replacePath = path.posix.join(nconf.get('relative_path'), nconf.get('upload_url'), 'files/'); + thumbs = thumbs.map(thumb => thumb.url.replace(replacePath, '')).filter(path => !validator.isURL(path, { require_protocol: true, })); uploads.push(...thumbs); diff --git a/src/posts/votes.js b/src/posts/votes.js index 60bbe6361a..c4d976a1ee 100644 --- a/src/posts/votes.js +++ b/src/posts/votes.js @@ -63,7 +63,8 @@ module.exports = function (Posts) { putVoteInProgress(pid, uid); try { - return await unvote(pid, uid, 'unvote'); + const voteStatus = await Posts.hasVoted(pid, uid); + return await unvote(pid, uid, 'unvote', voteStatus); } finally { clearVoteProgress(pid, uid); } @@ -114,48 +115,26 @@ module.exports = function (Posts) { } async function toggleVote(type, pid, uid) { - await unvote(pid, uid, type); - return await vote(type, false, pid, uid); + const voteStatus = await Posts.hasVoted(pid, uid); + await unvote(pid, uid, type, voteStatus); + return await vote(type, false, pid, uid, voteStatus); } - async function unvote(pid, uid, command) { - const [owner, voteStatus] = await Promise.all([ - Posts.getPostField(pid, 'uid'), - Posts.hasVoted(pid, uid), - ]); - + async function unvote(pid, uid, type, voteStatus) { + const owner = await Posts.getPostField(pid, 'uid'); if (parseInt(uid, 10) === parseInt(owner, 10)) { throw new Error('[[error:self-vote]]'); } - if (command === 'downvote') { + if (type === 'downvote') { await checkDownvoteLimitation(pid, uid); } - let hook; - let current = voteStatus.upvoted ? 'upvote' : 'downvote'; - - if ((voteStatus.upvoted && command === 'downvote') || (voteStatus.downvoted && command === 'upvote')) { // e.g. User *has* upvoted, and clicks downvote - hook = command; - } else if (voteStatus.upvoted || voteStatus.downvoted) { // e.g. User *has* upvoted, clicks upvote (so we "unvote") - hook = 'unvote'; - } else { // e.g. User *has not* voted, clicks upvote - hook = command; - current = 'unvote'; - } - - plugins.hooks.fire(`action:post.${hook}`, { - pid: pid, - uid: uid, - owner: owner, - current: current, - }); - if (!voteStatus || (!voteStatus.upvoted && !voteStatus.downvoted)) { return; } - return await vote(voteStatus.upvoted ? 'downvote' : 'upvote', true, pid, uid); + return await vote(voteStatus.upvoted ? 'downvote' : 'upvote', true, pid, uid, voteStatus); } async function checkDownvoteLimitation(pid, uid) { @@ -185,7 +164,7 @@ module.exports = function (Posts) { } } - async function vote(type, unvote, pid, uid) { + async function vote(type, unvote, pid, uid, voteStatus) { uid = parseInt(uid, 10); if (uid <= 0) { throw new Error('[[error:not-logged-in]]'); @@ -209,6 +188,8 @@ module.exports = function (Posts) { await adjustPostVotes(postData, uid, type, unvote); + await fireVoteHook(postData, uid, type, unvote, voteStatus); + return { user: { reputation: newReputation, @@ -220,6 +201,25 @@ module.exports = function (Posts) { }; } + async function fireVoteHook(postData, uid, type, unvote, voteStatus) { + let hook = type; + let current = voteStatus.upvoted ? 'upvote' : 'downvote'; + if (unvote) { // e.g. unvoting, removing a upvote or downvote + hook = 'unvote'; + } else { // e.g. User *has not* voted, clicks upvote or downvote + current = 'unvote'; + } + // action:post.upvote + // action:post.downvote + // action:post.unvote + plugins.hooks.fire(`action:post.${hook}`, { + pid: postData.pid, + uid: uid, + owner: postData.uid, + current: current, + }); + } + async function adjustPostVotes(postData, uid, type, unvote) { const notType = (type === 'upvote' ? 'downvote' : 'upvote'); if (unvote) { diff --git a/src/prestart.js b/src/prestart.js index 8f89336b9a..f2406e5ace 100644 --- a/src/prestart.js +++ b/src/prestart.js @@ -94,6 +94,9 @@ function loadConfig(configFile) { nconf.set('secure', urlObject.protocol === 'https:'); nconf.set('use_port', !!urlObject.port); nconf.set('relative_path', relativePath); + if (!nconf.get('asset_base_url')) { + nconf.set('asset_base_url', `${relativePath}/assets`); + } nconf.set('port', nconf.get('PORT') || nconf.get('port') || urlObject.port || (nconf.get('PORT_ENV_VAR') ? nconf.get(nconf.get('PORT_ENV_VAR')) : false) || 4567); // cookies don't provide isolation by port: http://stackoverflow.com/a/16328399/122353 diff --git a/src/privileges/admin.js b/src/privileges/admin.js index ead7f6fc5e..65e8cb2627 100644 --- a/src/privileges/admin.js +++ b/src/privileges/admin.js @@ -35,6 +35,18 @@ privsAdmin.userPrivilegeList = [ privsAdmin.groupPrivilegeList = privsAdmin.userPrivilegeList.map(privilege => `groups:${privilege}`); +privsAdmin.privilegeList = privsAdmin.userPrivilegeList.concat(privsAdmin.groupPrivilegeList); + +privsAdmin.getUserPrivilegeList = async () => await plugins.hooks.fire('filter:privileges.admin.list', privsAdmin.userPrivilegeList.slice()); +privsAdmin.getGroupPrivilegeList = async () => await plugins.hooks.fire('filter:privileges.admin.groups.list', privsAdmin.groupPrivilegeList.slice()); +privsAdmin.getPrivilegeList = async () => { + const [user, group] = await Promise.all([ + privsAdmin.getUserPrivilegeList(), + privsAdmin.getGroupPrivilegeList(), + ]); + return user.concat(group); +}; + // Mapping for a page route (via direct match or regexp) to a privilege privsAdmin.routeMap = { dashboard: 'admin:dashboard', @@ -157,13 +169,14 @@ privsAdmin.list = async function (uid) { }; privsAdmin.get = async function (uid) { + const userPrivilegeList = await privsAdmin.getUserPrivilegeList(); const [userPrivileges, isAdministrator] = await Promise.all([ - helpers.isAllowedTo(privsAdmin.userPrivilegeList, uid, 0), + helpers.isAllowedTo(userPrivilegeList, uid, 0), user.isAdministrator(uid), ]); const combined = userPrivileges.map(allowed => allowed || isAdministrator); - const privData = _.zipObject(privsAdmin.userPrivilegeList, combined); + const privData = _.zipObject(userPrivilegeList, combined); privData.superadmin = isAdministrator; return await plugins.hooks.fire('filter:privileges.admin.get', privData); @@ -198,9 +211,11 @@ privsAdmin.rescind = async function (privileges, groupName) { }; privsAdmin.userPrivileges = async function (uid) { - return await helpers.userOrGroupPrivileges(0, uid, privsAdmin.userPrivilegeList); + const userPrivilegeList = await privsAdmin.getUserPrivilegeList(); + return await helpers.userOrGroupPrivileges(0, uid, userPrivilegeList); }; privsAdmin.groupPrivileges = async function (groupName) { - return await helpers.userOrGroupPrivileges(0, groupName, privsAdmin.groupPrivilegeList); + const groupPrivilegeList = await privsAdmin.getGroupPrivilegeList(); + return await helpers.userOrGroupPrivileges(0, groupName, groupPrivilegeList); }; diff --git a/src/privileges/categories.js b/src/privileges/categories.js index 90fcffd2c3..f38291cb7b 100644 --- a/src/privileges/categories.js +++ b/src/privileges/categories.js @@ -54,6 +54,16 @@ privsCategories.groupPrivilegeList = privsCategories.userPrivilegeList.map(privi privsCategories.privilegeList = privsCategories.userPrivilegeList.concat(privsCategories.groupPrivilegeList); +privsCategories.getUserPrivilegeList = async () => await plugins.hooks.fire('filter:privileges.list', privsCategories.userPrivilegeList.slice()); +privsCategories.getGroupPrivilegeList = async () => await plugins.hooks.fire('filter:privileges.groups.list', privsCategories.groupPrivilegeList.slice()); +privsCategories.getPrivilegeList = async () => { + const [user, group] = await Promise.all([ + privsCategories.getUserPrivilegeList(), + privsCategories.getGroupPrivilegeList(), + ]); + return user.concat(group); +}; + // Method used in admin/category controller to show all users/groups with privs in that given cid privsCategories.list = async function (cid) { const labels = await utils.promiseParallel({ @@ -210,9 +220,11 @@ privsCategories.canMoveAllTopics = async function (currentCid, targetCid, uid) { }; privsCategories.userPrivileges = async function (cid, uid) { - return await helpers.userOrGroupPrivileges(cid, uid, privsCategories.userPrivilegeList); + const userPrivilegeList = await privsCategories.getUserPrivilegeList(); + return await helpers.userOrGroupPrivileges(cid, uid, userPrivilegeList); }; privsCategories.groupPrivileges = async function (cid, groupName) { - return await helpers.userOrGroupPrivileges(cid, groupName, privsCategories.groupPrivilegeList); + const groupPrivilegeList = await privsCategories.getGroupPrivilegeList(); + return await helpers.userOrGroupPrivileges(cid, groupName, groupPrivilegeList); }; diff --git a/src/privileges/global.js b/src/privileges/global.js index 563ae3d053..c614d82f86 100644 --- a/src/privileges/global.js +++ b/src/privileges/global.js @@ -16,8 +16,8 @@ privsGlobal.privilegeLabels = [ { name: '[[admin/manage/privileges:upload-images]]' }, { name: '[[admin/manage/privileges:upload-files]]' }, { name: '[[admin/manage/privileges:signature]]' }, - { name: '[[admin/manage/privileges:ban]]' }, { name: '[[admin/manage/privileges:invite]]' }, + { name: '[[admin/manage/privileges:allow-group-creation]]' }, { name: '[[admin/manage/privileges:search-content]]' }, { name: '[[admin/manage/privileges:search-users]]' }, { name: '[[admin/manage/privileges:search-tags]]' }, @@ -25,7 +25,7 @@ privsGlobal.privilegeLabels = [ { name: '[[admin/manage/privileges:view-tags]]' }, { name: '[[admin/manage/privileges:view-groups]]' }, { name: '[[admin/manage/privileges:allow-local-login]]' }, - { name: '[[admin/manage/privileges:allow-group-creation]]' }, + { name: '[[admin/manage/privileges:ban]]' }, { name: '[[admin/manage/privileges:view-users-info]]' }, ]; @@ -34,8 +34,8 @@ privsGlobal.userPrivilegeList = [ 'upload:post:image', 'upload:post:file', 'signature', - 'ban', 'invite', + 'group:create', 'search:content', 'search:users', 'search:tags', @@ -43,12 +43,24 @@ privsGlobal.userPrivilegeList = [ 'view:tags', 'view:groups', 'local:login', - 'group:create', + 'ban', 'view:users:info', ]; privsGlobal.groupPrivilegeList = privsGlobal.userPrivilegeList.map(privilege => `groups:${privilege}`); +privsGlobal.privilegeList = privsGlobal.userPrivilegeList.concat(privsGlobal.groupPrivilegeList); + +privsGlobal.getUserPrivilegeList = async () => await plugins.hooks.fire('filter:privileges.global.list', privsGlobal.userPrivilegeList.slice()); +privsGlobal.getGroupPrivilegeList = async () => await plugins.hooks.fire('filter:privileges.global.groups.list', privsGlobal.groupPrivilegeList.slice()); +privsGlobal.getPrivilegeList = async () => { + const [user, group] = await Promise.all([ + privsGlobal.getUserPrivilegeList(), + privsGlobal.getGroupPrivilegeList(), + ]); + return user.concat(group); +}; + privsGlobal.list = async function () { async function getLabels() { return await utils.promiseParallel({ @@ -75,13 +87,14 @@ privsGlobal.list = async function () { }; privsGlobal.get = async function (uid) { + const userPrivilegeList = await privsGlobal.getUserPrivilegeList(); const [userPrivileges, isAdministrator] = await Promise.all([ - helpers.isAllowedTo(privsGlobal.userPrivilegeList, uid, 0), + helpers.isAllowedTo(userPrivilegeList, uid, 0), user.isAdministrator(uid), ]); const combined = userPrivileges.map(allowed => allowed || isAdministrator); - const privData = _.zipObject(privsGlobal.userPrivilegeList, combined); + const privData = _.zipObject(userPrivilegeList, combined); return await plugins.hooks.fire('filter:privileges.global.get', privData); }; @@ -120,9 +133,11 @@ privsGlobal.rescind = async function (privileges, groupName) { }; privsGlobal.userPrivileges = async function (uid) { - return await helpers.userOrGroupPrivileges(0, uid, privsGlobal.userPrivilegeList); + const userPrivilegeList = await privsGlobal.getUserPrivilegeList(); + return await helpers.userOrGroupPrivileges(0, uid, userPrivilegeList); }; privsGlobal.groupPrivileges = async function (groupName) { - return await helpers.userOrGroupPrivileges(0, groupName, privsGlobal.groupPrivilegeList); + const groupPrivilegeList = await privsGlobal.getGroupPrivilegeList(); + return await helpers.userOrGroupPrivileges(0, groupName, groupPrivilegeList); }; diff --git a/src/privileges/index.js b/src/privileges/index.js index 4a9c4d2096..6445b07b97 100644 --- a/src/privileges/index.js +++ b/src/privileges/index.js @@ -9,20 +9,3 @@ privileges.posts = require('./posts'); privileges.users = require('./users'); require('../promisify')(privileges); - -// TODO: backwards compatibility remove in 1.18.0 -[ - 'privilegeLabels', - 'userPrivilegeList', - 'groupPrivilegeList', - 'privilegeList', -].forEach((fieldName) => { - Object.defineProperty(privileges, fieldName, { - configurable: true, - enumerable: true, - get: function () { - console.warn(`[deprecated] privileges.${fieldName} is deprecated. Use privileges.categories.${fieldName}`); - return privileges.categories[fieldName]; - }, - }); -}); diff --git a/src/privileges/topics.js b/src/privileges/topics.js index 20ad19aa19..cabbf3dfed 100644 --- a/src/privileges/topics.js +++ b/src/privileges/topics.js @@ -38,6 +38,7 @@ privsTopics.get = async function (tid, uid) { return await plugins.hooks.fire('filter:privileges.topics.get', { 'topics:reply': (privData['topics:reply'] && ((!topicData.locked && mayReply) || isModerator)) || isAdministrator, 'topics:read': privData['topics:read'] || isAdministrator, + 'topics:schedule': privData['topics:schedule'] || isAdministrator, 'topics:tag': privData['topics:tag'] || isAdministrator, 'topics:delete': (privData['topics:delete'] && (isOwner || isModerator)) || isAdministrator, 'posts:edit': (privData['posts:edit'] && (!topicData.locked || isModerator)) || isAdministrator, diff --git a/src/rewards/admin.js b/src/rewards/admin.js index 15a0c9fcc8..edd3202e26 100644 --- a/src/rewards/admin.js +++ b/src/rewards/admin.js @@ -24,6 +24,7 @@ rewards.save = async function (data) { await Promise.all(data.map(data => save(data))); await saveConditions(data); + return data; }; rewards.delete = async function (data) { diff --git a/src/routes/admin.js b/src/routes/admin.js index 97ecb66d7e..43c427dab9 100644 --- a/src/routes/admin.js +++ b/src/routes/admin.js @@ -11,6 +11,7 @@ module.exports = function (app, name, middleware, controllers) { helpers.setupAdminPageRoute(app, `/${name}/dashboard/logins`, middleware, middlewares, controllers.admin.dashboard.getLogins); helpers.setupAdminPageRoute(app, `/${name}/dashboard/users`, middleware, middlewares, controllers.admin.dashboard.getUsers); helpers.setupAdminPageRoute(app, `/${name}/dashboard/topics`, middleware, middlewares, controllers.admin.dashboard.getTopics); + helpers.setupAdminPageRoute(app, `/${name}/dashboard/searches`, middleware, middlewares, controllers.admin.dashboard.getSearches); helpers.setupAdminPageRoute(app, `/${name}/manage/categories`, middleware, middlewares, controllers.admin.categories.getAll); helpers.setupAdminPageRoute(app, `/${name}/manage/categories/:category_id`, middleware, middlewares, controllers.admin.categories.get); diff --git a/src/routes/api.js b/src/routes/api.js index 2ac922308a..caab9c66a3 100644 --- a/src/routes/api.js +++ b/src/routes/api.js @@ -1,33 +1,42 @@ 'use strict'; const express = require('express'); +const winston = require('winston'); const uploadsController = require('../controllers/uploads'); +const helpers = require('./helpers'); module.exports = function (app, middleware, controllers) { + const middlewares = [middleware.authenticateRequest]; const router = express.Router(); app.use('/api', router); - router.get('/config', middleware.applyCSRF, middleware.authenticateRequest, controllers.api.getConfig); + router.get('/config', [...middlewares, middleware.applyCSRF], helpers.tryRoute(controllers.api.getConfig)); - router.get('/self', controllers.user.getCurrentUser); - router.get('/user/uid/:uid', middleware.canViewUsers, controllers.user.getUserByUID); - router.get('/user/username/:username', middleware.canViewUsers, controllers.user.getUserByUsername); - router.get('/user/email/:email', middleware.canViewUsers, controllers.user.getUserByEmail); + router.get('/self', [...middlewares], helpers.tryRoute(controllers.user.getCurrentUser)); + router.get('/user/uid/:uid', [...middlewares, middleware.canViewUsers], helpers.tryRoute(controllers.user.getUserByUID)); + router.get('/user/username/:username', [...middlewares, middleware.canViewUsers], helpers.tryRoute(controllers.user.getUserByUsername)); + router.get('/user/email/:email', [...middlewares, middleware.canViewUsers], helpers.tryRoute(controllers.user.getUserByEmail)); - router.get('/user/uid/:userslug/export/posts', middleware.authenticateRequest, middleware.ensureLoggedIn, middleware.checkAccountPermissions, middleware.exposeUid, controllers.user.exportPosts); - router.get('/user/uid/:userslug/export/uploads', middleware.authenticateRequest, middleware.ensureLoggedIn, middleware.checkAccountPermissions, middleware.exposeUid, controllers.user.exportUploads); - router.get('/user/uid/:userslug/export/profile', middleware.authenticateRequest, middleware.ensureLoggedIn, middleware.checkAccountPermissions, middleware.exposeUid, controllers.user.exportProfile); + router.get('/user/:userslug/export/posts', [...middlewares, middleware.authenticateRequest, middleware.ensureLoggedIn, middleware.checkAccountPermissions, middleware.exposeUid], helpers.tryRoute(controllers.user.exportPosts)); + router.get('/user/:userslug/export/uploads', [...middlewares, middleware.authenticateRequest, middleware.ensureLoggedIn, middleware.checkAccountPermissions, middleware.exposeUid], helpers.tryRoute(controllers.user.exportUploads)); + router.get('/user/:userslug/export/profile', [...middlewares, middleware.authenticateRequest, middleware.ensureLoggedIn, middleware.checkAccountPermissions, middleware.exposeUid], helpers.tryRoute(controllers.user.exportProfile)); - router.get('/categories/:cid/moderators', controllers.api.getModerators); - router.get('/recent/posts/:term?', controllers.posts.getRecentPosts); - router.get('/unread/total', middleware.authenticateRequest, middleware.ensureLoggedIn, controllers.unread.unreadTotal); - router.get('/topic/teaser/:topic_id', controllers.topics.teaser); - router.get('/topic/pagination/:topic_id', controllers.topics.pagination); + // Deprecated, remove in v1.20.0 + router.get('/user/uid/:userslug/export/:type', (req, res) => { + winston.warn(`[router] \`/api/user/uid/${req.params.userslug}/export/${req.params.type}\` is deprecated, call it \`/api/user/${req.params.userslug}/export/${req.params.type}\`instead.`); + res.redirect(`/api/user/${req.params.userslug}/export/${req.params.type}`); + }); + + router.get('/categories/:cid/moderators', [...middlewares], helpers.tryRoute(controllers.api.getModerators)); + router.get('/recent/posts/:term?', [...middlewares], helpers.tryRoute(controllers.posts.getRecentPosts)); + router.get('/unread/total', [...middlewares, middleware.ensureLoggedIn], helpers.tryRoute(controllers.unread.unreadTotal)); + router.get('/topic/teaser/:topic_id', [...middlewares], helpers.tryRoute(controllers.topics.teaser)); + router.get('/topic/pagination/:topic_id', [...middlewares], helpers.tryRoute(controllers.topics.pagination)); const multipart = require('connect-multiparty'); const multipartMiddleware = multipart(); - const middlewares = [ + const postMiddlewares = [ middleware.maintenanceMode, multipartMiddleware, middleware.validateFiles, @@ -35,6 +44,13 @@ module.exports = function (app, middleware, controllers) { middleware.applyCSRF, ]; - router.post('/post/upload', middlewares, uploadsController.uploadPost); - router.post('/user/:userslug/uploadpicture', middlewares.concat([middleware.exposeUid, middleware.authenticateRequest, middleware.ensureLoggedIn, middleware.canViewUsers, middleware.checkAccountPermissions]), controllers.accounts.edit.uploadPicture); + router.post('/post/upload', postMiddlewares, helpers.tryRoute(uploadsController.uploadPost)); + router.post('/user/:userslug/uploadpicture', [ + ...middlewares, + ...postMiddlewares, + middleware.exposeUid, + middleware.ensureLoggedIn, + middleware.canViewUsers, + middleware.checkAccountPermissions, + ], helpers.tryRoute(controllers.accounts.edit.uploadPicture)); }; diff --git a/src/routes/authentication.js b/src/routes/authentication.js index cbc3841872..6d7a36e037 100644 --- a/src/routes/authentication.js +++ b/src/routes/authentication.js @@ -16,15 +16,8 @@ let loginStrategies = []; const Auth = module.exports; Auth.initialize = function (app, middleware) { - const passportInitMiddleware = passport.initialize(); - app.use((req, res, next) => { - passportInitMiddleware(req, res, next); - }); - const passportSessionMiddleware = passport.session(); - app.use((req, res, next) => { - passportSessionMiddleware(req, res, next); - }); - + app.use(passport.initialize()); + app.use(passport.session()); app.use((req, res, next) => { Auth.setAuthVars(req, res); next(); @@ -51,13 +44,9 @@ Auth.getLoginStrategies = function () { }; Auth.verifyToken = async function (token, done) { - let { tokens = [] } = await meta.settings.get('core.api'); - tokens = tokens.reduce((memo, cur) => { - memo[cur.token] = cur.uid; - return memo; - }, {}); - - const uid = tokens[token]; + const { tokens = [] } = await meta.settings.get('core.api'); + const tokenObj = tokens.find(t => t.token === token); + const uid = tokenObj ? tokenObj.uid : undefined; if (uid !== undefined) { if (parseInt(uid, 10) > 0) { diff --git a/src/routes/index.js b/src/routes/index.js index 550eb94b2c..076c7c36b2 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -60,7 +60,12 @@ _mounts.topic = (app, name, middleware, controllers) => { }; _mounts.post = (app, name, middleware, controllers) => { - const middlewares = [middleware.maintenanceMode, middleware.registrationComplete, middleware.pluginHooks]; + const middlewares = [ + middleware.maintenanceMode, + middleware.authenticateRequest, + middleware.registrationComplete, + middleware.pluginHooks, + ]; app.get(`/${name}/:pid`, middleware.busyCheck, middlewares, controllers.posts.redirectToPost); app.get(`/api/${name}/:pid`, middlewares, controllers.posts.redirectToPost); }; diff --git a/src/routes/write/users.js b/src/routes/write/users.js index 02a64e134f..418fa9897d 100644 --- a/src/routes/write/users.js +++ b/src/routes/write/users.js @@ -22,6 +22,7 @@ function authenticatedRoutes() { setupApiRoute(router, 'get', '/:uid', [...middlewares, middleware.assert.user], controllers.write.users.get); setupApiRoute(router, 'put', '/:uid', [...middlewares, middleware.assert.user], controllers.write.users.update); setupApiRoute(router, 'delete', '/:uid', [...middlewares, middleware.assert.user], controllers.write.users.delete); + setupApiRoute(router, 'put', '/:uid/picture', [...middlewares, middleware.assert.user], controllers.write.users.changePicture); setupApiRoute(router, 'delete', '/:uid/content', [...middlewares, middleware.assert.user], controllers.write.users.deleteContent); setupApiRoute(router, 'delete', '/:uid/account', [...middlewares, middleware.assert.user], controllers.write.users.deleteAccount); diff --git a/src/search.js b/src/search.js index 99fae633e8..d71430c437 100644 --- a/src/search.js +++ b/src/search.js @@ -45,21 +45,32 @@ async function searchInContent(data) { async function doSearch(type, searchIn) { if (searchIn.includes(data.searchIn)) { - return await plugins.hooks.fire('filter:search.query', { + const result = await plugins.hooks.fire('filter:search.query', { index: type, content: data.query, matchWords: data.matchWords || 'all', cid: searchCids, uid: searchUids, searchData: data, + ids: [], }); + return Array.isArray(result) ? result : result.ids; } return []; } - const [pids, tids] = await Promise.all([ - doSearch('post', ['posts', 'titlesposts']), - doSearch('topic', ['titles', 'titlesposts']), - ]); + let pids = []; + let tids = []; + const inTopic = String(data.query || '').match(/^in:topic-([\d]+) /); + if (inTopic) { + const tid = inTopic[1]; + const cleanedTerm = data.query.replace(inTopic[0], ''); + pids = await topics.search(tid, cleanedTerm); + } else { + [pids, tids] = await Promise.all([ + doSearch('post', ['posts', 'titlesposts']), + doSearch('topic', ['titles', 'titlesposts']), + ]); + } if (data.returnIds) { return { pids: pids, tids: tids }; diff --git a/src/socket.io/admin/cache.js b/src/socket.io/admin/cache.js index 534cefeac8..9188ac8f5b 100644 --- a/src/socket.io/admin/cache.js +++ b/src/socket.io/admin/cache.js @@ -2,11 +2,13 @@ const SocketCache = module.exports; +const db = require('../../database'); + SocketCache.clear = async function (socket, data) { if (data.name === 'post') { require('../../posts/cache').reset(); - } else if (data.name === 'object') { - require('../../database').objectCache.reset(); + } else if (data.name === 'object' && db.objectCache) { + db.objectCache.reset(); } else if (data.name === 'group') { require('../../groups').cache.reset(); } else if (data.name === 'local') { @@ -17,7 +19,7 @@ SocketCache.clear = async function (socket, data) { SocketCache.toggle = async function (socket, data) { const caches = { post: require('../../posts/cache'), - object: require('../../database').objectCache, + object: db.objectCache, group: require('../../groups').cache, local: require('../../cache'), }; diff --git a/src/socket.io/admin/email.js b/src/socket.io/admin/email.js index c85b44778c..2068cb1f7f 100644 --- a/src/socket.io/admin/email.js +++ b/src/socket.io/admin/email.js @@ -14,12 +14,11 @@ Email.test = async function (socket, data) { ...(data.payload || {}), subject: '[[email:test-email.subject]]', }; - let template; switch (data.template) { case 'digest': await userDigest.execute({ - interval: 'alltime', + interval: 'month', subscribers: [socket.uid], }); break; @@ -34,15 +33,11 @@ Email.test = async function (socket, data) { break; case 'verify-email': - template = 'verify-email'; - // falls through - case 'welcome': await userEmail.sendValidationEmail(socket.uid, { force: 1, - email: 'test@example.org', - template: template || 'welcome', - subject: !template ? `[[email:welcome-to, ${meta.config.title || meta.config.browserTitle || 'NodeBB'}]]` : undefined, + template: data.template, + subject: data.template === 'welcome' ? `[[email:welcome-to, ${meta.config.title || meta.config.browserTitle || 'NodeBB'}]]` : undefined, }); break; diff --git a/src/socket.io/admin/rewards.js b/src/socket.io/admin/rewards.js index e68527547c..278d5e6e0f 100644 --- a/src/socket.io/admin/rewards.js +++ b/src/socket.io/admin/rewards.js @@ -5,7 +5,7 @@ const rewardsAdmin = require('../../rewards/admin'); const SocketRewards = module.exports; SocketRewards.save = async function (socket, data) { - await rewardsAdmin.save(data); + return await rewardsAdmin.save(data); }; SocketRewards.delete = async function (socket, data) { diff --git a/src/socket.io/admin/user.js b/src/socket.io/admin/user.js index e813053137..abb12cceed 100644 --- a/src/socket.io/admin/user.js +++ b/src/socket.io/admin/user.js @@ -172,7 +172,9 @@ User.exportUsersCSV = async function (socket) { setTimeout(async () => { try { await user.exportUsersCSV(); - socket.emit('event:export-users-csv'); + if (socket.emit) { + socket.emit('event:export-users-csv'); + } const notifications = require('../../notifications'); const n = await notifications.create({ bodyShort: '[[notifications:users-csv-exported]]', @@ -182,7 +184,7 @@ User.exportUsersCSV = async function (socket) { }); await notifications.push(n, [socket.uid]); } catch (err) { - winston.error(err); + winston.error(err.stack); } }, 0); }; diff --git a/src/socket.io/categories/search.js b/src/socket.io/categories/search.js index 4d973a3f45..ad04c20edf 100644 --- a/src/socket.io/categories/search.js +++ b/src/socket.io/categories/search.js @@ -53,10 +53,10 @@ module.exports = function (SocketCategories) { const result = await categories.search({ uid: uid, query: data.search, + qs: data.query, paginate: false, }); - let matchedCids = result.categories.map(c => c.cid); // no need to filter if all 3 states are used const filterByWatchState = !Object.values(categories.watchStates) diff --git a/src/socket.io/index.js b/src/socket.io/index.js index 773f885791..4dd74db26f 100644 --- a/src/socket.io/index.js +++ b/src/socket.io/index.js @@ -26,10 +26,6 @@ Sockets.init = async function (server) { }); if (nconf.get('isCluster')) { - // socket.io-adapter-cluster needs update - // if (nconf.get('singleHostCluster')) { - // io.adapter(require('./single-host-cluster')); - // } else if (nconf.get('redis')) { if (nconf.get('redis')) { const adapter = await require('../database/redis').socketAdapter(); io.adapter(adapter); @@ -257,7 +253,7 @@ Sockets.getCountInRoom = function (room) { }; Sockets.warnDeprecated = (socket, replacement) => { - if (socket.previousEvents) { + if (socket.previousEvents && socket.emit) { socket.emit('event:deprecated_call', { eventName: socket.previousEvents[socket.previousEvents.length - 1], replacement: replacement, diff --git a/src/socket.io/plugins.js b/src/socket.io/plugins.js index 7bb2f456bf..ac197c0024 100644 --- a/src/socket.io/plugins.js +++ b/src/socket.io/plugins.js @@ -7,7 +7,7 @@ const SocketPlugins = {}; How? From your plugin: - var SocketPlugins = require.main.require('./src/socket.io/plugins'); + const SocketPlugins = require.main.require('./src/socket.io/plugins'); SocketPlugins.myPlugin = {}; SocketPlugins.myPlugin.myMethod = function(socket, data, callback) { ... }; diff --git a/src/socket.io/posts.js b/src/socket.io/posts.js index 7ff99d6992..9fc33293cf 100644 --- a/src/socket.io/posts.js +++ b/src/socket.io/posts.js @@ -98,6 +98,22 @@ SocketPosts.getPostSummaryByIndex = async function (socket, data) { return postsData[0]; }; +SocketPosts.getPostSummaryByPid = async function (socket, data) { + if (!data || !data.pid) { + throw new Error('[[error:invalid-data]]'); + } + const { pid } = data; + const tid = await posts.getPostField(pid, 'tid'); + const topicPrivileges = await privileges.topics.get(tid, socket.uid); + if (!topicPrivileges['topics:read']) { + throw new Error('[[error:no-privileges]]'); + } + + const postsData = await posts.getPostSummaryByPids([pid], socket.uid, { stripTags: false }); + posts.modifyPostByPrivilege(postsData[0], topicPrivileges); + return postsData[0]; +}; + SocketPosts.getPost = async function (socket, pid) { sockets.warnDeprecated(socket, 'GET /api/v3/posts/:pid'); return await api.posts.get(socket, { pid }); @@ -150,13 +166,17 @@ async function acceptOrReject(method, socket, data) { } async function sendQueueNotification(type, targetUid, path) { - const notifObj = await notifications.create({ + const notifData = { type: type, nid: `${type}-${targetUid}-${path}`, bodyShort: type === 'post-queue-accepted' ? '[[notifications:post-queue-accepted]]' : '[[notifications:post-queue-rejected]]', path: path, - }); + }; + if (parseInt(meta.config.postQueueNotificationUid, 10) > 0) { + notifData.from = meta.config.postQueueNotificationUid; + } + const notifObj = await notifications.create(notifData); await notifications.push(notifObj, [targetUid]); } diff --git a/src/socket.io/single-host-cluster.js b/src/socket.io/single-host-cluster.js deleted file mode 100644 index ca420997c5..0000000000 --- a/src/socket.io/single-host-cluster.js +++ /dev/null @@ -1,65 +0,0 @@ -'use strict'; - -const Client = { - sendMessage: function (channel, message) { - process.send({ - action: 'socket.io', - channel: channel, - message: message, - }); - }, - trigger: function (channel, message) { - Client.message.concat(Client.pmessage).forEach((callback) => { - setImmediate(() => { - callback.call(Client, channel, message); - }); - }); - }, - publish: function (channel, message) { - Client.sendMessage(channel, message); - }, - // we don't actually care about which channels we're subscribed to - subscribe: function () {}, - psubscribe: function () {}, - unsubscribe: function () {}, - unpsubscribe: function () {}, - message: [], - pmessage: [], - on: function (event, callback) { - if (event !== 'message' && event !== 'pmessage') { - return; - } - Client[event].push(callback); - }, - off: function (event, callback) { - if (event !== 'message' && event !== 'pmessage') { - return; - } - if (callback) { - Client[event] = Client[event].filter(c => c !== callback); - } else { - Client[event] = []; - } - }, -}; - -process.on('message', (message) => { - if (message && typeof message === 'object' && message.action === 'socket.io') { - Client.trigger(message.channel, message.message); - } -}); - -const adapter = require('socket.io-adapter-cluster')({ - client: Client, -}); -// Otherwise, every node thinks it is the master node and ignores messages -// because they are from "itself". -Object.defineProperty(adapter.prototype, 'id', { - get: function () { - return process.pid; - }, - set: function () { - // ignore - }, -}); -module.exports = adapter; diff --git a/src/socket.io/topics.js b/src/socket.io/topics.js index de4d356ad6..a60c629a24 100644 --- a/src/socket.io/topics.js +++ b/src/socket.io/topics.js @@ -83,6 +83,7 @@ SocketTopics.isFollowed = async function (socket, tid) { }; SocketTopics.search = async function (socket, data) { + sockets.warnDeprecated(socket, 'GET /api/search'); if (!data) { throw new Error('[[error:invalid-data]]'); } diff --git a/src/socket.io/topics/infinitescroll.js b/src/socket.io/topics/infinitescroll.js index 03266a3059..c20730229f 100644 --- a/src/socket.io/topics/infinitescroll.js +++ b/src/socket.io/topics/infinitescroll.js @@ -1,5 +1,7 @@ 'use strict'; +const winston = require('winston'); + const topics = require('../../topics'); const privileges = require('../../privileges'); const meta = require('../../meta'); @@ -62,6 +64,7 @@ module.exports = function (SocketTopics) { }; SocketTopics.loadMoreSortedTopics = async function (socket, data) { + winston.warn('[deprecated] SocketTopics.loadMoreSortedTopics use infinitescroll.loadMoreXhr'); // TODO: remove in 1.19.0 if (!data || !utils.isNumber(data.after) || parseInt(data.after, 10) < 0) { throw new Error('[[error:invalid-data]]'); } @@ -85,6 +88,7 @@ module.exports = function (SocketTopics) { }; SocketTopics.loadMoreFromSet = async function (socket, data) { + winston.warn('[deprecated] SocketTopics.loadMoreFromSet use infinitescroll.loadMoreXhr'); // TODO: remove in 1.19.0 if (!data || !utils.isNumber(data.after) || parseInt(data.after, 10) < 0 || !data.set) { throw new Error('[[error:invalid-data]]'); } diff --git a/src/socket.io/topics/tools.js b/src/socket.io/topics/tools.js index a4b6804576..e301cc33a3 100644 --- a/src/socket.io/topics/tools.js +++ b/src/socket.io/topics/tools.js @@ -68,7 +68,7 @@ module.exports = function (SocketTopics) { }; SocketTopics.orderPinnedTopics = async function (socket, data) { - if (!Array.isArray(data)) { + if (!data || !data.tid) { throw new Error('[[error:invalid-data]]'); } diff --git a/src/socket.io/uploads.js b/src/socket.io/uploads.js index c3fd025e0d..66b7266b01 100644 --- a/src/socket.io/uploads.js +++ b/src/socket.io/uploads.js @@ -15,11 +15,12 @@ uploads.upload = async function (socket, data) { 'user.updateCover': socketUser.updateCover, 'groups.cover.update': socketGroup.cover.update, }; - if (!socket.uid || !data || !data.chunk || !data.params || !data.params.method || !methodToFunc[data.params.method]) { + if (!socket.uid || !data || !data.chunk || + !data.params || !data.params.method || !methodToFunc.hasOwnProperty(data.params.method)) { throw new Error('[[error:invalid-data]]'); } - inProgress[socket.id] = inProgress[socket.id] || {}; + inProgress[socket.id] = inProgress[socket.id] || Object.create(null); const socketUploads = inProgress[socket.id]; const { method } = data.params; diff --git a/src/socket.io/user/picture.js b/src/socket.io/user/picture.js index a5a2fbbea7..7de2ef7165 100644 --- a/src/socket.io/user/picture.js +++ b/src/socket.io/user/picture.js @@ -3,42 +3,17 @@ const user = require('../../user'); const plugins = require('../../plugins'); +const websockets = require('../index'); +const api = require('../../api'); + module.exports = function (SocketUser) { SocketUser.changePicture = async function (socket, data) { if (!socket.uid) { throw new Error('[[error:invalid-uid]]'); } - if (!data) { - throw new Error('[[error:invalid-data]]'); - } - - const { type } = data; - let picture = ''; - await user.isAdminOrGlobalModOrSelf(socket.uid, data.uid); - if (type === 'default') { - picture = ''; - } else if (type === 'uploaded') { - picture = await user.getUserField(data.uid, 'uploadedpicture'); - } else { - const returnData = await plugins.hooks.fire('filter:user.getPicture', { - uid: socket.uid, - type: type, - picture: undefined, - }); - picture = returnData && returnData.picture; - } - - const validBackgrounds = await user.getIconBackgrounds(socket.uid); - if (!validBackgrounds.includes(data.bgColor)) { - data.bgColor = validBackgrounds[0]; - } - - await user.updateProfile(socket.uid, { - uid: data.uid, - picture: picture, - 'icon:bgColor': data.bgColor, - }, ['picture', 'icon:bgColor']); + websockets.warnDeprecated(socket, 'PUT /api/v3/users/:uid/picture'); + await api.users.changePicture(socket, data); }; SocketUser.removeUploadedPicture = async function (socket, data) { diff --git a/src/socket.io/user/profile.js b/src/socket.io/user/profile.js index 4f757943ac..0bce7240ff 100644 --- a/src/socket.io/user/profile.js +++ b/src/socket.io/user/profile.js @@ -6,6 +6,7 @@ const api = require('../../api'); const user = require('../../user'); const events = require('../../events'); const notifications = require('../../notifications'); +const privileges = require('../../privileges'); const db = require('../../database'); const plugins = require('../../plugins'); const sockets = require('..'); @@ -31,10 +32,10 @@ module.exports = function (SocketUser) { }; SocketUser.uploadCroppedPicture = async function (socket, data) { - if (!socket.uid) { + if (!socket.uid || !(await privileges.users.canEdit(socket.uid, data.uid))) { throw new Error('[[error:no-privileges]]'); } - await user.isAdminOrGlobalModOrSelf(socket.uid, data.uid); + await user.checkMinReputation(socket.uid, data.uid, 'min:rep:profile-picture'); data.callerUid = socket.uid; return await user.uploadCroppedPicture(data); @@ -139,7 +140,7 @@ module.exports = function (SocketUser) { const userData = await user.getUserFields(data.uid, ['username', 'userslug']); const n = await notifications.create({ bodyShort: `[[notifications:${type}-exported, ${userData.username}]]`, - path: `/api/user/uid/${userData.userslug}/export/${type}`, + path: `/api/user/${userData.userslug}/export/${type}`, nid: `${type}:export:${data.uid}`, from: data.uid, }); diff --git a/src/topics/create.js b/src/topics/create.js index 666d203648..d62eb9800c 100644 --- a/src/topics/create.js +++ b/src/topics/create.js @@ -60,6 +60,7 @@ module.exports = function (Topics) { 'topics:views', 'topics:posts', 'topics:votes', `cid:${topicData.cid}:tids:votes`, `cid:${topicData.cid}:tids:posts`, + `cid:${topicData.cid}:tids:views`, ], 0, topicData.tid), user.addTopicIdToUser(topicData.uid, topicData.tid, timestamp), db.incrObjectField(`category:${topicData.cid}`, 'topic_count'), @@ -76,7 +77,9 @@ module.exports = function (Topics) { }; Topics.post = async function (data) { + data = await plugins.hooks.fire('filter:topic.post', data); const { uid } = data; + data.title = String(data.title).trim(); data.tags = data.tags || []; if (data.content) { @@ -85,12 +88,14 @@ module.exports = function (Topics) { Topics.checkTitle(data.title); await Topics.validateTags(data.tags, data.cid, uid); data.tags = await Topics.filterTags(data.tags, data.cid); - Topics.checkContent(data.content); + if (!data.fromQueue) { + Topics.checkContent(data.content); + } const [categoryExists, canCreate, canTag] = await Promise.all([ categories.exists(data.cid), - privileges.categories.can('topics:create', data.cid, data.uid), - privileges.categories.can('topics:tag', data.cid, data.uid), + privileges.categories.can('topics:create', data.cid, uid), + privileges.categories.can('topics:tag', data.cid, uid), ]); if (!categoryExists) { @@ -103,10 +108,9 @@ module.exports = function (Topics) { await guestHandleValid(data); if (!data.fromQueue) { - await user.isReadyToPost(data.uid, data.cid); + await user.isReadyToPost(uid, data.cid); } - const filteredData = await plugins.hooks.fire('filter:topic.post', data); - data = filteredData; + const tid = await Topics.create(data); let postData = data; @@ -152,6 +156,7 @@ module.exports = function (Topics) { }; Topics.reply = async function (data) { + data = await plugins.hooks.fire('filter:topic.reply', data); const { tid } = data; const { uid } = data; @@ -162,14 +167,13 @@ module.exports = function (Topics) { data.cid = topicData.cid; await guestHandleValid(data); - if (!data.fromQueue) { - await user.isReadyToPost(uid, data.cid); - } - await plugins.hooks.fire('filter:topic.reply', data); if (data.content) { data.content = utils.rtrim(data.content); } - Topics.checkContent(data.content); + if (!data.fromQueue) { + await user.isReadyToPost(uid, data.cid); + Topics.checkContent(data.content); + } // For replies to scheduled topics, don't have a timestamp older than topic's itself if (topicData.scheduled) { @@ -216,6 +220,7 @@ module.exports = function (Topics) { posts.getUserInfoForPosts([postData.uid], uid), Topics.getTopicFields(tid, ['tid', 'uid', 'title', 'slug', 'cid', 'postcount', 'mainPid', 'scheduled']), Topics.addParentPosts([postData]), + Topics.syncBacklinks(postData), posts.parsePost(postData), ]); diff --git a/src/topics/delete.js b/src/topics/delete.js index 889f129c76..3e84a29cc8 100644 --- a/src/topics/delete.js +++ b/src/topics/delete.js @@ -125,6 +125,7 @@ module.exports = function (Topics) { `cid:${topicData.cid}:tids:posts`, `cid:${topicData.cid}:tids:lastposttime`, `cid:${topicData.cid}:tids:votes`, + `cid:${topicData.cid}:tids:views`, `cid:${topicData.cid}:recent_tids`, `cid:${topicData.cid}:uid:${topicData.uid}:tids`, `uid:${topicData.uid}:topics`, diff --git a/src/topics/events.js b/src/topics/events.js index 0e9e64182b..1d2688e5fd 100644 --- a/src/topics/events.js +++ b/src/topics/events.js @@ -2,6 +2,7 @@ const _ = require('lodash'); const db = require('../database'); +const meta = require('../meta'); const user = require('../user'); const posts = require('../posts'); const categories = require('../categories'); @@ -53,6 +54,10 @@ Events._types = { text: '[[topic:queued-by]]', href: '/post-queue', }, + backlink: { + icon: 'fa-link', + text: '[[topic:backlink]]', + }, }; Events.init = async () => { @@ -115,6 +120,11 @@ async function modifyEvent({ tid, uid, eventIds, timestamps, events }) { getCategoryInfo(events.map(event => event.fromCid).filter(Boolean)), ]); + // Remove backlink events if backlinks are disabled + if (meta.config.topicBacklinks !== 1) { + events = events.filter(event => event.type !== 'backlink'); + } + // Remove events whose types no longer exist (e.g. plugin uninstalled) events = events.filter(event => Events._types.hasOwnProperty(event.type)); diff --git a/src/topics/index.js b/src/topics/index.js index 85f9aefb49..67614927ae 100644 --- a/src/topics/index.js +++ b/src/topics/index.js @@ -137,7 +137,7 @@ Topics.getTopicsByTids = async function (tids, options) { topic.teaser = result.teasers[i] || null; topic.isOwner = topic.uid === parseInt(uid, 10); topic.ignored = isIgnored[i]; - topic.unread = parseInt(uid, 10) > 0 && !hasRead[i] && !isIgnored[i]; + topic.unread = parseInt(uid, 10) <= 0 || (!hasRead[i] && !isIgnored[i]); topic.bookmark = sortNewToOld ? Math.max(1, topic.postcount + 2 - bookmarks[i]) : Math.min(topic.postcount, bookmarks[i] + 1); @@ -240,9 +240,29 @@ async function getMainPostAndReplies(topic, set, uid, start, stop, reverse) { Topics.calculatePostIndices(replies, repliesStart); + await Topics.addNextPostTimestamp(postData, set, reverse); return await Topics.addPostData(postData, uid); } +Topics.addNextPostTimestamp = async function (postData, set, reverse) { + if (!postData.length) { + return; + } + postData.forEach((p, index) => { + if (p && postData[index + 1]) { + p.nextPostTimestamp = postData[index + 1].timestamp; + } + }); + const lastPost = postData[postData.length - 1]; + if (lastPost) { + lastPost.nextPostTimestamp = Date.now(); + if (lastPost.index) { + const data = await db[reverse ? 'getSortedSetRevRangeWithScores' : 'getSortedSetRangeWithScores'](set, lastPost.index, lastPost.index); + lastPost.nextPostTimestamp = data.length ? data[0].score : lastPost.nextPostTimestamp; + } + } +}; + async function getDeleter(topicData) { if (!parseInt(topicData.deleterUid, 10)) { return null; @@ -299,11 +319,12 @@ Topics.isLocked = async function (tid) { }; Topics.search = async function (tid, term) { - const pids = await plugins.hooks.fire('filter:topic.search', { + const result = await plugins.hooks.fire('filter:topic.search', { tid: tid, term: term, + ids: [], }); - return Array.isArray(pids) ? pids : []; + return Array.isArray(result) ? result : result.ids; }; require('../promisify')(Topics); diff --git a/src/topics/posts.js b/src/topics/posts.js index ed6f6089ad..dc947c6e67 100644 --- a/src/topics/posts.js +++ b/src/topics/posts.js @@ -3,6 +3,7 @@ const _ = require('lodash'); const validator = require('validator'); +const nconf = require('nconf'); const db = require('../database'); const user = require('../user'); @@ -11,6 +12,8 @@ const meta = require('../meta'); const plugins = require('../plugins'); const utils = require('../../public/src/utils'); +const backlinkRegex = new RegExp(`(?:${nconf.get('url').replace('/', '\\/')}|\b|\\s)\\/topic\\/(\\d+)(?:\\/\\w+)?`, 'g'); + module.exports = function (Topics) { Topics.onNewPostMade = async function (postData) { await Topics.updateLastPostTime(postData.tid, postData.timestamp); @@ -21,6 +24,7 @@ module.exports = function (Topics) { const postData = await posts.getPostsFromSet(set, start, stop, uid, reverse); Topics.calculatePostIndices(postData, start); + await Topics.addNextPostTimestamp(postData, set, reverse); return await Topics.addPostData(postData, uid); }; @@ -208,12 +212,13 @@ module.exports = function (Topics) { }; Topics.increaseViewCount = async function (tid) { - incrementFieldAndUpdateSortedSet(tid, 'viewcount', 1, 'topics:views'); + const cid = await Topics.getTopicField(tid, 'cid'); + incrementFieldAndUpdateSortedSet(tid, 'viewcount', 1, ['topics:views', `cid:${cid}:tids:views`]); }; async function incrementFieldAndUpdateSortedSet(tid, field, by, set) { const value = await db.incrObjectFieldBy(`topic:${tid}`, field, by); - await db.sortedSetAdd(set, value, tid); + await db[Array.isArray(set) ? 'sortedSetsAdd' : 'sortedSetAdd'](set, value, tid); } Topics.getTitleByPid = async function (pid) { @@ -287,4 +292,40 @@ module.exports = function (Topics) { return returnData; } + + Topics.syncBacklinks = async (postData) => { + if (!postData) { + throw new Error('[[error:invalid-data]]'); + } + + // Scan post content for topic links + const matches = [...postData.content.matchAll(backlinkRegex)]; + if (!matches) { + return 0; + } + + const { pid, uid, tid } = postData; + let add = matches.map(match => match[1]); + + const now = Date.now(); + const topicsExist = await Topics.exists(add); + const current = (await db.getSortedSetMembers(`pid:${pid}:backlinks`)).map(tid => parseInt(tid, 10)); + const remove = current.filter(tid => !add.includes(tid)); + add = add.filter((_tid, idx) => topicsExist[idx] && !current.includes(_tid) && tid !== parseInt(_tid, 10)); + + // Remove old backlinks + await db.sortedSetRemove(`pid:${pid}:backlinks`, remove); + + // Add new backlinks + await db.sortedSetAdd(`pid:${pid}:backlinks`, add.map(Number.bind(null, now)), add); + await Promise.all(add.map(async (tid) => { + await Topics.events.log(tid, { + uid, + type: 'backlink', + href: `/post/${pid}`, + }); + })); + + return add.length + (current - remove); + }; }; diff --git a/src/topics/scheduled.js b/src/topics/scheduled.js index ab9cb04ef0..5d99b1432b 100644 --- a/src/topics/scheduled.js +++ b/src/topics/scheduled.js @@ -54,6 +54,7 @@ Scheduled.pin = async function (tid, topicData) { `cid:${topicData.cid}:tids`, `cid:${topicData.cid}:tids:posts`, `cid:${topicData.cid}:tids:votes`, + `cid:${topicData.cid}:tids:views`, ], tid), ]); }; @@ -80,6 +81,7 @@ function unpin(tid, topicData) { [`cid:${topicData.cid}:tids`, topicData.lastposttime, tid], [`cid:${topicData.cid}:tids:posts`, topicData.postcount, tid], [`cid:${topicData.cid}:tids:votes`, parseInt(topicData.votes, 10) || 0, tid], + [`cid:${topicData.cid}:tids:views`, topicData.viewcount, tid], ]), ]; } diff --git a/src/topics/sorted.js b/src/topics/sorted.js index 52f96ed59e..af2cd344cb 100644 --- a/src/topics/sorted.js +++ b/src/topics/sorted.js @@ -118,6 +118,7 @@ module.exports = function (Topics) { old: sortOld, posts: sortPopular, votes: sortVotes, + views: sortViews, }; const sortFn = sortMap[params.sort] || sortRecent; @@ -156,6 +157,10 @@ module.exports = function (Topics) { return b.viewcount - a.viewcount; } + function sortViews(a, b) { + return b.viewcount - a.viewcount; + } + async function filterTids(tids, params) { const { filter } = params; const { uid } = params; diff --git a/src/topics/tools.js b/src/topics/tools.js index e296b2d333..887f166247 100644 --- a/src/topics/tools.js +++ b/src/topics/tools.js @@ -8,6 +8,7 @@ const categories = require('../categories'); const user = require('../user'); const plugins = require('../plugins'); const privileges = require('../privileges'); +const utils = require('../utils'); module.exports = function (Topics) { @@ -172,6 +173,7 @@ module.exports = function (Topics) { `cid:${topicData.cid}:tids`, `cid:${topicData.cid}:tids:posts`, `cid:${topicData.cid}:tids:votes`, + `cid:${topicData.cid}:tids:views`, ], tid)); } else { promises.push(db.sortedSetRemove(`cid:${topicData.cid}:tids:pinned`, tid)); @@ -180,6 +182,7 @@ module.exports = function (Topics) { [`cid:${topicData.cid}:tids`, topicData.lastposttime, tid], [`cid:${topicData.cid}:tids:posts`, topicData.postcount, tid], [`cid:${topicData.cid}:tids:votes`, parseInt(topicData.votes, 10) || 0, tid], + [`cid:${topicData.cid}:tids:views`, topicData.viewcount, tid], ])); topicData.pinExpiry = undefined; topicData.pinExpiryISO = undefined; @@ -197,25 +200,34 @@ module.exports = function (Topics) { } topicTools.orderPinnedTopics = async function (uid, data) { - const tids = data.map(topic => topic && topic.tid); - const topicData = await Topics.getTopicsFields(tids, ['cid']); + const { tid, order } = data; + const cid = await Topics.getTopicField(tid, 'cid'); - const uniqueCids = _.uniq(topicData.map(topicData => topicData && topicData.cid)); - if (uniqueCids.length > 1 || !uniqueCids.length || !uniqueCids[0]) { + if (!cid || !tid || !utils.isNumber(order) || order < 0) { throw new Error('[[error:invalid-data]]'); } - const cid = uniqueCids[0]; - const isAdminOrMod = await privileges.categories.isAdminOrMod(cid, uid); if (!isAdminOrMod) { throw new Error('[[error:no-privileges]]'); } - const isPinned = await db.isSortedSetMembers(`cid:${cid}:tids:pinned`, tids); - data = data.filter((topicData, index) => isPinned[index]); - const bulk = data.map(topicData => [`cid:${cid}:tids:pinned`, topicData.order, topicData.tid]); - await db.sortedSetAddBulk(bulk); + const pinnedTids = await db.getSortedSetRange(`cid:${cid}:tids:pinned`, 0, -1); + const currentIndex = pinnedTids.indexOf(String(tid)); + if (currentIndex === -1) { + return; + } + const newOrder = pinnedTids.length - order - 1; + // moves tid to index order in the array + if (pinnedTids.length > 1) { + pinnedTids.splice(Math.max(0, newOrder), 0, pinnedTids.splice(currentIndex, 1)[0]); + } + + await db.sortedSetAdd( + `cid:${cid}:tids:pinned`, + pinnedTids.map((tid, index) => index), + pinnedTids + ); }; topicTools.move = async function (tid, data) { @@ -233,6 +245,7 @@ module.exports = function (Topics) { `cid:${topicData.cid}:tids:pinned`, `cid:${topicData.cid}:tids:posts`, `cid:${topicData.cid}:tids:votes`, + `cid:${topicData.cid}:tids:views`, `cid:${topicData.cid}:tids:lastposttime`, `cid:${topicData.cid}:recent_tids`, `cid:${topicData.cid}:uid:${topicData.uid}:tids`, @@ -253,6 +266,7 @@ module.exports = function (Topics) { bulk.push([`cid:${cid}:tids`, topicData.lastposttime, tid]); bulk.push([`cid:${cid}:tids:posts`, topicData.postcount, tid]); bulk.push([`cid:${cid}:tids:votes`, votes, tid]); + bulk.push([`cid:${cid}:tids:views`, topicData.viewcount, tid]); } await db.sortedSetAddBulk(bulk); diff --git a/src/upgrades/1.0.0/global_moderators.js b/src/upgrades/1.0.0/global_moderators.js index 2c7895996a..2152bdcfc7 100644 --- a/src/upgrades/1.0.0/global_moderators.js +++ b/src/upgrades/1.0.0/global_moderators.js @@ -1,32 +1,22 @@ 'use strict'; -const async = require('async'); - module.exports = { name: 'Creating Global moderators group', timestamp: Date.UTC(2016, 0, 23), - method: function (callback) { + method: async function () { const groups = require('../../groups'); - async.waterfall([ - function (next) { - groups.exists('Global Moderators', next); - }, - function (exists, next) { - if (exists) { - return next(null, null); - } - groups.create({ - name: 'Global Moderators', - userTitle: 'Global Moderator', - description: 'Forum wide moderators', - hidden: 0, - private: 1, - disableJoinRequests: 1, - }, next); - }, - function (groupData, next) { - groups.show('Global Moderators', next); - }, - ], callback); + const exists = await groups.exists('Global Moderators'); + if (exists) { + return; + } + await groups.create({ + name: 'Global Moderators', + userTitle: 'Global Moderator', + description: 'Forum wide moderators', + hidden: 0, + private: 1, + disableJoinRequests: 1, + }); + await groups.show('Global Moderators'); }, }; diff --git a/src/upgrades/1.0.0/theme_to_active_plugins.js b/src/upgrades/1.0.0/theme_to_active_plugins.js index a541fde331..35f5b5865c 100644 --- a/src/upgrades/1.0.0/theme_to_active_plugins.js +++ b/src/upgrades/1.0.0/theme_to_active_plugins.js @@ -1,16 +1,13 @@ 'use strict'; -const async = require('async'); const db = require('../../database'); module.exports = { name: 'Adding theme to active plugins sorted set', timestamp: Date.UTC(2015, 11, 23), - method: function (callback) { - async.waterfall([ - async.apply(db.getObjectField, 'config', 'theme:id'), - async.apply(db.sortedSetAdd, 'plugins:active', 0), - ], callback); + method: async function () { + const themeId = await db.getObjectField('config', 'theme:id'); + await db.sortedSetAdd('plugins:active', 0, themeId); }, }; diff --git a/src/upgrades/1.1.0/assign_topic_read_privilege.js b/src/upgrades/1.1.0/assign_topic_read_privilege.js index 2b11904773..94c405cc83 100644 --- a/src/upgrades/1.1.0/assign_topic_read_privilege.js +++ b/src/upgrades/1.1.0/assign_topic_read_privilege.js @@ -1,71 +1,35 @@ -'use strict'; +/* eslint-disable no-await-in-loop */ +'use strict'; -const async = require('async'); const winston = require('winston'); const db = require('../../database'); module.exports = { - name: 'Giving topics:read privs to any group that was previously allowed to Find & Access Category', + name: 'Giving topics:read privs to any group/user that was previously allowed to Find & Access Category', timestamp: Date.UTC(2016, 4, 28), - method: function (callback) { + method: async function () { const groupsAPI = require('../../groups'); const privilegesAPI = require('../../privileges'); - db.getSortedSetRange('categories:cid', 0, -1, (err, cids) => { - if (err) { - return callback(err); - } - - async.eachSeries(cids, (cid, next) => { - privilegesAPI.categories.list(cid, (err, data) => { - if (err) { - return next(err); - } - - const { groups } = data; - const { users } = data; - - async.waterfall([ - function (next) { - async.eachSeries(groups, (group, next) => { - if (group.privileges['groups:read']) { - return groupsAPI.join(`cid:${cid}:privileges:groups:topics:read`, group.name, (err) => { - if (!err) { - winston.verbose(`cid:${cid}:privileges:groups:topics:read granted to gid: ${group.name}`); - } + const cids = await db.getSortedSetRange('categories:cid', 0, -1); + for (const cid of cids) { + const { groups, users } = await privilegesAPI.categories.list(cid); - return next(err); - }); - } - - next(null); - }, next); - }, - function (next) { - async.eachSeries(users, (user, next) => { - if (user.privileges.read) { - return groupsAPI.join(`cid:${cid}:privileges:topics:read`, user.uid, (err) => { - if (!err) { - winston.verbose(`cid:${cid}:privileges:topics:read granted to uid: ${user.uid}`); - } - - return next(err); - }); - } - - next(null); - }, next); - }, - ], (err) => { - if (!err) { - winston.verbose(`-- cid ${cid} upgraded`); - } + for (const group of groups) { + if (group.privileges['groups:read']) { + await groupsAPI.join(`cid:${cid}:privileges:groups:topics:read`, group.name); + winston.verbose(`cid:${cid}:privileges:groups:topics:read granted to gid: ${group.name}`); + } + } - next(err); - }); - }); - }, callback); - }); + for (const user of users) { + if (user.privileges.read) { + await groupsAPI.join(`cid:${cid}:privileges:topics:read`, user.uid); + winston.verbose(`cid:${cid}:privileges:topics:read granted to uid: ${user.uid}`); + } + } + winston.verbose(`-- cid ${cid} upgraded`); + } }, }; diff --git a/src/upgrades/1.1.0/dismiss_flags_from_deleted_topics.js b/src/upgrades/1.1.0/dismiss_flags_from_deleted_topics.js index b8f0325e31..0fa43b9090 100644 --- a/src/upgrades/1.1.0/dismiss_flags_from_deleted_topics.js +++ b/src/upgrades/1.1.0/dismiss_flags_from_deleted_topics.js @@ -1,101 +1,56 @@ 'use strict'; -const async = require('async'); const winston = require('winston'); const db = require('../../database'); module.exports = { name: 'Dismiss flags from deleted topics', timestamp: Date.UTC(2016, 3, 29), - method: function (callback) { + method: async function () { const posts = require('../../posts'); const topics = require('../../topics'); - let pids; - let tids; + const pids = await db.getSortedSetRange('posts:flagged', 0, -1); + const postData = await posts.getPostsFields(pids, ['tid']); + const tids = postData.map(t => t.tid); + const topicData = await topics.getTopicsFields(tids, ['deleted']); + const toDismiss = topicData.map((t, idx) => (parseInt(t.deleted, 10) === 1 ? pids[idx] : null)).filter(Boolean); - async.waterfall([ - async.apply(db.getSortedSetRange, 'posts:flagged', 0, -1), - function (_pids, next) { - pids = _pids; - posts.getPostsFields(pids, ['tid'], next); - }, - function (_tids, next) { - tids = _tids.map(a => a.tid); - - topics.getTopicsFields(tids, ['deleted'], next); - }, - function (state, next) { - const toDismiss = state.map((a, idx) => (parseInt(a.deleted, 10) === 1 ? pids[idx] : null)).filter(Boolean); - - winston.verbose(`[2016/04/29] ${toDismiss.length} dismissable flags found`); - async.each(toDismiss, dismissFlag, next); - }, - ], callback); + winston.verbose(`[2016/04/29] ${toDismiss.length} dismissable flags found`); + await Promise.all(toDismiss.map(dismissFlag)); }, }; // copied from core since this function was removed // https://github.com/NodeBB/NodeBB/blob/v1.x.x/src/posts/flags.js -function dismissFlag(pid, callback) { - async.waterfall([ - function (next) { - db.getObjectFields(`post:${pid}`, ['pid', 'uid', 'flags'], next); - }, - function (postData, next) { - if (!postData.pid) { - return callback(); - } - async.parallel([ - function (next) { - if (parseInt(postData.uid, 10)) { - if (parseInt(postData.flags, 10) > 0) { - async.parallel([ - async.apply(db.sortedSetIncrBy, 'users:flags', -postData.flags, postData.uid), - async.apply(db.incrObjectFieldBy, `user:${postData.uid}`, 'flags', -postData.flags), - ], next); - } else { - next(); - } - } else { - next(); - } - }, - function (next) { - db.sortedSetsRemove([ - 'posts:flagged', - 'posts:flags:count', - `uid:${postData.uid}:flag:pids`, - ], pid, next); - }, - function (next) { - async.series([ - function (next) { - db.getSortedSetRange(`pid:${pid}:flag:uids`, 0, -1, (err, uids) => { - if (err) { - return next(err); - } - - async.each(uids, (uid, next) => { - const nid = `post_flag:${pid}:uid:${uid}`; - async.parallel([ - async.apply(db.delete, `notifications:${nid}`), - async.apply(db.sortedSetRemove, 'notifications', `post_flag:${pid}:uid:${uid}`), - ], next); - }, next); - }); - }, - async.apply(db.delete, `pid:${pid}:flag:uids`), - ], next); - }, - async.apply(db.deleteObjectField, `post:${pid}`, 'flags'), - async.apply(db.delete, `pid:${pid}:flag:uid:reason`), - async.apply(db.deleteObjectFields, `post:${pid}`, ['flag:state', 'flag:assignee', 'flag:notes', 'flag:history']), - ], next); - }, - function (results, next) { - db.sortedSetsRemoveRangeByScore(['users:flags'], '-inf', 0, next); - }, - ], callback); +async function dismissFlag(pid) { + const postData = await db.getObjectFields(`post:${pid}`, ['pid', 'uid', 'flags']); + if (!postData.pid) { + return; + } + if (parseInt(postData.uid, 10) && parseInt(postData.flags, 10) > 0) { + await Promise.all([ + db.sortedSetIncrBy('users:flags', -postData.flags, postData.uid), + db.incrObjectFieldBy(`user:${postData.uid}`, 'flags', -postData.flags), + ]); + } + const uids = await db.getSortedSetRange(`pid:${pid}:flag:uids`, 0, -1); + const nids = uids.map(uid => `post_flag:${pid}:uid:${uid}`); + + await Promise.all([ + db.deleteAll(nids.map(nid => `notifications:${nid}`)), + db.sortedSetRemove('notifications', nids), + db.delete(`pid:${pid}:flag:uids`), + db.sortedSetsRemove([ + 'posts:flagged', + 'posts:flags:count', + `uid:${postData.uid}:flag:pids`, + ], pid), + db.deleteObjectField(`post:${pid}`, 'flags'), + db.delete(`pid:${pid}:flag:uid:reason`), + db.deleteObjectFields(`post:${pid}`, ['flag:state', 'flag:assignee', 'flag:notes', 'flag:history']), + ]); + + await db.sortedSetsRemoveRangeByScore(['users:flags'], '-inf', 0); } diff --git a/src/upgrades/1.10.0/search_privileges.js b/src/upgrades/1.10.0/search_privileges.js index 0d53aa07b6..f5576e818b 100644 --- a/src/upgrades/1.10.0/search_privileges.js +++ b/src/upgrades/1.10.0/search_privileges.js @@ -1,30 +1,23 @@ 'use strict'; -const async = require('async'); - module.exports = { name: 'Give global search privileges', timestamp: Date.UTC(2018, 4, 28), - method: function (callback) { + method: async function () { const meta = require('../../meta'); const privileges = require('../../privileges'); const allowGuestSearching = parseInt(meta.config.allowGuestSearching, 10) === 1; const allowGuestUserSearching = parseInt(meta.config.allowGuestUserSearching, 10) === 1; - async.waterfall([ - function (next) { - privileges.global.give(['groups:search:content', 'groups:search:users', 'groups:search:tags'], 'registered-users', next); - }, - function (next) { - const guestPrivs = []; - if (allowGuestSearching) { - guestPrivs.push('groups:search:content'); - } - if (allowGuestUserSearching) { - guestPrivs.push('groups:search:users'); - } - guestPrivs.push('groups:search:tags'); - privileges.global.give(guestPrivs, 'guests', next); - }, - ], callback); + + await privileges.global.give(['groups:search:content', 'groups:search:users', 'groups:search:tags'], 'registered-users'); + const guestPrivs = []; + if (allowGuestSearching) { + guestPrivs.push('groups:search:content'); + } + if (allowGuestUserSearching) { + guestPrivs.push('groups:search:users'); + } + guestPrivs.push('groups:search:tags'); + await privileges.global.give(guestPrivs, 'guests'); }, }; diff --git a/src/upgrades/1.10.0/view_deleted_privilege.js b/src/upgrades/1.10.0/view_deleted_privilege.js index ddf4b9c3fc..a483bcf417 100644 --- a/src/upgrades/1.10.0/view_deleted_privilege.js +++ b/src/upgrades/1.10.0/view_deleted_privilege.js @@ -1,7 +1,6 @@ -'use strict'; - +/* eslint-disable no-await-in-loop */ -const async = require('async'); +'use strict'; const groups = require('../../groups'); const db = require('../../database'); @@ -9,19 +8,15 @@ const db = require('../../database'); module.exports = { name: 'Give deleted post viewing privilege to moderators on all categories', timestamp: Date.UTC(2018, 5, 8), - method: function (callback) { - db.getSortedSetRange('categories:cid', 0, -1, (err, cids) => { - if (err) { - return callback(err); + method: async function () { + const { progress } = this; + const cids = await db.getSortedSetRange('categories:cid', 0, -1); + for (const cid of cids) { + const uids = await db.getSortedSetRange(`group:cid:${cid}:privileges:moderate:members`, 0, -1); + for (const uid of uids) { + await groups.join(`cid:${cid}:privileges:posts:view_deleted`, uid); } - async.eachSeries(cids, (cid, next) => { - async.waterfall([ - async.apply(db.getSortedSetRange.bind(db), `group:cid:${cid}:privileges:moderate:members`, 0, -1), - function (uids, next) { - async.eachSeries(uids, (uid, next) => groups.join(`cid:${cid}:privileges:posts:view_deleted`, uid, next), next); - }, - ], next); - }, callback); - }); + progress.incr(); + } }, }; diff --git a/src/upgrades/1.10.2/event_filters.js b/src/upgrades/1.10.2/event_filters.js index d87a526d5e..cf2709a5ab 100644 --- a/src/upgrades/1.10.2/event_filters.js +++ b/src/upgrades/1.10.2/event_filters.js @@ -1,6 +1,7 @@ +/* eslint-disable no-await-in-loop */ + 'use strict'; -const async = require('async'); const db = require('../../database'); const batch = require('../../batch'); @@ -8,39 +9,29 @@ const batch = require('../../batch'); module.exports = { name: 'add filters to events', timestamp: Date.UTC(2018, 9, 4), - method: function (callback) { + method: async function () { const { progress } = this; - batch.processSortedSet('events:time', (eids, next) => { - async.eachSeries(eids, (eid, next) => { + await batch.processSortedSet('events:time', async (eids) => { + for (const eid of eids) { progress.incr(); - db.getObject(`event:${eid}`, (err, eventData) => { - if (err) { - return next(err); - } - if (!eventData) { - return db.sortedSetRemove('events:time', eid, next); - } - // privilege events we're missing type field - if (!eventData.type && eventData.privilege) { - eventData.type = 'privilege-change'; - async.waterfall([ - function (next) { - db.setObjectField(`event:${eid}`, 'type', 'privilege-change', next); - }, - function (next) { - db.sortedSetAdd(`events:time:${eventData.type}`, eventData.timestamp, eid, next); - }, - ], next); - return; - } - - db.sortedSetAdd(`events:time:${eventData.type || ''}`, eventData.timestamp, eid, next); - }); - }, next); + const eventData = await db.getObject(`event:${eid}`); + if (!eventData) { + await db.sortedSetRemove('events:time', eid); + return; + } + // privilege events we're missing type field + if (!eventData.type && eventData.privilege) { + eventData.type = 'privilege-change'; + await db.setObjectField(`event:${eid}`, 'type', 'privilege-change'); + await db.sortedSetAdd(`events:time:${eventData.type}`, eventData.timestamp, eid); + return; + } + await db.sortedSetAdd(`events:time:${eventData.type || ''}`, eventData.timestamp, eid); + } }, { progress: this.progress, - }, callback); + }); }, }; diff --git a/src/upgrades/1.10.2/fix_category_topic_zsets.js b/src/upgrades/1.10.2/fix_category_topic_zsets.js index 692d35af18..999383feac 100644 --- a/src/upgrades/1.10.2/fix_category_topic_zsets.js +++ b/src/upgrades/1.10.2/fix_category_topic_zsets.js @@ -1,6 +1,7 @@ +/* eslint-disable no-await-in-loop */ + 'use strict'; -const async = require('async'); const db = require('../../database'); const batch = require('../../batch'); @@ -8,32 +9,22 @@ const batch = require('../../batch'); module.exports = { name: 'Fix category topic zsets', timestamp: Date.UTC(2018, 9, 11), - method: function (callback) { + method: async function () { const { progress } = this; const topics = require('../../topics'); - batch.processSortedSet('topics:tid', (tids, next) => { - async.eachSeries(tids, (tid, next) => { + await batch.processSortedSet('topics:tid', async (tids) => { + for (const tid of tids) { progress.incr(); - - async.waterfall([ - function (next) { - db.getObjectFields(`topic:${tid}`, ['cid', 'pinned', 'postcount'], next); - }, - function (topicData, next) { - if (parseInt(topicData.pinned, 10) === 1) { - return setImmediate(next); - } - topicData.postcount = parseInt(topicData.postcount, 10) || 0; - db.sortedSetAdd(`cid:${topicData.cid}:tids:posts`, topicData.postcount, tid, next); - }, - function (next) { - topics.updateLastPostTimeFromLastPid(tid, next); - }, - ], next); - }, next); + const topicData = await db.getObjectFields(`topic:${tid}`, ['cid', 'pinned', 'postcount']); + if (parseInt(topicData.pinned, 10) !== 1) { + topicData.postcount = parseInt(topicData.postcount, 10) || 0; + await db.sortedSetAdd(`cid:${topicData.cid}:tids:posts`, topicData.postcount, tid); + } + await topics.updateLastPostTimeFromLastPid(tid); + } }, { progress: progress, - }, callback); + }); }, }; diff --git a/src/upgrades/1.10.2/upgrade_bans_to_hashes.js b/src/upgrades/1.10.2/upgrade_bans_to_hashes.js index 24a2cd8cc0..84c7a0ed4d 100644 --- a/src/upgrades/1.10.2/upgrade_bans_to_hashes.js +++ b/src/upgrades/1.10.2/upgrade_bans_to_hashes.js @@ -1,69 +1,39 @@ +/* eslint-disable no-await-in-loop */ + 'use strict'; -const async = require('async'); const db = require('../../database'); - const batch = require('../../batch'); -// var user = require('../../user'); module.exports = { name: 'Upgrade bans to hashes', timestamp: Date.UTC(2018, 8, 24), - method: function (callback) { + method: async function () { const { progress } = this; - batch.processSortedSet('users:joindate', (uids, next) => { - async.eachSeries(uids, (uid, next) => { + await batch.processSortedSet('users:joindate', async (uids) => { + for (const uid of uids) { progress.incr(); + const [bans, reasons, userData] = await Promise.all([ + db.getSortedSetRevRangeWithScores(`uid:${uid}:bans`, 0, -1), + db.getSortedSetRevRangeWithScores(`banned:${uid}:reasons`, 0, -1), + db.getObjectFields(`user:${uid}`, ['banned', 'banned:expire', 'joindate', 'lastposttime', 'lastonline']), + ]); - async.parallel({ - bans: function (next) { - db.getSortedSetRevRangeWithScores(`uid:${uid}:bans`, 0, -1, next); - }, - reasons: function (next) { - db.getSortedSetRevRangeWithScores(`banned:${uid}:reasons`, 0, -1, next); - }, - userData: function (next) { - db.getObjectFields(`user:${uid}`, ['banned', 'banned:expire', 'joindate', 'lastposttime', 'lastonline'], next); - }, - }, (err, results) => { - function addBan(key, data, callback) { - async.waterfall([ - function (next) { - db.setObject(key, data, next); - }, - function (next) { - db.sortedSetAdd(`uid:${uid}:bans:timestamp`, data.timestamp, key, next); - }, - ], callback); - } - if (err) { - return next(err); - } - // has no ban history and isn't banned, skip - if (!results.bans.length && !parseInt(results.userData.banned, 10)) { - return next(); - } - - // has no history, but is banned, create plain object with just uid and timestmap - if (!results.bans.length && parseInt(results.userData.banned, 10)) { - const banTimestamp = ( - results.userData.lastonline || - results.userData.lastposttime || - results.userData.joindate || - Date.now() - ); - const banKey = `uid:${uid}:ban:${banTimestamp}`; - addBan(banKey, { uid: uid, timestamp: banTimestamp }, next); - return; - } - + // has no history, but is banned, create plain object with just uid and timestmap + if (!bans.length && parseInt(userData.banned, 10)) { + const banTimestamp = ( + userData.lastonline || + userData.lastposttime || + userData.joindate || + Date.now() + ); + const banKey = `uid:${uid}:ban:${banTimestamp}`; + await addBan(uid, banKey, { uid: uid, timestamp: banTimestamp }); + } else if (bans.length) { // process ban history - async.eachSeries(results.bans, (ban, next) => { - function findReason(score) { - return results.reasons.find(reasonData => reasonData.score === score); - } - const reasonData = findReason(ban.score); + for (const ban of bans) { + const reasonData = reasons.find(reasonData => reasonData.score === ban.score); const banKey = `uid:${uid}:ban:${ban.score}`; const data = { uid: uid, @@ -73,14 +43,17 @@ module.exports = { if (reasonData) { data.reason = reasonData.value; } - addBan(banKey, data, next); - }, (err) => { - next(err); - }); - }); - }, next); + await addBan(uid, banKey, data); + } + } + } }, { progress: this.progress, - }, callback); + }); }, }; + +async function addBan(uid, key, data) { + await db.setObject(key, data); + await db.sortedSetAdd(`uid:${uid}:bans:timestamp`, data.timestamp, key); +} diff --git a/src/upgrades/1.10.2/username_email_history.js b/src/upgrades/1.10.2/username_email_history.js index 69bae687d3..3b03568a69 100644 --- a/src/upgrades/1.10.2/username_email_history.js +++ b/src/upgrades/1.10.2/username_email_history.js @@ -1,6 +1,5 @@ 'use strict'; -const async = require('async'); const db = require('../../database'); const batch = require('../../batch'); @@ -9,61 +8,30 @@ const user = require('../../user'); module.exports = { name: 'Record first entry in username/email history', timestamp: Date.UTC(2018, 7, 28), - method: function (callback) { + method: async function () { const { progress } = this; - batch.processSortedSet('users:joindate', (ids, next) => { - async.each(ids, (uid, next) => { - async.parallel([ - function (next) { - // Username - async.waterfall([ - async.apply(db.sortedSetCard, `user:${uid}:usernames`), - (count, next) => { - if (count > 0) { - // User has changed their username before, no record of original username, skip. - return setImmediate(next, null, null); - } - - user.getUserFields(uid, ['username', 'joindate'], next); - }, - (userdata, next) => { - if (!userdata || !userdata.joindate) { - return setImmediate(next); - } - - db.sortedSetAdd(`user:${uid}:usernames`, userdata.joindate, [userdata.username, userdata.joindate].join(':'), next); - }, - ], next); - }, - function (next) { - // Email - async.waterfall([ - async.apply(db.sortedSetCard, `user:${uid}:emails`), - (count, next) => { - if (count > 0) { - // User has changed their email before, no record of original email, skip. - return setImmediate(next, null, null); - } - - user.getUserFields(uid, ['email', 'joindate'], next); - }, - (userdata, next) => { - if (!userdata || !userdata.joindate) { - return setImmediate(next); - } - - db.sortedSetAdd(`user:${uid}:emails`, userdata.joindate, [userdata.email, userdata.joindate].join(':'), next); - }, - ], next); - }, - ], (err) => { - progress.incr(); - setImmediate(next, err); - }); - }, next); + await batch.processSortedSet('users:joindate', async (uids) => { + async function updateHistory(uid, set, fieldName) { + const count = await db.sortedSetCard(set); + if (count <= 0) { + // User has not changed their username/email before, record original username + const userData = await user.getUserFields(uid, [fieldName, 'joindate']); + if (userData && userData.joindate && userData[fieldName]) { + await db.sortedSetAdd(set, userData.joindate, [userData[fieldName], userData.joindate].join(':')); + } + } + } + + await Promise.all(uids.map(async (uid) => { + await Promise.all([ + updateHistory(uid, `user:${uid}:usernames`, 'username'), + updateHistory(uid, `user:${uid}:emails`, 'email'), + ]); + progress.incr(); + })); }, { progress: this.progress, - }, callback); + }); }, }; diff --git a/src/upgrades/1.11.0/navigation_visibility_groups.js b/src/upgrades/1.11.0/navigation_visibility_groups.js index 0a5e0b733d..3567b3c9c1 100644 --- a/src/upgrades/1.11.0/navigation_visibility_groups.js +++ b/src/upgrades/1.11.0/navigation_visibility_groups.js @@ -1,36 +1,28 @@ 'use strict'; -const async = require('async'); - module.exports = { name: 'Navigation item visibility groups', timestamp: Date.UTC(2018, 10, 10), - method: function (callback) { + method: async function () { const navigationAdmin = require('../../navigation/admin'); - async.waterfall([ - function (next) { - navigationAdmin.get(next); - }, - function (data, next) { - data.forEach((navItem) => { - if (navItem && navItem.properties) { - navItem.groups = []; - if (navItem.properties.adminOnly) { - navItem.groups.push('administrators'); - } else if (navItem.properties.globalMod) { - navItem.groups.push('Global Moderators'); - } + const data = await navigationAdmin.get(); + data.forEach((navItem) => { + if (navItem && navItem.properties) { + navItem.groups = []; + if (navItem.properties.adminOnly) { + navItem.groups.push('administrators'); + } else if (navItem.properties.globalMod) { + navItem.groups.push('Global Moderators'); + } - if (navItem.properties.loggedIn) { - navItem.groups.push('registered-users'); - } else if (navItem.properties.guestOnly) { - navItem.groups.push('guests'); - } - } - }); - navigationAdmin.save(data, next); - }, - ], callback); + if (navItem.properties.loggedIn) { + navItem.groups.push('registered-users'); + } else if (navItem.properties.guestOnly) { + navItem.groups.push('guests'); + } + } + }); + await navigationAdmin.save(data); }, }; diff --git a/src/upgrades/1.11.0/resize_image_width.js b/src/upgrades/1.11.0/resize_image_width.js index faab4bb843..a29a45f4f3 100644 --- a/src/upgrades/1.11.0/resize_image_width.js +++ b/src/upgrades/1.11.0/resize_image_width.js @@ -1,24 +1,14 @@ 'use strict'; -const async = require('async'); const db = require('../../database'); - module.exports = { name: 'Rename maximumImageWidth to resizeImageWidth', timestamp: Date.UTC(2018, 9, 24), - method: function (callback) { + method: async function () { const meta = require('../../meta'); - async.waterfall([ - function (next) { - meta.configs.get('maximumImageWidth', next); - }, - function (value, next) { - meta.configs.set('resizeImageWidth', value, next); - }, - function (next) { - db.deleteObjectField('config', 'maximumImageWidth', next); - }, - ], callback); + const value = await meta.configs.get('maximumImageWidth'); + await meta.configs.set('resizeImageWidth', value); + await db.deleteObjectField('config', 'maximumImageWidth'); }, }; diff --git a/src/upgrades/1.11.0/widget_visibility_groups.js b/src/upgrades/1.11.0/widget_visibility_groups.js index 41db65a086..66ceac8795 100644 --- a/src/upgrades/1.11.0/widget_visibility_groups.js +++ b/src/upgrades/1.11.0/widget_visibility_groups.js @@ -1,47 +1,38 @@ 'use strict'; -const async = require('async'); - module.exports = { name: 'Widget visibility groups', timestamp: Date.UTC(2018, 10, 10), - method: function (callback) { + method: async function () { const widgetAdmin = require('../../widgets/admin'); const widgets = require('../../widgets'); - async.waterfall([ - function (next) { - widgetAdmin.getAreas(next); - }, - function (areas, next) { - async.eachSeries(areas, (area, next) => { - if (area.data.length) { - // area.data is actually an array of widgets - area.widgets = area.data; - area.widgets.forEach((widget) => { - if (widget && widget.data) { - const groupsToShow = ['administrators', 'Global Moderators']; - if (widget.data['hide-guests'] !== 'on') { - groupsToShow.push('guests'); - } - if (widget.data['hide-registered'] !== 'on') { - groupsToShow.push('registered-users'); - } + const areas = await widgetAdmin.getAreas(); + for (const area of areas) { + if (area.data.length) { + // area.data is actually an array of widgets + area.widgets = area.data; + area.widgets.forEach((widget) => { + if (widget && widget.data) { + const groupsToShow = ['administrators', 'Global Moderators']; + if (widget.data['hide-guests'] !== 'on') { + groupsToShow.push('guests'); + } + if (widget.data['hide-registered'] !== 'on') { + groupsToShow.push('registered-users'); + } - widget.data.groups = groupsToShow; + widget.data.groups = groupsToShow; - // if we are showing to all 4 groups, set to empty array - // empty groups is shown to everyone - if (groupsToShow.length === 4) { - widget.data.groups.length = 0; - } - } - }); - widgets.setArea(area, next); - } else { - next(); + // if we are showing to all 4 groups, set to empty array + // empty groups is shown to everyone + if (groupsToShow.length === 4) { + widget.data.groups.length = 0; + } } - }, next); - }, - ], callback); + }); + // eslint-disable-next-line no-await-in-loop + await widgets.setArea(area); + } + } }, }; diff --git a/src/upgrades/1.12.0/category_watch_state.js b/src/upgrades/1.12.0/category_watch_state.js index 71e430bedb..1363d50bc2 100644 --- a/src/upgrades/1.12.0/category_watch_state.js +++ b/src/upgrades/1.12.0/category_watch_state.js @@ -1,6 +1,6 @@ -'use strict'; +/* eslint-disable no-await-in-loop */ -const async = require('async'); +'use strict'; const db = require('../../database'); const batch = require('../../batch'); @@ -9,39 +9,27 @@ const categories = require('../../categories'); module.exports = { name: 'Update category watch data', timestamp: Date.UTC(2018, 11, 13), - method: function (callback) { + method: async function () { const { progress } = this; - let keys; - async.waterfall([ - function (next) { - db.getSortedSetRange('categories:cid', 0, -1, next); - }, - function (cids, next) { - keys = cids.map(cid => `cid:${cid}:ignorers`); - batch.processSortedSet('users:joindate', (uids, next) => { - progress.incr(uids.length); - async.eachSeries(cids, (cid, next) => { - db.isSortedSetMembers(`cid:${cid}:ignorers`, uids, (err, isMembers) => { - if (err) { - return next(err); - } - uids = uids.filter((uid, index) => isMembers[index]); - if (!uids.length) { - return setImmediate(next); - } - const states = uids.map(() => categories.watchStates.ignoring); - db.sortedSetAdd(`cid:${cid}:uid:watch:state`, states, uids, next); - }); - }, next); - }, { - progress: progress, - batch: 500, - }, next); - }, - function (next) { - db.deleteAll(keys, next); - }, - ], callback); + const cids = await db.getSortedSetRange('categories:cid', 0, -1); + const keys = cids.map(cid => `cid:${cid}:ignorers`); + + await batch.processSortedSet('users:joindate', async (uids) => { + progress.incr(uids.length); + for (const cid of cids) { + const isMembers = await db.isSortedSetMembers(`cid:${cid}:ignorers`, uids); + uids = uids.filter((uid, index) => isMembers[index]); + if (uids.length) { + const states = uids.map(() => categories.watchStates.ignoring); + await db.sortedSetAdd(`cid:${cid}:uid:watch:state`, states, uids); + } + } + }, { + progress: progress, + batch: 500, + }); + + await db.deleteAll(keys); }, }; diff --git a/src/upgrades/1.12.1/moderation_notes_refactor.js b/src/upgrades/1.12.1/moderation_notes_refactor.js index 59ea6e6b38..390273d74a 100644 --- a/src/upgrades/1.12.1/moderation_notes_refactor.js +++ b/src/upgrades/1.12.1/moderation_notes_refactor.js @@ -1,55 +1,35 @@ +/* eslint-disable no-await-in-loop */ + 'use strict'; -const async = require('async'); const db = require('../../database'); const batch = require('../../batch'); - module.exports = { name: 'Update moderation notes to hashes', timestamp: Date.UTC(2019, 3, 5), - method: function (callback) { + method: async function () { const { progress } = this; - batch.processSortedSet('users:joindate', (ids, next) => { - async.each(ids, (uid, next) => { + await batch.processSortedSet('users:joindate', async (uids) => { + await Promise.all(uids.map(async (uid) => { progress.incr(); - db.getSortedSetRevRange(`uid:${uid}:moderation:notes`, 0, -1, (err, notes) => { - if (err || !notes.length) { - return next(err); - } - async.eachSeries(notes, (note, next) => { - let noteData; - async.waterfall([ - function (next) { - try { - noteData = JSON.parse(note); - noteData.timestamp = noteData.timestamp || Date.now(); - setImmediate(next); - } catch (err) { - next(err); - } - }, - function (next) { - db.sortedSetRemove(`uid:${uid}:moderation:notes`, note, next); - }, - function (next) { - db.setObject(`uid:${uid}:moderation:note:${noteData.timestamp}`, { - uid: noteData.uid, - timestamp: noteData.timestamp, - note: noteData.note, - }, next); - }, - function (next) { - db.sortedSetAdd(`uid:${uid}:moderation:notes`, noteData.timestamp, noteData.timestamp, next); - }, - ], next); - }, next); - }); - }, next); + const notes = await db.getSortedSetRevRange(`uid:${uid}:moderation:notes`, 0, -1); + for (const note of notes) { + const noteData = JSON.parse(note); + noteData.timestamp = noteData.timestamp || Date.now(); + await db.sortedSetRemove(`uid:${uid}:moderation:notes`, note); + await db.setObject(`uid:${uid}:moderation:note:${noteData.timestamp}`, { + uid: noteData.uid, + timestamp: noteData.timestamp, + note: noteData.note, + }); + await db.sortedSetAdd(`uid:${uid}:moderation:notes`, noteData.timestamp, noteData.timestamp); + } + })); }, { progress: this.progress, - }, callback); + }); }, }; diff --git a/src/upgrades/1.12.3/give_mod_info_privilege.js b/src/upgrades/1.12.3/give_mod_info_privilege.js index c613bf6205..e8c51978bb 100644 --- a/src/upgrades/1.12.3/give_mod_info_privilege.js +++ b/src/upgrades/1.12.3/give_mod_info_privilege.js @@ -1,6 +1,7 @@ +/* eslint-disable no-await-in-loop */ + 'use strict'; -const async = require('async'); const db = require('../../database'); const privileges = require('../../privileges'); const groups = require('../../groups'); @@ -8,38 +9,19 @@ const groups = require('../../groups'); module.exports = { name: 'give mod info privilege', timestamp: Date.UTC(2019, 9, 8), - method: function (callback) { - async.waterfall([ - function (next) { - db.getSortedSetRevRange('categories:cid', 0, -1, next); - }, - function (cids, next) { - async.eachSeries(cids, (cid, next) => { - async.waterfall([ - function (next) { - givePrivsToModerators(cid, '', next); - }, - function (next) { - givePrivsToModerators(cid, 'groups:', next); - }, - ], next); - }, next); - }, - function (next) { - privileges.global.give(['groups:view:users:info'], 'Global Moderators', next); - }, - ], callback); - function givePrivsToModerators(cid, groupPrefix, callback) { - async.waterfall([ - function (next) { - db.getSortedSetRevRange(`group:cid:${cid}:privileges:${groupPrefix}moderate:members`, 0, -1, next); - }, - function (members, next) { - async.eachSeries(members, (member, next) => { - groups.join(['cid:0:privileges:view:users:info'], member, next); - }, next); - }, - ], callback); + method: async function () { + const cids = await db.getSortedSetRevRange('categories:cid', 0, -1); + for (const cid of cids) { + await givePrivsToModerators(cid, ''); + await givePrivsToModerators(cid, 'groups:'); + } + await privileges.global.give(['groups:view:users:info'], 'Global Moderators'); + + async function givePrivsToModerators(cid, groupPrefix) { + const members = await db.getSortedSetRevRange(`group:cid:${cid}:privileges:${groupPrefix}moderate:members`, 0, -1); + for (const member of members) { + await groups.join(['cid:0:privileges:view:users:info'], member); + } } }, }; diff --git a/src/upgrades/1.12.3/give_mod_privileges.js b/src/upgrades/1.12.3/give_mod_privileges.js index 03c6c6ff67..cf7439a16b 100644 --- a/src/upgrades/1.12.3/give_mod_privileges.js +++ b/src/upgrades/1.12.3/give_mod_privileges.js @@ -1,6 +1,7 @@ +/* eslint-disable no-await-in-loop */ + 'use strict'; -const async = require('async'); const privileges = require('../../privileges'); const groups = require('../../groups'); const db = require('../../database'); @@ -8,7 +9,7 @@ const db = require('../../database'); module.exports = { name: 'Give mods explicit privileges', timestamp: Date.UTC(2019, 4, 28), - method: function (callback) { + method: async function () { const defaultPrivileges = [ 'find', 'read', @@ -43,43 +44,20 @@ module.exports = { 'groups:local:login', ]; - async.waterfall([ - function (next) { - db.getSortedSetRevRange('categories:cid', 0, -1, next); - }, - function (cids, next) { - async.eachSeries(cids, (cid, next) => { - async.waterfall([ - function (next) { - givePrivsToModerators(cid, '', next); - }, - function (next) { - givePrivsToModerators(cid, 'groups:', next); - }, - function (next) { - privileges.categories.give(modPrivileges.map(p => `groups:${p}`), cid, ['Global Moderators'], next); - }, - ], next); - }, next); - }, - function (next) { - privileges.global.give(globalModPrivs, 'Global Moderators', next); - }, - ], callback); + const cids = await db.getSortedSetRevRange('categories:cid', 0, -1); + for (const cid of cids) { + await givePrivsToModerators(cid, ''); + await givePrivsToModerators(cid, 'groups:'); + await privileges.categories.give(modPrivileges.map(p => `groups:${p}`), cid, ['Global Moderators']); + } + await privileges.global.give(globalModPrivs, 'Global Moderators'); - function givePrivsToModerators(cid, groupPrefix, callback) { + async function givePrivsToModerators(cid, groupPrefix) { const privGroups = modPrivileges.map(priv => `cid:${cid}:privileges:${groupPrefix}${priv}`); - - async.waterfall([ - function (next) { - db.getSortedSetRevRange(`group:cid:${cid}:privileges:${groupPrefix}moderate:members`, 0, -1, next); - }, - function (members, next) { - async.eachSeries(members, (member, next) => { - groups.join(privGroups, member, next); - }, next); - }, - ], callback); + const members = await db.getSortedSetRevRange(`group:cid:${cid}:privileges:${groupPrefix}moderate:members`, 0, -1); + for (const member of members) { + await groups.join(privGroups, member); + } } }, }; diff --git a/src/upgrades/1.12.3/user_pid_sets.js b/src/upgrades/1.12.3/user_pid_sets.js index d98b4f9aab..543059705e 100644 --- a/src/upgrades/1.12.3/user_pid_sets.js +++ b/src/upgrades/1.12.3/user_pid_sets.js @@ -1,6 +1,6 @@ + 'use strict'; -const async = require('async'); const db = require('../../database'); const batch = require('../../batch'); @@ -10,36 +10,26 @@ const topics = require('../../topics'); module.exports = { name: 'Create zsets for user posts per category', timestamp: Date.UTC(2019, 5, 23), - method: function (callback) { + method: async function () { const { progress } = this; - batch.processSortedSet('posts:pid', (pids, next) => { + await batch.processSortedSet('posts:pid', async (pids) => { progress.incr(pids.length); - let postData; - async.waterfall([ - function (next) { - posts.getPostsFields(pids, ['pid', 'uid', 'tid', 'upvotes', 'downvotes', 'timestamp'], next); - }, - function (_postData, next) { - postData = _postData; - const tids = postData.map(p => p.tid); - topics.getTopicsFields(tids, ['cid'], next); - }, - function (topicData, next) { - const bulk = []; - postData.forEach((p, index) => { - if (p && p.uid && p.pid && p.tid && p.timestamp) { - bulk.push([`cid:${topicData[index].cid}:uid:${p.uid}:pids`, p.timestamp, p.pid]); - if (p.votes > 0) { - bulk.push([`cid:${topicData[index].cid}:uid:${p.uid}:pids:votes`, p.votes, p.pid]); - } - } - }); - db.sortedSetAddBulk(bulk, next); - }, - ], next); + const postData = await posts.getPostsFields(pids, ['pid', 'uid', 'tid', 'upvotes', 'downvotes', 'timestamp']); + const tids = postData.map(p => p.tid); + const topicData = await topics.getTopicsFields(tids, ['cid']); + const bulk = []; + postData.forEach((p, index) => { + if (p && p.uid && p.pid && p.tid && p.timestamp) { + bulk.push([`cid:${topicData[index].cid}:uid:${p.uid}:pids`, p.timestamp, p.pid]); + if (p.votes > 0) { + bulk.push([`cid:${topicData[index].cid}:uid:${p.uid}:pids:votes`, p.votes, p.pid]); + } + } + }); + await db.sortedSetAddBulk(bulk); }, { progress: progress, - }, callback); + }); }, }; diff --git a/src/upgrades/1.18.0/topic_tags_refactor.js b/src/upgrades/1.18.0/topic_tags_refactor.js index c81e139bb3..5fd2218c49 100644 --- a/src/upgrades/1.18.0/topic_tags_refactor.js +++ b/src/upgrades/1.18.0/topic_tags_refactor.js @@ -28,6 +28,7 @@ module.exports = { topicsWithTags.map(t => `topic:${t.tid}`), topicsWithTags.map(t => ({ tags: t.tags.join(',') })) ); + await db.deleteAll(tids.map(tid => `topic:${tid}:tags`)); progress.incr(tids.length); }, { batch: 500, diff --git a/src/upgrades/1.18.4/category_topics_views.js b/src/upgrades/1.18.4/category_topics_views.js new file mode 100644 index 0000000000..c2d78fbf9e --- /dev/null +++ b/src/upgrades/1.18.4/category_topics_views.js @@ -0,0 +1,23 @@ +'use strict'; + +const db = require('../../database'); +const batch = require('../../batch'); +const topics = require('../../topics'); + +module.exports = { + name: 'Category topics sorted sets by views', + timestamp: Date.UTC(2021, 8, 28), + method: async function () { + const { progress } = this; + + await batch.processSortedSet('topics:tid', async (tids) => { + let topicData = await topics.getTopicsData(tids); + topicData = topicData.filter(t => t && t.cid); + await db.sortedSetAddBulk(topicData.map(t => ([`cid:${t.cid}:tids:views`, t.viewcount || 0, t.tid]))); + progress.incr(tids.length); + }, { + batch: 500, + progress: progress, + }); + }, +}; diff --git a/src/upgrades/1.2.0/edit_delete_deletetopic_privileges.js b/src/upgrades/1.2.0/edit_delete_deletetopic_privileges.js index 0ebfd4b274..a6aa26ba4d 100644 --- a/src/upgrades/1.2.0/edit_delete_deletetopic_privileges.js +++ b/src/upgrades/1.2.0/edit_delete_deletetopic_privileges.js @@ -1,107 +1,52 @@ -'use strict'; +/* eslint-disable no-await-in-loop */ +'use strict'; -const async = require('async'); const winston = require('winston'); const db = require('../../database'); module.exports = { name: 'Granting edit/delete/delete topic on existing categories', timestamp: Date.UTC(2016, 7, 7), - method: function (callback) { + method: async function () { const groupsAPI = require('../../groups'); const privilegesAPI = require('../../privileges'); - db.getSortedSetRange('categories:cid', 0, -1, (err, cids) => { - if (err) { - return callback(err); + const cids = await db.getSortedSetRange('categories:cid', 0, -1); + + for (const cid of cids) { + const data = await privilegesAPI.categories.list(cid); + const { groups, users } = data; + + for (const group of groups) { + if (group.privileges['groups:topics:reply']) { + await Promise.all([ + groupsAPI.join(`cid:${cid}:privileges:groups:posts:edit`, group.name), + groupsAPI.join(`cid:${cid}:privileges:groups:posts:delete`, group.name), + ]); + winston.verbose(`cid:${cid}:privileges:groups:posts:edit, cid:${cid}:privileges:groups:posts:delete granted to gid: ${group.name}`); + } + + if (group.privileges['groups:topics:create']) { + await groupsAPI.join(`cid:${cid}:privileges:groups:topics:delete`, group.name); + winston.verbose(`cid:${cid}:privileges:groups:topics:delete granted to gid: ${group.name}`); + } } - async.eachSeries(cids, (cid, next) => { - privilegesAPI.categories.list(cid, (err, data) => { - if (err) { - return next(err); - } - - const { groups } = data; - const { users } = data; - - async.waterfall([ - function (next) { - async.eachSeries(groups, (group, next) => { - if (group.privileges['groups:topics:reply']) { - return async.parallel([ - async.apply(groupsAPI.join, `cid:${cid}:privileges:groups:posts:edit`, group.name), - async.apply(groupsAPI.join, `cid:${cid}:privileges:groups:posts:delete`, group.name), - ], (err) => { - if (!err) { - winston.verbose(`cid:${cid}:privileges:groups:posts:edit, cid:${cid}:privileges:groups:posts:delete granted to gid: ${group.name}`); - } - - return next(err); - }); - } - - next(null); - }, next); - }, - function (next) { - async.eachSeries(groups, (group, next) => { - if (group.privileges['groups:topics:create']) { - return groupsAPI.join(`cid:${cid}:privileges:groups:topics:delete`, group.name, (err) => { - if (!err) { - winston.verbose(`cid:${cid}:privileges:groups:topics:delete granted to gid: ${group.name}`); - } - - return next(err); - }); - } - - next(null); - }, next); - }, - function (next) { - async.eachSeries(users, (user, next) => { - if (user.privileges['topics:reply']) { - return async.parallel([ - async.apply(groupsAPI.join, `cid:${cid}:privileges:posts:edit`, user.uid), - async.apply(groupsAPI.join, `cid:${cid}:privileges:posts:delete`, user.uid), - ], (err) => { - if (!err) { - winston.verbose(`cid:${cid}:privileges:posts:edit, cid:${cid}:privileges:posts:delete granted to uid: ${user.uid}`); - } - - return next(err); - }); - } - - next(null); - }, next); - }, - function (next) { - async.eachSeries(users, (user, next) => { - if (user.privileges['topics:create']) { - return groupsAPI.join(`cid:${cid}:privileges:topics:delete`, user.uid, (err) => { - if (!err) { - winston.verbose(`cid:${cid}:privileges:topics:delete granted to uid: ${user.uid}`); - } - - return next(err); - }); - } - - next(null); - }, next); - }, - ], (err) => { - if (!err) { - winston.verbose(`-- cid ${cid} upgraded`); - } - - next(err); - }); - }); - }, callback); - }); + for (const user of users) { + if (user.privileges['topics:reply']) { + await Promise.all([ + groupsAPI.join(`cid:${cid}:privileges:posts:edit`, user.uid), + groupsAPI.join(`cid:${cid}:privileges:posts:delete`, user.uid), + ]); + winston.verbose(`cid:${cid}:privileges:posts:edit, cid:${cid}:privileges:posts:delete granted to uid: ${user.uid}`); + } + if (user.privileges['topics:create']) { + await groupsAPI.join(`cid:${cid}:privileges:topics:delete`, user.uid); + winston.verbose(`cid:${cid}:privileges:topics:delete granted to uid: ${user.uid}`); + } + } + winston.verbose(`-- cid ${cid} upgraded`); + } }, }; diff --git a/src/upgrades/1.3.0/favourites_to_bookmarks.js b/src/upgrades/1.3.0/favourites_to_bookmarks.js index 782b4774db..a08aa6a5d3 100644 --- a/src/upgrades/1.3.0/favourites_to_bookmarks.js +++ b/src/upgrades/1.3.0/favourites_to_bookmarks.js @@ -1,56 +1,39 @@ 'use strict'; -const async = require('async'); const db = require('../../database'); - module.exports = { name: 'Favourites to Bookmarks', timestamp: Date.UTC(2016, 9, 8), - method: function (callback) { + method: async function () { const { progress } = this; + const batch = require('../../batch'); - function upgradePosts(next) { - const batch = require('../../batch'); - - batch.processSortedSet('posts:pid', (ids, next) => { - async.each(ids, (id, next) => { + async function upgradePosts() { + await batch.processSortedSet('posts:pid', async (ids) => { + await Promise.all(ids.map(async (id) => { progress.incr(); - - async.waterfall([ - function (next) { - db.rename(`pid:${id}:users_favourited`, `pid:${id}:users_bookmarked`, next); - }, - function (next) { - db.getObjectField(`post:${id}`, 'reputation', next); - }, - function (reputation, next) { - if (parseInt(reputation, 10)) { - db.setObjectField(`post:${id}`, 'bookmarks', reputation, next); - } else { - next(); - } - }, - function (next) { - db.deleteObjectField(`post:${id}`, 'reputation', next); - }, - ], next); - }, next); + await db.rename(`pid:${id}:users_favourited`, `pid:${id}:users_bookmarked`); + const reputation = await db.getObjectField(`post:${id}`, 'reputation'); + if (parseInt(reputation, 10)) { + await db.setObjectField(`post:${id}`, 'bookmarks', reputation); + } + await db.deleteObjectField(`post:${id}`, 'reputation'); + })); }, { progress: progress, - }, next); + }); } - function upgradeUsers(next) { - const batch = require('../../batch'); - - batch.processSortedSet('users:joindate', (ids, next) => { - async.each(ids, (id, next) => { - db.rename(`uid:${id}:favourites`, `uid:${id}:bookmarks`, next); - }, next); - }, {}, next); + async function upgradeUsers() { + await batch.processSortedSet('users:joindate', async (ids) => { + await Promise.all(ids.map(async (id) => { + await db.rename(`uid:${id}:favourites`, `uid:${id}:bookmarks`); + })); + }, {}); } - async.series([upgradePosts, upgradeUsers], callback); + await upgradePosts(); + await upgradeUsers(); }, }; diff --git a/src/upgrades/1.4.0/global_and_user_language_keys.js b/src/upgrades/1.4.0/global_and_user_language_keys.js index f565d6c423..e18442e8f8 100644 --- a/src/upgrades/1.4.0/global_and_user_language_keys.js +++ b/src/upgrades/1.4.0/global_and_user_language_keys.js @@ -1,57 +1,37 @@ 'use strict'; -const async = require('async'); const db = require('../../database'); - module.exports = { name: 'Update global and user language keys', timestamp: Date.UTC(2016, 10, 22), - method: function (callback) { + method: async function () { + const { progress } = this; const user = require('../../user'); const meta = require('../../meta'); const batch = require('../../batch'); - let newLanguage; - async.parallel([ - function (next) { - meta.configs.get('defaultLang', (err, defaultLang) => { - if (err) { - return next(err); - } - if (!defaultLang) { - return setImmediate(next); - } + const defaultLang = await meta.configs.get('defaultLang'); + if (defaultLang) { + const newLanguage = defaultLang.replace('_', '-').replace('@', '-x-'); + if (newLanguage !== defaultLang) { + await meta.configs.set('defaultLang', newLanguage); + } + } - newLanguage = defaultLang.replace('_', '-').replace('@', '-x-'); - if (newLanguage !== defaultLang) { - meta.configs.set('defaultLang', newLanguage, next); - } else { - setImmediate(next); + await batch.processSortedSet('users:joindate', async (ids) => { + await Promise.all(ids.map(async (uid) => { + progress.incr(); + const language = await db.getObjectField(`user:${uid}:settings`, 'userLang'); + if (language) { + const newLanguage = language.replace('_', '-').replace('@', '-x-'); + if (newLanguage !== language) { + await user.setSetting(uid, 'userLang', newLanguage); } - }); - }, - function (next) { - batch.processSortedSet('users:joindate', (ids, next) => { - async.each(ids, (uid, next) => { - async.waterfall([ - async.apply(db.getObjectField, `user:${uid}:settings`, 'userLang'), - function (language, next) { - if (!language) { - return setImmediate(next); - } - - newLanguage = language.replace('_', '-').replace('@', '-x-'); - if (newLanguage !== language) { - user.setSetting(uid, 'userLang', newLanguage, next); - } else { - setImmediate(next); - } - }, - ], next); - }, next); - }, next); - }, - ], callback); + } + })); + }, { + progress: progress, + }); }, }; diff --git a/src/upgrades/1.4.4/config_urls_update.js b/src/upgrades/1.4.4/config_urls_update.js index 5bf80c14fa..97b3c95670 100644 --- a/src/upgrades/1.4.4/config_urls_update.js +++ b/src/upgrades/1.4.4/config_urls_update.js @@ -1,36 +1,34 @@ 'use strict'; -const async = require('async'); -const db = require('../../database'); +const db = require('../../database'); module.exports = { name: 'Upgrading config urls to use assets route', timestamp: Date.UTC(2017, 1, 28), - method: function (callback) { - async.waterfall([ - function (cb) { - db.getObject('config', cb); - }, - function (config, cb) { - if (!config) { - return cb(); + method: async function () { + const config = await db.getObject('config'); + if (config) { + const keys = [ + 'brand:favicon', + 'brand:touchicon', + 'og:image', + 'brand:logo:url', + 'defaultAvatar', + 'profile:defaultCovers', + ]; + + keys.forEach((key) => { + const oldValue = config[key]; + + if (!oldValue || typeof oldValue !== 'string') { + return; } - const keys = ['brand:favicon', 'brand:touchicon', 'og:image', 'brand:logo:url', 'defaultAvatar', 'profile:defaultCovers']; - - keys.forEach((key) => { - const oldValue = config[key]; - - if (!oldValue || typeof oldValue !== 'string') { - return; - } - - config[key] = oldValue.replace(/(?:\/assets)?\/(images|uploads)\//g, '/assets/$1/'); - }); + config[key] = oldValue.replace(/(?:\/assets)?\/(images|uploads)\//g, '/assets/$1/'); + }); - db.setObject('config', config, cb); - }, - ], callback); + await db.setObject('config', config); + } }, }; diff --git a/src/upgrades/1.4.6/delete_sessions.js b/src/upgrades/1.4.6/delete_sessions.js index a257da1725..dc3a1f465c 100644 --- a/src/upgrades/1.4.6/delete_sessions.js +++ b/src/upgrades/1.4.6/delete_sessions.js @@ -1,7 +1,5 @@ 'use strict'; -const async = require('async'); - const nconf = require('nconf'); const db = require('../../database'); const batch = require('../../batch'); @@ -9,7 +7,7 @@ const batch = require('../../batch'); module.exports = { name: 'Delete accidentally long-lived sessions', timestamp: Date.UTC(2017, 3, 16), - method: function (callback) { + method: async function () { let configJSON; try { configJSON = require('../../../config.json') || { [process.env.database]: true }; @@ -20,44 +18,24 @@ module.exports = { const isRedisSessionStore = configJSON.hasOwnProperty('redis'); const { progress } = this; - async.waterfall([ - function (next) { - if (isRedisSessionStore) { - const connection = require('../../database/redis/connection'); - let client; - async.waterfall([ - function (next) { - connection.connect(nconf.get('redis'), next); - }, - function (_client, next) { - client = _client; - client.keys('sess:*', next); - }, - function (sessionKeys, next) { - progress.total = sessionKeys.length; + if (isRedisSessionStore) { + const connection = require('../../database/redis/connection'); + const client = await connection.connect(nconf.get('redis')); + const sessionKeys = await client.keys('sess:*'); + progress.total = sessionKeys.length; - batch.processArray(sessionKeys, (keys, next) => { - const multi = client.multi(); - keys.forEach((key) => { - progress.incr(); - multi.del(key); - }); - multi.exec(next); - }, { - batch: 1000, - }, next); - }, - ], (err) => { - next(err); - }); - } else if (db.client && db.client.collection) { - db.client.collection('sessions').deleteMany({}, {}, (err) => { - next(err); - }); - } else { - next(); - } - }, - ], callback); + await batch.processArray(sessionKeys, async (keys) => { + const multi = client.multi(); + keys.forEach((key) => { + progress.incr(); + multi.del(key); + }); + await multi.exec(); + }, { + batch: 1000, + }); + } else if (db.client && db.client.collection) { + await db.client.collection('sessions').deleteMany({}, {}); + } }, }; diff --git a/src/upgrades/1.5.0/flags_refactor.js b/src/upgrades/1.5.0/flags_refactor.js index f16d9a5224..dfbb28d70d 100644 --- a/src/upgrades/1.5.0/flags_refactor.js +++ b/src/upgrades/1.5.0/flags_refactor.js @@ -1,88 +1,56 @@ 'use strict'; -const async = require('async'); const db = require('../../database'); - module.exports = { name: 'Migrating flags to new schema', timestamp: Date.UTC(2016, 11, 7), - method: function (callback) { + method: async function () { const batch = require('../../batch'); const posts = require('../../posts'); const flags = require('../../flags'); const { progress } = this; - batch.processSortedSet('posts:pid', (ids, next) => { - posts.getPostsByPids(ids, 1, (err, posts) => { - if (err) { - return next(err); - } - - posts = posts.filter(post => post.hasOwnProperty('flags')); - - async.each(posts, (post, next) => { - progress.incr(); - - async.parallel({ - uids: async.apply(db.getSortedSetRangeWithScores, `pid:${post.pid}:flag:uids`, 0, -1), - reasons: async.apply(db.getSortedSetRange, `pid:${post.pid}:flag:uid:reason`, 0, -1), - }, (err, data) => { - if (err) { - return next(err); + await batch.processSortedSet('posts:pid', async (ids) => { + let postData = await posts.getPostsByPids(ids, 1); + postData = postData.filter(post => post.hasOwnProperty('flags')); + await Promise.all(postData.map(async (post) => { + progress.incr(); + + const [uids, reasons] = await Promise.all([ + db.getSortedSetRangeWithScores(`pid:${post.pid}:flag:uids`, 0, -1), + db.getSortedSetRange(`pid:${post.pid}:flag:uid:reason`, 0, -1), + ]); + + // Adding in another check here in case a post was improperly dismissed (flag count > 1 but no flags in db) + if (uids.length && reasons.length) { + // Just take the first entry + const datetime = uids[0].score; + const reason = reasons[0].split(':')[1]; + + try { + const flagObj = await flags.create('post', post.pid, uids[0].value, reason, datetime); + if (post['flag:state'] || post['flag:assignee']) { + await flags.update(flagObj.flagId, 1, { + state: post['flag:state'], + assignee: post['flag:assignee'], + datetime: datetime, + }); } - - // Adding in another check here in case a post was improperly dismissed (flag count > 1 but no flags in db) - if (!data.uids.length || !data.reasons.length) { - return setImmediate(next); + if (post.hasOwnProperty('flag:notes') && post['flag:notes'].length) { + let history = JSON.parse(post['flag:history']); + history = history.filter(event => event.type === 'notes')[0]; + await flags.appendNote(flagObj.flagId, history.uid, post['flag:notes'], history.timestamp); } - - // Just take the first entry - const datetime = data.uids[0].score; - const reason = data.reasons[0].split(':')[1]; - let flagObj; - - async.waterfall([ - async.apply(flags.create, 'post', post.pid, data.uids[0].value, reason, datetime), - function (_flagObj, next) { - flagObj = _flagObj; - if (post['flag:state'] || post['flag:assignee']) { - flags.update(flagObj.flagId, 1, { - state: post['flag:state'], - assignee: post['flag:assignee'], - datetime: datetime, - }, next); - } else { - setImmediate(next); - } - }, - function (next) { - if (post.hasOwnProperty('flag:notes') && post['flag:notes'].length) { - try { - let history = JSON.parse(post['flag:history']); - history = history.filter(event => event.type === 'notes')[0]; - - flags.appendNote(flagObj.flagId, history.uid, post['flag:notes'], history.timestamp, next); - } catch (e) { - next(e); - } - } else { - setImmediate(next); - } - }, - ], (err) => { - if (err && err.message === '[[error:post-already-flagged]]') { - // Already flagged, no need to parse, but not an error - next(); - } else { - next(err); - } - }); - }); - }, next); - }); + } catch (err) { + if (err.message !== '[[error:post-already-flagged]]') { + throw err; + } + } + } + })); }, { progress: this.progress, - }, callback); + }); }, }; diff --git a/src/upgrades/1.5.0/remove_relative_uploaded_profile_cover.js b/src/upgrades/1.5.0/remove_relative_uploaded_profile_cover.js index 9cb34c5986..769ca20247 100644 --- a/src/upgrades/1.5.0/remove_relative_uploaded_profile_cover.js +++ b/src/upgrades/1.5.0/remove_relative_uploaded_profile_cover.js @@ -1,36 +1,26 @@ 'use strict'; -const async = require('async'); const db = require('../../database'); const batch = require('../../batch'); - module.exports = { name: 'Remove relative_path from uploaded profile cover urls', timestamp: Date.UTC(2017, 3, 26), - method: function (callback) { + method: async function () { const { progress } = this; - batch.processSortedSet('users:joindate', (ids, done) => { - async.each(ids, (uid, cb) => { - async.waterfall([ - function (next) { - db.getObjectField(`user:${uid}`, 'cover:url', next); - }, - function (url, next) { - progress.incr(); - - if (!url) { - return next(); - } + await batch.processSortedSet('users:joindate', async (ids) => { + await Promise.all(ids.map(async (uid) => { + const url = await db.getObjectField(`user:${uid}`, 'cover:url'); + progress.incr(); - const newUrl = url.replace(/^.*?\/uploads\//, '/assets/uploads/'); - db.setObjectField(`user:${uid}`, 'cover:url', newUrl, next); - }, - ], cb); - }, done); + if (url) { + const newUrl = url.replace(/^.*?\/uploads\//, '/assets/uploads/'); + await db.setObjectField(`user:${uid}`, 'cover:url', newUrl); + } + })); }, { progress: this.progress, - }, callback); + }); }, }; diff --git a/src/upgrades/1.6.0/clear-stale-digest-template.js b/src/upgrades/1.6.0/clear-stale-digest-template.js index ff34668691..3bcd11b49c 100644 --- a/src/upgrades/1.6.0/clear-stale-digest-template.js +++ b/src/upgrades/1.6.0/clear-stale-digest-template.js @@ -1,31 +1,21 @@ 'use strict'; - -const async = require('async'); const crypto = require('crypto'); const meta = require('../../meta'); module.exports = { name: 'Clearing stale digest templates that were accidentally saved as custom', timestamp: Date.UTC(2017, 8, 6), - method: function (callback) { + method: async function () { const matches = [ '112e541b40023d6530dd44df4b0d9c5d', // digest @ 75917e25b3b5ad7bed8ed0c36433fb35c9ab33eb '110b8805f70395b0282fd10555059e9f', // digest @ 9b02bb8f51f0e47c6e335578f776ffc17bc03537 '9538e7249edb369b2a25b03f2bd3282b', // digest @ 3314ab4b83138c7ae579ac1f1f463098b8c2d414 ]; - - async.waterfall([ - async.apply(meta.configs.getFields, ['email:custom:digest']), - function (fieldset, next) { - const hash = fieldset['email:custom:digest'] ? crypto.createHash('md5').update(fieldset['email:custom:digest']).digest('hex') : null; - - if (matches.includes(hash)) { - meta.configs.remove('email:custom:digest', next); - } else { - setImmediate(next); - } - }, - ], callback); + const fieldset = await meta.configs.getFields(['email:custom:digest']); + const hash = fieldset['email:custom:digest'] ? crypto.createHash('md5').update(fieldset['email:custom:digest']).digest('hex') : null; + if (matches.includes(hash)) { + await meta.configs.remove('email:custom:digest'); + } }, }; diff --git a/src/upgrades/1.6.0/ipblacklist-fix.js b/src/upgrades/1.6.0/ipblacklist-fix.js index 7e3f13d506..000de231ba 100644 --- a/src/upgrades/1.6.0/ipblacklist-fix.js +++ b/src/upgrades/1.6.0/ipblacklist-fix.js @@ -1,25 +1,13 @@ 'use strict'; -const async = require('async'); - const db = require('../../database'); module.exports = { name: 'Changing ip blacklist storage to object', timestamp: Date.UTC(2017, 8, 7), - method: function (callback) { - let rules; - async.waterfall([ - function (next) { - db.get('ip-blacklist-rules', next); - }, - function (_rules, next) { - rules = _rules; - db.delete('ip-blacklist-rules', rules ? next : callback); - }, - function (next) { - db.setObject('ip-blacklist-rules', { rules: rules }, next); - }, - ], callback); + method: async function () { + const rules = await db.get('ip-blacklist-rules'); + await db.delete('ip-blacklist-rules'); + await db.setObject('ip-blacklist-rules', { rules: rules }); }, }; diff --git a/src/upgrades/1.6.0/robots-config-change.js b/src/upgrades/1.6.0/robots-config-change.js index 7efc789357..7e787389e7 100644 --- a/src/upgrades/1.6.0/robots-config-change.js +++ b/src/upgrades/1.6.0/robots-config-change.js @@ -1,35 +1,21 @@ 'use strict'; -const async = require('async'); const db = require('../../database'); module.exports = { name: 'Fix incorrect robots.txt schema', timestamp: Date.UTC(2017, 6, 10), - method: function (callback) { - async.waterfall([ - function (next) { - db.getObject('config', next); - }, - function (config, next) { - if (!config) { - return callback(); - } - // fix mongo nested data - if (config.robots && config.robots.txt) { - db.setObjectField('config', 'robots:txt', config.robots.txt, next); - } else if (typeof config['robots.txt'] === 'string' && config['robots.txt']) { - db.setObjectField('config', 'robots:txt', config['robots.txt'], next); - } else { - next(); - } - }, - function (next) { - db.deleteObjectField('config', 'robots', next); - }, - function (next) { - db.deleteObjectField('config', 'robots.txt', next); - }, - ], callback); + method: async function () { + const config = await db.getObject('config'); + if (config) { + // fix mongo nested data + if (config.robots && config.robots.txt) { + await db.setObjectField('config', 'robots:txt', config.robots.txt); + } else if (typeof config['robots.txt'] === 'string' && config['robots.txt']) { + await db.setObjectField('config', 'robots:txt', config['robots.txt']); + } + await db.deleteObjectField('config', 'robots'); + await db.deleteObjectField('config', 'robots.txt'); + } }, }; diff --git a/src/upgrades/1.7.1/notification-settings.js b/src/upgrades/1.7.1/notification-settings.js index e7a455feb9..fed592effb 100644 --- a/src/upgrades/1.7.1/notification-settings.js +++ b/src/upgrades/1.7.1/notification-settings.js @@ -1,48 +1,31 @@ 'use strict'; -const async = require('async'); const batch = require('../../batch'); const db = require('../../database'); module.exports = { name: 'Convert old notification digest settings', timestamp: Date.UTC(2017, 10, 15), - method: function (callback) { + method: async function () { const { progress } = this; - batch.processSortedSet('users:joindate', (uids, next) => { - async.eachLimit(uids, 500, (uid, next) => { + await batch.processSortedSet('users:joindate', async (uids) => { + await Promise.all(uids.map(async (uid) => { progress.incr(); - async.waterfall([ - function (next) { - db.getObjectFields(`user:${uid}:settings`, ['sendChatNotifications', 'sendPostNotifications'], next); - }, - function (userSettings, _next) { - if (!userSettings) { - return next(); - } - const tasks = []; - if (parseInt(userSettings.sendChatNotifications, 10) === 1) { - tasks.push(async.apply(db.setObjectField, `user:${uid}:settings`, 'notificationType_new-chat', 'notificationemail')); - } - if (parseInt(userSettings.sendPostNotifications, 10) === 1) { - tasks.push(async.apply(db.setObjectField, `user:${uid}:settings`, 'notificationType_new-reply', 'notificationemail')); - } - if (!tasks.length) { - return next(); - } - - async.series(tasks, (err) => { - _next(err); - }); - }, - function (next) { - db.deleteObjectFields(`user:${uid}:settings`, ['sendChatNotifications', 'sendPostNotifications'], next); - }, - ], next); - }, next); + const userSettings = await db.getObjectFields(`user:${uid}:settings`, ['sendChatNotifications', 'sendPostNotifications']); + if (userSettings) { + if (parseInt(userSettings.sendChatNotifications, 10) === 1) { + await db.setObjectField(`user:${uid}:settings`, 'notificationType_new-chat', 'notificationemail'); + } + if (parseInt(userSettings.sendPostNotifications, 10) === 1) { + await db.setObjectField(`user:${uid}:settings`, 'notificationType_new-reply', 'notificationemail'); + } + } + await db.deleteObjectFields(`user:${uid}:settings`, ['sendChatNotifications', 'sendPostNotifications']); + })); }, { progress: progress, - }, callback); + batch: 500, + }); }, }; diff --git a/src/upgrades/1.7.3/key_value_schema_change.js b/src/upgrades/1.7.3/key_value_schema_change.js index 997db02551..0fb38f4c44 100644 --- a/src/upgrades/1.7.3/key_value_schema_change.js +++ b/src/upgrades/1.7.3/key_value_schema_change.js @@ -1,13 +1,13 @@ -'use strict'; +/* eslint-disable no-await-in-loop */ -const async = require('async'); +'use strict'; const db = require('../../database'); module.exports = { name: 'Change the schema of simple keys so they don\'t use value field (mongodb only)', timestamp: Date.UTC(2017, 11, 18), - method: function (callback) { + method: async function () { let configJSON; try { configJSON = require('../../../config.json') || { [process.env.database]: true, database: process.env.database }; @@ -17,56 +17,29 @@ module.exports = { const isMongo = configJSON.hasOwnProperty('mongo') && configJSON.database === 'mongo'; const { progress } = this; if (!isMongo) { - return callback(); + return; } const { client } = db; - let cursor; - async.waterfall([ - function (next) { - client.collection('objects').countDocuments({ - _key: { $exists: true }, - value: { $exists: true }, - score: { $exists: false }, - }, next); - }, - function (count, next) { - progress.total = count; - cursor = client.collection('objects').find({ - _key: { $exists: true }, - value: { $exists: true }, - score: { $exists: false }, - }).batchSize(1000); + const query = { + _key: { $exists: true }, + value: { $exists: true }, + score: { $exists: false }, + }; + progress.total = await client.collection('objects').countDocuments(query); + const cursor = await client.collection('objects').find(query).batchSize(1000); - let done = false; - async.whilst( - (next) => { - next(null, !done); - }, - (next) => { - async.waterfall([ - function (next) { - cursor.next(next); - }, - function (item, next) { - progress.incr(); - if (item === null) { - done = true; - return next(); - } - delete item.expireAt; - if (Object.keys(item).length === 3 && item.hasOwnProperty('_key') && item.hasOwnProperty('value')) { - client.collection('objects').updateOne({ _key: item._key }, { $rename: { value: 'data' } }, next); - } else { - next(); - } - }, - ], (err) => { - next(err); - }); - }, - next - ); - }, - ], callback); + let done = false; + while (!done) { + const item = await cursor.next(); + progress.incr(); + if (item === null) { + done = true; + } else { + delete item.expireAt; + if (Object.keys(item).length === 3 && item.hasOwnProperty('_key') && item.hasOwnProperty('value')) { + await client.collection('objects').updateOne({ _key: item._key }, { $rename: { value: 'data' } }); + } + } + } }, }; diff --git a/src/upgrades/1.7.3/topic_votes.js b/src/upgrades/1.7.3/topic_votes.js index f7c5d0c122..008aaece0a 100644 --- a/src/upgrades/1.7.3/topic_votes.js +++ b/src/upgrades/1.7.3/topic_votes.js @@ -1,34 +1,22 @@ 'use strict'; -const async = require('async'); + const batch = require('../../batch'); const db = require('../../database'); module.exports = { name: 'Add votes to topics', timestamp: Date.UTC(2017, 11, 8), - method: function (callback) { + method: async function () { const { progress } = this; - batch.processSortedSet('topics:tid', (tids, next) => { - async.eachLimit(tids, 500, (tid, _next) => { + batch.processSortedSet('topics:tid', async (tids) => { + await Promise.all(tids.map(async (tid) => { progress.incr(); - let topicData; - async.waterfall([ - function (next) { - db.getObjectFields(`topic:${tid}`, ['mainPid', 'cid', 'pinned'], next); - }, - function (_topicData, next) { - topicData = _topicData; - if (!topicData.mainPid || !topicData.cid) { - return _next(); - } - db.getObject(`post:${topicData.mainPid}`, next); - }, - function (postData, next) { - if (!postData) { - return _next(); - } + const topicData = await db.getObjectFields(`topic:${tid}`, ['mainPid', 'cid', 'pinned']); + if (topicData.mainPid && topicData.cid) { + const postData = await db.getObject(`post:${topicData.mainPid}`); + if (postData) { const upvotes = parseInt(postData.upvotes, 10) || 0; const downvotes = parseInt(postData.downvotes, 10) || 0; const data = { @@ -36,29 +24,19 @@ module.exports = { downvotes: downvotes, }; const votes = upvotes - downvotes; - async.parallel([ - function (next) { - db.setObject(`topic:${tid}`, data, next); - }, - function (next) { - db.sortedSetAdd('topics:votes', votes, tid, next); - }, - function (next) { - if (parseInt(topicData.pinned, 10) !== 1) { - db.sortedSetAdd(`cid:${topicData.cid}:tids:votes`, votes, tid, next); - } else { - next(); - } - }, - ], (err) => { - next(err); - }); - }, - ], _next); - }, next); + await Promise.all([ + db.setObject(`topic:${tid}`, data), + db.sortedSetAdd('topics:votes', votes, tid), + ]); + if (parseInt(topicData.pinned, 10) !== 1) { + await db.sortedSetAdd(`cid:${topicData.cid}:tids:votes`, votes, tid); + } + } + } + })); }, { progress: progress, batch: 500, - }, callback); + }); }, }; diff --git a/src/upgrades/1.7.4/fix_moved_topics_byvotes.js b/src/upgrades/1.7.4/fix_moved_topics_byvotes.js index bb183557ef..33aafcb70d 100644 --- a/src/upgrades/1.7.4/fix_moved_topics_byvotes.js +++ b/src/upgrades/1.7.4/fix_moved_topics_byvotes.js @@ -1,53 +1,31 @@ 'use strict'; -const async = require('async'); const batch = require('../../batch'); const db = require('../../database'); module.exports = { name: 'Fix sort by votes for moved topics', timestamp: Date.UTC(2018, 0, 8), - method: function (callback) { + method: async function () { const { progress } = this; - batch.processSortedSet('topics:tid', (tids, next) => { - async.eachLimit(tids, 500, (tid, _next) => { + await batch.processSortedSet('topics:tid', async (tids) => { + await Promise.all(tids.map(async (tid) => { progress.incr(); - let topicData; - async.waterfall([ - function (next) { - db.getObjectFields(`topic:${tid}`, ['cid', 'oldCid', 'upvotes', 'downvotes', 'pinned'], next); - }, - function (_topicData, next) { - topicData = _topicData; - if (!topicData.cid || !topicData.oldCid) { - return _next(); - } - - const upvotes = parseInt(topicData.upvotes, 10) || 0; - const downvotes = parseInt(topicData.downvotes, 10) || 0; - const votes = upvotes - downvotes; - - async.series([ - function (next) { - db.sortedSetRemove(`cid:${topicData.oldCid}:tids:votes`, tid, next); - }, - function (next) { - if (parseInt(topicData.pinned, 10) !== 1) { - db.sortedSetAdd(`cid:${topicData.cid}:tids:votes`, votes, tid, next); - } else { - next(); - } - }, - ], (err) => { - next(err); - }); - }, - ], _next); - }, next); + const topicData = await db.getObjectFields(`topic:${tid}`, ['cid', 'oldCid', 'upvotes', 'downvotes', 'pinned']); + if (topicData.cid && topicData.oldCid) { + const upvotes = parseInt(topicData.upvotes, 10) || 0; + const downvotes = parseInt(topicData.downvotes, 10) || 0; + const votes = upvotes - downvotes; + await db.sortedSetRemove(`cid:${topicData.oldCid}:tids:votes`, tid); + if (parseInt(topicData.pinned, 10) !== 1) { + await db.sortedSetAdd(`cid:${topicData.cid}:tids:votes`, votes, tid); + } + } + })); }, { progress: progress, batch: 500, - }, callback); + }); }, }; diff --git a/src/upgrades/1.7.4/fix_user_topics_per_category.js b/src/upgrades/1.7.4/fix_user_topics_per_category.js index 73b6671864..5ee19fb41f 100644 --- a/src/upgrades/1.7.4/fix_user_topics_per_category.js +++ b/src/upgrades/1.7.4/fix_user_topics_per_category.js @@ -1,52 +1,29 @@ 'use strict'; -const async = require('async'); const batch = require('../../batch'); const db = require('../../database'); module.exports = { name: 'Fix topics in categories per user if they were moved', timestamp: Date.UTC(2018, 0, 22), - method: function (callback) { + method: async function () { const { progress } = this; - batch.processSortedSet('topics:tid', (tids, next) => { - async.eachLimit(tids, 500, (tid, _next) => { + await batch.processSortedSet('topics:tid', async (tids) => { + await Promise.all(tids.map(async (tid) => { progress.incr(); - let topicData; - async.waterfall([ - function (next) { - db.getObjectFields(`topic:${tid}`, ['cid', 'tid', 'uid', 'oldCid', 'timestamp'], next); - }, - function (_topicData, next) { - topicData = _topicData; - if (!topicData.cid || !topicData.oldCid) { - return _next(); - } - - db.isSortedSetMember(`cid:${topicData.oldCid}:uid:${topicData.uid}`, topicData.tid, next); - }, - function (isMember, next) { - if (isMember) { - async.series([ - function (next) { - db.sortedSetRemove(`cid:${topicData.oldCid}:uid:${topicData.uid}:tids`, tid, next); - }, - function (next) { - db.sortedSetAdd(`cid:${topicData.cid}:uid:${topicData.uid}:tids`, topicData.timestamp, tid, next); - }, - ], (err) => { - next(err); - }); - } else { - next(); - } - }, - ], _next); - }, next); + const topicData = await db.getObjectFields(`topic:${tid}`, ['cid', 'tid', 'uid', 'oldCid', 'timestamp']); + if (topicData.cid && topicData.oldCid) { + const isMember = await db.isSortedSetMember(`cid:${topicData.oldCid}:uid:${topicData.uid}`, topicData.tid); + if (isMember) { + await db.sortedSetRemove(`cid:${topicData.oldCid}:uid:${topicData.uid}:tids`, tid); + await db.sortedSetAdd(`cid:${topicData.cid}:uid:${topicData.uid}:tids`, topicData.timestamp, tid); + } + } + })); }, { progress: progress, batch: 500, - }, callback); + }); }, }; diff --git a/src/upgrades/1.7.6/flatten_navigation_data.js b/src/upgrades/1.7.6/flatten_navigation_data.js index e5cb5932b8..96dc6408ae 100644 --- a/src/upgrades/1.7.6/flatten_navigation_data.js +++ b/src/upgrades/1.7.6/flatten_navigation_data.js @@ -1,38 +1,24 @@ 'use strict'; -const async = require('async'); const db = require('../../database'); module.exports = { name: 'Flatten navigation data', timestamp: Date.UTC(2018, 1, 17), - method: function (callback) { - async.waterfall([ - function (next) { - db.getSortedSetRangeWithScores('navigation:enabled', 0, -1, next); - }, - function (data, next) { - const order = []; - const items = []; - data.forEach((item) => { - let navItem = JSON.parse(item.value); - const keys = Object.keys(navItem); - if (keys.length && parseInt(keys[0], 10) >= 0) { - navItem = navItem[keys[0]]; - } - order.push(item.score); - items.push(JSON.stringify(navItem)); - }); - - async.series([ - function (next) { - db.delete('navigation:enabled', next); - }, - function (next) { - db.sortedSetAdd('navigation:enabled', order, items, next); - }, - ], next); - }, - ], callback); + method: async function () { + const data = await db.getSortedSetRangeWithScores('navigation:enabled', 0, -1); + const order = []; + const items = []; + data.forEach((item) => { + let navItem = JSON.parse(item.value); + const keys = Object.keys(navItem); + if (keys.length && parseInt(keys[0], 10) >= 0) { + navItem = navItem[keys[0]]; + } + order.push(item.score); + items.push(JSON.stringify(navItem)); + }); + await db.delete('navigation:enabled'); + await db.sortedSetAdd('navigation:enabled', order, items); }, }; diff --git a/src/upgrades/1.7.6/notification_types.js b/src/upgrades/1.7.6/notification_types.js index 8d022a157b..42d59cdd38 100644 --- a/src/upgrades/1.7.6/notification_types.js +++ b/src/upgrades/1.7.6/notification_types.js @@ -1,28 +1,21 @@ 'use strict'; -const async = require('async'); const db = require('../../database'); module.exports = { name: 'Add default settings for notification delivery types', timestamp: Date.UTC(2018, 1, 14), - method: function (callback) { - async.waterfall([ - function (next) { - db.getObject('config', next); - }, - function (config, next) { - const postNotifications = parseInt(config.sendPostNotifications, 10) === 1 ? 'notification' : 'none'; - const chatNotifications = parseInt(config.sendChatNotifications, 10) === 1 ? 'notification' : 'none'; - db.setObject('config', { - notificationType_upvote: config.notificationType_upvote || 'notification', - 'notificationType_new-topic': config['notificationType_new-topic'] || 'notification', - 'notificationType_new-reply': config['notificationType_new-reply'] || postNotifications, - notificationType_follow: config.notificationType_follow || 'notification', - 'notificationType_new-chat': config['notificationType_new-chat'] || chatNotifications, - 'notificationType_group-invite': config['notificationType_group-invite'] || 'notification', - }, next); - }, - ], callback); + method: async function () { + const config = await db.getObject('config'); + const postNotifications = parseInt(config.sendPostNotifications, 10) === 1 ? 'notification' : 'none'; + const chatNotifications = parseInt(config.sendChatNotifications, 10) === 1 ? 'notification' : 'none'; + await db.setObject('config', { + notificationType_upvote: config.notificationType_upvote || 'notification', + 'notificationType_new-topic': config['notificationType_new-topic'] || 'notification', + 'notificationType_new-reply': config['notificationType_new-reply'] || postNotifications, + notificationType_follow: config.notificationType_follow || 'notification', + 'notificationType_new-chat': config['notificationType_new-chat'] || chatNotifications, + 'notificationType_group-invite': config['notificationType_group-invite'] || 'notification', + }); }, }; diff --git a/src/upgrades/1.7.6/update_min_pass_strength.js b/src/upgrades/1.7.6/update_min_pass_strength.js index 5afdb4f3ec..b207e0c166 100644 --- a/src/upgrades/1.7.6/update_min_pass_strength.js +++ b/src/upgrades/1.7.6/update_min_pass_strength.js @@ -1,22 +1,14 @@ 'use strict'; -const async = require('async'); const db = require('../../database'); - module.exports = { name: 'Revising minimum password strength to 1 (from 0)', timestamp: Date.UTC(2018, 1, 21), - method: function (callback) { - async.waterfall([ - async.apply(db.getObjectField.bind(db), 'config', 'minimumPasswordStrength'), - function (strength, next) { - if (!strength) { - return db.setObjectField('config', 'minimumPasswordStrength', 1, next); - } - - setImmediate(next); - }, - ], callback); + method: async function () { + const strength = await db.getObjectField('config', 'minimumPasswordStrength'); + if (!strength) { + await db.setObjectField('config', 'minimumPasswordStrength', 1); + } }, }; diff --git a/src/user/approval.js b/src/user/approval.js index 639315f00d..24e84fba64 100644 --- a/src/user/approval.js +++ b/src/user/approval.js @@ -43,8 +43,8 @@ module.exports = function (User) { } const keys = usernames.filter(Boolean).map(username => `registration:queue:name:${username}`); const data = await db.getObjectsFields(keys, ['email']); - const emails = data.map(data => data && data.email); - if (emails.includes(userData.email)) { + const emails = data.map(data => data && data.email).filter(Boolean); + if (userData.email && emails.includes(userData.email)) { throw new Error('[[error:email-taken]]'); } } @@ -80,8 +80,8 @@ module.exports = function (User) { template: 'registration_accepted', uid: uid, }).catch(err => winston.error(`[emailer.send] ${err.stack}`)); - const total = await db.incrObjectField('registration:queue:approval:times', 'totalTime', Math.floor((Date.now() - creation_time) / 60000)); - const counter = await db.incrObjectField('registration:queue:approval:times', 'counter', 1); + const total = await db.incrObjectFieldBy('registration:queue:approval:times', 'totalTime', Math.floor((Date.now() - creation_time) / 60000)); + const counter = await db.incrObjectField('registration:queue:approval:times', 'counter'); await db.setObjectField('registration:queue:approval:times', 'average', total / counter); return uid; }; diff --git a/src/user/blocks.js b/src/user/blocks.js index 7980efc64d..7eef264e20 100644 --- a/src/user/blocks.js +++ b/src/user/blocks.js @@ -1,24 +1,25 @@ 'use strict'; -const async = require('async'); -const LRU = require('lru-cache'); - const db = require('../database'); -const pubsub = require('../pubsub'); const plugins = require('../plugins'); +const cacheCreate = require('../cacheCreate'); module.exports = function (User) { User.blocks = { - _cache: new LRU({ + _cache: cacheCreate({ + name: 'user:blocks', max: 100, length: function () { return 1; }, maxAge: 0, }), }; - User.blocks.is = async function (targetUid, uid) { - const blocks = await User.blocks.list(uid); - return blocks.includes(parseInt(targetUid, 10)); + User.blocks.is = async function (targetUid, uids) { + const isArray = Array.isArray(uids); + uids = isArray ? uids : [uids]; + const blocks = await User.blocks.list(uids); + const isBlocked = uids.map((uid, index) => blocks[index] && blocks[index].includes(parseInt(targetUid, 10))); + return isArray ? isBlocked : isBlocked[0]; }; User.blocks.can = async function (callerUid, blockerUid, blockeeUid) { @@ -43,27 +44,27 @@ module.exports = function (User) { } }; - User.blocks.list = async function (uid) { - if (User.blocks._cache.has(parseInt(uid, 10))) { - return User.blocks._cache.get(parseInt(uid, 10)); + User.blocks.list = async function (uids) { + const isArray = Array.isArray(uids); + uids = (isArray ? uids : [uids]).map(uid => parseInt(uid, 10)); + const cachedData = {}; + const unCachedUids = User.blocks._cache.getUnCachedKeys(uids, cachedData); + if (unCachedUids.length) { + const unCachedData = await db.getSortedSetsMembers(unCachedUids.map(uid => `uid:${uid}:blocked_uids`)); + unCachedUids.forEach((uid, index) => { + cachedData[uid] = (unCachedData[index] || []).map(uid => parseInt(uid, 10)); + User.blocks._cache.set(uid, cachedData[uid]); + }); } - - let blocked = await db.getSortedSetRange(`uid:${uid}:blocked_uids`, 0, -1); - blocked = blocked.map(uid => parseInt(uid, 10)).filter(Boolean); - User.blocks._cache.set(parseInt(uid, 10), blocked); - return blocked; + const result = uids.map(uid => cachedData[uid] || []); + return isArray ? result.slice() : result[0]; }; - pubsub.on('user:blocks:cache:del', (uid) => { - User.blocks._cache.del(uid); - }); - User.blocks.add = async function (targetUid, uid) { await User.blocks.applyChecks('block', targetUid, uid); await db.sortedSetAdd(`uid:${uid}:blocked_uids`, Date.now(), targetUid); await User.incrementUserFieldBy(uid, 'blocksCount', 1); User.blocks._cache.del(parseInt(uid, 10)); - pubsub.publish('user:blocks:cache:del', parseInt(uid, 10)); plugins.hooks.fire('action:user.blocks.add', { uid: uid, targetUid: targetUid }); }; @@ -72,7 +73,6 @@ module.exports = function (User) { await db.sortedSetRemove(`uid:${uid}:blocked_uids`, targetUid); await User.decrementUserFieldBy(uid, 'blocksCount', 1); User.blocks._cache.del(parseInt(uid, 10)); - pubsub.publish('user:blocks:cache:del', parseInt(uid, 10)); plugins.hooks.fire('action:user.blocks.remove', { uid: uid, targetUid: targetUid }); }; @@ -86,10 +86,8 @@ module.exports = function (User) { }; User.blocks.filterUids = async function (targetUid, uids) { - return await async.filter(uids, async (uid) => { - const isBlocked = await User.blocks.is(targetUid, uid); - return !isBlocked; - }); + const isBlocked = await User.blocks.is(targetUid, uids); + return uids.filter((uid, index) => !isBlocked[index]); }; User.blocks.filter = async function (uid, property, set) { diff --git a/src/user/delete.js b/src/user/delete.js index 8ee7e99146..1643d4643a 100644 --- a/src/user/delete.js +++ b/src/user/delete.js @@ -105,7 +105,7 @@ module.exports = function (User) { throw new Error('[[error:no-user]]'); } - await plugins.hooks.fire('static:user.delete', { uid: uid }); + await plugins.hooks.fire('static:user.delete', { uid: uid, userData: userData }); await deleteVotes(uid); await deleteChats(uid); await User.auth.revokeAllSessions(uid); diff --git a/src/user/digest.js b/src/user/digest.js index d9b7e1ef3e..5a23b0fd8f 100644 --- a/src/user/digest.js +++ b/src/user/digest.js @@ -53,6 +53,7 @@ Digest.getUsersInterval = async (uids) => { const settings = await Promise.all([ db.isSortedSetMembers('digest:day:uids', uids), db.isSortedSetMembers('digest:week:uids', uids), + db.isSortedSetMembers('digest:biweek:uids', uids), db.isSortedSetMembers('digest:month:uids', uids), ]); @@ -62,6 +63,8 @@ Digest.getUsersInterval = async (uids) => { } else if (settings[1][index]) { return 'week'; } else if (settings[2][index]) { + return 'biweek'; + } else if (settings[3][index]) { return 'month'; } return false; diff --git a/src/user/email.js b/src/user/email.js index 85162b8414..a97aa1f38f 100644 --- a/src/user/email.js +++ b/src/user/email.js @@ -2,6 +2,7 @@ 'use strict'; const nconf = require('nconf'); +const winston = require('winston'); const user = require('./index'); const utils = require('../utils'); @@ -69,6 +70,11 @@ UserEmail.sendValidationEmail = async function (uid, options) { * - force, sends email even if it is too soon to send another */ + if (meta.config.sendValidationEmail !== 1) { + winston.verbose(`[user/email] Validation email for uid ${uid} not sent due to config settings`); + return; + } + options = options || {}; // Fallback behaviour (email passed in as second argument) @@ -110,6 +116,7 @@ UserEmail.sendValidationEmail = async function (uid, options) { await db.expireAt(`confirm:${confirm_code}`, Math.floor((Date.now() / 1000) + (60 * 60 * 24))); const username = await user.getUserField(uid, 'username'); + winston.verbose(`[user/email] Validation email for uid ${uid} sent to ${options.email}`); events.log({ type: 'email-confirmation-sent', uid, @@ -143,6 +150,12 @@ UserEmail.confirmByCode = async function (code, sessionId) { throw new Error('[[error:invalid-data]]'); } + // If another uid has the same email, remove it + const oldUid = await db.sortedSetScore('email:uid', confirmObj.email.toLowerCase()); + if (oldUid) { + await UserEmail.remove(oldUid, sessionId); + } + const oldEmail = await user.getUserField(confirmObj.uid, 'email'); if (oldEmail && confirmObj.email !== oldEmail) { await UserEmail.remove(confirmObj.uid, sessionId); diff --git a/src/user/interstitials.js b/src/user/interstitials.js index 5467711503..ac401127d1 100644 --- a/src/user/interstitials.js +++ b/src/user/interstitials.js @@ -28,15 +28,22 @@ Interstitials.email = async (data) => { callback: async (userData, formData) => { // Validate and send email confirmation if (userData.uid) { - const [isAdminOrGlobalMod, canEdit, current] = await Promise.all([ + const [isAdminOrGlobalMod, canEdit, current, { allowed, error }] = await Promise.all([ user.isAdminOrGlobalMod(data.req.uid), privileges.users.canEdit(data.req.uid, userData.uid), user.getUserField(userData.uid, 'email'), + plugins.hooks.fire('filter:user.saveEmail', { + uid: userData.uid, + email: formData.email, + registration: false, + allowed: true, // change this value to disallow + error: '[[error:invalid-email]]', + }), ]); if (formData.email && formData.email.length) { - if (!utils.isEmailValid(formData.email)) { - throw new Error('[[error:invalid-email]]'); + if (!allowed || !utils.isEmailValid(formData.email)) { + throw new Error(error); } if (formData.email === current) { @@ -57,11 +64,29 @@ Interstitials.email = async (data) => { // User attempting to edit another user's email -- not allowed throw new Error('[[error:no-privileges]]'); } - } else if (current) { - // User explicitly clearing their email - await user.email.remove(userData.uid, data.req.session.id); + } else { + if (meta.config.requireEmailAddress) { + throw new Error('[[error:invalid-email]]'); + } + + if (current) { + // User explicitly clearing their email + await user.email.remove(userData.uid, data.req.session.id); + } } } else { + const { allowed, error } = await plugins.hooks.fire('filter:user.saveEmail', { + uid: null, + email: formData.email, + registration: true, + allowed: true, // change this value to disallow + error: '[[error:invalid-email]]', + }); + + if (!allowed || (meta.config.requireEmailAddress && !(formData.email && formData.email.length))) { + throw new Error(error); + } + // New registrants have the confirm email sent from user.create() userData.email = formData.email; } diff --git a/src/user/jobs.js b/src/user/jobs.js index 8e069f0435..34c797e9e5 100644 --- a/src/user/jobs.js +++ b/src/user/jobs.js @@ -2,7 +2,7 @@ const winston = require('winston'); const cronJob = require('cron').CronJob; - +const db = require('../database'); const meta = require('../meta'); const jobs = {}; @@ -11,7 +11,6 @@ module.exports = function (User) { User.startJobs = function () { winston.verbose('[user/jobs] (Re-)starting jobs...'); - let started = 0; let { digestHour } = meta.config; // Fix digest hour if invalid @@ -26,19 +25,27 @@ module.exports = function (User) { startDigestJob('digest.daily', `0 ${digestHour} * * *`, 'day'); startDigestJob('digest.weekly', `0 ${digestHour} * * 0`, 'week'); startDigestJob('digest.monthly', `0 ${digestHour} 1 * *`, 'month'); - started += 3; jobs['reset.clean'] = new cronJob('0 0 * * *', User.reset.clean, null, true); winston.verbose('[user/jobs] Starting job (reset.clean)'); - started += 1; - winston.verbose(`[user/jobs] ${started} jobs started`); + winston.verbose(`[user/jobs] jobs started`); }; function startDigestJob(name, cronString, term) { - jobs[name] = new cronJob(cronString, (() => { + jobs[name] = new cronJob(cronString, (async () => { winston.verbose(`[user/jobs] Digest job (${name}) started.`); - User.digest.execute({ interval: term }); + try { + if (name === 'digest.weekly') { + const counter = await db.increment('biweeklydigestcounter'); + if (counter % 2) { + await User.digest.execute({ interval: 'biweek' }); + } + } + await User.digest.execute({ interval: term }); + } catch (err) { + winston.error(err.stack); + } }), null, true); winston.verbose(`[user/jobs] Starting job (${name})`); } diff --git a/src/user/profile.js b/src/user/profile.js index 69c73eb729..76661faa69 100644 --- a/src/user/profile.js +++ b/src/user/profile.js @@ -71,7 +71,7 @@ module.exports = function (User) { }; async function validateData(callerUid, data) { - await isEmailAvailable(data, data.uid); + await isEmailValid(data); await isUsernameAvailable(data, data.uid); await isWebsiteValid(callerUid, data); await isAboutMeValid(callerUid, data); @@ -82,7 +82,7 @@ module.exports = function (User) { isGroupTitleValid(data); } - async function isEmailAvailable(data, uid) { + async function isEmailValid(data) { if (!data.email) { return; } @@ -91,14 +91,6 @@ module.exports = function (User) { if (!utils.isEmailValid(data.email)) { throw new Error('[[error:invalid-email]]'); } - const email = await User.getUserField(uid, 'email'); - if (email === data.email) { - return; - } - const available = await User.email.available(data.email); - if (!available) { - throw new Error('[[error:email-taken]]'); - } } async function isUsernameAvailable(data, uid) { diff --git a/src/user/settings.js b/src/user/settings.js index 4e657a605d..cb982a4014 100644 --- a/src/user/settings.js +++ b/src/user/settings.js @@ -148,7 +148,7 @@ module.exports = function (User) { settings[notificationType] = data[notificationType]; } }); - const result = await plugins.hooks.fire('filter:user.saveSettings', { settings: settings, data: data }); + const result = await plugins.hooks.fire('filter:user.saveSettings', { uid: uid, settings: settings, data: data }); await db.setObject(`user:${uid}:settings`, result.settings); await User.updateDigestSetting(uid, data.dailyDigestFreq); return await User.getSettings(uid); @@ -156,7 +156,7 @@ module.exports = function (User) { User.updateDigestSetting = async function (uid, dailyDigestFreq) { await db.sortedSetsRemove(['digest:day:uids', 'digest:week:uids', 'digest:month:uids'], uid); - if (['day', 'week', 'month'].includes(dailyDigestFreq)) { + if (['day', 'week', 'biweek', 'month'].includes(dailyDigestFreq)) { await db.sortedSetAdd(`digest:${dailyDigestFreq}:uids`, Date.now(), uid); } }; diff --git a/src/views/admin/advanced/events.tpl b/src/views/admin/advanced/events.tpl index f7a4a94c3c..ccaca544b6 100644 --- a/src/views/admin/advanced/events.tpl +++ b/src/views/admin/advanced/events.tpl @@ -15,7 +15,7 @@ {events.ip} - +
    {events.user.icon:text}
    @@ -38,9 +38,9 @@
    - - +
    diff --git a/src/views/admin/dashboard.tpl b/src/views/admin/dashboard.tpl index 26d64231b5..37649a8bc7 100644 --- a/src/views/admin/dashboard.tpl +++ b/src/views/admin/dashboard.tpl @@ -4,22 +4,22 @@
    -
    +
    -
    [[admin/dashboard:anonymous-registered-users]]
    +
    [[admin/dashboard:guest-registered-users]]
    • () [[admin/dashboard:registered]]
    • -
    • () [[admin/dashboard:anonymous]]
    • +
    • () [[admin/dashboard:guest]]
    -
    +
    [[admin/dashboard:user-presence]]
    @@ -36,7 +36,7 @@
    -
    +
    [[admin/dashboard:high-presence-topics]]
    @@ -47,6 +47,20 @@
    +
    +
    +
    [[admin/dashboard:popular-searches]]
    +
    +
    + +
    +
    +
    +
    diff --git a/src/views/admin/dashboard/searches.tpl b/src/views/admin/dashboard/searches.tpl new file mode 100644 index 0000000000..baefcea306 --- /dev/null +++ b/src/views/admin/dashboard/searches.tpl @@ -0,0 +1,25 @@ +
    +
    + + + [[admin/dashboard:back-to-dashboard]] + + + + + + {{{ if !searches.length}}} + + + + {{{ end }}} + {{{ each searches }}} + + + + + {{{ end }}} + +
    [[admin/dashboard:details.no-searches]]
    {searches.value}{searches.score}
    +
    +
    \ No newline at end of file diff --git a/src/views/admin/development/info.tpl b/src/views/admin/development/info.tpl index 604e13bfb7..bb113ab0d9 100644 --- a/src/views/admin/development/info.tpl +++ b/src/views/admin/development/info.tpl @@ -17,7 +17,8 @@ [[admin/development/info:online]] [[admin/development/info:git]] [[admin/development/info:cpu-usage]] - [[admin/development/info:memory]] + [[admin/development/info:process-memory]] + [[admin/development/info:system-memory]] [[admin/development/info:load]] [[admin/development/info:uptime]] @@ -38,8 +39,14 @@ {info.stats.socketCount} {info.git.branch}@{info.git.hashShort} - {info.process.cpuUsage.user} / {info.process.cpuUsage.system} - {info.process.memoryUsage.humanReadable} mb / {info.os.freemem} mb + {info.process.cpuUsage}% + + {info.process.memoryUsage.humanReadable} gb + + + {info.os.usedmem} gb / + {info.os.totalmem} gb + {info.os.load} {info.process.uptimeHumanReadable} diff --git a/src/views/admin/development/logger.tpl b/src/views/admin/development/logger.tpl index dc8704a52b..01c9688718 100644 --- a/src/views/admin/development/logger.tpl +++ b/src/views/admin/development/logger.tpl @@ -26,8 +26,8 @@

    - - + +
    diff --git a/src/views/admin/extend/rewards.tpl b/src/views/admin/extend/rewards.tpl index bd226cc39d..e53b639b2d 100644 --- a/src/views/admin/extend/rewards.tpl +++ b/src/views/admin/extend/rewards.tpl @@ -1,76 +1,82 @@ -
    -
    -
    -
    [[admin/extend/rewards:rewards]]
    -
    -
      - {{{ each active }}} -
    • -
      -
      -
      - -
      -
      -
      - - -
      -
      -
      - -
      -
      -
      -
      -
      -
      - -
      -
      -
      -
      - - [[admin/extend/rewards:zero-infinite]] -
      -
      -
      - - - - - - +
      +
        + {{{ each active }}} +
      • +
        +
        +
        +
        +
        + +
        +
        +
        +
        +
        + +
        +
        + +
        -
        -
      • - {{{ end }}} -
      +
      +
      + +
      + +
      +
      +
      +
      +
      +
      +
      + +
      +
      +
      +
      + +
      +
      -
      -
    -
    -
    -
    [[admin/extend/rewards:control-panel]]
    -
    - - +
    +
    + + + + + + +
    -
    -
    +
    + + {{{ end }}} + +
    + +
    + + +
    \ No newline at end of file diff --git a/src/views/admin/header.tpl b/src/views/admin/header.tpl index a0f5fef1e3..ee81bfeb2d 100644 --- a/src/views/admin/header.tpl +++ b/src/views/admin/header.tpl @@ -2,8 +2,9 @@ {title} - + {{{each metaTags}}}{function.buildMetaTag}{{{end}}} + {{{each linkTags}}}{function.buildLinkTag}{{{end}}} diff --git a/src/views/admin/manage/admins-mods.tpl b/src/views/admin/manage/admins-mods.tpl index e595f682db..cbe7198eae 100644 --- a/src/views/admin/manage/admins-mods.tpl +++ b/src/views/admin/manage/admins-mods.tpl @@ -4,7 +4,7 @@
    - +
    {admins.members.icon:text}
    @@ -22,7 +22,7 @@
    - +
    {globalMods.members.icon:text}
    @@ -47,7 +47,7 @@ {{{ each categoryMods.moderators }}}
    {{{ if categoryMods.moderators.picture }}} - + {{{ else }}}
    {categoryMods.moderators.icon:text}
    {{{ end }}} diff --git a/src/views/admin/manage/digest.tpl b/src/views/admin/manage/digest.tpl index 185e89d02b..96fc869331 100644 --- a/src/views/admin/manage/digest.tpl +++ b/src/views/admin/manage/digest.tpl @@ -44,6 +44,7 @@ [[admin/manage/digest:manual-run]] + diff --git a/src/views/admin/manage/group.tpl b/src/views/admin/manage/group.tpl index 0f63599a91..e3b9d3313f 100644 --- a/src/views/admin/manage/group.tpl +++ b/src/views/admin/manage/group.tpl @@ -133,11 +133,11 @@
    diff --git a/src/views/admin/manage/registration.tpl b/src/views/admin/manage/registration.tpl index d4effc8c5d..e12f08d370 100644 --- a/src/views/admin/manage/registration.tpl +++ b/src/views/admin/manage/registration.tpl @@ -58,7 +58,7 @@ {{{ each users.ipMatch }}}
    - +
    {users.ipMatch.icon:text}
    diff --git a/src/views/admin/manage/users.tpl b/src/views/admin/manage/users.tpl index 18005830c1..068ed4ea8f 100644 --- a/src/views/admin/manage/users.tpl +++ b/src/views/admin/manage/users.tpl @@ -55,13 +55,13 @@
    diff --git a/src/views/admin/partials/categories/select-category.tpl b/src/views/admin/partials/categories/select-category.tpl index a0a28b6eed..643f561289 100644 --- a/src/views/admin/partials/categories/select-category.tpl +++ b/src/views/admin/partials/categories/select-category.tpl @@ -9,11 +9,11 @@
    diff --git a/src/views/admin/partials/categories/users.tpl b/src/views/admin/partials/categories/users.tpl index 772053d5b0..67f272e6fd 100644 --- a/src/views/admin/partials/categories/users.tpl +++ b/src/views/admin/partials/categories/users.tpl @@ -17,6 +17,6 @@
  • Moderator
  • - {users.username} + {users.username} diff --git a/src/views/admin/partials/create_user_modal.tpl b/src/views/admin/partials/create_user_modal.tpl index ece62fc7f0..278e133165 100644 --- a/src/views/admin/partials/create_user_modal.tpl +++ b/src/views/admin/partials/create_user_modal.tpl @@ -1,21 +1,21 @@
    - +
    - +
    - +
    - +
    diff --git a/src/views/admin/partials/dashboard/graph.tpl b/src/views/admin/partials/dashboard/graph.tpl index d2604f2332..ee55cd7ce1 100644 --- a/src/views/admin/partials/dashboard/graph.tpl +++ b/src/views/admin/partials/dashboard/graph.tpl @@ -2,8 +2,10 @@
    [[admin/dashboard:forum-traffic]]
    - - + +
    +
    +
    diff --git a/src/views/admin/partials/groups/memberlist.tpl b/src/views/admin/partials/groups/memberlist.tpl index c6aae28af7..5e8b0c049e 100644 --- a/src/views/admin/partials/groups/memberlist.tpl +++ b/src/views/admin/partials/groups/memberlist.tpl @@ -19,7 +19,7 @@ - +
    {group.members.icon:text}
    diff --git a/src/views/admin/partials/groups/privileges-select-category.tpl b/src/views/admin/partials/groups/privileges-select-category.tpl index 10570034a1..ee2dcf0ac8 100644 --- a/src/views/admin/partials/groups/privileges-select-category.tpl +++ b/src/views/admin/partials/groups/privileges-select-category.tpl @@ -7,11 +7,11 @@
    diff --git a/src/views/admin/partials/menu.tpl b/src/views/admin/partials/menu.tpl index f519ecd475..343ba8445c 100644 --- a/src/views/admin/partials/menu.tpl +++ b/src/views/admin/partials/menu.tpl @@ -184,6 +184,7 @@
  • [[admin/menu:dashboard/logins]]
  • [[admin/menu:dashboard/users]]
  • [[admin/menu:dashboard/topics]]
  • +
  • [[admin/menu:dashboard/searches]]
  • {{{ end }}} diff --git a/src/views/admin/partials/privileges/category.tpl b/src/views/admin/partials/privileges/category.tpl index c2c03c2bec..19a21a9032 100644 --- a/src/views/admin/partials/privileges/category.tpl +++ b/src/views/admin/partials/privileges/category.tpl @@ -107,7 +107,7 @@ - +
    {../icon:text}
    diff --git a/src/views/admin/partials/privileges/global.tpl b/src/views/admin/partials/privileges/global.tpl index eec5fab737..28271a215d 100644 --- a/src/views/admin/partials/privileges/global.tpl +++ b/src/views/admin/partials/privileges/global.tpl @@ -1,6 +1,20 @@ + {{{ if !isAdminPriv }}} + + + + + + + {{{ end }}} @@ -67,7 +81,7 @@
    + [[admin/manage/categories:privileges.section-posting]] + + [[admin/manage/categories:privileges.section-viewing]] + + [[admin/manage/categories:privileges.section-moderation]] +
    [[admin/manage/categories:privileges.section-group]] [[admin/manage/privileges:select-clear-all]]
    - +
    {../icon:text}
    diff --git a/src/views/admin/settings/advanced.tpl b/src/views/admin/settings/advanced.tpl index eec61df531..b64a631f74 100644 --- a/src/views/admin/settings/advanced.tpl +++ b/src/views/admin/settings/advanced.tpl @@ -15,11 +15,11 @@

    - +
    - +
    diff --git a/src/views/admin/settings/api.tpl b/src/views/admin/settings/api.tpl index 0a6a989054..9ee1cd35f6 100644 --- a/src/views/admin/settings/api.tpl +++ b/src/views/admin/settings/api.tpl @@ -27,7 +27,7 @@
    [[admin/settings/api:tokens]]
    - +
      diff --git a/src/views/admin/settings/chat.tpl b/src/views/admin/settings/chat.tpl index d9ee142398..403c013d16 100644 --- a/src/views/admin/settings/chat.tpl +++ b/src/views/admin/settings/chat.tpl @@ -24,34 +24,34 @@
      - - + +
      - - + +
      - - + +
      - - + +
      - - + +
      - - + +
      diff --git a/src/views/admin/settings/email.tpl b/src/views/admin/settings/email.tpl index 1a8e98177e..e5b3fab134 100644 --- a/src/views/admin/settings/email.tpl +++ b/src/views/admin/settings/email.tpl @@ -21,9 +21,17 @@
      -
      +

      [[admin/settings/email:require-email-address-warning]]

      + +
      +
      @@ -34,6 +42,21 @@

      [[admin/settings/email:include-unverified-warning]]

      + +
      + +
      +

      [[admin/settings/email:prompt-help]]

      + +
      + +
      @@ -122,14 +145,14 @@
      - +

      [[admin/settings/email:smtp-transport.username-help]]

      - +
      @@ -138,7 +161,7 @@
      [[admin/settings/email:template]]
      -
      +
      diff --git a/src/views/admin/settings/general.tpl b/src/views/admin/settings/general.tpl index f2ece80c61..1453e047b5 100644 --- a/src/views/admin/settings/general.tpl +++ b/src/views/admin/settings/general.tpl @@ -6,8 +6,8 @@
      - - + + @@ -23,23 +23,23 @@
      - - + +

      [[admin/settings/general:browser-title-help]]

      - - + +

      [[admin/settings/general:title-layout-help]]

      - -
      + +
      -
      -
      +
      +
      @@ -52,7 +52,7 @@
      - +
      @@ -75,7 +75,7 @@
      - +
      @@ -92,7 +92,7 @@
      - +
      @@ -110,7 +110,7 @@
      - +
      @@ -120,11 +120,11 @@
      - +
      - +
      @@ -139,8 +139,8 @@
      [[admin/settings/general:search]]
      - - @@ -150,15 +150,15 @@
      - -
      - + + + - - + +

      [[admin/settings/general:background-color-help]]

      @@ -215,8 +215,8 @@
      [[admin/settings/general:topic-tools]]
      - - + +

      [[admin/settings/general:undo-timeout-help]]

      diff --git a/src/views/admin/settings/group.tpl b/src/views/admin/settings/group.tpl index cbfe08339c..8033448b01 100644 --- a/src/views/admin/settings/group.tpl +++ b/src/views/admin/settings/group.tpl @@ -29,11 +29,11 @@ [[admin/settings/group:allow-multiple-badges-help]]

      - - + + - - + +
      diff --git a/src/views/admin/settings/homepage.tpl b/src/views/admin/settings/homepage.tpl index 310185856b..625a59aa62 100644 --- a/src/views/admin/settings/homepage.tpl +++ b/src/views/admin/settings/homepage.tpl @@ -6,16 +6,16 @@

      - -
      @@ -26,8 +26,8 @@

      - - + +
      diff --git a/src/views/admin/settings/languages.tpl b/src/views/admin/settings/languages.tpl index 46033ef380..5f6bb114b1 100644 --- a/src/views/admin/settings/languages.tpl +++ b/src/views/admin/settings/languages.tpl @@ -9,7 +9,7 @@
      - + + +
      - - + +
      - - + +
      - - + +
      - - + +
      - - + +
      @@ -89,6 +89,19 @@ +
      + +
      +
      +

      + [[admin/settings/navigation:dropdown-placeholder]] +

      + +
      + diff --git a/src/views/admin/settings/notifications.tpl b/src/views/admin/settings/notifications.tpl index 3e68295699..7fc1934832 100644 --- a/src/views/admin/settings/notifications.tpl +++ b/src/views/admin/settings/notifications.tpl @@ -7,6 +7,7 @@ [[admin/settings/notifications:welcome-notification]]

      [[admin/settings/notifications:welcome-notification-link]]

      [[admin/settings/notifications:welcome-notification-uid]]

      + [[admin/settings/notifications:post-queue-notification-uid]]

      diff --git a/src/views/admin/settings/post.tpl b/src/views/admin/settings/post.tpl index bc9f5026ee..6c018ec974 100644 --- a/src/views/admin/settings/post.tpl +++ b/src/views/admin/settings/post.tpl @@ -5,16 +5,16 @@
      - -
      - - @@ -140,8 +140,8 @@
      - - @@ -186,13 +186,19 @@
      - -
      +
      + +
      @@ -254,8 +260,8 @@
      - - + +
      @@ -294,6 +300,21 @@ +
      +
      [[admin/settings/post:backlinks]]
      +
      +
      +
      + +
      +
      +
      +
      +
      [[admin/settings/post:ip-tracking]]
      diff --git a/src/views/admin/settings/tags.tpl b/src/views/admin/settings/tags.tpl index 81871bcd7c..ff10590ec4 100644 --- a/src/views/admin/settings/tags.tpl +++ b/src/views/admin/settings/tags.tpl @@ -12,7 +12,7 @@
      - +

      [[admin/settings/tags:system-tags-help]]

      diff --git a/src/views/admin/settings/uploads.tpl b/src/views/admin/settings/uploads.tpl index 2936f6abfb..36edc6ff8a 100644 --- a/src/views/admin/settings/uploads.tpl +++ b/src/views/admin/settings/uploads.tpl @@ -22,7 +22,7 @@
      - +

      [[admin/settings/uploads:private-uploads-extensions-help]]

      @@ -32,7 +32,7 @@
      - +

      [[admin/settings/uploads:resize-image-width-threshold-help]]

      @@ -42,7 +42,7 @@
      - +

      [[admin/settings/uploads:resize-image-width-help]]

      @@ -52,7 +52,7 @@
      - +

      [[admin/settings/uploads:resize-image-quality-help]]

      @@ -60,7 +60,7 @@
      - +

      [[admin/settings/uploads:max-file-size-help]]

      @@ -68,7 +68,7 @@
      - +

      [[admin/settings/uploads:reject-image-width-help]]

      @@ -76,7 +76,7 @@
      - +

      [[admin/settings/uploads:reject-image-height-help]]

      @@ -91,12 +91,12 @@
      - +
      - +

      [[admin/settings/uploads:allowed-file-extensions-help]]

      @@ -104,7 +104,7 @@
      -
      +
      @@ -148,7 +148,7 @@
      - +
      @@ -162,16 +162,16 @@
      - - + +

      [[admin/settings/uploads:max-profile-image-size-help]]

      - - + +

      [[admin/settings/uploads:max-cover-image-size-help]]

      diff --git a/src/views/admin/settings/user.tpl b/src/views/admin/settings/user.tpl index 1bfc772737..bc879b9d4c 100644 --- a/src/views/admin/settings/user.tpl +++ b/src/views/admin/settings/user.tpl @@ -12,8 +12,8 @@
      - - @@ -117,8 +117,8 @@
      - - + +
      @@ -133,14 +133,14 @@
      - - + +
      - - + +
      @@ -150,8 +150,8 @@
      - - + +

      [[admin/settings/user:online-cutoff-help]]

      @@ -163,8 +163,8 @@
      - - @@ -175,8 +175,8 @@

      - - @@ -186,8 +186,8 @@

      - - + +

      [[admin/settings/user:registration-queue-auto-approve-time-help]]

      @@ -198,35 +198,44 @@ [[admin/settings/user:registration-queue-show-average-time]]
      + +
      + +
      +

      [[admin/settings/email:require-email-address-warning]]

      +
      - - + +

      [[admin/settings/user:max-invites-help]]

      - - + +

      [[admin/settings/user:invite-expiration-help]]

      - - + +
      - - + +
      - - + +
      - - @@ -235,12 +244,12 @@
      - - + +
      - - + +
      @@ -251,8 +260,8 @@
      - - + +
      @@ -306,11 +315,12 @@
      - - +
      @@ -330,8 +340,8 @@
      - - diff --git a/src/views/admin/settings/web-crawler.tpl b/src/views/admin/settings/web-crawler.tpl index bc14fc72fd..bf818358e2 100644 --- a/src/views/admin/settings/web-crawler.tpl +++ b/src/views/admin/settings/web-crawler.tpl @@ -29,8 +29,8 @@
      - - + +

      diff --git a/src/views/emails/digest.tpl b/src/views/emails/digest.tpl index 1426807c2e..cb30a3afab 100644 --- a/src/views/emails/digest.tpl +++ b/src/views/emails/digest.tpl @@ -27,7 +27,7 @@
      - +
      {notifications.user.icon:text}
      diff --git a/src/views/install/index.tpl b/src/views/install/index.tpl index 5b84d66ee6..e022d64639 100644 --- a/src/views/install/index.tpl +++ b/src/views/install/index.tpl @@ -42,8 +42,8 @@
      - - + +
      @@ -58,28 +58,28 @@
      - +
      - +
      - +
      - +
      @@ -98,8 +98,8 @@
      - - @@ -140,7 +140,7 @@
      - +
      {{{ end }}} diff --git a/src/views/modals/crop_picture.tpl b/src/views/modals/crop_picture.tpl index c301436124..181302d756 100644 --- a/src/views/modals/crop_picture.tpl +++ b/src/views/modals/crop_picture.tpl @@ -12,7 +12,7 @@
      - +

      diff --git a/src/views/modals/topic-thumbs.tpl b/src/views/modals/topic-thumbs.tpl index 7c87b3e439..e7ecd24e13 100644 --- a/src/views/modals/topic-thumbs.tpl +++ b/src/views/modals/topic-thumbs.tpl @@ -5,7 +5,7 @@ {{{ each thumbs }}}
      - +

      diff --git a/src/views/partials/data/category.tpl b/src/views/partials/data/category.tpl index 2144c8a84c..3de7588b92 100644 --- a/src/views/partials/data/category.tpl +++ b/src/views/partials/data/category.tpl @@ -1 +1 @@ -data-tid="{topics.tid}" data-index="{topics.index}" data-cid="{topics.cid}" itemprop="itemListElement" \ No newline at end of file +data-tid="{topics.tid}" data-index="{topics.index}" data-cid="{topics.cid}" itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem" \ No newline at end of file diff --git a/src/views/partials/topic/post-preview.tpl b/src/views/partials/topic/post-preview.tpl new file mode 100644 index 0000000000..666313112c --- /dev/null +++ b/src/views/partials/topic/post-preview.tpl @@ -0,0 +1,13 @@ +

      + +
      {post.content}
      +
      diff --git a/src/webserver.js b/src/webserver.js index 22343f6036..c246272b85 100644 --- a/src/webserver.js +++ b/src/webserver.js @@ -148,15 +148,8 @@ function setupExpressApp(app) { configureBodyParser(app); app.use(cookieParser(nconf.get('secret'))); - const userAgentMiddleware = useragent.express(); - app.use((req, res, next) => { - userAgentMiddleware(req, res, next); - }); - const spiderDetectorMiddleware = detector.middleware(); - app.use((req, res, next) => { - spiderDetectorMiddleware(req, res, next); - }); - + app.use(useragent.express()); + app.use(detector.middleware()); app.use(session({ store: db.sessionStore, secret: nconf.get('secret'), diff --git a/test/authentication.js b/test/authentication.js index 8a8d5886e4..87d3b9f186 100644 --- a/test/authentication.js +++ b/test/authentication.js @@ -455,7 +455,15 @@ describe('authentication', () => { loginUser(bannedUser.username, bannedUser.pw, (err, res, body) => { assert.ifError(err); assert.equal(res.statusCode, 403); - assert.equal(body, '[[error:user-banned-reason, spammer]]'); + delete body.timestamp; + assert.deepStrictEqual(body, { + banned_until: 0, + banned_until_readable: '', + expiry: 0, + expiry_readable: '', + reason: 'spammer', + uid: 6, + }); user.bans.unban(bannedUser.uid, (err) => { assert.ifError(err); const expiry = Date.now() + 10000; @@ -464,7 +472,8 @@ describe('authentication', () => { loginUser(bannedUser.username, bannedUser.pw, (err, res, body) => { assert.ifError(err); assert.equal(res.statusCode, 403); - assert.equal(body, `[[error:user-banned-reason-until, ${utils.toISOString(expiry)}, No reason given.]]`); + assert(body.banned_until); + assert(body.reason, '[[user:info.banned-no-reason]]'); done(); }); }); diff --git a/test/build.js b/test/build.js index 41bc2f24c9..0727bc6d27 100644 --- a/test/build.js +++ b/test/build.js @@ -130,7 +130,6 @@ describe('Build', (done) => { it('should build plugin static dirs', (done) => { build.build(['plugin static dirs'], (err) => { assert.ifError(err); - assert(file.existsSync(path.join(__dirname, '../build/public/plugins/nodebb-plugin-dbsearch/dbsearch'))); done(); }); }); diff --git a/test/categories.js b/test/categories.js index d1e5cbf746..e91a51f619 100644 --- a/test/categories.js +++ b/test/categories.js @@ -432,6 +432,26 @@ describe('Categories', () => { }); }); + it('should not remove category from parent if parent is set again to same category', async () => { + const parentCat = await Categories.create({ name: 'parent', description: 'poor parent' }); + const updateData = {}; + updateData[cid] = { + parentCid: parentCat.cid, + }; + await Categories.update(updateData); + let data = await Categories.getCategoryData(cid); + assert.equal(data.parentCid, updateData[cid].parentCid); + let childrenCids = await db.getSortedSetRange(`cid:${parentCat.cid}:children`, 0, -1); + assert(childrenCids.includes(String(cid))); + + // update again to same parent + await Categories.update(updateData); + data = await Categories.getCategoryData(cid); + assert.equal(data.parentCid, updateData[cid].parentCid); + childrenCids = await db.getSortedSetRange(`cid:${parentCat.cid}:children`, 0, -1); + assert(childrenCids.includes(String(cid))); + }); + it('should purge category', (done) => { Categories.create({ name: 'purge me', diff --git a/test/controllers-admin.js b/test/controllers-admin.js index 3e720094f2..467652d88a 100644 --- a/test/controllers-admin.js +++ b/test/controllers-admin.js @@ -1,7 +1,7 @@ 'use strict'; const async = require('async'); -const assert = require('assert'); +const assert = require('assert'); const nconf = require('nconf'); const request = require('request'); @@ -719,4 +719,58 @@ describe('Admin Controllers', () => { }); }); }); + + describe('admin page privileges', () => { + let userJar; + let uid; + const privileges = require('../src/privileges'); + before((done) => { + user.create({ username: 'regularjoe', password: 'barbar' }, (err, _uid) => { + assert.ifError(err); + uid = _uid; + helpers.loginUser('regularjoe', 'barbar', (err, _jar) => { + assert.ifError(err); + userJar = _jar; + done(); + }); + }); + }); + + it('should allow normal user access to admin pages', async function () { + this.timeout(50000); + function makeRequest(url) { + return new Promise((resolve, reject) => { + request(url, { jar: userJar, json: true }, (err, res, body) => { + if (err) reject(err); + else resolve(res); + }); + }); + } + for (const route of Object.keys(privileges.admin.routeMap)) { + /* eslint-disable no-await-in-loop */ + await privileges.admin.rescind([privileges.admin.routeMap[route]], uid); + let res = await makeRequest(`${nconf.get('url')}/api/admin/${route}`); + assert.strictEqual(res.statusCode, 403); + + await privileges.admin.give([privileges.admin.routeMap[route]], uid); + res = await makeRequest(`${nconf.get('url')}/api/admin/${route}`); + assert.strictEqual(res.statusCode, 200); + + await privileges.admin.rescind([privileges.admin.routeMap[route]], uid); + } + + for (const route of Object.keys(privileges.admin.routeMap)) { + /* eslint-disable no-await-in-loop */ + await privileges.admin.rescind([privileges.admin.routeMap[route]], uid); + let res = await makeRequest(`${nconf.get('url')}/api/admin`); + assert.strictEqual(res.statusCode, 403); + + await privileges.admin.give([privileges.admin.routeMap[route]], uid); + res = await makeRequest(`${nconf.get('url')}/api/admin`); + assert.strictEqual(res.statusCode, 200); + + await privileges.admin.rescind([privileges.admin.routeMap[route]], uid); + } + }); + }); }); diff --git a/test/controllers.js b/test/controllers.js index 5696ea7073..4ac33196d5 100644 --- a/test/controllers.js +++ b/test/controllers.js @@ -302,16 +302,6 @@ describe('Controllers', () => { }); it('should load /register/complete', (done) => { - function hookMethod(data, next) { - data.interstitials.push({ template: 'topic.tpl', data: {} }); - next(null, data); - } - - plugins.hooks.register('myTestPlugin', { - hook: 'filter:register.interstitial', - method: hookMethod, - }); - const data = { username: 'interstitial', password: '123456', @@ -347,13 +337,86 @@ describe('Controllers', () => { assert(body.sections); assert(body.errors); assert(body.title); - plugins.hooks.unregister('myTestPlugin', 'filter:register.interstitial', hookMethod); done(); }); }); }); }); + describe('registration interstitials', () => { + let jar; + let token; + + it('email interstitial should still apply if empty email entered and requireEmailAddress is enabled', async () => { + meta.config.requireEmailAddress = 1; + + jar = await helpers.registerUser({ + username: 'testEmailReg', + password: 'asdasd', + }); + token = await helpers.getCsrfToken(jar); + + let res = await requestAsync(`${nconf.get('url')}/register/complete`, { + method: 'post', + jar, + json: true, + followRedirect: false, + simple: false, + resolveWithFullResponse: true, + headers: { + 'x-csrf-token': token, + }, + form: { + email: '', + }, + }); + + assert.strictEqual(res.headers.location, `${nconf.get('relative_path')}/register/complete`); + + res = await requestAsync(`${nconf.get('url')}/api/register/complete`, { + jar, + json: true, + resolveWithFullResponse: true, + }); + assert(res.body.errors.length, res.body); + assert(res.body.errors.includes('[[error:invalid-email]]'), res.body); + }); + + it('gdpr interstitial should still apply if email requirement is disabled', async () => { + meta.config.requireEmailAddress = 0; + + const res = await requestAsync(`${nconf.get('url')}/api/register/complete`, { + jar, + json: true, + resolveWithFullResponse: true, + }); + + assert(!res.body.errors.includes('[[error:invalid-email]]')); + assert(!res.body.errors.includes('[[error:gdpr_consent_denied]]')); + }); + + it('registration should succeed once gdpr prompts are agreed to', async () => { + const res = await requestAsync(`${nconf.get('url')}/register/complete`, { + method: 'post', + jar, + json: true, + followRedirect: false, + simple: false, + resolveWithFullResponse: true, + headers: { + 'x-csrf-token': token, + }, + form: { + gdpr_agree_data: 'on', + gdpr_agree_email: 'on', + }, + }); + + assert.strictEqual(res.statusCode, 302); + assert.strictEqual(res.headers.location, `${nconf.get('relative_path')}/`); + }); + }); + it('should load /robots.txt', (done) => { request(`${nconf.get('url')}/robots.txt`, (err, res, body) => { assert.ifError(err); @@ -1253,7 +1316,7 @@ describe('Controllers', () => { }); it('should export users posts', (done) => { - request(`${nconf.get('url')}/api/user/uid/foo/export/posts`, { jar: jar }, (err, res, body) => { + request(`${nconf.get('url')}/api/user/foo/export/posts`, { jar: jar }, (err, res, body) => { assert.ifError(err); assert.equal(res.statusCode, 200); assert(body); @@ -1262,7 +1325,7 @@ describe('Controllers', () => { }); it('should export users uploads', (done) => { - request(`${nconf.get('url')}/api/user/uid/foo/export/uploads`, { jar: jar }, (err, res, body) => { + request(`${nconf.get('url')}/api/user/foo/export/uploads`, { jar: jar }, (err, res, body) => { assert.ifError(err); assert.equal(res.statusCode, 200); assert(body); @@ -1271,7 +1334,7 @@ describe('Controllers', () => { }); it('should export users profile', (done) => { - request(`${nconf.get('url')}/api/user/uid/foo/export/profile`, { jar: jar }, (err, res, body) => { + request(`${nconf.get('url')}/api/user/foo/export/profile`, { jar: jar }, (err, res, body) => { assert.ifError(err); assert.equal(res.statusCode, 200); assert(body); diff --git a/test/database/hash.js b/test/database/hash.js index 39c4e39624..e2e2ee7364 100644 --- a/test/database/hash.js +++ b/test/database/hash.js @@ -72,7 +72,7 @@ describe('Hash methods', () => { }); }); - it('should set multiple keys to different okjects', async () => { + it('should set multiple keys to different objects', async () => { const keys = ['bulkKey1', 'bulkKey2']; const data = [{ foo: '1' }, { baz: 'baz' }]; @@ -80,6 +80,46 @@ describe('Hash methods', () => { const result = await db.getObjects(keys); assert.deepStrictEqual(result, data); }); + + it('should not error if object is empty', async () => { + const keys = ['bulkKey3', 'bulkKey4']; + const data = [{ foo: '1' }, { }]; + + await db.setObjectBulk(keys, data); + const result = await db.getObjects(keys); + assert.deepStrictEqual(result, [{ foo: '1' }, null]); + }); + + it('should update existing object on second call', async () => { + await db.setObjectBulk(['bulkKey3.5'], [{ foo: '1' }]); + await db.setObjectBulk(['bulkKey3.5'], [{ baz: '2' }]); + const result = await db.getObject('bulkKey3.5'); + assert.deepStrictEqual(result, { foo: '1', baz: '2' }); + }); + + it('should not error if object is empty', async () => { + const keys = ['bulkKey5']; + const data = [{ }]; + + await db.setObjectBulk(keys, data); + const result = await db.getObjects(keys); + assert.deepStrictEqual(result, [null]); + }); + + it('should not error if object is empty', async () => { + const keys = ['bulkKey6', 'bulkKey7']; + const data = {}; + + await db.setObject(keys, data); + const result = await db.getObjects(keys); + assert.deepStrictEqual(result, [null, null]); + }); + + it('should not error if object is empty', async () => { + await db.setObject('emptykey', {}); + const result = await db.getObject('emptykey'); + assert.deepStrictEqual(result, null); + }); }); describe('setObjectField()', () => { diff --git a/test/database/list.js b/test/database/list.js index 2d4c3ac454..f6f500f2f7 100644 --- a/test/database/list.js +++ b/test/database/list.js @@ -188,6 +188,15 @@ describe('List methods', () => { done(); }); }); + + it('should remove multiple elements from list', async () => { + await db.listAppend('multiRemoveList', ['a', 'b', 'c', 'd', 'e']); + const initial = await db.getListRange('multiRemoveList', 0, -1); + assert.deepStrictEqual(initial, ['a', 'b', 'c', 'd', 'e']); + await db.listRemoveAll('multiRemoveList', ['b', 'd']); + const values = await db.getListRange('multiRemoveList', 0, -1); + assert.deepStrictEqual(values, ['a', 'c', 'e']); + }); }); describe('listTrim()', () => { diff --git a/test/database/sorted.js b/test/database/sorted.js index 8b348961ee..ede6cc8c5d 100644 --- a/test/database/sorted.js +++ b/test/database/sorted.js @@ -1119,6 +1119,29 @@ describe('Sorted Set methods', () => { const members = await db.getSortedSetsMembers(['bulkRemove1', 'bulkRemove2']); assert.deepStrictEqual(members, [[], []]); }); + + it('should not remove wrong elements in bulk remove', async () => { + await db.sortedSetAddBulk([ + ['bulkRemove4', 1, 'value1'], + ['bulkRemove4', 2, 'value2'], + ['bulkRemove4', 3, 'value4'], + ['bulkRemove5', 1, 'value1'], + ['bulkRemove5', 2, 'value2'], + ['bulkRemove5', 3, 'value3'], + ]); + await db.sortedSetRemoveBulk([ + ['bulkRemove4', 'value1'], + ['bulkRemove4', 'value3'], + ['bulkRemove5', 'value1'], + ['bulkRemove5', 'value4'], + ]); + const members = await Promise.all([ + db.getSortedSetRange('bulkRemove4', 0, -1), + db.getSortedSetRange('bulkRemove5', 0, -1), + ]); + assert.deepStrictEqual(members[0], ['value2', 'value4']); + assert.deepStrictEqual(members[1], ['value2', 'value3']); + }); }); describe('sortedSetsRemove()', () => { diff --git a/test/emailer.js b/test/emailer.js index 17e639f0b9..ea19af58f4 100644 --- a/test/emailer.js +++ b/test/emailer.js @@ -87,7 +87,7 @@ describe('emailer', () => { assert.equal(output, text); done(); }); - }, 500); + }, 2000); }); }); }); diff --git a/test/files/toobig.jpg b/test/files/toobig.jpg deleted file mode 100644 index e25c2d3015..0000000000 Binary files a/test/files/toobig.jpg and /dev/null differ diff --git a/test/files/toobig.png b/test/files/toobig.png new file mode 100644 index 0000000000..1d2d94d143 Binary files /dev/null and b/test/files/toobig.png differ diff --git a/test/helpers/index.js b/test/helpers/index.js index ecb2ebd0a4..79a0e9e41e 100644 --- a/test/helpers/index.js +++ b/test/helpers/index.js @@ -10,6 +10,16 @@ const utils = require('../../public/src/utils'); const helpers = module.exports; +helpers.getCsrfToken = async (jar) => { + const { csrf_token: token } = await requestAsync({ + url: `${nconf.get('url')}/api/config`, + json: true, + jar, + }); + + return token; +}; + helpers.loginUser = function (username, password, callback) { const jar = request.jar(); diff --git a/test/i18n.js b/test/i18n.js new file mode 100644 index 0000000000..af066098f9 --- /dev/null +++ b/test/i18n.js @@ -0,0 +1,111 @@ +'use strict'; + +// For tests relating to the translator module, check translator.js + +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); + +const file = require('../src/file'); + +const db = require('./mocks/databasemock'); + +describe('i18n', () => { + let folders; + + before(async () => { + folders = await fs.promises.readdir(path.resolve(__dirname, '../public/language')); + folders = folders.filter(f => f !== 'README.md'); + }); + + it('should contain folders named after the language code', async () => { + const valid = /(?:README.md|^[a-z]{2}(?:-[A-Z]{2})?$|^[a-z]{2}(?:-x-[a-z]+)?$)/; // good luck + + folders.forEach((folder) => { + assert(valid.test(folder)); + }); + }); + + // There has to be a better way to generate tests asynchronously... + it('', async () => { + const sourcePath = path.resolve(__dirname, '../public/language/en-GB'); + const fullPaths = await file.walk(sourcePath); + const sourceFiles = fullPaths.map(path => path.replace(sourcePath, '')); + const sourceStrings = new Map(); + + describe('source language file structure', () => { + it('should only contain valid JSON files', async () => { + try { + fullPaths.forEach((fullPath) => { + const hash = require(fullPath); + sourceStrings.set(fullPath.replace(sourcePath, ''), hash); + }); + } catch (e) { + assert(!e, `Invalid JSON found: ${e.message}`); + } + }); + }); + + folders.forEach((language) => { + describe(`"${language}" file structure`, () => { + let files; + + before(async () => { + const translationPath = path.resolve(__dirname, `../public/language/${language}`); + files = (await file.walk(translationPath)).map(path => path.replace(translationPath, '')); + }); + + it('translations should contain every language file contained in the source language directory', () => { + sourceFiles.forEach((relativePath) => { + assert(files.includes(relativePath), `${relativePath.slice(1)} was found in source files but was not found in language "${language}" (likely not internationalized)`); + }); + }); + + it('should not contain any extraneous files not included in the source language directory', () => { + files.forEach((relativePath) => { + assert(sourceFiles.includes(relativePath), `${relativePath.slice(1)} was found in language "${language}" but there is no source file for it (likely removed from en-GB)`); + }); + }); + }); + + describe(`"${language}" file contents`, () => { + let fullPaths; + const translationPath = path.resolve(__dirname, `../public/language/${language}`); + const strings = new Map(); + + before(async () => { + fullPaths = await file.walk(translationPath); + }); + + it('should contain only valid JSON files', () => { + try { + fullPaths.forEach((fullPath) => { + const hash = require(fullPath); + strings.set(fullPath.replace(translationPath, ''), hash); + }); + } catch (e) { + assert(!e, `Invalid JSON found: ${e.message}`); + } + }); + + it('should contain every translation key contained in its source counterpart', () => { + const sourceArr = Array.from(sourceStrings.keys()); + sourceArr.forEach((namespace) => { + const sourceKeys = Object.keys(sourceStrings.get(namespace)); + const translationKeys = Object.keys(strings.get(namespace)); + + assert(sourceKeys && translationKeys); + sourceKeys.forEach((key) => { + assert(translationKeys.includes(key), `${namespace.slice(1, -5)}:${key} missing in ${language}`); + }); + assert.strictEqual( + sourceKeys.length, + translationKeys.length, + `Extra keys found in namespace ${namespace.slice(1, -5)} for language "${language}"` + ); + }); + }); + }); + }); + }); +}); diff --git a/test/messaging.js b/test/messaging.js index fbec60234f..996ad14bdb 100644 --- a/test/messaging.js +++ b/test/messaging.js @@ -408,7 +408,7 @@ describe('Messaging Library', () => { await db.sortedSetAdd('users:online', Date.now() - ((meta.config.onlineCutoff * 60000) + 50000), herpUid); await socketModules.chats.send({ uid: fooUid }, { roomId: roomId, message: 'second chat message **bold** text' }); - await sleep(1500); + await sleep(3000); const data = await User.notifications.get(herpUid); assert(data.unread[0]); const notification = data.unread[0]; diff --git a/test/meta.js b/test/meta.js index 841d7b11ef..86ea5a3637 100644 --- a/test/meta.js +++ b/test/meta.js @@ -602,4 +602,10 @@ describe('meta', () => { }); }); }); + + it('should log targets', (done) => { + const aliases = require('../src/meta/aliases'); + aliases.buildTargets(); + done(); + }); }); diff --git a/test/mocks/databasemock.js b/test/mocks/databasemock.js index 911398b708..414cb7cfdd 100644 --- a/test/mocks/databasemock.js +++ b/test/mocks/databasemock.js @@ -38,6 +38,7 @@ nconf.defaults({ const urlObject = url.parse(nconf.get('url')); const relativePath = urlObject.pathname !== '/' ? urlObject.pathname : ''; nconf.set('relative_path', relativePath); +nconf.set('asset_base_url', `${relativePath}/assets`); nconf.set('upload_path', path.join(nconf.get('base_dir'), nconf.get('upload_path'))); nconf.set('upload_url', '/assets/uploads'); nconf.set('url_parsed', urlObject); diff --git a/test/package-install.js b/test/package-install.js index 3305c2e532..34d256c9f4 100644 --- a/test/package-install.js +++ b/test/package-install.js @@ -1,6 +1,5 @@ 'use strict'; - const { execSync } = require('child_process'); const path = require('path'); const { readFileSync } = require('fs'); @@ -9,12 +8,14 @@ const assert = require('assert'); describe('Package install', () => { it('should remove non-`nodebb-` modules not specified in `install/package.json`', () => { + const oldValue = process.env.NODE_ENV; + process.env.NODE_ENV = 'development'; const packageFilePath = path.join(__dirname, '../package.json'); // install an extra package // chose dotenv because it's a popular package // and we use nconf instead - execSync('npm install dotenv --save --production'); + execSync('npm install dotenv --save'); // assert it saves in package.json const packageWithExtras = JSON.parse(readFileSync(packageFilePath, 'utf8')); @@ -26,5 +27,6 @@ describe('Package install', () => { // assert it removed the extra package const packageCleaned = JSON.parse(readFileSync(packageFilePath, 'utf8')); assert(!packageCleaned.dependencies.dotenv, 'dependency was not removed'); + process.env.NODE_ENV = oldValue; }); }); diff --git a/test/plugins.js b/test/plugins.js index ae5e403abf..1b9864a920 100644 --- a/test/plugins.js +++ b/test/plugins.js @@ -1,7 +1,6 @@ 'use strict'; - -const assert = require('assert'); +const assert = require('assert'); const path = require('path'); const nconf = require('nconf'); const request = require('request'); @@ -47,7 +46,7 @@ describe('Plugins', () => { }); }); - it('should register and fire a filter hook having 3 methods, one returning a promise, one calling the callback and one just returning', async () => { + it('should register and fire a filter hook having 3 methods', async () => { function method1(data, callback) { data.foo += 1; callback(null, data); @@ -214,6 +213,16 @@ describe('Plugins', () => { describe('install/activate/uninstall', () => { let latest; const pluginName = 'nodebb-plugin-imgur'; + const oldValue = process.env.NODE_ENV; + before((done) => { + process.env.NODE_ENV = 'development'; + done(); + }); + after((done) => { + process.env.NODE_ENV = oldValue; + done(); + }); + it('should install a plugin', function (done) { this.timeout(0); plugins.toggleInstall(pluginName, '1.0.16', (err, pluginData) => { @@ -284,7 +293,8 @@ describe('Plugins', () => { }); it('should 404 if resource does not exist', (done) => { - request.get(`${nconf.get('url')}/plugins/nodebb-plugin-dbsearch/dbsearch/templates/admin/plugins/should404.tpl`, (err, res, body) => { + const url = `${nconf.get('url')}/plugins/nodebb-plugin-dbsearch/dbsearch/templates/admin/plugins/should404.tpl`; + request.get(url, (err, res, body) => { assert.ifError(err); assert.equal(res.statusCode, 404); assert(body); @@ -293,7 +303,8 @@ describe('Plugins', () => { }); it('should get resource', (done) => { - request.get(`${nconf.get('url')}/plugins/nodebb-plugin-dbsearch/dbsearch/templates/admin/plugins/dbsearch.tpl`, (err, res, body) => { + const url = `${nconf.get('url')}/assets/templates/admin/plugins/dbsearch.tpl`; + request.get(url, (err, res, body) => { assert.ifError(err); assert.equal(res.statusCode, 200); assert(body); @@ -302,3 +313,5 @@ describe('Plugins', () => { }); }); }); + + diff --git a/test/posts.js b/test/posts.js index 200810ad89..e6fa092536 100644 --- a/test/posts.js +++ b/test/posts.js @@ -1,7 +1,7 @@ 'use strict'; -const assert = require('assert'); +const assert = require('assert'); const async = require('async'); const request = require('request'); const nconf = require('nconf'); @@ -1426,4 +1426,111 @@ describe('Post\'s', () => { }); }); }); + + describe('Topic Backlinks', () => { + let tid1; + before(async () => { + tid1 = await topics.post({ + uid: 1, + cid, + title: 'Topic backlink testing - topic 1', + content: 'Some text here for the OP', + }); + tid1 = tid1.topicData.tid; + }); + + describe('.syncBacklinks()', () => { + it('should error on invalid data', async () => { + try { + await topics.syncBacklinks(); + } catch (e) { + assert(e); + assert.strictEqual(e.message, '[[error:invalid-data]]'); + } + }); + + it('should do nothing if the post does not contain a link to a topic', async () => { + const backlinks = await topics.syncBacklinks({ + content: 'This is a post\'s content', + }); + + assert.strictEqual(backlinks, 0); + }); + + it('should create a backlink if it detects a topic link in a post', async () => { + const count = await topics.syncBacklinks({ + pid: 2, + content: `This is a link to [topic 1](${nconf.get('url')}/topic/1/abcdef)`, + }); + const events = await topics.events.get(1, 1); + const backlinks = await db.getSortedSetMembers('pid:2:backlinks'); + + assert.strictEqual(count, 1); + assert(events); + assert.strictEqual(events.length, 1); + assert(backlinks); + assert(backlinks.includes('1')); + }); + + it('should remove the backlink (but keep the event) if the post no longer contains a link to a topic', async () => { + const count = await topics.syncBacklinks({ + pid: 2, + content: 'This is a link to [nothing](http://example.org)', + }); + const events = await topics.events.get(1, 1); + const backlinks = await db.getSortedSetMembers('pid:2:backlinks'); + + assert.strictEqual(count, 0); + assert(events); + assert.strictEqual(events.length, 1); + assert(backlinks); + assert.strictEqual(backlinks.length, 0); + }); + }); + + describe('integration tests', () => { + it('should create a topic event in the referenced topic', async () => { + const topic = await topics.post({ + uid: 1, + cid, + title: 'Topic backlink testing - topic 2', + content: `Some text here for the OP – ${nconf.get('url')}/topic/${tid1}`, + }); + + const events = await topics.events.get(tid1, 1); + assert(events); + assert.strictEqual(events.length, 1); + assert.strictEqual(events[0].type, 'backlink'); + assert.strictEqual(parseInt(events[0].uid, 10), 1); + assert.strictEqual(events[0].href, `/post/${topic.postData.pid}`); + }); + + it('should not create a topic event if referenced topic is the same as current topic', async () => { + await topics.reply({ + uid: 1, + tid: tid1, + content: `Referencing itself – ${nconf.get('url')}/topic/${tid1}`, + }); + + const events = await topics.events.get(tid1, 1); + assert(events); + assert.strictEqual(events.length, 1); // should still equal 1 + }); + + it('should not show backlink events if the feature is disabled', async () => { + meta.config.topicBacklinks = 0; + + await topics.post({ + uid: 1, + cid, + title: 'Topic backlink testing - topic 3', + content: `Some text here for the OP – ${nconf.get('url')}/topic/${tid1}`, + }); + + const events = await topics.events.get(tid1, 1); + assert(events); + assert.strictEqual(events.length, 0); + }); + }); + }); }); diff --git a/test/search.js b/test/search.js index 909a1b7f2f..ee28dd3956 100644 --- a/test/search.js +++ b/test/search.js @@ -273,4 +273,20 @@ describe('Search', () => { }); }); }); + + it('should not crash without a search term', (done) => { + const qs = '/api/search'; + privileges.global.give(['groups:search:content'], 'guests', (err) => { + assert.ifError(err); + request({ + url: nconf.get('url') + qs, + json: true, + }, (err, response, body) => { + assert.ifError(err); + assert(body); + assert.strictEqual(response.statusCode, 200); + privileges.global.rescind(['groups:search:content'], 'guests', done); + }); + }); + }); }); diff --git a/test/socket.io.js b/test/socket.io.js index 7135d5a3a9..fad6591185 100644 --- a/test/socket.io.js +++ b/test/socket.io.js @@ -106,7 +106,12 @@ describe('socket.io', () => { }); it('should post a topic', (done) => { - io.emit('topics.post', { title: 'test topic title', content: 'test topic main post content', uid: adminUid, cid: cid }, (err, result) => { + io.emit('topics.post', { + title: 'test topic title', + content: 'test topic main post content', + uid: adminUid, + cid: cid, + }, (err, result) => { assert.ifError(err); assert.equal(result.user.username, 'admin'); assert.equal(result.category.cid, cid); @@ -473,9 +478,17 @@ describe('socket.io', () => { it('should toggle plugin install', function (done) { this.timeout(0); - socketAdmin.plugins.toggleInstall({ uid: adminUid }, { id: 'nodebb-plugin-location-to-map', version: 'latest' }, (err, data) => { + const oldValue = process.env.NODE_ENV; + process.env.NODE_ENV = 'development'; + socketAdmin.plugins.toggleInstall({ + uid: adminUid, + }, { + id: 'nodebb-plugin-location-to-map', + version: 'latest', + }, (err, data) => { assert.ifError(err); assert.equal(data.name, 'nodebb-plugin-location-to-map'); + process.env.NODE_ENV = oldValue; done(); }); }); @@ -507,8 +520,16 @@ describe('socket.io', () => { it('should upgrade plugin', function (done) { this.timeout(0); - socketAdmin.plugins.upgrade({ uid: adminUid }, { id: 'nodebb-plugin-location-to-map', version: 'latest' }, (err) => { + const oldValue = process.env.NODE_ENV; + process.env.NODE_ENV = 'development'; + socketAdmin.plugins.upgrade({ + uid: adminUid, + }, { + id: 'nodebb-plugin-location-to-map', + version: 'latest', + }, (err) => { assert.ifError(err); + process.env.NODE_ENV = oldValue; done(); }); }); @@ -521,7 +542,13 @@ describe('socket.io', () => { }); it('should error with invalid data', (done) => { - const data = [{ template: 'global', location: 'sidebar', widgets: [{ widget: 'html', data: { html: 'test', title: 'test', container: '' } }] }]; + const data = [ + { + template: 'global', + location: 'sidebar', + widgets: [{ widget: 'html', data: { html: 'test', title: 'test', container: '' } }], + }, + ]; socketAdmin.widgets.set({ uid: adminUid }, data, (err) => { assert.ifError(err); db.getObjectField('widgets:global', 'sidebar', (err, widgetData) => { @@ -547,6 +574,22 @@ describe('socket.io', () => { }); }); + it('should not error when resending digests', async () => { + await socketAdmin.digest.resend({ uid: adminUid }, { action: 'resend-day', uid: adminUid }); + await socketAdmin.digest.resend({ uid: adminUid }, { action: 'resend-day' }); + }); + + it('should error with invalid interval', async () => { + const oldValue = meta.config.dailyDigestFreq; + meta.config.dailyDigestFreq = 'off'; + try { + await socketAdmin.digest.resend({ uid: adminUid }, { action: 'resend-' }); + } catch (err) { + assert.strictEqual(err.message, '[[error:digest-not-enabled]]'); + } + meta.config.dailyDigestFreq = oldValue; + }); + it('should get logs', (done) => { const fs = require('fs'); const path = require('path'); @@ -706,4 +749,35 @@ describe('socket.io', () => { }); }); }); + + it('should clear caches', async () => { + await socketAdmin.cache.clear({ uid: adminUid }, { name: 'post' }); + await socketAdmin.cache.clear({ uid: adminUid }, { name: 'object' }); + await socketAdmin.cache.clear({ uid: adminUid }, { name: 'group' }); + await socketAdmin.cache.clear({ uid: adminUid }, { name: 'local' }); + }); + + it('should toggle caches', async () => { + const caches = { + post: require('../src/posts/cache'), + object: require('../src/database').objectCache, + group: require('../src/groups').cache, + local: require('../src/cache'), + }; + + await socketAdmin.cache.toggle({ uid: adminUid }, { name: 'post', enabled: !caches.post.enabled }); + if (caches.object) { + await socketAdmin.cache.toggle({ uid: adminUid }, { name: 'object', enabled: !caches.object.enabled }); + } + await socketAdmin.cache.toggle({ uid: adminUid }, { name: 'group', enabled: !caches.group.enabled }); + await socketAdmin.cache.toggle({ uid: adminUid }, { name: 'local', enabled: !caches.local.enabled }); + + // call again to return back to original state + await socketAdmin.cache.toggle({ uid: adminUid }, { name: 'post', enabled: !caches.post.enabled }); + if (caches.object) { + await socketAdmin.cache.toggle({ uid: adminUid }, { name: 'object', enabled: !caches.object.enabled }); + } + await socketAdmin.cache.toggle({ uid: adminUid }, { name: 'group', enabled: !caches.group.enabled }); + await socketAdmin.cache.toggle({ uid: adminUid }, { name: 'local', enabled: !caches.local.enabled }); + }); }); diff --git a/test/topics.js b/test/topics.js index 10a808f530..a50157cc8d 100644 --- a/test/topics.js +++ b/test/topics.js @@ -1,6 +1,7 @@ 'use strict'; const async = require('async'); +const path = require('path'); const assert = require('assert'); const validator = require('validator'); const mockdate = require('mockdate'); @@ -9,6 +10,7 @@ const request = require('request'); const util = require('util'); const db = require('./mocks/databasemock'); +const file = require('../src/file'); const topics = require('../src/topics'); const posts = require('../src/posts'); const categories = require('../src/categories'); @@ -20,7 +22,6 @@ const helpers = require('./helpers'); const socketPosts = require('../src/socket.io/posts'); const socketTopics = require('../src/socket.io/topics'); - const requestType = util.promisify((type, url, opts, cb) => { request[type](url, opts, (err, res, body) => cb(err, { res: res, body: body })); }); @@ -879,14 +880,14 @@ describe('Topic\'s', () => { }); it('should error with unprivileged user', (done) => { - socketTopics.orderPinnedTopics({ uid: 0 }, [{ tid: tid1 }, { tid: tid2 }], (err) => { + socketTopics.orderPinnedTopics({ uid: 0 }, { tid: tid1, order: 1 }, (err) => { assert.equal(err.message, '[[error:no-privileges]]'); done(); }); }); it('should not do anything if topics are not pinned', (done) => { - socketTopics.orderPinnedTopics({ uid: adminUid }, [{ tid: tid3 }], (err) => { + socketTopics.orderPinnedTopics({ uid: adminUid }, { tid: tid3, order: 1 }, (err) => { assert.ifError(err); db.isSortedSetMember(`cid:${topic.categoryId}:tids:pinned`, tid3, (err, isMember) => { assert.ifError(err); @@ -901,7 +902,7 @@ describe('Topic\'s', () => { assert.ifError(err); assert.equal(pinnedTids[0], tid2); assert.equal(pinnedTids[1], tid1); - socketTopics.orderPinnedTopics({ uid: adminUid }, [{ tid: tid1, order: 1 }, { tid: tid2, order: 0 }], (err) => { + socketTopics.orderPinnedTopics({ uid: adminUid }, { tid: tid1, order: 0 }, (err) => { assert.ifError(err); db.getSortedSetRevRange(`cid:${topic.categoryId}:tids:pinned`, 0, -1, (err, pinnedTids) => { assert.ifError(err); @@ -2860,3 +2861,17 @@ describe('Topic\'s', () => { }); }); }); + +describe('Topics\'s', async () => { + let files; + + before(async () => { + files = await file.walk(path.resolve(__dirname, './topics')); + }); + + it('subfolder tests', () => { + files.forEach((filePath) => { + require(filePath); + }); + }); +}); diff --git a/test/topicEvents.js b/test/topics/events.js similarity index 91% rename from test/topicEvents.js rename to test/topics/events.js index 889a73e5d5..8a35400587 100644 --- a/test/topicEvents.js +++ b/test/topics/events.js @@ -2,12 +2,12 @@ const assert = require('assert'); -const db = require('./mocks/databasemock'); +const db = require('../mocks/databasemock'); -const plugins = require('../src/plugins'); -const categories = require('../src/categories'); -const topics = require('../src/topics'); -const user = require('../src/user'); +const plugins = require('../../src/plugins'); +const categories = require('../../src/categories'); +const topics = require('../../src/topics'); +const user = require('../../src/user'); describe('Topic Events', () => { let fooUid; diff --git a/test/topicThumbs.js b/test/topics/thumbs.js similarity index 80% rename from test/topicThumbs.js rename to test/topics/thumbs.js index 024df284e1..2cdd1d66a9 100644 --- a/test/topicThumbs.js +++ b/test/topics/thumbs.js @@ -4,21 +4,20 @@ const fs = require('fs'); const path = require('path'); const assert = require('assert'); const nconf = require('nconf'); -const request = require('request-promise-native'); -const db = require('./mocks/databasemock'); +const db = require('../mocks/databasemock'); -const meta = require('../src/meta'); -const user = require('../src/user'); -const groups = require('../src/groups'); -const topics = require('../src/topics'); -const posts = require('../src/posts'); -const categories = require('../src/categories'); -const plugins = require('../src/plugins'); -const file = require('../src/file'); -const utils = require('../src/utils'); +const meta = require('../../src/meta'); +const user = require('../../src/user'); +const groups = require('../../src/groups'); +const topics = require('../../src/topics'); +const posts = require('../../src/posts'); +const categories = require('../../src/categories'); +const plugins = require('../../src/plugins'); +const file = require('../../src/file'); +const utils = require('../../src/utils'); -const helpers = require('./helpers'); +const helpers = require('../helpers'); describe('Topic thumbs', () => { let topicObj; @@ -38,8 +37,8 @@ describe('Topic thumbs', () => { const uuid = utils.generateUUID(); function createFiles() { - fs.closeSync(fs.openSync(path.resolve(__dirname, './uploads', thumbPaths[0]), 'w')); - fs.closeSync(fs.openSync(path.resolve(__dirname, './uploads', thumbPaths[1]), 'w')); + fs.closeSync(fs.openSync(path.resolve(__dirname, '../uploads', thumbPaths[0]), 'w')); + fs.closeSync(fs.openSync(path.resolve(__dirname, '../uploads', thumbPaths[1]), 'w')); } before(async () => { @@ -90,10 +89,10 @@ describe('Topic thumbs', () => { describe('.get()', () => { it('should return an array of thumbs', async () => { - require('../src/cache').del(`topic:${topicObj.topicData.tid}:thumbs`); + require('../../src/cache').del(`topic:${topicObj.topicData.tid}:thumbs`); const thumbs = await topics.thumbs.get(topicObj.topicData.tid); assert.deepStrictEqual(thumbs, [{ - id: 1, + id: topicObj.topicData.tid, name: 'test.png', url: `${nconf.get('relative_path')}${nconf.get('upload_url')}${relativeThumbPaths[0]}`, }]); @@ -103,7 +102,7 @@ describe('Topic thumbs', () => { const thumbs = await topics.thumbs.get([topicObj.topicData.tid, topicObj.topicData.tid + 1]); assert.deepStrictEqual(thumbs, [ [{ - id: 1, + id: topicObj.topicData.tid, name: 'test.png', url: `${nconf.get('relative_path')}${nconf.get('upload_url')}${relativeThumbPaths[0]}`, }], @@ -133,7 +132,7 @@ describe('Topic thumbs', () => { path: relativeThumbPaths[0], }); - const exists = await db.isSortedSetMember(`topic:2:thumbs`, relativeThumbPaths[0]); + const exists = await db.isSortedSetMember(`topic:${tid}:thumbs`, relativeThumbPaths[0]); assert(exists); }); @@ -154,17 +153,17 @@ describe('Topic thumbs', () => { path: relativeThumbPaths[2], }); - const exists = await db.isSortedSetMember(`topic:2:thumbs`, relativeThumbPaths[2]); + const exists = await db.isSortedSetMember(`topic:${tid}:thumbs`, relativeThumbPaths[2]); assert(exists); }); it('should have a score equal to the number of thumbs prior to addition', async () => { - const scores = await db.sortedSetScores('topic:2:thumbs', [relativeThumbPaths[0], relativeThumbPaths[2]]); + const scores = await db.sortedSetScores(`topic:${tid}:thumbs`, [relativeThumbPaths[0], relativeThumbPaths[2]]); assert.deepStrictEqual(scores, [0, 1]); }); it('should update the relevant topic hash with the number of thumbnails', async () => { - const numThumbs = await topics.getTopicField(2, 'numThumbs'); + const numThumbs = await topics.getTopicField(tid, 'numThumbs'); assert.strictEqual(parseInt(numThumbs, 10), 2); }); @@ -174,7 +173,7 @@ describe('Topic thumbs', () => { path: relativeThumbPaths[0], }); - const score = await db.sortedSetScore(`topic:2:thumbs`, relativeThumbPaths[0]); + const score = await db.sortedSetScore(`topic:${tid}:thumbs`, relativeThumbPaths[0]); assert(isFinite(score)); // exists in set assert.strictEqual(score, 2); @@ -187,7 +186,7 @@ describe('Topic thumbs', () => { score: 0, }); - const score = await db.sortedSetScore(`topic:2:thumbs`, relativeThumbPaths[0]); + const score = await db.sortedSetScore(`topic:${tid}:thumbs`, relativeThumbPaths[0]); assert(isFinite(score)); // exists in set assert.strictEqual(score, 0); @@ -203,27 +202,25 @@ describe('Topic thumbs', () => { const uploads = await posts.uploads.list(mainPid); assert(uploads.includes(path.basename(relativeThumbPaths[0]))); }); - }); - describe('.migrate()', () => { it('should combine the thumbs uploaded to a UUID zset and combine it with a topic\'s thumb zset', async () => { - await topics.thumbs.migrate(uuid, 2); + await topics.thumbs.migrate(uuid, tid); - const thumbs = await topics.thumbs.get(2); + const thumbs = await topics.thumbs.get(tid); assert.strictEqual(thumbs.length, 3); assert.deepStrictEqual(thumbs, [ { - id: 2, + id: tid, name: 'test.png', url: `${nconf.get('relative_path')}${nconf.get('upload_url')}${relativeThumbPaths[0]}`, }, { - id: 2, + id: tid, name: 'example.org', url: 'https://example.org', }, { - id: 2, + id: tid, name: 'test2.png', url: `${nconf.get('relative_path')}${nconf.get('upload_url')}${relativeThumbPaths[1]}`, }, @@ -283,7 +280,7 @@ describe('Topic thumbs', () => { }); it('should succeed with a valid tid', (done) => { - helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/1/thumbs`, path.join(__dirname, './files/test.png'), {}, adminJar, adminCSRF, (err, res, body) => { + helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/1/thumbs`, path.join(__dirname, '../files/test.png'), {}, adminJar, adminCSRF, (err, res, body) => { assert.ifError(err); assert.strictEqual(res.statusCode, 200); done(); @@ -291,7 +288,7 @@ describe('Topic thumbs', () => { }); it('should succeed with a uuid', (done) => { - helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/${uuid}/thumbs`, path.join(__dirname, './files/test.png'), {}, adminJar, adminCSRF, (err, res, body) => { + helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/${uuid}/thumbs`, path.join(__dirname, '../files/test.png'), {}, adminJar, adminCSRF, (err, res, body) => { assert.ifError(err); assert.strictEqual(res.statusCode, 200); done(); @@ -309,7 +306,7 @@ describe('Topic thumbs', () => { }); await new Promise((resolve) => { - helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/${uuid}/thumbs`, path.join(__dirname, './files/test.png'), {}, adminJar, adminCSRF, (err, res, body) => { + helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/${uuid}/thumbs`, path.join(__dirname, '../files/test.png'), {}, adminJar, adminCSRF, (err, res, body) => { assert.ifError(err); assert.strictEqual(res.statusCode, 200); resolve(); @@ -320,7 +317,7 @@ describe('Topic thumbs', () => { }); it('should fail with a non-existant tid', (done) => { - helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/3/thumbs`, path.join(__dirname, './files/test.png'), {}, adminJar, adminCSRF, (err, res, body) => { + helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/4/thumbs`, path.join(__dirname, '../files/test.png'), {}, adminJar, adminCSRF, (err, res, body) => { assert.ifError(err); assert.strictEqual(res.statusCode, 404); done(); @@ -328,7 +325,7 @@ describe('Topic thumbs', () => { }); it('should fail when garbage is passed in', (done) => { - helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/abracadabra/thumbs`, path.join(__dirname, './files/test.png'), {}, adminJar, adminCSRF, (err, res, body) => { + helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/abracadabra/thumbs`, path.join(__dirname, '../files/test.png'), {}, adminJar, adminCSRF, (err, res, body) => { assert.ifError(err); assert.strictEqual(res.statusCode, 404); done(); @@ -336,7 +333,7 @@ describe('Topic thumbs', () => { }); it('should fail when calling user cannot edit the tid', (done) => { - helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/1/thumbs`, path.join(__dirname, './files/test.png'), {}, fooJar, fooCSRF, (err, res, body) => { + helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/2/thumbs`, path.join(__dirname, '../files/test.png'), {}, fooJar, fooCSRF, (err, res, body) => { assert.ifError(err); assert.strictEqual(res.statusCode, 403); done(); @@ -346,7 +343,7 @@ describe('Topic thumbs', () => { it('should fail if thumbnails are not enabled', (done) => { meta.config.allowTopicsThumbnail = 0; - helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/${uuid}/thumbs`, path.join(__dirname, './files/test.png'), {}, adminJar, adminCSRF, (err, res, body) => { + helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/${uuid}/thumbs`, path.join(__dirname, '../files/test.png'), {}, adminJar, adminCSRF, (err, res, body) => { assert.ifError(err); assert.strictEqual(res.statusCode, 503); assert(body && body.status); @@ -358,7 +355,7 @@ describe('Topic thumbs', () => { it('should fail if file is not image', (done) => { meta.config.allowTopicsThumbnail = 1; - helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/${uuid}/thumbs`, path.join(__dirname, './files/503.html'), {}, adminJar, adminCSRF, (err, res, body) => { + helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/${uuid}/thumbs`, path.join(__dirname, '../files/503.html'), {}, adminJar, adminCSRF, (err, res, body) => { assert.ifError(err); assert.strictEqual(res.statusCode, 500); assert(body && body.status); diff --git a/test/translator.js b/test/translator.js index c5479ed7e8..02bc6de1ea 100644 --- a/test/translator.js +++ b/test/translator.js @@ -1,5 +1,6 @@ 'use strict'; +// For tests relating to Transifex configuration, check i18n.js const assert = require('assert'); const shim = require('../public/src/modules/translator'); @@ -34,6 +35,11 @@ describe('Translator shim', () => { const translated = await shim.translate('', 'en-GB'); assert.strictEqual(translated, ''); }); + + it('should not allow path traversal', async () => { + const t = await shim.translate('[[../../../../config:secret]]'); + assert.strictEqual(t, 'secret'); + }); }); }); diff --git a/test/uploads.js b/test/uploads.js index 6fbf1e4f11..4def21ed17 100644 --- a/test/uploads.js +++ b/test/uploads.js @@ -196,11 +196,11 @@ describe('Upload Controllers', () => { }); it('should fail to upload image to post if image dimensions are too big', (done) => { - helpers.uploadFile(`${nconf.get('url')}/api/post/upload`, path.join(__dirname, '../test/files/toobig.jpg'), {}, jar, csrf_token, (err, res, body) => { + helpers.uploadFile(`${nconf.get('url')}/api/post/upload`, path.join(__dirname, '../test/files/toobig.png'), {}, jar, csrf_token, (err, res, body) => { assert.ifError(err); assert.strictEqual(res.statusCode, 500); assert(body && body.status && body.status.message); - assert.strictEqual(body.status.message, 'Input image exceeds pixel limit'); + assert.strictEqual(body.status.message, 'Image dimensions are too big'); done(); }); }); diff --git a/test/user.js b/test/user.js index 4c9a61cd86..671cc06d20 100644 --- a/test/user.js +++ b/test/user.js @@ -5,6 +5,7 @@ const async = require('async'); const fs = require('fs'); const path = require('path'); const nconf = require('nconf'); +const validator = require('validator'); const request = require('request'); const requestAsync = require('request-promise-native'); const jwt = require('jsonwebtoken'); @@ -1070,6 +1071,28 @@ describe('User', () => { }); }); + it('should let you set an external image', async () => { + const token = await helpers.getCsrfToken(jar); + const body = await requestAsync(`${nconf.get('url')}/api/v3/users/${uid}/picture`, { + jar, + method: 'put', + json: true, + headers: { + 'x-csrf-token': token, + }, + body: { + type: 'external', + url: 'https://example.org/picture.jpg', + }, + }); + + assert(body && body.status && body.response); + assert.strictEqual(body.status.code, 'ok'); + + const picture = await User.getUserField(uid, 'picture'); + assert.strictEqual(picture, validator.escape('https://example.org/picture.jpg')); + }); + it('should fail to change user picture with invalid data', (done) => { socketUser.changePicture({ uid: uid }, null, (err) => { assert.equal(err.message, '[[error:invalid-data]]'); @@ -2439,6 +2462,22 @@ describe('User', () => { assert.strictEqual(parseInt(confirmed, 10), 1); assert.strictEqual(isVerified, true); }); + + it('should remove the email from a different account if the email is already in use', async () => { + const email = 'confirm2@me.com'; + const uid = await User.create({ + username: 'confirme3', + }); + + const oldUid = await db.sortedSetScore('email:uid', email); + const code = await User.email.sendValidationEmail(uid, email); + await User.email.confirmByCode(code); + + const oldUserData = await User.getUserData(oldUid); + + assert.strictEqual((await db.sortedSetScore('email:uid', email)), uid); + assert.strictEqual(oldUserData.email, ''); + }); }); describe('user jobs', () => {