diff --git a/README.md b/README.md index 4684d5c73a..4fc6d9aeb9 100644 --- a/README.md +++ b/README.md @@ -12,18 +12,26 @@ Additional functionality is enabled through the use of third-party plugins. * [Get NodeBB](http://www.nodebb.org/ "NodeBB") * [Demo & Meta Discussion](http://community.nodebb.org) -* [NodeBB Blog](http://blog.nodebb.org) * [Documentation & Installation Instructions](http://docs.nodebb.org) +* [Help translate NodeBB](https://www.transifex.com/projects/p/nodebb/) +* [NodeBB Blog](http://blog.nodebb.org) * [Join us on IRC](https://kiwiirc.com/client/irc.freenode.net/nodebb) - #nodebb on Freenode * [Follow us on Twitter](http://www.twitter.com/NodeBB/ "NodeBB Twitter") * [Like us on Facebook](http://www.facebook.com/NodeBB/ "NodeBB Facebook") -* [Get Plugins](http://community.nodebb.org/category/7/nodebb-plugins "NodeBB Plugins") -* [Get Themes](http://community.nodebb.org/category/10/nodebb-themes "NodeBB Themes") -* [Help translate NodeBB](https://www.transifex.com/projects/p/nodebb/) ## Screenshots -[](http://i.imgur.com/FLOUuIq.png) [](http://i.imgur.com/Ud1LrfI.png) [](http://i.imgur.com/ZC8W39a.png) [](http://i.imgur.com/o90kVPi.png) [](http://i.imgur.com/AaRRrU2.png) [](http://i.imgur.com/LmHtPho.png) [](http://i.imgur.com/paiJPJk.jpg) [](http://i.imgur.com/ZfavPHD.png) [](http://i.imgur.com/8OLssij.png) [](http://i.imgur.com/JKOc0LZ.png) +[![](http://i.imgur.com/VCoOFyqb.png)](http://i.imgur.com/VCoOFyq.png) +[![](http://i.imgur.com/FLOUuIqb.png)](http://i.imgur.com/FLOUuIq.png) +[![](http://i.imgur.com/Ud1LrfIb.png)](http://i.imgur.com/Ud1LrfI.png) +[![](http://i.imgur.com/h6yZ66sb.png)](http://i.imgur.com/h6yZ66s.png) +[![](http://i.imgur.com/o90kVPib.png)](http://i.imgur.com/o90kVPi.png) +[![](http://i.imgur.com/AaRRrU2b.png)](http://i.imgur.com/AaRRrU2.png) +[![](http://i.imgur.com/LmHtPhob.png)](http://i.imgur.com/LmHtPho.png) +[![](http://i.imgur.com/paiJPJkb.jpg)](http://i.imgur.com/paiJPJk.jpg) + +[![](http://i.imgur.com/8OLssij.png)](http://i.imgur.com/8OLssij.png) +[![](http://i.imgur.com/JKOc0LZ.png)](http://i.imgur.com/JKOc0LZ.png) ## How can I follow along/contribute? @@ -38,7 +46,7 @@ Additional functionality is enabled through the use of third-party plugins. NodeBB requires the following software to be installed: * A version of Node.js at least 0.10 or greater -* Redis, version 2.6 or greater **or** MongoDB, version 2.6 or greater +* Redis, version 2.8.9 or greater **or** MongoDB, version 2.6 or greater * nginx, version 1.3.13 or greater (**only if** intending to use nginx to proxy requests to a NodeBB) ## Installation @@ -62,4 +70,6 @@ Detailed upgrade instructions are listed in [Upgrading NodeBB](https://docs.node ## License -NodeBB is licensed under the **GNU General Public License v3 (GPL-3)** (http://www.gnu.org/copyleft/gpl.html) +NodeBB is licensed under the **GNU General Public License v3 (GPL-3)** (http://www.gnu.org/copyleft/gpl.html). + +Interested in a sublicense agreement for use of NodeBB in a non-free/restrictive environment? Contact us at sales@nodebb.org. \ No newline at end of file diff --git a/app.js b/app.js index 8ff5647023..78afb10b9e 100644 --- a/app.js +++ b/app.js @@ -29,11 +29,11 @@ var fs = require('fs'), async = require('async'), semver = require('semver'), winston = require('winston'), + colors = require('colors'), path = require('path'), pkg = require('./package.json'), utils = require('./public/src/utils.js'); - global.env = process.env.NODE_ENV || 'production'; winston.remove(winston.transports.Console); @@ -108,6 +108,7 @@ function loadConfig() { function start() { loadConfig(); + var db = require('./src/database'); // nconf defaults, if not set in config if (!nconf.get('upload_path')) { @@ -116,6 +117,7 @@ function start() { // Parse out the relative_url and other goodies from the configured URL var urlObject = url.parse(nconf.get('url')); var relativePath = urlObject.pathname !== '/' ? urlObject.pathname : ''; + nconf.set('base_url', urlObject.protocol + '//' + urlObject.host); nconf.set('use_port', !!urlObject.port); nconf.set('relative_path', relativePath); nconf.set('port', urlObject.port || nconf.get('port') || nconf.get('PORT') || 4567); @@ -175,9 +177,8 @@ function start() { }); async.waterfall([ - function(next) { - require('./src/database').init(next); - }, + async.apply(db.init), + async.apply(db.checkCompatibility), function(next) { require('./src/meta').configs.init(next); }, @@ -203,7 +204,12 @@ function start() { } ], function(err) { if (err) { - winston.error(err.stack); + if (err.stacktrace !== false) { + winston.error(err.stack); + } else { + winston.error(err.message); + } + process.exit(); } }); @@ -274,17 +280,19 @@ function reset() { process.exit(); } - if (nconf.get('theme')) { + if (nconf.get('t')) { resetThemes(); - } else if (nconf.get('plugin')) { - resetPlugin(nconf.get('plugin')); - } else if (nconf.get('plugins')) { - resetPlugins(); - } else if (nconf.get('widgets')) { + } else if (nconf.get('p')) { + if (nconf.get('p') === true) { + resetPlugins(); + } else { + resetPlugin(nconf.get('p')); + } + } else if (nconf.get('w')) { resetWidgets(); - } else if (nconf.get('settings')) { + } else if (nconf.get('s')) { resetSettings(); - } else if (nconf.get('all')) { + } else if (nconf.get('a')) { require('async').series([resetWidgets, resetThemes, resetPlugins, resetSettings], function(err) { if (!err) { winston.info('[reset] Reset complete.'); @@ -294,10 +302,17 @@ function reset() { process.exit(); }); } else { - winston.warn('[reset] Nothing reset.'); - winston.info('Use ./nodebb reset {theme|plugins|widgets|settings|all}'); - winston.info(' or'); - winston.info('Use ./nodebb reset plugin="nodebb-plugin-pluginName"'); + process.stdout.write('\nNodeBB Reset\n'.bold); + process.stdout.write('No arguments passed in, so nothing was reset.\n\n'.yellow); + process.stdout.write('Use ./nodebb reset ' + '{-t|-p|-w|-s|-a}\n'.red); + process.stdout.write(' -t\tthemes\n'); + process.stdout.write(' -p\tplugins\n'); + process.stdout.write(' -w\twidgets\n'); + process.stdout.write(' -s\tsettings\n'); + process.stdout.write(' -a\tall of the above\n'); + + process.stdout.write('\nPlugin reset flag (-p) can take a single argument\n'); + process.stdout.write(' e.g. ./nodebb reset -p nodebb-plugin-mentions\n'); process.exit(); } }); diff --git a/install/data/defaults.json b/install/data/defaults.json index 9a0d6eb1ec..c8bcdb9008 100644 --- a/install/data/defaults.json +++ b/install/data/defaults.json @@ -1,106 +1,28 @@ -[ - { - "field": "title", - "value": "NodeBB" - }, - { - "field": "showSiteTitle", - "value": "1" - }, - { - "field": "postDelay", - "value": 10 - }, - { - "field": "initialPostDelay", - "value": 10 - }, - { - "field": "newbiePostDelay", - "value": 120 - }, - { - "field": "newbiePostDelayThreshold", - "value": 3 - }, - { - "field": "minimumPostLength", - "value": 8 - }, - { - "field": "maximumPostLength", - "value": 32767 - }, - { - "field": "allowGuestSearching", - "value": 0 - }, - { - "field": "allowTopicsThumbnail", - "value": 0 - }, - { - "field": "allowRegistration", - "value": 1 - }, - { - "field": "allowLocalLogin", - "value": 1 - }, - { - "field": "allowAccountDelete", - "value": 1 - }, - { - "field": "allowFileUploads", - "value": 0 - }, - { - "field": "maximumFileSize", - "value": 2048 - }, - { - "field": "minimumTitleLength", - "value": 3 - }, - { - "field": "maximumTitleLength", - "value": 255 - }, - { - "field": "minimumUsernameLength", - "value": 2 - }, - { - "field": "maximumUsernameLength", - "value": 16 - }, - { - "field": "minimumPasswordLength", - "value": 6 - }, - { - "field": "maximumSignatureLength", - "value": 255 - }, - { - "field": "maximumAboutMeLength", - "value": 1000 - }, - { - "field": "maximumProfileImageSize", - "value": 256 - }, - { - "field": "profileImageDimension", - "value": 128 - }, - { - "field": "requireEmailConfirmation", - "value": 0 - }, - { - "field": "profile:allowProfileImageUploads", - "value": 1 - } -] +{ + "title": "NodeBB", + "showSiteTitle": 1, + "postDelay": 10, + "initialPostDelay": 10, + "newbiePostDelay": 120, + "newbiePostDelayThreshold": 3, + "minimumPostLength": 8, + "maximumPostLength": 32767, + "allowGuestSearching": 0, + "allowTopicsThumbnail": 0, + "registrationType": "normal", + "allowLocalLogin": 1, + "allowAccountDelete": 1, + "allowFileUploads": 0, + "maximumFileSize": 2048, + "minimumTitleLength": 3, + "maximumTitleLength": 255, + "minimumUsernameLength": 2, + "maximumUsernameLength": 16, + "minimumPasswordLength": 6, + "maximumSignatureLength": 255, + "maximumAboutMeLength": 1000, + "maximumProfileImageSize": 256, + "profileImageDimension": 128, + "requireEmailConfirmation": 0, + "profile:allowProfileImageUploads": 1 +} diff --git a/install/web.js b/install/web.js index 0587238244..0c7fb2521f 100644 --- a/install/web.js +++ b/install/web.js @@ -41,8 +41,7 @@ web.install = function(port) { function launchExpress(port) { server = app.listen(port, function() { - var host = server.address().address; - winston.info('Web installer listening on http://%s:%s', host, port); + winston.info('Web installer listening on http://%s:%s', '0.0.0.0', port); }); } @@ -104,6 +103,10 @@ function launch(req, res) { stdio: ['ignore', 'ignore', 'ignore'] }); + process.stdout.write('\nStarting NodeBB\n'); + process.stdout.write(' "./nodebb stop" to stop the NodeBB server\n'); + process.stdout.write(' "./nodebb log" to view server output\n'); + process.stdout.write(' "./nodebb restart" to restart NodeBB\n'); child.unref(); process.exit(0); diff --git a/loader.js b/loader.js index 5baf636dbf..c79756f09e 100644 --- a/loader.js +++ b/loader.js @@ -17,7 +17,7 @@ nconf.argv().env().file({ var pidFilePath = __dirname + '/pidfile', output = logrotate({ file: __dirname + '/logs/output.log', size: '1m', keep: 3, compress: true }), - silent = nconf.get('silent') !== false, + silent = nconf.get('silent') === 'false' ? false : nconf.get('silent') !== false, numProcs, workers = [], @@ -248,7 +248,7 @@ Loader.notifyWorkers = function(msg, worker_pid) { fs.open(path.join(__dirname, 'config.json'), 'r', function(err) { if (!err) { - if (nconf.get('daemon') !== false) { + if (nconf.get('daemon') !== 'false' && nconf.get('daemon') !== false) { if (fs.existsSync(pidFilePath)) { try { var pid = fs.readFileSync(pidFilePath, { encoding: 'utf-8' }); diff --git a/nodebb b/nodebb index 6e0e4f679e..8e6a32ddfa 100755 --- a/nodebb +++ b/nodebb @@ -1,137 +1,167 @@ -#!/bin/bash +#!/usr/bin/env node -# $0 script path -# $1 action -# $2 subaction +var colors = require('colors'), + cproc = require('child_process'), + argv = require('minimist')(process.argv.slice(2)), + fs = require('fs'), + async = require('async'), + touch = require('touch'), + npm = require('npm'); -node="$(which nodejs 2>/dev/null)"; -if [ $? -gt 0 ]; - then node="$(which node)"; -fi +var getRunningPid = function(callback) { + fs.readFile(__dirname + '/pidfile', { + encoding: 'utf-8' + }, function(err, pid) { + if (err) { + return callback(err); + } -function pidExists() { - if [ -e "pidfile" ]; - then - if ps -p $(cat pidfile) > /dev/null - then return 1; - else - rm ./pidfile; - return 0; - fi - else - return 0; - fi + try { + process.kill(parseInt(pid, 10), 0); + callback(null, parseInt(pid, 10)); + } catch(e) { + callback(e); + } + }); + }; + +switch(process.argv[2]) { + case 'status': + getRunningPid(function(err, pid) { + if (!err) { + process.stdout.write('\nNodeBB Running '.bold + '(pid '.cyan + pid.toString().cyan + ')\n'.cyan); + process.stdout.write('\t"' + './nodebb stop'.yellow + '" to stop the NodeBB server\n'); + process.stdout.write('\t"' + './nodebb log'.yellow + '" to view server output\n'); + process.stdout.write('\t"' + './nodebb restart'.yellow + '" to restart NodeBB\n\n'); + } else { + process.stdout.write('\nNodeBB is not running\n'.bold); + process.stdout.write('\t"' + './nodebb start'.yellow + '" to launch the NodeBB server\n\n'); + } + }) + break; + + case 'start': + process.stdout.write('\nStarting NodeBB\n'.bold); + process.stdout.write(' "' + './nodebb stop'.yellow + '" to stop the NodeBB server\n'); + process.stdout.write(' "' + './nodebb log'.yellow + '" to view server output\n'); + process.stdout.write(' "' + './nodebb restart'.yellow + '" to restart NodeBB\n\n'); + + // Spawn a new NodeBB process + cproc.fork(__dirname + '/loader.js', { + env: process.env + }); + break; + + case 'stop': + getRunningPid(function(err, pid) { + if (!err) { + process.kill(pid, 'SIGTERM'); + process.stdout.write('Stopping NodeBB. Goodbye!\n') + } else { + process.stdout.write('NodeBB is already stopped.\n'); + } + }); + break; + + case 'restart': + getRunningPid(function(err, pid) { + if (!err) { + process.kill(pid, 'SIGHUP'); + } else { + process.stdout.write('NodeBB could not be restarted, as a running instance could not be found.'); + } + }); + break; + + case 'reload': + getRunningPid(function(err, pid) { + if (!err) { + process.kill(pid, 'SIGUSR2'); + } else { + process.stdout.write('NodeBB could not be reloaded, as a running instance could not be found.'); + } + }); + break; + + case 'dev': + process.env.NODE_ENV = 'development'; + cproc.fork(__dirname + '/loader.js', ['--no-daemon', '--no-silent'], { + env: process.env + }); + break; + + case 'log': + process.stdout.write('\nType '.red + 'Ctrl-C '.bold + 'to exit\n\n'.red); + cproc.spawn('tail', ['-F', './logs/output.log'], { + cwd: __dirname, + stdio: 'inherit' + }); + break; + + case 'setup': + cproc.fork('app.js', ['--setup'], { + cwd: __dirname, + silent: false + }); + break; + + case 'reset': + var args = process.argv.slice(0); + args.unshift('--reset'); + + cproc.fork('app.js', args, { + cwd: __dirname, + silent: false + }); + break; + + case 'upgrade': + async.series([ + function(next) { + process.stdout.write('1. '.bold + 'Bringing base dependencies up to date\n'.yellow); + npm.load({ + loglevel: 'silent' + }, function() { + npm.commands.install(next); + }); + }, + function(next) { + process.stdout.write('2. '.bold + 'Updating NodeBB data store schema\n'.yellow); + var upgradeProc = cproc.fork('app.js', ['--upgrade'], { + cwd: __dirname, + silent: false + }); + + upgradeProc.on('close', next) + }, + function(next) { + process.stdout.write('3. '.bold + 'Storing upgrade date in "package.json"\n'.yellow); + touch(__dirname + '/package.json', {}, next); + } + ], function(err) { + if (err) { + process.stdout.write('\nError'.red + ': ' + err.message + '\n'); + } else { + var message = 'NodeBB Upgrade Complete!', + spaces = new Array(Math.floor(process.stdout.columns / 2) - (message.length / 2) + 1).join(' '); + process.stdout.write('\n' + spaces + message.green.bold + '\n\n'); + } + }); + break; + + default: + process.stdout.write('\nWelcome to NodeBB\n\n'.bold); + process.stdout.write('Usage: ./nodebb {start|stop|reload|restart|log|setup|reset|upgrade|dev}\n\n'); + process.stdout.write('\t' + 'start'.yellow + '\tStart the NodeBB server\n'); + process.stdout.write('\t' + 'stop'.yellow + '\tStops the NodeBB server\n'); + process.stdout.write('\t' + 'reload'.yellow + '\tRestarts NodeBB\n'); + process.stdout.write('\t' + 'restart'.yellow + '\tRestarts NodeBB\n'); + process.stdout.write('\t' + 'log'.yellow + '\tOpens the logging interface (useful for debugging)\n'); + process.stdout.write('\t' + 'setup'.yellow + '\tRuns the NodeBB setup script\n'); + process.stdout.write('\t' + 'reset'.yellow + '\tDisables all plugins, restores the default theme.\n'); + process.stdout.write('\t' + 'upgrade'.yellow + '\tRun NodeBB upgrade scripts, ensure packages are up-to-date\n'); + process.stdout.write('\t' + 'dev'.yellow + '\tStart NodeBB in interactive development mode\n'); + process.stdout.write('\t' + 'watch'.yellow + '\tStart NodeBB in development mode and watch for changes\n'); + process.stdout.write('\n'); + break; } - -case "$1" in - start) - echo "Starting NodeBB"; - echo " \"./nodebb stop\" to stop the NodeBB server"; - echo " \"./nodebb log\" to view server output"; - - # Start the loader daemon - "$node" loader "$@" - ;; - - stop) - pidExists; - if [ 0 -eq $? ]; - then - echo "NodeBB is already stopped."; - else - echo "Stopping NodeBB. Goodbye!"; - kill $(cat pidfile); - fi - ;; - - restart) - pidExists; - if [ 0 -eq $? ]; - then - echo "NodeBB could not be restarted, as a running instance could not be found."; - else - echo "Restarting NodeBB."; - kill -1 $(cat pidfile); - fi - ;; - - reload) - pidExists; - if [ 0 -eq $? ]; - then - echo "NodeBB could not be reloaded, as a running instance could not be found."; - else - echo "Reloading NodeBB."; - kill -12 $(cat pidfile); - fi - ;; - - status) - pidExists; - if [ 0 -eq $? ]; - then - echo "NodeBB is not running"; - echo " \"./nodebb start\" to launch the NodeBB server"; - else - echo "NodeBB Running (pid $(cat pidfile))"; - echo " \"./nodebb stop\" to stop the NodeBB server"; - echo " \"./nodebb log\" to view server output"; - echo " \"./nodebb restart\" to restart NodeBB"; - fi - ;; - - log) - clear; - tail -F ./logs/output.log; - ;; - - upgrade) - npm install - # ls -d node_modules/nodebb* | xargs -n1 basename | xargs npm install - # ls -d node_modules/nodebb* | xargs -n1 basename | xargs npm update - npm i nodebb-theme-vanilla nodebb-theme-lavender nodebb-widget-essentials - "$node" app --upgrade - touch package.json - ;; - - setup) - "$node" app --setup "$@" - ;; - - reset) - "$node" app --reset --$2 - ;; - - dev) - echo "Launching NodeBB in \"development\" mode." - echo "To run the production build of NodeBB, please use \"forever\"." - echo "More Information: https://docs.nodebb.org/en/latest/running/index.html" - NODE_ENV=development "$node" loader --no-daemon --no-silent "$@" - ;; - - watch) - echo "***************************************************************************" - echo "WARNING: ./nodebb watch will be deprecated soon. Please use grunt: " - echo "https://docs.nodebb.org/en/latest/running/index.html#grunt-development" - echo "***************************************************************************" - NODE_ENV=development supervisor -q --ignore public/templates,public/nodebb.min.js,public/nodebb.min.js.map --extensions 'node|js|tpl|less' -- app "$@" - ;; - - *) - echo "Welcome to NodeBB" - echo $"Usage: $0 {start|stop|reload|restart|log|setup|reset|upgrade|dev|watch}" - echo '' - column -s ' ' -t <<< ' - start Start the NodeBB server - stop Stops the NodeBB server - reload Restarts NodeBB - restart Restarts NodeBB - log Opens the logging interface (useful for debugging) - setup Runs the NodeBB setup script - reset Disables all plugins, restores the default theme. - upgrade Run NodeBB upgrade scripts, ensure packages are up-to-date - dev Start NodeBB in interactive development mode - watch Start NodeBB in development mode and watch for changes - ' - exit 1 -esac diff --git a/package.json b/package.json index 0428d04687..60b101277c 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "name": "nodebb", - "license": "GPLv3 or later", + "license": "GPL-3.0", "description": "NodeBB Forum", - "version": "0.7.0-dev", + "version": "0.7.1-dev", "homepage": "http://www.nodebb.org", "repository": { "type": "git", @@ -17,6 +17,7 @@ "async": "~0.9.0", "bcryptjs": "~2.1.0", "body-parser": "^1.9.0", + "colors": "^1.1.0", "compression": "^1.1.0", "connect-ensure-login": "^0.1.1", "connect-flash": "^0.1.1", @@ -27,28 +28,29 @@ "daemon": "~1.1.0", "express": "^4.9.5", "express-session": "^1.8.2", - "gm": "1.17.0", + "lwip": "0.0.7", "gravatar": "^1.1.0", "heapdump": "^0.3.0", "less": "^2.0.0", "logrotate-stream": "^0.2.3", "lru-cache": "^2.6.1", "mime": "^1.3.4", + "minimist": "^1.1.1", "mkdirp": "~0.5.0", "mmmagic": "^0.3.13", "morgan": "^1.3.2", "nconf": "~0.7.1", - "nodebb-plugin-dbsearch": "^0.2.5", - "nodebb-plugin-emoji-extended": "^0.4.1-4", - "nodebb-plugin-markdown": "^2.1.0", - "nodebb-plugin-mentions": "^0.11.0", - "nodebb-plugin-soundpack-default": "~0.1.1", + "nodebb-plugin-dbsearch": "^0.2.12", + "nodebb-plugin-emoji-extended": "^0.4.8", + "nodebb-plugin-markdown": "^3.0.0", + "nodebb-plugin-mentions": "^0.11.4", + "nodebb-plugin-soundpack-default": "^0.1.1", "nodebb-plugin-spam-be-gone": "^0.4.0", - "nodebb-theme-lavender": "^1.0.28", - "nodebb-theme-vanilla": "^1.0.120", - "nodebb-theme-persona": "^0.1.39", - "nodebb-widget-essentials": "^1.0.0", "nodebb-rewards-essentials": "^0.0.1", + "nodebb-theme-lavender": "^1.0.42", + "nodebb-theme-persona": "^0.2.0", + "nodebb-theme-vanilla": "^1.1.0", + "nodebb-widget-essentials": "^1.0.2", "npm": "^2.1.4", "passport": "^0.2.1", "passport-local": "1.0.0", @@ -64,9 +66,11 @@ "socket.io-redis": "^0.1.3", "socketio-wildcard": "~0.1.1", "string": "^3.0.0", - "templates.js": "^0.2.2", + "templates.js": "^0.2.6", + "touch": "0.0.3", "uglify-js": "git+https://github.com/julianlam/UglifyJS2.git", "underscore": "~1.8.3", + "underscore.deep": "^0.5.1", "validator": "^3.30.0", "winston": "^0.9.0", "xregexp": "~2.0.0" diff --git a/public/language/ar/category.json b/public/language/ar/category.json index 6a975425c0..b1732ff8ba 100644 --- a/public/language/ar/category.json +++ b/public/language/ar/category.json @@ -1,11 +1,11 @@ { "new_topic_button": "موضوع جديد", - "guest-login-post": "المرجو تسجيل الدخول أوَّلا", + "guest-login-post": "يجب عليك تسجيل الدخول للرد", "no_topics": "لا توجد مواضيع في هذه الفئةلم لا تحاول إنشاء موضوع؟
", "browsing": "تصفح", "no_replies": "لم يرد أحد", "share_this_category": "انشر هذه الفئة", - "watch": "Watch", + "watch": "متابعة", "ignore": "تجاهل", "watch.message": "You are now watching updates from this category", "ignore.message": "You are now ignoring updates from this category" diff --git a/public/language/ar/email.json b/public/language/ar/email.json index d295a3a70f..2eeef2c6cc 100644 --- a/public/language/ar/email.json +++ b/public/language/ar/email.json @@ -1,5 +1,5 @@ { - "password-reset-requested": "تم طلب إعادة تعيين كلمة السر - %1!", + "password-reset-requested": "تم طلب إعادة تعيين كلمة المرور - %1!", "welcome-to": "مرحبًا بك في %1", "greeting_no_name": "مرحبًا", "greeting_with_name": "مرحبًا بك يا %1", diff --git a/public/language/ar/error.json b/public/language/ar/error.json index 4273f07874..2e6908cbe4 100644 --- a/public/language/ar/error.json +++ b/public/language/ar/error.json @@ -1,8 +1,8 @@ { "invalid-data": "بيانات غير صالحة", "not-logged-in": "لم تقم بتسجيل الدخول", - "account-locked": "تم إقفال حسابكم مؤقتًا.", - "search-requires-login": "البحث في المنتدى يستلزم توفرك على حساب! المرجو تسجيل دخولك أو إنشاء حساب!", + "account-locked": "تم حظر حسابك مؤقتًا.", + "search-requires-login": "Searching requires an account - please login or register.", "invalid-cid": "قائمة غير موجودة", "invalid-tid": "موضوع غير متواجد", "invalid-pid": "رد غير موجود", @@ -25,7 +25,7 @@ "username-too-short": "اسم المستخدم قصير.", "username-too-long": "اسم المستخدم طويل", "user-banned": "المستخدم محظور", - "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", + "user-too-new": "عذرا, يجب أن تنتظر 1% ثواني قبل قيامك بأول مشاركة", "no-category": "قائمة غير موجودة", "no-topic": "موضوع غير موجود", "no-post": "رد غير موجود", @@ -68,9 +68,10 @@ "invalid-file": "ملف غير مقبول", "uploads-are-disabled": "رفع الملفات غير مفعل", "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", + "about-me-too-long": "Sorry, your about me cannot be longer than %1 character(s).", "cant-chat-with-yourself": "لايمكنك فتح محادثة مع نفسك", "chat-restricted": "هذا المستخدم عطل المحادثات الواردة عليه. يجب أن يتبعك حتى تتمكن من فتح محادثة معه.", - "too-many-messages": "You have sent too many messages, please wait awhile.", + "too-many-messages": "لقد أرسلت الكثير من الرسائل، الرجاء اﻹنتظار قليلاً", "reputation-system-disabled": "نظام السمعة معطل", "downvoting-disabled": "التصويتات السلبية معطلة", "not-enough-reputation-to-downvote": "ليس لديك سمعة تكفي لإضافة صوت سلبي لهذا الموضوع", @@ -78,6 +79,6 @@ "reload-failed": "المنتدى واجه مشكلة أثناء إعادة التحميل: \"%1\". سيواصل المنتدى خدمة العملاء السابقين لكن يجب عليك إلغاء أي تغيير قمت به قبل إعادة التحميل.", "registration-error": "حدث خطأ أثناء التسجيل", "parse-error": "Something went wrong while parsing server response", - "wrong-login-type-email": "Please use your email to login", - "wrong-login-type-username": "Please use your username to login" + "wrong-login-type-email": "الرجاء استعمال بريدك اﻹلكتروني للدخول", + "wrong-login-type-username": "الرجاء استعمال اسم المستخدم الخاص بك للدخول" } \ No newline at end of file diff --git a/public/language/ar/global.json b/public/language/ar/global.json index 1671e2806f..15275ddc39 100644 --- a/public/language/ar/global.json +++ b/public/language/ar/global.json @@ -27,7 +27,7 @@ "header.tags": "وسم", "header.popular": "الأكثر شهرة", "header.users": "المستخدمين", - "header.groups": "Groups", + "header.groups": "المجموعات", "header.chats": "المحادثات", "header.notifications": "التنبيهات", "header.search": "بحث", @@ -75,7 +75,7 @@ "updated.title": "تم تحديث المنتدى", "updated.message": "لقد تم تحديث المنتدى إلى آخر نسخة للتو. المرجو إعادة تحميل الصفحة.", "privacy": "الخصوصية", - "follow": "Follow", - "unfollow": "Unfollow", + "follow": "متابعة", + "unfollow": "إلغاء المتابعة", "delete_all": "حذف الكل" } \ No newline at end of file diff --git a/public/language/ar/groups.json b/public/language/ar/groups.json index 9e5c9fa799..c67f95ef8b 100644 --- a/public/language/ar/groups.json +++ b/public/language/ar/groups.json @@ -18,10 +18,10 @@ "details.private": "خاص", "details.grant": "منح/سحب المِلكية", "details.kick": "طرد", - "details.owner_options": "تدبير المجموعة", + "details.owner_options": "إدارة المجموعة", "details.group_name": "اسم المجموعة", - "details.member_count": "Member Count", - "details.creation_date": "Creation Date", + "details.member_count": "عدد اﻷعضاء", + "details.creation_date": "تاريخ الإنشاء", "details.description": "الوصف", "details.badge_preview": "معاينة الوسام", "details.change_icon": "تغيير الأيقونة", diff --git a/public/language/ar/login.json b/public/language/ar/login.json index 878a32533c..29d256203f 100644 --- a/public/language/ar/login.json +++ b/public/language/ar/login.json @@ -7,5 +7,5 @@ "alternative_logins": "تسجيلات الدخول البديلة", "failed_login_attempt": "فشلت محاولة تسجيل الدخول، يرجى المحاولة مرة أخرى.", "login_successful": "قمت بتسجيل الدخول بنجاح!", - "dont_have_account": "لم تفتح حسابك بعد؟" + "dont_have_account": "لا تملك حساب؟" } \ No newline at end of file diff --git a/public/language/ar/modules.json b/public/language/ar/modules.json index 3fdf629895..061fc4430c 100644 --- a/public/language/ar/modules.json +++ b/public/language/ar/modules.json @@ -16,8 +16,8 @@ "chat.thirty_days": "30 يومًا", "chat.three_months": "3 أشهر", "composer.compose": "Compose", - "composer.show_preview": "Show Preview", - "composer.hide_preview": "Hide Preview", + "composer.show_preview": "عرض المعاينة", + "composer.hide_preview": "إخفاء المعاينة", "composer.user_said_in": "%1 كتب في %2", "composer.user_said": "%1 كتب:", "composer.discard": "هل أنت متأكد أنك تريد التخلي عن التغييرات؟", diff --git a/public/language/ar/notifications.json b/public/language/ar/notifications.json index 98890331a5..2e87be20cd 100644 --- a/public/language/ar/notifications.json +++ b/public/language/ar/notifications.json @@ -1,5 +1,5 @@ { - "title": "تنبيهات", + "title": "التنبيهات", "no_notifs": "ليس لديك أية تنبيهات جديدة", "see_all": "معاينة كل التنبيهات", "mark_all_read": "اجعل كل التنبيهات مقروءة", diff --git a/public/language/ar/pages.json b/public/language/ar/pages.json index 793e2fd1dd..4b4e1af182 100644 --- a/public/language/ar/pages.json +++ b/public/language/ar/pages.json @@ -1,11 +1,11 @@ { "home": "الصفحة الرئيسية", - "unread": "المواضيع غير المقروءة", + "unread": "المواضيع الغير مقروءة", "popular": "المواضيع الأكثر شهرة", "recent": "المواضيع الحديثة", - "users": "المستخدمون المسجلون", + "users": "اﻷعضاء المسجلون", "notifications": "التنبيهات", - "tags": "Tags", + "tags": "الكلمات الدلالية", "tag": "Topics tagged under \"%1\"", "user.edit": "تعديل \"%1\"", "user.following": "المستخدمون الذين يتبعهم %1", @@ -15,7 +15,7 @@ "user.groups": "%1's Groups", "user.favourites": "مفضلات %1", "user.settings": "خيارات المستخدم", - "user.watched": "Topics watched by %1", + "user.watched": "المواضيع المتابعة من قبل %1 ", "maintenance.text": "جاري صيانة %1. المرجو العودة لاحقًا.", "maintenance.messageIntro": "بالإضافة إلى ذلك، قام مدبر النظام بترك هذه الرسالة:" } \ No newline at end of file diff --git a/public/language/ar/recent.json b/public/language/ar/recent.json index 02003e5d81..4fc13736d0 100644 --- a/public/language/ar/recent.json +++ b/public/language/ar/recent.json @@ -5,15 +5,15 @@ "month": "شهر", "year": "سنة", "alltime": "دائمًا", - "no_recent_topics": "لاوجود لمشاركات جديدة", + "no_recent_topics": "لايوجد مواضيع جديدة", "no_popular_topics": "There are no popular topics.", - "there-is-a-new-topic": "There is a new topic.", - "there-is-a-new-topic-and-a-new-post": "There is a new topic and a new post.", - "there-is-a-new-topic-and-new-posts": "There is a new topic and %1 new posts.", - "there-are-new-topics": "There are %1 new topics.", - "there-are-new-topics-and-a-new-post": "There are %1 new topics and a new post.", - "there-are-new-topics-and-new-posts": "There are %1 new topics and %2 new posts.", - "there-is-a-new-post": "There is a new post.", - "there-are-new-posts": "There are %1 new posts.", - "click-here-to-reload": "Click here to reload." + "there-is-a-new-topic": "يوجد موضوع جديد", + "there-is-a-new-topic-and-a-new-post": "يوجد موضوع جديد و رد جديد", + "there-is-a-new-topic-and-new-posts": "يوجد موضوع جديد و %1 ردود جديدة ", + "there-are-new-topics": "يوجد %1 مواضيع جديدة", + "there-are-new-topics-and-a-new-post": "يوجد %1 مواضيع جديدة و رد جديد", + "there-are-new-topics-and-new-posts": "يوجد %1 مواضيع جديدة و %2 مشاركات جديدة", + "there-is-a-new-post": "يوجد مشاركة جديدة", + "there-are-new-posts": "يوجد %1 مشاركات جديدة", + "click-here-to-reload": "إضغط هنا لإعادة التحميل" } \ No newline at end of file diff --git a/public/language/ar/register.json b/public/language/ar/register.json index 89ba850356..9e02b03dd7 100644 --- a/public/language/ar/register.json +++ b/public/language/ar/register.json @@ -1,18 +1,18 @@ { "register": "تسجيل", - "help.email": "افتراضيا، سيتم إخفاء بريدك الإلكتروني من الجمهور.", + "help.email": "افتراضيا، سيتم إخفاء بريدك الإلكتروني من العامة.", "help.username_restrictions": "اسم مستخدم فريدة من نوعها بين1% و2% حرفا. يمكن للآخرين ذكرك @ <'span id='your-username> اسم المستخدم .", - "help.minimum_password_length": "كلمتك السر يجب أن تكون على الأقل متألفة من 1% أحرف", + "help.minimum_password_length": "كلمة المرور يجب أن تكون على الأقل بها 1% أحرف", "email_address": "عنوان البريد الإلكتروني", "email_address_placeholder": "ادخل عنوان البريد الإلكتروني", "username": "اسم المستخدم", "username_placeholder": "أدخل اسم المستخدم", - "password": "كلمة السر", - "password_placeholder": "أدخل كلمة السر", - "confirm_password": "تأكيد كلمة السر", - "confirm_password_placeholder": "تأكيد كلمة السر", + "password": "كلمة المرور", + "password_placeholder": "أدخل كلمة المرور", + "confirm_password": "تأكيد كلمة المرور", + "confirm_password_placeholder": "تأكيد كلمة المرور", "register_now_button": "قم بالتسجيل الآن", "alternative_registration": "طريقة تسجيل بديلة", - "terms_of_use": "قوانين الاستخدام", - "agree_to_terms_of_use": "أوافق على قوانين الاستخدام" + "terms_of_use": "شروط الاستخدام", + "agree_to_terms_of_use": "أوافق على شروط الاستخدام" } \ No newline at end of file diff --git a/public/language/ar/reset_password.json b/public/language/ar/reset_password.json index cdd2b378a0..f6107ec45b 100644 --- a/public/language/ar/reset_password.json +++ b/public/language/ar/reset_password.json @@ -1,12 +1,12 @@ { - "reset_password": "إعادة تعيين كلمة السر", - "update_password": "تحديث كلمة السر", - "password_changed.title": "تم تغير كلمة السر", - "password_changed.message": "

تم تغير كلمة السر بنجاح. يرجى إعادة الدخول

", + "reset_password": "إعادة تعيين كلمة المرور", + "update_password": "تحديث كلمة المرور", + "password_changed.title": "تم تغير كلمة المرور", + "password_changed.message": "

تم تغير كلمة المرور بنجاح، الرجاء إعادة الدخول

", "wrong_reset_code.title": "رمز إعادة التعيين غير صحيح", "wrong_reset_code.message": "رمز إعادة التعين غير صحيح، يرجى المحاولة مرة أخرى أو اطلب رمزا جديدا", - "new_password": "كلمة السر الجديدة", - "repeat_password": "تأكيد كلمة السر", + "new_password": "كلمة المرور الجديدة", + "repeat_password": "تأكيد كلمة المرور", "enter_email": "يرجى إدخال عنوان البريد الإلكتروني الخاص بك وسوف نرسل لك رسالة بالبريد الالكتروني مع تعليمات حول كيفية إستعادة حسابك.", "enter_email_address": "ادخل عنوان البريد الإلكتروني", "password_reset_sent": "إعادة تعيين كلمة السر أرسلت", diff --git a/public/language/ar/search.json b/public/language/ar/search.json index ba8bb5da76..c126f78f86 100644 --- a/public/language/ar/search.json +++ b/public/language/ar/search.json @@ -1,40 +1,40 @@ { "results_matching": "%1 نتيجة (نتائج) موافقة ل \"%2\", (%3 ثواني)", "no-matches": "No matches found", - "advanced-search": "Advanced Search", - "in": "In", - "titles": "Titles", - "titles-posts": "Titles and Posts", + "advanced-search": "بحث متقدم", + "in": "في", + "titles": "العناوين", + "titles-posts": "العناوين والمشاركات", "posted-by": "Posted by", - "in-categories": "In Categories", - "search-child-categories": "Search child categories", - "reply-count": "Reply Count", - "at-least": "At least", - "at-most": "At most", - "post-time": "Post time", - "newer-than": "Newer than", - "older-than": "Older than", - "any-date": "Any date", - "yesterday": "Yesterday", - "one-week": "One week", - "two-weeks": "Two weeks", - "one-month": "One month", - "three-months": "Three months", - "six-months": "Six months", - "one-year": "One year", + "in-categories": "في الفئات", + "search-child-categories": "بحث في الفئات الفرعية", + "reply-count": "عدد المشاركات", + "at-least": "على اﻷقل", + "at-most": "على اﻷكثر", + "post-time": "تاريخ المشاركة", + "newer-than": "أحدث من", + "older-than": "أقدم من", + "any-date": "أي وقت", + "yesterday": "أمس", + "one-week": "أسبوع", + "two-weeks": "أسبوعان", + "one-month": "شهر", + "three-months": "ثلاثة أشهر", + "six-months": "ستة أشهر", + "one-year": "عام", "sort-by": "Sort by", - "last-reply-time": "Last reply time", - "topic-title": "Topic title", - "number-of-replies": "Number of replies", - "number-of-views": "Number of views", - "topic-start-date": "Topic start date", - "username": "Username", - "category": "Category", + "last-reply-time": "تاريخ آخر رد", + "topic-title": "عنوان الموضوع", + "number-of-replies": "عدد الردود", + "number-of-views": "عدد المشاهدات", + "topic-start-date": "تاريخ بدأ الموضوع", + "username": "اسم المستخدم", + "category": "فئة", "descending": "In descending order", "ascending": "In ascending order", - "save-preferences": "Save preferences", - "clear-preferences": "Clear preferences", - "search-preferences-saved": "Search preferences saved", - "search-preferences-cleared": "Search preferences cleared", - "show-results-as": "Show results as" + "save-preferences": "حفظ التفضيلات", + "clear-preferences": "ازالة التفضيلات", + "search-preferences-saved": "تم حفظ تفضيلات البحث", + "search-preferences-cleared": "تم ازالة تفضيلات البحث", + "show-results-as": "عرض النتائج كـ" } \ No newline at end of file diff --git a/public/language/ar/tags.json b/public/language/ar/tags.json index f2eccbd1c0..2798a0c8c8 100644 --- a/public/language/ar/tags.json +++ b/public/language/ar/tags.json @@ -1,7 +1,7 @@ { - "no_tag_topics": "لاوجود لمواضيع تحمل هذا الوسم.", - "tags": "بطاقات", + "no_tag_topics": "لا يوجد مواضيع بهذه الكلمة الدلالية.", + "tags": "الكلمات الدلالية", "enter_tags_here": "Enter tags here, between %1 and %2 characters each.", - "enter_tags_here_short": "أدخل البطاقات...", - "no_tags": "لاتوجد هناك بطاقات بعد." + "enter_tags_here_short": "أدخل الكلمات الدلالية...", + "no_tags": "لا يوجد كلمات دلالية بعد." } \ No newline at end of file diff --git a/public/language/ar/topic.json b/public/language/ar/topic.json index c63f32e61c..783a84a5fa 100644 --- a/public/language/ar/topic.json +++ b/public/language/ar/topic.json @@ -5,6 +5,7 @@ "no_topics_found": "لا توجد مواضيع !", "no_posts_found": "لا توجد مشاركات!", "post_is_deleted": "هذه المشاركة محذوفة!", + "topic_is_deleted": "هذا الموضوع محذوف", "profile": "الملف الشخصي", "posted_by": "كتب من طرف %1", "posted_by_guest": "كتب من طرف زائر", @@ -12,21 +13,21 @@ "notify_me": "تلق تنبيهات بالردود الجديدة في هذا الموضوع", "quote": "اقتبس", "reply": "رد", - "guest-login-reply": "Log in to reply", + "guest-login-reply": "يجب عليك تسجيل الدخول للرد", "edit": "تعديل", "delete": "حذف", "purge": "تطهير", "restore": "استعادة", - "move": "انقل", + "move": "نقل", "fork": "فرع", "link": "رابط", "share": "نشر", "tools": "أدوات", - "flag": "اشعار بمشاركة مخلة", + "flag": "تبليغ", "locked": "مقفل", - "bookmark_instructions": "انقر هنا للإكمال أو أغلق للإلغاء.", + "bookmark_instructions": "إضغط هنا للعودة إلى آخر موضع أو غلق للإلغاء", "flag_title": "إشعار بمشاركة مخلة.", - "flag_confirm": "هل تريد حقًّا أن تشعر بهذه المشاركة على أنها مخلة؟", + "flag_confirm": "هل تريد حقًّا التبليغ بهذه المشاركة؟", "flag_success": "تم الإشعار بهذه المشاركة على أنها مخلة", "deleted_message": "هذه المشاركة محذوفة. فقط من لهم صلاحية الإشراف على ا لمشاركات يمكنهم معاينتها.", "following_topic.message": "ستستلم تنبيها عند كل مشاركة جديدة في هذا الموضوع.", @@ -75,7 +76,7 @@ "fork_no_pids": "لم تختر أي مشاركة", "fork_success": "تم إنشاء فرع للموضوع بنجاح! إضغط هنا لمعاينة الفرع.", "composer.title_placeholder": "أدخل عنوان موضوعك هنا...", - "composer.handle_placeholder": "Name", + "composer.handle_placeholder": "اﻹسم", "composer.discard": "نبذ التغييرات", "composer.submit": "حفظ", "composer.replying_to": "الرد على %1", @@ -95,5 +96,5 @@ "oldest_to_newest": "من الأقدم إلى الأحدث", "newest_to_oldest": "من الأحدث إلى الأقدم", "most_votes": "الأكثر تصويتًا", - "most_posts": "Most posts" + "most_posts": "اﻷكثر رداً" } \ No newline at end of file diff --git a/public/language/ar/unread.json b/public/language/ar/unread.json index 6495771c72..9abf80b1e9 100644 --- a/public/language/ar/unread.json +++ b/public/language/ar/unread.json @@ -3,7 +3,7 @@ "no_unread_topics": "ليس هناك أي موضوع غير مقروء", "load_more": "حمل المزيد", "mark_as_read": "حدد غير مقروء", - "selected": "المختارة", + "selected": "المحددة", "all": "الكل", "topics_marked_as_read.success": "تم تحديد المواضيع على أنها مقروءة!" } \ No newline at end of file diff --git a/public/language/ar/user.json b/public/language/ar/user.json index 58faafe2e8..f3a33a5c59 100644 --- a/public/language/ar/user.json +++ b/public/language/ar/user.json @@ -1,9 +1,9 @@ { "banned": "محظور", - "offline": "ليس موجود حالياً", + "offline": "غير متصل", "username": "إسم المستخدم", - "joindate": "Join Date", - "postcount": "Post Count", + "joindate": "تاريخ الإنضمام", + "postcount": "عدد المشاركات", "email": "البريد الإلكتروني", "confirm_email": "تأكيد عنوان البريد الإلكتروني", "delete_account": "حذف الحساب", @@ -15,25 +15,26 @@ "joined": "تاريخ التسجيل", "lastonline": "تاريخ آخر دخول", "profile": "الملف الشخصي", - "profile_views": "عدد مشاهدات الملف الشخصي", + "profile_views": "عدد المشاهدات", "reputation": "السمعة", - "favourites": "المفضلات", - "watched": "Watched", + "favourites": "التفضيلات", + "watched": "متابع", "followers": "المتابعون", "following": "يتابع", + "aboutme": "معلومة عنك او السيرة الذاتية", "signature": "توقيع", "gravatar": "Gravatar", "birthday": "عيد ميلاد", "chat": "محادثة", "follow": "تابع", "unfollow": "إلغاء المتابعة", - "more": "More", + "more": "المزيد", "profile_update_success": "تم تحديث الملف الشخصي بنجاح", "change_picture": "تغيير الصورة", "edit": "تعديل", "uploaded_picture": "الصورة المرفوعة", "upload_new_picture": "رفع صورة جديدة", - "upload_new_picture_from_url": "رفع صورة جديدة بواسطة رابط", + "upload_new_picture_from_url": "رفع صورة جديدة من رابط", "current_password": "كلمة السر الحالية", "change_password": "تغيير كلمة السر", "change_password_error": "كلمة سر غير صحيحة", @@ -60,7 +61,7 @@ "digest_monthly": "شهريًّا", "send_chat_notifications": "استلام رسالة إلكترونية عند ورود محادثة وأنا غير متصل.", "send_post_notifications": "Send an email when replies are made to topics I am subscribed to", - "settings-require-reload": "Some setting changes require a reload. Click here to reload the page.", + "settings-require-reload": "تغيير بعض اﻹعدادات يتطلب تحديث الصفحة. إضغط هنا لتحديث الصفحة", "has_no_follower": "هذا المستخدم ليس لديه أي متابع :(", "follows_no_one": "هذا المستخدم لا يتابع أحد :(", "has_no_posts": "هذا المستخدم لم يكتب أي شيء بعد.", @@ -71,13 +72,13 @@ "paginate_description": "Paginate topics and posts instead of using infinite scroll", "topics_per_page": "المواضيع في كل صفحة", "posts_per_page": "الردود في كل صفحة", - "notification_sounds": "Play a sound when you receive a notification", + "notification_sounds": "تشغيل صوت عند تلقي تنبيه", "browsing": "خيارات التصفح", - "open_links_in_new_tab": "Open outgoing links in new tab", + "open_links_in_new_tab": "فتح الروابط الخارجية في نافدة جديدة", "enable_topic_searching": "تفعيل خاصية البحث داخل المواضيع", "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen", - "follow_topics_you_reply_to": "Follow topics that you reply to", - "follow_topics_you_create": "Follow topics you create", + "follow_topics_you_reply_to": "متابعة المواضيع التي تقوم بالرد فيها", + "follow_topics_you_create": "متابعة المواضيع التي تنشئها", "grouptitle": "Select the group title you would like to display", - "no-group-title": "No group title" + "no-group-title": "لا يوجد عنوان للمجموعة" } \ No newline at end of file diff --git a/public/language/ar/users.json b/public/language/ar/users.json index a842def62b..e10ab98e61 100644 --- a/public/language/ar/users.json +++ b/public/language/ar/users.json @@ -1,12 +1,12 @@ { - "latest_users": "أحدث المستخدمين", - "top_posters": "أكثر المشتركين", + "latest_users": "أحدث الأعضاء", + "top_posters": "اﻷكثر مشاركة", "most_reputation": "أعلى سمعة", "search": "بحث", "enter_username": "أدخل اسم مستخدم للبحث", "load_more": "حمل المزيد", "users-found-search-took": "%1 user(s) found! Search took %2 seconds.", "filter-by": "Filter By", - "online-only": "Online only", - "picture-only": "Picture only" + "online-only": "المتصلون فقط", + "picture-only": "صورة فقط" } \ No newline at end of file diff --git a/public/language/bg/error.json b/public/language/bg/error.json index 17907c8815..a48531a6ea 100644 --- a/public/language/bg/error.json +++ b/public/language/bg/error.json @@ -2,7 +2,7 @@ "invalid-data": "Невалидни данни", "not-logged-in": "Изглежда не сте влезли в системата.", "account-locked": "Вашият акаунт беше заключен временно", - "search-requires-login": "Търсенето изисква регистриран акаунт! Моля, влезте или се регистрирайте!", + "search-requires-login": "Търсенето изисква акаунт – моля, влезте или се регистрирайте.", "invalid-cid": "Невалиден идентификатор на категория", "invalid-tid": "Невалиден идентификатор на тема", "invalid-pid": "Невалиден идентификатор на публикация", @@ -68,6 +68,7 @@ "invalid-file": "Грешен файл", "uploads-are-disabled": "Качването не е разрешено", "signature-too-long": "Съжаляваме, но подписът Ви трябва да съдържа не повече от %1 символ(а).", + "about-me-too-long": "Съжаляваме, но информацията за Вас трябва да съдържа не повече от %1 символ(а).", "cant-chat-with-yourself": "Не можете да пишете чат съобщение на себе си!", "chat-restricted": "Този потребител е ограничил чат съобщенията до себе си. Той трябва първо да Ви последва, преди да можете да си пишете с него.", "too-many-messages": "Изпратили сте твърде много съобщения. Моля, изчакайте малко.", diff --git a/public/language/bg/topic.json b/public/language/bg/topic.json index 03062d7e70..492bf1bccf 100644 --- a/public/language/bg/topic.json +++ b/public/language/bg/topic.json @@ -5,6 +5,7 @@ "no_topics_found": "Няма открити теми!", "no_posts_found": "Няма открити публикации!", "post_is_deleted": "Тази публикация е изтрита!", + "topic_is_deleted": "Тази тема е изтрита!", "profile": "Профил", "posted_by": "Публикувано от %1", "posted_by_guest": "Публикувано от гост", diff --git a/public/language/bg/user.json b/public/language/bg/user.json index f6b68145f1..dee62ac943 100644 --- a/public/language/bg/user.json +++ b/public/language/bg/user.json @@ -21,6 +21,7 @@ "watched": "Наблюдавани", "followers": "Последователи", "following": "Следва", + "aboutme": "За мен", "signature": "Подпис", "gravatar": "Граватар", "birthday": "Рождена дата", diff --git a/public/language/bn/error.json b/public/language/bn/error.json index 40b8dc44d7..979dca250b 100644 --- a/public/language/bn/error.json +++ b/public/language/bn/error.json @@ -2,7 +2,7 @@ "invalid-data": "ভুল তথ্য", "not-logged-in": "আপনি লগিন করেননি", "account-locked": "আপনার অ্যাকাউন্ট সাময়িকভাবে লক করা হয়েছে", - "search-requires-login": "অনুসন্ধান করার জন্য একটি অ্যাকাউন্ট প্রয়োজন! অনুগ্রহপূর্বক প্রবেশ করুন অথবা নিবন্ধন করুন!", + "search-requires-login": "Searching requires an account - please login or register.", "invalid-cid": "ভুল বিভাগ নাম্বার", "invalid-tid": "ভুল টপিক নাম্বার", "invalid-pid": "ভুল পোস্ট নাম্বার", @@ -68,6 +68,7 @@ "invalid-file": "ভুল ফাইল", "uploads-are-disabled": "আপলোড নিষ্ক্রিয় করা", "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", + "about-me-too-long": "Sorry, your about me cannot be longer than %1 character(s).", "cant-chat-with-yourself": "আপনি নিজের সাথে চ্যাট করতে পারবেন না!", "chat-restricted": "এই সদস্য তার বার্তালাপ সংরক্ষিত রেখেছেন। এই সদস্য আপনাকে ফলো করার পরই কেবলমাত্র আপনি তার সাথে চ্যাট করতে পারবেন", "too-many-messages": "You have sent too many messages, please wait awhile.", diff --git a/public/language/bn/topic.json b/public/language/bn/topic.json index 3b0835cf2e..92466f18c8 100644 --- a/public/language/bn/topic.json +++ b/public/language/bn/topic.json @@ -5,6 +5,7 @@ "no_topics_found": "কোন টপিক পাওয়া যায়নি!", "no_posts_found": "কোন পোস্ট পাওয়া যায়নি", "post_is_deleted": "এই পোস্টটি মুছে ফেলা হয়েছে!", + "topic_is_deleted": "This topic is deleted!", "profile": "প্রোফাইল ", "posted_by": "পোস্ট করেছেন %1", "posted_by_guest": "অতিথি পোস্ট ", diff --git a/public/language/bn/user.json b/public/language/bn/user.json index 5107df1fa7..96b8943a2d 100644 --- a/public/language/bn/user.json +++ b/public/language/bn/user.json @@ -21,6 +21,7 @@ "watched": "Watched", "followers": "যাদের অনুসরণ করছেন", "following": "যারা আপনাকে অনুসরণ করছে", + "aboutme": "About me", "signature": "স্বাক্ষর", "gravatar": "গ্রাভাতার", "birthday": "জন্মদিন", diff --git a/public/language/cs/error.json b/public/language/cs/error.json index 1a82707bd9..60418d0ef0 100644 --- a/public/language/cs/error.json +++ b/public/language/cs/error.json @@ -2,7 +2,7 @@ "invalid-data": "Neplatná data", "not-logged-in": "Zdá se, že nejste přihlášen(a)", "account-locked": "Váš účet byl dočasně uzamčen", - "search-requires-login": "Chcete-li vyhledávat, musíte mít účet. Přihlašte se nebo zaregistrujte, prosím.", + "search-requires-login": "Searching requires an account - please login or register.", "invalid-cid": "Neplatné ID kategorie", "invalid-tid": "Neplatné ID tématu", "invalid-pid": "Neplatné ID příspěvku", @@ -68,6 +68,7 @@ "invalid-file": "Neplatný soubor", "uploads-are-disabled": "Nahrávání je zakázáno", "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", + "about-me-too-long": "Sorry, your about me cannot be longer than %1 character(s).", "cant-chat-with-yourself": "Nemůžete chatovat sami se sebou!", "chat-restricted": "This user has restricted their chat messages. They must follow you before you can chat with them", "too-many-messages": "You have sent too many messages, please wait awhile.", diff --git a/public/language/cs/topic.json b/public/language/cs/topic.json index 4f20b6e6db..7e930cfc23 100644 --- a/public/language/cs/topic.json +++ b/public/language/cs/topic.json @@ -5,6 +5,7 @@ "no_topics_found": "Nebyla nalezena žádná témata!", "no_posts_found": "Nebyly nalezeny žádné příspěvky!", "post_is_deleted": "Tento příspěvek je vymazán!", + "topic_is_deleted": "This topic is deleted!", "profile": "Profil", "posted_by": "Posted by %1", "posted_by_guest": "Posted by Guest", diff --git a/public/language/cs/user.json b/public/language/cs/user.json index 171ed20163..b41192c56f 100644 --- a/public/language/cs/user.json +++ b/public/language/cs/user.json @@ -21,6 +21,7 @@ "watched": "Watched", "followers": "Sledují ho", "following": "Sleduje", + "aboutme": "About me", "signature": "Podpis", "gravatar": "Gravatar", "birthday": "Datum narození", diff --git a/public/language/de/error.json b/public/language/de/error.json index 3c1248097a..61cb1fa3f9 100644 --- a/public/language/de/error.json +++ b/public/language/de/error.json @@ -2,7 +2,7 @@ "invalid-data": "Daten ungültig", "not-logged-in": "Du bist nicht angemeldet.", "account-locked": "Dein Account wurde vorübergehend gesperrt.", - "search-requires-login": "Die Suche erfordert ein Konto! Bitte log dich ein oder registriere dich!", + "search-requires-login": "Searching requires an account - please login or register.", "invalid-cid": "Ungültige Kategorie-ID", "invalid-tid": "Ungültige Themen-ID", "invalid-pid": "Ungültige Beitrags-ID", @@ -68,6 +68,7 @@ "invalid-file": "Datei ungültig", "uploads-are-disabled": "Uploads sind deaktiviert", "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", + "about-me-too-long": "Sorry, your about me cannot be longer than %1 character(s).", "cant-chat-with-yourself": "Du kannst nicht mit dir selber chatten!", "chat-restricted": "Dieser Benutzer hat seine Chatfunktion eingeschränkt. Du kannst nur mit diesem Benutzer chatten, wenn er dir folgt.", "too-many-messages": "Du hast zu viele Nachrichten versandt, bitte warte eine Weile.", diff --git a/public/language/de/topic.json b/public/language/de/topic.json index c4fe7732d8..43d59ecb73 100644 --- a/public/language/de/topic.json +++ b/public/language/de/topic.json @@ -5,6 +5,7 @@ "no_topics_found": "Keine passenden Themen gefunden.", "no_posts_found": "Keine Beiträge gefunden!", "post_is_deleted": "Dieser Beitrag wurde gelöscht!", + "topic_is_deleted": "This topic is deleted!", "profile": "Profil", "posted_by": "Geschrieben von %1", "posted_by_guest": "Verfasst von einem Gast", diff --git a/public/language/de/user.json b/public/language/de/user.json index 14772d4a2d..d1677c7609 100644 --- a/public/language/de/user.json +++ b/public/language/de/user.json @@ -21,6 +21,7 @@ "watched": "Beobachtet", "followers": "Folger", "following": "Folgt", + "aboutme": "About me", "signature": "Signatur", "gravatar": "Gravatar", "birthday": "Geburtstag", @@ -75,7 +76,7 @@ "browsing": "Stöbereinstellungen", "open_links_in_new_tab": "Ausgehende Links in neuem Tab öffnen", "enable_topic_searching": "Suchen innerhalb von Themen aktivieren", - "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen", + "topic_search_help": "Wenn aktiviert, ersetzt die im-Thema-Suche die Standardsuche des Browsers. Dadurch kannst du im ganzen Thema suchen, nicht nur im sichtbaren Abschnitt.", "follow_topics_you_reply_to": "Themen folgen, in denen auf dich geantwortet wird", "follow_topics_you_create": "Themen folgen, die du erstellst", "grouptitle": "Wähle den anzuzeigenden Gruppen Titel aus", diff --git a/public/language/el/error.json b/public/language/el/error.json index 5749bd7156..2cb627fbc4 100644 --- a/public/language/el/error.json +++ b/public/language/el/error.json @@ -2,7 +2,7 @@ "invalid-data": "Άκυρα Δεδομένα", "not-logged-in": "Φαίνεται πως δεν είσαι συνδεδεμένος/η.", "account-locked": "Ο λογαριασμός σου έχει κλειδωθεί προσωρινά", - "search-requires-login": "Πρέπει να είσαι συνδεδεμένος/η για να αναζητήσεις! Παρακαλώ συνδέσου ή εγγράψου!", + "search-requires-login": "Searching requires an account - please login or register.", "invalid-cid": "Άκυρο ID Κατηγορίας", "invalid-tid": "Άκυρο ID Θέματος", "invalid-pid": "Άκυρο ID Δημοσίευσης", @@ -68,6 +68,7 @@ "invalid-file": "Άκυρο Αρχείο", "uploads-are-disabled": "Το ανέβασμα αρχείων έχει απενεργοποιηθεί", "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", + "about-me-too-long": "Sorry, your about me cannot be longer than %1 character(s).", "cant-chat-with-yourself": "Δεν μπορείς να συνομιλήσεις με τον εαυτό σου!", "chat-restricted": "This user has restricted their chat messages. They must follow you before you can chat with them", "too-many-messages": "You have sent too many messages, please wait awhile.", diff --git a/public/language/el/topic.json b/public/language/el/topic.json index 5e93f49db9..2a905bf786 100644 --- a/public/language/el/topic.json +++ b/public/language/el/topic.json @@ -5,6 +5,7 @@ "no_topics_found": "Δεν βρέθηκαν θέματα!", "no_posts_found": "Δεν βρέθηκαν δημοσιεύσεις!", "post_is_deleted": "Αυτή η δημοσίευση έχει διαγραφεί!", + "topic_is_deleted": "This topic is deleted!", "profile": "Προφίλ", "posted_by": "Δημοσιεύτηκε από τον/την %1", "posted_by_guest": "Δημοσιεύτηκε από Επισκέπτη", diff --git a/public/language/el/user.json b/public/language/el/user.json index 984400744f..9524d5a529 100644 --- a/public/language/el/user.json +++ b/public/language/el/user.json @@ -21,6 +21,7 @@ "watched": "Watched", "followers": "Ακόλουθοι", "following": "Ακολουθά", + "aboutme": "About me", "signature": "Υπογραφή", "gravatar": "Gravatar", "birthday": "Γενέθλια", diff --git a/public/language/en@pirate/error.json b/public/language/en@pirate/error.json index 867f331c3c..ac2457e250 100644 --- a/public/language/en@pirate/error.json +++ b/public/language/en@pirate/error.json @@ -2,7 +2,7 @@ "invalid-data": "Invalid Data", "not-logged-in": "You don't seem to be logged in.", "account-locked": "Your account has been locked temporarily", - "search-requires-login": "Searching requires an account! Please login or register!", + "search-requires-login": "Searching requires an account - please login or register.", "invalid-cid": "Invalid Category ID", "invalid-tid": "Invalid Topic ID", "invalid-pid": "Invalid Post ID", @@ -68,6 +68,7 @@ "invalid-file": "Invalid File", "uploads-are-disabled": "Uploads are disabled", "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", + "about-me-too-long": "Sorry, your about me cannot be longer than %1 character(s).", "cant-chat-with-yourself": "You can't chat with yourself!", "chat-restricted": "This user has restricted their chat messages. They must follow you before you can chat with them", "too-many-messages": "You have sent too many messages, please wait awhile.", diff --git a/public/language/en@pirate/topic.json b/public/language/en@pirate/topic.json index 88cd2e2328..641e8f0346 100644 --- a/public/language/en@pirate/topic.json +++ b/public/language/en@pirate/topic.json @@ -5,6 +5,7 @@ "no_topics_found": "No topics found!", "no_posts_found": "No posts found!", "post_is_deleted": "This post is deleted!", + "topic_is_deleted": "This topic is deleted!", "profile": "Profile", "posted_by": "Posted by %1", "posted_by_guest": "Posted by Guest", diff --git a/public/language/en@pirate/user.json b/public/language/en@pirate/user.json index d93872539c..49ecb754ac 100644 --- a/public/language/en@pirate/user.json +++ b/public/language/en@pirate/user.json @@ -21,6 +21,7 @@ "watched": "Watched", "followers": "Followers", "following": "Following", + "aboutme": "About me", "signature": "Signature", "gravatar": "Gravatar", "birthday": "Birthday", diff --git a/public/language/en_GB/email.json b/public/language/en_GB/email.json index 33fd28377b..1aa66835b8 100644 --- a/public/language/en_GB/email.json +++ b/public/language/en_GB/email.json @@ -2,13 +2,19 @@ "password-reset-requested": "Password Reset Requested - %1!", "welcome-to": "Welcome to %1", + "invite": "Invitation from %1", + "greeting_no_name": "Hello", "greeting_with_name": "Hello %1", "welcome.text1": "Thank you for registering with %1!", "welcome.text2": "To fully activate your account, we need to verify that you own the email address you registered with.", + "welcome.text3": "An administrator has accepted your registration application. You can login with your username/password now.", "welcome.cta": "Click here to confirm your email address", + "invitation.text1": "%1 has invited you to join %2", + "invitation.ctr": "Click here to create your account.", + "reset.text1": "We received a request to reset your password, possibly because you have forgotten it. If this is not the case, please ignore this email.", "reset.text2": "To continue with the password reset, please click on the following link:", "reset.cta": "Click here to reset your password", diff --git a/public/language/en_GB/error.json b/public/language/en_GB/error.json index 40975c69b0..6a40edde0a 100644 --- a/public/language/en_GB/error.json +++ b/public/language/en_GB/error.json @@ -3,7 +3,7 @@ "not-logged-in": "You don't seem to be logged in.", "account-locked": "Your account has been locked temporarily", - "search-requires-login": "Searching requires an account! Please login or register!", + "search-requires-login": "Searching requires an account - please login or register.", "invalid-cid": "Invalid Category ID", "invalid-tid": "Invalid Topic ID", @@ -65,6 +65,7 @@ "already-unfavourited": "You have already unfavourited this post", "cant-ban-other-admins": "You can't ban other admins!", + "cant-remove-last-admin": "You are the only administrator. Add another user as an administrator before removing yourself as admin", "invalid-image-type": "Invalid image type. Allowed types are: %1", "invalid-image-extension": "Invalid image extension", @@ -88,8 +89,8 @@ "invalid-file": "Invalid File", "uploads-are-disabled": "Uploads are disabled", - "signature-too-long" : "Sorry, your signature cannot be longer than %1 characters.", - "about-me-too-long" : "Sorry, your about me cannot be longer than %1 characters.", + "signature-too-long" : "Sorry, your signature cannot be longer than %1 character(s).", + "about-me-too-long" : "Sorry, your about me cannot be longer than %1 character(s).", "cant-chat-with-yourself": "You can't chat with yourself!", "chat-restricted": "This user has restricted their chat messages. They must follow you before you can chat with them", @@ -99,6 +100,7 @@ "downvoting-disabled": "Downvoting is disabled", "not-enough-reputation-to-downvote": "You do not have enough reputation to downvote this post", "not-enough-reputation-to-flag": "You do not have enough reputation to flag this post", + "already-flagged": "You have already flagged this post", "reload-failed": "NodeBB encountered a problem while reloading: \"%1\". NodeBB will continue to serve the existing client-side assets, although you should undo what you did just prior to reloading.", diff --git a/public/language/en_GB/global.json b/public/language/en_GB/global.json index 92ef56998f..6fb6c8bc77 100644 --- a/public/language/en_GB/global.json +++ b/public/language/en_GB/global.json @@ -64,6 +64,7 @@ "reputation": "Reputation", "read_more": "read more", + "more": "More", "posted_ago_by_guest": "posted %1 by Guest", "posted_ago_by": "posted %1 by %2", diff --git a/public/language/en_GB/groups.json b/public/language/en_GB/groups.json index 644bd6a4cb..d6a5ed6b6e 100644 --- a/public/language/en_GB/groups.json +++ b/public/language/en_GB/groups.json @@ -7,6 +7,8 @@ "pending.accept": "Accept", "pending.reject": "Reject", + "pending.accept_all": "Accept All", + "pending.reject_all": "Reject All", "cover-instructions": "Drag and Drop a photo, drag to position, and hit Save", "cover-change": "Change", diff --git a/public/language/en_GB/notifications.json b/public/language/en_GB/notifications.json index 19de8e2c28..1c256bebb4 100644 --- a/public/language/en_GB/notifications.json +++ b/public/language/en_GB/notifications.json @@ -22,6 +22,7 @@ "user_posted_topic": "%1 has posted a new topic: %2", "user_mentioned_you_in": "%1 mentioned you in %2", "user_started_following_you": "%1 started following you.", + "new_register": "%1 sent a registration request.", "email-confirmed": "Email Confirmed", "email-confirmed-message": "Thank you for validating your email. Your account is now fully activated.", diff --git a/public/language/en_GB/register.json b/public/language/en_GB/register.json index 26196c765d..dcbd4bb03a 100644 --- a/public/language/en_GB/register.json +++ b/public/language/en_GB/register.json @@ -14,5 +14,6 @@ "register_now_button": "Register Now", "alternative_registration": "Alternative Registration", "terms_of_use": "Terms of Use", - "agree_to_terms_of_use": "I agree to the Terms of Use" + "agree_to_terms_of_use": "I agree to the Terms of Use", + "registration-added-to-queue": "Your registration has been added to the approval queue. You will receive an email when it is accepted by an administrator." } \ No newline at end of file diff --git a/public/language/en_GB/user.json b/public/language/en_GB/user.json index 99f118ad5a..73d2b6784c 100644 --- a/public/language/en_GB/user.json +++ b/public/language/en_GB/user.json @@ -7,8 +7,12 @@ "email": "Email", "confirm_email": "Confirm Email", + "ban_account": "Ban Account", + "ban_account_confirm": "Do you really want to ban this user?", + "unban_account": "Unban Account", "delete_account": "Delete Account", "delete_account_confirm": "Are you sure you want to delete your account?
This action is irreversible and you will not be able to recover any of your data

Enter your username to confirm that you wish to destroy this account.", + "delete_this_account_confirm": "Are you sure you want to delete this account?
This action is irreversible and you will not be able to recover any data

", "fullname": "Full Name", "website": "Website", diff --git a/public/language/en_GB/users.json b/public/language/en_GB/users.json index 0f3687c9ed..4b6cb8b1c3 100644 --- a/public/language/en_GB/users.json +++ b/public/language/en_GB/users.json @@ -8,5 +8,7 @@ "users-found-search-took": "%1 user(s) found! Search took %2 seconds.", "filter-by": "Filter By", "online-only": "Online only", - "picture-only": "Picture only" + "picture-only": "Picture only", + "invite": "Invite", + "invitation-email-sent": "An invitation email has been sent to %1" } \ No newline at end of file diff --git a/public/language/en_US/error.json b/public/language/en_US/error.json index 867f331c3c..ac2457e250 100644 --- a/public/language/en_US/error.json +++ b/public/language/en_US/error.json @@ -2,7 +2,7 @@ "invalid-data": "Invalid Data", "not-logged-in": "You don't seem to be logged in.", "account-locked": "Your account has been locked temporarily", - "search-requires-login": "Searching requires an account! Please login or register!", + "search-requires-login": "Searching requires an account - please login or register.", "invalid-cid": "Invalid Category ID", "invalid-tid": "Invalid Topic ID", "invalid-pid": "Invalid Post ID", @@ -68,6 +68,7 @@ "invalid-file": "Invalid File", "uploads-are-disabled": "Uploads are disabled", "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", + "about-me-too-long": "Sorry, your about me cannot be longer than %1 character(s).", "cant-chat-with-yourself": "You can't chat with yourself!", "chat-restricted": "This user has restricted their chat messages. They must follow you before you can chat with them", "too-many-messages": "You have sent too many messages, please wait awhile.", diff --git a/public/language/en_US/topic.json b/public/language/en_US/topic.json index 750ce2694a..d9742f2eda 100644 --- a/public/language/en_US/topic.json +++ b/public/language/en_US/topic.json @@ -5,6 +5,7 @@ "no_topics_found": "No topics found!", "no_posts_found": "No posts found!", "post_is_deleted": "This post is deleted!", + "topic_is_deleted": "This topic is deleted!", "profile": "Profile", "posted_by": "Posted by %1", "posted_by_guest": "Posted by Guest", diff --git a/public/language/en_US/user.json b/public/language/en_US/user.json index ddfb4ac04a..c3ebec37a2 100644 --- a/public/language/en_US/user.json +++ b/public/language/en_US/user.json @@ -21,6 +21,7 @@ "watched": "Watched", "followers": "Followers", "following": "Following", + "aboutme": "About me", "signature": "Signature", "gravatar": "Gravatar", "birthday": "Birthday", diff --git a/public/language/es/error.json b/public/language/es/error.json index 12132c317d..02b0c930cc 100644 --- a/public/language/es/error.json +++ b/public/language/es/error.json @@ -21,11 +21,11 @@ "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.", "no-email-to-confirm": "Este foro requiere confirmación de su email, por favor pulse aquí para introducir un email", "email-confirm-failed": "No se ha podido confirmar su email, por favor inténtelo de nuevo más tarde.", - "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", + "confirm-email-already-sent": "El email de confirmación ya ha sido enviado, por favor espera %1 minuto(s) para enviar otro.", "username-too-short": "Nombre de usuario es demasiado corto", "username-too-long": "Nombre de usuario demasiado largo", "user-banned": "Usuario baneado", - "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", + "user-too-new": "Lo sentimos, es necesario que esperes %1 segundo(s) antes poder hacer tu primera publicación", "no-category": "La categoría no existe", "no-topic": "El tema no existe", "no-post": "La publicación no existe", @@ -36,17 +36,17 @@ "no-emailers-configured": "No se ha cargado ningún plugin de email, así que no se pudo enviar el email de prueba.", "category-disabled": "Categoría deshabilitada", "topic-locked": "Tema bloqueado", - "post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting", + "post-edit-duration-expired": "Sólo puedes editar mensajes durante %1 segundo(s) después de haberlo escrito", "still-uploading": "Por favor, espera a que terminen las subidas.", - "content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).", - "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).", - "title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).", - "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).", - "too-many-posts": "You can only post once every %1 second(s) - please wait before posting again", - "too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again", - "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)", - "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)", - "file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file", + "content-too-short": "Por favor introduzca una publicación más larga. Las publicaciones deben contener al menos %1 caractere(s).", + "content-too-long": "Por favor introduzca un mensaje más corto. Los mensajes no pueden exceder los %1 caractere(s).", + "title-too-short": "Por favor introduzca un título más largo. Los títulos deben contener al menos %1 caractere(s).", + "title-too-long": "Por favor, introduce un título más corto, que no sobrepase los %1 caractere(s).", + "too-many-posts": "Solo puedes publicar una vez cada %1 segundo(s) - por favor espere antes de volver a publicar", + "too-many-posts-newbie": "Como nuevo usuario, solo puedes publicar una vez cada %1 segundo(s) hasta hayas ganado una reputación de %2 - por favor espera antes de volver a publicar", + "tag-too-short": "Por favor introduce una etiqueta más larga. Las etiquetas deben contener por lo menos %1 caractere(s)", + "tag-too-long": "Por favor introduce una etiqueta más corta. Las etiquetas no pueden exceder los %1 caractere(s)", + "file-too-big": "El tamaño de fichero máximo es de %1 kB - por favor, suba un fichero más pequeño", "cant-vote-self-post": "No puedes votar tus propios posts", "already-favourited": "Ya ha marcado esta publicación como favorita", "already-unfavourited": "Ya ha desmarcado esta publicación como favorita", @@ -63,11 +63,12 @@ "post-already-restored": "Esta publicación ya ha sido restaurada", "topic-already-deleted": "Este tema ya ha sido borrado", "topic-already-restored": "Este tema ya ha sido restaurado", - "cant-purge-main-post": "You can't purge the main post, please delete the topic instead", + "cant-purge-main-post": "No puedes purgar el mensaje principal, por favor utiliza borrar tema", "topic-thumbnails-are-disabled": "Las miniaturas de los temas están deshabilitadas.", "invalid-file": "Archivo no válido", "uploads-are-disabled": "Las subidas están deshabilitadas.", - "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", + "signature-too-long": "Lo sentimos, pero tu firma no puede ser más larga de %1 caractere(s).", + "about-me-too-long": "Lo sentimos, pero tu descripción no puede ser más larga de %1 caractere(s).", "cant-chat-with-yourself": "¡No puedes conversar contigo mismo!", "chat-restricted": "Este usuario tiene restringidos los mensajes de chat. Los usuarios deben seguirte antes de que pueda charlar con ellos", "too-many-messages": "Has enviado demasiados mensajes, por favor espera un poco.", diff --git a/public/language/es/register.json b/public/language/es/register.json index 4ffa4d8bdd..064f05fa64 100644 --- a/public/language/es/register.json +++ b/public/language/es/register.json @@ -1,5 +1,5 @@ { - "register": "Registrase", + "register": "Registrarse", "help.email": "Por defecto, tu cuenta de correo electrónico estará oculta al publico.", "help.username_restrictions": "El nombre de usuario debe tener entre %1 y %2 carácteres. Los miembros pueden responderte escribiendo @usuario.", "help.minimum_password_length": "Tu contraseña debe tener al menos %1 carácteres.", @@ -11,7 +11,7 @@ "password_placeholder": "Introduce tu contraseña", "confirm_password": "Confirmar contraseña", "confirm_password_placeholder": "Confirmar contraseña", - "register_now_button": "Registrarme ahora", + "register_now_button": "Registrarse ahora", "alternative_registration": "Métodos de registro alternativos", "terms_of_use": "Términos y Condiciones de uso", "agree_to_terms_of_use": "Acepto los Términos y Condiciones de uso" diff --git a/public/language/es/topic.json b/public/language/es/topic.json index 1938b05c7d..c90bccb9d0 100644 --- a/public/language/es/topic.json +++ b/public/language/es/topic.json @@ -5,6 +5,7 @@ "no_topics_found": "¡No se encontraron temas!", "no_posts_found": "¡No se encontraron publicaciones!", "post_is_deleted": "¡Esta publicación está eliminada!", + "topic_is_deleted": "¡Este tema ha sido eliminado!", "profile": "Perfil", "posted_by": "Publicado por %1", "posted_by_guest": "Publicado por Invitado", diff --git a/public/language/es/user.json b/public/language/es/user.json index 09782de1b5..be3ad91ed8 100644 --- a/public/language/es/user.json +++ b/public/language/es/user.json @@ -21,6 +21,7 @@ "watched": "Visto", "followers": "Seguidores", "following": "Siguiendo", + "aboutme": "Sobre mí", "signature": "Firma", "gravatar": "Gravatar", "birthday": "Cumpleaños", @@ -29,11 +30,11 @@ "unfollow": "Dejar de seguir", "more": "Más", "profile_update_success": "¡El perfil ha sido actualizado correctamente!", - "change_picture": "Cambiar imágen", + "change_picture": "Cambiar imagen", "edit": "Editar", - "uploaded_picture": "Imágen subida", - "upload_new_picture": "Subir nueva imágen", - "upload_new_picture_from_url": "Cargar imágen desde una URL", + "uploaded_picture": "Imagen subida", + "upload_new_picture": "Subir nueva imagen", + "upload_new_picture_from_url": "Cargar imagen desde una URL", "current_password": "Contraseña actual", "change_password": "Cambiar contraseña", "change_password_error": "¡Contraseña no válida!", @@ -68,16 +69,16 @@ "has_no_watched_topics": "Este usuario todavía no ha visto ninguna publicación.", "email_hidden": "Correo electrónico oculto", "hidden": "oculto", - "paginate_description": "Paginate topics and posts instead of using infinite scroll", + "paginate_description": "Paginar hilos y mensajes en lugar de usar desplazamiento infinito", "topics_per_page": "Temas por página", "posts_per_page": "Post por página", - "notification_sounds": "Play a sound when you receive a notification", + "notification_sounds": "Reproducir un sonido al recibir una notificación", "browsing": "Preferencias de navegación.", - "open_links_in_new_tab": "Open outgoing links in new tab", + "open_links_in_new_tab": "Abrir los enlaces externos en una nueva pestaña", "enable_topic_searching": "Activar la búsqueda \"in-topic\"", - "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen", - "follow_topics_you_reply_to": "Follow topics that you reply to", - "follow_topics_you_create": "Follow topics you create", + "topic_search_help": "Si está activada, la búsqueda 'in-topic' sustituirá el comportamiento por defecto del navegador y le permitirá buscar en el tema al completo, en vez de hacer una búsqueda únicamente sobre el contenido mostrado en pantalla", + "follow_topics_you_reply_to": "Seguir los temas en las que respondes", + "follow_topics_you_create": "Seguir publicaciones que creas", "grouptitle": "Selecciona el título del grupo que deseas visualizar", "no-group-title": "Sin título de grupo" } \ No newline at end of file diff --git a/public/language/et/email.json b/public/language/et/email.json index 9f3a49318e..1f1c1e853a 100644 --- a/public/language/et/email.json +++ b/public/language/et/email.json @@ -1,28 +1,28 @@ { "password-reset-requested": "Parooli muutmise taotlus - %1!", - "welcome-to": "Tere tulemast %1", + "welcome-to": "Tere tulemast foorumisse %1", "greeting_no_name": "Tere", "greeting_with_name": "Tere %1", - "welcome.text1": "Thank you for registering with %1!", - "welcome.text2": "To fully activate your account, we need to verify that you own the email address you registered with.", - "welcome.cta": "Click here to confirm your email address", - "reset.text1": "We received a request to reset your password, possibly because you have forgotten it. If this is not the case, please ignore this email.", - "reset.text2": "To continue with the password reset, please click on the following link:", - "reset.cta": "Click here to reset your password", - "reset.notify.subject": "Password successfully changed", - "reset.notify.text1": "We are notifying you that on %1, your password was changed successfully.", - "reset.notify.text2": "If you did not authorise this, please notify an administrator immediately.", - "digest.notifications": "You have unread notifications from %1:", - "digest.latest_topics": "Latest topics from %1", - "digest.cta": "Click here to visit %1", - "digest.unsub.info": "This digest was sent to you due to your subscription settings.", - "digest.no_topics": "There have been no active topics in the past %1", - "notif.chat.subject": "New chat message received from %1", - "notif.chat.cta": "Click here to continue the conversation", - "notif.chat.unsub.info": "This chat notification was sent to you due to your subscription settings.", - "notif.post.cta": "Click here to read the full topic", - "notif.post.unsub.info": "This post notification was sent to you due to your subscription settings.", - "test.text1": "This is a test email to verify that the emailer is set up correctly for your NodeBB.", - "unsub.cta": "Click here to alter those settings", - "closing": "Thanks!" + "welcome.text1": "Täname et oled registreerinud foorumisse %1!", + "welcome.text2": "Konto täielikuks aktiveerimiseks peame me kinnitama, et registreerimisel kasutatud e-mail kuulub teile.", + "welcome.cta": "Vajuta siia, et kinnitada oma e-maili aadress", + "reset.text1": "Meile laekus päring parooli muutmiseks. Kui päring ei ole teie poolt esitatud või te ei soovi parooli muuta, siis võite antud kirja ignoreerida.", + "reset.text2": "Selleks, et jätkata parooli muutmisega vajuta järgnevale lingile:", + "reset.cta": "Vajuta siia, et taotleda uut parooli", + "reset.notify.subject": "Parool edukalt muudetud", + "reset.notify.text1": "Teavitame sind, et sinu parool %1 foorumis on edukalt muudetud.", + "reset.notify.text2": "Kui te ei ole lubanud seda, siis teavitage koheselt administraatorit.", + "digest.notifications": "Sul on lugemata teateid %1 poolt:", + "digest.latest_topics": "Viimased teemad %1 poolt", + "digest.cta": "Vajuta siia et külastada %1", + "digest.unsub.info": "See uudiskiri on saadetud teile tellimuse seadistuse tõttu.", + "digest.no_topics": "Viimase %1 jooksul ei ole olnud ühtegi aktiivset teemat", + "notif.chat.subject": "Sulle on saabunud uus sõnum kasutajalt %1", + "notif.chat.cta": "Vajuta siia, et jätkata vestlusega", + "notif.chat.unsub.info": "See chat teavitus on saadetud teile tellimuse seadistuse tõttu.", + "notif.post.cta": "Vajuta siia, et lugeda teemat täies mahus", + "notif.post.unsub.info": "See postituse teavitus on saadetud teile tellimuse seadistuse tõttu.", + "test.text1": "See on test e-mail kinnitamaks, et emailer on korrektselt seadistatud sinu NodeBB jaoks.", + "unsub.cta": "Vajuta siia, et muuta neid seadeid", + "closing": "Aitäh!" } \ No newline at end of file diff --git a/public/language/et/error.json b/public/language/et/error.json index 776586b76c..bceffa41e1 100644 --- a/public/language/et/error.json +++ b/public/language/et/error.json @@ -2,7 +2,7 @@ "invalid-data": "Vigased andmed", "not-logged-in": "Sa ei ole sisse logitud", "account-locked": "Su kasutaja on ajutiselt lukustatud", - "search-requires-login": "Foorumis otsimiseks on vaja kasutajat! Palun logi sisse või registreeru kasutajaks!", + "search-requires-login": "Searching requires an account - please login or register.", "invalid-cid": "Vigane kategooria ID", "invalid-tid": "Vigane teema ID", "invalid-pid": "Vigane postituse ID", @@ -68,6 +68,7 @@ "invalid-file": "Vigane fail", "uploads-are-disabled": "Üleslaadimised on keelatud", "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", + "about-me-too-long": "Sorry, your about me cannot be longer than %1 character(s).", "cant-chat-with-yourself": "Sa ei saa endaga vestelda!", "chat-restricted": "Kasutaja on piiranud sõnumite saatmist. Privaatsõnumi saatmiseks peab kasutaja sind jälgima", "too-many-messages": "Oled saatnud liiga palju sõnumeid, oota natukene.", diff --git a/public/language/et/topic.json b/public/language/et/topic.json index f5d72479c9..4359384ac4 100644 --- a/public/language/et/topic.json +++ b/public/language/et/topic.json @@ -5,6 +5,7 @@ "no_topics_found": "Teemasid ei leitud!", "no_posts_found": "Postitusi ei leitud!", "post_is_deleted": "See postitus on kustutatud!", + "topic_is_deleted": "This topic is deleted!", "profile": "Profiil", "posted_by": "Postitas %1", "posted_by_guest": "Postitatud külalise ppolt", diff --git a/public/language/et/user.json b/public/language/et/user.json index 6c015502db..5cd87ac9a2 100644 --- a/public/language/et/user.json +++ b/public/language/et/user.json @@ -21,6 +21,7 @@ "watched": "Vaadatud", "followers": "Jälgijad", "following": "Jälgimised", + "aboutme": "About me", "signature": "Allkiri", "gravatar": "Gravatar", "birthday": "Sünnipäev", diff --git a/public/language/fa_IR/email.json b/public/language/fa_IR/email.json index 1ed97a0d8c..146d6ebb0d 100644 --- a/public/language/fa_IR/email.json +++ b/public/language/fa_IR/email.json @@ -1,5 +1,5 @@ { - "password-reset-requested": "درخواست گذرواژه مجدد- %1!", + "password-reset-requested": "درخواست بازیابی گذرواژه- %1!", "welcome-to": "به 1% خوش آمدید", "greeting_no_name": "سلام", "greeting_with_name": "سلام 1%", diff --git a/public/language/fa_IR/error.json b/public/language/fa_IR/error.json index b7e7237c4a..37e2a353b3 100644 --- a/public/language/fa_IR/error.json +++ b/public/language/fa_IR/error.json @@ -1,8 +1,8 @@ { - "invalid-data": "اطلاعات نامعتبر است.", + "invalid-data": "داده(های) نامعتبر", "not-logged-in": "به نظر میرسد که با حساب کاربری وارد نشده اید.", "account-locked": "حساب کاربری شما موقتاً مسدود شده است.", - "search-requires-login": "جستجو نیاز به حساب کاربری دارد! لطفا وارد شوید و یا ثبت نام کنید!", + "search-requires-login": "Searching requires an account - please login or register.", "invalid-cid": "شناسه دسته نامعتبر است.", "invalid-tid": "شناسه جستار نامعتبر است.", "invalid-pid": "شناسه دیدگاه نامعتبر است.", @@ -21,11 +21,11 @@ "email-not-confirmed-chat": "شما تا قبل از تایید رایانامه قادر به گفتگو نیستید، لطفا برای تایید رایانامه خود اینجا کلیک کنید", "no-email-to-confirm": "این انجمن نیاز به تایید رایانامه دارد، لطفا برای وارد کردن رایانامه اینجا کلیک کنید", "email-confirm-failed": "ما نتوانستیم رایانامه شما را تایید کنیم، لطفا بعدا دوباره سعی کنید", - "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", + "confirm-email-already-sent": "رایانامه فعال‌سازی پیش‌تر فرستاده شده، لطفا %1 دقیقه صبر کنید تا رایانامه دیگری بفرستید.", "username-too-short": "نام کاربری خیلی کوتاه است.", "username-too-long": "نام کاربری بسیار طولانیست", "user-banned": "کاربر محروم شد.", - "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", + "user-too-new": "با عرض پوزش، شما باید %1 ثانیه پیش از فرستادن دیدگاه نخست خود صبر کنید", "no-category": "دسته بندی وجود ندارد", "no-topic": "جستار وجود ندارد.", "no-post": "دیدگاه وجود ندارد", @@ -36,9 +36,9 @@ "no-emailers-configured": "افزونه ایمیلی بارگیری نشده است، پس رایانامه امتحانی نمیتواند فرستاده شود", "category-disabled": "دسته غیر‌فعال شد.", "topic-locked": "جستار بسته شد.", - "post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting", + "post-edit-duration-expired": "شما تنها می توانید %1 ثانیه پس از فرستادن دیدگاه آن‌را ویرایش کنید", "still-uploading": "خواهشمندیم تا پایان بارگذاری‌ها شکیبا باشید.", - "content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).", + "content-too-short": "خواهشمندیم دیدگاه بلندتری بنویسید. دیدگاه‌ها دست‌کم باید 1% نویسه داشته باشند.", "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).", "title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).", "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).", @@ -63,11 +63,12 @@ "post-already-restored": "دیدگاه پیش‌تر بازگردانی شده است.", "topic-already-deleted": "جستار پیش‌تر حذف شده است", "topic-already-restored": "جستار پیش‌تر بازگردانی شده است", - "cant-purge-main-post": "You can't purge the main post, please delete the topic instead", + "cant-purge-main-post": "شما نمی‌توانید دیدگاه اصلی را پاک کنید، لطفا جستار را به جای آن پاک کنید.", "topic-thumbnails-are-disabled": "چهرک‌های جستار غیرفعال شده است.", "invalid-file": "فایل نامعتبر است.", "uploads-are-disabled": "امکان بارگذاری غیرفعال شده است.", "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", + "about-me-too-long": "Sorry, your about me cannot be longer than %1 character(s).", "cant-chat-with-yourself": "شما نمی‌توانید با خودتان گفتگو کنید!", "chat-restricted": "این کاربر پیام های گفتگوی خود را محدود کرده است . آنها بایدشما را دنبال کنند تا اینکه شما بتوانید به آنها پیامی بفرستید", "too-many-messages": "شما پیامهای خیلی زیادی فرستاده اید، لطفا مدتی صبر نمایید", diff --git a/public/language/fa_IR/global.json b/public/language/fa_IR/global.json index b68c1d212d..f3594f29d3 100644 --- a/public/language/fa_IR/global.json +++ b/public/language/fa_IR/global.json @@ -27,7 +27,7 @@ "header.tags": "برچسب‌ها", "header.popular": "دوست‌داشتنی‌ها", "header.users": "کاربران", - "header.groups": "گروه ها", + "header.groups": "گروه‌ها", "header.chats": "گفتگوها", "header.notifications": "آگاه‌سازی‌ها", "header.search": "جستجو", diff --git a/public/language/fa_IR/groups.json b/public/language/fa_IR/groups.json index f16a3f3200..3f4d7d1c2c 100644 --- a/public/language/fa_IR/groups.json +++ b/public/language/fa_IR/groups.json @@ -1,5 +1,5 @@ { - "groups": "گروه ها", + "groups": "گروه‌ها", "view_group": "مشاهده گروه", "owner": "مالک گروه", "new_group": "ساخت گروه جدید", @@ -30,7 +30,7 @@ "details.userTitleEnabled": "نمایش نشان", "details.private_help": "اگر فعال باشد، پیوستن به گروه مستلزم موافقت صاحب گروه است", "details.hidden": "پنهان", - "details.hidden_help": "اگر فعال باشد، ایم گروه در فهرست گروه ها پیدا نمیشود، و کاربر باید دستی دعوت شود", + "details.hidden_help": "اگر فعال باشد، این گروه در فهرست گروه‌ها پیدا نمی‌شود و کاربران باید دستی فراخوانده شوند", "event.updated": "جزییات گروه با موفقیت به روز گردید", "event.deleted": "گروه \"%1\" حدف شد" } \ No newline at end of file diff --git a/public/language/fa_IR/language.json b/public/language/fa_IR/language.json index de83f729fb..c387007d53 100644 --- a/public/language/fa_IR/language.json +++ b/public/language/fa_IR/language.json @@ -1,5 +1,5 @@ { - "name": "Persian (Iran)", + "name": "فارسی", "code": "fa_IR", "dir": "rtl" } \ No newline at end of file diff --git a/public/language/fa_IR/pages.json b/public/language/fa_IR/pages.json index 18fa275e86..cf7accc690 100644 --- a/public/language/fa_IR/pages.json +++ b/public/language/fa_IR/pages.json @@ -12,7 +12,7 @@ "user.followers": "کاربرانی که %1 را دنبال می‌کنند", "user.posts": "دیدگاه‌های %1", "user.topics": "%1 این جستار را ساخت.", - "user.groups": "گروه های %1", + "user.groups": "گروه‌های %1", "user.favourites": "دیدگاه‌های پسندیدهٔ %1", "user.settings": "تنظیمات کاربر", "user.watched": "جستارهای پاییده شده توسط %1", diff --git a/public/language/fa_IR/search.json b/public/language/fa_IR/search.json index 52bcdc087a..6fe0ae4e0e 100644 --- a/public/language/fa_IR/search.json +++ b/public/language/fa_IR/search.json @@ -1,5 +1,5 @@ { - "results_matching": "%1 نتیجه (ها) مطابق با \"%2\" ,(%3 ثانیه)", + "results_matching": "%1 نتیجهٔ هم‌خوان با \"%2\"، (%3 ثانیه)", "no-matches": "هیچ موردی یافت نشد", "advanced-search": "جستجوی پیشرفته", "in": "در", diff --git a/public/language/fa_IR/success.json b/public/language/fa_IR/success.json index bfebde56e2..01984621ce 100644 --- a/public/language/fa_IR/success.json +++ b/public/language/fa_IR/success.json @@ -1,5 +1,5 @@ { - "success": "موفقیت", + "success": "موفقیت‌آمیز", "topic-post": "دیدگاه شما باموفقیت فرستاده شد.", "authentication-successful": "اعتبارسنجی موفق", "settings-saved": "تنظیمات اندوخته شد." diff --git a/public/language/fa_IR/topic.json b/public/language/fa_IR/topic.json index 2ed30ffa38..3a26922060 100644 --- a/public/language/fa_IR/topic.json +++ b/public/language/fa_IR/topic.json @@ -5,6 +5,7 @@ "no_topics_found": "هیچ جستاری یافت نشد!", "no_posts_found": "دیدگاهی یافت نشد!", "post_is_deleted": "این دیدگاه پاک شده!", + "topic_is_deleted": "This topic is deleted!", "profile": "نمایه", "posted_by": "ارسال شده توسط %1", "posted_by_guest": "ارسال شده توسط مهمان", diff --git a/public/language/fa_IR/user.json b/public/language/fa_IR/user.json index e3722d684c..2758770015 100644 --- a/public/language/fa_IR/user.json +++ b/public/language/fa_IR/user.json @@ -21,6 +21,7 @@ "watched": "پاییده شده", "followers": "دنبال‌کننده‌ها", "following": "دنبال‌شونده‌ها", + "aboutme": "About me", "signature": "امضا", "gravatar": "گراواتار", "birthday": "روز تولد", diff --git a/public/language/fi/error.json b/public/language/fi/error.json index 6001a3f211..0c38939c8c 100644 --- a/public/language/fi/error.json +++ b/public/language/fi/error.json @@ -2,7 +2,7 @@ "invalid-data": "Virheellinen data", "not-logged-in": "Et taida olla kirjautuneena sisään.", "account-locked": "Käyttäjätilisi on lukittu väliaikaisesti", - "search-requires-login": "Hakeminen vaatii käyttäjätilin! Kirjaudu sisään tai rekisteröidy!", + "search-requires-login": "Searching requires an account - please login or register.", "invalid-cid": "Virheellinen kategorian ID", "invalid-tid": "Virheellinen aiheen ID", "invalid-pid": "Virheellinen viestin ID", @@ -68,6 +68,7 @@ "invalid-file": "Virheellinen tiedosto", "uploads-are-disabled": "Et voi lähettää tiedostoa", "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", + "about-me-too-long": "Sorry, your about me cannot be longer than %1 character(s).", "cant-chat-with-yourself": "Et voi keskustella itsesi kanssa!", "chat-restricted": "This user has restricted their chat messages. They must follow you before you can chat with them", "too-many-messages": "You have sent too many messages, please wait awhile.", diff --git a/public/language/fi/topic.json b/public/language/fi/topic.json index d6812a7fa4..ec87d3db17 100644 --- a/public/language/fi/topic.json +++ b/public/language/fi/topic.json @@ -5,6 +5,7 @@ "no_topics_found": "Aiheita ei löytynyt!", "no_posts_found": "Viestejä ei löytynyt!", "post_is_deleted": "Tämä viesti poistettiin!", + "topic_is_deleted": "This topic is deleted!", "profile": "Profiili", "posted_by": "%1 kirjoitti", "posted_by_guest": "Vieras kirjoitti", diff --git a/public/language/fi/user.json b/public/language/fi/user.json index 3a4c176679..302f503beb 100644 --- a/public/language/fi/user.json +++ b/public/language/fi/user.json @@ -21,6 +21,7 @@ "watched": "Seurattu", "followers": "Seuraajat", "following": "Seuratut", + "aboutme": "About me", "signature": "Allekirjoitus", "gravatar": "Gravatar", "birthday": "Syntymäpäivä", diff --git a/public/language/fr/error.json b/public/language/fr/error.json index 880c9ce132..dd012bc393 100644 --- a/public/language/fr/error.json +++ b/public/language/fr/error.json @@ -2,7 +2,7 @@ "invalid-data": "Données invalides", "not-logged-in": "Vous ne semblez pas être connecté.", "account-locked": "Votre compte a été temporairement suspendu", - "search-requires-login": "Vous devez avoir un compte pour effectuer une recherche ! Veuillez vous identifier ou vous inscrire !", + "search-requires-login": "Rechercher nécessite d'avoir un compte. Veuillez vous identifier ou vous enregistrer.", "invalid-cid": "ID de catégorie invalide", "invalid-tid": "ID de sujet invalide", "invalid-pid": "ID de message invalide", @@ -68,6 +68,7 @@ "invalid-file": "Fichier invalide", "uploads-are-disabled": "Les envois sont désactivés", "signature-too-long": "La signature ne peut dépasser %1 caractère(s).", + "about-me-too-long": "Votre texte \"à propos de moi\" ne peut dépasser %1 caractère(s).", "cant-chat-with-yourself": "Vous ne pouvez chatter avec vous même !", "chat-restricted": "Cet utilisateur a restreint les ses messages de chat. Il doit d'abord vous suivre avant de pouvoir discuter avec lui.", "too-many-messages": "Vous avez envoyé trop de messages, veuillez patienter un instant.", diff --git a/public/language/fr/groups.json b/public/language/fr/groups.json index 6ad4028954..39b963ae57 100644 --- a/public/language/fr/groups.json +++ b/public/language/fr/groups.json @@ -32,5 +32,5 @@ "details.hidden": "Masqué", "details.hidden_help": "Si cette case est cochée, ce groupe n'est pas affiché dans la liste des groupes, et les utilisateurs devront être invités manuellement.", "event.updated": "Les détails du groupe ont été mis à jour", - "event.deleted": "Le groupe é%1\" a été supprimé" + "event.deleted": "Le groupe \"%1\" a été supprimé" } \ No newline at end of file diff --git a/public/language/fr/topic.json b/public/language/fr/topic.json index 8248eb2580..7a3649951c 100644 --- a/public/language/fr/topic.json +++ b/public/language/fr/topic.json @@ -5,6 +5,7 @@ "no_topics_found": "Aucun sujet n'a été trouvé !", "no_posts_found": "Aucun message trouvé !", "post_is_deleted": "Ce message a été supprimé !", + "topic_is_deleted": "Ce sujet a été supprimé !", "profile": "Profil", "posted_by": "Posté par %1", "posted_by_guest": "Posté par un invité", diff --git a/public/language/fr/user.json b/public/language/fr/user.json index b2e6c5a902..486ee48178 100644 --- a/public/language/fr/user.json +++ b/public/language/fr/user.json @@ -21,6 +21,7 @@ "watched": "Suivis", "followers": "Abonnés", "following": "Abonnements", + "aboutme": "À propos de moi", "signature": "Signature", "gravatar": "Gravatar", "birthday": "Anniversaire", diff --git a/public/language/he/error.json b/public/language/he/error.json index d3171986cc..ebe39d885d 100644 --- a/public/language/he/error.json +++ b/public/language/he/error.json @@ -2,7 +2,7 @@ "invalid-data": "נתונים שגויים", "not-logged-in": "נראה שאינך מחובר למערכת.", "account-locked": "חשבונך נחסם באופן זמני", - "search-requires-login": "החיפוש דורש משתמש רשום! יש להיכנס למערכת או להירשם!", + "search-requires-login": "Searching requires an account - please login or register.", "invalid-cid": "זהוי קטגוריה שגוי", "invalid-tid": "זהוי נושא שגוי", "invalid-pid": "זהוי פוסט שגוי", @@ -68,6 +68,7 @@ "invalid-file": "קובץ לא תקין", "uploads-are-disabled": "העלאת קבצים אינה מאופשרת", "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", + "about-me-too-long": "Sorry, your about me cannot be longer than %1 character(s).", "cant-chat-with-yourself": "לא ניתן לעשות צ'אט עם עצמך!", "chat-restricted": "משתמש זה חסם את הודעות הצ'אט שלו ממשתמשים זרים. המשתמש חייב לעקוב אחריך לפני שתוכל לשוחח איתו בצ'אט", "too-many-messages": "שלחת יותר מדי הודעות, אנא המתן לזמן מה.", diff --git a/public/language/he/topic.json b/public/language/he/topic.json index 3ff27d0a8a..62724d03a3 100644 --- a/public/language/he/topic.json +++ b/public/language/he/topic.json @@ -5,6 +5,7 @@ "no_topics_found": "לא נמצאו נושאים!", "no_posts_found": "לא נמצאו פוסטים!", "post_is_deleted": "פוסט זה נמחק!", + "topic_is_deleted": "This topic is deleted!", "profile": "פרופיל", "posted_by": "הפוסט הועלה על ידי %1", "posted_by_guest": "פורסם על-ידי אורח", diff --git a/public/language/he/user.json b/public/language/he/user.json index c2989b6233..6011356044 100644 --- a/public/language/he/user.json +++ b/public/language/he/user.json @@ -21,6 +21,7 @@ "watched": "נצפה", "followers": "עוקבים", "following": "עוקב אחרי", + "aboutme": "About me", "signature": "חתימה", "gravatar": "אווטר", "birthday": "יום הולדת", diff --git a/public/language/hu/error.json b/public/language/hu/error.json index e84486eb06..4f6a148af5 100644 --- a/public/language/hu/error.json +++ b/public/language/hu/error.json @@ -2,7 +2,7 @@ "invalid-data": "Érvénytelen adat", "not-logged-in": "Úgy tűnik, nem vagy bejelentkezve.", "account-locked": "A fiókod ideiglenesen zárolva lett.", - "search-requires-login": "A kereső használatához szükséges egy fiók! Kérlek jelenltkezz be vagy regisztrálj!", + "search-requires-login": "Searching requires an account - please login or register.", "invalid-cid": "Érvénytelen kategória azonosító", "invalid-tid": "Érvénytelen téma azonosító", "invalid-pid": "Érvénytelen hozzászólás azonosító", @@ -68,6 +68,7 @@ "invalid-file": "Érvénytelen fájl", "uploads-are-disabled": "A feltöltés nem engedélyezett", "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", + "about-me-too-long": "Sorry, your about me cannot be longer than %1 character(s).", "cant-chat-with-yourself": "Nem cseveghetsz magaddal!", "chat-restricted": "Ez a felhasználó korlátozta a chat beállításait. Csak akkor cseveghetsz vele, miután felvett a követettek közé téged", "too-many-messages": "Túl sok üzenetet küldtél, kérlek várj egy picit.", diff --git a/public/language/hu/search.json b/public/language/hu/search.json index d3df716f81..5309e9ec32 100644 --- a/public/language/hu/search.json +++ b/public/language/hu/search.json @@ -1,14 +1,14 @@ { "results_matching": "%1 eredmény(ek) erre \"%2\" (%3 másodperc alatt)", "no-matches": "No matches found", - "advanced-search": "Advanced Search", + "advanced-search": "Bővített keresés", "in": "In", - "titles": "Titles", - "titles-posts": "Titles and Posts", - "posted-by": "Posted by", - "in-categories": "In Categories", - "search-child-categories": "Search child categories", - "reply-count": "Reply Count", + "titles": "Címek", + "titles-posts": "Címek és Bejegyzések", + "posted-by": "Szerző", + "in-categories": "Kategória", + "search-child-categories": "Keresés az alkategóriák közt", + "reply-count": "Válaszok száma", "at-least": "At least", "at-most": "At most", "post-time": "Post time", diff --git a/public/language/hu/topic.json b/public/language/hu/topic.json index 30bf3ab2f8..bb467f2c5c 100644 --- a/public/language/hu/topic.json +++ b/public/language/hu/topic.json @@ -5,6 +5,7 @@ "no_topics_found": "Téma nem található!", "no_posts_found": "Hozzászólások nem találhatóak!", "post_is_deleted": "A bejegyzés törlésre került!", + "topic_is_deleted": "This topic is deleted!", "profile": "Profil", "posted_by": "Szerző %1", "posted_by_guest": "Szerző Vendég", diff --git a/public/language/hu/user.json b/public/language/hu/user.json index f35879c80e..b6f9906710 100644 --- a/public/language/hu/user.json +++ b/public/language/hu/user.json @@ -21,6 +21,7 @@ "watched": "Megfigyeli", "followers": "Követők", "following": "Követve", + "aboutme": "About me", "signature": "Aláírás", "gravatar": "Gravatar", "birthday": "Szülinap", diff --git a/public/language/id/error.json b/public/language/id/error.json index 508ebfcc6b..531b335a78 100644 --- a/public/language/id/error.json +++ b/public/language/id/error.json @@ -2,7 +2,7 @@ "invalid-data": "Data Salah", "not-logged-in": "Kamu terlihat belum login", "account-locked": "Akun kamu dikunci sementara", - "search-requires-login": "Pencarian butuh sebuah akun, silakan login atau register terlebih dulu", + "search-requires-login": "Searching requires an account - please login or register.", "invalid-cid": "ID Kategori Salah", "invalid-tid": "ID Topik Salah", "invalid-pid": "ID Post Salah", @@ -68,6 +68,7 @@ "invalid-file": "File Salah", "uploads-are-disabled": "Upload ditiadakan", "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", + "about-me-too-long": "Sorry, your about me cannot be longer than %1 character(s).", "cant-chat-with-yourself": "Kamu tidak dapat chat dengan akun sendiri", "chat-restricted": "Pengguna ini telah membatasi percakapa mereka. Mereka harus mengikutimu sebelum kamu dapat melakukan percakapan dengan mereka ", "too-many-messages": "You have sent too many messages, please wait awhile.", diff --git a/public/language/id/topic.json b/public/language/id/topic.json index c47b9612be..5f119c3e80 100644 --- a/public/language/id/topic.json +++ b/public/language/id/topic.json @@ -5,6 +5,7 @@ "no_topics_found": "Tidak topik yang ditemukan!", "no_posts_found": "Tidak ada posting yang ditemukan!", "post_is_deleted": "Posting ini telah dihapus!", + "topic_is_deleted": "This topic is deleted!", "profile": "Profil", "posted_by": "Dibuat oleh %1", "posted_by_guest": "Dibuat oleh Tamu", diff --git a/public/language/id/user.json b/public/language/id/user.json index 5faa4375a1..74cfa9f7d6 100644 --- a/public/language/id/user.json +++ b/public/language/id/user.json @@ -21,6 +21,7 @@ "watched": "Watched", "followers": "Pengikut", "following": "Mengikuti", + "aboutme": "About me", "signature": "Tanda Pengenal", "gravatar": "Gravatar", "birthday": "Hari Lahir", diff --git a/public/language/it/error.json b/public/language/it/error.json index fc59c81e2c..7577b34f43 100644 --- a/public/language/it/error.json +++ b/public/language/it/error.json @@ -2,7 +2,7 @@ "invalid-data": "Dati non validi", "not-logged-in": "Non sembri essere loggato.", "account-locked": "Il tuo account è stato bloccato temporaneamente", - "search-requires-login": "La ricerca richiede un account! Si prega di loggarsi o registrarsi!", + "search-requires-login": "Searching requires an account - please login or register.", "invalid-cid": "ID Categoria non valido", "invalid-tid": "ID Topic non valido", "invalid-pid": "ID Post non valido", @@ -68,6 +68,7 @@ "invalid-file": "File non valido", "uploads-are-disabled": "Uploads disabilitati", "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", + "about-me-too-long": "Sorry, your about me cannot be longer than %1 character(s).", "cant-chat-with-yourself": "Non puoi chattare con te stesso!", "chat-restricted": "Questo utente ha ristretto i suoi messaggi in chat alle persone che segue. Per poter chattare con te ti deve prima seguire.", "too-many-messages": "Hai inviato troppi messaggi, aspetta un attimo.", diff --git a/public/language/it/topic.json b/public/language/it/topic.json index e53ac5c6f2..d442472c00 100644 --- a/public/language/it/topic.json +++ b/public/language/it/topic.json @@ -5,6 +5,7 @@ "no_topics_found": "Nessuna discussione trovata!", "no_posts_found": "Nessun post trovato!", "post_is_deleted": "Questo post è eliminato!", + "topic_is_deleted": "This topic is deleted!", "profile": "Profilo", "posted_by": "scritto da %1", "posted_by_guest": "Scritto da Ospite", diff --git a/public/language/it/user.json b/public/language/it/user.json index ec6779bccc..38e2565652 100644 --- a/public/language/it/user.json +++ b/public/language/it/user.json @@ -21,6 +21,7 @@ "watched": "Osservati", "followers": "Da chi è seguito", "following": "Chi segue", + "aboutme": "About me", "signature": "Firma", "gravatar": "Gravatar", "birthday": "Data di nascita", diff --git a/public/language/ja/error.json b/public/language/ja/error.json index f4edefb38f..631406319a 100644 --- a/public/language/ja/error.json +++ b/public/language/ja/error.json @@ -2,7 +2,7 @@ "invalid-data": "無効なデータ", "not-logged-in": "ログインしていていないようです。", "account-locked": "Your account has been locked temporarily", - "search-requires-login": "Searching requires an account! Please login or register!", + "search-requires-login": "Searching requires an account - please login or register.", "invalid-cid": "無効な板ID", "invalid-tid": "無効なスレッドID", "invalid-pid": "無効なポストID", @@ -68,6 +68,7 @@ "invalid-file": "無効なファイル", "uploads-are-disabled": "アップロードが無効された", "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", + "about-me-too-long": "Sorry, your about me cannot be longer than %1 character(s).", "cant-chat-with-yourself": "自分にチャットすることはできません!", "chat-restricted": "This user has restricted their chat messages. They must follow you before you can chat with them", "too-many-messages": "You have sent too many messages, please wait awhile.", diff --git a/public/language/ja/topic.json b/public/language/ja/topic.json index 680b10b702..cd7dace96d 100644 --- a/public/language/ja/topic.json +++ b/public/language/ja/topic.json @@ -5,6 +5,7 @@ "no_topics_found": "スレッドが見つかりません!", "no_posts_found": "ポストはありません!", "post_is_deleted": "このポストが削除されます!", + "topic_is_deleted": "This topic is deleted!", "profile": "プロフィール", "posted_by": "%1 のポスト", "posted_by_guest": "Posted by Guest", diff --git a/public/language/ja/user.json b/public/language/ja/user.json index a480f54f67..55e52d7cb0 100644 --- a/public/language/ja/user.json +++ b/public/language/ja/user.json @@ -21,6 +21,7 @@ "watched": "Watched", "followers": "フォロワー", "following": "フォロー中", + "aboutme": "About me", "signature": "署名", "gravatar": "Gravatar", "birthday": "誕生日", diff --git a/public/language/ko/error.json b/public/language/ko/error.json index c9f81d74db..5bfcca4262 100644 --- a/public/language/ko/error.json +++ b/public/language/ko/error.json @@ -2,7 +2,7 @@ "invalid-data": "올바르지 않은 정보입니다.", "not-logged-in": "로그인하지 않았습니다.", "account-locked": "임시로 잠긴 계정입니다.", - "search-requires-login": "검색을 위해서는 계정이 필요합니다. 로그인하거나 회원가입 해 주십시오.", + "search-requires-login": "Searching requires an account - please login or register.", "invalid-cid": "올바르지 않은 카테고리 ID입니다.", "invalid-tid": "올바르지 않은 주제 ID입니다.", "invalid-pid": "올바르지 않은 게시물 ID입니다.", @@ -68,6 +68,7 @@ "invalid-file": "올바르지 않은 파일입니다.", "uploads-are-disabled": "업로드는 비활성화되어 있습니다.", "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", + "about-me-too-long": "Sorry, your about me cannot be longer than %1 character(s).", "cant-chat-with-yourself": "자신과는 채팅할 수 없습니다.", "chat-restricted": "This user has restricted their chat messages. They must follow you before you can chat with them", "too-many-messages": "You have sent too many messages, please wait awhile.", diff --git a/public/language/ko/topic.json b/public/language/ko/topic.json index 56714d7a0a..9e113910a1 100644 --- a/public/language/ko/topic.json +++ b/public/language/ko/topic.json @@ -5,6 +5,7 @@ "no_topics_found": "주제를 찾을 수 없습니다.", "no_posts_found": "게시물을 찾을 수 없습니다.", "post_is_deleted": "이 게시물은 삭제되었습니다.", + "topic_is_deleted": "This topic is deleted!", "profile": "프로필", "posted_by": "%1님이 작성했습니다.", "posted_by_guest": "익명 사용자가 작성했습니다.", diff --git a/public/language/ko/user.json b/public/language/ko/user.json index 70ce30520a..324eccb26f 100644 --- a/public/language/ko/user.json +++ b/public/language/ko/user.json @@ -21,6 +21,7 @@ "watched": "Watched", "followers": "이 사용자를 팔로우", "following": "이 사용자가 팔로우", + "aboutme": "About me", "signature": "서명", "gravatar": "그라바타", "birthday": "생일", diff --git a/public/language/lt/error.json b/public/language/lt/error.json index b87aac771c..49023f88aa 100644 --- a/public/language/lt/error.json +++ b/public/language/lt/error.json @@ -2,7 +2,7 @@ "invalid-data": "Klaidingi duomenys", "not-logged-in": "Atrodo, kad jūs neesate prisijungęs.", "account-locked": "Jūsų paskyra buvo laikinai užrakinta", - "search-requires-login": "Norint naudotis paieška, būtina paskyra! Prašome prisijungti arba užsiregistruoti.", + "search-requires-login": "Searching requires an account - please login or register.", "invalid-cid": "Klaidingas kategorijos ID", "invalid-tid": "Klaidingas temos ID", "invalid-pid": "Klaidingas pranešimo ID", @@ -68,6 +68,7 @@ "invalid-file": "Klaidingas failas", "uploads-are-disabled": "Įkėlimai neleidžiami", "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", + "about-me-too-long": "Sorry, your about me cannot be longer than %1 character(s).", "cant-chat-with-yourself": "Jūs negalite susirašinėti su savimi!", "chat-restricted": "This user has restricted their chat messages. They must follow you before you can chat with them", "too-many-messages": "Išsiuntėte per daug pranešimų, kurį laiką prašome palaukti.", diff --git a/public/language/lt/topic.json b/public/language/lt/topic.json index e79c869a1b..5969a8727b 100644 --- a/public/language/lt/topic.json +++ b/public/language/lt/topic.json @@ -5,6 +5,7 @@ "no_topics_found": "Temų nerasta!", "no_posts_found": "Įrašų nerasta!", "post_is_deleted": "Šis įrašas ištrintas!", + "topic_is_deleted": "This topic is deleted!", "profile": "Profilis", "posted_by": "Parašė %1", "posted_by_guest": "Parašė svečias", diff --git a/public/language/lt/user.json b/public/language/lt/user.json index 4924eb3b4d..147e525aa5 100644 --- a/public/language/lt/user.json +++ b/public/language/lt/user.json @@ -21,6 +21,7 @@ "watched": "Watched", "followers": "Sekėjai", "following": "Seka", + "aboutme": "About me", "signature": "Parašas", "gravatar": "Gravatar", "birthday": "Gimimo diena", diff --git a/public/language/ms/error.json b/public/language/ms/error.json index b0cc688b16..e9eefc19a6 100644 --- a/public/language/ms/error.json +++ b/public/language/ms/error.json @@ -2,7 +2,7 @@ "invalid-data": "Data Tak Sah", "not-logged-in": "Anda tidak log masuk.", "account-locked": "Akaun anda telah dikunci untuk seketika", - "search-requires-login": "Pencarian perlukan akaun! Log masuk atau daftar!", + "search-requires-login": "Searching requires an account - please login or register.", "invalid-cid": "Kategori ID Tak Sah", "invalid-tid": "Topik ID Tak Sah", "invalid-pid": "Kiriman ID Tak Sah", @@ -67,7 +67,8 @@ "topic-thumbnails-are-disabled": "Topik kecil dilumpuhkan.", "invalid-file": "Fail tak sah", "uploads-are-disabled": "Muatnaik dilumpuhkan", - "signature-too-long": "Maaf, tandatangan tidak boleh lebih daripada %1 aksara().", + "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", + "about-me-too-long": "Sorry, your about me cannot be longer than %1 character(s).", "cant-chat-with-yourself": "Anda tidak boleh sembang dengan diri sendiri!", "chat-restricted": "Pengguna ini menyekat ruangan sembangnya. Dia hendaklah mengikut anda sebelum kalian dapat bersembang", "too-many-messages": "Anda menghantar terlalu banyak pesanan, sila tunggu seketika.", diff --git a/public/language/ms/global.json b/public/language/ms/global.json index 5b1c91d6bb..1f8a8ffba5 100644 --- a/public/language/ms/global.json +++ b/public/language/ms/global.json @@ -11,7 +11,7 @@ "500.message": "Oops! Macam ada yang tidak kena", "register": "Daftar", "login": "Log Masuk", - "please_log_in": "Sila daftar masuk", + "please_log_in": "Sila log masuk", "logout": "Log Keluar", "posting_restriction_info": "Kiriman terhad kepada pengguna berdaftar sahaja, Sila click disini untuk daftar masuk", "welcome_back": "Selamat kembali", @@ -37,19 +37,19 @@ "motd.welcome": "Selamat datang ke NodeBB, platfom perbincangan masa hadapan", "previouspage": "Laman sebelum", "nextpage": "Laman berikut", - "alert.success": "berjaya", - "alert.error": "ralat", + "alert.success": "Berjaya", + "alert.error": "Ralat", "alert.banned": "Diharamkan", "alert.banned.message": "Amda baru sahaja diharamkan, anda sekarang akan di log keluar.", "alert.unfollow": "Anda tidak lagi mengikuti %1", "alert.follow": "Anda sekarang mengikuti %1", - "online": "dalam talian", + "online": "Dalam talian", "users": "Pengguna", "topics": "Topik", "posts": "Kiriman", - "views": "Paparan", + "views": "Lihat", "reputation": "Reputasi", - "read_more": "baca lafi", + "read_more": "baca lagi", "posted_ago_by_guest": "dikirim %1 oleh pelawat", "posted_ago_by": "dikirim %1 oleh %2", "posted_ago": "dikirim %1", @@ -64,16 +64,16 @@ "norecenttopics": "Tiada topik terkini", "recentposts": "Kiriman terkini", "recentips": "IP berdaftar terkini", - "away": "jauh", - "dnd": "jangan ganggu", + "away": "Jauh", + "dnd": "Jangan ganggu", "invisible": "Halimunan", - "offline": "Tidak ditalian", + "offline": "Luar talian", "email": "Emel", "language": "Bahasa", "guest": "Pelawat", "guests": "Pelawat", "updated.title": "Forum Dikemaskini", - "updated.message": "Forum ini baru dshsjs dikemaskini ke versi terkini. Klik sini untuk segar semula halaman.", + "updated.message": "Forum ini baru sahaja dikemaskini ke versi terkini. Klik sini untuk segar semula halaman.", "privacy": "Privasi", "follow": "Ikut", "unfollow": "Nyah-ikut", diff --git a/public/language/ms/topic.json b/public/language/ms/topic.json index fc1c334a0d..0b73e10449 100644 --- a/public/language/ms/topic.json +++ b/public/language/ms/topic.json @@ -5,6 +5,7 @@ "no_topics_found": "Tiada topik yang ditemui", "no_posts_found": "Tiada kirim yang dijumpai", "post_is_deleted": "Kiriman ini di padam!", + "topic_is_deleted": "This topic is deleted!", "profile": "Profil", "posted_by": "Dikirim oleh %1", "posted_by_guest": "Dikirim oleh pelawat", diff --git a/public/language/ms/user.json b/public/language/ms/user.json index 305f15bdb8..a9f7b41dd1 100644 --- a/public/language/ms/user.json +++ b/public/language/ms/user.json @@ -1,6 +1,6 @@ { "banned": "Diharamkan", - "offline": "Tidak ditalian", + "offline": "Luar talian", "username": "Nama pengguna", "joindate": "Tarikh Mula", "postcount": "Jumlah Kiriman", @@ -21,6 +21,7 @@ "watched": "Melihat", "followers": "Pengikut", "following": "Mengikuti", + "aboutme": "About me", "signature": "Tandatangan", "gravatar": "Gravatar", "birthday": "Tarikh lahir", diff --git a/public/language/nb/error.json b/public/language/nb/error.json index a03b17a6b4..fad46d11e6 100644 --- a/public/language/nb/error.json +++ b/public/language/nb/error.json @@ -2,7 +2,7 @@ "invalid-data": "Ugyldig data", "not-logged-in": "Du ser ikke ut til å være logget inn.", "account-locked": "Kontoen din har blitt midlertidig låst", - "search-requires-login": "Søking krever en konto! Vennligst registrer deg eller logg inn!", + "search-requires-login": "Searching requires an account - please login or register.", "invalid-cid": "Ugyldig kategori-ID", "invalid-tid": "Ugyldig emne-ID", "invalid-pid": "Ugyldig innlegg-ID", @@ -68,6 +68,7 @@ "invalid-file": "Ugyldig fil", "uploads-are-disabled": "Opplastninger er deaktivert", "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", + "about-me-too-long": "Sorry, your about me cannot be longer than %1 character(s).", "cant-chat-with-yourself": "Du kan ikke chatte med deg selv!", "chat-restricted": "Denne brukeren har begrenset sine chat-meldinger. De må følge deg før du kan chatte med dem", "too-many-messages": "Du har sendt for mange meldinger, vennligst vent en stund.", diff --git a/public/language/nb/topic.json b/public/language/nb/topic.json index 8a599b7942..3863fd48ae 100644 --- a/public/language/nb/topic.json +++ b/public/language/nb/topic.json @@ -5,6 +5,7 @@ "no_topics_found": "Ingen emner funnet!", "no_posts_found": "Ingen innlegg funnet!", "post_is_deleted": "Dette innlegget er slettet!", + "topic_is_deleted": "This topic is deleted!", "profile": "Profil", "posted_by": "Skapt av %1", "posted_by_guest": "Skapt av Gjest", diff --git a/public/language/nb/user.json b/public/language/nb/user.json index edb5f3d27d..be80c50470 100644 --- a/public/language/nb/user.json +++ b/public/language/nb/user.json @@ -21,6 +21,7 @@ "watched": "Overvåkede", "followers": "Følgere", "following": "Følger", + "aboutme": "About me", "signature": "Signatur", "gravatar": "Gravatar", "birthday": "Bursdag", diff --git a/public/language/nl/category.json b/public/language/nl/category.json index afb8eb21c8..9070eb6ba4 100644 --- a/public/language/nl/category.json +++ b/public/language/nl/category.json @@ -7,6 +7,6 @@ "share_this_category": "Deel deze categorie", "watch": "Volgen", "ignore": "Negeren", - "watch.message": "Je krijgt nu updates binnen van deze categorie", - "ignore.message": "Je krijgt geen updates meer binnen van deze categorie" + "watch.message": "Van deze categorie worden nu meldingen ontvangen", + "ignore.message": "Er worden geen meldingen van deze categorie ontvangen" } \ No newline at end of file diff --git a/public/language/nl/email.json b/public/language/nl/email.json index 18d7ab4a97..6eeb3de7f6 100644 --- a/public/language/nl/email.json +++ b/public/language/nl/email.json @@ -1,28 +1,28 @@ { - "password-reset-requested": "Wachtwoord Reset Aangevraagd - %1!", + "password-reset-requested": "Om wachtwoordherstel verzocht - %1!", "welcome-to": "Welkom bij %1", "greeting_no_name": "Hallo", "greeting_with_name": "Hallo %1", - "welcome.text1": "Bedank voor het registreren met %1!", - "welcome.text2": "Om u account volledig te activeren, moet u op de link klikken die u heeft ontvangen in uw inbox", - "welcome.cta": "Klik hier om te bevestigen met uw email adres", - "reset.text1": "Wij ontvingen een verzoek van u om uw wachtwoord te resetten. Als dat niet het geval is, kunt u deze mail negeren ", - "reset.text2": "Om uw wachtwoord te resetten, klik op de volgende link", - "reset.cta": "Klik hier om u wachtwoord te resetten", - "reset.notify.subject": "Wachtwoord succesvol veranderd", - "reset.notify.text1": "Wij brengen u bij deze op de hoogte that uw wachtwoord succesvol is gewijzigd op %1.", - "reset.notify.text2": "Neem contact op met een administrator als u hier geen toestemming voor gegeven heeft.", - "digest.notifications": "U heeft ongelezen notificaties van %1:", - "digest.latest_topics": "De laatste onderwerpen van %1", + "welcome.text1": "Bedank voor het registreren bij %1!", + "welcome.text2": "Om de account volledig te activeren, dienen de instructies uit het bevestigingsbericht opgevolgd te worden. Controleer daarom nu eerst de e-mail inbox voor de activeringscode en volg de link in het bericht.", + "welcome.cta": "Klik hier voor bevestigen van het e-mailadres", + "reset.text1": "Wij ontvingen zojuist een verzoek om het wachtwoord van de account, bij onze website bekend als gekoppeld aan dit e-mailadres, te herstellen. Is dit verzoek niet bekend en geautoriseerd, dan kan dit bericht genegeerd worden.", + "reset.text2": "Om het wachtwoord opnieuw in te stellen, klik op deze link:", + "reset.cta": "Klik hier voor wachtwoordherstel", + "reset.notify.subject": "Wachtwoord succesvol gewijzigd", + "reset.notify.text1": "Op %1 is het wachtwoord van de bij ons geregistreerde gebruikersaccount succesvol gewijzigd.", + "reset.notify.text2": "Neem onmiddellijk contact met een beheerder op wanneer geen toestemming voor deze actie gegeven is en deze niet vanuit hier aangevraagd is.", + "digest.notifications": "Er zijn ongelezen notificaties van %1:", + "digest.latest_topics": "De meest recente onderwerpen van %1", "digest.cta": "Klik hier om deze website te bezoeken %1 ", - "digest.unsub.info": "Deze overzicht was verzonden naar jou vanwege je abbonement instellingen", - "digest.no_topics": "Er zijn geen actieve onderwerpen in de afgelopen dagen %1", - "notif.chat.subject": "U heeft een chatbericht ontvangen van %1", + "digest.unsub.info": "Deze samenvatting is vanwege instellingen voor abonnementen van de gebruikersaccount door ons verzonden.", + "digest.no_topics": "Er zijn geen actieve onderwerpen geweest %1", + "notif.chat.subject": "Nieuw chatbericht ontvangen van %1", "notif.chat.cta": "Klik hier om het gesprek te hervatten", - "notif.chat.unsub.info": "Deze chat notificatie was verzonden naar jou vanwege je abbonement instellingen", + "notif.chat.unsub.info": "Deze notificatie is verzonden vanwege de gebruikersinstellingen voor abonnementen.", "notif.post.cta": "Klik hier om het volledige bericht te lezen", - "notif.post.unsub.info": "Deze bericht notificatie werd naar u verstuurd wegens uw abonnement instellingen.", - "test.text1": "Dit is een test email om te verifiëren dat de email service correct is opgezet voor jou NodeBB", - "unsub.cta": "Klik hier om u instellingen te wijzigen", + "notif.post.unsub.info": "Deze notificatie is door ons verzonden vanwege gebruikersinstellingen voor abonnementen en berichten.", + "test.text1": "Dit is een testbericht om te verifiëren dat NodeBB de e-mailberichtservice correct heeft opgezet.", + "unsub.cta": "Klik hier om deze instellingen te wijzigen", "closing": "Bedankt!" } \ No newline at end of file diff --git a/public/language/nl/error.json b/public/language/nl/error.json index 984ed8e4a1..95f7d04f1d 100644 --- a/public/language/nl/error.json +++ b/public/language/nl/error.json @@ -1,83 +1,84 @@ { "invalid-data": "Ongeldige Data", - "not-logged-in": "Het lijkt dat je niet bent ingelogd.", - "account-locked": "U account is tijdelijk geblockt", - "search-requires-login": "Het zoeken naar berichten of topics vereist een account. Registreer een account of meld u aan.", - "invalid-cid": "Ongeldig Categorie ID", - "invalid-tid": "Ongeldig Onderwerp ID", - "invalid-pid": "Ongeldig Bericht ID", - "invalid-uid": "Ongeldig Gebruikers ID", - "invalid-username": "Ongeldig Gebruikersnaam", - "invalid-email": "Ongeldig Email Adres", - "invalid-title": "Ongeldige Titel!", - "invalid-user-data": "Ongeldig Gebruikersdata", + "not-logged-in": "De account lijkt op dit moment niet aangemeld te zijn.", + "account-locked": "De account is tijdelijk vergrendeld", + "search-requires-login": "Searching requires an account - please login or register.", + "invalid-cid": "Ongeldig categoriesleutel", + "invalid-tid": "Ongeldig id voor onderwerp", + "invalid-pid": "Ongeldig berichtkenmerk", + "invalid-uid": "Ongeldig gebruikerskenmerk", + "invalid-username": "Ongeldige gebruikersnaam", + "invalid-email": "Ongeldig e-mailadres", + "invalid-title": "Ongeldige titel", + "invalid-user-data": "Ongeldige gebruikersgegevens", "invalid-password": "Ongeldig wachtwoord", - "invalid-username-or-password": "Geef alsjeblieft een gebruikersnaam en wachtwoord op", - "invalid-search-term": "Ongeldig zoekterm", - "invalid-pagination-value": "Ongeldig pagineringswaarde", - "username-taken": "Gebruikersnaam is al bezet", - "email-taken": "Email adres is al gebruikt", - "email-not-confirmed": "U email adres is niet bevestigd, Klik hier om uw email adres te bevestigen", - "email-not-confirmed-chat": "U kunt helaas geen gebruik maken van chats tot uw email adres bevestigd is.", - "no-email-to-confirm": "Dit forum vereist email bevestiging, klikt u alstublieft hier om uw email te vermelden", - "email-confirm-failed": "Uw email kon helaas niet bevestigd worden, probeert u het alstublieft later nog eens.", - "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", - "username-too-short": "Gebruikersnaam is te kort", - "username-too-long": "Gebruikersnaam is te lang", + "invalid-username-or-password": "Geef zowel een gebruikersnaam als wachtwoord op", + "invalid-search-term": "Ongeldig zoekopdracht, een of meerdere termen", + "invalid-pagination-value": "Ongeldig waarde voor paginering", + "username-taken": "Gebruikersnaam is al in gebruik bij een andere account", + "email-taken": "E-mailadres is al eens eerder gebruikt", + "email-not-confirmed": "Het e-mailadres van deze account is nog niet bevestigd. Klik hier om het e-mailadres te bevestigen en de registratie af te ronden.", + "email-not-confirmed-chat": "Het gebruik van chatfunctionaliteit is pas toegestaan na validatie van het e-mailadres.", + "no-email-to-confirm": "Dit berichtenforum vereist bevestiging per e-mail, klik hier om een e-mailadres te registreren", + "email-confirm-failed": "Helaas kon het e-mailadres niet bevestigd worden, probeert het later nog eens.", + "confirm-email-already-sent": "Bevestigingsbericht per e-mail al zojuist verzonden, wacht even een %1 tal minuutjes voordat opnieuw een bericht verzonden wordt. ", + "username-too-short": "Gebruikersnaam bevat niet voldoende tekens", + "username-too-long": "Gebruikersnaam bevat meer dan het toegestane aantal tekens", "user-banned": "Gebruiker verbannen", - "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", + "user-too-new": "Helaas, maar het is een vereiste om %1 seconde(n) te wachten voordat het eerste bericht geplaatst kan worden.", "no-category": "Categorie bestaat niet", - "no-topic": "Topic bestaat niet", + "no-topic": "Onderwerp bestaat niet", "no-post": "Bericht bestaat niet", "no-group": "Groep bestaat niet", "no-user": "Gebruiker bestaat niet", - "no-teaser": "Dit kort bericht bestaat niet", - "no-privileges": "U heeft niet voldoende rechten om deze actie te doen", - "no-emailers-configured": "Er zijn geen email plugins geladen, een test email kan dus niet verzonden worden", + "no-teaser": "Dit voorproefje bestaat niet", + "no-privileges": "Onvoldoende rechten om deze actie uit te voeren", + "no-emailers-configured": "Er zijn geen e-mailplugins geladen, een testbericht kan dus niet verzonden worden", "category-disabled": "Categorie uitgeschakeld", "topic-locked": "Onderwerp gesloten", - "post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting", - "still-uploading": "Heb even geduld totdat de alle bestanden geüpload zijn", - "content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).", - "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).", - "title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).", - "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).", - "too-many-posts": "You can only post once every %1 second(s) - please wait before posting again", - "too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again", - "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)", - "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)", - "file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file", - "cant-vote-self-post": "Je kan niet op je eigen berichten stemmen", - "already-favourited": "U heeft al dit bericht in uw favorieten staan", - "already-unfavourited": "U heeft al dit bericht uit uw favorieten gehaald", - "cant-ban-other-admins": "U kunt niet de andere admins bannen!", - "invalid-image-type": "Dit foto bestandstype is niet toegestaan. Toegestane foto bestandstypen zijn: %1", - "invalid-image-extension": "Ongeldige foto bestandsextensie", - "invalid-file-type": "Dit bestandstype is niet toegestaan. Toegestane bestandstypen zijn: %1", - "group-name-too-short": "De groepsnaam is te kort", - "group-already-exists": "Deze groep bestaat al", + "post-edit-duration-expired": "Het is slechts toegestaan om binnen %1 seconde(n) na plaatsen van het bericht, deze te bewerken.", + "still-uploading": "Een moment geduld tot alle bestanden overgebracht zijn...", + "content-too-short": "Geef wat meer volume, inhoud aan een bericht! Berichten dienen uit minimaal %1 teken(s) te bestaan.", + "content-too-long": "Kort het bericht wat in, het aantal gebruikte tekens overschrijdt het ingestelde limiet want berichten mogen niet meer dan %1 teken(s) bevatten.", + "title-too-short": "Geef een titel op die uit meer tekens bestaat. Titels dienen ten minste uit %1 teken(s) te bestaan.", + "title-too-long": "Geef een kortere titel op. Titels mogen uit niet meer dan %1 teken(s) bestaan.", + "too-many-posts": "Het is slechts toegestaan iedere %1 seconde(n) een bericht te plaatsen - wacht even voordat opnieuw een bericht verzonden wordt", + "too-many-posts-newbie": "Nieuwe gebruikersaccounts zoals deze zijn begrensd en mogen slechts iedere %1 seconde(n) berichten plaatsen, tot het moment dat %2 reputatie verdiend is - wacht daarom even met opnieuw een bericht te plaatsten", + "tag-too-short": "Geef een tag op die uit meer tekens bestaat. Tags dienen uit minimaal %1 teken(s) te bestaan.", + "tag-too-long": "Geef een kortere tag op. Tags mogen niet langer dan %1 teken(s) zijn", + "file-too-big": "Maximum toegestane bestandsgrootte is %1 kB - probeer een kleiner bestand te verzenden", + "cant-vote-self-post": "Het is niet mogelijk op eigen berichten te stemmen", + "already-favourited": "Dit bericht staat al tussen de favorieten", + "already-unfavourited": "Dit bericht is al uit favorieten verwijderd", + "cant-ban-other-admins": "Het is niet toegestaan andere beheerders te verbannen!", + "invalid-image-type": "Ongeldig bestandstype afbeelding. Deze afbeelding is van een bestandstype dat niet ondersteund wordt. Toegestane bestandstypes voor afbeeldingsbestanden zijn: %1", + "invalid-image-extension": "Ongeldige bestandstype afbeelding", + "invalid-file-type": "Dit bestandstype wordt niet ondersteund. Toegestane bestandstypen zijn: %1", + "group-name-too-short": "De groepsnaam bevat niet genoeg tekens", + "group-already-exists": "Een groep met deze naam bestaat al", "group-name-change-not-allowed": "Het veranderen van de groepsnaam is niet toegestaan!", - "group-already-member": "U bent al lid van deze groep", - "group-needs-owner": "Deze groep vereist minimaal 1 eigenaar", + "group-already-member": "Groepslidmaatschap al aanwezig", + "group-needs-owner": "De groep vereist ten minste 1 eigenaar", "post-already-deleted": "Dit bericht is al verwijderd", "post-already-restored": "Dit bericht is al hersteld", - "topic-already-deleted": "Deze topic is al verwijderd", - "topic-already-restored": "Deze topic is al hersteld", - "cant-purge-main-post": "Je kan het beginbericht niet verwijderen. Verwijder het onderwerp hiervoor.", - "topic-thumbnails-are-disabled": "Onderwerp thumbnails zijn uitgeschakeld", + "topic-already-deleted": "Dit onderwerp is al verwijderd", + "topic-already-restored": "Dit onderwerp is al hersteld", + "cant-purge-main-post": "Het is niet mogelijk het eerste bericht te verwijderen. Hiervoor dient het gehele onderwerp verwijderd te worden.", + "topic-thumbnails-are-disabled": "Miniatuurweergaven bij onderwerpen uitgeschakeld. ", "invalid-file": "Ongeldig bestand", "uploads-are-disabled": "Uploads zijn uitgeschakeld", "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", - "cant-chat-with-yourself": "Je kan niet met jezelf chatten!", - "chat-restricted": "Deze gebruiker heeft beperkingen gelegd op chatfunctie. Hun moeten jouw volgen voordat je met hun kan chatten", - "too-many-messages": "U heeft teveel berichten verstuurd in een korte tijd. Wacht u alstublieft even.", - "reputation-system-disabled": "Reputatie systeem is uitgeschakeld", - "downvoting-disabled": "Downvoten is uitgeschakeld", - "not-enough-reputation-to-downvote": "U heeft niet de benodigde reputatie om dit bericht te downvoten", - "not-enough-reputation-to-flag": "U heeft niet de benodigde reputatie om dit bericht te melden aan de admins", - "reload-failed": "NodeBB heeft een probleem geconstateerd tijdens het laden van: \"%1\".\nNodeBB blijft verder draaien. Het is wel verstandig om de actie wat u daarvoor heeft gedaan ongedaan te maken door te herladen.", - "registration-error": "Registratie fout", - "parse-error": "Er is iets fout gegaan tijdens het parsen van de server response", - "wrong-login-type-email": "Gebruikt u alstublieft uw email om in te loggen", - "wrong-login-type-username": "Gebruikt u alstublieft uw gebruikersnaam om in te loggen" + "about-me-too-long": "Sorry, your about me cannot be longer than %1 character(s).", + "cant-chat-with-yourself": "Het is niet mogelijk met jezelf een chatgesprek te houden.", + "chat-restricted": "Deze gebruiker heeft beperkingen aan de chatfunctie opgelegd waardoor deze eerst iemand moet volgen voordat deze persoon een nieuwe chat mag initiëren.", + "too-many-messages": "Er zijn in korte tijd teveel berichten verzonden, een moment geduld.", + "reputation-system-disabled": "Reputatie systeem is uitgeschakeld.", + "downvoting-disabled": "Negatief stemmen staat uitgeschakeld.", + "not-enough-reputation-to-downvote": "Deze gebruikersaccount beschikt over onvoldoende reputatie om een negatieve stem uit te mogen brengen.", + "not-enough-reputation-to-flag": "Onvoldoende reputatie om dit bericht aan beheerders te mogen melden.", + "reload-failed": "Tijdens het herladen van \"%1\" is NodeBB een fout of probleem tegen gekomen. NodeBB blijft operationeel echter het is verstandig om de oorzaak te onderzoeken en wellicht de vorige actie, voor het herladen, ongedaan te maken.", + "registration-error": "Fout tijdens registratie", + "parse-error": "Tijdens het verwerken van het antwoord van de server is iets misgegaan.", + "wrong-login-type-email": "Gebruik het e-mailadres voor aanmelden", + "wrong-login-type-username": "Geef de gebruikersnaam voor aanmelden" } \ No newline at end of file diff --git a/public/language/nl/global.json b/public/language/nl/global.json index bea96ad2b4..9d0b5c6b2a 100644 --- a/public/language/nl/global.json +++ b/public/language/nl/global.json @@ -2,26 +2,26 @@ "home": "Home", "search": "Zoeken", "buttons.close": "Sluiten", - "403.title": "Toegang Geweigerd", - "403.message": "Het lijkt er op dat u op een pagina bent beland waar u geen toegang tot heeft.", - "403.login": "Misschien moet u proberen in te loggen?", - "404.title": "Niet Gevonden", - "404.message": "Het lijkt er op dat u op een pagina bent beland die niet bestaat. Ga terug naar de home pagina.", + "403.title": "Geen toegang", + "403.message": "Deze account heeft onvoldoende systeemrechten om toegang tot de pagina te krijgen.", + "403.login": "Wellicht proberen aan te melden?", + "404.title": "Niet gevonden", + "404.message": "Deze pagina bestaat niet. Klik hier om naar de hoofdpagina van deze website te navigeren.", "500.title": "Interne fout.", - "500.message": "Oeps! Het lijkt erop dat iets is fout gegaan!", + "500.message": "Oeps! Ziet er naar uit dat iets fout ging!", "register": "Registeren", - "login": "Inloggen", - "please_log_in": "Log a.u.b. In", - "logout": "Uitloggen", - "posting_restriction_info": "Reageren is momenteel beperkt tot geregistreerde leden, klik hier om in te loggen.", - "welcome_back": "Welkom Terug", - "you_have_successfully_logged_in": "Je bent succesvol ingelogd", - "save_changes": "Aanpassingen Opslaan", + "login": "Login", + "please_log_in": "Aanmelden", + "logout": "Afmelden", + "posting_restriction_info": "Momenteel mogen alleen geregistreerde leden reageren. Klik om naar de aanmeldpagina te gaan.", + "welcome_back": "Welkom terug", + "you_have_successfully_logged_in": "Aanmelden succesvol", + "save_changes": "Wijzigingen opslaan", "close": "Sluiten", "pagination": "Paginering", "pagination.out_of": "%1 van %2", "pagination.enter_index": "Vul index in", - "header.admin": "Admin", + "header.admin": "Beheer", "header.recent": "Recent", "header.unread": "Ongelezen", "header.tags": "Tags", @@ -32,22 +32,22 @@ "header.notifications": "Notificaties", "header.search": "Zoeken", "header.profile": "Profiel", - "notifications.loading": "Notificaties Laden", - "chats.loading": "Chats Laden", + "notifications.loading": "Notificaties laden", + "chats.loading": "Chats laden", "motd.welcome": "Welkom bij NodeBB, het discussie platform van de toekomst.", - "previouspage": "Vorige Pagina", - "nextpage": "Volgende Pagina", + "previouspage": "Vorige pagina", + "nextpage": "Volgende pagina", "alert.success": "Succes", "alert.error": "Fout", "alert.banned": "Verbannen", - "alert.banned.message": "U bent verbannen en zal automatisch worden uitgelogd.", - "alert.unfollow": "Je volgt niet langer %1!", - "alert.follow": "Je volgt nu %1!", + "alert.banned.message": "Deze account is verbannen en wordt automatisch afgemeld.", + "alert.unfollow": "%1 wordt niet langer gevolgd!", + "alert.follow": "%1 wordt nu gevolgd!", "online": "Online", "users": "Gebruikers", - "topics": "Topics", + "topics": "Onderwerpen", "posts": "Berichten", - "views": "Weergaven", + "views": "Gezien", "reputation": "Reputatie", "read_more": "Lees meer", "posted_ago_by_guest": "geplaatst %1 door gast", @@ -60,20 +60,20 @@ "user_posted_ago": "%1 plaatste %2", "guest_posted_ago": "Gast plaatste %1", "last_edited_by_ago": "voor het laatst aangepast door %1 %2", - "norecentposts": "Geen Recente Berichten", - "norecenttopics": "Geen Recente Onderwerpen", - "recentposts": "Recente Berichten", - "recentips": "Recente Ingelogde IPs", + "norecentposts": "Geen recente berichten", + "norecenttopics": "Geen recente onderwerpen", + "recentposts": "Recente berichten", + "recentips": "IP-adressen van recente gebruikers", "away": "Afwezig", - "dnd": "Niet Storen", + "dnd": "Niet storen", "invisible": "Onzichtbaar", "offline": "Offline", - "email": "Email Adres", + "email": "E-mailadres", "language": "Taal", "guest": "Gast", "guests": "Gasten", - "updated.title": "Forum geüpdatet", - "updated.message": "Dit forum is zojuist geüpdatet naar de laatste versie. Klik hier om de pagina te verversen", + "updated.title": "Site update", + "updated.message": "Deze site heeft zojuist een update ontvangen. Klik hier om de pagina te verversen.", "privacy": "Privé", "follow": "Volgen", "unfollow": "Ontvolgen", diff --git a/public/language/nl/groups.json b/public/language/nl/groups.json index 8fbbbee137..3ee0fccb73 100644 --- a/public/language/nl/groups.json +++ b/public/language/nl/groups.json @@ -1,36 +1,36 @@ { "groups": "Groepen", - "view_group": "Bekijk Groep", - "owner": "Groep eigenaar", - "new_group": "Maak een nieuwe groep", - "no_groups_found": "Er zijn geen groepen om weer te geven", + "view_group": "Weergeven groep", + "owner": "Groepseigenaar", + "new_group": "Nieuwe groep", + "no_groups_found": "Geen groepen voor weergave", "pending.accept": "Accepteer", "pending.reject": "Afwijzen", - "cover-instructions": "Sleep een foto, positioneer en klik op Opslaan", - "cover-change": "Aanpassen", + "cover-instructions": "Sleep een afbeelding, sleep om te positioneren en klik tenslotte op Opslaan", + "cover-change": "Bewerken", "cover-save": "Opslaan", "cover-saving": "Bezig met opslaan", - "details.title": "Groep Details", + "details.title": "Groepsdetails", "details.members": "Ledenlijst", - "details.pending": "Afwachtende leden", + "details.pending": "Nog niet geaccepteerde leden", "details.has_no_posts": "Deze groepleden hebben nog geen berichten geplaatst", - "details.latest_posts": "Nieuwste Berichten", + "details.latest_posts": "Meest recente berichten", "details.private": "Prive", - "details.grant": "Toekennen/Herroepen van eigenaarschap", - "details.kick": "Verwijder", - "details.owner_options": "Groeps Administratie", + "details.grant": "Toekennen/herroepen van eigendom", + "details.kick": "Schoppen", + "details.owner_options": "Groepsadministratie", "details.group_name": "Groepsnaam", - "details.member_count": "Leden telling", - "details.creation_date": "Datum van het creëeren", + "details.member_count": "Ledentelling", + "details.creation_date": "Aangemaakt op", "details.description": "Beschrijving", - "details.badge_preview": "Badge Voorvertoning", - "details.change_icon": "Icoon veranderen", - "details.change_colour": "Kleur veranderen", - "details.badge_text": "Badge tekst", - "details.userTitleEnabled": "Badge tonen", - "details.private_help": "Indien geactiveerd, zal er goedkeuring moeten worden verleend door een groepseigenaar voor het toetreden van groepen", - "details.hidden": "Verborgen", + "details.badge_preview": "Draaginsigne voorvertoning", + "details.change_icon": "Wijzig icoon", + "details.change_colour": "Wijzig kleur", + "details.badge_text": "Draaginsigne tekst", + "details.userTitleEnabled": "Draaginsignes weergeven", + "details.private_help": "Wanneer ingeschakeld, zal eerst een groepseigenaar goedkeuring moeten verlenen voordat nieuwe leden kunnen toetreden", + "details.hidden": "Niet getoond", "details.hidden_help": "Indien geactiveerd zal deze groep niet getoond worden in de groepslijst en zullen gebruikers handmatig uitgenodigd moeten worden.", - "event.updated": "Groepsdetails zijn geupdate", + "event.updated": "Groepsdetails zijn bijgewerkt", "event.deleted": "De groep \"%1\" is verwijderd" } \ No newline at end of file diff --git a/public/language/nl/login.json b/public/language/nl/login.json index fa1c5bf8eb..0e2a65a11a 100644 --- a/public/language/nl/login.json +++ b/public/language/nl/login.json @@ -2,10 +2,10 @@ "username-email": "Gebruikersnaam / Email", "username": "Gebruikersnaam", "email": "Email", - "remember_me": "Mij Onthouden?", - "forgot_password": "Wachtwoord Vergeten?", - "alternative_logins": "Alternatieve Loginmethodes", - "failed_login_attempt": "Mislukte inlog poging, probeer het later opnieuw.", - "login_successful": "Je bent succesvol ingelogd!", - "dont_have_account": "Heeft u nog geen account?" + "remember_me": "Aangemeld blijven?", + "forgot_password": "Wachtwoord vergeten?", + "alternative_logins": "Andere manieren van aanmelden", + "failed_login_attempt": "Aanmelden niet geslaagd. Probeer het nog eens.", + "login_successful": "Aanmelden geslaagd!", + "dont_have_account": "Geen gebruikersaccount?" } \ No newline at end of file diff --git a/public/language/nl/modules.json b/public/language/nl/modules.json index 76dd6413a9..26f89112ba 100644 --- a/public/language/nl/modules.json +++ b/public/language/nl/modules.json @@ -1,26 +1,26 @@ { "chat.chatting_with": "Chat met ", - "chat.placeholder": "Type chat bericht hier, druk op enter om te verzenden", + "chat.placeholder": "Typ een chatbericht hier, toets enter om te verzenden", "chat.send": "Verzenden", - "chat.no_active": "Je hebt geen actieve chats.", + "chat.no_active": "Er zijn geen actieve chats.", "chat.user_typing": "%1 is aan het typen ...", - "chat.user_has_messaged_you": "%1 heeft een bericht naar u gestuurd", + "chat.user_has_messaged_you": "%1 heeft een bericht gestuurd", "chat.see_all": "Bekijk alle gesprekken", - "chat.no-messages": "Selecteer een ontvanger om de geschiedenis van het gesprek te bekijken", - "chat.recent-chats": "Recente Gesprekken", + "chat.no-messages": "Selecteer een ontvanger om de chatgeschiedenis in te zien", + "chat.recent-chats": "Recent gevoerde gesprekken", "chat.contacts": "Contacten", - "chat.message-history": "Berichten Geschiedenis", - "chat.pop-out": "tevoorschijn halen gesprek", + "chat.message-history": "Berichtengeschiedenis", + "chat.pop-out": "Chatvenster opbrengen bij chat", "chat.maximize": "Maximaliseren", - "chat.seven_days": "7 Dagen", - "chat.thirty_days": "30 Dagen", - "chat.three_months": "3 Maanden", - "composer.compose": "samenstellen", - "composer.show_preview": "Laat voorbeeld zien", + "chat.seven_days": "7 dagen", + "chat.thirty_days": "30 dagen", + "chat.three_months": "3 maanden", + "composer.compose": "Samenstellen", + "composer.show_preview": "Voorbeeldweergave", "composer.hide_preview": "Verberg voorbeeld", "composer.user_said_in": "%1 zegt in %2:", "composer.user_said": "%1 zegt:", - "composer.discard": "Weet u het zeker dat u dit bericht niet wilt plaatsen?", - "composer.submit_and_lock": "Plaatsen en vergrendelen", - "composer.toggle_dropdown": "pin menu" + "composer.discard": "Bericht plaatsen annuleren?", + "composer.submit_and_lock": "Plaats een bericht en vergrendel direct het onderwerp", + "composer.toggle_dropdown": "Keuzelijst schakelen" } \ No newline at end of file diff --git a/public/language/nl/notifications.json b/public/language/nl/notifications.json index 4e5a599896..fc2e904d90 100644 --- a/public/language/nl/notifications.json +++ b/public/language/nl/notifications.json @@ -1,27 +1,27 @@ { "title": "Notificaties", "no_notifs": "Je hebt geen nieuwe notificaties", - "see_all": "Bekijk alle Notificaties", - "mark_all_read": "Markeer alle meldingen als gelezen", + "see_all": "Bekijk alle notificaties", + "mark_all_read": "Markeer alles als gelezen", "back_to_home": "Terug naar %1", "outgoing_link": "Uitgaande Link", "outgoing_link_message": "Je verlaat nu %1", - "continue_to": "Doorgaan naar %1", - "return_to": "Teruggaan naar %1", - "new_notification": "een nieuwe notificatie", - "you_have_unread_notifications": "U heeft ongelezen notificaties", + "continue_to": "Door naar %1", + "return_to": "Terug naar %1", + "new_notification": "Nieuwe melding", + "you_have_unread_notifications": "Ongelezen berichten", "new_message_from": "Nieuw bericht van %1", - "upvoted_your_post_in": "%1 heeft uw bericht geupvote in %2.", - "moved_your_post": "%1 heeft uw bericht verplaatst", - "moved_your_topic": "%1 heeft uw onderwerp verplaatst.", - "favourited_your_post_in": "%1 heeft uw bericht gefavoriet in %2.", + "upvoted_your_post_in": "%1 heeft voor een bericht gestemd in %2.", + "moved_your_post": "%1 heeft het bericht verplaatst.", + "moved_your_topic": "%1 heeft het onderwerp verplaatst.", + "favourited_your_post_in": "%1 heeft een van onze berichten in %2 aan de favorieten toegevoegd.", "user_flagged_post_in": "%1 rapporteerde een bericht in %2", "user_posted_to": "%1 heeft een reactie op het bericht gegeven aan %2", "user_posted_topic": "%1 heeft een nieuw onderwerp geplaatst: %2", - "user_mentioned_you_in": "%1 heeft u genoemd in %2", - "user_started_following_you": "%1 volgt u nu.", - "email-confirmed": "Email adres bevestigd", - "email-confirmed-message": "Bedankt voor het bevestigen van uw email adres. Uw account is nu volledig actief.", - "email-confirm-error-message": "Er was een probleem met het bevestigen van uw email adres. Misschien was de code niet goed of was door de tijd verstreken.", - "email-confirm-sent": "Bevestigings email verstuurd" + "user_mentioned_you_in": "Onze naam is genoemd door %1 in %2.", + "user_started_following_you": "%1 volgt ons nu.", + "email-confirmed": "E-mailadres bevestigd", + "email-confirmed-message": "Bedankt voor het bevestigen van het e-mailadres. Deze account is nu volledig geactiveerd.", + "email-confirm-error-message": "Er was een probleem met het bevestigen van dit e-mailadres. Misschien is de code niet goed ingevoerd of was de beschikbare tijd inmiddels verstreken.", + "email-confirm-sent": "Bevestigingmail verstuurd" } \ No newline at end of file diff --git a/public/language/nl/pages.json b/public/language/nl/pages.json index f9db18bd4c..d4ac82cc54 100644 --- a/public/language/nl/pages.json +++ b/public/language/nl/pages.json @@ -1,21 +1,21 @@ { "home": "Home", - "unread": "Ongelezen Onderwerpen", - "popular": "Populaire Onderwerpen", - "recent": "Recente Onderwerpen", - "users": "Geregistreerde Gebruikers", + "unread": "Ongelezen onderwerpen", + "popular": "Populaire onderwerpen", + "recent": "Recente onderwerpen", + "users": "Geregistreerde gebruikers", "notifications": "Notificaties", "tags": "Tags", - "tag": "Onderwerpen getagd onder \"%1\"", + "tag": "Onderwerpen geplaatst onder \"%1\"", "user.edit": "\"%1\" aanpassen", - "user.following": "Mensen %1 Volgt", - "user.followers": "Mensen die %1 Volgen", + "user.following": "Door %1 gevolgd", + "user.followers": "Die %1 volgen", "user.posts": "Berichten geplaatst door %1", - "user.topics": "Topics gecreëerd door %1", + "user.topics": "Onderwerpen begonnen door %1", "user.groups": "%1's groepen", - "user.favourites": "%1's Favoriete Berichten", + "user.favourites": "Favoriete berichten van %1", "user.settings": "Gebruikersinstellingen", - "user.watched": "Berichten die worden bekeken door %1", - "maintenance.text": "%1 is momenteel in onderhoud modus. Probeer later opnieuw", - "maintenance.messageIntro": "daarnaast heeft de administrator het volgende bericht achtergelaten:" + "user.watched": "Berichten die door %1 bekeken worden", + "maintenance.text": "%1 is momenteel in onderhoud. Excuses voor het ongemak en probeer het later nog eens", + "maintenance.messageIntro": "Daarnaast heeft de beheerder het volgende bericht achtergelaten:" } \ No newline at end of file diff --git a/public/language/nl/reset_password.json b/public/language/nl/reset_password.json index 87a167b975..9849338c56 100644 --- a/public/language/nl/reset_password.json +++ b/public/language/nl/reset_password.json @@ -1,17 +1,17 @@ { "reset_password": "Wachtwoord opnieuw instellen", - "update_password": "Wachtwoord Updaten", - "password_changed.title": "Wachtwoord Veranderd", - "password_changed.message": "

Wachtwoord is met succes gereset, log a.u.b. opnieuw in.", - "wrong_reset_code.title": "Incorrecte Reset Code", - "wrong_reset_code.message": "De ontvangen reset code is incorrect. Probeer het opnieuw, of vraag een nieuwe code aan.", - "new_password": "Nieuw Wachtwoord", - "repeat_password": "Bevestig Wachtwoord", - "enter_email": "Vul a.u.b. je email address in en we versturen je een email met de stappen hoe je je account reset.", - "enter_email_address": "Vul uw Email Adres in", - "password_reset_sent": "Wachtwoord Reset Verzonden", - "invalid_email": "Fout Email Adres / Email Adres bestaat niet!", - "password_too_short": "Het ingegeven wachtwoord is te kort. Kiest u alstublieft een ander wachtwoord.", - "passwords_do_not_match": "De twee wachtwoorden die u heeft ingegeven komen niet overeen.", - "password_expired": "U wachtwoord is verlopen, kies een nieuw wachtwoord" + "update_password": "Wachtwoord bijwerken", + "password_changed.title": "Wachtwoord gewijzigd", + "password_changed.message": "

Wachtwoord met succes hersteld. Log nu eerst opnieuw in.", + "wrong_reset_code.title": "Onjuiste herstelcode", + "wrong_reset_code.message": "Opgegeven code voor wachtwoordherstel is niet juist. Probeer het opnieuw of vraag een andere code aan.", + "new_password": "Nieuw wachtwoord", + "repeat_password": "Bevestiging wachtwoord", + "enter_email": "Geef het e-mailadres op dat tijdens registratie gebruikt is, en we versturen je een bericht met vervolginstructies voor het ontgrendelen van de account.", + "enter_email_address": "Geef het e-mailadres op", + "password_reset_sent": "Het bericht met daarin een link en de vervolgstappen voor het wachtwoordherstel, is verzonden", + "invalid_email": "Onbekend e-mailadres!", + "password_too_short": "Het opgegeven wachtwoord bevat te weinig tekens. Kies een veiliger wachtwoord met meer tekens.", + "passwords_do_not_match": "De twee opgegeven wachtwoorden komen niet overeen`", + "password_expired": "Het huidige wachtwoord is verlopen en er dient een nieuwe gekozen te worden" } \ No newline at end of file diff --git a/public/language/nl/search.json b/public/language/nl/search.json index 91d52f12a2..7b558be954 100644 --- a/public/language/nl/search.json +++ b/public/language/nl/search.json @@ -1,20 +1,20 @@ { - "results_matching": "%1 resulta(a)ten was een match \"%2\", (%3 seconds)", - "no-matches": "Geen matches gevonden", - "advanced-search": "Geavanceerd Zoeken", + "results_matching": "%1 overeenkomstige resultaten \"%2\", (%3 seconds)", + "no-matches": "Geen overeenkomstige resultaten gevonden", + "advanced-search": "Geavanceerde zoekfunctie", "in": "in", "titles": "Titels", - "titles-posts": "Titels en Berichten", + "titles-posts": "Titels en berichten", "posted-by": "Geplaatst door", "in-categories": "In categorieën", - "search-child-categories": "Doorzoek sub categorieën ", + "search-child-categories": "Doorzoek subcategorieën ", "reply-count": "Aantal reacties", "at-least": "Minimaal", "at-most": "Maximaal", - "post-time": "Tijd van plaatsing", + "post-time": "Geplaatst op", "newer-than": "Nieuwer dan", "older-than": "Ouder dan", - "any-date": "Iedere datum", + "any-date": "Elke datum", "yesterday": "Gisteren", "one-week": "Eén week", "two-weeks": "Twee weken", @@ -22,12 +22,12 @@ "three-months": "Drie maanden", "six-months": "Zes maanden", "one-year": "Eén jaar", - "sort-by": "Gesorteerd op", + "sort-by": "Sorteer op", "last-reply-time": "Laatste keer geantwoord", "topic-title": "Onderwerp", "number-of-replies": "Aantal antwoorden", - "number-of-views": "Aantal weergaven", - "topic-start-date": "Onderwerp aanmaakdatum", + "number-of-views": "Aantal keer bekeken", + "topic-start-date": "Onderwerp gestart op datum", "username": "Gebruikersnaam", "category": "Categorie", "descending": "In aflopende volgorde", diff --git a/public/language/nl/success.json b/public/language/nl/success.json index f8fd81d695..be632b25e4 100644 --- a/public/language/nl/success.json +++ b/public/language/nl/success.json @@ -1,6 +1,6 @@ { - "success": "Success", - "topic-post": "U heeft succesvol een bericht geplaatst", - "authentication-successful": "Het inloggen is succesvol", + "success": "Geslaagd", + "topic-post": "Bericht succesvol geplaatst", + "authentication-successful": "Aanmelden geslaagd", "settings-saved": "Instellingen opgeslagen!" } \ No newline at end of file diff --git a/public/language/nl/tags.json b/public/language/nl/tags.json index ec028f3248..f4665e87c3 100644 --- a/public/language/nl/tags.json +++ b/public/language/nl/tags.json @@ -1,7 +1,7 @@ { "no_tag_topics": "Er zijn geen onderwerpen met deze tag", "tags": "Tags", - "enter_tags_here": "Voegt u hier tags toe, tussen de %1 en %2 karakters per stuk.", - "enter_tags_here_short": "Voer uw tags in...", - "no_tags": "Er zijn nog geen tags te vinden" + "enter_tags_here": "Voeg hier tags toe, tussen de %1 en %2 tekens per stuk.", + "enter_tags_here_short": "Voer tags in...", + "no_tags": "Er zijn nog geen tags geplaatst" } \ No newline at end of file diff --git a/public/language/nl/topic.json b/public/language/nl/topic.json index 35679bccfd..38593ecca8 100644 --- a/public/language/nl/topic.json +++ b/public/language/nl/topic.json @@ -5,95 +5,96 @@ "no_topics_found": "Geen onderwerpen gevonden!", "no_posts_found": "Geen berichten gevonden!", "post_is_deleted": "Dit bericht is verwijderd!", + "topic_is_deleted": "This topic is deleted!", "profile": "Profiel", "posted_by": "Geplaatst door %1", - "posted_by_guest": "Geplaatst door Gast", + "posted_by_guest": "Geplaatst door gast", "chat": "Chat", - "notify_me": "Krijg notificaties van nieuwe reacties op dit onderwerp", + "notify_me": "Krijg een melding wanneer nieuwe reacties volgen", "quote": "Citeren", "reply": "Reageren", - "guest-login-reply": "Log in om een reactie te plaatsen", + "guest-login-reply": "Aanmelden om te reageren", "edit": "Aanpassen", "delete": "Verwijderen", - "purge": "weggooien", + "purge": "Opschonen", "restore": "Herstellen", "move": "Verplaatsen", "fork": "Afsplitsen", "link": "Link", "share": "Delen", - "tools": "Gereedschap", + "tools": "Extra", "flag": "Markeren", - "locked": "gesloten", - "bookmark_instructions": "Klik hier om terug te gaan naar je laatste positie of sluiten om te annuleren.", - "flag_title": "Dit bericht markeren voor moderatie", - "flag_confirm": "Weet u het zeker dat u dit bericht wilt rapporteren?", - "flag_success": "Dit bericht is gerapporteerd aan de admins", - "deleted_message": "Dit onderwerp is verwijderd. Alleen gebruikers met onderwerp management privileges kunnen dit onderwerp zien.", - "following_topic.message": "Je zult nu notificaties ontvangen wanneer iemand reageert op dit onderwerp.", - "not_following_topic.message": "Je zult niet langer notificaties ontvangen van dit onderwerp.", - "login_to_subscribe": "Log a.u.b. in om op dit onderwerp te abonneren.", - "markAsUnreadForAll.success": "Onderwerp gemarkeerd als gelezen voor iedereen.", + "locked": "Gesloten", + "bookmark_instructions": "Klik hier om naar de vorige positie terug te keren of sluit af om te verwerpen.", + "flag_title": "Bericht aan beheerders melden", + "flag_confirm": "Is het echt de bedoeling dit bericht aan beheerders te rapporteren?", + "flag_success": "Het bericht is gerapporteerd aan beheer.", + "deleted_message": "Dit onderwerp is verwijderd. Alleen gebruikers met beheerrechten op onderwerpniveau kunnen dit inzien.", + "following_topic.message": "Vanaf nu worden meldingen ontvangen zodra iemand een reactie op dit onderwerp geeft.", + "not_following_topic.message": "Er worden niet langer meldingen ontvangen over dit onderwerp.", + "login_to_subscribe": "Aanmelden om op dit onderwerp te abonneren", + "markAsUnreadForAll.success": "Onderwerp is voor iedereen als 'gelezen' gemarkeerd.", "watch": "Volgen", "unwatch": "Niet volgen", - "watch.title": "Krijg notificaties van nieuwe reacties op dit onderwerp", - "unwatch.title": "Stop met dit onderwerp te volgen", - "share_this_post": "Deel dit Bericht", - "thread_tools.title": "Onderwerp Gereedschap", - "thread_tools.markAsUnreadForAll": "Ongelezen Markeren", - "thread_tools.pin": "Onderwerp Vastmaken", - "thread_tools.unpin": "Onderwerp Losmaken", - "thread_tools.lock": "Onderwerp Sluiten", - "thread_tools.unlock": "Onderwerp Openen", - "thread_tools.move": "Onderwerp Verplaatsen", + "watch.title": "Krijg meldingen van nieuwe reacties op dit onderwerp", + "unwatch.title": "Dit onderwerp niet langer volgen", + "share_this_post": "Deel dit bericht", + "thread_tools.title": "Acties", + "thread_tools.markAsUnreadForAll": "Ongelezen markeren", + "thread_tools.pin": "Onderwerp vastpinnen", + "thread_tools.unpin": "Onderwerp losmaken", + "thread_tools.lock": "Onderwerp op slot zetten", + "thread_tools.unlock": "Onderwerp openen", + "thread_tools.move": "Onderwerp verplaatsen", "thread_tools.move_all": "Verplaats alles", - "thread_tools.fork": "Onderwerp Afsplitsen", - "thread_tools.delete": "Onderwerp Verwijderen", - "thread_tools.delete_confirm": "Weet u het zeker dat u dit onderwerp wilt verwijderen?", - "thread_tools.restore": "Onderwerp Herstellen", - "thread_tools.restore_confirm": "Weet u het zeker dat u het onderwerp wilt herstellen?", - "thread_tools.purge": "Wis Onderwerp ", - "thread_tools.purge_confirm": "Weet u het zeker dat u dit onderwerp wilt weggooien?", - "topic_move_success": "Deze onderwerp is succesvol verplaatst naar %1", - "post_delete_confirm": "Weet u het zeker dat u dit bericht wilt verwijderen?", - "post_restore_confirm": "Weet u het zeker dat u dit bericht wilt herstellen?", - "post_purge_confirm": "Weet u het zeker dat u dit bericht wilt weggooien?", - "load_categories": "Categorieën Laden", - "disabled_categories_note": "Uitgeschakelde Categorieën zijn grijs", + "thread_tools.fork": "Onderwerp afsplitsen", + "thread_tools.delete": "Onderwerp verwijderen", + "thread_tools.delete_confirm": "Is het echt de bedoeling dit onderwerp te verwijderen?", + "thread_tools.restore": "Onderwerp erstellen", + "thread_tools.restore_confirm": "Zeker weten dit onderwerp te herstellen?", + "thread_tools.purge": "Wis onderwerp ", + "thread_tools.purge_confirm": "Is het echt de bedoeling dit onderwerp definitief te wissen?", + "topic_move_success": "Verplaatsen van onderwerp naar %1 succesvol", + "post_delete_confirm": "Is het absoluut de bedoeling dit bericht te verwijderen?", + "post_restore_confirm": "Is het de bedoeling dit bericht te herstellen?", + "post_purge_confirm": "Is het absoluut zeker dat dit bericht volledig verwijderd kan worden?", + "load_categories": "Categorieën laden", + "disabled_categories_note": "Uitgeschakelde categorieën zijn grijs", "confirm_move": "Verplaatsen", "confirm_fork": "Splits", "favourite": "Favoriet", "favourites": "Favorieten", - "favourites.has_no_favourites": "Je hebt geen favorieten, sla een aantal berichten op als favoriet om ze hier te zien!", - "loading_more_posts": "Meer Berichten Laden", - "move_topic": "Onderwerp Verplaatsen", + "favourites.has_no_favourites": "Er zijn momenteel nog geen favorieten, markeer eerst enkele berichten om ze hier te kunnen zien.", + "loading_more_posts": "Meer berichten...", + "move_topic": "Onderwerp verplaatsen", "move_topics": "Verplaats onderwerpen", - "move_post": "Bericht Verplaatsen", + "move_post": "Bericht verplaatsen", "post_moved": "Bericht verplaatst!", - "fork_topic": "Afgesplitste Onderwerp ", - "topic_will_be_moved_to": "Dit onderwerp zal verplaatst worden naar de categorie", - "fork_topic_instruction": "Klik op de berichten die je wilt forken", + "fork_topic": "Afgesplitst onderwerp ", + "topic_will_be_moved_to": "Dit onderwerp zal naar de categorie verplaatst worden", + "fork_topic_instruction": "Klik op de berichten die afgesplitst moeten worden", "fork_no_pids": "Geen berichten geselecteerd!", - "fork_success": "Met succes het onderwerp gesplitst. klik hier om naar het nieuwe onderwerp te gaan.", - "composer.title_placeholder": "Vul de titel voor het onderwerp hier in...", + "fork_success": "Onderwerp is succesvol afgesplitst. Klik hier om het nieuwe onderwerp te zien.", + "composer.title_placeholder": "Voer hier de titel van het onderwerp in...", "composer.handle_placeholder": "Naam", "composer.discard": "Annuleren", - "composer.submit": "Opslaan", - "composer.replying_to": "Reageren op %1", - "composer.new_topic": "Nieuw Onderwerp", + "composer.submit": "Verzenden", + "composer.replying_to": "Reactie op %1", + "composer.new_topic": "Nieuw onderwerp", "composer.uploading": "uploaden...", - "composer.thumb_url_label": "Plak een onderwerp thumbnail URL", - "composer.thumb_title": "Voeg een thumbnail toe aan dit onderwerp", + "composer.thumb_url_label": "Plak een URL naar een miniatuurweergave voor dit onderwerp", + "composer.thumb_title": "Voeg een miniatuurweergave toe aan dit onderwerp", "composer.thumb_url_placeholder": "http://example.com/thumb.png", "composer.thumb_file_label": "Of upload een bestand", - "composer.thumb_remove": "Velden leegmaken", - "composer.drag_and_drop_images": "Sleep en Zet Afbeeldingen Hier", + "composer.thumb_remove": "Velden legen", + "composer.drag_and_drop_images": "Sleep en zet afbeeldingen hier", "more_users_and_guests": "%1 of meerdere gebruiker(s) en %2 gast(en)", "more_users": "%1 meer gebruiker(s)", "more_guests": "%1 of meerdere gast(en)", "users_and_others": "%1 en %2 anderen", - "sort_by": "gesorteerd op", - "oldest_to_newest": "Oud naar Nieuw", - "newest_to_oldest": "Nieuw naar Oud", - "most_votes": "Meeste stemmen", - "most_posts": "Meeste berichten" + "sort_by": "Indeling", + "oldest_to_newest": "Oudste berichten bovenaan", + "newest_to_oldest": "Meest recente berichten bovenaan", + "most_votes": "Meeste aantal stemmen", + "most_posts": "Meeste aantal reacties" } \ No newline at end of file diff --git a/public/language/nl/unread.json b/public/language/nl/unread.json index 35938c3a72..3149f079ad 100644 --- a/public/language/nl/unread.json +++ b/public/language/nl/unread.json @@ -1,8 +1,8 @@ { "title": "Ongelezen", "no_unread_topics": "Er zijn geen ongelezen onderwerpen", - "load_more": "Meer Laden", - "mark_as_read": "Markeer als Gelezen", + "load_more": "Meer laden...", + "mark_as_read": "Markeer als gelezen", "selected": "Geselecteerd", "all": "Alles", "topics_marked_as_read.success": "Onderwerp gemarkeerd als gelezen!" diff --git a/public/language/nl/user.json b/public/language/nl/user.json index d30ff024a9..4822209b05 100644 --- a/public/language/nl/user.json +++ b/public/language/nl/user.json @@ -4,23 +4,24 @@ "username": "Gebruikersnaam", "joindate": "Datum van registratie", "postcount": "Aantal geplaatste berichten", - "email": "Email", - "confirm_email": "Bevestig uw email adres", - "delete_account": "Account Verwijderen", - "delete_account_confirm": "Weet u het zeker dat je dit account wilt verwijderen?
Deze actie kan niet ongedaan worden gemaakt en uw kan niet meer uw data herstellen

Typ hier uw gebruikersnaam om te bevestigen dat u dit account wilt verwijderen.", - "fullname": "Volledige Naam", + "email": "E-mail", + "confirm_email": "Bevestig e-mail", + "delete_account": "Account verwijderen", + "delete_account_confirm": "Controleer of dat het zeker is dat deze account verwijderd moet worden.
Deze actie kan niet ongedaan gemaakt worden en herstellen van gebruiker- of profielgegevens is niet mogelijk

Typ hier de gebruikersnaam als extra controle om te bevestigen dat deze account verwijderd moet worden.", + "fullname": "Volledige naam", "website": "Website", "location": "Locatie", "age": "Leeftijd", "joined": "Geregistreerd", - "lastonline": "Laatst Online", + "lastonline": "Laatste keer online gezien", "profile": "Profiel", - "profile_views": "Profiel weergaven", + "profile_views": "Bekeken", "reputation": "Reputatie", "favourites": "Favorieten", "watched": "Bekeken", "followers": "Volgers", "following": "Volgend", + "aboutme": "About me", "signature": "Handtekening", "gravatar": "Gravatar", "birthday": "Verjaardag", @@ -28,56 +29,56 @@ "follow": "Volgen", "unfollow": "Ontvolgen", "more": "Meer", - "profile_update_success": "Uw profiel is succesvol geüpdatet", - "change_picture": "Afbeelding Aanpassen", - "edit": "Aanpassen", - "uploaded_picture": "Afbeelding Uploaden", - "upload_new_picture": "Nieuwe Afbeelding Uploaden", - "upload_new_picture_from_url": "Nieuwe Afbeelding Uploaden vanaf een URL", - "current_password": "Huidige Wachtwoord", - "change_password": "Wachtwoord Aanpassen", + "profile_update_success": "Het gebruikersprofiel is met succes gewijzigd", + "change_picture": "Bewerk afbeelding", + "edit": "Bewerken", + "uploaded_picture": "Opgehaalde afbeelding", + "upload_new_picture": "Nieuwe afbeelding opsturen", + "upload_new_picture_from_url": "Nieuwe afbeelding vanaf een URL toevoegen", + "current_password": "Huidige wachtwoord", + "change_password": "Wijzig wachtwoord", "change_password_error": "Ongeldig wachtwoord!", - "change_password_error_wrong_current": "Uw huidige wachtwoord is niet correct!", - "change_password_error_length": "Wachtwoord is te kort!", - "change_password_error_match": "Het wachtwoord wat uw eerder heeft opgegeven moet matchen!", - "change_password_error_privileges": "Uw heeft niet de bevoegdheden om dit wachtwoord te veranderen", - "change_password_success": "Uw wachtwoord is geüpdatet!", - "confirm_password": "Bevestig Wachtwoord", + "change_password_error_wrong_current": "Het opgegeven huidige wachtwoord is onjuist!", + "change_password_error_length": "Wachtwoord bevat te weinig tekens!", + "change_password_error_match": "Het eerder opgegeven wachtwoord komt niet overeen!", + "change_password_error_privileges": "Niet geautoriseerd om dit wachtwoord te mogen wijzigen.", + "change_password_success": "Het wachtwoord is gewijzigd!", + "confirm_password": "Bevestig wachtwoord", "password": "Wachtwoord", - "username_taken_workaround": "Gebruikersnaam dat u wilde is al in gebruik, dus hebben we het een beetje aangepast naar %1", - "upload_picture": "Afbeelding Uploaden", + "username_taken_workaround": "Helaas, de gewenste gebruikersnaam is al door iemand in gebruik genomen dus vandaar een kleine aanpassing naar %1 doorgevoerd", + "upload_picture": "Upload afbeelding", "upload_a_picture": "Upload een afbeelding", - "image_spec": "Je mag alleen PNG, JPG, of GIF bestanden uploaden.", + "image_spec": "Alleen afbeeldingsbestanden van het type PNG, JPG/JPEG, of GIF worden ondersteund.", "settings": "Instellingen", - "show_email": "Laat Mijn Email Zien", + "show_email": "Inschakelen weergave van e-mailadres op profielpagina", "show_fullname": "Laat mijn volledige naam zien", - "restrict_chats": "Sta alleen chatsessies toe van gebruikers die ik volg", - "digest_label": "Abonneer op de overzicht", - "digest_description": "Abonneer op de email updates van dit forum ( nieuwe notificaties en onderwerpen) volgens een tijdsschema", + "restrict_chats": "Sta alleen chatsessies toe van gebruikers die ik zelf volg", + "digest_label": "Abonneer op een samenvatting", + "digest_description": "Abonneer op periodieke e-mail updates van onderwerpen in dit forum", "digest_off": "Uit", "digest_daily": "Dagelijks", - "digest_weekly": "Weekelijks", + "digest_weekly": "Wekelijks", "digest_monthly": "Maandelijks", - "send_chat_notifications": "Verstuur mij een email als iemand een chatbericht stuurt terwijl ik niet online ben", - "send_post_notifications": "Stuur een email als er een reactie wordt geplaatst in een topic waarop ik geabonneerd ben", - "settings-require-reload": "Sommige veranderingen vereisen het om de pagina te herladen. Klik hier om te herladen.", + "send_chat_notifications": "Meld het mij per e-mail als iemand een chatbericht verstuurt wanneer ik niet online ben", + "send_post_notifications": "Meld het mij per e-mail wanneer er reacties volgen op onderwerpen waarop geabonneerd is", + "settings-require-reload": "Sommige veranderingen vereisen het herladen van de pagina: klik hier om de pagina te herladen.", "has_no_follower": "Deze gebruiker heeft geen volgers :(", "follows_no_one": "Deze gebruiker volgt niemand :(", "has_no_posts": "Deze gebruiker heeft nog geen berichten geplaatst", "has_no_topics": "Deze gebruiker heeft nog geen berichten geplaatst", "has_no_watched_topics": "Deze gebruiker heeft nog geen berichten bekeken", - "email_hidden": "Email Verborgen", + "email_hidden": "E-mail niet beschikbaar", "hidden": "verborgen", "paginate_description": "Blader door onderwerpen en berichten in plaats van oneindig scrollen.", - "topics_per_page": "Onderwerpen per Pagina", - "posts_per_page": "Berichten per Pagina", - "notification_sounds": "Speel een geluid af wanneer ik een notificatie ontvang.", - "browsing": "Zoek Instellingen", - "open_links_in_new_tab": "Open de uitgaande links in een nieuw tabblad", - "enable_topic_searching": "Zet zoeken in het onderwerp aan", - "topic_search_help": "Als het is ingeschakeld, dan zal het standaard zoeken overschrijven en zal je vanaf nu het gehele onderwerp kunnen doorzoeken ipv wat je standaard ziet.", - "follow_topics_you_reply_to": "Volg de onderwerpen waarop u gereageerd heeft.", - "follow_topics_you_create": "Volg de onderwerpen die u gecreëerd heeft.", - "grouptitle": "Selecteer de groepstitel die u wilt weergeven ", + "topics_per_page": "Onderwerpen per pagina", + "posts_per_page": "Berichten per pagina", + "notification_sounds": "Speel een geluid af wanneer ik een notificatie ontvang", + "browsing": "Instellingen voor bladeren", + "open_links_in_new_tab": "Open uitgaande links naar een externe site in een nieuw tabblad", + "enable_topic_searching": "Inschakelen mogelijkheid op onderwerp te kunnen zoeken", + "topic_search_help": "Wanneer ingeschakeld zal de standaard zoekfunctie, met een aangepaste methode voor onderwerpen, overschreven worden", + "follow_topics_you_reply_to": "Volg de onderwerpen waarop ik gereageerd heb", + "follow_topics_you_create": "Volg de onderwerpen waarvan ik de oorspronkelijke auteur ben", + "grouptitle": "Selecteer de groepstitel voor weergave", "no-group-title": "Geen groepstitel" } \ No newline at end of file diff --git a/public/language/nl/users.json b/public/language/nl/users.json index 93e723d7b1..32692839eb 100644 --- a/public/language/nl/users.json +++ b/public/language/nl/users.json @@ -1,10 +1,10 @@ { - "latest_users": "Nieuwste Gebruikers", - "top_posters": "Meest Actief", - "most_reputation": "Meeste Reputatie", + "latest_users": "Meest recente gebruikers", + "top_posters": "Meest actieve leden", + "most_reputation": "Meeste reputatie", "search": "Zoeken", "enter_username": "Vul een gebruikersnaam in om te zoeken", - "load_more": "Meer Laden", + "load_more": "Meer laden...", "users-found-search-took": "%1 gebruiker(s) gevonden! Zoekactie duurde %2 seconden.", "filter-by": "Filter op", "online-only": "Online ", diff --git a/public/language/pl/error.json b/public/language/pl/error.json index ca267996bc..80678e8121 100644 --- a/public/language/pl/error.json +++ b/public/language/pl/error.json @@ -2,7 +2,7 @@ "invalid-data": "Błędne dane", "not-logged-in": "Nie jesteś zalogowany/a.", "account-locked": "Twoje konto zostało tymczasowo zablokowane.", - "search-requires-login": "Wyszukiwanie wymaga konta! Zaloguj się lub zarejestruj!", + "search-requires-login": "Searching requires an account - please login or register.", "invalid-cid": "Błędne ID kategorii.", "invalid-tid": "Błędne ID tematu", "invalid-pid": "Błędne ID postu", @@ -68,6 +68,7 @@ "invalid-file": "Błędny plik", "uploads-are-disabled": "Uploadowanie jest wyłączone", "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", + "about-me-too-long": "Sorry, your about me cannot be longer than %1 character(s).", "cant-chat-with-yourself": "Nie możesz rozmawiać ze sobą", "chat-restricted": "Ten użytkownik ograniczył swoje czaty. Musi Cię śledzić, zanim będziesz mógł z nim czatować.", "too-many-messages": "Wysłałeś zbyt wiele wiadomości, proszę poczekaj chwilę.", diff --git a/public/language/pl/topic.json b/public/language/pl/topic.json index f27171c5fa..496c38ba51 100644 --- a/public/language/pl/topic.json +++ b/public/language/pl/topic.json @@ -5,6 +5,7 @@ "no_topics_found": "Nie znaleziono żadnych wątków.", "no_posts_found": "Nie znaleziono żadnych postów.", "post_is_deleted": "Ten post jest usunięty", + "topic_is_deleted": "This topic is deleted!", "profile": "Profil", "posted_by": "Napisane przez %1", "posted_by_guest": "Wysłany przez Gościa", diff --git a/public/language/pl/user.json b/public/language/pl/user.json index e8e495ae0c..30593edb85 100644 --- a/public/language/pl/user.json +++ b/public/language/pl/user.json @@ -21,6 +21,7 @@ "watched": "Obserwowane", "followers": "Obserwujących", "following": "Obserwowanych", + "aboutme": "About me", "signature": "Sygnatura", "gravatar": "Gravatar", "birthday": "Urodziny", diff --git a/public/language/pt_BR/error.json b/public/language/pt_BR/error.json index a2518e7d1d..2e4890c900 100644 --- a/public/language/pt_BR/error.json +++ b/public/language/pt_BR/error.json @@ -2,7 +2,7 @@ "invalid-data": "Dados Inválidos", "not-logged-in": "Você não parece estar logado.", "account-locked": "Sua conta foi temporariamente bloqueada ", - "search-requires-login": "É necessário ter uma conta para realizar buscas! Efetue o login ou Crie uma nova conta.", + "search-requires-login": "É necessário ter uma conta para realizar buscas - por favor efetue o login ou cadastre-se.", "invalid-cid": "ID de Categoria Inválido", "invalid-tid": "ID de Tópico Inválido", "invalid-pid": "ID de Post Inválido", @@ -21,11 +21,11 @@ "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.", "no-email-to-confirm": "Este fórum exige confirmação de email, por gentileza clique aqui para digitar um email", "email-confirm-failed": "Nós não pudemos confirmar seu email, por gentileza tente novamente mais tarde.", - "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", + "confirm-email-already-sent": "O email de confirmação já foi enviado, por favor aguarde %1 minuto(s) para enviar outro.", "username-too-short": "Nome de usuário muito curto", "username-too-long": "Nome de usuário muito longo", "user-banned": "Usuário banido", - "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", + "user-too-new": "Desculpe, é necessário que você aguarde %1 segundo(s) antes de fazer seu primeiro post.", "no-category": "A categoria não existe", "no-topic": "O tópico não existe", "no-post": "O post não existe", @@ -36,17 +36,17 @@ "no-emailers-configured": "Nenhum plugin de email foi carregado, por isso um email de teste não pôde ser enviado", "category-disabled": "Categoria desativada", "topic-locked": "Tópico Trancado", - "post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting", + "post-edit-duration-expired": "Você só pode editar posts %1 segundo(s) após postar.", "still-uploading": "Aguarde a conclusão dos uploads.", - "content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).", - "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).", - "title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).", - "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).", - "too-many-posts": "You can only post once every %1 second(s) - please wait before posting again", - "too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again", - "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)", - "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)", - "file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file", + "content-too-short": "Por favor digite um post maior. Posts precisam conter ao menos %1 caractere(s).", + "content-too-long": "Por favor digite um post mais curto. Posts não podem ser maiores que %1 caractere(s)", + "title-too-short": "Por favor digite um título maior. Títulos devem conter no mínimo %1 caractere(s)", + "title-too-long": "Por favor digite um título menor. Títulos não podem ser maiores que %1 caractere(s).", + "too-many-posts": "Você pode postar uma vez a cada %1 segundo(s) - por favor aguarde antes de postar novamente", + "too-many-posts-newbie": "Como novo usuário, você pode postar uma vez a cada %1 segundo(s) até que você tenha recebido reputação de %2 - por favor aguarde antes de postar novamente", + "tag-too-short": "Por favor digite uma tag maior. Tags devem conter pelo menos %1 caractere(s)", + "tag-too-long": "Por favor digite uma tag menor. Tags não podem conter mais que %1 caractere(s)", + "file-too-big": "O tamanho máximo permitido de arquivo é %1 kB - por favor faça upload de um arquivo menor", "cant-vote-self-post": "Você não pode votar no seu próprio post", "already-favourited": "Você já adicionou este post aos favoritos", "already-unfavourited": "Você já removeu este post dos favoritos", @@ -63,11 +63,12 @@ "post-already-restored": "Este post já foi restaurado", "topic-already-deleted": "Esté tópico já foi deletado", "topic-already-restored": "Este tópico já foi restaurado", - "cant-purge-main-post": "You can't purge the main post, please delete the topic instead", + "cant-purge-main-post": "Você não pode remover o post principal, em vez disso, apague o tópico por favor.", "topic-thumbnails-are-disabled": "Thumbnails para tópico estão desativados.", "invalid-file": "Arquivo Inválido", "uploads-are-disabled": "Uploads estão desativados", - "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", + "signature-too-long": "Desculpe, sua assinatura não pode ser maior que %1 caractere(s).", + "about-me-too-long": "Desculpe, o sobre não pode ser maior que %1 caractere(s).", "cant-chat-with-yourself": "Você não pode iniciar um chat consigo mesmo!", "chat-restricted": "Este usuário restringiu suas mensagens de chat. Eles devem seguir você antes que você possa conversar com eles", "too-many-messages": "Você enviou muitas mensagens, por favor aguarde um momento.", diff --git a/public/language/pt_BR/topic.json b/public/language/pt_BR/topic.json index 2c15fac095..2b0132ba02 100644 --- a/public/language/pt_BR/topic.json +++ b/public/language/pt_BR/topic.json @@ -5,6 +5,7 @@ "no_topics_found": "Nenhum tópico encontrado!", "no_posts_found": "Nenhum post encontrado!", "post_is_deleted": "Este post está deletado!", + "topic_is_deleted": "Este tópico foi deletado!", "profile": "Perfil", "posted_by": "Postado por %1", "posted_by_guest": "Postado por Visitante", diff --git a/public/language/pt_BR/user.json b/public/language/pt_BR/user.json index 677358d58d..01b3b1d9a9 100644 --- a/public/language/pt_BR/user.json +++ b/public/language/pt_BR/user.json @@ -21,6 +21,7 @@ "watched": "Acompanhado", "followers": "Seguidores", "following": "Seguindo", + "aboutme": "Sobre", "signature": "Assinatura", "gravatar": "Gravatar", "birthday": "Aniversário", diff --git a/public/language/ro/error.json b/public/language/ro/error.json index 29d8d514b7..c7595f1509 100644 --- a/public/language/ro/error.json +++ b/public/language/ro/error.json @@ -2,7 +2,7 @@ "invalid-data": "Date invalide", "not-logged-in": "Se pare ca nu ești logat.", "account-locked": "Contul tău a fost blocat temporar", - "search-requires-login": "Ca să poți cauta ai nevoie de un cont! Te rugăm să te autentifici sau să te înregistrezi!", + "search-requires-login": "Searching requires an account - please login or register.", "invalid-cid": "ID Categorie Invalid", "invalid-tid": "ID Subiect Invalid", "invalid-pid": "ID Mesaj Invalid", @@ -68,6 +68,7 @@ "invalid-file": "Fișier invalid", "uploads-are-disabled": "Uploadurile sunt dezactivate", "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", + "about-me-too-long": "Sorry, your about me cannot be longer than %1 character(s).", "cant-chat-with-yourself": "Nu poți conversa cu tine!", "chat-restricted": "This user has restricted their chat messages. They must follow you before you can chat with them", "too-many-messages": "You have sent too many messages, please wait awhile.", diff --git a/public/language/ro/topic.json b/public/language/ro/topic.json index 590dfed6d5..f6a2d86eef 100644 --- a/public/language/ro/topic.json +++ b/public/language/ro/topic.json @@ -5,6 +5,7 @@ "no_topics_found": "Nu a fost găsit nici un subiect!", "no_posts_found": "Nu a fost găsit nici un mesaj!", "post_is_deleted": "Acest mesaj a fost șters!", + "topic_is_deleted": "This topic is deleted!", "profile": "Profil", "posted_by": "Postat de %1", "posted_by_guest": "Postat de Vizitator", diff --git a/public/language/ro/user.json b/public/language/ro/user.json index c9c3554b44..90724690b1 100644 --- a/public/language/ro/user.json +++ b/public/language/ro/user.json @@ -21,6 +21,7 @@ "watched": "Watched", "followers": "Urmărit de", "following": "Îi urmărește pe", + "aboutme": "About me", "signature": "Semnătură", "gravatar": "Gravatar", "birthday": "Zi de naștere", diff --git a/public/language/ru/error.json b/public/language/ru/error.json index 99d893e336..ddb8693245 100644 --- a/public/language/ru/error.json +++ b/public/language/ru/error.json @@ -2,7 +2,7 @@ "invalid-data": "Неверные данные", "not-logged-in": "Вы не вошли в свой аккаунт.", "account-locked": "Ваш аккаунт временно заблокирован", - "search-requires-login": "Поиск доступен зарегистрированным пользователям! Пожалуйста, войдите или зарегистрируйтесь!", + "search-requires-login": "Поиск требует аккаунта - пожалуйста, войдите или зарегистрируйтесь.", "invalid-cid": "Неверный ID категории", "invalid-tid": "Неверный ID темы", "invalid-pid": "Неверный ID поста", @@ -21,11 +21,11 @@ "email-not-confirmed-chat": "Вы не можете оставлять сообщения, пока Ваш email не подтверждён. Нажмите на это сообщение чтобы получить письмо повторно.", "no-email-to-confirm": "Этот форум требует подтверждения по E-mail. Нажмите здесь для ввода E-mail.", "email-confirm-failed": "Мы не можем подтвердить Ваш E-mail, попробуйте позже.", - "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", + "confirm-email-already-sent": "Сообщение для подтверждения уже выслано на E-mail. Повторная отправка возможна через %1 мин.", "username-too-short": "Слишком короткое имя пользователя", "username-too-long": "Имя пользователя слишком длинное", "user-banned": "Пользователь заблокирован", - "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", + "user-too-new": "Вы можете написать свой первый пост через %1 сек.", "no-category": "Категория не существует", "no-topic": "Тема не существует", "no-post": "Сообщение не существует", @@ -36,40 +36,41 @@ "no-emailers-configured": "Не подключен ни один плагин для отправки почты, поэтому тестовый email не может быть отправлен", "category-disabled": "Категория отключена", "topic-locked": "Тема закрыта", - "post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting", + "post-edit-duration-expired": "Сообщения можно редактировать только в течение %1 секунд(ы) после опубликования", "still-uploading": "Пожалуйста, подождите завершения загрузки.", - "content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).", - "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).", - "title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).", - "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).", - "too-many-posts": "You can only post once every %1 second(s) - please wait before posting again", - "too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again", - "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)", - "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)", - "file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file", + "content-too-short": "Слишком короткий пост. Минимум символов: %1.", + "content-too-long": "Слишком длинный пост. Максимум символов: %1.", + "title-too-short": "Слишком короткий заголовок. Минимум символов: %1.", + "title-too-long": "Слишком длинный заголовок. Максимум символов: %1.", + "too-many-posts": "Вы можете делать пост только один раз в %1 сек.", + "too-many-posts-newbie": "Вы новый пользователь, поэтому можете делать пост раз в %1 сек., пока не заработаете %2 п. репутации.", + "tag-too-short": "Слишком короткий тэг. Минимум символов: %1.", + "tag-too-long": "Слишком длинный тэг. Максимум символов: %1.", + "file-too-big": "Слишком большой файл. Максимальный размер: %1 Кбайт.", "cant-vote-self-post": "Вы не можете проголосовать за Ваш пост", "already-favourited": "Вы уже добавили этот пост в избранное", "already-unfavourited": "Вы уже удалили этот пост из избранного", "cant-ban-other-admins": "Вы не можете забанить других администраторов!", "invalid-image-type": "Неверный формат изображения. Поддерживаемые форматы: %1", "invalid-image-extension": "Недопустимое расширение файла", - "invalid-file-type": "Неверный формат фаила. Поддерживаемые форматы : %1", + "invalid-file-type": "Неверный формат файла. Поддерживаемые форматы: %1", "group-name-too-short": "Название группы слишком короткое", "group-already-exists": "Группа уже существует", "group-name-change-not-allowed": "Изменение названия группы запрещено", "group-already-member": "Вы уже состоите в этой группе", "group-needs-owner": "У группы должен быть как минимум один владелец", "post-already-deleted": "Этот пост уже удалён", - "post-already-restored": "Этот пост уже восстановлен.", + "post-already-restored": "Этот пост уже восстановлен", "topic-already-deleted": "Тема уже удалена", "topic-already-restored": "Тема уже восстановлена", "cant-purge-main-post": "Вы не можете удалить главное сообщение темы, вместо этого, пожалуйста, удалите топик", "topic-thumbnails-are-disabled": "Иконки для темы запрещены", - "invalid-file": "Файл испорчен", + "invalid-file": "Неверный файл", "uploads-are-disabled": "Загрузка запрещена", - "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", + "signature-too-long": "К сожалению Ваша подпись не может быть длиннее %1 символа(ов).", + "about-me-too-long": "К сожалению поле \"О себе\" не может быть длиннее чем %1 символа(ов).", "cant-chat-with-yourself": "Вы не можете общаться с самим собой", - "chat-restricted": "Пользователь ограничил прием сообщений. Он должен подписаться на Вас, чтобы Вы могли вести переписку с ним.", + "chat-restricted": "Пользователь ограничил прием сообщений. Он должен быть подписан на Вас, чтобы Вы могли вести переписку с ним.", "too-many-messages": "Вы отправили слишком много сообщений, подождите немного.", "reputation-system-disabled": "Система репутации отключена.", "downvoting-disabled": "Понижение оценки отключено", diff --git a/public/language/ru/modules.json b/public/language/ru/modules.json index 770b9446f5..ec246f2652 100644 --- a/public/language/ru/modules.json +++ b/public/language/ru/modules.json @@ -22,5 +22,5 @@ "composer.user_said": "%1 сказал:", "composer.discard": "Вы уверены, что хотите отказаться от этого поста?", "composer.submit_and_lock": "Отправить и закрыть", - "composer.toggle_dropdown": "Toggle Dropdown" + "composer.toggle_dropdown": "Показать выпадающий список" } \ No newline at end of file diff --git a/public/language/ru/topic.json b/public/language/ru/topic.json index 635e31a765..a2a50aced2 100644 --- a/public/language/ru/topic.json +++ b/public/language/ru/topic.json @@ -5,6 +5,7 @@ "no_topics_found": "Тем не найдено!", "no_posts_found": "Посты не найдены!", "post_is_deleted": "Этот пост удален!", + "topic_is_deleted": "Эта тема удалена!", "profile": "Профиль", "posted_by": "Создано %1", "posted_by_guest": "Опубликовано гостем", diff --git a/public/language/ru/user.json b/public/language/ru/user.json index 800be7cf79..755cbce9f3 100644 --- a/public/language/ru/user.json +++ b/public/language/ru/user.json @@ -21,6 +21,7 @@ "watched": "Просмотров", "followers": "Читателей", "following": "Читаемых", + "aboutme": "Обо мне", "signature": "Подпись", "gravatar": "Gravatar", "birthday": "День рождения", diff --git a/public/language/sc/error.json b/public/language/sc/error.json index 867f331c3c..ac2457e250 100644 --- a/public/language/sc/error.json +++ b/public/language/sc/error.json @@ -2,7 +2,7 @@ "invalid-data": "Invalid Data", "not-logged-in": "You don't seem to be logged in.", "account-locked": "Your account has been locked temporarily", - "search-requires-login": "Searching requires an account! Please login or register!", + "search-requires-login": "Searching requires an account - please login or register.", "invalid-cid": "Invalid Category ID", "invalid-tid": "Invalid Topic ID", "invalid-pid": "Invalid Post ID", @@ -68,6 +68,7 @@ "invalid-file": "Invalid File", "uploads-are-disabled": "Uploads are disabled", "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", + "about-me-too-long": "Sorry, your about me cannot be longer than %1 character(s).", "cant-chat-with-yourself": "You can't chat with yourself!", "chat-restricted": "This user has restricted their chat messages. They must follow you before you can chat with them", "too-many-messages": "You have sent too many messages, please wait awhile.", diff --git a/public/language/sc/topic.json b/public/language/sc/topic.json index c0ce4121ff..206ecf6465 100644 --- a/public/language/sc/topic.json +++ b/public/language/sc/topic.json @@ -5,6 +5,7 @@ "no_topics_found": "Peruna arresonada agatada!", "no_posts_found": "Perunu arresonu agatadu!", "post_is_deleted": "This post is deleted!", + "topic_is_deleted": "This topic is deleted!", "profile": "Perfilu", "posted_by": "Posted by %1", "posted_by_guest": "Posted by Guest", diff --git a/public/language/sc/user.json b/public/language/sc/user.json index 8f73839294..70097b829d 100644 --- a/public/language/sc/user.json +++ b/public/language/sc/user.json @@ -21,6 +21,7 @@ "watched": "Watched", "followers": "Sighidores", "following": "Sighende", + "aboutme": "About me", "signature": "Firma", "gravatar": "Gravatas", "birthday": "Cumpleannu", diff --git a/public/language/sk/error.json b/public/language/sk/error.json index aac7aeee03..b86131117b 100644 --- a/public/language/sk/error.json +++ b/public/language/sk/error.json @@ -2,7 +2,7 @@ "invalid-data": "Nesprávne údaje", "not-logged-in": "Nie ste prihlásený", "account-locked": "Váš účet bol dočasne uzamknutý.", - "search-requires-login": "Searching requires an account! Please login or register!", + "search-requires-login": "Searching requires an account - please login or register.", "invalid-cid": "Nesprávne ID kategórie", "invalid-tid": "Nesprávne ID témy", "invalid-pid": "Nesprávne IČ príspevku", @@ -68,6 +68,7 @@ "invalid-file": "Neplatný súbor", "uploads-are-disabled": "Nahrávanie je znefunkčnené", "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", + "about-me-too-long": "Sorry, your about me cannot be longer than %1 character(s).", "cant-chat-with-yourself": "Nemôžete chatovat so samým sebou.", "chat-restricted": "This user has restricted their chat messages. They must follow you before you can chat with them", "too-many-messages": "You have sent too many messages, please wait awhile.", diff --git a/public/language/sk/topic.json b/public/language/sk/topic.json index 007cf0f22a..108ab80c20 100644 --- a/public/language/sk/topic.json +++ b/public/language/sk/topic.json @@ -5,6 +5,7 @@ "no_topics_found": "Neboli nájdené žiadne témy!", "no_posts_found": "Neboli nájdené žiadne príspevky", "post_is_deleted": "Tento príspevok bol vymazaný!", + "topic_is_deleted": "This topic is deleted!", "profile": "Profil", "posted_by": "Publikované %1", "posted_by_guest": "Publikované %1 od hosťa", diff --git a/public/language/sk/user.json b/public/language/sk/user.json index da71890f8c..957dde36e1 100644 --- a/public/language/sk/user.json +++ b/public/language/sk/user.json @@ -21,6 +21,7 @@ "watched": "Watched", "followers": "Nasledujú ho", "following": "Nasleduje", + "aboutme": "About me", "signature": "Podpis", "gravatar": "Gravatar", "birthday": "Dátum narodenia", diff --git a/public/language/sv/category.json b/public/language/sv/category.json index 722a8d0c45..d82abe3a24 100644 --- a/public/language/sv/category.json +++ b/public/language/sv/category.json @@ -1,6 +1,6 @@ { "new_topic_button": "Nytt ämne", - "guest-login-post": "Log in to post", + "guest-login-post": "Logga in för att posta", "no_topics": "Det finns inga ämnen i denna kategori.
Varför skapar inte du ett ämne?", "browsing": "läser", "no_replies": "Ingen har svarat", diff --git a/public/language/sv/email.json b/public/language/sv/email.json index 808c73caa8..410aea5b6a 100644 --- a/public/language/sv/email.json +++ b/public/language/sv/email.json @@ -9,9 +9,9 @@ "reset.text1": "Vi fick en förfrågan om att återställa ditt lösenord, möjligen för att du har glömt det. Om detta inte är fallet, så kan du bortse från det här epostmeddelandet. ", "reset.text2": "För att fortsätta med återställning av lösenordet så kan du klicka på följande länk:", "reset.cta": "Klicka här för att återställa ditt lösenord", - "reset.notify.subject": "Password successfully changed", - "reset.notify.text1": "We are notifying you that on %1, your password was changed successfully.", - "reset.notify.text2": "If you did not authorise this, please notify an administrator immediately.", + "reset.notify.subject": "Lösenordet ändrat", + "reset.notify.text1": "Vi vill uppmärksamma dig på att ditt lösenord ändrades den %1", + "reset.notify.text2": "Om du inte godkänt det här så vänligen kontakta en admin snarast. ", "digest.notifications": "Du har olästa notiser från %1:", "digest.latest_topics": "Senaste ämnen från %1", "digest.cta": "Klicka här för att besöka %1", @@ -20,8 +20,8 @@ "notif.chat.subject": "Nytt chatt-meddelande från %1", "notif.chat.cta": "Klicka här för att fortsätta konversationen", "notif.chat.unsub.info": "Denna chatt-notifikation skickades till dig på grund av dina inställningar för prenumerationer.", - "notif.post.cta": "Click here to read the full topic", - "notif.post.unsub.info": "This post notification was sent to you due to your subscription settings.", + "notif.post.cta": "Klicka här för att läsa hela ämnet", + "notif.post.unsub.info": "Det här meddelandet fick du på grund av dina inställningar för prenumeration. ", "test.text1": "\nDet här är ett textmeddelande som verifierar att eposten är korrekt installerat för din NodeBB. ", "unsub.cta": "Klicka här för att ändra dom inställningarna", "closing": "Tack!" diff --git a/public/language/sv/error.json b/public/language/sv/error.json index bfd4e763eb..0ab94a23fc 100644 --- a/public/language/sv/error.json +++ b/public/language/sv/error.json @@ -2,7 +2,7 @@ "invalid-data": "Ogiltig data", "not-logged-in": "Du verkar inte vara inloggad.", "account-locked": "Ditt konto har tillfälligt blivit låst", - "search-requires-login": "Sökningar kräver att du har ett konto. Logga in eller registrera dig. ", + "search-requires-login": "Searching requires an account - please login or register.", "invalid-cid": "Ogiltigt id för kategori", "invalid-tid": "Ogiltigt id för ämne", "invalid-pid": "Ogiltigt id för inlägg", @@ -51,9 +51,9 @@ "already-favourited": "Du har redan favoriserat det här inlägget", "already-unfavourited": "Du har redan avfavoriserat det här inlägget", "cant-ban-other-admins": "Du kan inte bannlysa andra administratörer.", - "invalid-image-type": "Invalid image type. Allowed types are: %1", - "invalid-image-extension": "Invalid image extension", - "invalid-file-type": "Invalid file type. Allowed types are: %1", + "invalid-image-type": "Ogiltig bildtyp. Tillåtna typer är: % 1", + "invalid-image-extension": "Ogiltigt bildformat", + "invalid-file-type": "Ogiltig filtyp. Tillåtna typer är: % 1", "group-name-too-short": "Gruppnamnet är för kort", "group-already-exists": "Gruppen existerar redan", "group-name-change-not-allowed": "Gruppnamnet får inte ändras", @@ -68,6 +68,7 @@ "invalid-file": "Ogiltig fil", "uploads-are-disabled": "Uppladdningar är inaktiverat", "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", + "about-me-too-long": "Sorry, your about me cannot be longer than %1 character(s).", "cant-chat-with-yourself": "Du kan inte chatta med dig själv.", "chat-restricted": "Denna användaren har begränsat sina chatt-meddelanden. Användaren måste följa dig innan ni kan chatta med varann", "too-many-messages": "You have sent too many messages, please wait awhile.", @@ -77,7 +78,7 @@ "not-enough-reputation-to-flag": "Du har inte tillräckligt förtroende för att flagga det här inlägget.", "reload-failed": "NodeBB stötte på problem med att ladda om: \"%1\". NodeBB kommer fortsätta servera den befintliga resurser till klienten, men du borde återställa det du gjorde alldeles innan du försökte ladda om.", "registration-error": "Registreringsfel", - "parse-error": "Something went wrong while parsing server response", - "wrong-login-type-email": "Please use your email to login", - "wrong-login-type-username": "Please use your username to login" + "parse-error": "Något gick fel vid tolkning av svar från servern", + "wrong-login-type-email": "Använd din e-post adress för att logga in", + "wrong-login-type-username": "Använd ditt användarnamn för att logga in" } \ No newline at end of file diff --git a/public/language/sv/login.json b/public/language/sv/login.json index c96dd64485..363da51e9b 100644 --- a/public/language/sv/login.json +++ b/public/language/sv/login.json @@ -1,6 +1,6 @@ { - "username-email": "Username / Email", - "username": "Username", + "username-email": "Användarnamn eller epostadress", + "username": "Användarnamn", "email": "Email", "remember_me": "Kom ihåg mig?", "forgot_password": "Glömt lösenord?", diff --git a/public/language/sv/search.json b/public/language/sv/search.json index 7822eeae96..c023bf5927 100644 --- a/public/language/sv/search.json +++ b/public/language/sv/search.json @@ -1,6 +1,6 @@ { "results_matching": "%1 resultat matchar \"%2\", (%3 sekunder)", - "no-matches": "No matches found", + "no-matches": "Inga träffar", "advanced-search": "Advanced Search", "in": "In", "titles": "Titles", @@ -16,9 +16,9 @@ "older-than": "Older than", "any-date": "Any date", "yesterday": "Yesterday", - "one-week": "One week", - "two-weeks": "Two weeks", - "one-month": "One month", + "one-week": "En vecka", + "two-weeks": "Två veckor", + "one-month": "En månad", "three-months": "Three months", "six-months": "Six months", "one-year": "One year", @@ -32,7 +32,7 @@ "category": "Category", "descending": "In descending order", "ascending": "In ascending order", - "save-preferences": "Save preferences", + "save-preferences": "Spara inställningar", "clear-preferences": "Clear preferences", "search-preferences-saved": "Search preferences saved", "search-preferences-cleared": "Search preferences cleared", diff --git a/public/language/sv/topic.json b/public/language/sv/topic.json index 40b81f0ec4..d589412c3a 100644 --- a/public/language/sv/topic.json +++ b/public/language/sv/topic.json @@ -4,7 +4,8 @@ "topic_id_placeholder": "Skriv in ID för ämne", "no_topics_found": "Inga ämnen hittades!", "no_posts_found": "Inga inlägg hittades!", - "post_is_deleted": "Det här inlägget är raderat.", + "post_is_deleted": "Detta inlägg är raderat!", + "topic_is_deleted": "Detta ämne är raderat!", "profile": "Profil", "posted_by": "Skapat av %1", "posted_by_guest": "Inlägg av anonym", @@ -12,7 +13,7 @@ "notify_me": "Få notiser om nya svar i detta ämne", "quote": "Citera", "reply": "Svara", - "guest-login-reply": "Log in to reply", + "guest-login-reply": "Logga in för att posta", "edit": "Ändra", "delete": "Ta bort", "purge": "Rensa", @@ -75,9 +76,9 @@ "fork_no_pids": "Inga inlägg valda!", "fork_success": "Ämnet har blivit förgrenat. Klicka här för att gå till det förgrenade ämnet.", "composer.title_placeholder": "Skriv in ämnets titel här...", - "composer.handle_placeholder": "Name", + "composer.handle_placeholder": "Namn", "composer.discard": "Avbryt", - "composer.submit": "Spara", + "composer.submit": "Skicka", "composer.replying_to": "Svarar till %1", "composer.new_topic": "Nytt ämne", "composer.uploading": "laddar upp...", @@ -95,5 +96,5 @@ "oldest_to_newest": "Äldst till nyaste", "newest_to_oldest": "Nyaste till äldst", "most_votes": "Mest röster", - "most_posts": "Most posts" + "most_posts": "Felst inlägg" } \ No newline at end of file diff --git a/public/language/sv/user.json b/public/language/sv/user.json index 2702388846..9131f6ff9d 100644 --- a/public/language/sv/user.json +++ b/public/language/sv/user.json @@ -2,8 +2,8 @@ "banned": "Bannad", "offline": "Offline", "username": "Användarnamn", - "joindate": "Join Date", - "postcount": "Post Count", + "joindate": "Gick med", + "postcount": "Antal inlägg", "email": "Epost", "confirm_email": "Bekräfta epostadress ", "delete_account": "Ta bort ämne", @@ -18,16 +18,17 @@ "profile_views": "Profil-visningar", "reputation": "Rykte", "favourites": "Favoriter", - "watched": "Watched", + "watched": "Bevakad", "followers": "Följare", "following": "Följer", + "aboutme": "Om mig", "signature": "Signatur", "gravatar": "Gravatar", "birthday": "Födelsedag", "chat": "Chatta", "follow": "Följ", "unfollow": "Sluta följ", - "more": "More", + "more": "Mer", "profile_update_success": "Profilen uppdaterades.", "change_picture": "Ändra bild", "edit": "Ändra", @@ -59,25 +60,25 @@ "digest_weekly": "Veckovis", "digest_monthly": "Månadsvis", "send_chat_notifications": "Skicka ett epostmeddelande om nya chatt-meddelanden tas emot när jag inte är online.", - "send_post_notifications": "Send an email when replies are made to topics I am subscribed to", - "settings-require-reload": "Some setting changes require a reload. Click here to reload the page.", + "send_post_notifications": "Skicka ett epost när svar kommit på ämnen jag prenumererar på till", + "settings-require-reload": "Vissa inställningar som ändrades kräver att sidan laddas om. Klicka här för att ladda om sidan.", "has_no_follower": "Denna användare har inga följare :(", "follows_no_one": "Denna användare följer ingen :(", "has_no_posts": "Denna användare har inte gjort några inlägg än.", "has_no_topics": "Den här användaren har inte skrivit något inlägg ännu.", - "has_no_watched_topics": "This user didn't watch any topics yet.", + "has_no_watched_topics": "Den här användaren bevakar inga ämnen ännu.", "email_hidden": "Epost dold", "hidden": "dold", - "paginate_description": "Paginate topics and posts instead of using infinite scroll", + "paginate_description": "Gör så att ämnen och inlägg visas som sidor istället för oändlig skroll", "topics_per_page": "Ämnen per sida", "posts_per_page": "Inlägg per sida", - "notification_sounds": "Play a sound when you receive a notification", + "notification_sounds": "Spela ett ljud när du får en notis", "browsing": "Inställning för bläddring", - "open_links_in_new_tab": "Open outgoing links in new tab", + "open_links_in_new_tab": "Öppna utgående länkar på ny flik", "enable_topic_searching": "Aktivera Sökning Inom Ämne", - "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen", - "follow_topics_you_reply_to": "Follow topics that you reply to", - "follow_topics_you_create": "Follow topics you create", - "grouptitle": "Select the group title you would like to display", - "no-group-title": "No group title" + "topic_search_help": "Om aktiverat kommer sökning inom ämne överskrida webbläsarens vanliga funktionen för sökning bland sidor och tillåta dig att söka genom hela ämnet istället för det som endast visas på skärmen.", + "follow_topics_you_reply_to": "Följ ämnen som du svarat på", + "follow_topics_you_create": "Följ ämnen du skapat", + "grouptitle": "Välj tittel för gruppen så som du vill att den ska visas", + "no-group-title": "Ingen titel på gruppen" } \ No newline at end of file diff --git a/public/language/th/error.json b/public/language/th/error.json index 1c33a57db0..a962e365f4 100644 --- a/public/language/th/error.json +++ b/public/language/th/error.json @@ -2,7 +2,7 @@ "invalid-data": "ข้อมูลไม่ถูกต้อง", "not-logged-in": "คุณยังไม่ได้ลงชื่อเข้าระบบ", "account-locked": "บัญชีของคุณถูกระงับการใช้งานชั่วคราว", - "search-requires-login": "ต้องลงทะเบียนบัญชีผู้ใช้สำหรับการค้นหา! โปรดลงชื่อเข้าระบบ หรือ ลงทะเบียน!", + "search-requires-login": "Searching requires an account - please login or register.", "invalid-cid": "Category ID ไม่ถูกต้อง", "invalid-tid": "Topic ID ไม่ถูกต้อง", "invalid-pid": "Post ID ไม่ถูกต้อง", @@ -68,6 +68,7 @@ "invalid-file": "Invalid File", "uploads-are-disabled": "Uploads are disabled", "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", + "about-me-too-long": "Sorry, your about me cannot be longer than %1 character(s).", "cant-chat-with-yourself": "You can't chat with yourself!", "chat-restricted": "This user has restricted their chat messages. They must follow you before you can chat with them", "too-many-messages": "You have sent too many messages, please wait awhile.", diff --git a/public/language/th/topic.json b/public/language/th/topic.json index ea6a1a8998..15d1718498 100644 --- a/public/language/th/topic.json +++ b/public/language/th/topic.json @@ -5,6 +5,7 @@ "no_topics_found": "ไม่พบกระทู้", "no_posts_found": "ไม่พบโพส", "post_is_deleted": "ลบ Post นี้เรียบร้อยแล้ว!", + "topic_is_deleted": "This topic is deleted!", "profile": "รายละเอียด", "posted_by": "โพสโดย %1", "posted_by_guest": "โพสโดย Guest", diff --git a/public/language/th/user.json b/public/language/th/user.json index f53b390dd7..15cd6fb01b 100644 --- a/public/language/th/user.json +++ b/public/language/th/user.json @@ -21,6 +21,7 @@ "watched": "ดูแล้ว", "followers": "คนติดตาม", "following": "ติดตาม", + "aboutme": "About me", "signature": "ลายเซ็น", "gravatar": "Gravatar", "birthday": "วันเกิด", diff --git a/public/language/tr/error.json b/public/language/tr/error.json index 49e7247239..c270b7fb27 100644 --- a/public/language/tr/error.json +++ b/public/language/tr/error.json @@ -25,7 +25,7 @@ "username-too-short": "Kullanıcı ismi çok kısa", "username-too-long": "Kullanıcı ismi çok uzun.", "user-banned": "Kullanıcı Yasaklı", - "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", + "user-too-new": "Özür dileriz, ilk iletinizi yapmadan önce %1 saniye beklemeniz gerekiyor", "no-category": "Kategori Yok", "no-topic": "Başlık Yok", "no-post": "İleti Yok", @@ -36,17 +36,17 @@ "no-emailers-configured": "E-posta eklentisi kurulu değil bu yüzden test e-postası gönderilemedi", "category-disabled": "Kategori aktif değil", "topic-locked": "Başlık Kilitli", - "post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting", + "post-edit-duration-expired": "Gönderilen iletiler %1 saniyeden sonra değiştirilemez", "still-uploading": "Lütfen yüklemelerin bitmesini bekleyin.", - "content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).", - "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).", - "title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).", - "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).", - "too-many-posts": "You can only post once every %1 second(s) - please wait before posting again", - "too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again", - "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)", - "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)", - "file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file", + "content-too-short": "Lütfen daha uzun bir ileti girin. En az %1 karakter.", + "content-too-long": "Lütfen daha kısa bir ileti girin. İletiler %1 karakterden uzun olamaz.", + "title-too-short": "Lütfen daha uzun bir başlık girin. Başlıklar en az %1 karakter içermelidir.", + "title-too-long": "Lütfen daha kısa bir başlık girin. Başlıklar %1 karakterden uzun olamaz.", + "too-many-posts": "%1 saniye içinde yalnızca bir ileti gönderebilirsiniz - tekrar ileti göndermeden önce lütfen bekleyin.", + "too-many-posts-newbie": "Yeni bir kullanıcı olarak, %2 saygınlık kazanana kadar %1 saniye içinde bir ileti gönderebilirsiniz - tekrar ileti göndermeden önce lütfen bekleyin.", + "tag-too-short": "Lütfen daha uzun bir etiket girin. Etiketler en az %1 karakter içermelidir.", + "tag-too-long": "Lütfen daha kısa bir etiket girin. Etiketler %1 karakterden uzun olamaz.", + "file-too-big": "İzin verilen en büyük dosya boyutu %1 kb - lütfen daha küçük bir dosya yükleyin", "cant-vote-self-post": "Kendi iletinize oy veremezsiniz", "already-favourited": "Bu iletiyi zaten favorilerinize eklediniz", "already-unfavourited": "Bu iletiyi zaten favorilerinizden çıkardınız", @@ -67,7 +67,8 @@ "topic-thumbnails-are-disabled": "Başlık resimleri kapalı.", "invalid-file": "Geçersiz Dosya", "uploads-are-disabled": "Yüklemeler kapalı", - "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", + "signature-too-long": "Üzgünüm, imzanız %1 karakterden uzun olamaz.", + "about-me-too-long": "Hakkınızda kısmı en fazla %1 karakter olabilir.", "cant-chat-with-yourself": "Kendinizle sohbet edemezsiniz!", "chat-restricted": "Bu kullanıcı sohbet ayarlarını kısıtlamış. Bu kişiye mesaj gönderebilmeniz için sizi takip etmeleri gerekiyor", "too-many-messages": "Ardı ardına çok fazla mesaj yolladınız, lütfen biraz bekleyiniz.", diff --git a/public/language/tr/topic.json b/public/language/tr/topic.json index ef6276ca31..f5a42a48d2 100644 --- a/public/language/tr/topic.json +++ b/public/language/tr/topic.json @@ -5,6 +5,7 @@ "no_topics_found": "Hiç konu bulunamadı!", "no_posts_found": "Hiç bir ileti bulunamadı!", "post_is_deleted": "Bu ileti silinmiş!", + "topic_is_deleted": "Bu başlık silindi!", "profile": "Profil", "posted_by": "%1 tarafından gönderildi", "posted_by_guest": "Ziyaretçi tarafından yayımlandı", diff --git a/public/language/tr/user.json b/public/language/tr/user.json index 285cb3ebc5..6d13937fa7 100644 --- a/public/language/tr/user.json +++ b/public/language/tr/user.json @@ -21,6 +21,7 @@ "watched": "İzlendi", "followers": "Takipçiler", "following": "Takip Ediyor", + "aboutme": "Hakkımda", "signature": "İmza", "gravatar": "Avatar", "birthday": "Doğum Tarihi", diff --git a/public/language/vi/error.json b/public/language/vi/error.json index 3aecae1d63..33da9ba7ce 100644 --- a/public/language/vi/error.json +++ b/public/language/vi/error.json @@ -2,7 +2,7 @@ "invalid-data": "Dữ liệu không hợp lệ", "not-logged-in": "Bạn chưa đăng nhập", "account-locked": "Tài khoản của bạn đang tạm thời bị khóa", - "search-requires-login": "Bạn cần đăng nhập để thực hiện việc tìm kiếm! Xin hãy đăng nhập hoặc đăng ký!", + "search-requires-login": "Searching requires an account - please login or register.", "invalid-cid": "Danh mục ID không hợp lệ", "invalid-tid": "ID chủ đề không hợp lệ", "invalid-pid": "ID bài viết không hợp lệ", @@ -68,6 +68,7 @@ "invalid-file": "File không hợp lệ", "uploads-are-disabled": "Đã khóa lựa chọn tải lên", "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", + "about-me-too-long": "Sorry, your about me cannot be longer than %1 character(s).", "cant-chat-with-yourself": "Bạn không thể chat với chính bạn!", "chat-restricted": "Người dùng này đã bật chế độ hạn chế tin nhắn chat. Bạn phải được anh/cô ta follow thì mới có thể gởi tin nhắn đến họ được.", "too-many-messages": "You have sent too many messages, please wait awhile.", diff --git a/public/language/vi/topic.json b/public/language/vi/topic.json index 9d0ca5e82d..f2c2193892 100644 --- a/public/language/vi/topic.json +++ b/public/language/vi/topic.json @@ -5,6 +5,7 @@ "no_topics_found": "Không tìm thấy chủ đề nào!", "no_posts_found": "Không tìm thấy bài gửi nào", "post_is_deleted": "Bài gửi này đã bị xóa!", + "topic_is_deleted": "This topic is deleted!", "profile": "Hồ sơ", "posted_by": "Được viết bởi %1", "posted_by_guest": "Đăng bởi khách", diff --git a/public/language/vi/user.json b/public/language/vi/user.json index d567c55285..5d60c007a1 100644 --- a/public/language/vi/user.json +++ b/public/language/vi/user.json @@ -21,6 +21,7 @@ "watched": "Đã theo dõi", "followers": "Số người theo dõi", "following": "Đang theo dõi", + "aboutme": "About me", "signature": "Chữ ký", "gravatar": "Gavatar", "birthday": "Ngày sinh ", diff --git a/public/language/zh_CN/category.json b/public/language/zh_CN/category.json index 14f4a36667..f28e696cc7 100644 --- a/public/language/zh_CN/category.json +++ b/public/language/zh_CN/category.json @@ -1,6 +1,6 @@ { "new_topic_button": "新主题", - "guest-login-post": "登陆后发表", + "guest-login-post": "登录后发表", "no_topics": "此版块还没有任何内容。
赶紧来发帖吧!", "browsing": "正在浏览", "no_replies": "尚无回复", diff --git a/public/language/zh_CN/error.json b/public/language/zh_CN/error.json index 934a7e3991..b19f5cf538 100644 --- a/public/language/zh_CN/error.json +++ b/public/language/zh_CN/error.json @@ -1,8 +1,8 @@ { "invalid-data": "无效数据", - "not-logged-in": "您还没有登陆。", + "not-logged-in": "您还没有登录。", "account-locked": "您的帐号已被临时锁定", - "search-requires-login": "搜索功能仅限会员使用!请先登录或者注册!", + "search-requires-login": "搜索功能仅限会员使用 - 请先登录或者注册。", "invalid-cid": "无效版块 ID", "invalid-tid": "无效主题 ID", "invalid-pid": "无效帖子 ID", @@ -15,51 +15,51 @@ "invalid-username-or-password": "请确认用户名和密码", "invalid-search-term": "无效的搜索关键字", "invalid-pagination-value": "无效页码", - "username-taken": "用户名已被占用", - "email-taken": "电子邮箱已被占用", + "username-taken": "此用户名已被占用", + "email-taken": "此电子邮箱已被占用", "email-not-confirmed": "您的电子邮箱尚未确认,请点击这里确认您的电子邮箱。", "email-not-confirmed-chat": "您的电子邮箱尚未确认,无法聊天,请点击这里确认您的电子邮箱。", - "no-email-to-confirm": "本论坛需要电子邮箱确认,请点击这里输入一个电子邮箱地址", + "no-email-to-confirm": "本论坛需要电子邮箱确认,请点击这里输入电子邮箱地址", "email-confirm-failed": "我们无法确认您的电子邮箱,请重试", - "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", + "confirm-email-already-sent": "确认邮件已发出,如需重新发送请等待 %1 分钟后再试。", "username-too-short": "用户名太短", "username-too-long": "用户名太长", "user-banned": "用户已禁止", - "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", - "no-category": "版面不存在", + "user-too-new": "抱歉,您需要等待 %1 秒后,才可以发帖!", + "no-category": "版块不存在", "no-topic": "主题不存在", "no-post": "帖子不存在", "no-group": "用户组不存在", "no-user": "用户不存在", "no-teaser": "主题预览不存在", - "no-privileges": "您没有足够的权限执行此操作。", - "no-emailers-configured": "未加载任何电子邮箱插件,无法发送测试邮件", + "no-privileges": "您没有权限执行此操作。", + "no-emailers-configured": "因未配置电子邮箱插件,无法发送测试邮件", "category-disabled": "版块已禁用", "topic-locked": "主题已锁定", - "post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting", + "post-edit-duration-expired": "您必须在发表 %1 秒后才能修改内容", "still-uploading": "请等待上传完成", - "content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).", - "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).", - "title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).", - "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).", - "too-many-posts": "You can only post once every %1 second(s) - please wait before posting again", - "too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again", - "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)", - "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)", - "file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file", + "content-too-short": "请增添发帖内容,不能少于 %1 个字符。", + "content-too-long": "请删减发帖内容,不能超过 %1 个字符。", + "title-too-short": "请增加标题,不能少于 %1 个字符。", + "title-too-long": "请缩减标题,不超过 %1 个字符。", + "too-many-posts": "发帖需要间隔 %1 秒以上 - 请稍候再发帖", + "too-many-posts-newbie": "因为您是新用户,所以限制每隔 %1 秒才能发帖一次,直到您有 %2 点威望为止 —— 请稍候再发帖", + "tag-too-short": "话题太短,不能少于 %1 个字符", + "tag-too-long": "话题太长,不能超过 %1 个字符", + "file-too-big": "上传文件的大小限制为 %1 KB - 请缩减文件大小", "cant-vote-self-post": "您不能给自己的帖子投票。", "already-favourited": "您已收藏该帖", "already-unfavourited": "您已取消收藏此帖", - "cant-ban-other-admins": "您不能禁止其他管理员!", + "cant-ban-other-admins": "您不能封禁其他管理员!", "invalid-image-type": "无效的图像类型。允许的类型有:%1", "invalid-image-extension": "无效的图像扩展", "invalid-file-type": "无效文件格式,允许的格式有:%1", - "group-name-too-short": "用户组名称太短", - "group-already-exists": "用户组已存在", - "group-name-change-not-allowed": "不允许更改用户组名称", - "group-already-member": "您已是此小组成员", - "group-needs-owner": "此小组需要至少一名组长", - "post-already-deleted": "此帖子已被删除", + "group-name-too-short": "小组名太短", + "group-already-exists": "小组已存在", + "group-name-change-not-allowed": "不允许更改小组名称", + "group-already-member": "您已经是此小组的成员", + "group-needs-owner": "小组需要指定至少一名组长", + "post-already-deleted": "此帖已被删除", "post-already-restored": "此帖已经恢复", "topic-already-deleted": "此主题已被删除", "topic-already-restored": "此主题已恢复", @@ -67,17 +67,18 @@ "topic-thumbnails-are-disabled": "主题缩略图已禁用", "invalid-file": "无效文件", "uploads-are-disabled": "上传已禁用", - "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", + "signature-too-long": "抱歉,您的签名不能超过 %1 个字符。", + "about-me-too-long": "抱歉,您的‘关于我’不能超过 %1 个字符。", "cant-chat-with-yourself": "您不能和自己聊天!", "chat-restricted": "此用户限制了他的聊天消息。必须他先关注您,您才能和他聊天。", "too-many-messages": "您发送了太多消息,请稍等片刻。", "reputation-system-disabled": "威望系统已禁用。", - "downvoting-disabled": "反对功能已禁用", - "not-enough-reputation-to-downvote": "您还没有足够的威望为此帖扣分", - "not-enough-reputation-to-flag": "您没有足够的威望标记此帖", - "reload-failed": "NodeBB 重新加载时遇到问题: \"%1\"。NodeBB 会继续给已存在的客户端组件服务,虽然您应该撤销在重新加载前执行的操作。", + "downvoting-disabled": "扣分功能已禁用", + "not-enough-reputation-to-downvote": "您的威望不足以给此帖扣分", + "not-enough-reputation-to-flag": "您的威望不足以举报此帖", + "reload-failed": "刷新 NodeBB 时遇到问题: \"%1\"。NodeBB 保持给已连接的客户端服务,您应该撤销刷新前做的更改。", "registration-error": "注册错误", - "parse-error": "解析服务器响应时出错", + "parse-error": "服务器响应解析出错", "wrong-login-type-email": "请输入您的电子邮箱地址登录", "wrong-login-type-username": "请输入您的用户名登录" } \ No newline at end of file diff --git a/public/language/zh_CN/global.json b/public/language/zh_CN/global.json index ede930cbbf..084a16029e 100644 --- a/public/language/zh_CN/global.json +++ b/public/language/zh_CN/global.json @@ -39,10 +39,10 @@ "nextpage": "下一页", "alert.success": "成功", "alert.error": "错误", - "alert.banned": "禁止", - "alert.banned.message": "您刚刚被禁止,现在您将退出登录。", + "alert.banned": "封禁", + "alert.banned.message": "您刚刚被封,现在您将退出登录。", "alert.unfollow": "您已取消关注 %1!", - "alert.follow": "您正在关注 %1!", + "alert.follow": "您已关注 %1!", "online": "在线", "users": "会员", "topics": "主题", @@ -73,7 +73,7 @@ "guest": "游客", "guests": "游客", "updated.title": "论坛已更新", - "updated.message": "论坛已更新到最新版本。点这里刷新页面。", + "updated.message": "论坛已更新。请点这里刷新页面。", "privacy": "隐私", "follow": "关注", "unfollow": "取消关注", diff --git a/public/language/zh_CN/groups.json b/public/language/zh_CN/groups.json index 821707c77f..2d6e927d40 100644 --- a/public/language/zh_CN/groups.json +++ b/public/language/zh_CN/groups.json @@ -1,36 +1,36 @@ { - "groups": "用户组", - "view_group": "查看用户组", - "owner": "用户组组长", - "new_group": "创建新用户组", - "no_groups_found": "还没有用户组", + "groups": "小组", + "view_group": "查看小组", + "owner": "组长", + "new_group": "创建小组", + "no_groups_found": "尚无小组信息", "pending.accept": "接受", "pending.reject": "拒绝", - "cover-instructions": "拖放照片,拖动位置,然后点击 保存", - "cover-change": "变更", + "cover-instructions": "拖放照片到此位置,然后点击 保存", + "cover-change": "更改", "cover-save": "保存", "cover-saving": "正在保存", - "details.title": "用户组详情", - "details.members": "会员列表", - "details.pending": "预备成员", - "details.has_no_posts": "此用户组的会员尚未发表任何帖子。", + "details.title": "小组信息", + "details.members": "成员列表", + "details.pending": "待加入成员", + "details.has_no_posts": "此小组的会员尚未发表任何帖子。", "details.latest_posts": "最新帖子", "details.private": "私有", - "details.grant": "授予/取消所有权", - "details.kick": "踢", - "details.owner_options": "用户组管理", - "details.group_name": "用户组名", - "details.member_count": "会员总数", + "details.grant": "授予/取消管理权", + "details.kick": "踢出小组", + "details.owner_options": "小组管理", + "details.group_name": "小组名", + "details.member_count": "小组成员数", "details.creation_date": "创建时间", "details.description": "描述", - "details.badge_preview": "标志预览", + "details.badge_preview": "徽章预览", "details.change_icon": "更改图标", - "details.change_colour": "更新颜色", - "details.badge_text": "标志文字", - "details.userTitleEnabled": "显示标志", - "details.private_help": "如果条件允许,必须得到群主批准才能加入该群。", + "details.change_colour": "更改颜色", + "details.badge_text": "徽章文本", + "details.userTitleEnabled": "显示组内称号", + "details.private_help": "启用此选项后,加入小组需要组长审批。", "details.hidden": "隐藏", - "details.hidden_help": "如果条件允许,这个群不再出现在此列表,所有用户要人工邀请加入。", - "event.updated": "用户组信息已更新", - "event.deleted": "用户组 \"%1\" 已被删除" + "details.hidden_help": "启用此选项后,小组将不在小组列表中展现,成员只能通过邀请加入。", + "event.updated": "小组信息已更新", + "event.deleted": "小组 \"%1\" 已被删除" } \ No newline at end of file diff --git a/public/language/zh_CN/recent.json b/public/language/zh_CN/recent.json index 1e76727b6a..428985f0e5 100644 --- a/public/language/zh_CN/recent.json +++ b/public/language/zh_CN/recent.json @@ -6,14 +6,14 @@ "year": "年度热帖榜", "alltime": "总热帖榜", "no_recent_topics": "暂无主题。", - "no_popular_topics": "没有热门主题", - "there-is-a-new-topic": "有一个新主题", - "there-is-a-new-topic-and-a-new-post": "有一个新主题和一个新发表", - "there-is-a-new-topic-and-new-posts": "有一个新主题和 %1 新发表", - "there-are-new-topics": "有 %1 个新主题", - "there-are-new-topics-and-a-new-post": "有 %1个新主题和一个新发表", - "there-are-new-topics-and-new-posts": "有 %1个新主题和 %2个新发表", - "there-is-a-new-post": "有一个新发表", - "there-are-new-posts": "有 %1个新发表", - "click-here-to-reload": "点击这里重新加载" + "no_popular_topics": "没有热门主题。", + "there-is-a-new-topic": "共计 1 个新主题。", + "there-is-a-new-topic-and-a-new-post": "共计 1 个新主题和 1 个新回复。", + "there-is-a-new-topic-and-new-posts": "共计 1 个新主题和 %1 个新回复。", + "there-are-new-topics": "共计 %1 个新主题。", + "there-are-new-topics-and-a-new-post": "共计 %1 个新主题和 1 个新回复。", + "there-are-new-topics-and-new-posts": "共计 %1 个新主题和 %2 个新回复。", + "there-is-a-new-post": "共计 1 个新回复。", + "there-are-new-posts": "共计 %1 个新回复。", + "click-here-to-reload": "点击这里重新加载。" } \ No newline at end of file diff --git a/public/language/zh_CN/topic.json b/public/language/zh_CN/topic.json index 9b113c0c90..655bf46f55 100644 --- a/public/language/zh_CN/topic.json +++ b/public/language/zh_CN/topic.json @@ -4,7 +4,8 @@ "topic_id_placeholder": "输入主题 ID", "no_topics_found": "没有找到主题!", "no_posts_found": "没有找到帖子!", - "post_is_deleted": "此帖已被删除!", + "post_is_deleted": "此回复已被删除!", + "topic_is_deleted": "此主题已被删除!", "profile": "资料", "posted_by": "%1 发布", "posted_by_guest": "游客发布", @@ -25,21 +26,21 @@ "flag": "标记", "locked": "已锁定", "bookmark_instructions": "点击这里返回您最后浏览的位置。", - "flag_title": "对此帖设置管理标志", - "flag_confirm": "确认给此帖设置标记吗?", - "flag_success": "此回帖已设置管理标记。", + "flag_title": "举报", + "flag_confirm": "您确认要举报此帖吗?", + "flag_success": "已举报此回帖。", "deleted_message": "此主题已被删除。只有拥有主题管理权限的用户可以查看。", - "following_topic.message": "当有人回复此主题时您会收到通知。", + "following_topic.message": "当有人回复此主题时,您会收到通知。", "not_following_topic.message": "您已停止接收此主题的通知。", - "login_to_subscribe": "请注册或登录后再订阅此主题。", + "login_to_subscribe": "请注册或登录后,再订阅此主题。", "markAsUnreadForAll.success": "将全部主题标为未读。", - "watch": "监视", - "unwatch": "停止监视", - "watch.title": "此主题有新回复时通知我", - "unwatch.title": "停止监视此主题", - "share_this_post": "分享此帖", + "watch": "关注", + "unwatch": "取消关注", + "watch.title": "当此主题有新回复时,通知我", + "unwatch.title": "取消关注此主题", + "share_this_post": "分享", "thread_tools.title": "主题工具", - "thread_tools.markAsUnreadForAll": "未读标记", + "thread_tools.markAsUnreadForAll": "标记为未读", "thread_tools.pin": "置顶主题", "thread_tools.unpin": "取消置顶主题", "thread_tools.lock": "锁定主题", @@ -63,7 +64,7 @@ "confirm_fork": "分割", "favourite": "收藏", "favourites": "收藏", - "favourites.has_no_favourites": "您还没有任何收藏,收藏的帖子会在这里展示!", + "favourites.has_no_favourites": "您还没有任何收藏,收藏后的帖子会显示在这里!", "loading_more_posts": "正在加载更多帖子", "move_topic": "移动主题", "move_topics": "移动主题", @@ -86,7 +87,7 @@ "composer.thumb_url_placeholder": "http://example.com/thumb.png", "composer.thumb_file_label": "或上传文件", "composer.thumb_remove": "清除字段", - "composer.drag_and_drop_images": "拖拽图片到这里", + "composer.drag_and_drop_images": "拖拽图片到此处", "more_users_and_guests": "%1 名会员和 %2 名游客", "more_users": "%1 名会员", "more_guests": "%1 名游客", @@ -95,5 +96,5 @@ "oldest_to_newest": "从旧到新", "newest_to_oldest": "从新到旧", "most_votes": "最多投票", - "most_posts": "最多发表" + "most_posts": "最多回复" } \ No newline at end of file diff --git a/public/language/zh_CN/user.json b/public/language/zh_CN/user.json index 583c03fd09..6ad1a8fa72 100644 --- a/public/language/zh_CN/user.json +++ b/public/language/zh_CN/user.json @@ -7,7 +7,7 @@ "email": "电子邮件", "confirm_email": "确认电子邮箱", "delete_account": "删除帐号", - "delete_account_confirm": "确认要删除您的帐户吗?
此操作是不可逆转的,您会无法恢复您的任何数据

请输入您的用户名,确认您想要删除此帐户。", + "delete_account_confirm": "确认要删除您的帐户吗?
此操作是不可逆转的,您将无法恢复您的任何数据

请输入您的用户名,确认您想要删除此帐户。", "fullname": "姓名", "website": "网站", "location": "位置", @@ -21,6 +21,7 @@ "watched": "已订阅", "followers": "粉丝", "following": "关注", + "aboutme": "About me", "signature": "签名档", "gravatar": "头像", "birthday": "生日", @@ -58,9 +59,9 @@ "digest_daily": "每天", "digest_weekly": "每周", "digest_monthly": "每月", - "send_chat_notifications": "当我不在线,并受到新的聊天消息时给我发邮件", - "send_post_notifications": "我订阅的主题有回复时发送邮件", - "settings-require-reload": "一些设置变更需要刷新页面。点击这里刷新页面。", + "send_chat_notifications": "当我不在线并收到新的聊天消息时,给我发送邮件通知", + "send_post_notifications": "当我订阅的主题有新回复时,给我发送邮件通知", + "settings-require-reload": "某些设置变更需要刷新页面。点击这里刷新页面。", "has_no_follower": "此用户还没有粉丝 :(", "follows_no_one": "此用户尚未关注任何人 :(", "has_no_posts": "此用户尚未发布任何帖子。", @@ -75,9 +76,9 @@ "browsing": "浏览设置", "open_links_in_new_tab": "在新标签打开外部链接", "enable_topic_searching": "启用主题内搜索", - "topic_search_help": "如果启用,主题内搜索会替代浏览器默认的网页搜索,使你可以在整个主题内搜索,而不仅仅时页面上展现的内容。", - "follow_topics_you_reply_to": "关注你回复的主题", - "follow_topics_you_create": "关注你创建的主题", - "grouptitle": "选择展现的组内称号", - "no-group-title": "无组内头衔" + "topic_search_help": "如果启用此项,主题内搜索会替代浏览器默认的页面搜索,您将可以在整个主题内搜索,而不仅仅只搜索页面上展现的内容。", + "follow_topics_you_reply_to": "关注您回复的主题", + "follow_topics_you_create": "关注您创建的主题", + "grouptitle": "选择展示的小组称号", + "no-group-title": "不展示小组称号" } \ No newline at end of file diff --git a/public/language/zh_TW/category.json b/public/language/zh_TW/category.json index b018f21e89..0dff52556a 100644 --- a/public/language/zh_TW/category.json +++ b/public/language/zh_TW/category.json @@ -1,12 +1,12 @@ { "new_topic_button": "新主題", - "guest-login-post": "Log in to post", - "no_topics": "這個版面還沒有任何內容。
趕緊來發文章吧!", + "guest-login-post": "登錄後才能發表", + "no_topics": "這個類別還沒有任何主題。
為何不來發點東西呢?", "browsing": "正在瀏覽", "no_replies": "還沒有回覆", "share_this_category": "分享這類別", - "watch": "Watch", + "watch": "觀看", "ignore": "忽略", - "watch.message": "You are now watching updates from this category", - "ignore.message": "You are now ignoring updates from this category" + "watch.message": "您正觀看著此類別的更新", + "ignore.message": "您已忽略此類別的更新" } \ No newline at end of file diff --git a/public/language/zh_TW/error.json b/public/language/zh_TW/error.json index 5fa681cee7..d2f4f0806a 100644 --- a/public/language/zh_TW/error.json +++ b/public/language/zh_TW/error.json @@ -2,7 +2,7 @@ "invalid-data": "無效的資料", "not-logged-in": "您似乎還沒有登入喔!", "account-locked": "您的帳戶暫時被鎖定!", - "search-requires-login": "你需要一個帳戶才能搜索!請登錄或註冊!", + "search-requires-login": "Searching requires an account - please login or register.", "invalid-cid": "無效的類別 ID", "invalid-tid": "無效的主題 ID", "invalid-pid": "無效的文章 ID", @@ -22,10 +22,10 @@ "no-email-to-confirm": "This forum requires email confirmation, please click here to enter an email", "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.", - "username-too-short": "用戶名太短", - "username-too-long": "用戶名太長", + "username-too-short": "使用者名稱太簡短", + "username-too-long": "使用者名稱太長", "user-banned": "該使用者已被停用", - "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", + "user-too-new": "抱歉,發表您第一篇文章須要等待 %1 秒", "no-category": "類別並不存在", "no-topic": "主題並不存在", "no-post": "文章並不存在", @@ -33,7 +33,7 @@ "no-user": "使用者並不存在", "no-teaser": "Teaser 並不存在", "no-privileges": "您似乎沒有執行這個行為的權限!", - "no-emailers-configured": "未加載電郵插件,所以無法發送測試郵件", + "no-emailers-configured": "未載入電子郵件套件,所以無法寄送測試郵件", "category-disabled": "該類別已被關閉", "topic-locked": "該主題已被鎖定", "post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting", @@ -50,10 +50,10 @@ "cant-vote-self-post": "你不能對自己的文章說讚!", "already-favourited": "你已經收藏了這篇文章", "already-unfavourited": "你已放棄收藏這篇文章", - "cant-ban-other-admins": "你不能禁用其他管理員!", - "invalid-image-type": "Invalid image type. Allowed types are: %1", - "invalid-image-extension": "Invalid image extension", - "invalid-file-type": "Invalid file type. Allowed types are: %1", + "cant-ban-other-admins": "您無法禁止其他的管理員!", + "invalid-image-type": "無效的圖像類型。允許的類型:%1", + "invalid-image-extension": "無效的圖像擴充元件", + "invalid-file-type": "無效的檔案類型。允許的類型:%1", "group-name-too-short": "群組名稱太短了", "group-already-exists": "群組名稱已存在", "group-name-change-not-allowed": "變更群組名稱不被允許", @@ -68,6 +68,7 @@ "invalid-file": "無效的檔案", "uploads-are-disabled": "上傳功能被停用", "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", + "about-me-too-long": "Sorry, your about me cannot be longer than %1 character(s).", "cant-chat-with-yourself": "你不能與自己聊天!", "chat-restricted": "此用戶已限制了他的聊天功能。你要在他關注你之後,才能跟他聊天", "too-many-messages": "You have sent too many messages, please wait awhile.", @@ -78,6 +79,6 @@ "reload-failed": "NodeBB重載\"%1\"時遇到了問題。 NodeBB將繼續提供現有的客戶端資源,但請你撤消重載前的動作。", "registration-error": "註冊錯誤", "parse-error": "Something went wrong while parsing server response", - "wrong-login-type-email": "Please use your email to login", - "wrong-login-type-username": "Please use your username to login" + "wrong-login-type-email": "請使用您的電子郵件進行登錄", + "wrong-login-type-username": "請使用您的使用者名稱進行登錄" } \ No newline at end of file diff --git a/public/language/zh_TW/global.json b/public/language/zh_TW/global.json index f0a81aed7a..6014f8c4ac 100644 --- a/public/language/zh_TW/global.json +++ b/public/language/zh_TW/global.json @@ -1,6 +1,6 @@ { "home": "首頁", - "search": "搜索", + "search": "搜尋", "buttons.close": "關閉", "403.title": "禁止存取", "403.message": "你沒有該頁面的存取權限", @@ -30,7 +30,7 @@ "header.groups": "群組", "header.chats": "聊天", "header.notifications": "通知", - "header.search": "搜索", + "header.search": "搜尋", "header.profile": "設置", "notifications.loading": "消息載入中", "chats.loading": "聊天載入中···", diff --git a/public/language/zh_TW/groups.json b/public/language/zh_TW/groups.json index caaab4b118..d4ac57f556 100644 --- a/public/language/zh_TW/groups.json +++ b/public/language/zh_TW/groups.json @@ -1,36 +1,36 @@ { "groups": "群組", "view_group": "查看群組", - "owner": "Group Owner", - "new_group": "Create New Group", - "no_groups_found": "There are no groups to see", - "pending.accept": "Accept", - "pending.reject": "Reject", - "cover-instructions": "Drag and Drop a photo, drag to position, and hit Save", - "cover-change": "Change", - "cover-save": "Save", - "cover-saving": "Saving", + "owner": "群組擁有者", + "new_group": "建立新群組", + "no_groups_found": "這裡看不到任何群組", + "pending.accept": "接受", + "pending.reject": "拒絕", + "cover-instructions": "拖拉一本相簿,並拖到此位置,以及點擊 儲存", + "cover-change": "變更", + "cover-save": "儲存", + "cover-saving": "儲存中", "details.title": "群組詳細信息", "details.members": "成員列表", - "details.pending": "Pending Members", + "details.pending": "待審成員", "details.has_no_posts": "這個群組的成員還未發出任何帖子。", - "details.latest_posts": "最新帖子", - "details.private": "Private", - "details.grant": "Grant/Rescind Ownership", - "details.kick": "Kick", - "details.owner_options": "Group Administration", - "details.group_name": "Group Name", - "details.member_count": "Member Count", - "details.creation_date": "Creation Date", - "details.description": "Description", - "details.badge_preview": "Badge Preview", - "details.change_icon": "Change Icon", - "details.change_colour": "Change Colour", - "details.badge_text": "Badge Text", - "details.userTitleEnabled": "Show Badge", + "details.latest_posts": "最新文章", + "details.private": "私人", + "details.grant": "准許/撤銷 所有權", + "details.kick": "剔除", + "details.owner_options": "群組管理員", + "details.group_name": "群組名稱", + "details.member_count": "成員數", + "details.creation_date": "建立日期", + "details.description": "簡介", + "details.badge_preview": "徽章預覽", + "details.change_icon": "變更圖標", + "details.change_colour": "變更顏色", + "details.badge_text": "徽章字串", + "details.userTitleEnabled": "顯示徽章", "details.private_help": "If enabled, joining of groups requires approval from a group owner", - "details.hidden": "Hidden", + "details.hidden": "隱藏", "details.hidden_help": "If enabled, this group will not be found in the groups listing, and users will have to be invited manually", - "event.updated": "Group details have been updated", - "event.deleted": "The group \"%1\" has been deleted" + "event.updated": "群組詳細訊息已被更新", + "event.deleted": "此 \"%1\" 群組已被刪除了" } \ No newline at end of file diff --git a/public/language/zh_TW/login.json b/public/language/zh_TW/login.json index f93286b21a..4ffc8773e6 100644 --- a/public/language/zh_TW/login.json +++ b/public/language/zh_TW/login.json @@ -1,7 +1,7 @@ { - "username-email": "Username / Email", - "username": "Username", - "email": "Email", + "username-email": "用戶名稱 / 電子郵件", + "username": "用戶名稱", + "email": "電子郵件", "remember_me": "記住我?", "forgot_password": "忘記密碼?", "alternative_logins": "其他登錄方式", diff --git a/public/language/zh_TW/pages.json b/public/language/zh_TW/pages.json index 43825b4004..d004a0f742 100644 --- a/public/language/zh_TW/pages.json +++ b/public/language/zh_TW/pages.json @@ -5,7 +5,7 @@ "recent": "近期的主題", "users": "已註冊的使用者", "notifications": "新訊息通知", - "tags": "Tags", + "tags": "標籤", "tag": "Topics tagged under \"%1\"", "user.edit": "編輯中 \"%1\"", "user.following": "People %1 Follows", @@ -15,7 +15,7 @@ "user.groups": "%1 的群組", "user.favourites": "%1's 最喜愛的文章", "user.settings": "使用者設定", - "user.watched": "Topics watched by %1", - "maintenance.text": "%1目前正在進行維修。請稍後再來。", - "maintenance.messageIntro": "此外,管理員有以下信息:" + "user.watched": "主題由 %1 所觀看", + "maintenance.text": "目前 %1 正在進行維修。請稍後再來。", + "maintenance.messageIntro": "此外,管理員有以下訊息:" } \ No newline at end of file diff --git a/public/language/zh_TW/recent.json b/public/language/zh_TW/recent.json index 476bc25844..cf46a366d3 100644 --- a/public/language/zh_TW/recent.json +++ b/public/language/zh_TW/recent.json @@ -1,12 +1,12 @@ { "title": "最近", - "day": "今日", - "week": "本周", - "month": "本月", - "year": "全年", + "day": "日", + "week": "周", + "month": "月", + "year": "年", "alltime": "所有時間", - "no_recent_topics": "最近沒新主題.", - "no_popular_topics": "There are no popular topics.", + "no_recent_topics": "最近沒有新的主題。", + "no_popular_topics": "最近沒有受歡迎的主題。", "there-is-a-new-topic": "There is a new topic.", "there-is-a-new-topic-and-a-new-post": "There is a new topic and a new post.", "there-is-a-new-topic-and-new-posts": "There is a new topic and %1 new posts.", @@ -15,5 +15,5 @@ "there-are-new-topics-and-new-posts": "There are %1 new topics and %2 new posts.", "there-is-a-new-post": "There is a new post.", "there-are-new-posts": "There are %1 new posts.", - "click-here-to-reload": "Click here to reload." + "click-here-to-reload": "點擊這裡進行重整。" } \ No newline at end of file diff --git a/public/language/zh_TW/reset_password.json b/public/language/zh_TW/reset_password.json index af7cbce277..5c2964579c 100644 --- a/public/language/zh_TW/reset_password.json +++ b/public/language/zh_TW/reset_password.json @@ -7,10 +7,10 @@ "wrong_reset_code.message": "您輸入的驗証碼有誤,請重新輸入,或申請新的驗証碼。", "new_password": "輸入新的密碼", "repeat_password": "再次確認新密碼", - "enter_email": "請輸入您的Email地址,我們會發送郵件告訴您如何重設密碼。", - "enter_email_address": "輸入郵箱地址", + "enter_email": "請輸入您的電子郵件地址,我們會寄送郵件告訴您如何重設密碼。", + "enter_email_address": "輸入電子郵件地址", "password_reset_sent": "密碼重設郵件已發送。", - "invalid_email": "非法的郵箱地址/郵箱不存在!", + "invalid_email": "無效的電子郵件 / 電子郵件不存在!", "password_too_short": "The password entered is too short, please pick a different password.", "passwords_do_not_match": "The two passwords you've entered do not match.", "password_expired": "Your password has expired, please choose a new password" diff --git a/public/language/zh_TW/topic.json b/public/language/zh_TW/topic.json index a65ccc6833..71c592aea9 100644 --- a/public/language/zh_TW/topic.json +++ b/public/language/zh_TW/topic.json @@ -5,6 +5,7 @@ "no_topics_found": "沒有找到主題!", "no_posts_found": "找不到文章!", "post_is_deleted": "文章已被刪除!", + "topic_is_deleted": "This topic is deleted!", "profile": "資料", "posted_by": "由 %1 張貼", "posted_by_guest": "由訪客張貼", diff --git a/public/language/zh_TW/user.json b/public/language/zh_TW/user.json index a177bfb876..7296da0c9f 100644 --- a/public/language/zh_TW/user.json +++ b/public/language/zh_TW/user.json @@ -3,32 +3,33 @@ "offline": "下線", "username": "使用者名稱", "joindate": "加入時間", - "postcount": "Post數量", - "email": "Email", + "postcount": "文章數量", + "email": "電子郵件", "confirm_email": "確認電郵", "delete_account": "刪除帳戶", - "delete_account_confirm": "你確定要刪除自己的帳戶?
此操作不能復原,您將無法恢復任何數據的

輸入您的用戶名,以確認您希望刪除這個帳戶。", - "fullname": "姓名", + "delete_account_confirm": "你確定要刪除自己的帳戶?
此操作不能復原,您將無法恢復任何數據

輸入您的使用者名稱,以確認您希望刪除這個帳戶。", + "fullname": "全名", "website": "網站", "location": "地址", "age": "年齡", "joined": "加入時間", - "lastonline": "最后在線", + "lastonline": "最後上線", "profile": "個人資料", "profile_views": "資料被查看", - "reputation": "聲望", + "reputation": "聲譽", "favourites": "我的最愛", - "watched": "Watched", + "watched": "觀看者", "followers": "跟隨者", "following": "正在關注", + "aboutme": "About me", "signature": "簽名", "gravatar": "Gravatar頭像", "birthday": "生日", "chat": "聊天", - "follow": "關注", - "unfollow": "取消關注", - "more": "More", - "profile_update_success": "您的個人資料已更新成功!", + "follow": "跟隨", + "unfollow": "取消跟隨", + "more": "更多", + "profile_update_success": "您的個人資料已更新成功!", "change_picture": "改變頭像", "edit": "編輯", "uploaded_picture": "已有頭像", @@ -79,5 +80,5 @@ "follow_topics_you_reply_to": "Follow topics that you reply to", "follow_topics_you_create": "Follow topics you create", "grouptitle": "Select the group title you would like to display", - "no-group-title": "No group title" + "no-group-title": "無此群組標題" } \ No newline at end of file diff --git a/public/language/zh_TW/users.json b/public/language/zh_TW/users.json index b3c22fb165..34f7db93c9 100644 --- a/public/language/zh_TW/users.json +++ b/public/language/zh_TW/users.json @@ -5,8 +5,8 @@ "search": "搜尋", "enter_username": "輸入想找的使用者帳號", "load_more": "載入更多", - "users-found-search-took": "%1 user(s) found! Search took %2 seconds.", + "users-found-search-took": "發現 %1 用戶!搜尋只用 %2 秒。", "filter-by": "Filter By", - "online-only": "Online only", - "picture-only": "Picture only" + "online-only": "線上僅有", + "picture-only": "圖片僅有" } \ No newline at end of file diff --git a/public/less/admin/admin.less b/public/less/admin/admin.less index 40ddcf08e3..c316ae6e19 100644 --- a/public/less/admin/admin.less +++ b/public/less/admin/admin.less @@ -1,9 +1,10 @@ @import "./bootstrap/bootstrap"; @import "./mixins"; +@import "./vars"; @import "./general/dashboard"; @import "./general/navigation"; -@import "./manage/groups"; +@import "./manage/categories"; @import "./manage/tags"; @import "./manage/flags"; @import "./manage/users"; @@ -25,6 +26,10 @@ padding: 0px 15px; } + .btn { + border-radius: 0; + } + .user-img { width:24px; height:24px; @@ -135,10 +140,6 @@ padding-left: 11px; outline: 0; - &:focus { - background-color: transparent; - } - span { opacity: 0; margin-right: -8px; @@ -169,6 +170,12 @@ overflow-y: hidden; } + .acp-panel-heading { + padding: 7px 14px; + border: 0; + .box-header-font; + } + .panel { background-color: #FFF; box-sizing: border-box; @@ -177,11 +184,13 @@ margin-bottom: 20px; &.panel-default .panel-heading { + .acp-panel-heading; background: #fefefe; color: #333; - padding: 7px 14px; - border: 0; - .box-header-font; + } + + &.panel-danger .panel-heading { + .acp-panel-heading; } } @@ -283,6 +292,11 @@ #taskbar { display: none; /* not sure why I have to do this, but it only seems to show up on prod */ } + + /* Allows the autocomplete dropbox to appear on top of a modal's backdrop */ + .ui-autocomplete { + z-index: @zindex-popover; + } } // Allowing text to the right of an image-type brand @@ -345,3 +359,9 @@ max-height: 24px; } } + +.groups-list { + .description { + font-size: 1rem; + } +} diff --git a/public/less/admin/manage/categories.less b/public/less/admin/manage/categories.less new file mode 100644 index 0000000000..acc730e081 --- /dev/null +++ b/public/less/admin/manage/categories.less @@ -0,0 +1,71 @@ +div.categories { + + ul { + .no-select; + list-style-type: none; + margin: 0; + padding: 0; + + > li > ul > li { + margin-left: 4.5rem; + } + } + + .stats { + display: inline-block; + + li { + min-height: 0; + display: inline; + margin: 0 @acp-margin 0 0; + left: 0; + } + } + + li { + min-height: @acp-line-height; + margin: @acp-base-line 0; + + &.placeholder { + border: 1px dashed #2196F3; + background-color: #E1F5FE; + } + } + + .disabled { + + .icon, .header, .description { + opacity: 0.5; + } + + .stats { + opacity: 0.3; + } + } + + .icon { + width: @acp-line-height; + height: @acp-line-height; + border-radius: 50%; + line-height: @acp-line-height; + text-align: center; + vertical-align: bottom; + background-size: cover; + float: left; + margin-right: @acp-margin; + cursor: move; + } + + .information { + float: left; + } + + .header { + margin-top: 0; + margin-bottom: @acp-base-line; + } + + .description { + margin: 0; + } +} \ No newline at end of file diff --git a/public/less/admin/manage/groups.less b/public/less/admin/manage/groups.less deleted file mode 100644 index 03f42694d9..0000000000 --- a/public/less/admin/manage/groups.less +++ /dev/null @@ -1,54 +0,0 @@ -.groups { - #groups-list { - padding-left: 0; - - > li { - list-style-type: none; - margin-bottom: 1em; - padding: 1em; - .zebra; - - h2 { - margin-top: 0; - font-size: 26px; - } - } - } - - .members { - padding: 1em; - - &.current_members { - li { - margin-right: 1em; - } - } - - li { - display: inline-block; - border: 1px solid rgb(200, 200, 200); - - img { - width: 32px; - } - - span { - padding: 0 1em; - } - - &:hover { - .pointer; - background: rgba(192, 192, 192, 0.2); - } - - &.more { - width: 34px; - height: 34px; - vertical-align: top; - padding-top: 3px; - position: relative; - left: -4px; - } - } - } -} \ No newline at end of file diff --git a/public/less/admin/manage/tags.less b/public/less/admin/manage/tags.less index f26fa00327..e034d3b3f6 100644 --- a/public/less/admin/manage/tags.less +++ b/public/less/admin/manage/tags.less @@ -31,7 +31,6 @@ .tag-topic-count { border: solid 1px lighten(@brand-primary, 20%); background-color: lighten(@brand-primary, 20%); - color: #FFFFFF; padding: .2em .6em .3em; font-size: 75%; font-weight: 700; @@ -41,5 +40,9 @@ padding-left: 5px; border-bottom-right-radius: 5px; border-top-right-radius: 5px; + + a { + color: #FFFFFF; + } } } \ No newline at end of file diff --git a/public/less/admin/vars.less b/public/less/admin/vars.less new file mode 100644 index 0000000000..8e7bc2bb30 --- /dev/null +++ b/public/less/admin/vars.less @@ -0,0 +1,3 @@ +@acp-base-line: 8px; +@acp-line-height: @acp-base-line * 6; +@acp-margin: @acp-base-line * 2; \ No newline at end of file diff --git a/public/less/generics.less b/public/less/generics.less index e44d5f0d72..4ce04c4337 100644 --- a/public/less/generics.less +++ b/public/less/generics.less @@ -13,4 +13,31 @@ .opacity(0.5); } } +} + +.user-list { + padding-left: 2rem; + padding-top: 1rem; + + li { + .pointer; + display: inline-block; + list-style-type: none; + padding: 0.5rem 1rem; + + &:hover { + background: @gray-lighter; + } + + img { + float: left; + max-width: 36px; + padding-right: 1rem; + } + + span { + vertical-align: middle; + display: inline-block; + } + } } \ No newline at end of file diff --git a/public/src/admin/admin.js b/public/src/admin/admin.js index f7b7817591..fe411adeb3 100644 --- a/public/src/admin/admin.js +++ b/public/src/admin/admin.js @@ -22,6 +22,8 @@ setupRestartLinks(); }); + $('[component="logout"]').on('click', app.logout); + $(window).resize(setupHeaderMenu); }); @@ -63,8 +65,9 @@ function setupKeybindings() { Mousetrap.bind('ctrl+shift+a r', function() { - console.log('[admin] Reloading NodeBB...'); - socket.emit('admin.reload'); + require(['admin/modules/instance'], function(instance) { + instance.reload(); + }); }); Mousetrap.bind('ctrl+shift+a R', function() { @@ -161,55 +164,16 @@ $('.restart').off('click').on('click', function() { bootbox.confirm('Are you sure you wish to restart NodeBB?', function(confirm) { if (confirm) { - app.alert({ - alert_id: 'instance_restart', - type: 'info', - title: 'Restarting... ', - message: 'NodeBB is restarting.', - timeout: 5000 + require(['admin/modules/instance'], function(instance) { + instance.restart(); }); - - $(window).one('action:reconnected', function() { - app.alert({ - alert_id: 'instance_restart', - type: 'success', - title: ' Success', - message: 'NodeBB has successfully restarted.', - timeout: 5000 - }); - }); - - socket.emit('admin.restart'); } }); }); $('.reload').off('click').on('click', function() { - app.alert({ - alert_id: 'instance_reload', - type: 'info', - title: 'Reloading... ', - message: 'NodeBB is reloading.', - timeout: 5000 - }); - - socket.emit('admin.reload', function(err) { - if (!err) { - app.alert({ - alert_id: 'instance_reload', - type: 'success', - title: ' Success', - message: 'NodeBB has successfully reloaded.', - timeout: 5000 - }); - } else { - app.alert({ - alert_id: 'instance_reload', - type: 'danger', - title: '[[global:alert.error]]', - message: '[[error:reload-failed, ' + err.message + ']]' - }); - } + require(['admin/modules/instance'], function(instance) { + instance.reload(); }); }); } diff --git a/public/src/admin/appearance/skins.js b/public/src/admin/appearance/skins.js index 374b88a7b4..1a24b80ef5 100644 --- a/public/src/admin/appearance/skins.js +++ b/public/src/admin/appearance/skins.js @@ -33,7 +33,7 @@ define('admin/appearance/skins', function() { alert_id: 'admin:theme', type: 'info', title: 'Skin Updated', - message: themeId + ' skin was successfully applied', + message: themeId ? (themeId + ' skin was successfully applied') : 'Skin reverted to base colours', timeout: 5000 }); }); diff --git a/public/src/admin/extend/plugins.js b/public/src/admin/extend/plugins.js index e2484b1f07..4987d87552 100644 --- a/public/src/admin/extend/plugins.js +++ b/public/src/admin/extend/plugins.js @@ -27,7 +27,9 @@ define('admin/extend/plugins', function() { type: status.active ? 'warning' : 'success', timeout: 5000, clickfn: function() { - socket.emit('admin.reload'); + require(['admin/modules/instance'], function(instance) { + instance.reload(); + }); } }); }); @@ -160,7 +162,9 @@ define('admin/extend/plugins', function() { type: 'warning', timeout: 5000, clickfn: function() { - socket.emit('admin.reload'); + require(['admin/modules/instance'], function(instance) { + instance.reload(); + }); } }); } diff --git a/public/src/admin/extend/widgets.js b/public/src/admin/extend/widgets.js index b57453ffab..4e8d3160aa 100644 --- a/public/src/admin/extend/widgets.js +++ b/public/src/admin/extend/widgets.js @@ -58,9 +58,9 @@ define('admin/extend/widgets', function() { panel.remove(); } }); - }).on('mouseup', '.panel-heading', function(evt) { - if ( !( $(this).parents('.widget-panel').is('.ui-sortable-helper') || $(evt.target).closest('.delete-widget').length ) ) { - $(this).parents('.widget-panel').children('.panel-body').toggleClass('hidden'); + }).on('mouseup', '> .panel > .panel-heading', function(evt) { + if ( !( $(this).parent().is('.ui-sortable-helper') || $(evt.target).closest('.delete-widget').length ) ) { + $(this).parent().children('.panel-body').toggleClass('hidden'); } }); diff --git a/public/src/admin/general/dashboard.js b/public/src/admin/general/dashboard.js index 6b7cdd5c28..8e82b33058 100644 --- a/public/src/admin/general/dashboard.js +++ b/public/src/admin/general/dashboard.js @@ -31,8 +31,6 @@ define('admin/general/dashboard', ['semver'], function(semver) { usedTopicColors.length = 0; }); - $('[component="logout"]').on('click', app.logout); - $.get('https://api.github.com/repos/NodeBB/NodeBB/tags', function(releases) { // Re-sort the releases, as they do not follow Semver (wrt pre-releases) releases = releases.sort(function(a, b) { @@ -60,6 +58,7 @@ define('admin/general/dashboard', ['semver'], function(semver) { }); setupGraphs(); + $('[data-toggle="tooltip"]').tooltip(); }; Admin.updateRoomUsage = function(err, data) { diff --git a/public/src/admin/general/navigation.js b/public/src/admin/general/navigation.js index c98b9fa7be..e725e39ad1 100644 --- a/public/src/admin/general/navigation.js +++ b/public/src/admin/general/navigation.js @@ -49,18 +49,31 @@ define('admin/general/navigation', ['translator'], function(translator) { $('#enabled li').each(function() { var form = $(this).find('form').serializeArray(), - data = {}; + data = {}, + properties = {}; form.forEach(function(input) { - data[input.name] = translator.escape(input.value); + if (input.name.slice(0, 9) === 'property:' && input.value === 'on') { + properties[input.name.slice(9)] = true; + } else { + data[input.name] = translator.escape(input.value); + } }); + data.properties = {}; + available.forEach(function(item) { if (item.route.match(data.route)) { - data.properties = item.properties; + data.properties = item.properties || {}; } }); + for (var prop in properties) { + if (properties.hasOwnProperty(prop)) { + data.properties[prop] = properties[prop]; + } + } + nav.push(data); }); diff --git a/public/src/admin/manage/categories.js b/public/src/admin/manage/categories.js index 535d902dfe..6841702093 100644 --- a/public/src/admin/manage/categories.js +++ b/public/src/admin/manage/categories.js @@ -1,53 +1,29 @@ "use strict"; -/*global define, socket, app, bootbox, templates, ajaxify, RELATIVE_PATH*/ +/*global define, socket, app, bootbox, templates, ajaxify, RELATIVE_PATH, Sortable */ define('admin/manage/categories', function() { - var Categories = {}; + var Categories = {}, newCategoryId = -1, sortables; Categories.init = function() { - var bothEl = $('#active-categories, #disabled-categories'); - - function updateCategoryOrders(evt, ui) { - var categories = $(evt.target).children(), - modified = {}, - cid; - - for(var i=0;i') + .addClass('alert alert-info text-center') + .text('You have no active categories.') + .appendTo(container); + } else { + sortables = {}; + renderList(categories, container, 0); + } + }; + + Categories.toggle = function(cid, state) { + var payload = {}; + + payload[cid] = { + disabled: !state | 0 + }; + + socket.emit('admin.categories.update', payload, function(err, result) { + if (err) { + return app.alertError(err.message); + } else { + ajaxify.refresh(); + } + }); + } + + function itemDidAdd(e){ + newCategoryId = e.to.dataset.cid; + } + + function itemDragDidEnd(e){ + var isCategoryUpdate = (newCategoryId != -1); + //Update needed? + if((e.newIndex != undefined && e.oldIndex != e.newIndex) || isCategoryUpdate){ + var parentCategory = isCategoryUpdate ? sortables[newCategoryId] : sortables[e.from.dataset.cid], + modified = {}, i = 0, list = parentCategory.toArray(), len = list.length; + + for(i; i < len; ++i) { + modified[list[i]] = { + order: (i + 1) + } + } + + if(isCategoryUpdate){ + modified[e.item.dataset.cid]['parentCid'] = newCategoryId; + } + + newCategoryId = -1 + socket.emit('admin.categories.update', modified); + } + } + + /** + * Render categories - recursively + * + * @param categories {array} categories tree + * @param level {number} current sub-level of rendering + * @param container {object} parent jquery element for the list + * @param parentId {number} parent category identifier + */ + function renderList(categories, container, parentId){ + templates.parse('admin/partials/categories/category-rows', { + cid: parentId, + categories: categories + }, function(html) { + container.append(html); + + // Handle and children categories in this level have + for(var x=0,numCategories=categories.length;x 0) { + renderList(categories[x].children, $('li[data-cid="' + categories[x].cid + '"]'), categories[x].cid); + } + } + + // Make list sortable + sortables[parentId] = Sortable.create($('ul[data-cid="' + parentId + '"]')[0], { + group: 'cross-categories', + animation: 150, + handle: '.icon', + dataIdAttr: 'data-cid', + ghostClass: "placeholder", + onAdd: itemDidAdd, + onEnd: itemDragDidEnd + }); + }); + } + return Categories; }); \ No newline at end of file diff --git a/public/src/admin/manage/category.js b/public/src/admin/manage/category.js index 238317144c..ac4b6a50cc 100644 --- a/public/src/admin/manage/category.js +++ b/public/src/admin/manage/category.js @@ -158,26 +158,6 @@ define('admin/manage/category', [ }; Category.setupPrivilegeTable = function() { - var searchEl = $('.privilege-search'), - searchObj = autocomplete.user(searchEl); - - // User search + addition to table - searchObj.on('autocompleteselect', function(ev, ui) { - socket.emit('admin.categories.setPrivilege', { - cid: ajaxify.variables.get('cid'), - privilege: 'read', - set: true, - member: ui.item.user.uid - }, function(err) { - if (err) { - return app.alertError(err.message); - } - - Category.refreshPrivilegeTable(); - searchEl.val(''); - }); - }); - // Checkbox event capture $('.privilege-table-container').on('change', 'input[type="checkbox"]', function() { var checkboxEl = $(this), @@ -205,6 +185,9 @@ define('admin/manage/category', [ } }); + $('.privilege-table-container').on('click', '[data-action="search.user"]', Category.addUserToPrivilegeTable); + $('.privilege-table-container').on('click', '[data-action="search.group"]', Category.addGroupToPrivilegeTable); + Category.exposeAssumedPrivileges(); }; @@ -292,5 +275,61 @@ define('admin/manage/category', [ }); }; + Category.addUserToPrivilegeTable = function() { + var modal = bootbox.dialog({ + title: 'Find a User', + message: '', + show: true + }); + + modal.on('shown.bs.modal', function() { + var inputEl = modal.find('input'); + + autocomplete.user(inputEl, function(ev, ui) { + socket.emit('admin.categories.setPrivilege', { + cid: ajaxify.variables.get('cid'), + privilege: ['find', 'read'], + set: true, + member: ui.item.user.uid + }, function(err) { + if (err) { + return app.alertError(err.message); + } + + Category.refreshPrivilegeTable(); + modal.modal('hide'); + }); + }); + }); + }; + + Category.addGroupToPrivilegeTable = function() { + var modal = bootbox.dialog({ + title: 'Find a Group', + message: '', + show: true + }); + + modal.on('shown.bs.modal', function() { + var inputEl = modal.find('input'); + + autocomplete.group(inputEl, function(ev, ui) { + socket.emit('admin.categories.setPrivilege', { + cid: ajaxify.variables.get('cid'), + privilege: ['groups:find', 'groups:read'], + set: true, + member: ui.item.group.name + }, function(err) { + if (err) { + return app.alertError(err.message); + } + + Category.refreshPrivilegeTable(); + modal.modal('hide'); + }); + }); + }); + }; + return Category; }); \ No newline at end of file diff --git a/public/src/admin/manage/group.js b/public/src/admin/manage/group.js new file mode 100644 index 0000000000..08feeb726c --- /dev/null +++ b/public/src/admin/manage/group.js @@ -0,0 +1,144 @@ +"use strict"; +/*global define, templates, socket, ajaxify, app, admin, bootbox, utils, config */ + +define('admin/manage/group', [ + 'iconSelect', + 'admin/modules/colorpicker' +], function(iconSelect, colorpicker) { + var Groups = {}; + + Groups.init = function() { + var groupDetailsSearch = $('#group-details-search'), + groupDetailsSearchResults = $('#group-details-search-results'), + groupMembersEl = $('ul.current_members'), + groupIcon = $('#group-icon'), + changeGroupUserTitle = $('#change-group-user-title'), + changeGroupLabelColor = $('#change-group-label-color'), + groupLabelPreview = $('#group-label-preview'), + searchDelay; + + + var groupName = ajaxify.variables.get('groupName'); + + changeGroupUserTitle.keyup(function() { + groupLabelPreview.text(changeGroupUserTitle.val()); + }); + + changeGroupLabelColor.keyup(function() { + groupLabelPreview.css('background', changeGroupLabelColor.val() || '#000000'); + }); + + groupDetailsSearch.on('keyup', function() { + + if (searchDelay) { + clearTimeout(searchDelay); + } + + searchDelay = setTimeout(function() { + var searchText = groupDetailsSearch.val(), + foundUser; + + socket.emit('admin.user.search', {query: searchText}, function(err, results) { + if (!err && results && results.users.length > 0) { + var numResults = results.users.length, x; + if (numResults > 20) { + numResults = 20; + } + + groupDetailsSearchResults.empty(); + for (x = 0; x < numResults; x++) { + foundUser = $('

  • '); + foundUser + .attr({title: results.users[x].username, 'data-uid': results.users[x].uid}) + .append($('').attr('src', results.users[x].picture)) + .append($('').html(results.users[x].username)); + + groupDetailsSearchResults.append(foundUser); + } + } else { + groupDetailsSearchResults.html('
  • No Users Found
  • '); + } + }); + }, 200); + }); + + groupDetailsSearchResults.on('click', 'li[data-uid]', function() { + var userLabel = $(this), + uid = parseInt(userLabel.attr('data-uid'), 10), + members = []; + + groupMembersEl.find('li[data-uid]').each(function() { + members.push(parseInt($(this).attr('data-uid'), 10)); + }); + + if (members.indexOf(uid) === -1) { + socket.emit('admin.groups.join', { + groupName: groupName, + uid: uid + }, function(err, data) { + if (!err) { + groupMembersEl.append(userLabel.clone(true)); + } + }); + } + }); + + groupMembersEl.on('click', 'li[data-uid]', function() { + var uid = $(this).attr('data-uid'); + + socket.emit('admin.groups.get', groupName, function(err, groupObj){ + if (err) { + return app.alertError(err.message); + } + + bootbox.confirm('Are you sure you want to remove this user?', function(confirm) { + if (!confirm) { + return; + } + + socket.emit('admin.groups.leave', { + groupName: groupName, + uid: uid + }, function(err, data) { + if (err) { + return app.alertError(err.message); + } + groupMembersEl.find('li[data-uid="' + uid + '"]').remove(); + }); + }); + }); + }); + + $('#group-icon').on('click', function() { + iconSelect.init(groupIcon); + }); + + colorpicker.enable(changeGroupLabelColor, function(hsb, hex) { + groupLabelPreview.css('background-color', '#' + hex); + }); + + $('.save').on('click', function() { + socket.emit('admin.groups.update', { + groupName: groupName, + values: { + name: $('#change-group-name').val(), + userTitle: changeGroupUserTitle.val(), + description: $('#change-group-desc').val(), + icon: groupIcon.attr('value'), + labelColor: changeGroupLabelColor.val(), + private: $('#group-private').is(':checked'), + hidden: $('#group-hidden').is(':checked') + } + }, function(err) { + if (err) { + return app.alertError(err.message); + } + app.alertSuccess('Changes saved!'); + }); + return false; + }); + + }; + + return Groups; +}); diff --git a/public/src/admin/manage/groups.js b/public/src/admin/manage/groups.js index f604bc9387..2bf84d4f18 100644 --- a/public/src/admin/manage/groups.js +++ b/public/src/admin/manage/groups.js @@ -2,32 +2,16 @@ /*global define, templates, socket, ajaxify, app, admin, bootbox, utils, config */ define('admin/manage/groups', [ - 'iconSelect', - 'admin/modules/colorpicker', - 'translator' -], function(iconSelect, colorpicker, translator) { + 'translator', + 'components' +], function(translator, components) { var Groups = {}; Groups.init = function() { var createModal = $('#create-modal'), createGroupName = $('#create-group-name'), createModalGo = $('#create-modal-go'), - createModalError = $('#create-modal-error'), - groupDetailsModal = $('#group-details-modal'), - groupDetailsSearch = $('#group-details-search'), - groupDetailsSearchResults = $('#group-details-search-results'), - groupMembersEl = $('ul.current_members'), - detailsModalSave = $('#details-modal-save'), - groupIcon = $('#group-icon'), - changeGroupName = $('#change-group-name'), - changeGroupDesc = $('#change-group-desc'), - changeGroupUserTitle = $('#change-group-user-title'), - changeGroupLabelColor = $('#change-group-label-color'), - groupLabelPreview = $('#group-label-preview'), - searchDelay; - - // Tooltips - $('#groups-list .members li').tooltip(); + createModalError = $('#create-modal-error'); createModal.on('keypress', function(e) { if (e.keyCode === 13) { @@ -69,28 +53,10 @@ define('admin/manage/groups', [ }); }); - groupDetailsModal.find('form').keypress(function(e) { - if (e.keyCode === 13) { - detailsModalSave.click(); - } - }); - - changeGroupUserTitle.keydown(function() { - setTimeout(function() { - groupLabelPreview.text(changeGroupUserTitle.val()); - }, 0); - }); - - changeGroupLabelColor.keydown(function() { - setTimeout(function() { - groupLabelPreview.css('background', changeGroupLabelColor.val() || '#000000'); - }, 0); - }); - - $('#groups-list').on('click', 'button[data-action]', function() { + $('.groups-list').on('click', 'button[data-action]', function() { var el = $(this), action = el.attr('data-action'), - groupName = el.parents('li[data-groupname]').attr('data-groupname'); + groupName = el.parents('tr[data-groupname]').attr('data-groupname'); switch (action) { case 'delete': @@ -108,140 +74,9 @@ define('admin/manage/groups', [ } }); break; - case 'members': - socket.emit('admin.groups.get', groupName, function(err, groupObj) { - - changeGroupName.val(groupObj.name).prop('readonly', groupObj.system); - changeGroupDesc.val(groupObj.description); - changeGroupUserTitle.val(groupObj.userTitle); - groupIcon.attr('class', 'fa fa-2x ' + groupObj.icon).attr('value', groupObj.icon); - changeGroupLabelColor.val(groupObj.labelColor); - groupLabelPreview.css('background', groupObj.labelColor || '#000000').text(groupObj.userTitle); - groupMembersEl.empty(); - - if (groupObj.members.length > 0) { - for (var x = 0; x < groupObj.members.length; x++) { - var memberIcon = $('
  • ') - .attr('data-uid', groupObj.members[x].uid) - .append($('').attr('src', groupObj.members[x].picture)) - .append($('').html(groupObj.members[x].username)); - groupMembersEl.append(memberIcon); - } - } - - groupDetailsModal.attr('data-groupname', groupObj.name); - groupDetailsModal.modal('show'); - }); - break; } }); - groupDetailsSearch.on('keyup', function() { - - if (searchDelay) { - clearTimeout(searchDelay); - } - - searchDelay = setTimeout(function() { - var searchText = groupDetailsSearch.val(), - foundUser; - - socket.emit('admin.user.search', {query: searchText}, function(err, results) { - if (!err && results && results.users.length > 0) { - var numResults = results.users.length, x; - if (numResults > 20) { - numResults = 20; - } - - groupDetailsSearchResults.empty(); - for (x = 0; x < numResults; x++) { - foundUser = $('
  • '); - foundUser - .attr({title: results.users[x].username, 'data-uid': results.users[x].uid}) - .append($('').attr('src', results.users[x].picture)) - .append($('').html(results.users[x].username)); - - groupDetailsSearchResults.append(foundUser); - } - } else { - groupDetailsSearchResults.html('
  • No Users Found
  • '); - } - }); - }, 200); - }); - - groupDetailsSearchResults.on('click', 'li[data-uid]', function() { - var userLabel = $(this), - uid = parseInt(userLabel.attr('data-uid'), 10), - groupName = groupDetailsModal.attr('data-groupname'), - members = []; - - groupMembersEl.find('li[data-uid]').each(function() { - members.push(parseInt($(this).attr('data-uid'), 10)); - }); - - if (members.indexOf(uid) === -1) { - socket.emit('admin.groups.join', { - groupName: groupName, - uid: uid - }, function(err, data) { - if (!err) { - groupMembersEl.append(userLabel.clone(true)); - } - }); - } - }); - - groupMembersEl.on('click', 'li[data-uid]', function() { - var uid = $(this).attr('data-uid'), - groupName = groupDetailsModal.attr('data-groupname'); - - socket.emit('admin.groups.get', groupName, function(err, groupObj){ - if (!err){ - bootbox.confirm('Are you sure you want to remove this user?', function(confirm) { - if (confirm){ - socket.emit('admin.groups.leave', { - groupName: groupName, - uid: uid - }, function(err, data) { - if (!err) { - groupMembersEl.find('li[data-uid="' + uid + '"]').remove(); - } - }); - } - }); - } - }); - }); - - $('#change-group-icon').on('click', function() { - iconSelect.init(groupIcon); - }); - - colorpicker.enable(changeGroupLabelColor, function(hsb, hex) { - groupLabelPreview.css('background-color', '#' + hex); - }); - - detailsModalSave.on('click', function() { - socket.emit('admin.groups.update', { - groupName: groupDetailsModal.attr('data-groupname'), - values: { - name: changeGroupName.val(), - userTitle: changeGroupUserTitle.val(), - description: changeGroupDesc.val(), - icon: groupIcon.attr('value'), - labelColor: changeGroupLabelColor.val() - } - }, function(err) { - if (!err) { - groupDetailsModal.on('hidden.bs.modal', function() { - ajaxify.go('admin/manage/groups'); - }); - groupDetailsModal.modal('hide'); - } - }); - }); - }; return Groups; diff --git a/public/src/admin/manage/registration.js b/public/src/admin/manage/registration.js new file mode 100644 index 0000000000..0592fc02a9 --- /dev/null +++ b/public/src/admin/manage/registration.js @@ -0,0 +1,28 @@ +"use strict"; + +/* global config, socket, define, templates, bootbox, app, ajaxify, */ + +define('admin/manage/registration', function() { + var Registration = {}; + + Registration.init = function() { + + $('.users-list').on('click', '[data-action]', function(ev) { + var $this = this; + var parent = $(this).parents('[data-username]'); + var action = $(this).attr('data-action'); + var username = parent.attr('data-username'); + var method = action === 'accept' ? 'admin.user.acceptRegistration' : 'admin.user.rejectRegistration'; + + socket.emit(method, {username: username}, function(err) { + if (err) { + return app.alertError(err.message); + } + parent.remove(); + }); + return false; + }); + }; + + return Registration; +}); diff --git a/public/src/admin/manage/users.js b/public/src/admin/manage/users.js index 5909522f8c..09137c161a 100644 --- a/public/src/admin/manage/users.js +++ b/public/src/admin/manage/users.js @@ -250,7 +250,7 @@ define('admin/manage/users', ['admin/modules/selectable'], function(selectable) timeoutId = setTimeout(function() { $('.fa-spinner').removeClass('hidden'); - socket.emit('admin.user.search', {searchBy: [type], query: $this.val()}, function(err, data) { + socket.emit('admin.user.search', {searchBy: type, query: $this.val()}, function(err, data) { if (err) { return app.alertError(err.message); } diff --git a/public/src/admin/modules/instance.js b/public/src/admin/modules/instance.js new file mode 100644 index 0000000000..edad60b80e --- /dev/null +++ b/public/src/admin/modules/instance.js @@ -0,0 +1,68 @@ +"use strict"; + +/*globals define, app, socket*/ + +define('admin/modules/instance', function() { + var instance = {}; + + instance.reload = function(callback) { + app.alert({ + alert_id: 'instance_reload', + type: 'info', + title: 'Reloading... ', + message: 'NodeBB is reloading.', + timeout: 5000 + }); + + socket.emit('admin.reload', function(err) { + if (!err) { + app.alert({ + alert_id: 'instance_reload', + type: 'success', + title: ' Success', + message: 'NodeBB has successfully reloaded.', + timeout: 5000 + }); + } else { + app.alert({ + alert_id: 'instance_reload', + type: 'danger', + title: '[[global:alert.error]]', + message: '[[error:reload-failed, ' + err.message + ']]' + }); + } + + if (typeof callback === 'function') { + callback(); + } + }); + }; + + instance.restart = function(callback) { + app.alert({ + alert_id: 'instance_restart', + type: 'info', + title: 'Restarting... ', + message: 'NodeBB is restarting.', + timeout: 5000 + }); + + $(window).one('action:reconnected', function() { + app.alert({ + alert_id: 'instance_restart', + type: 'success', + title: ' Success', + message: 'NodeBB has successfully restarted.', + timeout: 5000 + }); + + if (typeof callback === 'function') { + callback(); + } + }); + + socket.emit('admin.restart'); + }; + + return instance; +}); diff --git a/public/src/ajaxify.js b/public/src/ajaxify.js index dc1845f99e..436a8249a1 100644 --- a/public/src/ajaxify.js +++ b/public/src/ajaxify.js @@ -30,7 +30,7 @@ $(document).ready(function() { ajaxify.currentPage = null; - ajaxify.go = function (url, callback, quiet) { + ajaxify.go = function (url, callback, quiet, search) { if (ajaxify.handleRedirects(url)) { return true; } @@ -43,7 +43,7 @@ $(document).ready(function() { apiXHR.abort(); } - url = ajaxify.start(url, quiet); + url = ajaxify.start(url, quiet, search); $('#footer, #content').removeClass('hide').addClass('ajaxifying'); @@ -61,12 +61,6 @@ $(document).ready(function() { }); }); - require(['search'], function(search) { - if (search.topicDOM.active && !url.startsWith('topic/')) { - search.topicDOM.end(); - } - }); - return true; }; @@ -150,13 +144,18 @@ $(document).ready(function() { } ajaxify.end = function(url, tpl_url) { + function done() { + if (--count === 0) { + $(window).trigger('action:ajaxify.end', {url: url}); + } + } + var count = 2; + ajaxify.variables.parse(); - ajaxify.loadScript(tpl_url); + ajaxify.loadScript(tpl_url, done); - ajaxify.widgets.render(tpl_url, url, function() { - $(window).trigger('action:ajaxify.end', {url: url}); - }); + ajaxify.widgets.render(tpl_url, url, done); $(window).trigger('action:ajaxify.contentLoaded', {url: url, tpl: tpl_url}); @@ -261,7 +260,11 @@ $(document).ready(function() { } if (!e.ctrlKey && !e.shiftKey && !e.metaKey && e.which === 1) { - if (this.host === '' || (this.host === window.location.host && this.protocol === window.location.protocol)) { + if ( + this.host === '' || // Relative paths are always internal links... + (this.host === window.location.host && this.protocol === window.location.protocol && // Otherwise need to check that protocol and host match + (RELATIVE_PATH.length > 0 ? this.pathname.indexOf(RELATIVE_PATH) === 0 : true)) // Subfolder installs need this additional check + ) { // Internal link var url = this.href.replace(rootUrl + '/', ''); diff --git a/public/src/app.js b/public/src/app.js index c2c1f179f0..74829543af 100644 --- a/public/src/app.js +++ b/public/src/app.js @@ -169,7 +169,8 @@ app.cacheBuster = null; }); }; - app.enterRoom = function (room) { + app.enterRoom = function (room, callback) { + callback = callback || function() {}; if (socket) { if (app.currentRoom === room) { return; @@ -180,9 +181,13 @@ app.cacheBuster = null; username: app.user.username, userslug: app.user.userslug, picture: app.user.picture + }, function(err) { + if (err) { + app.alertError(err.message); + return; + } + app.currentRoom = room; }); - - app.currentRoom = room; } }; @@ -401,12 +406,6 @@ app.cacheBuster = null; searchButton.show(); } - function prepareSearch() { - searchFields.removeClass('hide').show(); - searchButton.hide(); - searchInput.focus(); - } - searchButton.on('click', function(e) { if (!config.loggedIn && !config.allowGuestSearching) { app.alert({ @@ -418,43 +417,26 @@ app.cacheBuster = null; } e.stopPropagation(); - prepareSearch(); + app.prepareSearch(); return false; }); - require(['search', 'mousetrap'], function(search, Mousetrap) { - $('#search-form').on('submit', function (e) { - e.preventDefault(); - var input = $(this).find('input'); - + $('#search-form').on('submit', function () { + var input = $(this).find('input'); + require(['search'], function(search) { search.query({term: input.val()}, function() { input.val(''); }); }); - - $('.topic-search') - .on('click', '.prev', function() { - search.topicDOM.prev(); - }) - .on('click', '.next', function() { - search.topicDOM.next(); - }); - - Mousetrap.bind('ctrl+f', function(e) { - if (config.topicSearchEnabled) { - // If in topic, open search window and populate, otherwise regular behaviour - var match = ajaxify.currentPage.match(/^topic\/([\d]+)/), - tid; - if (match) { - e.preventDefault(); - tid = match[1]; - searchInput.val('in:topic-' + tid + ' '); - prepareSearch(); - } - } - }); + return false; }); - } + }; + + app.prepareSearch = function() { + $("#search-fields").removeClass('hide').show(); + $("#search-button").hide(); + $('#search-fields input').focus(); + }; function handleStatusChange() { $('#user-control-list .user-status').off('click').on('click', function(e) { @@ -560,6 +542,14 @@ app.cacheBuster = null; }); }; + app.loadJQueryUI = function(callback) { + if (typeof $().autocomplete === 'function') { + return callback(); + } + + $.getScript(RELATIVE_PATH + '/vendor/jquery/js/jquery-ui-1.10.4.custom.js', callback); + }; + app.showEmailConfirmWarning = function(err) { if (!config.requireEmailConfirmation || !app.user.uid) { return; diff --git a/public/src/client/account/edit.js b/public/src/client/account/edit.js index e7ed54cbbe..c0e90dc818 100644 --- a/public/src/client/account/edit.js +++ b/public/src/client/account/edit.js @@ -17,10 +17,12 @@ define('forum/account/edit', ['forum/account/header', 'uploader', 'translator'], $('#submitBtn').on('click', updateProfile); - $('#inputBirthday').datepicker({ - changeMonth: true, - changeYear: true, - yearRange: '1900:+0' + app.loadJQueryUI(function() { + $('#inputBirthday').datepicker({ + changeMonth: true, + changeYear: true, + yearRange: '1900:+0' + }); }); currentEmail = $('#inputEmail').val(); diff --git a/public/src/client/account/profile.js b/public/src/client/account/profile.js index 00be98d99f..daf602d81d 100644 --- a/public/src/client/account/profile.js +++ b/public/src/client/account/profile.js @@ -1,6 +1,6 @@ 'use strict'; -/* globals define, ajaxify, app, utils, socket */ +/* globals define, ajaxify, app, utils, socket, bootbox */ define('forum/account/profile', ['forum/account/header', 'forum/infinitescroll', 'translator'], function(header, infinitescroll, translator) { var Account = {}, @@ -33,6 +33,10 @@ define('forum/account/profile', ['forum/account/header', 'forum/infinitescroll', app.openChat($('.account-username').html(), theirid); }); + $('#banAccountBtn').on('click', banAccount); + $('#unbanAccountBtn').on('click', unbanAccount); + $('#deleteAccountBtn').on('click', deleteAccount); + socket.removeListener('event:user_status_change', onUserStatusChange); socket.on('event:user_status_change', onUserStatusChange); @@ -112,5 +116,49 @@ define('forum/account/profile', ['forum/account/header', 'forum/infinitescroll', }); } + function banAccount() { + translator.translate('[[user:ban_account_confirm]]', function(translated) { + bootbox.confirm(translated, function(confirm) { + if (!confirm) { + return; + } + socket.emit('admin.user.banUsers', [ajaxify.variables.get('theirid')], function(err) { + if (err) { + return app.alertError(err.message); + } + $('#banAccountBtn').toggleClass('hide', true); + $('#banLabel, #unbanAccountBtn').toggleClass('hide', false); + }); + }); + }); + } + + function unbanAccount() { + socket.emit('admin.user.unbanUsers', [ajaxify.variables.get('theirid')], function(err) { + if (err) { + return app.alertError(err.message); + } + $('#banAccountBtn').toggleClass('hide', false); + $('#banLabel, #unbanAccountBtn').toggleClass('hide', true); + }); + } + + function deleteAccount() { + translator.translate('[[user:delete_this_account_confirm]]', function(translated) { + bootbox.confirm(translated, function(confirm) { + if (!confirm) { + return; + } + + socket.emit('admin.user.deleteUsers', [ajaxify.variables.get('theirid')], function(err) { + if (err) { + return app.alertError(err.message); + } + history.back(); + }); + }); + }); + } + return Account; }); diff --git a/public/src/client/groups/details.js b/public/src/client/groups/details.js index 1a2742fb6b..1f7bd3f2df 100644 --- a/public/src/client/groups/details.js +++ b/public/src/client/groups/details.js @@ -66,6 +66,8 @@ define('forum/groups/details', ['iconSelect', 'components', 'vendor/colorpicker/ case 'reject': case 'acceptInvite': case 'rejectInvite': + case 'acceptAll': + case 'rejectAll': socket.emit('groups.' + action, { toUid: uid, groupName: ajaxify.variables.get('group_name') diff --git a/public/src/client/groups/list.js b/public/src/client/groups/list.js index 42bdbf88ae..d36d73bac3 100644 --- a/public/src/client/groups/list.js +++ b/public/src/client/groups/list.js @@ -1,7 +1,7 @@ "use strict"; /* globals app, define, ajaxify, socket, bootbox, utils, templates */ -define('forum/groups/list', function() { +define('forum/groups/list', ['forum/infinitescroll'], function(infinitescroll) { var Groups = {}; Groups.init = function() { @@ -13,6 +13,8 @@ define('forum/groups/list', function() { ajaxify.go('groups/' + groupSlug); }); + infinitescroll.init(Groups.loadMoreGroups); + // Group creation $('button[data-action="new"]').on('click', function() { bootbox.prompt('Group Name:', function(name) { @@ -29,13 +31,43 @@ define('forum/groups/list', function() { } }); }); + var params = utils.params(); + $('#search-sort').val(params.sort || 'alpha'); // Group searching $('#search-text').on('keyup', Groups.search); $('#search-button').on('click', Groups.search); - $('#search-sort').on('change', Groups.search); + $('#search-sort').on('change', function() { + ajaxify.go('groups?sort=' + $('#search-sort').val()); + }); }; + Groups.loadMoreGroups = function(direction) { + if (direction < 0) { + return; + } + + infinitescroll.loadMore('groups.loadMore', { + sort: $('#search-sort').val(), + after: $('[component="groups/container"]').attr('data-nextstart') + }, function(data, done) { + if (data && data.groups.length) { + templates.parse('partials/groups/list', { + groups: data.groups + }, function(html) { + $('#groups-list').append(html); + done(); + }); + } else { + done(); + } + + if (data && data.nextStart) { + $('[component="groups/container"]').attr('data-nextstart', data.nextStart); + } + }); + } + Groups.search = function() { var groupsEl = $('#groups-list'), queryEl = $('#search-text'), @@ -44,8 +76,6 @@ define('forum/groups/list', function() { socket.emit('groups.search', { query: queryEl.val(), options: { - expand: true, - truncateUserList: true, sort: sortEl.val() } }, function(err, groups) { diff --git a/public/src/client/register.js b/public/src/client/register.js index 6955fcb989..cca8617972 100644 --- a/public/src/client/register.js +++ b/public/src/client/register.js @@ -24,6 +24,12 @@ define('forum/register', ['csrf', 'translator'], function(csrf, translator) { } }); + var query = utils.params(); + if (query.email && query.token) { + email.val(query.email); + $('#token').val(query.token); + } + // Update the "others can mention you via" text username.on('keyup', function() { $('#yourUsername').text(this.value.length > 0 ? utils.slugify(this.value) : 'username'); @@ -69,7 +75,15 @@ define('forum/register', ['csrf', 'translator'], function(csrf, translator) { 'x-csrf-token': csrf.get() }, success: function(data, status) { - window.location.href = data; + registerBtn.removeClass('disabled'); + if (!data) { + return; + } + if (data.referrer) { + window.location.href = data.referrer; + } else if (data.message) { + app.alert({message: data.message, timeout: 20000}); + } }, error: function(data, status) { var errorEl = $('#register-error-notify'); @@ -84,7 +98,7 @@ define('forum/register', ['csrf', 'translator'], function(csrf, translator) { }); }); - if(agreeTerms.length) { + if (agreeTerms.length) { agreeTerms.on('click', function() { if ($(this).prop('checked')) { register.removeAttr('disabled'); diff --git a/public/src/client/search.js b/public/src/client/search.js index 027f76ca18..5633884ff5 100644 --- a/public/src/client/search.js +++ b/public/src/client/search.js @@ -123,8 +123,15 @@ define('forum/search', ['search', 'autocomplete'], function(searchModule, autoco $('.search-result-text').each(function() { var result = $(this); + var text = result.html().replace(regex, '$1'); - result.html(text).find('img').addClass('img-responsive'); + result.html(text).find('img').addClass('img-responsive').each(function() { + $(this).attr('src', $(this).attr('src').replace(/([\s\S]*?)<\/strong>/gi, '$1')); + }); + + result.find('a').each(function() { + $(this).attr('href', $(this).attr('href').replace(/([\s\S]*?)<\/strong>/gi, '$1')); + }); }); } catch(e) { return; diff --git a/public/src/client/topic.js b/public/src/client/topic.js index 95b84204fe..e6b932cb98 100644 --- a/public/src/client/topic.js +++ b/public/src/client/topic.js @@ -27,6 +27,14 @@ define('forum/topic', [ events.removeListeners(); } + + if (!data.url.startsWith('topic/')) { + require(['search'], function(search) { + if (search.topicDOM.active) { + search.topicDOM.end(); + } + }); + } }); Topic.init = function() { @@ -36,7 +44,7 @@ define('forum/topic', [ app.enterRoom('topic_' + tid); - posts.processPage($('.topic')); + posts.processPage(components.get('post')); postTools.init(tid); threadTools.init(tid); @@ -64,8 +72,36 @@ define('forum/topic', [ browsing.onUpdateUsersInRoom(data); }); } + + handleTopicSearch(); }; + function handleTopicSearch() { + require(['search', 'mousetrap'], function(search, Mousetrap) { + $('.topic-search') + .on('click', '.prev', function() { + search.topicDOM.prev(); + }) + .on('click', '.next', function() { + search.topicDOM.next(); + }); + + Mousetrap.bind('ctrl+f', function(e) { + if (config.topicSearchEnabled) { + // If in topic, open search window and populate, otherwise regular behaviour + var match = ajaxify.currentPage.match(/^topic\/([\d]+)/), + tid; + if (match) { + e.preventDefault(); + tid = match[1]; + $('#search-fields input').val('in:topic-' + tid + ' '); + app.prepareSearch(); + } + } + }); + }); + } + Topic.toTop = function() { navigator.scrollTop(0); }; diff --git a/public/src/client/topic/posts.js b/public/src/client/topic/posts.js index 2104bc04fa..3548e1bdf1 100644 --- a/public/src/client/topic/posts.js +++ b/public/src/client/topic/posts.js @@ -247,21 +247,21 @@ define('forum/topic/posts', [ }); } - Posts.processPage = function(element) { + Posts.processPage = function(posts) { app.createUserTooltips(); - app.replaceSelfLinks(element.find('a')); - utils.addCommasToNumbers(element.find('.formatted-number')); - utils.makeNumbersHumanReadable(element.find('.human-readable-number')); - element.find('.timeago').timeago(); - element.find('[component="post/content"] img:not(.emoji)').each(function() { + app.replaceSelfLinks(posts.find('a')); + utils.addCommasToNumbers(posts.find('.formatted-number')); + utils.makeNumbersHumanReadable(posts.find('.human-readable-number')); + posts.find('.timeago').timeago(); + posts.find('[component="post/content"] img:not(.emoji)').each(function() { var $this = $(this); if (!$this.parent().is('a')) { $this.wrap(''); } }); postTools.updatePostCount(); - addBlockquoteEllipses(element.find('[component="post/content"] > blockquote')); - hidePostToolsForDeletedPosts(element); + addBlockquoteEllipses(posts.find('[component="post/content"] > blockquote')); + hidePostToolsForDeletedPosts(posts); showBottomPostBar(); }; @@ -271,9 +271,11 @@ define('forum/topic/posts', [ } } - function hidePostToolsForDeletedPosts(element) { - element.find('[data-pid].deleted').each(function() { - postTools.toggle($(this).attr('data-pid'), true); + function hidePostToolsForDeletedPosts(posts) { + posts.each(function() { + if ($(this).hasClass('deleted')) { + postTools.toggle($(this).attr('data-pid'), true); + } }); } diff --git a/public/src/client/unread.js b/public/src/client/unread.js index 406116e9f1..acb66d4c2f 100644 --- a/public/src/client/unread.js +++ b/public/src/client/unread.js @@ -68,7 +68,7 @@ define('forum/unread', ['forum/recent', 'topicSelect', 'forum/infinitescroll', ' }); }); - socket.emit('categories.get', onCategoriesLoaded); + socket.emit('categories.getWatchedCategories', onCategoriesLoaded); topicSelect.init(); diff --git a/public/src/client/users.js b/public/src/client/users.js index eaa9467a00..35e56dc30a 100644 --- a/public/src/client/users.js +++ b/public/src/client/users.js @@ -22,6 +22,8 @@ define('forum/users', ['translator'], function(translator) { handleSearch(); + handleInvite(); + socket.removeListener('event:user_status_change', onUserStatusChange); socket.on('event:user_status_change', onUserStatusChange); @@ -109,71 +111,82 @@ define('forum/users', ['translator'], function(translator) { } function doSearch(page) { - function reset() { - notify.html(''); - notify.parent().removeClass('btn-warning label-warning btn-success label-success'); - } - var username = $('#search-user').val(); var notify = $('#user-notfound-notify'); page = page || 1; + if (!username) { + return loadPage(page); + } + notify.html(''); - var filters = []; - $('.user-filter').each(function() { - var $this = $(this); - if($this.is(':checked')) { - filters.push({ - field:$this.attr('data-filter-field'), - type: $this.attr('data-filter-type'), - value: $this.attr('data-filter-value') - }); - } - }); socket.emit('user.search', { query: username, page: page, - searchBy: ['username', 'fullname'], + searchBy: 'username', sortBy: $('.search select').val(), - filterBy: filters + onlineOnly: $('.search .online-only').is(':checked') }, function(err, data) { if (err) { - reset(); + resetSearchNotify(); return app.alertError(err.message); } if (!data) { - return reset(); + return resetSearchNotify(); } - templates.parse('partials/paginator', {pagination: data.pagination}, function(html) { - $('.pagination-container').replaceWith(html); - }); + renderSearchResults(data); + }); + } - templates.parse('users', 'users', data, function(html) { - translator.translate(html, function(translated) { - $('#users-container').html(translated); + function resetSearchNotify() { + var notify = $('#user-notfound-notify'); + notify.html(''); + notify.parent().removeClass('btn-warning label-warning btn-success label-success'); + } - if (!data.users.length) { - translator.translate('[[error:no-user]]', function(translated) { - notify.html(translated); - notify.parent().removeClass('btn-success label-success').addClass('btn-warning label-warning'); - }); - } else { - translator.translate('[[users:users-found-search-took, ' + data.matchCount + ', ' + data.timing + ']]', function(translated) { - notify.html(translated); - notify.parent().removeClass('btn-warning label-warning').addClass('btn-success label-success'); - }); - } - }); + + function loadPage(page) { + socket.emit('user.loadSearchPage', {page: page, onlineOnly: $('.search .online-only').is(':checked')}, function(err, data) { + resetSearchNotify(); + if (err) { + return app.alertError(err.message); + } + + renderSearchResults(data); + }); + } + + function renderSearchResults(data) { + var notify = $('#user-notfound-notify'); + templates.parse('partials/paginator', {pagination: data.pagination}, function(html) { + $('.pagination-container').replaceWith(html); + }); + + templates.parse('users', 'users', data, function(html) { + translator.translate(html, function(translated) { + $('#users-container').html(translated); + + if (!data.users.length) { + translator.translate('[[error:no-user]]', function(translated) { + notify.html(translated); + notify.parent().removeClass('btn-success label-success').addClass('btn-warning label-warning'); + }); + } else { + translator.translate('[[users:users-found-search-took, ' + data.matchCount + ', ' + data.timing + ']]', function(translated) { + notify.html(translated); + notify.parent().removeClass('btn-warning label-warning').addClass('btn-success label-success'); + }); + } }); }); } function onUserStatusChange(data) { var section = getActiveSection(); - + if ((section.startsWith('online') || section.startsWith('users'))) { updateUser(data); } @@ -189,5 +202,22 @@ define('forum/users', ['translator'], function(translator) { return parts[parts.length - 1]; } + function handleInvite() { + $('[component="user/invite"]').on('click', function() { + bootbox.prompt('Email: ', function(email) { + if (!email) { + return; + } + + socket.emit('user.invite', email, function(err) { + if (err) { + return app.alertError(err.message); + } + app.alertSuccess('[[users:invitation-email-sent, ' + email + ']]'); + }); + }); + }); + } + return Users; }); diff --git a/public/src/modules/autocomplete.js b/public/src/modules/autocomplete.js index 55c15b8686..0f4ca57c2e 100644 --- a/public/src/modules/autocomplete.js +++ b/public/src/modules/autocomplete.js @@ -6,32 +6,69 @@ define('autocomplete', function() { var module = {}; - module.user = function (input) { - return input.autocomplete({ - delay: 100, - source: function(request, response) { - socket.emit('user.search', {query: request.term}, function(err, result) { - if (err) { - return app.alertError(err.message); - } + module.user = function (input, onselect) { + app.loadJQueryUI(function() { + input.autocomplete({ + delay: 100, + select: onselect, + source: function(request, response) { + socket.emit('user.search', {query: request.term}, function(err, result) { + if (err) { + return app.alertError(err.message); + } - if (result && result.users) { - var names = result.users.map(function(user) { - return user && { - label: user.username, - value: user.username, - user: { - uid: user.uid, - name: user.username, - slug: user.userslug - } - }; - }); - response(names); - } - $('.ui-autocomplete a').attr('data-ajaxify', 'false'); - }); - } + if (result && result.users) { + var names = result.users.map(function(user) { + return user && { + label: user.username, + value: user.username, + user: { + uid: user.uid, + name: user.username, + slug: user.userslug + } + }; + }); + response(names); + } + $('.ui-autocomplete a').attr('data-ajaxify', 'false'); + }); + } + }); + }); + }; + + module.group = function(input, onselect) { + app.loadJQueryUI(function() { + input.autocomplete({ + delay: 100, + select: onselect, + source: function(request, response) { + socket.emit('groups.search', { + query: request.term, + options: {} + }, function(err, results) { + if (err) { + return app.alertError(err.message); + } + + if (results && results.length) { + var names = results.map(function(group) { + return group && { + label: group.name, + value: group.name, + group: { + name: group.name, + slug: group.slug + } + }; + }); + response(names); + } + $('.ui-autocomplete a').attr('data-ajaxify', 'false'); + }); + } + }); }); }; diff --git a/public/src/modules/chat.js b/public/src/modules/chat.js index 9c1183d6a1..b27b932eec 100644 --- a/public/src/modules/chat.js +++ b/public/src/modules/chat.js @@ -179,28 +179,31 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra chatModal.css('zIndex', 100); chatModal.appendTo($('body')); module.center(chatModal); - chatModal.draggable({ - start:function() { - module.bringModalToTop(chatModal); - }, - stop:function() { - chatModal.find('#chat-message-input').focus(); - }, - distance: 10, - handle: '.modal-header' - }); - chatModal.find('.modal-content').resizable({ - minHeight: 250, - minWidth: 400 - }); + app.loadJQueryUI(function() { + chatModal.find('.modal-content').resizable({ + minHeight: 250, + minWidth: 400 + }); - chatModal.find('.modal-content').on('resize', function(event, ui) { - if (ui.originalSize.height === ui.size.height) { - return; - } + chatModal.find('.modal-content').on('resize', function(event, ui) { + if (ui.originalSize.height === ui.size.height) { + return; + } - chatModal.find('#chat-content').css('height', module.calculateChatListHeight(chatModal)); + chatModal.find('#chat-content').css('height', module.calculateChatListHeight(chatModal)); + }); + + chatModal.draggable({ + start:function() { + module.bringModalToTop(chatModal); + }, + stop:function() { + chatModal.find('#chat-message-input').focus(); + }, + distance: 10, + handle: '.modal-header' + }); }); chatModal.find('#chat-with-name').html(username); diff --git a/public/src/modules/composer.js b/public/src/modules/composer.js index 4e8ce528f3..1e32892617 100644 --- a/public/src/modules/composer.js +++ b/public/src/modules/composer.js @@ -23,6 +23,11 @@ define('composer', [ $(window).off('resize', onWindowResize).on('resize', onWindowResize); + $(window).on('action:composer.topics.post', function(ev, data) { + localStorage.removeItem('category:' + data.data.cid + ':bookmark'); + localStorage.removeItem('category:' + data.data.cid + ':bookmark:clicked'); + }); + $(window).on('popstate', function(ev, data) { var env = utils.findBootstrapEnvironment(); @@ -36,21 +41,12 @@ define('composer', [ bootbox.confirm(translated, function(confirm) { if (confirm) { discard(composer.active); - } else { - history.pushState({}, ''); } }); }); } }); - $(window).on('action:composer.topics.post', function(ev, data) { - localStorage.removeItem('category:' + data.data.cid + ':bookmark'); - localStorage.removeItem('category:' + data.data.cid + ':bookmark:clicked'); - ajaxify.go('topic/' + data.data.slug); - removeComposerHistory(); - }); - function removeComposerHistory() { var env = utils.findBootstrapEnvironment(); if (env === 'xs' || env ==='sm') { @@ -121,13 +117,7 @@ define('composer', [ } composer.posts[uuid] = post; - composer.load(uuid); - - var env = utils.findBootstrapEnvironment(); - if (env === 'xs' || env ==='sm') { - history.pushState({}, ''); - } } function composerAlert(post_uuid, message) { @@ -321,117 +311,129 @@ define('composer', [ isAdminOrMod: app.user.isAdmin || postData.isMod }; - parseAndTranslate('composer', data, function(composerTemplate) { - if ($('#cmp-uuid-' + post_uuid).length) { - return; - } - composerTemplate = $(composerTemplate); + if (data.mobile) { + var qs = '?p=' + window.location.pathname; + ajaxify.go('compose', function() { + renderComposer(); + }, false, qs); + } else { + renderComposer(); + } - composerTemplate.attr('id', 'cmp-uuid-' + post_uuid); - - $(document.body).append(composerTemplate); - - var postContainer = $(composerTemplate[0]), - bodyEl = postContainer.find('textarea'), - draft = drafts.getDraft(postData.save_id), - submitBtn = postContainer.find('.composer-submit'); - - preview.handleToggler(postContainer); - tags.init(postContainer, composer.posts[post_uuid]); - categoryList.init(postContainer, composer.posts[post_uuid]); - - activate(post_uuid); - resize.reposition(postContainer); - - if (config.allowFileUploads || config.hasImageUploadPlugin) { - uploads.initialize(post_uuid); - } - - formatting.addHandler(postContainer); - - if (allowTopicsThumbnail) { - uploads.toggleThumbEls(postContainer, composer.posts[post_uuid].topic_thumb || ''); - } - - postContainer.on('change', 'input, textarea', function() { - composer.posts[post_uuid].modified = true; - }); - - submitBtn.on('click', function() { - var action = $(this).attr('data-action'); - - switch(action) { - case 'post-lock': - $(this).attr('disabled', true); - post(post_uuid, {lock: true}); - break; - - case 'post': // intentional fall-through - default: - $(this).attr('disabled', true); - post(post_uuid); - break; - } - }); - - postContainer.on('click', 'a[data-switch-action]', function() { - var action = $(this).attr('data-switch-action'), - label = $(this).html(); - - submitBtn.attr('data-action', action).html(label); - }); - - postContainer.find('.composer-discard').on('click', function() { - if (!composer.posts[post_uuid].modified) { - removeComposerHistory(); - discard(post_uuid); + function renderComposer() { + parseAndTranslate('composer', data, function(composerTemplate) { + if ($('#cmp-uuid-' + post_uuid).length) { return; } - var btn = $(this).prop('disabled', true); - translator.translate('[[modules:composer.discard]]', function(translated) { - bootbox.confirm(translated, function(confirm) { - if (confirm) { - removeComposerHistory(); - discard(post_uuid); - } - btn.prop('disabled', false); + composerTemplate = $(composerTemplate); + + composerTemplate.attr('id', 'cmp-uuid-' + post_uuid); + + $(document.body).append(composerTemplate); + + var postContainer = $(composerTemplate[0]), + bodyEl = postContainer.find('textarea'), + draft = drafts.getDraft(postData.save_id), + submitBtn = postContainer.find('.composer-submit'); + + preview.handleToggler(postContainer); + tags.init(postContainer, composer.posts[post_uuid]); + categoryList.init(postContainer, composer.posts[post_uuid]); + + activate(post_uuid); + resize.reposition(postContainer); + + if (config.allowFileUploads || config.hasImageUploadPlugin) { + uploads.initialize(post_uuid); + } + + formatting.addHandler(postContainer); + + if (allowTopicsThumbnail) { + uploads.toggleThumbEls(postContainer, composer.posts[post_uuid].topic_thumb || ''); + } + + postContainer.on('change', 'input, textarea', function() { + composer.posts[post_uuid].modified = true; + }); + + submitBtn.on('click', function() { + var action = $(this).attr('data-action'); + + switch(action) { + case 'post-lock': + $(this).attr('disabled', true); + post(post_uuid, {lock: true}); + break; + + case 'post': // intentional fall-through + default: + $(this).attr('disabled', true); + post(post_uuid); + break; + } + }); + + postContainer.on('click', 'a[data-switch-action]', function() { + var action = $(this).attr('data-switch-action'), + label = $(this).html(); + + submitBtn.attr('data-action', action).html(label); + }); + + postContainer.find('.composer-discard').on('click', function() { + if (!composer.posts[post_uuid].modified) { + removeComposerHistory(); + discard(post_uuid); + return; + } + var btn = $(this).prop('disabled', true); + translator.translate('[[modules:composer.discard]]', function(translated) { + bootbox.confirm(translated, function(confirm) { + if (confirm) { + removeComposerHistory(); + discard(post_uuid); + } + btn.prop('disabled', false); + }); }); }); + + postContainer.on('click', function() { + if (!taskbar.isActive(post_uuid)) { + taskbar.updateActive(post_uuid); + } + }); + + bodyEl.on('input propertychange', function() { + preview.render(postContainer); + }); + + bodyEl.on('scroll', function() { + preview.matchScroll(postContainer); + }); + + bodyEl.val(draft ? draft : postData.body); + + preview.render(postContainer, function() { + preview.matchScroll(postContainer); + }); + + drafts.init(postContainer, postData); + + resize.handleResize(postContainer); + + handleHelp(postContainer); + + $(window).trigger('action:composer.loaded', { + post_uuid: post_uuid, + composerData: composer.posts[post_uuid] + }); + + formatting.addComposerButtons(); + focusElements(postContainer); }); - - postContainer.on('click', function() { - if (!taskbar.isActive(post_uuid)) { - taskbar.updateActive(post_uuid); - } - }); - - bodyEl.on('input propertychange', function() { - preview.render(postContainer); - }); - - bodyEl.on('scroll', function() { - preview.matchScroll(postContainer); - }); - - bodyEl.val(draft ? draft : postData.body); - - preview.render(postContainer, function() { - preview.matchScroll(postContainer); - }); - - drafts.init(postContainer, postData); - - resize.handleResize(postContainer); - - handleHelp(postContainer); - - $(window).trigger('action:composer.loaded', { - post_uuid: post_uuid - }); - - formatting.addComposerButtons(); - focusElements(postContainer); - }); + } } function parseAndTranslate(template, data, callback) { @@ -548,6 +550,12 @@ define('composer', [ discard(post_uuid); drafts.removeDraft(postData.save_id); + if (action === 'topics.post') { + ajaxify.go('topic/' + data.slug); + } else { + removeComposerHistory(); + } + $(window).trigger('action:composer.' + action, {composerData: composerData, data: data}); }); } diff --git a/public/src/modules/composer/tags.js b/public/src/modules/composer/tags.js index f074a5abc2..8e516e83b8 100644 --- a/public/src/modules/composer/tags.js +++ b/public/src/modules/composer/tags.js @@ -14,8 +14,9 @@ define('composer/tags', function() { tagEl.tagsinput({ maxTags: config.tagsPerTopic, + maxChars: config.maximumTagLength, confirmKeys: [13, 44], - trimValue: true + trimValue: true }); tagEl.on('beforeItemAdd', function(event) { @@ -34,23 +35,29 @@ define('composer/tags', function() { addTags(postData.tags, tagEl); var input = postContainer.find('.bootstrap-tagsinput input'); - input.autocomplete({ - delay: 100, - source: function(request, response) { - socket.emit('topics.searchTags', {query: request.term, cid: postData.cid}, function(err, tags) { - if (err) { - return app.alertError(err.message); - } - if (tags) { - response(tags); - } - $('.ui-autocomplete a').attr('data-ajaxify', 'false'); - }); - }, - select: function(event, ui) { - // when autocomplete is selected from the dropdown simulate a enter key down to turn it into a tag - triggerEnter(input); - } + + app.loadJQueryUI(function() { + input.autocomplete({ + delay: 100, + open: function() { + $(this).autocomplete('widget').css('z-index', 20000); + }, + source: function(request, response) { + socket.emit('topics.searchTags', {query: request.term, cid: postData.cid}, function(err, tags) { + if (err) { + return app.alertError(err.message); + } + if (tags) { + response(tags); + } + $('.ui-autocomplete a').attr('data-ajaxify', 'false'); + }); + }, + select: function(event, ui) { + // when autocomplete is selected from the dropdown simulate a enter key down to turn it into a tag + triggerEnter(input); + } + }); }); input.attr('tabIndex', tagEl.attr('tabIndex')); diff --git a/public/src/modules/helpers.js b/public/src/modules/helpers.js index b0f48d6f15..1cc4f897fb 100644 --- a/public/src/modules/helpers.js +++ b/public/src/modules/helpers.js @@ -102,10 +102,6 @@ style.push('unread'); } - if (topic.extraClass) { - style.push(topic.extraClass); - } - return style.join(' '); }; @@ -113,6 +109,10 @@ return (topic.index || 0) + 1; }; + helpers.displayUserSearch = function(data, allowGuestUserSearching) { + return data.loggedIn || allowGuestUserSearching === 'true'; + }; + // Groups helpers helpers.membershipBtn = function(groupObj) { if (groupObj.isMember) { diff --git a/public/src/modules/translator.js b/public/src/modules/translator.js index ac3be2728e..8030913d76 100644 --- a/public/src/modules/translator.js +++ b/public/src/modules/translator.js @@ -9,7 +9,7 @@ var languages = {}, regexes = { - match: /\[\[.*?\]\]/g, + match: /\[\[[\s\S]*?\]\]/g, split: /[,][\s]*/, replace: /\]+$/ }; @@ -283,11 +283,12 @@ } try { - callback(JSON.parse(data.toString())); + data = JSON.parse(data.toString()); } catch (e) { winston.error('Could not parse `' + filename + '.json`, syntax error? Skipping...'); - callback({}); + data = {}; } + callback(data); }); } @@ -295,11 +296,13 @@ if (typeof define === 'function' && define.amd) { define('translator', translator); + var _translator = translator; + // Expose a global `translator` object for backwards compatibility window.translator = { translate: function() { console.warn('[translator] Global invocation of the translator is now deprecated, please `require` the module instead.'); - translator.translate.apply(translator, arguments); + _translator.translate.apply(_translator, arguments); } } } @@ -307,4 +310,4 @@ typeof exports === 'object' ? exports : typeof define === 'function' && define.amd ? {} : translator = {} -); \ No newline at end of file +); diff --git a/public/src/overrides.js b/public/src/overrides.js index 8c0e492cce..dde1b55dd3 100644 --- a/public/src/overrides.js +++ b/public/src/overrides.js @@ -79,25 +79,27 @@ if ('undefined' !== typeof window) { })(jQuery || {fn:{}}); - - // 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) { - return el.namespace === 'bs.data-api.dropdown' && el.selector === undefined; - }); - - if(_clearMenus.length) { - _clearMenus = _clearMenus[0].handler; - } - - // disable the old listener - $(document) - .off('click.data-api.dropdown', _clearMenus) - .on('click.data-api.dropdown', function (e) { - // call the handler only when not right-click - if (e.button !== 2) { - _clearMenus(); - } + (function(){ + // 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) { + return el.namespace === 'bs.data-api.dropdown' && el.selector === undefined; }); + + if(_clearMenus.length) { + _clearMenus = _clearMenus[0].handler; + } + + // disable the old listener + $(document) + .off('click.data-api.dropdown', _clearMenus) + .on('click.data-api.dropdown', function (e) { + // call the handler only when not right-click + if (e.button !== 2) { + _clearMenus(); + } + }); + })(); + } diff --git a/public/src/widgets.js b/public/src/widgets.js index e83353ec1b..02c71f8b54 100644 --- a/public/src/widgets.js +++ b/public/src/widgets.js @@ -15,6 +15,10 @@ }; ajaxify.widgets.render = function(template, url, callback) { + if (template.match(/^admin/)) { + return false; + } + var widgetLocations = ['sidebar', 'footer', 'header'], numLocations; $('#content [widget-area]').each(function() { diff --git a/public/vendor/jquery/sortable/Sortable.js b/public/vendor/jquery/sortable/Sortable.js new file mode 100644 index 0000000000..cf0b5f047b --- /dev/null +++ b/public/vendor/jquery/sortable/Sortable.js @@ -0,0 +1,1145 @@ +/**! + * Sortable + * @author RubaXa + * @license MIT + */ + + +(function (factory) { + "use strict"; + + if (typeof define === "function" && define.amd) { + define(factory); + } + else if (typeof module != "undefined" && typeof module.exports != "undefined") { + module.exports = factory(); + } + else if (typeof Package !== "undefined") { + Sortable = factory(); // export for Meteor.js + } + else { + /* jshint sub:true */ + window["Sortable"] = factory(); + } +})(function () { + "use strict"; + + var dragEl, + ghostEl, + cloneEl, + rootEl, + nextEl, + + scrollEl, + scrollParentEl, + + lastEl, + lastCSS, + + oldIndex, + newIndex, + + activeGroup, + autoScroll = {}, + + tapEvt, + touchEvt, + + /** @const */ + RSPACE = /\s+/g, + + expando = 'Sortable' + (new Date).getTime(), + + win = window, + document = win.document, + parseInt = win.parseInt, + + supportDraggable = !!('draggable' in document.createElement('div')), + + _silent = false, + + abs = Math.abs, + slice = [].slice, + + touchDragOverListeners = [], + + _autoScroll = _throttle(function (/**Event*/evt, /**Object*/options, /**HTMLElement*/rootEl) { + // Bug: https://bugzilla.mozilla.org/show_bug.cgi?id=505521 + if (rootEl && options.scroll) { + var el, + rect, + sens = options.scrollSensitivity, + speed = options.scrollSpeed, + + x = evt.clientX, + y = evt.clientY, + + winWidth = window.innerWidth, + winHeight = window.innerHeight, + + vx, + vy + ; + + // Delect scrollEl + if (scrollParentEl !== rootEl) { + scrollEl = options.scroll; + scrollParentEl = rootEl; + + if (scrollEl === true) { + scrollEl = rootEl; + + do { + if ((scrollEl.offsetWidth < scrollEl.scrollWidth) || + (scrollEl.offsetHeight < scrollEl.scrollHeight) + ) { + break; + } + /* jshint boss:true */ + } while (scrollEl = scrollEl.parentNode); + } + } + + if (scrollEl) { + el = scrollEl; + rect = scrollEl.getBoundingClientRect(); + vx = (abs(rect.right - x) <= sens) - (abs(rect.left - x) <= sens); + vy = (abs(rect.bottom - y) <= sens) - (abs(rect.top - y) <= sens); + } + + + if (!(vx || vy)) { + vx = (winWidth - x <= sens) - (x <= sens); + vy = (winHeight - y <= sens) - (y <= sens); + + /* jshint expr:true */ + (vx || vy) && (el = win); + } + + + if (autoScroll.vx !== vx || autoScroll.vy !== vy || autoScroll.el !== el) { + autoScroll.el = el; + autoScroll.vx = vx; + autoScroll.vy = vy; + + clearInterval(autoScroll.pid); + + if (el) { + autoScroll.pid = setInterval(function () { + if (el === win) { + win.scrollTo(win.pageXOffset + vx * speed, win.pageYOffset + vy * speed); + } else { + vy && (el.scrollTop += vy * speed); + vx && (el.scrollLeft += vx * speed); + } + }, 24); + } + } + } + }, 30) + ; + + + + /** + * @class Sortable + * @param {HTMLElement} el + * @param {Object} [options] + */ + function Sortable(el, options) { + this.el = el; // root element + this.options = options = _extend({}, options); + + + // Export instance + el[expando] = this; + + + // Default options + var defaults = { + group: Math.random(), + sort: true, + disabled: false, + store: null, + handle: null, + scroll: true, + scrollSensitivity: 30, + scrollSpeed: 10, + draggable: /[uo]l/i.test(el.nodeName) ? 'li' : '>*', + ghostClass: 'sortable-ghost', + ignore: 'a, img', + filter: null, + animation: 0, + setData: function (dataTransfer, dragEl) { + dataTransfer.setData('Text', dragEl.textContent); + }, + dropBubble: false, + dragoverBubble: false, + dataIdAttr: 'data-id', + delay: 0 + }; + + + // Set default options + for (var name in defaults) { + !(name in options) && (options[name] = defaults[name]); + } + + + var group = options.group; + + if (!group || typeof group != 'object') { + group = options.group = { name: group }; + } + + + ['pull', 'put'].forEach(function (key) { + if (!(key in group)) { + group[key] = true; + } + }); + + + options.groups = ' ' + group.name + (group.put.join ? ' ' + group.put.join(' ') : '') + ' '; + + + // Bind all private methods + for (var fn in this) { + if (fn.charAt(0) === '_') { + this[fn] = _bind(this, this[fn]); + } + } + + + // Bind events + _on(el, 'mousedown', this._onTapStart); + _on(el, 'touchstart', this._onTapStart); + + _on(el, 'dragover', this); + _on(el, 'dragenter', this); + + touchDragOverListeners.push(this._onDragOver); + + // Restore sorting + options.store && this.sort(options.store.get(this)); + } + + + Sortable.prototype = /** @lends Sortable.prototype */ { + constructor: Sortable, + + _onTapStart: function (/** Event|TouchEvent */evt) { + var _this = this, + el = this.el, + options = this.options, + type = evt.type, + touch = evt.touches && evt.touches[0], + target = (touch || evt).target, + originalTarget = target, + filter = options.filter; + + + if (type === 'mousedown' && evt.button !== 0 || options.disabled) { + return; // only left button or enabled + } + + target = _closest(target, options.draggable, el); + + if (!target) { + return; + } + + // get the index of the dragged element within its parent + oldIndex = _index(target); + + // Check filter + if (typeof filter === 'function') { + if (filter.call(this, evt, target, this)) { + _dispatchEvent(_this, originalTarget, 'filter', target, el, oldIndex); + evt.preventDefault(); + return; // cancel dnd + } + } + else if (filter) { + filter = filter.split(',').some(function (criteria) { + criteria = _closest(originalTarget, criteria.trim(), el); + + if (criteria) { + _dispatchEvent(_this, criteria, 'filter', target, el, oldIndex); + return true; + } + }); + + if (filter) { + evt.preventDefault(); + return; // cancel dnd + } + } + + + if (options.handle && !_closest(originalTarget, options.handle, el)) { + return; + } + + + // Prepare `dragstart` + this._prepareDragStart(evt, touch, target); + }, + + _prepareDragStart: function (/** Event */evt, /** Touch */touch, /** HTMLElement */target) { + var _this = this, + el = _this.el, + options = _this.options, + ownerDocument = el.ownerDocument, + dragStartFn; + + if (target && !dragEl && (target.parentNode === el)) { + tapEvt = evt; + + rootEl = el; + dragEl = target; + nextEl = dragEl.nextSibling; + activeGroup = options.group; + + dragStartFn = function () { + // Delayed drag has been triggered + // we can re-enable the events: touchmove/mousemove + _this._disableDelayedDrag(); + + // Make the element draggable + dragEl.draggable = true; + + // Disable "draggable" + options.ignore.split(',').forEach(function (criteria) { + _find(dragEl, criteria.trim(), _disableDraggable); + }); + + // Bind the events: dragstart/dragend + _this._triggerDragStart(touch); + }; + + _on(ownerDocument, 'mouseup', _this._onDrop); + _on(ownerDocument, 'touchend', _this._onDrop); + _on(ownerDocument, 'touchcancel', _this._onDrop); + + if (options.delay) { + // If the user moves the pointer before the delay has been reached: + // disable the delayed drag + _on(ownerDocument, 'mousemove', _this._disableDelayedDrag); + _on(ownerDocument, 'touchmove', _this._disableDelayedDrag); + + _this._dragStartTimer = setTimeout(dragStartFn, options.delay); + } else { + dragStartFn(); + } + } + }, + + _disableDelayedDrag: function () { + var ownerDocument = this.el.ownerDocument; + + clearTimeout(this._dragStartTimer); + + _off(ownerDocument, 'mousemove', this._disableDelayedDrag); + _off(ownerDocument, 'touchmove', this._disableDelayedDrag); + }, + + _triggerDragStart: function (/** Touch */touch) { + if (touch) { + // Touch device support + tapEvt = { + target: dragEl, + clientX: touch.clientX, + clientY: touch.clientY + }; + + this._onDragStart(tapEvt, 'touch'); + } + else if (!supportDraggable) { + this._onDragStart(tapEvt, true); + } + else { + _on(dragEl, 'dragend', this); + _on(rootEl, 'dragstart', this._onDragStart); + } + + try { + if (document.selection) { + document.selection.empty(); + } else { + window.getSelection().removeAllRanges(); + } + } catch (err) { + } + }, + + _dragStarted: function () { + if (rootEl && dragEl) { + // Apply effect + _toggleClass(dragEl, this.options.ghostClass, true); + + Sortable.active = this; + + // Drag start event + _dispatchEvent(this, rootEl, 'start', dragEl, rootEl, oldIndex); + } + }, + + _emulateDragOver: function () { + if (touchEvt) { + _css(ghostEl, 'display', 'none'); + + var target = document.elementFromPoint(touchEvt.clientX, touchEvt.clientY), + parent = target, + groupName = ' ' + this.options.group.name + '', + i = touchDragOverListeners.length; + + if (parent) { + do { + if (parent[expando] && parent[expando].options.groups.indexOf(groupName) > -1) { + while (i--) { + touchDragOverListeners[i]({ + clientX: touchEvt.clientX, + clientY: touchEvt.clientY, + target: target, + rootEl: parent + }); + } + + break; + } + + target = parent; // store last element + } + /* jshint boss:true */ + while (parent = parent.parentNode); + } + + _css(ghostEl, 'display', ''); + } + }, + + + _onTouchMove: function (/**TouchEvent*/evt) { + if (tapEvt) { + var touch = evt.touches ? evt.touches[0] : evt, + dx = touch.clientX - tapEvt.clientX, + dy = touch.clientY - tapEvt.clientY, + translate3d = evt.touches ? 'translate3d(' + dx + 'px,' + dy + 'px,0)' : 'translate(' + dx + 'px,' + dy + 'px)'; + + touchEvt = touch; + + _css(ghostEl, 'webkitTransform', translate3d); + _css(ghostEl, 'mozTransform', translate3d); + _css(ghostEl, 'msTransform', translate3d); + _css(ghostEl, 'transform', translate3d); + + evt.preventDefault(); + } + }, + + + _onDragStart: function (/**Event*/evt, /**boolean*/useFallback) { + var dataTransfer = evt.dataTransfer, + options = this.options; + + this._offUpEvents(); + + if (activeGroup.pull == 'clone') { + cloneEl = dragEl.cloneNode(true); + _css(cloneEl, 'display', 'none'); + rootEl.insertBefore(cloneEl, dragEl); + } + + if (useFallback) { + var rect = dragEl.getBoundingClientRect(), + css = _css(dragEl), + ghostRect; + + ghostEl = dragEl.cloneNode(true); + + _css(ghostEl, 'top', rect.top - parseInt(css.marginTop, 10)); + _css(ghostEl, 'left', rect.left - parseInt(css.marginLeft, 10)); + _css(ghostEl, 'width', rect.width); + _css(ghostEl, 'height', rect.height); + _css(ghostEl, 'opacity', '0.8'); + _css(ghostEl, 'position', 'fixed'); + _css(ghostEl, 'zIndex', '100000'); + + rootEl.appendChild(ghostEl); + + // Fixing dimensions. + ghostRect = ghostEl.getBoundingClientRect(); + _css(ghostEl, 'width', rect.width * 2 - ghostRect.width); + _css(ghostEl, 'height', rect.height * 2 - ghostRect.height); + + if (useFallback === 'touch') { + // Bind touch events + _on(document, 'touchmove', this._onTouchMove); + _on(document, 'touchend', this._onDrop); + _on(document, 'touchcancel', this._onDrop); + } else { + // Old brwoser + _on(document, 'mousemove', this._onTouchMove); + _on(document, 'mouseup', this._onDrop); + } + + this._loopId = setInterval(this._emulateDragOver, 150); + } + else { + if (dataTransfer) { + dataTransfer.effectAllowed = 'move'; + options.setData && options.setData.call(this, dataTransfer, dragEl); + } + + _on(document, 'drop', this); + } + + setTimeout(this._dragStarted, 0); + }, + + _onDragOver: function (/**Event*/evt) { + var el = this.el, + target, + dragRect, + revert, + options = this.options, + group = options.group, + groupPut = group.put, + isOwner = (activeGroup === group), + canSort = options.sort; + + if (evt.preventDefault !== void 0) { + evt.preventDefault(); + !options.dragoverBubble && evt.stopPropagation(); + } + + if (activeGroup && !options.disabled && + (isOwner + ? canSort || (revert = !rootEl.contains(dragEl)) // Reverting item into the original list + : activeGroup.pull && groupPut && ( + (activeGroup.name === group.name) || // by Name + (groupPut.indexOf && ~groupPut.indexOf(activeGroup.name)) // by Array + ) + ) && + (evt.rootEl === void 0 || evt.rootEl === this.el) // touch fallback + ) { + // Smart auto-scrolling + _autoScroll(evt, options, this.el); + + if (_silent) { + return; + } + + target = _closest(evt.target, options.draggable, el); + dragRect = dragEl.getBoundingClientRect(); + + + if (revert) { + _cloneHide(true); + + if (cloneEl || nextEl) { + rootEl.insertBefore(dragEl, cloneEl || nextEl); + } + else if (!canSort) { + rootEl.appendChild(dragEl); + } + + return; + } + + + if ((el.children.length === 0) || (el.children[0] === ghostEl) || + (el === evt.target) && (target = _ghostInBottom(el, evt)) + ) { + if (target) { + if (target.animated) { + return; + } + targetRect = target.getBoundingClientRect(); + } + + _cloneHide(isOwner); + + if (_onMove(rootEl, el, dragEl, dragRect, target, targetRect) !== false) { + el.appendChild(dragEl); + this._animate(dragRect, dragEl); + target && this._animate(targetRect, target); + } + } + else if (target && !target.animated && target !== dragEl && (target.parentNode[expando] !== void 0)) { + if (lastEl !== target) { + lastEl = target; + lastCSS = _css(target); + } + + + var targetRect = target.getBoundingClientRect(), + width = targetRect.right - targetRect.left, + height = targetRect.bottom - targetRect.top, + floating = /left|right|inline/.test(lastCSS.cssFloat + lastCSS.display), + isWide = (target.offsetWidth > dragEl.offsetWidth), + isLong = (target.offsetHeight > dragEl.offsetHeight), + halfway = (floating ? (evt.clientX - targetRect.left) / width : (evt.clientY - targetRect.top) / height) > 0.5, + nextSibling = target.nextElementSibling, + moveVector = _onMove(rootEl, el, dragEl, dragRect, target, targetRect), + after + ; + + if (moveVector !== false) { + _silent = true; + setTimeout(_unsilent, 30); + + _cloneHide(isOwner); + + if (moveVector === 1 || moveVector === -1) { + after = (moveVector === 1); + } + else if (floating) { + after = (target.previousElementSibling === dragEl) && !isWide || halfway && isWide; + } else { + after = (nextSibling !== dragEl) && !isLong || halfway && isLong; + } + + if (after && !nextSibling) { + el.appendChild(dragEl); + } else { + target.parentNode.insertBefore(dragEl, after ? nextSibling : target); + } + + this._animate(dragRect, dragEl); + this._animate(targetRect, target); + } + } + } + }, + + _animate: function (prevRect, target) { + var ms = this.options.animation; + + if (ms) { + var currentRect = target.getBoundingClientRect(); + + _css(target, 'transition', 'none'); + _css(target, 'transform', 'translate3d(' + + (prevRect.left - currentRect.left) + 'px,' + + (prevRect.top - currentRect.top) + 'px,0)' + ); + + target.offsetWidth; // repaint + + _css(target, 'transition', 'all ' + ms + 'ms'); + _css(target, 'transform', 'translate3d(0,0,0)'); + + clearTimeout(target.animated); + target.animated = setTimeout(function () { + _css(target, 'transition', ''); + _css(target, 'transform', ''); + target.animated = false; + }, ms); + } + }, + + _offUpEvents: function () { + var ownerDocument = this.el.ownerDocument; + + _off(document, 'touchmove', this._onTouchMove); + _off(ownerDocument, 'mouseup', this._onDrop); + _off(ownerDocument, 'touchend', this._onDrop); + _off(ownerDocument, 'touchcancel', this._onDrop); + }, + + _onDrop: function (/**Event*/evt) { + var el = this.el, + options = this.options; + + clearInterval(this._loopId); + clearInterval(autoScroll.pid); + + clearTimeout(this.dragStartTimer); + + // Unbind events + _off(document, 'drop', this); + _off(document, 'mousemove', this._onTouchMove); + _off(el, 'dragstart', this._onDragStart); + + this._offUpEvents(); + + if (evt) { + evt.preventDefault(); + !options.dropBubble && evt.stopPropagation(); + + ghostEl && ghostEl.parentNode.removeChild(ghostEl); + + if (dragEl) { + _off(dragEl, 'dragend', this); + + _disableDraggable(dragEl); + _toggleClass(dragEl, this.options.ghostClass, false); + + if (rootEl !== dragEl.parentNode) { + newIndex = _index(dragEl); + + // drag from one list and drop into another + _dispatchEvent(null, dragEl.parentNode, 'sort', dragEl, rootEl, oldIndex, newIndex); + _dispatchEvent(this, rootEl, 'sort', dragEl, rootEl, oldIndex, newIndex); + + // Add event + _dispatchEvent(null, dragEl.parentNode, 'add', dragEl, rootEl, oldIndex, newIndex); + + // Remove event + _dispatchEvent(this, rootEl, 'remove', dragEl, rootEl, oldIndex, newIndex); + } + else { + // Remove clone + cloneEl && cloneEl.parentNode.removeChild(cloneEl); + + if (dragEl.nextSibling !== nextEl) { + // Get the index of the dragged element within its parent + newIndex = _index(dragEl); + + // drag & drop within the same list + _dispatchEvent(this, rootEl, 'update', dragEl, rootEl, oldIndex, newIndex); + _dispatchEvent(this, rootEl, 'sort', dragEl, rootEl, oldIndex, newIndex); + } + } + + if (Sortable.active) { + // Drag end event + _dispatchEvent(this, rootEl, 'end', dragEl, rootEl, oldIndex, newIndex); + + // Save sorting + this.save(); + } + } + + // Nulling + rootEl = + dragEl = + ghostEl = + nextEl = + cloneEl = + + scrollEl = + scrollParentEl = + + tapEvt = + touchEvt = + + lastEl = + lastCSS = + + activeGroup = + Sortable.active = null; + } + }, + + + handleEvent: function (/**Event*/evt) { + var type = evt.type; + + if (type === 'dragover' || type === 'dragenter') { + if (dragEl) { + this._onDragOver(evt); + _globalDragOver(evt); + } + } + else if (type === 'drop' || type === 'dragend') { + this._onDrop(evt); + } + }, + + + /** + * Serializes the item into an array of string. + * @returns {String[]} + */ + toArray: function () { + var order = [], + el, + children = this.el.children, + i = 0, + n = children.length, + options = this.options; + + for (; i < n; i++) { + el = children[i]; + if (_closest(el, options.draggable, this.el)) { + order.push(el.getAttribute(options.dataIdAttr) || _generateId(el)); + } + } + + return order; + }, + + + /** + * Sorts the elements according to the array. + * @param {String[]} order order of the items + */ + sort: function (order) { + var items = {}, rootEl = this.el; + + this.toArray().forEach(function (id, i) { + var el = rootEl.children[i]; + + if (_closest(el, this.options.draggable, rootEl)) { + items[id] = el; + } + }, this); + + order.forEach(function (id) { + if (items[id]) { + rootEl.removeChild(items[id]); + rootEl.appendChild(items[id]); + } + }); + }, + + + /** + * Save the current sorting + */ + save: function () { + var store = this.options.store; + store && store.set(this); + }, + + + /** + * For each element in the set, get the first element that matches the selector by testing the element itself and traversing up through its ancestors in the DOM tree. + * @param {HTMLElement} el + * @param {String} [selector] default: `options.draggable` + * @returns {HTMLElement|null} + */ + closest: function (el, selector) { + return _closest(el, selector || this.options.draggable, this.el); + }, + + + /** + * Set/get option + * @param {string} name + * @param {*} [value] + * @returns {*} + */ + option: function (name, value) { + var options = this.options; + + if (value === void 0) { + return options[name]; + } else { + options[name] = value; + } + }, + + + /** + * Destroy + */ + destroy: function () { + var el = this.el; + + el[expando] = null; + + _off(el, 'mousedown', this._onTapStart); + _off(el, 'touchstart', this._onTapStart); + + _off(el, 'dragover', this); + _off(el, 'dragenter', this); + + // Remove draggable attributes + Array.prototype.forEach.call(el.querySelectorAll('[draggable]'), function (el) { + el.removeAttribute('draggable'); + }); + + touchDragOverListeners.splice(touchDragOverListeners.indexOf(this._onDragOver), 1); + + this._onDrop(); + + this.el = el = null; + } + }; + + + function _cloneHide(state) { + if (cloneEl && (cloneEl.state !== state)) { + _css(cloneEl, 'display', state ? 'none' : ''); + !state && cloneEl.state && rootEl.insertBefore(cloneEl, dragEl); + cloneEl.state = state; + } + } + + + function _bind(ctx, fn) { + var args = slice.call(arguments, 2); + return fn.bind ? fn.bind.apply(fn, [ctx].concat(args)) : function () { + return fn.apply(ctx, args.concat(slice.call(arguments))); + }; + } + + + function _closest(/**HTMLElement*/el, /**String*/selector, /**HTMLElement*/ctx) { + if (el) { + ctx = ctx || document; + selector = selector.split('.'); + + var tag = selector.shift().toUpperCase(), + re = new RegExp('\\s(' + selector.join('|') + ')\\s', 'g'); + + do { + if ( + (tag === '>*' && el.parentNode === ctx) || ( + (tag === '' || el.nodeName.toUpperCase() == tag) && + (!selector.length || ((' ' + el.className + ' ').match(re) || []).length == selector.length) + ) + ) { + return el; + } + } + while (el !== ctx && (el = el.parentNode)); + } + + return null; + } + + + function _globalDragOver(/**Event*/evt) { + evt.dataTransfer.dropEffect = 'move'; + evt.preventDefault(); + } + + + function _on(el, event, fn) { + el.addEventListener(event, fn, false); + } + + + function _off(el, event, fn) { + el.removeEventListener(event, fn, false); + } + + + function _toggleClass(el, name, state) { + if (el) { + if (el.classList) { + el.classList[state ? 'add' : 'remove'](name); + } + else { + var className = (' ' + el.className + ' ').replace(RSPACE, ' ').replace(' ' + name + ' ', ' '); + el.className = (className + (state ? ' ' + name : '')).replace(RSPACE, ' '); + } + } + } + + + function _css(el, prop, val) { + var style = el && el.style; + + if (style) { + if (val === void 0) { + if (document.defaultView && document.defaultView.getComputedStyle) { + val = document.defaultView.getComputedStyle(el, ''); + } + else if (el.currentStyle) { + val = el.currentStyle; + } + + return prop === void 0 ? val : val[prop]; + } + else { + if (!(prop in style)) { + prop = '-webkit-' + prop; + } + + style[prop] = val + (typeof val === 'string' ? '' : 'px'); + } + } + } + + + function _find(ctx, tagName, iterator) { + if (ctx) { + var list = ctx.getElementsByTagName(tagName), i = 0, n = list.length; + + if (iterator) { + for (; i < n; i++) { + iterator(list[i], i); + } + } + + return list; + } + + return []; + } + + + + function _dispatchEvent(sortable, rootEl, name, targetEl, fromEl, startIndex, newIndex) { + var evt = document.createEvent('Event'), + options = (sortable || rootEl[expando]).options, + onName = 'on' + name.charAt(0).toUpperCase() + name.substr(1); + + evt.initEvent(name, true, true); + + evt.to = rootEl; + evt.from = fromEl || rootEl; + evt.item = targetEl || rootEl; + evt.clone = cloneEl; + + evt.oldIndex = startIndex; + evt.newIndex = newIndex; + + rootEl.dispatchEvent(evt); + + if (options[onName]) { + options[onName].call(sortable, evt); + } + } + + + function _onMove(fromEl, toEl, dragEl, dragRect, targetEl, targetRect) { + var evt, + sortable = fromEl[expando], + onMoveFn = sortable.options.onMove, + retVal; + + if (onMoveFn) { + evt = document.createEvent('Event'); + evt.initEvent('move', true, true); + + evt.to = toEl; + evt.from = fromEl; + evt.dragged = dragEl; + evt.draggedRect = dragRect; + evt.related = targetEl || toEl; + evt.relatedRect = targetRect || toEl.getBoundingClientRect(); + + retVal = onMoveFn.call(sortable, evt); + } + + return retVal; + } + + + function _disableDraggable(el) { + el.draggable = false; + } + + + function _unsilent() { + _silent = false; + } + + + /** @returns {HTMLElement|false} */ + function _ghostInBottom(el, evt) { + var lastEl = el.lastElementChild, + rect = lastEl.getBoundingClientRect(); + + return (evt.clientY - (rect.top + rect.height) > 5) && lastEl; // min delta + } + + + /** + * Generate id + * @param {HTMLElement} el + * @returns {String} + * @private + */ + function _generateId(el) { + var str = el.tagName + el.className + el.src + el.href + el.textContent, + i = str.length, + sum = 0; + + while (i--) { + sum += str.charCodeAt(i); + } + + return sum.toString(36); + } + + /** + * Returns the index of an element within its parent + * @param el + * @returns {number} + * @private + */ + function _index(/**HTMLElement*/el) { + var index = 0; + while (el && (el = el.previousElementSibling)) { + if (el.nodeName.toUpperCase() !== 'TEMPLATE') { + index++; + } + } + return index; + } + + function _throttle(callback, ms) { + var args, _this; + + return function () { + if (args === void 0) { + args = arguments; + _this = this; + + setTimeout(function () { + if (args.length === 1) { + callback.call(_this, args[0]); + } else { + callback.apply(_this, args); + } + + args = void 0; + }, ms); + } + }; + } + + function _extend(dst, src) { + if (dst && src) { + for (var key in src) { + if (src.hasOwnProperty(key)) { + dst[key] = src[key]; + } + } + } + + return dst; + } + + + // Export utils + Sortable.utils = { + on: _on, + off: _off, + css: _css, + find: _find, + bind: _bind, + is: function (el, selector) { + return !!_closest(el, selector, el); + }, + extend: _extend, + throttle: _throttle, + closest: _closest, + toggleClass: _toggleClass, + index: _index + }; + + + Sortable.version = '1.2.0'; + + + /** + * Create sortable instance + * @param {HTMLElement} el + * @param {Object} [options] + */ + Sortable.create = function (el, options) { + return new Sortable(el, options); + }; + + // Export + return Sortable; +}); diff --git a/src/categories.js b/src/categories.js index 6112c5dc58..1a51c192e6 100644 --- a/src/categories.js +++ b/src/categories.js @@ -159,7 +159,11 @@ var async = require('async'), category.disabled = parseInt(category.disabled, 10) === 1; category.icon = category.icon || 'hidden'; if (category.hasOwnProperty('post_count')) { - category.post_count = category.post_count || 0; + category.post_count = category.totalPostCount = category.post_count || 0; + } + + if (category.hasOwnProperty('topic_count')) { + category.topic_count = category.totalTopicCount = category.topic_count || 0; } if (category.description) { @@ -240,7 +244,7 @@ var async = require('async'), if (categories[i]) { categories[i]['unread-class'] = (parseInt(categories[i].topic_count, 10) === 0 || (hasRead[i] && uid !== 0)) ? '' : 'unread'; categories[i].children = results.children[i]; - categories[i].parent = results.parents[i] && !results.parents[i].disabled ? results.parents[i] : null; + categories[i].parent = results.parents[i] || undefined; calculateTopicPostCount(categories[i]); } } @@ -250,18 +254,23 @@ var async = require('async'), }; function calculateTopicPostCount(category) { - if (!Array.isArray(category.children) || !category.children.length) { + if (!category) { return; } var postCount = parseInt(category.post_count, 10) || 0; var topicCount = parseInt(category.topic_count, 10) || 0; + if (!Array.isArray(category.children) || !category.children.length) { + category.totalPostCount = postCount; + category.totalTopicCount = topicCount; + return; + } category.children.forEach(function(child) { postCount += parseInt(child.post_count, 10) || 0; topicCount += parseInt(child.topic_count, 10) || 0; }); - category.post_count = postCount; - category.topic_count = topicCount; + category.totalPostCount = postCount; + category.totalTopicCount = topicCount; } Categories.getParents = function(cids, callback) { @@ -301,4 +310,28 @@ var async = require('async'), ], callback); }; + /** + * Recursively build tree + * + * @param categories {array} flat list of categories + * @param parentCid {number} start from 0 to build full tree + */ + Categories.getTree = function(categories, parentCid) { + var tree = [], i = 0, len = categories.length, category; + + for(i; i < len; ++i) { + category = categories[i]; + if (!category.hasOwnProperty('parentCid')) { + category.parentCid = 0; + } + + if(category.parentCid == parentCid){ + tree.push(category); + category.children = Categories.getTree(categories, category.cid); + } + } + + return tree; + }; + }(exports)); diff --git a/src/categories/create.js b/src/categories/create.js index b18f2b17dc..1b47f87760 100644 --- a/src/categories/create.js +++ b/src/categories/create.js @@ -3,6 +3,7 @@ var async = require('async'), db = require('../database'), privileges = require('../privileges'), + plugins = require('../plugins'), utils = require('../../public/src/utils'); module.exports = function(Categories) { @@ -37,17 +38,23 @@ module.exports = function(Categories) { imageClass: 'auto' }; + plugins.fireHook('filter:category.create', {category: category}, next); + }, + function(data, next) { + category = data.category; + var defaultPrivileges = ['find', 'read', 'topics:create', 'topics:reply']; async.series([ - async.apply(db.setObject, 'category:' + cid, category), - async.apply(db.sortedSetAdd, 'categories:cid', order, cid), - async.apply(privileges.categories.give, defaultPrivileges, cid, 'administrators'), - async.apply(privileges.categories.give, defaultPrivileges, cid, 'registered-users'), - async.apply(privileges.categories.give, ['find', 'read'], cid, 'guests') + async.apply(db.setObject, 'category:' + category.cid, category), + async.apply(db.sortedSetAdd, 'categories:cid', category.order, category.cid), + async.apply(privileges.categories.give, defaultPrivileges, category.cid, 'administrators'), + async.apply(privileges.categories.give, defaultPrivileges, category.cid, 'registered-users'), + async.apply(privileges.categories.give, ['find', 'read'], category.cid, 'guests') ], next); }, function(results, next) { + plugins.fireHook('action:category.create', category); next(null, category); } ], callback); diff --git a/src/categories/delete.js b/src/categories/delete.js index 0ebac949d7..1679ab6731 100644 --- a/src/categories/delete.js +++ b/src/categories/delete.js @@ -3,6 +3,7 @@ var async = require('async'), db = require('../database'), batch = require('../batch'), + plugins = require('../plugins'), threadTools = require('../threadTools'); @@ -17,7 +18,10 @@ module.exports = function(Categories) { if (err) { return callback(err); } - purgeCategory(cid, callback); + async.series([ + async.apply(purgeCategory, cid), + async.apply(plugins.fireHook, 'action:category.delete', cid) + ], callback); }); }; diff --git a/src/categories/update.js b/src/categories/update.js index 6fcf7d4b57..f1d9ee773c 100644 --- a/src/categories/update.js +++ b/src/categories/update.js @@ -20,7 +20,13 @@ module.exports = function(Categories) { var fields = Object.keys(category); async.each(fields, function(key, next) { updateCategoryField(cid, key, category[key], next); - }, next); + }, function(err) { + if (err) { + return next(err); + } + plugins.fireHook('action:category.update', {cid: cid, modified: category}); + next(); + }); }); }); } diff --git a/src/controllers/accounts.js b/src/controllers/accounts.js index 55ff41656b..3fdb810c83 100644 --- a/src/controllers/accounts.js +++ b/src/controllers/accounts.js @@ -80,6 +80,7 @@ function getUserDataByUserSlug(userslug, callerUID, callback) { userData.uid = userData.uid; userData.yourid = callerUID; userData.theirid = userData.uid; + userData.isAdmin = isAdmin; userData.isSelf = self; userData.showHidden = self || isAdmin; userData.groups = Array.isArray(results.groups) && results.groups.length ? results.groups[0] : []; @@ -88,7 +89,8 @@ function getUserDataByUserSlug(userslug, callerUID, callback) { userData.profile_links = results.profile_links; userData.status = require('../socket.io').isUserOnline(userData.uid) ? (userData.status || 'online') : 'offline'; userData.banned = parseInt(userData.banned, 10) === 1; - userData.websiteName = userData.website.replace('http://', '').replace('https://', ''); + userData.website = validator.escape(userData.website); + userData.websiteName = userData.website.replace(validator.escape('http://'), '').replace(validator.escape('https://'), ''); userData.followingCount = parseInt(userData.followingCount, 10) || 0; userData.followerCount = parseInt(userData.followerCount, 10) || 0; @@ -254,12 +256,13 @@ accountsController.getGroups = function(req, res, next) { return helpers.notFound(req, res); } - groups.getUserGroups([userData.uid], function(err, groups) { + groups.getUserGroups([userData.uid], function(err, groupsData) { if (err) { return next(err); } - userData.groups = groups[0]; + userData.groups = groupsData[0]; + userData.groups.forEach(groups.escapeGroupData); res.render('account/groups', userData); }); diff --git a/src/controllers/admin.js b/src/controllers/admin.js index 50c8deb009..6ad32991eb 100644 --- a/src/controllers/admin.js +++ b/src/controllers/admin.js @@ -15,7 +15,6 @@ var async = require('async'), events = require('../events'), languages = require('../languages'), plugins = require('../plugins'), - groups = require('../groups'), validator = require('validator'); @@ -24,7 +23,7 @@ var adminController = { tags: {}, flags: {}, topics: {}, - groups: {}, + groups: require('./admin/groups'), appearance: {}, extend: { widgets: {} @@ -52,9 +51,25 @@ adminController.home = function(req, res, next) { }, notices: function(next) { var notices = [ - {done: !meta.reloadRequired, doneText: 'Reload not required', notDoneText:'Reload required'}, - {done: plugins.hasListeners('action:email.send'), doneText: 'Emailer Installed', notDoneText:'Emailer not installed'}, - {done: plugins.hasListeners('filter:search.query'), doneText: 'Search Plugin Installed', notDoneText:'Search Plugin not installed'} + { + done: !meta.reloadRequired, + doneText: 'Reload not required', + notDoneText:'Reload required' + }, + { + done: plugins.hasListeners('action:email.send'), + doneText: 'Emailer Installed', + notDoneText:'Emailer not installed', + tooltip:'Install an emailer plugin from the plugin page in order to activate registration emails and email digests', + link:'/admin/extend/plugins' + }, + { + done: plugins.hasListeners('filter:search.query'), + doneText: 'Search Plugin Installed', + notDoneText:'Search Plugin not installed', + tooltip: 'Install a search plugin from the plugin page in order to activate search functionality', + link:'/admin/extend/plugins' + } ]; plugins.fireHook('filter:admin.notices', notices, next); } @@ -150,32 +165,8 @@ adminController.categories.get = function(req, res, next) { }; adminController.categories.getAll = function(req, res, next) { - var active = [], - disabled = []; - - async.waterfall([ - function(next) { - db.getSortedSetRange('categories:cid', 0, -1, next); - }, - function(cids, next) { - categories.getCategoriesData(cids, next); - }, - function(categories, next) { - plugins.fireHook('filter:admin.categories.get', {req: req, res: res, categories: categories}, next); - } - ], function(err, data) { - if (err) { - return next(err); - } - data.categories.filter(Boolean).forEach(function(category) { - (category.disabled ? disabled : active).push(category); - }); - - res.render('admin/manage/categories', { - active: active, - disabled: disabled - }); - }); + //Categories list will be rendered on client side with recursion, etc. + res.render('admin/manage/categories', {}); }; adminController.tags.get = function(req, res, next) { @@ -294,9 +285,11 @@ adminController.languages.get = function(req, res, next) { if (err) { return next(err); } + languages.forEach(function(language) { - language.selected = language.code === meta.config.defaultLang; + language.selected = language.code === (meta.config.defaultLang || 'en_GB'); }); + res.render('admin/general/languages', { languages: languages }); @@ -382,23 +375,6 @@ adminController.extend.rewards = function(req, res, next) { }); }; -adminController.groups.get = function(req, res, next) { - groups.list({ - expand: true, - truncateUserList: true, - isAdmin: true, - showSystemGroups: true - }, function(err, groups) { - groups = groups.filter(function(group) { - return group.name !== 'registered-users' && group.name !== 'guests' && group.name.indexOf(':privileges:') === -1; - }); - res.render('admin/manage/groups', { - groups: groups, - yourid: req.user.uid - }); - }); -}; - adminController.themes.get = function(req, res, next) { var themeDir = path.join(__dirname, '../../node_modules/' + req.params.theme); fs.exists(themeDir, function(exists) { diff --git a/src/controllers/admin/groups.js b/src/controllers/admin/groups.js new file mode 100644 index 0000000000..05c7294fdc --- /dev/null +++ b/src/controllers/admin/groups.js @@ -0,0 +1,49 @@ +"use strict"; + +var async = require('async'), + groups = require('../../groups'), + meta = require('../../meta'), + helpers = require('../helpers'); + + +var groupsController = {}; + + +groupsController.list = function(req, res, next) { + groups.getGroupsFromSet('groups:createtime', req.uid, 0, -1, function(err, groups) { + if (err) { + return next(err); + } + + groups = groups.filter(function(group) { + return group && group.name.indexOf(':privileges:') === -1 && group.name !== 'registered-users'; + }); + + res.render('admin/manage/groups', { + groups: groups, + yourid: req.user.uid + }); + }); +}; + +groupsController.get = function(req, res, next) { + var groupName = req.params.name; + async.waterfall([ + function(next){ + groups.exists(groupName, next); + }, + function(exists, next) { + if (!exists) { + helpers.notFound(req, res); + } + groups.get(groupName, {uid: req.uid}, next); + } + ], function(err, group) { + if (err) { + return next(err); + } + res.render('admin/manage/group', {group: group}); + }); +}; + +module.exports = groupsController; diff --git a/src/controllers/admin/users.js b/src/controllers/admin/users.js index 7f884b79ce..6c10694ba6 100644 --- a/src/controllers/admin/users.js +++ b/src/controllers/admin/users.js @@ -30,6 +30,15 @@ usersController.banned = function(req, res, next) { getUsers('users:banned', req, res, next); }; +usersController.registrationQueue = function(req, res, next) { + user.getRegistrationQueue(0, -1, function(err, data) { + if (err) { + return next(err); + } + res.render('admin/manage/registration', {users: data}); + }) +}; + function getUsers(set, req, res, next) { user.getUsersFromSet(set, req.uid, 0, 49, function(err, users) { if (err) { diff --git a/src/controllers/api.js b/src/controllers/api.js index a5a5c14e68..9e024f92e9 100644 --- a/src/controllers/api.js +++ b/src/controllers/api.js @@ -42,6 +42,7 @@ apiController.getConfig = function(req, res, next) { config.maximumAboutMeLength = meta.config.maximumAboutMeLength || 1000; config.useOutgoingLinksPage = parseInt(meta.config.useOutgoingLinksPage, 10) === 1; config.allowGuestSearching = parseInt(meta.config.allowGuestSearching, 10) === 1; + config.allowGuestUserSearching = parseInt(meta.config.allowGuestUserSearching, 10) === 1; config.allowGuestHandles = parseInt(meta.config.allowGuestHandles, 10) === 1; config.allowFileUploads = parseInt(meta.config.allowFileUploads, 10) === 1; config.allowProfileImageUploads = parseInt(meta.config.allowProfileImageUploads) === 1; diff --git a/src/controllers/authentication.js b/src/controllers/authentication.js new file mode 100644 index 0000000000..c303fb825d --- /dev/null +++ b/src/controllers/authentication.js @@ -0,0 +1,273 @@ +"use strict"; + +var async = require('async'), + winston = require('winston'), + passport = require('passport'), + nconf = require('nconf'), + validator = require('validator'), + + db = require('../database'), + meta = require('../meta'), + user = require('../user'), + plugins = require('../plugins'), + utils = require('../../public/src/utils'), + Password = require('../password'), + + authenticationController = {}; + +authenticationController.register = function(req, res, next) { + var registrationType = meta.config.registrationType || 'normal'; + + if (registrationType === 'disabled') { + return res.sendStatus(403); + } + + var userData = {}; + + for (var key in req.body) { + if (req.body.hasOwnProperty(key)) { + userData[key] = req.body[key]; + } + } + + var uid; + async.waterfall([ + function(next) { + if (registrationType === 'invite-only') { + user.verifyInvitation(userData, next); + } else { + next(); + } + }, + function(next) { + console.log(userData); + if (!userData.email) { + return next(new Error('[[error:invalid-email]]')); + } + + if (!userData.username || userData.username.length < meta.config.minimumUsernameLength) { + return next(new Error('[[error:username-too-short]]')); + } + + if (userData.username.length > meta.config.maximumUsernameLength) { + return next(new Error('[[error:username-too-long')); + } + + if (!userData.password || userData.password.length < meta.config.minimumPasswordLength) { + return next(new Error('[[user:change_password_error_length]]')); + } + + next(); + }, + function(next) { + plugins.fireHook('filter:register.check', {req: req, res: res, userData: userData}, next); + }, + function(data, next) { + if (registrationType === 'normal' || registrationType === 'invite-only') { + registerAndLoginUser(req, res, userData, next); + } else if (registrationType === 'admin-approval') { + addToApprovalQueue(req, res, userData, next); + } + } + ], function(err, data) { + if (err) { + return res.status(400).send(err.message); + } + + res.json(data); + }); +}; + +function registerAndLoginUser(req, res, userData, callback) { + var uid; + async.waterfall([ + function(next) { + user.create(userData, next); + }, + function(_uid, next) { + uid = _uid; + req.login({uid: uid}, next); + }, + function(next) { + user.logIP(uid, req.ip); + + user.deleteInvitation(userData.email); + + user.notifications.sendWelcomeNotification(uid); + + plugins.fireHook('filter:register.complete', {uid: uid, referrer: req.body.referrer || nconf.get('relative_path') + '/'}, next); + } + ], callback); +} + +function addToApprovalQueue(req, res, userData, callback) { + async.waterfall([ + function(next) { + userData.ip = req.ip; + user.addToApprovalQueue(userData, next); + }, + function(next) { + next(null, {message: '[[register:registration-added-to-queue]]'}); + } + ], callback); +} + +authenticationController.login = function(req, res, next) { + // Handle returnTo data + if (req.body.hasOwnProperty('returnTo') && !req.session.returnTo) { + req.session.returnTo = req.body.returnTo; + } + + if (plugins.hasListeners('action:auth.overrideLogin')) { + return continueLogin(req, res, next); + } + + var loginWith = meta.config.allowLoginWith || 'username-email'; + + if (req.body.username && utils.isEmailValid(req.body.username) && loginWith.indexOf('email') !== -1) { + user.getUsernameByEmail(req.body.username, function(err, username) { + if (err) { + return next(err); + } + req.body.username = username ? username : req.body.username; + continueLogin(req, res, next); + }); + } else if (loginWith.indexOf('username') !== -1 && !validator.isEmail(req.body.username)) { + continueLogin(req, res, next); + } else { + res.status(500).send('[[error:wrong-login-type-' + loginWith + ']]'); + } +}; + +function continueLogin(req, res, next) { + passport.authenticate('local', function(err, userData, info) { + if (err) { + return res.status(403).send(err.message); + } + + if (!userData) { + if (typeof info === 'object') { + info = '[[error:invalid-username-or-password]]'; + } + + return res.status(403).send(info); + } + + var passwordExpiry = userData.passwordExpiry !== undefined ? parseInt(userData.passwordExpiry, 10) : null; + + // Alter user cookie depending on passed-in option + if (req.body.remember === 'on') { + var duration = 1000*60*60*24*parseInt(meta.config.loginDays || 14, 10); + req.session.cookie.maxAge = duration; + req.session.cookie.expires = new Date(Date.now() + duration); + } else { + req.session.cookie.maxAge = false; + req.session.cookie.expires = false; + } + + if (passwordExpiry && passwordExpiry < Date.now()) { + winston.verbose('[auth] Triggering password reset for uid ' + userData.uid + ' due to password policy'); + req.session.passwordExpired = true; + user.reset.generate(userData.uid, function(err, code) { + res.status(200).send(nconf.get('relative_path') + '/reset/' + code); + }); + } else { + req.login({ + uid: userData.uid + }, function(err) { + if (err) { + return res.status(403).send(err.message); + } + if (userData.uid) { + user.logIP(userData.uid, req.ip); + + plugins.fireHook('action:user.loggedIn', userData.uid); + } + + if (!req.session.returnTo) { + res.status(200).send(nconf.get('relative_path') + '/'); + } else { + var next = req.session.returnTo; + delete req.session.returnTo; + + res.status(200).send(next); + } + }); + } + })(req, res, next); +} + +authenticationController.localLogin = function(req, username, password, next) { + if (!username || !password) { + return next(new Error('[[error:invalid-password]]')); + } + + var userslug = utils.slugify(username); + var uid, userData = {}; + + async.waterfall([ + function(next) { + user.getUidByUserslug(userslug, next); + }, + function(_uid, next) { + if (!_uid) { + return next(new Error('[[error:no-user]]')); + } + uid = _uid; + user.auth.logAttempt(uid, req.ip, next); + }, + function(next) { + async.parallel({ + userData: function(next) { + db.getObjectFields('user:' + uid, ['password', 'banned', 'passwordExpiry'], next); + }, + isAdmin: function(next) { + user.isAdministrator(uid, next); + } + }, next); + }, + function(result, next) { + userData = result.userData; + userData.uid = uid; + userData.isAdmin = result.isAdmin; + + if (!result.isAdmin && parseInt(meta.config.allowLocalLogin, 10) === 0) { + return next(new Error('[[error:local-login-disabled]]')); + } + + if (!userData || !userData.password) { + return next(new Error('[[error:invalid-user-data]]')); + } + if (userData.banned && parseInt(userData.banned, 10) === 1) { + return next(new Error('[[error:user-banned]]')); + } + Password.compare(password, userData.password, next); + }, + function(passwordMatch, next) { + if (!passwordMatch) { + return next(new Error('[[error:invalid-password]]')); + } + user.auth.clearLoginAttempts(uid); + next(null, userData, '[[success:authentication-successful]]'); + } + ], next); +}; + +authenticationController.logout = function(req, res, next) { + if (req.user && parseInt(req.user.uid, 10) > 0 && req.sessionID) { + + require('../socket.io').logoutUser(req.user.uid); + db.sessionStore.destroy(req.sessionID, function(err) { + if (err) { + return next(err); + } + req.logout(); + res.status(200).send(''); + }); + } else { + res.status(200).send(''); + } +}; + + +module.exports = authenticationController; diff --git a/src/controllers/groups.js b/src/controllers/groups.js index e3bdd500a6..8d7ddac3e2 100644 --- a/src/controllers/groups.js +++ b/src/controllers/groups.js @@ -2,6 +2,7 @@ var async = require('async'), nconf = require('nconf'), + db = require('../database'), meta = require('../meta'), groups = require('../groups'), user = require('../user'), @@ -9,17 +10,33 @@ var async = require('async'), groupsController = {}; groupsController.list = function(req, res, next) { - groups.list({ - truncateUserList: true, - expand: true, - uid: req.uid - }, function(err, groups) { + var sort = req.query.sort || 'alpha'; + + groupsController.getGroupsFromSet(req.uid, sort, 0, 14, function(err, data) { if (err) { return next(err); } - res.render('groups/list', { + res.render('groups/list', data); + }); +}; + +groupsController.getGroupsFromSet = function(uid, sort, start, stop, callback) { + var set = 'groups:visible:name'; + if (sort === 'count') { + set = 'groups:visible:memberCount'; + } else if (sort === 'date') { + set = 'groups:visible:createtime'; + } + + groups.getGroupsFromSet(set, uid, start, stop, function(err, groups) { + if (err) { + return callback(err); + } + + callback(null, { groups: groups, - allowGroupCreation: parseInt(meta.config.allowGroupCreation, 10) === 1 + allowGroupCreation: parseInt(meta.config.allowGroupCreation, 10) === 1, + nextStart: stop + 1 }); }); }; @@ -62,7 +79,6 @@ groupsController.details = function(req, res, next) { async.parallel({ group: function(next) { groups.get(res.locals.groupName, { - expand: true, uid: req.uid }, next); }, diff --git a/src/controllers/helpers.js b/src/controllers/helpers.js index 8d4ccb8895..6a35540088 100644 --- a/src/controllers/helpers.js +++ b/src/controllers/helpers.js @@ -57,15 +57,17 @@ helpers.buildCategoryBreadcrumbs = function(cid, callback) { async.whilst(function() { return parseInt(cid, 10); }, function(next) { - categories.getCategoryFields(cid, ['name', 'slug', 'parentCid'], function(err, data) { + categories.getCategoryFields(cid, ['name', 'slug', 'parentCid', 'disabled'], function(err, data) { if (err) { return next(err); } - breadcrumbs.unshift({ - text: validator.escape(data.name), - url: nconf.get('relative_path') + '/category/' + data.slug - }); + if (!parseInt(data.disabled, 10)) { + breadcrumbs.unshift({ + text: validator.escape(data.name), + url: nconf.get('relative_path') + '/category/' + data.slug + }); + } cid = data.parentCid; next(); diff --git a/src/controllers/index.js b/src/controllers/index.js index 7a2f225290..ae6cb5c497 100644 --- a/src/controllers/index.js +++ b/src/controllers/index.js @@ -5,7 +5,6 @@ var async = require('async'), validator = require('validator'), winston = require('winston'), - auth = require('../routes/authentication'), meta = require('../meta'), user = require('../user'), posts = require('../posts'), @@ -24,7 +23,7 @@ var Controllers = { users: require('./users'), groups: require('./groups'), accounts: require('./accounts'), - static: require('./static'), + authentication: require('./authentication'), api: require('./api'), admin: require('./admin') }; @@ -75,14 +74,16 @@ Controllers.reset = function(req, res, next) { Controllers.login = function(req, res, next) { var data = {}, - loginStrategies = auth.getLoginStrategies(), + loginStrategies = require('../routes/authentication').getLoginStrategies(), emailersPresent = plugins.hasListeners('action:email.send'); + var registrationType = meta.config.registrationType || 'normal'; + data.alternate_logins = loginStrategies.length > 0; data.authentication = loginStrategies; data.showResetLink = emailersPresent; data.allowLocalLogin = parseInt(meta.config.allowLocalLogin, 10) === 1 || parseInt(req.query.local, 10) === 1; - data.allowRegistration = parseInt(meta.config.allowRegistration, 10) === 1; + data.allowRegistration = registrationType === 'normal' || registrationType === 'admin-approval'; data.allowLoginWith = '[[login:' + (meta.config.allowLoginWith || 'username-email') + ']]'; data.breadcrumbs = helpers.buildBreadcrumbs([{text: '[[global:login]]'}]); data.error = req.flash('error')[0]; @@ -91,44 +92,54 @@ Controllers.login = function(req, res, next) { }; Controllers.register = function(req, res, next) { - if(meta.config.allowRegistration !== undefined && parseInt(meta.config.allowRegistration, 10) === 0) { - return res.redirect(nconf.get('relative_path') + '/403'); + var registrationType = meta.config.registrationType || 'normal'; + + if (registrationType === 'disabled') { + return helpers.notFound(req, res); } - var data = {}, - loginStrategies = auth.getLoginStrategies(); + async.waterfall([ + function(next) { + if (registrationType === 'invite-only') { + user.verifyInvitation(req.query, next); + } else { + next(); + } + }, + function(next) { + var loginStrategies = require('../routes/authentication').getLoginStrategies(); + var data = { + 'register_window:spansize': loginStrategies.length ? 'col-md-6' : 'col-md-12', + 'alternate_logins': !!loginStrategies.length + }; - if (loginStrategies.length === 0) { - data = { - 'register_window:spansize': 'col-md-12', - 'alternate_logins': false - }; - } else { - data = { - 'register_window:spansize': 'col-md-6', - 'alternate_logins': true - }; - } + data.authentication = loginStrategies; - data.authentication = loginStrategies; + data.minimumUsernameLength = meta.config.minimumUsernameLength; + data.maximumUsernameLength = meta.config.maximumUsernameLength; + data.minimumPasswordLength = meta.config.minimumPasswordLength; + data.termsOfUse = meta.config.termsOfUse; + data.breadcrumbs = helpers.buildBreadcrumbs([{text: '[[register:register]]'}]); + data.regFormEntry = []; + data.error = req.flash('error')[0]; - data.minimumUsernameLength = meta.config.minimumUsernameLength; - data.maximumUsernameLength = meta.config.maximumUsernameLength; - data.minimumPasswordLength = meta.config.minimumPasswordLength; - data.termsOfUse = meta.config.termsOfUse; - data.breadcrumbs = helpers.buildBreadcrumbs([{text: '[[register:register]]'}]); - data.regFormEntry = []; - data.error = req.flash('error')[0]; - - plugins.fireHook('filter:register.build', {req: req, res: res, templateData: data}, function(err, data) { - if (err && global.env === 'development') { - winston.warn(JSON.stringify(err)); + plugins.fireHook('filter:register.build', {req: req, res: res, templateData: data}, next); + } + ], function(err, data) { + if (err) { return next(err); } res.render('register', data.templateData); }); }; +Controllers.compose = function(req, res, next) { + if (req.query.p && !res.locals.isAPI) { + return helpers.redirect(res, req.query.p); + } + + res.render('', {}); +}; Controllers.confirmEmail = function(req, res, next) { user.email.confirm(req.params.code, function (err) { diff --git a/src/controllers/static.js b/src/controllers/static.js deleted file mode 100644 index a2355ef8dd..0000000000 --- a/src/controllers/static.js +++ /dev/null @@ -1,23 +0,0 @@ -"use strict"; - -var staticController = {}; - -createStatic('404'); -createStatic('403'); -createStatic('500'); - -function createStatic(statusCode) { - staticController[statusCode] = function(req, res) { - if (!res.locals.isAPI) { - res.statusCode = parseInt(statusCode, 10); - } - - res.render(statusCode, { - errorMessage: req.flash('errorMessage')[0] || undefined - }); - }; -} - -module.exports = staticController; - - diff --git a/src/controllers/users.js b/src/controllers/users.js index 29dd552b87..e2298ade29 100644 --- a/src/controllers/users.js +++ b/src/controllers/users.js @@ -36,7 +36,7 @@ usersController.getOnlineUsers = function(req, res, next) { } var userData = { - 'route_users:online': true, + 'users:online': true, search_display: 'hidden', loadmore_display: results.count > 50 ? 'block' : 'hide', users: results.users, @@ -48,38 +48,38 @@ usersController.getOnlineUsers = function(req, res, next) { }; usersController.getUsersSortedByPosts = function(req, res, next) { - usersController.getUsers('users:postcount', 50, req, res, next); + usersController.getUsers('users:postcount', 0, 49, req, res, next); }; usersController.getUsersSortedByReputation = function(req, res, next) { - usersController.getUsers('users:reputation', 50, req, res, next); + usersController.getUsers('users:reputation', 0, 49, req, res, next); }; usersController.getUsersSortedByJoinDate = function(req, res, next) { - usersController.getUsers('users:joindate', 50, req, res, next); + usersController.getUsers('users:joindate', 0, 49, req, res, next); }; -usersController.getUsers = function(set, count, req, res, next) { - getUsersAndCount(set, req.uid, count, function(err, data) { +usersController.getUsers = function(set, start, stop, req, res, next) { + usersController.getUsersAndCount(set, req.uid, start, stop, function(err, data) { if (err) { return next(err); } var pageCount = Math.ceil(data.count / (parseInt(meta.config.userSearchResultsPerPage, 10) || 20)); var userData = { search_display: 'hidden', - loadmore_display: data.count > count ? 'block' : 'hide', + loadmore_display: data.count > (stop - start + 1) ? 'block' : 'hide', users: data.users, pagination: pagination.create(1, pageCount) }; - userData['route_' + set] = true; + userData[set] = true; render(req, res, userData, next); }); }; -function getUsersAndCount(set, uid, count, callback) { +usersController.getUsersAndCount = function(set, uid, start, stop, callback) { async.parallel({ users: function(next) { - user.getUsersFromSet(set, uid, 0, count - 1, next); + user.getUsersFromSet(set, uid, start, stop, next); }, count: function(next) { db.getObjectField('global', 'userCount', next); @@ -94,15 +94,15 @@ function getUsersAndCount(set, uid, count, callback) { callback(null, results); }); -} +}; usersController.getUsersForSearch = function(req, res, next) { - if (!req.uid) { + if (!req.uid && parseInt(meta.config.allowGuestUserSearching, 10) !== 1) { return helpers.notAllowed(req, res); } var resultsPerPage = parseInt(meta.config.userSearchResultsPerPage, 10) || 20; - getUsersAndCount('users:joindate', req.uid, resultsPerPage, function(err, data) { + usersController.getUsersAndCount('users:joindate', req.uid, 0, resultsPerPage - 1, function(err, data) { if (err) { return next(err); } @@ -122,6 +122,7 @@ function render(req, res, data, next) { if (err) { return next(err); } + data.templateData.inviteOnly = meta.config.registrationType === 'invite-only'; res.render('users', data.templateData); }); } diff --git a/src/database.js b/src/database.js index 40399aa028..14a5a52892 100644 --- a/src/database.js +++ b/src/database.js @@ -30,11 +30,15 @@ function setupSecondaryDB() { primaryDBhelpers = primaryDB.helpers; primaryDB.init = function(callback) { - async.parallel([primaryDBinit, secondaryDB.init], callback); + async.parallel([primaryDBinit, secondaryDB.init], function(err, results) { + callback(err); + }); }; primaryDB.close = function(callback) { - async.parallel([primaryDBclose, secondaryDB.close], callback); + async.parallel([primaryDBclose, secondaryDB.close], function(err, results) { + callback(err); + }); }; primaryDB.helpers = {}; diff --git a/src/database/mongo.js b/src/database/mongo.js index cc9bacec1c..2d9ab4ee14 100644 --- a/src/database/mongo.js +++ b/src/database/mongo.js @@ -7,8 +7,12 @@ async = require('async'), nconf = require('nconf'), session = require('express-session'), + _ = require('underscore'), + semver = require('semver'), db, mongoClient; + _.mixin(require('underscore.deep')); + module.questions = [ { name: 'mongo:host', @@ -88,6 +92,9 @@ poolSize: parseInt(nconf.get('mongo:poolSize'), 10) || 10 } }; + + connOptions = _.deepExtend((nconf.get('mongo:options') || {}), connOptions); + mongoClient.connect(connString, connOptions, function(err, _db) { if (err) { winston.error("NodeBB could not connect to your Mongo database. Mongo returned the following error: " + err.message); @@ -131,6 +138,7 @@ } function createIndices() { + winston.info('[database] Checking database indices.') async.parallel([ async.apply(createIndex, 'objects', {_key: 1, score: -1}, {background: true}), async.apply(createIndex, 'objects', {_key: 1, value: -1}, {background: true, unique: true, sparse: true}), @@ -158,6 +166,16 @@ }); }; + module.checkCompatibility = function(callback) { + var mongoPkg = require.main.require('./node_modules/mongodb/package.json'), + err = semver.lt(mongoPkg.version, '2.0.0') ? new Error('The `mongodb` package is out-of-date, please run `./nodebb setup` again.') : null; + + if (err) { + err.stacktrace = false; + } + callback(err); + }; + module.info = function(db, callback) { db.stats({scale:1024}, function(err, stats) { if(err) { diff --git a/src/database/mongo/hash.js b/src/database/mongo/hash.js index 65157d8cb9..8a833699f9 100644 --- a/src/database/mongo/hash.js +++ b/src/database/mongo/hash.js @@ -39,14 +39,14 @@ module.exports = function(db, module) { return callback(null, []); } db.collection('objects').find({_key: {$in: keys}}, {_id: 0}).toArray(function(err, data) { - if(err) { + if (err) { return callback(err); } var map = helpers.toMap(data); var returnData = []; - for(var i=0; i data.members.length; + }); + + callback(null, data.groups); }); - }); + } }; Groups.getGroups = function(start, stop, callback) { @@ -98,125 +110,47 @@ var async = require('async'), }; Groups.get = function(groupName, options, callback) { - var truncated = false, - numUsers; + if (!groupName) { + return callback(new Error('[[error:invalid-group]]')); + } + + options.escape = options.hasOwnProperty('escape') ? options.escape : true; async.parallel({ base: function (next) { - if (ephemeralGroups.indexOf(groupName) === -1) { - db.getObject('group:' + groupName, next); - } else { - next(null, internals.getEphemeralGroup(groupName)); - } + db.getObject('group:' + groupName, next); }, - users: function (next) { - db.getSortedSetRevRange('group:' + groupName + ':members', 0, -1, function (err, uids) { - if (err) { - return next(err); + owners: function (next) { + async.waterfall([ + function(next) { + db.getSetMembers('group:' + groupName + ':owners', next); + }, + function(uids, next) { + user.getUsers(uids, options.uid, next); } - - uids = uids.filter(function(uid) { - return uid && parseInt(uid, 10); - }); - - if (options.truncateUserList) { - var userListCount = parseInt(options.userListCount, 10) || 4; - if (uids.length > userListCount) { - numUsers = uids.length; - uids.length = userListCount; - truncated = true; - } - } - - if (options.expand) { - async.waterfall([ - async.apply(user.getUsers, uids, options.uid || 0), - function(users, next) { - // Filter out non-matches - users = users.filter(Boolean); - - async.mapLimit(users, 10, function(userObj, next) { - Groups.ownership.isOwner(userObj.uid, groupName, function(err, isOwner) { - if (err) { - winston.warn('[groups.get] Could not determine ownership in group `' + groupName + '` for uid `' + userObj.uid + '`: ' + err.message); - return next(null, userObj); - } - - userObj.isOwner = isOwner; - next(null, userObj); - }); - }, function(err, users) { - if (err) { - return next(); - } - - next(null, users.sort(function(a, b) { - if (a.isOwner === b.isOwner) { - return 0; - } else { - return a.isOwner && !b.isOwner ? -1 : 1; - } - })); - }); - } - ], next); - } else { - next(err, uids); - } - }); + ], next); + }, + members: function (next) { + var stop = -1; + if (options.truncateUserList) { + stop = (parseInt(options.userListCount, 10) || 4) - 1; + } + user.getUsersFromSet('group:' + groupName + ':members', options.uid, 0, stop, next); }, pending: function (next) { - db.getSetMembers('group:' + groupName + ':pending', function (err, uids) { - if (err) { - return next(err); + async.waterfall([ + function(next) { + db.getSetMembers('group:' + groupName + ':pending', next); + }, + function(uids, next) { + user.getUsersData(uids, next); } - - if (options.expand && uids.length) { - async.map(uids, user.getUserData, next); - } else { - next(err, uids); - } - }); - }, - isMember: function(next) { - // Retrieve group membership state, if uid is passed in - if (!options.uid) { - return next(); - } - - Groups.isMember(options.uid, groupName, function(err, isMember) { - if (err) { - winston.warn('[groups.get] Could not determine membership in group `' + groupName + '` for uid `' + options.uid + '`: ' + err.message); - return next(); - } - - next(null, isMember); - }); - }, - isPending: function(next) { - // Retrieve group membership state, if uid is passed in - if (!options.uid) { - return next(); - } - - db.isSetMember('group:' + groupName + ':pending', options.uid, next); + ], next); }, + isMember: async.apply(Groups.isMember, options.uid, groupName), + isPending: async.apply(Groups.isPending, options.uid, groupName), isInvited: async.apply(Groups.isInvited, options.uid, groupName), - isOwner: function(next) { - // Retrieve group ownership state, if uid is passed in - if (!options.uid) { - return next(); - } - - Groups.ownership.isOwner(options.uid, groupName, function(err, isOwner) { - if (err) { - winston.warn('[groups.get] Could not determine ownership in group `' + groupName + '` for uid `' + options.uid + '`: ' + err.message); - return next(); - } - - next(null, isOwner); - }); - } + isOwner: async.apply(Groups.ownership.isOwner, options.uid, groupName) }, function (err, results) { if (err) { return callback(err); @@ -230,32 +164,42 @@ var async = require('async'), results.base['cover:position'] = '50% 50%'; } + var ownerUids = []; + results.owners.forEach(function(user) { + if (user) { + user.isOwner = true; + ownerUids.push(user.uid.toString()); + } + }); + + results.members = results.members.filter(function(user, index, array) { + return user && user.uid && ownerUids.indexOf(user.uid.toString()) === -1; + }); + results.members = results.owners.concat(results.members); + plugins.fireHook('filter:parse.raw', results.base.description, function(err, descriptionParsed) { if (err) { return callback(err); } - results.base.name = !options.unescape ? validator.escape(results.base.name) : results.base.name; - results.base.description = !options.unescape ? validator.escape(results.base.description) : results.base.description; + + if (options.escape) { + Groups.escapeGroupData(results.base); + } + results.base.descriptionParsed = descriptionParsed; - results.base.userTitle = !options.unescape ? validator.escape(results.base.userTitle) : results.base.userTitle; results.base.userTitleEnabled = results.base.userTitleEnabled ? !!parseInt(results.base.userTitleEnabled, 10) : true; results.base.createtimeISO = utils.toISOString(results.base.createtime); - results.base.members = results.users.filter(Boolean); + results.base.members = results.members; results.base.pending = results.pending.filter(Boolean); - results.base.count = numUsers || results.base.members.length; - results.base.memberCount = numUsers || results.base.members.length; results.base.deleted = !!parseInt(results.base.deleted, 10); results.base.hidden = !!parseInt(results.base.hidden, 10); results.base.system = !!parseInt(results.base.system, 10); results.base.private = results.base.private ? !!parseInt(results.base.private, 10) : true; - results.base.deletable = !results.base.system; - results.base.truncated = truncated; results.base.isMember = results.isMember; results.base.isPending = results.isPending; results.base.isInvited = results.isInvited; results.base.isOwner = results.isOwner; - plugins.fireHook('filter:group.get', {group: results.base}, function(err, data) { callback(err, data ? data.group : null); }); @@ -263,6 +207,15 @@ var async = require('async'), }); }; + Groups.escapeGroupData = function(group) { + if (group) { + group.nameEncoded = encodeURIComponent(group.name); + group.name = validator.escape(group.name); + group.description = validator.escape(group.description); + group.userTitle = validator.escape(group.userTitle) || group.name; + } + }; + Groups.getByGroupslug = function(slug, options, callback) { db.getObjectField('groupslug:groupname', slug, function(err, groupName) { if (err) { @@ -292,8 +245,13 @@ var async = require('async'), }; Groups.setGroupField = function(groupName, field, value, callback) { - plugins.fireHook('action:group.set', {field: field, value: value, type: 'set'}); - db.setObjectField('group:' + groupName, field, value, callback); + db.setObjectField('group:' + groupName, field, value, function(err) { + if (err) { + return callback(err); + } + plugins.fireHook('action:group.set', {field: field, value: value, type: 'set'}); + callback(); + }); }; Groups.isPrivate = function(groupName, callback) { @@ -319,139 +277,6 @@ var async = require('async'), }); }; - Groups.getMembers = function(groupName, start, stop, callback) { - db.getSortedSetRevRange('group:' + groupName + ':members', start, stop, callback); - }; - - Groups.getMembersOfGroups = function(groupNames, callback) { - db.getSortedSetsMembers(groupNames.map(function(name) { - return 'group:' + name + ':members'; - }), callback); - }; - - Groups.isMember = function(uid, groupName, callback) { - if (!uid || parseInt(uid, 10) <= 0) { - return callback(null, false); - } - db.isSortedSetMember('group:' + groupName + ':members', uid, callback); - }; - - Groups.isMembers = function(uids, groupName, callback) { - db.isSortedSetMembers('group:' + groupName + ':members', uids, callback); - }; - - Groups.isMemberOfGroups = function(uid, groups, callback) { - if (!uid || parseInt(uid, 10) <= 0) { - return callback(null, groups.map(function() {return false;})); - } - groups = groups.map(function(groupName) { - return 'group:' + groupName + ':members'; - }); - - db.isMemberOfSortedSets(groups, uid, callback); - }; - - Groups.getMemberCount = function(groupName, callback) { - db.getObjectField('group:' + groupName, 'memberCount', callback); - }; - - Groups.isMemberOfGroupList = function(uid, groupListKey, callback) { - db.getSortedSetRange('group:' + groupListKey + ':members', 0, -1, function(err, groupNames) { - if (err) { - return callback(err); - } - groupNames = internals.removeEphemeralGroups(groupNames); - if (groupNames.length === 0) { - return callback(null, null); - } - - Groups.isMemberOfGroups(uid, groupNames, function(err, isMembers) { - if (err) { - return callback(err); - } - - callback(null, isMembers.indexOf(true) !== -1); - }); - }); - }; - - Groups.isMemberOfGroupsList = function(uid, groupListKeys, callback) { - var sets = groupListKeys.map(function(groupName) { - return 'group:' + groupName + ':members'; - }); - - db.getSortedSetsMembers(sets, function(err, members) { - if (err) { - return callback(err); - } - - var uniqueGroups = _.unique(_.flatten(members)); - uniqueGroups = internals.removeEphemeralGroups(uniqueGroups); - - Groups.isMemberOfGroups(uid, uniqueGroups, function(err, isMembers) { - if (err) { - return callback(err); - } - - var map = {}; - - uniqueGroups.forEach(function(groupName, index) { - map[groupName] = isMembers[index]; - }); - - var result = members.map(function(groupNames) { - for (var i=0; i

    - Full documentation regarding plugin authoring can be found in the NodeBB Wiki. + Full documentation regarding plugin authoring can be found in the NodeBB Docs Portal.

    diff --git a/src/views/admin/general/dashboard.tpl b/src/views/admin/general/dashboard.tpl index 132797fb3e..6d6162f079 100644 --- a/src/views/admin/general/dashboard.tpl +++ b/src/views/admin/general/dashboard.tpl @@ -65,14 +65,6 @@

    Always make sure that your NodeBB is up to date for the latest security patches and bug fixes.

    -

    - - -

    -

    - Restarting your NodeBB will drop all existing connections. A reload is lighter and is probably - what you want 99% of the time. -

    @@ -83,7 +75,13 @@
    - {notices.doneText} {notices.notDoneText} + + {notices.doneText} + + + {notices.notDoneText} + +
    @@ -93,6 +91,19 @@
    +
    +
    System Control
    +
    +

    + + +

    +

    + Maintenance Mode +

    +
    +
    +
    Anonymous vs Registered Users
    diff --git a/src/views/admin/general/navigation.tpl b/src/views/admin/general/navigation.tpl index fe956cc5bc..7ac6ee85e9 100644 --- a/src/views/admin/general/navigation.tpl +++ b/src/views/admin/general/navigation.tpl @@ -25,6 +25,21 @@ + +
    + Properties: +
    + +
    +
    + +
    + +
    diff --git a/src/views/admin/header.tpl b/src/views/admin/header.tpl index d0460bb058..9516b945d7 100644 --- a/src/views/admin/header.tpl +++ b/src/views/admin/header.tpl @@ -26,6 +26,7 @@ + + diff --git a/src/views/admin/manage/categories.tpl b/src/views/admin/manage/categories.tpl index 39d2a15c9b..c57996551a 100644 --- a/src/views/admin/manage/categories.tpl +++ b/src/views/admin/manage/categories.tpl @@ -1,120 +1,20 @@
    -
    -
    -
    Active Categories
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameDescriptionTopicsPosts
    - - - - {active.name}{active.description}{active.topic_count}{active.post_count} -
    - Edit - -
    -
    -
    - You have no active categories. -
    -
    -
    -
    +
    +
    +
    Categories
    +
    +
    +
    +
    +
    -
    -
    Disabled Categories
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameDescriptionTopicsPosts
    - - - - {disabled.name}{disabled.description}{disabled.topic_count}{disabled.post_count} -
    - Edit - -
    -
    -
    - You have no disabled categories. -
    -
    -
    -
    -
    - -
    -
    -
    Categories Control Panel
    -
    - -
    -
    -
    +
    +
    +
    Categories Control Panel
    +
    + +
    +
    +
    diff --git a/src/views/admin/manage/category.tpl b/src/views/admin/manage/category.tpl index 80b5dbe694..48d07b0e6c 100644 --- a/src/views/admin/manage/category.tpl +++ b/src/views/admin/manage/category.tpl @@ -86,9 +86,9 @@ these settings.


    - -
    - +
    + +
    diff --git a/src/views/admin/manage/flags.tpl b/src/views/admin/manage/flags.tpl index f463d829d2..1679f4087f 100644 --- a/src/views/admin/manage/flags.tpl +++ b/src/views/admin/manage/flags.tpl @@ -38,11 +38,11 @@
    diff --git a/src/views/admin/manage/group.tpl b/src/views/admin/manage/group.tpl new file mode 100644 index 0000000000..3836cb74ee --- /dev/null +++ b/src/views/admin/manage/group.tpl @@ -0,0 +1,126 @@ +
    +
    +
    +
    +
    Group Settings
    +
    + + +
    +
    + + readonly/> +
    +
    + +
    +
    + + +
    +
    + +
    +
    + + +
    +
    + +
    +
    +
    + +
    +
    + +
    +
    + + {group.userTitle} + +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    + + +
      +
      +
      + +
      +
      + +

      Click on a user to remove them from the group

      +
        + +
      • + + {group.members.username} +
      • + +
      +
      +
      + + +
      +
      + +
      +
      +
      Groups Control Panel
      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      + + +
      + + + + \ No newline at end of file diff --git a/src/views/admin/manage/groups.tpl b/src/views/admin/manage/groups.tpl index 26fe157dac..302ac2760a 100644 --- a/src/views/admin/manage/groups.tpl +++ b/src/views/admin/manage/groups.tpl @@ -2,37 +2,32 @@
      Groups List
      -
      -
        - -
      • -
        -
        -

        - {groups.name} - - System Group - -

        -

        {groups.description}

        - -
        - - - - -
        +
        + + + + + + + + + + + +
        Group NameGroup Description
        + {groups.name} + + System Group + + +
        + Edit + + +
        -
        -
          -
        • -
        • -
        -
        - - - - +

        {groups.description}

        +
        @@ -48,8 +43,6 @@
      - - - + diff --git a/src/views/admin/manage/registration.tpl b/src/views/admin/manage/registration.tpl new file mode 100644 index 0000000000..b602f54141 --- /dev/null +++ b/src/views/admin/manage/registration.tpl @@ -0,0 +1,55 @@ +
      +
      +
      +
      Registration Queue
      +
      + + + + + + + + + + + + + + + + + +
      NameEmailIPTime
      + + + + + + {users.username} + + + + + + + {users.email} + + + + + + + {users.ip} + + + +
      + + +
      +
      +
      +
      +
      +
      \ No newline at end of file diff --git a/src/views/admin/manage/tags.tpl b/src/views/admin/manage/tags.tpl index 1b8c687b00..e503c0d418 100644 --- a/src/views/admin/manage/tags.tpl +++ b/src/views/admin/manage/tags.tpl @@ -14,7 +14,7 @@
      - {tags.value}{tags.score} + {tags.value}{tags.score}
      diff --git a/src/views/admin/manage/users.tpl b/src/views/admin/manage/users.tpl index 37b993dba7..8b60cd2b0a 100644 --- a/src/views/admin/manage/users.tpl +++ b/src/views/admin/manage/users.tpl @@ -4,11 +4,12 @@
      Users
      - {users.username} ({users.uid})
      + {users.username} ({users.uid})
      {users.email} @@ -129,7 +130,7 @@
      Users Control Panel
      - Download CSV + Download CSV
      diff --git a/src/views/admin/partials/categories/category-rows.tpl b/src/views/admin/partials/categories/category-rows.tpl new file mode 100644 index 0000000000..944ccb0170 --- /dev/null +++ b/src/views/admin/partials/categories/category-rows.tpl @@ -0,0 +1,33 @@ +
        + +
      • class="disabled"> +
        +
        +
        +
        + +
        +
        +
        {categories.name}
        +

        {categories.description}

        +
        +
        +
        +
        +
        +
          +
        • {categories.topic_count}
        • +
        • {categories.post_count}
        • +
        +
        + + Edit +
        +
        +
        +
        +
      • + +
      \ No newline at end of file diff --git a/src/views/admin/partials/categories/privileges.tpl b/src/views/admin/partials/categories/privileges.tpl index d18329753e..eb97e6bcac 100644 --- a/src/views/admin/partials/categories/privileges.tpl +++ b/src/views/admin/partials/categories/privileges.tpl @@ -1,51 +1,74 @@ -
      - - - - - - - - - - - - - {function.spawnPrivilegeStates, username, privileges} - - - - - - - -
      User{privileges.labels.users.name}
      {username}
      -
      No user-specific privileges in this category.
      -
      + + + + + + + + + + + + + {function.spawnPrivilegeStates, privileges.users.username, privileges} + + + + + + + + + + +
      User{privileges.labels.users.name}
      {privileges.users.username}
      + +
      +
      + + No user-specific privileges in this category. +
      +
      - - - - - - - - - - - {function.spawnPrivilegeStates, name, privileges} - - -
      Group{privileges.labels.groups.name}
      - - - - {privileges.groups.name} -
      -
      - 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. -
      -
      \ No newline at end of file + + + + + + + + + + + + + {function.spawnPrivilegeStates, name, privileges} + + + + + + + + + + +
      Group{privileges.labels.groups.name}
      + + + + {privileges.groups.name} +
      + +
      +
      + + No group-specific privileges in this category. +
      +
      +
      + 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. +
      diff --git a/src/views/admin/partials/menu.tpl b/src/views/admin/partials/menu.tpl index 4ef3372288..ff59c181d9 100644 --- a/src/views/admin/partials/menu.tpl +++ b/src/views/admin/partials/menu.tpl @@ -26,7 +26,7 @@
    • Email
    • User
    • Group
    • -
    • Guest
    • +
    • Guests
    • Post
    • Pagination
    • Tags
    • @@ -79,7 +79,12 @@
    • - {plugins.name} + + + + + {plugins.name} +
    • diff --git a/src/views/admin/settings/general.tpl b/src/views/admin/settings/general.tpl index 65039333ee..b3f359250e 100644 --- a/src/views/admin/settings/general.tpl +++ b/src/views/admin/settings/general.tpl @@ -34,7 +34,7 @@

      - +
    • @@ -45,7 +45,7 @@


      - +
      @@ -54,11 +54,6 @@
      Miscellaneous
      -
      - -
      + +
      + +
      + +
      + +
      + +
      diff --git a/src/views/admin/settings/tags.tpl b/src/views/admin/settings/tags.tpl index 716cdbf56d..b54e6295f3 100644 --- a/src/views/admin/settings/tags.tpl +++ b/src/views/admin/settings/tags.tpl @@ -22,6 +22,7 @@ + Click here to visit the tag management page. diff --git a/src/views/admin/settings/user.tpl b/src/views/admin/settings/user.tpl index c2832c530a..f03c84d838 100644 --- a/src/views/admin/settings/user.tpl +++ b/src/views/admin/settings/user.tpl @@ -4,11 +4,6 @@
      User List
      -
      - -
      + +
      + + +
      @@ -73,7 +78,7 @@

      - +
      diff --git a/src/views/admin/settings/web-crawler.tpl b/src/views/admin/settings/web-crawler.tpl index 349b5f6664..672360d150 100644 --- a/src/views/admin/settings/web-crawler.tpl +++ b/src/views/admin/settings/web-crawler.tpl @@ -27,11 +27,12 @@
      - - + +
      + View Sitemap diff --git a/src/views/emails/digest.tpl b/src/views/emails/digest.tpl index aca42a05e0..e763b53755 100644 --- a/src/views/emails/digest.tpl +++ b/src/views/emails/digest.tpl @@ -40,5 +40,5 @@

      - [[email:digest.unsub.info]] [[email:unsub.cta]]. + [[email:digest.unsub.info]] [[email:unsub.cta]].

      \ No newline at end of file diff --git a/src/views/emails/digest_plaintext.tpl b/src/views/emails/digest_plaintext.tpl index eb539eb59a..25e44bac68 100644 --- a/src/views/emails/digest_plaintext.tpl +++ b/src/views/emails/digest_plaintext.tpl @@ -28,4 +28,4 @@ === -[[email:digest.unsub.info]] [[email:unsub.cta]]: {url}/user/{username}/settings. \ No newline at end of file +[[email:digest.unsub.info]] [[email:unsub.cta]]: {url}/user/{userslug}/settings. \ No newline at end of file diff --git a/src/views/emails/invitation.tpl b/src/views/emails/invitation.tpl new file mode 100644 index 0000000000..a19543d084 --- /dev/null +++ b/src/views/emails/invitation.tpl @@ -0,0 +1,14 @@ +

      [[email:greeting_no_name]],

      + +

      + [[email:invitation.text1, {username}, {site_title}]] +

      + +
      + [[email:invitation.ctr]] +
      + +

      + [[email:closing]]
      + {site_title} +

      diff --git a/src/views/emails/invitation_plaintext.tpl b/src/views/emails/invitation_plaintext.tpl new file mode 100644 index 0000000000..64bb6c73e1 --- /dev/null +++ b/src/views/emails/invitation_plaintext.tpl @@ -0,0 +1,11 @@ +[[email:greeting_no_name]], + +[[email:invitation.text1, {username}, {site_title}]] + +[[email:invitation.ctr]] + + {registerLink} + +[[email:closing]] +{site_title} + diff --git a/src/views/emails/notif_chat.tpl b/src/views/emails/notif_chat.tpl index a7e6be420d..da7d1611d3 100644 --- a/src/views/emails/notif_chat.tpl +++ b/src/views/emails/notif_chat.tpl @@ -12,5 +12,5 @@

      - [[email:notif.chat.unsub.info]] [[email:unsub.cta]]. + [[email:notif.chat.unsub.info]] [[email:unsub.cta]].

      \ No newline at end of file diff --git a/src/views/emails/notif_chat_plaintext.tpl b/src/views/emails/notif_chat_plaintext.tpl index 3ecc1f82fc..d548b5643f 100644 --- a/src/views/emails/notif_chat_plaintext.tpl +++ b/src/views/emails/notif_chat_plaintext.tpl @@ -12,4 +12,4 @@ === -[[email:notif.chat.unsub.info]] [[email:unsub.cta]]. +[[email:notif.chat.unsub.info]] [[email:unsub.cta]]. diff --git a/src/views/emails/registration_accepted.tpl b/src/views/emails/registration_accepted.tpl new file mode 100644 index 0000000000..5c978aa22a --- /dev/null +++ b/src/views/emails/registration_accepted.tpl @@ -0,0 +1,12 @@ +

      [[email:greeting_with_name, {username}]],

      + +

      + [[email:welcome.text1, {site_title}]] +

      + +

      [[email:welcome.text3]]

      + +

      + [[email:closing]]
      + {site_title} +

      diff --git a/src/views/emails/registration_accepted_plaintext.tpl b/src/views/emails/registration_accepted_plaintext.tpl new file mode 100644 index 0000000000..a1bacf0f9f --- /dev/null +++ b/src/views/emails/registration_accepted_plaintext.tpl @@ -0,0 +1,9 @@ +[[email:greeting_with_name, {username}]], + +[[email:welcome.text1, {site_title}]] + +[[email:welcome.text3]] + + +[[email:closing]] +{site_title} \ No newline at end of file diff --git a/src/views/partials/requirejs-config.tpl b/src/views/partials/requirejs-config.tpl index 932c571f4a..0666e6ffdf 100644 --- a/src/views/partials/requirejs-config.tpl +++ b/src/views/partials/requirejs-config.tpl @@ -4,7 +4,7 @@ waitSeconds: 3, urlArgs: "{cache-buster}", paths: { - 'forum': '../forum', + 'forum': '../client', 'vendor': '../../vendor', 'mousetrap': '../../bower/mousetrap/mousetrap' } diff --git a/tests/database/keys.js b/tests/database/keys.js index 43a44e5753..36e5fe5989 100644 --- a/tests/database/keys.js +++ b/tests/database/keys.js @@ -7,6 +7,9 @@ var async = require('async'), describe('Key methods', function() { + beforeEach(function(done) { + db.set('testKey', 'testValue', done); + }); it('should set a key without error', function(done) { db.set('testKey', 'testValue', function(err) { @@ -56,11 +59,15 @@ describe('Key methods', function() { }); }); - it('should return false if key does not exist or was deleted', function(done) { - db.exists('testKey', function(err, exists) { + it('should return false if key was deleted', function(done) { + db.delete('testKey', function(err) { assert.equal(err, null); - assert.strictEqual(exists, false); - done(); + assert.equal(arguments.length, 1); + db.exists('testKey', function(err, exists) { + assert.equal(err, null); + assert.strictEqual(exists, false); + done(); + }); }); }); diff --git a/tests/groups.js b/tests/groups.js index a4d5c9da30..095518ae3b 100644 --- a/tests/groups.js +++ b/tests/groups.js @@ -34,10 +34,10 @@ describe('Groups', function() { describe('.list()', function() { it('should list the groups present', function(done) { - Groups.list({}, function(err, groups) { + Groups.getGroupsFromSet('groups:createtime', 0, 0, -1, function(err, groups) { if (err) return done(err); - assert.equal(groups.length, 1); + assert.equal(groups.length, 3); done(); }); }); @@ -52,21 +52,6 @@ describe('Groups', function() { Groups.get('Test', {}, function(err, groupObj) { if (err) return done(err); - assert.equal(typeof groupObj, 'object'); - assert(Array.isArray(groupObj.members)); - assert.strictEqual(groupObj.name, 'Test'); - assert.strictEqual(groupObj.description, 'Foobar!'); - assert.strictEqual(groupObj.memberCount, 1); - assert.notEqual(typeof groupObj.members[0], 'object'); - - done(); - }); - }); - - it('with the "expand" option, should show both group information and user information', function(done) { - Groups.get('Test', { expand: true }, function(err, groupObj) { - if (err) return done(err); - assert.equal(typeof groupObj, 'object'); assert(Array.isArray(groupObj.members)); assert.strictEqual(groupObj.name, 'Test');