diff --git a/.tx/config b/.tx/config index 65e41f74b2..d25edbb3a2 100644 --- a/.tx/config +++ b/.tx/config @@ -613,4 +613,112 @@ trans.tr = public/language/tr/tags.json trans.vi = public/language/vi/tags.json trans.zh_CN = public/language/zh_CN/tags.json trans.zh_TW = public/language/zh_TW/tags.json +type = KEYVALUEJSON + +[nodebb.email] +source_file = public/language/en_GB/email.json +source_lang = en_GB +trans.ar = public/language/ar/email.json +trans.cs = public/language/cs/email.json +trans.de = public/language/de/email.json +trans.en_US = public/language/en_US/email.json +trans.en@pirate = public/language/en@pirate/email.json +trans.es = public/language/es/email.json +trans.et = public/language/et/email.json +trans.fa_IR = public/language/fa_IR/email.json +trans.fi = public/language/fi/email.json +trans.fr = public/language/fr/email.json +trans.he = public/language/he/email.json +trans.hu = public/language/hu/email.json +trans.it = public/language/it/email.json +trans.ja = public/language/ja/email.json +trans.ko = public/language/ko/email.json +trans.lt = public/language/lt/email.json +trans.ms = public/language/ms/email.json +trans.nb = public/language/nb/email.json +trans.nl = public/language/nl/email.json +trans.pl = public/language/pl/email.json +trans.pt_BR = public/language/pt_BR/email.json +trans.ru = public/language/ru/email.json +trans.ro = public/language/ro/email.json +trans.sc = public/language/sc/email.json +trans.sk = public/language/sk/email.json +trans.sv = public/language/sv/email.json +trans.th = public/language/th/email.json +trans.tr = public/language/tr/email.json +trans.vi = public/language/vi/email.json +trans.zh_CN = public/language/zh_CN/email.json +trans.zh_TW = public/language/zh_TW/email.json +type = KEYVALUEJSON + +[nodebb.search] +source_file = public/language/en_GB/search.json +source_lang = en_GB +trans.ar = public/language/ar/search.json +trans.cs = public/language/cs/search.json +trans.de = public/language/de/search.json +trans.en_US = public/language/en_US/search.json +trans.en@pirate = public/language/en@pirate/search.json +trans.es = public/language/es/search.json +trans.et = public/language/et/search.json +trans.fa_IR = public/language/fa_IR/search.json +trans.fi = public/language/fi/search.json +trans.fr = public/language/fr/search.json +trans.he = public/language/he/search.json +trans.hu = public/language/hu/search.json +trans.it = public/language/it/search.json +trans.ja = public/language/ja/search.json +trans.ko = public/language/ko/search.json +trans.lt = public/language/lt/search.json +trans.ms = public/language/ms/search.json +trans.nb = public/language/nb/search.json +trans.nl = public/language/nl/search.json +trans.pl = public/language/pl/search.json +trans.pt_BR = public/language/pt_BR/search.json +trans.ru = public/language/ru/search.json +trans.ro = public/language/ro/search.json +trans.sc = public/language/sc/search.json +trans.sk = public/language/sk/search.json +trans.sv = public/language/sv/search.json +trans.th = public/language/th/search.json +trans.tr = public/language/tr/search.json +trans.vi = public/language/vi/search.json +trans.zh_CN = public/language/zh_CN/search.json +trans.zh_TW = public/language/zh_TW/search.json +type = KEYVALUEJSON + +[nodebb.groups] +source_file = public/language/en_GB/groups.json +source_lang = en_GB +trans.ar = public/language/ar/groups.json +trans.cs = public/language/cs/groups.json +trans.de = public/language/de/groups.json +trans.en_US = public/language/en_US/groups.json +trans.en@pirate = public/language/en@pirate/groups.json +trans.es = public/language/es/groups.json +trans.et = public/language/et/groups.json +trans.fa_IR = public/language/fa_IR/groups.json +trans.fi = public/language/fi/groups.json +trans.fr = public/language/fr/groups.json +trans.he = public/language/he/groups.json +trans.hu = public/language/hu/groups.json +trans.it = public/language/it/groups.json +trans.ja = public/language/ja/groups.json +trans.ko = public/language/ko/groups.json +trans.lt = public/language/lt/groups.json +trans.ms = public/language/ms/groups.json +trans.nb = public/language/nb/groups.json +trans.nl = public/language/nl/groups.json +trans.pl = public/language/pl/groups.json +trans.pt_BR = public/language/pt_BR/groups.json +trans.ru = public/language/ru/groups.json +trans.ro = public/language/ro/groups.json +trans.sc = public/language/sc/groups.json +trans.sk = public/language/sk/groups.json +trans.sv = public/language/sv/groups.json +trans.th = public/language/th/groups.json +trans.tr = public/language/tr/groups.json +trans.vi = public/language/vi/groups.json +trans.zh_CN = public/language/zh_CN/groups.json +trans.zh_TW = public/language/zh_TW/groups.json type = KEYVALUEJSON \ No newline at end of file diff --git a/README.md b/README.md index 485922d449..b08b6c3a84 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # NodeBB -[![Build Status](https://travis-ci.org/NodeBB/NodeBB.svg?branch=master)](https://travis-ci.org/nodebb/nodebb) +[![Build Status](https://travis-ci.org/NodeBB/NodeBB.svg?branch=master)](https://travis-ci.org/NodeBB/NodeBB) [![Dependency Status](https://david-dm.org/nodebb/nodebb.svg)](https://david-dm.org/nodebb/nodebb) [![Code Climate](https://codeclimate.com/github/designcreateplay/NodeBB.png)](https://codeclimate.com/github/designcreateplay/NodeBB) diff --git a/app.js b/app.js index 26fe077f87..bd9cb02e0a 100644 --- a/app.js +++ b/app.js @@ -141,7 +141,13 @@ function start() { nconf.set('url', nconf.get('base_url') + (nconf.get('use_port') ? ':' + nconf.get('port') : '') + nconf.get('relative_path')); plugins.ready(function() { - webserver.init(); + webserver.init(function() { + // If this callback is called, this means that loader.js is used + process.on('SIGCONT', webserver.listen); + process.send({ + action: 'ready' + }); + }); }); process.on('SIGTERM', shutdown); @@ -313,6 +319,8 @@ function shutdown(code) { winston.info('[app] Shutdown (SIGTERM/SIGINT) Initialised.'); require('./src/database').close(); winston.info('[app] Database connection closed.'); + require('./src/webserver').server.close(); + winston.info('[app] Web server closed to connections.'); winston.info('[app] Shutdown complete.'); process.exit(code || 0); diff --git a/bcrypt.js b/bcrypt.js new file mode 100644 index 0000000000..a926310923 --- /dev/null +++ b/bcrypt.js @@ -0,0 +1,30 @@ + +'use strict'; + + +var bcrypt = require('bcryptjs'); + +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}); + }); +} \ No newline at end of file diff --git a/install/data/defaults.json b/install/data/defaults.json index fc5369de82..0ecd7b5fd1 100644 --- a/install/data/defaults.json +++ b/install/data/defaults.json @@ -63,6 +63,10 @@ "field": "maximumProfileImageSize", "value": 256 }, + { + "field": "profileImageDimension", + "value": 128 + }, { "field": "chatMessagesToDisplay", "value": 50 diff --git a/loader.js b/loader.js index 6448615348..a3ce275704 100644 --- a/loader.js +++ b/loader.js @@ -5,8 +5,7 @@ var nconf = require('nconf'), pidFilePath = __dirname + '/pidfile', output = fs.openSync(__dirname + '/logs/output.log', 'a'), start = function() { - var fork = require('child_process').fork, - nbb_start = function() { + var nbb_start = function(callback) { if (timesStarted > 3) { console.log('\n[loader] Experienced three start attempts in 10 seconds, most likely an error on startup. Halting.'); return nbb_stop(); @@ -18,14 +17,24 @@ var nconf = require('nconf'), } startTimer = setTimeout(resetTimer, 1000*10); - nbb = fork('./app', process.argv.slice(2), { + if (nbb) { + nbbOld = nbb; + } + + nbb = require('child_process').fork('./app', process.argv.slice(2), { env: process.env }); nbb.on('message', function(message) { if (message && typeof message === 'object' && message.action) { - if (message.action === 'restart') { - nbb_restart(); + switch (message.action) { + case 'ready': + if (!callback) return nbb.kill('SIGCONT'); + callback(); + break; + case 'restart': + nbb_restart(); + break; } } }); @@ -52,10 +61,12 @@ var nconf = require('nconf'), } }, nbb_restart = function() { - nbb.removeAllListeners('exit').on('exit', function() { - nbb_start(); + nbb_start(function() { + nbbOld.removeAllListeners('exit').on('exit', function() { + nbb.kill('SIGCONT'); + }); + nbbOld.kill(); }); - nbb.kill(); }, resetTimer = function() { clearTimeout(startTimer); @@ -70,7 +81,7 @@ var nconf = require('nconf'), nbb_start(); }, - nbb; + nbb, nbbOld; nconf.argv(); diff --git a/package.json b/package.json index b185dc0af9..01592c9035 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "nodebb", "license": "GPLv3 or later", "description": "NodeBB Forum", - "version": "0.4.3", + "version": "0.5.0-2", "homepage": "http://www.nodebb.org", "repository": { "type": "git", @@ -34,7 +34,7 @@ "less": "~1.7.3", "mkdirp": "~0.5.0", "nconf": "~0.6.7", - "nodebb-plugin-dbsearch": "0.0.12", + "nodebb-plugin-dbsearch": "0.0.13", "nodebb-plugin-markdown": "~0.5.0", "nodebb-plugin-mentions": "~0.5.0", "nodebb-plugin-soundpack-default": "~0.1.1", @@ -58,7 +58,7 @@ "validator": "~3.16.1", "winston": "~0.7.2", "xregexp": "~2.0.0", - "templates.js": "0.0.8" + "templates.js": "0.0.13" }, "devDependencies": { "mocha": "~1.13.0" @@ -67,7 +67,7 @@ "url": "https://github.com/NodeBB/NodeBB/issues" }, "engines": { - "node": ">=0.8" + "node": ">=0.10" }, "maintainers": [ { diff --git a/public/language/ar/email.json b/public/language/ar/email.json new file mode 100644 index 0000000000..98e591ab02 --- /dev/null +++ b/public/language/ar/email.json @@ -0,0 +1,20 @@ +{ + "password-reset-requested": "Password Reset Requested - %1!", + "welcome-to": "Welcome to %1", + "greeting_no_name": "Hello", + "greeting_with_name": "Hello %1", + "welcome.text1": "Thank you for registering with %1!", + "welcome.text2": "To fully activate your account, we need to verify that you own the email address you registered with.", + "welcome.cta": "Click here to confirm your email address", + "reset.text1": "We received a request to reset your password, possibly because you have forgotten it. If this is not the case, please ignore this email.", + "reset.text2": "To continue with the password reset, please click on the following link:", + "reset.cta": "Click here to reset your password", + "digest.notifications": "You have some unread notifications from %1:", + "digest.latest_topics": "Latest topics from %1", + "digest.cta": "Click here to visit %1", + "digest.unsub.info": "This digest was sent to you due to your subscription settings.", + "digest.unsub.cta": "Click here to alter those settings", + "digest.daily.no_topics": "There have been no active topics in the past day", + "test.text1": "This is a test email to verify that the emailer is set up correctly for your NodeBB.", + "closing": "Thanks!" +} \ No newline at end of file diff --git a/public/language/ar/error.json b/public/language/ar/error.json index a45bbc0651..df25651826 100644 --- a/public/language/ar/error.json +++ b/public/language/ar/error.json @@ -25,6 +25,7 @@ "no-user": "المستخدم لا يوجد", "no-teaser": "Teaser doesn't exist", "no-privileges": "You don't 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", "topic-locked": "الموضوع مقفول", "still-uploading": "الرجاء انتظار الرفع", diff --git a/public/language/ar/footer.json b/public/language/ar/footer.json deleted file mode 100644 index 23e87f04e4..0000000000 --- a/public/language/ar/footer.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "stats.online": "حالياً على الموقع", - "stats.users": "مستخدمين", - "stats.topics": "مواضيع", - "stats.posts": "مشاركات", - "success": "نجاح" -} \ No newline at end of file diff --git a/public/language/ar/global.json b/public/language/ar/global.json index 195a295122..8808c4a827 100644 --- a/public/language/ar/global.json +++ b/public/language/ar/global.json @@ -13,7 +13,7 @@ "please_log_in": "Please Log In", "logout": "تسجيل الخروج", "posting_restriction_info": "Posting is currently restricted to registered members only, click here to log in.", - "welcome_back": "Welcome Back ", + "welcome_back": "Welcome Back", "you_have_successfully_logged_in": "You have successfully logged in", "save_changes": "حفظ التغييرات", "close": "أغلق", diff --git a/public/language/ar/groups.json b/public/language/ar/groups.json new file mode 100644 index 0000000000..c00c111e11 --- /dev/null +++ b/public/language/ar/groups.json @@ -0,0 +1,7 @@ +{ + "view_group": "View Group", + "details.title": "Group Details", + "details.members": "Member List", + "details.has_no_posts": "This group's members have not made any posts.", + "details.latest_posts": "Latest Posts" +} \ No newline at end of file diff --git a/public/language/ar/notifications.json b/public/language/ar/notifications.json index 26f294bdd7..32d6b62dcf 100644 --- a/public/language/ar/notifications.json +++ b/public/language/ar/notifications.json @@ -4,12 +4,11 @@ "see_all": "See all Notifications", "back_to_home": "Back to %1", "outgoing_link": "رابط خارجي", - "outgoing_link_message": "أنت الأن ترحل", - "continue_to": "أكمل إلى", - "return_to": "Return to", + "outgoing_link_message": "You are now leaving %1.", + "continue_to": "Continue to %1", + "return_to": "Return to %1", "new_notification": "New Notification", "you_have_unread_notifications": "You have unread notifications.", - "user_made_post": "%1 made a new post", "new_message_from": "New message from %1", "upvoted_your_post": "%1 has upvoted your post.", "favourited_your_post": "%1 has favourited your post.", diff --git a/public/language/ar/pages.json b/public/language/ar/pages.json index 4624ca716b..c39934513a 100644 --- a/public/language/ar/pages.json +++ b/public/language/ar/pages.json @@ -5,6 +5,7 @@ "recent": "Recent Topics", "users": "Registered Users", "notifications": "Notifications", + "tags": "Topics tagged under \"%1\"", "user.edit": "Editing \"%1\"", "user.following": "People %1 Follows", "user.followers": "People who Follow %1", diff --git a/public/language/ar/search.json b/public/language/ar/search.json new file mode 100644 index 0000000000..d0ffc64f36 --- /dev/null +++ b/public/language/ar/search.json @@ -0,0 +1,3 @@ +{ + "results_matching": "%1 result(s) matching \"%2\", (%3 seconds)" +} \ No newline at end of file diff --git a/public/language/ar/topic.json b/public/language/ar/topic.json index 50f8e43a59..930f642748 100644 --- a/public/language/ar/topic.json +++ b/public/language/ar/topic.json @@ -87,6 +87,7 @@ "more_users_and_guests": "%1 more user(s) and %2 guest(s)", "more_users": "%1 more user(s)", "more_guests": "%1 more guest(s)", + "users_and_others": "%1 and %2 others", "sort_by": "Sort by", "oldest_to_newest": "Oldest to Newest", "newest_to_oldest": "Newest to Oldest", diff --git a/public/language/cs/email.json b/public/language/cs/email.json new file mode 100644 index 0000000000..98e591ab02 --- /dev/null +++ b/public/language/cs/email.json @@ -0,0 +1,20 @@ +{ + "password-reset-requested": "Password Reset Requested - %1!", + "welcome-to": "Welcome to %1", + "greeting_no_name": "Hello", + "greeting_with_name": "Hello %1", + "welcome.text1": "Thank you for registering with %1!", + "welcome.text2": "To fully activate your account, we need to verify that you own the email address you registered with.", + "welcome.cta": "Click here to confirm your email address", + "reset.text1": "We received a request to reset your password, possibly because you have forgotten it. If this is not the case, please ignore this email.", + "reset.text2": "To continue with the password reset, please click on the following link:", + "reset.cta": "Click here to reset your password", + "digest.notifications": "You have some unread notifications from %1:", + "digest.latest_topics": "Latest topics from %1", + "digest.cta": "Click here to visit %1", + "digest.unsub.info": "This digest was sent to you due to your subscription settings.", + "digest.unsub.cta": "Click here to alter those settings", + "digest.daily.no_topics": "There have been no active topics in the past day", + "test.text1": "This is a test email to verify that the emailer is set up correctly for your NodeBB.", + "closing": "Thanks!" +} \ No newline at end of file diff --git a/public/language/cs/error.json b/public/language/cs/error.json index 4d55d9a4f3..77c582b23f 100644 --- a/public/language/cs/error.json +++ b/public/language/cs/error.json @@ -25,6 +25,7 @@ "no-user": "User doesn't exist", "no-teaser": "Teaser doesn't exist", "no-privileges": "You don't 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", "topic-locked": "Topic Locked", "still-uploading": "Please wait for uploads to complete.", diff --git a/public/language/cs/footer.json b/public/language/cs/footer.json deleted file mode 100644 index adbbcde71c..0000000000 --- a/public/language/cs/footer.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "stats.online": "Online", - "stats.users": "Uživatelé", - "stats.topics": "Témata", - "stats.posts": "Příspěvky", - "success": "úspěch" -} \ No newline at end of file diff --git a/public/language/cs/global.json b/public/language/cs/global.json index a32b50ff1a..d833eb88a0 100644 --- a/public/language/cs/global.json +++ b/public/language/cs/global.json @@ -13,7 +13,7 @@ "please_log_in": "Please Log In", "logout": "Odhlásit se", "posting_restriction_info": "Posting is currently restricted to registered members only, click here to log in.", - "welcome_back": "Welcome Back ", + "welcome_back": "Welcome Back", "you_have_successfully_logged_in": "You have successfully logged in", "save_changes": "Uložit změny", "close": "Zrušit", diff --git a/public/language/cs/groups.json b/public/language/cs/groups.json new file mode 100644 index 0000000000..c00c111e11 --- /dev/null +++ b/public/language/cs/groups.json @@ -0,0 +1,7 @@ +{ + "view_group": "View Group", + "details.title": "Group Details", + "details.members": "Member List", + "details.has_no_posts": "This group's members have not made any posts.", + "details.latest_posts": "Latest Posts" +} \ No newline at end of file diff --git a/public/language/cs/notifications.json b/public/language/cs/notifications.json index 4d419dd18d..e315bfd6c0 100644 --- a/public/language/cs/notifications.json +++ b/public/language/cs/notifications.json @@ -4,12 +4,11 @@ "see_all": "See all Notifications", "back_to_home": "Back to %1", "outgoing_link": "Odkaz mimo fórum", - "outgoing_link_message": "Nyní opouštíte fórum", - "continue_to": "Přejít na", - "return_to": "Return to", + "outgoing_link_message": "You are now leaving %1.", + "continue_to": "Continue to %1", + "return_to": "Return to %1", "new_notification": "New Notification", "you_have_unread_notifications": "You have unread notifications.", - "user_made_post": "%1 made a new post", "new_message_from": "New message from %1", "upvoted_your_post": "%1 has upvoted your post.", "favourited_your_post": "%1 has favourited your post.", diff --git a/public/language/cs/pages.json b/public/language/cs/pages.json index 4624ca716b..c39934513a 100644 --- a/public/language/cs/pages.json +++ b/public/language/cs/pages.json @@ -5,6 +5,7 @@ "recent": "Recent Topics", "users": "Registered Users", "notifications": "Notifications", + "tags": "Topics tagged under \"%1\"", "user.edit": "Editing \"%1\"", "user.following": "People %1 Follows", "user.followers": "People who Follow %1", diff --git a/public/language/cs/search.json b/public/language/cs/search.json new file mode 100644 index 0000000000..d0ffc64f36 --- /dev/null +++ b/public/language/cs/search.json @@ -0,0 +1,3 @@ +{ + "results_matching": "%1 result(s) matching \"%2\", (%3 seconds)" +} \ No newline at end of file diff --git a/public/language/cs/topic.json b/public/language/cs/topic.json index 380ce53ff2..fd1e316089 100644 --- a/public/language/cs/topic.json +++ b/public/language/cs/topic.json @@ -87,6 +87,7 @@ "more_users_and_guests": "%1 more user(s) and %2 guest(s)", "more_users": "%1 more user(s)", "more_guests": "%1 more guest(s)", + "users_and_others": "%1 and %2 others", "sort_by": "Sort by", "oldest_to_newest": "Oldest to Newest", "newest_to_oldest": "Newest to Oldest", diff --git a/public/language/de/email.json b/public/language/de/email.json new file mode 100644 index 0000000000..e3c67dc47b --- /dev/null +++ b/public/language/de/email.json @@ -0,0 +1,20 @@ +{ + "password-reset-requested": "Zurücksetzung des Passworts beantragt - %1!", + "welcome-to": "Willkommen zu %1", + "greeting_no_name": "Hallo", + "greeting_with_name": "Hallo %1", + "welcome.text1": "Vielen Dank für die Registrierung mit %1!", + "welcome.text2": "Um dein Konto vollständig zu aktivieren, müssen wir überprüfen, ob du Besitzer der E-Mail-Adresse bist, mit der du dich registriert hast.", + "welcome.cta": "Klicke hier, um deine E-Mail-Adresse zu bestätigen.", + "reset.text1": "Wir haben eine Anfrage auf Zurücksetzung deines Passworts erhalten, wahrscheinlich, weil du es vergessen hast. Falls dies nicht der Fall ist, ignoriere bitte diese E-Mail.", + "reset.text2": "Klicke bitte auf den folgenden Link, um mit der Zurücksetzung deines Passworts fortzufahren:", + "reset.cta": "Klicke hier, um dein Passwort zurückzusetzen", + "digest.notifications": "Du hast einige ungelesene Benachrichtigungen vom %1:", + "digest.latest_topics": "Aktuellste Themen vom %1", + "digest.cta": "Klicke hier, um %1 zu besuchen", + "digest.unsub.info": "Diese Zusammenfassung wurde dir aufgrund deiner Abonnement-Einstellungen gesendet.", + "digest.unsub.cta": "Klicke hier, um diese Einstellungen zu ändern", + "digest.daily.no_topics": "Es gab heute keine aktiven Themen", + "test.text1": "Dies ist eine Test-E-Mail, um zu überprüfen, ob der E-Mailer deines NodeBB korrekt eingestellt wurde.", + "closing": "Danke!" +} \ No newline at end of file diff --git a/public/language/de/error.json b/public/language/de/error.json index a829c0839e..73e0f64c0e 100644 --- a/public/language/de/error.json +++ b/public/language/de/error.json @@ -25,6 +25,7 @@ "no-user": "Der Benutzer existiert nicht", "no-teaser": "Kurztext existiert nicht", "no-privileges": "Du verfügst nicht über ausreichende Berechtigungen, um die Aktion durchzuführen.", + "no-emailers-configured": "Es wurde keine E-Mail-Plugins geladen, weshalb eine Test-E-Mail nicht gesendet werden konnte.", "category-disabled": "Kategorie ist deaktiviert", "topic-locked": "Thema ist gesperrt", "still-uploading": "Bitte warte bis der Vorgang abgeschlossen ist.", diff --git a/public/language/de/footer.json b/public/language/de/footer.json deleted file mode 100644 index fefe7b710c..0000000000 --- a/public/language/de/footer.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "stats.online": "Online", - "stats.users": "Benutzer", - "stats.topics": "Themen", - "stats.posts": "Beiträge", - "success": "Erfolg" -} \ No newline at end of file diff --git a/public/language/de/groups.json b/public/language/de/groups.json new file mode 100644 index 0000000000..445891944a --- /dev/null +++ b/public/language/de/groups.json @@ -0,0 +1,7 @@ +{ + "view_group": "Gruppe betrachten", + "details.title": "Gruppendetails", + "details.members": "Mitgliederliste", + "details.has_no_posts": "Die Mitglieder dieser Gruppe haben keine Beiträge verfasst.", + "details.latest_posts": "Aktuelle Beiträge" +} \ No newline at end of file diff --git a/public/language/de/notifications.json b/public/language/de/notifications.json index 1f1f8401f0..295796f5d6 100644 --- a/public/language/de/notifications.json +++ b/public/language/de/notifications.json @@ -4,12 +4,11 @@ "see_all": "Alle Benachrichtigungen ansehen", "back_to_home": "Zurück zu %1", "outgoing_link": "Externer Link", - "outgoing_link_message": "Du verlässt nun", - "continue_to": "Gehe weiter zu", - "return_to": "Zurück zu", + "outgoing_link_message": "Du verlässt nun %1.", + "continue_to": "Fortfahren zu %1", + "return_to": "Kehre zurück zu %1", "new_notification": "Neue Benachrichtigung", "you_have_unread_notifications": "Du hast ungelesene Benachrichtigungen.", - "user_made_post": "%1 hat einen Beitrag erstellt.", "new_message_from": "Neue Nachricht von %1", "upvoted_your_post": "%1 hat deinen Beitrag positiv bewertet.", "favourited_your_post": "%1 favorisiert deinen Beitrag.", diff --git a/public/language/de/pages.json b/public/language/de/pages.json index 41ddcbb35c..c49854a2a4 100644 --- a/public/language/de/pages.json +++ b/public/language/de/pages.json @@ -5,6 +5,7 @@ "recent": "Neueste Themen", "users": "Registrierte User", "notifications": "Benachrichtigungen", + "tags": "Topics tagged under \"%1\"", "user.edit": "Bearbeite \"%1\"", "user.following": "Nutzer, die %1 folgt", "user.followers": "Nutzer, die %1 folgen", diff --git a/public/language/de/search.json b/public/language/de/search.json new file mode 100644 index 0000000000..47ca0be16b --- /dev/null +++ b/public/language/de/search.json @@ -0,0 +1,3 @@ +{ + "results_matching": "%1 Ergebniss(e) stimmen mit \"%2\" überein, (%3 Sekunden)" +} \ No newline at end of file diff --git a/public/language/de/tags.json b/public/language/de/tags.json index f065d4bbfa..79fdd2d405 100644 --- a/public/language/de/tags.json +++ b/public/language/de/tags.json @@ -1,6 +1,6 @@ { - "no_tag_topics": "There are no topics with this tag.", + "no_tag_topics": "Es gibt keine Themen mit diesem Tag.", "tags": "Tags", - "enter_tags_here": "Enter tags here. Press enter after each tag.", - "no_tags": "There are no tags yet." + "enter_tags_here": "Gib hier Tags ein und drück die Eingabetaste nach jedem Tag.", + "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 5e3e4b7b7f..85edcd53c7 100644 --- a/public/language/de/topic.json +++ b/public/language/de/topic.json @@ -87,6 +87,7 @@ "more_users_and_guests": "%1 weitere(r) Nutzer und %2 Gäste", "more_users": "%1 weitere(r) Nutzer", "more_guests": "%1 weitere Gäste", + "users_and_others": "%1 and %2 others", "sort_by": "Sortieren nach", "oldest_to_newest": "Älteste zuerst", "newest_to_oldest": "Neuster zuerst", diff --git a/public/language/en@pirate/email.json b/public/language/en@pirate/email.json new file mode 100644 index 0000000000..98e591ab02 --- /dev/null +++ b/public/language/en@pirate/email.json @@ -0,0 +1,20 @@ +{ + "password-reset-requested": "Password Reset Requested - %1!", + "welcome-to": "Welcome to %1", + "greeting_no_name": "Hello", + "greeting_with_name": "Hello %1", + "welcome.text1": "Thank you for registering with %1!", + "welcome.text2": "To fully activate your account, we need to verify that you own the email address you registered with.", + "welcome.cta": "Click here to confirm your email address", + "reset.text1": "We received a request to reset your password, possibly because you have forgotten it. If this is not the case, please ignore this email.", + "reset.text2": "To continue with the password reset, please click on the following link:", + "reset.cta": "Click here to reset your password", + "digest.notifications": "You have some unread notifications from %1:", + "digest.latest_topics": "Latest topics from %1", + "digest.cta": "Click here to visit %1", + "digest.unsub.info": "This digest was sent to you due to your subscription settings.", + "digest.unsub.cta": "Click here to alter those settings", + "digest.daily.no_topics": "There have been no active topics in the past day", + "test.text1": "This is a test email to verify that the emailer is set up correctly for your NodeBB.", + "closing": "Thanks!" +} \ No newline at end of file diff --git a/public/language/en@pirate/error.json b/public/language/en@pirate/error.json index 4d55d9a4f3..77c582b23f 100644 --- a/public/language/en@pirate/error.json +++ b/public/language/en@pirate/error.json @@ -25,6 +25,7 @@ "no-user": "User doesn't exist", "no-teaser": "Teaser doesn't exist", "no-privileges": "You don't 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", "topic-locked": "Topic Locked", "still-uploading": "Please wait for uploads to complete.", diff --git a/public/language/en@pirate/footer.json b/public/language/en@pirate/footer.json deleted file mode 100644 index 128b7a5c8a..0000000000 --- a/public/language/en@pirate/footer.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "stats.online": "Online", - "stats.users": "Mates", - "stats.topics": "Topics", - "stats.posts": "Messages", - "success": "success" -} \ No newline at end of file diff --git a/public/language/en@pirate/global.json b/public/language/en@pirate/global.json index 210e920d4b..1b828eb73c 100644 --- a/public/language/en@pirate/global.json +++ b/public/language/en@pirate/global.json @@ -13,7 +13,7 @@ "please_log_in": "Please Log In", "logout": "Logout", "posting_restriction_info": "Postin' be currently restricted to registered members only, click here to log in.", - "welcome_back": "Welcome to Port", + "welcome_back": "Welcome Back", "you_have_successfully_logged_in": "Ye have successfully logged in", "save_changes": "Save yer Changes", "close": "Shoot down", diff --git a/public/language/en@pirate/groups.json b/public/language/en@pirate/groups.json new file mode 100644 index 0000000000..c00c111e11 --- /dev/null +++ b/public/language/en@pirate/groups.json @@ -0,0 +1,7 @@ +{ + "view_group": "View Group", + "details.title": "Group Details", + "details.members": "Member List", + "details.has_no_posts": "This group's members have not made any posts.", + "details.latest_posts": "Latest Posts" +} \ No newline at end of file diff --git a/public/language/en@pirate/notifications.json b/public/language/en@pirate/notifications.json index 8452e25378..cc1705eff6 100644 --- a/public/language/en@pirate/notifications.json +++ b/public/language/en@pirate/notifications.json @@ -4,12 +4,11 @@ "see_all": "Spy wit' ye eye all ye notifications", "back_to_home": "Back to %1", "outgoing_link": "Go offshore", - "outgoing_link_message": "Ye be goin' offshore", - "continue_to": "Continue to", - "return_to": "Return to", + "outgoing_link_message": "You are now leaving %1.", + "continue_to": "Continue to %1", + "return_to": "Return to %1", "new_notification": "New Notification", "you_have_unread_notifications": "You have unread notifications.", - "user_made_post": "%1 made a new post", "new_message_from": "New message from %1", "upvoted_your_post": "%1 has upvoted your post.", "favourited_your_post": "%1 has favourited your post.", diff --git a/public/language/en@pirate/pages.json b/public/language/en@pirate/pages.json index 4624ca716b..c39934513a 100644 --- a/public/language/en@pirate/pages.json +++ b/public/language/en@pirate/pages.json @@ -5,6 +5,7 @@ "recent": "Recent Topics", "users": "Registered Users", "notifications": "Notifications", + "tags": "Topics tagged under \"%1\"", "user.edit": "Editing \"%1\"", "user.following": "People %1 Follows", "user.followers": "People who Follow %1", diff --git a/public/language/en@pirate/search.json b/public/language/en@pirate/search.json new file mode 100644 index 0000000000..d0ffc64f36 --- /dev/null +++ b/public/language/en@pirate/search.json @@ -0,0 +1,3 @@ +{ + "results_matching": "%1 result(s) matching \"%2\", (%3 seconds)" +} \ No newline at end of file diff --git a/public/language/en@pirate/topic.json b/public/language/en@pirate/topic.json index 1bc77817a0..aa69261f90 100644 --- a/public/language/en@pirate/topic.json +++ b/public/language/en@pirate/topic.json @@ -87,6 +87,7 @@ "more_users_and_guests": "%1 more user(s) and %2 guest(s)", "more_users": "%1 more user(s)", "more_guests": "%1 more guest(s)", + "users_and_others": "%1 and %2 others", "sort_by": "Sort by", "oldest_to_newest": "Oldest to Newest", "newest_to_oldest": "Newest to Oldest", diff --git a/public/language/en_GB/error.json b/public/language/en_GB/error.json index 2b9a0aec46..75d43fce3f 100644 --- a/public/language/en_GB/error.json +++ b/public/language/en_GB/error.json @@ -75,5 +75,7 @@ "cant-chat-with-yourself": "You can't chat with yourself!", - "not-enough-reputation-to-downvote": "You do not have enough reputation to downvote this post" + "not-enough-reputation-to-downvote": "You do not have enough reputation to downvote 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/modules.json b/public/language/en_GB/modules.json index fdaf14dd5b..9bf107bec6 100644 --- a/public/language/en_GB/modules.json +++ b/public/language/en_GB/modules.json @@ -13,7 +13,7 @@ "chat.pop-out": "Pop out chat", "chat.maximize": "Maximize", - "composer.user_said_in": "%1 said in %2:\n", - "composer.user_said": "%1 said:\n", + "composer.user_said_in": "%1 said in %2:", + "composer.user_said": "%1 said:", "composer.discard": "Are you sure you wish to discard this post?" } \ No newline at end of file diff --git a/public/language/en_GB/notifications.json b/public/language/en_GB/notifications.json index b45129b467..5bb153ccbc 100644 --- a/public/language/en_GB/notifications.json +++ b/public/language/en_GB/notifications.json @@ -13,10 +13,13 @@ "new_message_from": "New message from %1", "upvoted_your_post": "%1 has upvoted your post.", + "moved_your_post": "%1 has moved your post.", + "moved_your_topic": "%1 has moved your topic.", "favourited_your_post": "%1 has favourited your post.", "user_flagged_post": "%1 flagged a post.", "user_posted_to" : "%1 has posted a reply to: %2", "user_mentioned_you_in": "%1 mentioned you in %2", + "user_started_following_you": "%1 started following you.", "email-confirmed": "Email Confirmed", "email-confirmed-message": "Thank you for validating your email. Your account is now fully activated.", diff --git a/public/language/en_GB/pages.json b/public/language/en_GB/pages.json index a91aec5e8e..3d444963e1 100644 --- a/public/language/en_GB/pages.json +++ b/public/language/en_GB/pages.json @@ -5,6 +5,7 @@ "recent": "Recent Topics", "users": "Registered Users", "notifications": "Notifications", + "tags": "Topics tagged under \"%1\"", "user.edit": "Editing \"%1\"", "user.following": "People %1 Follows", "user.followers": "People who Follow %1", diff --git a/public/language/en_GB/recent.json b/public/language/en_GB/recent.json index 32b31d2666..ff6400f27a 100644 --- a/public/language/en_GB/recent.json +++ b/public/language/en_GB/recent.json @@ -4,5 +4,6 @@ "week": "Week", "month": "Month", "year": "Year", + "alltime": "All Time", "no_recent_topics": "There are no recent topics." } \ No newline at end of file diff --git a/public/language/en_GB/topic.json b/public/language/en_GB/topic.json index 8c77751419..c6fe5bc4ad 100644 --- a/public/language/en_GB/topic.json +++ b/public/language/en_GB/topic.json @@ -105,6 +105,7 @@ "more_users_and_guests": "%1 more user(s) and %2 guest(s)", "more_users": "%1 more user(s)", "more_guests": "%1 more guest(s)", + "users_and_others": "%1 and %2 others", "sort_by": "Sort by", "oldest_to_newest": "Oldest to Newest", diff --git a/public/language/en_GB/user.json b/public/language/en_GB/user.json index ab8c1cdae6..a113ac03d8 100644 --- a/public/language/en_GB/user.json +++ b/public/language/en_GB/user.json @@ -5,6 +5,8 @@ "email": "Email", "confirm_email": "Confirm Email", + "delete_account": "Delete Account", + "delete_account_confirm": "Are you sure you want to delete your account?", "fullname": "Full Name", "website": "Website", @@ -71,5 +73,8 @@ "notification_sounds" : "Play a sound when you receive a notification.", "browsing": "Browsing Settings", - "open_links_in_new_tab": "Open outgoing links in new tab?" + "open_links_in_new_tab": "Open outgoing links in new tab?", + + "follow_topics_you_reply_to": "Follow topics that you reply to.", + "follow_topics_you_create": "Follow topics you create." } diff --git a/public/language/en_US/email.json b/public/language/en_US/email.json new file mode 100644 index 0000000000..98e591ab02 --- /dev/null +++ b/public/language/en_US/email.json @@ -0,0 +1,20 @@ +{ + "password-reset-requested": "Password Reset Requested - %1!", + "welcome-to": "Welcome to %1", + "greeting_no_name": "Hello", + "greeting_with_name": "Hello %1", + "welcome.text1": "Thank you for registering with %1!", + "welcome.text2": "To fully activate your account, we need to verify that you own the email address you registered with.", + "welcome.cta": "Click here to confirm your email address", + "reset.text1": "We received a request to reset your password, possibly because you have forgotten it. If this is not the case, please ignore this email.", + "reset.text2": "To continue with the password reset, please click on the following link:", + "reset.cta": "Click here to reset your password", + "digest.notifications": "You have some unread notifications from %1:", + "digest.latest_topics": "Latest topics from %1", + "digest.cta": "Click here to visit %1", + "digest.unsub.info": "This digest was sent to you due to your subscription settings.", + "digest.unsub.cta": "Click here to alter those settings", + "digest.daily.no_topics": "There have been no active topics in the past day", + "test.text1": "This is a test email to verify that the emailer is set up correctly for your NodeBB.", + "closing": "Thanks!" +} \ No newline at end of file diff --git a/public/language/en_US/error.json b/public/language/en_US/error.json index 89b996b720..bfec6e00d7 100644 --- a/public/language/en_US/error.json +++ b/public/language/en_US/error.json @@ -25,6 +25,7 @@ "no-user": "User doesn't exist", "no-teaser": "Teaser doesn't exist", "no-privileges": "You don't 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", "topic-locked": "Topic Locked", "still-uploading": "Please wait for uploads to complete.", diff --git a/public/language/en_US/footer.json b/public/language/en_US/footer.json deleted file mode 100644 index ba75cdc608..0000000000 --- a/public/language/en_US/footer.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "stats.online": "Online", - "stats.users": "Users", - "stats.topics": "Topics", - "stats.posts": "Posts", - "success": "success" -} \ No newline at end of file diff --git a/public/language/en_US/groups.json b/public/language/en_US/groups.json new file mode 100644 index 0000000000..c00c111e11 --- /dev/null +++ b/public/language/en_US/groups.json @@ -0,0 +1,7 @@ +{ + "view_group": "View Group", + "details.title": "Group Details", + "details.members": "Member List", + "details.has_no_posts": "This group's members have not made any posts.", + "details.latest_posts": "Latest Posts" +} \ No newline at end of file diff --git a/public/language/en_US/notifications.json b/public/language/en_US/notifications.json index 74d6704f1c..7f97d92275 100644 --- a/public/language/en_US/notifications.json +++ b/public/language/en_US/notifications.json @@ -4,12 +4,11 @@ "see_all": "See all Notifications", "back_to_home": "Back to %1", "outgoing_link": "Outgoing Link", - "outgoing_link_message": "You are now leaving", - "continue_to": "Continue to", - "return_to": "Return to", + "outgoing_link_message": "You are now leaving %1.", + "continue_to": "Continue to %1", + "return_to": "Return to %1", "new_notification": "New Notification", "you_have_unread_notifications": "You have unread notifications.", - "user_made_post": "%1 made a new post", "new_message_from": "New message from %1", "upvoted_your_post": "%1 has upvoted your post.", "favourited_your_post": "%1 has favorited your post.", diff --git a/public/language/en_US/pages.json b/public/language/en_US/pages.json index 9925d77393..d235dcb87f 100644 --- a/public/language/en_US/pages.json +++ b/public/language/en_US/pages.json @@ -5,6 +5,7 @@ "recent": "Recent Topics", "users": "Registered Users", "notifications": "Notifications", + "tags": "Topics tagged under \"%1\"", "user.edit": "Editing \"%1\"", "user.following": "People %1 Follows", "user.followers": "People who Follow %1", diff --git a/public/language/en_US/search.json b/public/language/en_US/search.json new file mode 100644 index 0000000000..d0ffc64f36 --- /dev/null +++ b/public/language/en_US/search.json @@ -0,0 +1,3 @@ +{ + "results_matching": "%1 result(s) matching \"%2\", (%3 seconds)" +} \ No newline at end of file diff --git a/public/language/en_US/topic.json b/public/language/en_US/topic.json index c7909fb23b..8ffca481ab 100644 --- a/public/language/en_US/topic.json +++ b/public/language/en_US/topic.json @@ -87,6 +87,7 @@ "more_users_and_guests": "%1 more user(s) and %2 guest(s)", "more_users": "%1 more user(s)", "more_guests": "%1 more guest(s)", + "users_and_others": "%1 and %2 others", "sort_by": "Sort by", "oldest_to_newest": "Oldest to Newest", "newest_to_oldest": "Newest to Oldest", diff --git a/public/language/es/email.json b/public/language/es/email.json new file mode 100644 index 0000000000..98e591ab02 --- /dev/null +++ b/public/language/es/email.json @@ -0,0 +1,20 @@ +{ + "password-reset-requested": "Password Reset Requested - %1!", + "welcome-to": "Welcome to %1", + "greeting_no_name": "Hello", + "greeting_with_name": "Hello %1", + "welcome.text1": "Thank you for registering with %1!", + "welcome.text2": "To fully activate your account, we need to verify that you own the email address you registered with.", + "welcome.cta": "Click here to confirm your email address", + "reset.text1": "We received a request to reset your password, possibly because you have forgotten it. If this is not the case, please ignore this email.", + "reset.text2": "To continue with the password reset, please click on the following link:", + "reset.cta": "Click here to reset your password", + "digest.notifications": "You have some unread notifications from %1:", + "digest.latest_topics": "Latest topics from %1", + "digest.cta": "Click here to visit %1", + "digest.unsub.info": "This digest was sent to you due to your subscription settings.", + "digest.unsub.cta": "Click here to alter those settings", + "digest.daily.no_topics": "There have been no active topics in the past day", + "test.text1": "This is a test email to verify that the emailer is set up correctly for your NodeBB.", + "closing": "Thanks!" +} \ No newline at end of file diff --git a/public/language/es/error.json b/public/language/es/error.json index 7065f57dc1..38ff5515ae 100644 --- a/public/language/es/error.json +++ b/public/language/es/error.json @@ -25,6 +25,7 @@ "no-user": "El usuario no existe", "no-teaser": "El extracto del tema no existe.", "no-privileges": "No tienes los privilegios necesarios para esa acción.", + "no-emailers-configured": "No email plugins were loaded, so a test email could not be sent", "category-disabled": "Categoría deshabilitada.", "topic-locked": "Tema bloqueado.", "still-uploading": "Por favor, espera a que terminen las subidas.", diff --git a/public/language/es/footer.json b/public/language/es/footer.json deleted file mode 100644 index 4f4abaf711..0000000000 --- a/public/language/es/footer.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "stats.online": "Online", - "stats.users": "Gente", - "stats.topics": "Temas", - "stats.posts": "Posts", - "success": "éxito!" -} \ No newline at end of file diff --git a/public/language/es/groups.json b/public/language/es/groups.json new file mode 100644 index 0000000000..c00c111e11 --- /dev/null +++ b/public/language/es/groups.json @@ -0,0 +1,7 @@ +{ + "view_group": "View Group", + "details.title": "Group Details", + "details.members": "Member List", + "details.has_no_posts": "This group's members have not made any posts.", + "details.latest_posts": "Latest Posts" +} \ No newline at end of file diff --git a/public/language/es/notifications.json b/public/language/es/notifications.json index bbb45a3d94..3a7168a44d 100644 --- a/public/language/es/notifications.json +++ b/public/language/es/notifications.json @@ -4,12 +4,11 @@ "see_all": "Ver todas las notificaciones", "back_to_home": "Volver a %1", "outgoing_link": "Enlace Externo", - "outgoing_link_message": "Estas saliendo del sitio", - "continue_to": "Continuar", - "return_to": "Volver a", + "outgoing_link_message": "You are now leaving %1.", + "continue_to": "Continue to %1", + "return_to": "Return to %1", "new_notification": "Nueva Notificación", "you_have_unread_notifications": "Tienes notificaciones sin leer.", - "user_made_post": "%1 hizo una nueva publicación", "new_message_from": "Nuevo mensaje de %1", "upvoted_your_post": "%1 ha marcado como favorita tu respuesta.", "favourited_your_post": "%1 ha marcado como favorita tu respuesta.", diff --git a/public/language/es/pages.json b/public/language/es/pages.json index 8d3b12a377..4b79bc293f 100644 --- a/public/language/es/pages.json +++ b/public/language/es/pages.json @@ -5,6 +5,7 @@ "recent": "Temas Recientes", "users": "Usuarios Registrado", "notifications": "Notificaciones", + "tags": "Topics tagged under \"%1\"", "user.edit": "Editando \"%1\"", "user.following": "Gente que sigue %1 ", "user.followers": "Seguidores de %1", diff --git a/public/language/es/search.json b/public/language/es/search.json new file mode 100644 index 0000000000..d0ffc64f36 --- /dev/null +++ b/public/language/es/search.json @@ -0,0 +1,3 @@ +{ + "results_matching": "%1 result(s) matching \"%2\", (%3 seconds)" +} \ No newline at end of file diff --git a/public/language/es/tags.json b/public/language/es/tags.json index f065d4bbfa..cb679cdc7f 100644 --- a/public/language/es/tags.json +++ b/public/language/es/tags.json @@ -1,6 +1,6 @@ { - "no_tag_topics": "There are no topics with this tag.", - "tags": "Tags", - "enter_tags_here": "Enter tags here. Press enter after each tag.", - "no_tags": "There are no tags yet." + "no_tag_topics": "No hay temas con esta etiqueta.", + "tags": "Etiquetas", + "enter_tags_here": "Introduce las etiquetas aquí. Pulsa intro desde de cada una.", + "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 1809fa5344..ae3fa08fc1 100644 --- a/public/language/es/topic.json +++ b/public/language/es/topic.json @@ -87,6 +87,7 @@ "more_users_and_guests": "%1 usuario(s) y %2 invitado(s) más", "more_users": "%1 usuario(s) más", "more_guests": "%1 invitado(s) más", + "users_and_others": "%1 and %2 others", "sort_by": "Ordenar por", "oldest_to_newest": "Más antiguo a más nuevo", "newest_to_oldest": "Más nuevo a más antiguo", diff --git a/public/language/et/email.json b/public/language/et/email.json new file mode 100644 index 0000000000..98e591ab02 --- /dev/null +++ b/public/language/et/email.json @@ -0,0 +1,20 @@ +{ + "password-reset-requested": "Password Reset Requested - %1!", + "welcome-to": "Welcome to %1", + "greeting_no_name": "Hello", + "greeting_with_name": "Hello %1", + "welcome.text1": "Thank you for registering with %1!", + "welcome.text2": "To fully activate your account, we need to verify that you own the email address you registered with.", + "welcome.cta": "Click here to confirm your email address", + "reset.text1": "We received a request to reset your password, possibly because you have forgotten it. If this is not the case, please ignore this email.", + "reset.text2": "To continue with the password reset, please click on the following link:", + "reset.cta": "Click here to reset your password", + "digest.notifications": "You have some unread notifications from %1:", + "digest.latest_topics": "Latest topics from %1", + "digest.cta": "Click here to visit %1", + "digest.unsub.info": "This digest was sent to you due to your subscription settings.", + "digest.unsub.cta": "Click here to alter those settings", + "digest.daily.no_topics": "There have been no active topics in the past day", + "test.text1": "This is a test email to verify that the emailer is set up correctly for your NodeBB.", + "closing": "Thanks!" +} \ No newline at end of file diff --git a/public/language/et/error.json b/public/language/et/error.json index 464f89ef96..596a04b4df 100644 --- a/public/language/et/error.json +++ b/public/language/et/error.json @@ -25,6 +25,7 @@ "no-user": "Kasutajat ei eksisteeri", "no-teaser": "Eelvaadet ei eksisteeri", "no-privileges": "Sul pole piisvalt õigusi.", + "no-emailers-configured": "No email plugins were loaded, so a test email could not be sent", "category-disabled": "Kategooria keelatud", "topic-locked": "Teema lukustatud", "still-uploading": "Palun oota, kuni üleslaadimised on laetud.", diff --git a/public/language/et/footer.json b/public/language/et/footer.json deleted file mode 100644 index f11c2d6e56..0000000000 --- a/public/language/et/footer.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "stats.online": "Sees", - "stats.users": "Kasutajad", - "stats.topics": "Teemad", - "stats.posts": "Postitused", - "success": "õnnestus" -} \ No newline at end of file diff --git a/public/language/et/global.json b/public/language/et/global.json index 45608e00bf..3cfa44bef3 100644 --- a/public/language/et/global.json +++ b/public/language/et/global.json @@ -50,9 +50,9 @@ "read_more": "loe veel", "posted_ago_by_guest": "postitatud %1 tagasi külalise poolt", "posted_ago_by": "postitatud %1 tagasi kasutaja %2 poolt", - "posted_ago": "postitatud %1 tagasi", + "posted_ago": "postitatud %1", "posted_in_ago_by_guest": "postitatud kategooriasse %1 %2 tagasi külalise poolt", - "posted_in_ago_by": "postitatud kategooriasse %1 %2 aega tagasi kasutaja %3 poolt", + "posted_in_ago_by": "postitatud kategooriasse %1 %2 kasutaja %3 poolt", "posted_in_ago": "postitatud kategooriasse %1 %2 tagasi", "replied_ago": "vastas %1", "user_posted_ago": "kasutaja %1 postitas %2 tagasi", diff --git a/public/language/et/groups.json b/public/language/et/groups.json new file mode 100644 index 0000000000..c00c111e11 --- /dev/null +++ b/public/language/et/groups.json @@ -0,0 +1,7 @@ +{ + "view_group": "View Group", + "details.title": "Group Details", + "details.members": "Member List", + "details.has_no_posts": "This group's members have not made any posts.", + "details.latest_posts": "Latest Posts" +} \ No newline at end of file diff --git a/public/language/et/notifications.json b/public/language/et/notifications.json index 3db1e076ab..4de13a6e0f 100644 --- a/public/language/et/notifications.json +++ b/public/language/et/notifications.json @@ -4,12 +4,11 @@ "see_all": "Vaata kõiki teateid", "back_to_home": "Tagasi %1", "outgoing_link": "Väljaminev link", - "outgoing_link_message": "Lahkud foorumist", - "continue_to": "Jätka", - "return_to": "Pöördu tagasi", + "outgoing_link_message": "You are now leaving %1.", + "continue_to": "Continue to %1", + "return_to": "Return to %1", "new_notification": "Uus teade", "you_have_unread_notifications": "Sul ei ole lugemata teateid.", - "user_made_post": "%1 tegi uue postituse", "new_message_from": "Uus sõnum kasutajalt %1", "upvoted_your_post": "%1 hääletas sinu postituse poolt.", "favourited_your_post": "%1 märkis sinu postituse lemmikuks.", diff --git a/public/language/et/pages.json b/public/language/et/pages.json index 10e7b528cd..9cc027eaaf 100644 --- a/public/language/et/pages.json +++ b/public/language/et/pages.json @@ -5,6 +5,7 @@ "recent": "Hiljutised teemad", "users": "Registreeritud kasutajad", "notifications": "Teated", + "tags": "Topics tagged under \"%1\"", "user.edit": "Muudan \"%1\"", "user.following": "Kasutaja %1 jälgib", "user.followers": "Kasutajad, kes jälgivad %1", diff --git a/public/language/et/search.json b/public/language/et/search.json new file mode 100644 index 0000000000..d0ffc64f36 --- /dev/null +++ b/public/language/et/search.json @@ -0,0 +1,3 @@ +{ + "results_matching": "%1 result(s) matching \"%2\", (%3 seconds)" +} \ No newline at end of file diff --git a/public/language/et/topic.json b/public/language/et/topic.json index ff182da119..dc4dcc62cb 100644 --- a/public/language/et/topic.json +++ b/public/language/et/topic.json @@ -41,7 +41,7 @@ "thread_tools.pin": "Tõsta esile teema", "thread_tools.unpin": "Märgista teema", "thread_tools.lock": "Lukusta teema", - "thread_tools.unlock": "Eemalda märgistatud teema", + "thread_tools.unlock": "Taasava teema", "thread_tools.move": "Liiguta teema", "thread_tools.move_all": "Liiguta kõik", "thread_tools.fork": "Fork Topic", @@ -87,6 +87,7 @@ "more_users_and_guests": "%1 kasutaja(t) ja %2 külalist", "more_users": "veel %1 kasutaja(t)", "more_guests": "veel %1 külalist", + "users_and_others": "%1 and %2 others", "sort_by": "Sorteeri", "oldest_to_newest": "Vanematest uuemateni", "newest_to_oldest": "Uuematest vanemateni", diff --git a/public/language/fa_IR/email.json b/public/language/fa_IR/email.json new file mode 100644 index 0000000000..98e591ab02 --- /dev/null +++ b/public/language/fa_IR/email.json @@ -0,0 +1,20 @@ +{ + "password-reset-requested": "Password Reset Requested - %1!", + "welcome-to": "Welcome to %1", + "greeting_no_name": "Hello", + "greeting_with_name": "Hello %1", + "welcome.text1": "Thank you for registering with %1!", + "welcome.text2": "To fully activate your account, we need to verify that you own the email address you registered with.", + "welcome.cta": "Click here to confirm your email address", + "reset.text1": "We received a request to reset your password, possibly because you have forgotten it. If this is not the case, please ignore this email.", + "reset.text2": "To continue with the password reset, please click on the following link:", + "reset.cta": "Click here to reset your password", + "digest.notifications": "You have some unread notifications from %1:", + "digest.latest_topics": "Latest topics from %1", + "digest.cta": "Click here to visit %1", + "digest.unsub.info": "This digest was sent to you due to your subscription settings.", + "digest.unsub.cta": "Click here to alter those settings", + "digest.daily.no_topics": "There have been no active topics in the past day", + "test.text1": "This is a test email to verify that the emailer is set up correctly for your NodeBB.", + "closing": "Thanks!" +} \ No newline at end of file diff --git a/public/language/fa_IR/error.json b/public/language/fa_IR/error.json index 2fad56ff15..cf82fa6622 100644 --- a/public/language/fa_IR/error.json +++ b/public/language/fa_IR/error.json @@ -15,7 +15,7 @@ "invalid-pagination-value": "عدد صفحه‌بندی نامعتبر است.", "username-taken": "این نام کاربری گرفته شده است.", "email-taken": "این رایانامه گرفته شده است.", - "email-not-confirmed": "Your email is not confirmed, please click here to confirm your email.", + "email-not-confirmed": "رایانامه شما تأیید نشده است، لطفاً برای تأیید رایانامه‌تان اینجا را بفشارید.", "username-too-short": "نام کاربری خیلی کوتاه است.", "user-banned": "کاربر محروم شد.", "no-category": "چنین دسته‌ای وجود ندارد.", @@ -25,6 +25,7 @@ "no-user": "چنین کاربری وجود ندارد.", "no-teaser": "چکیدهٔ دیدگاه وجود ندارد.", "no-privileges": "شما دسترسی کافی برای این کار را ندارید.", + "no-emailers-configured": "No email plugins were loaded, so a test email could not be sent", "category-disabled": "دسته غیر‌فعال شد.", "topic-locked": "جستار بسته شد.", "still-uploading": "خواهشمندیم تا پایان بارگذاری‌ها شکیبا باشید.", diff --git a/public/language/fa_IR/footer.json b/public/language/fa_IR/footer.json deleted file mode 100644 index d5702d5653..0000000000 --- a/public/language/fa_IR/footer.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "stats.online": "حاضر", - "stats.users": "کاربران", - "stats.topics": "جُستارها", - "stats.posts": "دیدگاه‌ها", - "success": "موفقيت" -} \ No newline at end of file diff --git a/public/language/fa_IR/groups.json b/public/language/fa_IR/groups.json new file mode 100644 index 0000000000..c00c111e11 --- /dev/null +++ b/public/language/fa_IR/groups.json @@ -0,0 +1,7 @@ +{ + "view_group": "View Group", + "details.title": "Group Details", + "details.members": "Member List", + "details.has_no_posts": "This group's members have not made any posts.", + "details.latest_posts": "Latest Posts" +} \ No newline at end of file diff --git a/public/language/fa_IR/modules.json b/public/language/fa_IR/modules.json index ce4c5f2900..4d22d32319 100644 --- a/public/language/fa_IR/modules.json +++ b/public/language/fa_IR/modules.json @@ -1,17 +1,17 @@ { "chat.chatting_with": "گفتگو با ", - "chat.placeholder": "Type chat message here, press enter to send", + "chat.placeholder": "پیام گفتگو را اینجا بنویسید، دکمه Enter را بزنید تا فرستاده شود.", "chat.send": "فرستادن", "chat.no_active": "شما هیچ گفتگوی فعالی ندارید.", "chat.user_typing": "%1 در حال نوشتن است...", "chat.user_has_messaged_you": "%1 به شما پیام داده است.", "chat.see_all": "نمایش تمامی گفتگوها", - "chat.no-messages": "Please select a recipient to view chat message history", - "chat.recent-chats": "Recent Chats", - "chat.contacts": "Contacts", - "chat.message-history": "Message History", + "chat.no-messages": "مشخص کنید تاریخچه گفتگوهایتان با چه کاربری را می‌خواهید ببینید", + "chat.recent-chats": "گفتگوهای اخیر", + "chat.contacts": "تماس‌ها", + "chat.message-history": "تاریخچه پیام‌ها", "chat.pop-out": "Pop out chat", - "chat.maximize": "Maximize", + "chat.maximize": "تمام صفحه", "composer.user_said_in": "%1 در %2 گفته است:", "composer.user_said": "%1 گفته است:", "composer.discard": "آیا از دور انداختن این دیدگاه اطمینان دارید؟" diff --git a/public/language/fa_IR/notifications.json b/public/language/fa_IR/notifications.json index 9c598ed25e..a643005e04 100644 --- a/public/language/fa_IR/notifications.json +++ b/public/language/fa_IR/notifications.json @@ -4,12 +4,11 @@ "see_all": "دیدن همهٔ آگاه‌سازی‌ها", "back_to_home": "بازگشت به %1", "outgoing_link": "پیوند برون‌رو", - "outgoing_link_message": "شما در حال ترک اینجایید", - "continue_to": "رفتن به", - "return_to": "بازگشت به", + "outgoing_link_message": "You are now leaving %1.", + "continue_to": "Continue to %1", + "return_to": "Return to %1", "new_notification": "آکاه‌سازی تازه", "you_have_unread_notifications": "شما آگاه‌سازی‌های نخوانده دارید.", - "user_made_post": "%1 یک دیدگاه تازه فرستاد.", "new_message_from": "پیام تازه از %1", "upvoted_your_post": "%1 به دیدگاه شما رای داده است.", "favourited_your_post": "%1 دیدگاه شما را پسندیده است.", diff --git a/public/language/fa_IR/pages.json b/public/language/fa_IR/pages.json index cb5c9f00c8..94458a27a2 100644 --- a/public/language/fa_IR/pages.json +++ b/public/language/fa_IR/pages.json @@ -5,6 +5,7 @@ "recent": "جستارهای تازه", "users": "کاربران نام‌نویسی شده", "notifications": "آگاه‌سازی‌ها", + "tags": "Topics tagged under \"%1\"", "user.edit": "ویرایش \"%1\"", "user.following": "%1 کاربر دنبال می‌کنند", "user.followers": "کاربرانی که %1 را دنبال می‌کنند", diff --git a/public/language/fa_IR/recent.json b/public/language/fa_IR/recent.json index 5f9b274d43..74a8876ebf 100644 --- a/public/language/fa_IR/recent.json +++ b/public/language/fa_IR/recent.json @@ -3,6 +3,6 @@ "day": "روز", "week": "هفته", "month": "ماه", - "year": "Year", + "year": "سال", "no_recent_topics": "هیچ جستار تازه‌ای نیست." } \ No newline at end of file diff --git a/public/language/fa_IR/search.json b/public/language/fa_IR/search.json new file mode 100644 index 0000000000..d0ffc64f36 --- /dev/null +++ b/public/language/fa_IR/search.json @@ -0,0 +1,3 @@ +{ + "results_matching": "%1 result(s) matching \"%2\", (%3 seconds)" +} \ No newline at end of file diff --git a/public/language/fa_IR/topic.json b/public/language/fa_IR/topic.json index 57218b749b..1c525fad8b 100644 --- a/public/language/fa_IR/topic.json +++ b/public/language/fa_IR/topic.json @@ -87,6 +87,7 @@ "more_users_and_guests": "%1 کاربر() و %2 مهمان()", "more_users": "1% کاربر()", "more_guests": "1% مهمان()", + "users_and_others": "%1 and %2 others", "sort_by": "مرتب‌سازی بر اساس", "oldest_to_newest": "قدیمی‌ترین به جدید‌ترین", "newest_to_oldest": "جدید‌ترین به قدیمی‌ترین", diff --git a/public/language/fa_IR/user.json b/public/language/fa_IR/user.json index 6cd8180930..cfe346d5fd 100644 --- a/public/language/fa_IR/user.json +++ b/public/language/fa_IR/user.json @@ -3,7 +3,7 @@ "offline": "آفلاین", "username": "نام کاربری", "email": "رایانامه", - "confirm_email": "Confirm Email", + "confirm_email": "تأیید رایانامه", "fullname": "نام کامل", "website": "تارنما", "location": "محل سکونت", diff --git a/public/language/fi/email.json b/public/language/fi/email.json new file mode 100644 index 0000000000..98e591ab02 --- /dev/null +++ b/public/language/fi/email.json @@ -0,0 +1,20 @@ +{ + "password-reset-requested": "Password Reset Requested - %1!", + "welcome-to": "Welcome to %1", + "greeting_no_name": "Hello", + "greeting_with_name": "Hello %1", + "welcome.text1": "Thank you for registering with %1!", + "welcome.text2": "To fully activate your account, we need to verify that you own the email address you registered with.", + "welcome.cta": "Click here to confirm your email address", + "reset.text1": "We received a request to reset your password, possibly because you have forgotten it. If this is not the case, please ignore this email.", + "reset.text2": "To continue with the password reset, please click on the following link:", + "reset.cta": "Click here to reset your password", + "digest.notifications": "You have some unread notifications from %1:", + "digest.latest_topics": "Latest topics from %1", + "digest.cta": "Click here to visit %1", + "digest.unsub.info": "This digest was sent to you due to your subscription settings.", + "digest.unsub.cta": "Click here to alter those settings", + "digest.daily.no_topics": "There have been no active topics in the past day", + "test.text1": "This is a test email to verify that the emailer is set up correctly for your NodeBB.", + "closing": "Thanks!" +} \ No newline at end of file diff --git a/public/language/fi/error.json b/public/language/fi/error.json index 466f7aa63e..a9c4718431 100644 --- a/public/language/fi/error.json +++ b/public/language/fi/error.json @@ -25,6 +25,7 @@ "no-user": "Käyttäjää ei ole olemassa", "no-teaser": "Teaseria ei ole olemassa", "no-privileges": "Oikeutesi eivät riitä toiminnon suorittamiseen", + "no-emailers-configured": "No email plugins were loaded, so a test email could not be sent", "category-disabled": "Kategoria ei ole käytössä", "topic-locked": "Aihe lukittu", "still-uploading": "Ole hyvä ja odota tiedostojen lähettämisen valmistumista.", diff --git a/public/language/fi/footer.json b/public/language/fi/footer.json deleted file mode 100644 index 46ffa8334d..0000000000 --- a/public/language/fi/footer.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "stats.online": "Online", - "stats.users": "Käyttäjää", - "stats.topics": "Aihetta", - "stats.posts": "Viestiä", - "success": "onnistunut" -} \ No newline at end of file diff --git a/public/language/fi/global.json b/public/language/fi/global.json index 0826c43bc2..03e0a817ba 100644 --- a/public/language/fi/global.json +++ b/public/language/fi/global.json @@ -13,7 +13,7 @@ "please_log_in": "Kirjaudu, ole hyvä", "logout": "Kirjaudu ulos", "posting_restriction_info": "Kirjoittaminen on tällä hetkellä rajattu vain rekisteröityneille käyttäjille. Napsauta tätä kirjautuaksesi.", - "welcome_back": "Tervetuloa takaisin", + "welcome_back": "Welcome Back", "you_have_successfully_logged_in": "Olet onnistuneesti kirjautunut sisään", "save_changes": "Tallenna muutokset", "close": "Sulje", diff --git a/public/language/fi/groups.json b/public/language/fi/groups.json new file mode 100644 index 0000000000..c00c111e11 --- /dev/null +++ b/public/language/fi/groups.json @@ -0,0 +1,7 @@ +{ + "view_group": "View Group", + "details.title": "Group Details", + "details.members": "Member List", + "details.has_no_posts": "This group's members have not made any posts.", + "details.latest_posts": "Latest Posts" +} \ No newline at end of file diff --git a/public/language/fi/notifications.json b/public/language/fi/notifications.json index 84c4f97043..e9507997a0 100644 --- a/public/language/fi/notifications.json +++ b/public/language/fi/notifications.json @@ -4,12 +4,11 @@ "see_all": "Katso kaikki ilmoitukset", "back_to_home": "Back to %1", "outgoing_link": "Ulkopuolinen linkki", - "outgoing_link_message": "Olet nyt poistumassa", - "continue_to": "Jatka", - "return_to": "Return to", + "outgoing_link_message": "You are now leaving %1.", + "continue_to": "Continue to %1", + "return_to": "Return to %1", "new_notification": "Uusi ilmoitus", "you_have_unread_notifications": "Sinulla on lukemattomia ilmoituksia.", - "user_made_post": "%1 kirjoitti uuden viestin", "new_message_from": "Uusi viesti käyttäjältä %1", "upvoted_your_post": "%1 has upvoted your post.", "favourited_your_post": "%1 lisäsi viestisi suosikkeihinsa.", diff --git a/public/language/fi/pages.json b/public/language/fi/pages.json index 90f897fa57..899ce00bf2 100644 --- a/public/language/fi/pages.json +++ b/public/language/fi/pages.json @@ -5,6 +5,7 @@ "recent": "Viimeisimmät aiheet", "users": "Rekisteröityneet käyttäjät", "notifications": "Ilmoitukset", + "tags": "Topics tagged under \"%1\"", "user.edit": "Muokataan \"%1\"", "user.following": "Käyttäjät, joita %1 seuraa", "user.followers": "Käyttäjät, jotka seuraavat käyttäjää %1", diff --git a/public/language/fi/search.json b/public/language/fi/search.json new file mode 100644 index 0000000000..d0ffc64f36 --- /dev/null +++ b/public/language/fi/search.json @@ -0,0 +1,3 @@ +{ + "results_matching": "%1 result(s) matching \"%2\", (%3 seconds)" +} \ No newline at end of file diff --git a/public/language/fi/topic.json b/public/language/fi/topic.json index ae13539de4..ad1ef0f1a0 100644 --- a/public/language/fi/topic.json +++ b/public/language/fi/topic.json @@ -87,6 +87,7 @@ "more_users_and_guests": "%1 more user(s) and %2 guest(s)", "more_users": "%1 more user(s)", "more_guests": "%1 more guest(s)", + "users_and_others": "%1 and %2 others", "sort_by": "Sort by", "oldest_to_newest": "Oldest to Newest", "newest_to_oldest": "Newest to Oldest", diff --git a/public/language/fr/email.json b/public/language/fr/email.json new file mode 100644 index 0000000000..88cb8c270a --- /dev/null +++ b/public/language/fr/email.json @@ -0,0 +1,20 @@ +{ + "password-reset-requested": "Demande de réinitialisation du mot de passe - %1", + "welcome-to": "Bienvenue sur %1", + "greeting_no_name": "Bonjour", + "greeting_with_name": "Bonjour %1", + "welcome.text1": "Merci de vous être inscrit avec %1!", + "welcome.text2": "Pour activer totalement votre compte, nous devons vérifier que vous êtes bien propriétaire de l'adresse email que vous avez utilisé pour vous inscrire.", + "welcome.cta": "Cliquez ici pour confirmer votre adresse email", + "reset.text1": "Nous avons reçu une demande de réinitialisation de votre mot de passe, probablement parce que vous l'avez oublié. Si ce n'est pas le cas, veuillez ignorer cet email.", + "reset.text2": "Pour confirmer la réinitialisation de votre mot de passe, veuillez cliquer sur le lien suivant :", + "reset.cta": "Cliquez ici pour réinitialiser votre mot de passe", + "digest.notifications": "Vous avez des notifications non-lues de %1 :", + "digest.latest_topics": "Derniers sujets de %1 :", + "digest.cta": "Cliquez ici pour aller sur %1", + "digest.unsub.info": "Ce message vous a été envoyé en raison de vos paramètres d'abonnement.", + "digest.unsub.cta": "Cliquez ici pour modifier ces paramètres", + "digest.daily.no_topics": "Il n'y a eu aucun sujet actif ces derniers jours", + "test.text1": "Ceci est un email de test pour vérifier que l'emailer est correctement configuré pour NodeBB.", + "closing": "Merci !" +} \ No newline at end of file diff --git a/public/language/fr/error.json b/public/language/fr/error.json index e471852ed6..63d9a1765f 100644 --- a/public/language/fr/error.json +++ b/public/language/fr/error.json @@ -25,6 +25,7 @@ "no-user": "Cet utilisateur n'existe pas", "no-teaser": "L’aperçu n'existe pas", "no-privileges": "Vous n'avez pas les privilèges nécessaires pour effectuer cette action.", + "no-emailers-configured": "Un email de test n'a pas pu être envoyé car aucun plugin de gestion des emails n'était chargé", "category-disabled": "Catégorie désactivée", "topic-locked": "Sujet verrouillé", "still-uploading": "Veuillez patienter pendant le téléchargement.", diff --git a/public/language/fr/footer.json b/public/language/fr/footer.json deleted file mode 100644 index a29857801f..0000000000 --- a/public/language/fr/footer.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "stats.online": "En ligne", - "stats.users": "Utilisateurs", - "stats.topics": "Sujets", - "stats.posts": "Message", - "success": "succès" -} \ No newline at end of file diff --git a/public/language/fr/groups.json b/public/language/fr/groups.json new file mode 100644 index 0000000000..df85d154f8 --- /dev/null +++ b/public/language/fr/groups.json @@ -0,0 +1,7 @@ +{ + "view_group": "Voir le groupe", + "details.title": "Informations du groupe", + "details.members": "Liste des membres", + "details.has_no_posts": "Ce membre du groupe n'a envoyé aucun message.", + "details.latest_posts": "Derniers messages" +} \ No newline at end of file diff --git a/public/language/fr/notifications.json b/public/language/fr/notifications.json index d5b281808f..560fb83751 100644 --- a/public/language/fr/notifications.json +++ b/public/language/fr/notifications.json @@ -4,12 +4,11 @@ "see_all": "Voir toutes les notifications.", "back_to_home": "Revenir à %1", "outgoing_link": "Lien sortant", - "outgoing_link_message": "Vous quittez le forum", - "continue_to": "Continuer vers", - "return_to": "Retourner à", + "outgoing_link_message": "Vous quittez %1.", + "continue_to": "Continuer vers %1", + "return_to": "Revenir à %1", "new_notification": "Nouvelle notification", "you_have_unread_notifications": "Vous avez des notifications non-lues", - "user_made_post": "%1 a posté un nouveau message", "new_message_from": "Nouveau message de %1", "upvoted_your_post": "%1 a voté pour votre message.", "favourited_your_post": "%1 a mis votre message en favoris.", diff --git a/public/language/fr/pages.json b/public/language/fr/pages.json index 994604a5b8..ad713d898b 100644 --- a/public/language/fr/pages.json +++ b/public/language/fr/pages.json @@ -5,6 +5,7 @@ "recent": "Sujets récents", "users": "Utilisateurs enregistrés", "notifications": "Notifications", + "tags": "Sujets contenant le mot-clé \"%1\"", "user.edit": "Edite \"%1\"", "user.following": "Personnes que %1 suit", "user.followers": "Personnes qui suivent %1", diff --git a/public/language/fr/search.json b/public/language/fr/search.json new file mode 100644 index 0000000000..ab88db2e52 --- /dev/null +++ b/public/language/fr/search.json @@ -0,0 +1,3 @@ +{ + "results_matching": "%1 résultat(s) correspondant(s) à \"%2\", (%3 secondes)" +} \ No newline at end of file diff --git a/public/language/fr/tags.json b/public/language/fr/tags.json index f065d4bbfa..d89f217a37 100644 --- a/public/language/fr/tags.json +++ b/public/language/fr/tags.json @@ -1,6 +1,6 @@ { - "no_tag_topics": "There are no topics with this tag.", - "tags": "Tags", - "enter_tags_here": "Enter tags here. Press enter after each tag.", - "no_tags": "There are no tags yet." + "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é.", + "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 87d6eb4065..47473793ec 100644 --- a/public/language/fr/topic.json +++ b/public/language/fr/topic.json @@ -87,6 +87,7 @@ "more_users_and_guests": "%1 autre(s) utilisateur(s) et %2 invité(s)", "more_users": "%1 autre(s) utilisateur(s)", "more_guests": "%1 autre(s) invité(s)", + "users_and_others": "%1 and %2 others", "sort_by": "Trier", "oldest_to_newest": "Du plus ancien au plus récent", "newest_to_oldest": "Du plus récent au plus ancien", diff --git a/public/language/he/email.json b/public/language/he/email.json new file mode 100644 index 0000000000..98e591ab02 --- /dev/null +++ b/public/language/he/email.json @@ -0,0 +1,20 @@ +{ + "password-reset-requested": "Password Reset Requested - %1!", + "welcome-to": "Welcome to %1", + "greeting_no_name": "Hello", + "greeting_with_name": "Hello %1", + "welcome.text1": "Thank you for registering with %1!", + "welcome.text2": "To fully activate your account, we need to verify that you own the email address you registered with.", + "welcome.cta": "Click here to confirm your email address", + "reset.text1": "We received a request to reset your password, possibly because you have forgotten it. If this is not the case, please ignore this email.", + "reset.text2": "To continue with the password reset, please click on the following link:", + "reset.cta": "Click here to reset your password", + "digest.notifications": "You have some unread notifications from %1:", + "digest.latest_topics": "Latest topics from %1", + "digest.cta": "Click here to visit %1", + "digest.unsub.info": "This digest was sent to you due to your subscription settings.", + "digest.unsub.cta": "Click here to alter those settings", + "digest.daily.no_topics": "There have been no active topics in the past day", + "test.text1": "This is a test email to verify that the emailer is set up correctly for your NodeBB.", + "closing": "Thanks!" +} \ No newline at end of file diff --git a/public/language/he/error.json b/public/language/he/error.json index a247274584..c5cd72a2a0 100644 --- a/public/language/he/error.json +++ b/public/language/he/error.json @@ -25,6 +25,7 @@ "no-user": "משתמש אינו קיים", "no-teaser": "גרין (טיזר) אינו קיים", "no-privileges": "ההרשאות שלך אינן מספיקות לביצוי פעולה זו", + "no-emailers-configured": "No email plugins were loaded, so a test email could not be sent", "category-disabled": "קטגוריה לא פעילה", "topic-locked": "נושא נעול", "still-uploading": "אנא המתן לסיום ההעלאות", diff --git a/public/language/he/footer.json b/public/language/he/footer.json deleted file mode 100644 index 5be5a59dc2..0000000000 --- a/public/language/he/footer.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "stats.online": "מחוברים", - "stats.users": "משתמשים", - "stats.topics": "נושאים", - "stats.posts": "פוסטים", - "success": "הצלחה" -} \ No newline at end of file diff --git a/public/language/he/groups.json b/public/language/he/groups.json new file mode 100644 index 0000000000..c00c111e11 --- /dev/null +++ b/public/language/he/groups.json @@ -0,0 +1,7 @@ +{ + "view_group": "View Group", + "details.title": "Group Details", + "details.members": "Member List", + "details.has_no_posts": "This group's members have not made any posts.", + "details.latest_posts": "Latest Posts" +} \ No newline at end of file diff --git a/public/language/he/notifications.json b/public/language/he/notifications.json index 819f00cbb4..bcbb5eac8b 100644 --- a/public/language/he/notifications.json +++ b/public/language/he/notifications.json @@ -4,12 +4,11 @@ "see_all": "צפה בכל ההתראות", "back_to_home": "Back to %1", "outgoing_link": "לינק", - "outgoing_link_message": "אתה כעת עוזב", - "continue_to": "המשך ל", - "return_to": "Return to", + "outgoing_link_message": "You are now leaving %1.", + "continue_to": "Continue to %1", + "return_to": "Return to %1", "new_notification": "New Notification", "you_have_unread_notifications": "You have unread notifications.", - "user_made_post": "%1 made a new post", "new_message_from": "New message from %1", "upvoted_your_post": "%1 has upvoted your post.", "favourited_your_post": "%1 has favourited your post.", diff --git a/public/language/he/pages.json b/public/language/he/pages.json index 223dfd6d5d..d12dc6944f 100644 --- a/public/language/he/pages.json +++ b/public/language/he/pages.json @@ -5,6 +5,7 @@ "recent": "נושאים אחרונים", "users": "משתמשים רשומים", "notifications": "התראות", + "tags": "Topics tagged under \"%1\"", "user.edit": "עורך את %1", "user.following": "אנשים ש%1 עוקב אחריהם", "user.followers": "אנשים שעוקבים אחרי %1", diff --git a/public/language/he/search.json b/public/language/he/search.json new file mode 100644 index 0000000000..d0ffc64f36 --- /dev/null +++ b/public/language/he/search.json @@ -0,0 +1,3 @@ +{ + "results_matching": "%1 result(s) matching \"%2\", (%3 seconds)" +} \ No newline at end of file diff --git a/public/language/he/topic.json b/public/language/he/topic.json index 134317c2d8..8bff5a472c 100644 --- a/public/language/he/topic.json +++ b/public/language/he/topic.json @@ -87,6 +87,7 @@ "more_users_and_guests": "%1 more user(s) and %2 guest(s)", "more_users": "%1 more user(s)", "more_guests": "%1 more guest(s)", + "users_and_others": "%1 and %2 others", "sort_by": "Sort by", "oldest_to_newest": "Oldest to Newest", "newest_to_oldest": "Newest to Oldest", diff --git a/public/language/hu/email.json b/public/language/hu/email.json new file mode 100644 index 0000000000..98e591ab02 --- /dev/null +++ b/public/language/hu/email.json @@ -0,0 +1,20 @@ +{ + "password-reset-requested": "Password Reset Requested - %1!", + "welcome-to": "Welcome to %1", + "greeting_no_name": "Hello", + "greeting_with_name": "Hello %1", + "welcome.text1": "Thank you for registering with %1!", + "welcome.text2": "To fully activate your account, we need to verify that you own the email address you registered with.", + "welcome.cta": "Click here to confirm your email address", + "reset.text1": "We received a request to reset your password, possibly because you have forgotten it. If this is not the case, please ignore this email.", + "reset.text2": "To continue with the password reset, please click on the following link:", + "reset.cta": "Click here to reset your password", + "digest.notifications": "You have some unread notifications from %1:", + "digest.latest_topics": "Latest topics from %1", + "digest.cta": "Click here to visit %1", + "digest.unsub.info": "This digest was sent to you due to your subscription settings.", + "digest.unsub.cta": "Click here to alter those settings", + "digest.daily.no_topics": "There have been no active topics in the past day", + "test.text1": "This is a test email to verify that the emailer is set up correctly for your NodeBB.", + "closing": "Thanks!" +} \ No newline at end of file diff --git a/public/language/hu/error.json b/public/language/hu/error.json index 4d55d9a4f3..77c582b23f 100644 --- a/public/language/hu/error.json +++ b/public/language/hu/error.json @@ -25,6 +25,7 @@ "no-user": "User doesn't exist", "no-teaser": "Teaser doesn't exist", "no-privileges": "You don't 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", "topic-locked": "Topic Locked", "still-uploading": "Please wait for uploads to complete.", diff --git a/public/language/hu/footer.json b/public/language/hu/footer.json deleted file mode 100644 index 163b0fb930..0000000000 --- a/public/language/hu/footer.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "stats.online": "Online", - "stats.users": "Tag", - "stats.topics": "Téma", - "stats.posts": "Hozzászólás", - "success": "sikeres" -} \ No newline at end of file diff --git a/public/language/hu/global.json b/public/language/hu/global.json index 856e8b74a8..58d326cbd5 100644 --- a/public/language/hu/global.json +++ b/public/language/hu/global.json @@ -13,7 +13,7 @@ "please_log_in": "Kérjük, jelentkezzen be", "logout": "Kijelentkezés", "posting_restriction_info": "Posting is currently restricted to registered members only, click here to log in.", - "welcome_back": "Welcome Back ", + "welcome_back": "Welcome Back", "you_have_successfully_logged_in": "Sikeresen bejelentkeztél", "save_changes": "Változások mentése", "close": "Bezár", diff --git a/public/language/hu/groups.json b/public/language/hu/groups.json new file mode 100644 index 0000000000..c00c111e11 --- /dev/null +++ b/public/language/hu/groups.json @@ -0,0 +1,7 @@ +{ + "view_group": "View Group", + "details.title": "Group Details", + "details.members": "Member List", + "details.has_no_posts": "This group's members have not made any posts.", + "details.latest_posts": "Latest Posts" +} \ No newline at end of file diff --git a/public/language/hu/notifications.json b/public/language/hu/notifications.json index 7a138c71ec..c189a4e5bb 100644 --- a/public/language/hu/notifications.json +++ b/public/language/hu/notifications.json @@ -4,12 +4,11 @@ "see_all": "Összes értesítés megtekintése", "back_to_home": "Back to %1", "outgoing_link": "Külső Link", - "outgoing_link_message": "Most távozol", - "continue_to": "Folytatás", - "return_to": "Return to", + "outgoing_link_message": "You are now leaving %1.", + "continue_to": "Continue to %1", + "return_to": "Return to %1", "new_notification": "New Notification", "you_have_unread_notifications": "You have unread notifications.", - "user_made_post": "%1 made a new post", "new_message_from": "New message from %1", "upvoted_your_post": "%1 has upvoted your post.", "favourited_your_post": "%1 has favourited your post.", diff --git a/public/language/hu/pages.json b/public/language/hu/pages.json index 5ea9bdd9a7..7057c11031 100644 --- a/public/language/hu/pages.json +++ b/public/language/hu/pages.json @@ -5,6 +5,7 @@ "recent": "Friss Topikok", "users": "Regisztrált Felhasználók", "notifications": "Értesítések", + "tags": "Topics tagged under \"%1\"", "user.edit": "Szerkesztés \"%1\"", "user.following": "Tagok akiket %1 követ", "user.followers": "Tagok akik követik %1 -t", diff --git a/public/language/hu/search.json b/public/language/hu/search.json new file mode 100644 index 0000000000..d0ffc64f36 --- /dev/null +++ b/public/language/hu/search.json @@ -0,0 +1,3 @@ +{ + "results_matching": "%1 result(s) matching \"%2\", (%3 seconds)" +} \ No newline at end of file diff --git a/public/language/hu/topic.json b/public/language/hu/topic.json index bd8d405739..693d3466f5 100644 --- a/public/language/hu/topic.json +++ b/public/language/hu/topic.json @@ -87,6 +87,7 @@ "more_users_and_guests": "%1 more user(s) and %2 guest(s)", "more_users": "%1 more user(s)", "more_guests": "%1 more guest(s)", + "users_and_others": "%1 and %2 others", "sort_by": "Sort by", "oldest_to_newest": "Oldest to Newest", "newest_to_oldest": "Newest to Oldest", diff --git a/public/language/it/email.json b/public/language/it/email.json new file mode 100644 index 0000000000..98e591ab02 --- /dev/null +++ b/public/language/it/email.json @@ -0,0 +1,20 @@ +{ + "password-reset-requested": "Password Reset Requested - %1!", + "welcome-to": "Welcome to %1", + "greeting_no_name": "Hello", + "greeting_with_name": "Hello %1", + "welcome.text1": "Thank you for registering with %1!", + "welcome.text2": "To fully activate your account, we need to verify that you own the email address you registered with.", + "welcome.cta": "Click here to confirm your email address", + "reset.text1": "We received a request to reset your password, possibly because you have forgotten it. If this is not the case, please ignore this email.", + "reset.text2": "To continue with the password reset, please click on the following link:", + "reset.cta": "Click here to reset your password", + "digest.notifications": "You have some unread notifications from %1:", + "digest.latest_topics": "Latest topics from %1", + "digest.cta": "Click here to visit %1", + "digest.unsub.info": "This digest was sent to you due to your subscription settings.", + "digest.unsub.cta": "Click here to alter those settings", + "digest.daily.no_topics": "There have been no active topics in the past day", + "test.text1": "This is a test email to verify that the emailer is set up correctly for your NodeBB.", + "closing": "Thanks!" +} \ No newline at end of file diff --git a/public/language/it/error.json b/public/language/it/error.json index 4d55d9a4f3..77c582b23f 100644 --- a/public/language/it/error.json +++ b/public/language/it/error.json @@ -25,6 +25,7 @@ "no-user": "User doesn't exist", "no-teaser": "Teaser doesn't exist", "no-privileges": "You don't 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", "topic-locked": "Topic Locked", "still-uploading": "Please wait for uploads to complete.", diff --git a/public/language/it/footer.json b/public/language/it/footer.json deleted file mode 100644 index 99dcfe3894..0000000000 --- a/public/language/it/footer.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "stats.online": "Online", - "stats.users": "Utenti", - "stats.topics": "Argomenti", - "stats.posts": "Post", - "success": "successo" -} \ No newline at end of file diff --git a/public/language/it/global.json b/public/language/it/global.json index 6e97099f5e..95a6d2c42a 100644 --- a/public/language/it/global.json +++ b/public/language/it/global.json @@ -13,7 +13,7 @@ "please_log_in": "Per favore Accedi", "logout": "Logout", "posting_restriction_info": "L'inserimento è attualmente ristretto ai soli utenti registrati, clicca qui per effettuare l'accesso.", - "welcome_back": "Bentornato", + "welcome_back": "Welcome Back", "you_have_successfully_logged_in": "Login avvenuto con successo", "save_changes": "Salva cambiamenti", "close": "Chiudi", diff --git a/public/language/it/groups.json b/public/language/it/groups.json new file mode 100644 index 0000000000..c00c111e11 --- /dev/null +++ b/public/language/it/groups.json @@ -0,0 +1,7 @@ +{ + "view_group": "View Group", + "details.title": "Group Details", + "details.members": "Member List", + "details.has_no_posts": "This group's members have not made any posts.", + "details.latest_posts": "Latest Posts" +} \ No newline at end of file diff --git a/public/language/it/notifications.json b/public/language/it/notifications.json index 15af65a07d..1d2e122b74 100644 --- a/public/language/it/notifications.json +++ b/public/language/it/notifications.json @@ -4,12 +4,11 @@ "see_all": "Vedi tutte le Notifiche", "back_to_home": "Back to %1", "outgoing_link": "Link in uscita", - "outgoing_link_message": "Stai lasciando", - "continue_to": "Continua verso", - "return_to": "Return to", + "outgoing_link_message": "You are now leaving %1.", + "continue_to": "Continue to %1", + "return_to": "Return to %1", "new_notification": "Nuove Notifiche", "you_have_unread_notifications": "Hai notifiche non lette.", - "user_made_post": "%1 ha scritto un nuovo post", "new_message_from": "Nuovo messaggio da %1", "upvoted_your_post": "%1 has upvoted your post.", "favourited_your_post": "%1 has favourited your post.", diff --git a/public/language/it/pages.json b/public/language/it/pages.json index b60ecd15b3..68cf073ea7 100644 --- a/public/language/it/pages.json +++ b/public/language/it/pages.json @@ -5,6 +5,7 @@ "recent": "Argomenti Recenti", "users": "Utenti Registrati", "notifications": "Notifiche", + "tags": "Topics tagged under \"%1\"", "user.edit": "Modificando \"%1\"", "user.following": "%1 Persone seguono", "user.followers": "Persone che seguono %1", diff --git a/public/language/it/search.json b/public/language/it/search.json new file mode 100644 index 0000000000..d0ffc64f36 --- /dev/null +++ b/public/language/it/search.json @@ -0,0 +1,3 @@ +{ + "results_matching": "%1 result(s) matching \"%2\", (%3 seconds)" +} \ No newline at end of file diff --git a/public/language/it/topic.json b/public/language/it/topic.json index bf4f5051e8..5f18f9cd39 100644 --- a/public/language/it/topic.json +++ b/public/language/it/topic.json @@ -87,6 +87,7 @@ "more_users_and_guests": "%1 more user(s) and %2 guest(s)", "more_users": "%1 more user(s)", "more_guests": "%1 more guest(s)", + "users_and_others": "%1 and %2 others", "sort_by": "Sort by", "oldest_to_newest": "Oldest to Newest", "newest_to_oldest": "Newest to Oldest", diff --git a/public/language/ja/email.json b/public/language/ja/email.json new file mode 100644 index 0000000000..98e591ab02 --- /dev/null +++ b/public/language/ja/email.json @@ -0,0 +1,20 @@ +{ + "password-reset-requested": "Password Reset Requested - %1!", + "welcome-to": "Welcome to %1", + "greeting_no_name": "Hello", + "greeting_with_name": "Hello %1", + "welcome.text1": "Thank you for registering with %1!", + "welcome.text2": "To fully activate your account, we need to verify that you own the email address you registered with.", + "welcome.cta": "Click here to confirm your email address", + "reset.text1": "We received a request to reset your password, possibly because you have forgotten it. If this is not the case, please ignore this email.", + "reset.text2": "To continue with the password reset, please click on the following link:", + "reset.cta": "Click here to reset your password", + "digest.notifications": "You have some unread notifications from %1:", + "digest.latest_topics": "Latest topics from %1", + "digest.cta": "Click here to visit %1", + "digest.unsub.info": "This digest was sent to you due to your subscription settings.", + "digest.unsub.cta": "Click here to alter those settings", + "digest.daily.no_topics": "There have been no active topics in the past day", + "test.text1": "This is a test email to verify that the emailer is set up correctly for your NodeBB.", + "closing": "Thanks!" +} \ No newline at end of file diff --git a/public/language/ja/error.json b/public/language/ja/error.json index d3d79457a2..cbaf323360 100644 --- a/public/language/ja/error.json +++ b/public/language/ja/error.json @@ -25,6 +25,7 @@ "no-user": "ユーザーが存在しない", "no-teaser": "ティーザーが存在しない", "no-privileges": "このアクションを実行する権限を持っていない。", + "no-emailers-configured": "No email plugins were loaded, so a test email could not be sent", "category-disabled": "この板は無効された", "topic-locked": "スレッドがロックされた", "still-uploading": "アップロードが完成するまでお待ちください。", diff --git a/public/language/ja/footer.json b/public/language/ja/footer.json deleted file mode 100644 index 41437643ee..0000000000 --- a/public/language/ja/footer.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "stats.online": "利用者", - "stats.users": "登録", - "stats.topics": "スレッド", - "stats.posts": "ポスト", - "success": "成功" -} diff --git a/public/language/ja/global.json b/public/language/ja/global.json index d9a27e58f4..4f60af52bb 100644 --- a/public/language/ja/global.json +++ b/public/language/ja/global.json @@ -13,7 +13,7 @@ "please_log_in": "ログインください", "logout": "ログアウト", "posting_restriction_info": "登録ユーザーのみが投稿可能となります.こちらからログインください。", - "welcome_back": "お帰りなさい", + "welcome_back": "Welcome Back", "you_have_successfully_logged_in": "ログインできました", "save_changes": "保存する", "close": "閉じる", diff --git a/public/language/ja/groups.json b/public/language/ja/groups.json new file mode 100644 index 0000000000..c00c111e11 --- /dev/null +++ b/public/language/ja/groups.json @@ -0,0 +1,7 @@ +{ + "view_group": "View Group", + "details.title": "Group Details", + "details.members": "Member List", + "details.has_no_posts": "This group's members have not made any posts.", + "details.latest_posts": "Latest Posts" +} \ No newline at end of file diff --git a/public/language/ja/notifications.json b/public/language/ja/notifications.json index 4892251e80..efe8333fac 100644 --- a/public/language/ja/notifications.json +++ b/public/language/ja/notifications.json @@ -4,12 +4,11 @@ "see_all": "すべての通知を確認", "back_to_home": "Back to %1", "outgoing_link": "外部サイトへのリンク", - "outgoing_link_message": "リービング", - "continue_to": "続き", - "return_to": "Return to", + "outgoing_link_message": "You are now leaving %1.", + "continue_to": "Continue to %1", + "return_to": "Return to %1", "new_notification": "新しい通知", "you_have_unread_notifications": "未読の通知があります。", - "user_made_post": "%1は新しいポストを投稿しました。", "new_message_from": "%1からの新しいメッセージ", "upvoted_your_post": "%1はあなたのポストを評価しました。", "favourited_your_post": "%1はあなたのポストをお気に入りにしました。", diff --git a/public/language/ja/pages.json b/public/language/ja/pages.json index e9494c8d3f..36295b2fa5 100644 --- a/public/language/ja/pages.json +++ b/public/language/ja/pages.json @@ -5,6 +5,7 @@ "recent": "最新スレッド", "users": "登録したユーザー", "notifications": "通知", + "tags": "Topics tagged under \"%1\"", "user.edit": "編集中 \"%1\"", "user.following": "%1がフォロー中", "user.followers": "%1のフォロワー", diff --git a/public/language/ja/search.json b/public/language/ja/search.json new file mode 100644 index 0000000000..d0ffc64f36 --- /dev/null +++ b/public/language/ja/search.json @@ -0,0 +1,3 @@ +{ + "results_matching": "%1 result(s) matching \"%2\", (%3 seconds)" +} \ No newline at end of file diff --git a/public/language/ja/topic.json b/public/language/ja/topic.json index f2411f91c8..7d24e2e34f 100644 --- a/public/language/ja/topic.json +++ b/public/language/ja/topic.json @@ -87,6 +87,7 @@ "more_users_and_guests": "%1 more user(s) and %2 guest(s)", "more_users": "%1 more user(s)", "more_guests": "%1 more guest(s)", + "users_and_others": "%1 and %2 others", "sort_by": "Sort by", "oldest_to_newest": "Oldest to Newest", "newest_to_oldest": "Newest to Oldest", diff --git a/public/language/ko/email.json b/public/language/ko/email.json new file mode 100644 index 0000000000..98e591ab02 --- /dev/null +++ b/public/language/ko/email.json @@ -0,0 +1,20 @@ +{ + "password-reset-requested": "Password Reset Requested - %1!", + "welcome-to": "Welcome to %1", + "greeting_no_name": "Hello", + "greeting_with_name": "Hello %1", + "welcome.text1": "Thank you for registering with %1!", + "welcome.text2": "To fully activate your account, we need to verify that you own the email address you registered with.", + "welcome.cta": "Click here to confirm your email address", + "reset.text1": "We received a request to reset your password, possibly because you have forgotten it. If this is not the case, please ignore this email.", + "reset.text2": "To continue with the password reset, please click on the following link:", + "reset.cta": "Click here to reset your password", + "digest.notifications": "You have some unread notifications from %1:", + "digest.latest_topics": "Latest topics from %1", + "digest.cta": "Click here to visit %1", + "digest.unsub.info": "This digest was sent to you due to your subscription settings.", + "digest.unsub.cta": "Click here to alter those settings", + "digest.daily.no_topics": "There have been no active topics in the past day", + "test.text1": "This is a test email to verify that the emailer is set up correctly for your NodeBB.", + "closing": "Thanks!" +} \ No newline at end of file diff --git a/public/language/ko/error.json b/public/language/ko/error.json index 3a91281f17..fa961bc709 100644 --- a/public/language/ko/error.json +++ b/public/language/ko/error.json @@ -2,21 +2,21 @@ "invalid-data": "올바르지 않은 정보입니다.", "not-logged-in": "로그인하지 않았습니다.", "account-locked": "임시로 잠긴 계정입니다.", - "search-requires-login": "Searching requires an account! Please login or register!", + "search-requires-login": "검색을 위해서는 계정이 필요합니다. 로그인하거나 회원가입 해 주십시오.", "invalid-cid": "올바르지 않은 카테고리 ID입니다.", "invalid-tid": "올바르지 않은 주제 ID입니다.", "invalid-pid": "올바르지 않은 게시물 ID입니다.", "invalid-uid": "올바르지 않은 사용자 ID입니다.", "invalid-username": "올바르지 않은 사용자 이름입니다.", "invalid-email": "올바르지 않은 이메일입니다.", - "invalid-title": "Invalid title!", + "invalid-title": "올바르지 않은 제목입니다.", "invalid-user-data": "올바르지 않은 사용자 정보입니다.", "invalid-password": "올바르지 않은 비밀번호입니다.", "invalid-pagination-value": "올바르지 않은 페이지입니다.", "username-taken": "이미 사용 중인 사용자 이름입니다.", "email-taken": "이미 사용 중인 이메일입니다.", - "email-not-confirmed": "Your email is not confirmed, please click here to confirm your email.", - "username-too-short": "Username too short", + "email-not-confirmed": "아직 이메일이 인증되지 않았습니다. 여기를 누르면 인증 메일을 발송할 수 있습니다.", + "username-too-short": "사용자 이름이 너무 짧습니다.", "user-banned": "차단된 사용자입니다.", "no-category": "존재하지 않는 카테고리입니다.", "no-topic": "존재하지 않는 주제입니다.", @@ -25,6 +25,7 @@ "no-user": "존재하지 않는 사용자입니다.", "no-teaser": "존재하지 않는 미리보기입니다.", "no-privileges": "이 작업을 할 수 있는 권한이 없습니다.", + "no-emailers-configured": "이메일 추가기능이 로드되지 않았으므로 테스트 메일을 발송할 수 없습니다.", "category-disabled": "비활성화된 카테고리입니다.", "topic-locked": "잠긴 주제입니다.", "still-uploading": "업로드가 끝날 때까지 기다려 주세요.", @@ -33,9 +34,9 @@ "title-too-long": "제목은 최대 %1자로 제한됩니다.", "too-many-posts": "새 게시물 작성은 %1초 간격으로 제한됩니다", "file-too-big": "파일의 크기는 최대 %1KB로 제한됩니다.", - "cant-vote-self-post": "자신의 게시물은 추천할 수 없습니다.", + "cant-vote-self-post": "자신의 게시물에는 투표할 수 없습니다.", "already-favourited": "이미 이 게시물을 좋아하는 중입니다.", - "already-unfavourited": "You already unfavourited this post", + "already-unfavourited": "이미 이 게시물을 좋아하고 있지 않습니다.", "cant-ban-other-admins": "다른 관리자를 차단할 수 없습니다.", "invalid-image-type": "올바르지 않은 이미지입니다.", "group-name-too-short": "그룹 이름이 너무 짧습니다.", @@ -48,8 +49,8 @@ "topic-thumbnails-are-disabled": "주제 섬네일이 이미 해제되었습니다.", "invalid-file": "올바르지 않은 파일입니다.", "uploads-are-disabled": "업로드는 비활성화되어 있습니다.", - "upload-error": "Upload Error : %1", + "upload-error": "업로드 오류가 발생했습니다. : %1", "signature-too-long": "서명은 최대 %1자로 제한됩니다.", "cant-chat-with-yourself": "자신과는 채팅할 수 없습니다.", - "not-enough-reputation-to-downvote": "You do not have enough reputation to downvote this post" + "not-enough-reputation-to-downvote": "인기도가 낮아 이 게시물에 반대할 수 없습니다." } \ No newline at end of file diff --git a/public/language/ko/footer.json b/public/language/ko/footer.json deleted file mode 100644 index ef75c4211d..0000000000 --- a/public/language/ko/footer.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "stats.online": "온라인", - "stats.users": "사용자", - "stats.topics": "주제", - "stats.posts": "게시물", - "success": "성공" -} diff --git a/public/language/ko/global.json b/public/language/ko/global.json index 9e4ea9c9da..0a124a111c 100644 --- a/public/language/ko/global.json +++ b/public/language/ko/global.json @@ -13,17 +13,17 @@ "please_log_in": "로그인해 주세요.", "logout": "로그아웃", "posting_restriction_info": "게시물 작성은 현재 회원에게만 제한되고 있습니다. 여기를 누르면 로그인 페이지로 이동합니다.", - "welcome_back": "환영합니다 : ", + "welcome_back": "환영합니다.", "you_have_successfully_logged_in": "성공적으로 로그인했습니다.", "save_changes": "저장", "close": "닫기", "pagination": "페이지", "pagination.out_of": "%1개 중 %2개", - "pagination.enter_index": "Enter index", + "pagination.enter_index": "이동할 게시물 번호를 입력하세요.", "header.admin": "관리자", "header.recent": "최근 주제", "header.unread": "읽지 않은 주제", - "header.tags": "Tags", + "header.tags": "태그", "header.popular": "인기 주제", "header.users": "사용자", "header.chats": "채팅", @@ -70,6 +70,6 @@ "language": "언어", "guest": "익명 사용자", "guests": "익명 사용자", - "updated.title": "Forum Updated", - "updated.message": "This forum has just been updated to the latest version. Click here to refresh the page." + "updated.title": "포럼이 업데이트 되었습니다.", + "updated.message": "이 포럼은 지금 최신 버전으로 업데이트 되었습니다. 여기를 누르면 페이지를 새로고침합니다." } \ No newline at end of file diff --git a/public/language/ko/groups.json b/public/language/ko/groups.json new file mode 100644 index 0000000000..c00c111e11 --- /dev/null +++ b/public/language/ko/groups.json @@ -0,0 +1,7 @@ +{ + "view_group": "View Group", + "details.title": "Group Details", + "details.members": "Member List", + "details.has_no_posts": "This group's members have not made any posts.", + "details.latest_posts": "Latest Posts" +} \ No newline at end of file diff --git a/public/language/ko/notifications.json b/public/language/ko/notifications.json index 815c69f92c..8ececb6c49 100644 --- a/public/language/ko/notifications.json +++ b/public/language/ko/notifications.json @@ -4,12 +4,11 @@ "see_all": "모든 알림 보기", "back_to_home": "Back to %1", "outgoing_link": "외부 링크", - "outgoing_link_message": "다른 사이트로 이동합니다.", - "continue_to": "계속", - "return_to": "Return to", + "outgoing_link_message": "You are now leaving %1.", + "continue_to": "Continue to %1", + "return_to": "Return to %1", "new_notification": "새 알림", "you_have_unread_notifications": "읽지 않은 알림이 있습니다.", - "user_made_post": "%1님이 새 게시물을 작성했습니다.", "new_message_from": "%1님이 메시지를 보냈습니다.", "upvoted_your_post": "%1님이 내 게시물을 추천했습니다.", "favourited_your_post": "%1님이 내 게시물을 관심글로 등록했습니다.", diff --git a/public/language/ko/pages.json b/public/language/ko/pages.json index 230a012757..e72e0b57c0 100644 --- a/public/language/ko/pages.json +++ b/public/language/ko/pages.json @@ -5,6 +5,7 @@ "recent": "최근 주제", "users": "사용자", "notifications": "알림", + "tags": "Topics tagged under \"%1\"", "user.edit": "%1님의 프로필 수정", "user.following": "%1님이 팔로우하는 사용자", "user.followers": "%1님을 팔로우하는 사용자", diff --git a/public/language/ko/search.json b/public/language/ko/search.json new file mode 100644 index 0000000000..d0ffc64f36 --- /dev/null +++ b/public/language/ko/search.json @@ -0,0 +1,3 @@ +{ + "results_matching": "%1 result(s) matching \"%2\", (%3 seconds)" +} \ No newline at end of file diff --git a/public/language/ko/topic.json b/public/language/ko/topic.json index e881cd9285..3619cdc736 100644 --- a/public/language/ko/topic.json +++ b/public/language/ko/topic.json @@ -87,6 +87,7 @@ "more_users_and_guests": "%1명 이상의 회원과 %2명의 익명 사용자", "more_users": "%1명 이상의 회원", "more_guests": "%1명 이상의 익명 사용자", + "users_and_others": "%1 and %2 others", "sort_by": "Sort by", "oldest_to_newest": "Oldest to Newest", "newest_to_oldest": "Newest to Oldest", diff --git a/public/language/ko/user.json b/public/language/ko/user.json index af07583300..baf5183211 100644 --- a/public/language/ko/user.json +++ b/public/language/ko/user.json @@ -3,7 +3,7 @@ "offline": "오프라인", "username": "사용자 이름", "email": "이메일", - "confirm_email": "Confirm Email", + "confirm_email": "이메일 확인", "fullname": "이름", "website": "웹 사이트", "location": "거주지", diff --git a/public/language/lt/email.json b/public/language/lt/email.json new file mode 100644 index 0000000000..98e591ab02 --- /dev/null +++ b/public/language/lt/email.json @@ -0,0 +1,20 @@ +{ + "password-reset-requested": "Password Reset Requested - %1!", + "welcome-to": "Welcome to %1", + "greeting_no_name": "Hello", + "greeting_with_name": "Hello %1", + "welcome.text1": "Thank you for registering with %1!", + "welcome.text2": "To fully activate your account, we need to verify that you own the email address you registered with.", + "welcome.cta": "Click here to confirm your email address", + "reset.text1": "We received a request to reset your password, possibly because you have forgotten it. If this is not the case, please ignore this email.", + "reset.text2": "To continue with the password reset, please click on the following link:", + "reset.cta": "Click here to reset your password", + "digest.notifications": "You have some unread notifications from %1:", + "digest.latest_topics": "Latest topics from %1", + "digest.cta": "Click here to visit %1", + "digest.unsub.info": "This digest was sent to you due to your subscription settings.", + "digest.unsub.cta": "Click here to alter those settings", + "digest.daily.no_topics": "There have been no active topics in the past day", + "test.text1": "This is a test email to verify that the emailer is set up correctly for your NodeBB.", + "closing": "Thanks!" +} \ No newline at end of file diff --git a/public/language/lt/error.json b/public/language/lt/error.json index 11eb013410..ca771bc828 100644 --- a/public/language/lt/error.json +++ b/public/language/lt/error.json @@ -25,6 +25,7 @@ "no-user": "Vartotojas neegzistuoja", "no-teaser": "Trumpas skelbimas neegzistuoja!", "no-privileges": "Jūs neturite teisės atlikti šį veiksmą.", + "no-emailers-configured": "No email plugins were loaded, so a test email could not be sent", "category-disabled": "Kategorija išjungta", "topic-locked": "Tema užrakinta", "still-uploading": "Prašome palaukti kol bus baigti visi kėlimai į serverį", diff --git a/public/language/lt/footer.json b/public/language/lt/footer.json deleted file mode 100644 index fcb1c4f205..0000000000 --- a/public/language/lt/footer.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "stats.online": "Prisijungę", - "stats.users": "Vartotojai", - "stats.topics": "Temos", - "stats.posts": "Pranešimai", - "success": "pasisekė" -} \ No newline at end of file diff --git a/public/language/lt/groups.json b/public/language/lt/groups.json new file mode 100644 index 0000000000..c00c111e11 --- /dev/null +++ b/public/language/lt/groups.json @@ -0,0 +1,7 @@ +{ + "view_group": "View Group", + "details.title": "Group Details", + "details.members": "Member List", + "details.has_no_posts": "This group's members have not made any posts.", + "details.latest_posts": "Latest Posts" +} \ No newline at end of file diff --git a/public/language/lt/notifications.json b/public/language/lt/notifications.json index e5ba3f111d..96ffad091b 100644 --- a/public/language/lt/notifications.json +++ b/public/language/lt/notifications.json @@ -4,12 +4,11 @@ "see_all": "Peržiūrėti visus pranešimus", "back_to_home": "Atgal į %1", "outgoing_link": "Išeinanti nuoroda", - "outgoing_link_message": "Dabar jūs išeinate", - "continue_to": "Tęsti", - "return_to": "Grįžti į", + "outgoing_link_message": "You are now leaving %1.", + "continue_to": "Continue to %1", + "return_to": "Return to %1", "new_notification": "Naujas pranešimas", "you_have_unread_notifications": "Jūs turite neperskaitytų pranešimų.", - "user_made_post": "%1 parašė naują pranešimą", "new_message_from": "Nauja žinutė nuo %1", "upvoted_your_post": "%1 teigiamai įvertino jūsų pranešimą.", "favourited_your_post": "%1 pamėgo jūsų pranešimą.", diff --git a/public/language/lt/pages.json b/public/language/lt/pages.json index 78be933e04..4e62692bd3 100644 --- a/public/language/lt/pages.json +++ b/public/language/lt/pages.json @@ -5,6 +5,7 @@ "recent": "Paskutinės temos", "users": "Registruoti vartotojai", "notifications": "Pranešimai", + "tags": "Topics tagged under \"%1\"", "user.edit": "Redaguojama \"%1\"", "user.following": "Vartotojas %1 seka", "user.followers": "Žmonės, kurie seka %1", diff --git a/public/language/lt/search.json b/public/language/lt/search.json new file mode 100644 index 0000000000..d0ffc64f36 --- /dev/null +++ b/public/language/lt/search.json @@ -0,0 +1,3 @@ +{ + "results_matching": "%1 result(s) matching \"%2\", (%3 seconds)" +} \ No newline at end of file diff --git a/public/language/lt/topic.json b/public/language/lt/topic.json index 2642f0dd00..2eb059c363 100644 --- a/public/language/lt/topic.json +++ b/public/language/lt/topic.json @@ -87,6 +87,7 @@ "more_users_and_guests": "dar %1 vartotojai(-ų) ir %2 svečiai(-ių)", "more_users": "dar %1 vartotojai(-ų)", "more_guests": "dar %1 svečiai(-ių)", + "users_and_others": "%1 and %2 others", "sort_by": "Rūšiuoti pagal", "oldest_to_newest": "Nuo seniausių iki naujausių", "newest_to_oldest": "Nuo naujausių iki seniausių", diff --git a/public/language/ms/email.json b/public/language/ms/email.json new file mode 100644 index 0000000000..3126f0da9d --- /dev/null +++ b/public/language/ms/email.json @@ -0,0 +1,20 @@ +{ + "password-reset-requested": "Password Reset Requested - %1!", + "welcome-to": "Selemat datang ke %1", + "greeting_no_name": "Hello", + "greeting_with_name": "Hello %1", + "welcome.text1": "Thank you for registering with %1!", + "welcome.text2": "To fully activate your account, we need to verify that you own the email address you registered with.", + "welcome.cta": "Click here to confirm your email address", + "reset.text1": "We received a request to reset your password, possibly because you have forgotten it. If this is not the case, please ignore this email.", + "reset.text2": "To continue with the password reset, please click on the following link:", + "reset.cta": "Click here to reset your password", + "digest.notifications": "You have some unread notifications from %1:", + "digest.latest_topics": "Latest topics from %1", + "digest.cta": "Click here to visit %1", + "digest.unsub.info": "This digest was sent to you due to your subscription settings.", + "digest.unsub.cta": "Click here to alter those settings", + "digest.daily.no_topics": "There have been no active topics in the past day", + "test.text1": "This is a test email to verify that the emailer is set up correctly for your NodeBB.", + "closing": "Terima Kasih!" +} \ No newline at end of file diff --git a/public/language/ms/error.json b/public/language/ms/error.json index 4d55d9a4f3..c76dc586f1 100644 --- a/public/language/ms/error.json +++ b/public/language/ms/error.json @@ -11,12 +11,12 @@ "invalid-email": "Invalid Email", "invalid-title": "Invalid title!", "invalid-user-data": "Invalid User Data", - "invalid-password": "Invalid Password", + "invalid-password": "Password salah!", "invalid-pagination-value": "Invalid pagination value", "username-taken": "Username taken", "email-taken": "Email taken", "email-not-confirmed": "Your email is not confirmed, please click here to confirm your email.", - "username-too-short": "Username too short", + "username-too-short": "Nama pengunna terlalu pendek", "user-banned": "User banned", "no-category": "Category doesn't exist", "no-topic": "Topic doesn't exist", @@ -25,6 +25,7 @@ "no-user": "User doesn't exist", "no-teaser": "Teaser doesn't exist", "no-privileges": "You don't 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", "topic-locked": "Topic Locked", "still-uploading": "Please wait for uploads to complete.", diff --git a/public/language/ms/global.json b/public/language/ms/global.json index 7f37cd336b..0180ca4e49 100644 --- a/public/language/ms/global.json +++ b/public/language/ms/global.json @@ -13,7 +13,7 @@ "please_log_in": "Sila daftar masuk", "logout": "Log Keluar", "posting_restriction_info": "Kiriman terhad kepada pengguna berdaftar sahaja, Sila click disini untuk daftar masuk", - "welcome_back": "Selamat datang kembali", + "welcome_back": "Welcome Back", "you_have_successfully_logged_in": "Anda telah daftar keluar", "save_changes": "simpan perubahan", "close": "Tutup", diff --git a/public/language/ms/groups.json b/public/language/ms/groups.json new file mode 100644 index 0000000000..c00c111e11 --- /dev/null +++ b/public/language/ms/groups.json @@ -0,0 +1,7 @@ +{ + "view_group": "View Group", + "details.title": "Group Details", + "details.members": "Member List", + "details.has_no_posts": "This group's members have not made any posts.", + "details.latest_posts": "Latest Posts" +} \ No newline at end of file diff --git a/public/language/ms/notifications.json b/public/language/ms/notifications.json index 9f367710cc..530c9d1f93 100644 --- a/public/language/ms/notifications.json +++ b/public/language/ms/notifications.json @@ -4,12 +4,11 @@ "see_all": "LIhat semua pemberitahuan", "back_to_home": "Back to %1", "outgoing_link": "Sambungan luar", - "outgoing_link_message": "Anda sedang keluar", - "continue_to": "Teruskan ke", - "return_to": "Return to", + "outgoing_link_message": "You are now leaving %1.", + "continue_to": "Continue to %1", + "return_to": "Return to %1", "new_notification": "Pemberitahuan baru", "you_have_unread_notifications": "Anda ada pemberitahuan yang belum dibaca", - "user_made_post": "%1 membuat posting baru", "new_message_from": "Pesanan baru daripada %1", "upvoted_your_post": "%1 telah undi-naik posting anda", "favourited_your_post": "strong>%1 telah menggemari posting anda", diff --git a/public/language/ms/pages.json b/public/language/ms/pages.json index 4c5ed0a755..0d1ab9ca76 100644 --- a/public/language/ms/pages.json +++ b/public/language/ms/pages.json @@ -5,6 +5,7 @@ "recent": "Topik Baru", "users": "Pengguna Berdaftar", "notifications": "Makluman", + "tags": "Topics tagged under \"%1\"", "user.edit": "Menyunting \"%1\"", "user.following": "Pengguna yang %1 Ikuti", "user.followers": "Pengguna yang Mengikuti %1", diff --git a/public/language/ms/search.json b/public/language/ms/search.json new file mode 100644 index 0000000000..d0ffc64f36 --- /dev/null +++ b/public/language/ms/search.json @@ -0,0 +1,3 @@ +{ + "results_matching": "%1 result(s) matching \"%2\", (%3 seconds)" +} \ No newline at end of file diff --git a/public/language/ms/topic.json b/public/language/ms/topic.json index e18961bfc9..a40da8e224 100644 --- a/public/language/ms/topic.json +++ b/public/language/ms/topic.json @@ -87,6 +87,7 @@ "more_users_and_guests": "%1 more user(s) and %2 guest(s)", "more_users": "%1 more user(s)", "more_guests": "%1 more guest(s)", + "users_and_others": "%1 and %2 others", "sort_by": "Sort by", "oldest_to_newest": "Oldest to Newest", "newest_to_oldest": "Newest to Oldest", diff --git a/public/language/nb/email.json b/public/language/nb/email.json new file mode 100644 index 0000000000..98e591ab02 --- /dev/null +++ b/public/language/nb/email.json @@ -0,0 +1,20 @@ +{ + "password-reset-requested": "Password Reset Requested - %1!", + "welcome-to": "Welcome to %1", + "greeting_no_name": "Hello", + "greeting_with_name": "Hello %1", + "welcome.text1": "Thank you for registering with %1!", + "welcome.text2": "To fully activate your account, we need to verify that you own the email address you registered with.", + "welcome.cta": "Click here to confirm your email address", + "reset.text1": "We received a request to reset your password, possibly because you have forgotten it. If this is not the case, please ignore this email.", + "reset.text2": "To continue with the password reset, please click on the following link:", + "reset.cta": "Click here to reset your password", + "digest.notifications": "You have some unread notifications from %1:", + "digest.latest_topics": "Latest topics from %1", + "digest.cta": "Click here to visit %1", + "digest.unsub.info": "This digest was sent to you due to your subscription settings.", + "digest.unsub.cta": "Click here to alter those settings", + "digest.daily.no_topics": "There have been no active topics in the past day", + "test.text1": "This is a test email to verify that the emailer is set up correctly for your NodeBB.", + "closing": "Thanks!" +} \ No newline at end of file diff --git a/public/language/nb/error.json b/public/language/nb/error.json index 4d55d9a4f3..77c582b23f 100644 --- a/public/language/nb/error.json +++ b/public/language/nb/error.json @@ -25,6 +25,7 @@ "no-user": "User doesn't exist", "no-teaser": "Teaser doesn't exist", "no-privileges": "You don't 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", "topic-locked": "Topic Locked", "still-uploading": "Please wait for uploads to complete.", diff --git a/public/language/nb/footer.json b/public/language/nb/footer.json deleted file mode 100644 index a6319b8a25..0000000000 --- a/public/language/nb/footer.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "stats.online": "Online", - "stats.users": "Brukere", - "stats.topics": "Emner", - "stats.posts": "Innlegg", - "success": "suksess" -} \ No newline at end of file diff --git a/public/language/nb/groups.json b/public/language/nb/groups.json new file mode 100644 index 0000000000..c00c111e11 --- /dev/null +++ b/public/language/nb/groups.json @@ -0,0 +1,7 @@ +{ + "view_group": "View Group", + "details.title": "Group Details", + "details.members": "Member List", + "details.has_no_posts": "This group's members have not made any posts.", + "details.latest_posts": "Latest Posts" +} \ No newline at end of file diff --git a/public/language/nb/notifications.json b/public/language/nb/notifications.json index 4680231060..96afebd800 100644 --- a/public/language/nb/notifications.json +++ b/public/language/nb/notifications.json @@ -4,12 +4,11 @@ "see_all": "Se alle varsler", "back_to_home": "Back to %1", "outgoing_link": "Utgående link", - "outgoing_link_message": "Du forlatter nå", - "continue_to": "Fortsett til", - "return_to": "Return to", + "outgoing_link_message": "You are now leaving %1.", + "continue_to": "Continue to %1", + "return_to": "Return to %1", "new_notification": "Nytt varsel", "you_have_unread_notifications": "Du har uleste varsler.", - "user_made_post": "%1 lagde ett nytt innlegg", "new_message_from": "Ny melding fra %1", "upvoted_your_post": "%1 har stemt opp ditt innlegg.", "favourited_your_post": "%1 har favorittmerket ditt innlegg.", diff --git a/public/language/nb/pages.json b/public/language/nb/pages.json index 614bbaa676..b0b5326983 100644 --- a/public/language/nb/pages.json +++ b/public/language/nb/pages.json @@ -5,6 +5,7 @@ "recent": "Seneste emner", "users": "Registrerte brukere", "notifications": "Varsler", + "tags": "Topics tagged under \"%1\"", "user.edit": "Endrer \"%1\"", "user.following": "Personer %1 følger", "user.followers": "Personer som følger %1", diff --git a/public/language/nb/search.json b/public/language/nb/search.json new file mode 100644 index 0000000000..d0ffc64f36 --- /dev/null +++ b/public/language/nb/search.json @@ -0,0 +1,3 @@ +{ + "results_matching": "%1 result(s) matching \"%2\", (%3 seconds)" +} \ No newline at end of file diff --git a/public/language/nb/topic.json b/public/language/nb/topic.json index 7bc2eeff46..9aeddb6ecf 100644 --- a/public/language/nb/topic.json +++ b/public/language/nb/topic.json @@ -87,6 +87,7 @@ "more_users_and_guests": "%1 more user(s) and %2 guest(s)", "more_users": "%1 more user(s)", "more_guests": "%1 more guest(s)", + "users_and_others": "%1 and %2 others", "sort_by": "Sort by", "oldest_to_newest": "Oldest to Newest", "newest_to_oldest": "Newest to Oldest", diff --git a/public/language/nl/category.json b/public/language/nl/category.json index d2dae2aa81..59ca9e6530 100644 --- a/public/language/nl/category.json +++ b/public/language/nl/category.json @@ -3,5 +3,5 @@ "no_topics": "Er zijn geen onderwerpen in deze categorie.
Waarom maak je er niet een aan?", "browsing": "verkennen", "no_replies": "Niemand heeft gereageerd", - "share_this_category": "Share this category" + "share_this_category": "Deel deze categorie" } \ No newline at end of file diff --git a/public/language/nl/email.json b/public/language/nl/email.json new file mode 100644 index 0000000000..a4bd403e2a --- /dev/null +++ b/public/language/nl/email.json @@ -0,0 +1,20 @@ +{ + "password-reset-requested": "Wachtwoord Reset Aangevraagd - %1!", + "welcome-to": "Welkom bij %1", + "greeting_no_name": "Hallo", + "greeting_with_name": "Hallo %1", + "welcome.text1": "Bedank voor het registreren met %1!", + "welcome.text2": "Om u account volledig te activeren, moet u op de link klikken die u heeft ontvangen in uw inbox", + "welcome.cta": "Klik hier om te bevestigen met uw email adres", + "reset.text1": "Wij ontvingen een verzoek van u om uw wachtwoord te resetten. Als dat niet het geval is, kunt u deze mail negeren ", + "reset.text2": "Om uw wachtwoord te resetten, klik op de volgende link", + "reset.cta": "Klik hier om u wachtwoord te resetten", + "digest.notifications": "U heeft ongelezen notificaties van %1:", + "digest.latest_topics": "De laatste onderwerpen van %1", + "digest.cta": "Klik hier om deze website te bezoeken %1 ", + "digest.unsub.info": "Deze overzicht was verzonden naar jou vanwege je abbonement instellingen", + "digest.unsub.cta": "Klik hier om u instellingen te wijzigen", + "digest.daily.no_topics": "Er zijn geen actieve topics vandaag", + "test.text1": "Dit is een test email om te verifiëren dat de email service correct is opgezet voor jou NodeBB", + "closing": "Bedankt!" +} \ No newline at end of file diff --git a/public/language/nl/error.json b/public/language/nl/error.json index 4d55d9a4f3..b6a85567d1 100644 --- a/public/language/nl/error.json +++ b/public/language/nl/error.json @@ -1,55 +1,56 @@ { - "invalid-data": "Invalid Data", - "not-logged-in": "You don't seem to be logged in.", - "account-locked": "Your account has been locked temporarily", - "search-requires-login": "Searching requires an account! Please login or register!", - "invalid-cid": "Invalid Category ID", - "invalid-tid": "Invalid Topic ID", - "invalid-pid": "Invalid Post ID", - "invalid-uid": "Invalid User ID", - "invalid-username": "Invalid Username", - "invalid-email": "Invalid Email", - "invalid-title": "Invalid title!", - "invalid-user-data": "Invalid User Data", - "invalid-password": "Invalid Password", - "invalid-pagination-value": "Invalid pagination value", - "username-taken": "Username taken", - "email-taken": "Email taken", - "email-not-confirmed": "Your email is not confirmed, please click here to confirm your email.", - "username-too-short": "Username too short", - "user-banned": "User banned", - "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.", - "category-disabled": "Category disabled", - "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.", - "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 characters.", - "too-many-posts": "You can only post every %1 seconds.", - "file-too-big": "Maximum allowed file size is %1 kbs", - "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", - "cant-ban-other-admins": "You can't ban other admins!", - "invalid-image-type": "Invalid image type", - "group-name-too-short": "Group name too short", - "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", - "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!", - "cant-chat-with-yourself": "You can't chat with yourself!", - "not-enough-reputation-to-downvote": "You do not have enough reputation to downvote this post" + "invalid-data": "Ongeldige Data", + "not-logged-in": "Het lijkt dat je niet bent ingelogd.", + "account-locked": "U account is tijdelijk geblockt", + "search-requires-login": "Het zoeken naar berichten of topics vereist een account. Registreer een account of meld u aan.", + "invalid-cid": "Ongeldig Categorie ID", + "invalid-tid": "Ongeldig Onderwerp ID", + "invalid-pid": "Ongeldig Bericht ID", + "invalid-uid": "Ongeldig Gebruikers ID", + "invalid-username": "Ongeldig Gebruikersnaam", + "invalid-email": "Ongeldig Email Adres", + "invalid-title": "Ongeldig Titel!", + "invalid-user-data": "Ongeldig Gebruikersdata", + "invalid-password": "Ongeldig wachtwoord", + "invalid-pagination-value": "Ongeldig pagineringswaarde", + "username-taken": "Gebruikersnaam is al bezet", + "email-taken": "Email adres is al gebruikt", + "email-not-confirmed": "U email adres is niet bevestigd, Klik hier om uw email adres te bevestigen", + "username-too-short": "Gebruikersnaam is te kort", + "user-banned": "Gebruiker verbannen", + "no-category": "Categorie bestaat niet", + "no-topic": "Onderwerp bestaat niet", + "no-post": "Bericht bestaat niet", + "no-group": "Groep bestaat niet", + "no-user": "Gebruiker bestaat niet", + "no-teaser": "kort bericht bestaat niet", + "no-privileges": "U heeft niet voldoende rechten om deze actie te doen", + "no-emailers-configured": "Er zijn geen email plugins geladen, een test email kan dus niet verzonden worden", + "category-disabled": "Categorie uitgeschakeld", + "topic-locked": "Onderwerp gesloten", + "still-uploading": "Heb even geduld totdat de alle bestanden geüpload zijn", + "content-too-short": "Maak de bericht wat langer. Op z'n minst %1 karakters", + "title-too-short": "Maak de titel wat langer. Op z'n minst %1 karakters", + "title-too-long": "Maak de titel wat korter. Het kan niet langer zijn dan %1 karakters", + "too-many-posts": "Je kan elke %1 seconden een bericht aanmaken", + "file-too-big": "De maximale bestandsgrootte is %1 kbs", + "cant-vote-self-post": "Je kan niet op je eigen berichten stemmen", + "already-favourited": "U heeft al dit bericht in uw favorieten staan", + "already-unfavourited": "U heeft al dit bericht uit uw favorieten gehaald", + "cant-ban-other-admins": "U kunt niet de andere admins bannen!", + "invalid-image-type": "Ongeldig foto type", + "group-name-too-short": "De groepsnaam is te kort", + "group-already-exists": "Deze groep bestaat al", + "group-name-change-not-allowed": "Het veranderen van de groepsnaam is niet toegestaan!", + "post-already-deleted": "Bericht is al verwijderd", + "post-already-restored": "Bericht is al hersteld", + "topic-already-deleted": "Onderwerp is al verwijderd", + "topic-already-restored": "Onderwerp is al hersteld", + "topic-thumbnails-are-disabled": "Onderwerp thumbnails zijn uitgeschakeld", + "invalid-file": "Ongeldig bestand", + "uploads-are-disabled": "Uploads zijn uitgeschakeld", + "upload-error": "Upload Fout : %1", + "signature-too-long": "Deze handtekening kan niet groter zijn dan %1 karakters!", + "cant-chat-with-yourself": "Je kan niet met jezelf chatten!", + "not-enough-reputation-to-downvote": "U heeft niet de benodigde reputatie om dit bericht te downvoten" } \ No newline at end of file diff --git a/public/language/nl/footer.json b/public/language/nl/footer.json deleted file mode 100644 index 7ed4195115..0000000000 --- a/public/language/nl/footer.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "stats.online": "Online", - "stats.users": "Gebruikers", - "stats.topics": "Onderwerpen", - "stats.posts": "Berichten", - "success": "succes" -} \ No newline at end of file diff --git a/public/language/nl/global.json b/public/language/nl/global.json index 35e4dc2f0a..94f5da88ba 100644 --- a/public/language/nl/global.json +++ b/public/language/nl/global.json @@ -13,13 +13,13 @@ "please_log_in": "Log a.u.b. In", "logout": "Uitloggen", "posting_restriction_info": "Reageren is momenteel beperkt tot geregistreerde leden, klik hier om in te loggen.", - "welcome_back": "Welkom Terug!", + "welcome_back": "Welkom Terug", "you_have_successfully_logged_in": "Je bent succesvol ingelogd", "save_changes": "Aanpassingen Opslaan", "close": "Sluiten", "pagination": "Paginering", - "pagination.out_of": "%1 out of %2", - "pagination.enter_index": "Enter index", + "pagination.out_of": "%1 van %2", + "pagination.enter_index": "Vul index in", "header.admin": "Admin", "header.recent": "Recent", "header.unread": "Ongelezen", @@ -38,38 +38,38 @@ "alert.success": "Succes", "alert.error": "Fout", "alert.banned": "Verbannen", - "alert.banned.message": "You have just been banned, you will now be logged out.", + "alert.banned.message": "U bent verbannen en zal automatisch worden uitgelogd.", "alert.unfollow": "Je volgt niet langer %1!", "alert.follow": "Je volgt nu %1!", "online": "Online", - "users": "Users", + "users": "Gebruikers", "topics": "Topics", "posts": "Berichten", "views": "Weergaven", - "reputation": "Reputation", - "read_more": "read more", - "posted_ago_by_guest": "posted %1 by Guest", - "posted_ago_by": "posted %1 by %2", - "posted_ago": "posted %1", - "posted_in_ago_by_guest": "posted in %1 %2 by Guest", - "posted_in_ago_by": "posted in %1 %2 by %3", - "posted_in_ago": "posted in %1 %2", - "replied_ago": "replied %1", - "user_posted_ago": "%1 posted %2", - "guest_posted_ago": "Guest posted %1", - "last_edited_by_ago": "last edited by %1 %2", - "norecentposts": "No Recent Posts", - "norecenttopics": "No Recent Topics", + "reputation": "Reputatie", + "read_more": "Lees meer", + "posted_ago_by_guest": "geplaatst %1 door gast", + "posted_ago_by": "geplaatst %1 door %2", + "posted_ago": "geplaatst %1", + "posted_in_ago_by_guest": "geplaatst in % 1 %2 door gast", + "posted_in_ago_by": "geplaatst in %1 %2 door %3", + "posted_in_ago": "geplaatst in %1 %2", + "replied_ago": "gereageerd %1", + "user_posted_ago": "%1 geplaatst %2", + "guest_posted_ago": "Gast plaatste %1", + "last_edited_by_ago": "voor het laatst aangepast door %1 %2", + "norecentposts": "Geen Recente Berichten", + "norecenttopics": "Geen Recente Topics", "recentposts": "Recente Berichten", "recentips": "Recente Ingelogde IPs", "away": "Afwezig", "dnd": "Niet Storen", "invisible": "Onzichtbaar", "offline": "Offline", - "email": "Email", - "language": "Language", - "guest": "Guest", - "guests": "Guests", - "updated.title": "Forum Updated", - "updated.message": "This forum has just been updated to the latest version. Click here to refresh the page." + "email": "Email Adres", + "language": "Taal", + "guest": "Gast", + "guests": "Gasten", + "updated.title": "Forum geüpdatet", + "updated.message": "Dit forum is zojuist geüpdatet naar de laatste versie. Klik hier om de pagina te verversen" } \ No newline at end of file diff --git a/public/language/nl/groups.json b/public/language/nl/groups.json new file mode 100644 index 0000000000..bc1f6d2d7f --- /dev/null +++ b/public/language/nl/groups.json @@ -0,0 +1,7 @@ +{ + "view_group": "Bekijk Groep", + "details.title": "Groep Details", + "details.members": "Ledenlijst", + "details.has_no_posts": "Deze groepleden hebben nog geen berichten geplaatst", + "details.latest_posts": "Nieuwste Berichten" +} \ No newline at end of file diff --git a/public/language/nl/login.json b/public/language/nl/login.json index b23c3742a3..ea7a6e3bcd 100644 --- a/public/language/nl/login.json +++ b/public/language/nl/login.json @@ -1,9 +1,9 @@ { - "username": "Username / Email", + "username": "Gebruikersnaam / Emailadres", "remember_me": "Mij Onthouden?", "forgot_password": "Wachtwoord Vergeten?", "alternative_logins": "Alternatieve Logins", "failed_login_attempt": "Mislukte inlog poging, probeer het later opnieuw.", "login_successful": "Je bent succesvol ingelogd!", - "dont_have_account": "Don't have an account?" + "dont_have_account": "Heeft u nog geen account?" } \ No newline at end of file diff --git a/public/language/nl/modules.json b/public/language/nl/modules.json index f499ce34e5..caaae8472f 100644 --- a/public/language/nl/modules.json +++ b/public/language/nl/modules.json @@ -1,18 +1,18 @@ { "chat.chatting_with": "Chat met ", - "chat.placeholder": "Type chat message here, press enter to send", + "chat.placeholder": "Type chat bericht hier, druk op enter om te verzenden", "chat.send": "Verzenden", "chat.no_active": "Je hebt geen actieve chats.", - "chat.user_typing": "%1 is typing ...", - "chat.user_has_messaged_you": "%1 has messaged you.", - "chat.see_all": "See all Chats", - "chat.no-messages": "Please select a recipient to view chat message history", - "chat.recent-chats": "Recent Chats", - "chat.contacts": "Contacts", - "chat.message-history": "Message History", - "chat.pop-out": "Pop out chat", - "chat.maximize": "Maximize", - "composer.user_said_in": "%1 said in %2:", - "composer.user_said": "%1 said:", - "composer.discard": "Are you sure you wish to discard this post?" + "chat.user_typing": "%1 is aan het typen ...", + "chat.user_has_messaged_you": "%1 heeft een bericht naar u gestuurd", + "chat.see_all": "Bekijk alle gesprekken", + "chat.no-messages": "Selecteer een ontvanger om de geschiedenis van het gesprek te bekijken", + "chat.recent-chats": "Recente Gesprekken", + "chat.contacts": "Contacten", + "chat.message-history": "Berichten Geschiedenis", + "chat.pop-out": "tevoorschijn halen gesprek", + "chat.maximize": "Maximaliseren", + "composer.user_said_in": "%1 zegt in %2:", + "composer.user_said": "%1 zegt:", + "composer.discard": "Weet u het zeker dat u dit bericht niet wilt plaatsen?" } \ No newline at end of file diff --git a/public/language/nl/notifications.json b/public/language/nl/notifications.json index 2e62689bee..6758b7959f 100644 --- a/public/language/nl/notifications.json +++ b/public/language/nl/notifications.json @@ -2,23 +2,22 @@ "title": "Notificaties", "no_notifs": "Je hebt geen nieuwe notificaties", "see_all": "Bekijk alle Notificaties", - "back_to_home": "Back to %1", + "back_to_home": "Terug naar %1", "outgoing_link": "Uitgaande Link", - "outgoing_link_message": "Je verlaat nu", - "continue_to": "Doorgaan naar", - "return_to": "Return to", - "new_notification": "New Notification", - "you_have_unread_notifications": "You have unread notifications.", - "user_made_post": "%1 made a new post", - "new_message_from": "New message from %1", - "upvoted_your_post": "%1 has upvoted your post.", - "favourited_your_post": "%1 has favourited your post.", - "user_flagged_post": "%1 flagged a post.", - "user_posted_to": "%1 has posted a reply to: %2", - "user_mentioned_you_in": "%1 mentioned you in %2", - "email-confirmed": "Email Confirmed", - "email-confirmed-message": "Thank you for validating your email. Your account is now fully activated.", - "email-confirm-error": "An error occurred...", - "email-confirm-error-message": "There was a problem validating your email address. Perhaps the code was invalid or has expired.", - "email-confirm-sent": "Confirmation email sent." + "outgoing_link_message": "Je verlaat nu %1", + "continue_to": "Doorgaan naar %1", + "return_to": "Teruggaan naar %1", + "new_notification": "een nieuwe notificatie", + "you_have_unread_notifications": "U heeft ongelezen notificaties", + "new_message_from": "Nieuw bericht van %1", + "upvoted_your_post": "%1 heeft uw bericht geupvote", + "favourited_your_post": "%1 heeft uw bericht aan zijn favorieten toegevoegd", + "user_flagged_post": "%1 rapporteert uw bericht", + "user_posted_to": "%1 heeft een reactie op het bericht gegeven aan %2", + "user_mentioned_you_in": "%1 heeft u genoemd in %2", + "email-confirmed": "Email adres bevestigd", + "email-confirmed-message": "Bedankt voor het bevestigen van uw email adres. Uw account is nu volledig actief.", + "email-confirm-error": "Een fout vond plaats", + "email-confirm-error-message": "Er was een probleem met het bevestigen van uw email adres. Misschien was de code niet goed of was door de tijd verstreken.", + "email-confirm-sent": "Bevestigings email verstuurd" } \ No newline at end of file diff --git a/public/language/nl/pages.json b/public/language/nl/pages.json index c1e46c21a6..3a82ff7af7 100644 --- a/public/language/nl/pages.json +++ b/public/language/nl/pages.json @@ -5,11 +5,12 @@ "recent": "Recente Onderwerpen", "users": "Geregistreerde Gebruikers", "notifications": "Notificaties", + "tags": "Onderwerpen getagd onder \"%1\"", "user.edit": "\"%1\" aanpassen", "user.following": "Mensen %1 Volgt", "user.followers": "Mensen die %1 Volgen", "user.posts": "Berichten geplaatst door %1", - "user.topics": "Topics created by %1", + "user.topics": "Topics gecreëerd door %1", "user.favourites": "%1's Favoriete Berichten", "user.settings": "Gebruikersinstellingen" } \ No newline at end of file diff --git a/public/language/nl/recent.json b/public/language/nl/recent.json index b5181aa778..2bfc4af744 100644 --- a/public/language/nl/recent.json +++ b/public/language/nl/recent.json @@ -3,6 +3,6 @@ "day": "Dag", "week": "Week", "month": "Maand", - "year": "Year", + "year": "Jaar", "no_recent_topics": "Er zijn geen recente reacties." } \ No newline at end of file diff --git a/public/language/nl/reset_password.json b/public/language/nl/reset_password.json index fc49520840..a529b6b8f8 100644 --- a/public/language/nl/reset_password.json +++ b/public/language/nl/reset_password.json @@ -8,7 +8,7 @@ "new_password": "Nieuw Wachtwoord", "repeat_password": "Bevestig Wachtwoord", "enter_email": "Vul a.u.b. je email address in en we versturen je een email met de stappen hoe je je account reset.", - "enter_email_address": "Enter Email Address", + "enter_email_address": "Vul uw Email Adres in", "password_reset_sent": "Wachtwoord Reset Verzonden", "invalid_email": "Fout Email Adres / Email Adres bestaat niet!" } \ No newline at end of file diff --git a/public/language/nl/search.json b/public/language/nl/search.json new file mode 100644 index 0000000000..b7035add16 --- /dev/null +++ b/public/language/nl/search.json @@ -0,0 +1,3 @@ +{ + "results_matching": "%1 resulta(a)ten was een match \"%2\", (%3 seconds)" +} \ No newline at end of file diff --git a/public/language/nl/success.json b/public/language/nl/success.json index fde8a77044..f8fd81d695 100644 --- a/public/language/nl/success.json +++ b/public/language/nl/success.json @@ -1,6 +1,6 @@ { "success": "Success", - "topic-post": "You have successfully posted.", - "authentication-successful": "Authentication Successful", - "settings-saved": "Settings saved!" + "topic-post": "U heeft succesvol een bericht geplaatst", + "authentication-successful": "Het inloggen is succesvol", + "settings-saved": "Instellingen opgeslagen!" } \ No newline at end of file diff --git a/public/language/nl/tags.json b/public/language/nl/tags.json index f065d4bbfa..b657763651 100644 --- a/public/language/nl/tags.json +++ b/public/language/nl/tags.json @@ -1,6 +1,6 @@ { - "no_tag_topics": "There are no topics with this tag.", + "no_tag_topics": "Er zijn geen onderwerpen met deze tag", "tags": "Tags", - "enter_tags_here": "Enter tags here. Press enter after each tag.", - "no_tags": "There are no tags yet." + "enter_tags_here": "Voer hier uw tags in. Druk op enter na elke tag ingevoerd te hebben", + "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 4e91a3c4ae..800d6348ca 100644 --- a/public/language/nl/topic.json +++ b/public/language/nl/topic.json @@ -1,20 +1,20 @@ { "topic": "Onderwerp", - "topic_id": "Topic ID", - "topic_id_placeholder": "Enter topic ID", + "topic_id": "Onderwerp ID", + "topic_id_placeholder": "Vul Onderwerp ID in", "no_topics_found": "Geen onderwerpen gevonden!", "no_posts_found": "Geen berichten gevonden!", - "post_is_deleted": "This post is deleted!", + "post_is_deleted": "Dit bericht is verwijderd!", "profile": "Profiel", - "posted_by": "Posted by %1", - "posted_by_guest": "Posted by Guest", + "posted_by": "Geplaatst door %1", + "posted_by_guest": "Geplaatst door Gast", "chat": "Chat", "notify_me": "Krijg notificaties van nieuwe reacties op dit onderwerp", "quote": "Citeren", "reply": "Reageren", "edit": "Aanpassen", "delete": "Verwijderen", - "purge": "Purge", + "purge": "weggooien", "restore": "Herstellen", "move": "Verplaatsen", "fork": "Fork", @@ -23,18 +23,18 @@ "share": "Delen", "tools": "Gereedschap", "flag": "Markeren", - "locked": "Locked", + "locked": "gesloten", "bookmark_instructions": "Klik hier om terug te gaan naar je laatste positie of sluiten om te annuleren.", "flag_title": "Dit bericht markeren voor moderatie", - "flag_confirm": "Are you sure you want to flag this post?", - "flag_success": "This post has been flagged for moderation.", + "flag_confirm": "Weet u het zeker dat u dit bericht wilt rapporteren?", + "flag_success": "Dit bericht is gerapporteerd aan de admins", "deleted_message": "Dit onderwerp is verwijderd. Alleen gebruikers met onderwerp management privileges kunnen dit onderwerp zien.", "following_topic.message": "Je zult nu notificaties ontvangen wanneer iemand reageert op dit onderwerp.", "not_following_topic.message": "Je zult niet langer notificaties ontvangen van dit onderwerp.", "login_to_subscribe": "Log a.u.b. in om op dit onderwerp te abonneren.", "markAsUnreadForAll.success": "Onderwerp gemarkeerd als gelezen voor iedereen.", "watch": "Volgen", - "watch.title": "Be notified of new replies in this topic", + "watch.title": "Krijg notificaties van nieuwe reacties op dit onderwerp", "share_this_post": "Deel dit Bericht", "thread_tools.title": "Thread Gereedschap", "thread_tools.markAsUnreadForAll": "Ongelezen Markeren", @@ -43,18 +43,18 @@ "thread_tools.lock": "Onderwerp Sluiten", "thread_tools.unlock": "Onderwerp Openen", "thread_tools.move": "Onderwerp Verplaatsen", - "thread_tools.move_all": "Move All", + "thread_tools.move_all": "Verplaats alles", "thread_tools.fork": "Onderwerp Forken", "thread_tools.delete": "Onderwerp Verwijderen", - "thread_tools.delete_confirm": "Are you sure you want to delete this thread?", + "thread_tools.delete_confirm": "Weet u het zeker dat u dit onderwerp wilt verwijderen?", "thread_tools.restore": "Onderwerp Herstellen", - "thread_tools.restore_confirm": "Are you sure you want to restore this thread?", - "thread_tools.purge": "Purge Topic", - "thread_tools.purge_confirm": "Are you sure you want to purge this thread?", - "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?", - "post_purge_confirm": "Are you sure you want to purge this post?", + "thread_tools.restore_confirm": "Weet u het zeker dat u het onderwerp wilt herstellen?", + "thread_tools.purge": "Gooi onderwerp weg", + "thread_tools.purge_confirm": "Weet u het zeker dat u dit onderwerp wilt weggooien?", + "topic_move_success": "Deze onderwerp is succesvol verplaatst naar %1", + "post_delete_confirm": "Weet u het zeker dat u dit bericht wilt verwijderen?", + "post_restore_confirm": "Weet u het zeker dat u dit bericht wilt herstellen?", + "post_purge_confirm": "Weet u het zeker dat u dit bericht wilt weggooien?", "load_categories": "Categorieën Laden", "disabled_categories_note": "Uitgeschakelde Categorieën zijn grijs", "confirm_move": "Verplaatsen", @@ -64,9 +64,9 @@ "favourites.has_no_favourites": "Je hebt geen favorieten, sla een aantal berichten op als favoriet om ze hier te zien!", "loading_more_posts": "Meer Berichten Laden", "move_topic": "Onderwerp Verplaatsen", - "move_topics": "Move Topics", + "move_topics": "Verplaats onderwerpen", "move_post": "Bericht Verplaatsen", - "post_moved": "Post moved!", + "post_moved": "Bericht verplaatst!", "fork_topic": "Onderwerp Forken", "topic_will_be_moved_to": "Dit onderwerp zal verplaatst worden naar de categorie", "fork_topic_instruction": "Klik op de berichten die je wilt forken", @@ -75,7 +75,7 @@ "composer.title_placeholder": "Vul de titel voor het onderwerp hier in...", "composer.discard": "Annuleren", "composer.submit": "Opslaan", - "composer.replying_to": "Replying to %1", + "composer.replying_to": "Reageren op %1", "composer.new_topic": "Nieuw Onderwerp", "composer.uploading": "uploaden...", "composer.thumb_url_label": "Plak een onderwerp thumbnail URL", @@ -84,11 +84,12 @@ "composer.thumb_file_label": "Of upload een bestand", "composer.thumb_remove": "Velden leegmaken", "composer.drag_and_drop_images": "Sleep en Zet Afbeeldingen Hier", - "more_users_and_guests": "%1 more user(s) and %2 guest(s)", - "more_users": "%1 more user(s)", - "more_guests": "%1 more guest(s)", - "sort_by": "Sort by", - "oldest_to_newest": "Oldest to Newest", - "newest_to_oldest": "Newest to Oldest", - "most_votes": "Most votes" + "more_users_and_guests": "%1 meer gebruiker(s) en %2 gast(en)", + "more_users": "%1 meer gebruiker(s)", + "more_guests": "%1 meer gast(en)", + "users_and_others": "%1 en %2 anderen", + "sort_by": "gesorteerd op", + "oldest_to_newest": "Oud naar Nieuw", + "newest_to_oldest": "Nieuw naar Oud", + "most_votes": "Meeste stemmen" } \ No newline at end of file diff --git a/public/language/nl/unread.json b/public/language/nl/unread.json index efee7eb23c..35938c3a72 100644 --- a/public/language/nl/unread.json +++ b/public/language/nl/unread.json @@ -2,8 +2,8 @@ "title": "Ongelezen", "no_unread_topics": "Er zijn geen ongelezen onderwerpen", "load_more": "Meer Laden", - "mark_as_read": "Mark as Read", - "selected": "Selected", - "all": "All", - "topics_marked_as_read.success": "Topics marked as read!" + "mark_as_read": "Markeer als Gelezen", + "selected": "Geselecteerd", + "all": "Alles", + "topics_marked_as_read.success": "Onderwerp gemarkeerd als gelezen!" } \ No newline at end of file diff --git a/public/language/nl/user.json b/public/language/nl/user.json index f75336c234..37103e7644 100644 --- a/public/language/nl/user.json +++ b/public/language/nl/user.json @@ -3,7 +3,7 @@ "offline": "Offline", "username": "Gebruikersnaam", "email": "Email", - "confirm_email": "Confirm Email", + "confirm_email": "Bevestig uw email adres", "fullname": "Volledige Naam", "website": "Website", "location": "Locatie", @@ -22,44 +22,44 @@ "chat": "Chat", "follow": "Volgen", "unfollow": "Ontvolgen", - "profile_update_success": "Profile has been updated successfully!", + "profile_update_success": "Uw profiel is succesvol geüpdatet", "change_picture": "Afbeelding Aanpassen", "edit": "Aanpassen", "uploaded_picture": "Afbeelding Uploaden", "upload_new_picture": "Nieuwe Afbeelding Uploaden", "current_password": "Huidige Wachtwoord", "change_password": "Wachtwoord Aanpassen", - "change_password_error": "Invalid Password!", - "change_password_error_wrong_current": "Your current password is not correct!", - "change_password_error_length": "Password too short!", - "change_password_error_match": "Passwords must match!", - "change_password_error_privileges": "You do not have the rights to change this password.", - "change_password_success": "Your password is updated!", + "change_password_error": "Ongeldig wachtwoord!", + "change_password_error_wrong_current": "Uw huidige wachtwoord is niet correct!", + "change_password_error_length": "Wachtwoord is te kort!", + "change_password_error_match": "Het wachtwoord wat uw eerder heeft opgegeven moet matchen!", + "change_password_error_privileges": "Uw heeft niet de bevoegdheden om dit wachtwoord te veranderen", + "change_password_success": "Uw wachtwoord is geüpdatet!", "confirm_password": "Bevestig Wachtwoord", "password": "Wachtwoord", - "username_taken_workaround": "The username you requested was already taken, so we have altered it slightly. You are now known as %1", + "username_taken_workaround": "Gebruikersnaam dat u wilde is al in gebruik, dus hebben we het een beetje aangepast naar %1", "upload_picture": "Afbeelding Uploaden", "upload_a_picture": "Upload een afbeelding", "image_spec": "Je mag alleen PNG, JPG, of GIF bestanden uploaden.", "max": "max.", "settings": "Instellingen", "show_email": "Laat Mijn Email Zien", - "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", - "digest_daily": "Daily", - "digest_weekly": "Weekly", - "digest_monthly": "Monthly", + "digest_label": "Abonneer op de overzicht", + "digest_description": "Abonneer op de email updates van dit forum ( nieuwe notificaties en onderwerpen) volgens een tijdsschema", + "digest_off": "Uit", + "digest_daily": "Dagelijks", + "digest_weekly": "Weekelijks", + "digest_monthly": "Maandelijks", "has_no_follower": "Deze gebruiker heeft geen volgers :(", "follows_no_one": "Deze gebruiker volgt niemand :(", "has_no_posts": "Deze gebruiker heeft nog geen berichten geplaatst", - "has_no_topics": "This user didn't post any topics yet.", + "has_no_topics": "Deze gebruiker heeft nog geen berichten geplaatst", "email_hidden": "Email Verborgen", "hidden": "verborgen", "paginate_description": "Blader door onderwerpen en berichten in plaats van oneindig scrollen.", "topics_per_page": "Onderwerpen per Pagina", "posts_per_page": "Berichten per Pagina", - "notification_sounds": "Play a sound when you receive a notification.", - "browsing": "Browsing Settings", - "open_links_in_new_tab": "Open outgoing links in new tab?" + "notification_sounds": "Speel een geluid af wanneer ik een notificatie ontvang.", + "browsing": "Zoek Instellingen", + "open_links_in_new_tab": "Open de uitgaande links in een nieuw tabblad?" } \ No newline at end of file diff --git a/public/language/nl/users.json b/public/language/nl/users.json index 27bffb5a41..2a590dfbd3 100644 --- a/public/language/nl/users.json +++ b/public/language/nl/users.json @@ -5,6 +5,6 @@ "search": "Zoeken", "enter_username": "Vul een gebruikersnaam in om te zoeken", "load_more": "Meer Laden", - "user-not-found": "User not found!", - "users-found-search-took": "%1 user(s) found! Search took %2 ms." + "user-not-found": "Gebruiker niet gevonden!", + "users-found-search-took": "%1 gebruiker(s) gevonden! Zoekactie duurde %2 ms." } \ No newline at end of file diff --git a/public/language/pl/email.json b/public/language/pl/email.json new file mode 100644 index 0000000000..98e591ab02 --- /dev/null +++ b/public/language/pl/email.json @@ -0,0 +1,20 @@ +{ + "password-reset-requested": "Password Reset Requested - %1!", + "welcome-to": "Welcome to %1", + "greeting_no_name": "Hello", + "greeting_with_name": "Hello %1", + "welcome.text1": "Thank you for registering with %1!", + "welcome.text2": "To fully activate your account, we need to verify that you own the email address you registered with.", + "welcome.cta": "Click here to confirm your email address", + "reset.text1": "We received a request to reset your password, possibly because you have forgotten it. If this is not the case, please ignore this email.", + "reset.text2": "To continue with the password reset, please click on the following link:", + "reset.cta": "Click here to reset your password", + "digest.notifications": "You have some unread notifications from %1:", + "digest.latest_topics": "Latest topics from %1", + "digest.cta": "Click here to visit %1", + "digest.unsub.info": "This digest was sent to you due to your subscription settings.", + "digest.unsub.cta": "Click here to alter those settings", + "digest.daily.no_topics": "There have been no active topics in the past day", + "test.text1": "This is a test email to verify that the emailer is set up correctly for your NodeBB.", + "closing": "Thanks!" +} \ No newline at end of file diff --git a/public/language/pl/error.json b/public/language/pl/error.json index 98d6e588ad..e0df9469bb 100644 --- a/public/language/pl/error.json +++ b/public/language/pl/error.json @@ -2,7 +2,7 @@ "invalid-data": "Błędne dane", "not-logged-in": "Nie jesteś zalogowany/a.", "account-locked": "Twoje konto zostało tymczasowo zablokowane.", - "search-requires-login": "Searching requires an account! Please login or register!", + "search-requires-login": "Wyszukiwanie wymaga konta! Zaloguj się lub zarejestruj!", "invalid-cid": "Błędne ID kategorii.", "invalid-tid": "Błędne ID tematu", "invalid-pid": "Błędne ID postu", @@ -15,8 +15,8 @@ "invalid-pagination-value": "Błędna wartość paginacji", "username-taken": "Login zajęty.", "email-taken": "E-mail zajęty.", - "email-not-confirmed": "Your email is not confirmed, please click here to confirm your email.", - "username-too-short": "Username too short", + "email-not-confirmed": "Twój email nie jest potwierdzony, kliknij tutaj aby go potwierdzić.", + "username-too-short": "Nazwa użytkownika za krótka.", "user-banned": "Użytkownik zbanowany", "no-category": "Kategoria nie istnieje.", "no-topic": "Temat nie istnieje", @@ -25,6 +25,7 @@ "no-user": "Użytkownik nie istnieje", "no-teaser": "Podgląd nie istnieje", "no-privileges": "Nie masz wystarczających praw by to wykonać.", + "no-emailers-configured": "No email plugins were loaded, so a test email could not be sent", "category-disabled": "Kategoria wyłączona.", "topic-locked": "Temat zamknięty", "still-uploading": "Poczekaj na pełne załadowanie", @@ -35,7 +36,7 @@ "file-too-big": "Maksymalna wielkość pliku to %1 kilobajtów.", "cant-vote-self-post": "Nie możesz głosować na własny post.", "already-favourited": "Już polubiłeś/aś ten post.", - "already-unfavourited": "You already unfavourited this post", + "already-unfavourited": "Usunąłeś post z ulubionych.", "cant-ban-other-admins": "Nie możesz zbanować innych adminów!", "invalid-image-type": "Błędny typ pliku", "group-name-too-short": "Nazwa grupy za krótka", @@ -51,5 +52,5 @@ "upload-error": "Błąd uploadu: %1", "signature-too-long": "Sygnatura nie może mieć więcej niż %1 znaków.", "cant-chat-with-yourself": "Nie możesz czatować ze sobą", - "not-enough-reputation-to-downvote": "You do not have enough reputation to downvote this post" + "not-enough-reputation-to-downvote": "Masz za mało reputacji by ocenić ten post." } \ No newline at end of file diff --git a/public/language/pl/footer.json b/public/language/pl/footer.json deleted file mode 100644 index fc6c13bbae..0000000000 --- a/public/language/pl/footer.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "stats.online": "online", - "stats.users": "użytkowników", - "stats.topics": "wątków", - "stats.posts": "odpowiedzi", - "success": "powodzenie" -} \ No newline at end of file diff --git a/public/language/pl/global.json b/public/language/pl/global.json index 7a5dd92ff2..84ac6226d5 100644 --- a/public/language/pl/global.json +++ b/public/language/pl/global.json @@ -13,13 +13,13 @@ "please_log_in": "Proszę się zalogować", "logout": "Wyloguj się", "posting_restriction_info": "Pisanie jest dostępne tylko dla zarejestrowanych członków forum, kliknij tutaj aby się zalogować.", - "welcome_back": "Witaj z powrotem!", + "welcome_back": "Witamy ponownie!", "you_have_successfully_logged_in": "Zostałeś pomyślnie zalogowany.", "save_changes": "Zapisz zmiany", "close": "Zamknij", "pagination": "Numerowanie stron", "pagination.out_of": "%1 poza %2", - "pagination.enter_index": "Enter index", + "pagination.enter_index": "Wpisz indeks.", "header.admin": "Administracja", "header.recent": "Ostatnie", "header.unread": "Nieprzeczytane", diff --git a/public/language/pl/groups.json b/public/language/pl/groups.json new file mode 100644 index 0000000000..c00c111e11 --- /dev/null +++ b/public/language/pl/groups.json @@ -0,0 +1,7 @@ +{ + "view_group": "View Group", + "details.title": "Group Details", + "details.members": "Member List", + "details.has_no_posts": "This group's members have not made any posts.", + "details.latest_posts": "Latest Posts" +} \ No newline at end of file diff --git a/public/language/pl/modules.json b/public/language/pl/modules.json index 6416f70bd5..e63c958dc9 100644 --- a/public/language/pl/modules.json +++ b/public/language/pl/modules.json @@ -1,18 +1,18 @@ { "chat.chatting_with": "Rozmawiaj z ", - "chat.placeholder": "Type chat message here, press enter to send", + "chat.placeholder": "Wpisz wiadomość czatu tutaj, naciśnij enter by wysłać.", "chat.send": "Wyślij", "chat.no_active": "Nie prowadzisz obecnie żadnych rozmów.", "chat.user_typing": "%1 pisze...", "chat.user_has_messaged_you": "%1 napisał do Ciebie", "chat.see_all": "Pokaż wszystkie czaty", - "chat.no-messages": "Please select a recipient to view chat message history", - "chat.recent-chats": "Recent Chats", - "chat.contacts": "Contacts", - "chat.message-history": "Message History", - "chat.pop-out": "Pop out chat", - "chat.maximize": "Maximize", - "composer.user_said_in": "%1 said in %2:", - "composer.user_said": "%1 said:", - "composer.discard": "Are you sure you wish to discard this post?" + "chat.no-messages": "Wybierz odbiorce by zobaczyć historię czatu.", + "chat.recent-chats": "Ostatnie czaty.", + "chat.contacts": "Kontakty", + "chat.message-history": "Historia wiadomości", + "chat.pop-out": "Pokaż czat", + "chat.maximize": "Maksymalizuj", + "composer.user_said_in": "%1 powiedział w %2:", + "composer.user_said": "%1 powiedział:", + "composer.discard": "Na pewno chcesz ignorować ten post?" } \ No newline at end of file diff --git a/public/language/pl/notifications.json b/public/language/pl/notifications.json index 7c1626f647..493b232147 100644 --- a/public/language/pl/notifications.json +++ b/public/language/pl/notifications.json @@ -4,12 +4,11 @@ "see_all": "Zobacz wszystkie powiadomienia", "back_to_home": "Back to %1", "outgoing_link": "Łącze wychodzące", - "outgoing_link_message": "Opuszczasz", - "continue_to": "Kontynuuj do", - "return_to": "Return to", + "outgoing_link_message": "You are now leaving %1.", + "continue_to": "Continue to %1", + "return_to": "Return to %1", "new_notification": "Nowe powiadomienie", "you_have_unread_notifications": "Masz nieprzeczytane powiadomienia.", - "user_made_post": "%1 napisał nowy post", "new_message_from": "Nowa wiadomość od %1", "upvoted_your_post": "%1
zagłosował na Twój post", "favourited_your_post": "%1 polubił/a Twój post.", diff --git a/public/language/pl/pages.json b/public/language/pl/pages.json index 155c4619a1..fb43e50c1a 100644 --- a/public/language/pl/pages.json +++ b/public/language/pl/pages.json @@ -5,6 +5,7 @@ "recent": "Ostatnie wątki", "users": "Zarejestrowani użytkownicy", "notifications": "Powiadomienia", + "tags": "Topics tagged under \"%1\"", "user.edit": "Edytowanie \"%1\"", "user.following": "Obserwowani przez %1", "user.followers": "Obserwujący %1", diff --git a/public/language/pl/search.json b/public/language/pl/search.json new file mode 100644 index 0000000000..d0ffc64f36 --- /dev/null +++ b/public/language/pl/search.json @@ -0,0 +1,3 @@ +{ + "results_matching": "%1 result(s) matching \"%2\", (%3 seconds)" +} \ No newline at end of file diff --git a/public/language/pl/topic.json b/public/language/pl/topic.json index 30290807ea..bcb2ad0721 100644 --- a/public/language/pl/topic.json +++ b/public/language/pl/topic.json @@ -87,6 +87,7 @@ "more_users_and_guests": "%1 więcej użytkownik(ów) i %2 gośc(i)", "more_users": "%1 więcej użytkownik(ów)", "more_guests": "%1 więcej gośc(i)", + "users_and_others": "%1 and %2 others", "sort_by": "Sort by", "oldest_to_newest": "Oldest to Newest", "newest_to_oldest": "Newest to Oldest", diff --git a/public/language/pt_BR/email.json b/public/language/pt_BR/email.json new file mode 100644 index 0000000000..92f058e6ff --- /dev/null +++ b/public/language/pt_BR/email.json @@ -0,0 +1,20 @@ +{ + "password-reset-requested": "Recuperação de Senha Solicitada - %1!", + "welcome-to": "Bem vindo a %1", + "greeting_no_name": "Olá", + "greeting_with_name": "Olà %1", + "welcome.text1": "Obrigado por se registrar com %1!", + "welcome.text2": "To fully activate your account, we need to verify that you own the email address you registered with.", + "welcome.cta": "Clique aqui para confirmar seu endereço de email", + "reset.text1": "We received a request to reset your password, possibly because you have forgotten it. If this is not the case, please ignore this email.", + "reset.text2": "To continue with the password reset, please click on the following link:", + "reset.cta": "Clique aqui para recuperar sua senha", + "digest.notifications": "You have some unread notifications from %1:", + "digest.latest_topics": "Últimos tópicos de %1", + "digest.cta": "Clique aqui para visitar %1", + "digest.unsub.info": "This digest was sent to you due to your subscription settings.", + "digest.unsub.cta": "Clique aqui para alterar suas configurações", + "digest.daily.no_topics": "There have been no active topics in the past day", + "test.text1": "This is a test email to verify that the emailer is set up correctly for your NodeBB.", + "closing": "Obrigado!" +} \ No newline at end of file diff --git a/public/language/pt_BR/error.json b/public/language/pt_BR/error.json index 9fc7c5553d..9dca9b0ae2 100644 --- a/public/language/pt_BR/error.json +++ b/public/language/pt_BR/error.json @@ -25,6 +25,7 @@ "no-user": "Usuário não existe", "no-teaser": "Chamada não existe", "no-privileges": "Você não possui permissões para esta ação.", + "no-emailers-configured": "Nenhum plugin de email foi carregado, por isso o email de teste não pode ser enviado", "category-disabled": "Categoria desativada", "topic-locked": "Tópico trancado", "still-uploading": "Aguarde a conclusão dos uploads.", diff --git a/public/language/pt_BR/footer.json b/public/language/pt_BR/footer.json deleted file mode 100644 index cb007090d7..0000000000 --- a/public/language/pt_BR/footer.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "stats.online": "Online", - "stats.users": "Usuários", - "stats.topics": "Tópicos", - "stats.posts": "Posts", - "success": "Sucesso" -} \ No newline at end of file diff --git a/public/language/pt_BR/groups.json b/public/language/pt_BR/groups.json new file mode 100644 index 0000000000..c00c111e11 --- /dev/null +++ b/public/language/pt_BR/groups.json @@ -0,0 +1,7 @@ +{ + "view_group": "View Group", + "details.title": "Group Details", + "details.members": "Member List", + "details.has_no_posts": "This group's members have not made any posts.", + "details.latest_posts": "Latest Posts" +} \ No newline at end of file diff --git a/public/language/pt_BR/notifications.json b/public/language/pt_BR/notifications.json index f731583ad1..08a4240c19 100644 --- a/public/language/pt_BR/notifications.json +++ b/public/language/pt_BR/notifications.json @@ -4,12 +4,11 @@ "see_all": "Visualizar todas as notificações", "back_to_home": "Voltar para %1", "outgoing_link": "Link Externo", - "outgoing_link_message": "Você está saindo para um link externo", - "continue_to": "Continuar para", - "return_to": "Voltar para", + "outgoing_link_message": "Você deixou de seguir %1.", + "continue_to": "Continuar para %1", + "return_to": "Voltar para %1", "new_notification": "Nova notificação", "you_have_unread_notifications": "Você possui notificações não lidas.", - "user_made_post": "%1 fez um novo post", "new_message_from": "Nova mensagem de %1", "upvoted_your_post": "%1 votou no seu post.", "favourited_your_post": "%1 favoritou seu post.", diff --git a/public/language/pt_BR/pages.json b/public/language/pt_BR/pages.json index c8bda7e9cc..b700cccb53 100644 --- a/public/language/pt_BR/pages.json +++ b/public/language/pt_BR/pages.json @@ -5,6 +5,7 @@ "recent": "Tópicos Recentes", "users": "Usuários Registrados", "notifications": "Notificações", + "tags": "Topics tagged under \"%1\"", "user.edit": "Editando \"%1\"", "user.following": "Seguidos por %1", "user.followers": "Seguidores de %1", diff --git a/public/language/pt_BR/search.json b/public/language/pt_BR/search.json new file mode 100644 index 0000000000..d0ffc64f36 --- /dev/null +++ b/public/language/pt_BR/search.json @@ -0,0 +1,3 @@ +{ + "results_matching": "%1 result(s) matching \"%2\", (%3 seconds)" +} \ No newline at end of file diff --git a/public/language/pt_BR/topic.json b/public/language/pt_BR/topic.json index fabe31247a..eaa541ed33 100644 --- a/public/language/pt_BR/topic.json +++ b/public/language/pt_BR/topic.json @@ -87,6 +87,7 @@ "more_users_and_guests": "%1 mais usuário(s) e %2 visitante(s)", "more_users": "%1 mais usuário(s)", "more_guests": "%1 mais visitante(s)", + "users_and_others": "%1 and %2 others", "sort_by": "Ordenar por", "oldest_to_newest": "Mais Antigo para Recente", "newest_to_oldest": "Recente para mais Antigo", diff --git a/public/language/ro/email.json b/public/language/ro/email.json new file mode 100644 index 0000000000..98e591ab02 --- /dev/null +++ b/public/language/ro/email.json @@ -0,0 +1,20 @@ +{ + "password-reset-requested": "Password Reset Requested - %1!", + "welcome-to": "Welcome to %1", + "greeting_no_name": "Hello", + "greeting_with_name": "Hello %1", + "welcome.text1": "Thank you for registering with %1!", + "welcome.text2": "To fully activate your account, we need to verify that you own the email address you registered with.", + "welcome.cta": "Click here to confirm your email address", + "reset.text1": "We received a request to reset your password, possibly because you have forgotten it. If this is not the case, please ignore this email.", + "reset.text2": "To continue with the password reset, please click on the following link:", + "reset.cta": "Click here to reset your password", + "digest.notifications": "You have some unread notifications from %1:", + "digest.latest_topics": "Latest topics from %1", + "digest.cta": "Click here to visit %1", + "digest.unsub.info": "This digest was sent to you due to your subscription settings.", + "digest.unsub.cta": "Click here to alter those settings", + "digest.daily.no_topics": "There have been no active topics in the past day", + "test.text1": "This is a test email to verify that the emailer is set up correctly for your NodeBB.", + "closing": "Thanks!" +} \ No newline at end of file diff --git a/public/language/ro/error.json b/public/language/ro/error.json index 39edf3e9e2..5b54c36e72 100644 --- a/public/language/ro/error.json +++ b/public/language/ro/error.json @@ -25,6 +25,7 @@ "no-user": "Utilizatorul nu există", "no-teaser": "Rezumatul nu există", "no-privileges": "Nu ai destule privilegii pentru această acțiune.", + "no-emailers-configured": "No email plugins were loaded, so a test email could not be sent", "category-disabled": "Categorie dezactivată", "topic-locked": "Subiect Închis", "still-uploading": "Te rugăm să aștepți până se termină uploadul.", diff --git a/public/language/ro/global.json b/public/language/ro/global.json index 4f9c772e9d..028c2d5eb4 100644 --- a/public/language/ro/global.json +++ b/public/language/ro/global.json @@ -13,7 +13,7 @@ "please_log_in": "Autentifică-te", "logout": "Ieşire", "posting_restriction_info": "Pentru a posta trebuie să fi înregistrat. Apasă aici pentru a te atentifica.", - "welcome_back": "Bine ai revenit", + "welcome_back": "Welcome Back", "you_have_successfully_logged_in": "Te-ai conectat cu succes", "save_changes": "Salvează Modificări", "close": "Închide", diff --git a/public/language/ro/groups.json b/public/language/ro/groups.json new file mode 100644 index 0000000000..c00c111e11 --- /dev/null +++ b/public/language/ro/groups.json @@ -0,0 +1,7 @@ +{ + "view_group": "View Group", + "details.title": "Group Details", + "details.members": "Member List", + "details.has_no_posts": "This group's members have not made any posts.", + "details.latest_posts": "Latest Posts" +} \ No newline at end of file diff --git a/public/language/ro/language.json b/public/language/ro/language.json index cd46631dcc..671c4dc6d6 100644 --- a/public/language/ro/language.json +++ b/public/language/ro/language.json @@ -1,5 +1,5 @@ { - "name": "Română", + "name": "Română (România)", "code": "ro", "dir": "ltr" } \ No newline at end of file diff --git a/public/language/ro/notifications.json b/public/language/ro/notifications.json index 4b73e50109..682cb05ee2 100644 --- a/public/language/ro/notifications.json +++ b/public/language/ro/notifications.json @@ -4,12 +4,11 @@ "see_all": "Vezi toate notificările", "back_to_home": "Înapoi la %1", "outgoing_link": "Link Extern", - "outgoing_link_message": "Acum părăsești pagina", - "continue_to": "Continuă la", - "return_to": "Return to", + "outgoing_link_message": "You are now leaving %1.", + "continue_to": "Continue to %1", + "return_to": "Return to %1", "new_notification": "Notificare Nouă", "you_have_unread_notifications": "Ai notificări necitite.", - "user_made_post": "%1 a postat un nou mesaj", "new_message_from": "Un mesaj nou de la %1", "upvoted_your_post": "%1 a votat pozitiv mesajul tău.", "favourited_your_post": "%1 a adăugat mesajul tău la favorite.", diff --git a/public/language/ro/pages.json b/public/language/ro/pages.json index 7211444934..3b69d67327 100644 --- a/public/language/ro/pages.json +++ b/public/language/ro/pages.json @@ -5,6 +5,7 @@ "recent": "Subiecte Noi", "users": "Utilizatori înregistrați", "notifications": "Notificări", + "tags": "Topics tagged under \"%1\"", "user.edit": "Editează \"%1\"", "user.following": "Utilizatori urmăriți de %1", "user.followers": "Utilizatori care îl urmăresc pe %1", diff --git a/public/language/ro/search.json b/public/language/ro/search.json new file mode 100644 index 0000000000..d0ffc64f36 --- /dev/null +++ b/public/language/ro/search.json @@ -0,0 +1,3 @@ +{ + "results_matching": "%1 result(s) matching \"%2\", (%3 seconds)" +} \ No newline at end of file diff --git a/public/language/ro/tags.json b/public/language/ro/tags.json index f065d4bbfa..cfd27f9ec3 100644 --- a/public/language/ro/tags.json +++ b/public/language/ro/tags.json @@ -1,6 +1,6 @@ { - "no_tag_topics": "There are no topics with this tag.", - "tags": "Tags", - "enter_tags_here": "Enter tags here. Press enter after each tag.", - "no_tags": "There are no tags yet." + "no_tag_topics": "Nu există nici un subiect cu acest tag.", + "tags": "Taguri", + "enter_tags_here": "Introdu tagurile aici. Apasă enter după fiecare tag.", + "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 95d8bfd4a7..eccb976288 100644 --- a/public/language/ro/topic.json +++ b/public/language/ro/topic.json @@ -87,6 +87,7 @@ "more_users_and_guests": "%1 utlizator(i) și %2 vizitator(i)", "more_users": "%1 utilizator(i)", "more_guests": "%1 vizitator(i)", + "users_and_others": "%1 and %2 others", "sort_by": "Sortează de la", "oldest_to_newest": "Vechi la Noi", "newest_to_oldest": "Noi la Vechi", diff --git a/public/language/ru/email.json b/public/language/ru/email.json new file mode 100644 index 0000000000..d0fc8fabed --- /dev/null +++ b/public/language/ru/email.json @@ -0,0 +1,20 @@ +{ + "password-reset-requested": "Пароль сброшен - %1!", + "welcome-to": "Добро пожаловать на %1", + "greeting_no_name": "Здравствуйте!", + "greeting_with_name": "Здравствуйте, %1!", + "welcome.text1": "Благодарим за регистрацию %1! ", + "welcome.text2": "Для активации вашей учетной записи мы должны убедиться, что вы указали верный email адрес.", + "welcome.cta": "Перейдите по ссылке для подтверждения вашего email", + "reset.text1": "Мы получили запрос на изменение вашего пароля. Если не подавали запрос на изменение пароля, пожалуйста, проигнорируйте это сообщение.", + "reset.text2": "Для продолжения процедуры изменения пароля, пожалуйста, перейдите по ссылке:", + "reset.cta": "Кликните здесь для изменения пароля", + "digest.notifications": "У вас есть непрочитанные уведомления от %1:", + "digest.latest_topics": "Последние темы %1", + "digest.cta": "Кликните здесь для просмотра %1", + "digest.unsub.info": "Вам была выслана сводка новостей в соответствии с вашими настройками.", + "digest.unsub.cta": "Кликните здесь для изменения ваших настроек.", + "digest.daily.no_topics": "За минувший день новых тем нет.", + "test.text1": "Это тестовое сообщение для проверки почтового сервиса NodeBB.", + "closing": "Спасибо!" +} \ No newline at end of file diff --git a/public/language/ru/error.json b/public/language/ru/error.json index 1e7698e3be..63a6a93355 100644 --- a/public/language/ru/error.json +++ b/public/language/ru/error.json @@ -15,7 +15,7 @@ "invalid-pagination-value": "Неверное значение пагинации", "username-taken": "Имя пользователя занято", "email-taken": "Email занят", - "email-not-confirmed": "Your email is not confirmed, please click here to confirm your email.", + "email-not-confirmed": "Ваш email не подтвержден, нажмите для подтверждения.", "username-too-short": "Слишком короткое имя пользователя", "user-banned": "Пользователь заблокирован", "no-category": "Несуществующая категория", @@ -25,9 +25,10 @@ "no-user": "Несуществующий пользователь", "no-teaser": "Несуществующее превью", "no-privileges": "У вас недостаточно прав, чтобы совершить данное действие.", + "no-emailers-configured": "Не подключен ни один плагин для отправки почты, поэтому тестовый email не может быть отправлен", "category-disabled": "Категория отключена", - "topic-locked": "Тема закрыт", - "still-uploading": "Пожалуйста, подождите завершения загрузки", + "topic-locked": "Тема закрыта", + "still-uploading": "Пожалуйста, подождите завершения загрузки.", "content-too-short": "Пост должен содержать минимум %1 символов.", "title-too-short": "Заголовок должен содержать минимум %1 символов.", "title-too-long": "Заголовок не может быть длиннее %1 символов.", diff --git a/public/language/ru/footer.json b/public/language/ru/footer.json deleted file mode 100644 index 6e426d99e4..0000000000 --- a/public/language/ru/footer.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "stats.online": "В сети", - "stats.users": "Пользователей", - "stats.topics": "Тем", - "stats.posts": "Сообщений", - "success": "успешно" -} \ No newline at end of file diff --git a/public/language/ru/global.json b/public/language/ru/global.json index e83c49da26..e75eaadf5a 100644 --- a/public/language/ru/global.json +++ b/public/language/ru/global.json @@ -14,7 +14,7 @@ "logout": "Выйти", "posting_restriction_info": "Сообщения могут оставлять только зарегистрированные пользователи, нажмите сюда, чтобы войти", "welcome_back": "С возвращением", - "you_have_successfully_logged_in": "Вы вышли из аккаунта", + "you_have_successfully_logged_in": "Вы успешно вошли на форум", "save_changes": "Сохранить изменения", "close": "Закрыть", "pagination": "Пагинация", diff --git a/public/language/ru/groups.json b/public/language/ru/groups.json new file mode 100644 index 0000000000..a82a2bfc4b --- /dev/null +++ b/public/language/ru/groups.json @@ -0,0 +1,7 @@ +{ + "view_group": "Просмотр группы", + "details.title": "Информация о группе", + "details.members": "Список пользователей", + "details.has_no_posts": "Пользователями этой группы не публиковали никаких записей", + "details.latest_posts": "Последние записи" +} \ No newline at end of file diff --git a/public/language/ru/modules.json b/public/language/ru/modules.json index 92d22ffa20..5cd840d3d0 100644 --- a/public/language/ru/modules.json +++ b/public/language/ru/modules.json @@ -1,18 +1,18 @@ { "chat.chatting_with": "Чат с ", - "chat.placeholder": "Type chat message here, press enter to send", + "chat.placeholder": "Введите сообщение, нажмите ENTER для отправки", "chat.send": "Отправить", "chat.no_active": "У вас нет активных чатов.", "chat.user_typing": "%1 печатает ...", "chat.user_has_messaged_you": "%1 отправил вам сообщение.", "chat.see_all": "Просмотр всех диалогов", - "chat.no-messages": "Please select a recipient to view chat message history", - "chat.recent-chats": "Recent Chats", - "chat.contacts": "Contacts", - "chat.message-history": "Message History", - "chat.pop-out": "Pop out chat", - "chat.maximize": "Maximize", - "composer.user_said_in": "%1 said in %2:", - "composer.user_said": "%1 said:", + "chat.no-messages": "Пожалуйста, выберите собеседника для просмотра истории сообщений", + "chat.recent-chats": "Последние переписки", + "chat.contacts": "Контакты", + "chat.message-history": "История сообщений", + "chat.pop-out": "Покинуть диалог", + "chat.maximize": "Развернуть", + "composer.user_said_in": "%1 сказал %2:", + "composer.user_said": "%1 сказал:", "composer.discard": "Вы уверены, что хотите отказаться от этого поста?" } \ No newline at end of file diff --git a/public/language/ru/notifications.json b/public/language/ru/notifications.json index f549ae9239..1648ebc8f8 100644 --- a/public/language/ru/notifications.json +++ b/public/language/ru/notifications.json @@ -4,12 +4,11 @@ "see_all": "Просмотреть все уведомления", "back_to_home": "Назад к %1", "outgoing_link": "Внешняя ссылка", - "outgoing_link_message": "Вы покидаете", - "continue_to": "Перейти на", - "return_to": "Вернуться к", + "outgoing_link_message": "Вы покидаете %1.", + "continue_to": "Перейти на %1", + "return_to": "Вернуться к %1", "new_notification": "Новое Уведомление", "you_have_unread_notifications": "У вас есть непрочитанные уведомления", - "user_made_post": "%1 создал новую запись", "new_message_from": "Новое сообщение от %1", "upvoted_your_post": "%1 проголосовал за Ваш пост", "favourited_your_post": "%1 добавил Ваш пост в избранное", diff --git a/public/language/ru/pages.json b/public/language/ru/pages.json index 9fadc51c13..adf2827471 100644 --- a/public/language/ru/pages.json +++ b/public/language/ru/pages.json @@ -5,6 +5,7 @@ "recent": "Последние темы", "users": "Зарегистрированные пользователи", "notifications": "Уведомления", + "tags": "Topics tagged under \"%1\"", "user.edit": "Редактирование \"%1\"", "user.following": "%1 читает", "user.followers": "Читают %1", diff --git a/public/language/ru/recent.json b/public/language/ru/recent.json index 6e0ecb69ad..7dabef695e 100644 --- a/public/language/ru/recent.json +++ b/public/language/ru/recent.json @@ -3,6 +3,6 @@ "day": "День", "week": "Неделя", "month": "Месяц", - "year": "Year", + "year": "Год", "no_recent_topics": "Нет свежих тем." } \ No newline at end of file diff --git a/public/language/ru/search.json b/public/language/ru/search.json new file mode 100644 index 0000000000..5f0a069bef --- /dev/null +++ b/public/language/ru/search.json @@ -0,0 +1,3 @@ +{ + "results_matching": "%1 результатов по фразе \"%2\", (%3 секунды) " +} \ No newline at end of file diff --git a/public/language/ru/tags.json b/public/language/ru/tags.json index f065d4bbfa..91aa21d2d6 100644 --- a/public/language/ru/tags.json +++ b/public/language/ru/tags.json @@ -1,6 +1,6 @@ { - "no_tag_topics": "There are no topics with this tag.", - "tags": "Tags", - "enter_tags_here": "Enter tags here. Press enter after each tag.", - "no_tags": "There are no tags yet." + "no_tag_topics": "Нет топиков с таким тэгом.", + "tags": "Тэги", + "enter_tags_here": "Укажите тэги здесь. Нажимайте Enter после каждого тэга.", + "no_tags": "Здесь еще нет тэгов." } \ No newline at end of file diff --git a/public/language/ru/topic.json b/public/language/ru/topic.json index ba7e7662c2..9757210077 100644 --- a/public/language/ru/topic.json +++ b/public/language/ru/topic.json @@ -54,7 +54,7 @@ "topic_move_success": "Эта тема успешно перемещена в %1", "post_delete_confirm": "Вы уверены, что хотите удалить этот пост?", "post_restore_confirm": "Вы уверены, что хотите восстановить этот пост?", - "post_purge_confirm": "Are you sure you want to purge this post?", + "post_purge_confirm": "Вы уверены, что хотите очистить эту запись?", "load_categories": "Загружаем Категории", "disabled_categories_note": "Отключенные категории затемненны", "confirm_move": "Перенести", @@ -87,6 +87,7 @@ "more_users_and_guests": "еще %1 пользователя(ей) и %2 гостя(ей)", "more_users": "еще %1 пользователя(ей)", "more_guests": "еще %1 гостя(ей)", + "users_and_others": "%1 and %2 others", "sort_by": "Сортировать", "oldest_to_newest": "От Старых к Новым", "newest_to_oldest": "От Новых к Старым", diff --git a/public/language/ru/user.json b/public/language/ru/user.json index c3493e26ce..6f3e7c92e2 100644 --- a/public/language/ru/user.json +++ b/public/language/ru/user.json @@ -3,7 +3,7 @@ "offline": "Не в сети", "username": "Имя пользователя", "email": "Email", - "confirm_email": "Confirm Email", + "confirm_email": "Подтвердить Email", "fullname": "Полное имя", "website": "Сайт", "location": "Откуда", diff --git a/public/language/ru/users.json b/public/language/ru/users.json index b4121f10c8..46ebc41b69 100644 --- a/public/language/ru/users.json +++ b/public/language/ru/users.json @@ -6,5 +6,5 @@ "enter_username": "Введите имя пользователя для поиска", "load_more": "Загрузить еще", "user-not-found": "Пользователь не найден!", - "users-found-search-took": "Нашел %1 пользователя(ей)! Поиск занял %2 ms." + "users-found-search-took": "Нашел %1 пользователя(ей)! Поиск занял %2 мс." } \ No newline at end of file diff --git a/public/language/sc/email.json b/public/language/sc/email.json new file mode 100644 index 0000000000..98e591ab02 --- /dev/null +++ b/public/language/sc/email.json @@ -0,0 +1,20 @@ +{ + "password-reset-requested": "Password Reset Requested - %1!", + "welcome-to": "Welcome to %1", + "greeting_no_name": "Hello", + "greeting_with_name": "Hello %1", + "welcome.text1": "Thank you for registering with %1!", + "welcome.text2": "To fully activate your account, we need to verify that you own the email address you registered with.", + "welcome.cta": "Click here to confirm your email address", + "reset.text1": "We received a request to reset your password, possibly because you have forgotten it. If this is not the case, please ignore this email.", + "reset.text2": "To continue with the password reset, please click on the following link:", + "reset.cta": "Click here to reset your password", + "digest.notifications": "You have some unread notifications from %1:", + "digest.latest_topics": "Latest topics from %1", + "digest.cta": "Click here to visit %1", + "digest.unsub.info": "This digest was sent to you due to your subscription settings.", + "digest.unsub.cta": "Click here to alter those settings", + "digest.daily.no_topics": "There have been no active topics in the past day", + "test.text1": "This is a test email to verify that the emailer is set up correctly for your NodeBB.", + "closing": "Thanks!" +} \ No newline at end of file diff --git a/public/language/sc/error.json b/public/language/sc/error.json index 4d55d9a4f3..77c582b23f 100644 --- a/public/language/sc/error.json +++ b/public/language/sc/error.json @@ -25,6 +25,7 @@ "no-user": "User doesn't exist", "no-teaser": "Teaser doesn't exist", "no-privileges": "You don't 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", "topic-locked": "Topic Locked", "still-uploading": "Please wait for uploads to complete.", diff --git a/public/language/sc/footer.json b/public/language/sc/footer.json deleted file mode 100644 index 741c7ae19f..0000000000 --- a/public/language/sc/footer.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "stats.online": "In lìnia", - "stats.users": "Impitadores", - "stats.topics": "Arresonadas", - "stats.posts": "Arresonos", - "success": "andat bene" -} \ No newline at end of file diff --git a/public/language/sc/global.json b/public/language/sc/global.json index 222a12c2d0..2d995fca01 100644 --- a/public/language/sc/global.json +++ b/public/language/sc/global.json @@ -13,7 +13,7 @@ "please_log_in": "Pro praghere Intra", "logout": "Essi·nche", "posting_restriction_info": "Sa publicatzione immoe est limitada isceti a is impitadores registrados, carca inoghe pro intrare.", - "welcome_back": "Bene Torradu", + "welcome_back": "Welcome Back", "you_have_successfully_logged_in": "Ses intradu", "save_changes": "Alloga Acontzos", "close": "Serra", diff --git a/public/language/sc/groups.json b/public/language/sc/groups.json new file mode 100644 index 0000000000..c00c111e11 --- /dev/null +++ b/public/language/sc/groups.json @@ -0,0 +1,7 @@ +{ + "view_group": "View Group", + "details.title": "Group Details", + "details.members": "Member List", + "details.has_no_posts": "This group's members have not made any posts.", + "details.latest_posts": "Latest Posts" +} \ No newline at end of file diff --git a/public/language/sc/notifications.json b/public/language/sc/notifications.json index f9417af5ab..c5c59c39e1 100644 --- a/public/language/sc/notifications.json +++ b/public/language/sc/notifications.json @@ -4,12 +4,11 @@ "see_all": "Càstia totus is Notìficas", "back_to_home": "Back to %1", "outgoing_link": "Acàpiu a Foras", - "outgoing_link_message": "Immoe ses essende", - "continue_to": "Sighi a", - "return_to": "Return to", + "outgoing_link_message": "You are now leaving %1.", + "continue_to": "Continue to %1", + "return_to": "Return to %1", "new_notification": "New Notification", "you_have_unread_notifications": "You have unread notifications.", - "user_made_post": "%1 made a new post", "new_message_from": "New message from %1", "upvoted_your_post": "%1 has upvoted your post.", "favourited_your_post": "%1 has favourited your post.", diff --git a/public/language/sc/pages.json b/public/language/sc/pages.json index d64f46318e..2f975b787d 100644 --- a/public/language/sc/pages.json +++ b/public/language/sc/pages.json @@ -5,6 +5,7 @@ "recent": "Ùrtimas Arresonadas", "users": "Impitadores Registrados", "notifications": "Notìficas", + "tags": "Topics tagged under \"%1\"", "user.edit": "Acontzende \"%1\"", "user.following": "Gente chi %1 Sighit", "user.followers": "Gente chi Sighit %1", diff --git a/public/language/sc/search.json b/public/language/sc/search.json new file mode 100644 index 0000000000..d0ffc64f36 --- /dev/null +++ b/public/language/sc/search.json @@ -0,0 +1,3 @@ +{ + "results_matching": "%1 result(s) matching \"%2\", (%3 seconds)" +} \ No newline at end of file diff --git a/public/language/sc/topic.json b/public/language/sc/topic.json index 59c79f3b96..afc9d94ddd 100644 --- a/public/language/sc/topic.json +++ b/public/language/sc/topic.json @@ -87,6 +87,7 @@ "more_users_and_guests": "%1 more user(s) and %2 guest(s)", "more_users": "%1 more user(s)", "more_guests": "%1 more guest(s)", + "users_and_others": "%1 and %2 others", "sort_by": "Sort by", "oldest_to_newest": "Oldest to Newest", "newest_to_oldest": "Newest to Oldest", diff --git a/public/language/sk/email.json b/public/language/sk/email.json new file mode 100644 index 0000000000..98e591ab02 --- /dev/null +++ b/public/language/sk/email.json @@ -0,0 +1,20 @@ +{ + "password-reset-requested": "Password Reset Requested - %1!", + "welcome-to": "Welcome to %1", + "greeting_no_name": "Hello", + "greeting_with_name": "Hello %1", + "welcome.text1": "Thank you for registering with %1!", + "welcome.text2": "To fully activate your account, we need to verify that you own the email address you registered with.", + "welcome.cta": "Click here to confirm your email address", + "reset.text1": "We received a request to reset your password, possibly because you have forgotten it. If this is not the case, please ignore this email.", + "reset.text2": "To continue with the password reset, please click on the following link:", + "reset.cta": "Click here to reset your password", + "digest.notifications": "You have some unread notifications from %1:", + "digest.latest_topics": "Latest topics from %1", + "digest.cta": "Click here to visit %1", + "digest.unsub.info": "This digest was sent to you due to your subscription settings.", + "digest.unsub.cta": "Click here to alter those settings", + "digest.daily.no_topics": "There have been no active topics in the past day", + "test.text1": "This is a test email to verify that the emailer is set up correctly for your NodeBB.", + "closing": "Thanks!" +} \ No newline at end of file diff --git a/public/language/sk/error.json b/public/language/sk/error.json index 32d95d97fd..8481519d05 100644 --- a/public/language/sk/error.json +++ b/public/language/sk/error.json @@ -25,6 +25,7 @@ "no-user": "Užívateľ neexistuje ", "no-teaser": "Teaser neexistuje", "no-privileges": "Nemáte dostatočné oprávnenia pre túto akciu ", + "no-emailers-configured": "No email plugins were loaded, so a test email could not be sent", "category-disabled": "Kategória je znefunkčená.", "topic-locked": "Uzamknutá téma", "still-uploading": "Prosím čakajte na dokončenie nahrávania", diff --git a/public/language/sk/footer.json b/public/language/sk/footer.json deleted file mode 100644 index 519a14412e..0000000000 --- a/public/language/sk/footer.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "stats.online": "Online", - "stats.users": "Užívateľov", - "stats.topics": "Tém", - "stats.posts": "Príspevkov", - "success": "úspech" -} \ No newline at end of file diff --git a/public/language/sk/global.json b/public/language/sk/global.json index 8da1918f92..62534c8017 100644 --- a/public/language/sk/global.json +++ b/public/language/sk/global.json @@ -13,7 +13,7 @@ "please_log_in": "Prosím, prihláste sa", "logout": "Odhlásiť sa", "posting_restriction_info": "Prispievanie je obmedzené len pre registrovaných, kliknite pre prihlásenie sa ", - "welcome_back": "Vitaj naspäť", + "welcome_back": "Welcome Back", "you_have_successfully_logged_in": "Úspešne si sa prihlásil", "save_changes": "Uložiť zmeny", "close": "Zrušiť", diff --git a/public/language/sk/groups.json b/public/language/sk/groups.json new file mode 100644 index 0000000000..c00c111e11 --- /dev/null +++ b/public/language/sk/groups.json @@ -0,0 +1,7 @@ +{ + "view_group": "View Group", + "details.title": "Group Details", + "details.members": "Member List", + "details.has_no_posts": "This group's members have not made any posts.", + "details.latest_posts": "Latest Posts" +} \ No newline at end of file diff --git a/public/language/sk/notifications.json b/public/language/sk/notifications.json index 26a94c255c..7a5b20cbb7 100644 --- a/public/language/sk/notifications.json +++ b/public/language/sk/notifications.json @@ -4,12 +4,11 @@ "see_all": "Pozri všetky notifikácie", "back_to_home": "Back to %1", "outgoing_link": "Odkaz mimo fórum", - "outgoing_link_message": "Teraz opúšťate fórum", - "continue_to": "Prejsť na", - "return_to": "Return to", + "outgoing_link_message": "You are now leaving %1.", + "continue_to": "Continue to %1", + "return_to": "Return to %1", "new_notification": "Nová notifikácia", "you_have_unread_notifications": "Máte neprečítané notifikácie", - "user_made_post": "%1vytvoril nový príspevok", "new_message_from": "Nova spáva od %1", "upvoted_your_post": "%1 zahlasoval za Váš príspevok.", "favourited_your_post": "%1 pridal do obľubených Váš príspevok.", diff --git a/public/language/sk/pages.json b/public/language/sk/pages.json index 5901e7bec1..764229a282 100644 --- a/public/language/sk/pages.json +++ b/public/language/sk/pages.json @@ -5,6 +5,7 @@ "recent": "Najnovšie príspevky", "users": "Prihlásení uživatelia", "notifications": "Notifikácie", + "tags": "Topics tagged under \"%1\"", "user.edit": "Uprav \"%1\"", "user.following": "Užívatelia %1 následujú", "user.followers": "Užívatelia následujúci %1", diff --git a/public/language/sk/search.json b/public/language/sk/search.json new file mode 100644 index 0000000000..d0ffc64f36 --- /dev/null +++ b/public/language/sk/search.json @@ -0,0 +1,3 @@ +{ + "results_matching": "%1 result(s) matching \"%2\", (%3 seconds)" +} \ No newline at end of file diff --git a/public/language/sk/topic.json b/public/language/sk/topic.json index 382ac83e71..697c0880ca 100644 --- a/public/language/sk/topic.json +++ b/public/language/sk/topic.json @@ -87,6 +87,7 @@ "more_users_and_guests": "%1 užívateľ(ov) a %2 hostí.", "more_users": "%1 a viac host(í)", "more_guests": "%1 a viac host(í)", + "users_and_others": "%1 and %2 others", "sort_by": "Sort by", "oldest_to_newest": "Oldest to Newest", "newest_to_oldest": "Newest to Oldest", diff --git a/public/language/sv/email.json b/public/language/sv/email.json new file mode 100644 index 0000000000..2a6f643862 --- /dev/null +++ b/public/language/sv/email.json @@ -0,0 +1,20 @@ +{ + "password-reset-requested": "Återställning av lösenord efterfrågat - %1!", + "welcome-to": "Välkommen till %1", + "greeting_no_name": "Hej", + "greeting_with_name": "Hej %1", + "welcome.text1": "Tack för att du registerar dig på %1!", + "welcome.text2": "För att slutföra aktivering av ditt konto, behöver vi verifiera att du har tillgång till epostadressen du registererade.", + "welcome.cta": "Klicka här för att bekräfta din epostadress ", + "reset.text1": "Vi fick en förfrågan att återställa ditt lösenord, kanske för att du glömt det. Om det inte är så, så kan du ignorera det här epostmeddelandet. ", + "reset.text2": "För att fortsätta med återställning av lösenordet så kan du klicka på följande länk:", + "reset.cta": "Klicka här för att återställa ditt lösenord", + "digest.notifications": "Du har några olästa notiser från %1:", + "digest.latest_topics": "Senaste ämnen från %1", + "digest.cta": "Klicka här för att besöka %1", + "digest.unsub.info": "Det här meddelandet fick du på grund av dina inställningar för prenumeration. ", + "digest.unsub.cta": "Klicka här för att ändra dom inställningarna", + "digest.daily.no_topics": "Det verkar inte varit några aktiva ämnen dom senaste dagarna ", + "test.text1": "\nDet här är ett textmeddelande som verifierar att eposten är korrekt installerat för din NodeBB. ", + "closing": "Tack!" +} \ No newline at end of file diff --git a/public/language/sv/error.json b/public/language/sv/error.json index 955c342e80..8de10daaae 100644 --- a/public/language/sv/error.json +++ b/public/language/sv/error.json @@ -2,7 +2,7 @@ "invalid-data": "Ogiltig data", "not-logged-in": "Du verkar inte vara inloggad.", "account-locked": "Ditt konto har tillfälligt blivit låst", - "search-requires-login": "Searching requires an account! Please login or register!", + "search-requires-login": "Sökningar kräver att du har ett konto. Logga in eller registrera dig. ", "invalid-cid": "Ogiltigt id för kategori", "invalid-tid": "Ogiltigt id för ämne", "invalid-pid": "Ogiltigt id för inlägg", @@ -15,8 +15,8 @@ "invalid-pagination-value": "Ogiltigt sidnummer", "username-taken": "Användarnamn upptaget", "email-taken": "Epostadress upptagen", - "email-not-confirmed": "Your email is not confirmed, please click here to confirm your email.", - "username-too-short": "Username too short", + "email-not-confirmed": "Din epostadress är ännu inte bekräftad. Klicka här för att bekräfta din epostadress.", + "username-too-short": "Användarnamnet är för kort", "user-banned": "Användare bannad", "no-category": "Kategori hittades inte", "no-topic": "Ämne hittades inte", @@ -25,6 +25,7 @@ "no-user": "Användare hittades inte", "no-teaser": "Förtexten hittades inte", "no-privileges": "Du har inte rättigheter nog för den här åtgärden.", + "no-emailers-configured": "Inga tillägg för epostadress har laddats, så något textmeddelande kunde inte skickas", "category-disabled": "Kategorin inaktiverad", "topic-locked": "Ämnet låst", "still-uploading": "Vänta medan uppladdningen slutförs.", @@ -35,7 +36,7 @@ "file-too-big": "Maximalt antal filstorlek är %1 kilobits.", "cant-vote-self-post": "Du kan in rösta på ditt eget inlägg.", "already-favourited": "Du har redan favoriserat det här inlägget", - "already-unfavourited": "You already unfavourited this post", + "already-unfavourited": "Du har redan avfavoriserat det här meddelandet ", "cant-ban-other-admins": "Du kan inte banna andra administratörer.", "invalid-image-type": "Ogiltigt bildformat", "group-name-too-short": "Gruppnamnet är för kort", @@ -51,5 +52,5 @@ "upload-error": "Fel vid uppladdning: %1", "signature-too-long": "Signaturer kan inte vara längre än %1 tecken.", "cant-chat-with-yourself": "Du kan inte chatta med dig själv.", - "not-enough-reputation-to-downvote": "You do not have enough reputation to downvote this post" + "not-enough-reputation-to-downvote": "Du har inte tillräckligt förtroende för att rösta ner det här meddelandet" } \ No newline at end of file diff --git a/public/language/sv/footer.json b/public/language/sv/footer.json deleted file mode 100644 index c449308bbb..0000000000 --- a/public/language/sv/footer.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "stats.online": "Online", - "stats.users": "Användare", - "stats.topics": "Ämnen", - "stats.posts": "Inlägg", - "success": "success" -} \ No newline at end of file diff --git a/public/language/sv/global.json b/public/language/sv/global.json index d6ecebaf97..20a457bc73 100644 --- a/public/language/sv/global.json +++ b/public/language/sv/global.json @@ -13,13 +13,13 @@ "please_log_in": "Var god logga in", "logout": "Logga ut", "posting_restriction_info": "Man måste vara inloggad för att kunna skapa inlägg, klicka här för att logga in.", - "welcome_back": "Välkommen tillbaka", + "welcome_back": "Välkommen tillbaka ", "you_have_successfully_logged_in": "Inloggningen lyckades", "save_changes": "Spara ändringar", "close": "Stäng", "pagination": "Siduppdelning", "pagination.out_of": "%1 av %2", - "pagination.enter_index": "Enter index", + "pagination.enter_index": "Skriv in index ", "header.admin": "Admin", "header.recent": "Senaste", "header.unread": "Olästa", diff --git a/public/language/sv/groups.json b/public/language/sv/groups.json new file mode 100644 index 0000000000..e82144f093 --- /dev/null +++ b/public/language/sv/groups.json @@ -0,0 +1,7 @@ +{ + "view_group": "Visa grupp ", + "details.title": "Detaljer för gruppen ", + "details.members": "Medlemmar", + "details.has_no_posts": "Den här gruppens medlemmar har inte skrivit några inlägg.", + "details.latest_posts": "Senaste inlägg" +} \ No newline at end of file diff --git a/public/language/sv/modules.json b/public/language/sv/modules.json index cefc297e4d..a996e30676 100644 --- a/public/language/sv/modules.json +++ b/public/language/sv/modules.json @@ -1,18 +1,18 @@ { "chat.chatting_with": "Chatta med ", - "chat.placeholder": "Type chat message here, press enter to send", + "chat.placeholder": "Skriv chatmeddelande här och tryck sen enter för att skicka ", "chat.send": "Skicka", "chat.no_active": "Du har inte några aktiva chattar.", "chat.user_typing": "%1 skriver ...", "chat.user_has_messaged_you": "%1 har skickat ett medelande till dig.", "chat.see_all": "Visa alla chattar", - "chat.no-messages": "Please select a recipient to view chat message history", - "chat.recent-chats": "Recent Chats", - "chat.contacts": "Contacts", - "chat.message-history": "Message History", - "chat.pop-out": "Pop out chat", - "chat.maximize": "Maximize", - "composer.user_said_in": "%1 said in %2:", - "composer.user_said": "%1 said:", - "composer.discard": "Are you sure you wish to discard this post?" + "chat.no-messages": "Välj mottagare för att visa historik för chatmeddelande", + "chat.recent-chats": "Senaste chattarna", + "chat.contacts": "Kontakter ", + "chat.message-history": "Historik för meddelande", + "chat.pop-out": "Utskjutande chatt", + "chat.maximize": "Maximera", + "composer.user_said_in": "%1 sa i %2:", + "composer.user_said": "%1 sa:", + "composer.discard": "Är du säker på att du vill förkasta det här inlägget?" } \ No newline at end of file diff --git a/public/language/sv/notifications.json b/public/language/sv/notifications.json index ec78033e27..9f262b008a 100644 --- a/public/language/sv/notifications.json +++ b/public/language/sv/notifications.json @@ -2,20 +2,19 @@ "title": "Notiser", "no_notifs": "Du har inga nya notiser", "see_all": "Visa alla notiser", - "back_to_home": "Back to %1", + "back_to_home": "Tillbaka till %1", "outgoing_link": "Utgående länk", - "outgoing_link_message": "Du lämnar nu", - "continue_to": "Fortsätt till", - "return_to": "Return to", + "outgoing_link_message": "Du lämnar nu %1. ", + "continue_to": "Fortsätt till %1", + "return_to": "Återgå till %1", "new_notification": "Ny notis", "you_have_unread_notifications": "Du har olästa notiser.", - "user_made_post": "%1 skrev ett nytt inlägg", "new_message_from": "Nytt medelande från %1", "upvoted_your_post": "%1 har röstat på ditt inlägg.", "favourited_your_post": "%1 har favoriserat ditt inlägg.", "user_flagged_post": "%1 flaggade ett inlägg.", "user_posted_to": "%1 har skrivit ett svar på: %2", - "user_mentioned_you_in": "%1 mentioned you in %2", + "user_mentioned_you_in": "%1 nämnde dig i %2", "email-confirmed": "Epost bekräftad", "email-confirmed-message": "Tack för att du bekräftat din epostadress. Ditt konto är nu fullt ut aktiverat.", "email-confirm-error": "Ett fel uppstod...", diff --git a/public/language/sv/pages.json b/public/language/sv/pages.json index 09116fe68c..c4c0efc382 100644 --- a/public/language/sv/pages.json +++ b/public/language/sv/pages.json @@ -5,6 +5,7 @@ "recent": "Senaste ämnena", "users": "Registrerade användare", "notifications": "Notiser", + "tags": "Topics tagged under \"%1\"", "user.edit": "Ändrar \"%1\"", "user.following": "Personer %1 Följer", "user.followers": "Personer som följer %1", diff --git a/public/language/sv/recent.json b/public/language/sv/recent.json index 5edc33a830..4478c47921 100644 --- a/public/language/sv/recent.json +++ b/public/language/sv/recent.json @@ -3,6 +3,6 @@ "day": "Dag", "week": "Vecka", "month": "Månad", - "year": "Year", + "year": "År", "no_recent_topics": "Det finns inga olästa ämnen." } \ No newline at end of file diff --git a/public/language/sv/search.json b/public/language/sv/search.json new file mode 100644 index 0000000000..996a7d81fd --- /dev/null +++ b/public/language/sv/search.json @@ -0,0 +1,3 @@ +{ + "results_matching": "%1 resultat matchar \"%2\", (%3 sekunder)" +} \ No newline at end of file diff --git a/public/language/sv/tags.json b/public/language/sv/tags.json index f065d4bbfa..e7f66208a8 100644 --- a/public/language/sv/tags.json +++ b/public/language/sv/tags.json @@ -1,6 +1,6 @@ { - "no_tag_topics": "There are no topics with this tag.", - "tags": "Tags", - "enter_tags_here": "Enter tags here. Press enter after each tag.", - "no_tags": "There are no tags yet." + "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.", + "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 50c9cfed07..8615e49c77 100644 --- a/public/language/sv/topic.json +++ b/public/language/sv/topic.json @@ -14,7 +14,7 @@ "reply": "Svara", "edit": "Ändra", "delete": "Ta bort", - "purge": "Purge", + "purge": "Rensa", "restore": "Återställ", "move": "Flytta", "fork": "Grena", @@ -23,7 +23,7 @@ "share": "Dela", "tools": "Verktyg", "flag": "Rapportera", - "locked": "Locked", + "locked": "Låst", "bookmark_instructions": "Klicka här för att återgå till den senaste positionen eller stäng för att kasta.", "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?", @@ -49,12 +49,12 @@ "thread_tools.delete_confirm": "Är du säker på att du vill ta bort den här tråden?", "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.purge": "Purge Topic", - "thread_tools.purge_confirm": "Are you sure you want to purge this thread?", + "thread_tools.purge": "Rensa ämne", + "thread_tools.purge_confirm": "Är du säker att du vill rensa ut den här tråden?", "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?", - "post_purge_confirm": "Are you sure you want to purge this post?", + "post_purge_confirm": "Är du säker att du vill rensa ut det här inlägget?", "load_categories": "Laddar kategorier", "disabled_categories_note": "Inaktiverade kategorier är utgråade", "confirm_move": "Flytta", @@ -87,8 +87,9 @@ "more_users_and_guests": "%1 fler användare() och %2 gäst(er)", "more_users": "%1 fler användare()", "more_guests": "1% fler gäst(er)", - "sort_by": "Sort by", - "oldest_to_newest": "Oldest to Newest", - "newest_to_oldest": "Newest to Oldest", - "most_votes": "Most votes" + "users_and_others": "%1 and %2 others", + "sort_by": "Sortera på", + "oldest_to_newest": "Äldst till nyaste", + "newest_to_oldest": "Nyaste till äldst", + "most_votes": "Mest röster" } \ No newline at end of file diff --git a/public/language/sv/user.json b/public/language/sv/user.json index 3eb6a04a81..a6b3a753fb 100644 --- a/public/language/sv/user.json +++ b/public/language/sv/user.json @@ -3,7 +3,7 @@ "offline": "Offline", "username": "Användarnamn", "email": "Epost", - "confirm_email": "Confirm Email", + "confirm_email": "Bekräfta epostadress ", "fullname": "Hela namnet", "website": "Webbsida", "location": "Plats", diff --git a/public/language/sv/users.json b/public/language/sv/users.json index 2085529ffe..a1d5a36410 100644 --- a/public/language/sv/users.json +++ b/public/language/sv/users.json @@ -5,6 +5,6 @@ "search": "Sök", "enter_username": "Ange ett användarnamn för att söka", "load_more": "Ladda fler", - "user-not-found": "User not found!", - "users-found-search-took": "%1 user(s) found! Search took %2 ms." + "user-not-found": "Användare hittades inte.", + "users-found-search-took": "%1 användare hittades! Sökningen tog %2 ms." } \ No newline at end of file diff --git a/public/language/th/email.json b/public/language/th/email.json new file mode 100644 index 0000000000..98e591ab02 --- /dev/null +++ b/public/language/th/email.json @@ -0,0 +1,20 @@ +{ + "password-reset-requested": "Password Reset Requested - %1!", + "welcome-to": "Welcome to %1", + "greeting_no_name": "Hello", + "greeting_with_name": "Hello %1", + "welcome.text1": "Thank you for registering with %1!", + "welcome.text2": "To fully activate your account, we need to verify that you own the email address you registered with.", + "welcome.cta": "Click here to confirm your email address", + "reset.text1": "We received a request to reset your password, possibly because you have forgotten it. If this is not the case, please ignore this email.", + "reset.text2": "To continue with the password reset, please click on the following link:", + "reset.cta": "Click here to reset your password", + "digest.notifications": "You have some unread notifications from %1:", + "digest.latest_topics": "Latest topics from %1", + "digest.cta": "Click here to visit %1", + "digest.unsub.info": "This digest was sent to you due to your subscription settings.", + "digest.unsub.cta": "Click here to alter those settings", + "digest.daily.no_topics": "There have been no active topics in the past day", + "test.text1": "This is a test email to verify that the emailer is set up correctly for your NodeBB.", + "closing": "Thanks!" +} \ No newline at end of file diff --git a/public/language/th/error.json b/public/language/th/error.json index 4d55d9a4f3..77c582b23f 100644 --- a/public/language/th/error.json +++ b/public/language/th/error.json @@ -25,6 +25,7 @@ "no-user": "User doesn't exist", "no-teaser": "Teaser doesn't exist", "no-privileges": "You don't 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", "topic-locked": "Topic Locked", "still-uploading": "Please wait for uploads to complete.", diff --git a/public/language/th/footer.json b/public/language/th/footer.json deleted file mode 100644 index 22fd13fcc5..0000000000 --- a/public/language/th/footer.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "stats.online": "ออนไลน์", - "stats.users": "ผู้ใช้", - "stats.topics": "กระทู้", - "stats.posts": "กระทู้", - "success": "สำเร็จ" -} \ No newline at end of file diff --git a/public/language/th/global.json b/public/language/th/global.json index 6c42d36f89..9bc26fe372 100644 --- a/public/language/th/global.json +++ b/public/language/th/global.json @@ -13,7 +13,7 @@ "please_log_in": "กรุณาเข้าสู่ระบบ", "logout": "ออกจากระบบ", "posting_restriction_info": "คุณต้องต้องเป็นสมาชิกเพื่อทำการโพสต์ คลิกที่นี่เพื่อเข้าสู่ระบบ", - "welcome_back": "ยินดีต้อนรับ", + "welcome_back": "Welcome Back", "you_have_successfully_logged_in": "คุณได้เข้าสู่ระบบแล้ว", "save_changes": "บันทึกการเปลี่ยนแปลง", "close": "ปิด", diff --git a/public/language/th/groups.json b/public/language/th/groups.json new file mode 100644 index 0000000000..c00c111e11 --- /dev/null +++ b/public/language/th/groups.json @@ -0,0 +1,7 @@ +{ + "view_group": "View Group", + "details.title": "Group Details", + "details.members": "Member List", + "details.has_no_posts": "This group's members have not made any posts.", + "details.latest_posts": "Latest Posts" +} \ No newline at end of file diff --git a/public/language/th/notifications.json b/public/language/th/notifications.json index 0013362fac..4c33f78276 100644 --- a/public/language/th/notifications.json +++ b/public/language/th/notifications.json @@ -4,12 +4,11 @@ "see_all": "ดูข้อแจ้งเตือนทั้งหมด", "back_to_home": "Back to %1", "outgoing_link": "ลิงค์ออก", - "outgoing_link_message": "ตอนนี้คุณจะออกจาก", - "continue_to": "ดำเนินการต่อไป", - "return_to": "Return to", + "outgoing_link_message": "You are now leaving %1.", + "continue_to": "Continue to %1", + "return_to": "Return to %1", "new_notification": "New Notification", "you_have_unread_notifications": "You have unread notifications.", - "user_made_post": "%1 made a new post", "new_message_from": "New message from %1", "upvoted_your_post": "%1 has upvoted your post.", "favourited_your_post": "%1 has favourited your post.", diff --git a/public/language/th/pages.json b/public/language/th/pages.json index 8b031c9dec..b6cb79bcc3 100644 --- a/public/language/th/pages.json +++ b/public/language/th/pages.json @@ -5,6 +5,7 @@ "recent": "กระทู้ล่าสุด", "users": "ผู้ใช้ที่ลงทะเบียน", "notifications": "แจ้งเตือน", + "tags": "Topics tagged under \"%1\"", "user.edit": "แก้ไข \"%1\"", "user.following": "ผู้ใช้ที่ %1 ติดตาม", "user.followers": "ผู้ใช้ที่ติดตาม %1", diff --git a/public/language/th/search.json b/public/language/th/search.json new file mode 100644 index 0000000000..d0ffc64f36 --- /dev/null +++ b/public/language/th/search.json @@ -0,0 +1,3 @@ +{ + "results_matching": "%1 result(s) matching \"%2\", (%3 seconds)" +} \ No newline at end of file diff --git a/public/language/th/topic.json b/public/language/th/topic.json index e3892710d6..c72667cb33 100644 --- a/public/language/th/topic.json +++ b/public/language/th/topic.json @@ -87,6 +87,7 @@ "more_users_and_guests": "%1 more user(s) and %2 guest(s)", "more_users": "%1 more user(s)", "more_guests": "%1 more guest(s)", + "users_and_others": "%1 and %2 others", "sort_by": "Sort by", "oldest_to_newest": "Oldest to Newest", "newest_to_oldest": "Newest to Oldest", diff --git a/public/language/tr/email.json b/public/language/tr/email.json new file mode 100644 index 0000000000..96fd81431e --- /dev/null +++ b/public/language/tr/email.json @@ -0,0 +1,20 @@ +{ + "password-reset-requested": "Parola Değiştirme İsteği Gönderildi", + "welcome-to": "Hoşgeldiniz", + "greeting_no_name": "Merhaba", + "greeting_with_name": "Merhaba %1", + "welcome.text1": "Kaydolduğunuz için teşekkürler!", + "welcome.text2": "Hesabınızı aktif hale getirmek için, kaydolduğunuz e-posta adresinin size ait olduğunu onaylamamız gerekiyor.", + "welcome.cta": "E-posta adresinizi onaylamak için buraya tıklayın", + "reset.text1": "Şifrenizi değiştirmek istediğinize dair bir ileti aldık. Eğer böyle bir istek göndermediyseniz, lütfen bu e-postayı görmezden gelin.", + "reset.text2": "Parola değiştirme işlemine devam etmek için aşağıdaki bağlantıya tıklayın:", + "reset.cta": "Parolanızı değiştirmek için buraya tıklayın", + "digest.notifications": "Okunmamış bazı bildirimleriniz var", + "digest.latest_topics": "En güncel konular", + "digest.cta": "Ziyaret etmek için buraya tıklayın", + "digest.unsub.info": "Bu e-posta seçtiğiniz ayarlar nedeniyle gönderildi.", + "digest.unsub.cta": "Bu ayarları değiştirmek için buraya tıklayın", + "digest.daily.no_topics": "Geçtiğimiz gün içinde aktif bir konu yok.", + "test.text1": "Bu ileti NodeBB e-posta ayarlarınızın doğru çalışıp çalışmadığını kontrol etmek için gönderildi.", + "closing": "Teşekkürler!" +} \ No newline at end of file diff --git a/public/language/tr/error.json b/public/language/tr/error.json index e18f572e78..cec4db9683 100644 --- a/public/language/tr/error.json +++ b/public/language/tr/error.json @@ -25,6 +25,7 @@ "no-user": "Kullanıcı Yok", "no-teaser": "İleti Yok", "no-privileges": "Bu işlemi yapmak için yeterli yetkiniz yok.", + "no-emailers-configured": "E-posta eklentisi kurulu değil bu yüzden test e-postası gönderilemedi", "category-disabled": "Kategori aktif değil", "topic-locked": "Başlık Kilitli", "still-uploading": "Lütfen yüklemelerin bitmesini bekleyin.", diff --git a/public/language/tr/footer.json b/public/language/tr/footer.json deleted file mode 100644 index 7298842150..0000000000 --- a/public/language/tr/footer.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "stats.online": "Çevrimiçi", - "stats.users": "Kullanıcılar", - "stats.topics": "Başlıklar", - "stats.posts": "Mesajlar", - "success": "başarı" -} \ No newline at end of file diff --git a/public/language/tr/groups.json b/public/language/tr/groups.json new file mode 100644 index 0000000000..804046d192 --- /dev/null +++ b/public/language/tr/groups.json @@ -0,0 +1,7 @@ +{ + "view_group": "Grubu Gör", + "details.title": "Grup Detayları", + "details.members": "Üye Listesi", + "details.has_no_posts": "Bu grubun üyeleri henüz bir ileti göndermedi.", + "details.latest_posts": "En son iletiler" +} \ No newline at end of file diff --git a/public/language/tr/notifications.json b/public/language/tr/notifications.json index 4a35e30b88..3ff542c19a 100644 --- a/public/language/tr/notifications.json +++ b/public/language/tr/notifications.json @@ -4,12 +4,11 @@ "see_all": "Bütün bildirimleri gör", "back_to_home": "Geri dön %1", "outgoing_link": "Harici Link", - "outgoing_link_message": "Şimdi ayrılıyorsunuz", - "continue_to": "Devam", - "return_to": "Geri dön", + "outgoing_link_message": "Bu forumdan ayrılıyorsunuz", + "continue_to": "Devam et", + "return_to": "Geri dön.", "new_notification": "Yeni bildirim", "you_have_unread_notifications": "Okunmamış bildirimleriniz var.", - "user_made_post": "%1 yeni bir ileti gönderdi", "new_message_from": "%1 size bir mesaj gönderdi", "upvoted_your_post": "%1 iletinizi beğendi.", "favourited_your_post": "%1 iletinizi favorilerine ekledi.", diff --git a/public/language/tr/pages.json b/public/language/tr/pages.json index 44d85b553b..582a524b6b 100644 --- a/public/language/tr/pages.json +++ b/public/language/tr/pages.json @@ -5,6 +5,7 @@ "recent": "Güncel Konular", "users": "Kayıtlı Kullanıcılar", "notifications": "Bildirimler", + "tags": "Topics tagged under \"%1\"", "user.edit": "\"% 1\" düzenleniyor", "user.following": "İnsanlar %1 Takip Ediyor", "user.followers": "%1 takip edenler", diff --git a/public/language/tr/search.json b/public/language/tr/search.json new file mode 100644 index 0000000000..b384c82c72 --- /dev/null +++ b/public/language/tr/search.json @@ -0,0 +1,3 @@ +{ + "results_matching": "%1 tane “%2“ bulundu (%3 saniye)" +} \ No newline at end of file diff --git a/public/language/tr/tags.json b/public/language/tr/tags.json index f065d4bbfa..a60ce28e1a 100644 --- a/public/language/tr/tags.json +++ b/public/language/tr/tags.json @@ -1,6 +1,6 @@ { - "no_tag_topics": "There are no topics with this tag.", - "tags": "Tags", - "enter_tags_here": "Enter tags here. Press enter after each tag.", - "no_tags": "There are no tags yet." + "no_tag_topics": "Bu etiketli başlık yok.", + "tags": "Etiketler", + "enter_tags_here": "Etiketleri buraya girin.", + "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 24e2efc337..0716b3e6f1 100644 --- a/public/language/tr/topic.json +++ b/public/language/tr/topic.json @@ -87,6 +87,7 @@ "more_users_and_guests": "%1 tane daha kullanıcı ve %2 ziyaretçi", "more_users": "%1 tane daha kullanıcı", "more_guests": "%1 tane daha ziyaretçi", + "users_and_others": "%1 and %2 others", "sort_by": "Sırala", "oldest_to_newest": "En eskiden en yeniye", "newest_to_oldest": "En yeniden en eskiye", diff --git a/public/language/vi/email.json b/public/language/vi/email.json new file mode 100644 index 0000000000..98e591ab02 --- /dev/null +++ b/public/language/vi/email.json @@ -0,0 +1,20 @@ +{ + "password-reset-requested": "Password Reset Requested - %1!", + "welcome-to": "Welcome to %1", + "greeting_no_name": "Hello", + "greeting_with_name": "Hello %1", + "welcome.text1": "Thank you for registering with %1!", + "welcome.text2": "To fully activate your account, we need to verify that you own the email address you registered with.", + "welcome.cta": "Click here to confirm your email address", + "reset.text1": "We received a request to reset your password, possibly because you have forgotten it. If this is not the case, please ignore this email.", + "reset.text2": "To continue with the password reset, please click on the following link:", + "reset.cta": "Click here to reset your password", + "digest.notifications": "You have some unread notifications from %1:", + "digest.latest_topics": "Latest topics from %1", + "digest.cta": "Click here to visit %1", + "digest.unsub.info": "This digest was sent to you due to your subscription settings.", + "digest.unsub.cta": "Click here to alter those settings", + "digest.daily.no_topics": "There have been no active topics in the past day", + "test.text1": "This is a test email to verify that the emailer is set up correctly for your NodeBB.", + "closing": "Thanks!" +} \ No newline at end of file diff --git a/public/language/vi/error.json b/public/language/vi/error.json index 54d23f605e..6b93d3cd98 100644 --- a/public/language/vi/error.json +++ b/public/language/vi/error.json @@ -25,6 +25,7 @@ "no-user": "Tài khoản không tồn tại", "no-teaser": "Teaser không tồn tại", "no-privileges": "Bạn không đủ quyền cho hành động này", + "no-emailers-configured": "No email plugins were loaded, so a test email could not be sent", "category-disabled": "Danh mục bị disabled", "topic-locked": "Chủ đề bị khóa", "still-uploading": "Vui lòng chờ upload", diff --git a/public/language/vi/global.json b/public/language/vi/global.json index 72fbde516a..5554138a31 100644 --- a/public/language/vi/global.json +++ b/public/language/vi/global.json @@ -13,7 +13,7 @@ "please_log_in": "Xin hãy đăng nhập", "logout": "Đăng xuất", "posting_restriction_info": "Hiện giờ chỉ có các thành viên mới được quyền gửi bài viết, hãy nhấn vào đây để đăng nhập", - "welcome_back": "Chào mừng bạn quay lại", + "welcome_back": "Welcome Back", "you_have_successfully_logged_in": "Bạn đã đăng nhập thành công", "save_changes": "Lưu thay đổi", "close": "Đóng lại", diff --git a/public/language/vi/groups.json b/public/language/vi/groups.json new file mode 100644 index 0000000000..c00c111e11 --- /dev/null +++ b/public/language/vi/groups.json @@ -0,0 +1,7 @@ +{ + "view_group": "View Group", + "details.title": "Group Details", + "details.members": "Member List", + "details.has_no_posts": "This group's members have not made any posts.", + "details.latest_posts": "Latest Posts" +} \ No newline at end of file diff --git a/public/language/vi/notifications.json b/public/language/vi/notifications.json index b569bb43ee..431305ad23 100644 --- a/public/language/vi/notifications.json +++ b/public/language/vi/notifications.json @@ -4,12 +4,11 @@ "see_all": "Xem tất cả thông báo", "back_to_home": "Back to %1", "outgoing_link": "Liên kết ngoài", - "outgoing_link_message": "Bạn giờ đang thoát", - "continue_to": "Tiếp tục đến", - "return_to": "Return to", + "outgoing_link_message": "You are now leaving %1.", + "continue_to": "Continue to %1", + "return_to": "Return to %1", "new_notification": "Thông báo mới", "you_have_unread_notifications": "Bạn có thông báo chưa đọc", - "user_made_post": "%1 đã viết bài mới", "new_message_from": "Tin nhắn mới từ %1", "upvoted_your_post": "%1 đã hủy vote cho bài viết của bạn", "favourited_your_post": "%1 thích bài viết của bạn", diff --git a/public/language/vi/pages.json b/public/language/vi/pages.json index 36cb72eb47..a5ee695599 100644 --- a/public/language/vi/pages.json +++ b/public/language/vi/pages.json @@ -5,6 +5,7 @@ "recent": "Chủ đề gần đây", "users": "Số người dùng đã đăng ký", "notifications": "Thông báo", + "tags": "Topics tagged under \"%1\"", "user.edit": "Chỉnh sửa \"%1\"", "user.following": "Người mà %1 theo dõi", "user.followers": "Người đang theo dõi %1", diff --git a/public/language/vi/search.json b/public/language/vi/search.json new file mode 100644 index 0000000000..d0ffc64f36 --- /dev/null +++ b/public/language/vi/search.json @@ -0,0 +1,3 @@ +{ + "results_matching": "%1 result(s) matching \"%2\", (%3 seconds)" +} \ No newline at end of file diff --git a/public/language/vi/topic.json b/public/language/vi/topic.json index fd1dac9881..597c8cd4b9 100644 --- a/public/language/vi/topic.json +++ b/public/language/vi/topic.json @@ -87,6 +87,7 @@ "more_users_and_guests": "%1 người dùng và %2 khách nữa", "more_users": "%1 người dùng nữa", "more_guests": "%1 khách nữa", + "users_and_others": "%1 and %2 others", "sort_by": "Sort by", "oldest_to_newest": "Oldest to Newest", "newest_to_oldest": "Newest to Oldest", diff --git a/public/language/zh_CN/email.json b/public/language/zh_CN/email.json new file mode 100644 index 0000000000..ae359e04fb --- /dev/null +++ b/public/language/zh_CN/email.json @@ -0,0 +1,20 @@ +{ + "password-reset-requested": "密码重设申请 - %1!", + "welcome-to": "欢迎来到 %1", + "greeting_no_name": "您好", + "greeting_with_name": "%1,您好", + "welcome.text1": "谢谢您使用 %1 注册帐户!", + "welcome.text2": "如需完全激活您的帐户,我们需要校验您注册的电子邮箱地址。", + "welcome.cta": "点击这里确认您的电子邮箱地址", + "reset.text1": "我们收到了重置您密码的申请,可能因为您忘记了密码。如果不是,请忽略这封邮件。", + "reset.text2": "如需继续重置密码,请点击下面的链接:", + "reset.cta": "点击这里重设您的密码", + "digest.notifications": "您有一些来自 %1 的未读通知:", + "digest.latest_topics": "来自 %1 的最新主题", + "digest.cta": "点击这里访问 %1", + "digest.unsub.info": "根据您的订阅设置,为您发送此摘要。", + "digest.unsub.cta": "点击这里修改这些设置", + "digest.daily.no_topics": "最近几天有一些未激活的主题", + "test.text1": "这是一封测试邮件,用来验证 NodeBB 的邮件配置是否设置正确。", + "closing": "谢谢!" +} \ No newline at end of file diff --git a/public/language/zh_CN/error.json b/public/language/zh_CN/error.json index 96438847ad..b0678b8537 100644 --- a/public/language/zh_CN/error.json +++ b/public/language/zh_CN/error.json @@ -25,6 +25,7 @@ "no-user": "用户不存在", "no-teaser": "主题预览不存在", "no-privileges": "您没有足够的权限进行此操作。", + "no-emailers-configured": "未加载任何电子邮箱插件,无法发送测试邮件", "category-disabled": "版块已禁用", "topic-locked": "主题已锁定", "still-uploading": "请等待上传完成", diff --git a/public/language/zh_CN/groups.json b/public/language/zh_CN/groups.json new file mode 100644 index 0000000000..42cd7e9ccd --- /dev/null +++ b/public/language/zh_CN/groups.json @@ -0,0 +1,7 @@ +{ + "view_group": "查看用户组", + "details.title": "用户组详情", + "details.members": "会员列表", + "details.has_no_posts": "此用户组的会员尚未发表任何帖子。", + "details.latest_posts": "最新帖子" +} \ No newline at end of file diff --git a/public/language/zh_CN/login.json b/public/language/zh_CN/login.json index bdee8b778a..49cf1cc83f 100644 --- a/public/language/zh_CN/login.json +++ b/public/language/zh_CN/login.json @@ -2,7 +2,7 @@ "username": "用户名/电子邮箱", "remember_me": "记住我?", "forgot_password": "忘记密码?", - "alternative_logins": "其他登录方式使用合作网站账号登录京东", + "alternative_logins": "使用合作网站帐号登录", "failed_login_attempt": "登录失败,请重试。", "login_successful": "您已经成功登录!", "dont_have_account": "没有帐号?" diff --git a/public/language/zh_CN/notifications.json b/public/language/zh_CN/notifications.json index 9dcae00709..e2054a17f7 100644 --- a/public/language/zh_CN/notifications.json +++ b/public/language/zh_CN/notifications.json @@ -4,12 +4,11 @@ "see_all": "查看全部通知", "back_to_home": "返回 %1", "outgoing_link": "站外链接", - "outgoing_link_message": "您正在离开本站", - "continue_to": "继续前往", - "return_to": "返回", + "outgoing_link_message": "您正在离开 %1。", + "continue_to": "继续前往 %1", + "return_to": "返回 %1", "new_notification": "新通知", "you_have_unread_notifications": "您有未读的通知。", - "user_made_post": "%1 发布了一个新帖", "new_message_from": "来自 %1 的新消息", "upvoted_your_post": "%1 赞了您的帖子。", "favourited_your_post": "%1 收藏了您的帖子。", diff --git a/public/language/zh_CN/pages.json b/public/language/zh_CN/pages.json index 89eff2ecd1..32c3ba5e56 100644 --- a/public/language/zh_CN/pages.json +++ b/public/language/zh_CN/pages.json @@ -5,6 +5,7 @@ "recent": "最新主题", "users": "已注册用户", "notifications": "提醒", + "tags": "Topics tagged under \"%1\"", "user.edit": "正在编辑 \"%1\"", "user.following": "%1 关注", "user.followers": "关注 %1 的人", diff --git a/public/language/zh_CN/search.json b/public/language/zh_CN/search.json new file mode 100644 index 0000000000..acbed61943 --- /dev/null +++ b/public/language/zh_CN/search.json @@ -0,0 +1,3 @@ +{ + "results_matching": "%1 条结果,匹配 \"%2\",(耗时 %3 秒)" +} \ No newline at end of file diff --git a/public/language/zh_CN/topic.json b/public/language/zh_CN/topic.json index 579b2c062a..84275cb148 100644 --- a/public/language/zh_CN/topic.json +++ b/public/language/zh_CN/topic.json @@ -87,6 +87,7 @@ "more_users_and_guests": "%1 名会员和 %2 名游客", "more_users": "%1 名会员", "more_guests": "%1 名游客", + "users_and_others": "%1 and %2 others", "sort_by": "排序", "oldest_to_newest": "从旧到新", "newest_to_oldest": "从新到旧", diff --git a/public/language/zh_TW/email.json b/public/language/zh_TW/email.json new file mode 100644 index 0000000000..98e591ab02 --- /dev/null +++ b/public/language/zh_TW/email.json @@ -0,0 +1,20 @@ +{ + "password-reset-requested": "Password Reset Requested - %1!", + "welcome-to": "Welcome to %1", + "greeting_no_name": "Hello", + "greeting_with_name": "Hello %1", + "welcome.text1": "Thank you for registering with %1!", + "welcome.text2": "To fully activate your account, we need to verify that you own the email address you registered with.", + "welcome.cta": "Click here to confirm your email address", + "reset.text1": "We received a request to reset your password, possibly because you have forgotten it. If this is not the case, please ignore this email.", + "reset.text2": "To continue with the password reset, please click on the following link:", + "reset.cta": "Click here to reset your password", + "digest.notifications": "You have some unread notifications from %1:", + "digest.latest_topics": "Latest topics from %1", + "digest.cta": "Click here to visit %1", + "digest.unsub.info": "This digest was sent to you due to your subscription settings.", + "digest.unsub.cta": "Click here to alter those settings", + "digest.daily.no_topics": "There have been no active topics in the past day", + "test.text1": "This is a test email to verify that the emailer is set up correctly for your NodeBB.", + "closing": "Thanks!" +} \ No newline at end of file diff --git a/public/language/zh_TW/error.json b/public/language/zh_TW/error.json index 9219212736..a843fee3b9 100644 --- a/public/language/zh_TW/error.json +++ b/public/language/zh_TW/error.json @@ -25,6 +25,7 @@ "no-user": "使用者並不存在", "no-teaser": "Teaser 並不存在", "no-privileges": "您似乎沒有執行這個行為的權限!", + "no-emailers-configured": "No email plugins were loaded, so a test email could not be sent", "category-disabled": "該類別已被關閉", "topic-locked": "該主題已被鎖定", "still-uploading": "請等待上傳完成。", diff --git a/public/language/zh_TW/footer.json b/public/language/zh_TW/footer.json deleted file mode 100644 index cbccec6c32..0000000000 --- a/public/language/zh_TW/footer.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "stats.online": "在線", - "stats.users": "用戶", - "stats.topics": "主題", - "stats.posts": "文章", - "success": "成功" -} \ No newline at end of file diff --git a/public/language/zh_TW/global.json b/public/language/zh_TW/global.json index 4dc963a6c8..ba14211878 100644 --- a/public/language/zh_TW/global.json +++ b/public/language/zh_TW/global.json @@ -13,7 +13,7 @@ "please_log_in": "請登入", "logout": "退出", "posting_restriction_info": "發表文章目前僅限於註冊的會員,點擊此處進行登錄。", - "welcome_back": "歡迎回來 ~", + "welcome_back": "Welcome Back", "you_have_successfully_logged_in": "您已經成功登錄!", "save_changes": "保存修改", "close": "關閉", diff --git a/public/language/zh_TW/groups.json b/public/language/zh_TW/groups.json new file mode 100644 index 0000000000..c00c111e11 --- /dev/null +++ b/public/language/zh_TW/groups.json @@ -0,0 +1,7 @@ +{ + "view_group": "View Group", + "details.title": "Group Details", + "details.members": "Member List", + "details.has_no_posts": "This group's members have not made any posts.", + "details.latest_posts": "Latest Posts" +} \ No newline at end of file diff --git a/public/language/zh_TW/notifications.json b/public/language/zh_TW/notifications.json index 70202341a0..56c9534641 100644 --- a/public/language/zh_TW/notifications.json +++ b/public/language/zh_TW/notifications.json @@ -4,12 +4,11 @@ "see_all": "顯示全部", "back_to_home": "Back to %1", "outgoing_link": "站外鏈接", - "outgoing_link_message": "你正在離開本站。", - "continue_to": "繼續前往", - "return_to": "Return to", + "outgoing_link_message": "You are now leaving %1.", + "continue_to": "Continue to %1", + "return_to": "Return to %1", "new_notification": "新訊息通知", "you_have_unread_notifications": "您有未讀的訊息!", - "user_made_post": "%1 發表了一篇新文章", "new_message_from": "來自 %1 的新訊息", "upvoted_your_post": "%1 has upvoted your post.", "favourited_your_post": "%1 has favourited your post.", diff --git a/public/language/zh_TW/pages.json b/public/language/zh_TW/pages.json index c96d8d357e..dc1bcacfe0 100644 --- a/public/language/zh_TW/pages.json +++ b/public/language/zh_TW/pages.json @@ -5,6 +5,7 @@ "recent": "近期的主題", "users": "已註冊的使用者", "notifications": "新訊息通知", + "tags": "Topics tagged under \"%1\"", "user.edit": "編輯中 \"%1\"", "user.following": "People %1 Follows", "user.followers": "People who Follow %1", diff --git a/public/language/zh_TW/search.json b/public/language/zh_TW/search.json new file mode 100644 index 0000000000..d0ffc64f36 --- /dev/null +++ b/public/language/zh_TW/search.json @@ -0,0 +1,3 @@ +{ + "results_matching": "%1 result(s) matching \"%2\", (%3 seconds)" +} \ No newline at end of file diff --git a/public/language/zh_TW/topic.json b/public/language/zh_TW/topic.json index 948e56a8a7..696de46b27 100644 --- a/public/language/zh_TW/topic.json +++ b/public/language/zh_TW/topic.json @@ -87,6 +87,7 @@ "more_users_and_guests": "%1 more user(s) and %2 guest(s)", "more_users": "%1 more user(s)", "more_guests": "%1 more guest(s)", + "users_and_others": "%1 and %2 others", "sort_by": "Sort by", "oldest_to_newest": "Oldest to Newest", "newest_to_oldest": "Newest to Oldest", diff --git a/public/language/zh_TW/users.json b/public/language/zh_TW/users.json index 44d7d58a02..6fd23be181 100644 --- a/public/language/zh_TW/users.json +++ b/public/language/zh_TW/users.json @@ -5,6 +5,6 @@ "search": "搜尋", "enter_username": "輸入想找的使用者帳號", "load_more": "載入更多", - "user-not-found": "沒有找到該使用者!", + "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/src/ajaxify.js b/public/src/ajaxify.js index 798c59d43c..f1fe33c40d 100644 --- a/public/src/ajaxify.js +++ b/public/src/ajaxify.js @@ -2,340 +2,333 @@ var ajaxify = ajaxify || {}; -(function () { - /*global app, templates, utils, socket, translator, config, RELATIVE_PATH*/ - - var location = document.location || window.location, - rootUrl = location.protocol + '//' + (location.hostname || location.host) + (location.port ? ':' + location.port : ''), - templatesConfig = null, - availableTemplates = null, - apiXHR = null, - - PRELOADER_RATE_LIMIT = 10000; - - window.onpopstate = function (event) { - if (event !== null && event.state && event.state.url !== undefined && !ajaxify.initialLoad) { - ajaxify.go(event.state.url, function() { - $(window).trigger('action:popstate', {url: event.state.url}); - }, true); - } - }; - - ajaxify.currentPage = null; - ajaxify.initialLoad = false; - ajaxify.preloader = {}; - - function onAjaxError(err, url) { - var data = err.data, textStatus = err.textStatus; - - $('#content, #footer').removeClass('ajaxifying'); - - if (data) { - if (data.status === 404) { - return ajaxify.go('404'); - } else if (data.status === 403) { - app.alertError('[[global:please_log_in]]'); - app.previousUrl = url; - return ajaxify.go('login'); - } else if (data.status === 302) { - return ajaxify.go(data.responseJSON.slice(1)); - } - } else if (textStatus !== "abort") { - app.alertError(data.responseJSON.error); - } - } - - ajaxify.go = function (url, callback, quiet) { - // "quiet": If set to true, will not call pushState - app.enterRoom('global'); - - $(window).off('scroll'); - - $(window).trigger('action:ajaxify.start', {url: url}); - - if ($('#content').hasClass('ajaxifying') && apiXHR) { - apiXHR.abort(); - } - - // Remove trailing slash - url = url.replace(/\/$/, ""); - - url = ajaxify.removeRelativePath(url); - - var tpl_url = ajaxify.getTemplateMapping(url); - - var hash = ''; - if(ajaxify.initialLoad) { - hash = window.location.hash ? window.location.hash : ''; - } - - if (ajaxify.isTemplateAvailable(tpl_url) && !!!templatesConfig.force_refresh[tpl_url]) { - ajaxify.currentPage = url; - - if (window.history && window.history.pushState) { - window.history[!quiet ? 'pushState' : 'replaceState']({ - url: url + hash - }, url, RELATIVE_PATH + '/' + url + hash); - } - - translator.load(config.defaultLang, tpl_url); - - $('#footer, #content').removeClass('hide').addClass('ajaxifying'); - var animationDuration = parseFloat($('#content').css('transition-duration')) || 0.2, - startTime = (new Date()).getTime(); - - ajaxify.variables.flush(); - ajaxify.loadData(url, function(err, data) { - if (err) { - return onAjaxError(err, url); - } - - $(window).trigger('action:ajaxify.loadingTemplates', {}); - - templates.parse(tpl_url, data, function(template) { - translator.translate(template, function(translatedTemplate) { - setTimeout(function() { - $('#content').html(translatedTemplate); - ajaxify.widgets.render(tpl_url, url, function() { - $(window).trigger('action:ajaxify.end', {url: url}); - }); - - ajaxify.variables.parse(); - - $(window).trigger('action:ajaxify.contentLoaded', {url: url}); - - ajaxify.loadScript(tpl_url); - - if (typeof callback === 'function') { - callback(); - } - - app.processPage(); - - $('#content, #footer').removeClass('ajaxifying'); - ajaxify.initialLoad = false; - - app.refreshTitle(url); - }, animationDuration * 1000 - ((new Date()).getTime() - startTime)) - - }); - }); - }); - - return true; - } - - return false; - }; - - ajaxify.removeRelativePath = function(url) { - if (url.indexOf(RELATIVE_PATH.slice(1)) === 0) { - url = url.slice(RELATIVE_PATH.length); - } - return url; - }; - - ajaxify.refresh = function() { - ajaxify.go(ajaxify.currentPage); - }; - - ajaxify.loadScript = function(tpl_url, callback) { - require(['forum/' + tpl_url], function(script) { - if (script && script.init) { - script.init(); - } - - if (callback) { - callback(); - } - }); - }; - - ajaxify.isTemplateAvailable = function(tpl) { - return $.inArray(tpl + '.tpl', availableTemplates) !== -1; - }; - - ajaxify.getTemplateMapping = function(url) { - var tpl_url = ajaxify.getCustomTemplateMapping(url.split('?')[0]); - - if (tpl_url === false && !templates[url]) { - tpl_url = url.split('/'); - - while(tpl_url.length) { - if (ajaxify.isTemplateAvailable(tpl_url.join('/'))) { - tpl_url = tpl_url.join('/'); - break; - } - tpl_url.pop(); - } - - if (!tpl_url.length) { - tpl_url = url.split('/')[0].split('?')[0]; - } - } else if (templates[url]) { - tpl_url = url; - } - - return tpl_url; - }; - - ajaxify.getCustomTemplateMapping = function(tpl) { - if (templatesConfig && templatesConfig.custom_mapping && tpl !== undefined) { - for (var pattern in templatesConfig.custom_mapping) { - if (tpl.match(pattern)) { - return (templatesConfig.custom_mapping[pattern]); - } - } - } - - return false; - }; - - ajaxify.loadData = function(url, callback) { - url = ajaxify.removeRelativePath(url); - - $(window).trigger('action:ajaxify.loadingData', {url: url}); - - if (ajaxify.preloader && ajaxify.preloader[url] && !ajaxify.preloader[url].loading) { - callback(null, ajaxify.preloader[url].data); - ajaxify.preloader = {}; - return; - } +$(document).ready(function() { + require(['templates'], function (templatesModule) { + /*global app, templates, utils, socket, translator, config, RELATIVE_PATH*/ var location = document.location || window.location, - api_url = (url === '' || url === '/') ? 'home' : url, - tpl_url = ajaxify.getCustomTemplateMapping(url.split('?')[0]); + rootUrl = location.protocol + '//' + (location.hostname || location.host) + (location.port ? ':' + location.port : ''), + apiXHR = null, - if (!tpl_url) { - tpl_url = ajaxify.getTemplateMapping(url); + PRELOADER_RATE_LIMIT = 10000; + + window.onpopstate = function (event) { + if (event !== null && event.state && event.state.url !== undefined && !ajaxify.initialLoad) { + ajaxify.go(event.state.url, function() { + $(window).trigger('action:popstate', {url: event.state.url}); + }, true); + } + }; + + ajaxify.currentPage = null; + ajaxify.initialLoad = false; + ajaxify.preloader = {}; + + function onAjaxError(err, url) { + var data = err.data, textStatus = err.textStatus; + + $('#content, #footer').removeClass('ajaxifying'); + + if (data) { + if (data.status === 404) { + return ajaxify.go('404'); + } else if (data.status === 403) { + app.alertError('[[global:please_log_in]]'); + app.previousUrl = url; + return ajaxify.go('login'); + } else if (data.status === 302) { + return ajaxify.go(data.responseJSON.slice(1)); + } + } else if (textStatus !== "abort") { + app.alertError(data.responseJSON.error); + } } - apiXHR = $.ajax({ - url: RELATIVE_PATH + '/api/' + api_url, - cache: false, - success: function(data) { - if (!data) { - ajaxify.go('404'); + ajaxify.go = function (url, callback, quiet) { + // "quiet": If set to true, will not call pushState + app.enterRoom('global'); + + $(window).off('scroll'); + + $(window).trigger('action:ajaxify.start', {url: url}); + + if ($('#content').hasClass('ajaxifying') && apiXHR) { + apiXHR.abort(); + } + + // Remove trailing slash + url = url.replace(/\/$/, ""); + + url = ajaxify.removeRelativePath(url); + + var tpl_url = ajaxify.getTemplateMapping(url); + + var hash = ''; + if(ajaxify.initialLoad) { + hash = window.location.hash ? window.location.hash : ''; + } + + if (ajaxify.isTemplateAvailable(tpl_url) && !!!templatesModule.config.force_refresh[tpl_url]) { + ajaxify.currentPage = url; + + if (window.history && window.history.pushState) { + window.history[!quiet ? 'pushState' : 'replaceState']({ + url: url + hash + }, url, RELATIVE_PATH + '/' + url + hash); + } + + translator.load(config.defaultLang, tpl_url); + + $('#footer, #content').removeClass('hide').addClass('ajaxifying'); + var animationDuration = parseFloat($('#content').css('transition-duration')) || 0.2, + startTime = (new Date()).getTime(); + + ajaxify.variables.flush(); + ajaxify.loadData(url, function(err, data) { + if (err) { + return onAjaxError(err, url); + } + + $(window).trigger('action:ajaxify.loadingTemplates', {}); + + templates.parse(tpl_url, data, function(template) { + translator.translate(template, function(translatedTemplate) { + setTimeout(function() { + $('#content').html(translatedTemplate); + + ajaxify.variables.parse(); + + ajaxify.widgets.render(tpl_url, url, function() { + $(window).trigger('action:ajaxify.end', {url: url}); + }); + + $(window).trigger('action:ajaxify.contentLoaded', {url: url}); + + ajaxify.loadScript(tpl_url); + + if (typeof callback === 'function') { + callback(); + } + + app.processPage(); + + $('#content, #footer').removeClass('ajaxifying'); + ajaxify.initialLoad = false; + + app.refreshTitle(url); + }, animationDuration * 1000 - ((new Date()).getTime() - startTime)) + + }); + }); + }); + + return true; + } + + return false; + }; + + ajaxify.removeRelativePath = function(url) { + if (url.indexOf(RELATIVE_PATH.slice(1)) === 0) { + url = url.slice(RELATIVE_PATH.length); + } + return url; + }; + + ajaxify.refresh = function() { + ajaxify.go(ajaxify.currentPage); + }; + + ajaxify.loadScript = function(tpl_url, callback) { + require(['forum/' + tpl_url], function(script) { + if (script && script.init) { + script.init(); + } + + if (callback) { + callback(); + } + }); + }; + + ajaxify.isTemplateAvailable = function(tpl) { + return $.inArray(tpl + '.tpl', templatesModule.available) !== -1; + }; + + ajaxify.getTemplateMapping = function(url) { + var tpl_url = ajaxify.getCustomTemplateMapping(url.split('?')[0]); + + if (tpl_url === false && !templates[url]) { + tpl_url = url.split('/'); + + while(tpl_url.length) { + if (ajaxify.isTemplateAvailable(tpl_url.join('/'))) { + tpl_url = tpl_url.join('/'); + break; + } + tpl_url.pop(); + } + + if (!tpl_url.length) { + tpl_url = url.split('/')[0].split('?')[0]; + } + } else if (templates[url]) { + tpl_url = url; + } + + return tpl_url; + }; + + ajaxify.getCustomTemplateMapping = function(tpl) { + if (templatesModule.config && templatesModule.config.custom_mapping && tpl !== undefined) { + for (var pattern in templatesModule.config.custom_mapping) { + if (tpl.match(pattern)) { + return (templatesModule.config.custom_mapping[pattern]); + } + } + } + + return false; + }; + + ajaxify.loadData = function(url, callback) { + url = ajaxify.removeRelativePath(url); + + $(window).trigger('action:ajaxify.loadingData', {url: url}); + + if (ajaxify.preloader && ajaxify.preloader[url] && !ajaxify.preloader[url].loading) { + callback(null, ajaxify.preloader[url].data); + ajaxify.preloader = {}; + return; + } + + var location = document.location || window.location, + tpl_url = ajaxify.getCustomTemplateMapping(url.split('?')[0]); + + if (!tpl_url) { + tpl_url = ajaxify.getTemplateMapping(url); + } + + apiXHR = $.ajax({ + url: RELATIVE_PATH + '/api/' + url, + cache: false, + success: function(data) { + if (!data) { + ajaxify.go('404'); + return; + } + + data.relative_path = RELATIVE_PATH; + + if (callback) { + callback(null, data); + } + }, + error: function(data, textStatus) { + callback({ + data: data, + textStatus: textStatus + }); + } + }); + }; + + ajaxify.loadTemplate = function(template, callback) { + if (templates.cache[template]) { + callback(templates.cache[template]); + } else { + $.ajax({ + url: RELATIVE_PATH + '/templates/' + template + '.tpl' + (config['cache-buster'] ? '?v=' + config['cache-buster'] : ''), + type: 'GET', + success: function(data) { + callback(data.toString()); + }, + error: function(error) { + throw new Error("Unable to load template: " + template + " (" + error.statusText + ")"); + } + }); + } + }; + + $('document').ready(function () { + if (!window.history || !window.history.pushState) { + return; // no ajaxification for old browsers + } + + function hrefEmpty(href) { + return href === undefined || href === '' || href === 'javascript:;' || href === window.location.href + "#" || href.slice(0, 1) === "#"; + } + + // Enhancing all anchors to ajaxify... + $(document.body).on('click', 'a', function (e) { + if (hrefEmpty(this.href) || this.target !== '' || this.protocol === 'javascript:' || $(this).attr('data-ajaxify') === 'false') { return; } - data.relative_path = RELATIVE_PATH; - - if (callback) { - callback(null, data); + if(!window.location.pathname.match(/\/(403|404)$/g)) { + app.previousUrl = window.location.href; } - }, - error: function(data, textStatus) { - callback({ - data: data, - textStatus: textStatus - }); - } - }); - }; - ajaxify.loadTemplate = function(template, callback) { - if (templates.cache[template]) { - callback(templates.cache[template]); - } else { - $.ajax({ - url: RELATIVE_PATH + '/templates/' + template + '.tpl' + (config['cache-buster'] ? '?v=' + config['cache-buster'] : ''), - type: 'GET', - success: function(data) { - callback(data.toString()); - }, - error: function(error) { - throw new Error("Unable to load template: " + template + " (" + error.statusText + ")"); - } - }); - } - }; + if ((!e.ctrlKey && !e.shiftKey && !e.metaKey) && e.which === 1) { + if (this.host === window.location.host) { + // Internal link + var url = this.href.replace(rootUrl + '/', ''); - $('document').ready(function () { - if (!window.history || !window.history.pushState) { - return; // no ajaxification for old browsers - } + if(window.location.pathname === this.pathname && this.hash) { + if (this.hash !== window.location.hash) { + window.location.hash = this.hash; + } - function hrefEmpty(href) { - return href === undefined || href === '' || href === 'javascript:;' || href === window.location.href + "#" || href.slice(-1) === "#"; - } - - // Enhancing all anchors to ajaxify... - $(document.body).on('click', 'a', function (e) { - if (hrefEmpty(this.href) || this.target !== '' || this.protocol === 'javascript:' || $(this).attr('data-ajaxify') === 'false') { - return; - } - - if(!window.location.pathname.match(/\/(403|404)$/g)) { - app.previousUrl = window.location.href; - } - - if ((!e.ctrlKey && !e.shiftKey && !e.metaKey) && e.which === 1) { - if (this.host === window.location.host) { - // Internal link - var url = this.href.replace(rootUrl + '/', ''); - - if(window.location.pathname === this.pathname && this.hash) { - if (this.hash !== window.location.hash) { - window.location.hash = this.hash; + ajaxify.loadScript(ajaxify.getTemplateMapping(url)); + e.preventDefault(); + } else { + if (ajaxify.go(url)) { + e.preventDefault(); + } } - - ajaxify.loadScript(ajaxify.getTemplateMapping(url)); - e.preventDefault(); - } else { - if (ajaxify.go(url)) { + } else if (window.location.pathname !== '/outgoing') { + // External Link + if (config.openOutgoingLinksInNewTab) { + window.open(this.href, '_blank'); + e.preventDefault(); + } else if (config.useOutgoingLinksPage) { + ajaxify.go('outgoing?url=' + encodeURIComponent(this.href)); e.preventDefault(); } } - } else if (window.location.pathname !== '/outgoing') { - // External Link + } + }); - if (config.useOutgoingLinksPage) { - ajaxify.go('outgoing?url=' + encodeURIComponent(this.href)); - e.preventDefault(); - } else if (config.openOutgoingLinksInNewTab) { - window.open(this.href, '_blank'); - e.preventDefault(); + $(document.body).on('mouseover', 'a', function (e) { + if (hrefEmpty(this.href) || this.target !== '' || this.protocol === 'javascript:' || $(this).attr('data-ajaxify') === 'false') { + return; + } + + if (this.host === window.location.host) { + // Internal link + var url = this.href.replace(rootUrl + '/', ''), + currentTime = (new Date()).getTime(); + + if (!ajaxify.preloader[url] || (!ajaxify.preloader[url].loading && currentTime - ajaxify.preloader[url].lastFetched > PRELOADER_RATE_LIMIT)) { + ajaxify.preloader[url] = { + loading: true + }; + ajaxify.loadData(url, function(err, data) { + ajaxify.preloader[url] = err ? null : { + url: url, + data: data, + lastFetched: currentTime, + loading: false + }; + }); } } - } + + }); + + templates.registerLoader(ajaxify.loadTemplate); + templatesModule.refresh(app.load); }); - $(document.body).on('mouseover', 'a', function (e) { - if (hrefEmpty(this.href) || this.target !== '' || this.protocol === 'javascript:' || $(this).attr('data-ajaxify') === 'false') { - return; - } - - if (this.host === window.location.host) { - // Internal link - var url = this.href.replace(rootUrl + '/', ''), - currentTime = (new Date()).getTime(); - - if (!ajaxify.preloader[url] || (!ajaxify.preloader[url].loading && currentTime - ajaxify.preloader[url].lastFetched > PRELOADER_RATE_LIMIT)) { - ajaxify.preloader[url] = { - loading: true - }; - ajaxify.loadData(url, function(err, data) { - ajaxify.preloader[url] = err ? null : { - url: url, - data: data, - lastFetched: currentTime, - loading: false - }; - }); - } - } - - }); - - templates.registerLoader(ajaxify.loadTemplate); - - $.when($.getJSON(RELATIVE_PATH + '/templates/config.json'), $.getJSON(RELATIVE_PATH + '/api/get_templates_listing')).done(function (config_data, templates_data) { - templatesConfig = config_data[0]; - availableTemplates = templates_data[0]; - - app.load(); - }); }); - -}()); +}); \ No newline at end of file diff --git a/public/src/app.js b/public/src/app.js index eff176c266..b84a528764 100644 --- a/public/src/app.js +++ b/public/src/app.js @@ -54,23 +54,6 @@ var socket, socket.emit('meta.reconnected'); - socket.removeAllListeners('event:nodebb.ready'); - socket.on('event:nodebb.ready', function(cacheBuster) { - if (app.cacheBuster !== cacheBuster) { - app.cacheBuster = cacheBuster; - - app.alert({ - alert_id: 'forum_updated', - title: '[[global:updated.title]]', - message: '[[global:updated.message]]', - clickfn: function() { - window.location.reload(); - }, - type: 'warning' - }); - } - }); - $(window).trigger('action:reconnected'); setTimeout(function() { @@ -413,8 +396,8 @@ var socket, if (utils.findBootstrapEnvironment() === 'xs') { return; } - $('#header-menu li i[title]').each(function() { - $(this).parents('a').tooltip({ + $('#header-menu li [title]').each(function() { + $(this).tooltip({ placement: 'bottom', title: $(this).attr('title') }); @@ -441,7 +424,7 @@ var socket, searchButton.show(); } - searchButton.off().on('click', function(e) { + searchButton.on('click', function(e) { if (!config.loggedIn && !config.allowGuestSearching) { app.alert({ message:'[[error:search-requires-login]]', @@ -542,6 +525,28 @@ var socket, }); }); }); + + socket.removeAllListeners('event:nodebb.ready'); + socket.on('event:nodebb.ready', function(cacheBusters) { + if ( + !app.cacheBusters || + app.cacheBusters.general !== cacheBusters.general || + app.cacheBusters.css !== cacheBusters.css || + app.cacheBusters.js !== cacheBusters.js + ) { + app.cacheBusters = cacheBusters; + + app.alert({ + alert_id: 'forum_updated', + title: '[[global:updated.title]]', + message: '[[global:updated.message]]', + clickfn: function() { + window.location.reload(); + }, + type: 'warning' + }); + } + }); }); }; diff --git a/public/src/forum/account/edit.js b/public/src/forum/account/edit.js index 0a9237c7af..d93ca01bbd 100644 --- a/public/src/forum/account/edit.js +++ b/public/src/forum/account/edit.js @@ -1,6 +1,6 @@ 'use strict'; -/* globals define, ajaxify, socket, app, config, utils, translator */ +/* globals define, ajaxify, socket, app, config, utils, translator, bootbox */ define('forum/account/edit', ['forum/account/header', 'uploader'], function(header, uploader) { var AccountEdit = {}, @@ -26,6 +26,7 @@ define('forum/account/edit', ['forum/account/header', 'uploader'], function(head currentEmail = $('#inputEmail').val(); handleImageChange(); + handleAccountDelete(); handleImageUpload(); handleEmailConfirm(); handlePasswordChange(); @@ -125,6 +126,24 @@ define('forum/account/edit', ['forum/account/header', 'uploader'], function(head }); } + function handleAccountDelete() { + $('#deleteAccountBtn').on('click', function() { + translator.translate('[[user:delete_account_confirm]]', function(translated) { + bootbox.confirm(translated, function(confirm) { + if (!confirm) { + return; + } + socket.emit('user.deleteAccount', {}, function(err) { + if (!err) { + app.logout(); + } + }); + }); + }); + return false; + }); + } + function handleImageUpload() { $('#upload-picture-modal').on('hide', function() { $('#userPhotoInput').val(''); diff --git a/public/src/forum/account/header.js b/public/src/forum/account/header.js index 6efa692a0f..f552bf4056 100644 --- a/public/src/forum/account/header.js +++ b/public/src/forum/account/header.js @@ -18,10 +18,6 @@ define('forum/account/header', function() { $this.toggleClass('hide', $this.hasClass('private')); }); } - - if (app.isAdmin) { - $('#editLink, #settingsLink').removeClass('hide'); - } } function selectActivePill() { diff --git a/public/src/forum/admin/categories.js b/public/src/forum/admin/categories.js index cc1627fdf6..c7851c1e7e 100644 --- a/public/src/forum/admin/categories.js +++ b/public/src/forum/admin/categories.js @@ -231,6 +231,30 @@ define('forum/admin/categories', ['uploader', 'forum/admin/iconSelect'], functio }); setupEditTargets(); + + $('button[data-action="setParent"]').on('click', function() { + var cid = $(this).parents('[data-cid]').attr('data-cid'), + modal = $('#setParent'); + + modal.find('select').val($(this).attr('data-parentCid')); + modal.attr('data-cid', cid).modal(); + }); + $('#setParent [data-cid]').on('click', function() { + var modalEl = $('#setParent'), + parentCid = $(this).attr('data-cid'), + payload = {}; + + payload[modalEl.attr('data-cid')] = { + parentCid: parentCid + }; + + socket.emit('admin.categories.update', payload, function(err) { + modalEl.one('hidden.bs.modal', function() { + ajaxify.go('admin/categories/active'); + }); + modalEl.modal('hide'); + }); + }); }); }; diff --git a/public/src/forum/admin/groups.js b/public/src/forum/admin/groups.js index 5e4f0c34d1..34a1b59ed0 100644 --- a/public/src/forum/admin/groups.js +++ b/public/src/forum/admin/groups.js @@ -78,14 +78,33 @@ define('forum/admin/groups', ['forum/admin/iconSelect'], function(iconSelect) { break; case 'members': socket.emit('admin.groups.get', groupName, function(err, groupObj) { - var formEl = detailsModal.find('form'); + var formEl = detailsModal.find('form').keypress(function(e) { + switch(e.keyCode) { + case 13: + detailsModalSave.click(); + break; + default: + break; + } + }), + groupLabelPreview = formEl.find('#group-label-preview'), + changeGroupUserTitle = formEl.find('#change-group-user-title'), + changeGroupLabelColor = formEl.find('#change-group-label-color'); formEl.find('#change-group-name').val(groupObj.name).prop('readonly', groupObj.system); formEl.find('#change-group-desc').val(groupObj.description); - formEl.find('#change-group-user-title').val(groupObj.userTitle); + changeGroupUserTitle.val(groupObj.userTitle).keydown(function() { + setTimeout(function() { + groupLabelPreview.text(changeGroupUserTitle.val()); + }, 0); + }); formEl.find('#group-icon').attr('class', 'fa fa-2x ' + groupObj.icon).attr('value', groupObj.icon); - formEl.find('#change-group-label-color').val(groupObj.labelColor); - formEl.find('#group-label-preview').css('background', groupObj.labelColor || '#000000').text(groupObj.userTitle); + changeGroupLabelColor.val(groupObj.labelColor).keydown(function() { + setTimeout(function() { + groupLabelPreview.css('background', changeGroupLabelColor.val() || '#000000'); + }, 0); + }); + groupLabelPreview.css('background', groupObj.labelColor || '#000000').text(groupObj.userTitle); groupMembersEl.empty(); if (groupObj.members.length > 0) { diff --git a/public/src/forum/admin/index.js b/public/src/forum/admin/index.js index c10706e257..e45c661628 100644 --- a/public/src/forum/admin/index.js +++ b/public/src/forum/admin/index.js @@ -37,18 +37,58 @@ define('forum/admin/index', function() { }); $('.restart').on('click', function() { + bootbox.confirm('Are you sure you wish to restart NodeBB?', function(confirm) { + if (confirm) { + app.alert({ + alert_id: 'instance_restart', + type: 'info', + title: 'Restarting... ', + message: 'NodeBB is restarting.', + timeout: 5000 + }); + + $(window).one('action:reconnected', function() { + app.alert({ + alert_id: 'instance_restart', + type: 'success', + title: ' Success', + message: 'NodeBB has successfully restarted.', + timeout: 5000 + }); + }); + + socket.emit('admin.restart'); + } + }); + }); + + $('.reload').on('click', function() { app.alert({ - timeout: 5000, - title: 'Restarting...', - message: 'NodeBB is restarting.', - type: 'info' + alert_id: 'instance_reload', + type: 'info', + title: 'Reloading... ', + message: 'NodeBB is reloading.', + timeout: 5000 }); - $(window).one('action:reconnected', function() { - app.alertSuccess('NodeBB has successfully restarted.'); + socket.emit('admin.reload', function(err) { + if (!err) { + app.alert({ + alert_id: 'instance_reload', + type: 'success', + title: ' Success', + message: 'NodeBB has successfully reloaded.', + timeout: 5000 + }); + } else { + app.alert({ + alert_id: 'instance_reload', + type: 'danger', + title: '[[global:alert.error]]', + message: '[[error:reload-failed, ' + err.message + ']]' + }); + } }); - - socket.emit('admin.restart'); }); }; diff --git a/public/src/forum/admin/tags.js b/public/src/forum/admin/tags.js new file mode 100644 index 0000000000..e6468c7f21 --- /dev/null +++ b/public/src/forum/admin/tags.js @@ -0,0 +1,60 @@ +"use strict"; +/*global define, socket, app, admin*/ + +define('forum/admin/tags', [], function() { + var Tags = {}; + + Tags.init = function() { + handleColorPickers(); + + $('.tag-list').on('click', '.save', function() { + save($(this)); + }); + + $('#tag-search').on('input propertychange', function() { + $('.tag-list').children().each(function() { + var $this = $(this); + $this.toggleClass('hide', $this.attr('data-tag').indexOf($('#tag-search').val()) === -1); + }); + }); + }; + + function handleColorPickers() { + function enableColorPicker(idx, inputEl) { + var $inputEl = $(inputEl), + previewEl = $inputEl.parents('.tag-row').find('.tag-item'); + + admin.enableColorPicker($inputEl, function(hsb, hex) { + if ($inputEl.attr('data-name') === 'bgColor') { + previewEl.css('background-color', '#' + hex); + } else if ($inputEl.attr('data-name') === 'color') { + previewEl.css('color', '#' + hex); + } + }); + } + + + $('[data-name="bgColor"], [data-name="color"]').each(enableColorPicker); + } + + function save(saveBtn) { + var tagRow = saveBtn.parents('.tag-row'); + + var data = { + tag: tagRow.attr('data-tag'), + bgColor : tagRow.find('[data-name="bgColor"]').val(), + color : tagRow.find('[data-name="color"]').val() + }; + + 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/themes.js b/public/src/forum/admin/themes.js index c76fea0c9a..6a1b3db477 100644 --- a/public/src/forum/admin/themes.js +++ b/public/src/forum/admin/themes.js @@ -1,5 +1,5 @@ "use strict"; -/*global define, socket, app, bootbox, tabIndent, config, RELATIVE_PATH*/ +/*global define, socket, app, bootbox, tabIndent, config, RELATIVE_PATH, templates */ define('forum/admin/themes', ['forum/admin/settings'], function(Settings) { var Themes = {}; @@ -71,17 +71,17 @@ define('forum/admin/themes', ['forum/admin/settings'], function(Settings) { if (confirm) { socket.emit('admin.themes.set', { type: 'local', - id: 'nodebb-theme-cerulean' + id: 'nodebb-theme-vanilla' }, function(err) { if (err) { return app.alertError(err.message); } - highlightSelectedTheme('nodebb-theme-cerulean'); + highlightSelectedTheme('nodebb-theme-vanilla'); app.alert({ alert_id: 'admin:theme', type: 'success', title: 'Theme Changed', - message: 'You have successfully reverted your NodeBB back to it\'s default theme. Restarting your NodeBB ', + message: 'You have successfully reverted your NodeBB back to it\'s default theme.', timeout: 3500 }); }); @@ -95,34 +95,19 @@ define('forum/admin/themes', ['forum/admin/settings'], function(Settings) { return app.alertError(err.message); } - var instListEl = $('#installed_themes').empty(), liEl; + var instListEl = $('#installed_themes'); if (!themes.length) { instListEl.append($('
  • ').addClass('no-themes').html('No installed themes found')); return; + } else { + templates.parse('partials/admin/theme_list', { + themes: themes + }, function(html) { + instListEl.html(html); + highlightSelectedTheme(config['theme:id']); + }); } - - for (var x = 0, numThemes = themes.length; x < numThemes; x++) { - liEl = $('
  • ').attr({ - 'data-type': 'local', - 'data-theme': themes[x].id - }).html('' + - '
    ' + - '
    ' + - ' ' + - '
    ' + - '

    ' + themes[x].name + '

    ' + - '

    ' + - themes[x].description + - (themes[x].url ? ' (Homepage)' : '') + - '

    ' + - '
    ' + - '
    '); - - instListEl.append(liEl); - } - - highlightSelectedTheme(config['theme:id']); }); // Proper tabbing for "Custom CSS" field @@ -138,26 +123,23 @@ define('forum/admin/themes', ['forum/admin/settings'], function(Settings) { }; Themes.render = function(bootswatch) { - var themeContainer = $('#bootstrap_themes').empty(), - numThemes = bootswatch.themes.length, themeEl, theme; + var themeContainer = $('#bootstrap_themes'); - for (var x = 0; x < numThemes; x++) { - theme = bootswatch.themes[x]; - themeEl = $('
  • ').attr({ - 'data-type': 'bootswatch', - 'data-css': theme.cssCdn, - 'data-theme': theme.name - }).html('' + - '
    ' + - '
    ' + - ' ' + - '
    ' + - '

    ' + theme.name + '

    ' + - '

    ' + theme.description + '

    ' + - '
    ' + - '
    '); - themeContainer.append(themeEl); - } + templates.parse('partials/admin/theme_list', { + themes: bootswatch.themes.map(function(theme) { + return { + type: 'bootswatch', + id: theme.name, + name: theme.name, + description: theme.description, + screenshot_url: theme.thumbnail, + url: theme.preview, + css: theme.cssCdn + }; + }) + }, function(html) { + themeContainer.html(html); + }); }; Themes.prepareWidgets = function() { diff --git a/public/src/forum/admin/users.js b/public/src/forum/admin/users.js index 4bd33c8657..554f78660b 100644 --- a/public/src/forum/admin/users.js +++ b/public/src/forum/admin/users.js @@ -73,6 +73,22 @@ define('forum/admin/users', function() { unselectAll(); }); + $('.reset-lockout').on('click', function() { + var uids = getSelectedUids(); + if (!uids.length) { + return; + } + + socket.emit('admin.user.resetLockouts', uids, function(err) { + if (err) { + return app.alertError(err.message); + } + app.alertSuccess('Lockout(s) reset!'); + }); + + unselectAll(); + }); + $('.admin-user').on('click', function() { var uids = getSelectedUids(); if (!uids.length) { diff --git a/public/src/forum/category.js b/public/src/forum/category.js index 81c082ba85..d62719fb9d 100644 --- a/public/src/forum/category.js +++ b/public/src/forum/category.js @@ -35,7 +35,7 @@ define('forum/category', ['composer', 'forum/pagination', 'forum/infinitescroll' enableInfiniteLoadingOrPagination(); if (!config.usePagination) { - navigator.init('#topics-container > .category-item', ajaxify.variables.get('topic_count'), undefined, Category.toTop, Category.toBottom); + navigator.init('#topics-container > .category-item', ajaxify.variables.get('topic_count'), Category.toTop, Category.toBottom, Category.navigatorCallback); } $('#topics-container').on('click', '.topic-title', function() { @@ -60,6 +60,10 @@ define('forum/category', ['composer', 'forum/pagination', 'forum/infinitescroll' }); }; + Category.navigatorCallback = function(element, elementCount) { + return parseInt(element.attr('data-index'), 10) + 1; + } + $(window).on('action:popstate', function(ev, data) { if(data.url.indexOf('category/') === 0) { var bookmark = localStorage.getItem('category:bookmark'); @@ -285,8 +289,7 @@ define('forum/category', ['composer', 'forum/pagination', 'forum/infinitescroll' return; } - infinitescroll.calculateAfter(direction, '#topics-container .category-item[data-tid]', config.topicsPerPage, function(after, offset, el) { - + infinitescroll.calculateAfter(direction, '#topics-container .category-item[data-tid]', config.topicsPerPage, false, function(after, offset, el) { loadTopicsAfter(after, function() { if (direction < 0 && el) { Category.scrollToTopic(el.attr('data-tid'), null, 0, offset); diff --git a/public/src/forum/chats.js b/public/src/forum/chats.js index d84dbac203..cf35f8d5f8 100644 --- a/public/src/forum/chats.js +++ b/public/src/forum/chats.js @@ -170,6 +170,7 @@ define('forum/chats', ['string', 'sounds'], function(S, sounds) { var recipientUid = Chats.getRecipientUid(); if (recipientUid) { socket.emit('modules.chats.markRead', recipientUid); + $('.expanded-chat input').focus(); } $('.chats-list li').removeClass('bg-primary'); $('.chats-list li[data-uid="' + recipientUid + '"]').addClass('bg-primary'); diff --git a/public/src/forum/infinitescroll.js b/public/src/forum/infinitescroll.js index 053635f312..870f555a8c 100644 --- a/public/src/forum/infinitescroll.js +++ b/public/src/forum/infinitescroll.js @@ -55,13 +55,16 @@ define('forum/infinitescroll', function() { }); }; - scroll.calculateAfter = function(direction, selector, count, callback) { + scroll.calculateAfter = function(direction, selector, count, reverse, callback) { var after = 0, offset = 0, el = direction > 0 ? $(selector).last() : $(selector).first(); + var count = reverse ? -count : count; + var increment = reverse ? -1 : 1; + if (direction > 0) { - after = parseInt(el.attr('data-index'), 10) + 1; + after = parseInt(el.attr('data-index'), 10) + increment; } else { after = parseInt(el.attr('data-index'), 10); if (isNaN(after)) { diff --git a/public/src/forum/login.js b/public/src/forum/login.js index 75754b04ca..4f4c9b6061 100644 --- a/public/src/forum/login.js +++ b/public/src/forum/login.js @@ -28,7 +28,7 @@ define('forum/login', function() { if (previousUrl) { app.previousUrl = previousUrl; } else if (!app.previousUrl) { - app.previousUrl = '/'; + app.previousUrl = RELATIVE_PATH || '/'; } if(app.previousUrl.indexOf('/reset/') !== -1) { diff --git a/public/src/forum/topic.js b/public/src/forum/topic.js index d6663ed3a9..61ad6f4837 100644 --- a/public/src/forum/topic.js +++ b/public/src/forum/topic.js @@ -26,6 +26,7 @@ define('forum/topic', dependencies, function(pagination, infinitescroll, threadT events.removeListeners(); socket.removeListener('event:new_post', onNewPost); + socket.removeListener('event:new_notification', onNewNotification); } }); @@ -42,8 +43,7 @@ define('forum/topic', dependencies, function(pagination, infinitescroll, threadT app.enterRoom('topic_' + tid); - browsing.populateOnlineUsers(); - $('.post-content img').addClass('img-responsive'); + processPage($('.topic')); showBottomPostBar(); @@ -63,25 +63,28 @@ define('forum/topic', dependencies, function(pagination, infinitescroll, threadT handleBookmark(tid); - navigator.init('.posts > .post-row', postCount, Topic.navigatorCallback, Topic.toTop, Topic.toBottom); + navigator.init('.posts > .post-row', postCount, Topic.toTop, Topic.toBottom, Topic.navigatorCallback, Topic.calculateIndex); socket.on('event:new_post', onNewPost); + socket.on('event:new_notification', onNewNotification); $(window).on('scroll', updateTopicTitle); $(window).trigger('action:topic.loaded'); - socket.emit('topics.markAsRead', tid); - socket.emit('topics.increaseViewCount', tid); + socket.emit('topics.enter', tid); }; Topic.toTop = function() { - navigator.scrollTop(1); + navigator.scrollTop(0); }; Topic.toBottom = function() { - socket.emit('topics.lastPostIndex', ajaxify.variables.get('topic_id'), function(err, index) { - navigator.scrollBottom(index); + socket.emit('topics.postcount', ajaxify.variables.get('topic_id'), function(err, postCount) { + if (config.topicPostSort !== 'oldest_to_newest') { + postCount = 1; + } + navigator.scrollBottom(postCount); }); }; @@ -146,11 +149,17 @@ 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); } + function onNewNotification(data) { + var tid = ajaxify.variables.get('topic_id'); + if (data && data.tid && parseInt(data.tid, 10) === parseInt(tid, 10)) { + socket.emit('topics.markTopicNotificationsRead', tid); + } + } + function addBlockQuoteHandler() { $('#post-container').on('click', 'blockquote .toggle', function() { var blockQuote = $(this).parent('blockquote'); @@ -195,8 +204,23 @@ define('forum/topic', dependencies, function(pagination, infinitescroll, threadT } } - Topic.navigatorCallback = function(element) { + Topic.calculateIndex = function(index, elementCount) { + if (index !== 1 && config.topicPostSort !== 'oldest_to_newest') { + return elementCount - index + 2; + } + return index; + }; + + Topic.navigatorCallback = function(element, elementCount) { var postIndex = parseInt(element.attr('data-index'), 10); + var index = postIndex + 1; + if (config.topicPostSort !== 'oldest_to_newest') { + if (postIndex === 0) { + index = 1; + } else { + index = Math.max(elementCount - postIndex + 1, 1); + } + } var currentBookmark = localStorage.getItem('topic:' + ajaxify.variables.get('topic_id') + ':bookmark'); @@ -224,6 +248,7 @@ define('forum/topic', dependencies, function(pagination, infinitescroll, threadT currentUrl = newUrl; } } + return index; }; function onNewPostPagination(data) { @@ -241,8 +266,9 @@ define('forum/topic', dependencies, function(pagination, infinitescroll, threadT } function createNewPosts(data, callback) { + callback = callback || function() {}; if(!data || (data.posts && !data.posts.length)) { - return; + return callback(false); } function removeAlreadyAddedPosts() { @@ -291,7 +317,7 @@ define('forum/topic', dependencies, function(pagination, infinitescroll, threadT removeAlreadyAddedPosts(); if(!data.posts.length) { - return; + return callback(false); } findInsertionPoint(); @@ -314,9 +340,7 @@ define('forum/topic', dependencies, function(pagination, infinitescroll, threadT $(window).trigger('action:posts.loaded'); onNewPostsLoaded(html, data.posts); - if (typeof callback === 'function') { - callback(); - } + callback(true); }); } @@ -334,13 +358,22 @@ define('forum/topic', dependencies, function(pagination, infinitescroll, threadT getPostPrivileges(posts[x].pid); } + processPage(html); + } + + function processPage(element) { browsing.populateOnlineUsers(); app.createUserTooltips(); - app.replaceSelfLinks(html.find('a')); - utils.addCommasToNumbers(html.find('.formatted-number')); - utils.makeNumbersHumanReadable(html.find('.human-readable-number')); - html.find('span.timeago').timeago(); - html.find('.post-content img').addClass('img-responsive'); + app.replaceSelfLinks(element.find('a')); + utils.addCommasToNumbers(element.find('.formatted-number')); + utils.makeNumbersHumanReadable(element.find('.human-readable-number')); + element.find('span.timeago').timeago(); + element.find('.post-content img:not(.emoji)').addClass('img-responsive').each(function() { + var $this = $(this); + if (!$this.parent().is('a')) { + $this.wrap(''); + } + }); postTools.updatePostCount(); showBottomPostBar(); } @@ -350,7 +383,7 @@ define('forum/topic', dependencies, function(pagination, infinitescroll, threadT postHtml.find('.move').toggleClass('none', !privileges.move); postHtml.find('.reply, .quote').toggleClass('none', !$('.post_reply').length); var isSelfPost = parseInt(postHtml.attr('data-uid'), 10) === parseInt(app.uid, 10); - postHtml.find('.chat, .flag').toggleClass('none', isSelfPost); + postHtml.find('.chat, .flag').toggleClass('none', isSelfPost || !app.uid); } function loadMorePosts(direction) { @@ -358,7 +391,9 @@ define('forum/topic', dependencies, function(pagination, infinitescroll, threadT return; } - infinitescroll.calculateAfter(direction, '#post-container .post-row[data-index!="0"]', config.postsPerPage, function(after, offset, el) { + var reverse = config.topicPostSort === 'newest_to_oldest' || config.topicPostSort === 'most_votes'; + + infinitescroll.calculateAfter(direction, '#post-container .post-row[data-index!="0"]', config.postsPerPage, reverse, function(after, offset, el) { loadPostsAfter(after, function() { if (direction < 0 && el) { navigator.scrollToPost(el.attr('data-index'), false, 0, offset); @@ -386,9 +421,11 @@ define('forum/topic', dependencies, function(pagination, infinitescroll, threadT indicatorEl.fadeOut(); if (data && data.posts && data.posts.length) { - createNewPosts(data, function() { + createNewPosts(data, function(postsCreated) { done(); - callback(); + if (postsCreated) { + callback(); + } }); hidePostToolsForDeletedPosts(); } else { diff --git a/public/src/forum/topic/move.js b/public/src/forum/topic/move.js index c94dfb40f2..11f82c85f3 100644 --- a/public/src/forum/topic/move.js +++ b/public/src/forum/topic/move.js @@ -90,24 +90,10 @@ define('forum/topic/move', function() { } function renderCategories(categories) { - var categoriesEl = modal.find('.category-list'), - info; - - for (var x = 0; x < categories.length; ++x) { - info = categories[x]; - if(parseInt(info.cid, 10) === parseInt(Move.currentCid, 10)) { - continue; - } - - $('
  • ') - .css({background: info.bgColor, color: info.color || '#fff'}) - .toggleClass('disabled', info.disabled) - .attr('data-cid', info.cid) - .html(' ' + info.name) - .appendTo(categoriesEl); - } - - $('#categories-loading').remove(); + templates.parse('partials/category_list', {categories: categories}, function(html) { + modal.find('.modal-body').prepend(html); + $('#categories-loading').remove(); + }); } return Move; diff --git a/public/src/forum/topic/postTools.js b/public/src/forum/topic/postTools.js index 24d7491fa5..3b63650942 100644 --- a/public/src/forum/topic/postTools.js +++ b/public/src/forum/topic/postTools.js @@ -15,6 +15,8 @@ define('forum/topic/postTools', ['composer', 'share', 'navigator'], function(com share.addShareHandlers(topicName); addFavouriteHandler(); + + addVoteHandler(); }; PostTools.toggle = function(pid, isDeleted) { @@ -36,18 +38,43 @@ define('forum/topic/postTools', ['composer', 'share', 'navigator'], function(com }; function addFavouriteHandler() { - $('#post-container').on('mouseenter', '.favourite-tooltip', function(e) { - if (!$(this).data('users-loaded')) { - $(this).data('users-loaded', "true"); - var pid = $(this).parents('.post-row').attr('data-pid'); - var el = $(this).attr('title', "Loading..."); - socket.emit('posts.getFavouritedUsers', pid, function(err, usernames) { - el.attr('title', usernames).tooltip('show'); - }); + $('#post-container').on('mouseenter', '.favourite-tooltip', function() { + loadDataAndCreateTooltip($(this), 'posts.getFavouritedUsers'); + }); + } + + function addVoteHandler() { + $('#post-container').on('mouseenter', '.post-row .votes', function() { + loadDataAndCreateTooltip($(this), 'posts.getUpvoters'); + }); + } + + function loadDataAndCreateTooltip(el, method) { + var pid = el.parents('.post-row').attr('data-pid'); + socket.emit(method, pid, function(err, usernames) { + if (!err) { + createTooltip(el, usernames); } }); } + function createTooltip(el, usernames) { + if (!usernames.length) { + return; + } + if (usernames.length > 6) { + var otherCount = usernames.length - 5; + usernames = usernames.slice(0, 5).join(', ').replace(/,/g, '|'); + translator.translate('[[topic:users_and_others, ' + usernames + ', ' + otherCount + ']]', function(translated) { + translated = translated.replace(/\|/g, ','); + el.attr('title', translated).tooltip('destroy').tooltip('show'); + }); + } else { + usernames = usernames.join(', '); + el.attr('title', usernames).tooltip('destroy').tooltip('show'); + } + } + function addPostHandlers(tid, threadState) { $('.topic').on('click', '.post_reply', function() { if (!threadState.locked) { @@ -137,7 +164,7 @@ define('forum/topic/postTools', ['composer', 'share', 'navigator'], function(com if($('.composer').length) { composer.addQuote(tid, ajaxify.variables.get('topic_slug'), getData(button, 'data-index'), pid, topicName, username, quoted); } else { - composer.newReply(tid, pid, topicName, '[[modules:composer.user_said, ' + username + ']]' + quoted); + composer.newReply(tid, pid, topicName, '[[modules:composer.user_said, ' + username + ']]\n' + quoted); } }); } diff --git a/public/src/forum/users.js b/public/src/forum/users.js index 35c68f18cb..e30ab1ae33 100644 --- a/public/src/forum/users.js +++ b/public/src/forum/users.js @@ -163,21 +163,29 @@ define('forum/users', function() { } function updateUser(data) { - var userEl = $('#users-container li[data-uid="' + data.uid +'"]'); + var usersContainer = $('#users-container'); + var userEl = usersContainer.find('li[data-uid="' + data.uid +'"]'); if (!data.online) { userEl.remove(); - } else { - ajaxify.loadTemplate('users', function(usersTemplate) { - var html = templates.parse(templates.getBlock(usersTemplate, 'users'), {users: [data]}); - translator.translate(html, function(translated) { - if (!userEl.length) { - $('#users-container').append(translated); - } else { - userEl.replaceWith(translated); - } - }); - }); + return; } + + ajaxify.loadTemplate('users', function(usersTemplate) { + var html = templates.parse(templates.getBlock(usersTemplate, 'users'), {users: [data]}); + translator.translate(html, function(translated) { + if (userEl.length) { + userEl.replaceWith(translated); + return; + } + + var anonBox = usersContainer.find('li.anon-user'); + if (anonBox.length) { + $(translated).insertBefore(anonBox); + } else { + usersContainer.append(translated); + } + }); + }); } function updateAnonCount() { diff --git a/public/src/helpers.js b/public/src/helpers.js index 4dbd5419bc..a89aeae0e2 100644 --- a/public/src/helpers.js +++ b/public/src/helpers.js @@ -14,7 +14,7 @@ property = tag.property ? 'property="' + tag.property + '" ' : '', content = tag.content ? 'content="' + tag.content.replace(/\n/g, ' ') + '" ' : ''; - return ''; + return ''; }; if ('undefined' !== typeof window) { diff --git a/public/src/modules/alerts.js b/public/src/modules/alerts.js index 9cf301f5af..83e4daa833 100644 --- a/public/src/modules/alerts.js +++ b/public/src/modules/alerts.js @@ -63,7 +63,9 @@ define('alerts', function() { alert.attr('class', 'alert alert-dismissable alert-' + params.type); clearTimeout(parseInt(alert.attr('timeoutId'), 10)); - startTimeout(alert, params.timeout); + if (params.timeout) { + startTimeout(alert, params.timeout); + } alert.children().fadeOut(100); translator.translate(alert.html(), function(translatedHTML) { diff --git a/public/src/modules/chat.js b/public/src/modules/chat.js index 4eb25b108d..73b94a0442 100644 --- a/public/src/modules/chat.js +++ b/public/src/modules/chat.js @@ -125,6 +125,7 @@ define('chat', ['taskbar', 'string', 'sounds', 'forum/chats'], function(taskbar, } }); chatModal.css('zIndex', topZ + 1); + taskbar.updateActive(chatModal.attr('UUID')); }; module.getModal = function(touid) { @@ -213,7 +214,6 @@ define('chat', ['taskbar', 'string', 'sounds', 'forum/chats'], function(taskbar, chatModal.on('mousemove keypress click', function() { if (newMessage) { socket.emit('modules.chats.markRead', touid); - console.log('sent') newMessage = false; } }); @@ -250,7 +250,7 @@ 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", $(window).height() / 4 - $(chatModal).outerHeight() / 2); + chatModal.css("top", Math.max(0, $(window).height() / 4 - $(chatModal).outerHeight() / 2)); chatModal.css("zIndex", 2000); chatModal.find('#chat-message-input').focus(); return chatModal; diff --git a/public/src/modules/composer.js b/public/src/modules/composer.js index 05b0f82c41..337938b4dd 100644 --- a/public/src/modules/composer.js +++ b/public/src/modules/composer.js @@ -9,11 +9,12 @@ var dependencies = [ 'composer/formatting', 'composer/drafts', 'composer/tags', + 'composer/categoryList', 'composer/preview', 'composer/resize' ]; -define('composer', dependencies, function(taskbar, controls, uploads, formatting, drafts, tags, preview, resize) { +define('composer', dependencies, function(taskbar, controls, uploads, formatting, drafts, tags, categoryList, preview, resize) { var composer = { active: undefined, posts: {}, @@ -119,7 +120,7 @@ define('composer', dependencies, function(taskbar, controls, uploads, formatting var uuid = composer.active; if (uuid === undefined) { - composer.newReply(tid, pid, title, '[[modules:composer.user_said, ' + username + ']]' + text); + composer.newReply(tid, pid, title, '[[modules:composer.user_said, ' + username + ']]\n' + text); return; } var postContainer = $('#cmp-uuid-' + uuid); @@ -127,9 +128,9 @@ define('composer', dependencies, function(taskbar, controls, uploads, formatting var prevText = bodyEl.val(); if (parseInt(tid, 10) !== parseInt(composer.posts[uuid].tid, 10)) { var link = '[' + title + '](/topic/' + topicSlug + '/' + (parseInt(postIndex, 10) + 1) + ')'; - translator.translate('[[modules:composer.user_said_in, ' + username + ', ' + link + ']]', onTranslated); + translator.translate('[[modules:composer.user_said_in, ' + username + ', ' + link + ']]\n', onTranslated); } else { - translator.translate('[[modules:composer.user_said, ' + username + ']]', onTranslated); + translator.translate('[[modules:composer.user_said, ' + username + ']]\n', onTranslated); } function onTranslated(translated) { @@ -200,84 +201,96 @@ define('composer', dependencies, function(taskbar, controls, uploads, formatting var template = (composer.bsEnvironment === 'xs' || composer.bsEnvironment === 'sm') ? 'composer-mobile' : 'composer'; - templates.parse(template, {allowTopicsThumbnail: allowTopicsThumbnail, showTags: isTopic || isMain}, function(composerTemplate) { - translator.translate(composerTemplate, function(composerTemplate) { - composerTemplate = $(composerTemplate); + var data = { + allowTopicsThumbnail: allowTopicsThumbnail, + showTags: isTopic || isMain, + isTopic: isTopic + }; - composerTemplate.attr('id', 'cmp-uuid-' + post_uuid); + parseAndTranslate(template, data, function(composerTemplate) { - $(document.body).append(composerTemplate); + composerTemplate = $(composerTemplate); - var postContainer = $(composerTemplate[0]), - postData = composer.posts[post_uuid], - bodyEl = postContainer.find('textarea'), - draft = drafts.getDraft(postData.save_id); + composerTemplate.attr('id', 'cmp-uuid-' + post_uuid); - tags.init(postContainer, composer.posts[post_uuid]); - updateTitle(postData, postContainer); + $(document.body).append(composerTemplate); - activate(post_uuid); - resize.reposition(postContainer); + var postContainer = $(composerTemplate[0]), + postData = composer.posts[post_uuid], + bodyEl = postContainer.find('textarea'), + draft = drafts.getDraft(postData.save_id); - if (config.allowFileUploads || config.hasImageUploadPlugin) { - uploads.initialize(post_uuid); + tags.init(postContainer, composer.posts[post_uuid]); + categoryList.init(postContainer, composer.posts[post_uuid]); + updateTitle(postData, postContainer); + + activate(post_uuid); + resize.reposition(postContainer); + + if (config.allowFileUploads || config.hasImageUploadPlugin) { + uploads.initialize(post_uuid); + } + + formatting.addHandler(postContainer); + + if (allowTopicsThumbnail) { + uploads.toggleThumbEls(postContainer, composer.posts[post_uuid].topic_thumb || ''); + } + + postContainer.on('change', 'input, textarea', function() { + composer.posts[post_uuid].modified = true; + }); + + postContainer.on('click', '.action-bar button[data-action="post"]', function() { + $(this).attr('disabled', true); + post(post_uuid); + }); + + postContainer.on('click', '.action-bar button[data-action="discard"]', function() { + if (!composer.posts[post_uuid].modified) { + discard(post_uuid); + return; } - formatting.addHandler(postContainer); - - if (allowTopicsThumbnail) { - uploads.toggleThumbEls(postContainer, composer.posts[post_uuid].topic_thumb || ''); - } - - postContainer.on('change', 'input, textarea', function() { - composer.posts[post_uuid].modified = true; - }); - - postContainer.on('click', '.action-bar button[data-action="post"]', function() { - $(this).attr('disabled', true); - post(post_uuid); - }); - - postContainer.on('click', '.action-bar button[data-action="discard"]', function() { - if (!composer.posts[post_uuid].modified) { - discard(post_uuid); - return; - } - - translator.translate('[[modules:composer.discard]]', function(translated) { - bootbox.confirm(translated, function(confirm) { - if (confirm) { - discard(post_uuid); - } - }); + translator.translate('[[modules:composer.discard]]', function(translated) { + bootbox.confirm(translated, function(confirm) { + if (confirm) { + discard(post_uuid); + } }); }); - - bodyEl.on('input propertychange', function() { - preview.render(postContainer); - }); - - bodyEl.on('scroll', function() { - preview.matchScroll(postContainer); - }); - - bodyEl.val(draft ? draft : postData.body); - preview.render(postContainer, function() { - preview.matchScroll(postContainer); - }); - drafts.init(postContainer, postData); - - resize.handleResize(postContainer); - - handleHelp(postContainer); - - $(window).trigger('action:composer.loaded', { - post_uuid: post_uuid - }); - - formatting.addComposerButtons(); - focusElements(postContainer); }); + + bodyEl.on('input propertychange', function() { + preview.render(postContainer); + }); + + bodyEl.on('scroll', function() { + preview.matchScroll(postContainer); + }); + + bodyEl.val(draft ? draft : postData.body); + preview.render(postContainer, function() { + preview.matchScroll(postContainer); + }); + drafts.init(postContainer, postData); + + resize.handleResize(postContainer); + + handleHelp(postContainer); + + $(window).trigger('action:composer.loaded', { + post_uuid: post_uuid + }); + + formatting.addComposerButtons(); + focusElements(postContainer); + }); + } + + function parseAndTranslate(template, data, callback) { + templates.parse(template, data, function(composerTemplate) { + translator.translate(composerTemplate, callback); }); } @@ -360,33 +373,45 @@ define('composer', dependencies, function(taskbar, controls, uploads, formatting return composerAlert('[[error:content-too-short, ' + config.minimumPostLength + ']]'); } + var composerData = {}, action; + if (parseInt(postData.cid, 10) > 0) { - socket.emit('topics.post', { + composerData = { title: titleEl.val(), content: bodyEl.val(), topic_thumb: thumbEl.val() || '', category_id: postData.cid, tags: tags.getTags(post_uuid) - }, function(err, topic) { + }; + + action = 'topics.post'; + socket.emit(action, composerData, function(err, topic) { done(err); + if (!err) { ajaxify.go('topic/' + topic.slug); } }); } else if (parseInt(postData.tid, 10) > 0) { - socket.emit('posts.reply', { + composerData = { tid: postData.tid, content: bodyEl.val(), toPid: postData.toPid - }, done); + }; + + action = 'posts.reply'; + socket.emit(action, composerData, done); } else if (parseInt(postData.pid, 10) > 0) { - socket.emit('posts.edit', { + composerData = { pid: postData.pid, content: bodyEl.val(), title: titleEl.val(), topic_thumb: thumbEl.val() || '', tags: tags.getTags(post_uuid) - }, done); + }; + + action = 'posts.edit'; + socket.emit(action, composerData, done); } function done(err) { @@ -401,6 +426,8 @@ define('composer', dependencies, function(taskbar, controls, uploads, formatting discard(post_uuid); drafts.removeDraft(postData.save_id); + + $(window).trigger('action:composer.' + action, composerData); } } diff --git a/public/src/modules/composer/categoryList.js b/public/src/modules/composer/categoryList.js new file mode 100644 index 0000000000..776e435bc9 --- /dev/null +++ b/public/src/modules/composer/categoryList.js @@ -0,0 +1,37 @@ + +'use strict'; + +/*globals define, config, socket, app*/ + +define('composer/categoryList', function() { + var categoryList = {}; + + categoryList.init = function(postContainer, postData) { + var listEl = postContainer.find('.category-list'); + if (!listEl.length) { + return; + } + + socket.emit('categories.getCategoriesByPrivilege', 'topics:create', function(err, categories) { + if (err) { + return app.alertError(err.message); + } + + categories.forEach(function(category) { + $('').appendTo(listEl); + }); + + if (postData.cid) { + listEl.find('option[value="' + postData.cid + '"]').prop('selected', true); + } + }); + + listEl.on('change', function() { + if (postData.cid) { + postData.cid = this.value; + } + }); + }; + + return categoryList; +}); diff --git a/public/src/modules/composer/resize.js b/public/src/modules/composer/resize.js index a544aaf9b6..75382b2b6e 100644 --- a/public/src/modules/composer/resize.js +++ b/public/src/modules/composer/resize.js @@ -137,8 +137,8 @@ define('composer/resize', function() { function resizeWritePreview(postContainer) { - var h1 = postContainer.find('.title').outerHeight(true); - var h2 = postContainer.find('.tags-container').outerHeight(true); + var h1 = postContainer.find('.title-container').outerHeight(true); + var h2 = postContainer.find('.category-tag-row').outerHeight(true); var h3 = postContainer.find('.formatting-bar').outerHeight(true); var h4 = postContainer.find('.topic-thumb-container').outerHeight(true); var h5 = $('.taskbar').height(); diff --git a/public/src/modules/navigator.js b/public/src/modules/navigator.js index bc1bf3e2b8..3392a7bd83 100644 --- a/public/src/modules/navigator.js +++ b/public/src/modules/navigator.js @@ -11,8 +11,8 @@ define('navigator', ['forum/pagination'], function(pagination) { var count = 0; navigator.scrollActive = false; - navigator.init = function(selector, count, callback, toTop, toBottom) { - + navigator.init = function(selector, count, toTop, toBottom, callback, calculateIndex) { + index = 1; navigator.selector = selector; navigator.callback = callback; toTop = toTop || function() {}; @@ -36,7 +36,13 @@ define('navigator', ['forum/pagination'], function(pagination) { input.val(''); return; } - var url = generateUrl(input.val()); + + var index = parseInt(input.val(), 10); + if (typeof calculateIndex === 'function') { + index = calculateIndex(index, count); + } + + var url = generateUrl(index); input.val(''); $('.pagination-block .dropdown-toggle').trigger('click'); ajaxify.go(url); @@ -76,12 +82,9 @@ define('navigator', ['forum/pagination'], function(pagination) { var el = $(this); if (elementInView(el)) { - index = parseInt(el.attr('data-index'), 10) + 1; - - navigator.updateTextAndProgressBar(); - if (typeof navigator.callback === 'function') { - navigator.callback(el); + index = navigator.callback(el, count); + navigator.updateTextAndProgressBar(); } return false; @@ -110,7 +113,7 @@ define('navigator', ['forum/pagination'], function(pagination) { navigator.scrollTop = function(index) { if ($('li[data-index="' + index + '"]').length) { - navigator.scrollUp(); + navigator.scrollToPost(index, true); } else { ajaxify.go(generateUrl()); } @@ -118,7 +121,7 @@ define('navigator', ['forum/pagination'], function(pagination) { navigator.scrollBottom = function(index) { if ($('li[data-index="' + index + '"]').length) { - navigator.scrollDown(); + navigator.scrollToPost(index, true); } else { index = parseInt(index, 10) + 1; ajaxify.go(generateUrl(index)); @@ -147,7 +150,7 @@ define('navigator', ['forum/pagination'], function(pagination) { return scrollToPid(postIndex, highlight, duration, offset); } - if(config.usePagination) { + if (config.usePagination) { if (window.location.search.indexOf('page') !== -1) { navigator.update(); return; @@ -162,12 +165,19 @@ define('navigator', ['forum/pagination'], function(pagination) { } else { scrollToPid(postIndex, highlight, duration, offset); } + } else { + navigator.scrollActive = false; } }; function scrollToPid(postIndex, highlight, duration, offset) { var scrollTo = $('#post_anchor_' + postIndex); + if (!scrollTo) { + navigator.scrollActive = false; + return; + } + var done = false; function animateScroll() { $('html, body').animate({ @@ -195,7 +205,7 @@ define('navigator', ['forum/pagination'], function(pagination) { } } - if ($('#post-container').length && scrollTo.length) { + if ($('#post-container').length) { animateScroll(); } } diff --git a/public/src/modules/settings.js b/public/src/modules/settings.js index 29bc4a4c4e..78ac0ad044 100644 --- a/public/src/modules/settings.js +++ b/public/src/modules/settings.js @@ -448,17 +448,18 @@ define('settings', function () { helper.persistSettings(hash, Settings.cfg, notify, callback); }, load: function (hash, formEl, callback) { + callback = callback || function() {}; socket.emit('admin.settings.get', { hash: hash }, function (err, values) { - if (!err) { - $(formEl).deserialize(values); - if (typeof callback === 'function') { - callback(); - } - } else { + if (err) { console.log('[settings] Unable to load settings for hash: ', hash); + return callback(err); } + + $(formEl).deserialize(values); + + callback(null, values); }); }, save: function (hash, formEl, callback) { diff --git a/public/src/modules/templates.js b/public/src/modules/templates.js new file mode 100644 index 0000000000..2c25dbdb64 --- /dev/null +++ b/public/src/modules/templates.js @@ -0,0 +1,14 @@ +define('templates', function() { + var Templates = {}; + + Templates.refresh = function(callback) { + $.getJSON(RELATIVE_PATH + '/api/get_templates_listing', function (data) { + Templates.config = data.templatesConfig; + Templates.available = data.availableTemplates; + + if (callback) callback(); + }); + }; + + return Templates; +}); \ No newline at end of file diff --git a/public/src/templates.js b/public/src/templates.js index 5cf10aeb4b..b629b03d0b 100644 --- a/public/src/templates.js +++ b/public/src/templates.js @@ -36,7 +36,7 @@ Please use the npm module instead - require('templates.js') } callback(parse(loaded, obj, bind)); - }); + }); } else { callback(parse(templates.cache[template], obj, bind)); } @@ -58,14 +58,15 @@ Please use the npm module instead - require('templates.js') }; templates.getBlock = function(template, block) { - return template.replace(new RegExp('[\\s\\S]*([\r\n]*[\\s\\S]*?[\r\n]*)[\\s\\S]*', 'g'), '$1'); + return template.replace(new RegExp('[\\s\\S]*([\\s\\S]*?)[\\s\\S]*', 'g'), '$1'); }; function express(filename, options, fn) { - console.log(filename, options, fn); var fs = require('fs'), tpl = filename.replace(options.settings.views + '/', ''); + options['_locals'] = null; + if (!templates.cache[tpl]) { fs.readFile(filename, function(err, html) { templates.cache[tpl] = (html || '').toString(); @@ -82,11 +83,11 @@ Please use the npm module instead - require('templates.js') } function makeRegex(block) { - return new RegExp('[\\s\\S]*?'); + return new RegExp('[\\t ]*[\\s\\S]*?'); } function makeBlockRegex(block) { - return new RegExp('([\\n]?[\\n]?)|([\\n]?[\\n]?)', 'g'); + return new RegExp('([\\t ]*[\\r\\n?|\\n]?)|()', 'g'); } function makeConditionalRegex(block) { @@ -94,7 +95,7 @@ Please use the npm module instead - require('templates.js') } function makeStatementRegex(key) { - return new RegExp('([\\s]*)|([\\s]*)', 'gi'); + return new RegExp('()|()', 'g'); } function registerGlobals(obj) { @@ -113,23 +114,23 @@ Please use the npm module instead - require('templates.js') if (matches !== null) { for (var i = 0, ii = matches.length; i < ii; i++) { var statement = makeStatementRegex(key), - nestedConditionals = matches[i].match(/[\s|\S][\s|\S]/), - match = matches[i].replace(statement, '').replace(/[\s|\S][\s|\S]/gi, ''), - conditionalBlock = match.split(/\s*\s*/); + nestedConditionals = matches[i].match(/(?!^)(?!$)/gi), + match = matches[i].replace(statement, '').replace(/(?!^)(?!$)/gi, ''), + conditionalBlock = match.split(/[\r\n?\n]*?[\r\n?\n]*?/); if (conditionalBlock[1]) { // there is an else statement - if (!value) { - template = template.replace(matches[i], conditionalBlock[1].replace(/(^[\r\n\t]*)|([\r\n\t]*$)/gi, '')); + if (!value) { // todo check second line break conditional, doesn't match. + template = template.replace(matches[i], conditionalBlock[1].replace(/(^[\r\n?|\n]*)|([\r\n\t]*$)/gi, '')); } else { - template = template.replace(matches[i], conditionalBlock[0].replace(/(^[\r\n\t]*)|([\r\n\t]*$)/gi, '')); + template = template.replace(matches[i], conditionalBlock[0].replace(/(^[\r\n?|\n]*)|([\r\n\t]*$)/gi, '')); } } else { // regular if statement if (!value) { template = template.replace(matches[i], ''); } else { - template = template.replace(matches[i], match.replace(/(^[\r\n\t]*)|([\r\n\t]*$)/gi, '')); + template = template.replace(matches[i], match.replace(/(^[\r\n?|\n]*)|([\r\n\t]*$)/gi, '')); } } @@ -181,40 +182,43 @@ Please use the npm module instead - require('templates.js') var regex = makeRegex(key), block; if (!array[key].length) { - return template; + return template.replace(regex, ''); } while (block = template.match(regex)) { block = block[0].replace(makeBlockRegex(key), ''); - + var numblocks = array[key].length - 1, iterator = 0, result = '', parsedBlock; do { - parsedBlock = parse(block, array[key][iterator], bind, namespace, {iterator: iterator, total: numblocks}) + ((iterator < numblocks) ? '\r\n':''); - + parsedBlock = parse(block, array[key][iterator], bind, namespace, {iterator: iterator, total: numblocks}); + result += (!bind) ? parsedBlock : setBindContainer(parsedBlock, bind + namespace + iterator); - result = parseFunctions(block, result, { - data: array[key][iterator], - iterator: iterator, - numblocks: numblocks - }); result = checkConditional(result, '@first', iterator === 0); result = checkConditional(result, '!@first', iterator !== 0); result = checkConditional(result, '@last', iterator === numblocks); result = checkConditional(result, '!@last', iterator !== numblocks); + result = result.replace(/^[\r\n?|\n|\t]*?|[\r\n?|\n|\t]*?$/g, ''); + + result = parseFunctions(block, result, { + data: array[key][iterator], + iterator: iterator, + numblocks: numblocks + }); + if (bind) { array[key][iterator].__template = block; } } while (iterator++ < numblocks); - template = template.replace(regex, result); + template = template.replace(regex, result.replace(/^[\r\n?|\n]|[\r\n?|\n]$/g, '')); } - + return template; } @@ -248,14 +252,14 @@ Please use the npm module instead - require('templates.js') this['__' + key] = value; var els = document.querySelectorAll('[data-binding="' + (this.__iterator !== false ? (bind + this.__namespace + this.__iterator) : bind) + '"]'); - + for (var el in els) { if (els.hasOwnProperty(el)) { if (this.__parent) { var parent = this.__parent(); els[el].innerHTML = parse(parent.template, parent.data, false); } else { - els[el].innerHTML = parse(this.__template, obj, false, this.__namespace); + els[el].innerHTML = parse(this.__template, obj, false, this.__namespace); } } } @@ -295,7 +299,7 @@ Please use the npm module instead - require('templates.js') template = parse(template, obj[key], bind, namespace + key + '.'); } else { template = parseValue(template, namespace + key, obj[key]); - + if (bind && obj[key]) { setupBindings({ obj: obj, diff --git a/src/categories.js b/src/categories.js index ceae7d8f01..2245517d5b 100644 --- a/src/categories.js +++ b/src/categories.js @@ -67,12 +67,13 @@ var db = require('./database'), }; Categories.getCategoryById = function(cid, start, end, uid, callback) { - Categories.getCategoryData(cid, function(err, category) { - if(err || !category) { + Categories.getCategories([cid], uid, function(err, categories) { + if (err || !Array.isArray(categories) || !categories[0]) { return callback(err || new Error('[[error:invalid-cid]]')); } + var category = categories[0]; - if(parseInt(uid, 10)) { + if (parseInt(uid, 10)) { Categories.markAsRead(cid, uid); } @@ -93,7 +94,7 @@ var db = require('./database'), category.pageCount = results.pageCount; category.topic_row_size = 'col-md-9'; - callback(null, category); + plugins.fireHook('filter:category.get', category, uid, callback); }); }); }; @@ -174,7 +175,7 @@ var db = require('./database'), }); }; - Categories.getAllCategories = function(callback) { + Categories.getAllCategories = function(uid, callback) { db.getSortedSetRange('categories:cid', 0, -1, function(err, cids) { if (err) { return callback(err); @@ -184,11 +185,11 @@ var db = require('./database'), return callback(null, []); } - Categories.getCategoriesData(cids, callback); + Categories.getCategories(cids, uid, callback); }); }; - Categories.getVisibleCategories = function(uid, callback) { + Categories.getCategoriesByPrivilege = function(uid, privilege, callback) { db.getSortedSetRange('categories:cid', 0, -1, function(err, cids) { if (err) { return callback(err); @@ -198,7 +199,7 @@ var db = require('./database'), return callback(null, []); } - privileges.categories.filter('find', cids, uid, function(err, cids) { + privileges.categories.filter(privilege, cids, uid, function(err, cids) { if (err) { return callback(err); } @@ -237,10 +238,9 @@ var db = require('./database'), }; Categories.markAsUnreadForAll = function(cid, callback) { + callback = callback || function() {}; db.delete('cid:' + cid + ':read_by_uid', function(err) { - if(typeof callback === 'function') { - callback(err); - } + callback(err); }); }; @@ -286,6 +286,7 @@ var db = require('./database'), category.description = validator.escape(category.description); category.backgroundImage = category.image ? nconf.get('relative_path') + category.image : ''; category.disabled = category.disabled ? parseInt(category.disabled, 10) !== 0 : false; + next(null, category); }, callback); }); @@ -295,6 +296,13 @@ var db = require('./database'), db.getObjectField('category:' + cid, field, callback); }; + Categories.getMultipleCategoryFields = function(cids, fields, callback) { + var keys = cids.map(function(cid) { + return 'category:' + cid; + }); + db.getObjectsFields(keys, fields, callback); + }; + Categories.getCategoryFields = function(cid, fields, callback) { db.getObjectFields('category:' + cid, fields, callback); }; @@ -320,6 +328,12 @@ var db = require('./database'), categories: function(next) { Categories.getCategoriesData(cids, next); }, + children: function(next) { + Categories.getChildren(cids, uid, next); + }, + parents: function(next) { + Categories.getParents(cids, next); + }, hasRead: function(next) { Categories.hasReadCategories(cids, uid, next); } @@ -333,12 +347,51 @@ var db = require('./database'), uid = parseInt(uid, 10); for(var i=0; i=0; --i) { - if (!isMembers[i]) { - groupData.splice(i, 1); + async.map(uids, function(uid, next) { + db.isMemberOfSets(groupSets, uid, function(err, isMembers) { + if (err) { + return next(err); } - } - callback(null, groupData); - }); + var memberOf = []; + isMembers.forEach(function(isMember, index) { + if (isMember) { + memberOf.push(groupData[index]); + } + }); + + next(null, memberOf); + }); + }, callback); }); }); }; diff --git a/src/hotswap.js b/src/hotswap.js new file mode 100644 index 0000000000..717c099052 --- /dev/null +++ b/src/hotswap.js @@ -0,0 +1,33 @@ +var HotSwap = {}, + winston = require('winston'), + stack; + +HotSwap.prepare = function(app) { + stack = app._router.stack; +}; + +HotSwap.find = function(id) { + if (stack) { + for(var x=0,numEntries=stack.length;x[ \t]*/; + + while(matches = file.match(regex)) { + var partial = "/" + matches[1]; + + if (paths[partial] && relativePath !== partial) { + file = file.replace(regex, fs.readFileSync(paths[partial]).toString()); + } else { + winston.warn('[themes] Partial not loaded: ' + matches[1]); + file = file.replace(regex, ""); + } + } + + mkdirp.sync(path.join(viewsPath, relativePath.split('/').slice(0, -1).join('/'))); + fs.writeFile(path.join(viewsPath, relativePath), file, next); + }, function(err) { + if (err) { + winston.error(err); + } else { + winston.info('[themes] Successfully compiled templates.'); + emitter.emit('templates:compiled'); + if (callback) callback(); + } + }); + }); + }); +}; + +module.exports = Templates; \ No newline at end of file diff --git a/src/meta/themes.js b/src/meta/themes.js index b26253a495..f41620cc1b 100644 --- a/src/meta/themes.js +++ b/src/meta/themes.js @@ -39,6 +39,15 @@ module.exports = function(Meta) { return next(); } else { var configObj = JSON.parse(file.toString()); + + // Minor adjustments for API output + configObj.type = 'local'; + if (configObj.screenshot) { + configObj.screenshot_url = nconf.get('relative_path') + '/css/previews/' + configObj.id + } else { + configObj.screenshot_url = nconf.get('relative_path') + '/images/themes/default.png'; + } + next(err, configObj); } }); diff --git a/src/meta/title.js b/src/meta/title.js index 58a03876d5..652001af97 100644 --- a/src/meta/title.js +++ b/src/meta/title.js @@ -9,6 +9,7 @@ module.exports = function(Meta) { var tests = { isCategory: /^category\/\d+\/?/, isTopic: /^topic\/\d+\/?/, + isTag: /^tags\/[\s\S]+\/?/, isUserPage: /^user\/[^\/]+(\/[\w]+)?/ }; @@ -42,6 +43,12 @@ module.exports = function(Meta) { var tid = urlFragment.match(/topic\/(\d+)/)[1]; require('../topics').getTopicField(tid, 'title', callback); + } else if (tests.isTag.test(urlFragment)) { + var tag = urlFragment.match(/tags\/([\s\S]+)/)[1]; + + translator.translate('[[pages:tags, ' + tag + ']]', language, function(translated) { + callback(null, translated); + }); } else if (tests.isUserPage.test(urlFragment)) { var matches = urlFragment.match(/user\/([^\/]+)\/?([\w]+)?/), userslug = matches[1], diff --git a/src/middleware/admin.js b/src/middleware/admin.js index d9d36791ce..92ab6de9e2 100644 --- a/src/middleware/admin.js +++ b/src/middleware/admin.js @@ -16,7 +16,7 @@ var app, middleware.isAdmin = function(req, res, next) { if (!req.user) { - return res.redirect('/login?next=admin'); + return res.redirect(nconf.get('relative_path') + '/login?next=admin'); } user.isAdministrator((req.user && req.user.uid) ? req.user.uid : 0, function (err, isAdmin) { diff --git a/src/middleware/index.js b/src/middleware/index.js index 9fbaa0f97a..5ea711b46c 100644 --- a/src/middleware/index.js +++ b/src/middleware/index.js @@ -24,10 +24,7 @@ var utils = require('./../../public/src/utils'), session = require('express-session'), relativePath, - viewsPath, - themesPath, - baseTemplatesPath, - themeTemplatesPath; + themesPath; var middleware = {}; @@ -41,7 +38,7 @@ function routeThemeScreenshots(app, themes) { (function(id, path) { fs.exists(path, function(exists) { if (exists) { - app.get('/css/previews/' + id, function(req, res) { + app.get(relativePath + '/css/previews/' + id, function(req, res) { res.sendfile(path); }); } @@ -68,91 +65,17 @@ function routeCurrentTheme(app, themeId, themesData) { // Theme's templates path nconf.set('theme_templates_path', themeObj.templates ? path.join(themesPath, themeObj.id, themeObj.templates) : nconf.get('base_templates_path')); - themeTemplatesPath = nconf.get('theme_templates_path'); -} - -function compileTemplates(pluginTemplates) { - var mkdirp = require('mkdirp'), - rimraf = require('rimraf'); - - winston.info('[themes] Compiling templates'); - rimraf.sync(viewsPath); - mkdirp.sync(viewsPath); - - async.parallel({ - baseTpls: function(next) { - utils.walk(baseTemplatesPath, next); - }, - themeTpls: function(next) { - utils.walk(themeTemplatesPath, next); - } - }, function(err, data) { - var baseTpls = data.baseTpls, - themeTpls = data.themeTpls, - paths = {}; - - if (!baseTpls || !themeTpls) { - winston.warn('[themes] Could not find base template files at: ' + baseTemplatesPath); - } - - baseTpls = !baseTpls ? [] : baseTpls.map(function(tpl) { return tpl.replace(baseTemplatesPath, ''); }); - themeTpls = !themeTpls ? [] : themeTpls.map(function(tpl) { return tpl.replace(themeTemplatesPath, ''); }); - - baseTpls.forEach(function(el, i) { - paths[baseTpls[i]] = path.join(baseTemplatesPath, baseTpls[i]); - }); - - themeTpls.forEach(function(el, i) { - paths[themeTpls[i]] = path.join(themeTemplatesPath, themeTpls[i]); - }); - - for (var tpl in pluginTemplates) { - if (pluginTemplates.hasOwnProperty(tpl)) { - paths[tpl] = pluginTemplates[tpl]; - } - } - - async.each(Object.keys(paths), function(relativePath, next) { - var file = fs.readFileSync(paths[relativePath]).toString(), - matches = null, - regex = /[ \t]*[ \t]*/; - - while(matches = file.match(regex)) { - var partial = "/" + matches[1]; - - if (paths[partial] && relativePath !== partial) { - file = file.replace(regex, fs.readFileSync(paths[partial]).toString()); - } else { - winston.warn('[themes] Partial not loaded: ' + matches[1]); - file = file.replace(regex, ""); - } - } - - mkdirp.sync(path.join(viewsPath, relativePath.split('/').slice(0, -1).join('/'))); - fs.writeFile(path.join(viewsPath, relativePath), file, next); - }, function(err) { - if (err) { - winston.error(err); - } else { - winston.info('[themes] Successfully compiled templates.'); - emitter.emit('templates:compiled'); - } - }); - }); } module.exports = function(app, data) { middleware = require('./middleware')(app); relativePath = nconf.get('relative_path'); - viewsPath = nconf.get('views_dir'); themesPath = nconf.get('themes_path'); - baseTemplatesPath = nconf.get('base_templates_path'); - app.engine('tpl', templates.__express); app.set('view engine', 'tpl'); - app.set('views', viewsPath); + app.set('views', nconf.get('views_dir')); app.set('json spaces', process.env.NODE_ENV === 'development' ? 4 : 0); app.use(flash()); @@ -173,7 +96,7 @@ module.exports = function(app, data) { if(meta.config.cookieDomain) { cookie.domain = meta.config.cookieDomain; } - + app.use(session({ store: db.sessionStore, secret: nconf.get('secret'), @@ -205,10 +128,7 @@ module.exports = function(app, data) { routeCurrentTheme(app, data.currentThemeId, data.themesData); routeThemeScreenshots(app, data.themesData); - plugins.getTemplates(function(err, pluginTemplates) { - compileTemplates(pluginTemplates); - }); - + meta.templates.compile(); return middleware; }; diff --git a/src/middleware/middleware.js b/src/middleware/middleware.js index 831b506f5e..296a445ca4 100644 --- a/src/middleware/middleware.js +++ b/src/middleware/middleware.js @@ -6,7 +6,6 @@ var app, path = require('path'), winston = require('winston'), validator = require('validator'), - fs = require('fs'), nconf = require('nconf'), plugins = require('./../plugins'), meta = require('./../meta'), @@ -209,6 +208,7 @@ middleware.renderHeader = function(req, res, callback) { var uid = req.user ? parseInt(req.user.uid, 10) : 0; var custom_header = { + uid: uid, 'navigation': [] }; diff --git a/src/notifications.js b/src/notifications.js index 818f89f338..f0e36a5d76 100644 --- a/src/notifications.js +++ b/src/notifications.js @@ -24,19 +24,24 @@ var async = require('async'), }; Notifications.get = function(nid, callback) { - db.exists('notifications:' + nid, function(err, exists) { + Notifications.getMultiple([nid], function(err, notifications) { + callback(err, Array.isArray(notifications) && notifications.length ? notifications[0] : null); + }); + }; + + Notifications.getMultiple = function(nids, callback) { + var keys = nids.map(function(nid) { + return 'notifications:' + nid; + }); + + db.getObjects(keys, function(err, notifications) { if (err) { - winston.error('[notifications.get] Could not retrieve nid ' + nid + ': ' + err.message); return callback(err); } - if (!exists) { - return callback(null, null); - } - - db.getObject('notifications:' + nid, function(err, notification) { - if (err) { - return callback(err); + async.map(notifications, function(notification, next) { + if (!notification) { + return next(null, null); } // Backwards compatibility for old notification schema @@ -53,10 +58,10 @@ var async = require('async'), if (notification.from && !notification.image) { User.getUserField(notification.from, 'picture', function(err, picture) { if (err) { - return callback(err); + return next(err); } notification.image = picture; - callback(null, notification); + next(null, notification); }); return; } else if (notification.image) { @@ -66,11 +71,10 @@ var async = require('async'), break; } - return callback(null, notification); + return next(null, notification); } - callback(null, notification); - }); + }, callback); }); }; @@ -124,7 +128,7 @@ var async = require('async'), return callback(err); } - async.each(uids, function(uid, next) { + async.eachLimit(uids, 10, function(uid, next) { if (!parseInt(uid, 10)) { return next(); } @@ -160,6 +164,10 @@ var async = require('async'), }; function shouldPush(uid, newNotifObj, callback) { + if (!newNotifObj) { + return callback(null, false); + } + hasNotification(newNotifObj.uniqueId, uid, function(err, hasNotification) { if (err) { return callback(err); @@ -203,29 +211,24 @@ var async = require('async'), } Notifications.pushGroup = function(nid, groupName, callback) { - if (!callback) { - callback = function() {}; - } - + callback = callback || function() {}; groups.get(groupName, {}, function(err, groupObj) { - if (!err && groupObj) { - if (groupObj.memberCount > 0) { - Notifications.push(nid, groupObj.members, callback); - } - } else { - callback(err); + if (err || !groupObj || !Array.isArray(groupObj.members) || !groupObj.members.length) { + return callback(err); } + + Notifications.push(nid, groupObj.members, callback); }); }; Notifications.markRead = function(nid, uid, callback) { callback = callback || function() {}; - if (!parseInt(uid, 10)) { + if (!parseInt(uid, 10) || !parseInt(nid, 10)) { return callback(); } - Notifications.get(nid, function(err, notificationData) { + db.getObjectFields('notifications:' + nid, ['uniqueId', 'datetime'], function(err, notificationData) { if (err || !notificationData) { return callback(err); } @@ -292,12 +295,22 @@ var async = require('async'), return winston.error(err.message); } - async.filter(nids, function(nid, next) { - db.getObjectField('notifications:' + nid, 'datetime', function(err, datetime) { - next(!err && parseInt(datetime, 10) < cutoffTime); + var keys = nids.map(function(nid) { + return 'notifications:' + nid; + }); + + db.getObjectsFields(keys, ['nid', 'datetime'], function(err, notifs) { + if (err) { + return winston.error(err.message); + } + + var expiredNids = notifs.filter(function(notif) { + return notif && parseInt(notif.datetime, 10) < cutoffTime; + }).map(function(notif) { + return notif.nid; }); - }, function(expiredNids) { - async.each(expiredNids, function(nid, next) { + + async.eachLimit(expiredNids, 50, function(nid, next) { async.parallel([ function(next) { db.setRemove('notifications', nid, next); @@ -310,15 +323,15 @@ var async = require('async'), next(err); }); }, function(err) { - if (!err) { - if (process.env.NODE_ENV === 'development') { - winston.info('[notifications.prune] Notification pruning completed. ' + numPruned + ' expired notification' + (numPruned !== 1 ? 's' : '') + ' removed.'); - } - var diff = process.hrtime(start); - events.log('Pruning notifications took : ' + (diff[0] * 1e3 + diff[1] / 1e6) + ' ms'); - } else { - winston.error('Encountered error pruning notifications: ' + err.message); + if (err) { + return winston.error('Encountered error pruning notifications: ' + err.message); } + + if (process.env.NODE_ENV === 'development') { + winston.info('[notifications.prune] Notification pruning completed. ' + numPruned + ' expired notification' + (numPruned !== 1 ? 's' : '') + ' removed.'); + } + var diff = process.hrtime(start); + events.log('Pruning '+ numPruned + ' notifications took : ' + (diff[0] * 1e3 + diff[1] / 1e6) + ' ms'); }); }); }); diff --git a/src/password.js b/src/password.js new file mode 100644 index 0000000000..c4a7febca8 --- /dev/null +++ b/src/password.js @@ -0,0 +1,51 @@ + + +'use strict'; +var fork = require('child_process').fork; + +(function(module) { + + var child = fork('./bcrypt', process.argv.slice(2), { + env: process.env + }); + + var callbacks = { + 'hash': {}, + 'compare': {} + }; + + module.hash = function(rounds, password, callback) { + sendCommand({type: 'hash', password: password, rounds: rounds}, callback); + }; + + module.compare = function(password, hash, callback) { + sendCommand({type: 'compare', password: password, hash: hash}, callback); + }; + + function sendCommand(data, callback) { + callbacks[data.type][data.password] = callbacks[data.type][data.password] || []; + callbacks[data.type][data.password].push(callback); + child.send(data); + } + + child.on('message', function(msg) { + var cbs = callbacks[msg.type] ? callbacks[msg.type][msg.password] : null; + + if (Array.isArray(cbs)) { + if (msg.err) { + var err = new Error(msg.err.message); + cbs.forEach(function(callback) { + callback(err); + }); + cbs.length = 0; + return; + } + + cbs.forEach(function(callback) { + callback(null, msg.type === 'hash' ? msg.hash : msg.result); + }); + cbs.length = 0; + } + }); + +}(exports)); diff --git a/src/plugins.js b/src/plugins.js index 5ca41bd353..427816c7bc 100644 --- a/src/plugins.js +++ b/src/plugins.js @@ -6,13 +6,18 @@ var fs = require('fs'), winston = require('winston'), nconf = require('nconf'), semver = require('semver'), + express = require('express'), db = require('./database'), emitter = require('./emitter'), meta = require('./meta'), translator = require('../public/src/translator'), utils = require('../public/src/utils'), - pkg = require('../package.json'); + hotswap = require('./hotswap'), + pkg = require('../package.json'), + + controllers = require('./controllers'), + app, middleware; (function(Plugins) { @@ -57,6 +62,12 @@ var fs = require('fs'), }); }; + Plugins.prepareApp = function(nbbApp, nbbMiddleware) { + app = nbbApp; + middleware = nbbMiddleware; + hotswap.prepare(nbbApp); + }; + Plugins.ready = function(callback) { if (!Plugins.initialized) { emitter.once('plugins:loaded', callback); @@ -107,10 +118,32 @@ var fs = require('fs'), }); next(); - } + }, + async.apply(Plugins.reloadRoutes) ], callback); }; + Plugins.reloadRoutes = function(callback) { + if (!app || !middleware || !controllers) { + return; + } else { + var router = express.Router(); + router.hotswapId = 'plugins'; + router.render = function() { + app.render.apply(app, arguments); + }; + + // Deprecated as of v0.5.0, remove this hook call for NodeBB v0.6.0-1 + Plugins.fireHook('action:app.load', router, middleware, controllers); + + Plugins.fireHook('static:app.load', router, middleware, controllers, function() { + hotswap.replace('plugins', router); + winston.info('[plugins] All plugins reloaded and rerouted'); + callback(); + }); + } + }; + Plugins.loadPlugin = function(pluginPath, callback) { fs.readFile(path.join(pluginPath, 'plugin.json'), function(err, data) { if (err) { @@ -184,29 +217,31 @@ var fs = require('fs'), Plugins.staticDirs[pluginData.id] = path.join(pluginPath, pluginData.staticDir); } - for(var key in pluginData.staticDirs) { - (function(mappedPath) { - if (pluginData.staticDirs.hasOwnProperty(mappedPath)) { - if (Plugins.staticDirs[mappedPath]) { - winston.warn('[plugins/' + pluginData.id + '] Mapped path (' + mappedPath + ') already specified!'); - } else if (!validMappedPath.test(mappedPath)) { - winston.warn('[plugins/' + pluginData.id + '] Invalid mapped path specified: ' + mappedPath + '. Path must adhere to: ' + validMappedPath.toString()); - } else { - realPath = pluginData.staticDirs[mappedPath]; - staticDir = path.join(pluginPath, realPath); + function mapStaticDirs(mappedPath) { + if (Plugins.staticDirs[mappedPath]) { + winston.warn('[plugins/' + pluginData.id + '] Mapped path (' + mappedPath + ') already specified!'); + } else if (!validMappedPath.test(mappedPath)) { + winston.warn('[plugins/' + pluginData.id + '] Invalid mapped path specified: ' + mappedPath + '. Path must adhere to: ' + validMappedPath.toString()); + } else { + realPath = pluginData.staticDirs[mappedPath]; + staticDir = path.join(pluginPath, realPath); - (function(staticDir) { - fs.exists(staticDir, function(exists) { - if (exists) { - Plugins.staticDirs[pluginData.id + '/' + mappedPath] = staticDir; - } else { - winston.warn('[plugins/' + pluginData.id + '] Mapped path \'' + mappedPath + ' => ' + staticDir + '\' not found.'); - } - }); - }(staticDir)); - } - } - }(key)); + (function(staticDir) { + fs.exists(staticDir, function(exists) { + if (exists) { + Plugins.staticDirs[pluginData.id + '/' + mappedPath] = staticDir; + } else { + winston.warn('[plugins/' + pluginData.id + '] Mapped path \'' + mappedPath + ' => ' + staticDir + '\' not found.'); + } + }); + }(staticDir)); + } + } + + for(var key in pluginData.staticDirs) { + if (pluginData.staticDirs.hasOwnProperty(key)) { + mapStaticDirs(key); + } } next(); @@ -262,8 +297,10 @@ var fs = require('fs'), async.each(languages, function(pathToLang, next) { fs.readFile(pathToLang, function(err, file) { + var json; + try { - var json = JSON.parse(file.toString()); + json = JSON.parse(file.toString()); } catch (err) { winston.error('[plugins] Unable to parse custom language file: ' + pathToLang + '\r\n' + err.stack); return next(err); @@ -370,7 +407,7 @@ var fs = require('fs'), // omg, after 6 months I finally realised what this does... // It adds the callback to the arguments passed-in, since the callback // is defined in *this* file (the async cb), and not the hooks themselves. - var value = hookObj.method.apply(Plugins, value.concat(function() { + value = hookObj.method.apply(Plugins, value.concat(function() { next(arguments[0], Array.prototype.slice.call(arguments, 1)); })); @@ -493,7 +530,6 @@ var fs = require('fs'), // Reload meta data Plugins.reload(function() { - if(!active) { Plugins.fireHook('action:plugin.activate', id); } @@ -587,6 +623,7 @@ var fs = require('fs'), plugins[i].id = plugins[i].name; plugins[i].installed = false; plugins[i].active = false; + plugins[i].url = plugins[i].repository ? plugins[i].repository.url : ''; pluginMap[plugins[i].name] = plugins[i]; } @@ -601,7 +638,7 @@ var fs = require('fs'), pluginMap[plugin.id].id = pluginMap[plugin.id].id || plugin.id; pluginMap[plugin.id].name = pluginMap[plugin.id].name || plugin.id; pluginMap[plugin.id].description = plugin.description; - pluginMap[plugin.id].url = plugin.url; + pluginMap[plugin.id].url = pluginMap[plugin.id].url || plugin.url; pluginMap[plugin.id].installed = true; Plugins.isActive(plugin.id, function(err, active) { @@ -685,8 +722,10 @@ var fs = require('fs'), fs.readFile(path.join(file, 'plugin.json'), next); }, function(configJSON, next) { + var config; + try { - var config = JSON.parse(configJSON); + config = JSON.parse(configJSON); } catch (err) { winston.warn("Plugin: " + file + " is corrupted or invalid. Please check plugin.json for errors."); return next(err, null); diff --git a/src/postTools.js b/src/postTools.js index 97d3e71928..a162561740 100644 --- a/src/postTools.js +++ b/src/postTools.js @@ -54,33 +54,44 @@ var winston = require('winston'), if (err) { return next(err); } + options.tags = options.tags || []; - if (isMainPost) { - title = title.trim(); - var topicData = { - title: title, - slug: tid + '/' + utils.slugify(title) - }; - if (options.topic_thumb) { - topicData.thumb = options.topic_thumb; - } - - db.setObject('topic:' + tid, topicData, function(err) { - plugins.fireHook('action:topic.edit', tid); + if (!isMainPost) { + return next(null, { + tid: tid, + isMainPost: false }); - - topics.updateTags(tid, options.tags); } - next(null, { - tid: tid, - title: validator.escape(title), - isMainPost: isMainPost, - tags: options.tags.map(function(tag) { return {name:tag}; }) + title = title.trim(); + + var topicData = { + title: title, + slug: tid + '/' + utils.slugify(title) + }; + if (options.topic_thumb) { + topicData.thumb = options.topic_thumb; + } + + db.setObject('topic:' + tid, topicData, function(err) { + plugins.fireHook('action:topic.edit', tid); + }); + + topics.updateTags(tid, options.tags, function(err) { + if (err) { + return next(err); + } + topics.getTopicTagsObjects(tid, function(err, tags) { + next(err, { + tid: tid, + title: validator.escape(title), + isMainPost: isMainPost, + tags: tags + }); + }); }); }); - }, content: function(next) { PostTools.parse(postData.content, next); diff --git a/src/posts.js b/src/posts.js index df218c14c8..aa2345db0c 100644 --- a/src/posts.js +++ b/src/posts.js @@ -93,7 +93,7 @@ var async = require('async'), }; Posts.getPostsByTid = function(tid, set, start, end, reverse, callback) { - db[reverse ? 'getSortedSetRevRange' : 'getSortedSetRange'](set, start, end, function(err, pids) { + Posts.getPidsFromSet(set, start, end, reverse, function(err, pids) { if(err) { return callback(err); } @@ -102,30 +102,14 @@ var async = require('async'), return callback(null, []); } - Posts.getPostsByPids(pids, function(err, posts) { - if(err) { - return callback(err); - } - - if(!Array.isArray(posts) || !posts.length) { - return callback(null, []); - } - - plugins.fireHook('filter:post.getPosts', {tid: tid, posts: posts}, function(err, data) { - if(err) { - return callback(err); - } - - if(!data || !Array.isArray(data.posts)) { - return callback(null, []); - } - - callback(null, data.posts); - }); - }); + Posts.getPostsByPids(pids, callback); }); }; + Posts.getPidsFromSet = function(set, start, end, reverse, callback) { + db[reverse ? 'getSortedSetRevRange' : 'getSortedSetRange'](set, start, end, callback); + }; + Posts.getPostsByPids = function(pids, callback) { var keys = []; @@ -155,7 +139,23 @@ var async = require('async'), next(null, postData); }); - }, callback); + }, function(err, posts) { + if (err) { + return callback(err); + } + + plugins.fireHook('filter:post.getPosts', {posts: posts}, function(err, data) { + if (err) { + return callback(err); + } + + if (!data || !Array.isArray(data.posts)) { + return callback(null, []); + } + + callback(null, data.posts); + }); + }); }); }; @@ -186,7 +186,7 @@ var async = require('async'), if (err) { return callback(err); } - Posts.getPostSummaryByPids(pids, {stripTags: true}, callback); + Posts.getPostSummaryByPids(pids, uid, {stripTags: true}, callback); }); }); }; @@ -222,21 +222,31 @@ var async = require('async'), }; Posts.getUserInfoForPosts = function(uids, callback) { - user.getMultipleUserFields(uids, ['uid', 'username', 'userslug', 'reputation', 'postcount', 'picture', 'signature', 'banned'], function(err, userData) { + async.parallel({ + groups: function(next) { + groups.getUserGroups(uids, next); + }, + userData: function(next) { + user.getMultipleUserFields(uids, ['uid', 'username', 'userslug', 'reputation', 'postcount', 'picture', 'signature', 'banned'], next); + } + }, function(err, results) { if (err) { return callback(err); } + var userData = results.userData; + for(var i=0; i parseInt(meta.config.maximumTitleLength, 10)) { + return callback(new Error('[[error:title-too-long, ' + meta.config.maximumTitleLength + ']]')); } else if (!data.content || data.content.length < parseInt(meta.config.minimumPostLength, 10)) { return callback(new Error('[[error:content-too-short, ' + meta.config.minimumPostLength + ']]')); } @@ -245,46 +247,23 @@ SocketPosts.getPrivileges = function(socket, pid, callback) { }; SocketPosts.getFavouritedUsers = function(socket, pid, callback) { - favourites.getFavouritedUidsByPids([pid], function(err, data) { - - if(err) { + if (err || !Array.isArray(data) || !data.length) { return callback(err); } - if(!Array.isArray(data) || !data.length) { - callback(null, ""); - } - - var max = 5; //hardcoded - var finalText = ""; - - var pid_uids = data[0]; - var rest_amount = 0; - - if (pid_uids.length > max) { - rest_amount = pid_uids.length - max; - pid_uids = pid_uids.slice(0, max); - } - - user.getUsernamesByUids(pid_uids, function(err, usernames) { - if(err) { - return callback(err); - } - - finalText = usernames.join(', ') + (rest_amount > 0 ? - (" and " + rest_amount + (rest_amount > 1 ? " others" : " other")) : ""); - callback(null, finalText); - }); + user.getUsernamesByUids(data[0], callback); }); }; -SocketPosts.getPidPage = function(socket, pid, callback) { - posts.getPidPage(pid, socket.uid, callback); -}; +SocketPosts.getUpvoters = function(socket, pid, callback) { + favourites.getUpvotedUidsByPids([pid], function(err, data) { + if (err || !Array.isArray(data) || !data.length) { + return callback(err, []); + } -SocketPosts.getPidIndex = function(socket, pid, callback) { - posts.getPidIndex(pid, callback); + user.getUsernamesByUids(data[0], callback); + }); }; SocketPosts.flag = function(socket, pid, callback) { @@ -293,7 +272,6 @@ SocketPosts.flag = function(socket, pid, callback) { } var message = '', - path = '', post; async.waterfall([ @@ -312,21 +290,13 @@ SocketPosts.flag = function(socket, pid, callback) { }, function(postData, next) { post = postData; - topics.getTopicField(postData.tid, 'slug', next); - }, - function(topicSlug, next) { - path = nconf.get('relative_path') + '/topic/' + topicSlug; - posts.getPidIndex(pid, next); - }, - function(postIndex, next) { - path += '/' + postIndex; groups.get('administrators', {}, next); }, function(adminGroup, next) { notifications.create({ bodyShort: message, bodyLong: post.content, - path: path, + pid: pid, uniqueId: 'post_flag:' + pid, from: socket.uid }, function(err, nid) { diff --git a/src/socket.io/topics.js b/src/socket.io/topics.js index c00745d2ae..510b7fb23b 100644 --- a/src/socket.io/topics.js +++ b/src/socket.io/topics.js @@ -1,17 +1,20 @@ 'use strict'; -var topics = require('../topics'), +var nconf = require('nconf'), + async = require('async'), + + topics = require('../topics'), categories = require('../categories'), privileges = require('../privileges'), + notifications = require('../notifications'), threadTools = require('../threadTools'), websockets = require('./index'), user = require('../user'), - db = require('./../database'), - meta = require('./../meta'), + db = require('../database'), + meta = require('../meta'), utils = require('../../public/src/utils'), - - async = require('async'), + SocketPosts = require('./posts'), SocketTopics = {}; @@ -54,12 +57,17 @@ SocketTopics.post = function(socket, data, callback) { }); }; -SocketTopics.postcount = function(socket, tid, callback) { - topics.getTopicField(tid, 'postcount', callback); +SocketTopics.enter = function(socket, tid, callback) { + if (!tid || !socket.uid) { + return; + } + SocketTopics.markAsRead(socket, tid); + topics.markTopicNotificationsRead(tid, socket.uid); + topics.increaseViewCount(tid); }; -SocketTopics.lastPostIndex = function(socket, tid, callback) { - db.sortedSetCard('tid:' + tid + ':posts', callback); +SocketTopics.postcount = function(socket, tid, callback) { + topics.getTopicField(tid, 'postcount', callback); }; SocketTopics.increaseViewCount = function(socket, tid) { @@ -73,12 +81,10 @@ SocketTopics.markAsRead = function(socket, tid) { topics.markAsRead(tid, socket.uid, function(err) { topics.pushUnreadCount(socket.uid); - topics.markTopicNotificationsRead(tid, socket.uid); }); }; SocketTopics.markTidsRead = function(socket, tids, callback) { - if (!Array.isArray(tids)) { return callback(new Error('[[error:invalid-data]]')); } @@ -98,6 +104,13 @@ SocketTopics.markTidsRead = function(socket, tids, callback) { }); }; +SocketTopics.markTopicNotificationsRead = function(socket, tid, callback) { + if(!tid || !socket.uid) { + return callback(new Error('[[error:invalid-data]]')); + } + topics.markTopicNotificationsRead(tid, socket.uid); +}; + SocketTopics.markAllRead = function(socket, data, callback) { topics.getUnreadTids(socket.uid, 0, -1, function(err, tids) { if (err) { @@ -136,25 +149,58 @@ SocketTopics.markCategoryTopicsRead = function(socket, cid, callback) { }; SocketTopics.markAsUnreadForAll = function(socket, tids, callback) { - if(!Array.isArray(tids)) { + if (!Array.isArray(tids)) { return callback(new Error('[[error:invalid-tid]]')); } - async.each(tids, function(tid, next) { - topics.markAsUnreadForAll(tid, function(err) { - if(err) { - return next(err); - } + if (!socket.uid) { + return callback(new Error('[[error:no-privileges]]')); + } - db.sortedSetAdd('topics:recent', Date.now(), tid, function(err) { - if(err) { + user.isAdministrator(socket.uid, function(err, isAdmin) { + if (err) { + return callback(err); + } + + async.each(tids, function(tid, next) { + async.waterfall([ + function(next) { + threadTools.exists(tid, next); + }, + function(exists, next) { + if (!exists) { + return next(new Error('[[error:invalid-tid]]')); + } + topics.getTopicField(tid, 'cid', next); + }, + function(cid, next) { + user.isModerator(socket.uid, cid, next); + } + ], function(err, isMod) { + if (err) { return next(err); } - topics.pushUnreadCount(); - next(); + + if (!isAdmin && !isMod) { + return next(new Error('[[error:no-privileges]]')); + } + + topics.markAsUnreadForAll(tid, function(err) { + if(err) { + return next(err); + } + + db.sortedSetAdd('topics:recent', Date.now(), tid, function(err) { + if(err) { + return next(err); + } + topics.pushUnreadCount(); + next(); + }); + }); }); - }); - }, callback); + }, callback); + }); }; SocketTopics.delete = function(socket, data, callback) { @@ -243,7 +289,14 @@ SocketTopics.movePost = function(socket, data, callback) { return callback(err || new Error('[[error:no-privileges]]')); } - topics.movePostToTopic(data.pid, data.tid, callback); + topics.movePostToTopic(data.pid, data.tid, function(err) { + if (err) { + return callback(err); + } + + SocketPosts.sendNotificationToPostOwner(data.pid, socket.uid, 'notifications:moved_your_post'); + callback(); + }); }); }; @@ -252,7 +305,7 @@ SocketTopics.move = function(socket, data, callback) { return callback(new Error('[[error:invalid-data]]')); } - async.each(data.tids, function(tid, next) { + async.eachLimit(data.tids, 10, function(tid, next) { var oldCid; async.waterfall([ function(next) { @@ -284,11 +337,41 @@ SocketTopics.move = function(socket, data, callback) { tid: tid }); + SocketTopics.sendNotificationToTopicOwner(tid, socket.uid, 'notifications:moved_your_topic'); + next(); }); }, callback); }; + +SocketTopics.sendNotificationToTopicOwner = function(tid, fromuid, notification) { + if(!tid || !fromuid) { + return; + } + + async.parallel({ + username: async.apply(user.getUserField, fromuid, 'username'), + topicData: async.apply(topics.getTopicFields, tid, ['uid', 'slug']), + }, function(err, results) { + if (err || fromuid === parseInt(results.topicData.uid, 10)) { + return; + } + + notifications.create({ + bodyShort: '[[' + notification + ', ' + results.username + ']]', + path: nconf.get('relative_path') + '/topic/' + results.topicData.slug, + uniqueId: 'topic:' + tid + ':uid:' + fromuid, + from: fromuid + }, function(err, nid) { + if (!err) { + notifications.push(nid, [results.topicData.uid]); + } + }); + }); +}; + + SocketTopics.moveAll = function(socket, data, callback) { if(!data || !data.cid || !data.currentCid) { return callback(new Error('[[error:invalid-data]]')); @@ -304,7 +387,7 @@ SocketTopics.moveAll = function(socket, data, callback) { return callback(err); } - async.each(tids, function(tid, next) { + async.eachLimit(tids, 10, function(tid, next) { threadTools.move(tid, data.cid, socket.uid, next); }, callback); }); @@ -328,30 +411,48 @@ SocketTopics.loadMore = function(socket, data, callback) { return callback(new Error('[[error:invalid-data]]')); } - user.getSettings(socket.uid, function(err, settings) { - if(err) { + async.parallel({ + settings: function(next) { + user.getSettings(socket.uid, next); + }, + privileges: function(next) { + privileges.topics.get(data.tid, socket.uid, next); + }, + postCount: function(next) { + topics.getPostCount(data.tid, next); + } + }, function(err, results) { + if (err) { return callback(err); } - var start = Math.max(parseInt(data.after, 10) - 1, 0), - end = start + settings.postsPerPage - 1; + if (!results.privileges.read) { + return callback(new Error('[[error:no-privileges]]')); + } var set = 'tid:' + data.tid + ':posts', - reverse = false; + reverse = false, + start = Math.max(parseInt(data.after, 10) - 1, 0); - if (settings.topicPostSort === 'newest_to_oldest') { + if (results.settings.topicPostSort === 'newest_to_oldest') { reverse = true; - } else if (settings.topicPostSort === 'most_votes') { + data.after = results.postCount - data.after; + start = Math.max(parseInt(data.after, 10), 0); + } else if (results.settings.topicPostSort === 'most_votes') { reverse = true; + data.after = results.postCount - data.after; + start = Math.max(parseInt(data.after, 10), 0); set = 'tid:' + data.tid + ':posts:votes'; } + var end = start + results.settings.postsPerPage - 1; + async.parallel({ posts: function(next) { topics.getTopicPosts(data.tid, set, start, end, socket.uid, reverse, next); }, privileges: function(next) { - privileges.topics.get(data.tid, socket.uid, next); + next(null, results.privileges); }, 'reputation:disabled': function(next) { next(null, parseInt(meta.config['reputation:disabled'], 10) === 1); diff --git a/src/socket.io/user.js b/src/socket.io/user.js index 41bba06ba6..d1e743d901 100644 --- a/src/socket.io/user.js +++ b/src/socket.io/user.js @@ -1,10 +1,13 @@ 'use strict'; var async = require('async'), + nconf = require('nconf'), user = require('../user'), groups = require('../groups'), topics = require('../topics'), + notifications = require('../notifications'), messaging = require('../messaging'), + plugins = require('../plugins'), utils = require('./../../public/src/utils'), meta = require('../meta'), SocketUser = {}; @@ -15,6 +18,12 @@ SocketUser.exists = function(socket, data, callback) { } }; +SocketUser.deleteAccount = function(socket, data, callback) { + if (socket.uid) { + user.deleteAccount(socket.uid, callback); + } +}; + SocketUser.count = function(socket, data, callback) { user.count(callback); }; @@ -79,7 +88,9 @@ SocketUser.reset.commit = function(socket, data, callback) { }; SocketUser.isOnline = function(socket, uid, callback) { - user.isOnline(uid, callback); + user.isOnline([uid], function(err, data) { + callback(err, Array.isArray(data) ? data[0] : null); + }); }; SocketUser.changePassword = function(socket, data, callback) { @@ -159,16 +170,52 @@ SocketUser.changePicture = function(socket, data, callback) { SocketUser.follow = function(socket, data, callback) { if (socket.uid && data) { - user.follow(socket.uid, data.uid, callback); + toggleFollow('follow', socket.uid, data.uid, function(err) { + if (err) { + return callback(err); + } + + user.getUserFields(socket.uid, ['username', 'userslug'], function(err, userData) { + if (err) { + return callback(err); + } + + notifications.create({ + bodyShort: '[[notifications:user_started_following_you, ' + userData.username + ']]', + path: nconf.get('relative_path') + '/user/' + userData.userslug, + uniqueId: 'follow:uid:' + socket.uid, + from: socket.uid + }, function(err, nid) { + if (!err) { + notifications.push(nid, [data.uid]); + } + callback(err); + }); + }); + }); } }; SocketUser.unfollow = function(socket, data, callback) { if (socket.uid && data) { - user.unfollow(socket.uid, data.uid, callback); + toggleFollow('unfollow', socket.uid, data.uid, callback); } }; +function toggleFollow(method, uid, theiruid, callback) { + user[method](uid, theiruid, function(err) { + if (err) { + return callback(err); + } + + plugins.fireHook('action:user.' + method, { + fromUid: uid, + toUid: theiruid + }); + callback(); + }); +} + SocketUser.getSettings = function(socket, data, callback) { if (socket.uid) { if (socket.uid === parseInt(data.uid, 10)) { @@ -217,24 +264,23 @@ SocketUser.setTopicSort = function(socket, sort, callback) { } }; -SocketUser.getOnlineUsers = function(socket, data, callback) { +SocketUser.getOnlineUsers = function(socket, uids, callback) { var returnData = {}; - if(!data) { + if (!uids) { return callback(new Error('[[error:invalid-data]]')); } - function getUserStatus(uid, next) { - SocketUser.isOnline(socket, uid, function(err, data) { - if(err) { - return next(err); - } - returnData[uid] = data; - next(); - }); - } + user.isOnline(uids, function(err, userData) { + if (err) { + return callback(err); + } - async.each(data, getUserStatus, function(err) { - callback(err, returnData); + userData.forEach(function(user) { + if (user) { + returnData[user.uid] = user; + } + }); + callback(null, returnData); }); }; diff --git a/src/threadTools.js b/src/threadTools.js index 5e52d043c7..edd478c26a 100644 --- a/src/threadTools.js +++ b/src/threadTools.js @@ -120,8 +120,7 @@ var winston = require('winston'), plugins.fireHook('action:topic.lock', { tid: tid, isLocked: lock, - uid: uid, - timestamp: Date.now() + uid: uid }); emitTo('topic_' + tid); @@ -165,8 +164,7 @@ var winston = require('winston'), plugins.fireHook('action:topic.pin', { tid: tid, isPinned: pin, - uid: uid, - timestamp: Date.now() + uid: uid }); emitTo('topic_' + tid); @@ -214,8 +212,7 @@ var winston = require('winston'), tid: tid, fromCid: oldCid, toCid: cid, - uid: uid, - timestamp: Date.now() + uid: uid }); }); }; @@ -240,4 +237,19 @@ var winston = require('winston'), ], callback); }; + ThreadTools.follow = function(tid, uid, callback) { + callback = callback || function() {}; + async.waterfall([ + function (next) { + ThreadTools.exists(tid, next); + }, + function (exists, next) { + if (!exists) { + return next(new Error('[[error:no-topic]]')); + } + db.setAdd('tid:' + tid + ':followers', uid, next); + } + ], callback); + }; + }(exports)); diff --git a/src/topics.js b/src/topics.js index a497764ad3..b10709eb47 100644 --- a/src/topics.js +++ b/src/topics.js @@ -1,12 +1,12 @@ "use strict"; var async = require('async'), - gravatar = require('gravatar'), validator = require('validator'), + _ = require('underscore'), db = require('./database'), posts = require('./posts'), - utils = require('./../public/src/utils'), + utils = require('../public/src/utils'), plugins = require('./plugins'), user = require('./user'), categories = require('./categories'), @@ -26,11 +26,7 @@ var async = require('async'), Topics.getTopicData = function(tid, callback) { Topics.getTopicsData([tid], function(err, topics) { - if (err) { - return callback(err); - } - - callback(null, topics ? topics[0] : null); + callback(err, Array.isArray(topics) && topics.length ? topics[0] : null); }); }; @@ -128,7 +124,7 @@ var async = require('async'), nextStart: 0 }; - if (!tids || !tids.length) { + if (!Array.isArray(tids) || !tids.length) { return callback(null, returnTopics); } @@ -170,96 +166,75 @@ var async = require('async'), }; Topics.getTopicsByTids = function(tids, uid, callback) { - if (!Array.isArray(tids) || tids.length === 0) { + if (!Array.isArray(tids) || !tids.length) { return callback(null, []); } - var categoryCache = {}, - privilegeCache = {}, - userCache = {}; - - - function loadTopicInfo(topicData, next) { - if (!topicData) { - return next(null, null); - } - - function isTopicVisible(topicData, topicInfo) { - if (parseInt(topicInfo.categoryData.disabled, 10) === 1) { - return false; - } - var deleted = parseInt(topicData.deleted, 10) !== 0; - return !deleted || (deleted && topicInfo.privileges.view_deleted) || parseInt(topicData.uid, 10) === parseInt(uid, 10); - } - - async.parallel({ - hasread: function(next) { - Topics.hasReadTopic(topicData.tid, uid, next); - }, - teaser: function(next) { - Topics.getTeaser(topicData.tid, next); - }, - privileges: function(next) { - if (privilegeCache[topicData.cid]) { - return next(null, privilegeCache[topicData.cid]); - } - privileges.categories.get(topicData.cid, uid, next); - }, - categoryData: function(next) { - if (categoryCache[topicData.cid]) { - return next(null, categoryCache[topicData.cid]); - } - categories.getCategoryFields(topicData.cid, ['name', 'slug', 'icon', 'bgColor', 'color', 'disabled'], next); - }, - user: function(next) { - if (userCache[topicData.uid]) { - return next(null, userCache[topicData.uid]); - } - user.getUserFields(topicData.uid, ['username', 'userslug', 'picture'], next); - }, - tags: function(next) { - Topics.getTopicTagsObjects(topicData.tid, next); - } - }, function(err, topicInfo) { - if(err) { - return next(err); - } - - privilegeCache[topicData.cid] = topicInfo.privileges; - categoryCache[topicData.cid] = topicInfo.categoryData; - userCache[topicData.uid] = topicInfo.user; - - if (!isTopicVisible(topicData, topicInfo)) { - return next(null, null); - } - - topicData.pinned = parseInt(topicData.pinned, 10) === 1; - topicData.locked = parseInt(topicData.locked, 10) === 1; - topicData.deleted = parseInt(topicData.deleted, 10) === 1; - topicData.unread = !(topicInfo.hasread && parseInt(uid, 10) !== 0); - topicData.unreplied = parseInt(topicData.postcount, 10) <= 1; - - topicData.category = topicInfo.categoryData; - topicData.teaser = topicInfo.teaser; - topicData.user = topicInfo.user; - topicData.tags = topicInfo.tags; - - next(null, topicData); - }); - } - Topics.getTopicsData(tids, function(err, topics) { + function mapFilter(array, field) { + return array.map(function(topic) { + return topic[field]; + }).filter(function(value, index, array) { + return array.indexOf(value) === index; + }); + } + if (err) { return callback(err); } - async.mapSeries(topics, loadTopicInfo, function(err, topics) { - if(err) { + var uids = mapFilter(topics, 'uid'); + var cids = mapFilter(topics, 'cid'); + + async.parallel({ + users: function(next) { + user.getMultipleUserFields(uids, ['uid', 'username', 'userslug', 'picture'], next); + }, + categories: function(next) { + categories.getMultipleCategoryFields(cids, ['cid', 'name', 'slug', 'icon', 'bgColor', 'color', 'disabled'], next); + }, + hasRead: function(next) { + Topics.hasReadTopics(tids, uid, next); + }, + isAdminOrMod: function(next) { + privileges.categories.isAdminOrMod(cids, uid, next); + }, + teasers: function(next) { + Topics.getTeasers(tids, uid, next); + }, + tags: function(next) { + Topics.getTopicsTagsObjects(tids, next); + } + }, function(err, results) { + if (err) { return callback(err); } + var users = _.object(uids, results.users); + var categories = _.object(cids, results.categories); + var isAdminOrMod = {}; + cids.forEach(function(cid, index) { + isAdminOrMod[cid] = results.isAdminOrMod[index]; + }); + + for (var i=0; i'"~()?\|]/g, ''); tag = tag.substr(0, meta.config.maximumTagLength || 15); var matches = tag.match(/^[.-]*(.+?)[.-]*$/); @@ -57,6 +57,10 @@ module.exports = function(Topics) { return tag; }; + Topics.updateTag = function(tag, data, callback) { + db.setObject('tag:' + tag, data, callback); + }; + function updateTagCount(tag, callback) { callback = callback || function() {}; Topics.getTagTopicCount(tag, function(err, count) { @@ -80,38 +84,101 @@ module.exports = function(Topics) { }; Topics.getTags = function(start, end, callback) { - db.getSortedSetRevRangeWithScores('tags:topic:count', start, end, callback); + db.getSortedSetRevRangeWithScores('tags:topic:count', start, end, function(err, tags) { + if (err) { + return callback(err); + } + + addTagData(tags, callback); + }); }; + function addTagData(tags, callback) { + var keys = tags.map(function(tag) { + return 'tag:' + tag.value; + }); + + db.getObjects(keys, function(err, tagData) { + if (err) { + return callback(err); + } + + tags.forEach(function(tag, index) { + tag.color = tagData[index] ? tagData[index].color : ''; + tag.bgColor = tagData[index] ? tagData[index].bgColor : ''; + }); + callback(null, tags); + }); + } + Topics.getTopicTags = function(tid, callback) { db.getSetMembers('topic:' + tid + ':tags', callback); }; Topics.getTopicTagsObjects = function(tid, callback) { - Topics.getTopicTags(tid, function(err, tags) { - callback(err, mapToObject(tags)); + Topics.getTopicsTagsObjects([tid], function(err, data) { + callback(err, Array.isArray(data) && data.length ? data[0] : []); }); }; - function mapToObject(tags) { - if (!tags) { - return tags; - } - return tags.map(function(tag) { - return {name: tag}; + Topics.getTopicsTagsObjects = function(tids, callback) { + var sets = tids.map(function(tid) { + return 'topic:' + tid + ':tags'; }); - } - Topics.updateTags = function(tid, tags) { + db.getSetsMembers(sets, function(err, topicTags) { + if (err) { + return callback(err); + } + + var uniqueTopicTags = _.uniq(_.flatten(topicTags)); + + var tags = uniqueTopicTags.map(function(tag) { + return {value: tag}; + }); + + async.parallel({ + tagData: function(next) { + addTagData(tags, next); + }, + counts: function(next) { + db.sortedSetScores('tags:topic:count', uniqueTopicTags, next); + } + }, function(err, results) { + if (err) { + return callback(err); + } + + results.tagData.forEach(function(tag, index) { + tag.score = results.counts[index] ? results.counts[index] : 0; + }); + + var tagData = _.object(uniqueTopicTags, results.tagData); + + topicTags.forEach(function(tags, index) { + if (Array.isArray(tags)) { + topicTags[index] = tags.map(function(tag) {return tagData[tag];}); + } + }); + + callback(null, topicTags); + }); + }); + }; + + Topics.updateTags = function(tid, tags, callback) { + callback = callback || function() {}; Topics.getTopicField(tid, 'timestamp', function(err, timestamp) { if (err) { - return winston.error(err.message); + return callback(err); } Topics.deleteTopicTags(tid, function(err) { - if (!err) { - Topics.createTags(tags, tid, timestamp); + if (err) { + return callback(err); } + + Topics.createTags(tags, tid, timestamp, callback); }); }); }; @@ -147,19 +214,8 @@ module.exports = function(Topics) { return callback(null, []); } - if (plugins.hasListeners('filter:tags.category')) { - plugins.fireHook('filter:tags.category', {tags: [], cid: data.cid}, function(err, result) { - if (data.query.length === 1) { - callback(err, result.tags); - } else { - doSearch(err, result ? result.tags : null); - } - }); - } else { - db.getSortedSetRevRange('tags:topic:count', 0, -1, doSearch); - } - function doSearch(err, tags) { + db.getSortedSetRevRange('tags:topic:count', 0, -1, function(err, tags) { if (err) { return callback(null, []); } @@ -177,7 +233,7 @@ module.exports = function(Topics) { }); callback(null, matches); - } + }); }; }; \ No newline at end of file diff --git a/src/topics/unread.js b/src/topics/unread.js index d33603108a..fce061cd07 100644 --- a/src/topics/unread.js +++ b/src/topics/unread.js @@ -4,10 +4,11 @@ var async = require('async'), winston = require('winston'), - db = require('./../database'), - user = require('./../user'), - notifications = require('./../notifications'), - categories = require('./../categories'), + db = require('../database'), + user = require('../user'), + meta = require('../meta'), + notifications = require('../notifications'), + categories = require('../categories'), privileges = require('../privileges'); module.exports = function(Topics) { @@ -23,48 +24,53 @@ module.exports = function(Topics) { done = false; uid = parseInt(uid, 10); - if(uid === 0) { + if (uid === 0) { return callback(null, unreadTids); } + var count = 0; + if (stop === -1) { + count = Infinity; + } else { + count = stop - start + 1; + } async.whilst(function() { - return unreadTids.length < 21 && !done; - }, function(callback) { + return unreadTids.length < count && !done; + }, function(next) { Topics.getLatestTids(start, stop, 'month', function(err, tids) { if (err) { - return callback(err); + return next(err); } if (tids && !tids.length) { done = true; - return callback(); + return next(); } Topics.hasReadTopics(tids, uid, function(err, read) { - if(err) { - return callback(err); + if (err) { + return next(err); } - var newtids = tids.filter(function(tid, index, self) { + var newtids = tids.filter(function(tid, index) { return !read[index]; }); - privileges.topics.filter('read', newtids, uid, function(err, newTids) { - if(err) { - return callback(err); + privileges.topics.filter('read', newtids, uid, function(err, newtids) { + if (err) { + return next(err); } - unreadTids.push.apply(unreadTids, newtids); start = stop + 1; stop = start + 19; - callback(); + next(); }); }); }); }, function(err) { - callback(err, unreadTids); + callback(err, unreadTids.slice(0, count)); }); }; @@ -165,7 +171,6 @@ module.exports = function(Topics) { }; Topics.markAsRead = function(tid, uid, callback) { - db.setAdd('tid:' + tid + ':read_by_uid', uid, function(err) { if (err) { return callback(err); diff --git a/src/user.js b/src/user.js index f20686fc7c..7abee910cb 100644 --- a/src/user.js +++ b/src/user.js @@ -1,6 +1,6 @@ 'use strict'; -var bcrypt = require('bcryptjs'), +var async = require('async'), nconf = require('nconf'), gravatar = require('gravatar'), @@ -9,7 +9,8 @@ var bcrypt = require('bcryptjs'), db = require('./database'), meta = require('./meta'), groups = require('./groups'), - emitter = require('./emitter'); + emitter = require('./emitter'), + Password = require('./password'); (function(User) { @@ -69,7 +70,7 @@ var bcrypt = require('bcryptjs'), return callback(err); } - callback(null, modifyUserData(users, fieldsToRemove)); + modifyUserData(users, fieldsToRemove, callback); }); }; @@ -94,11 +95,11 @@ var bcrypt = require('bcryptjs'), return callback(err); } - callback(null, modifyUserData(users, [])); + modifyUserData(users, [], callback); }); }; - function modifyUserData(users, fieldsToRemove) { + function modifyUserData(users, fieldsToRemove, callback) { users.forEach(function(user) { if (!user) { return; @@ -128,7 +129,8 @@ var bcrypt = require('bcryptjs'), user[fieldsToRemove[i]] = undefined; } }); - return users; + + plugins.fireHook('filter:users.get', users, callback); } User.updateLastOnlineTime = function(uid, callback) { @@ -239,36 +241,32 @@ var bcrypt = require('bcryptjs'), }; User.getUsers = function(uids, callback) { - function loadUserInfo(user, callback) { - if (!user) { - return callback(null, user); + async.parallel({ + userData: function(next) { + User.getMultipleUserFields(uids, ['uid', 'username', 'userslug', 'picture', 'status', 'banned', 'postcount', 'reputation'], next); + }, + isAdmin: function(next) { + User.isAdministrator(uids, next); + }, + isOnline: function(next) { + db.isSortedSetMembers('users:online', uids, next); } - - async.waterfall([ - function(next) { - User.isAdministrator(user.uid, next); - }, - function(isAdmin, next) { - user.status = !user.status ? 'online' : user.status; - user.administrator = isAdmin; - user.banned = parseInt(user.banned, 10) === 1; - db.isSortedSetMember('users:online', user.uid, next); - }, - function(isMember, next) { - if (!isMember) { - user.status = 'offline'; - } - next(null, user); - } - ], callback); - } - - User.getMultipleUserFields(uids, ['uid', 'username', 'userslug', 'picture', 'status', 'banned', 'postcount', 'reputation'], function(err, usersData) { + }, function(err, results) { if (err) { return callback(err); } - async.map(usersData, loadUserInfo, callback); + results.userData.forEach(function(user, index) { + if (!user) { + return; + } + user.status = !user.status ? 'online' : user.status; + user.status = !results.isOnline[index] ? 'offline' : user.status; + user.administrator = results.isAdmin[index]; + user.banned = parseInt(user.banned, 10) === 1; + }); + + callback(err, results.userData); }); }; @@ -279,7 +277,7 @@ var bcrypt = require('bcryptjs'), } var options = { - size: '128', + size: parseInt(meta.config.profileImageDimension, 10) || 128, default: customGravatarDefaultImage || meta.config.defaultGravatarImage || 'identicon', rating: 'pg' }; @@ -296,12 +294,7 @@ var bcrypt = require('bcryptjs'), return callback(null, password); } - bcrypt.genSalt(nconf.get('bcrypt_rounds'), function(err, salt) { - if (err) { - return callback(err); - } - bcrypt.hash(password, salt, callback); - }); + Password.hash(nconf.get('bcrypt_rounds'), password, callback); }; User.onNewPostMade = function(postData) { @@ -315,12 +308,10 @@ var bcrypt = require('bcryptjs'), emitter.on('event:newpost', User.onNewPostMade); User.incrementUserPostCountBy = function(uid, value, callback) { + callback = callback || function() {}; User.incrementUserFieldBy(uid, 'postcount', value, function(err, newpostcount) { if (err) { - if(typeof callback === 'function') { - callback(err); - } - return; + return callback(err); } db.sortedSetAdd('users:postcount', newpostcount, uid, callback); }); @@ -410,29 +401,40 @@ var bcrypt = require('bcryptjs'), }; User.isAdministrator = function(uid, callback) { - groups.isMember(uid, 'administrators', callback); + if (Array.isArray(uid)) { + groups.isMembers(uid, 'administrators', callback); + } else { + groups.isMember(uid, 'administrators', callback); + } }; - User.isOnline = function(uid, callback) { - User.getUserFields(uid, ['username', 'userslug', 'picture', 'status', 'reputation', 'postcount'] , function(err, data) { - if(err) { + User.isOnline = function(uids, callback) { + if (!Array.isArray(uids)) { + uids = [uids]; + } + + User.getMultipleUserFields(uids, ['uid', 'username', 'userslug', 'picture', 'status', 'reputation', 'postcount'] , function(err, userData) { + if (err) { return callback(err); } + var websockets = require('./socket.io'); - var online = websockets.isUserOnline(uid); - data.status = online ? (data.status || 'online') : 'offline'; + userData = userData.map(function(user) { + var online = websockets.isUserOnline(user.uid); + user.status = online ? (user.status || 'online') : 'offline'; - if(data.status === 'offline') { - online = false; - } + if (user.status === 'offline') { + online = false; + } - data.online = online; - data.uid = uid; - data.timestamp = Date.now(); - data.rooms = websockets.getUserRooms(uid); + user.online = online; + user.timestamp = Date.now(); + user.rooms = websockets.getUserRooms(user.uid); + return user; + }); - callback(null, data); + callback(null, userData); }); }; diff --git a/src/user/auth.js b/src/user/auth.js index b9c7b348ce..0aaa6f658a 100644 --- a/src/user/auth.js +++ b/src/user/auth.js @@ -1,6 +1,7 @@ 'use strict'; -var db = require('../database'), +var async = require('async'), + db = require('../database'), meta = require('../meta'); module.exports = function(User) { @@ -42,4 +43,11 @@ module.exports = function(User) { User.auth.clearLoginAttempts = function(uid) { db.delete('loginAttempts:' + uid); }; + + User.auth.resetLockout = function(uid, callback) { + async.parallel([ + async.apply(db.delete, 'loginAttemps:' + uid), + async.apply(db.delete, 'lockout:' + uid) + ], callback); + } }; \ No newline at end of file diff --git a/src/user/create.js b/src/user/create.js index b3b556edb7..80a5877d7f 100644 --- a/src/user/create.js +++ b/src/user/create.js @@ -12,19 +12,38 @@ var async = require('async'), module.exports = function(User) { - User.create = function(userData, callback) { - userData = userData || {}; + User.create = function(data, callback) { + var gravatar = User.createGravatarURLFromEmail(data.email); + var timestamp = Date.now(); + var password = data.password; + + var userData = { + 'username': data.username.trim(), + 'email': data.email, + 'joindate': timestamp, + 'picture': gravatar, + 'gravatarpicture': gravatar, + 'fullname': '', + 'location': '', + 'birthday': '', + 'website': '', + 'signature': '', + 'uploadedpicture': '', + 'profileviews': 0, + 'reputation': 0, + 'postcount': 0, + 'lastposttime': 0, + 'banned': 0, + 'status': 'online' + }; + userData.userslug = utils.slugify(userData.username); - userData.username = userData.username.trim(); if (userData.email !== undefined) { userData.email = userData.email.trim(); userData.email = validator.escape(userData.email); } - var password = userData.password; - userData.password = null; - async.parallel({ emailValid: function(next) { if (userData.email) { @@ -84,7 +103,7 @@ module.exports = function(User) { } }, customFields: function(next) { - plugins.fireHook('filter:user.custom_fields', userData, next); + plugins.fireHook('filter:user.custom_fields', [], next); }, userData: function(next) { plugins.fireHook('filter:user.create', userData, next); @@ -94,7 +113,14 @@ module.exports = function(User) { return callback(err); } - userData = utils.merge(results.userData, results.customFields); + var customData = {}; + results.customFields.forEach(function(customField) { + if (data[customField]) { + customData[customField] = data[customField]; + } + }); + + userData = utils.merge(results.userData, customData); var userNameChanged = !!results.renamedUsername; @@ -104,37 +130,14 @@ module.exports = function(User) { } db.incrObjectField('global', 'nextUid', function(err, uid) { - if(err) { + if (err) { return callback(err); } - var gravatar = User.createGravatarURLFromEmail(userData.email); - var timestamp = Date.now(); - - userData = utils.merge({ - 'uid': uid, - 'username': userData.username, - 'userslug': userData.userslug, - 'fullname': '', - 'location': '', - 'birthday': '', - 'website': '', - 'email': userData.email || '', - 'signature': '', - 'joindate': timestamp, - 'picture': gravatar, - 'gravatarpicture': gravatar, - 'uploadedpicture': '', - 'profileviews': 0, - 'reputation': 0, - 'postcount': 0, - 'lastposttime': 0, - 'banned': 0, - 'status': 'online' - }, userData); + userData.uid = uid; db.setObject('user:' + uid, userData, function(err) { - if(err) { + if (err) { return callback(err); } diff --git a/src/user/delete.js b/src/user/delete.js index 8ab14c4cea..214511dffe 100644 --- a/src/user/delete.js +++ b/src/user/delete.js @@ -18,14 +18,11 @@ module.exports = function(User) { }, function(next) { deleteTopics(uid, next); + }, + function(next) { + User.deleteAccount(uid, next); } - ], function(err) { - if (err) { - return callback(err); - } - - deleteAccount(uid, callback); - }); + ], callback); }; function deletePosts(uid, callback) { @@ -46,7 +43,7 @@ module.exports = function(User) { }); } - function deleteAccount(uid, callback) { + User.deleteAccount = function(uid, callback) { user.getUserFields(uid, ['username', 'userslug', 'email'], function(err, userData) { if (err) { return callback(err); @@ -137,7 +134,7 @@ module.exports = function(User) { ], callback); }); }); - } + }; function deleteUserFromFollowers(uid, callback) { db.getSetMembers('followers:' + uid, function(err, uids) { diff --git a/src/user/email.js b/src/user/email.js index 6ff114173a..892d740e0f 100644 --- a/src/user/email.js +++ b/src/user/email.js @@ -49,9 +49,10 @@ var async = require('async'), return winston.error(err.message); } - translator.translate('[[email:welcome-to, ' + (meta.config.title || 'NodeBB') + ']]', meta.config.defaultLang, function(subject) { + var title = meta.config.title || meta.config.browserTitle || 'NodeBB'; + translator.translate('[[email:welcome-to, ' + title + ']]', meta.config.defaultLang, function(subject) { var data = { - site_title: (meta.config.title || 'NodeBB'), + site_title: title, username: username, confirm_link: confirm_link, confirm_code: confirm_code, diff --git a/src/user/jobs.js b/src/user/jobs.js index 7659a87aea..160f3bb33c 100644 --- a/src/user/jobs.js +++ b/src/user/jobs.js @@ -22,6 +22,11 @@ module.exports = function(User) { }; User.sendDailyDigests = function() { + var digestsDisabled = meta.config.disableEmailSubscriptions !== undefined && parseInt(meta.config.disableEmailSubscriptions, 10) === 1; + if (digestsDisabled) { + return winston.log('[user/jobs] Did not send daily digests because subscription system is disabled.'); + } + async.parallel({ recent: function(next) { topics.getLatestTopics(0, 0, 10, 'day', next); diff --git a/src/user/notifications.js b/src/user/notifications.js index f6c8832dbe..b6e82e6d23 100644 --- a/src/user/notifications.js +++ b/src/user/notifications.js @@ -17,85 +17,22 @@ var async = require('async'), (function(UserNotifications) { UserNotifications.get = function(uid, callback) { - function getNotifications(set, start, stop, iterator, done) { - db.getSortedSetRevRange(set, start, stop, function(err, uniqueIds) { - if(err) { - return done(err); - } - - if(!Array.isArray(uniqueIds) || !uniqueIds.length) { - return done(null, []); - } - - if (uniqueIds.length > maxNotifs) { - uniqueIds.length = maxNotifs; - } - - db.getObjectFields('uid:' + uid + ':notifications:uniqueId:nid', uniqueIds, function(err, uniqueIdToNids) { - if (err) { - return done(err); - } - - var nidsToUniqueIds = {}; - var nids = []; - uniqueIds.forEach(function(uniqueId) { - nidsToUniqueIds[uniqueIdToNids[uniqueId]] = uniqueId; - nids.push(uniqueIdToNids[uniqueId]); - }); - - async.map(nids, function(nid, next) { - notifications.get(nid, function(err, notif_data) { - if (err) { - return next(err); - } - - if (!notif_data) { - if (process.env.NODE_ENV === 'development') { - winston.info('[notifications.get] nid ' + nid + ' not found. Removing.'); - } - - db.sortedSetRemove(set, nidsToUniqueIds[nid]); - return next(); - } - - if (typeof iterator === 'function') { - iterator(notif_data, next); - } else { - next(null, notif_data); - } - }); - }, done); - }); - }); - } - var maxNotifs = 15; async.parallel({ unread: function(next) { - getNotifications('uid:' + uid + ':notifications:unread', 0, 9, function(notif_data, next) { - notif_data.read = false; - notif_data.readClass = !notif_data.read ? 'label-warning' : ''; - next(null, notif_data); - }, next); + getNotificationsFromSet('uid:' + uid + ':notifications:unread', uid, 0, 9, maxNotifs, next); }, read: function(next) { - getNotifications('uid:' + uid + ':notifications:read', 0, 9, function(notif_data, next) { - notif_data.read = true; - next(null, notif_data); - }, next); + getNotificationsFromSet('uid:' + uid + ':notifications:read', uid, 0, 9, maxNotifs, next); } }, function(err, notifications) { - function filterDeleted(notifObj) { - return !!notifObj; - } - if (err) { return callback(err); } - notifications.read = notifications.read.filter(filterDeleted); - notifications.unread = notifications.unread.filter(filterDeleted); + notifications.read = notifications.read.filter(Boolean); + notifications.unread = notifications.unread.filter(Boolean); // Limit the number of notifications to `maxNotifs`, prioritising unread notifications if (notifications.read.length + notifications.unread.length > maxNotifs) { @@ -106,48 +43,71 @@ var async = require('async'), }); }; - UserNotifications.getAll = function(uid, limit, before, callback) { - var now = new Date(); + function getNotificationsFromSet(set, uid, start, stop, max, callback) { + db.getSortedSetRevRange(set, start, stop, function(err, uniqueIds) { + if (err) { + return callback(err); + } + if(!Array.isArray(uniqueIds) || !uniqueIds.length) { + return callback(null, []); + } + + if (uniqueIds.length > max) { + uniqueIds.length = max; + } + + db.getObjectFields('uid:' + uid + ':notifications:uniqueId:nid', uniqueIds, function(err, uniqueIdToNids) { + if (err) { + return callback(err); + } + + var nidsToUniqueIds = {}; + var nids = []; + uniqueIds.forEach(function(uniqueId) { + nidsToUniqueIds[uniqueIdToNids[uniqueId]] = uniqueId; + nids.push(uniqueIdToNids[uniqueId]); + }); + + UserNotifications.getNotifications(nids, uid, function(err, notifications) { + if (err) { + return callback(err); + } + + notifications.forEach(function(notification, index) { + if (!notification) { + if (process.env.NODE_ENV === 'development') { + winston.info('[notifications.get] nid ' + nids[index] + ' not found. Removing.'); + } + + db.sortedSetRemove(set, nidsToUniqueIds[nids[index]]); + db.deleteObjectField('uid:' + uid + ':notifications:uniqueId:nid', nidsToUniqueIds[nids[index]]); + } + }); + + callback(null, notifications); + }); + }); + }); + } + + UserNotifications.getAll = function(uid, limit, callback) { if (!limit || parseInt(limit, 10) <= 0) { limit = 25; } - if (before) { - before = new Date(parseInt(before, 10)); - } db.getObjectValues('uid:' + uid + ':notifications:uniqueId:nid', function(err, nids) { if (err) { return callback(err); } - async.map(nids, function(nid, next) { - notifications.get(nid, function(err, notif_data) { - if (err || !notif_data) { - return next(err); - } - UserNotifications.isNotificationRead(notif_data.nid, uid, function(err, isRead) { - if (err) { - return next(err); - } - - notif_data.read = isRead; - next(null, notif_data); - }); - }); - }, function(err, notifs) { + UserNotifications.getNotifications(nids, uid, function(err, notifs) { if (err) { return callback(err); } - notifs = notifs.filter(function(notif) { - return notif !== null; - }).sort(function(a, b) { + notifs = notifs.filter(Boolean).sort(function(a, b) { return parseInt(b.datetime, 10) - parseInt(a.datetime, 10); - }).map(function(notif) { - notif.datetimeISO = utils.toISOString(notif.datetime); - notif.readClass = !notif.read ? 'label-warning' : ''; - return notif; }); callback(null, notifs); @@ -155,10 +115,89 @@ var async = require('async'), }); }; - UserNotifications.isNotificationRead = function(nid, uid, callback) { - db.isSortedSetMember('uid:' + uid + ':notifications:read', nid, callback); + UserNotifications.getNotifications = function(nids, uid, callback) { + notifications.getMultiple(nids, function(err, notifications) { + if (err) { + return callback(err); + } + + var uniqueIds = notifications.map(function(notification) { + return notification ? notification.uniqueId : null; + }); + + db.isSortedSetMembers('uid:' + uid + ':notifications:read', uniqueIds, function(err, hasRead) { + if (err) { + return callback(err); + } + + var pids = notifications.map(function(notification) { + return notification ? notification.pid : null; + }); + + generatePostPaths(pids, uid, function(err, paths) { + if (err) { + return callback(err); + } + + notifications = notifications.map(function(notification, index) { + if (!notification) { + return null; + } + + notification.read = hasRead[index]; + notification.path = paths[index] || notification.path || ''; + notification.datetimeISO = utils.toISOString(notification.datetime); + notification.readClass = !notification.read ? 'label-warning' : ''; + return notification; + }); + + callback(null, notifications); + }); + }); + }); }; + function generatePostPaths(pids, uid, callback) { + var postKeys = pids.map(function(pid) { + return 'post:' + pid; + }); + + db.getObjectsFields(postKeys, ['pid', 'tid'], function(err, postData) { + if (err) { + return callback(err); + } + + var topicKeys = postData.map(function(post) { + return post ? 'topic:' + post.tid : null; + }); + + async.parallel({ + indices: function(next) { + posts.getPostIndices(postData, uid, next); + }, + topics: function(next) { + db.getObjectsFields(topicKeys, ['slug'], next); + } + }, function(err, results) { + if (err) { + return callback(err); + } + + var paths = []; + pids.forEach(function(pid, index) { + var slug = results.topics[index] ? results.topics[index].slug : null; + var postIndex = results.indices[index] ? parseInt(results.indices[index], 10) + 1 : null; + if (slug && postIndex) { + paths.push(nconf.get('relative_path') + '/topic/' + slug + '/' + postIndex); + } else { + paths.push(null); + } + }); + callback(null, paths); + }); + }); + } + UserNotifications.getDailyUnread = function(uid, callback) { var now = Date.now(), yesterday = now - (1000*60*60*24); // Approximate, can be more or less depending on time changes, makes no difference really. @@ -181,15 +220,13 @@ var async = require('async'), return uniqueIdToNids[uniqueId]; }); - async.map(nids, function(nid, next) { - notifications.get(nid, next); - }, callback); + UserNotifications.getNotifications(nids, uid, callback); }); }); }; UserNotifications.getUnreadCount = function(uid, callback) { - db.sortedSetCount('uid:' + uid + ':notifications:unread', -Infinity, Infinity, callback); + db.sortedSetCard('uid:' + uid + ':notifications:unread', callback); }; UserNotifications.getUnreadByField = function(uid, field, value, callback) { @@ -211,15 +248,17 @@ var async = require('async'), return uniqueIdsToNids[uniqueId]; }); - async.filter(nids, function(nid, next) { - notifications.get(nid, function(err, notifObj) { - if (err || !notifObj) { - return next(false); - } + UserNotifications.getNotifications(nids, uid, function(err, notifications) { + if (err) { + return callback(err); + } - next(notifObj[field] === value.toString()); + notifications = notifications.filter(function(notification) { + return !!notification || notification[field] !== value.toString(); + }).map(function(notification) { + return notification.nid; }); - }, function(nids) { + callback(null, nids); }); }); @@ -235,8 +274,7 @@ var async = require('async'), async.parallel({ username: async.apply(user.getUserField, uid, 'username'), - topic: async.apply(topics.getTopicFields, tid, ['slug', 'cid', 'title']), - postIndex: async.apply(posts.getPidIndex, pid), + topic: async.apply(topics.getTopicFields, tid, ['cid', 'title']), postContent: function(next) { async.waterfall([ async.apply(posts.getPostField, pid, 'content'), @@ -260,8 +298,9 @@ var async = require('async'), notifications.create({ bodyShort: '[[notifications:user_posted_to, ' + results.username + ', ' + results.topic.title + ']]', bodyLong: results.postContent, - path: nconf.get('relative_path') + '/topic/' + results.topic.slug + '/' + results.postIndex, + pid: pid, uniqueId: 'topic:' + tid + ':uid:' + uid, + tid: tid, from: uid }, function(err, nid) { if (err) { diff --git a/src/user/profile.js b/src/user/profile.js index ea332dee21..d76ac2bf65 100644 --- a/src/user/profile.js +++ b/src/user/profile.js @@ -1,15 +1,15 @@ 'use strict'; -var bcrypt = require('bcryptjs'), - async = require('async'), +var async = require('async'), validator = require('validator'), S = require('string'), - utils = require('./../../public/src/utils'), - meta = require('./../meta'), - events = require('./../events'), - db = require('./../database'); + utils = require('../../public/src/utils'), + meta = require('../meta'), + events = require('../events'), + db = require('../database'), + Password = require('../password'); module.exports = function(User) { @@ -248,7 +248,7 @@ module.exports = function(User) { return hashAndSetPassword(callback); } - bcrypt.compare(data.currentPassword, currentPassword, function(err, res) { + Password.compare(data.currentPassword, currentPassword, function(err, res) { if (err || !res) { return callback(err || new Error('[[user:change_password_error_wrong_current]]')); } diff --git a/src/user/reset.js b/src/user/reset.js index ee80679bcd..2b101e8c27 100644 --- a/src/user/reset.js +++ b/src/user/reset.js @@ -88,10 +88,8 @@ var async = require('async'), db.deleteObjectField('reset:uid', code); db.deleteObjectField('reset:expiry', code); - db.delete('lockout:' + uid); - user.auth.clearLoginAttempts(uid); - callback(); + user.auth.resetLockout(uid, callback); }); }); }); diff --git a/src/user/settings.js b/src/user/settings.js index e023eda5eb..2cf9fba084 100644 --- a/src/user/settings.js +++ b/src/user/settings.js @@ -33,6 +33,8 @@ module.exports = function(User) { settings.notificationSounds = settings.notificationSounds ? parseInt(settings.notificationSounds, 10) === 1 : true; settings.language = settings.language || meta.config.defaultLang || 'en_GB'; settings.topicPostSort = settings.topicPostSort || meta.config.topicPostSort || 'oldest_to_newest'; + settings.followTopicsOnCreate = settings.followTopicsOnCreate ? parseInt(settings.followTopicsOnCreate, 10) === 1 : true; + settings.followTopicsOnReply = settings.followTopicsOnReply ? parseInt(settings.followTopicsOnReply, 10) === 1 : false; callback(null, settings); }); }); @@ -80,7 +82,9 @@ module.exports = function(User) { topicsPerPage: data.topicsPerPage, postsPerPage: data.postsPerPage, notificationSounds: data.notificationSounds, - language: data.language || meta.config.defaultLang + language: data.language || meta.config.defaultLang, + followTopicsOnCreate: data.followTopicsOnCreate, + followTopicsOnReply: data.followTopicsOnReply }, callback); }; diff --git a/src/webserver.js b/src/webserver.js index 9b50f0b553..3278713dd1 100644 --- a/src/webserver.js +++ b/src/webserver.js @@ -56,6 +56,7 @@ if(nconf.get('ssl')) { }, function(err, data) { middleware = middleware(app, data); routes(app, middleware); + plugins.prepareApp(app, middleware); if (err) { winston.error('Errors were encountered while attempting to initialise NodeBB.'); @@ -96,7 +97,7 @@ if(nconf.get('ssl')) { } module.exports.server = server; - module.exports.init = function () { + module.exports.init = function(callback) { server.on("error", function(err){ if (err.code === 'EADDRINUSE') { winston.error('NodeBB address in use, exiting...'); @@ -109,21 +110,30 @@ if(nconf.get('ssl')) { emitter.all(['templates:compiled', 'meta:js.compiled', 'meta:css.compiled'], function() { winston.info('NodeBB Ready'); emitter.emit('nodebb:ready'); + emitter.removeAllListeners('templates:compiled').removeAllListeners('meta:js.compiled').removeAllListeners('meta:css.compiled'); }); emitter.on('templates:compiled', function() { - var bind_address = ((nconf.get('bind_address') === "0.0.0.0" || !nconf.get('bind_address')) ? '0.0.0.0' : nconf.get('bind_address')) + ':' + port; - winston.info('NodeBB attempting to listen on: ' + bind_address); + if (process.send) { + callback(); + } else { + module.exports.listen(); + } + }); + }; - server.listen(port, nconf.get('bind_address'), function(){ - winston.info('NodeBB is now listening on: ' + bind_address); - if (process.send) { - process.send({ - action: 'ready', - bind_address: bind_address - }); - } - }); + module.exports.listen = function() { + var bind_address = ((nconf.get('bind_address') === "0.0.0.0" || !nconf.get('bind_address')) ? '0.0.0.0' : nconf.get('bind_address')) + ':' + port; + winston.info('NodeBB attempting to listen on: ' + bind_address); + + server.listen(port, nconf.get('bind_address'), function(){ + winston.info('NodeBB is now listening on: ' + bind_address); + if (process.send) { + process.send({ + action: 'listening', + bind_address: bind_address + }); + } }); };