diff --git a/.gitignore b/.gitignore index c7e1d383b1..9cc6516546 100644 --- a/.gitignore +++ b/.gitignore @@ -27,8 +27,10 @@ pidfile /public/templates /public/sounds +/public/uploads + # compiled files /public/stylesheet.css /public/admin.css /public/nodebb.min.js -/public/nodebb.min.js.map +/public/nodebb.min.js.map \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..8ea926152f --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,30 @@ +# Issues & Bugs + +Thanks for reporting an issue with NodeBB! Please follow these guidelines in order to streamline the debugging process. The more guidelines you follow, the easier it will be for us to reproduce your problem. + +In general, if we can't reproduce it, we can't fix it! + +## Try the latest version of NodeBB + +There is a chance that the issue you are experiencing may have already been fixed. + +## Provide the NodeBB version number and git hash + +You can find the NodeBB version number in the Admin Control Panel (ACP), as well as the first line output to the shell when running NodeBB + +``` plaintext +info: NodeBB v0.5.2-dev Copyright (C) 2013-2014 NodeBB Inc. +info: This program comes with ABSOLUTELY NO WARRANTY. +info: This is free software, and you are welcome to redistribute it under certain conditions. +info: +info: Time: Tue Oct 07 2014 20:25:20 GMT-0400 (EDT) +``` + +If you are running NodeBB via git, it is also helpful to let the maintainers know what commit hash you are on. To find the commit hash, execute the following command: + +``` bash +$ cd /path/to/my/nodebb +$ git rev-parse HEAD +``` + +If you have downloaded the `.zip` or `.tar.gz` packages from GitHub (or elsewhere), please let us know. \ No newline at end of file diff --git a/app.js b/app.js index c0822a6edc..f69b0d2b6a 100644 --- a/app.js +++ b/app.js @@ -58,11 +58,13 @@ if(os.platform() === 'linux') { }); } -// Log GNU copyright info along with server info -winston.info('NodeBB v' + pkg.version + ' Copyright (C) 2013-2014 NodeBB Inc.'); -winston.info('This program comes with ABSOLUTELY NO WARRANTY.'); -winston.info('This is free software, and you are welcome to redistribute it under certain conditions.'); -winston.info(''); +if (!cluster.isWorker) { + // If run using `node app`, log GNU copyright info along with server info + winston.info('NodeBB v' + pkg.version + ' Copyright (C) 2013-2014 NodeBB Inc.'); + winston.info('This program comes with ABSOLUTELY NO WARRANTY.'); + winston.info('This is free software, and you are welcome to redistribute it under certain conditions.'); + winston.info(''); +} // Alternate configuration file support var configFile = path.join(__dirname, '/config.json'), @@ -73,7 +75,7 @@ if (nconf.get('config')) { } configExists = fs.existsSync(configFile); -if (!nconf.get('help') && !nconf.get('setup') && !nconf.get('install') && !nconf.get('upgrade') && !nconf.get('reset') && configExists) { +if (!nconf.get('setup') && !nconf.get('install') && !nconf.get('upgrade') && !nconf.get('reset') && configExists) { start(); } else if (nconf.get('setup') || nconf.get('install') || !configExists) { setup(); @@ -81,8 +83,6 @@ if (!nconf.get('help') && !nconf.get('setup') && !nconf.get('install') && !nconf upgrade(); } else if (nconf.get('reset')) { reset(); -} else { - displayHelp(); } function loadConfig() { @@ -104,20 +104,20 @@ function loadConfig() { } function start() { - loadConfig(); - winston.info('Time: ' + new Date()); - winston.info('Initializing NodeBB v' + pkg.version); - winston.info('* using configuration stored in: ' + configFile); - var host = nconf.get(nconf.get('database') + ':host'), - storeLocation = host ? 'at ' + host + (host.indexOf('/') === -1 ? ':' + nconf.get(nconf.get('database') + ':port') : '') : ''; + if (!cluster.isWorker) { + winston.info('Time: ' + new Date()); + winston.info('Initializing NodeBB v' + pkg.version); + winston.info('* using configuration stored in: ' + configFile); + } - winston.info('* using ' + nconf.get('database') +' store ' + storeLocation); - winston.info('* using themes stored in: ' + nconf.get('themes_path')); + if (cluster.isWorker && process.env.cluster_setup === 'true') { + var host = nconf.get(nconf.get('database') + ':host'), + storeLocation = host ? 'at ' + host + (host.indexOf('/') === -1 ? ':' + nconf.get(nconf.get('database') + ':port') : '') : ''; - if (process.env.NODE_ENV === 'development') { - winston.info('Base Configuration OK.'); + winston.info('* using ' + nconf.get('database') +' store ' + storeLocation); + winston.info('* using themes stored in: ' + nconf.get('themes_path')); } require('./src/database').init(function(err) { @@ -167,13 +167,13 @@ function start() { case 'css-propagate': meta.css.cache = message.cache; meta.css.acpCache = message.acpCache; - winston.info('[cluster] Stylesheet propagated to worker ' + cluster.worker.id); + winston.info('[cluster] Stylesheets propagated to worker ' + cluster.worker.id); break; } }); - + process.on('uncaughtException', function(err) { - winston.error(err.message); + winston.error(err.stack); console.log(err.stack); meta.js.killMinifier(); @@ -181,10 +181,12 @@ function start() { }); } else { winston.warn('Your NodeBB schema is out-of-date. Please run the following command to bring your dataset up to spec:'); - winston.warn(' node app --upgrade'); - winston.warn('To ignore this error (not recommended):'); - winston.warn(' node app --no-check-schema'); - process.exit(); + winston.warn(' ./nodebb upgrade'); + if (cluster.isWorker) { + cluster.worker.kill(); + } else { + process.exit(); + } } }); }); @@ -355,16 +357,4 @@ function restart() { winston.error('[app] Could not restart server. Shutting down.'); shutdown(1); } -} - -function displayHelp() { - winston.info('Usage: node app [options] [arguments]'); - winston.info(' [NODE_ENV=development | NODE_ENV=production] node app [--start] [arguments]'); - winston.info(''); - winston.info('Options:'); - winston.info(' --help displays this usage information'); - winston.info(' --setup configure your environment and setup NodeBB'); - winston.info(' --upgrade upgrade NodeBB, first read: https://docs.nodebb.org/en/latest/upgrading/'); - winston.info(' --reset soft resets NodeBB; disables all plugins and restores selected theme to Vanilla'); - winston.info(' --start manually start NodeBB (default when no options are given)'); -} +} diff --git a/bcrypt.js b/bcrypt.js index a926310923..2d27fddcd1 100644 --- a/bcrypt.js +++ b/bcrypt.js @@ -1,30 +1,29 @@ 'use strict'; +var bcrypt = require('bcryptjs'), + async = require('async'), + action = process.argv[2]; -var bcrypt = require('bcryptjs'); +switch(action) { + case 'compare': + bcrypt.compare(process.argv[3], process.argv[4], function(err, res) { + process.stdout.write(res ? 'true' : 'false'); + }); + break; -process.on('message', function(m) { - if (m.type === 'hash') { - hash(m.rounds, m.password); - } else if (m.type === 'compare') { - compare(m.password, m.hash); - } -}); - -function hash(rounds, password) { - bcrypt.genSalt(rounds, function(err, salt) { - if (err) { - return process.send({type:'hash', err: {message: err.message}}); - } - bcrypt.hash(password, salt, function(err, hash) { - process.send({type:'hash', err: err ? {message: err.message} : null, hash: hash, password: password}); - }); - }); -} - -function compare(password, hash) { - bcrypt.compare(password, hash, function(err, res) { - process.send({type:'compare', err: err ? {message: err.message} : null, hash: hash, password: password, result: res}); - }); + case 'hash': + async.waterfall([ + async.apply(bcrypt.genSalt, parseInt(process.argv[3], 10)), + function(salt, next) { + bcrypt.hash(process.argv[4], salt, next); + } + ], function(err, hash) { + if (!err) { + process.stdout.write(hash); + } else { + process.stderr.write(err.message); + } + }); + break; } \ No newline at end of file diff --git a/install/databases.js b/install/databases.js index 752e36e0d6..08907dca3b 100644 --- a/install/databases.js +++ b/install/databases.js @@ -12,7 +12,7 @@ function success(err, config, callback) { return callback(new Error('aborted')); } - var database = (config.redis || config.mongo || config.level) ? config.secondary_database : config.database; + var database = (config.redis || config.mongo) ? config.secondary_database : config.database; function dbQuestionsSuccess(err, databaseConfig) { if (!databaseConfig) { @@ -39,15 +39,11 @@ function success(err, config, callback) { password: databaseConfig['mongo:password'], database: databaseConfig['mongo:database'] }; - } else if (database === 'level') { - config.level = { - database: databaseConfig['level:database'] - }; } else { return callback(new Error('unknown database : ' + database)); } - var allQuestions = questions.redis.concat(questions.mongo.concat(questions.level)); + var allQuestions = questions.redis.concat(questions.mongo); for(var x=0;x%1", "upvoted_your_post_in": "%1 has upvoted your post in %2.", - "moved_your_post": "%1 has moved your post.", - "moved_your_topic": "%1 has moved your topic.", + "moved_your_post": "%1 has moved your post.", + "moved_your_topic": "%1 has moved your topic.", "favourited_your_post_in": "%1 has favourited your post in %2.", "user_flagged_post_in": "%1 flagged a post in %2", "user_posted_to": "%1 has posted a reply to: %2", diff --git a/public/language/ar/pages.json b/public/language/ar/pages.json index c39934513a..eab53dc3dc 100644 --- a/public/language/ar/pages.json +++ b/public/language/ar/pages.json @@ -12,5 +12,7 @@ "user.posts": "Posts made by %1", "user.topics": "Topics created by %1", "user.favourites": "%1's Favourite Posts", - "user.settings": "User Settings" + "user.settings": "User Settings", + "maintenance.text": "%1 is currently undergoing maintenance. Please come back another time.", + "maintenance.messageIntro": "Additionally, the administator has left this message:" } \ No newline at end of file diff --git a/public/language/ar/tags.json b/public/language/ar/tags.json index f065d4bbfa..d2e9a213ac 100644 --- a/public/language/ar/tags.json +++ b/public/language/ar/tags.json @@ -2,5 +2,6 @@ "no_tag_topics": "There are no topics with this tag.", "tags": "Tags", "enter_tags_here": "Enter tags here. Press enter after each tag.", + "enter_tags_here_short": "Enter tags...", "no_tags": "There are no tags yet." } \ No newline at end of file diff --git a/public/language/ar/topic.json b/public/language/ar/topic.json index eed42e4f01..4c2ba2f1ca 100644 --- a/public/language/ar/topic.json +++ b/public/language/ar/topic.json @@ -28,15 +28,17 @@ "flag_title": "Flag this post for moderation", "flag_confirm": "Are you sure you want to flag this post?", "flag_success": "This post has been flagged for moderation.", - "deleted_message": "This thread has been deleted. Only users with thread management privileges can see it.", + "deleted_message": "This topic has been deleted. Only users with topic management privileges can see it.", "following_topic.message": "You will now be receiving notifications when somebody posts to this topic.", "not_following_topic.message": "You will no longer receive notifications from this topic.", "login_to_subscribe": "Please register or log in in order to subscribe to this topic.", "markAsUnreadForAll.success": "Topic marked as unread for all.", "watch": "Watch", + "unwatch": "Unwatch", "watch.title": "Be notified of new replies in this topic", + "unwatch.title": "Stop watching this topic", "share_this_post": "Share this Post", - "thread_tools.title": "أدوات الموضوع", + "thread_tools.title": "Topic Tools", "thread_tools.markAsUnreadForAll": "علم غير مقروء", "thread_tools.pin": "علق الموضوع", "thread_tools.unpin": "Unpin Topic", @@ -46,11 +48,11 @@ "thread_tools.move_all": "Move All", "thread_tools.fork": "تفرع الموضوع", "thread_tools.delete": "حذف الموضوع", - "thread_tools.delete_confirm": "Are you sure you want to delete this thread?", + "thread_tools.delete_confirm": "Are you sure you want to delete this topic?", "thread_tools.restore": "Restore Topic", - "thread_tools.restore_confirm": "Are you sure you want to restore this thread?", + "thread_tools.restore_confirm": "Are you sure you want to restore this topic?", "thread_tools.purge": "Purge Topic", - "thread_tools.purge_confirm": "Are you sure you want to purge this thread?", + "thread_tools.purge_confirm": "Are you sure you want to purge this topic?", "topic_move_success": "This topic has been successfully moved to %1", "post_delete_confirm": "Are you sure you want to delete this post?", "post_restore_confirm": "Are you sure you want to restore this post?", diff --git a/public/language/ar/user.json b/public/language/ar/user.json index 01f1439527..ab925f5ab2 100644 --- a/public/language/ar/user.json +++ b/public/language/ar/user.json @@ -29,6 +29,7 @@ "edit": "صحح", "uploaded_picture": "صورة تم تحميلها", "upload_new_picture": "تحميل صورة جديدة", + "upload_new_picture_from_url": "Upload New Picture From URL", "current_password": "Current Password", "change_password": "تغيير كلمة السر", "change_password_error": "Invalid Password!", diff --git a/public/language/cs/error.json b/public/language/cs/error.json index f90d459cce..a46573d5b9 100644 --- a/public/language/cs/error.json +++ b/public/language/cs/error.json @@ -13,6 +13,7 @@ "invalid-user-data": "Invalid User Data", "invalid-password": "Invalid Password", "invalid-username-or-password": "Please specify both a username and password", + "invalid-search-term": "Invalid search term", "invalid-pagination-value": "Invalid pagination value", "username-taken": "Username taken", "email-taken": "Email taken", diff --git a/public/language/cs/notifications.json b/public/language/cs/notifications.json index 8a77183b5e..e482f15fd6 100644 --- a/public/language/cs/notifications.json +++ b/public/language/cs/notifications.json @@ -11,8 +11,8 @@ "you_have_unread_notifications": "You have unread notifications.", "new_message_from": "New message from %1", "upvoted_your_post_in": "%1 has upvoted your post in %2.", - "moved_your_post": "%1 has moved your post.", - "moved_your_topic": "%1 has moved your topic.", + "moved_your_post": "%1 has moved your post.", + "moved_your_topic": "%1 has moved your topic.", "favourited_your_post_in": "%1 has favourited your post in %2.", "user_flagged_post_in": "%1 flagged a post in %2", "user_posted_to": "%1 has posted a reply to: %2", diff --git a/public/language/cs/pages.json b/public/language/cs/pages.json index c39934513a..eab53dc3dc 100644 --- a/public/language/cs/pages.json +++ b/public/language/cs/pages.json @@ -12,5 +12,7 @@ "user.posts": "Posts made by %1", "user.topics": "Topics created by %1", "user.favourites": "%1's Favourite Posts", - "user.settings": "User Settings" + "user.settings": "User Settings", + "maintenance.text": "%1 is currently undergoing maintenance. Please come back another time.", + "maintenance.messageIntro": "Additionally, the administator has left this message:" } \ No newline at end of file diff --git a/public/language/cs/tags.json b/public/language/cs/tags.json index f065d4bbfa..d2e9a213ac 100644 --- a/public/language/cs/tags.json +++ b/public/language/cs/tags.json @@ -2,5 +2,6 @@ "no_tag_topics": "There are no topics with this tag.", "tags": "Tags", "enter_tags_here": "Enter tags here. Press enter after each tag.", + "enter_tags_here_short": "Enter tags...", "no_tags": "There are no tags yet." } \ No newline at end of file diff --git a/public/language/cs/topic.json b/public/language/cs/topic.json index b3a0eb1aeb..528e29f3b8 100644 --- a/public/language/cs/topic.json +++ b/public/language/cs/topic.json @@ -28,15 +28,17 @@ "flag_title": "Flag this post for moderation", "flag_confirm": "Are you sure you want to flag this post?", "flag_success": "This post has been flagged for moderation.", - "deleted_message": "This thread has been deleted. Only users with thread management privileges can see it.", + "deleted_message": "This topic has been deleted. Only users with topic management privileges can see it.", "following_topic.message": "You will now be receiving notifications when somebody posts to this topic.", "not_following_topic.message": "You will no longer receive notifications from this topic.", "login_to_subscribe": "Please register or log in in order to subscribe to this topic.", "markAsUnreadForAll.success": "Topic marked as unread for all.", "watch": "Watch", + "unwatch": "Unwatch", "watch.title": "Be notified of new replies in this topic", + "unwatch.title": "Stop watching this topic", "share_this_post": "Share this Post", - "thread_tools.title": "Nástroje", + "thread_tools.title": "Topic Tools", "thread_tools.markAsUnreadForAll": "Označit jako nepřečtené", "thread_tools.pin": "Pin Topic", "thread_tools.unpin": "Unpin Topic", @@ -46,11 +48,11 @@ "thread_tools.move_all": "Move All", "thread_tools.fork": "Fork Topic", "thread_tools.delete": "Delete Topic", - "thread_tools.delete_confirm": "Are you sure you want to delete this thread?", + "thread_tools.delete_confirm": "Are you sure you want to delete this topic?", "thread_tools.restore": "Restore Topic", - "thread_tools.restore_confirm": "Are you sure you want to restore this thread?", + "thread_tools.restore_confirm": "Are you sure you want to restore this topic?", "thread_tools.purge": "Purge Topic", - "thread_tools.purge_confirm": "Are you sure you want to purge this thread?", + "thread_tools.purge_confirm": "Are you sure you want to purge this topic?", "topic_move_success": "This topic has been successfully moved to %1", "post_delete_confirm": "Are you sure you want to delete this post?", "post_restore_confirm": "Are you sure you want to restore this post?", diff --git a/public/language/cs/user.json b/public/language/cs/user.json index 1f1230bde3..582724d7df 100644 --- a/public/language/cs/user.json +++ b/public/language/cs/user.json @@ -29,6 +29,7 @@ "edit": "Upravit", "uploaded_picture": "Nahraný obrázek", "upload_new_picture": "Nahrát nový obrázek", + "upload_new_picture_from_url": "Upload New Picture From URL", "current_password": "Current Password", "change_password": "Změnit heslo", "change_password_error": "Invalid Password!", diff --git a/public/language/de/error.json b/public/language/de/error.json index 42030b649a..b7c1045803 100644 --- a/public/language/de/error.json +++ b/public/language/de/error.json @@ -13,6 +13,7 @@ "invalid-user-data": "Ungültige Benutzerdaten", "invalid-password": "Ungültiges Passwort", "invalid-username-or-password": "Bitte gebe einen Benutzernamen und ein Passwort an", + "invalid-search-term": "Invalid search term", "invalid-pagination-value": "Die Nummerierung ist ungültig", "username-taken": "Der Benutzername ist bereits vergeben", "email-taken": "Die E-Mail-Adresse ist bereits vergeben", diff --git a/public/language/de/notifications.json b/public/language/de/notifications.json index 7140887186..e4cd4e5989 100644 --- a/public/language/de/notifications.json +++ b/public/language/de/notifications.json @@ -11,8 +11,8 @@ "you_have_unread_notifications": "Du hast ungelesene Benachrichtigungen.", "new_message_from": "Neue Nachricht von %1", "upvoted_your_post_in": "%1 hat deinen Beitrag in %2 positiv bewertet.", - "moved_your_post": "%1 hat deinen Beitrag verschoben.", - "moved_your_topic": "%1 hat dein Thema verschoben.", + "moved_your_post": "%1 has moved your post.", + "moved_your_topic": "%1 has moved your topic.", "favourited_your_post_in": "%1 hat deinen Beitrag in %2 favorisiert.", "user_flagged_post_in": "%1 hat einen Beitrag in %2 gemeldet", "user_posted_to": "%1 hat auf %2 geantwortet.", diff --git a/public/language/de/pages.json b/public/language/de/pages.json index 2df893a878..75b25dc01a 100644 --- a/public/language/de/pages.json +++ b/public/language/de/pages.json @@ -12,5 +12,7 @@ "user.posts": "Beiträge von %1", "user.topics": "Themen von %1", "user.favourites": "Von %1 favorisierte Beiträge", - "user.settings": "Benutzer-Einstellungen" + "user.settings": "Benutzer-Einstellungen", + "maintenance.text": "%1 is currently undergoing maintenance. Please come back another time.", + "maintenance.messageIntro": "Additionally, the administator has left this message:" } \ No newline at end of file diff --git a/public/language/de/tags.json b/public/language/de/tags.json index 79fdd2d405..9a24949a0b 100644 --- a/public/language/de/tags.json +++ b/public/language/de/tags.json @@ -2,5 +2,6 @@ "no_tag_topics": "Es gibt keine Themen mit diesem Tag.", "tags": "Tags", "enter_tags_here": "Gib hier Tags ein und drück die Eingabetaste nach jedem Tag.", + "enter_tags_here_short": "Enter tags...", "no_tags": "Es gibt bisher keine Tags." } \ No newline at end of file diff --git a/public/language/de/topic.json b/public/language/de/topic.json index d75edf3633..8c0baa19e9 100644 --- a/public/language/de/topic.json +++ b/public/language/de/topic.json @@ -28,15 +28,17 @@ "flag_title": "Diesen Beitrag zur Moderation markieren", "flag_confirm": "Sind Sie sicher, dass Sie diesen Post markieren möchten?", "flag_success": "Dieser Beitrag wurde erfolgreich für die Moderation markiert.", - "deleted_message": "Dieses Thema wurde gelöscht. Nur Nutzer mit entsprechenden Rechten können es sehen.", + "deleted_message": "This topic has been deleted. Only users with topic management privileges can see it.", "following_topic.message": "Du erhälst nun eine Benachrichtigung, wenn jemand einen Beitrag zu diesem Thema verfasst.", "not_following_topic.message": "Du erhälst keine weiteren Benachrichtigungen zu diesem Thema.", "login_to_subscribe": "Bitte registrieren oder einloggen um dieses Thema zu abonnieren", "markAsUnreadForAll.success": "Thema für Alle als ungelesen markiert.", "watch": "Beobachten", + "unwatch": "Unwatch", "watch.title": "Bei neuen Antworten benachrichtigen", + "unwatch.title": "Stop watching this topic", "share_this_post": "Diesen Beitrag teilen", - "thread_tools.title": "Tools", + "thread_tools.title": "Topic Tools", "thread_tools.markAsUnreadForAll": "Als ungelesen markieren", "thread_tools.pin": "Thema anpinnen", "thread_tools.unpin": "Thema nicht mehr anpinnen", @@ -46,11 +48,11 @@ "thread_tools.move_all": "Alle verschieben", "thread_tools.fork": "Thema aufspalten", "thread_tools.delete": "Thema löschen", - "thread_tools.delete_confirm": "Sind Sie sicher, dass Sie dieses Thema löschen möchten?", + "thread_tools.delete_confirm": "Are you sure you want to delete this topic?", "thread_tools.restore": "Thema wiederherstellen", - "thread_tools.restore_confirm": "Sind Sie sicher, dass Sie dieses Thema wiederherstellen möchten?", + "thread_tools.restore_confirm": "Are you sure you want to restore this topic?", "thread_tools.purge": "Thema bereinigen", - "thread_tools.purge_confirm": "Sind Sie sicher, dass Sie dieses Thema bereinigen möchten?", + "thread_tools.purge_confirm": "Are you sure you want to purge this topic?", "topic_move_success": "Thema wurde erfolgreich zu %1 verschoben.", "post_delete_confirm": "Sind Sie sicher, dass Sie diesen Beitrag löschen möchten?", "post_restore_confirm": "Sind Sie sicher, dass Sie diesen Beitrag wiederherstellen möchten?", diff --git a/public/language/de/user.json b/public/language/de/user.json index 9c0e0a1e2a..78e8e4d6c5 100644 --- a/public/language/de/user.json +++ b/public/language/de/user.json @@ -29,6 +29,7 @@ "edit": "Ändern", "uploaded_picture": "Hochgeladene Bilder", "upload_new_picture": "Neues Bild hochladen", + "upload_new_picture_from_url": "Upload New Picture From URL", "current_password": "Aktuelles Passwort", "change_password": "Passwort ändern", "change_password_error": "Ungültiges Passwort!", diff --git a/public/language/en@pirate/error.json b/public/language/en@pirate/error.json index f90d459cce..a46573d5b9 100644 --- a/public/language/en@pirate/error.json +++ b/public/language/en@pirate/error.json @@ -13,6 +13,7 @@ "invalid-user-data": "Invalid User Data", "invalid-password": "Invalid Password", "invalid-username-or-password": "Please specify both a username and password", + "invalid-search-term": "Invalid search term", "invalid-pagination-value": "Invalid pagination value", "username-taken": "Username taken", "email-taken": "Email taken", diff --git a/public/language/en@pirate/notifications.json b/public/language/en@pirate/notifications.json index 49a3a10e05..657875d3ac 100644 --- a/public/language/en@pirate/notifications.json +++ b/public/language/en@pirate/notifications.json @@ -11,8 +11,8 @@ "you_have_unread_notifications": "You have unread notifications.", "new_message_from": "New message from %1", "upvoted_your_post_in": "%1 has upvoted your post in %2.", - "moved_your_post": "%1 has moved your post.", - "moved_your_topic": "%1 has moved your topic.", + "moved_your_post": "%1 has moved your post.", + "moved_your_topic": "%1 has moved your topic.", "favourited_your_post_in": "%1 has favourited your post in %2.", "user_flagged_post_in": "%1 flagged a post in %2", "user_posted_to": "%1 has posted a reply to: %2", diff --git a/public/language/en@pirate/pages.json b/public/language/en@pirate/pages.json index c39934513a..eab53dc3dc 100644 --- a/public/language/en@pirate/pages.json +++ b/public/language/en@pirate/pages.json @@ -12,5 +12,7 @@ "user.posts": "Posts made by %1", "user.topics": "Topics created by %1", "user.favourites": "%1's Favourite Posts", - "user.settings": "User Settings" + "user.settings": "User Settings", + "maintenance.text": "%1 is currently undergoing maintenance. Please come back another time.", + "maintenance.messageIntro": "Additionally, the administator has left this message:" } \ No newline at end of file diff --git a/public/language/en@pirate/tags.json b/public/language/en@pirate/tags.json index f065d4bbfa..d2e9a213ac 100644 --- a/public/language/en@pirate/tags.json +++ b/public/language/en@pirate/tags.json @@ -2,5 +2,6 @@ "no_tag_topics": "There are no topics with this tag.", "tags": "Tags", "enter_tags_here": "Enter tags here. Press enter after each tag.", + "enter_tags_here_short": "Enter tags...", "no_tags": "There are no tags yet." } \ No newline at end of file diff --git a/public/language/en@pirate/topic.json b/public/language/en@pirate/topic.json index 6093dc2302..bcb92860a6 100644 --- a/public/language/en@pirate/topic.json +++ b/public/language/en@pirate/topic.json @@ -28,15 +28,17 @@ "flag_title": "Flag this post for moderation", "flag_confirm": "Are you sure you want to flag this post?", "flag_success": "This post has been flagged for moderation.", - "deleted_message": "This thread has been deleted. Only users with thread management privileges can see it.", + "deleted_message": "This topic has been deleted. Only users with topic management privileges can see it.", "following_topic.message": "You will now be receiving notifications when somebody posts to this topic.", "not_following_topic.message": "You will no longer receive notifications from this topic.", "login_to_subscribe": "Please register or log in in order to subscribe to this topic.", "markAsUnreadForAll.success": "Topic marked as unread for all.", "watch": "Watch", + "unwatch": "Unwatch", "watch.title": "Be notified of new replies in this topic", + "unwatch.title": "Stop watching this topic", "share_this_post": "Share this Post", - "thread_tools.title": "Thread Tools", + "thread_tools.title": "Topic Tools", "thread_tools.markAsUnreadForAll": "Mark Unread", "thread_tools.pin": "Pin Topic", "thread_tools.unpin": "Unpin Topic", @@ -46,11 +48,11 @@ "thread_tools.move_all": "Move All", "thread_tools.fork": "Fork Topic", "thread_tools.delete": "Delete Topic", - "thread_tools.delete_confirm": "Are you sure you want to delete this thread?", + "thread_tools.delete_confirm": "Are you sure you want to delete this topic?", "thread_tools.restore": "Restore Topic", - "thread_tools.restore_confirm": "Are you sure you want to restore this thread?", + "thread_tools.restore_confirm": "Are you sure you want to restore this topic?", "thread_tools.purge": "Purge Topic", - "thread_tools.purge_confirm": "Are you sure you want to purge this thread?", + "thread_tools.purge_confirm": "Are you sure you want to purge this topic?", "topic_move_success": "This topic has been successfully moved to %1", "post_delete_confirm": "Are you sure you want to delete this post?", "post_restore_confirm": "Are you sure you want to restore this post?", diff --git a/public/language/en@pirate/user.json b/public/language/en@pirate/user.json index 6492a2ab79..079d49534c 100644 --- a/public/language/en@pirate/user.json +++ b/public/language/en@pirate/user.json @@ -29,6 +29,7 @@ "edit": "Edit", "uploaded_picture": "Uploaded Picture", "upload_new_picture": "Upload New Picture", + "upload_new_picture_from_url": "Upload New Picture From URL", "current_password": "Current Password", "change_password": "Change Password", "change_password_error": "Invalid Password!", diff --git a/public/language/en_GB/email.json b/public/language/en_GB/email.json index bfcf159de8..9c1cacb36b 100644 --- a/public/language/en_GB/email.json +++ b/public/language/en_GB/email.json @@ -13,7 +13,7 @@ "reset.text2": "To continue with the password reset, please click on the following link:", "reset.cta": "Click here to reset your password", - "digest.notifications": "You have some unread notifications from %1:", + "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.", diff --git a/public/language/en_GB/error.json b/public/language/en_GB/error.json index 7c4baf4cc4..8bd73d2c93 100644 --- a/public/language/en_GB/error.json +++ b/public/language/en_GB/error.json @@ -22,21 +22,21 @@ "username-taken": "Username taken", "email-taken": "Email taken", - "email-not-confirmed": "Your email is not confirmed, please click here to confirm your email.", + "email-not-confirmed": "Your email has not been confirmed yet, please click here to confirm your email.", "username-too-short": "Username too short", "username-too-long": "Username too long", "user-banned": "User banned", - "user-too-new": "You need to wait %1 seconds before making your first post!", + "user-too-new": "Sorry, you are required to wait %1 seconds before making your first post", - "no-category": "Category doesn't exist", - "no-topic": "Topic doesn't exist", - "no-post": "Post doesn't exist", - "no-group": "Group doesn't exist", - "no-user": "User doesn't exist", - "no-teaser": "Teaser doesn't exist", - "no-privileges": "You don't have enough privileges for this action.", + "no-category": "Category does not exist", + "no-topic": "Topic does not exist", + "no-post": "Post does not exist", + "no-group": "Group does not exist", + "no-user": "User does not exist", + "no-teaser": "Teaser does not exist", + "no-privileges": "You do not have enough privileges for this action.", "no-emailers-configured": "No email plugins were loaded, so a test email could not be sent", "category-disabled": "Category disabled", @@ -44,16 +44,16 @@ "topic-locked": "Topic Locked", "still-uploading": "Please wait for uploads to complete.", - "content-too-short": "Please enter a longer post. At least %1 characters.", - "title-too-short": "Please enter a longer title. At least %1 characters.", + "content-too-short": "Please enter a longer post. Posts should contain at least %1 characters.", + "title-too-short": "Please enter a longer title. Titles should contain at least %1 characters.", "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 characters.", "invalid-title": "Invalid title!", - "too-many-posts": "You can only post every %1 seconds.", - "file-too-big": "Maximum allowed file size is %1 kbs", + "too-many-posts": "You can only post once every %1 seconds - please wait before posting again", + "file-too-big": "Maximum allowed file size is %1 kbs - please upload a smaller file", "cant-vote-self-post": "You cannot vote for your own post", - "already-favourited": "You already favourited this post", - "already-unfavourited": "You already unfavourited this post", + "already-favourited": "You have already favourited this post", + "already-unfavourited": "You have already unfavourited this post", "cant-ban-other-admins": "You can't ban other admins!", @@ -63,26 +63,25 @@ "group-already-exists": "Group already exists", "group-name-change-not-allowed": "Group name change not allowed", - "post-already-deleted": "Post already deleted", - "post-already-restored": "Post already restored", - - "topic-already-deleted": "Topic already deleted", - "topic-already-restored": "Topic already restored", + "post-already-deleted": "This post has already been deleted", + "post-already-restored": "This post has already been restored", + "topic-already-deleted": "This topic has already been deleted", + "topic-already-restored": "This topic has already been restored", "topic-thumbnails-are-disabled": "Topic thumbnails are disabled.", "invalid-file": "Invalid File", "uploads-are-disabled": "Uploads are disabled", "upload-error": "Upload Error : %1", - "signature-too-long" : "Signature can't be longer than %1 characters!", + "signature-too-long" : "Sorry, your signature cannot be longer than %1 characters.", "cant-chat-with-yourself": "You can't chat with yourself!", "reputation-system-disabled": "Reputation system is disabled.", "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": "Yo do not have enough reputation to flag this post", + "not-enough-reputation-to-flag": "You do not have enough reputation to flag 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." } \ No newline at end of file diff --git a/public/language/en_GB/global.json b/public/language/en_GB/global.json index dab7fd1df6..3536cb67f9 100644 --- a/public/language/en_GB/global.json +++ b/public/language/en_GB/global.json @@ -94,5 +94,7 @@ "guests": "Guests", "updated.title": "Forum Updated", - "updated.message": "This forum has just been updated to the latest version. Click here to refresh the page." + "updated.message": "This forum has just been updated to the latest version. Click here to refresh the page.", + + "privacy": "Privacy" } diff --git a/public/language/en_GB/groups.json b/public/language/en_GB/groups.json index 954c9780e5..cc4aeb8997 100644 --- a/public/language/en_GB/groups.json +++ b/public/language/en_GB/groups.json @@ -1,4 +1,5 @@ { + "groups": "Groups", "view_group": "View Group", "details.title": "Group Details", diff --git a/public/language/en_GB/notifications.json b/public/language/en_GB/notifications.json index 3cb6ac6872..54c926a597 100644 --- a/public/language/en_GB/notifications.json +++ b/public/language/en_GB/notifications.json @@ -13,11 +13,12 @@ "new_message_from": "New message from %1", "upvoted_your_post_in": "%1 has upvoted your post in %2.", - "moved_your_post": "%1 has moved your post.", - "moved_your_topic": "%1 has moved your topic.", + "moved_your_post": "%1 has moved your post.", + "moved_your_topic": "%1 has moved your topic.", "favourited_your_post_in": "%1 has favourited your post in %2.", "user_flagged_post_in": "%1 flagged a post in %2", "user_posted_to" : "%1 has posted a reply to: %2", + "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.", diff --git a/public/language/en_GB/pages.json b/public/language/en_GB/pages.json index 3d444963e1..8b231f8a94 100644 --- a/public/language/en_GB/pages.json +++ b/public/language/en_GB/pages.json @@ -12,5 +12,8 @@ "user.posts": "Posts made by %1", "user.topics": "Topics created by %1", "user.favourites": "%1's Favourite Posts", - "user.settings": "User Settings" + "user.settings": "User Settings", + + "maintenance.text": "%1 is currently undergoing maintenance. Please come back another time.", + "maintenance.messageIntro": "Additionally, the administrator has left this message:" } \ No newline at end of file diff --git a/public/language/en_GB/topic.json b/public/language/en_GB/topic.json index e837ba664d..88b1662e3b 100644 --- a/public/language/en_GB/topic.json +++ b/public/language/en_GB/topic.json @@ -21,7 +21,6 @@ "restore": "Restore", "move": "Move", "fork": "Fork", - "banned": "banned", "link": "Link", "share": "Share", "tools": "Tools", @@ -88,7 +87,7 @@ "topic_will_be_moved_to": "This topic will be moved to the category", "fork_topic_instruction": "Click the posts you want to fork", "fork_no_pids": "No posts selected!", - "fork_success": "Succesfully forked topic! Click here to go to the forked topic.", + "fork_success": "Successfully forked topic! Click here to go to the forked topic.", "composer.title_placeholder": "Enter your topic title here...", "composer.discard": "Discard", diff --git a/public/language/en_GB/user.json b/public/language/en_GB/user.json index a2995db1b3..223363df05 100644 --- a/public/language/en_GB/user.json +++ b/public/language/en_GB/user.json @@ -52,6 +52,7 @@ "settings": "Settings", "show_email": "Show My Email", + "show_fullname": "Show My Full Name", "digest_label": "Subscribe to Digest", "digest_description": "Subscribe to email updates for this forum (new notifications and topics) according to a set schedule", "digest_off": "Off", diff --git a/public/language/en_GB/users.json b/public/language/en_GB/users.json index 0e391e4505..fa46c52cdd 100644 --- a/public/language/en_GB/users.json +++ b/public/language/en_GB/users.json @@ -5,6 +5,5 @@ "search": "Search", "enter_username": "Enter a username to search", "load_more": "Load More", - "user-not-found": "User not found!", "users-found-search-took": "%1 user(s) found! Search took %2 ms." } \ No newline at end of file diff --git a/public/language/en_US/error.json b/public/language/en_US/error.json index c63073194a..6cbbb9eeb3 100644 --- a/public/language/en_US/error.json +++ b/public/language/en_US/error.json @@ -13,6 +13,7 @@ "invalid-user-data": "Invalid User Data", "invalid-password": "Invalid Password", "invalid-username-or-password": "Please specify both a username and password", + "invalid-search-term": "Invalid search term", "invalid-pagination-value": "Invalid pagination value", "username-taken": "Username taken", "email-taken": "Email taken", diff --git a/public/language/en_US/notifications.json b/public/language/en_US/notifications.json index 4d2461226d..67072ea4ab 100644 --- a/public/language/en_US/notifications.json +++ b/public/language/en_US/notifications.json @@ -11,9 +11,9 @@ "you_have_unread_notifications": "You have unread notifications.", "new_message_from": "New message from %1", "upvoted_your_post_in": "%1 has upvoted your post in %2.", - "moved_your_post": "%1 has moved your post.", - "moved_your_topic": "%1 has moved your topic.", - "favourited_your_post_in": "%1 has favourited your post in %2.", + "moved_your_post": "%1 has moved your post.", + "moved_your_topic": "%1 has moved your topic.", + "favourited_your_post_in": "%1 has favorited your post in %2.", "user_flagged_post_in": "%1 flagged a post in %2", "user_posted_to": "%1 has posted a reply to: %2", "user_mentioned_you_in": "%1 mentioned you in %2", diff --git a/public/language/en_US/pages.json b/public/language/en_US/pages.json index d235dcb87f..65ee8cde3f 100644 --- a/public/language/en_US/pages.json +++ b/public/language/en_US/pages.json @@ -12,5 +12,7 @@ "user.posts": "Posts made by %1", "user.topics": "Topics created by %1", "user.favourites": "%1's Favorite Posts", - "user.settings": "User Settings" + "user.settings": "User Settings", + "maintenance.text": "%1 is currently undergoing maintenance. Please come back another time.", + "maintenance.messageIntro": "Additionally, the administator has left this message:" } \ No newline at end of file diff --git a/public/language/en_US/tags.json b/public/language/en_US/tags.json index f065d4bbfa..d2e9a213ac 100644 --- a/public/language/en_US/tags.json +++ b/public/language/en_US/tags.json @@ -2,5 +2,6 @@ "no_tag_topics": "There are no topics with this tag.", "tags": "Tags", "enter_tags_here": "Enter tags here. Press enter after each tag.", + "enter_tags_here_short": "Enter tags...", "no_tags": "There are no tags yet." } \ No newline at end of file diff --git a/public/language/en_US/topic.json b/public/language/en_US/topic.json index c64d0c9881..cdde46ebd2 100644 --- a/public/language/en_US/topic.json +++ b/public/language/en_US/topic.json @@ -28,15 +28,17 @@ "flag_title": "Flag this post for moderation", "flag_confirm": "Are you sure you want to flag this post?", "flag_success": "This post has been flagged for moderation.", - "deleted_message": "This thread has been deleted. Only users with thread management privileges can see it.", + "deleted_message": "This topic has been deleted. Only users with topic management privileges can see it.", "following_topic.message": "You will now be receiving notifications when somebody posts to this topic.", "not_following_topic.message": "You will no longer receive notifications from this topic.", "login_to_subscribe": "Please register or log in in order to subscribe to this topic.", "markAsUnreadForAll.success": "Topic marked as unread for all.", "watch": "Watch", + "unwatch": "Unwatch", "watch.title": "Be notified of new replies in this topic", + "unwatch.title": "Stop watching this topic", "share_this_post": "Share this Post", - "thread_tools.title": "Thread Tools", + "thread_tools.title": "Topic Tools", "thread_tools.markAsUnreadForAll": "Mark Unread", "thread_tools.pin": "Pin Topic", "thread_tools.unpin": "Unpin Topic", @@ -46,11 +48,11 @@ "thread_tools.move_all": "Move All", "thread_tools.fork": "Fork Topic", "thread_tools.delete": "Delete Topic", - "thread_tools.delete_confirm": "Are you sure you want to delete this thread?", + "thread_tools.delete_confirm": "Are you sure you want to delete this topic?", "thread_tools.restore": "Restore Topic", - "thread_tools.restore_confirm": "Are you sure you want to restore this thread?", + "thread_tools.restore_confirm": "Are you sure you want to restore this topic?", "thread_tools.purge": "Purge Topic", - "thread_tools.purge_confirm": "Are you sure you want to purge this thread?", + "thread_tools.purge_confirm": "Are you sure you want to purge this topic?", "topic_move_success": "This topic has been successfully moved to %1", "post_delete_confirm": "Are you sure you want to delete this post?", "post_restore_confirm": "Are you sure you want to restore this post?", diff --git a/public/language/en_US/user.json b/public/language/en_US/user.json index cdb91a91d1..4e5e30f3fe 100644 --- a/public/language/en_US/user.json +++ b/public/language/en_US/user.json @@ -29,6 +29,7 @@ "edit": "Edit", "uploaded_picture": "Uploaded Picture", "upload_new_picture": "Upload New Picture", + "upload_new_picture_from_url": "Upload New Picture From URL", "current_password": "Current Password", "change_password": "Change Password", "change_password_error": "Invalid Password!", diff --git a/public/language/es/error.json b/public/language/es/error.json index d4c30e6d91..58430eeb56 100644 --- a/public/language/es/error.json +++ b/public/language/es/error.json @@ -13,6 +13,7 @@ "invalid-user-data": "Datos de Usuario no válidos", "invalid-password": "Contraseña no válida", "invalid-username-or-password": "Por favor especifica tanto un usuario como contraseña", + "invalid-search-term": "Invalid search term", "invalid-pagination-value": "Valor de paginación no válido.", "username-taken": "Nombre de usuario ya escogido", "email-taken": "El correo electrónico ya está escogido.", diff --git a/public/language/es/notifications.json b/public/language/es/notifications.json index 31f55eace7..f9007c1f05 100644 --- a/public/language/es/notifications.json +++ b/public/language/es/notifications.json @@ -11,8 +11,8 @@ "you_have_unread_notifications": "Tienes notificaciones sin leer.", "new_message_from": "Nuevo mensaje de %1", "upvoted_your_post_in": "%1 ha votado como relevante tu respuesta en %2.", - "moved_your_post": "%1 ha movido su publicación.", - "moved_your_topic": "%1 ha movido su tema.", + "moved_your_post": "%1 has moved your post.", + "moved_your_topic": "%1 has moved your topic.", "favourited_your_post_in": "%1 ha marcado como favorito su publicación en %2.", "user_flagged_post_in": "%1 ha marcado como indebida una respuesta en %2", "user_posted_to": "%1 ha publicado una respuesta a: %2", diff --git a/public/language/es/pages.json b/public/language/es/pages.json index ab6e4eaedd..de68eca1f7 100644 --- a/public/language/es/pages.json +++ b/public/language/es/pages.json @@ -12,5 +12,7 @@ "user.posts": "Posteos de %1", "user.topics": "Temas creados por %1", "user.favourites": "Publicaciones favoritas de %1 ", - "user.settings": "Preferencias del Usuario" + "user.settings": "Preferencias del Usuario", + "maintenance.text": "%1 is currently undergoing maintenance. Please come back another time.", + "maintenance.messageIntro": "Additionally, the administator has left this message:" } \ No newline at end of file diff --git a/public/language/es/tags.json b/public/language/es/tags.json index cb679cdc7f..36dabaf8f1 100644 --- a/public/language/es/tags.json +++ b/public/language/es/tags.json @@ -2,5 +2,6 @@ "no_tag_topics": "No hay temas con esta etiqueta.", "tags": "Etiquetas", "enter_tags_here": "Introduce las etiquetas aquí. Pulsa intro desde de cada una.", + "enter_tags_here_short": "Enter tags...", "no_tags": "Aún no hay etiquetas." } \ No newline at end of file diff --git a/public/language/es/topic.json b/public/language/es/topic.json index d8eafd749c..0cbd926f40 100644 --- a/public/language/es/topic.json +++ b/public/language/es/topic.json @@ -28,15 +28,17 @@ "flag_title": "Reportar esta publicación a los moderadores", "flag_confirm": "¿Estás seguro de que quieres marcar como indebido este mensaje?", "flag_success": "Este mensaje ha sido marcado para la moderación.", - "deleted_message": "Este tema ha sido borrado. Solo los miembros con privilegios pueden verlo.", + "deleted_message": "This topic has been deleted. Only users with topic management privileges can see it.", "following_topic.message": "Ahora recibiras notificaciones cuando alguien publique en este tema.", "not_following_topic.message": "No recibiras notificaciones de este tema.", "login_to_subscribe": "Por favor, conectate para subscribirte a este tema.", "markAsUnreadForAll.success": "Publicación marcada como no leída para todos.", "watch": "Seguir", + "unwatch": "Unwatch", "watch.title": "Serás notificado cuando haya nuevas respuestas en este tema", + "unwatch.title": "Stop watching this topic", "share_this_post": "Compartir este post", - "thread_tools.title": "Herramientas del Tema", + "thread_tools.title": "Topic Tools", "thread_tools.markAsUnreadForAll": "Marcar como no leído", "thread_tools.pin": "Tema Importante", "thread_tools.unpin": "Quitar Importante", @@ -46,11 +48,11 @@ "thread_tools.move_all": "Mover todo", "thread_tools.fork": "Bifurcar Tema", "thread_tools.delete": "Borrar Tema", - "thread_tools.delete_confirm": "¿Estás seguro de que quieres eliminar este hilo?", + "thread_tools.delete_confirm": "Are you sure you want to delete this topic?", "thread_tools.restore": "Restaurar Tema", - "thread_tools.restore_confirm": "¿Estás seguro de que quieres restaurar este hilo?", + "thread_tools.restore_confirm": "Are you sure you want to restore this topic?", "thread_tools.purge": "Purgar publicación", - "thread_tools.purge_confirm": "¿Estás seguro que deseas purgar este hilo?", + "thread_tools.purge_confirm": "Are you sure you want to purge this topic?", "topic_move_success": "El tema ha sido movido correctamente a %1", "post_delete_confirm": "¿Estás seguro de que quieres eliminar esta respuesta?", "post_restore_confirm": "¿Estás seguro de que quieres restaurar esta respuesta?", diff --git a/public/language/es/user.json b/public/language/es/user.json index 4c2f89a501..4d03c0768d 100644 --- a/public/language/es/user.json +++ b/public/language/es/user.json @@ -29,6 +29,7 @@ "edit": "Editar", "uploaded_picture": "Fotos subidas", "upload_new_picture": "Subir Nueva Foto", + "upload_new_picture_from_url": "Upload New Picture From URL", "current_password": "Contraseña actual", "change_password": "Cambiar Contraseña", "change_password_error": "Contraseña no válida!", diff --git a/public/language/et/error.json b/public/language/et/error.json index 51926c86b5..971f42eaaf 100644 --- a/public/language/et/error.json +++ b/public/language/et/error.json @@ -13,6 +13,7 @@ "invalid-user-data": "Vigased kasutaja andmed", "invalid-password": "Vigane parool", "invalid-username-or-password": "Please specify both a username and password", + "invalid-search-term": "Invalid search term", "invalid-pagination-value": "Vigane lehe väärtus", "username-taken": "Kasutajanimi on juba võetud", "email-taken": "Email on võetud", diff --git a/public/language/et/notifications.json b/public/language/et/notifications.json index 8ee69d3072..55352cc6df 100644 --- a/public/language/et/notifications.json +++ b/public/language/et/notifications.json @@ -11,8 +11,8 @@ "you_have_unread_notifications": "Sul ei ole lugemata teateid.", "new_message_from": "Uus sõnum kasutajalt %1", "upvoted_your_post_in": "%1 has upvoted your post in %2.", - "moved_your_post": "%1 has moved your post.", - "moved_your_topic": "%1 has moved your topic.", + "moved_your_post": "%1 has moved your post.", + "moved_your_topic": "%1 has moved your topic.", "favourited_your_post_in": "%1 has favourited your post in %2.", "user_flagged_post_in": "%1 flagged a post in %2", "user_posted_to": "Kasutaja %1 postitas vastuse teemasse %2", diff --git a/public/language/et/pages.json b/public/language/et/pages.json index 9cc027eaaf..24801a804f 100644 --- a/public/language/et/pages.json +++ b/public/language/et/pages.json @@ -12,5 +12,7 @@ "user.posts": "Postitused, mis on tehtud kasutaja %1 poolt", "user.topics": "Teemad on kirjutanud %1", "user.favourites": "%1's lemmikud postitused", - "user.settings": "Kasutaja sätted" + "user.settings": "Kasutaja sätted", + "maintenance.text": "%1 is currently undergoing maintenance. Please come back another time.", + "maintenance.messageIntro": "Additionally, the administator has left this message:" } \ No newline at end of file diff --git a/public/language/et/tags.json b/public/language/et/tags.json index b746eae6d1..083121ea1b 100644 --- a/public/language/et/tags.json +++ b/public/language/et/tags.json @@ -2,5 +2,6 @@ "no_tag_topics": "Teemasid, mis sisaldaksid seda märksõna, ei eksisteeri.", "tags": "Märksõnad", "enter_tags_here": "Sisesta märksõnad siia. Iga märksõna tagant sisesta enterit.", + "enter_tags_here_short": "Enter tags...", "no_tags": "Siin ei ole veel ühtegi märksõna." } \ No newline at end of file diff --git a/public/language/et/topic.json b/public/language/et/topic.json index 8b57bef58d..7c0f745a23 100644 --- a/public/language/et/topic.json +++ b/public/language/et/topic.json @@ -28,15 +28,17 @@ "flag_title": "Märgista see postitus modereerimiseks", "flag_confirm": "Oled kindel, et soovid märgistada antud postituse?", "flag_success": "See posits on nüüd märgistatud modereerimiseks.", - "deleted_message": "Teema kustutati. Ainult kasutajad, kellel on piisavad õigused saavad seda veel näha.", + "deleted_message": "This topic has been deleted. Only users with topic management privileges can see it.", "following_topic.message": "Sulle ei edastata enam teateid uutest postitustest kui keegi postitab siia teemasse.", "not_following_topic.message": "Sulle ei edastata enam teateid uutest postitustest siin teemas.", "login_to_subscribe": "Palun registreeru kasutajaks või logi sisse, et tellida teateid selle postituse kohta.", "markAsUnreadForAll.success": "Teema märgitud mitte-loetuks kõikidele.", "watch": "Vaata", + "unwatch": "Unwatch", "watch.title": "Saa teateid uutest postitustest siin teemas", + "unwatch.title": "Stop watching this topic", "share_this_post": "Jaga seda postitust", - "thread_tools.title": "Teema tööriistad", + "thread_tools.title": "Topic Tools", "thread_tools.markAsUnreadForAll": "Märgi mitte-loetuks", "thread_tools.pin": "Tõsta esile teema", "thread_tools.unpin": "Märgista teema", @@ -46,11 +48,11 @@ "thread_tools.move_all": "Liiguta kõik", "thread_tools.fork": "Fork Topic", "thread_tools.delete": "Kustuta teema", - "thread_tools.delete_confirm": "Oled kindel, et soovid kustutada antud teema?", + "thread_tools.delete_confirm": "Are you sure you want to delete this topic?", "thread_tools.restore": "Taasta teema", - "thread_tools.restore_confirm": "Oled kindel, et soovid taastada antud teema?", + "thread_tools.restore_confirm": "Are you sure you want to restore this topic?", "thread_tools.purge": "Kustuta teema täielikult", - "thread_tools.purge_confirm": "Oled kindel, et soovid täielikult kustutada selle teema?", + "thread_tools.purge_confirm": "Are you sure you want to purge this topic?", "topic_move_success": "Teema liigutatud kategooriasse %1", "post_delete_confirm": "Oled kindel, et soovid kustutada selle postituse?", "post_restore_confirm": "Oled kindel, et soovid taastada antud postituse?", diff --git a/public/language/et/user.json b/public/language/et/user.json index 9decf2a23e..088492c645 100644 --- a/public/language/et/user.json +++ b/public/language/et/user.json @@ -29,6 +29,7 @@ "edit": "Muuda", "uploaded_picture": "Üleslaetud pilt", "upload_new_picture": "Laadi uus pilt", + "upload_new_picture_from_url": "Upload New Picture From URL", "current_password": "Praegune parool", "change_password": "Vaheta parooli", "change_password_error": "Vigane parool!", diff --git a/public/language/fa_IR/error.json b/public/language/fa_IR/error.json index 64f4d35823..c677c890fb 100644 --- a/public/language/fa_IR/error.json +++ b/public/language/fa_IR/error.json @@ -13,6 +13,7 @@ "invalid-user-data": "داده‌های کاربری نامعتبر است.", "invalid-password": "گذرواژه نامعتبر است.", "invalid-username-or-password": "Please specify both a username and password", + "invalid-search-term": "Invalid search term", "invalid-pagination-value": "عدد صفحه‌بندی نامعتبر است.", "username-taken": "این نام کاربری گرفته شده است.", "email-taken": "این رایانامه گرفته شده است.", diff --git a/public/language/fa_IR/notifications.json b/public/language/fa_IR/notifications.json index 62b48b71af..d572a8099c 100644 --- a/public/language/fa_IR/notifications.json +++ b/public/language/fa_IR/notifications.json @@ -11,8 +11,8 @@ "you_have_unread_notifications": "شما آگاه‌سازی‌های نخوانده دارید.", "new_message_from": "پیام تازه از %1", "upvoted_your_post_in": "%1 has upvoted your post in %2.", - "moved_your_post": "%1 has moved your post.", - "moved_your_topic": "%1 has moved your topic.", + "moved_your_post": "%1 has moved your post.", + "moved_your_topic": "%1 has moved your topic.", "favourited_your_post_in": "%1 has favourited your post in %2.", "user_flagged_post_in": "%1 flagged a post in %2", "user_posted_to": "پاسخ دادن به %2 از سوی %1", diff --git a/public/language/fa_IR/pages.json b/public/language/fa_IR/pages.json index 94458a27a2..66ad6a0aa6 100644 --- a/public/language/fa_IR/pages.json +++ b/public/language/fa_IR/pages.json @@ -12,5 +12,7 @@ "user.posts": "دیدگاه‌های %1", "user.topics": "%1 این جستار را ساخت.", "user.favourites": "دیدگاه‌های پسندیدهٔ %1", - "user.settings": "تنظیمات کاربر" + "user.settings": "تنظیمات کاربر", + "maintenance.text": "%1 is currently undergoing maintenance. Please come back another time.", + "maintenance.messageIntro": "Additionally, the administator has left this message:" } \ No newline at end of file diff --git a/public/language/fa_IR/tags.json b/public/language/fa_IR/tags.json index 5f9c79ea58..78c658e958 100644 --- a/public/language/fa_IR/tags.json +++ b/public/language/fa_IR/tags.json @@ -2,5 +2,6 @@ "no_tag_topics": "جُستاری با این برچسب وجود ندارد.", "tags": "برچسب‌ها", "enter_tags_here": "برچسب‌ها را اینجا وارد کنید. بعد از هر برچسب، اینتر بزنید.", + "enter_tags_here_short": "Enter tags...", "no_tags": "هنوز برچسبی وجود ندارد." } \ No newline at end of file diff --git a/public/language/fa_IR/topic.json b/public/language/fa_IR/topic.json index 47f345fc6d..3472dc0792 100644 --- a/public/language/fa_IR/topic.json +++ b/public/language/fa_IR/topic.json @@ -28,15 +28,17 @@ "flag_title": "پرچم‌گذاری این جستار برای بررسی ناظران", "flag_confirm": "آیا مطمئنید که می‌خواهید روی این دیدگاه پرچم بگذارید.", "flag_success": "این جستار برای بررسی ناظران پرچم گذاشته شد.", - "deleted_message": "این جستار پاک شده است. تنها کاربران دارای پروانه‌های مدیریت جستار می‌توانند آن را ببینند.", + "deleted_message": "This topic has been deleted. Only users with topic management privileges can see it.", "following_topic.message": "از این پس اگر کسی در این جستار دیدگاه بگذارد، شما آگاه خواهید شد.", "not_following_topic.message": "شما دیگر آگاه‌سازی‌های این جستار را دریافت نخواهید کرد.", "login_to_subscribe": "برای دنبال کردن این جستار، لطفا نام‌نویسی کنید یا به درون بیایید.", "markAsUnreadForAll.success": "جستار برای همگان نخوانده در نظر گرفته شد.", "watch": "تماشا کردن", + "unwatch": "Unwatch", "watch.title": "از پاسخ‌های تازه به این جستار آگاه شوید.", + "unwatch.title": "Stop watching this topic", "share_this_post": "به اشتراک‌گذاری این جستار", - "thread_tools.title": "ابزاهای جستار", + "thread_tools.title": "Topic Tools", "thread_tools.markAsUnreadForAll": "نخوانده بگیر", "thread_tools.pin": "سنجاق زدن جستار", "thread_tools.unpin": "برداشتن سنجاق جستار", @@ -46,11 +48,11 @@ "thread_tools.move_all": "جابجایی همه", "thread_tools.fork": "شاخه ساختن از جستار", "thread_tools.delete": "پاک کردن جستار", - "thread_tools.delete_confirm": "آیا از پاک کردن این جستار اطمینان دارید؟", + "thread_tools.delete_confirm": "Are you sure you want to delete this topic?", "thread_tools.restore": "برگرداندن جستار", - "thread_tools.restore_confirm": "آیا از بازگردانی این جستار اطمینان دارید؟", + "thread_tools.restore_confirm": "Are you sure you want to restore this topic?", "thread_tools.purge": "پاک کردن جستار", - "thread_tools.purge_confirm": "آیا از پاک کردن این جستار اطمینان دارید؟", + "thread_tools.purge_confirm": "Are you sure you want to purge this topic?", "topic_move_success": "جابه‌جایی این جستار به %1 باموفقیت انجام شد.", "post_delete_confirm": "آیا از پاک کردن این دیدگاه اطمینان دارید؟", "post_restore_confirm": "آیا از بازگردانی این دیدگاه اطمینان دارید؟", diff --git a/public/language/fa_IR/user.json b/public/language/fa_IR/user.json index e6085077d4..1abaf529e4 100644 --- a/public/language/fa_IR/user.json +++ b/public/language/fa_IR/user.json @@ -29,6 +29,7 @@ "edit": "ویرایش", "uploaded_picture": "تصویر بارشده", "upload_new_picture": "بارگذاری تصویر تازه", + "upload_new_picture_from_url": "Upload New Picture From URL", "current_password": "گذرواژه کنونی", "change_password": "تغیر گذرواژه", "change_password_error": "گذرواژهٔ نامعتبر!", diff --git a/public/language/fi/error.json b/public/language/fi/error.json index 73febab0c3..6898e63d6c 100644 --- a/public/language/fi/error.json +++ b/public/language/fi/error.json @@ -13,6 +13,7 @@ "invalid-user-data": "Virheellinen käyttäjätieto", "invalid-password": "Virheellinen salasana", "invalid-username-or-password": "Please specify both a username and password", + "invalid-search-term": "Invalid search term", "invalid-pagination-value": "Virheellinen taittoarvo", "username-taken": "Käyttäjänimi varattu", "email-taken": "Sähköpostiosoite varattu", diff --git a/public/language/fi/notifications.json b/public/language/fi/notifications.json index 2eb5163d4a..94a9200830 100644 --- a/public/language/fi/notifications.json +++ b/public/language/fi/notifications.json @@ -11,8 +11,8 @@ "you_have_unread_notifications": "Sinulla on lukemattomia ilmoituksia.", "new_message_from": "Uusi viesti käyttäjältä %1", "upvoted_your_post_in": "%1 has upvoted your post in %2.", - "moved_your_post": "%1 has moved your post.", - "moved_your_topic": "%1 has moved your topic.", + "moved_your_post": "%1 has moved your post.", + "moved_your_topic": "%1 has moved your topic.", "favourited_your_post_in": "%1 has favourited your post in %2.", "user_flagged_post_in": "%1 flagged a post in %2", "user_posted_to": "%1 has posted a reply to: %2", diff --git a/public/language/fi/pages.json b/public/language/fi/pages.json index 899ce00bf2..7141fe172a 100644 --- a/public/language/fi/pages.json +++ b/public/language/fi/pages.json @@ -12,5 +12,7 @@ "user.posts": "Käyttäjän %1 kirjoittamat viestit", "user.topics": "Käyttäjän %1 aloittamat aiheet", "user.favourites": "Käyttäjän %1 suosikkiviestit", - "user.settings": "Käyttäjän asetukset" + "user.settings": "Käyttäjän asetukset", + "maintenance.text": "%1 is currently undergoing maintenance. Please come back another time.", + "maintenance.messageIntro": "Additionally, the administator has left this message:" } \ No newline at end of file diff --git a/public/language/fi/tags.json b/public/language/fi/tags.json index f065d4bbfa..d2e9a213ac 100644 --- a/public/language/fi/tags.json +++ b/public/language/fi/tags.json @@ -2,5 +2,6 @@ "no_tag_topics": "There are no topics with this tag.", "tags": "Tags", "enter_tags_here": "Enter tags here. Press enter after each tag.", + "enter_tags_here_short": "Enter tags...", "no_tags": "There are no tags yet." } \ No newline at end of file diff --git a/public/language/fi/topic.json b/public/language/fi/topic.json index bccb155bf4..ea5ad2122b 100644 --- a/public/language/fi/topic.json +++ b/public/language/fi/topic.json @@ -28,15 +28,17 @@ "flag_title": "Ilmianna tämä viesti moderaattoreille", "flag_confirm": "Are you sure you want to flag this post?", "flag_success": "This post has been flagged for moderation.", - "deleted_message": "Tämä viestiketju on poistettu. Vain käyttäjät, joilla on viestiketjujen hallintaoikeudet, voivat nähdä sen.", + "deleted_message": "This topic has been deleted. Only users with topic management privileges can see it.", "following_topic.message": "Saat nyt ilmoituksen, kun joku kirjoittaa tähän aiheeseen.", "not_following_topic.message": "Et saa enää ilmoituksia tästä aiheesta.", "login_to_subscribe": "Please register or log in in order to subscribe to this topic.", "markAsUnreadForAll.success": "Topic marked as unread for all.", "watch": "Tarkkaile", + "unwatch": "Unwatch", "watch.title": "Be notified of new replies in this topic", + "unwatch.title": "Stop watching this topic", "share_this_post": "Jaa tämä viesti", - "thread_tools.title": "Aiheen työkalut", + "thread_tools.title": "Topic Tools", "thread_tools.markAsUnreadForAll": "Merkitse lukemattomaksi", "thread_tools.pin": "Kiinnitä aihe", "thread_tools.unpin": "Poista aiheen kiinnitys", @@ -46,11 +48,11 @@ "thread_tools.move_all": "Move All", "thread_tools.fork": "Haaroita aihe", "thread_tools.delete": "Poista aihe", - "thread_tools.delete_confirm": "Are you sure you want to delete this thread?", + "thread_tools.delete_confirm": "Are you sure you want to delete this topic?", "thread_tools.restore": "Palauta aihe", - "thread_tools.restore_confirm": "Are you sure you want to restore this thread?", + "thread_tools.restore_confirm": "Are you sure you want to restore this topic?", "thread_tools.purge": "Purge Topic", - "thread_tools.purge_confirm": "Are you sure you want to purge this thread?", + "thread_tools.purge_confirm": "Are you sure you want to purge this topic?", "topic_move_success": "This topic has been successfully moved to %1", "post_delete_confirm": "Are you sure you want to delete this post?", "post_restore_confirm": "Are you sure you want to restore this post?", diff --git a/public/language/fi/user.json b/public/language/fi/user.json index 930f9cffb0..4df8432aec 100644 --- a/public/language/fi/user.json +++ b/public/language/fi/user.json @@ -29,6 +29,7 @@ "edit": "Muokkaa", "uploaded_picture": "Ladattu kuva", "upload_new_picture": "Lataa uusi kuva", + "upload_new_picture_from_url": "Upload New Picture From URL", "current_password": "Nykyinen salasana", "change_password": "Vaihda salasana", "change_password_error": "Virheellinen salasana", diff --git a/public/language/fr/error.json b/public/language/fr/error.json index bcc272f6d8..63885e4418 100644 --- a/public/language/fr/error.json +++ b/public/language/fr/error.json @@ -13,6 +13,7 @@ "invalid-user-data": "Données utilisateur invalides", "invalid-password": "Mot de passe invalide", "invalid-username-or-password": "Please specify both a username and password", + "invalid-search-term": "Invalid search term", "invalid-pagination-value": "Valeur de pagination invalide", "username-taken": "Nom d’utilisateur déjà utilisé", "email-taken": "Email déjà utilisé", diff --git a/public/language/fr/notifications.json b/public/language/fr/notifications.json index 5e1178f78c..ee94871b46 100644 --- a/public/language/fr/notifications.json +++ b/public/language/fr/notifications.json @@ -11,8 +11,8 @@ "you_have_unread_notifications": "Vous avez des notifications non-lues", "new_message_from": "Nouveau message de %1", "upvoted_your_post_in": "%1 has upvoted your post in %2.", - "moved_your_post": "%1 has moved your post.", - "moved_your_topic": "%1 has moved your topic.", + "moved_your_post": "%1 has moved your post.", + "moved_your_topic": "%1 has moved your topic.", "favourited_your_post_in": "%1 has favourited your post in %2.", "user_flagged_post_in": "%1 flagged a post in %2", "user_posted_to": "%1 a répondu à : %2", diff --git a/public/language/fr/pages.json b/public/language/fr/pages.json index ad713d898b..e4c70d413a 100644 --- a/public/language/fr/pages.json +++ b/public/language/fr/pages.json @@ -12,5 +12,7 @@ "user.posts": "Message écrit par %1", "user.topics": "Sujets créés par %1", "user.favourites": "Messages favoris de %1", - "user.settings": "Préférences utilisateur" + "user.settings": "Préférences utilisateur", + "maintenance.text": "%1 is currently undergoing maintenance. Please come back another time.", + "maintenance.messageIntro": "Additionally, the administator has left this message:" } \ No newline at end of file diff --git a/public/language/fr/tags.json b/public/language/fr/tags.json index d89f217a37..ba411020b9 100644 --- a/public/language/fr/tags.json +++ b/public/language/fr/tags.json @@ -2,5 +2,6 @@ "no_tag_topics": "Il n'y a aucun sujet ayant ce mot-clé", "tags": "Mots-clés", "enter_tags_here": "Entrez les mots-clés ici. Appuyez sur entrer après chaque mot-clé.", + "enter_tags_here_short": "Enter tags...", "no_tags": "Il n'y a pas encore de mots-clés." } \ No newline at end of file diff --git a/public/language/fr/topic.json b/public/language/fr/topic.json index f73fad2ed0..eee07ade43 100644 --- a/public/language/fr/topic.json +++ b/public/language/fr/topic.json @@ -28,15 +28,17 @@ "flag_title": "Signaler ce post à la modération", "flag_confirm": "Êtes-vous sûr de bien vouloir signaler ce message ?", "flag_success": "Ce message a bien été signalé aux modérateurs.", - "deleted_message": "Ce sujet a été supprimé. Seuls les utilsateurs ayant les droits d'administration peuvent le voir.", + "deleted_message": "This topic has been deleted. Only users with topic management privileges can see it.", "following_topic.message": "Vous recevrez désormais des notifications lorsque quelqu'un postera dans ce sujet.", "not_following_topic.message": "Vous ne recevrez plus de notifications pour ce sujet.", "login_to_subscribe": "Veuillez vous enregistrer ou vous connecter afin de vous abonner à ce sujet.", "markAsUnreadForAll.success": "Sujet marqué comme non lu pour tout le monde.", "watch": "Suivre", + "unwatch": "Unwatch", "watch.title": "Être notifié des nouvelles réponses dans ce sujet", + "unwatch.title": "Stop watching this topic", "share_this_post": "Partager ce message", - "thread_tools.title": "Outils", + "thread_tools.title": "Topic Tools", "thread_tools.markAsUnreadForAll": "Marquer comme non lu", "thread_tools.pin": "Epingler le sujet", "thread_tools.unpin": "Désépingler le sujet", @@ -46,11 +48,11 @@ "thread_tools.move_all": "Déplacer tout", "thread_tools.fork": "Scinder le sujet", "thread_tools.delete": "Supprimer le sujet", - "thread_tools.delete_confirm": "Êtes-vous sûr de bien vouloir supprimer ce sujet ?", + "thread_tools.delete_confirm": "Are you sure you want to delete this topic?", "thread_tools.restore": "Restaurer le sujet", - "thread_tools.restore_confirm": "Êtes-vous sûr de bien vouloir restaurer ce sujet ?", + "thread_tools.restore_confirm": "Are you sure you want to restore this topic?", "thread_tools.purge": "Supprimer définitivement le(s) sujet(s)", - "thread_tools.purge_confirm": "Êtes-vous sûr de bien vouloir supprimer définitivement ce fil ?", + "thread_tools.purge_confirm": "Are you sure you want to purge this topic?", "topic_move_success": "Ce sujet a bien été déplacé vers %1.", "post_delete_confirm": "Êtes-vous sûr de bien vouloir supprimer ce message ?", "post_restore_confirm": "Êtes-vous sûr de bien vouloir restaurer ce message ?", diff --git a/public/language/fr/user.json b/public/language/fr/user.json index a476ad9f05..26912f28a9 100644 --- a/public/language/fr/user.json +++ b/public/language/fr/user.json @@ -29,6 +29,7 @@ "edit": "Editer", "uploaded_picture": "Image envoyée", "upload_new_picture": "Envoyer une nouvelle image", + "upload_new_picture_from_url": "Upload New Picture From URL", "current_password": "Mot de passe actuel", "change_password": "Changer le mot de passe", "change_password_error": "Mot de passe invalide !", diff --git a/public/language/he/error.json b/public/language/he/error.json index 28cd04e663..2cc13fa0bb 100644 --- a/public/language/he/error.json +++ b/public/language/he/error.json @@ -13,6 +13,7 @@ "invalid-user-data": "מידע משתמש שגוי", "invalid-password": "סיסמא שגויה", "invalid-username-or-password": "Please specify both a username and password", + "invalid-search-term": "Invalid search term", "invalid-pagination-value": "ערך דפדוף שגוי", "username-taken": "שם משתמש תפוס", "email-taken": "כתובת אימייל תפוסה", diff --git a/public/language/he/notifications.json b/public/language/he/notifications.json index f3f8efa00f..02d485149b 100644 --- a/public/language/he/notifications.json +++ b/public/language/he/notifications.json @@ -11,8 +11,8 @@ "you_have_unread_notifications": "You have unread notifications.", "new_message_from": "New message from %1", "upvoted_your_post_in": "%1 has upvoted your post in %2.", - "moved_your_post": "%1 has moved your post.", - "moved_your_topic": "%1 has moved your topic.", + "moved_your_post": "%1 has moved your post.", + "moved_your_topic": "%1 has moved your topic.", "favourited_your_post_in": "%1 has favourited your post in %2.", "user_flagged_post_in": "%1 flagged a post in %2", "user_posted_to": "%1 has posted a reply to: %2", diff --git a/public/language/he/pages.json b/public/language/he/pages.json index d12dc6944f..38955ecec1 100644 --- a/public/language/he/pages.json +++ b/public/language/he/pages.json @@ -12,5 +12,7 @@ "user.posts": "פוסטים שהועלו על ידי %1", "user.topics": "נושאים שנוצרו על ידי %1", "user.favourites": "הפוסטים המועדפים על %1", - "user.settings": "הגדרות משתמש" + "user.settings": "הגדרות משתמש", + "maintenance.text": "%1 is currently undergoing maintenance. Please come back another time.", + "maintenance.messageIntro": "Additionally, the administator has left this message:" } \ No newline at end of file diff --git a/public/language/he/tags.json b/public/language/he/tags.json index 3ddd4ab34b..94d002603f 100644 --- a/public/language/he/tags.json +++ b/public/language/he/tags.json @@ -2,5 +2,6 @@ "no_tag_topics": "אין פוסטים עם תגית זו.", "tags": "תגיות", "enter_tags_here": "יש להכניס כאן את התגיות. לחץ אנטר אחרי כל תגית.", + "enter_tags_here_short": "Enter tags...", "no_tags": "אין עדיין תגיות." } \ No newline at end of file diff --git a/public/language/he/topic.json b/public/language/he/topic.json index 59d3ab6736..6b49bd2ec0 100644 --- a/public/language/he/topic.json +++ b/public/language/he/topic.json @@ -28,15 +28,17 @@ "flag_title": "דווח על פוסט זה למנהל", "flag_confirm": "Are you sure you want to flag this post?", "flag_success": "This post has been flagged for moderation.", - "deleted_message": "הנושא הזה נמחק. רק מנהלים מורשים לראות אותו", + "deleted_message": "This topic has been deleted. Only users with topic management privileges can see it.", "following_topic.message": "מעתה, תקבל הודעות כאשר מישהו יעלה פוסט לנושא זה.", "not_following_topic.message": "לא תקבל הודעות נוספות בנושא זה.", "login_to_subscribe": "Please register or log in in order to subscribe to this topic.", "markAsUnreadForAll.success": "Topic marked as unread for all.", "watch": "עקוב", + "unwatch": "Unwatch", "watch.title": "Be notified of new replies in this topic", + "unwatch.title": "Stop watching this topic", "share_this_post": "שתף פוסט זה", - "thread_tools.title": "כלים", + "thread_tools.title": "Topic Tools", "thread_tools.markAsUnreadForAll": "סמן כלא נקרא", "thread_tools.pin": "נעץ נושא", "thread_tools.unpin": "הסר נעץ", @@ -46,11 +48,11 @@ "thread_tools.move_all": "Move All", "thread_tools.fork": "שכפל נושא", "thread_tools.delete": "מחק נושא", - "thread_tools.delete_confirm": "Are you sure you want to delete this thread?", + "thread_tools.delete_confirm": "Are you sure you want to delete this topic?", "thread_tools.restore": "שחזר נושא", - "thread_tools.restore_confirm": "Are you sure you want to restore this thread?", + "thread_tools.restore_confirm": "Are you sure you want to restore this topic?", "thread_tools.purge": "Purge Topic", - "thread_tools.purge_confirm": "Are you sure you want to purge this thread?", + "thread_tools.purge_confirm": "Are you sure you want to purge this topic?", "topic_move_success": "This topic has been successfully moved to %1", "post_delete_confirm": "Are you sure you want to delete this post?", "post_restore_confirm": "Are you sure you want to restore this post?", diff --git a/public/language/he/user.json b/public/language/he/user.json index 58ee8081e5..ea6a281a62 100644 --- a/public/language/he/user.json +++ b/public/language/he/user.json @@ -29,6 +29,7 @@ "edit": "ערוך", "uploaded_picture": "התמונה הועלתה", "upload_new_picture": "העלה תמונה חדשה", + "upload_new_picture_from_url": "Upload New Picture From URL", "current_password": "סיסמה נוכחית", "change_password": "שנה סיסמה", "change_password_error": "סיסמה אינה תקינה!", diff --git a/public/language/hu/error.json b/public/language/hu/error.json index f90d459cce..a46573d5b9 100644 --- a/public/language/hu/error.json +++ b/public/language/hu/error.json @@ -13,6 +13,7 @@ "invalid-user-data": "Invalid User Data", "invalid-password": "Invalid Password", "invalid-username-or-password": "Please specify both a username and password", + "invalid-search-term": "Invalid search term", "invalid-pagination-value": "Invalid pagination value", "username-taken": "Username taken", "email-taken": "Email taken", diff --git a/public/language/hu/notifications.json b/public/language/hu/notifications.json index 86ce42f88a..efe19c3892 100644 --- a/public/language/hu/notifications.json +++ b/public/language/hu/notifications.json @@ -11,8 +11,8 @@ "you_have_unread_notifications": "You have unread notifications.", "new_message_from": "New message from %1", "upvoted_your_post_in": "%1 has upvoted your post in %2.", - "moved_your_post": "%1 has moved your post.", - "moved_your_topic": "%1 has moved your topic.", + "moved_your_post": "%1 has moved your post.", + "moved_your_topic": "%1 has moved your topic.", "favourited_your_post_in": "%1 has favourited your post in %2.", "user_flagged_post_in": "%1 flagged a post in %2", "user_posted_to": "%1 has posted a reply to: %2", diff --git a/public/language/hu/pages.json b/public/language/hu/pages.json index 7057c11031..94c1922e5a 100644 --- a/public/language/hu/pages.json +++ b/public/language/hu/pages.json @@ -12,5 +12,7 @@ "user.posts": "Hozzászólások által %1", "user.topics": "Topics created by %1", "user.favourites": "%1 Kedvenc Hozzászólásai", - "user.settings": "Felhasználói Beállítások" + "user.settings": "Felhasználói Beállítások", + "maintenance.text": "%1 is currently undergoing maintenance. Please come back another time.", + "maintenance.messageIntro": "Additionally, the administator has left this message:" } \ No newline at end of file diff --git a/public/language/hu/tags.json b/public/language/hu/tags.json index f065d4bbfa..d2e9a213ac 100644 --- a/public/language/hu/tags.json +++ b/public/language/hu/tags.json @@ -2,5 +2,6 @@ "no_tag_topics": "There are no topics with this tag.", "tags": "Tags", "enter_tags_here": "Enter tags here. Press enter after each tag.", + "enter_tags_here_short": "Enter tags...", "no_tags": "There are no tags yet." } \ No newline at end of file diff --git a/public/language/hu/topic.json b/public/language/hu/topic.json index 44e43200be..e65a4ca164 100644 --- a/public/language/hu/topic.json +++ b/public/language/hu/topic.json @@ -28,15 +28,17 @@ "flag_title": "A hozzászólás jelentése a moderátoroknál", "flag_confirm": "Are you sure you want to flag this post?", "flag_success": "This post has been flagged for moderation.", - "deleted_message": "Ez a topik törölve lett. Kizárólag azok a felhasználók láthatják, akiknek joga van hozzá.", + "deleted_message": "This topic has been deleted. Only users with topic management privileges can see it.", "following_topic.message": "You will now be receiving notifications when somebody posts to this topic.", "not_following_topic.message": "You will no longer receive notifications from this topic.", "login_to_subscribe": "Please register or log in in order to subscribe to this topic.", "markAsUnreadForAll.success": "Topic marked as unread for all.", "watch": "Követés", + "unwatch": "Unwatch", "watch.title": "Be notified of new replies in this topic", + "unwatch.title": "Stop watching this topic", "share_this_post": "Hozzászólás megosztása", - "thread_tools.title": "Téma Eszközök", + "thread_tools.title": "Topic Tools", "thread_tools.markAsUnreadForAll": "Olvasatlannak jelölés", "thread_tools.pin": "Kiemel", "thread_tools.unpin": "Kiemelés megszűntetése", @@ -46,11 +48,11 @@ "thread_tools.move_all": "Move All", "thread_tools.fork": "Topik szétszedése", "thread_tools.delete": "Topik törlése", - "thread_tools.delete_confirm": "Are you sure you want to delete this thread?", + "thread_tools.delete_confirm": "Are you sure you want to delete this topic?", "thread_tools.restore": "Topik visszaállítása", - "thread_tools.restore_confirm": "Are you sure you want to restore this thread?", + "thread_tools.restore_confirm": "Are you sure you want to restore this topic?", "thread_tools.purge": "Purge Topic", - "thread_tools.purge_confirm": "Are you sure you want to purge this thread?", + "thread_tools.purge_confirm": "Are you sure you want to purge this topic?", "topic_move_success": "This topic has been successfully moved to %1", "post_delete_confirm": "Are you sure you want to delete this post?", "post_restore_confirm": "Are you sure you want to restore this post?", diff --git a/public/language/hu/user.json b/public/language/hu/user.json index a7ef8fc8c0..b84772af8d 100644 --- a/public/language/hu/user.json +++ b/public/language/hu/user.json @@ -29,6 +29,7 @@ "edit": "Szerkeszt", "uploaded_picture": "Feltöltött kép", "upload_new_picture": "Új kép feltöltése", + "upload_new_picture_from_url": "Upload New Picture From URL", "current_password": "Current Password", "change_password": "Jelszó megváltoztatása", "change_password_error": "Helytelen jelszó!", diff --git a/public/language/it/error.json b/public/language/it/error.json index 2f3761f472..79bc136692 100644 --- a/public/language/it/error.json +++ b/public/language/it/error.json @@ -13,6 +13,7 @@ "invalid-user-data": "Dati Utente non validi", "invalid-password": "Password non valida", "invalid-username-or-password": "Si prega di specificare sia un nome utente che una password", + "invalid-search-term": "Invalid search term", "invalid-pagination-value": "Valore di paginazione non valido", "username-taken": "Nome utente già preso", "email-taken": "Email già esistente", diff --git a/public/language/it/notifications.json b/public/language/it/notifications.json index 91ee4d5a1b..f6591255f3 100644 --- a/public/language/it/notifications.json +++ b/public/language/it/notifications.json @@ -11,8 +11,8 @@ "you_have_unread_notifications": "Hai notifiche non lette.", "new_message_from": "Nuovo messaggio da %1", "upvoted_your_post_in": "%1 ha votato positivamente il tuo post in %2.", - "moved_your_post": "%1 has spostato il tuo post.", - "moved_your_topic": "%1 ha spostato la tua discussione.", + "moved_your_post": "%1 has moved your post.", + "moved_your_topic": "%1 has moved your topic.", "favourited_your_post_in": "%1 ha messo nei favoriti il tuo post in %2.", "user_flagged_post_in": "%1 ha segnalato un post in %2", "user_posted_to": "%1 ha postato una risposta a: %2", diff --git a/public/language/it/pages.json b/public/language/it/pages.json index 7ce7f4cd7f..0186ca5a79 100644 --- a/public/language/it/pages.json +++ b/public/language/it/pages.json @@ -12,5 +12,7 @@ "user.posts": "Post creati da %1", "user.topics": "Discussioni create da %1", "user.favourites": "Post Favoriti da %1", - "user.settings": "Impostazioni Utente" + "user.settings": "Impostazioni Utente", + "maintenance.text": "%1 is currently undergoing maintenance. Please come back another time.", + "maintenance.messageIntro": "Additionally, the administator has left this message:" } \ No newline at end of file diff --git a/public/language/it/tags.json b/public/language/it/tags.json index 49f0d53fa6..a5eb7f96e6 100644 --- a/public/language/it/tags.json +++ b/public/language/it/tags.json @@ -2,5 +2,6 @@ "no_tag_topics": "Non ci sono discussioni con questo tag.", "tags": "Tags", "enter_tags_here": "Inserire qui i tags. Premere invio dopo ogni tag.", + "enter_tags_here_short": "Enter tags...", "no_tags": "Non ci sono ancora tags." } \ No newline at end of file diff --git a/public/language/it/topic.json b/public/language/it/topic.json index adeb47a71d..2eb9541a01 100644 --- a/public/language/it/topic.json +++ b/public/language/it/topic.json @@ -28,15 +28,17 @@ "flag_title": "Segnala questo post per la moderazione", "flag_confirm": "Sei sicuro di voler contrassegnare questo post?", "flag_success": "Questo post è stato contrassegnato per la moderazione.", - "deleted_message": "Questa discussione è stata cancellata. Solo gli utenti con diritti di gestione possono vederla.", + "deleted_message": "This topic has been deleted. Only users with topic management privileges can see it.", "following_topic.message": "Da ora riceverai notifiche quando qualcuno posterà in questa discussione.", "not_following_topic.message": "Non riceverai più notifiche da questa discussione.", "login_to_subscribe": "Si prega di accedere o registrarsi per potersi iscrivere a questa discussione.", "markAsUnreadForAll.success": "Discussione segnata come non letta per tutti.", "watch": "Osserva", + "unwatch": "Unwatch", "watch.title": "Ricevi notifiche di nuove risposte in questa discussione", + "unwatch.title": "Stop watching this topic", "share_this_post": "Condividi questo Post", - "thread_tools.title": "Strumenti per la Discussione", + "thread_tools.title": "Topic Tools", "thread_tools.markAsUnreadForAll": "Segna come non letto", "thread_tools.pin": "Fissa Discussione", "thread_tools.unpin": "Sblocca Discussione", @@ -46,11 +48,11 @@ "thread_tools.move_all": "Sposta Tutto", "thread_tools.fork": "Dividi Discussione", "thread_tools.delete": "Elimina Discussione", - "thread_tools.delete_confirm": "Sei sicuro di voler cancellare questa discussione?", + "thread_tools.delete_confirm": "Are you sure you want to delete this topic?", "thread_tools.restore": "Ripristina Discussione", - "thread_tools.restore_confirm": "Sei sicuro di voler ripristinare questa discussione?", + "thread_tools.restore_confirm": "Are you sure you want to restore this topic?", "thread_tools.purge": "Svuota Discussione", - "thread_tools.purge_confirm": "Sei sicuro di voler svuotare questa discussione?", + "thread_tools.purge_confirm": "Are you sure you want to purge this topic?", "topic_move_success": "Questa discussione è stata correttamente spostata in %1", "post_delete_confirm": "Sei sicuro di voler cancellare questo post?", "post_restore_confirm": "Sei sicuro di voler ripristinare questo post?", diff --git a/public/language/it/user.json b/public/language/it/user.json index 9eb7b2a6d1..f3481db16e 100644 --- a/public/language/it/user.json +++ b/public/language/it/user.json @@ -29,6 +29,7 @@ "edit": "Modifica", "uploaded_picture": "Foto caricata", "upload_new_picture": "Carica una nuova foto", + "upload_new_picture_from_url": "Upload New Picture From URL", "current_password": "Password corrente", "change_password": "Cambia la Password", "change_password_error": "Password non valida!", diff --git a/public/language/ja/error.json b/public/language/ja/error.json index 0d6aad1afd..76f1cadd85 100644 --- a/public/language/ja/error.json +++ b/public/language/ja/error.json @@ -13,6 +13,7 @@ "invalid-user-data": "無効なユーザーデータ", "invalid-password": "無効なパスワード", "invalid-username-or-password": "Please specify both a username and password", + "invalid-search-term": "Invalid search term", "invalid-pagination-value": "無効な改ページ設定値", "username-taken": "ユーザー名が取られた", "email-taken": "メールアドレスが使用された", diff --git a/public/language/ja/notifications.json b/public/language/ja/notifications.json index 9823deff19..d06de648c4 100644 --- a/public/language/ja/notifications.json +++ b/public/language/ja/notifications.json @@ -11,8 +11,8 @@ "you_have_unread_notifications": "未読の通知があります。", "new_message_from": "%1からの新しいメッセージ", "upvoted_your_post_in": "%1 has upvoted your post in %2.", - "moved_your_post": "%1 has moved your post.", - "moved_your_topic": "%1 has moved your topic.", + "moved_your_post": "%1 has moved your post.", + "moved_your_topic": "%1 has moved your topic.", "favourited_your_post_in": "%1 has favourited your post in %2.", "user_flagged_post_in": "%1 flagged a post in %2", "user_posted_to": "%1%2 への返事を作成しました。", diff --git a/public/language/ja/pages.json b/public/language/ja/pages.json index 36295b2fa5..34a0e00267 100644 --- a/public/language/ja/pages.json +++ b/public/language/ja/pages.json @@ -12,5 +12,7 @@ "user.posts": "%1が作成したポスト", "user.topics": "%1が作成したスレッド", "user.favourites": "%1のお気に入りポスト", - "user.settings": "ユーザー設定" + "user.settings": "ユーザー設定", + "maintenance.text": "%1 is currently undergoing maintenance. Please come back another time.", + "maintenance.messageIntro": "Additionally, the administator has left this message:" } \ No newline at end of file diff --git a/public/language/ja/tags.json b/public/language/ja/tags.json index f065d4bbfa..d2e9a213ac 100644 --- a/public/language/ja/tags.json +++ b/public/language/ja/tags.json @@ -2,5 +2,6 @@ "no_tag_topics": "There are no topics with this tag.", "tags": "Tags", "enter_tags_here": "Enter tags here. Press enter after each tag.", + "enter_tags_here_short": "Enter tags...", "no_tags": "There are no tags yet." } \ No newline at end of file diff --git a/public/language/ja/topic.json b/public/language/ja/topic.json index 94366f766e..543ee3b3d6 100644 --- a/public/language/ja/topic.json +++ b/public/language/ja/topic.json @@ -28,15 +28,17 @@ "flag_title": "リポートする", "flag_confirm": "本当にこのポストをリポートするか?", "flag_success": "このポストをリポートしました。", - "deleted_message": "このスレッドは削除されました。管理者しか見れません。", + "deleted_message": "This topic has been deleted. Only users with topic management privileges can see it.", "following_topic.message": "このスレッドが更新された際に通知を受け取ります。", "not_following_topic.message": "このスレッドの更新通知を停止しました。", "login_to_subscribe": "このスレッドを購読するためにログインが必要です。", "markAsUnreadForAll.success": "すべてのスレッドを未読にしました。", "watch": "ウオッチ", + "unwatch": "Unwatch", "watch.title": "新しいポストの通知を受ける", + "unwatch.title": "Stop watching this topic", "share_this_post": "ポストを共有", - "thread_tools.title": "スレッドツール", + "thread_tools.title": "Topic Tools", "thread_tools.markAsUnreadForAll": "未読にする", "thread_tools.pin": "スレッドを最上部に固定", "thread_tools.unpin": "スレッドの固定を解除", @@ -46,11 +48,11 @@ "thread_tools.move_all": "すべてを移動", "thread_tools.fork": "スレッドをフォーク", "thread_tools.delete": "スレッドを削除", - "thread_tools.delete_confirm": "本当にスレッドを削除しますか?", + "thread_tools.delete_confirm": "Are you sure you want to delete this topic?", "thread_tools.restore": "スレッドをリストア", - "thread_tools.restore_confirm": "本当にスレッドをリストアしますか?", + "thread_tools.restore_confirm": "Are you sure you want to restore this topic?", "thread_tools.purge": "Purge Topic", - "thread_tools.purge_confirm": "Are you sure you want to purge this thread?", + "thread_tools.purge_confirm": "Are you sure you want to purge this topic?", "topic_move_success": "このスレッドを %1 に移動しました。", "post_delete_confirm": "本当にポストを削除しますか?", "post_restore_confirm": "本当にポストをリストアしますか?", diff --git a/public/language/ja/user.json b/public/language/ja/user.json index ba3b2c1ed0..29cd6ebada 100644 --- a/public/language/ja/user.json +++ b/public/language/ja/user.json @@ -29,6 +29,7 @@ "edit": "編集", "uploaded_picture": "アップロード済みの画像", "upload_new_picture": "新しい画像をアップロード", + "upload_new_picture_from_url": "Upload New Picture From URL", "current_password": "現在のパスワード", "change_password": "パスワードを変更", "change_password_error": "無効のパスワード!", diff --git a/public/language/ko/error.json b/public/language/ko/error.json index be2363f55a..8aa27aa2a6 100644 --- a/public/language/ko/error.json +++ b/public/language/ko/error.json @@ -13,6 +13,7 @@ "invalid-user-data": "올바르지 않은 사용자 정보입니다.", "invalid-password": "올바르지 않은 비밀번호입니다.", "invalid-username-or-password": "Please specify both a username and password", + "invalid-search-term": "Invalid search term", "invalid-pagination-value": "올바르지 않은 페이지입니다.", "username-taken": "이미 사용 중인 사용자 이름입니다.", "email-taken": "이미 사용 중인 이메일입니다.", diff --git a/public/language/ko/notifications.json b/public/language/ko/notifications.json index 90d004d7e0..82ec4bb972 100644 --- a/public/language/ko/notifications.json +++ b/public/language/ko/notifications.json @@ -11,8 +11,8 @@ "you_have_unread_notifications": "읽지 않은 알림이 있습니다.", "new_message_from": "%1님이 메시지를 보냈습니다.", "upvoted_your_post_in": "%1 has upvoted your post in %2.", - "moved_your_post": "%1 has moved your post.", - "moved_your_topic": "%1 has moved your topic.", + "moved_your_post": "%1 has moved your post.", + "moved_your_topic": "%1 has moved your topic.", "favourited_your_post_in": "%1 has favourited your post in %2.", "user_flagged_post_in": "%1 flagged a post in %2", "user_posted_to": "%1님이 %2님의 게시물에 답글을 작성했습니다.", diff --git a/public/language/ko/pages.json b/public/language/ko/pages.json index e72e0b57c0..cf6d0503f8 100644 --- a/public/language/ko/pages.json +++ b/public/language/ko/pages.json @@ -12,5 +12,7 @@ "user.posts": "%1님이 작성한 게시물", "user.topics": "%1님이 생성한 주제", "user.favourites": "%1님이 좋아하는 게시물", - "user.settings": "설정" + "user.settings": "설정", + "maintenance.text": "%1 is currently undergoing maintenance. Please come back another time.", + "maintenance.messageIntro": "Additionally, the administator has left this message:" } \ No newline at end of file diff --git a/public/language/ko/tags.json b/public/language/ko/tags.json index f065d4bbfa..d2e9a213ac 100644 --- a/public/language/ko/tags.json +++ b/public/language/ko/tags.json @@ -2,5 +2,6 @@ "no_tag_topics": "There are no topics with this tag.", "tags": "Tags", "enter_tags_here": "Enter tags here. Press enter after each tag.", + "enter_tags_here_short": "Enter tags...", "no_tags": "There are no tags yet." } \ No newline at end of file diff --git a/public/language/ko/topic.json b/public/language/ko/topic.json index 7e4d14a405..45ea767a7c 100644 --- a/public/language/ko/topic.json +++ b/public/language/ko/topic.json @@ -28,15 +28,17 @@ "flag_title": "이 게시물을 신고", "flag_confirm": "정말로 신고하시겠습니까?", "flag_success": "이 게시물은 신고되었습니다.", - "deleted_message": "이 주제는 삭제되었습니다. 게시물 관리 권한이 있는 사용자만 접근할 수 있습니다.", + "deleted_message": "This topic has been deleted. Only users with topic management privileges can see it.", "following_topic.message": "이제 이 주제에 새 답글이 달리면 알림을 받습니다.", "not_following_topic.message": "더 이상 이 주제의 새 답글을 알리지 않습니다.", "login_to_subscribe": "이 주제의 알림을 받기 위해서는 로그인해야 합니다.", "markAsUnreadForAll.success": "모든 사용자에 대해 읽지 않음으로 표시했습니다.", "watch": "관심 주제", + "unwatch": "Unwatch", "watch.title": "이 주제의 새 답글 알리기", + "unwatch.title": "Stop watching this topic", "share_this_post": "이 게시물 공유", - "thread_tools.title": "주제 도구", + "thread_tools.title": "Topic Tools", "thread_tools.markAsUnreadForAll": "모두에게 읽지 않음으로 표시", "thread_tools.pin": "상단 고정", "thread_tools.unpin": "상단 고정 해제", @@ -46,11 +48,11 @@ "thread_tools.move_all": "모두 이동", "thread_tools.fork": "복제", "thread_tools.delete": "삭제", - "thread_tools.delete_confirm": "이 주제를 삭제하시겠습니까?", + "thread_tools.delete_confirm": "Are you sure you want to delete this topic?", "thread_tools.restore": "복원", - "thread_tools.restore_confirm": "이 주제를 복원하시겠습니까?", + "thread_tools.restore_confirm": "Are you sure you want to restore this topic?", "thread_tools.purge": "Purge Topic", - "thread_tools.purge_confirm": "Are you sure you want to purge this thread?", + "thread_tools.purge_confirm": "Are you sure you want to purge this topic?", "topic_move_success": "성공적으로 이 주제를 %1로 이동했습니다.", "post_delete_confirm": "이 게시물을 삭제하시겠습니까?", "post_restore_confirm": "이 게시물을 복원하시겠습니까?", diff --git a/public/language/ko/user.json b/public/language/ko/user.json index 37ef4e717d..e228bbd7f7 100644 --- a/public/language/ko/user.json +++ b/public/language/ko/user.json @@ -29,6 +29,7 @@ "edit": "프로필 수정", "uploaded_picture": "사진 업로드", "upload_new_picture": "새 사진 업로드", + "upload_new_picture_from_url": "Upload New Picture From URL", "current_password": "현재 비밀번호", "change_password": "비밀번호 변경", "change_password_error": "유효하지 않은 비밀번호", diff --git a/public/language/lt/error.json b/public/language/lt/error.json index 48edfbb72a..77ad0ede5f 100644 --- a/public/language/lt/error.json +++ b/public/language/lt/error.json @@ -13,6 +13,7 @@ "invalid-user-data": "Klaidingi vartotojo duomenys", "invalid-password": "Klaidingas slaptažodis", "invalid-username-or-password": "Please specify both a username and password", + "invalid-search-term": "Invalid search term", "invalid-pagination-value": "Klaidinga puslapiavimo reikšmė", "username-taken": "Vartotojo vardas jau užimtas", "email-taken": "El. pašto adresas jau užimtas", diff --git a/public/language/lt/notifications.json b/public/language/lt/notifications.json index 864652cb57..8f8a3c6184 100644 --- a/public/language/lt/notifications.json +++ b/public/language/lt/notifications.json @@ -11,8 +11,8 @@ "you_have_unread_notifications": "Jūs turite neperskaitytų pranešimų.", "new_message_from": "Nauja žinutė nuo %1", "upvoted_your_post_in": "%1 has upvoted your post in %2.", - "moved_your_post": "%1 has moved your post.", - "moved_your_topic": "%1 has moved your topic.", + "moved_your_post": "%1 has moved your post.", + "moved_your_topic": "%1 has moved your topic.", "favourited_your_post_in": "%1 has favourited your post in %2.", "user_flagged_post_in": "%1 flagged a post in %2", "user_posted_to": "%1 parašė atsaką %2", diff --git a/public/language/lt/pages.json b/public/language/lt/pages.json index 4e62692bd3..074199f7ae 100644 --- a/public/language/lt/pages.json +++ b/public/language/lt/pages.json @@ -12,5 +12,7 @@ "user.posts": "Pranešimai, kuriuos parašė %1", "user.topics": "Temos, kurias sukūrė %1", "user.favourites": "Vartotojo %1 mėgstami pranešimai", - "user.settings": "Vartotojo nustatymai" + "user.settings": "Vartotojo nustatymai", + "maintenance.text": "%1 is currently undergoing maintenance. Please come back another time.", + "maintenance.messageIntro": "Additionally, the administator has left this message:" } \ No newline at end of file diff --git a/public/language/lt/tags.json b/public/language/lt/tags.json index db32f31aba..93a06c9cbe 100644 --- a/public/language/lt/tags.json +++ b/public/language/lt/tags.json @@ -2,5 +2,6 @@ "no_tag_topics": "Temų su šią žyma nėra.", "tags": "Žymos", "enter_tags_here": "Čia surašykite žymas. Paspauskite enter po kiekvienos žymos.", + "enter_tags_here_short": "Enter tags...", "no_tags": "Žymų kolkas nėra." } \ No newline at end of file diff --git a/public/language/lt/topic.json b/public/language/lt/topic.json index 2d1896a77c..28a5924eda 100644 --- a/public/language/lt/topic.json +++ b/public/language/lt/topic.json @@ -28,15 +28,17 @@ "flag_title": "Pažymėti ši pranešimą moderatoriams", "flag_confirm": "Ar jūs tikras, kad norite pažymėti šį įrašą?", "flag_success": "Šis pranešimas buvo pažymėtas moderatorių patikrinimui.", - "deleted_message": "Ši tema buvo ištrinta. Ją gali matyti tik vartotojai su temų valdymo privilegijomis.", + "deleted_message": "This topic has been deleted. Only users with topic management privileges can see it.", "following_topic.message": "Dabar jūs gausite pranešimus kai kas nors atrašys šioje temoje.", "not_following_topic.message": "Jūs daugiau negausite pranešimų iš šios temos.", "login_to_subscribe": "Norėdami prenumeruoti šią temą, prašome prisiregistruoti arba prisijungti.", "markAsUnreadForAll.success": "Tema visiems vartotojams pažymėta kaip neskaityta.", "watch": "Žiūrėti", + "unwatch": "Unwatch", "watch.title": "Gauti pranešimą apie naujus įrašus šioje temoje", + "unwatch.title": "Stop watching this topic", "share_this_post": "Dalintis šiuo įrašu", - "thread_tools.title": "Temos įrankiai", + "thread_tools.title": "Topic Tools", "thread_tools.markAsUnreadForAll": "Pažymėti kaip neskaitytą", "thread_tools.pin": "Prisegti temą", "thread_tools.unpin": "Atsegti temą", @@ -46,11 +48,11 @@ "thread_tools.move_all": "Perkelti visus", "thread_tools.fork": "Išskaidyti temą", "thread_tools.delete": "Ištrinti temą", - "thread_tools.delete_confirm": "Ar jūs tikrai norite ištrinti šią temą?", + "thread_tools.delete_confirm": "Are you sure you want to delete this topic?", "thread_tools.restore": "Atkurti temą", - "thread_tools.restore_confirm": "Ar jūs tikrai norite atkurti šią temą?", + "thread_tools.restore_confirm": "Are you sure you want to restore this topic?", "thread_tools.purge": "Išvalyti temą", - "thread_tools.purge_confirm": "Ar tikrai norite išvalyti šią temą?", + "thread_tools.purge_confirm": "Are you sure you want to purge this topic?", "topic_move_success": "Ši tema buvo sėkmingai perkelta į %1", "post_delete_confirm": "Ar jūs tikrai norite ištrinti šį įrašą?", "post_restore_confirm": "Ar jūs tikrai norite atkurti šį įrašą?", diff --git a/public/language/lt/user.json b/public/language/lt/user.json index 2ef87f0c0c..868d3db880 100644 --- a/public/language/lt/user.json +++ b/public/language/lt/user.json @@ -29,6 +29,7 @@ "edit": "Redaguoti", "uploaded_picture": "Įkeltas paveikslėlis", "upload_new_picture": "Įkelti naują paveikslėlį", + "upload_new_picture_from_url": "Upload New Picture From URL", "current_password": "Dabartinis slaptažodis", "change_password": "Pakeisti slaptažodį", "change_password_error": "Negalimas slaptažodis!", diff --git a/public/language/ms/error.json b/public/language/ms/error.json index b6ad0bd586..1af71bfec7 100644 --- a/public/language/ms/error.json +++ b/public/language/ms/error.json @@ -13,6 +13,7 @@ "invalid-user-data": "Invalid User Data", "invalid-password": "Password salah!", "invalid-username-or-password": "Please specify both a username and password", + "invalid-search-term": "Invalid search term", "invalid-pagination-value": "Invalid pagination value", "username-taken": "Username taken", "email-taken": "Email taken", diff --git a/public/language/ms/notifications.json b/public/language/ms/notifications.json index 02cc302284..28d165c250 100644 --- a/public/language/ms/notifications.json +++ b/public/language/ms/notifications.json @@ -11,8 +11,8 @@ "you_have_unread_notifications": "Anda ada pemberitahuan yang belum dibaca", "new_message_from": "Pesanan baru daripada %1", "upvoted_your_post_in": "%1 has upvoted your post in %2.", - "moved_your_post": "%1 has moved your post.", - "moved_your_topic": "%1 has moved your topic.", + "moved_your_post": "%1 has moved your post.", + "moved_your_topic": "%1 has moved your topic.", "favourited_your_post_in": "%1 has favourited your post in %2.", "user_flagged_post_in": "%1 flagged a post in %2", "user_posted_to": "%1 telah membalas posting kepada: %2", diff --git a/public/language/ms/pages.json b/public/language/ms/pages.json index 0d1ab9ca76..cf392a6348 100644 --- a/public/language/ms/pages.json +++ b/public/language/ms/pages.json @@ -12,5 +12,7 @@ "user.posts": "Kiriman dibuat oleh %1", "user.topics": "Topik dibuat oleh %1", "user.favourites": "Mesej Kegemaran %1", - "user.settings": "Tetapan pengguna" + "user.settings": "Tetapan pengguna", + "maintenance.text": "%1 is currently undergoing maintenance. Please come back another time.", + "maintenance.messageIntro": "Additionally, the administator has left this message:" } \ No newline at end of file diff --git a/public/language/ms/tags.json b/public/language/ms/tags.json index f065d4bbfa..d2e9a213ac 100644 --- a/public/language/ms/tags.json +++ b/public/language/ms/tags.json @@ -2,5 +2,6 @@ "no_tag_topics": "There are no topics with this tag.", "tags": "Tags", "enter_tags_here": "Enter tags here. Press enter after each tag.", + "enter_tags_here_short": "Enter tags...", "no_tags": "There are no tags yet." } \ No newline at end of file diff --git a/public/language/ms/topic.json b/public/language/ms/topic.json index dde98de2a8..fc3995784d 100644 --- a/public/language/ms/topic.json +++ b/public/language/ms/topic.json @@ -28,15 +28,17 @@ "flag_title": "Tanda posting ini untuk moderasi", "flag_confirm": "Adakah anda pasti untuk menanda kiriman ini", "flag_success": "Kiriman ini telah ditandakan untuk moderasi", - "deleted_message": "Thread ini telah dipadamkan. Hanya pengguna yang menguruskan thread ini boleh mellihatnya", + "deleted_message": "This topic has been deleted. Only users with topic management privileges can see it.", "following_topic.message": "Anda akan menerima makluman apabila ada kiriman kedalam topik ini", "not_following_topic.message": "Anda tidak lagi akan menerima makluman daripada topik ini", "login_to_subscribe": "SIla daftar atau log masuk untuk melanggani topik ini", "markAsUnreadForAll.success": "Topik ditanda sebagai belum dibaca untuk semua", "watch": "Saksikan", + "unwatch": "Unwatch", "watch.title": "Akan dimaklumkan sekiranya ada balasan dalam topik ini", + "unwatch.title": "Stop watching this topic", "share_this_post": "Kongsi kiriman ini", - "thread_tools.title": "Perkakas untuk thread", + "thread_tools.title": "Topic Tools", "thread_tools.markAsUnreadForAll": "Tanda sebagai belum dibaca", "thread_tools.pin": "Pinkan topik", "thread_tools.unpin": "Unpin topik", @@ -46,11 +48,11 @@ "thread_tools.move_all": "Move All", "thread_tools.fork": "Fork topik", "thread_tools.delete": "Padamkan topik", - "thread_tools.delete_confirm": "Adakah anda pasti untuk memadamkan thread ini?", + "thread_tools.delete_confirm": "Are you sure you want to delete this topic?", "thread_tools.restore": "Pulihkan topik", - "thread_tools.restore_confirm": "Adakah anda pasti untuk memulihkan threa ini?", + "thread_tools.restore_confirm": "Are you sure you want to restore this topic?", "thread_tools.purge": "Purge Topic", - "thread_tools.purge_confirm": "Are you sure you want to purge this thread?", + "thread_tools.purge_confirm": "Are you sure you want to purge this topic?", "topic_move_success": "Topik telah dipindahkan ke %1", "post_delete_confirm": "Adakah anda pasti untuk memadam kiriman ini?", "post_restore_confirm": "Adakah anda pasti untuk memulihkan kiriman ini?", diff --git a/public/language/ms/user.json b/public/language/ms/user.json index 366c6ae8fb..62781c0d88 100644 --- a/public/language/ms/user.json +++ b/public/language/ms/user.json @@ -29,6 +29,7 @@ "edit": "Edit", "uploaded_picture": "Muatnaik gambak", "upload_new_picture": "Muatnaik gambar baru", + "upload_new_picture_from_url": "Upload New Picture From URL", "current_password": "Password sekarang", "change_password": "TUkar password", "change_password_error": "Password salah!", diff --git a/public/language/nb/error.json b/public/language/nb/error.json index f90d459cce..a46573d5b9 100644 --- a/public/language/nb/error.json +++ b/public/language/nb/error.json @@ -13,6 +13,7 @@ "invalid-user-data": "Invalid User Data", "invalid-password": "Invalid Password", "invalid-username-or-password": "Please specify both a username and password", + "invalid-search-term": "Invalid search term", "invalid-pagination-value": "Invalid pagination value", "username-taken": "Username taken", "email-taken": "Email taken", diff --git a/public/language/nb/notifications.json b/public/language/nb/notifications.json index 2ffabea7c1..54308671e1 100644 --- a/public/language/nb/notifications.json +++ b/public/language/nb/notifications.json @@ -11,8 +11,8 @@ "you_have_unread_notifications": "Du har uleste varsler.", "new_message_from": "Ny melding fra %1", "upvoted_your_post_in": "%1 has upvoted your post in %2.", - "moved_your_post": "%1 has moved your post.", - "moved_your_topic": "%1 has moved your topic.", + "moved_your_post": "%1 has moved your post.", + "moved_your_topic": "%1 has moved your topic.", "favourited_your_post_in": "%1 has favourited your post in %2.", "user_flagged_post_in": "%1 flagged a post in %2", "user_posted_to": "%1 har skrevet et svar til: %2", diff --git a/public/language/nb/pages.json b/public/language/nb/pages.json index 862d9fffdb..3c1986ebe5 100644 --- a/public/language/nb/pages.json +++ b/public/language/nb/pages.json @@ -12,5 +12,7 @@ "user.posts": "Innlegg laget av %1", "user.topics": "Emner opprettet av %1", "user.favourites": "%1 sine favoritt-innlegg", - "user.settings": "Brukerinnstillinger" + "user.settings": "Brukerinnstillinger", + "maintenance.text": "%1 is currently undergoing maintenance. Please come back another time.", + "maintenance.messageIntro": "Additionally, the administator has left this message:" } \ No newline at end of file diff --git a/public/language/nb/tags.json b/public/language/nb/tags.json index f065d4bbfa..d2e9a213ac 100644 --- a/public/language/nb/tags.json +++ b/public/language/nb/tags.json @@ -2,5 +2,6 @@ "no_tag_topics": "There are no topics with this tag.", "tags": "Tags", "enter_tags_here": "Enter tags here. Press enter after each tag.", + "enter_tags_here_short": "Enter tags...", "no_tags": "There are no tags yet." } \ No newline at end of file diff --git a/public/language/nb/topic.json b/public/language/nb/topic.json index 0462cf426f..fb602168dc 100644 --- a/public/language/nb/topic.json +++ b/public/language/nb/topic.json @@ -28,15 +28,17 @@ "flag_title": "Rapporter dette innlegget for granskning", "flag_confirm": "Er du sikker på at du vil rapportere dette innlegget?", "flag_success": "Dette innlegget har blitt rapportert.", - "deleted_message": "Denne tråden har blitt slettet. Bare brukere med trådhåndterings-privilegier kan se den.", + "deleted_message": "This topic has been deleted. Only users with topic management privileges can see it.", "following_topic.message": "Du vil nå motta varsler når noen skriver i denne tråden.", "not_following_topic.message": "Du vil ikke lenger motta varsler fra denne tråden.", "login_to_subscribe": "Vennligst registrer deg eller logg inn for å abonnere på denne tråden.", "markAsUnreadForAll.success": "Tråd markert som ulest for alle.", "watch": "Overvåk", + "unwatch": "Unwatch", "watch.title": "Bli varslet om nye svar i dette emnet", + "unwatch.title": "Stop watching this topic", "share_this_post": "Del ditt innlegg", - "thread_tools.title": "Trådverktøy", + "thread_tools.title": "Topic Tools", "thread_tools.markAsUnreadForAll": "Marker som ulest", "thread_tools.pin": "Fest tråd", "thread_tools.unpin": "Ufest tråd", @@ -46,11 +48,11 @@ "thread_tools.move_all": "Flytt alle", "thread_tools.fork": "Forgren tråd", "thread_tools.delete": "Slett tråd", - "thread_tools.delete_confirm": "Er du sikker på at du vil slette denne tråden?", + "thread_tools.delete_confirm": "Are you sure you want to delete this topic?", "thread_tools.restore": "Gjenopprett tråd", - "thread_tools.restore_confirm": "Er du sikker på at du vil gjenopprette denne tråden?", + "thread_tools.restore_confirm": "Are you sure you want to restore this topic?", "thread_tools.purge": "Tøm emne", - "thread_tools.purge_confirm": "Er du sikker på at du vil tømme dette emnet?", + "thread_tools.purge_confirm": "Are you sure you want to purge this topic?", "topic_move_success": "Emnet har blitt flyttet til %1", "post_delete_confirm": "Er du sikker på at du vil slette dette innlegget?", "post_restore_confirm": "Er du sikker på at du vil gjenopprette dette innlegget?", diff --git a/public/language/nb/user.json b/public/language/nb/user.json index c327dd1d87..b794482f5c 100644 --- a/public/language/nb/user.json +++ b/public/language/nb/user.json @@ -29,6 +29,7 @@ "edit": "Endre", "uploaded_picture": "Opplastet bilde", "upload_new_picture": "Last opp nytt bidle", + "upload_new_picture_from_url": "Upload New Picture From URL", "current_password": "Gjeldende passord", "change_password": "Endre passord", "change_password_error": "Ugyldig passord!", diff --git a/public/language/nl/error.json b/public/language/nl/error.json index d27566d742..18602121cd 100644 --- a/public/language/nl/error.json +++ b/public/language/nl/error.json @@ -13,6 +13,7 @@ "invalid-user-data": "Ongeldig Gebruikersdata", "invalid-password": "Ongeldig wachtwoord", "invalid-username-or-password": "Geef alsjeblieft een gebruikersnaam en wachtwoord op", + "invalid-search-term": "Invalid search term", "invalid-pagination-value": "Ongeldig pagineringswaarde", "username-taken": "Gebruikersnaam is al bezet", "email-taken": "Email adres is al gebruikt", diff --git a/public/language/nl/notifications.json b/public/language/nl/notifications.json index 4c51c977b5..c071096b88 100644 --- a/public/language/nl/notifications.json +++ b/public/language/nl/notifications.json @@ -11,8 +11,8 @@ "you_have_unread_notifications": "U heeft ongelezen notificaties", "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.", + "moved_your_post": "%1 has moved your post.", + "moved_your_topic": "%1 has moved your topic.", "favourited_your_post_in": "%1 heeft uw bericht gefavoriet in %2.", "user_flagged_post_in": "%1 rapporteerde een bericht in %2", "user_posted_to": "%1 heeft een reactie op het bericht gegeven aan %2", diff --git a/public/language/nl/pages.json b/public/language/nl/pages.json index 3a82ff7af7..2aab50be65 100644 --- a/public/language/nl/pages.json +++ b/public/language/nl/pages.json @@ -12,5 +12,7 @@ "user.posts": "Berichten geplaatst door %1", "user.topics": "Topics gecreëerd door %1", "user.favourites": "%1's Favoriete Berichten", - "user.settings": "Gebruikersinstellingen" + "user.settings": "Gebruikersinstellingen", + "maintenance.text": "%1 is currently undergoing maintenance. Please come back another time.", + "maintenance.messageIntro": "Additionally, the administator has left this message:" } \ No newline at end of file diff --git a/public/language/nl/tags.json b/public/language/nl/tags.json index b657763651..881149e5e1 100644 --- a/public/language/nl/tags.json +++ b/public/language/nl/tags.json @@ -2,5 +2,6 @@ "no_tag_topics": "Er zijn geen onderwerpen met deze tag", "tags": "Tags", "enter_tags_here": "Voer hier uw tags in. Druk op enter na elke tag ingevoerd te hebben", + "enter_tags_here_short": "Enter tags...", "no_tags": "Er zijn nog geen tags te vinden" } \ No newline at end of file diff --git a/public/language/nl/topic.json b/public/language/nl/topic.json index 0530a16f5c..f449e551f1 100644 --- a/public/language/nl/topic.json +++ b/public/language/nl/topic.json @@ -28,15 +28,17 @@ "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.", + "deleted_message": "This topic has been deleted. Only users with topic management privileges can see it.", "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.", "watch": "Volgen", + "unwatch": "Unwatch", "watch.title": "Krijg notificaties van nieuwe reacties op dit onderwerp", + "unwatch.title": "Stop watching this topic", "share_this_post": "Deel dit Bericht", - "thread_tools.title": "Acties", + "thread_tools.title": "Topic Tools", "thread_tools.markAsUnreadForAll": "Ongelezen Markeren", "thread_tools.pin": "Onderwerp Vastmaken", "thread_tools.unpin": "Onderwerp Losmaken", @@ -46,11 +48,11 @@ "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.delete_confirm": "Are you sure you want to delete this topic?", "thread_tools.restore": "Onderwerp Herstellen", - "thread_tools.restore_confirm": "Weet u het zeker dat u het onderwerp wilt herstellen?", + "thread_tools.restore_confirm": "Are you sure you want to restore this topic?", "thread_tools.purge": "Wis Onderwerp ", - "thread_tools.purge_confirm": "Weet u het zeker dat u dit onderwerp wilt wissen?", + "thread_tools.purge_confirm": "Are you sure you want to purge this topic?", "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?", diff --git a/public/language/nl/user.json b/public/language/nl/user.json index e91b069cae..c6803a4031 100644 --- a/public/language/nl/user.json +++ b/public/language/nl/user.json @@ -29,6 +29,7 @@ "edit": "Aanpassen", "uploaded_picture": "Afbeelding Uploaden", "upload_new_picture": "Nieuwe Afbeelding Uploaden", + "upload_new_picture_from_url": "Upload New Picture From URL", "current_password": "Huidige Wachtwoord", "change_password": "Wachtwoord Aanpassen", "change_password_error": "Ongeldig wachtwoord!", diff --git a/public/language/pl/error.json b/public/language/pl/error.json index 19b93b1951..9b64bc0161 100644 --- a/public/language/pl/error.json +++ b/public/language/pl/error.json @@ -13,6 +13,7 @@ "invalid-user-data": "Błędne dane użytkownika.", "invalid-password": "Błędne hasło", "invalid-username-or-password": "Proszę podać nazwę użytkownika i hasło", + "invalid-search-term": "Invalid search term", "invalid-pagination-value": "Błędna wartość paginacji", "username-taken": "Login zajęty.", "email-taken": "E-mail zajęty.", diff --git a/public/language/pl/notifications.json b/public/language/pl/notifications.json index ff00d9b7ad..cdecbf1bc6 100644 --- a/public/language/pl/notifications.json +++ b/public/language/pl/notifications.json @@ -11,8 +11,8 @@ "you_have_unread_notifications": "Masz nieprzeczytane powiadomienia.", "new_message_from": "Nowa wiadomość od %1", "upvoted_your_post_in": "%1 zagłosował na Twój post w %2", - "moved_your_post": "%1 przesunął Twój post.", - "moved_your_topic": "%1 przesunął Twój temat.", + "moved_your_post": "%1 has moved your post.", + "moved_your_topic": "%1 has moved your topic.", "favourited_your_post_in": "%1 polubił Twój post w %2.", "user_flagged_post_in": "%1 oflagował Twój post w %2", "user_posted_to": "%1 dodał odpowiedź do %2", diff --git a/public/language/pl/pages.json b/public/language/pl/pages.json index 02e459b55a..6c36e275b5 100644 --- a/public/language/pl/pages.json +++ b/public/language/pl/pages.json @@ -12,5 +12,7 @@ "user.posts": "Posty napisane przez %1", "user.topics": "Wątki stworzone przez %1", "user.favourites": "Ulubione posty %1", - "user.settings": "Ustawienia użytkownika" + "user.settings": "Ustawienia użytkownika", + "maintenance.text": "%1 is currently undergoing maintenance. Please come back another time.", + "maintenance.messageIntro": "Additionally, the administator has left this message:" } \ No newline at end of file diff --git a/public/language/pl/tags.json b/public/language/pl/tags.json index d4b35e3b39..886b5d180b 100644 --- a/public/language/pl/tags.json +++ b/public/language/pl/tags.json @@ -2,5 +2,6 @@ "no_tag_topics": "Nie ma tematów z tym tagiem", "tags": "Tagi", "enter_tags_here": "Tutaj wpisz tagi. Naciśnij enter po każdym.", + "enter_tags_here_short": "Enter tags...", "no_tags": "Jeszcze nie ma tagów." } \ No newline at end of file diff --git a/public/language/pl/topic.json b/public/language/pl/topic.json index 42a2a81aee..1a7294be92 100644 --- a/public/language/pl/topic.json +++ b/public/language/pl/topic.json @@ -28,15 +28,17 @@ "flag_title": "Zgłoś post do moderacji", "flag_confirm": "Na pewno chcesz oznaczyć ten post?", "flag_success": "Ten post został oznaczony do moderacji.", - "deleted_message": "Ten wątek został usunięty. Tylko użytkownicy z uprawnieniami do zarządzania wątkami mogą go widzieć.", + "deleted_message": "This topic has been deleted. Only users with topic management privileges can see it.", "following_topic.message": "Będziesz otrzymywał powiadomienia, gdy ktoś odpowie w tym wątku.", "not_following_topic.message": "Nie będziesz otrzymywał więcej powiadomień z tego wątku.", "login_to_subscribe": "Zaloguj się, aby subskrybować ten wątek.", "markAsUnreadForAll.success": "Wątek oznaczony jako nieprzeczytany dla wszystkich.", "watch": "Obserwuj", + "unwatch": "Unwatch", "watch.title": "Otrzymuj powiadomienia o nowych odpowiedziach w tym wątku", + "unwatch.title": "Stop watching this topic", "share_this_post": "Udostępnij", - "thread_tools.title": "Narzędzia wątków", + "thread_tools.title": "Topic Tools", "thread_tools.markAsUnreadForAll": "Oznacz jako nieprzeczytany", "thread_tools.pin": "Przypnij wątek", "thread_tools.unpin": "Odepnij wątek", @@ -46,11 +48,11 @@ "thread_tools.move_all": "Przenieś wszystko", "thread_tools.fork": "Podziel wątek", "thread_tools.delete": "Usuń wątek", - "thread_tools.delete_confirm": "Na pewno chcesz usunąć ten wątek?", + "thread_tools.delete_confirm": "Are you sure you want to delete this topic?", "thread_tools.restore": "Przywróć wątek", - "thread_tools.restore_confirm": "Na pewno chcesz przywrócić ten wątek?", + "thread_tools.restore_confirm": "Are you sure you want to restore this topic?", "thread_tools.purge": "Wyczyść wątek", - "thread_tools.purge_confirm": "Jesteś pewien, że chcesz wyczyścić ten wątek?", + "thread_tools.purge_confirm": "Are you sure you want to purge this topic?", "topic_move_success": "Temat przeniesiono do %1", "post_delete_confirm": "Na pewno chcesz usunąć ten post?", "post_restore_confirm": "Na pewno chcesz przywrócić ten post?", diff --git a/public/language/pl/user.json b/public/language/pl/user.json index 957954f1a2..c40d4da460 100644 --- a/public/language/pl/user.json +++ b/public/language/pl/user.json @@ -29,6 +29,7 @@ "edit": "Edytuj", "uploaded_picture": "Przesłane zdjęcie", "upload_new_picture": "Prześlij nowe zdjęcie", + "upload_new_picture_from_url": "Upload New Picture From URL", "current_password": "Obecne hasło", "change_password": "Zmień hasło", "change_password_error": "Błędne hasło!", diff --git a/public/language/pt_BR/error.json b/public/language/pt_BR/error.json index d6e0da1ede..3d85a8a275 100644 --- a/public/language/pt_BR/error.json +++ b/public/language/pt_BR/error.json @@ -13,6 +13,7 @@ "invalid-user-data": "Informação de usuário inválida", "invalid-password": "Senha inválida", "invalid-username-or-password": "Please specify both a username and password", + "invalid-search-term": "Invalid search term", "invalid-pagination-value": "Informação inválida de paginação", "username-taken": "Usuário já existe", "email-taken": "Email já foi utilizado em um cadastro", diff --git a/public/language/pt_BR/notifications.json b/public/language/pt_BR/notifications.json index fc82e93866..276cc97a3f 100644 --- a/public/language/pt_BR/notifications.json +++ b/public/language/pt_BR/notifications.json @@ -11,8 +11,8 @@ "you_have_unread_notifications": "Você possui notificações não lidas.", "new_message_from": "Nova mensagem de %1", "upvoted_your_post_in": "%1 has upvoted your post in %2.", - "moved_your_post": "%1 has moved your post.", - "moved_your_topic": "%1 has moved your topic.", + "moved_your_post": "%1 has moved your post.", + "moved_your_topic": "%1 has moved your topic.", "favourited_your_post_in": "%1 has favourited your post in %2.", "user_flagged_post_in": "%1 flagged a post in %2", "user_posted_to": "%1 respondeu para: %2", diff --git a/public/language/pt_BR/pages.json b/public/language/pt_BR/pages.json index 48c03f52d9..12cba35b85 100644 --- a/public/language/pt_BR/pages.json +++ b/public/language/pt_BR/pages.json @@ -12,5 +12,7 @@ "user.posts": "Posts feito por %1", "user.topics": "Tópicos criados por %1", "user.favourites": "Favoritos de %1", - "user.settings": "Configurações de Usuário" + "user.settings": "Configurações de Usuário", + "maintenance.text": "%1 is currently undergoing maintenance. Please come back another time.", + "maintenance.messageIntro": "Additionally, the administator has left this message:" } \ No newline at end of file diff --git a/public/language/pt_BR/tags.json b/public/language/pt_BR/tags.json index f4d82896c7..8b9861a507 100644 --- a/public/language/pt_BR/tags.json +++ b/public/language/pt_BR/tags.json @@ -2,5 +2,6 @@ "no_tag_topics": "Não há tópicos com esta tag.", "tags": "Tags", "enter_tags_here": "Digite as tags aqui. Pressione enter após cada tag.", + "enter_tags_here_short": "Enter tags...", "no_tags": "Ainda não há tags." } \ No newline at end of file diff --git a/public/language/pt_BR/topic.json b/public/language/pt_BR/topic.json index a0f0caea3d..b328c25385 100644 --- a/public/language/pt_BR/topic.json +++ b/public/language/pt_BR/topic.json @@ -28,15 +28,17 @@ "flag_title": "Sinalizar este post para moderação", "flag_confirm": "Tem certeza que deseja sinalizar este post?", "flag_success": "Este post foi sinalizado para ser moderado.", - "deleted_message": "Este tópico foi deletado. Somente usuários com privilégios administrativos podem visualizá-lo.", + "deleted_message": "This topic has been deleted. Only users with topic management privileges can see it.", "following_topic.message": "Você receberá notificações quando alguém responder este tópico.", "not_following_topic.message": "Você não receberá mais notificações sobre este tópico.", "login_to_subscribe": "Por favor registre ou logue para assinar este tópico.", "markAsUnreadForAll.success": "Marcar Tópico como não lido para todos.", "watch": "Acompanhar", + "unwatch": "Unwatch", "watch.title": "Seja notificado sobre novas respostas neste tópico", + "unwatch.title": "Stop watching this topic", "share_this_post": "Compartilhar este Post", - "thread_tools.title": "Administrar tópicos", + "thread_tools.title": "Topic Tools", "thread_tools.markAsUnreadForAll": "Marcar como não lido", "thread_tools.pin": "Fixar Tópico", "thread_tools.unpin": "Remover Fixação do Tópico", @@ -46,11 +48,11 @@ "thread_tools.move_all": "Mover todos", "thread_tools.fork": "Clonar Tópico", "thread_tools.delete": "Deletar Tópico", - "thread_tools.delete_confirm": "Tem certeza que deseja deletar este tópico?", + "thread_tools.delete_confirm": "Are you sure you want to delete this topic?", "thread_tools.restore": "Restaurar Tópico", - "thread_tools.restore_confirm": "Tem certeza que deseja restaurar este tópico?", + "thread_tools.restore_confirm": "Are you sure you want to restore this topic?", "thread_tools.purge": "Limpar Tópico", - "thread_tools.purge_confirm": "Deseja mesmo limpar esse tópico?", + "thread_tools.purge_confirm": "Are you sure you want to purge this topic?", "topic_move_success": "Tópico movido com sucesso para %1", "post_delete_confirm": "Tem certeza que deseja deletar este post?", "post_restore_confirm": "Tem certeza que deseja restaurar este post?", diff --git a/public/language/pt_BR/user.json b/public/language/pt_BR/user.json index 60b2827e2a..7b46939cd1 100644 --- a/public/language/pt_BR/user.json +++ b/public/language/pt_BR/user.json @@ -29,6 +29,7 @@ "edit": "Editar", "uploaded_picture": "Foto Carregada", "upload_new_picture": "Carregar nova Foto", + "upload_new_picture_from_url": "Upload New Picture From URL", "current_password": "Senha Atual", "change_password": "Alterar Senha", "change_password_error": "Senha Inválida!", diff --git a/public/language/ro/error.json b/public/language/ro/error.json index 979fd4c5a5..20dd86fcfa 100644 --- a/public/language/ro/error.json +++ b/public/language/ro/error.json @@ -13,6 +13,7 @@ "invalid-user-data": "Date utilizator invalide", "invalid-password": "Parolă Invalidă", "invalid-username-or-password": "Please specify both a username and password", + "invalid-search-term": "Invalid search term", "invalid-pagination-value": "Date paginație invalide", "username-taken": "Numele de utilizator este deja folosit", "email-taken": "Adresa de email este deja folostă", diff --git a/public/language/ro/notifications.json b/public/language/ro/notifications.json index 59fdb82116..9a73f389a4 100644 --- a/public/language/ro/notifications.json +++ b/public/language/ro/notifications.json @@ -11,8 +11,8 @@ "you_have_unread_notifications": "Ai notificări necitite.", "new_message_from": "Un mesaj nou de la %1", "upvoted_your_post_in": "%1 has upvoted your post in %2.", - "moved_your_post": "%1 has moved your post.", - "moved_your_topic": "%1 has moved your topic.", + "moved_your_post": "%1 has moved your post.", + "moved_your_topic": "%1 has moved your topic.", "favourited_your_post_in": "%1 has favourited your post in %2.", "user_flagged_post_in": "%1 flagged a post in %2", "user_posted_to": "%1 a postat un răspuns la: %2", diff --git a/public/language/ro/pages.json b/public/language/ro/pages.json index 3b69d67327..ec2748f07a 100644 --- a/public/language/ro/pages.json +++ b/public/language/ro/pages.json @@ -12,5 +12,7 @@ "user.posts": "Mesaje postate de %1", "user.topics": "Subiecte create de %1", "user.favourites": "Mesajele favorite ale lui %1", - "user.settings": "Setări Utilizator" + "user.settings": "Setări Utilizator", + "maintenance.text": "%1 is currently undergoing maintenance. Please come back another time.", + "maintenance.messageIntro": "Additionally, the administator has left this message:" } \ No newline at end of file diff --git a/public/language/ro/tags.json b/public/language/ro/tags.json index cfd27f9ec3..185e6bf299 100644 --- a/public/language/ro/tags.json +++ b/public/language/ro/tags.json @@ -2,5 +2,6 @@ "no_tag_topics": "Nu există nici un subiect cu acest tag.", "tags": "Taguri", "enter_tags_here": "Introdu tagurile aici. Apasă enter după fiecare tag.", + "enter_tags_here_short": "Enter tags...", "no_tags": "În acest moment nu există nici un tag." } \ No newline at end of file diff --git a/public/language/ro/topic.json b/public/language/ro/topic.json index 3bcdf5805e..ef7eac7b0f 100644 --- a/public/language/ro/topic.json +++ b/public/language/ro/topic.json @@ -28,15 +28,17 @@ "flag_title": "Semnalizează acest mesaj pentru moderare", "flag_confirm": "Ești sigur că vrei să semnalezi acest mesaj?", "flag_success": "Acest mesaj a fost semnalizat pentru moderare.", - "deleted_message": "Acest subiect a fost șters. Doar utilizatorii cu drepturi de thread management îl pot vedea.", + "deleted_message": "This topic has been deleted. Only users with topic management privileges can see it.", "following_topic.message": "Vei primi notificări când cineva va posta un nou mesaj in acest subiect.", "not_following_topic.message": "Nu vei mai primi notificări legate de acest subiect.", "login_to_subscribe": "Te rugăm să te înregistrezi sau să te autentifici ca să te poți abona la acest subiect.", "markAsUnreadForAll.success": "Subiect marcat ca citit pentru toți.", "watch": "Vezi", + "unwatch": "Unwatch", "watch.title": "Abonează-te la notificări legate de acest subiect", + "unwatch.title": "Stop watching this topic", "share_this_post": "Distribuie acest mesaj", - "thread_tools.title": "Unelte Subiect", + "thread_tools.title": "Topic Tools", "thread_tools.markAsUnreadForAll": "Marchează Necitit", "thread_tools.pin": "Pin Subiect", "thread_tools.unpin": "Unpin Subiect", @@ -46,11 +48,11 @@ "thread_tools.move_all": "Mută-le pe toate", "thread_tools.fork": "Bifurcă Subiect", "thread_tools.delete": "Șterge Subiect", - "thread_tools.delete_confirm": "Ești sigur că vrei să ștergi acest subiect?", + "thread_tools.delete_confirm": "Are you sure you want to delete this topic?", "thread_tools.restore": "Restaurează Subiect", - "thread_tools.restore_confirm": "Ești sigur că vrei să restaurezi acest subiect?", + "thread_tools.restore_confirm": "Are you sure you want to restore this topic?", "thread_tools.purge": "Curăță Subiect", - "thread_tools.purge_confirm": "Ești sigur că vrei sa cureți acest subiect?", + "thread_tools.purge_confirm": "Are you sure you want to purge this topic?", "topic_move_success": "Acest mesaj a fost mutat cu succes în %1", "post_delete_confirm": "Ești sigur că vrei să ștergi acest mesaj?", "post_restore_confirm": "Esti sigur că vrei să restaurezi acest mesaj?", diff --git a/public/language/ro/user.json b/public/language/ro/user.json index 2bb96c0c9b..8b7c9b2bec 100644 --- a/public/language/ro/user.json +++ b/public/language/ro/user.json @@ -29,6 +29,7 @@ "edit": "Editează", "uploaded_picture": "Poză uploadată", "upload_new_picture": "Uploadează poză nouă", + "upload_new_picture_from_url": "Upload New Picture From URL", "current_password": "Parola curentă", "change_password": "Schimbă Parola", "change_password_error": "Parola invalidă!", diff --git a/public/language/ru/error.json b/public/language/ru/error.json index a715f9c9a2..4222468931 100644 --- a/public/language/ru/error.json +++ b/public/language/ru/error.json @@ -13,6 +13,7 @@ "invalid-user-data": "Неверные Пользовательские Данные", "invalid-password": "Неверный Пароль", "invalid-username-or-password": "Please specify both a username and password", + "invalid-search-term": "Invalid search term", "invalid-pagination-value": "Неверное значение пагинации", "username-taken": "Имя пользователя занято", "email-taken": "Email занят", diff --git a/public/language/ru/notifications.json b/public/language/ru/notifications.json index 0bd4597dd7..0cf9fae9db 100644 --- a/public/language/ru/notifications.json +++ b/public/language/ru/notifications.json @@ -11,8 +11,8 @@ "you_have_unread_notifications": "У вас есть непрочитанные уведомления", "new_message_from": "Новое сообщение от %1", "upvoted_your_post_in": "%1 has upvoted your post in %2.", - "moved_your_post": "%1 has moved your post.", - "moved_your_topic": "%1 has moved your topic.", + "moved_your_post": "%1 has moved your post.", + "moved_your_topic": "%1 has moved your topic.", "favourited_your_post_in": "%1 has favourited your post in %2.", "user_flagged_post_in": "%1 flagged a post in %2", "user_posted_to": "%1 ответил на запись: %2", diff --git a/public/language/ru/pages.json b/public/language/ru/pages.json index adf2827471..f2353953cd 100644 --- a/public/language/ru/pages.json +++ b/public/language/ru/pages.json @@ -12,5 +12,7 @@ "user.posts": "Пост написан %1", "user.topics": "Темы созданы %1", "user.favourites": "Избранные сообщения %1", - "user.settings": "Настройки" + "user.settings": "Настройки", + "maintenance.text": "%1 is currently undergoing maintenance. Please come back another time.", + "maintenance.messageIntro": "Additionally, the administator has left this message:" } \ No newline at end of file diff --git a/public/language/ru/tags.json b/public/language/ru/tags.json index 91aa21d2d6..346581ea36 100644 --- a/public/language/ru/tags.json +++ b/public/language/ru/tags.json @@ -2,5 +2,6 @@ "no_tag_topics": "Нет топиков с таким тэгом.", "tags": "Тэги", "enter_tags_here": "Укажите тэги здесь. Нажимайте Enter после каждого тэга.", + "enter_tags_here_short": "Enter tags...", "no_tags": "Здесь еще нет тэгов." } \ No newline at end of file diff --git a/public/language/ru/topic.json b/public/language/ru/topic.json index 5a97aa09ea..29ee573a71 100644 --- a/public/language/ru/topic.json +++ b/public/language/ru/topic.json @@ -28,15 +28,17 @@ "flag_title": "Отметить сообщение для модерирования", "flag_confirm": "Вы уверены, что хотите отметить этот пост?", "flag_success": "Этот пост был помечен для модерации", - "deleted_message": "Эта ветка была удалена. Только пользователи с правами управления ветвями могут ее видеть.", + "deleted_message": "This topic has been deleted. Only users with topic management privileges can see it.", "following_topic.message": "Теперь вы будете получать уведомления при обновлении этой темы.", "not_following_topic.message": "Вы больше не будете получать уведомления из этой темы.", "login_to_subscribe": "Пожалуйста зарегистрируйтесь, или войдите под своим аккаунтом, чтобы подписаться на эту тему.", "markAsUnreadForAll.success": "Тема помечена как непрочитанная для всех.", "watch": "Следить", + "unwatch": "Unwatch", "watch.title": "Сообщать мне об ответах в этой теме", + "unwatch.title": "Stop watching this topic", "share_this_post": "Поделиться этим Постом", - "thread_tools.title": "Опции Темы", + "thread_tools.title": "Topic Tools", "thread_tools.markAsUnreadForAll": "Отметить как непрочитанные", "thread_tools.pin": "Прикрепить Тему", "thread_tools.unpin": "Открепить Тему", @@ -46,11 +48,11 @@ "thread_tools.move_all": "Переместить Все", "thread_tools.fork": "Ответвить Тему", "thread_tools.delete": "Удалить Тему", - "thread_tools.delete_confirm": "Вы уверены, что хотите удалить эту ветку?", + "thread_tools.delete_confirm": "Are you sure you want to delete this topic?", "thread_tools.restore": "Восстановить Тему", - "thread_tools.restore_confirm": "Вы уверены, что хотите восстановить эту ветку?", + "thread_tools.restore_confirm": "Are you sure you want to restore this topic?", "thread_tools.purge": "Очистить Тему", - "thread_tools.purge_confirm": "Вы уверены, что хотите очистить эту ветку?", + "thread_tools.purge_confirm": "Are you sure you want to purge this topic?", "topic_move_success": "Эта тема успешно перемещена в %1", "post_delete_confirm": "Вы уверены, что хотите удалить этот пост?", "post_restore_confirm": "Вы уверены, что хотите восстановить этот пост?", diff --git a/public/language/ru/user.json b/public/language/ru/user.json index e09a5fd425..fa0e78e253 100644 --- a/public/language/ru/user.json +++ b/public/language/ru/user.json @@ -29,6 +29,7 @@ "edit": "Редактировать", "uploaded_picture": "Загруженные Фотографии", "upload_new_picture": "Загрузить новую фотографию", + "upload_new_picture_from_url": "Upload New Picture From URL", "current_password": "Текущий Пароль", "change_password": "Изменить Пароль", "change_password_error": "Неверный Пароль!", diff --git a/public/language/sc/error.json b/public/language/sc/error.json index f90d459cce..a46573d5b9 100644 --- a/public/language/sc/error.json +++ b/public/language/sc/error.json @@ -13,6 +13,7 @@ "invalid-user-data": "Invalid User Data", "invalid-password": "Invalid Password", "invalid-username-or-password": "Please specify both a username and password", + "invalid-search-term": "Invalid search term", "invalid-pagination-value": "Invalid pagination value", "username-taken": "Username taken", "email-taken": "Email taken", diff --git a/public/language/sc/notifications.json b/public/language/sc/notifications.json index 0fecd4d1a7..4bc769a7c8 100644 --- a/public/language/sc/notifications.json +++ b/public/language/sc/notifications.json @@ -11,8 +11,8 @@ "you_have_unread_notifications": "You have unread notifications.", "new_message_from": "New message from %1", "upvoted_your_post_in": "%1 has upvoted your post in %2.", - "moved_your_post": "%1 has moved your post.", - "moved_your_topic": "%1 has moved your topic.", + "moved_your_post": "%1 has moved your post.", + "moved_your_topic": "%1 has moved your topic.", "favourited_your_post_in": "%1 has favourited your post in %2.", "user_flagged_post_in": "%1 flagged a post in %2", "user_posted_to": "%1 has posted a reply to: %2", diff --git a/public/language/sc/pages.json b/public/language/sc/pages.json index 2f975b787d..4ad8a7c731 100644 --- a/public/language/sc/pages.json +++ b/public/language/sc/pages.json @@ -12,5 +12,7 @@ "user.posts": "Arresonos fatos dae %1", "user.topics": "Topics created by %1", "user.favourites": "Arresonos Preferidos de %1", - "user.settings": "Sèberos de Impitadore" + "user.settings": "Sèberos de Impitadore", + "maintenance.text": "%1 is currently undergoing maintenance. Please come back another time.", + "maintenance.messageIntro": "Additionally, the administator has left this message:" } \ No newline at end of file diff --git a/public/language/sc/tags.json b/public/language/sc/tags.json index f065d4bbfa..d2e9a213ac 100644 --- a/public/language/sc/tags.json +++ b/public/language/sc/tags.json @@ -2,5 +2,6 @@ "no_tag_topics": "There are no topics with this tag.", "tags": "Tags", "enter_tags_here": "Enter tags here. Press enter after each tag.", + "enter_tags_here_short": "Enter tags...", "no_tags": "There are no tags yet." } \ No newline at end of file diff --git a/public/language/sc/topic.json b/public/language/sc/topic.json index b6eee990c6..d97f81d2ee 100644 --- a/public/language/sc/topic.json +++ b/public/language/sc/topic.json @@ -28,15 +28,17 @@ "flag_title": "Signala custu arresonu pro sa moderatzione", "flag_confirm": "Are you sure you want to flag this post?", "flag_success": "This post has been flagged for moderation.", - "deleted_message": "Custa arresonada est istada cantzellada. Isceti is impitadores chi tenent is permissos pro manigiare is arresonadas dda podent bìdere.", + "deleted_message": "This topic has been deleted. Only users with topic management privileges can see it.", "following_topic.message": "As a retzire notìficas si calincunu pùblica in custa arresonada.", "not_following_topic.message": "No as a retzire prus notìficas pro custa arresonada.", "login_to_subscribe": "Pro praghere registra·ti o intra pro sutascrìere custa arresonada.", "markAsUnreadForAll.success": "Arresonada marcada comente de lèghere pro totus.", "watch": "Càstia", + "unwatch": "Unwatch", "watch.title": "Be notified of new replies in this topic", + "unwatch.title": "Stop watching this topic", "share_this_post": "Cumpartzi custu Arresonu", - "thread_tools.title": "Ainas de Arresonada", + "thread_tools.title": "Topic Tools", "thread_tools.markAsUnreadForAll": "Signa comente De Lèghere", "thread_tools.pin": "Pone in evidèntzia s'Arresonda", "thread_tools.unpin": "Boga dae s'Evidèntzia s'Arresonasa", @@ -46,11 +48,11 @@ "thread_tools.move_all": "Move All", "thread_tools.fork": "Partzi Arresonada", "thread_tools.delete": "Cantzella Arresonada", - "thread_tools.delete_confirm": "Are you sure you want to delete this thread?", + "thread_tools.delete_confirm": "Are you sure you want to delete this topic?", "thread_tools.restore": "Torra a s'Arresonada Allogada", - "thread_tools.restore_confirm": "Are you sure you want to restore this thread?", + "thread_tools.restore_confirm": "Are you sure you want to restore this topic?", "thread_tools.purge": "Purge Topic", - "thread_tools.purge_confirm": "Are you sure you want to purge this thread?", + "thread_tools.purge_confirm": "Are you sure you want to purge this topic?", "topic_move_success": "This topic has been successfully moved to %1", "post_delete_confirm": "Are you sure you want to delete this post?", "post_restore_confirm": "Are you sure you want to restore this post?", diff --git a/public/language/sc/user.json b/public/language/sc/user.json index 298fc5ffa0..6a01840a34 100644 --- a/public/language/sc/user.json +++ b/public/language/sc/user.json @@ -29,6 +29,7 @@ "edit": "Acontza", "uploaded_picture": "Immàgine Carrigada", "upload_new_picture": "Càrriga Immàgine Noa", + "upload_new_picture_from_url": "Upload New Picture From URL", "current_password": "Password Presente", "change_password": "Muda Password", "change_password_error": "Invalid Password!", diff --git a/public/language/sk/error.json b/public/language/sk/error.json index 60262ff3e4..1e32d8d59f 100644 --- a/public/language/sk/error.json +++ b/public/language/sk/error.json @@ -13,6 +13,7 @@ "invalid-user-data": "Neplatné užívatelské údaje", "invalid-password": "Nesprávne heslo", "invalid-username-or-password": "Please specify both a username and password", + "invalid-search-term": "Invalid search term", "invalid-pagination-value": "Nesprávna hodnota stránkovania", "username-taken": "Užívateľske meno je obsadené", "email-taken": "Email je obsadený", diff --git a/public/language/sk/notifications.json b/public/language/sk/notifications.json index cffd1a1fc0..8dc04fa258 100644 --- a/public/language/sk/notifications.json +++ b/public/language/sk/notifications.json @@ -11,8 +11,8 @@ "you_have_unread_notifications": "Máte neprečítané notifikácie", "new_message_from": "Nova spáva od %1", "upvoted_your_post_in": "%1 has upvoted your post in %2.", - "moved_your_post": "%1 has moved your post.", - "moved_your_topic": "%1 has moved your topic.", + "moved_your_post": "%1 has moved your post.", + "moved_your_topic": "%1 has moved your topic.", "favourited_your_post_in": "%1 has favourited your post in %2.", "user_flagged_post_in": "%1 flagged a post in %2", "user_posted_to": "%1 odpovedal: %2", diff --git a/public/language/sk/pages.json b/public/language/sk/pages.json index 764229a282..22120a855b 100644 --- a/public/language/sk/pages.json +++ b/public/language/sk/pages.json @@ -12,5 +12,7 @@ "user.posts": "Príspevky od %1", "user.topics": "Téma vytvorená %1\n", "user.favourites": "%1's obľubených príspevkov", - "user.settings": "Užívatelské nadstavenie" + "user.settings": "Užívatelské nadstavenie", + "maintenance.text": "%1 is currently undergoing maintenance. Please come back another time.", + "maintenance.messageIntro": "Additionally, the administator has left this message:" } \ No newline at end of file diff --git a/public/language/sk/tags.json b/public/language/sk/tags.json index f065d4bbfa..d2e9a213ac 100644 --- a/public/language/sk/tags.json +++ b/public/language/sk/tags.json @@ -2,5 +2,6 @@ "no_tag_topics": "There are no topics with this tag.", "tags": "Tags", "enter_tags_here": "Enter tags here. Press enter after each tag.", + "enter_tags_here_short": "Enter tags...", "no_tags": "There are no tags yet." } \ No newline at end of file diff --git a/public/language/sk/topic.json b/public/language/sk/topic.json index 72da1a0869..c295cca131 100644 --- a/public/language/sk/topic.json +++ b/public/language/sk/topic.json @@ -28,15 +28,17 @@ "flag_title": "Označiť príspevok pre moderáciu", "flag_confirm": "Ste si istý, že chcete označit tento príspevok?", "flag_success": "Tento príspevok bol označený na úpravu. ", - "deleted_message": "Toto vlákno bolo vymazané. Iba užívatelia s privilégiami ho môžu vidieť.", + "deleted_message": "This topic has been deleted. Only users with topic management privileges can see it.", "following_topic.message": "Budete teraz príjimať notifikácie, ked niekto prispeje do témy.", "not_following_topic.message": "Nebudete už dostávať notifikácie z tejto Témy", "login_to_subscribe": "Prosím Zaregistrujte sa alebo sa Prihláste, aby ste mohli odoberať túto Tému", "markAsUnreadForAll.success": "Téma označená ako neprečítaná pre všetkých.", "watch": "Sledovať", + "unwatch": "Unwatch", "watch.title": "Buďte informovaní o nových odpovediach k tejto téme", + "unwatch.title": "Stop watching this topic", "share_this_post": "Zdielaj tento príspevok", - "thread_tools.title": "Nástroje", + "thread_tools.title": "Topic Tools", "thread_tools.markAsUnreadForAll": "Označ ako neprečítané", "thread_tools.pin": "Zviditeľniť tému", "thread_tools.unpin": "Odstrániť zviditeľnenie témy", @@ -46,11 +48,11 @@ "thread_tools.move_all": "Presunúť všetko", "thread_tools.fork": "Rozvetviť tému", "thread_tools.delete": "Vymazať tému", - "thread_tools.delete_confirm": "Ste si istý, že chcete vymazať tento príspevok?", + "thread_tools.delete_confirm": "Are you sure you want to delete this topic?", "thread_tools.restore": "Vrátiť tému", - "thread_tools.restore_confirm": "Ste si istý, že chcete obnoviť tento príspevok?", + "thread_tools.restore_confirm": "Are you sure you want to restore this topic?", "thread_tools.purge": "Purge Topic", - "thread_tools.purge_confirm": "Are you sure you want to purge this thread?", + "thread_tools.purge_confirm": "Are you sure you want to purge this topic?", "topic_move_success": "Téma bola úspešne presunutá do %1", "post_delete_confirm": "Ste si istý, že chcete vymazať tento príspevok?", "post_restore_confirm": "Ste si istí, že chcete obnoviť tento príspevok?", diff --git a/public/language/sk/user.json b/public/language/sk/user.json index 2fa171f6ef..eec0ca9486 100644 --- a/public/language/sk/user.json +++ b/public/language/sk/user.json @@ -29,6 +29,7 @@ "edit": "Upraviť", "uploaded_picture": "Nahraný obrázok", "upload_new_picture": "Nahrať nový obrázok", + "upload_new_picture_from_url": "Upload New Picture From URL", "current_password": "Aktuálne heslo", "change_password": "Zmeniť heslo", "change_password_error": "Nesprávne heslo!", diff --git a/public/language/sv/error.json b/public/language/sv/error.json index 6f118116f3..e36d1d33c3 100644 --- a/public/language/sv/error.json +++ b/public/language/sv/error.json @@ -13,6 +13,7 @@ "invalid-user-data": "Ogiltig användardata", "invalid-password": "Ogiltigt lösenord", "invalid-username-or-password": "Specificera både användarnamn och lösenord", + "invalid-search-term": "Invalid search term", "invalid-pagination-value": "Ogiltigt sidnummer", "username-taken": "Användarnamn upptaget", "email-taken": "Epostadress upptagen", diff --git a/public/language/sv/notifications.json b/public/language/sv/notifications.json index 2e8e5a3f2a..01a1094540 100644 --- a/public/language/sv/notifications.json +++ b/public/language/sv/notifications.json @@ -11,8 +11,8 @@ "you_have_unread_notifications": "Du har olästa notiser.", "new_message_from": "Nytt medelande från %1", "upvoted_your_post_in": "%1 har röstat upp ditt inlägg i %2", - "moved_your_post": "%1 har flyttat dit inlägg.", - "moved_your_topic": "%1 har flyttat ditt ämne.", + "moved_your_post": "%1 has moved your post.", + "moved_your_topic": "%1 has moved your topic.", "favourited_your_post_in": "%1 har favoriserat ditt inlägg i %2.", "user_flagged_post_in": "%1 flaggade ett inlägg i %2", "user_posted_to": "%1 har skrivit ett svar på: %2", diff --git a/public/language/sv/pages.json b/public/language/sv/pages.json index 72c9d13e33..dbed4da96d 100644 --- a/public/language/sv/pages.json +++ b/public/language/sv/pages.json @@ -12,5 +12,7 @@ "user.posts": "Inlägg skapat av %1", "user.topics": "Ämnen skapade av %1", "user.favourites": "%1's favorit-inlägg", - "user.settings": "Avnändarinställningar" + "user.settings": "Avnändarinställningar", + "maintenance.text": "%1 is currently undergoing maintenance. Please come back another time.", + "maintenance.messageIntro": "Additionally, the administator has left this message:" } \ No newline at end of file diff --git a/public/language/sv/tags.json b/public/language/sv/tags.json index e7f66208a8..e2e8199384 100644 --- a/public/language/sv/tags.json +++ b/public/language/sv/tags.json @@ -2,5 +2,6 @@ "no_tag_topics": "Det finns inga ämnen med detta märkord.", "tags": "Märkord", "enter_tags_here": "Skriv in märkord här. Tryck på enter efter varje märkord.", + "enter_tags_here_short": "Enter tags...", "no_tags": "Det finns inga märkord ännu." } \ No newline at end of file diff --git a/public/language/sv/topic.json b/public/language/sv/topic.json index f2f1adb18d..012b4e9735 100644 --- a/public/language/sv/topic.json +++ b/public/language/sv/topic.json @@ -28,15 +28,17 @@ "flag_title": "Rapportera detta inlägg för granskning", "flag_confirm": "Är du säker på att du vill flagga det här inlägget?", "flag_success": "Det här inlägget har flaggats för moderering.", - "deleted_message": "Denna tråd har tagits bort. Endast användare med administrations-rättigheter kan se den.", + "deleted_message": "This topic has been deleted. Only users with topic management privileges can see it.", "following_topic.message": "Du kommer nu få notiser när någon gör inlägg i detta ämne.", "not_following_topic.message": "Du kommer inte längre få notiser från detta ämne.", "login_to_subscribe": "Var god registrera eller logga in för att kunna prenumerera på detta ämne.", "markAsUnreadForAll.success": "Ämne markerat som oläst av alla.", "watch": "Följ", + "unwatch": "Unwatch", "watch.title": "Få notis om nya svar till det här ämnet", + "unwatch.title": "Stop watching this topic", "share_this_post": "Dela detta inlägg", - "thread_tools.title": "Trådverktyg", + "thread_tools.title": "Topic Tools", "thread_tools.markAsUnreadForAll": "Markera som oläst", "thread_tools.pin": "Fäst ämne", "thread_tools.unpin": "Lösgör ämne", @@ -46,11 +48,11 @@ "thread_tools.move_all": "Flytta alla.", "thread_tools.fork": "Grena ämne", "thread_tools.delete": "Ta bort ämne", - "thread_tools.delete_confirm": "Är du säker på att du vill ta bort den här tråden?", + "thread_tools.delete_confirm": "Are you sure you want to delete this topic?", "thread_tools.restore": "Återställ ämne", - "thread_tools.restore_confirm": "Är du säker på att du vill återställa den här tråden?", + "thread_tools.restore_confirm": "Are you sure you want to restore this topic?", "thread_tools.purge": "Rensa ämne", - "thread_tools.purge_confirm": "Är du säker att du vill rensa ut den här tråden?", + "thread_tools.purge_confirm": "Are you sure you want to purge this topic?", "topic_move_success": "Det här ämnet har flyttats till %1", "post_delete_confirm": "Är du säker på att du vill ta bort det här inlägget?", "post_restore_confirm": "Är du säker på att du vill återställa det här inlägget?", diff --git a/public/language/sv/user.json b/public/language/sv/user.json index e4e18aafaa..b9689c5f98 100644 --- a/public/language/sv/user.json +++ b/public/language/sv/user.json @@ -29,6 +29,7 @@ "edit": "Ändra", "uploaded_picture": "Uppladdad bild", "upload_new_picture": "Ladda upp ny bild", + "upload_new_picture_from_url": "Upload New Picture From URL", "current_password": "Nuvarande lösenord", "change_password": "Ändra lösenord", "change_password_error": "Ogiltigt lösenord.", diff --git a/public/language/th/error.json b/public/language/th/error.json index f90d459cce..a46573d5b9 100644 --- a/public/language/th/error.json +++ b/public/language/th/error.json @@ -13,6 +13,7 @@ "invalid-user-data": "Invalid User Data", "invalid-password": "Invalid Password", "invalid-username-or-password": "Please specify both a username and password", + "invalid-search-term": "Invalid search term", "invalid-pagination-value": "Invalid pagination value", "username-taken": "Username taken", "email-taken": "Email taken", diff --git a/public/language/th/notifications.json b/public/language/th/notifications.json index d78ad8e8f6..c98ef643a2 100644 --- a/public/language/th/notifications.json +++ b/public/language/th/notifications.json @@ -11,8 +11,8 @@ "you_have_unread_notifications": "You have unread notifications.", "new_message_from": "New message from %1", "upvoted_your_post_in": "%1 has upvoted your post in %2.", - "moved_your_post": "%1 has moved your post.", - "moved_your_topic": "%1 has moved your topic.", + "moved_your_post": "%1 has moved your post.", + "moved_your_topic": "%1 has moved your topic.", "favourited_your_post_in": "%1 has favourited your post in %2.", "user_flagged_post_in": "%1 flagged a post in %2", "user_posted_to": "%1 has posted a reply to: %2", diff --git a/public/language/th/pages.json b/public/language/th/pages.json index b6cb79bcc3..3659c376ae 100644 --- a/public/language/th/pages.json +++ b/public/language/th/pages.json @@ -12,5 +12,7 @@ "user.posts": "กระทู้โดย %1", "user.topics": "Topics created by %1", "user.favourites": "กระทู้ที่ %1 ชอบ", - "user.settings": "ตั้งค่าผู้ใช้" + "user.settings": "ตั้งค่าผู้ใช้", + "maintenance.text": "%1 is currently undergoing maintenance. Please come back another time.", + "maintenance.messageIntro": "Additionally, the administator has left this message:" } \ No newline at end of file diff --git a/public/language/th/tags.json b/public/language/th/tags.json index f065d4bbfa..d2e9a213ac 100644 --- a/public/language/th/tags.json +++ b/public/language/th/tags.json @@ -2,5 +2,6 @@ "no_tag_topics": "There are no topics with this tag.", "tags": "Tags", "enter_tags_here": "Enter tags here. Press enter after each tag.", + "enter_tags_here_short": "Enter tags...", "no_tags": "There are no tags yet." } \ No newline at end of file diff --git a/public/language/th/topic.json b/public/language/th/topic.json index 0736d6be97..a938ce0255 100644 --- a/public/language/th/topic.json +++ b/public/language/th/topic.json @@ -28,15 +28,17 @@ "flag_title": "ปักธงโพสต์นี้เพื่อดำเนินการ", "flag_confirm": "Are you sure you want to flag this post?", "flag_success": "This post has been flagged for moderation.", - "deleted_message": "กระทู้นี้ได้ถูกลบไปแล้ว เฉพาะผู้ใช้ที่มีสิทธิ์จัดการสามารถดูได้", + "deleted_message": "This topic has been deleted. Only users with topic management privileges can see it.", "following_topic.message": "คุณจะได้รับการแจ้งเตือนเมื่อมีคนโพสต์ในกระทู้นี้", "not_following_topic.message": "คุณจะไม่รับการแจ้งเตือนจากกระทู้นี้", "login_to_subscribe": "กรุณาลงทะเบียนหรือเข้าสู่ระบบเพื่อที่จะติดตามกระทู้นี้", "markAsUnreadForAll.success": "ทำเครื่องหมายว่ายังไม่ได้อ่านทั้งหมด", "watch": "ติดตาม", + "unwatch": "Unwatch", "watch.title": "Be notified of new replies in this topic", + "unwatch.title": "Stop watching this topic", "share_this_post": "แชร์โพสต์นี้", - "thread_tools.title": "เครื่องมือจัดการกระทู้", + "thread_tools.title": "Topic Tools", "thread_tools.markAsUnreadForAll": "ทำหมายว่ายังไม่ได้อ่าน", "thread_tools.pin": "ปักหมุดกระทู้", "thread_tools.unpin": "เลิกปักหมุดกระทู้", @@ -46,11 +48,11 @@ "thread_tools.move_all": "Move All", "thread_tools.fork": "แยกกระทู้", "thread_tools.delete": "ลบกระทู้", - "thread_tools.delete_confirm": "Are you sure you want to delete this thread?", + "thread_tools.delete_confirm": "Are you sure you want to delete this topic?", "thread_tools.restore": "กู้กระทู้", - "thread_tools.restore_confirm": "Are you sure you want to restore this thread?", + "thread_tools.restore_confirm": "Are you sure you want to restore this topic?", "thread_tools.purge": "Purge Topic", - "thread_tools.purge_confirm": "Are you sure you want to purge this thread?", + "thread_tools.purge_confirm": "Are you sure you want to purge this topic?", "topic_move_success": "This topic has been successfully moved to %1", "post_delete_confirm": "Are you sure you want to delete this post?", "post_restore_confirm": "Are you sure you want to restore this post?", diff --git a/public/language/th/user.json b/public/language/th/user.json index ba6a427e10..5ec3c1b5f7 100644 --- a/public/language/th/user.json +++ b/public/language/th/user.json @@ -29,6 +29,7 @@ "edit": "แก้ไข", "uploaded_picture": "อัปโหลดรูป", "upload_new_picture": "อัพโหลดรูปใหม่", + "upload_new_picture_from_url": "Upload New Picture From URL", "current_password": "รหัสผ่านปัจจุบัน", "change_password": "เปลี่ยนรหัสผ่าน", "change_password_error": "Invalid Password!", diff --git a/public/language/tr/error.json b/public/language/tr/error.json index 86b7a7567d..a6e8e7d436 100644 --- a/public/language/tr/error.json +++ b/public/language/tr/error.json @@ -13,6 +13,7 @@ "invalid-user-data": "Geçersiz Kullancı Verisi", "invalid-password": "Geçersiz Şifre", "invalid-username-or-password": "Please specify both a username and password", + "invalid-search-term": "Invalid search term", "invalid-pagination-value": "Geçersiz Sayfa Değeri", "username-taken": "Kullanıcı İsmi Alınmış", "email-taken": "E-posta Alınmış", diff --git a/public/language/tr/notifications.json b/public/language/tr/notifications.json index 8b5db97764..a846939221 100644 --- a/public/language/tr/notifications.json +++ b/public/language/tr/notifications.json @@ -11,8 +11,8 @@ "you_have_unread_notifications": "Okunmamış bildirimleriniz var.", "new_message_from": "%1 size bir mesaj gönderdi", "upvoted_your_post_in": "%1 has upvoted your post in %2.", - "moved_your_post": "%1 has moved your post.", - "moved_your_topic": "%1 has moved your topic.", + "moved_your_post": "%1 has moved your post.", + "moved_your_topic": "%1 has moved your topic.", "favourited_your_post_in": "%1 has favourited your post in %2.", "user_flagged_post_in": "%1 flagged a post in %2", "user_posted_to": "%1 %2 başlığına bir ileti gönderdi.", diff --git a/public/language/tr/pages.json b/public/language/tr/pages.json index 582a524b6b..a6e6ef22e3 100644 --- a/public/language/tr/pages.json +++ b/public/language/tr/pages.json @@ -12,5 +12,7 @@ "user.posts": "%1 tarafından gönderilen iletiler", "user.topics": "%1 tarafından gönderilen başlıklar", "user.favourites": "%1'in Favori İletileri", - "user.settings": "Kullanıcı Ayarları" + "user.settings": "Kullanıcı Ayarları", + "maintenance.text": "%1 is currently undergoing maintenance. Please come back another time.", + "maintenance.messageIntro": "Additionally, the administator has left this message:" } \ No newline at end of file diff --git a/public/language/tr/tags.json b/public/language/tr/tags.json index a60ce28e1a..34ae901f26 100644 --- a/public/language/tr/tags.json +++ b/public/language/tr/tags.json @@ -2,5 +2,6 @@ "no_tag_topics": "Bu etiketli başlık yok.", "tags": "Etiketler", "enter_tags_here": "Etiketleri buraya girin.", + "enter_tags_here_short": "Enter tags...", "no_tags": "Henüz etiket yok." } \ No newline at end of file diff --git a/public/language/tr/topic.json b/public/language/tr/topic.json index f4968ca57a..1fc21d664d 100644 --- a/public/language/tr/topic.json +++ b/public/language/tr/topic.json @@ -28,15 +28,17 @@ "flag_title": "Bu iletiyi moderatöre haber et", "flag_confirm": "Bu iletiyi yöneticilere bildirmek istiyor musun?", "flag_success": "Bu ileti yöneticilere bildirildi.", - "deleted_message": "Bu başlık silindi. Sadece başlık düzenleme yetkisi olan kullanıcılar görebilir.", + "deleted_message": "This topic has been deleted. Only users with topic management privileges can see it.", "following_topic.message": "Artık bir kullanıcı bu başlığa ileti gönderdiğinde bildirim alacaksınız.", "not_following_topic.message": "Artık bu başlık için bildirim almayacaksınız.", "login_to_subscribe": "Lütfen bu iletiyi başlığa üye olmak için giriş yapın.", "markAsUnreadForAll.success": "Başlık herkes için okunmadı olarak işaretlendi.", "watch": "İzle", + "unwatch": "Unwatch", "watch.title": "Bu başlığa gelen yeni iletilerden haberdar ol", + "unwatch.title": "Stop watching this topic", "share_this_post": "Bu iletiyi paylaş", - "thread_tools.title": "Seçenekler", + "thread_tools.title": "Topic Tools", "thread_tools.markAsUnreadForAll": "Okunmadı Olarak İşaretle", "thread_tools.pin": "Başlığı İğnele", "thread_tools.unpin": "Başlığı İğneleme", @@ -46,11 +48,11 @@ "thread_tools.move_all": "Hepsini Taşı", "thread_tools.fork": "Başlığı Ayır", "thread_tools.delete": "Başlığı Sil", - "thread_tools.delete_confirm": "Bu başlığı gerçekten silmek istiyor musun?", + "thread_tools.delete_confirm": "Are you sure you want to delete this topic?", "thread_tools.restore": "Başlığı Geri Getir", - "thread_tools.restore_confirm": "Bu başlığı gerçekten geri getirmek istiyor musun?", + "thread_tools.restore_confirm": "Are you sure you want to restore this topic?", "thread_tools.purge": "Konuyu Temizle", - "thread_tools.purge_confirm": "Bu konuyu temizlemek istediğinize eminmisiniz?", + "thread_tools.purge_confirm": "Are you sure you want to purge this topic?", "topic_move_success": "Başlık %1 kategorisine başarıyla taşındı.", "post_delete_confirm": "Bu iletiyi gerçekten silmek istiyor musun?", "post_restore_confirm": "Bu iletiyi gerçekten geri getirmek istiyor musun?", diff --git a/public/language/tr/user.json b/public/language/tr/user.json index 8d9584f0bc..23ca56979d 100644 --- a/public/language/tr/user.json +++ b/public/language/tr/user.json @@ -29,6 +29,7 @@ "edit": "Düzenle", "uploaded_picture": "Yüklenmiş Fotoğraflar", "upload_new_picture": "Yeni bir resim Yükle", + "upload_new_picture_from_url": "Upload New Picture From URL", "current_password": "Şu anki şifre", "change_password": "Şifre Değiştir", "change_password_error": "Geçersiz Şifre", diff --git a/public/language/vi/error.json b/public/language/vi/error.json index a70d1676dd..eea4b0c679 100644 --- a/public/language/vi/error.json +++ b/public/language/vi/error.json @@ -13,6 +13,7 @@ "invalid-user-data": "Dữ liệu tài khoản không hợp lệ", "invalid-password": "Mật khẩu không hợp lệ", "invalid-username-or-password": "Xin hãy nhập cả tên đăng nhập và mật khẩu", + "invalid-search-term": "Invalid search term", "invalid-pagination-value": "Số trang không hợp lệ", "username-taken": "Tên đăng nhập đã tồn tại", "email-taken": "Email đã tồn tại", diff --git a/public/language/vi/notifications.json b/public/language/vi/notifications.json index 48f6174299..cbe6605a39 100644 --- a/public/language/vi/notifications.json +++ b/public/language/vi/notifications.json @@ -11,8 +11,8 @@ "you_have_unread_notifications": "Bạn có thông báo chưa đọc", "new_message_from": "Tin nhắn mới từ %1", "upvoted_your_post_in": "%1 đã bình chọn bài của bạn trong %2.", - "moved_your_post": "%1 đã di chuyển bài của bạn.", - "moved_your_topic": "%1 đã di chuyển chủ đề của bạn.", + "moved_your_post": "%1 has moved your post.", + "moved_your_topic": "%1 has moved your topic.", "favourited_your_post_in": "%1 đã thích bài của bạn trong %2.", "user_flagged_post_in": "%1 gắn cờ 1 bài trong %2", "user_posted_to": "%1 đã trả lời %2", diff --git a/public/language/vi/pages.json b/public/language/vi/pages.json index 87143000ad..ee4d6b92e9 100644 --- a/public/language/vi/pages.json +++ b/public/language/vi/pages.json @@ -12,5 +12,7 @@ "user.posts": "Các bài được %1 viết", "user.topics": "Các chủ đề được %1 tạo", "user.favourites": "Các bài gửi yêu thích của %1", - "user.settings": "Thiết lập cho người dùng" + "user.settings": "Thiết lập cho người dùng", + "maintenance.text": "%1 is currently undergoing maintenance. Please come back another time.", + "maintenance.messageIntro": "Additionally, the administator has left this message:" } \ No newline at end of file diff --git a/public/language/vi/tags.json b/public/language/vi/tags.json index e309559c94..d8dba17a92 100644 --- a/public/language/vi/tags.json +++ b/public/language/vi/tags.json @@ -2,5 +2,6 @@ "no_tag_topics": "Không có bài viết nào với nhãn này.", "tags": "Nhãn", "enter_tags_here": "Nhập nhãn ở đây. Nhấn enter sau mỗi nhãn.", + "enter_tags_here_short": "Enter tags...", "no_tags": "Chưa có nhãn nào." } \ No newline at end of file diff --git a/public/language/vi/topic.json b/public/language/vi/topic.json index a1bfe804ae..1c4722f0f3 100644 --- a/public/language/vi/topic.json +++ b/public/language/vi/topic.json @@ -28,15 +28,17 @@ "flag_title": "Flag bài viết này để chỉnh sửa", "flag_confirm": "Bạn có chắc là muốn flag bài viết này không?", "flag_success": "Chủ đề này đã được flag để chỉnh sửa", - "deleted_message": "Thread này đã bị xóa. Chỉ người dùng có quyền quản lý thread mới xem được.", + "deleted_message": "This topic has been deleted. Only users with topic management privileges can see it.", "following_topic.message": "Từ giờ bạn sẽ nhận được thông báo khi có ai đó gửi bài viết trong chủ đề này", "not_following_topic.message": "Bạn sẽ không còn nhận được thông báo từ chủ đề này", "login_to_subscribe": "Xin hãy đăng ký hoặc đăng nhập để theo dõi topic này", "markAsUnreadForAll.success": "Chủ đề đã được đánh dấu là chưa đọc toàn bộ", "watch": "Xem", + "unwatch": "Unwatch", "watch.title": "Được thông báo khi có trả lời mới trong chủ đề này", + "unwatch.title": "Stop watching this topic", "share_this_post": "Chia sẻ bài viết này", - "thread_tools.title": "Công cụ cho Thread", + "thread_tools.title": "Topic Tools", "thread_tools.markAsUnreadForAll": "Đánh dấu chưa đọc", "thread_tools.pin": "Pin chủ đề", "thread_tools.unpin": "Bỏ pin chủ đề", @@ -46,11 +48,11 @@ "thread_tools.move_all": "Chuyển tất cả", "thread_tools.fork": "Fork chủ đề", "thread_tools.delete": "Xóa chủ đề", - "thread_tools.delete_confirm": "Bạn có chắc là muốn hủy thread này không?", + "thread_tools.delete_confirm": "Are you sure you want to delete this topic?", "thread_tools.restore": "Phục hồi chủ đề", - "thread_tools.restore_confirm": "Bạn có chắc là muốn phục hồi thread này không", + "thread_tools.restore_confirm": "Are you sure you want to restore this topic?", "thread_tools.purge": "Xóa hẳn chủ đề", - "thread_tools.purge_confirm": "Bạn có chắc muốn xóa hẳn chủ đề này?", + "thread_tools.purge_confirm": "Are you sure you want to purge this topic?", "topic_move_success": "Đã chuyển thành công chủ đề này sang %1", "post_delete_confirm": "Bạn có chắc là muốn xóa bài gửi này không?", "post_restore_confirm": "Bạn có chắc là muốn phục hồi bài gửi này không?", diff --git a/public/language/vi/user.json b/public/language/vi/user.json index c7bc8ac37e..76ad5b7582 100644 --- a/public/language/vi/user.json +++ b/public/language/vi/user.json @@ -29,6 +29,7 @@ "edit": "Chỉnh sửa", "uploaded_picture": "Ảnh đã tải lên", "upload_new_picture": "Tải lên ảnh mới", + "upload_new_picture_from_url": "Upload New Picture From URL", "current_password": "Mật khẩu hiện tại", "change_password": "Thay đổi mật khẩu", "change_password_error": "Mật khẩu không đúng!", diff --git a/public/language/zh_CN/error.json b/public/language/zh_CN/error.json index 2e9a43df6d..03f184af4c 100644 --- a/public/language/zh_CN/error.json +++ b/public/language/zh_CN/error.json @@ -13,6 +13,7 @@ "invalid-user-data": "无效用户数据", "invalid-password": "无效密码", "invalid-username-or-password": "请确认用户名和密码", + "invalid-search-term": "Invalid search term", "invalid-pagination-value": "无效页码", "username-taken": "用户名已注册", "email-taken": "电子邮箱已注册", diff --git a/public/language/zh_CN/notifications.json b/public/language/zh_CN/notifications.json index 13d533bc53..f3b9cb4573 100644 --- a/public/language/zh_CN/notifications.json +++ b/public/language/zh_CN/notifications.json @@ -11,8 +11,8 @@ "you_have_unread_notifications": "您有未读的通知。", "new_message_from": "来自 %1 的新消息", "upvoted_your_post_in": "%1%2 点赞了您的帖子。", - "moved_your_post": "%1 移动了您的帖子。", - "moved_your_topic": "%1 移动了您的主题帖。", + "moved_your_post": "%1 has moved your post.", + "moved_your_topic": "%1 has moved your topic.", "favourited_your_post_in": "%1%2 收藏了您的帖子。", "user_flagged_post_in": "%1%2 标记了一个帖子", "user_posted_to": "%1 回复了:%2", diff --git a/public/language/zh_CN/pages.json b/public/language/zh_CN/pages.json index 28af9ea173..15d7fb9b1c 100644 --- a/public/language/zh_CN/pages.json +++ b/public/language/zh_CN/pages.json @@ -12,5 +12,7 @@ "user.posts": "%1 发布的帖子", "user.topics": "%1 创建的主题", "user.favourites": "%1 收藏的帖子", - "user.settings": "用户设置" + "user.settings": "用户设置", + "maintenance.text": "%1 is currently undergoing maintenance. Please come back another time.", + "maintenance.messageIntro": "Additionally, the administator has left this message:" } \ No newline at end of file diff --git a/public/language/zh_CN/tags.json b/public/language/zh_CN/tags.json index 95c058e0f0..4e5e24d5b2 100644 --- a/public/language/zh_CN/tags.json +++ b/public/language/zh_CN/tags.json @@ -2,5 +2,6 @@ "no_tag_topics": "此话题还没有主题帖。", "tags": "话题", "enter_tags_here": "在此输入话题。每输入一个话题按一次回车。", + "enter_tags_here_short": "Enter tags...", "no_tags": "尚无话题。" } \ No newline at end of file diff --git a/public/language/zh_CN/topic.json b/public/language/zh_CN/topic.json index eb57246dbf..73ba83a2eb 100644 --- a/public/language/zh_CN/topic.json +++ b/public/language/zh_CN/topic.json @@ -28,15 +28,17 @@ "flag_title": "对此帖设置管理标志", "flag_confirm": "确认给此帖设置标记吗?", "flag_success": "此回帖已设置管理标记。", - "deleted_message": "此帖已删除,仅限管理者查看。", + "deleted_message": "This topic has been deleted. Only users with topic management privileges can see it.", "following_topic.message": "当有人回复此主题时您会收到通知。", "not_following_topic.message": "您已停止接收此主题的通知。", "login_to_subscribe": "请注册或登录后再订阅此主题。", "markAsUnreadForAll.success": "将全部主题标为未读。", "watch": "监视", + "unwatch": "Unwatch", "watch.title": "此主题有新回复时通知我", + "unwatch.title": "Stop watching this topic", "share_this_post": "分享此帖", - "thread_tools.title": "管理工具", + "thread_tools.title": "Topic Tools", "thread_tools.markAsUnreadForAll": "未读标记", "thread_tools.pin": "置顶主题", "thread_tools.unpin": "取消置顶主题", @@ -46,11 +48,11 @@ "thread_tools.move_all": "移动全部", "thread_tools.fork": "分割主题", "thread_tools.delete": "删除主题", - "thread_tools.delete_confirm": "确认删除此帖吗?", + "thread_tools.delete_confirm": "Are you sure you want to delete this topic?", "thread_tools.restore": "恢复主题", - "thread_tools.restore_confirm": "确认恢复此帖吗?", + "thread_tools.restore_confirm": "Are you sure you want to restore this topic?", "thread_tools.purge": "清除主题", - "thread_tools.purge_confirm": "确认清除此帖吗?", + "thread_tools.purge_confirm": "Are you sure you want to purge this topic?", "topic_move_success": "此主题已成功移到 %1", "post_delete_confirm": "确定删除此帖吗?", "post_restore_confirm": "确定恢复此帖吗?", diff --git a/public/language/zh_CN/user.json b/public/language/zh_CN/user.json index 39a83ee44d..be55fcb1a6 100644 --- a/public/language/zh_CN/user.json +++ b/public/language/zh_CN/user.json @@ -29,6 +29,7 @@ "edit": "编辑", "uploaded_picture": "已有头像", "upload_new_picture": "上传新头像", + "upload_new_picture_from_url": "Upload New Picture From URL", "current_password": "当前密码", "change_password": "更改密码", "change_password_error": "无效的密码!", diff --git a/public/language/zh_TW/error.json b/public/language/zh_TW/error.json index 544d11f42e..6700970456 100644 --- a/public/language/zh_TW/error.json +++ b/public/language/zh_TW/error.json @@ -13,6 +13,7 @@ "invalid-user-data": "無效的使用者資料", "invalid-password": "無效的密碼", "invalid-username-or-password": "Please specify both a username and password", + "invalid-search-term": "Invalid search term", "invalid-pagination-value": "無效的分頁數值", "username-taken": "該使用者名稱已被使用", "email-taken": "該信箱已被使用", diff --git a/public/language/zh_TW/notifications.json b/public/language/zh_TW/notifications.json index e9132a07df..525a7cf610 100644 --- a/public/language/zh_TW/notifications.json +++ b/public/language/zh_TW/notifications.json @@ -11,8 +11,8 @@ "you_have_unread_notifications": "您有未讀的訊息!", "new_message_from": "來自 %1 的新訊息", "upvoted_your_post_in": "%1 has upvoted your post in %2.", - "moved_your_post": "%1 has moved your post.", - "moved_your_topic": "%1 has moved your topic.", + "moved_your_post": "%1 has moved your post.", + "moved_your_topic": "%1 has moved your topic.", "favourited_your_post_in": "%1 has favourited your post in %2.", "user_flagged_post_in": "%1 flagged a post in %2", "user_posted_to": "%1 has posted a reply to: %2", diff --git a/public/language/zh_TW/pages.json b/public/language/zh_TW/pages.json index dc1bcacfe0..64610801d3 100644 --- a/public/language/zh_TW/pages.json +++ b/public/language/zh_TW/pages.json @@ -12,5 +12,7 @@ "user.posts": "文章由 %1 所張貼", "user.topics": "主題由 %1 所創建", "user.favourites": "%1's 最喜愛的文章", - "user.settings": "使用者設定" + "user.settings": "使用者設定", + "maintenance.text": "%1 is currently undergoing maintenance. Please come back another time.", + "maintenance.messageIntro": "Additionally, the administator has left this message:" } \ No newline at end of file diff --git a/public/language/zh_TW/tags.json b/public/language/zh_TW/tags.json index f065d4bbfa..d2e9a213ac 100644 --- a/public/language/zh_TW/tags.json +++ b/public/language/zh_TW/tags.json @@ -2,5 +2,6 @@ "no_tag_topics": "There are no topics with this tag.", "tags": "Tags", "enter_tags_here": "Enter tags here. Press enter after each tag.", + "enter_tags_here_short": "Enter tags...", "no_tags": "There are no tags yet." } \ No newline at end of file diff --git a/public/language/zh_TW/topic.json b/public/language/zh_TW/topic.json index bba85af9e6..e1f964990e 100644 --- a/public/language/zh_TW/topic.json +++ b/public/language/zh_TW/topic.json @@ -28,15 +28,17 @@ "flag_title": "檢舉這篇文章, 交給仲裁者來審閱.", "flag_confirm": "你確定要檢舉這文章嗎?", "flag_success": "這文章已經被檢舉要求仲裁.", - "deleted_message": "已被刪除, 擁有管理權限的使用者才可觀看.", + "deleted_message": "This topic has been deleted. Only users with topic management privileges can see it.", "following_topic.message": "有人貼文回覆主題時, 你將會收到新通知.", "not_following_topic.message": "有人貼文回覆主題時, 你將不會收到通知.", "login_to_subscribe": "請先註冊或登錄, 才可訂閱此主題.", "markAsUnreadForAll.success": "將全部的主題設為未讀.", "watch": "關注", + "unwatch": "Unwatch", "watch.title": "當主題有新回覆時將收到通知", + "unwatch.title": "Stop watching this topic", "share_this_post": "分享這篇文章", - "thread_tools.title": "管理工具", + "thread_tools.title": "Topic Tools", "thread_tools.markAsUnreadForAll": "設為未讀", "thread_tools.pin": "釘選主題", "thread_tools.unpin": "取消釘選主題", @@ -46,11 +48,11 @@ "thread_tools.move_all": "移動全部", "thread_tools.fork": "Fork 主題", "thread_tools.delete": "刪除主題", - "thread_tools.delete_confirm": "你確定要刪除這討論串嗎?", + "thread_tools.delete_confirm": "Are you sure you want to delete this topic?", "thread_tools.restore": "還原刪除的主題", - "thread_tools.restore_confirm": "你確定要還原這討論串嗎?", + "thread_tools.restore_confirm": "Are you sure you want to restore this topic?", "thread_tools.purge": "Purge Topic", - "thread_tools.purge_confirm": "Are you sure you want to purge this thread?", + "thread_tools.purge_confirm": "Are you sure you want to purge this topic?", "topic_move_success": "主題已成功移至 %1", "post_delete_confirm": "你確定要刪除這文章嗎?", "post_restore_confirm": "你確定要還原這文章嗎?", diff --git a/public/language/zh_TW/user.json b/public/language/zh_TW/user.json index 46def28d26..52c61fdf6b 100644 --- a/public/language/zh_TW/user.json +++ b/public/language/zh_TW/user.json @@ -29,6 +29,7 @@ "edit": "編輯", "uploaded_picture": "已有頭像", "upload_new_picture": "上傳新頭像", + "upload_new_picture_from_url": "Upload New Picture From URL", "current_password": "目前的密碼", "change_password": "更改密碼", "change_password_error": "無效的密碼!", diff --git a/public/less/admin/admin.less b/public/less/admin/admin.less index 24822a43b0..a2f8366969 100644 --- a/public/less/admin/admin.less +++ b/public/less/admin/admin.less @@ -1,17 +1,20 @@ @import "./bootstrap/bootstrap"; @import "./mixins"; -@import "./components"; @import "./general/dashboard"; @import "./manage/categories"; @import "./manage/groups"; @import "./manage/tags"; +@import "./manage/flags"; @import "./manage/users"; @import "./appearance/customise"; @import "./appearance/themes"; @import "./extend/plugins"; @import "./advanced/database"; +@import "./modules/alerts"; +@import "./modules/selectable"; + .admin { padding-top: 70px; background: #f0f0f0; @@ -21,6 +24,11 @@ padding: 0px 15px; } + .user-img { + width:24px; + height:24px; + } + .nodebb-logo { img { height: 31px; @@ -42,7 +50,7 @@ width: 200px; height: 100%; - + -webkit-transform: translateX(-190px); transform: translateX(-190px); @@ -79,7 +87,7 @@ max-height: 100%; cursor: pointer; max-height: 38px; - + &.open { max-height: 500px; } @@ -180,8 +188,10 @@ padding: 6px; img { - width: 35px; - height: 35px; + width: 30px; + height: 30px; + vertical-align: -88%; + margin-right: 5px; } } @@ -204,7 +214,43 @@ } } + .navbar { + padding: 0 5px; + + .nav-home a, .nav-home a:hover { + width: 30px; + height: 30px; + padding: 5px; + text-align: center; + margin-top: 10px; + background: #DDD; + + i { + color: black; + font-size: 17px; + } + } + + } + #acp-search { + input { + background: black; + border: 0; + color: white; + box-shadow: none; + .transition(.4s ease width); + font-family: @font-family-monospace; + width: 30px; + height: 30px; + vertical-align: -30%; + border-radius: 0; + + &:focus { + width: 200px; + } + } + .search-match { font-weight: 700; color: black; diff --git a/public/less/admin/manage/categories.less b/public/less/admin/manage/categories.less index 37b7b939a5..5982a108dd 100644 --- a/public/less/admin/manage/categories.less +++ b/public/less/admin/manage/categories.less @@ -113,4 +113,21 @@ } } } + + .category-list { + padding: 0; + + li { + .inline-block; + .pointer; + padding: 0.5em; + margin: 0.25em; + .border-radius(3px); + + &.disabled { + -webkit-filter: grayscale(30%); + .opacity(0.5); + } + } + } } \ No newline at end of file diff --git a/public/less/admin/manage/flags.less b/public/less/admin/manage/flags.less new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/public/less/admin/manage/flags.less @@ -0,0 +1 @@ + diff --git a/public/less/admin/manage/tags.less b/public/less/admin/manage/tags.less index 69cc32eb2b..f26fa00327 100644 --- a/public/less/admin/manage/tags.less +++ b/public/less/admin/manage/tags.less @@ -11,14 +11,9 @@ .tag-item { cursor: pointer; display: inline-block; - margin-top: 22px; font-size: 11px; } } - - .save { - margin-top:25px; - } } .tag-item { diff --git a/public/less/admin/manage/users.less b/public/less/admin/manage/users.less index e30949416e..3a0b741046 100644 --- a/public/less/admin/manage/users.less +++ b/public/less/admin/manage/users.less @@ -1,26 +1,47 @@ .users { #users-container { - padding: 0; - } + border: 1px solid #eee; + padding: 0px 20px 20px; - .users-box{ - display: inline-block; - margin-top: 20px; - text-align: center; - vertical-align: top; - white-space: nowrap; - text-overflow: ellipsis; - overflow: hidden; - height: auto; - max-width: 125px; + .users-box { + display: inline-block; + margin-top: 20px; + text-align: center; + vertical-align: top; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + height: auto; + max-width: 125px; + min-width: 125px; - img { - width:80px; - height:80px; - } + img { + width:80px; + height:80px; + } - a { - margin-bottom:5px; + a { + margin-bottom:5px; + font-size: 12px; + } + + .user-image { + position: relative; + + .labels { + position: absolute; + bottom: 0px; + width: 100%; + + span { + width: 100%; + opacity: 0.9; + font-size: 10px; + display: block; + border-radius: 0; + } + } + } } } } \ No newline at end of file diff --git a/public/less/admin/components.less b/public/less/admin/modules/alerts.less similarity index 99% rename from public/less/admin/components.less rename to public/less/admin/modules/alerts.less index 06d2424d71..8d52e439e1 100644 --- a/public/less/admin/components.less +++ b/public/less/admin/modules/alerts.less @@ -32,4 +32,4 @@ .alert-right-bottom { right:20px; bottom:20px; -} \ No newline at end of file +} diff --git a/public/less/admin/modules/selectable.less b/public/less/admin/modules/selectable.less new file mode 100644 index 0000000000..7a1721fe23 --- /dev/null +++ b/public/less/admin/modules/selectable.less @@ -0,0 +1,23 @@ +.selectable { + .user-select(none); + position: relative; + + .selector { + position: absolute; + border: 1px solid #89B; + background: #BCE; + background-color: #BEC; + border-color: #8B9; + z-index: 999; + } + + .selection { + border: 1px solid transparent; + margin: 2px; + + &.selected, &.active { + background-color: #ECF1DB; + border: 1px dashed #9B8; + } + } +} \ No newline at end of file diff --git a/public/src/admin.js b/public/src/admin.js deleted file mode 100644 index 22eb108125..0000000000 --- a/public/src/admin.js +++ /dev/null @@ -1,67 +0,0 @@ -"use strict"; -/*global app, socket*/ - -var admin = {}; - -(function() { - - admin.enableColorPicker = function(inputEl, callback) { - (inputEl instanceof jQuery ? inputEl : $(inputEl)).each(function() { - var $this = $(this); - - $this.ColorPicker({ - color: $this.val() || '#000', - onChange: function(hsb, hex) { - $this.val('#' + hex); - if (typeof callback === 'function') { - callback(hsb, hex); - } - }, - onShow: function(colpkr) { - $(colpkr).css('z-index', 1051); - } - }); - }); - }; - - $(function() { - var menuEl = $('.sidebar-nav'), - liEls = menuEl.find('li'), - parentEl, - activate = function(li) { - liEls.removeClass('active'); - li.addClass('active'); - }; - - // also on ready, check the pathname, maybe it was a page refresh and no item was clicked - liEls.each(function(i, li){ - li = $(li); - if ((li.find('a').attr('href') || '').indexOf(location.pathname) >= 0) { - activate(li); - } - }); - - // On menu click, change "active" state - menuEl.on('click', function(e) { - parentEl = $(e.target).parent(); - if (parentEl.is('li')) { - activate(parentEl); - } - }); - }); - - socket.emit('admin.config.get', function(err, config) { - if(err) { - return app.alert({ - alert_id: 'config_status', - timeout: 2500, - title: 'Error', - message: 'NodeBB encountered a problem getting config', - type: 'danger' - }); - } - - // move this to admin.config - app.config = config; - }); -}()); \ No newline at end of file diff --git a/public/src/admin/admin.js b/public/src/admin/admin.js new file mode 100644 index 0000000000..77dbe50947 --- /dev/null +++ b/public/src/admin/admin.js @@ -0,0 +1,145 @@ +"use strict"; +/*global define, socket, app, Mousetrap, Hammer, RELATIVE_PATH*/ + +var admin = {}; + +(function() { + admin.enableColorPicker = function(inputEl, callback) { + (inputEl instanceof jQuery ? inputEl : $(inputEl)).each(function() { + var $this = $(this); + + $this.ColorPicker({ + color: $this.val() || '#000', + onChange: function(hsb, hex) { + $this.val('#' + hex); + if (typeof callback === 'function') { + callback(hsb, hex); + } + }, + onShow: function(colpkr) { + $(colpkr).css('z-index', 1051); + } + }); + }); + }; + + $(document).ready(function() { + setupMenu(); + setupKeybindings(); + + if(!/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) { + require(['admin/modules/search'], function(search) { + search.init(); + }); + } else { + activateMobile(); + } + + $(window).on('action:ajaxify.end', function(ev, data) { + var url = data.url; + + selectMenuItem(data.url); + }); + }); + + socket.emit('admin.config.get', function(err, config) { + if(err) { + return app.alert({ + alert_id: 'config_status', + timeout: 2500, + title: 'Error', + message: 'NodeBB encountered a problem getting config: ' + err.message, + type: 'danger' + }); + } + + // move this to admin.config + app.config = config; + }); + + function setupMenu() { + var listElements = $('.sidebar-nav li'); + + listElements.on('click', function() { + var $this = $(this); + + if ($this.hasClass('nav-header')) { + $this.parents('.sidebar-nav').toggleClass('open').bind('animationend webkitAnimationEnd MSAnimationEnd oAnimationEnd', function (ev) { + $('.nano').nanoScroller(); + }); + } else { + listElements.removeClass('active'); + $this.addClass('active'); + } + }); + + $('.nano').nanoScroller(); + + $('#main-menu .nav-list > li a').append(' '); + } + + function setupKeybindings() { + Mousetrap.bind('ctrl+shift+a r', function() { + console.log('[admin] Reloading NodeBB...'); + socket.emit('admin.reload'); + }); + + Mousetrap.bind('ctrl+shift+a R', function() { + console.log('[admin] Restarting NodeBB...'); + socket.emit('admin.restart'); + }); + + Mousetrap.bind('/', function(e) { + $('#acp-search input').focus(); + + return false; + }); + } + + + function activateMobile() { + $('.admin').addClass('mobile'); + $('#main-menu').addClass('transitioning'); + + Hammer(document.body).on('swiperight', function(e) { + $('#main-menu').addClass('open'); + }); + + Hammer(document.body).on('swipeleft', function(e) { + $('#main-menu').removeClass('open'); + }); + + Hammer($('#main-menu')[0]).on('swiperight', function(e) { + $('#main-menu').addClass('open'); + }); + + Hammer($('#main-menu')[0]).on('swipeleft', function(e) { + $('#main-menu').removeClass('open'); + }); + + $(window).on('scroll', function() { + $('#main-menu').height($(window).height() + 20); + }); + } + + function selectMenuItem(url) { + $('#main-menu .nav-list > li').removeClass('active').each(function() { + var menu = $(this), + category = menu.parents('.sidebar-nav'), + href = menu.children('a').attr('href'); + + if (href && href.slice(1).indexOf(url) !== -1) { + category.addClass('open'); + menu.addClass('active'); + modifyBreadcrumb(category.find('.nav-header').text(), menu.text()); + return false; + } + }); + } + + function modifyBreadcrumb() { + var caret = ' '; + + $('#breadcrumbs').html(caret + Array.prototype.slice.call(arguments).join(caret)); + } +}()); \ No newline at end of file diff --git a/public/src/forum/admin/appearance/customise.js b/public/src/admin/appearance/customise.js similarity index 89% rename from public/src/forum/admin/appearance/customise.js rename to public/src/admin/appearance/customise.js index 482dd7fb6d..97dd77290b 100644 --- a/public/src/forum/admin/appearance/customise.js +++ b/public/src/admin/appearance/customise.js @@ -1,7 +1,7 @@ "use strict"; /* global define, app, socket */ -define('forum/admin/appearance/customise', ['forum/admin/settings'], function(Settings) { +define('admin/appearance/customise', ['admin/settings'], function(Settings) { var Customise = {}; Customise.init = function() { diff --git a/public/src/forum/admin/appearance/skins.js b/public/src/admin/appearance/skins.js similarity index 97% rename from public/src/forum/admin/appearance/skins.js rename to public/src/admin/appearance/skins.js index 75a57e60e5..8fd9208c7e 100644 --- a/public/src/forum/admin/appearance/skins.js +++ b/public/src/admin/appearance/skins.js @@ -1,7 +1,7 @@ "use strict"; /* global define, app, socket */ -define('forum/admin/appearance/skins', function() { +define('admin/appearance/skins', function() { var Skins = {}; Skins.init = function() { diff --git a/public/src/forum/admin/appearance/themes.js b/public/src/admin/appearance/themes.js similarity index 95% rename from public/src/forum/admin/appearance/themes.js rename to public/src/admin/appearance/themes.js index d61c1061f7..e513d677ec 100644 --- a/public/src/forum/admin/appearance/themes.js +++ b/public/src/admin/appearance/themes.js @@ -1,7 +1,7 @@ "use strict"; -/* global define, app, socket */ +/* global define, app, socket, bootbox, templates, config */ -define('forum/admin/appearance/themes', function() { +define('admin/appearance/themes', function() { var Themes = {}; Themes.init = function() { @@ -62,7 +62,6 @@ define('forum/admin/appearance/themes', function() { }); }); - // Installed Themes socket.emit('admin.themes.getInstalled', function(err, themes) { if(err) { return app.alertError(err.message); diff --git a/public/src/forum/admin/extend/plugins.js b/public/src/admin/extend/plugins.js similarity index 70% rename from public/src/forum/admin/extend/plugins.js rename to public/src/admin/extend/plugins.js index d8bfcec156..b5f61a9801 100644 --- a/public/src/forum/admin/extend/plugins.js +++ b/public/src/admin/extend/plugins.js @@ -1,7 +1,7 @@ "use strict"; /* global define, app, socket */ -define('forum/admin/extend/plugins', function() { +define('admin/extend/plugins', function() { var Plugins = { init: function() { var pluginsList = $('.plugins'), @@ -34,36 +34,55 @@ define('forum/admin/extend/plugins', function() { pluginID = $(this).parents('li').attr('data-plugin-id'); var btn = $(this); + var activateBtn = btn.siblings('[data-action="toggleActive"]'); btn.html(btn.html() + 'ing') .attr('disabled', true) .find('i').attr('class', 'fa fa-refresh fa-spin'); socket.emit('admin.plugins.toggleInstall', pluginID, function(err, status) { - var activateBtn = $('.plugins li[data-plugin-id="' + pluginID + '"] button[data-action="toggleActive"]'); + if (err) { + return app.alertError(err.message); + } if (status.installed) { btn.html(' Uninstall'); } else { btn.html(' Install'); + } + activateBtn.toggleClass('hidden', !status.installed); btn.toggleClass('btn-danger', status.installed).toggleClass('btn-success', !status.installed) .attr('disabled', false); - activateBtn.toggleClass('hide', !status.installed); - activateBtn.html(' Activate'); - activateBtn.toggleClass('btn-success', true).toggleClass('btn-warning', false); - app.alert({ alert_id: 'plugin_toggled', title: 'Plugin ' + (status.installed ? 'Installed' : 'Uninstalled'), - message: status.installed ? 'You still have to activate this plugin to use it!' : 'The plugin is also deactivated!', + message: status.installed ? 'Plugin successfully installed, please activate the plugin.' : 'The plugin has been successfully deactivated and uninstalled.', type: 'info', timeout: 5000 }); }); }); + pluginsList.on('click', 'button[data-action="upgrade"]', function() { + var btn = $(this); + var parent = btn.parents('li'); + pluginID = parent.attr('data-plugin-id'); + + btn.attr('disabled', true).find('i').attr('class', 'fa fa-refresh fa-spin'); + + socket.emit('admin.plugins.upgrade', pluginID, function(err) { + if (err) { + return app.alertError(err.message); + } + parent.find('.fa-exclamation-triangle').remove(); + parent.find('.currentVersion').text(parent.find('.latestVersion').text()); + btn.remove(); + }); + }); + + $('#plugin-search').on('input propertychange', function() { var term = $(this).val(); $('.plugins li').each(function() { diff --git a/public/src/forum/admin/extend/widgets.js b/public/src/admin/extend/widgets.js similarity index 99% rename from public/src/forum/admin/extend/widgets.js rename to public/src/admin/extend/widgets.js index 393a58df4b..2546258d5c 100644 --- a/public/src/forum/admin/extend/widgets.js +++ b/public/src/admin/extend/widgets.js @@ -1,7 +1,7 @@ "use strict"; /* global define, app, socket */ -define('forum/admin/extend/widgets', function() { +define('admin/extend/widgets', function() { var Widgets = {}; Widgets.init = function() { diff --git a/public/src/forum/admin/general/dashboard.js b/public/src/admin/general/dashboard.js similarity index 86% rename from public/src/forum/admin/general/dashboard.js rename to public/src/admin/general/dashboard.js index 8ef3ef0c06..c2bd8c704b 100644 --- a/public/src/forum/admin/general/dashboard.js +++ b/public/src/admin/general/dashboard.js @@ -1,20 +1,35 @@ "use strict"; -/*global define, ajaxify, app, socket, RELATIVE_PATH*/ +/*global define, ajaxify, app, socket, bootbox, Chart, RELATIVE_PATH*/ + +define('admin/general/dashboard', ['semver'], function(semver) { + var Admin = {}, + intervals = { + rooms: false, + graphs: false + }, + isMobile = false; + -define('forum/admin/general/dashboard', ['semver'], function(semver) { - var Admin = {}; - var updateIntervalId = 0; Admin.init = function() { app.enterRoom('admin'); socket.emit('meta.rooms.getAll', Admin.updateRoomUsage); - if (updateIntervalId) { - clearInterval(updateIntervalId); - } - updateIntervalId = setInterval(function() { - socket.emit('meta.rooms.getAll', Admin.updateRoomUsage); + isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); + + intervals.rooms = setInterval(function() { + if (app.isFocused && app.isConnected) { + socket.emit('meta.rooms.getAll', Admin.updateRoomUsage); + } }, 5000); + $(window).on('action:ajaxify.start', function(ev, data) { + clearInterval(intervals.rooms); + clearInterval(intervals.graphs); + + intervals.rooms = null; + intervals.graphs = null; + }); + $('#logout-link').on('click', function() { $.post(RELATIVE_PATH + '/logout', function() { window.location.href = RELATIVE_PATH + '/'; @@ -35,12 +50,15 @@ define('forum/admin/general/dashboard', ['semver'], function(semver) { checkEl.html($('.version-check').html().replace('', 'v' + latestVersion)); // Alter box colour accordingly - if (latestVersion === version) { + if (semver.eq(latestVersion, version)) { checkEl.removeClass('alert-info').addClass('alert-success'); checkEl.append('

You are up-to-date

'); - } else if (latestVersion > version) { + } else if (semver.gt(latestVersion, version)) { checkEl.removeClass('alert-info').addClass('alert-danger'); checkEl.append('

A new version (v' + latestVersion + ') has been released. Consider upgrading your NodeBB.

'); + } else if (semver.gt(version, latestVersion)) { + checkEl.removeClass('alert-info').addClass('alert-warning'); + checkEl.append('

You are running a development version! Unintended bugs may occur.

'); } }); @@ -107,20 +125,20 @@ define('forum/admin/general/dashboard', ['semver'], function(semver) { return app.alertError(err.message); } - var html = '
' + - '
'+ data.onlineRegisteredCount +'
' + + var html = '
' + + '
'+ data.onlineRegisteredCount +'
' + '
Users
' + '
' + - '
' + - '
'+ data.onlineGuestCount +'
' + + '
' + + '
'+ data.onlineGuestCount +'
' + '
Guests
' + '
' + - '
' + - '
'+ (data.onlineRegisteredCount + data.onlineGuestCount) +'
' + + '
' + + '
'+ (data.onlineRegisteredCount + data.onlineGuestCount) +'
' + '
Total
' + '
' + - '
' + - '
'+ data.socketCount +'
' + + '
' + + '
'+ data.socketCount +'
' + '
Connections
' + '
'; @@ -195,6 +213,10 @@ define('forum/admin/general/dashboard', ['semver'], function(semver) { topicsCtx = topicsCanvas.getContext('2d'), trafficLabels = getHoursArray(); + if (isMobile) { + Chart.defaults.global.showTooltips = false; + } + var data = { labels: trafficLabels, datasets: [ @@ -274,7 +296,7 @@ define('forum/admin/general/dashboard', ['semver'], function(semver) { window.open(RELATIVE_PATH + '/topic/' + obj[0].tid); }; - setInterval(updateTrafficGraph, 15000); + intervals.graphs = setInterval(updateTrafficGraph, 15000); updateTrafficGraph(); $(window).on('resize', adjustPieCharts); @@ -294,6 +316,10 @@ define('forum/admin/general/dashboard', ['semver'], function(semver) { } function updateTrafficGraph() { + if (!app.isFocused || !app.isConnected) { + return; + } + socket.emit('admin.analytics.get', {graph: "traffic"}, function (err, data) { for (var i = 0, ii = data.pageviews.length; i < ii; i++) { graphs.traffic.datasets[0].points[i].value = data.pageviews[i]; @@ -333,7 +359,7 @@ define('forum/admin/general/dashboard', ['semver'], function(semver) { segments = graphs.topics.segments; function reassignExistingTopics() { - for (var i = 0, ii = segments.length; i < ii; i++ ) { + for (var i = segments.length - 1; i >= 0; i--) { if (!segments[i]) { continue; } @@ -411,9 +437,5 @@ define('forum/admin/general/dashboard', ['semver'], function(semver) { graphs.topics.update(); } - function buildTopicsLegend() { - - } - return Admin; }); diff --git a/public/src/admin/general/languages.js b/public/src/admin/general/languages.js new file mode 100644 index 0000000000..8720f96df2 --- /dev/null +++ b/public/src/admin/general/languages.js @@ -0,0 +1,8 @@ +"use strict"; +/*global define*/ + +define('admin/general/languages', ['admin/settings'], function(Settings) { + $(function() { + Settings.prepare(); + }); +}); diff --git a/public/src/forum/admin/general/sounds.js b/public/src/admin/general/sounds.js similarity index 88% rename from public/src/forum/admin/general/sounds.js rename to public/src/admin/general/sounds.js index 63d83789f4..9ec9708df5 100644 --- a/public/src/forum/admin/general/sounds.js +++ b/public/src/admin/general/sounds.js @@ -1,7 +1,7 @@ "use strict"; /* global define, socket */ -define('forum/admin/general/sounds', ['sounds', 'settings'], function(Sounds, Settings) { +define('admin/general/sounds', ['sounds', 'settings'], function(Sounds, Settings) { var SoundsAdmin = {}; SoundsAdmin.init = function() { diff --git a/public/src/forum/admin/manage/categories.js b/public/src/admin/manage/categories.js similarity index 98% rename from public/src/forum/admin/manage/categories.js rename to public/src/admin/manage/categories.js index b613f396fc..a77c8d67d2 100644 --- a/public/src/forum/admin/manage/categories.js +++ b/public/src/admin/manage/categories.js @@ -1,7 +1,7 @@ "use strict"; /*global define, socket, app, bootbox, templates, ajaxify, RELATIVE_PATH*/ -define('forum/admin/manage/categories', ['uploader', 'forum/admin/iconSelect'], function(uploader, iconSelect) { +define('admin/manage/categories', ['uploader', 'admin/modules/iconSelect'], function(uploader, iconSelect) { var Categories = {}; Categories.init = function() { @@ -244,7 +244,7 @@ define('forum/admin/manage/categories', ['uploader', 'forum/admin/iconSelect'], if (err) { return app.alertError(err.message); } - ajaxify.go('admin/categories/active'); + ajaxify.go('admin/manage/categories/active'); }); }); @@ -259,7 +259,7 @@ define('forum/admin/manage/categories', ['uploader', 'forum/admin/iconSelect'], socket.emit('admin.categories.update', payload, function(err) { modalEl.one('hidden.bs.modal', function() { - ajaxify.go('admin/categories/active'); + ajaxify.go('admin/manage/categories/active'); }); modalEl.modal('hide'); }); diff --git a/public/src/admin/manage/flags.js b/public/src/admin/manage/flags.js new file mode 100644 index 0000000000..edea968ae5 --- /dev/null +++ b/public/src/admin/manage/flags.js @@ -0,0 +1,69 @@ +"use strict"; +/*global define, socket, app, admin, utils, RELATIVE_PATH*/ + +define('admin/manage/flags', ['forum/infinitescroll', 'admin/modules/selectable'], function(infinitescroll, selectable) { + var Flags = {}; + + Flags.init = function() { + $('.post-container .content img').addClass('img-responsive') + handleDismiss(); + handleDelete(); + handleInfiniteScroll(); + }; + + function handleDismiss() { + $('.flags').on('click', '.dismiss', function() { + var btn = $(this); + var pid = btn.siblings('[data-pid]').attr('data-pid'); + + socket.emit('admin.dismissFlag', pid, function(err) { + done(err, btn); + }); + }); + } + + function handleDelete() { + $('.flags').on('click', '.delete', function() { + var btn = $(this); + var pid = btn.siblings('[data-pid]').attr('data-pid'); + var tid = btn.siblings('[data-pid]').attr('data-tid'); + socket.emit('posts.delete', {pid: pid, tid: tid}, function(err) { + done(err, btn); + }); + }); + } + + function done(err, btn) { + if (err) { + return app.alertError(err.messaage); + } + btn.parent().fadeOut(function() { + $(this).remove(); + if (!$('.flags [data-pid]').length) { + $('.post-container').text('No flagged posts!'); + } + }); + } + + function handleInfiniteScroll() { + infinitescroll.init(function(direction) { + if (direction < 0 && !$('.flags').length) { + return; + } + + infinitescroll.loadMore('admin.getMoreFlags', $('[data-next]').attr('data-next'), function(data, done) { + if (data.posts && data.posts.length) { + infinitescroll.parseAndTranslate('admin/manage/flags', 'posts', {posts: data.posts}, function(html) { + $('[data-next]').attr('data-next', data.next); + $('.post-container').append(html); + done(); + }); + } else { + done(); + } + }); + }); + } + + return Flags; +}); \ No newline at end of file diff --git a/public/src/forum/admin/manage/groups.js b/public/src/admin/manage/groups.js similarity index 98% rename from public/src/forum/admin/manage/groups.js rename to public/src/admin/manage/groups.js index b0b5d282c7..6871bc07b1 100644 --- a/public/src/forum/admin/manage/groups.js +++ b/public/src/admin/manage/groups.js @@ -1,7 +1,7 @@ "use strict"; /*global define, templates, socket, ajaxify, app, bootbox*/ -define('forum/admin/manage/groups', ['forum/admin/iconSelect'], function(iconSelect) { +define('admin/manage/groups', ['admin/modules/iconSelect'], function(iconSelect) { var Groups = {}; Groups.init = function() { diff --git a/public/src/forum/admin/manage/tags.js b/public/src/admin/manage/tags.js similarity index 57% rename from public/src/forum/admin/manage/tags.js rename to public/src/admin/manage/tags.js index 472aab3280..84cd492ea0 100644 --- a/public/src/forum/admin/manage/tags.js +++ b/public/src/admin/manage/tags.js @@ -1,44 +1,49 @@ "use strict"; /*global define, socket, app, admin, utils, bootbox, RELATIVE_PATH*/ -define('forum/admin/manage/tags', ['forum/infinitescroll'], function(infinitescroll) { - var Tags = {}; - var timeoutId = 0; +define('admin/manage/tags', ['forum/infinitescroll', 'admin/modules/selectable'], function(infinitescroll, selectable) { + var Tags = {}, + timeoutId = 0; Tags.init = function() { handleColorPickers(); - - $('.tag-list').on('click', '.save', function() { - save($(this)); - }); + selectable.enable('.tag-management', '.tag-row'); $('#tag-search').on('input propertychange', function() { if (timeoutId) { clearTimeout(timeoutId); timeoutId = 0; } + timeoutId = setTimeout(function() { socket.emit('topics.searchAndLoadTags', {query: $('#tag-search').val()}, function(err, tags) { if (err) { return app.alertError(err.message); } - infinitescroll.parseAndTranslate('admin/tags', 'tags', {tags: tags}, function(html) { + + infinitescroll.parseAndTranslate('admin/manage/tags', 'tags', {tags: tags}, function(html) { $('.tag-list').html(html); utils.makeNumbersHumanReadable(html.find('.human-readable-number')); timeoutId = 0; + + selectable.enable('.tag-management', '.tag-row'); }); }); }, 100); }); - $('.tag-item').on('click', function(ev) { - var tagName = $(this).text(), - tag = $(this), - row = tag.parents('.tag-row'); + $('#modify').on('click', function(ev) { + var tagsToModify = $('.tag-row.selected'); + if (!tagsToModify.length) { + return; + } + + var firstTag = $(tagsToModify[0]), + title = tagsToModify.length > 1 ? 'Editing multiple tags' : 'Editing ' + firstTag.find('.tag-item').text() + ' tag'; bootbox.dialog({ - title: "Editing " + tagName + " tag", - message: $(this).parents('.tag-row').find('.tag-modal').html(), + title: title, + message: firstTag.find('.tag-modal').html(), buttons: { success: { label: "Save", @@ -46,20 +51,17 @@ define('forum/admin/manage/tags', ['forum/infinitescroll'], function(infinitescr callback: function() { var modal = $('.bootbox'), bgColor = modal.find('[data-name="bgColor"]').val(), - color = modal.find('[data-name="color"]').val(); + color = modal.find('[data-name="color"]').val(); - row.find('[data-name="bgColor"]').val(bgColor); - row.find('[data-name="color"]').val(color); - row.find('.tag-item').css('background-color', bgColor).css('color', color); + tagsToModify.each(function(idx, tag) { + tag = $(tag); - save(tag); - } - }, - info: { - label: "Click to view topics tagged \"" + tagName + "\"", - className: "btn-info", - callback: function() { - window.open(RELATIVE_PATH + '/tags/' + tagName); + tag.find('[data-name="bgColor"]').val(bgColor); + tag.find('[data-name="color"]').val(color); + tag.find('.tag-item').css('background-color', bgColor).css('color', color); + + save(tag); + }); } } } @@ -67,7 +69,7 @@ define('forum/admin/manage/tags', ['forum/infinitescroll'], function(infinitescr setTimeout(function() { handleColorPickers(); - }, 500); // bootbox made me do it. + }, 500); }); }; @@ -85,28 +87,24 @@ define('forum/admin/manage/tags', ['forum/infinitescroll'], function(infinitescr }); } - $('[data-name="bgColor"], [data-name="color"]').each(enableColorPicker); } - function save(saveBtn) { - var tagRow = saveBtn.parents('.tag-row'); - + function save(tag) { var data = { - tag: tagRow.attr('data-tag'), - bgColor : tagRow.find('[data-name="bgColor"]').val(), - color : tagRow.find('[data-name="color"]').val() + tag: tag.attr('data-tag'), + bgColor : tag.find('[data-name="bgColor"]').val(), + color : tag.find('[data-name="color"]').val() }; - console.log(data); + socket.emit('admin.tags.update', data, function(err) { if (err) { return app.alertError(err.message); } + app.alertSuccess('Tag Updated!'); }); } - - return Tags; }); \ No newline at end of file diff --git a/public/src/forum/admin/manage/users.js b/public/src/admin/manage/users.js similarity index 66% rename from public/src/forum/admin/manage/users.js rename to public/src/admin/manage/users.js index 268f6a500f..54eead3572 100644 --- a/public/src/forum/admin/manage/users.js +++ b/public/src/admin/manage/users.js @@ -1,59 +1,61 @@ "use strict"; /* global socket, define, templates, bootbox, app, ajaxify, */ -define('forum/admin/manage/users', function() { +define('admin/manage/users', ['admin/modules/selectable'], function(selectable) { var Users = {}; Users.init = function() { var yourid = ajaxify.variables.get('yourid'); - $('#users-container').on('click', '.select', function() { - var userBox = $(this).parents('.users-box'); - var isSelected = userBox.hasClass('selected'); - userBox.toggleClass('selected', !isSelected); - $(this).toggleClass('fa-square-o', isSelected).toggleClass('fa-check-square-o', !isSelected); - }); + selectable.enable('#users-container', '.user-selectable'); function getSelectedUids() { var uids = []; - $('#users-container .users-box.selected').each(function() { - uids.push($(this).attr('data-uid')); + $('#users-container .users-box .selected').each(function() { + uids.push($(this).parents('[data-uid]').attr('data-uid')); }); + return uids; } function update(className, state) { - $('#users-container .users-box.selected ' + className +'.label').each(function() { + $('#users-container .users-box .selected').siblings('.labels').find(className).each(function() { $(this).toggleClass('hide', !state); }); } function unselectAll() { - $('#users-container .users-box.selected').removeClass('selected') - .find('.select').toggleClass('fa-square-o', true).toggleClass('fa-check-square-o', false); + $('#users-container .users-box .selected').removeClass('selected'); } function removeSelected() { - $('#users-container .users-box.selected').remove(); + $('#users-container .users-box .selected').remove(); + } + + function done(successMessage, className, flag) { + return function(err) { + if (err) { + return app.alertError(err.message); + } + app.alertSuccess(successMessage); + if (className) { + update(className, flag); + } + unselectAll(); + }; } $('.ban-user').on('click', function() { var uids = getSelectedUids(); if (!uids.length) { - return; + return false; } bootbox.confirm('Do you really want to ban?', function(confirm) { if (confirm) { - socket.emit('admin.user.banUsers', uids, function(err) { - if (err) { - return app.alertError(err.message); - } - app.alertSuccess('User(s) banned!'); - }); - update('.ban', true); - unselectAll(); + socket.emit('admin.user.banUsers', uids, done('User(s) banned!', '.ban', true)); } }); + return false; }); $('.unban-user').on('click', function() { @@ -62,15 +64,8 @@ define('forum/admin/manage/users', function() { return; } - socket.emit('admin.user.unbanUsers', uids, function(err) { - if (err) { - return app.alertError(err.message); - } - app.alertSuccess('User(s) unbanned!'); - }); - - update('.ban', false); - unselectAll(); + socket.emit('admin.user.unbanUsers', uids, done('User(s) unbanned!', '.ban', false)); + return false; }); $('.reset-lockout').on('click', function() { @@ -79,14 +74,8 @@ define('forum/admin/manage/users', function() { return; } - socket.emit('admin.user.resetLockouts', uids, function(err) { - if (err) { - return app.alertError(err.message); - } - app.alertSuccess('Lockout(s) reset!'); - }); - - unselectAll(); + socket.emit('admin.user.resetLockouts', uids, done('Lockout(s) reset!')); + return false; }); $('.admin-user').on('click', function() { @@ -98,16 +87,9 @@ define('forum/admin/manage/users', function() { if (uids.indexOf(yourid) !== -1) { app.alertError('You can\'t remove yourself as Administrator!'); } else { - socket.emit('admin.user.makeAdmins', uids, function(err) { - if (err) { - return app.alertError(err.message); - } - app.alertSuccess('User(s) are now administrators.'); - }); - - update('.administrator', true); - unselectAll(); + socket.emit('admin.user.makeAdmins', uids, done('User(s) are now administrators.', '.administrator', true)); } + return false; }); $('.remove-admin-user').on('click', function() { @@ -121,18 +103,25 @@ define('forum/admin/manage/users', function() { } else { bootbox.confirm('Do you really want to remove admins?', function(confirm) { if (confirm) { - socket.emit('admin.user.removeAdmins', uids, function(err) { - if (err) { - return app.alertError(err.message); - } - app.alertSuccess('User(s) are no longer administrators.'); - }); - - update('.administrator', false); - unselectAll(); + socket.emit('admin.user.removeAdmins', uids, done('User(s) are no longer administrators.', '.administrator', false)); } }); } + return false; + }); + + $('.validate-email').on('click', function() { + var uids = getSelectedUids(); + if (!uids.length) { + return; + } + + bootbox.confirm('Do you want to validate email(s) of these user(s)?', function(confirm) { + if (confirm) { + socket.emit('admin.user.validateEmail', uids, done('Emails validated', '.notvalidated', false)); + } + }); + return false; }); $('.delete-user').on('click', function() { @@ -154,6 +143,7 @@ define('forum/admin/manage/users', function() { }); } }); + return false; }); function handleUserCreate() { @@ -223,12 +213,12 @@ define('forum/admin/manage/users', function() { $('.fa-spinner').removeClass('hidden'); socket.emit('admin.user.search', username, function(err, data) { - if(err) { + if (err) { return app.alertError(err.message); } - ajaxify.loadTemplate('admin/manage/users', function(adminUsers) { - $('.users').html(templates.parse(templates.getBlock(adminUsers, 'users'), data)); + templates.parse('admin/manage/users', 'users', data, function(html) { + $('#users-container').html(html); $('.fa-spinner').addClass('hidden'); @@ -243,6 +233,8 @@ define('forum/admin/manage/users', function() { .addClass('label-success') .removeClass('label-danger'); } + + selectable.enable('#users-container', '.user-selectable'); }); }); }, 250); @@ -251,34 +243,32 @@ define('forum/admin/manage/users', function() { handleUserCreate(); function onUsersLoaded(users) { - ajaxify.loadTemplate('admin/manage/users', function(adminUsers) { - var html = $(templates.parse(templates.getBlock(adminUsers, 'users'), {users: users})); - $('#users-container').append(html); + templates.parse('admin/manage/users', 'users', {users: users}, function(html) { + $('#users-container').append($(html)); }); } function loadMoreUsers() { - var set = ''; - if (active === 'latest') { - set = 'users:joindate'; - } else if (active === 'sort-posts') { + var set = 'users:joindate'; + if (active === 'sort-posts') { set = 'users:postcount'; } else if (active === 'sort-reputation') { set = 'users:reputation'; + } else if (active === 'banned') { + set = 'users:banned'; } - if (set) { - loadingMoreUsers = true; - socket.emit('user.loadMore', { - set: set, - after: $('#users-container').children().length - }, function(err, data) { - if (data && data.users.length) { - onUsersLoaded(data.users); - } - loadingMoreUsers = false; - }); - } + + loadingMoreUsers = true; + socket.emit('user.loadMore', { + set: set, + after: $('#users-container').children().length + }, function(err, data) { + if (data && data.users.length) { + onUsersLoaded(data.users); + } + loadingMoreUsers = false; + }); } $('#load-more-users-btn').on('click', loadMoreUsers); diff --git a/public/src/forum/admin/iconSelect.js b/public/src/admin/modules/iconSelect.js similarity index 88% rename from public/src/forum/admin/iconSelect.js rename to public/src/admin/modules/iconSelect.js index 348619bf37..286009bf0f 100644 --- a/public/src/forum/admin/iconSelect.js +++ b/public/src/admin/modules/iconSelect.js @@ -1,5 +1,4 @@ - -'use strict'; +"use strict"; /* globals define, bootbox */ @@ -34,7 +33,7 @@ define(function() { } }); - setTimeout(function() { //bootbox was rewritten for BS3 and I had to add this timeout for the previous code to work. TODO: to look into + setTimeout(function() { $('.bootbox .fa-icons i').on('click', function() { $('.bootbox .selected').removeClass('selected'); $(this).addClass('selected'); diff --git a/public/src/admin/modules/search.js b/public/src/admin/modules/search.js new file mode 100644 index 0000000000..b7cd131925 --- /dev/null +++ b/public/src/admin/modules/search.js @@ -0,0 +1,118 @@ +"use strict"; +/*globals define, admin, ajaxify, RELATIVE_PATH*/ + +define(function() { + var search = {}, + searchIndex; + + search.init = function() { + $.getJSON(RELATIVE_PATH + '/templates/indexed.json', function (data) { + searchIndex = data; + for (var file in searchIndex) { + if (searchIndex.hasOwnProperty(file)) { + searchIndex[file] = searchIndex[file].replace(/' + searchIndex[file] + '
'); + searchIndex[file].find('script').remove(); + + searchIndex[file] = searchIndex[file].text().toLowerCase().replace(/[ |\r|\n]+/g, ' '); + } + } + + delete searchIndex['/admin/header.tpl']; + delete searchIndex['/admin/footer.tpl']; + + setupACPSearch(); + }); + };; + + function setupACPSearch() { + var menu = $('#acp-search .dropdown-menu'), + routes = [], + input = $('#acp-search input'), + firstResult = null; + + input.on('keyup', function() { + $('#acp-search .dropdown').addClass('open'); + }); + + $('#acp-search').parents('form').on('submit', function(ev) { + var input = $(this).find('input'), + href = firstResult ? firstResult : RELATIVE_PATH + '/search/' + input.val(); + + ajaxify.go(href.replace(/^\//, '')); + + setTimeout(function() { + $('#acp-search .dropdown').removeClass('open'); + $(input).blur(); + }, 150); + + ev.preventDefault(); + return false; + }); + + $('.sidebar-nav a').each(function(idx, link) { + routes.push($(link).attr('href')); + }); + + input.on('blur', function() { + $(this).val('').attr('placeholder', '/'); + }); + + input.on('keyup focus', function() { + var $input = $(this), + value = $input.val().toLowerCase(), + menuItems = $('#acp-search .dropdown-menu').html(''); + + function toUpperCase(txt){ + return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); + } + + $input.attr('placeholder', ''); + + firstResult = null; + + if (value.length >= 3) { + for (var file in searchIndex) { + if (searchIndex.hasOwnProperty(file)) { + var position = searchIndex[file].indexOf(value); + + if (position !== -1) { + var href = file.replace('.tpl', ''), + title = href.replace(/^\/admin\//, '').split('/'), + description = searchIndex[file].substring(Math.max(0, position - 25), Math.min(searchIndex[file].length - 1, position + 25)) + .replace(value, '' + value + ''); + + for (var t in title) { + if (title.hasOwnProperty(t)) { + title[t] = title[t] + .replace('-', ' ') + .replace(/\w\S*/g, toUpperCase); + } + } + + title = title.join(' > '); + href = RELATIVE_PATH + href; + firstResult = firstResult ? firstResult : href; + + if ($.inArray(href, routes) !== -1) { + menuItems.append('
  • ' + title + '
    ...' + description + '...
  • '); + } + } + } + } + + if (menuItems.html() !== '') { + menuItems.append(''); + } + } + + if (value.length > 0) { + menuItems.append('
  • Search the forum for ' + value + '
  • '); + } else { + menuItems.append('
  • Start typing to see results...
  • '); + } + }); + } + + return search; +}); \ No newline at end of file diff --git a/public/src/admin/modules/selectable.js b/public/src/admin/modules/selectable.js new file mode 100644 index 0000000000..a2416cec84 --- /dev/null +++ b/public/src/admin/modules/selectable.js @@ -0,0 +1,80 @@ +"use strict"; + +/*globals define*/ + +define('admin/modules/selectable', function() { + var selectable = {}; + + // modified from http://threedubmedia.com/code/event/drop/demo/selection + selectable.enable = function(parentElement, elementsToSelect, events) { + function selected(element) { + var $element = $(element).toggleClass('selected'); + + if (events && typeof events.onSelected === 'function') { + events.onSelected($element); + } + } + + function unselected(element) { + var $element = $(element).removeClass('selected'); + + if (events && typeof events.onUnselected === 'function') { + events.onUnselected($element); + } + } + + parentElement = $(parentElement); + elementsToSelect = $(elementsToSelect); + + var offset = parentElement.offset(); + + parentElement + .addClass('selectable') + .on('mousedown', function(ev) { + if (!ev.shiftKey) { + unselected(elementsToSelect); + } + }) + .drag('start',function(ev, dd) { + if (!ev.shiftKey) { + unselected(elementsToSelect); + } + + return $('
    ') + .css('opacity', 0.65 ) + .appendTo(parentElement); + }) + .drag(function(ev, dd){ + $(dd.proxy).css({ + top: Math.min(ev.pageY - offset.top, dd.startY - offset.top), + left: Math.min(ev.pageX - offset.left, dd.startX - offset.left), + height: Math.abs(ev.pageY - dd.startY), + width: Math.abs(ev.pageX - dd.startX) + }); + }) + .drag('end',function(ev, dd){ + $(dd.proxy).remove(); + }); + + elementsToSelect + .addClass('selection') + .on('mouseup', function(ev) { + selected(this); + }) + .drop('start',function(){ + $(this).addClass('active'); + }) + .drop(function( ev, dd ){ + selected(this); + }) + .drop('end',function(){ + $(this).removeClass('active'); + }); + + $.drop({ + multi: true + }); + }; + + return selectable; +}); diff --git a/public/src/forum/admin/settings.js b/public/src/admin/settings.js similarity index 97% rename from public/src/forum/admin/settings.js rename to public/src/admin/settings.js index a2c32f7f7f..3dca037514 100644 --- a/public/src/forum/admin/settings.js +++ b/public/src/admin/settings.js @@ -1,7 +1,7 @@ "use strict"; /*global define, app, socket, ajaxify, RELATIVE_PATH */ -define('forum/admin/settings', ['uploader', 'sounds'], function(uploader, sounds) { +define('admin/settings', ['uploader', 'sounds'], function(uploader, sounds) { var Settings = {}; Settings.init = function() { diff --git a/public/src/ajaxify.js b/public/src/ajaxify.js index 287dd63b4c..73b7d22a69 100644 --- a/public/src/ajaxify.js +++ b/public/src/ajaxify.js @@ -145,7 +145,9 @@ $(document).ready(function() { }; ajaxify.loadScript = function(tpl_url, callback) { - require(['forum/' + tpl_url], function(script) { + var location = !app.inAdmin ? 'forum/' : ''; + + require([location + tpl_url], function(script) { if (script && script.init) { script.init(); } diff --git a/public/src/app.js b/public/src/app.js index 8138f956f2..815f4752cb 100644 --- a/public/src/app.js +++ b/public/src/app.js @@ -1,5 +1,5 @@ "use strict"; -/*global io, templates, translator, ajaxify, utils, RELATIVE_PATH*/ +/*global io, templates, translator, ajaxify, utils, bootbox, RELATIVE_PATH*/ var socket, config, @@ -7,6 +7,7 @@ var socket, 'username': null, 'uid': null, 'isFocused': true, + 'isConnected': false, 'currentRoom': null, 'widgets': {}, 'cacheBuster': null @@ -54,6 +55,7 @@ var socket, socket.emit('meta.reconnected'); + app.isConnected = true; $(window).trigger('action:reconnected'); setTimeout(function() { @@ -98,6 +100,7 @@ var socket, app.showLoginMessage(); app.replaceSelfLinks(); $(window).trigger('action:connected'); + app.isConnected = true; }); socket.on('event:alert', function (data) { @@ -108,6 +111,7 @@ var socket, socket.on('event:disconnect', function() { $(window).trigger('action:disconnected'); + app.isConnected = false; socket.socket.connect(); }); @@ -525,13 +529,13 @@ var socket, $('#logout-link').on('click', app.logout); - $window.blur(function(){ - app.isFocused = false; - }); - - $window.focus(function(){ - app.isFocused = true; - app.alternatingTitle(''); + Visibility.change(function(e, state){ + if (state === 'visible') { + app.isFocused = true; + app.alternatingTitle(''); + } else if (state === 'hidden') { + app.isFocused = false; + } }); createHeaderTooltips(); @@ -578,11 +582,6 @@ var socket, }); } }); - - // Admin keyboard shortcuts - require(['admin'], function(Admin) { - Admin.init(); - }); }); }; diff --git a/public/src/forum/account/edit.js b/public/src/client/account/edit.js similarity index 100% rename from public/src/forum/account/edit.js rename to public/src/client/account/edit.js diff --git a/public/src/forum/account/favourites.js b/public/src/client/account/favourites.js similarity index 100% rename from public/src/forum/account/favourites.js rename to public/src/client/account/favourites.js diff --git a/public/src/forum/account/followers.js b/public/src/client/account/followers.js similarity index 100% rename from public/src/forum/account/followers.js rename to public/src/client/account/followers.js diff --git a/public/src/forum/account/following.js b/public/src/client/account/following.js similarity index 100% rename from public/src/forum/account/following.js rename to public/src/client/account/following.js diff --git a/public/src/forum/account/header.js b/public/src/client/account/header.js similarity index 100% rename from public/src/forum/account/header.js rename to public/src/client/account/header.js diff --git a/public/src/forum/account/posts.js b/public/src/client/account/posts.js similarity index 100% rename from public/src/forum/account/posts.js rename to public/src/client/account/posts.js diff --git a/public/src/forum/account/profile.js b/public/src/client/account/profile.js similarity index 100% rename from public/src/forum/account/profile.js rename to public/src/client/account/profile.js diff --git a/public/src/forum/account/settings.js b/public/src/client/account/settings.js similarity index 100% rename from public/src/forum/account/settings.js rename to public/src/client/account/settings.js diff --git a/public/src/forum/account/topics.js b/public/src/client/account/topics.js similarity index 100% rename from public/src/forum/account/topics.js rename to public/src/client/account/topics.js diff --git a/public/src/forum/category.js b/public/src/client/category.js similarity index 94% rename from public/src/forum/category.js rename to public/src/client/category.js index 30bcf629f1..a40781e642 100644 --- a/public/src/forum/category.js +++ b/public/src/client/category.js @@ -73,7 +73,7 @@ define('forum/category', ['composer', 'forum/pagination', 'forum/infinitescroll' }; Category.toBottom = function() { - socket.emit('categories.lastTopicIndex', ajaxify.variables.get('category_id'), function(err, index) { + socket.emit('categories.getTopicCount', ajaxify.variables.get('category_id'), function(err, index) { navigator.scrollBottom(index); }); }; @@ -181,12 +181,10 @@ define('forum/category', ['composer', 'forum/pagination', 'forum/infinitescroll' Category.onNewTopic = function(topic) { $(window).trigger('filter:categories.new_topic', topic); - ajaxify.loadTemplate('category', function(categoryTemplate) { - var html = templates.parse(templates.getBlock(categoryTemplate, 'topics'), { - privileges: {editable: !!$('.thread-tools').length}, - topics: [topic] - }); - + templates.parse('category', 'topics', { + privileges: {editable: !!$('.thread-tools').length}, + topics: [topic] + }, function(html) { translator.translate(html, function(translatedHTML) { var topic = $(translatedHTML), container = $('#topics-container'), @@ -276,9 +274,7 @@ define('forum/category', ['composer', 'forum/pagination', 'forum/infinitescroll' findInsertionPoint(); - ajaxify.loadTemplate('category', function(categoryTemplate) { - var html = templates.parse(templates.getBlock(categoryTemplate, 'topics'), data); - + templates.parse('category', 'topics', data, function(html) { translator.translate(html, function(translatedHTML) { var container = $('#topics-container'), html = $(translatedHTML); @@ -348,4 +344,4 @@ define('forum/category', ['composer', 'forum/pagination', 'forum/infinitescroll' } return Category; -}); +}); \ No newline at end of file diff --git a/public/src/forum/categoryTools.js b/public/src/client/categoryTools.js similarity index 98% rename from public/src/forum/categoryTools.js rename to public/src/client/categoryTools.js index f40fb26b2f..620b3af8b0 100644 --- a/public/src/forum/categoryTools.js +++ b/public/src/client/categoryTools.js @@ -132,7 +132,7 @@ define('forum/categoryTools', ['forum/topic/move', 'topicSelect'], function(move $('.delete_thread span').translateHtml(' [[topic:thread_tools.' + (isAnyDeleted ? 'restore' : 'delete') + ']]'); $('.pin_thread').translateHtml(' [[topic:thread_tools.' + (isAnyPinned ? 'unpin' : 'pin') + ']]'); $('.lock_thread').translateHtml(' [[topic:thread_tools.' + (isAnyLocked ? 'un': '') + 'lock]]'); - $('.purge_thread').toggleClass('none', !areAllDeleted); + $('.purge_thread').toggleClass('hidden', !areAllDeleted); } function isAny(method, tids) { diff --git a/public/src/forum/chats.js b/public/src/client/chats.js similarity index 100% rename from public/src/forum/chats.js rename to public/src/client/chats.js diff --git a/public/src/forum/footer.js b/public/src/client/footer.js similarity index 100% rename from public/src/forum/footer.js rename to public/src/client/footer.js diff --git a/public/src/forum/groups/details.js b/public/src/client/groups/details.js similarity index 100% rename from public/src/forum/groups/details.js rename to public/src/client/groups/details.js diff --git a/public/src/forum/home.js b/public/src/client/home.js similarity index 91% rename from public/src/forum/home.js rename to public/src/client/home.js index c770aceb36..013c351176 100644 --- a/public/src/forum/home.js +++ b/public/src/client/home.js @@ -55,13 +55,13 @@ define('forum/home', function() { if (categoryBox.find('.post-preview').length > parseInt(numRecentReplies, 10)) { recentPosts.last().remove(); } + + $(window).trigger('action:posts.loaded'); }); } function parseAndTranslate(posts, callback) { - ajaxify.loadTemplate('home', function(homeTemplate) { - var html = templates.parse(templates.getBlock(homeTemplate, 'posts'), {categories: {posts: posts}}); - + templates.parse('home', 'posts', {categories: {posts: posts}}, function(html) { translator.translate(html, function(translatedHTML) { translatedHTML = $(translatedHTML); translatedHTML.find('img').addClass('img-responsive'); diff --git a/public/src/forum/infinitescroll.js b/public/src/client/infinitescroll.js similarity index 93% rename from public/src/forum/infinitescroll.js rename to public/src/client/infinitescroll.js index 6f527f1fa3..e600d16fbe 100644 --- a/public/src/forum/infinitescroll.js +++ b/public/src/client/infinitescroll.js @@ -51,9 +51,7 @@ define('forum/infinitescroll', function() { }; scroll.parseAndTranslate = function(template, blockName, data, callback) { - ajaxify.loadTemplate(template, function(templateHtml) { - var html = templates.parse(templates.getBlock(templateHtml, blockName), data); - + templates.parse(template, blockName, data, function(html) { translator.translate(html, function(translatedHTML) { callback($(translatedHTML)); }); diff --git a/public/src/forum/login.js b/public/src/client/login.js similarity index 71% rename from public/src/forum/login.js rename to public/src/client/login.js index c4600c36ac..e7079ddfed 100644 --- a/public/src/forum/login.js +++ b/public/src/client/login.js @@ -33,6 +33,16 @@ define('forum/login', function() { }); $('#content #username').focus(); + + // Add "returnTo" data if present + if (app.previousUrl) { + var returnToEl = document.createElement('input'); + returnToEl.type = 'hidden'; + returnToEl.name = 'returnTo'; + returnToEl.value = app.previousUrl.replace(window.location.origin + RELATIVE_PATH, ''); + $(returnToEl).appendTo(formEl); + console.log('appended'); + } }; return Login; diff --git a/public/src/forum/notifications.js b/public/src/client/notifications.js similarity index 100% rename from public/src/forum/notifications.js rename to public/src/client/notifications.js diff --git a/public/src/forum/pagination.js b/public/src/client/pagination.js similarity index 100% rename from public/src/forum/pagination.js rename to public/src/client/pagination.js diff --git a/public/src/forum/popular.js b/public/src/client/popular.js similarity index 100% rename from public/src/forum/popular.js rename to public/src/client/popular.js diff --git a/public/src/forum/recent.js b/public/src/client/recent.js similarity index 100% rename from public/src/forum/recent.js rename to public/src/client/recent.js diff --git a/public/src/forum/register.js b/public/src/client/register.js similarity index 100% rename from public/src/forum/register.js rename to public/src/client/register.js diff --git a/public/src/forum/reset.js b/public/src/client/reset.js similarity index 100% rename from public/src/forum/reset.js rename to public/src/client/reset.js diff --git a/public/src/forum/reset_code.js b/public/src/client/reset_code.js similarity index 100% rename from public/src/forum/reset_code.js rename to public/src/client/reset_code.js diff --git a/public/src/forum/search.js b/public/src/client/search.js similarity index 100% rename from public/src/forum/search.js rename to public/src/client/search.js diff --git a/public/src/forum/tag.js b/public/src/client/tag.js similarity index 100% rename from public/src/forum/tag.js rename to public/src/client/tag.js diff --git a/public/src/forum/tags.js b/public/src/client/tags.js similarity index 96% rename from public/src/forum/tags.js rename to public/src/client/tags.js index bf4395f497..e5c2dcaa45 100644 --- a/public/src/forum/tags.js +++ b/public/src/client/tags.js @@ -14,6 +14,11 @@ define('forum/tags', ['forum/infinitescroll'], function(infinitescroll) { clearTimeout(timeoutId); timeoutId = 0; } + + if (!$('#tag-search').val().length) { + return; + } + timeoutId = setTimeout(function() { socket.emit('topics.searchAndLoadTags', {query: $('#tag-search').val()}, function(err, results) { if (err) { diff --git a/public/src/forum/topic.js b/public/src/client/topic.js similarity index 93% rename from public/src/forum/topic.js rename to public/src/client/topic.js index bcfdc0187c..8890bf1097 100644 --- a/public/src/forum/topic.js +++ b/public/src/client/topic.js @@ -84,7 +84,7 @@ define('forum/topic', dependencies, function(pagination, infinitescroll, threadT if (config.topicPostSort !== 'oldest_to_newest') { postCount = 1; } - navigator.scrollBottom(postCount); + navigator.scrollBottom(postCount - 1); }); }; @@ -149,7 +149,7 @@ define('forum/topic', dependencies, function(pagination, infinitescroll, threadT var postcount = $('.user_postcount_' + data.posts[i].uid); postcount.html(parseInt(postcount.html(), 10) + 1); } - socket.emit('topics.markAsRead', [tid]); + createNewPosts(data); } @@ -195,7 +195,6 @@ define('forum/topic', dependencies, function(pagination, infinitescroll, threadT }); } - function updateTopicTitle() { if($(window).scrollTop() > 50) { $('.header-topic-title').find('span').text(ajaxify.variables.get('topic_name')).show(); @@ -239,7 +238,7 @@ define('forum/topic', dependencies, function(pagination, infinitescroll, threadT slug = parts[2]; var newUrl = 'topic/' + topicId + '/' + (slug ? slug : ''); if (postIndex > 0) { - newUrl += '/' + (postIndex + 1); + newUrl += '/' + (postIndex + 1); } if (newUrl !== currentUrl) { @@ -337,7 +336,7 @@ define('forum/topic', dependencies, function(pagination, infinitescroll, threadT var height = $(document).height(), scrollTop = $(document).scrollTop(), originalPostEl = $('li[data-index="0"]'); - + // Insert the new post html.insertBefore(before); @@ -361,18 +360,21 @@ define('forum/topic', dependencies, function(pagination, infinitescroll, threadT } function onNewPostsLoaded(html, posts) { - function getPostPrivileges(pid) { - socket.emit('posts.getPrivileges', pid, function(err, privileges) { - if(err) { - return app.alertError(err.message); - } - toggleModTools(html, privileges); - }); + + var pids = []; + for(var i=0; i ' + translated : translated); }); - threadEl.find('.quote, .edit, .delete').toggleClass('none', isLocked); + threadEl.find('.quote, .edit, .delete').toggleClass('hidden', isLocked); $('.topic-title i.fa-lock').toggleClass('hide', !data.isLocked); ThreadTools.threadState.locked = data.isLocked; } @@ -125,7 +125,7 @@ define('forum/topic/threadTools', ['forum/topic/fork', 'forum/topic/move'], func threadEl.toggleClass('deleted', data.isDelete); ThreadTools.threadState.deleted = data.isDelete; - $('.purge_thread').toggleClass('none', !data.isDelete); + $('.purge_thread').toggleClass('hidden', !data.isDelete); if (data.isDelete) { translator.translate('[[topic:deleted_message]]', function(translated) { diff --git a/public/src/forum/unread.js b/public/src/client/unread.js similarity index 100% rename from public/src/forum/unread.js rename to public/src/client/unread.js diff --git a/public/src/forum/users.js b/public/src/client/users.js similarity index 92% rename from public/src/forum/users.js rename to public/src/client/users.js index 77a56c7b51..a1e852bb9f 100644 --- a/public/src/forum/users.js +++ b/public/src/client/users.js @@ -76,9 +76,7 @@ define('forum/users', function() { return !$('.users-box[data-uid="' + user.uid + '"]').length; }); - ajaxify.loadTemplate('users', function(usersTemplate) { - var html = templates.parse(templates.getBlock(usersTemplate, 'users'), {users: users}); - + templates.parse('users', 'users', {users: users}, function(html) { translator.translate(html, function(translated) { $('#users-container').append(translated); $('#users-container .anon-user').appendTo($('#users-container')); @@ -128,13 +126,12 @@ define('forum/users', function() { return; } - ajaxify.loadTemplate('users', function(usersTemplate) { - var html = templates.parse(templates.getBlock(usersTemplate, 'users'), data); - + templates.parse('users', 'users', data, function(html) { translator.translate(html, function(translated) { $('#users-container').html(translated); + if (!data.users.length) { - translator.translate('[[users:user-not-found]]', function(translated) { + translator.translate('[[error:no-user]]', function(translated) { notify.html(translated); notify.parent().addClass('btn-warning label-warning'); }); diff --git a/public/src/forum/admin/footer.js b/public/src/forum/admin/footer.js deleted file mode 100644 index 81dca5ce2f..0000000000 --- a/public/src/forum/admin/footer.js +++ /dev/null @@ -1,156 +0,0 @@ -"use strict"; -/*global define, app, socket, Hammer, RELATIVE_PATH */ - -define('forum/admin/footer', ['forum/admin/settings'], function(Settings) { - var acpIndex; - - $(document).ready(function() { - if(!/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) { - getSearchIndex(); - } else { - activateMobile(); - } - - $(window).on('action:ajaxify.end', function(ev, data) { - var url = data.url; - - selectMenuItem(data.url); - }); - - setupMainMenu(); - }); - - function activateMobile() { - $('.admin').addClass('mobile'); - $('#main-menu').addClass('transitioning'); - - Hammer(document.body).on('swiperight', function(e) { - $('#main-menu').addClass('open'); - }); - - Hammer(document.body).on('swipeleft', function(e) { - $('#main-menu').removeClass('open'); - }); - - Hammer($('#main-menu')[0]).on('swiperight', function(e) { - $('#main-menu').addClass('open'); - }); - - Hammer($('#main-menu')[0]).on('swipeleft', function(e) { - $('#main-menu').removeClass('open'); - }); - - $(window).on('scroll', function() { - $('#main-menu').height($(window).height() + 20); - }); - } - - function setupMainMenu() { - $('.sidebar-nav .nav-header').on('click', function() { - $(this).parents('.sidebar-nav').toggleClass('open'); - setTimeout(function() { - $('.nano').nanoScroller(); - }, 500); // replace with animationend event - }); - - $('.nano').nanoScroller(); - - $('#main-menu .nav-list > li a').append(' '); - } - - function selectMenuItem(url) { - $('#main-menu .nav-list > li').removeClass('active').each(function() { - var menu = $(this), - category = menu.parents('.sidebar-nav'), - href = menu.children('a').attr('href'); - - if (href && href.slice(1).indexOf(url) !== -1) { - category.addClass('open'); - menu.addClass('active'); - modifyBreadcrumb(category.find('.nav-header').text(), menu.text()); - return false; - } - }); - } - - function modifyBreadcrumb() { - var caret = ' '; - - $('#breadcrumbs').html(caret + Array.prototype.slice.call(arguments).join(caret)); - } - - function getSearchIndex() { - $.getJSON(RELATIVE_PATH + '/templates/indexed.json', function (data) { - acpIndex = data; - for (var file in acpIndex) { - if (acpIndex.hasOwnProperty(file)) { - acpIndex[file] = acpIndex[file].replace(/' + acpIndex[file] + '
    '); - acpIndex[file].find('ul.nav, script').remove(); - - acpIndex[file] = acpIndex[file].text().toLowerCase().replace(/[ |\r|\n]+/g, ' '); - } - } - - delete acpIndex['/admin/header.tpl']; - delete acpIndex['/admin/footer.tpl']; - - setupACPSearch(); - }); - } - - function setupACPSearch() { - var menu = $('#acp-search .dropdown-menu'); - - $('#acp-search input').on('keyup', function() { - $('#acp-search .dropdown').addClass('open'); - }); - - $('#acp-search input').on('keyup focus', function() { - var $input = $(this), - value = $input.val().toLowerCase(), - menuItems = $('#acp-search .dropdown-menu').html(''); - - function toUpperCase(txt){ - return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); - } - - if (value.length >= 3) { - for (var file in acpIndex) { - if (acpIndex.hasOwnProperty(file)) { - var position = acpIndex[file].indexOf(value); - - if (position !== -1) { - var href = file.replace('.tpl', ''), - title = href.replace(/^\/admin\//, '').split('/'), - description = acpIndex[file].substring(Math.max(0, position - 25), Math.min(acpIndex[file].length - 1, position + 25)) - .replace(value, '' + value + ''); - - for (var t in title) { - if (title.hasOwnProperty(t)) { - title[t] = title[t] - .replace('-', ' ') - .replace(/\w\S*/g, toUpperCase); - } - } - - title = title.join(' > '); - - menuItems.append('
  • ' + title + '
    ...' + description + '...
  • '); - } - } - } - - if (menuItems.html() !== '') { - menuItems.append(''); - } - } - - if (value.length > 0) { - menuItems.append('
  • Search the forum for ' + value + '
  • '); - } else { - menuItems.append('
  • Start typing to see results...
  • '); - } - }); - } -}); \ No newline at end of file diff --git a/public/src/forum/admin/general/languages.js b/public/src/forum/admin/general/languages.js deleted file mode 100644 index 0d3b4ddf6e..0000000000 --- a/public/src/forum/admin/general/languages.js +++ /dev/null @@ -1,8 +0,0 @@ -"use strict"; -/*global define*/ - -define('forum/admin/general/languages', ['forum/admin/settings'], function(Settings) { - $(function() { - Settings.prepare(); - }); -}); diff --git a/public/src/modules/admin.js b/public/src/modules/admin.js deleted file mode 100644 index 8b447942be..0000000000 --- a/public/src/modules/admin.js +++ /dev/null @@ -1,19 +0,0 @@ -define('admin', ['mousetrap'], function(Mousetrap) { - var Admin= {}; - - Admin.init = function() { - if (app.isAdmin) { - Mousetrap.bind('ctrl+shift+a r', function() { - console.log('[admin] Reloading NodeBB...'); - socket.emit('admin.reload'); - }); - - Mousetrap.bind('ctrl+shift+a R', function() { - console.log('[admin] Restarting NodeBB...'); - socket.emit('admin.restart'); - }); - } - }; - - return Admin; -}); \ No newline at end of file diff --git a/public/src/modules/alerts.js b/public/src/modules/alerts.js index ccbec68d0a..4db3a1d9ab 100644 --- a/public/src/modules/alerts.js +++ b/public/src/modules/alerts.js @@ -7,7 +7,7 @@ define('alerts', function() { module.alert = function (params) { params.alert_id = 'alert_button_' + (params.alert_id ? params.alert_id : new Date().getTime()); params.title = params.title ? params.title.trim() || '' : ''; - params.message = params.message.trim(); + params.message = params.message ? params.message.trim() : ''; params.location = params.location || 'right-top'; params.type = params.type || 'info'; diff --git a/public/src/modules/chat.js b/public/src/modules/chat.js index 9e24641e5a..8922e90df4 100644 --- a/public/src/modules/chat.js +++ b/public/src/modules/chat.js @@ -121,12 +121,16 @@ define('chat', ['taskbar', 'string', 'sounds', 'forum/chats'], function(taskbar, module.bringModalToTop = function(chatModal) { var topZ = 0; + if ($('.chat-modal').length === 1) { + return; + } $('.chat-modal').each(function() { var thisZ = parseInt($(this).css('zIndex'), 10); if (thisZ > topZ) { topZ = thisZ; } }); + chatModal.css('zIndex', topZ + 1); taskbar.updateActive(chatModal.attr('UUID')); }; @@ -168,7 +172,8 @@ define('chat', ['taskbar', 'string', 'sounds', 'forum/chats'], function(taskbar, chatModal.attr('touid', touid); chatModal.attr('intervalId', 0); chatModal.attr('UUID', uuid); - chatModal.css("position", "fixed"); + chatModal.css('position', 'fixed'); + chatModal.css('zIndex', 100); chatModal.appendTo($('body')); chatModal.draggable({ start:function() { @@ -265,7 +270,6 @@ define('chat', ['taskbar', 'string', 'sounds', 'forum/chats'], function(taskbar, module.center = function(chatModal) { chatModal.css("left", Math.max(0, (($(window).width() - $(chatModal).outerWidth()) / 2) + $(window).scrollLeft()) + "px"); chatModal.css("top", Math.max(0, $(window).height() / 2 - $(chatModal).outerHeight() / 2) + "px"); - chatModal.css("zIndex", 100); chatModal.find('#chat-message-input').focus(); return chatModal; }; diff --git a/public/src/modules/composer/categoryList.js b/public/src/modules/composer/categoryList.js index 776e435bc9..30ece1875a 100644 --- a/public/src/modules/composer/categoryList.js +++ b/public/src/modules/composer/categoryList.js @@ -17,6 +17,11 @@ define('composer/categoryList', function() { return app.alertError(err.message); } + // Remove categories that are just external links + categories = categories.filter(function(category) { + return !category.link + }); + categories.forEach(function(category) { $('').appendTo(listEl); }); diff --git a/public/src/modules/navigator.js b/public/src/modules/navigator.js index 0d62b52b00..5528b78b51 100644 --- a/public/src/modules/navigator.js +++ b/public/src/modules/navigator.js @@ -112,13 +112,13 @@ define('navigator', ['forum/pagination'], function(pagination) { navigator.scrollUp = function () { $('body,html').animate({ - scrollTop: $('body').scrollTop() - $(window).height() + scrollTop: $(window).scrollTop() - $(window).height() }); }; navigator.scrollDown = function () { $('body,html').animate({ - scrollTop: $('body').scrollTop() + $(window).height() + scrollTop: $(window).scrollTop() + $(window).height() }); }; @@ -131,6 +131,9 @@ define('navigator', ['forum/pagination'], function(pagination) { }; navigator.scrollBottom = function(index) { + if (parseInt(index, 10) < 0) { + return; + } if ($('li[data-index="' + index + '"]').length) { navigator.scrollToPost(index, true); } else { @@ -178,6 +181,8 @@ define('navigator', ['forum/pagination'], function(pagination) { } } else { navigator.scrollActive = false; + postIndex = parseInt(postIndex, 10) + 1; + ajaxify.go(generateUrl(postIndex)); } }; diff --git a/public/src/modules/settings.js b/public/src/modules/settings.js index 78ac0ad044..b31975c1ab 100644 --- a/public/src/modules/settings.js +++ b/public/src/modules/settings.js @@ -298,6 +298,7 @@ define('settings', function () { app.alert({ title: 'Settings Saved', type: 'success', + message: "Settings have been successfully saved", timeout: 2500 }); } diff --git a/public/src/widgets.js b/public/src/widgets.js index 32b01f599f..9e34ba9513 100644 --- a/public/src/widgets.js +++ b/public/src/widgets.js @@ -1,5 +1,5 @@ "use strict"; -/*global ajaxify, templates*/ +/*global ajaxify, templates, config, RELATIVE_PATH*/ (function(ajaxify) { ajaxify.widgets = {}; @@ -33,7 +33,7 @@ function renderWidgets(locations) { var areaDatas = []; - $.get(RELATIVE_PATH + '/api/widgets/render', { + $.get(RELATIVE_PATH + '/api/widgets/render' + (config['cache-buster'] ? '?v=' + config['cache-buster'] : ''), { locations: locations, template: template + '.tpl', url: url diff --git a/public/uploads/.gitignore b/public/uploads/.gitignore deleted file mode 100644 index 72e8ffc0db..0000000000 --- a/public/uploads/.gitignore +++ /dev/null @@ -1 +0,0 @@ -* diff --git a/public/uploads/category/.gitignore b/public/uploads/category/.gitignore new file mode 100644 index 0000000000..e69de29bb2 diff --git a/public/uploads/files/.gitignore b/public/uploads/files/.gitignore new file mode 100644 index 0000000000..e69de29bb2 diff --git a/public/uploads/profile/.gitignore b/public/uploads/profile/.gitignore new file mode 100644 index 0000000000..e69de29bb2 diff --git a/public/vendor/jquery/event/jquery.event.drag.js b/public/vendor/jquery/event/jquery.event.drag.js new file mode 100644 index 0000000000..969ff6b0fe --- /dev/null +++ b/public/vendor/jquery/event/jquery.event.drag.js @@ -0,0 +1,402 @@ +/*! + * jquery.event.drag - v 2.2 + * Copyright (c) 2010 Three Dub Media - http://threedubmedia.com + * Open Source MIT License - http://threedubmedia.com/code/license + */ +// Created: 2008-06-04 +// Updated: 2012-05-21 +// REQUIRES: jquery 1.7.x + +;(function( $ ){ + +// add the jquery instance method +$.fn.drag = function( str, arg, opts ){ + // figure out the event type + var type = typeof str == "string" ? str : "", + // figure out the event handler... + fn = $.isFunction( str ) ? str : $.isFunction( arg ) ? arg : null; + // fix the event type + if ( type.indexOf("drag") !== 0 ) + type = "drag"+ type; + // were options passed + opts = ( str == fn ? arg : opts ) || {}; + // trigger or bind event handler + return fn ? this.bind( type, opts, fn ) : this.trigger( type ); +}; + +// local refs (increase compression) +var $event = $.event, +$special = $event.special, +// configure the drag special event +drag = $special.drag = { + + // these are the default settings + defaults: { + which: 1, // mouse button pressed to start drag sequence + distance: 0, // distance dragged before dragstart + not: ':input', // selector to suppress dragging on target elements + handle: null, // selector to match handle target elements + relative: false, // true to use "position", false to use "offset" + drop: true, // false to suppress drop events, true or selector to allow + click: false // false to suppress click events after dragend (no proxy) + }, + + // the key name for stored drag data + datakey: "dragdata", + + // prevent bubbling for better performance + noBubble: true, + + // count bound related events + add: function( obj ){ + // read the interaction data + var data = $.data( this, drag.datakey ), + // read any passed options + opts = obj.data || {}; + // count another realted event + data.related += 1; + // extend data options bound with this event + // don't iterate "opts" in case it is a node + $.each( drag.defaults, function( key, def ){ + if ( opts[ key ] !== undefined ) + data[ key ] = opts[ key ]; + }); + }, + + // forget unbound related events + remove: function(){ + $.data( this, drag.datakey ).related -= 1; + }, + + // configure interaction, capture settings + setup: function(){ + // check for related events + if ( $.data( this, drag.datakey ) ) + return; + // initialize the drag data with copied defaults + var data = $.extend({ related:0 }, drag.defaults ); + // store the interaction data + $.data( this, drag.datakey, data ); + // bind the mousedown event, which starts drag interactions + $event.add( this, "touchstart mousedown", drag.init, data ); + // prevent image dragging in IE... + if ( this.attachEvent ) + this.attachEvent("ondragstart", drag.dontstart ); + }, + + // destroy configured interaction + teardown: function(){ + var data = $.data( this, drag.datakey ) || {}; + // check for related events + if ( data.related ) + return; + // remove the stored data + $.removeData( this, drag.datakey ); + // remove the mousedown event + $event.remove( this, "touchstart mousedown", drag.init ); + // enable text selection + drag.textselect( true ); + // un-prevent image dragging in IE... + if ( this.detachEvent ) + this.detachEvent("ondragstart", drag.dontstart ); + }, + + // initialize the interaction + init: function( event ){ + // sorry, only one touch at a time + if ( drag.touched ) + return; + // the drag/drop interaction data + var dd = event.data, results; + // check the which directive + if ( event.which != 0 && dd.which > 0 && event.which != dd.which ) + return; + // check for suppressed selector + if ( $( event.target ).is( dd.not ) ) + return; + // check for handle selector + if ( dd.handle && !$( event.target ).closest( dd.handle, event.currentTarget ).length ) + return; + + drag.touched = event.type == 'touchstart' ? this : null; + dd.propagates = 1; + dd.mousedown = this; + dd.interactions = [ drag.interaction( this, dd ) ]; + dd.target = event.target; + dd.pageX = event.pageX; + dd.pageY = event.pageY; + dd.dragging = null; + // handle draginit event... + results = drag.hijack( event, "draginit", dd ); + // early cancel + if ( !dd.propagates ) + return; + // flatten the result set + results = drag.flatten( results ); + // insert new interaction elements + if ( results && results.length ){ + dd.interactions = []; + $.each( results, function(){ + dd.interactions.push( drag.interaction( this, dd ) ); + }); + } + // remember how many interactions are propagating + dd.propagates = dd.interactions.length; + // locate and init the drop targets + if ( dd.drop !== false && $special.drop ) + $special.drop.handler( event, dd ); + // disable text selection + drag.textselect( false ); + // bind additional events... + if ( drag.touched ) + $event.add( drag.touched, "touchmove touchend", drag.handler, dd ); + else + $event.add( document, "mousemove mouseup", drag.handler, dd ); + // helps prevent text selection or scrolling + if ( !drag.touched || dd.live ) + return false; + }, + + // returns an interaction object + interaction: function( elem, dd ){ + var offset = $( elem )[ dd.relative ? "position" : "offset" ]() || { top:0, left:0 }; + return { + drag: elem, + callback: new drag.callback(), + droppable: [], + offset: offset + }; + }, + + // handle drag-releatd DOM events + handler: function( event ){ + // read the data before hijacking anything + var dd = event.data; + // handle various events + switch ( event.type ){ + // mousemove, check distance, start dragging + case !dd.dragging && 'touchmove': + event.preventDefault(); + case !dd.dragging && 'mousemove': + // drag tolerance, x² + y² = distance² + if ( Math.pow( event.pageX-dd.pageX, 2 ) + Math.pow( event.pageY-dd.pageY, 2 ) < Math.pow( dd.distance, 2 ) ) + break; // distance tolerance not reached + event.target = dd.target; // force target from "mousedown" event (fix distance issue) + drag.hijack( event, "dragstart", dd ); // trigger "dragstart" + if ( dd.propagates ) // "dragstart" not rejected + dd.dragging = true; // activate interaction + // mousemove, dragging + case 'touchmove': + event.preventDefault(); + case 'mousemove': + if ( dd.dragging ){ + // trigger "drag" + drag.hijack( event, "drag", dd ); + if ( dd.propagates ){ + // manage drop events + if ( dd.drop !== false && $special.drop ) + $special.drop.handler( event, dd ); // "dropstart", "dropend" + break; // "drag" not rejected, stop + } + event.type = "mouseup"; // helps "drop" handler behave + } + // mouseup, stop dragging + case 'touchend': + case 'mouseup': + default: + if ( drag.touched ) + $event.remove( drag.touched, "touchmove touchend", drag.handler ); // remove touch events + else + $event.remove( document, "mousemove mouseup", drag.handler ); // remove page events + if ( dd.dragging ){ + if ( dd.drop !== false && $special.drop ) + $special.drop.handler( event, dd ); // "drop" + drag.hijack( event, "dragend", dd ); // trigger "dragend" + } + drag.textselect( true ); // enable text selection + // if suppressing click events... + if ( dd.click === false && dd.dragging ) + $.data( dd.mousedown, "suppress.click", new Date().getTime() + 5 ); + dd.dragging = drag.touched = false; // deactivate element + break; + } + }, + + // re-use event object for custom events + hijack: function( event, type, dd, x, elem ){ + // not configured + if ( !dd ) + return; + // remember the original event and type + var orig = { event:event.originalEvent, type:event.type }, + // is the event drag related or drog related? + mode = type.indexOf("drop") ? "drag" : "drop", + // iteration vars + result, i = x || 0, ia, $elems, callback, + len = !isNaN( x ) ? x : dd.interactions.length; + // modify the event type + event.type = type; + // remove the original event + event.originalEvent = null; + // initialize the results + dd.results = []; + // handle each interacted element + do if ( ia = dd.interactions[ i ] ){ + // validate the interaction + if ( type !== "dragend" && ia.cancelled ) + continue; + // set the dragdrop properties on the event object + callback = drag.properties( event, dd, ia ); + // prepare for more results + ia.results = []; + // handle each element + $( elem || ia[ mode ] || dd.droppable ).each(function( p, subject ){ + // identify drag or drop targets individually + callback.target = subject; + // force propagtion of the custom event + event.isPropagationStopped = function(){ return false; }; + // handle the event + result = subject ? $event.dispatch.call( subject, event, callback ) : null; + // stop the drag interaction for this element + if ( result === false ){ + if ( mode == "drag" ){ + ia.cancelled = true; + dd.propagates -= 1; + } + if ( type == "drop" ){ + ia[ mode ][p] = null; + } + } + // assign any dropinit elements + else if ( type == "dropinit" ) + ia.droppable.push( drag.element( result ) || subject ); + // accept a returned proxy element + if ( type == "dragstart" ) + ia.proxy = $( drag.element( result ) || ia.drag )[0]; + // remember this result + ia.results.push( result ); + // forget the event result, for recycling + delete event.result; + // break on cancelled handler + if ( type !== "dropinit" ) + return result; + }); + // flatten the results + dd.results[ i ] = drag.flatten( ia.results ); + // accept a set of valid drop targets + if ( type == "dropinit" ) + ia.droppable = drag.flatten( ia.droppable ); + // locate drop targets + if ( type == "dragstart" && !ia.cancelled ) + callback.update(); + } + while ( ++i < len ) + // restore the original event & type + event.type = orig.type; + event.originalEvent = orig.event; + // return all handler results + return drag.flatten( dd.results ); + }, + + // extend the callback object with drag/drop properties... + properties: function( event, dd, ia ){ + var obj = ia.callback; + // elements + obj.drag = ia.drag; + obj.proxy = ia.proxy || ia.drag; + // starting mouse position + obj.startX = dd.pageX; + obj.startY = dd.pageY; + // current distance dragged + obj.deltaX = event.pageX - dd.pageX; + obj.deltaY = event.pageY - dd.pageY; + // original element position + obj.originalX = ia.offset.left; + obj.originalY = ia.offset.top; + // adjusted element position + obj.offsetX = obj.originalX + obj.deltaX; + obj.offsetY = obj.originalY + obj.deltaY; + // assign the drop targets information + obj.drop = drag.flatten( ( ia.drop || [] ).slice() ); + obj.available = drag.flatten( ( ia.droppable || [] ).slice() ); + return obj; + }, + + // determine is the argument is an element or jquery instance + element: function( arg ){ + if ( arg && ( arg.jquery || arg.nodeType == 1 ) ) + return arg; + }, + + // flatten nested jquery objects and arrays into a single dimension array + flatten: function( arr ){ + return $.map( arr, function( member ){ + return member && member.jquery ? $.makeArray( member ) : + member && member.length ? drag.flatten( member ) : member; + }); + }, + + // toggles text selection attributes ON (true) or OFF (false) + textselect: function( bool ){ + $( document )[ bool ? "unbind" : "bind" ]("selectstart", drag.dontstart ) + .css("MozUserSelect", bool ? "" : "none" ); + // .attr("unselectable", bool ? "off" : "on" ) + document.unselectable = bool ? "off" : "on"; + }, + + // suppress "selectstart" and "ondragstart" events + dontstart: function(){ + return false; + }, + + // a callback instance contructor + callback: function(){} + +}; + +// callback methods +drag.callback.prototype = { + update: function(){ + if ( $special.drop && this.available.length ) + $.each( this.available, function( i ){ + $special.drop.locate( this, i ); + }); + } +}; + +// patch $.event.$dispatch to allow suppressing clicks +var $dispatch = $event.dispatch; +$event.dispatch = function( event ){ + if ( $.data( this, "suppress."+ event.type ) - new Date().getTime() > 0 ){ + $.removeData( this, "suppress."+ event.type ); + return; + } + return $dispatch.apply( this, arguments ); +}; + +// event fix hooks for touch events... +var touchHooks = +$event.fixHooks.touchstart = +$event.fixHooks.touchmove = +$event.fixHooks.touchend = +$event.fixHooks.touchcancel = { + props: "clientX clientY pageX pageY screenX screenY".split( " " ), + filter: function( event, orig ) { + if ( orig ){ + var touched = ( orig.touches && orig.touches[0] ) + || ( orig.changedTouches && orig.changedTouches[0] ) + || null; + // iOS webkit: touchstart, touchmove, touchend + if ( touched ) + $.each( touchHooks.props, function( i, prop ){ + event[ prop ] = touched[ prop ]; + }); + } + return event; + } +}; + +// share the same special event configuration with related events... +$special.draginit = $special.dragstart = $special.dragend = drag; + +})( jQuery ); \ No newline at end of file diff --git a/public/vendor/jquery/event/jquery.event.drop.js b/public/vendor/jquery/event/jquery.event.drop.js new file mode 100644 index 0000000000..7599ef91e7 --- /dev/null +++ b/public/vendor/jquery/event/jquery.event.drop.js @@ -0,0 +1,302 @@ +/*! + * jquery.event.drop - v 2.2 + * Copyright (c) 2010 Three Dub Media - http://threedubmedia.com + * Open Source MIT License - http://threedubmedia.com/code/license + */ +// Created: 2008-06-04 +// Updated: 2012-05-21 +// REQUIRES: jquery 1.7.x, event.drag 2.2 + +;(function($){ // secure $ jQuery alias + +// Events: drop, dropstart, dropend + +// add the jquery instance method +$.fn.drop = function( str, arg, opts ){ + // figure out the event type + var type = typeof str == "string" ? str : "", + // figure out the event handler... + fn = $.isFunction( str ) ? str : $.isFunction( arg ) ? arg : null; + // fix the event type + if ( type.indexOf("drop") !== 0 ) + type = "drop"+ type; + // were options passed + opts = ( str == fn ? arg : opts ) || {}; + // trigger or bind event handler + return fn ? this.bind( type, opts, fn ) : this.trigger( type ); +}; + +// DROP MANAGEMENT UTILITY +// returns filtered drop target elements, caches their positions +$.drop = function( opts ){ + opts = opts || {}; + // safely set new options... + drop.multi = opts.multi === true ? Infinity : + opts.multi === false ? 1 : !isNaN( opts.multi ) ? opts.multi : drop.multi; + drop.delay = opts.delay || drop.delay; + drop.tolerance = $.isFunction( opts.tolerance ) ? opts.tolerance : + opts.tolerance === null ? null : drop.tolerance; + drop.mode = opts.mode || drop.mode || 'intersect'; +}; + +// local refs (increase compression) +var $event = $.event, +$special = $event.special, +// configure the drop special event +drop = $.event.special.drop = { + + // these are the default settings + multi: 1, // allow multiple drop winners per dragged element + delay: 20, // async timeout delay + mode: 'overlap', // drop tolerance mode + + // internal cache + targets: [], + + // the key name for stored drop data + datakey: "dropdata", + + // prevent bubbling for better performance + noBubble: true, + + // count bound related events + add: function( obj ){ + // read the interaction data + var data = $.data( this, drop.datakey ); + // count another realted event + data.related += 1; + }, + + // forget unbound related events + remove: function(){ + $.data( this, drop.datakey ).related -= 1; + }, + + // configure the interactions + setup: function(){ + // check for related events + if ( $.data( this, drop.datakey ) ) + return; + // initialize the drop element data + var data = { + related: 0, + active: [], + anyactive: 0, + winner: 0, + location: {} + }; + // store the drop data on the element + $.data( this, drop.datakey, data ); + // store the drop target in internal cache + drop.targets.push( this ); + }, + + // destroy the configure interaction + teardown: function(){ + var data = $.data( this, drop.datakey ) || {}; + // check for related events + if ( data.related ) + return; + // remove the stored data + $.removeData( this, drop.datakey ); + // reference the targeted element + var element = this; + // remove from the internal cache + drop.targets = $.grep( drop.targets, function( target ){ + return ( target !== element ); + }); + }, + + // shared event handler + handler: function( event, dd ){ + // local vars + var results, $targets; + // make sure the right data is available + if ( !dd ) + return; + // handle various events + switch ( event.type ){ + // draginit, from $.event.special.drag + case 'mousedown': // DROPINIT >> + case 'touchstart': // DROPINIT >> + // collect and assign the drop targets + $targets = $( drop.targets ); + if ( typeof dd.drop == "string" ) + $targets = $targets.filter( dd.drop ); + // reset drop data winner properties + $targets.each(function(){ + var data = $.data( this, drop.datakey ); + data.active = []; + data.anyactive = 0; + data.winner = 0; + }); + // set available target elements + dd.droppable = $targets; + // activate drop targets for the initial element being dragged + $special.drag.hijack( event, "dropinit", dd ); + break; + // drag, from $.event.special.drag + case 'mousemove': // TOLERATE >> + case 'touchmove': // TOLERATE >> + drop.event = event; // store the mousemove event + if ( !drop.timer ) + // monitor drop targets + drop.tolerate( dd ); + break; + // dragend, from $.event.special.drag + case 'mouseup': // DROP >> DROPEND >> + case 'touchend': // DROP >> DROPEND >> + drop.timer = clearTimeout( drop.timer ); // delete timer + if ( dd.propagates ){ + $special.drag.hijack( event, "drop", dd ); + $special.drag.hijack( event, "dropend", dd ); + } + break; + + } + }, + + // returns the location positions of an element + locate: function( elem, index ){ + var data = $.data( elem, drop.datakey ), + $elem = $( elem ), + posi = $elem.offset() || {}, + height = $elem.outerHeight(), + width = $elem.outerWidth(), + location = { + elem: elem, + width: width, + height: height, + top: posi.top, + left: posi.left, + right: posi.left + width, + bottom: posi.top + height + }; + // drag elements might not have dropdata + if ( data ){ + data.location = location; + data.index = index; + data.elem = elem; + } + return location; + }, + + // test the location positions of an element against another OR an X,Y coord + contains: function( target, test ){ // target { location } contains test [x,y] or { location } + return ( ( test[0] || test.left ) >= target.left && ( test[0] || test.right ) <= target.right + && ( test[1] || test.top ) >= target.top && ( test[1] || test.bottom ) <= target.bottom ); + }, + + // stored tolerance modes + modes: { // fn scope: "$.event.special.drop" object + // target with mouse wins, else target with most overlap wins + 'intersect': function( event, proxy, target ){ + return this.contains( target, [ event.pageX, event.pageY ] ) ? // check cursor + 1e9 : this.modes.overlap.apply( this, arguments ); // check overlap + }, + // target with most overlap wins + 'overlap': function( event, proxy, target ){ + // calculate the area of overlap... + return Math.max( 0, Math.min( target.bottom, proxy.bottom ) - Math.max( target.top, proxy.top ) ) + * Math.max( 0, Math.min( target.right, proxy.right ) - Math.max( target.left, proxy.left ) ); + }, + // proxy is completely contained within target bounds + 'fit': function( event, proxy, target ){ + return this.contains( target, proxy ) ? 1 : 0; + }, + // center of the proxy is contained within target bounds + 'middle': function( event, proxy, target ){ + return this.contains( target, [ proxy.left + proxy.width * .5, proxy.top + proxy.height * .5 ] ) ? 1 : 0; + } + }, + + // sort drop target cache by by winner (dsc), then index (asc) + sort: function( a, b ){ + return ( b.winner - a.winner ) || ( a.index - b.index ); + }, + + // async, recursive tolerance execution + tolerate: function( dd ){ + // declare local refs + var i, drp, drg, data, arr, len, elem, + // interaction iteration variables + x = 0, ia, end = dd.interactions.length, + // determine the mouse coords + xy = [ drop.event.pageX, drop.event.pageY ], + // custom or stored tolerance fn + tolerance = drop.tolerance || drop.modes[ drop.mode ]; + // go through each passed interaction... + do if ( ia = dd.interactions[x] ){ + // check valid interaction + if ( !ia ) + return; + // initialize or clear the drop data + ia.drop = []; + // holds the drop elements + arr = []; + len = ia.droppable.length; + // determine the proxy location, if needed + if ( tolerance ) + drg = drop.locate( ia.proxy ); + // reset the loop + i = 0; + // loop each stored drop target + do if ( elem = ia.droppable[i] ){ + data = $.data( elem, drop.datakey ); + drp = data.location; + if ( !drp ) continue; + // find a winner: tolerance function is defined, call it + data.winner = tolerance ? tolerance.call( drop, drop.event, drg, drp ) + // mouse position is always the fallback + : drop.contains( drp, xy ) ? 1 : 0; + arr.push( data ); + } while ( ++i < len ); // loop + // sort the drop targets + arr.sort( drop.sort ); + // reset the loop + i = 0; + // loop through all of the targets again + do if ( data = arr[ i ] ){ + // winners... + if ( data.winner && ia.drop.length < drop.multi ){ + // new winner... dropstart + if ( !data.active[x] && !data.anyactive ){ + // check to make sure that this is not prevented + if ( $special.drag.hijack( drop.event, "dropstart", dd, x, data.elem )[0] !== false ){ + data.active[x] = 1; + data.anyactive += 1; + } + // if false, it is not a winner + else + data.winner = 0; + } + // if it is still a winner + if ( data.winner ) + ia.drop.push( data.elem ); + } + // losers... + else if ( data.active[x] && data.anyactive == 1 ){ + // former winner... dropend + $special.drag.hijack( drop.event, "dropend", dd, x, data.elem ); + data.active[x] = 0; + data.anyactive -= 1; + } + } while ( ++i < len ); // loop + } while ( ++x < end ) // loop + // check if the mouse is still moving or is idle + if ( drop.last && xy[0] == drop.last.pageX && xy[1] == drop.last.pageY ) + delete drop.timer; // idle, don't recurse + else // recurse + drop.timer = setTimeout(function(){ + drop.tolerate( dd ); + }, drop.delay ); + // remember event, to compare idleness + drop.last = drop.event; + } + +}; + +// share the same special event configuration with related events... +$special.dropinit = $special.dropstart = $special.dropend = drop; + +})(jQuery); // confine scope \ No newline at end of file diff --git a/public/vendor/visibility/visibility.min.js b/public/vendor/visibility/visibility.min.js new file mode 100644 index 0000000000..3733ccd08a --- /dev/null +++ b/public/vendor/visibility/visibility.min.js @@ -0,0 +1 @@ +!function(e){"use strict";var i=-1,t={onVisible:function(e){var i=t.isSupported();if(!i||!t.hidden())return e(),i;var n=t.change(function(){t.hidden()||(t.unbind(n),e())});return n},change:function(e){if(!t.isSupported())return!1;i+=1;var n=i;return t._callbacks[n]=e,t._listen(),n},unbind:function(e){delete t._callbacks[e]},afterPrerendering:function(e){var i=t.isSupported(),n="prerender";if(!i||n!=t.state())return e(),i;var r=t.change(function(i,d){n!=d&&(t.unbind(r),e())});return r},hidden:function(){return!(!t._doc.hidden&&!t._doc.webkitHidden)},state:function(){return t._doc.visibilityState||t._doc.webkitVisibilityState||"visible"},isSupported:function(){return!(!t._doc.visibilityState&&!t._doc.webkitVisibilityState)},_doc:document||{},_callbacks:{},_change:function(e){var i=t.state();for(var n in t._callbacks)t._callbacks[n].call(t._doc,e,i)},_listen:function(){if(!t._init){var e="visibilitychange";t._doc.webkitVisibilityState&&(e="webkit"+e);var i=function(){t._change.apply(t,arguments)};t._doc.addEventListener?t._doc.addEventListener(e,i):t._doc.attachEvent(e,i),t._init=!0}}};"undefined"!=typeof module&&module.exports?module.exports=t:e.Visibility=t}(this),function(e){"use strict";var i=-1,t=function(t){return t.every=function(e,n,r){t._time(),r||(r=n,n=null),i+=1;var d=i;return t._timers[d]={visible:e,hidden:n,callback:r},t._run(d,!1),t.isSupported()&&t._listen(),d},t.stop=function(e){return t._timers[e]?(t._stop(e),delete t._timers[e],!0):!1},t._timers={},t._time=function(){t._timed||(t._timed=!0,t._wasHidden=t.hidden(),t.change(function(){t._stopRun(),t._wasHidden=t.hidden()}))},t._run=function(i,n){var r,d=t._timers[i];if(t.hidden()){if(null===d.hidden)return;r=d.hidden}else r=d.visible;var a=function(){d.last=new Date,d.callback.call(e)};if(n){var o=new Date,u=o-d.last;r>u?d.delay=setTimeout(function(){a(),d.id=setInterval(a,r)},r-u):(a(),d.id=setInterval(a,r))}else d.id=setInterval(a,r)},t._stop=function(e){var i=t._timers[e];clearInterval(i.id),clearTimeout(i.delay),delete i.id,delete i.delay},t._stopRun=function(){var e=t.hidden(),i=t._wasHidden;if(e&&!i||!e&&i)for(var n in t._timers)t._stop(n),t._run(n,!e)},t};"undefined"!=typeof module&&module.exports?module.exports=t(require("./visibility.core")):t(e.Visibility)}(window); \ No newline at end of file diff --git a/src/categories.js b/src/categories.js index 2a80c4a67b..797a3cc823 100644 --- a/src/categories.js +++ b/src/categories.js @@ -114,7 +114,7 @@ var db = require('./database'), topics.getTopicsByTids(tids, uid, next); }, function(topics, next) { - if (!topics || !topics.length) { + if (!Array.isArray(topics) || !topics.length) { return next(null, { topics: [], nextStart: 1 @@ -131,15 +131,9 @@ var db = require('./database'), topics[i].index = indices[topics[i].tid]; } - db.sortedSetRevRank('categories:' + cid + ':tid', topics[topics.length - 1].tid, function(err, rank) { - if(err) { - return next(err); - } - - next(null, { - topics: topics, - nextStart: parseInt(rank, 10) + 1 - }); + next(null, { + topics: topics, + nextStart: stop + 1 }); } ], callback); @@ -173,8 +167,8 @@ var db = require('./database'), }; Categories.getPageCount = function(cid, uid, callback) { - db.sortedSetCard('categories:' + cid + ':tid', function(err, topicCount) { - if(err) { + Categories.getCategoryField(cid, 'topic_count', function(err, topicCount) { + if (err) { return callback(err); } @@ -183,7 +177,7 @@ var db = require('./database'), } user.getSettings(uid, function(err, settings) { - if(err) { + if (err) { return callback(err); } @@ -254,13 +248,29 @@ var db = require('./database'), }; Categories.markAsRead = function(cids, uid, callback) { + callback = callback || function() {}; if (!Array.isArray(cids) || !cids.length) { return callback(); } var keys = cids.map(function(cid) { return 'cid:' + cid + ':read_by_uid'; }); - db.setsAdd(keys, uid, callback); + + db.isMemberOfSets(keys, uid, function(err, hasRead) { + if (err) { + return callback(err); + } + + keys = keys.filter(function(key, index) { + return !hasRead[index]; + }); + + if (!keys.length) { + return callback(); + } + + db.setsAdd(keys, uid, callback); + }); }; Categories.markAsUnreadForAll = function(cid, callback) { @@ -441,10 +451,10 @@ var db = require('./database'), db.incrObjectField('category:' + cid, 'post_count', next); }, function(next) { - if(parseInt(topicData.pinned, 10) === 0) { - db.sortedSetAdd('categories:' + cid + ':tid', postData.timestamp, postData.tid, next); - } else { + if (parseInt(topicData.pinned, 10) === 1) { next(); + } else { + db.sortedSetAdd('categories:' + cid + ':tid', postData.timestamp, postData.tid, next); } } ], callback); diff --git a/src/categories/recentreplies.js b/src/categories/recentreplies.js index 7365956219..7d9cbcc047 100644 --- a/src/categories/recentreplies.js +++ b/src/categories/recentreplies.js @@ -113,31 +113,50 @@ module.exports = function(Categories) { return; } - var keys = pids.map(function(pid) { - return 'post:' + pid; - }); + var start = 0, + done = false, + batch = 50; - db.getObjectsFields(keys, ['timestamp'], function(err, postData) { - if (err) { - return winston.error(err.message); + async.whilst(function() { + return !done; + }, function(next) { + var movePids = pids.slice(start, start + batch); + if (!movePids.length) { + done = true; + return next(); } - - var timestamps = postData.map(function(post) { - return post && post.timestamp; + var keys = movePids.map(function(pid) { + return 'post:' + pid; }); - async.parallel([ - function(next) { - db.sortedSetRemove('categories:recent_posts:cid:' + oldCid, pids, next); - }, - function(next) { - db.sortedSetAdd('categories:recent_posts:cid:' + cid, timestamps, pids, next); - } - ], function(err) { + db.getObjectsFields(keys, ['timestamp'], function(err, postData) { if (err) { - winston.error(err.message); + return next(err); } + + var timestamps = postData.map(function(post) { + return post && post.timestamp; + }); + + async.parallel([ + function(next) { + db.sortedSetRemove('categories:recent_posts:cid:' + oldCid, movePids, next); + }, + function(next) { + db.sortedSetAdd('categories:recent_posts:cid:' + cid, timestamps, movePids, next); + } + ], function(err) { + if (err) { + return next(err); + } + start += batch; + next(); + }); }); + }, function(err) { + if (err) { + winston.error(err.stack); + } }); }); }; @@ -147,7 +166,9 @@ module.exports = function(Categories) { if (err) { return winston.error(err.message); } - + if (!parseInt(postCount, 10)) { + return; + } async.parallel([ function(next) { db.incrObjectFieldBy('category:' + oldCid, 'post_count', -postCount, next); diff --git a/src/controllers/accounts.js b/src/controllers/accounts.js index 9e49dfc3ee..042b48f45a 100644 --- a/src/controllers/accounts.js +++ b/src/controllers/accounts.js @@ -12,6 +12,7 @@ var fs = require('fs'), user = require('../user'), posts = require('../posts'), topics = require('../topics'), + groups = require('../groups'), messaging = require('../messaging'), postTools = require('../postTools'), utils = require('../../public/src/utils'), @@ -23,22 +24,22 @@ var fs = require('fs'), websockets = require('../socket.io'); function userNotFound(res) { + res.locals.notFound = true; + if (res.locals.isAPI) { - res.json(404, 'user-not-found'); + res.status(404).json('no-user'); } else { res.render('404', { - error: 'User not found!' + error: '[[error:no-user]]' }); } } function userNotAllowed(res) { if (res.locals.isAPI) { - res.json(403, 'not-allowed'); + res.status(403).json('not-allowed'); } else { - res.render('403', { - error: 'Not allowed.' - }); + res.render('403'); } } @@ -70,6 +71,9 @@ function getUserDataByUserSlug(userslug, callerUID, callback) { }, profile_links: function(next) { plugins.fireHook('filter:user.profileLinks', [], next); + }, + groups: function(next) { + groups.getUserGroups([uid], next); } }, function(err, results) { if(err || !results.userData) { @@ -82,7 +86,7 @@ function getUserDataByUserSlug(userslug, callerUID, callback) { var self = parseInt(callerUID, 10) === parseInt(userData.uid, 10); userData.joindate = utils.toISOString(userData.joindate); - if(userData.lastonline) { + if (userData.lastonline) { userData.lastonline = utils.toISOString(userData.lastonline); } else { userData.lastonline = userData.joindate; @@ -95,30 +99,31 @@ function getUserDataByUserSlug(userslug, callerUID, callback) { } if (!(isAdmin || self || (userData.email && userSettings.showemail))) { - userData.email = ""; + userData.email = ''; } - if (self && !userSettings.showemail) { - userData.emailClass = ""; - } else { - userData.emailClass = "hide"; + userData.emailClass = (self && !userSettings.showemail) ? '' : 'hide'; + + if (!self && !userSettings.showfullname) { + userData.fullname = ''; } if (isAdmin || self) { userData.ips = results.ips; } - userData.websiteName = userData.website.replace('http://', '').replace('https://', ''); - userData.banned = parseInt(userData.banned, 10) === 1; userData.uid = userData.uid; userData.yourid = callerUID; userData.theirid = userData.uid; - userData.isSelf = parseInt(callerUID, 10) === parseInt(userData.uid, 10); - userData.showSettings = userData.isSelf || isAdmin; + userData.isSelf = self; + userData.showSettings = self || isAdmin; + userData.groups = Array.isArray(results.groups) && results.groups.length ? results.groups[0] : []; userData.disableSignatures = meta.config.disableSignatures !== undefined && parseInt(meta.config.disableSignatures, 10) === 1; userData['email:confirmed'] = !!parseInt(userData['email:confirmed'], 10); userData.profile_links = results.profile_links; - userData.status = !websockets.isUserOnline(userData.uid) ? 'offline' : userData.status; + userData.status = websockets.isUserOnline(userData.uid) ? (userData.status || 'online') : 'offline'; + userData.banned = parseInt(userData.banned, 10) === 1; + userData.websiteName = userData.website.replace('http://', '').replace('https://', ''); userData.followingCount = results.followStats.followingCount; userData.followerCount = results.followStats.followerCount; @@ -484,18 +489,18 @@ accountsController.uploadPicture = function (req, res, next) { user.getUserField(updateUid, 'uploadedpicture', function (err, oldpicture) { if (!oldpicture) { - file.saveFileToLocal(filename, req.files.userPhoto.path, done); + file.saveFileToLocal(filename, 'profile', req.files.userPhoto.path, done); return; } - var absolutePath = path.join(nconf.get('base_dir'), nconf.get('upload_path'), path.basename(oldpicture)); + var absolutePath = path.join(nconf.get('base_dir'), nconf.get('upload_path'), 'profile', path.basename(oldpicture)); fs.unlink(absolutePath, function (err) { if (err) { winston.err(err); } - file.saveFileToLocal(filename, req.files.userPhoto.path, done); + file.saveFileToLocal(filename, 'profile', req.files.userPhoto.path, done); }); }); }); diff --git a/src/controllers/admin.js b/src/controllers/admin.js index 10c852a74d..e5f0461785 100644 --- a/src/controllers/admin.js +++ b/src/controllers/admin.js @@ -6,6 +6,7 @@ var async = require('async'), user = require('../user'), categories = require('../categories'), + posts = require('../posts'), topics = require('../topics'), meta = require('../meta'), db = require('../database'), @@ -21,6 +22,7 @@ var async = require('async'), var adminController = { categories: {}, tags: {}, + flags: {}, topics: {}, groups: {}, appearance: {}, @@ -28,6 +30,7 @@ var adminController = { widgets: {} }, events: {}, + logs: {}, database: {}, plugins: {}, languages: {}, @@ -67,16 +70,16 @@ adminController.home = function(req, res, next) { function getStats(callback) { async.parallel([ function(next) { - getStatsForSet('ip:recent', next); + getStatsForSet('ip:recent', 'uniqueIPCount', next); }, function(next) { - getStatsForSet('users:joindate', next); + getStatsForSet('users:joindate', 'userCount', next); }, function(next) { - getStatsForSet('posts:pid', next); + getStatsForSet('posts:pid', 'postCount', next); }, function(next) { - getStatsForSet('topics:tid', next); + getStatsForSet('topics:tid', 'topicCount', next); } ], function(err, results) { if (err) { @@ -91,12 +94,13 @@ function getStats(callback) { }); } -function getStatsForSet(set, callback) { +function getStatsForSet(set, field, callback) { var terms = { day: 86400000, week: 604800000, month: 2592000000 }; + var now = Date.now(); async.parallel({ day: function(next) { @@ -109,7 +113,7 @@ function getStatsForSet(set, callback) { db.sortedSetCount(set, now - terms.month, now, next); }, alltime: function(next) { - db.sortedSetCount(set, 0, now, next); + db.getObjectField('global', field, next); } }, callback); } @@ -153,6 +157,17 @@ adminController.tags.get = function(req, res, next) { }); }; +adminController.flags.get = function(req, res, next) { + var uid = req.user ? parseInt(req.user.uid, 10) : 0; + posts.getFlags(uid, 0, 19, function(err, posts) { + if (err) { + return next(err); + } + + res.render('admin/manage/flags', {posts: posts, next: 20}); + }); +}; + adminController.database.get = function(req, res, next) { db.info(function (err, data) { res.render('admin/advanced/database', data); @@ -160,14 +175,27 @@ adminController.database.get = function(req, res, next) { }; adminController.events.get = function(req, res, next) { - events.getLog(function(err, data) { + events.getLog(-1, 5000, function(err, data) { if(err || !data) { return next(err); } - data = data.toString().split('\n').reverse().join('\n'); res.render('admin/advanced/events', { - eventdata: data + eventdata: data.data, + next: data.next + }); + }); +}; + +adminController.logs.get = function(req, res, next) { + var logPath = path.join('logs', path.sep, 'output.log'); + fs.readFile(logPath, function(err, data) { + if (err || !data) { + data = ''; + } + + res.render('admin/advanced/logs', { + data: data.toString() }); }); }; @@ -181,7 +209,7 @@ adminController.plugins.get = function(req, res, next) { res.render('admin/extend/plugins' , { plugins: plugins }); - }) + }); }; adminController.languages.get = function(req, res, next) { @@ -315,6 +343,6 @@ adminController.themes.get = function(req, res, next) { return next(); } }); -} +}; module.exports = adminController; diff --git a/src/controllers/admin/uploads.js b/src/controllers/admin/uploads.js index 081f2c0c04..1c31a00ebc 100644 --- a/src/controllers/admin/uploads.js +++ b/src/controllers/admin/uploads.js @@ -23,7 +23,7 @@ function validateUpload(res, req, allowedTypes) { -uploadsController.uploadImage = function(filename, req, res) { +uploadsController.uploadImage = function(filename, folder, req, res) { function done(err, image) { var er, rs; fs.unlink(req.files.userPhoto.path); @@ -40,7 +40,7 @@ uploadsController.uploadImage = function(filename, req, res) { if(plugins.hasListeners('filter:uploadImage')) { plugins.fireHook('filter:uploadImage', req.files.userPhoto, done); } else { - file.saveFileToLocal(filename, req.files.userPhoto.path, done); + file.saveFileToLocal(filename, folder, req.files.userPhoto.path, done); } }; @@ -59,7 +59,7 @@ uploadsController.uploadCategoryPicture = function(req, res, next) { if (validateUpload(res, req, allowedTypes)) { var filename = 'category-' + params.cid + path.extname(req.files.userPhoto.name); - uploadsController.uploadImage(filename, req, res); + uploadsController.uploadImage(filename, 'category', req, res); } }; @@ -67,7 +67,7 @@ uploadsController.uploadFavicon = function(req, res, next) { var allowedTypes = ['image/x-icon', 'image/vnd.microsoft.icon']; if (validateUpload(res, req, allowedTypes)) { - file.saveFileToLocal('favicon.ico', req.files.userPhoto.path, function(err, image) { + file.saveFileToLocal('favicon.ico', 'files', req.files.userPhoto.path, function(err, image) { fs.unlink(req.files.userPhoto.path); if(err) { @@ -93,7 +93,7 @@ function upload(name, req, res, next) { if (validateUpload(res, req, allowedTypes)) { var filename = name + path.extname(req.files.userPhoto.name); - uploadsController.uploadImage(filename, req, res); + uploadsController.uploadImage(filename, 'files', req, res); } } diff --git a/src/controllers/admin/users.js b/src/controllers/admin/users.js index 14f02b7fd0..597e87f16c 100644 --- a/src/controllers/admin/users.js +++ b/src/controllers/admin/users.js @@ -1,6 +1,7 @@ "use strict"; -var user = require('./../../user'); +var user = require('../../user'), + meta = require('../../meta'); var usersController = {}; @@ -25,6 +26,10 @@ usersController.sortByJoinDate = function(req, res, next) { getUsers('users:joindate', req, res, next); }; +usersController.banned = function(req, res, next) { + getUsers('users:banned', req, res, next); +}; + function getUsers(set, req, res, next) { user.getUsersFromSet(set, 0, 49, function(err, users) { if (err) { @@ -34,7 +39,8 @@ function getUsers(set, req, res, next) { search_display: 'hidden', loadmore_display: 'block', users: users, - yourid: req.user.uid + yourid: req.user.uid, + requireEmailConfirmation: parseInt(meta.config.requireEmailConfirmation, 10) === 1 }); }); } diff --git a/src/controllers/api.js b/src/controllers/api.js index a43678d22a..baa07ffe82 100644 --- a/src/controllers/api.js +++ b/src/controllers/api.js @@ -54,7 +54,7 @@ apiController.getConfig = function(req, res, next) { if (!req.user) { if (res.locals.isAPI) { - res.json(200, config); + res.status(200).json(config); } else { next(null, config); } @@ -75,7 +75,7 @@ apiController.getConfig = function(req, res, next) { config.topicPostSort = settings.topicPostSort || config.topicPostSort; if (res.locals.isAPI) { - res.json(200, config); + res.status(200).json(config); } else { next(err, config); } @@ -95,7 +95,7 @@ apiController.renderWidgets = function(req, res, next) { renderedWidgets = []; if (!areas.template || !areas.locations) { - return res.json(200, {}); + return res.status(200).json({}); } widgets.render(uid, { @@ -106,7 +106,7 @@ apiController.renderWidgets = function(req, res, next) { if (err) { return next(err); } - res.json(200, widgets); + res.status(200).json(widgets); }); }; diff --git a/src/controllers/categories.js b/src/controllers/categories.js index d62bf385b9..6ade29e383 100644 --- a/src/controllers/categories.js +++ b/src/controllers/categories.js @@ -227,17 +227,26 @@ categoriesController.get = function(req, res, next) { }; categoriesController.notFound = function(req, res) { - res.locals.isAPI ? res.json(404, 'not-found') : res.status(404).render('404'); + if (res.locals.isAPI) { + res.status(404).json('not-found'); + } else { + res.status(404).render('404'); + } }; categoriesController.notAllowed = function(req, res) { var uid = req.user ? req.user.uid : 0; + if (uid) { - res.locals.isAPI ? res.json(403, 'not-allowed') : res.status(403).render('403'); + if (res.locals.isAPI) { + res.status(403).json('not-allowed'); + } else { + res.status(403).render('403'); + } } else { if (res.locals.isAPI) { req.session.returnTo = apiToRegular(req.url); - res.json(401, 'not-authorized'); + res.status(401).json('not-authorized'); } else { req.session.returnTo = req.url; res.redirect(nconf.get('relative_path') + '/login'); diff --git a/src/controllers/index.js b/src/controllers/index.js index 228c477415..ad60533d23 100644 --- a/src/controllers/index.js +++ b/src/controllers/index.js @@ -145,8 +145,8 @@ Controllers.login = function(req, res, next) { data.authentication = login_strategies; data.token = req.csrfToken(); data.showResetLink = emailersPresent; - data.allowLocalLogin = meta.config.allowLocalLogin === undefined || parseInt(meta.config.allowLocalLogin, 10) === 1; - data.allowRegistration = meta.config.allowRegistration; + data.allowLocalLogin = parseInt(meta.config.allowLocalLogin, 10) === 1; + data.allowRegistration = parseInt(meta.config.allowRegistration, 10) === 1; data.error = req.flash('error')[0]; res.render('login', data); diff --git a/src/controllers/tags.js b/src/controllers/tags.js index 755ede4b91..53b418f6b9 100644 --- a/src/controllers/tags.js +++ b/src/controllers/tags.js @@ -42,6 +42,7 @@ tagsController.getTag = function(req, res, next) { ]; data.tag = tag; + data.nextStart = end + 1; res.render('tag', data); }); }); diff --git a/src/controllers/topics.js b/src/controllers/topics.js index dfda8d4e44..01246b8adc 100644 --- a/src/controllers/topics.js +++ b/src/controllers/topics.js @@ -49,10 +49,10 @@ topicsController.get = function(req, res, next) { var url = ''; if (req.params.post_index > postCount) { url = '/topic/' + req.params.topic_id + '/' + req.params.slug + '/' + postCount; - return res.locals.isAPI ? res.json(302, url) : res.redirect(url); + return res.locals.isAPI ? res.status(302).json(url) : res.redirect(url); } else if (req.params.post_index < 1) { url = '/topic/' + req.params.topic_id + '/' + req.params.slug; - return res.locals.isAPI ? res.json(302, url) : res.redirect(url); + return res.locals.isAPI ? res.status(302).json(url) : res.redirect(url); } } @@ -172,7 +172,7 @@ topicsController.get = function(req, res, next) { }, { property: 'og:title', - content: topicData.title + content: topicData.title.replace(/&/g, '&') }, { property: 'og:description', @@ -275,7 +275,7 @@ topicsController.teaser = function(req, res, next) { } if (!canRead) { - return res.json(403, '[[error:no-priveges]]'); + return res.status(403).json('[[error:no-priveges]]'); } topics.getLatestUndeletedPid(tid, function(err, pid) { @@ -284,7 +284,7 @@ topicsController.teaser = function(req, res, next) { } if (!pid) { - return res.json(404, 'not-found'); + return res.status(404).json('not-found'); } posts.getPostSummaryByPids([pid], uid, {stripTags: false}, function(err, posts) { @@ -293,7 +293,7 @@ topicsController.teaser = function(req, res, next) { } if (!Array.isArray(posts) || !posts.length) { - return res.json(404, 'not-found'); + return res.status(404).json('not-found'); } res.json(posts[0]); diff --git a/src/controllers/users.js b/src/controllers/users.js index c365188bb3..84fde728fd 100644 --- a/src/controllers/users.js +++ b/src/controllers/users.js @@ -15,7 +15,7 @@ usersController.getOnlineUsers = function(req, res, next) { user.getUsersFromSet('users:online', 0, 49, next); }, count: function(next) { - db.sortedSetCard('users:online', next); + next(null, websockets.getConnectedClients().length); }, isAdministrator: function(next) { user.isAdministrator(uid, next); @@ -63,7 +63,7 @@ function getUsers(set, res, next) { user.getUsersFromSet(set, 0, 49, next); }, count: function(next) { - db.sortedSetCard(set, next); + db.getObjectField('global', 'userCount', next); } }, function(err, results) { if (err) { diff --git a/src/database/level.js b/src/database/level.js deleted file mode 100644 index f9b056e1ad..0000000000 --- a/src/database/level.js +++ /dev/null @@ -1,84 +0,0 @@ -'use strict'; - -(function(module) { - /* - * Okay, so LevelDB was made by Google. Therefore it's skalable. - * BUT, I created 99% of the rest of NodeBB's expected functionality out of just simple get and set commands. - * Therefore, it is unskalable. I totally should have read the docs before starting. - * - * With much <3, psychobunny. - */ - - - var winston = require('winston'), - nconf = require('nconf'), - path = require('path'), - session = require('express-session'), - utils = require('./../../public/src/utils.js'), - levelup, - leveldown, - connectLevel, - db; - - module.questions = [ - { - name: "level:database", - description: "Enter the path to your Level database", - 'default': nconf.get('level:database') || '/var/level/nodebb' - } - ]; - - module.init = function(callback) { - try { - levelup = require('levelup'); - leveldown = require('leveldown'); - connectLevel = require('connect-leveldb')(session); - } catch (err) { - winston.error('Unable to initialize Level DB! Is Level DB installed? Error :' + err.message); - process.exit(); - } - - if (db) { - if(typeof callback === 'function') { - callback(); - } - - return; - } - - db = levelup(nconf.get('level:database'), { - valueEncoding: 'json' - }); - - leveldown(nconf.get('level:database')); - - db.on('error', function (err) { - winston.error(err.stack); - }); - - module.client = db; - module.leveldown = leveldown; - - module.sessionStore = new connectLevel({ - db: db, - ttl: 60 * 60 * 24 * 14 - }); - - require('./level/main')(db, module); - require('./level/hash')(db, module); - require('./level/sets')(db, module); - require('./level/sorted')(db, module); - require('./level/list')(db, module); - - if(typeof callback === 'function') { - callback(); - } - }; - - module.close = function(callback) { - db.close(callback); - }; - - module.helpers = module.helpers || {}; - module.helpers.level = require('./level/helpers'); -}(exports)); \ No newline at end of file diff --git a/src/database/level/hash.js b/src/database/level/hash.js deleted file mode 100644 index b35a1b1193..0000000000 --- a/src/database/level/hash.js +++ /dev/null @@ -1,146 +0,0 @@ -"use strict"; - -var async = require('async'); - -module.exports = function(db, module) { - var helpers = module.helpers.level; - - module.setObject = function(key, obj, callback) { - async.parallel([ - function(next) { - async.each(Object.keys(obj), function(objKey, next) { - module.setObjectField(key, objKey, obj[objKey], next); - }, next); - }, - function(next) { - module.set(key, Object.keys(obj).join('-ldb-')); - next(); - } - ], function(err) { - if (typeof callback === 'function') { - callback(err); - } - }); - }; - - module.setObjectField = function(key, field, value, callback) { - module.set(key + ':' + field, value, callback); - }; - - module.getObject = function(key, callback) { - var obj = {}; - - module.getObjectKeys(key, function(err, keys) { - if (keys) { - keys = keys.split('-ldb-'); - async.each(keys, function(field, next) { - module.getObjectField(key, field, function(err, value) { - obj[field] = value; - next(err); - }); - }, function(err) { - if (typeof callback === 'function') { - callback(err, obj); - } - }); - } else { - if (typeof callback === 'function') { - callback(err, {}); - } - } - }); - }; - - module.getObjects = function(keys, callback) { - var arr = []; - - async.each(keys, function(key, next) { - module.getObject(key, function(err, val) { - arr.push(val); - next(); - }); - }, function(err) { - callback(err, arr); - }); - }; - - module.getObjectField = function(key, field, callback) { - module.get(key + ':' + field, function(err, val) { - callback(err, typeof val !== 'undefined' ? val : ''); - }); - }; - - module.getObjectFields = function(key, fields, callback) { - // can be improved with multi. - var obj = {}; - async.each(fields, function(field, next) { - module.getObjectField(key, field, function(err, value) { - obj[field] = value; - next(); - }); - }, function(err) { - callback(err, obj); - }); - }; - - module.getObjectsFields = function(keys, fields, callback) { - helpers.iterator('getObjectFields', keys, fields, callback); - }; - - module.getObjectKeys = function(key, callback) { - module.get(key, callback); - }; - - module.getObjectValues = function(key, callback) { - module.getObject(key, function(err, obj) { - var values = []; - for (var key in obj) { - if (obj.hasOwnProperty(key)) { - values.push(obj[key]); - } - } - - callback(err, values); - }); - }; - - module.isObjectField = function(key, field, callback) { - module.get(key + ':' + field, function(err, val) { - callback(err, !!val); - }); - }; - - module.deleteObjectField = function(key, field, callback) { - module.delete(key + ':' + field, callback); - }; - - module.incrObjectField = function(key, field, callback) { - module.incrObjectFieldBy(key, field, 1, callback); - }; - - module.decrObjectField = function(key, field, callback) { - module.decrObjectFieldBy(key, field, 1, callback); - }; - - module.incrObjectFieldBy = function(key, field, value, callback) { - module.get(key + ':' + field, function(err, val) { - val = val ? (val + value) : value; - module.set(key + ':' + field, val, function(err) { - if (typeof callback === 'function') { - callback(err, val); - } - }); - }); - }; - - module.decrObjectFieldBy = function(key, field, value, callback) { - module.get(key + ':' + field, function(err, val) { - val = val ? (val - value) : -value; - module.set(key + ':' + field, val, function(err) { - if (typeof callback === 'function') { - callback(err, val); - } - }); - }); - }; -}; \ No newline at end of file diff --git a/src/database/level/helpers.js b/src/database/level/helpers.js deleted file mode 100644 index 2058066f89..0000000000 --- a/src/database/level/helpers.js +++ /dev/null @@ -1,19 +0,0 @@ -"use strict"; - -var helpers = {}, - async = require('async'); - -helpers.iterator = function(fn, keys, value, callback) { - var results = []; - - async.each(keys, function(key, next) { - module.parent.exports[fn](key, value, function(err, result) { - results.push(result); - next(); - }); - }, function(err) { - callback(err, results); - }); -}; - -module.exports = helpers; \ No newline at end of file diff --git a/src/database/level/list.js b/src/database/level/list.js deleted file mode 100644 index ca579178f0..0000000000 --- a/src/database/level/list.js +++ /dev/null @@ -1,56 +0,0 @@ -"use strict"; - -module.exports = function(db, module) { - var helpers = module.helpers.level; - - module.listPrepend = function(key, value, callback) { - module.getListRange(key, 0, -1, function(err, list) { - var arr = list || []; - arr.unshift(value); - module.set(key, arr, callback); - }); - }; - - module.listAppend = function(key, value, callback) { - module.getListRange(key, 0, -1, function(err, list) { - var arr = list || []; - arr.push(value); - module.set(key, arr, function(err) { - if (typeof callback === 'function') { - callback(err); - } - }); - }); - }; - - module.listRemoveLast = function(key, callback) { - module.getListRange(key, 0, -1, function(err, list) { - var arr = list || []; - list.pop(); - module.set(key, list, callback); - }); - }; - - module.listRemoveFirst = function(key, callback) { - module.getListRange(key, 0, -1, function(err, list) { - var arr = list || []; - list.shift(); - module.set(key, list, callback); - }); - }; - - module.listRemoveAll = function(key, value, callback) { - module.set(key, [], callback); - }; - - module.getListRange = function(key, start, stop, callback) { - // needs testing. - module.get(key, function(err, list) { - if (list) { - callback(err, list.slice(start, stop === -1 ? list.length : stop)); - } else { - callback(null, []); - } - }); - }; -}; \ No newline at end of file diff --git a/src/database/level/main.js b/src/database/level/main.js deleted file mode 100644 index 00c55bb16d..0000000000 --- a/src/database/level/main.js +++ /dev/null @@ -1,94 +0,0 @@ -"use strict"; - -var nconf = require('nconf'), - async = require('async'); - -module.exports = function(db, module) { - var helpers = module.helpers.level; - - module.searchIndex = function(key, content, id, callback) { - // o.O - }; - - module.search = function(key, term, limit, callback) { - // O.o - }; - - module.searchRemove = function(key, id, callback) { - // o___O - }; - - module.flushdb = function(callback) { - db.close(function() { - module.leveldown.destroy(nconf.get('level:database'), function() { - db.open(callback); - }); - }); - }; - - module.info = function(callback) { - // O____O GIEF FOOD - // v v - }; - - module.exists = function(key, callback) { - db.get(key, function(err, value) { - callback(null, !!value); - }); - }; - - module.delete = function(key, callback) { - db.del(key, callback); - }; - - module.deleteAll = function(keys, callback) { - async.each(keys, module.delete, callback); - }; - - module.get = function(key, callback) { - db.get(key, function(err, value) { - callback(false, value); - }); - }; - - module.set = function(key, value, callback, sync) { - if (value === '') { - callback(false); - } else { - var options = { - sync: typeof sync !== 'undefined' - }; - - db.put(key, value, options, function(err) { - // uh, err is {}.. why?? - if (typeof callback === 'function') { - callback(null); - } - }); - } - }; - - module.increment = function(key, callback) { - // ^_^ - }; - - module.rename = function(oldKey, newKey, callback) { - // G__G - }; - - module.expire = function(key, seconds, callback) { - // >__> - }; - - module.expireAt = function(key, timestamp, callback) { - // <__< - }; - - module.pexpire = function(key, ms, callback) { - // o_o - }; - - module.pexpireAt = function(key, timestamp, callback) { - // d-_-b - }; -}; \ No newline at end of file diff --git a/src/database/level/sets.js b/src/database/level/sets.js deleted file mode 100644 index b7697f1209..0000000000 --- a/src/database/level/sets.js +++ /dev/null @@ -1,82 +0,0 @@ -"use strict"; - -var async = require('async'); - -module.exports = function(db, module) { - var helpers = module.helpers.level; - - module.setAdd = function(key, value, callback) { - callback = callback || function() {}; - module.getListRange(key, 0, -1, function(err, set) { - if (err) { - return callback(err); - } - if (set.indexOf(value) === -1) { - module.listAppend(key, value, callback); - } else { - callback(null); - } - }); - }; - - module.setsAdd = function(keys, value, callback) { - throw new Error('not-implemented'); - }; - - module.setRemove = function(key, value, callback) { - module.getListRange(key, 0, -1, function(err, set) { - module.set(key, set.splice(set.indexOf(value), 1), callback); - }); - }; - - module.setsRemove = function(keys, value, callback) { - throw new Error('not-implemented'); - }; - - module.isSetMember = function(key, value, callback) { - module.getListRange(key, 0, -1, function(err, set) { - callback(err, set.indexOf(value) !== -1); - }); - }; - - module.isSetMembers = function(key, values, callback) { - var members = {}; - - async.each(values, function(value, next) { - module.isSetMember(key, value, function(err, isMember) { - members[key] = isMember; - }); - }, function(err) { - callback(err, members); - }); - }; - - module.isMemberOfSets = function(sets, value, callback) { - helpers.iterator('isSetMember', sets, value, callback); - }; - - module.getSetMembers = function(key, callback) { - module.getListRange(key, 0, -1, function(err, set) { - callback(err, set); - }); - }; - - module.getSetsMembers = function(keys, callback) { - throw new Error('not-implemented'); - }; - - module.setCount = function(key, callback) { - module.getListRange(key, 0, -1, function(err, set) { - callback(err, set.length); - }); - }; - - module.setRemoveRandom = function(key, callback) { - // how random is this? well, how random are the other implementations of this? - // imo rename this to setRemoveOne - - module.getListRange(key, 1, -1, function(err, set) { - module.set(key, set, callback); - }); - }; -}; \ No newline at end of file diff --git a/src/database/level/sorted.js b/src/database/level/sorted.js deleted file mode 100644 index b974c1a981..0000000000 --- a/src/database/level/sorted.js +++ /dev/null @@ -1,299 +0,0 @@ -"use strict"; - -var async = require('async'); - - -module.exports = function(db, module) { - var helpers = module.helpers.level; - - module.sortedSetAdd = function(key, score, value, callback) { - if (Array.isArray(score) && Array.isArray(value)) { - return sortedSetAddMulti(key, score, value, callback); - } - module.getListRange(key, 0, -1, function(err, set) { - set = set.filter(function(a) {return a.value !== value.toString();}); - - set.push({ - value: value.toString(), - score: parseInt(score, 10) - }); - - set.sort(function(a, b) {return a.score - b.score;}); - module.set(key, set, callback); - }); - }; - - function sortedSetAddMulti(key, scores, values, callback) { - throw new Error('not implemented'); - } - - module.sortedSetsAdd = function(keys, score, value, callback) { - async.each(keys, function(key, next) { - module.sortedSetAdd(key, score, value, next); - }, function(err) { - callback(err); - }); - }; - - module.sortedSetRemove = function(key, value, callback) { - if (!Array.isArray(value)) { - value = [value]; - } - module.getListRange(key, 0, -1, function(err, set) { - set = set.filter(function(a) { return value.indexOf(a) === -1;}); - module.set(key, set, callback); - }); - }; - - module.sortedSetsRemove = function(keys, value, callback) { - async.each(keys, function(key, next) { - module.sortedSetRemove(key, value, next); - }, callback); - }; - - module.sortedSetsRemoveRangeByScore = function(keys, min, max, callback) { - throw new Error('not implemented'); - }; - - function flattenSortedSet(set, callback) { - callback(null, !set.length ? [] : set.reduce(function(a, b) { - return (a.length ? a : [a.value]).concat([b.value]); - })); - } - - module.getSortedSetRange = function(key, start, stop, callback) { - module.getListRange(key, start, stop, function(err, set) { - set = !set.length ? [] : set.reduce(function(a, b) { - return (a.length ? a : [a.value]).concat(b.value); - }); - if (set.value) { - set = [set.value]; - } - callback(err, set); - }); - }; - - module.getSortedSetRevRange = function(key, start, stop, callback) { - module.getListRange(key, start, stop, function(err, set) { - set = !set.length ? [] : set.reverse().reduce(function(a, b) { - return (a.length ? a : [a.value]).concat(b.value); - }); - if (set.value) { - set = [set.value]; - } - callback(err, set); - }); - }; - - module.getSortedSetRevRangeWithScores = function(key, start, stop, callback) { - module.getListRange(key, start, stop, function(err, set) { - if (err) { - return callback(err); - } - set.sort(function(a, b) {return b.score - a.score;}); - callback(null, set); - }); - }; - - module.getSortedSetRangeByScore = function(key, start, count, min, max, callback) { - module.getListRange(key, 0, -1, function(err, list) { - if (min && max) { - list.filter(function(a) { - return a.score >= min && a.score <= max; // to check: greater or and equal? - }); - } - - flattenSortedSet(list.slice(start ? start : 0, count ? count : list.length), callback); - }); - }; - - module.getSortedSetRevRangeByScore = function(key, start, count, max, min, callback) { - module.getListRange(key, 0, -1, function(err, list) { - if (min && max) { - list.filter(function(a) { - return a.score >= min && a.score <= max; // to check: greater or and equal? - }); - } - - flattenSortedSet(list.slice(start ? start : 0, count ? count : list.length).reverse(), callback); - }); - }; - - module.sortedSetCount = function(key, min, max, callback) { - module.getListRange(key, 0, -1, function(err, list) { - list.filter(function(a) { - return a.score >= min && a.score <= max; // to check: greater or and equal? - }); - - callback(err, list.length); - }); - }; - - module.sortedSetCard = function(key, callback) { - module.getListRange(key, 0, -1, function(err, list) { - callback(err, list.length); - }); - }; - - module.sortedSetsCard = function(keys, callback) { - async.map(keys, module.sortedSetCard, callback); - }; - - module.sortedSetRank = function(key, value, callback) { - module.getListRange(key, 0, -1, function(err, list) { - for (var i = 0, ii=list.length; i< ii; i++) { - if (list[i].value === value) { - return callback(err, i); - } - } - - callback(err, null); - }); - }; - - module.sortedSetsRanks = function(keys, values, callback) { - throw new Error('not implemented'); - }; - - module.sortedSetRanks = function(key, values, callback) { - throw new Error('not implemented'); - }; - - module.sortedSetRevRank = function(key, value, callback) { - module.getListRange(key, 0, -1, function(err, list) { - for (var i = list.length - 1, ii=0; i > ii; i--) { - if (list[i].value === value.toString()) { - return callback(err, i); - } - } - - callback(err, null); - }); - }; - - module.sortedSetScore = function(key, value, callback) { - module.getListRange(key, 0, -1, function(err, list) { - for (var i = 0, ii=list.length; i< ii; i++) { - if (list[i].value === value.toString()) { - return callback(err, list[i].score); - } - } - - callback(err, null); - }); - }; - - module.sortedSetScores = function(key, values, callback) { - values = values.map(function(value) { - return value ? value.toString() : value; - }); - - module.getListRange(key, 0, -1, function(err, list) { - if (err) { - return callback(err); - } - - var map = {}; - list = list.filter(function(item) { - return values.indexOf(item.value) !== -1; - }).forEach(function(item) { - map[item.value] = item.score; - }); - - var returnData = new Array(values.length), - score; - - for(var i=0; i= 0) { /* If redis.host contains a path name character, use the unix dom sock connection. ie, /tmp/redis.sock */ diff --git a/src/database/redis/sorted.js b/src/database/redis/sorted.js index 86b3f3d92c..54b09b1915 100644 --- a/src/database/redis/sorted.js +++ b/src/database/redis/sorted.js @@ -12,6 +12,10 @@ module.exports = function(redisClient, module) { }; function sortedSetAddMulti(key, scores, values, callback) { + if (!scores.length || !values.length) { + return callback(); + } + if (scores.length !== values.length) { return callback(new Error('[[error:invalid-data]]')); } diff --git a/src/emailer.js b/src/emailer.js index 362309f7d7..d77cc9556a 100644 --- a/src/emailer.js +++ b/src/emailer.js @@ -8,7 +8,7 @@ var fs = require('fs'), User = require('./user'), Plugins = require('./plugins'), - Meta = require('./meta'), + meta = require('./meta'), translator = require('../public/src/translator'), app = {}, @@ -44,7 +44,7 @@ Emailer.send = function(template, uid, params) { Plugins.fireHook('action:email.send', { to: results.email, - from: Meta.config['email:from'] || 'no-reply@localhost.lan', + from: meta.config['email:from'] || 'no-reply@localhost.lan', subject: translated[2], html: translated[0], plaintext: translated[1], diff --git a/src/emitter.js b/src/emitter.js index e95b01c216..4d9b0f0164 100644 --- a/src/emitter.js +++ b/src/emitter.js @@ -4,11 +4,13 @@ var eventEmitter = new (require('events')).EventEmitter(); eventEmitter.all = function(events, callback) { + var eventList = events.slice(0); + function onEvent(event) { eventEmitter.on(events[event], function() { - events.splice(events.indexOf(event), 1); + eventList.splice(eventList.indexOf(events[event]), 1); - if (events.length === 0) { + if (eventList.length === 0) { callback(); } }); diff --git a/src/events.js b/src/events.js index 3bb4c1c78a..d24c6aec4f 100644 --- a/src/events.js +++ b/src/events.js @@ -62,6 +62,14 @@ var fs = require('fs'), events.logWithUser(uid, 'restored post (pid ' + pid + ')'); }; + events.logPostPurge = function(uid, pid) { + events.logWithUser(uid, 'purged post (pid ' + pid + ')'); + }; + + events.logTopicMove = function(uid, tid) { + events.logWithUser(uid, 'moved topic (tid ' + tid + ')'); + }; + events.logTopicDelete = function(uid, tid) { events.logWithUser(uid, 'deleted topic (tid ' + tid + ')'); }; @@ -79,7 +87,7 @@ var fs = require('fs'), var msg = username + '(uid ' + uid + ') ' + string; events.log(msg); }); - } + }; events.log = function(msg, callback) { var logFile = path.join(nconf.get('base_dir'), logFileName); @@ -101,15 +109,39 @@ var fs = require('fs'), }); }; - events.getLog = function(callback) { + events.getLog = function(end, len, callback) { var logFile = path.join(nconf.get('base_dir'), logFileName); - fs.readFile(logFile, function(err, res) { - if(err) { + fs.stat(logFile, function(err, stat) { + if (err) { return callback(null, 'No logs found!'); } - callback(null, res); + + var buffer = ''; + var size = stat.size; + if (end === -1) { + end = size; + } + + end = parseInt(end, 10); + var start = Math.max(0, end - len); + + var rs = fs.createReadStream(logFile, {start: start, end: end}); + rs.addListener('data', function(lines) { + buffer += lines.toString(); + }); + + rs.addListener('end', function() { + var firstNewline = buffer.indexOf('\n'); + if (firstNewline !== -1) { + buffer = buffer.slice(firstNewline); + buffer = buffer.split('\n').reverse().join('\n'); + } + + callback(null, {data: buffer, next: end - buffer.length}); + }); }); + }; }(module.exports)); diff --git a/src/file.js b/src/file.js index f88728b860..9433dfb40b 100644 --- a/src/file.js +++ b/src/file.js @@ -7,9 +7,9 @@ var fs = require('fs'), var file = {}; -file.saveFileToLocal = function(filename, tempPath, callback) { +file.saveFileToLocal = function(filename, folder, tempPath, callback) { - var uploadPath = path.join(nconf.get('base_dir'), nconf.get('upload_path'), filename); + var uploadPath = path.join(nconf.get('base_dir'), nconf.get('upload_path'), folder, filename); winston.info('Saving file '+ filename +' to : ' + uploadPath); @@ -18,7 +18,7 @@ file.saveFileToLocal = function(filename, tempPath, callback) { is.on('end', function () { callback(null, { - url: nconf.get('upload_url') + filename + url: nconf.get('upload_url') + folder + '/' + filename }); }); diff --git a/src/hotswap.js b/src/hotswap.js index b56beae22e..f852838550 100644 --- a/src/hotswap.js +++ b/src/hotswap.js @@ -1,3 +1,5 @@ +"use strict"; + var HotSwap = {}, winston = require('winston'), stack; @@ -11,7 +13,6 @@ HotSwap.find = function(id) { for(var x=0,numEntries=stack.length;x topicCount) { url = '/category/' + req.params.category_id + '/' + req.params.slug + '/' + topicCount; - return res.locals.isAPI ? res.json(302, url) : res.redirect(url); + return res.locals.isAPI ? res.status(302).json(url) : res.redirect(url); } else if (topicIndex < 1) { url = '/category/' + req.params.category_id + '/' + req.params.slug; - return res.locals.isAPI ? res.json(302, url) : res.redirect(url); + return res.locals.isAPI ? res.status(302).json(url) : res.redirect(url); } next(); }); @@ -146,7 +165,7 @@ middleware.checkGlobalPrivacySettings = function(req, res, next) { if (!callerUID && !!parseInt(meta.config.privateUserInfo, 10)) { if (res.locals.isAPI) { - return res.json(403, 'not-allowed'); + return res.status(403).json('not-allowed'); } else { req.session.returnTo = req.url; return res.redirect('login'); @@ -171,7 +190,7 @@ middleware.checkAccountPermissions = function(req, res, next) { } if (!uid) { - return res.locals.isAPI ? res.json(404, 'not-found') : res.redirect(nconf.get('relative_path') + '/404'); + return res.locals.isAPI ? res.status(404).json('not-found') : res.redirect(nconf.get('relative_path') + '/404'); } if (parseInt(uid, 10) === callerUID) { @@ -187,7 +206,11 @@ middleware.checkAccountPermissions = function(req, res, next) { return next(); } - res.locals.isAPI ? res.json(403, 'not-allowed') : res.redirect(nconf.get('relative_path') + '/403'); + if (res.locals.isAPI) { + res.status(403).json('not-allowed'); + } else { + res.redirect(nconf.get('relative_path') + '/403'); + } }); }); }; @@ -329,10 +352,10 @@ middleware.renderHeader = function(req, res, callback) { if (err) { return next(err); } - meta.title.build(req.url.slice(1), settings.language, next); + meta.title.build(req.url.slice(1), settings.language, res.locals, next); }); } else { - meta.title.build(req.url.slice(1), meta.config.defaultLang, next); + meta.title.build(req.url.slice(1), meta.config.defaultLang, res.locals, next); } }, isAdmin: function(next) { @@ -355,6 +378,7 @@ middleware.renderHeader = function(req, res, callback) { templateValues.user = results.user; templateValues.customCSS = results.customCSS; templateValues.customJS = results.customJS; + templateValues.maintenanceHeader = meta.config.maintenanceMode === '1' && !results.isAdmin; app.render('header', templateValues, callback); }); @@ -443,13 +467,19 @@ middleware.addExpiresHeaders = function(req, res, next) { }; middleware.maintenanceMode = function(req, res, next) { - var render = function() { - res.render('maintenance', { - site_title: meta.config.site_title || 'NodeBB' - }); - }; + var allowedRoutes = [ + '/login' + ], + render = function() { + middleware.buildHeader(req, res, function() { + res.render('maintenance', { + site_title: meta.config.site_title || 'NodeBB', + message: meta.config.maintenanceModeMessage + }); + }); + }; - if (meta.config.maintenanceMode === '1') { + if (meta.config.maintenanceMode === '1' && allowedRoutes.indexOf(req.url) === -1) { if (!req.user) { return render(); } else { diff --git a/src/notifications.js b/src/notifications.js index ca4537ef45..f7859fced1 100644 --- a/src/notifications.js +++ b/src/notifications.js @@ -128,43 +128,91 @@ var async = require('async'), return callback(); } - var websockets = require('./socket.io'); if (!Array.isArray(uids)) { uids = [uids]; } + uids = uids.filter(function(uid) { + return parseInt(uid, 10); + }); + + if (!uids.length) { + return callback(); + } + + var done = false; + var start = 0; + var batchSize = 50; + + setTimeout(function() { + async.whilst( + function() { + return !done; + }, + function(next) { + var currentUids = uids.slice(start, start + batchSize); + if (!currentUids.length) { + done = true; + return next(); + } + pushToUids(currentUids, notification, function(err) { + if (err) { + return next(err); + } + start = start + batchSize; + + setTimeout(next, 1000); + }); + }, + function(err) { + if (err) { + winston.error(err.stack); + } + } + ); + }, 1000); + + callback(); + }; + + function pushToUids(uids, notification, callback) { var unreadKeys = []; var readKeys = []; - uids.filter(Boolean).forEach(function(uid) { + uids.forEach(function(uid) { unreadKeys.push('uid:' + uid + ':notifications:unread'); readKeys.push('uid:' + uid + ':notifications:read'); }); - async.parallel([ + var oneWeekAgo = Date.now() - 604800000; + async.series([ function(next) { db.sortedSetsAdd(unreadKeys, notification.datetime, notification.nid, next); }, function(next) { db.sortedSetsRemove(readKeys, notification.nid, next); + }, + function(next) { + db.sortedSetsRemoveRangeByScore(unreadKeys, 0, oneWeekAgo, next); + }, + function(next) { + db.sortedSetsRemoveRangeByScore(readKeys, 0, oneWeekAgo, next); } ], function(err) { if (err) { return callback(err); } - var oneWeekAgo = Date.now() - 604800000; - db.sortedSetsRemoveRangeByScore(unreadKeys, 0, oneWeekAgo); - db.sortedSetsRemoveRangeByScore(readKeys, 0, oneWeekAgo); - plugins.fireHook('action:notification.pushed', {notification: notification, uids: uids}); - callback(); + var websockets = require('./socket.io'); for(var i=0; i b.name ) { return 1; + } else if (a.name < b.name ){ + return -1; + } else { + return 0; } - return 0; }); callback(null, pluginArray); @@ -681,6 +693,18 @@ var fs = require('fs'), }); }; + function getVersion(name, callback) { + npm.load({}, function() { + npm.commands.show([name, 'version'], true, function(err, version) { + if (err || !version) { + return callback(); + } + var obj = Object.keys(version); + callback(null, Array.isArray(obj) && obj.length ? obj[0] : null); + }); + }); + } + Plugins.isInstalled = function(id, callback) { var pluginDir = path.join(__dirname, '../node_modules', id); @@ -723,29 +747,38 @@ var fs = require('fs'), async.waterfall([ function(next) { - fs.readFile(path.join(file, 'plugin.json'), next); + async.parallel({ + packageJSON: function(next) { + fs.readFile(path.join(file, 'package.json'), next); + }, + pluginJSON: function(next) { + fs.readFile(path.join(file, 'plugin.json'), next); + } + }, next); }, - function(configJSON, next) { - var config; + function(results, next) { + var packageInfo, pluginInfo; try { - config = JSON.parse(configJSON); + packageInfo = JSON.parse(results.packageJSON); + pluginInfo = JSON.parse(results.pluginJSON); } catch (err) { winston.warn("Plugin: " + file + " is corrupted or invalid. Please check plugin.json for errors."); return next(err, null); } - Plugins.isActive(config.id, function(err, active) { + Plugins.isActive(packageInfo.name, function(err, active) { if (err) { next(new Error('no-active-state')); } - delete config.library; - delete config.hooks; - config.active = active; - config.installed = true; + delete pluginInfo.hooks; + delete pluginInfo.library; + pluginInfo.active = active; + pluginInfo.installed = true; + pluginInfo.version = packageInfo.version; - next(null, config); + next(null, pluginInfo); }); } ], function(err, config) { @@ -765,7 +798,16 @@ var fs = require('fs'), }); }; + Plugins.clearRequireCache = function(next) { + async.map(Plugins.libraryPaths, fs.realpath, function(err, paths) { + for (var x=0,numPaths=paths.length;x 0) { + feed.pubDate = new Date(parseInt(posts[0].timestamp, 10)).toUTCString(); + } + + posts.forEach(function(postData) { + feed.item({ + title: postData.topic ? postData.topic.title : '', + description: postData.content, + url: nconf.get('url') + '/topic/' + (postData.topic ? postData.topic.slug : '#') + '/'+postData.index, + author: postData.user ? postData.user.username : '', + date: new Date(parseInt(postData.timestamp, 10)).toUTCString() + }); + }); + + return feed; +} + function sendFeed(feed, res) { var xml = feed.xml(); res.type('xml').set('Content-Length', Buffer.byteLength(xml)).send(xml); @@ -186,4 +258,6 @@ module.exports = function(app, middleware, controllers){ app.get('/category/:category_id.rss', hasCategoryPrivileges, disabledRSS, generateForCategory); app.get('/recent.rss', disabledRSS, generateForRecent); app.get('/popular.rss', disabledRSS, generateForPopular); + app.get('/recentposts.rss', disabledRSS, generateForRecentPosts); + app.get('/category/:category_id/recentposts.rss', hasCategoryPrivileges, disabledRSS, generateForCategoryRecentPosts); }; diff --git a/src/routes/index.js b/src/routes/index.js index 78d89406ce..5b8bb8d638 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -172,20 +172,20 @@ function handleErrors(err, req, res, next) { req.flash('errorMessage', err.message); - res.redirect(nconf.get('relative_path') + '/500') + res.redirect(nconf.get('relative_path') + '/500'); } function catch404(req, res, next) { var relativePath = nconf.get('relative_path'); var isLanguage = new RegExp('^' + relativePath + '/language/[\\w]{2,}/.*.json'), - isClientScript = new RegExp('^' + relativePath + '\\/src\\/forum(\\/admin)?\\/.+\\.js'); + isClientScript = new RegExp('^' + relativePath + '\\/src\\/.+\\.js'); res.status(404); if (isClientScript.test(req.url)) { - res.type('text/javascript').send(200, ''); + res.type('text/javascript').status(200).send(''); } else if (isLanguage.test(req.url)) { - res.json(200, {}); + res.status(200).json({}); } else if (req.accepts('html')) { if (process.env.NODE_ENV === 'development') { winston.warn('Route requested but not found: ' + req.url); diff --git a/src/routes/meta.js b/src/routes/meta.js index 9cfcc2faf8..dd888199dc 100644 --- a/src/routes/meta.js +++ b/src/routes/meta.js @@ -24,11 +24,11 @@ function sendSourceMap(req, res) { } function sendStylesheet(req, res, next) { - res.type('text/css').send(200, meta.css.cache); + res.type('text/css').status(200).send(meta.css.cache); } function sendACPStylesheet(req, res, next) { - res.type('text/css').send(200, meta.css.acpCache); + res.type('text/css').status(200).send(meta.css.acpCache); } function setupPluginSourceMapping(app) { diff --git a/src/routes/plugins.js b/src/routes/plugins.js index 17efdb8c53..7615aaef00 100644 --- a/src/routes/plugins.js +++ b/src/routes/plugins.js @@ -41,7 +41,7 @@ module.exports = function(app, middleware, controllers) { }); if (matches.length) { - res.sendfile(matches[0]); + res.sendFile(matches[0]); } else { res.redirect(nconf.get('relative_path') + '/404'); } diff --git a/src/sitemap.js b/src/sitemap.js index 288d75bb13..fb36538280 100644 --- a/src/sitemap.js +++ b/src/sitemap.js @@ -5,8 +5,10 @@ var path = require('path'), sm = require('sitemap'), url = require('url'), nconf = require('nconf'), + db = require('./database'), categories = require('./categories'), topics = require('./topics'), + privileges = require('./privileges'), utils = require('../public/src/utils'), sitemap = { obj: undefined, @@ -49,21 +51,33 @@ var path = require('path'), }, function(next) { var topicUrls = []; - topics.getTopicsFromSet(0, 'topics:recent', 0, 49, function(err, data) { + + db.getSortedSetRevRange('topics:recent', 0, 49, function(err, tids) { if (err) { return next(err); } + privileges.topics.filter('read', tids, 0, function(err, tids) { + if (err) { + return next(err); + } - data.topics.forEach(function(topic) { - topicUrls.push({ - url: path.join('/topic', topic.slug), - lastmodISO: utils.toISOString(topic.lastposttime), - changefreq: 'daily', - priority: '0.6' + topics.getTopicsFields(tids, ['slug', 'lastposttime'], function(err, topics) { + if (err) { + return next(err); + } + + topics.forEach(function(topic) { + topicUrls.push({ + url: path.join('/topic', topic.slug), + lastmodISO: utils.toISOString(topic.lastposttime), + changefreq: 'daily', + priority: '0.6' + }); + }); + + next(null, topicUrls); }); }); - - next(null, topicUrls); }); } ], function(err, data) { diff --git a/src/socket.io/admin.js b/src/socket.io/admin.js index 17bc3d0e20..e4e3c10550 100644 --- a/src/socket.io/admin.js +++ b/src/socket.io/admin.js @@ -1,20 +1,25 @@ "use strict"; -var groups = require('../groups'), +var async = require('async'), + winston = require('winston'), + cluster = require('cluster'), + fs = require('fs'), + path = require('path'), + + groups = require('../groups'), meta = require('../meta'), plugins = require('../plugins'), widgets = require('../widgets'), user = require('../user'), topics = require('../topics'), + posts = require('../posts'), categories = require('../categories'), logger = require('../logger'), events = require('../events'), emailer = require('../emailer'), db = require('../database'), - async = require('async'), - winston = require('winston'), index = require('./index'), - cluster = require('cluster'), + SocketAdmin = { user: require('./admin/user'), @@ -93,6 +98,10 @@ SocketAdmin.plugins.toggleInstall = function(socket, plugin_id, callback) { plugins.toggleInstall(plugin_id, callback); }; +SocketAdmin.plugins.upgrade = function(socket, plugin_id, callback) { + plugins.upgrade(plugin_id, callback); +}; + SocketAdmin.widgets.set = function(socket, data, callback) { if(!data) { return callback(new Error('[[error:invalid-data]]')); @@ -183,7 +192,7 @@ SocketAdmin.analytics.get = function(socket, data, callback) { if (data.graph === 'traffic') { async.parallel({ uniqueVisitors: function(next) { - getHourlyStatsForSet('ip:recent', data.amount, next); + getHourlyStatsForSet('analytics:uniquevisitors', data.amount, next); }, pageviews: function(next) { getHourlyStatsForSet('analytics:pageviews', data.amount, next); @@ -232,4 +241,30 @@ function getHourlyStatsForSet(set, hours, callback) { }); } +SocketAdmin.getMoreEvents = function(socket, next, callback) { + if (parseInt(next, 10) < 0) { + return callback(null, {data: [], next: next}); + } + events.getLog(next, 5000, callback); +}; + + +SocketAdmin.dismissFlag = function(socket, pid, callback) { + if (!pid) { + return callback('[[error:invalid-data]]'); + } + + posts.dismissFlag(pid, callback); +}; + +SocketAdmin.getMoreFlags = function(socket, after, callback) { + if (!parseInt(after, 10)) { + return callback('[[error:invalid-data]]'); + } + after = parseInt(after, 10); + posts.getFlags(socket.uid, after, after + 19, function(err, posts) { + callback(err, {posts: posts, next: after + 20}); + }); +}; + module.exports = SocketAdmin; diff --git a/src/socket.io/admin/user.js b/src/socket.io/admin/user.js index b861806df1..0647b1397b 100644 --- a/src/socket.io/admin/user.js +++ b/src/socket.io/admin/user.js @@ -14,9 +14,21 @@ User.makeAdmins = function(socket, uids, callback) { return callback(new Error('[[error:invalid-data]]')); } - async.each(uids, function(uid, next) { - groups.join('administrators', uid, next); - }, callback); + user.getMultipleUserFields(uids, ['banned'], function(err, userData) { + if (err) { + return callback(err); + } + + for(var i=0; i= 0)) { + if(!data || !data.tid || !utils.isNumber(data.after) || parseInt(data.after, 10) < 0) { return callback(new Error('[[error:invalid-data]]')); } @@ -523,6 +521,12 @@ SocketTopics.search = function(socket, data, callback) { }; SocketTopics.searchAndLoadTags = function(socket, data, callback) { + if (!data) { + return callback(new Error('[[error:invalid-data]]')); + } + if (!data.query || !data.query.length) { + return callback(null, []); + } topics.searchTags(data, function(err, tags) { if (err) { return callback(err); diff --git a/src/socket.io/user.js b/src/socket.io/user.js index 8bfe07417d..a23b3f77c5 100644 --- a/src/socket.io/user.js +++ b/src/socket.io/user.js @@ -200,7 +200,7 @@ SocketUser.uploadProfileImageFromUrl = function(socket, url, callback) { callback(err, image.url); }); }); -} +}; SocketUser.follow = function(socket, data, callback) { if (!socket.uid || !data) { diff --git a/src/threadTools.js b/src/threadTools.js index 1e69868e62..22ceeafa07 100644 --- a/src/threadTools.js +++ b/src/threadTools.js @@ -207,6 +207,8 @@ var winston = require('winston'), topics.setTopicField(tid, 'cid', cid, callback); + events.logTopicMove(uid, tid); + plugins.fireHook('action:topic.move', { tid: tid, fromCid: oldCid, diff --git a/src/topics.js b/src/topics.js index da8ffb4776..4f011c937c 100644 --- a/src/topics.js +++ b/src/topics.js @@ -77,11 +77,11 @@ var async = require('async'), }; Topics.getPageCount = function(tid, uid, callback) { - db.sortedSetCard('tid:' + tid + ':posts', function(err, postCount) { - if(err) { + Topics.getTopicField(tid, 'postcount', function(err, postCount) { + if (err) { return callback(err); } - if(!parseInt(postCount, 10)) { + if (!parseInt(postCount, 10)) { return callback(null, 1); } user.getSettings(uid, function(err, settings) { @@ -140,34 +140,34 @@ var async = require('async'), } Topics.getTopicsByTids(tids, uid, function(err, topicData) { - if(err) { + if (err) { return callback(err); } - if(!topicData || !topicData.length) { + if(!Array.isArray(topicData) || !topicData.length) { return callback(null, returnTopics); } - db.sortedSetRevRank(set, topicData[topicData.length - 1].tid, function(err, rank) { - if(err) { - return callback(err); - } - - returnTopics.nextStart = parseInt(rank, 10) + 1; - returnTopics.topics = topicData; - callback(null, returnTopics); - }); + returnTopics.topics = topicData; + callback(null, returnTopics); }); }); }; Topics.getTopicsFromSet = function(uid, set, start, end, callback) { db.getSortedSetRevRange(set, start, end, function(err, tids) { - if(err) { + if (err) { return callback(err); } - Topics.getTopics(set, uid, tids, callback); + Topics.getTopics(set, uid, tids, function(err, data) { + if (err) { + return callback(err); + } + + data.nextStart = end + 1; + callback(null, data); + }); }); }; @@ -179,7 +179,7 @@ var async = require('async'), Topics.getTopicsData(tids, function(err, topics) { function mapFilter(array, field) { return array.map(function(topic) { - return topic && topic[field]; + return topic && topic[field] && topic[field].toString(); }).filter(function(value, index, array) { return utils.isNumber(value) && array.indexOf(value) === index; }); @@ -193,6 +193,9 @@ var async = require('async'), var cids = mapFilter(topics, 'cid'); async.parallel({ + teasers: function(next) { + Topics.getTeasers(tids, uid, next); + }, users: function(next) { user.getMultipleUserFields(uids, ['uid', 'username', 'userslug', 'picture'], next); }, @@ -205,9 +208,6 @@ var async = require('async'), isAdminOrMod: function(next) { privileges.categories.isAdminOrMod(cids, uid, next); }, - teasers: function(next) { - Topics.getTeasers(tids, uid, next); - }, tags: function(next) { Topics.getTopicsTagsObjects(tids, next); } @@ -246,7 +246,7 @@ var async = require('async'), }); plugins.fireHook('filter:topics.get', {topics: topics, uid: uid}, function(err, topicData) { - callback(err, topicData.topics) + callback(err, topicData.topics); }); }); }); @@ -346,20 +346,36 @@ var async = require('async'), }; Topics.getTeasers = function(tids, uid, callback) { - if(!Array.isArray(tids)) { + if (!Array.isArray(tids) || !tids.length) { return callback(null, []); } - async.map(tids, function(tid, next) { - db.getSortedSetRevRange('tid:' + tid + ':posts', 0, 0, function(err, data) { - next(err, Array.isArray(data) && data.length ? data[0] : null); - }); - }, function(err, pids) { + async.parallel({ + counts: function(next) { + Topics.getTopicsFields(tids, ['postcount'], function(err, topics) { + if (err) { + return next(err); + } + topics = topics.map(function(topic) { + return topic && topic.postcount; + }); + + next(null, topics); + }); + }, + pids: function(next) { + async.map(tids, function(tid, next) { + db.getSortedSetRevRange('tid:' + tid + ':posts', 0, 0, function(err, data) { + next(err, Array.isArray(data) && data.length ? data[0] : null); + }); + }, next); + } + }, function(err, results) { if (err) { return callback(err); } - var postKeys = pids.filter(Boolean).map(function(pid) { + var postKeys = results.pids.filter(Boolean).map(function(pid) { return 'post:' + pid; }); @@ -374,31 +390,27 @@ var async = require('async'), return array.indexOf(uid) === index; }); - async.parallel({ - users: function(next) { - user.getMultipleUserFields(uids, ['uid', 'username', 'userslug', 'picture'], next); - }, - indices: function(next) { - posts.getPostIndices(postData, uid, next); - } - }, function(err, results) { + + user.getMultipleUserFields(uids, ['uid', 'username', 'userslug', 'picture'], function(err, usersData) { if (err) { return callback(err); } var users = {}; - results.users.forEach(function(user) { + usersData.forEach(function(user) { users[user.uid] = user; }); var tidToPost = {}; - postData.forEach(function(post, index) { + postData.forEach(function(post) { post.user = users[post.uid]; - post.index = results.indices[index] + 1; post.timestamp = utils.toISOString(post.timestamp); tidToPost[post.tid] = post; }); - var teasers = tids.map(function(tid) { + var teasers = tids.map(function(tid, index) { + if (tidToPost[tid]) { + tidToPost[tid].index = results.counts[index] + 1; + } return tidToPost[tid]; }); diff --git a/src/topics/create.js b/src/topics/create.js index fc7033bfc7..63d6c478fb 100644 --- a/src/topics/create.js +++ b/src/topics/create.js @@ -10,6 +10,7 @@ var async = require('async'), meta = require('../meta'), posts = require('../posts'), threadTools = require('../threadTools'), + postTools = require('../postTools'), privileges = require('../privileges'), categories = require('../categories'); @@ -160,6 +161,10 @@ module.exports = function(Topics) { plugins.fireHook('action:topic.post', data.topicData); + if (parseInt(uid, 10)) { + user.notifications.sendTopicNotificationToFollowers(uid, data.topicData, data.postData); + } + next(null, { topicData: data.topicData, postData: data.postData @@ -237,6 +242,9 @@ module.exports = function(Topics) { }, postIndex: function(next) { posts.getPidIndex(postData.pid, uid, next); + }, + content: function(next) { + postTools.parse(postData.content, next); } }, next); }, @@ -244,6 +252,7 @@ module.exports = function(Topics) { postData.user = results.userInfo[0]; results.topicInfo.title = validator.escape(results.topicInfo.title); postData.topic = results.topicInfo; + postData.content = results.content; if (results.settings.followTopicsOnReply) { threadTools.follow(postData.tid, uid); @@ -257,9 +266,7 @@ module.exports = function(Topics) { postData.relativeTime = utils.toISOString(postData.timestamp); if (parseInt(uid, 10)) { - Topics.notifyFollowers(tid, postData.pid, uid); - - user.notifications.sendPostNotificationToFollowers(uid, tid, postData.pid); + Topics.notifyFollowers(postData.topic, postData, uid); } next(null, postData); diff --git a/src/topics/delete.js b/src/topics/delete.js index 06e289bdaf..8daec210be 100644 --- a/src/topics/delete.js +++ b/src/topics/delete.js @@ -8,33 +8,6 @@ var async = require('async'), module.exports = function(Topics) { - function updateCounters(tid, incr, callback) { - async.parallel([ - function(next) { - db.incrObjectFieldBy('global', 'topicCount', incr, next); - }, - function(next) { - Topics.getTopicFields(tid, ['cid', 'postcount'], function(err, topicData) { - if (err) { - return next(err); - } - var postCountChange = incr * parseInt(topicData.postcount, 10); - async.parallel([ - function(next) { - db.incrObjectFieldBy('global', 'postCount', postCountChange, next); - }, - function(next) { - db.incrObjectFieldBy('category:' + topicData.cid, 'post_count', postCountChange, next); - }, - function(next) { - db.incrObjectFieldBy('category:' + topicData.cid, 'topic_count', incr, next); - } - ], next); - }); - } - ], callback); - } - Topics.delete = function(tid, callback) { async.parallel([ function(next) { @@ -46,18 +19,12 @@ module.exports = function(Topics) { function(next) { db.sortedSetsRemove(['topics:posts', 'topics:views'], tid, next); } - ], function(err) { - if (err) { - return callback(err); - } - - updateCounters(tid, -1, callback); - }); + ], callback); }; Topics.restore = function(tid, callback) { Topics.getTopicFields(tid, ['lastposttime', 'postcount', 'viewcount'], function(err, topicData) { - if(err) { + if (err) { return callback(err); } @@ -74,13 +41,7 @@ module.exports = function(Topics) { function(next) { db.sortedSetAdd('topics:views', topicData.viewcount, tid, next); } - ], function(err) { - if (err) { - return callback(err); - } - - updateCounters(tid, 1, callback); - }); + ], callback); }); }; @@ -97,6 +58,9 @@ module.exports = function(Topics) { }, function(next) { Topics.deleteTopicTags(tid, next); + }, + function(next) { + reduceCounters(tid, next); } ], function(err) { if (err) { @@ -108,29 +72,43 @@ module.exports = function(Topics) { }; function deleteTopicFromCategoryAndUser(tid, callback) { - Topics.getTopicFields(tid, ['cid', 'uid', 'deleted'], function(err, topicData) { + Topics.getTopicFields(tid, ['cid', 'uid'], function(err, topicData) { if (err) { return callback(err); } - db.sortedSetsRemove(['categories:' + topicData.cid + ':tid', 'uid:' + topicData.uid + ':topics'], tid, function(err) { - if (err) { - return callback(err); - } - - if (parseInt(topicData.deleted, 10) === 0) { - async.parallel([ - function(next) { - db.decrObjectField('category:' + topicData.cid, 'topic_count', next); - }, - function(next) { - db.decrObjectField('global', 'topicCount', next); - } - ], callback); - } else { - callback(); - } - }); + db.sortedSetsRemove(['categories:' + topicData.cid + ':tid', 'uid:' + topicData.uid + ':topics'], tid, callback); }); } + + function reduceCounters(tid, callback) { + var incr = -1; + async.parallel([ + function(next) { + db.incrObjectFieldBy('global', 'topicCount', incr, next); + }, + function(next) { + Topics.getTopicFields(tid, ['cid', 'postcount'], function(err, topicData) { + if (err) { + return next(err); + } + topicData.postcount = parseInt(topicData.postcount, 10); + topicData.postcount = topicData.postcount || 0; + var postCountChange = incr * topicData.postcount; + + async.parallel([ + function(next) { + db.incrObjectFieldBy('global', 'postCount', postCountChange, next); + }, + function(next) { + db.incrObjectFieldBy('category:' + topicData.cid, 'post_count', postCountChange, next); + }, + function(next) { + db.incrObjectFieldBy('category:' + topicData.cid, 'topic_count', incr, next); + } + ], next); + }); + } + ], callback); + } }; diff --git a/src/topics/follow.js b/src/topics/follow.js index 588d629c82..740e410f29 100644 --- a/src/topics/follow.js +++ b/src/topics/follow.js @@ -21,8 +21,8 @@ module.exports = function(Topics) { db.getSetMembers('tid:' + tid + ':followers', callback); }; - Topics.notifyFollowers = function(tid, pid, exceptUid) { - Topics.getFollowers(tid, function(err, followers) { + Topics.notifyFollowers = function(topicData, postData, exceptUid) { + Topics.getFollowers(topicData.tid, function(err, followers) { if (err || !Array.isArray(followers) || !followers.length) { return; } @@ -36,34 +36,17 @@ module.exports = function(Topics) { return; } - async.parallel({ - title: async.apply(Topics.getTopicField, tid, 'title'), - username: async.apply(user.getUserField, exceptUid, 'username'), - postContent: function(next) { - async.waterfall([ - async.apply(posts.getPostField, pid, 'content'), - function(content, next) { - postTools.parse(content, next); - } - ], next); + notifications.create({ + bodyShort: '[[notifications:user_posted_to, ' + postData.user.username + ', ' + topicData.title + ']]', + bodyLong: postData.content, + pid: postData.pid, + nid: 'tid:' + topicData.tid + ':pid:' + postData.pid + ':uid:' + exceptUid, + tid: topicData.tid, + from: exceptUid + }, function(err, notification) { + if (!err && notification) { + notifications.push(notification, followers); } - }, function(err, results) { - if (err) { - return; - } - - notifications.create({ - bodyShort: '[[notifications:user_posted_to, ' + results.username + ', ' + results.title + ']]', - bodyLong: results.postContent, - pid: pid, - nid: 'tid:' + tid + ':pid:' + pid + ':uid:' + exceptUid, - tid: tid, - from: exceptUid - }, function(err, notification) { - if (!err && notification) { - notifications.push(notification, followers); - } - }); }); }); }; diff --git a/src/topics/popular.js b/src/topics/popular.js index 017153de69..b6dba43017 100644 --- a/src/topics/popular.js +++ b/src/topics/popular.js @@ -10,8 +10,7 @@ module.exports = function(Topics) { var terms = { daily: 'day', weekly: 'week', - monthly: 'month', - yearly: 'year' + monthly: 'month' }; Topics.getPopular = function(term, uid, count, callback) { @@ -29,23 +28,6 @@ module.exports = function(Topics) { }, function(tids, next) { getTopics(tids, uid, count, next); - }, - function(topics, next) { - var tids = topics.map(function(topic) { - return topic.tid; - }); - - privileges.topics.filter('read', tids, uid, function(err, tids) { - if (err) { - return next(err); - } - - topics = topics.filter(function(topic) { - return tids.indexOf(topic.tid) !== -1; - }); - - next(null, topics); - }); } ], callback); }; @@ -66,13 +48,19 @@ module.exports = function(Topics) { return callback(err); } - topics = topics.sort(function(a, b) { + tids = topics.sort(function(a, b) { return b.postcount - a.postcount; }).slice(0, count).map(function(topic) { return topic.tid; }); - Topics.getTopicsByTids(topics, uid, callback); + privileges.topics.filter('read', tids, uid, function(err, tids) { + if (err) { + return callback(err); + } + + Topics.getTopicsByTids(tids, uid, callback); + }); }); } }; diff --git a/src/topics/posts.js b/src/topics/posts.js index ee769add0e..67ceefe4c2 100644 --- a/src/topics/posts.js +++ b/src/topics/posts.js @@ -263,7 +263,7 @@ module.exports = function(Topics) { }; Topics.getPostCount = function(tid, callback) { - db.sortedSetCard('tid:' + tid + ':posts', callback); + db.getObjectField('topic:' + tid, 'postcount', callback); }; }; diff --git a/src/topics/recent.js b/src/topics/recent.js index e94dbe23a8..c8b6b551bc 100644 --- a/src/topics/recent.js +++ b/src/topics/recent.js @@ -17,11 +17,18 @@ module.exports = function(Topics) { Topics.getLatestTopics = function(uid, start, end, term, callback) { Topics.getLatestTids(start, end, term, function(err, tids) { - if(err) { + if (err) { return callback(err); } - Topics.getTopics('topics:recent', uid, tids, callback); + Topics.getTopics('topics:recent', uid, tids, function(err, data) { + if (err) { + return callback(err); + } + + data.nextStart = end + 1; + callback(null, data); + }); }); }; diff --git a/src/topics/tags.js b/src/topics/tags.js index 681d137fc9..07b65b9ceb 100644 --- a/src/topics/tags.js +++ b/src/topics/tags.js @@ -222,14 +222,15 @@ module.exports = function(Topics) { return callback(null, tags); } data.query = data.query.toLowerCase(); + var matches = []; for(var i=0; i b; }); diff --git a/src/topics/unread.js b/src/topics/unread.js index 7777f82ae5..271eed8ed7 100644 --- a/src/topics/unread.js +++ b/src/topics/unread.js @@ -45,16 +45,10 @@ module.exports = function(Topics) { return callback(null, unreadTopics); } - db.sortedSetRevRank('topics:recent', topicData[topicData.length - 1].tid, function(err, rank) { - if (err) { - return callback(err); - } + unreadTopics.topics = topicData; + unreadTopics.nextStart = stop + 1; - unreadTopics.topics = topicData; - unreadTopics.nextStart = parseInt(rank, 10) + 1; - - callback(null, unreadTopics); - }); + callback(null, unreadTopics); }); }); }; @@ -98,6 +92,8 @@ module.exports = function(Topics) { return topic.value; }); + tids = tids.slice(0, 100); + filterTopics(uid, tids, results.ignoredCids, function(err, tids) { if (err) { return callback(err); @@ -186,24 +182,45 @@ module.exports = function(Topics) { }); async.parallel({ - markRead: function(next) { - db.sortedSetAdd('uid:' + uid + ':tids_read', scores, tids, next); + topicScores: function(next) { + db.sortedSetScores('topics:recent', tids, next); }, - topicData: function(next) { - Topics.getTopicsFields(tids, ['cid'], next); + userScores: function(next) { + db.sortedSetScores('uid:' + uid + ':tids_read', tids, next); } }, function(err, results) { if (err) { return callback(err); } - var cids = results.topicData.map(function(topic) { - return topic && topic.cid; - }).filter(function(topic, index, array) { - return topic && array.indexOf(topic) === index; + tids = tids.filter(function(tid, index) { + return !results.userScores[index] || results.userScores[index] < results.topicScores[index]; }); - categories.markAsRead(cids, uid, callback); + if (!tids.length) { + return callback(); + } + + async.parallel({ + markRead: function(next) { + db.sortedSetAdd('uid:' + uid + ':tids_read', scores, tids, next); + }, + topicData: function(next) { + Topics.getTopicsFields(tids, ['cid'], next); + } + }, function(err, results) { + if (err) { + return callback(err); + } + + var cids = results.topicData.map(function(topic) { + return topic && topic.cid; + }).filter(function(topic, index, array) { + return topic && array.indexOf(topic) === index; + }); + + categories.markAsRead(cids, uid, callback); + }); }); }; diff --git a/src/upgrade.js b/src/upgrade.js index 65bbdd7a2d..606dcf722f 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -1,4 +1,4 @@ -"use strict"; +"use strict"; var db = require('./database'), async = require('async'), @@ -19,7 +19,7 @@ var db = require('./database'), schemaDate, thisSchemaDate, // IMPORTANT: REMEMBER TO UPDATE VALUE OF latestSchema - latestSchema = Date.UTC(2014, 8, 27); + latestSchema = Date.UTC(2014, 9, 14); Upgrade.check = function(callback) { db.get('schemaDate', function(err, value) { @@ -354,9 +354,11 @@ Upgrade.upgrade = function(callback) { gid; for(var groupName in mapping) { - gid = mapping[groupName]; - if (mapping.hasOwnProperty(groupName) && !reverseMapping.hasOwnProperty(gid)) { - reverseMapping[parseInt(gid, 10)] = groupName; + if (mapping.hasOwnProperty(groupName)) { + gid = mapping[groupName]; + if (mapping.hasOwnProperty(groupName) && !reverseMapping.hasOwnProperty(gid)) { + reverseMapping[parseInt(gid, 10)] = groupName; + } } } @@ -1042,6 +1044,107 @@ Upgrade.upgrade = function(callback) { winston.info('[2014/9/27] Deleting tid::read_by_uid skipped'); next(); } + }, + function(next) { + thisSchemaDate = Date.UTC(2014, 9, 7); + if (schemaDate < thisSchemaDate) { + winston.info('[2014/10/7] Banned users sorted set'); + + db.getSortedSetRange('users:joindate', 0, -1, function(err, uids) { + if (err) { + return next(err); + } + + var now = Date.now(); + + async.eachLimit(uids, 50, function(uid, next) { + User.getUserField(uid, 'banned', function(err, banned) { + if (err) { + return next(err); + } + + if (parseInt(banned, 10) !== 1) { + return next(); + } + + db.sortedSetAdd('users:banned', now, uid, next); + }); + }, function(err) { + if (err) { + winston.error('[2014/10/7] Error encountered while updating banned users sorted set'); + return next(err); + } + + winston.info('[2014/10/7] Banned users added to sorted set'); + Upgrade.update(thisSchemaDate, next); + }); + }); + } else { + winston.info('[2014/10/7] Banned users sorted set skipped'); + next(); + } + }, + function(next) { + function updateCategories(next) { + db.getSortedSetRange('categories:cid', 0, -1, function(err, cids) { + if (err) { + return next(err); + } + + async.eachLimit(cids, 5, function(cid, next) { + db.sortedSetCard('categories:' + cid + ':tid', function(err, count) { + if (err) { + return next(err); + } + count = parseInt(count, 10) || 0; + db.setObjectField('category:' + cid, 'topic_count', count, next); + }); + }, next); + }); + } + + function updateTopics(next) { + db.getSortedSetRange('topics:tid', 0, -1, function(err, tids) { + if (err) { + return next(err); + } + + async.eachLimit(tids, 50, function(tid, next) { + db.sortedSetCard('tid:' + tid + ':posts', function(err, count) { + if (err) { + return next(err); + } + count = parseInt(count, 10) || 0; + winston.info('updating tid ' + tid + ' count ' + count); + db.setObjectField('topic:' + tid, 'postcount', count, next); + }); + }, next); + }); + } + + thisSchemaDate = Date.UTC(2014, 9, 14); + if (schemaDate < thisSchemaDate) { + winston.info('[2014/10/14] Topic post count migration'); + + async.series([ + function(next) { + updateCategories(next); + }, + function(next) { + updateTopics(next); + } + ], function(err) { + if (err) { + winston.error('[2014/10/14] Error encountered while Topic post count migration'); + return next(err); + } + winston.info('[2014/10/14] Topic post count migration done'); + Upgrade.update(thisSchemaDate, next); + }); + } else { + winston.info('[2014/10/14] Topic post count migration skipped'); + next(); + } } // Add new schema updates here // IMPORTANT: REMEMBER TO UPDATE VALUE OF latestSchema IN LINE 22!!! diff --git a/src/user.js b/src/user.js index f9ef005c6b..a4920e14da 100644 --- a/src/user.js +++ b/src/user.js @@ -111,6 +111,7 @@ var async = require('async'), user.uid = 0; user.username = '[[global:guest]]'; user.userslug = ''; + user.picture = User.createGravatarURLFromEmail(''); } if (user.picture) { @@ -119,8 +120,6 @@ var async = require('async'), } else { user.picture = User.createGravatarURLFromEmail(user.email); } - } else { - user.picture = User.createGravatarURLFromEmail(''); } for(var i=0; i maxNotifs) { notifications.read.length = maxNotifs - notifications.unread.length; } @@ -45,7 +36,18 @@ var async = require('async'), }); }; - function getNotificationsFromSet(set, uid, start, stop, max, callback) { + function getNotifications(uid, count, callback) { + async.parallel({ + unread: function(next) { + getNotificationsFromSet('uid:' + uid + ':notifications:unread', false, uid, 0, count - 1, next); + }, + read: function(next) { + getNotificationsFromSet('uid:' + uid + ':notifications:read', true, uid, 0, count - 1, next); + } + }, callback); + } + + function getNotificationsFromSet(set, read, uid, start, stop, callback) { db.getSortedSetRevRange(set, start, stop, function(err, nids) { if (err) { return callback(err); @@ -55,10 +57,6 @@ var async = require('async'), return callback(null, []); } - if (nids.length > max) { - nids.length = max; - } - UserNotifications.getNotifications(nids, uid, function(err, notifications) { if (err) { return callback(err); @@ -73,6 +71,9 @@ var async = require('async'), } deletedNids.push(nids[index]); + } else { + notification.read = read; + notification.readClass = !notification.read ? 'label-warning' : ''; } }); @@ -86,30 +87,16 @@ var async = require('async'), } UserNotifications.getAll = function(uid, count, callback) { - async.parallel({ - unread: function(next) { - db.getSortedSetRevRange('uid:' + uid + ':notifications:unread', 0, count, next); - }, - read: function(next) { - db.getSortedSetRevRange('uid:' + uid + ':notifications:read', 0, count, next); - } - }, function(err, results) { + getNotifications(uid, count, function(err, notifs) { if (err) { return callback(err); } - - var nids = results.unread.concat(results.read); - UserNotifications.getNotifications(nids, uid, function(err, notifs) { - if (err) { - return callback(err); - } - - notifs = notifs.filter(Boolean).sort(function(a, b) { - return b.datetime - a.datetime; - }); - - callback(null, notifs); + notifs = notifs.unread.concat(notifs.read); + notifs = notifs.filter(Boolean).sort(function(a, b) { + return b.datetime - a.datetime; }); + + callback(null, notifs); }); }; @@ -119,34 +106,26 @@ var async = require('async'), return callback(err); } - db.isSortedSetMembers('uid:' + uid + ':notifications:read', nids, function(err, hasRead) { + var pids = notifications.map(function(notification) { + return notification ? notification.pid : null; + }); + + generatePostPaths(pids, uid, function(err, pidToPaths) { if (err) { return callback(err); } - var pids = notifications.map(function(notification) { - return notification ? notification.pid : null; - }); - - generatePostPaths(pids, uid, function(err, pidToPaths) { - if (err) { - return callback(err); + notifications = notifications.map(function(notification, index) { + if (!notification) { + return null; } - notifications = notifications.map(function(notification, index) { - if (!notification) { - return null; - } - - notification.read = hasRead[index]; - notification.path = pidToPaths[notification.pid] || notification.path || ''; - notification.datetimeISO = utils.toISOString(notification.datetime); - notification.readClass = !notification.read ? 'label-warning' : ''; - return notification; - }); - - callback(null, notifications); + notification.path = pidToPaths[notification.pid] || notification.path || ''; + notification.datetimeISO = utils.toISOString(notification.datetime); + return notification; }); + + callback(null, notifications); }); }); }; @@ -197,7 +176,7 @@ var async = require('async'), var now = Date.now(), yesterday = now - (1000*60*60*24); // Approximate, can be more or less depending on time changes, makes no difference really. - db.getSortedSetRangeByScore('uid:' + uid + ':notifications:unread', 0, 20, yesterday, now, function(err, nids) { + db.getSortedSetRevRangeByScore('uid:' + uid + ':notifications:unread', 0, 20, now, yesterday, function(err, nids) { if (err) { return callback(err); } @@ -206,16 +185,21 @@ var async = require('async'), return callback(null, []); } - UserNotifications.getNotifications(nids, uid, callback); + UserNotifications.getNotifications(nids, uid); }); }; UserNotifications.getUnreadCount = function(uid, callback) { - db.sortedSetCard('uid:' + uid + ':notifications:unread', callback); + if (!parseInt(uid, 10)) { + return callback(null, 0); + } + db.getSortedSetRevRange('uid:' + uid + ':notifications:unread', 0, 20, function(err, nids) { + callback(err, Array.isArray(nids) ? nids.length : 0); + }); }; UserNotifications.getUnreadByField = function(uid, field, value, callback) { - db.getSortedSetRange('uid:' + uid + ':notifications:unread', 0, -1, function(err, nids) { + db.getSortedSetRevRange('uid:' + uid + ':notifications:unread', 0, 99, function(err, nids) { if (err) { return callback(err); } @@ -224,7 +208,11 @@ var async = require('async'), return callback(null, []); } - UserNotifications.getNotifications(nids, uid, function(err, notifications) { + var keys = nids.map(function(nid) { + return 'notifications:' + nid; + }); + + db.getObjectsFields(keys, ['nid', field], function(err, notifications) { if (err) { return callback(err); } @@ -241,57 +229,28 @@ var async = require('async'), }); }; - - UserNotifications.sendPostNotificationToFollowers = function(uid, tid, pid) { + UserNotifications.sendTopicNotificationToFollowers = function(uid, topicData, postData) { db.getSetMembers('followers:' + uid, function(err, followers) { if (err || !Array.isArray(followers) || !followers.length) { return; } - async.parallel({ - username: async.apply(user.getUserField, uid, 'username'), - topic: async.apply(topics.getTopicFields, tid, ['cid', 'title']), - postContent: function(next) { - async.waterfall([ - async.apply(posts.getPostField, pid, 'content'), - function(content, next) { - postTools.parse(content, next); - } - ], next); - }, - topicFollowers: function(next) { - db.isSetMembers('tid:' + tid + ':followers', followers, next); - } - }, function(err, results) { - if (err) { + privileges.categories.filterUids('read', topicData.cid, followers, function(err, followers) { + if (err || !followers.length) { return; } - followers = followers.filter(function(value, index) { - return !results.topicFollowers[index]; - }); - - if (!followers.length) { - return; - } - - privileges.categories.filterUids('read', results.topic.cid, followers, function(err, followers) { - if (err || !followers.length) { - return; + notifications.create({ + bodyShort: '[[notifications:user_posted_topic, ' + postData.user.username + ', ' + topicData.title + ']]', + bodyLong: postData.content, + pid: postData.pid, + nid: 'tid:' + postData.tid + ':pid:' + postData.pid + ':uid:' + uid, + tid: postData.tid, + from: uid + }, function(err, notification) { + if (!err && notification) { + notifications.push(notification, followers); } - - notifications.create({ - bodyShort: '[[notifications:user_posted_to, ' + results.username + ', ' + results.topic.title + ']]', - bodyLong: results.postContent, - pid: pid, - nid: 'tid:' + tid + ':pid:' + pid + ':uid:' + uid, - tid: tid, - from: uid - }, function(err, notification) { - if (!err && notification) { - notifications.push(notification, followers); - } - }); }); }); }); diff --git a/src/user/settings.js b/src/user/settings.js index a98a050006..0107410b63 100644 --- a/src/user/settings.js +++ b/src/user/settings.js @@ -25,6 +25,7 @@ module.exports = function(User) { settings = data.settings; settings.showemail = parseInt(settings.showemail, 10) === 1; + settings.showfullname = parseInt(settings.showfullname, 10) === 1; settings.openOutgoingLinksInNewTab = parseInt(settings.openOutgoingLinksInNewTab, 10) === 1; settings.dailyDigestFreq = settings.dailyDigestFreq || 'off'; settings.usePagination = (settings.usePagination === null || settings.usePagination === undefined) ? parseInt(meta.config.usePagination, 10) === 1 : parseInt(settings.usePagination, 10) === 1; @@ -77,6 +78,7 @@ module.exports = function(User) { plugins.fireHook('action:user.saveSettings', {uid: uid, settings: data}); db.setObject('user:' + uid + ':settings', { showemail: data.showemail, + showfullname: data.showfullname, openOutgoingLinksInNewTab: data.openOutgoingLinksInNewTab, dailyDigestFreq: data.dailyDigestFreq || 'off', usePagination: data.usePagination, diff --git a/src/views/403.tpl b/src/views/403.tpl index 46478a224c..954637c16c 100644 --- a/src/views/403.tpl +++ b/src/views/403.tpl @@ -1,4 +1,8 @@
    [[global:403.title]] + +

    {error}

    +

    [[global:403.message]]

    +
    \ No newline at end of file diff --git a/src/views/404.tpl b/src/views/404.tpl index 95cd1b9383..2c2e4a89e9 100644 --- a/src/views/404.tpl +++ b/src/views/404.tpl @@ -1,4 +1,8 @@
    [[global:404.title]] + +

    {error}

    +

    [[global:404.message]]

    +
    \ No newline at end of file diff --git a/src/views/admin/advanced/events.tpl b/src/views/admin/advanced/events.tpl index 691ec39c3b..03e3b7f4eb 100644 --- a/src/views/admin/advanced/events.tpl +++ b/src/views/admin/advanced/events.tpl @@ -2,9 +2,30 @@
    Events
    -
    +
    {eventdata}
    -
    \ No newline at end of file +
    + + + \ No newline at end of file diff --git a/src/views/admin/advanced/logs.tpl b/src/views/admin/advanced/logs.tpl new file mode 100644 index 0000000000..e4f8d2b2fc --- /dev/null +++ b/src/views/admin/advanced/logs.tpl @@ -0,0 +1,10 @@ +
    +
    +
    +
    Logs
    +
    +
    {data}
    +
    +
    +
    +
    diff --git a/src/views/admin/appearance/skins.tpl b/src/views/admin/appearance/skins.tpl index 2b74899879..e3e91680af 100644 --- a/src/views/admin/appearance/skins.tpl +++ b/src/views/admin/appearance/skins.tpl @@ -25,7 +25,7 @@ diff --git a/src/views/admin/extend/plugins.tpl b/src/views/admin/extend/plugins.tpl index 66245e26bc..63e5dbbad0 100644 --- a/src/views/admin/extend/plugins.tpl +++ b/src/views/admin/extend/plugins.tpl @@ -1,5 +1,5 @@
    -
    +
    Installed Plugins
    @@ -10,6 +10,8 @@
    + +
    @@ -18,6 +20,10 @@

    {plugins.description}

    + Installed {plugins.version} | Latest {plugins.latest} + + +

    For more information: {plugins.url}

    @@ -36,6 +42,7 @@
  • +
    diff --git a/src/views/admin/extend/widgets.tpl b/src/views/admin/extend/widgets.tpl index c856a70645..aa47e49ffe 100644 --- a/src/views/admin/extend/widgets.tpl +++ b/src/views/admin/extend/widgets.tpl @@ -38,7 +38,7 @@

    Drag and drop widgets into templates

    -
    No widgets found! Activate the essential widgets plugin in the plugins control panel.
    +
    No widgets found! Activate the essential widgets plugin in the plugins control panel.
    diff --git a/src/views/admin/footer.tpl b/src/views/admin/footer.tpl index df590eb6f9..394d4499b5 100644 --- a/src/views/admin/footer.tpl +++ b/src/views/admin/footer.tpl @@ -46,10 +46,5 @@ - - - \ No newline at end of file diff --git a/src/views/admin/general/dashboard.tpl b/src/views/admin/general/dashboard.tpl index 27a840a60c..32c4fd7a56 100644 --- a/src/views/admin/general/dashboard.tpl +++ b/src/views/admin/general/dashboard.tpl @@ -13,46 +13,67 @@
    - - -
    -
    Notices
    -
    - -
    - {notices.doneText} {notices.notDoneText} +
    + +
    +
    +
    {stats.name}
    +
    +
    +
    +
    {stats.day}
    +
    Day
    +
    +
    +
    {stats.week}
    +
    Week
    +
    +
    +
    {stats.month}
    +
    Month
    +
    +
    +
    {stats.alltime}
    +
    All Time
    +
    +
    +
    -
    -
    + - -
    -
    Updates
    -
    -
    -

    You are running NodeBB v{version}.

    +
    +
    +
    Notices
    +
    + +
    + {notices.doneText} {notices.notDoneText} +
    + +
    +
    + + +
    +
    Updates
    +
    +
    +

    You are running NodeBB v{version}.

    +
    +

    + 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. +

    +
    -

    - 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. -

    @@ -95,31 +116,7 @@
    - -
    -
    {stats.name}
    -
    -
    -
    -
    {stats.day}
    -
    Day
    -
    -
    -
    {stats.week}
    -
    Week
    -
    -
    -
    {stats.month}
    -
    Month
    -
    -
    -
    {stats.alltime}
    -
    All Time
    -
    -
    -
    -
    - +
    Active Users
    diff --git a/src/views/admin/general/languages.tpl b/src/views/admin/general/languages.tpl index ab13f5a592..bc75b2fee4 100644 --- a/src/views/admin/general/languages.tpl +++ b/src/views/admin/general/languages.tpl @@ -1,5 +1,5 @@
    -
    +
    Language Settings
    diff --git a/src/views/admin/general/sounds.tpl b/src/views/admin/general/sounds.tpl index 13194c95c5..3556bcbb7d 100644 --- a/src/views/admin/general/sounds.tpl +++ b/src/views/admin/general/sounds.tpl @@ -8,6 +8,7 @@
    + @@ -41,6 +43,7 @@
    +
    +
  • Categories
  • Tags
  • -
  • Users
  • +
  • Users
  • Groups
  • +
  • Flags
  • + + + +