diff --git a/README.md b/README.md index 9062b049ae..200a444b6d 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![Code Climate](https://codeclimate.com/github/NodeBB/NodeBB/badges/gpa.svg)](https://codeclimate.com/github/NodeBB/NodeBB) [![Documentation Status](https://readthedocs.org/projects/nodebb/badge/?version=latest)](https://readthedocs.org/projects/nodebb/?badge=latest) -**NodeBB Forum Software** is powered by Node.js and built on either a Redis or MongoDB database. It utilizes web sockets for instant interactions and real-time notifications. NodeBB is compatible down to IE8 and has many modern features out of the box such as social network integration and streaming discussions. +[**NodeBB Forum Software**](https://nodebb.org) is powered by Node.js and built on either a Redis or MongoDB database. It utilizes web sockets for instant interactions and real-time notifications. NodeBB has many modern features out of the box such as social network integration and streaming discussions, while still making sure to be compatible with older browsers. Additional functionality is enabled through the use of third-party plugins. @@ -72,4 +72,4 @@ Detailed upgrade instructions are listed in [Upgrading NodeBB](https://docs.node NodeBB is licensed under the **GNU General Public License v3 (GPL-3)** (http://www.gnu.org/copyleft/gpl.html). -Interested in a sublicense agreement for use of NodeBB in a non-free/restrictive environment? Contact us at sales@nodebb.org. \ No newline at end of file +Interested in a sublicense agreement for use of NodeBB in a non-free/restrictive environment? Contact us at sales@nodebb.org. diff --git a/app.js b/app.js index 4c4f6854bb..292b599335 100644 --- a/app.js +++ b/app.js @@ -152,14 +152,12 @@ function start() { case 'js-propagate': meta.js.cache = message.cache; meta.js.map = message.map; - meta.js.hash = message.hash; emitter.emit('meta:js.compiled'); winston.verbose('[cluster] Client-side javascript and mapping propagated to worker %s', process.pid); break; case 'css-propagate': meta.css.cache = message.cache; meta.css.acpCache = message.acpCache; - meta.css.hash = message.hash; emitter.emit('meta:css.compiled'); winston.verbose('[cluster] Stylesheets propagated to worker %s', process.pid); break; diff --git a/install/web.js b/install/web.js index 0c7fb2521f..7d2cfad21a 100644 --- a/install/web.js +++ b/install/web.js @@ -12,6 +12,16 @@ var winston = require('winston'), app = express(), server; +winston.add(winston.transports.File, { + filename: 'logs/webinstall.log', + colorize: true, + timestamp: function() { + var date = new Date(); + return date.getDate() + '/' + (date.getMonth() + 1) + ' ' + date.toTimeString().substr(0,5) + ' [' + global.process.pid + ']'; + }, + level: 'verbose' +}); + var web = {}, scripts = [ 'public/vendor/xregexp/xregexp.js', @@ -30,7 +40,7 @@ web.install = function(port) { app.set('views', path.join(__dirname, '../src/views')); app.use(bodyParser.urlencoded({ extended: true - })); + })); async.parallel([compileLess, compileJS], function() { setupRoutes(); @@ -107,10 +117,10 @@ function launch(req, res) { process.stdout.write(' "./nodebb stop" to stop the NodeBB server\n'); process.stdout.write(' "./nodebb log" to view server output\n'); process.stdout.write(' "./nodebb restart" to restart NodeBB\n'); - + child.unref(); process.exit(0); - + } function compileLess(callback) { diff --git a/loader.js b/loader.js index c79756f09e..96c78da4ee 100644 --- a/loader.js +++ b/loader.js @@ -90,8 +90,7 @@ Loader.addWorkerEvents = function(worker) { worker.send({ action: 'js-propagate', cache: Loader.js.cache, - map: Loader.js.map, - hash: Loader.js.hash + map: Loader.js.map }); } @@ -99,8 +98,7 @@ Loader.addWorkerEvents = function(worker) { worker.send({ action: 'css-propagate', cache: Loader.css.cache, - acpCache: Loader.css.acpCache, - hash: Loader.css.hash + acpCache: Loader.css.acpCache }); } @@ -117,25 +115,21 @@ Loader.addWorkerEvents = function(worker) { case 'js-propagate': Loader.js.cache = message.cache; Loader.js.map = message.map; - Loader.js.hash = message.hash; Loader.notifyWorkers({ action: 'js-propagate', cache: message.cache, - map: message.map, - hash: message.hash + map: message.map }, worker.pid); break; case 'css-propagate': Loader.css.cache = message.cache; Loader.css.acpCache = message.acpCache; - Loader.css.hash = message.hash; Loader.notifyWorkers({ action: 'css-propagate', cache: message.cache, - acpCache: message.acpCache, - hash: message.hash + acpCache: message.acpCache }, worker.pid); break; case 'templates:compiled': diff --git a/minifier.js b/minifier.js index 7289843b2f..621d61fa08 100644 --- a/minifier.js +++ b/minifier.js @@ -6,7 +6,7 @@ var uglifyjs = require('uglify-js'), fs = require('fs'), crypto = require('crypto'), utils = require('./public/src/utils'), - + Minifier = { js: {} }; @@ -47,17 +47,7 @@ function minifyScripts(scripts, callback) { var minified = uglifyjs.minify(scripts, { // outSourceMap: "nodebb.min.js.map", compress: false - }), - hasher = crypto.createHash('md5'), - hash; - - // Calculate js hash - hasher.update(minified.code, 'utf-8'); - hash = hasher.digest('hex'); - process.send({ - type: 'hash', - payload: hash.slice(0, 8) - }); + }); callback(minified.code/*, minified.map*/); } catch(err) { diff --git a/package.json b/package.json index 91dc038584..5531e3370e 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "nodebb", "license": "GPL-3.0", "description": "NodeBB Forum", - "version": "0.8.1", + "version": "0.8.2", "homepage": "http://www.nodebb.org", "repository": { "type": "git", @@ -37,21 +37,21 @@ "mime": "^1.3.4", "minimist": "^1.1.1", "mkdirp": "~0.5.0", - "mmmagic": "^0.3.13", + "mmmagic": "^0.4.0", "morgan": "^1.3.2", "nconf": "~0.7.1", - "nodebb-plugin-composer-default": "1.0.13", + "nodebb-plugin-composer-default": "1.0.14", "nodebb-plugin-dbsearch": "0.2.16", - "nodebb-plugin-emoji-extended": "0.4.9", + "nodebb-plugin-emoji-extended": "0.4.13", "nodebb-plugin-markdown": "4.0.5", - "nodebb-plugin-mentions": "1.0.1", + "nodebb-plugin-mentions": "1.0.2", "nodebb-plugin-soundpack-default": "0.1.4", - "nodebb-plugin-spam-be-gone": "0.4.1", + "nodebb-plugin-spam-be-gone": "0.4.2", "nodebb-rewards-essentials": "0.0.5", - "nodebb-theme-lavender": "1.0.54", - "nodebb-theme-persona": "2.1.25", - "nodebb-theme-vanilla": "3.1.7", - "nodebb-widget-essentials": "2.0.0", + "nodebb-theme-lavender": "2.0.1", + "nodebb-theme-persona": "3.0.12", + "nodebb-theme-vanilla": "4.0.7", + "nodebb-widget-essentials": "2.0.1", "npm": "^2.1.4", "passport": "^0.3.0", "passport-local": "1.0.0", diff --git a/public/language/ar/error.json b/public/language/ar/error.json index 82f31d3ae7..56d314dbfe 100644 --- a/public/language/ar/error.json +++ b/public/language/ar/error.json @@ -77,6 +77,8 @@ "cant-chat-with-yourself": "لايمكنك فتح محادثة مع نفسك", "chat-restricted": "هذا المستخدم عطل المحادثات الواردة عليه. يجب أن يتبعك حتى تتمكن من فتح محادثة معه.", "too-many-messages": "لقد أرسلت الكثير من الرسائل، الرجاء اﻹنتظار قليلاً", + "invalid-chat-message": "Invalid chat message", + "chat-message-too-long": "Chat message is too long", "reputation-system-disabled": "نظام السمعة معطل", "downvoting-disabled": "التصويتات السلبية معطلة", "not-enough-reputation-to-downvote": "ليس لديك سمعة تكفي لإضافة صوت سلبي لهذا الموضوع", diff --git a/public/language/ar/groups.json b/public/language/ar/groups.json index ea0917cdd7..cde387b1f1 100644 --- a/public/language/ar/groups.json +++ b/public/language/ar/groups.json @@ -12,6 +12,9 @@ "invited.none": "لايوجد أعضاء مدعوون في حالياً", "invited.uninvite": "إلغ الدعوة", "invited.search": "ابحث عن أعضاء لدعوتهم للمجموعة", + "invited.notification_title": "You have been invited to join %1", + "request.notification_title": "Group Membership Request from %1", + "request.notification_text": "%1 has requested to become a member of %2", "cover-instructions": "اسحب وأسقِط صورة، اسحبها للموضع المرغوب، وانقر على حفظ", "cover-change": "تغيير", "cover-save": "حفظ", diff --git a/public/language/ar/user.json b/public/language/ar/user.json index 7b6a26fcb1..83ce1f7704 100644 --- a/public/language/ar/user.json +++ b/public/language/ar/user.json @@ -12,6 +12,7 @@ "delete_account": "حذف الحساب", "delete_account_confirm": "هل أن متأكد أنك تريد حذف حسابك؟
هذه العملية غير قابلة للإلغاء ولن يكون بالإمكان استعادة بياناتك

أدخل اسم المستخدم الخاص بك لتأكيد عملية الحذف", "delete_this_account_confirm": "Are you sure you want to delete this account?
This action is irreversible and you will not be able to recover any data

", + "account-deleted": "Account deleted", "fullname": "الاسم الكامل", "website": "الموقع الإلكتروني", "location": "الموقع", @@ -52,6 +53,7 @@ "username_taken_workaround": "اسم المستخدم الذي اخترته سبق أخذه، لذا تم تغييره قليلا. أن الآن مسجل تحت الاسم %1", "upload_picture": "ارفع الصورة", "upload_a_picture": "رفع صورة", + "remove_uploaded_picture": "Remove Uploaded Picture", "image_spec": "لايمكنك رفع إلا الصور ذات الصيغ PNG أو JPG أو GIF.", "settings": "خيارات", "show_email": "أظهر بريدي الإلكتروني", diff --git a/public/language/bg/error.json b/public/language/bg/error.json index 4a89a5a864..0a63951924 100644 --- a/public/language/bg/error.json +++ b/public/language/bg/error.json @@ -77,6 +77,8 @@ "cant-chat-with-yourself": "Не можете да пишете съобщение на себе си!", "chat-restricted": "Този потребител е ограничил съобщенията до себе си. Той трябва първо да Ви последва, преди да можете да си пишете с него.", "too-many-messages": "Изпратили сте твърде много съобщения. Моля, изчакайте малко.", + "invalid-chat-message": "Invalid chat message", + "chat-message-too-long": "Chat message is too long", "reputation-system-disabled": "Системата за репутация е изключена.", "downvoting-disabled": "Отрицателното гласуване е изключено", "not-enough-reputation-to-downvote": "Нямате достатъчно репутация, за да гласувате отрицателно за тази публикация", diff --git a/public/language/bg/groups.json b/public/language/bg/groups.json index 1fa3860f20..03cc738a19 100644 --- a/public/language/bg/groups.json +++ b/public/language/bg/groups.json @@ -12,6 +12,9 @@ "invited.none": "В момента няма поканени членове", "invited.uninvite": "Отмяна на поканата", "invited.search": "Потърсете потребител, когото да поканите в тази група", + "invited.notification_title": "You have been invited to join %1", + "request.notification_title": "Group Membership Request from %1", + "request.notification_text": "%1 has requested to become a member of %2", "cover-instructions": "Плъзнете снимка, наместете я в предпочитаната позиция и натистнете Запазване", "cover-change": "Промяна", "cover-save": "Запазване", diff --git a/public/language/bg/user.json b/public/language/bg/user.json index 1d19910de4..52c7c124d1 100644 --- a/public/language/bg/user.json +++ b/public/language/bg/user.json @@ -12,6 +12,7 @@ "delete_account": "Изтриване на акаунта", "delete_account_confirm": "Сигурни ли сте, че искате да изтриете акаунта си?
Това действие е необратимо и няма да можете да възстановите нищо от данните си

Въведете потребителското си име, за да потвърдите, че искате да унищожите този акаунт.", "delete_this_account_confirm": "Сигурни ли сте, че искате да изтриете този акаунт?
Това действие е необратимо и няма да можете да възстановите нищо от данните

", + "account-deleted": "Account deleted", "fullname": "Цяло име", "website": "Уеб сайт", "location": "Местоположение", @@ -52,6 +53,7 @@ "username_taken_workaround": "Потребителското име, което искате, е заето и затова ние го променихме малко. Вие ще се наричате %1", "upload_picture": "Качване на снимка", "upload_a_picture": "Качване на снимка", + "remove_uploaded_picture": "Remove Uploaded Picture", "image_spec": "Можете да качвате само PNG, JPG, или GIF файлове", "settings": "Настройки", "show_email": "Да се показва е-пощата ми", diff --git a/public/language/bn/error.json b/public/language/bn/error.json index 6de3031ccb..3d18111c11 100644 --- a/public/language/bn/error.json +++ b/public/language/bn/error.json @@ -77,6 +77,8 @@ "cant-chat-with-yourself": "আপনি নিজের সাথে চ্যাট করতে পারবেন না!", "chat-restricted": "এই সদস্য তার বার্তালাপ সংরক্ষিত রেখেছেন। এই সদস্য আপনাকে ফলো করার পরই কেবলমাত্র আপনি তার সাথে চ্যাট করতে পারবেন", "too-many-messages": "You have sent too many messages, please wait awhile.", + "invalid-chat-message": "Invalid chat message", + "chat-message-too-long": "Chat message is too long", "reputation-system-disabled": "সম্মাননা ব্যাবস্থা নিস্ক্রীয় রাখা হয়েছে", "downvoting-disabled": "ঋণাত্মক ভোট নিস্ক্রীয় রাখা হয়েছে।", "not-enough-reputation-to-downvote": "আপনার এই পোস্ট downvote করার জন্য পর্যাপ্ত সম্মাননা নেই", diff --git a/public/language/bn/groups.json b/public/language/bn/groups.json index ee043d39b6..0441c246a1 100644 --- a/public/language/bn/groups.json +++ b/public/language/bn/groups.json @@ -12,6 +12,9 @@ "invited.none": "There are no invited members at this time", "invited.uninvite": "Rescind Invitation", "invited.search": "Search for a user to invite to this group", + "invited.notification_title": "You have been invited to join %1", + "request.notification_title": "Group Membership Request from %1", + "request.notification_text": "%1 has requested to become a member of %2", "cover-instructions": "Drag and Drop a photo, drag to position, and hit Save", "cover-change": "Change", "cover-save": "Save", diff --git a/public/language/bn/user.json b/public/language/bn/user.json index d5c99082d8..55b03044b3 100644 --- a/public/language/bn/user.json +++ b/public/language/bn/user.json @@ -12,6 +12,7 @@ "delete_account": "একাউন্ট মুছে ফেলুন", "delete_account_confirm": "আপনি কি নিশ্চিত যে আপনি আপনার একাউন্ট মুছে ফেলতে চান ?
এই কাজটির ফলে আপনার কোন তথ্য পুনরূদ্ধার করা সম্ভব নয়

নিশ্চিত করতে আপনার ইউজারনেম প্রবেশ করান। ", "delete_this_account_confirm": "Are you sure you want to delete this account?
This action is irreversible and you will not be able to recover any data

", + "account-deleted": "Account deleted", "fullname": "পুর্ণ নাম", "website": "ওয়েবসাইট", "location": "স্থান", @@ -52,6 +53,7 @@ "username_taken_workaround": "আপনি যে ইউজারনেম চাচ্ছিলেন সেটি ইতিমধ্যে নেয়া হয়ে গেছে, কাজেই আমরা এটি কিঞ্চিং পরিবর্তন করেছি। আপনি এখন %1 হিসেবে পরিচিত", "upload_picture": "ছবি আপলোড করুন", "upload_a_picture": "ছবি (একটি) আপলোড করুন", + "remove_uploaded_picture": "Remove Uploaded Picture", "image_spec": "আপনি কেবলমাত্র PNG, JPG অথবা GIF ফাইল আপলোড করতে পারবেন", "settings": "সেটিংস", "show_email": "আমার ইমেইল দেখাও", diff --git a/public/language/cs/error.json b/public/language/cs/error.json index 693837095e..86b9cc79be 100644 --- a/public/language/cs/error.json +++ b/public/language/cs/error.json @@ -77,6 +77,8 @@ "cant-chat-with-yourself": "Nemůžete chatovat sami se sebou!", "chat-restricted": "This user has restricted their chat messages. They must follow you before you can chat with them", "too-many-messages": "You have sent too many messages, please wait awhile.", + "invalid-chat-message": "Invalid chat message", + "chat-message-too-long": "Chat message is too long", "reputation-system-disabled": "Systém reputací je zakázán.", "downvoting-disabled": "Downvoting is disabled", "not-enough-reputation-to-downvote": "You do not have enough reputation to downvote this post", diff --git a/public/language/cs/groups.json b/public/language/cs/groups.json index dce192132e..50e9f05d0b 100644 --- a/public/language/cs/groups.json +++ b/public/language/cs/groups.json @@ -12,6 +12,9 @@ "invited.none": "There are no invited members at this time", "invited.uninvite": "Rescind Invitation", "invited.search": "Search for a user to invite to this group", + "invited.notification_title": "You have been invited to join %1", + "request.notification_title": "Group Membership Request from %1", + "request.notification_text": "%1 has requested to become a member of %2", "cover-instructions": "Drag and Drop a photo, drag to position, and hit Save", "cover-change": "Change", "cover-save": "Save", diff --git a/public/language/cs/user.json b/public/language/cs/user.json index db573533f7..6436d02690 100644 --- a/public/language/cs/user.json +++ b/public/language/cs/user.json @@ -12,6 +12,7 @@ "delete_account": "Vymazat účet", "delete_account_confirm": "Opravdu chcete smazat váš účet?
Tato akce je nevratná a nebude možné obnovit žádné vaše data.

Pro potvrzení smazání účtu napište vaše uživatelské jméno.", "delete_this_account_confirm": "Are you sure you want to delete this account?
This action is irreversible and you will not be able to recover any data

", + "account-deleted": "Account deleted", "fullname": "Jméno a příjmení", "website": "Webové stránky", "location": "Poloha", @@ -52,6 +53,7 @@ "username_taken_workaround": "The username you requested was already taken, so we have altered it slightly. You are now known as %1", "upload_picture": "Nahrát obrázek", "upload_a_picture": "Nahrát obrázek", + "remove_uploaded_picture": "Remove Uploaded Picture", "image_spec": "Nahrávat lze pouze soubory PNG, JPG a GIF", "settings": "Nastavení", "show_email": "Zobrazovat můj email v profilu", diff --git a/public/language/da/error.json b/public/language/da/error.json index 4dd97c89b1..b3e8ccce2c 100644 --- a/public/language/da/error.json +++ b/public/language/da/error.json @@ -77,6 +77,8 @@ "cant-chat-with-yourself": "Du kan ikke chatte med dig selv!", "chat-restricted": "Denne bruger har spæret adgangen til chat beskeder. Brugeren må følge dig før du kan chatte med ham/hende", "too-many-messages": "Du har sendt for mange beskeder, vent venligt lidt.", + "invalid-chat-message": "Ugyldig chat besked", + "chat-message-too-long": "Chat beskeden er for lang", "reputation-system-disabled": "Vurderingssystem er slået fra.", "downvoting-disabled": "Nedvurdering er slået fra", "not-enough-reputation-to-downvote": "Du har ikke nok omdømme til at nedstemme dette indlæg", diff --git a/public/language/da/global.json b/public/language/da/global.json index 34afe49fbf..93a7a68676 100644 --- a/public/language/da/global.json +++ b/public/language/da/global.json @@ -68,7 +68,7 @@ "recentposts": "Seneste indlæg", "recentips": "Seneste loggede ind IPer", "away": "Væk", - "dnd": "Do not disturb", + "dnd": "Vil ikke forstyres", "invisible": "Usynlig", "offline": "Offline", "email": "Email", diff --git a/public/language/da/groups.json b/public/language/da/groups.json index da21bef337..cefdd86e98 100644 --- a/public/language/da/groups.json +++ b/public/language/da/groups.json @@ -12,6 +12,9 @@ "invited.none": "Der er ingen inviterede medlemmer i øjeblikket", "invited.uninvite": "Tilbagetræk invitation", "invited.search": "Søg efter en bruger at invitere til denne gruppe", + "invited.notification_title": "Du er blevet inviteret til at blive medlem af %1", + "request.notification_title": "Gruppe medlemskab anmodning fra %1", + "request.notification_text": "%1 har anmodet om at blive medlem af %2", "cover-instructions": "Træk og slip et billede, træk til positionen og tryk Gem", "cover-change": "Ændre", "cover-save": "Gem", diff --git a/public/language/da/user.json b/public/language/da/user.json index 871933e1d0..f779c9a1a5 100644 --- a/public/language/da/user.json +++ b/public/language/da/user.json @@ -12,6 +12,7 @@ "delete_account": "Slet konto", "delete_account_confirm": "Er du sikker på du vil slette din konto?
Dette kan ikke fortrydes, og du vil ikke være istand til at genoprette dine data

Indtast dit brugernavn for at bekræfte at du ønsker at slette din konto.", "delete_this_account_confirm": "Er du sikker på at du vil slette denne konto?
Denne handling kan ikke fortrydes, og du kan ikke genskabe nogen data.

", + "account-deleted": "Account deleted", "fullname": "Fulde navn", "website": "Webside", "location": "Lokation", @@ -52,6 +53,7 @@ "username_taken_workaround": "Det valgte brugernavn er allerede taget, så vi har ændret det en smule. Du hedder nu %1", "upload_picture": "Upload billede", "upload_a_picture": "Upload et billede", + "remove_uploaded_picture": "Remove Uploaded Picture", "image_spec": "Du kan kun uploade PNG, JPG eller GIF billeder", "settings": "Indstillinger", "show_email": "Vis min emailaddresse", diff --git a/public/language/de/error.json b/public/language/de/error.json index e2176721e4..ad8daf5660 100644 --- a/public/language/de/error.json +++ b/public/language/de/error.json @@ -77,6 +77,8 @@ "cant-chat-with-yourself": "Du kannst nicht mit dir selber chatten!", "chat-restricted": "Dieser Benutzer hat seine Chatfunktion eingeschränkt. Du kannst nur mit diesem Benutzer chatten, wenn er dir folgt.", "too-many-messages": "Du hast zu viele Nachrichten versandt, bitte warte eine Weile.", + "invalid-chat-message": "Invalid chat message", + "chat-message-too-long": "Chat message is too long", "reputation-system-disabled": "Das Reputationssystem ist deaktiviert.", "downvoting-disabled": "Downvotes sind deaktiviert.", "not-enough-reputation-to-downvote": "Deine Reputation ist zu niedrig, um diesen Beitrag negativ zu bewerten.", diff --git a/public/language/de/global.json b/public/language/de/global.json index 8002e59ead..2cb475d5eb 100644 --- a/public/language/de/global.json +++ b/public/language/de/global.json @@ -68,7 +68,7 @@ "recentposts": "Aktuelle Beiträge", "recentips": "Zuletzt eingeloggte IPs", "away": "Abwesend", - "dnd": "Do not disturb", + "dnd": "Nicht stören", "invisible": "Unsichtbar", "offline": "Offline", "email": "E-Mail", diff --git a/public/language/de/groups.json b/public/language/de/groups.json index be381125a2..393a89d0e9 100644 --- a/public/language/de/groups.json +++ b/public/language/de/groups.json @@ -12,6 +12,9 @@ "invited.none": "Es sind zur Zeit keine weiteren Mitglieder eingeladen", "invited.uninvite": "Einladung zurücknehmen", "invited.search": "Suche nach einem Benutzer um ihn in diese Gruppe aufzunehmen", + "invited.notification_title": "You have been invited to join %1", + "request.notification_title": "Group Membership Request from %1", + "request.notification_text": "%1 has requested to become a member of %2", "cover-instructions": "Foto auf eine Position bewegen, und Speichern drücken", "cover-change": "Ändern", "cover-save": "Speichern", diff --git a/public/language/de/modules.json b/public/language/de/modules.json index c872442e56..5d6e544127 100644 --- a/public/language/de/modules.json +++ b/public/language/de/modules.json @@ -5,7 +5,7 @@ "chat.no_active": "Du hast keine aktiven Chats.", "chat.user_typing": "%1 tippt gerade ...", "chat.user_has_messaged_you": "%1 hat dir geschrieben.", - "chat.see_all": "See all chats", + "chat.see_all": "Alle Diagramme anzeigen", "chat.no-messages": "Bitte wähle einen Empfänger, um den jeweiligen Nachrichtenverlauf anzuzeigen.", "chat.recent-chats": "Aktuelle Chats", "chat.contacts": "Kontakte", @@ -24,6 +24,6 @@ "composer.submit_and_lock": "Einreichen und Sperren", "composer.toggle_dropdown": "Menu aus-/einblenden", "bootbox.ok": "OK", - "bootbox.cancel": "Cancel", - "bootbox.confirm": "Confirm" + "bootbox.cancel": "Abbrechen", + "bootbox.confirm": "Bestätigen" } \ No newline at end of file diff --git a/public/language/de/notifications.json b/public/language/de/notifications.json index 3ba55b59a2..ff2d5e316d 100644 --- a/public/language/de/notifications.json +++ b/public/language/de/notifications.json @@ -1,7 +1,7 @@ { "title": "Benachrichtigungen", "no_notifs": "Du hast keine neuen Benachrichtigungen", - "see_all": "See all notifications", + "see_all": "Alle Benachrichtigungen anzeigen", "mark_all_read": "Alle Benachrichtigungen als gelesen markieren", "back_to_home": "Zurück zu %1", "outgoing_link": "Externer Link", diff --git a/public/language/de/pages.json b/public/language/de/pages.json index 239414954d..014968761a 100644 --- a/public/language/de/pages.json +++ b/public/language/de/pages.json @@ -1,37 +1,37 @@ { "home": "Home", "unread": "Ungelesene Themen", - "popular-day": "Popular topics today", - "popular-week": "Popular topics this week", - "popular-month": "Popular topics this month", - "popular-alltime": "All time popular topics", + "popular-day": "Beliebte Themen von Heute", + "popular-week": "Beliebte Themen dieser Woche", + "popular-month": "Beliebte Themen dieses Monats", + "popular-alltime": "Beliebteste Themen", "recent": "Neueste Themen", - "users/online": "Online Users", - "users/latest": "Latest Users", - "users/sort-posts": "Users with the most posts", - "users/sort-reputation": "Users with the most reputation", - "users/map": "User Map", - "users/search": "User Search", + "users/online": "Benutzer online", + "users/latest": "Neuste Benutzer", + "users/sort-posts": "Benutzer mit den meisten Beiträgen", + "users/sort-reputation": "Benutzer mit der besten Reputation", + "users/map": "Benutzer Karte", + "users/search": "Benutzer Suche", "notifications": "Benachrichtigungen", "tags": "Markierungen", "tag": "Themen markiert unter \"%1\"", - "register": "Register an account", - "login": "Login to your account", - "reset": "Reset your account password", - "categories": "Categories", - "groups": "Groups", - "group": "%1 group", + "register": "Einen Benutzer erstellen", + "login": "Einloggen", + "reset": "Passwort zurücksetzen", + "categories": "Kategorien", + "groups": "Gruppen", + "group": "%1's Gruppen", "chats": "Chats", - "chat": "Chatting with %1", - "account/edit": "Editing \"%1\"", - "account/following": "People %1 follows", - "account/followers": "People who follow %1", - "account/posts": "Posts made by %1", - "account/topics": "Topics created by %1", - "account/groups": "%1's Groups", - "account/favourites": "%1's Favourite Posts", - "account/settings": "User Settings", - "account/watched": "Topics watched by %1", + "chat": "Chatte mit %1", + "account/edit": "Bearbeite %1", + "account/following": "Nutzer, die %1 folgt", + "account/followers": "Nutzer, die %1 folgen", + "account/posts": "Beiträge von %1", + "account/topics": "Themen verfasst von %1", + "account/groups": "%1's Gruppen", + "account/favourites": "Von %1 favorisierte Beiträge", + "account/settings": "Benutzer-Einstellungen", + "account/watched": "Themen angeschaut von %1", "maintenance.text": "%1 befindet sich derzeit in der Wartung. Bitte komm später wieder.", "maintenance.messageIntro": "Zusätzlich hat der Administrator diese Nachricht hinterlassen:" } \ No newline at end of file diff --git a/public/language/de/recent.json b/public/language/de/recent.json index e64846b674..d9fa50b996 100644 --- a/public/language/de/recent.json +++ b/public/language/de/recent.json @@ -15,5 +15,5 @@ "there-are-new-topics-and-new-posts": "Es gibt %1 neue Themen und %2 neue Beiträge.", "there-is-a-new-post": "Es gibt einen neuen Beitrag.", "there-are-new-posts": "Es gibt %1 neue Beiträge.", - "click-here-to-reload": "Hier klicken um zu aktualisieren." + "click-here-to-reload": "Zum aktualisieren hier klicken." } \ No newline at end of file diff --git a/public/language/de/user.json b/public/language/de/user.json index 7d5ba0bbd4..88208df71c 100644 --- a/public/language/de/user.json +++ b/public/language/de/user.json @@ -12,6 +12,7 @@ "delete_account": "Konto löschen", "delete_account_confirm": "Bist du sicher, dass du dein Konto löschen möchtest?
Diese Aktion kann nicht rückgängig gemacht werden und du kannst deine Daten nicht wiederherstellen

Gebe deinen Benutzernamen ein, um zu bestätigen, dass du dieses Konto löschen möchtest.", "delete_this_account_confirm": "Bist du sicher, dass du dieses Konto löschen möchtest?
Diese Aktion kann nicht rückgangig gemacht werden und du kannst die Daten nicht wiederherstellen

", + "account-deleted": "Account deleted", "fullname": "Kompletter Name", "website": "Homepage", "location": "Wohnort", @@ -52,6 +53,7 @@ "username_taken_workaround": "Der gewünschte Benutzername ist bereits vergeben, deshalb haben wir ihn ein wenig verändert. Du bist jetzt unter dem Namen %1 bekannt.", "upload_picture": "Bild hochladen", "upload_a_picture": "Ein Bild hochladen", + "remove_uploaded_picture": "Remove Uploaded Picture", "image_spec": "Sie dürfen nur Dateien vom Typ PNG, JPG oder GIF hochladen", "settings": "Einstellungen", "show_email": "Zeige meine E-Mail Adresse an.", @@ -65,7 +67,7 @@ "digest_monthly": "Monatlich", "send_chat_notifications": "Sende eine E-Mail, wenn eine neue Chat-Nachricht eingeht und ich nicht online bin", "send_post_notifications": "Sende eine E-Mail wenn auf Themen die ich abonniert habe geantwortet wird", - "settings-require-reload": "Manche Einstellungsänderung benötigt ein aktualisieren. Drücke hier um die Seite neu zu laden.", + "settings-require-reload": "Einige Einstellungsänderung benötigen eine Aktualisierung. Hier klicken um die Seite neu zu laden.", "has_no_follower": "Dieser User hat noch keine Follower.", "follows_no_one": "Dieser User folgt noch niemandem :(", "has_no_posts": "Dieser Nutzer hat noch nichts gepostet.", @@ -85,5 +87,5 @@ "follow_topics_you_create": "Themen folgen, die du erstellst", "grouptitle": "Wähle den anzuzeigenden Gruppen Titel aus", "no-group-title": "Kein Gruppentitel", - "select-skin": "Select a Skin" + "select-skin": "Einen Skin auswählen" } \ No newline at end of file diff --git a/public/language/el/error.json b/public/language/el/error.json index 72a6a3e711..2ec20b7f4e 100644 --- a/public/language/el/error.json +++ b/public/language/el/error.json @@ -77,6 +77,8 @@ "cant-chat-with-yourself": "Δεν μπορείς να συνομιλήσεις με τον εαυτό σου!", "chat-restricted": "This user has restricted their chat messages. They must follow you before you can chat with them", "too-many-messages": "You have sent too many messages, please wait awhile.", + "invalid-chat-message": "Invalid chat message", + "chat-message-too-long": "Chat message is too long", "reputation-system-disabled": "Το σύστημα φήμης έχει απενεργοποιηθεί.", "downvoting-disabled": "Η καταψήφιση έχει απενεργοποιηθεί", "not-enough-reputation-to-downvote": "Δεν έχεις αρκετή φήμη για να καταψηφίσεις αυτή την δημοσίευση", diff --git a/public/language/el/groups.json b/public/language/el/groups.json index 4eb70181f2..1168d3260c 100644 --- a/public/language/el/groups.json +++ b/public/language/el/groups.json @@ -12,6 +12,9 @@ "invited.none": "There are no invited members at this time", "invited.uninvite": "Rescind Invitation", "invited.search": "Search for a user to invite to this group", + "invited.notification_title": "You have been invited to join %1", + "request.notification_title": "Group Membership Request from %1", + "request.notification_text": "%1 has requested to become a member of %2", "cover-instructions": "Drag and Drop a photo, drag to position, and hit Save", "cover-change": "Change", "cover-save": "Save", diff --git a/public/language/el/user.json b/public/language/el/user.json index 5639096008..450a6e3ea6 100644 --- a/public/language/el/user.json +++ b/public/language/el/user.json @@ -12,6 +12,7 @@ "delete_account": "Διαγραφή Λογαριασμού", "delete_account_confirm": "Είσαι σίγουρος/η πως θέλεις να διαγράψεις τον λογαριασμό σου;
Αυτή η ενέργεια δεν μπορεί να αναιρεθεί και δεν θα μπορέσεις να επανακτήσεις τα δεδομένα σου

Γράψε το όνομα χρήστη σου για να επιβεβαιώσεις πως θέλεις να καταστρέψεις αυτόν τον λογαριασμό.", "delete_this_account_confirm": "Are you sure you want to delete this account?
This action is irreversible and you will not be able to recover any data

", + "account-deleted": "Account deleted", "fullname": "Πλήρες Όνομα", "website": "Ιστοσελίδα", "location": "Τοποθεσία", @@ -52,6 +53,7 @@ "username_taken_workaround": "Το όνομα χρήστη που ζήτησες χρησιμοποιείται ήδη, οπότε το τροποποιήσαμε λίγο. Πλέον είσαι γνωστός/ή ώς %1", "upload_picture": "Ανέβασμα φωτογραφίας", "upload_a_picture": "Ανέβασε μια φωτογραφία", + "remove_uploaded_picture": "Remove Uploaded Picture", "image_spec": "Μπορείς να ανεβάσεις αρχεία τύπου PNG, JPG ή GIF μόνο", "settings": "Επιλογές", "show_email": "Εμφάνιση του email μου", diff --git a/public/language/en@pirate/error.json b/public/language/en@pirate/error.json index 065234c18d..f346641522 100644 --- a/public/language/en@pirate/error.json +++ b/public/language/en@pirate/error.json @@ -77,6 +77,8 @@ "cant-chat-with-yourself": "You can't chat with yourself!", "chat-restricted": "This user has restricted their chat messages. They must follow you before you can chat with them", "too-many-messages": "You have sent too many messages, please wait awhile.", + "invalid-chat-message": "Invalid chat message", + "chat-message-too-long": "Chat message is too long", "reputation-system-disabled": "Reputation system is disabled.", "downvoting-disabled": "Downvoting is disabled", "not-enough-reputation-to-downvote": "You do not have enough reputation to downvote this post", diff --git a/public/language/en@pirate/groups.json b/public/language/en@pirate/groups.json index 3bd63ad94a..1d1a6cf52b 100644 --- a/public/language/en@pirate/groups.json +++ b/public/language/en@pirate/groups.json @@ -12,6 +12,9 @@ "invited.none": "There are no invited members at this time", "invited.uninvite": "Rescind Invitation", "invited.search": "Search for a user to invite to this group", + "invited.notification_title": "You have been invited to join %1", + "request.notification_title": "Group Membership Request from %1", + "request.notification_text": "%1 has requested to become a member of %2", "cover-instructions": "Drag and Drop a photo, drag to position, and hit Save", "cover-change": "Change", "cover-save": "Save", diff --git a/public/language/en@pirate/user.json b/public/language/en@pirate/user.json index 65ee32db71..8c32d9de58 100644 --- a/public/language/en@pirate/user.json +++ b/public/language/en@pirate/user.json @@ -12,6 +12,7 @@ "delete_account": "Delete Account", "delete_account_confirm": "Are you sure you want to delete your account?
This action is irreversible and you will not be able to recover any of your data

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

", + "account-deleted": "Account deleted", "fullname": "Full Name", "website": "Website", "location": "Location", @@ -52,6 +53,7 @@ "username_taken_workaround": "The username you requested was already taken, so we have altered it slightly. You are now known as %1", "upload_picture": "Upload picture", "upload_a_picture": "Upload a picture", + "remove_uploaded_picture": "Remove Uploaded Picture", "image_spec": "You may only upload PNG, JPG, or GIF files", "settings": "Settings", "show_email": "Show My Email", diff --git a/public/language/en_GB/error.json b/public/language/en_GB/error.json index 3419301602..252e893d0d 100644 --- a/public/language/en_GB/error.json +++ b/public/language/en_GB/error.json @@ -100,6 +100,8 @@ "cant-chat-with-yourself": "You can't chat with yourself!", "chat-restricted": "This user has restricted their chat messages. They must follow you before you can chat with them", "too-many-messages": "You have sent too many messages, please wait awhile.", + "invalid-chat-message": "Invalid chat message", + "chat-message-too-long": "Chat message is too long", "reputation-system-disabled": "Reputation system is disabled.", "downvoting-disabled": "Downvoting is disabled", diff --git a/public/language/en_GB/groups.json b/public/language/en_GB/groups.json index 958efd13e0..d800d74b1c 100644 --- a/public/language/en_GB/groups.json +++ b/public/language/en_GB/groups.json @@ -14,6 +14,10 @@ "invited.none": "There are no invited members at this time", "invited.uninvite": "Rescind Invitation", "invited.search": "Search for a user to invite to this group", + "invited.notification_title": "You have been invited to join %1", + + "request.notification_title": "Group Membership Request from %1", + "request.notification_text": "%1 has requested to become a member of %2", "cover-instructions": "Drag and Drop a photo, drag to position, and hit Save", "cover-change": "Change", diff --git a/public/language/en_GB/user.json b/public/language/en_GB/user.json index b11db877cb..b4d3dcaaf5 100644 --- a/public/language/en_GB/user.json +++ b/public/language/en_GB/user.json @@ -13,6 +13,7 @@ "delete_account": "Delete Account", "delete_account_confirm": "Are you sure you want to delete your account?
This action is irreversible and you will not be able to recover any of your data

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

", + "account-deleted": "Account deleted", "fullname": "Full Name", "website": "Website", @@ -56,6 +57,7 @@ "upload_picture": "Upload picture", "upload_a_picture": "Upload a picture", + "remove_uploaded_picture" : "Remove Uploaded Picture", "image_spec": "You may only upload PNG, JPG, or GIF files", "settings": "Settings", diff --git a/public/language/en_US/error.json b/public/language/en_US/error.json index 447993b3ee..85770bc64d 100644 --- a/public/language/en_US/error.json +++ b/public/language/en_US/error.json @@ -77,6 +77,8 @@ "cant-chat-with-yourself": "You can't chat with yourself!", "chat-restricted": "This user has restricted their chat messages. They must follow you before you can chat with them", "too-many-messages": "You have sent too many messages, please wait awhile.", + "invalid-chat-message": "Invalid chat message", + "chat-message-too-long": "Chat message is too long", "reputation-system-disabled": "Reputation system is disabled.", "downvoting-disabled": "Downvoting is disabled", "not-enough-reputation-to-downvote": "You do not have enough reputation to downvote this post", diff --git a/public/language/en_US/groups.json b/public/language/en_US/groups.json index 38f3e8dc8a..e9af691f8c 100644 --- a/public/language/en_US/groups.json +++ b/public/language/en_US/groups.json @@ -12,6 +12,9 @@ "invited.none": "There are no invited members at this time", "invited.uninvite": "Rescind Invitation", "invited.search": "Search for a user to invite to this group", + "invited.notification_title": "You have been invited to join %1", + "request.notification_title": "Group Membership Request from %1", + "request.notification_text": "%1 has requested to become a member of %2", "cover-instructions": "Drag and Drop a photo, drag to position, and hit Save", "cover-change": "Change", "cover-save": "Save", diff --git a/public/language/en_US/pages.json b/public/language/en_US/pages.json index ebf02ba2c9..88b2ed946e 100644 --- a/public/language/en_US/pages.json +++ b/public/language/en_US/pages.json @@ -29,7 +29,7 @@ "account/posts": "Posts made by %1", "account/topics": "Topics created by %1", "account/groups": "%1's Groups", - "account/favourites": "%1's Favourite Posts", + "account/favourites": "%1's Favorite Posts", "account/settings": "User Settings", "account/watched": "Topics watched by %1", "maintenance.text": "%1 is currently undergoing maintenance. Please come back another time.", diff --git a/public/language/en_US/user.json b/public/language/en_US/user.json index e06df9e525..234b3efe73 100644 --- a/public/language/en_US/user.json +++ b/public/language/en_US/user.json @@ -12,6 +12,7 @@ "delete_account": "Delete Account", "delete_account_confirm": "Are you sure you want to delete your account?
This action is irreversible and you will not be able to recover any of your data

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

", + "account-deleted": "Account deleted", "fullname": "Full Name", "website": "Website", "location": "Location", @@ -52,6 +53,7 @@ "username_taken_workaround": "The username you requested was already taken, so we have altered it slightly. You are now known as %1", "upload_picture": "Upload picture", "upload_a_picture": "Upload a picture", + "remove_uploaded_picture": "Remove Uploaded Picture", "image_spec": "You may only upload PNG, JPG, or GIF files", "settings": "Settings", "show_email": "Show My Email", @@ -80,7 +82,7 @@ "browsing": "Browsing Settings", "open_links_in_new_tab": "Open outgoing links in new tab", "enable_topic_searching": "Enable In-Topic Searching", - "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen", + "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behavior and allow you to search through the entire topic, instead of what is only shown on screen", "follow_topics_you_reply_to": "Follow topics that you reply to", "follow_topics_you_create": "Follow topics you create", "grouptitle": "Select the group title you would like to display", diff --git a/public/language/es/error.json b/public/language/es/error.json index d4e2062425..31c6d2a5d4 100644 --- a/public/language/es/error.json +++ b/public/language/es/error.json @@ -77,6 +77,8 @@ "cant-chat-with-yourself": "¡No puedes conversar contigo mismo!", "chat-restricted": "Este usuario tiene restringidos los mensajes de chat. Los usuarios deben seguirte antes de que pueda charlar con ellos", "too-many-messages": "Has enviado demasiados mensajes, por favor espera un poco.", + "invalid-chat-message": "Invalid chat message", + "chat-message-too-long": "Chat message is too long", "reputation-system-disabled": "El sistema de reputación está deshabilitado.", "downvoting-disabled": "La votación negativa está deshabilitada.", "not-enough-reputation-to-downvote": "No tienes suficiente reputación para votar negativo este post", diff --git a/public/language/es/global.json b/public/language/es/global.json index 138161bb35..ca995941b2 100644 --- a/public/language/es/global.json +++ b/public/language/es/global.json @@ -33,7 +33,7 @@ "header.notifications": "Notificaciones", "header.search": "Buscar", "header.profile": "Perfil", - "header.navigation": "Navigation", + "header.navigation": "Navegación", "notifications.loading": "Cargando notificaciones", "chats.loading": "Cargando chats", "motd.welcome": "Bienvenido a NodeBB, la plataforma de debate del el futuro.", @@ -68,7 +68,7 @@ "recentposts": "Publicaciones recientes", "recentips": "IP's conectadas recientemente", "away": "Ausente", - "dnd": "Do not disturb", + "dnd": "No molestar", "invisible": "Invisible", "offline": "Desconectado", "email": "Email", diff --git a/public/language/es/groups.json b/public/language/es/groups.json index 6c7fde309f..ef924a38c6 100644 --- a/public/language/es/groups.json +++ b/public/language/es/groups.json @@ -12,6 +12,9 @@ "invited.none": "No hay miembros invitados en este momento", "invited.uninvite": "Cancelar invitación", "invited.search": "Buscar un usuario para invitar a este grupo", + "invited.notification_title": "You have been invited to join %1", + "request.notification_title": "Group Membership Request from %1", + "request.notification_text": "%1 has requested to become a member of %2", "cover-instructions": "Arrastra y suelta una foto, arrastra a la posición, y pulsa Guardar ", "cover-change": "Cambiar", "cover-save": "Guardar", diff --git a/public/language/es/modules.json b/public/language/es/modules.json index 7597bce0c3..7b3e15e5c3 100644 --- a/public/language/es/modules.json +++ b/public/language/es/modules.json @@ -5,7 +5,7 @@ "chat.no_active": "No tiene conversaciones activas.", "chat.user_typing": "%1 está escribiendo...", "chat.user_has_messaged_you": "%1 te ha enviado un mensaje.", - "chat.see_all": "See all chats", + "chat.see_all": "Ver todos los chats", "chat.no-messages": "Por favor, selecciona un contacto para ver el historial de mensajes", "chat.recent-chats": "Chats recientes", "chat.contacts": "Contactos", @@ -24,6 +24,6 @@ "composer.submit_and_lock": "Enviar y Bloquear", "composer.toggle_dropdown": "Alternar desplegable", "bootbox.ok": "OK", - "bootbox.cancel": "Cancel", - "bootbox.confirm": "Confirm" + "bootbox.cancel": "Cancelar", + "bootbox.confirm": "Confirmar" } \ No newline at end of file diff --git a/public/language/es/notifications.json b/public/language/es/notifications.json index 62d03601a7..5258be8a9a 100644 --- a/public/language/es/notifications.json +++ b/public/language/es/notifications.json @@ -1,7 +1,7 @@ { "title": "Notificaciones", "no_notifs": "No tienes nuevas notificaciones", - "see_all": "See all notifications", + "see_all": "Ver todas las notificaciones", "mark_all_read": "Marcar todas las notificaciones como leídas", "back_to_home": "Volver a %1", "outgoing_link": "Enlace externo", diff --git a/public/language/es/pages.json b/public/language/es/pages.json index 2e43b8021b..1e1f34addc 100644 --- a/public/language/es/pages.json +++ b/public/language/es/pages.json @@ -1,37 +1,37 @@ { "home": "Inicio", "unread": "Temas no leídos", - "popular-day": "Popular topics today", - "popular-week": "Popular topics this week", - "popular-month": "Popular topics this month", - "popular-alltime": "All time popular topics", + "popular-day": "Temas populares hoy", + "popular-week": "Temas populares de la semana", + "popular-month": "Temas populares del mes", + "popular-alltime": "Temas populares de siempre", "recent": "Temas recientes", - "users/online": "Online Users", - "users/latest": "Latest Users", - "users/sort-posts": "Users with the most posts", - "users/sort-reputation": "Users with the most reputation", - "users/map": "User Map", - "users/search": "User Search", + "users/online": "Conectados", + "users/latest": "Últimos usuarios", + "users/sort-posts": "Top por mensajes", + "users/sort-reputation": "Más reputados", + "users/map": "Actividad", + "users/search": "Buscar", "notifications": "Notificaciones", "tags": "Etiquetas", "tag": "Temas etiquetados en \"%1\"", - "register": "Register an account", - "login": "Login to your account", - "reset": "Reset your account password", - "categories": "Categories", - "groups": "Groups", - "group": "%1 group", + "register": "Registrar una cuenta", + "login": "Acceder a tu cuenta", + "reset": "Restablecer contraseña", + "categories": "Categorías", + "groups": "Grupos", + "group": "Grupo de %1", "chats": "Chats", - "chat": "Chatting with %1", - "account/edit": "Editing \"%1\"", - "account/following": "People %1 follows", - "account/followers": "People who follow %1", - "account/posts": "Posts made by %1", - "account/topics": "Topics created by %1", - "account/groups": "%1's Groups", - "account/favourites": "%1's Favourite Posts", - "account/settings": "User Settings", - "account/watched": "Topics watched by %1", + "chat": "Chateando con %1", + "account/edit": "Editando \"%1\"", + "account/following": "Gente que sigue %1", + "account/followers": "Seguidores de %1", + "account/posts": "Publicados por %1", + "account/topics": "Temas creados por %1", + "account/groups": "Grupos de %1", + "account/favourites": "Favoritos de %1", + "account/settings": "Preferencias", + "account/watched": "Temas seguidos por %1", "maintenance.text": "%1 está en mantenimiento actualmente. Por favor vuelva en otro momento.", "maintenance.messageIntro": "Adicionalmente, la administración ha dejado este mensaje:" } \ No newline at end of file diff --git a/public/language/es/user.json b/public/language/es/user.json index 2a795889e4..e2ba5d2853 100644 --- a/public/language/es/user.json +++ b/public/language/es/user.json @@ -12,6 +12,7 @@ "delete_account": "Eliminar cuenta", "delete_account_confirm": "¿Estás seguro de que quieres eliminar tu cuenta?
Esta acción es irreversible y no podrás recuperar ninguno de tus datos.

Introduce tu nombre de usuario para confirmar la eliminación de la cuenta.", "delete_this_account_confirm": "Esta seguro de eliminar este usuario?
Esta acción de irreversible y no podrás recuperar ningún dato

", + "account-deleted": "Account deleted", "fullname": "Nombre completo", "website": "Sitio web", "location": "Ubicación", @@ -52,6 +53,7 @@ "username_taken_workaround": "El nombre de usuario que has solicitada ya está siendo usado, por tanto lo hemos alterado ligeramente. Ahora eres conocido como %1.", "upload_picture": "Subir foto", "upload_a_picture": "Subir una foto", + "remove_uploaded_picture": "Remove Uploaded Picture", "image_spec": "Sólo puedes subir imágenes en formato PNG, JPG o GIF.", "settings": "Opciones", "show_email": "Mostrar mi correo electrónico", @@ -85,5 +87,5 @@ "follow_topics_you_create": "Seguir publicaciones que creas", "grouptitle": "Selecciona el título del grupo que deseas visualizar", "no-group-title": "Sin título de grupo", - "select-skin": "Select a Skin" + "select-skin": "Seleccionar una plantilla" } \ No newline at end of file diff --git a/public/language/et/error.json b/public/language/et/error.json index 415dfea720..abcbd7bd1c 100644 --- a/public/language/et/error.json +++ b/public/language/et/error.json @@ -77,6 +77,8 @@ "cant-chat-with-yourself": "Sa ei saa endaga vestelda!", "chat-restricted": "Kasutaja on piiranud sõnumite saatmist. Privaatsõnumi saatmiseks peab kasutaja sind jälgima", "too-many-messages": "Oled saatnud liiga palju sõnumeid, oota natukene.", + "invalid-chat-message": "Invalid chat message", + "chat-message-too-long": "Chat message is too long", "reputation-system-disabled": "Reputatsiooni süsteem ei ole aktiveeritud", "downvoting-disabled": "Negatiivsete häälte andmine ei ole võimaldatud", "not-enough-reputation-to-downvote": "Sul ei ole piisavalt reputatsiooni, et anda negatiivset hinnangut sellele postitusele.", diff --git a/public/language/et/groups.json b/public/language/et/groups.json index 5f1331155f..ce8f982982 100644 --- a/public/language/et/groups.json +++ b/public/language/et/groups.json @@ -12,6 +12,9 @@ "invited.none": "Hetkel ei ole kutsutud kasutajaid", "invited.uninvite": "Tühistage kutse", "invited.search": "Otsige kasutajat, keda kutsuda antud gruppi.", + "invited.notification_title": "You have been invited to join %1", + "request.notification_title": "Group Membership Request from %1", + "request.notification_text": "%1 has requested to become a member of %2", "cover-instructions": "Lohista kaanepilt siia ning vajuta salvesta", "cover-change": "Muuda", "cover-save": "Salvesta", diff --git a/public/language/et/user.json b/public/language/et/user.json index 62297ad120..d9c7db5749 100644 --- a/public/language/et/user.json +++ b/public/language/et/user.json @@ -12,6 +12,7 @@ "delete_account": "Kustuta kasutaja", "delete_account_confirm": "Oled kindel, et soovid oma kasutaja kustutada?
Pärast kustutamist pole võimalik andmeid ja kasutajat taastada.

Sisesta oma kasutajanimi, et kinnitada kasutaja kustutamine.", "delete_this_account_confirm": "Olete kindel, et soovide seda kasutajat kustutada?
Pärast seda pole võimalik kasutajat ja andmeid taastada

", + "account-deleted": "Account deleted", "fullname": "Täisnimi", "website": "Koduleht", "location": "Asukoht", @@ -52,6 +53,7 @@ "username_taken_workaround": "Kasutajanimi mida soovisid, ei olnud saadaval, seeg muutsime seda natukene. Sinu uus kasutajanimi on nüüd: %1", "upload_picture": "Laadi pilt", "upload_a_picture": "Lae pilt üles", + "remove_uploaded_picture": "Remove Uploaded Picture", "image_spec": "Failid peavad olema PNG, JPG või GIF vormingus.", "settings": "Seaded", "show_email": "Näita minu emaili", diff --git a/public/language/fa_IR/error.json b/public/language/fa_IR/error.json index 22c980a709..dbd206632b 100644 --- a/public/language/fa_IR/error.json +++ b/public/language/fa_IR/error.json @@ -77,6 +77,8 @@ "cant-chat-with-yourself": "شما نمی‌توانید با خودتان گفتگو کنید!", "chat-restricted": "این کاربر پیام های گفتگوی خود را محدود کرده است . آنها بایدشما را دنبال کنند تا اینکه شما بتوانید به آنها پیامی بفرستید", "too-many-messages": "شما پیامهای خیلی زیادی فرستاده اید، لطفا مدتی صبر نمایید", + "invalid-chat-message": "Invalid chat message", + "chat-message-too-long": "Chat message is too long", "reputation-system-disabled": "سیستم اعتبار غیر فعال شده است", "downvoting-disabled": "رای منفی غیر فعال شده است", "not-enough-reputation-to-downvote": "شما اعتبار کافی برای دادن رای منفی به این دیدگاه را ندارید.", diff --git a/public/language/fa_IR/groups.json b/public/language/fa_IR/groups.json index 023b83da7d..bfa5510293 100644 --- a/public/language/fa_IR/groups.json +++ b/public/language/fa_IR/groups.json @@ -12,6 +12,9 @@ "invited.none": "در حال حاضر هیچ کسی دعوت نشده است", "invited.uninvite": "لغو دعوت", "invited.search": "جستجو به دنبال کاربرانی به جهت دعوت به این گروه", + "invited.notification_title": "You have been invited to join %1", + "request.notification_title": "Group Membership Request from %1", + "request.notification_text": "%1 has requested to become a member of %2", "cover-instructions": "عکس را با کلیک موس بگیرید و بکشید به محل و رها کنید، سپس ذخیره را بزنید", "cover-change": "تغییر", "cover-save": "ذخیره", diff --git a/public/language/fa_IR/user.json b/public/language/fa_IR/user.json index 087ed224f2..0f553a3ff3 100644 --- a/public/language/fa_IR/user.json +++ b/public/language/fa_IR/user.json @@ -12,6 +12,7 @@ "delete_account": "حذف حساب کاربری", "delete_account_confirm": "آیا مطمئنید که میخواهید حساب کاربری خود را حذف کنید؟
این عمل غیر قابل بازگشت است و شما قادر نخواهید بود هیچ کدام از اطلاعات خود را بازیابی کنید./strong>

برای تایید حذف این حساب کاربری، نام کاربری خود را وارد کنید", "delete_this_account_confirm": " آیا مطمئنید که میخواهید این حساب کاربری را حذف کنید؟
این عمل غیر قابل بازگشت است و شما قادر نخواهید بود هیچ کدام از اطلاعات را بازیابی کنید.

", + "account-deleted": "Account deleted", "fullname": "نام و نام خانوادگی", "website": "تارنما", "location": "محل سکونت", @@ -52,6 +53,7 @@ "username_taken_workaround": "نام کاربری درخواستی شما در حال حاضر گرفته شده است، بنابراین ما آن را کمی تغییر داده‌ایم. شما هم‌اکنون با نام %1%1
", + "request.notification_title": "Group Membership Request from %1", + "request.notification_text": "%1 has requested to become a member of %2", "cover-instructions": "Vedä ja pudota kuva, raahaa oikeaan kohtaan ja paina Tallenna", "cover-change": "Change", "cover-save": "Tallenna", diff --git a/public/language/fi/user.json b/public/language/fi/user.json index 30bb4294c3..0c6603a6f4 100644 --- a/public/language/fi/user.json +++ b/public/language/fi/user.json @@ -12,6 +12,7 @@ "delete_account": "Poista käyttäjätili", "delete_account_confirm": "Oletko täysin varma, että haluat poistaa käyttäjätilisi?
Tätä toimintoa ei voi kumonta eikä poistettua dataa voida palauttaa

Syötä käyttäjänimesi vahvistaaksesi, että haluat poistaa tilisi.", "delete_this_account_confirm": "Are you sure you want to delete this account?
This action is irreversible and you will not be able to recover any data

", + "account-deleted": "Account deleted", "fullname": "Koko nimi", "website": "Kotisivu", "location": "Sijainti", @@ -52,6 +53,7 @@ "username_taken_workaround": "Pyytämäsi käyttäjänimi oli jo varattu, joten muutimme sitä hieman. Käyttäjänimesi on siis nyt %1", "upload_picture": "Lataa kuva", "upload_a_picture": "Lataa kuva", + "remove_uploaded_picture": "Remove Uploaded Picture", "image_spec": "Voit ladata vain PNG-, JPG- tai GIF-tiedostoja", "settings": "Asetukset", "show_email": "Näytä sähköpostiosoitteeni", diff --git a/public/language/fr/error.json b/public/language/fr/error.json index 4d92d5a7f0..5969dba4db 100644 --- a/public/language/fr/error.json +++ b/public/language/fr/error.json @@ -77,6 +77,8 @@ "cant-chat-with-yourself": "Vous ne pouvez chatter avec vous même !", "chat-restricted": "Cet utilisateur a restreint les ses messages de chat. Il doit d'abord vous suivre avant de pouvoir discuter avec lui.", "too-many-messages": "Vous avez envoyé trop de messages, veuillez patienter un instant.", + "invalid-chat-message": "Invalid chat message", + "chat-message-too-long": "Chat message is too long", "reputation-system-disabled": "Le système de réputation est désactivé", "downvoting-disabled": "Les votes négatifs ne sont pas autorisés", "not-enough-reputation-to-downvote": "Vous n'avez pas une réputation assez élevée pour noter négativement ce message", diff --git a/public/language/fr/groups.json b/public/language/fr/groups.json index 75bee36650..37dcd79a16 100644 --- a/public/language/fr/groups.json +++ b/public/language/fr/groups.json @@ -12,6 +12,9 @@ "invited.none": "Il n'y a aucun membre invité pour le moment", "invited.uninvite": "Résilier l'invitation", "invited.search": "Chercher un utilisateur a inviter dans ce groupe", + "invited.notification_title": "You have been invited to join %1", + "request.notification_title": "Group Membership Request from %1", + "request.notification_text": "%1 has requested to become a member of %2", "cover-instructions": "Glissez-déposez une image, ajustez la position, et cliquez sur Enregistrer", "cover-change": "Modifier", "cover-save": "Enregistrer", diff --git a/public/language/fr/modules.json b/public/language/fr/modules.json index cfb05ef8c2..6adb73b460 100644 --- a/public/language/fr/modules.json +++ b/public/language/fr/modules.json @@ -21,7 +21,7 @@ "composer.user_said_in": "%1 a dit dans %2 :", "composer.user_said": "%1 a dit :", "composer.discard": "Êtes-vous sûr de bien vouloir supprimer ce message ?", - "composer.submit_and_lock": "Soumettre et Verrouiller", + "composer.submit_and_lock": "Envoyer et verrouiller", "composer.toggle_dropdown": "Afficher/masquer le menu", "bootbox.ok": "OK", "bootbox.cancel": "Annuler", diff --git a/public/language/fr/user.json b/public/language/fr/user.json index 9cd36737a8..94dd27d002 100644 --- a/public/language/fr/user.json +++ b/public/language/fr/user.json @@ -12,6 +12,7 @@ "delete_account": "Supprimer le compte", "delete_account_confirm": "Êtes-vous sûr de vouloir supprimer votre compte?
Cette action est irréversible et vous ne serez pas en mesure de récupérer vos données

Entrez votre nom d'utilisateur pour confirmer que vous souhaitez détruire votre compte.", "delete_this_account_confirm": "Etes-vous sûr de vouloir supprimer ce compte?
Cette action est irréversible et vous ne pourrez récupérer aucune donnée.", + "account-deleted": "Account deleted", "fullname": "Nom", "website": "Site web", "location": "Emplacement", @@ -52,6 +53,7 @@ "username_taken_workaround": "Le nom d'utilisateur désiré est déjà utilisé, nous l'avons donc légèrement modifié. Vous êtes maintenant connu comme %1", "upload_picture": "Envoyer l'image", "upload_a_picture": "Envoyer une image", + "remove_uploaded_picture": "Remove Uploaded Picture", "image_spec": "Vous ne pouvez envoyer que des fichiers PNG, JPG ou GIF", "settings": "Paramètres", "show_email": "Afficher mon email", diff --git a/public/language/he/error.json b/public/language/he/error.json index d0cd6b0db7..2cdea3d222 100644 --- a/public/language/he/error.json +++ b/public/language/he/error.json @@ -77,6 +77,8 @@ "cant-chat-with-yourself": "לא ניתן לעשות צ'אט עם עצמך!", "chat-restricted": "משתמש זה חסם את הודעות הצ'אט שלו ממשתמשים זרים. המשתמש חייב לעקוב אחריך לפני שתוכל לשוחח איתו בצ'אט", "too-many-messages": "שלחת יותר מדי הודעות, אנא המתן לזמן מה.", + "invalid-chat-message": "Invalid chat message", + "chat-message-too-long": "Chat message is too long", "reputation-system-disabled": "מערכת המוניטין לא פעילה.", "downvoting-disabled": "היכולת להצביע נגד לא פעילה", "not-enough-reputation-to-downvote": "אין לך מספיק מוניטין כדי להוריד את הדירוג של פוסט זה", diff --git a/public/language/he/groups.json b/public/language/he/groups.json index b1637e28ac..ab8e0f3c60 100644 --- a/public/language/he/groups.json +++ b/public/language/he/groups.json @@ -12,6 +12,9 @@ "invited.none": "There are no invited members at this time", "invited.uninvite": "Rescind Invitation", "invited.search": "Search for a user to invite to this group", + "invited.notification_title": "You have been invited to join %1", + "request.notification_title": "Group Membership Request from %1", + "request.notification_text": "%1 has requested to become a member of %2", "cover-instructions": "גרור תמונה, הזז למיקום הרצוי ואז לחץ על שמור", "cover-change": "שנה", "cover-save": "שמור", diff --git a/public/language/he/user.json b/public/language/he/user.json index 53a7c3311c..b4d112a815 100644 --- a/public/language/he/user.json +++ b/public/language/he/user.json @@ -12,6 +12,7 @@ "delete_account": "מחק חשבון", "delete_account_confirm": "אתה בטוח שאתה רוצה למחוק את חשבונך?
פעולה זו לא ניתנת לשחזור ולא תוכל לגשת למידע שלך

הזן את שם המשתמש שלך על מנת לאשר שברצונך למחוק את חשבונך.", "delete_this_account_confirm": "Are you sure you want to delete this account?
This action is irreversible and you will not be able to recover any data

", + "account-deleted": "Account deleted", "fullname": "שם מלא", "website": "אתר", "location": "מיקום", @@ -52,6 +53,7 @@ "username_taken_workaround": "שם המשתמש שבחרת כבר תפוס, אז שינינו אותו מעט. שם המשתמש שלך כעת הוא %1", "upload_picture": "העלה תמונה", "upload_a_picture": "העלה תמונה", + "remove_uploaded_picture": "Remove Uploaded Picture", "image_spec": "ניתן להעלות תמונות בפורמט PNG, JPG או GIF בלבד", "settings": "הגדרות", "show_email": "פרסם את כתובת האימייל שלי", diff --git a/public/language/hu/error.json b/public/language/hu/error.json index 43409ab53e..e79bdb3bf3 100644 --- a/public/language/hu/error.json +++ b/public/language/hu/error.json @@ -77,6 +77,8 @@ "cant-chat-with-yourself": "Nem cseveghetsz magaddal!", "chat-restricted": "Ez a felhasználó korlátozta a chat beállításait. Csak akkor cseveghetsz vele, miután felvett a követettek közé téged", "too-many-messages": "Túl sok üzenetet küldtél, kérlek várj egy picit.", + "invalid-chat-message": "Invalid chat message", + "chat-message-too-long": "Chat message is too long", "reputation-system-disabled": "Hírnév funkció kikapcsolva.", "downvoting-disabled": "Leszavazás funkció kikapcsolva", "not-enough-reputation-to-downvote": "Nem rendelkezel elég Hírnév ponttal, hogy leszavazhasd ezt a hozzászólást", diff --git a/public/language/hu/groups.json b/public/language/hu/groups.json index 5c1ff14024..cdf0ac3fad 100644 --- a/public/language/hu/groups.json +++ b/public/language/hu/groups.json @@ -12,6 +12,9 @@ "invited.none": "There are no invited members at this time", "invited.uninvite": "Rescind Invitation", "invited.search": "Search for a user to invite to this group", + "invited.notification_title": "You have been invited to join %1", + "request.notification_title": "Group Membership Request from %1", + "request.notification_text": "%1 has requested to become a member of %2", "cover-instructions": "Drag and Drop a photo, drag to position, and hit Save", "cover-change": "Change", "cover-save": "Save", diff --git a/public/language/hu/user.json b/public/language/hu/user.json index ceef9ff878..81018e82ff 100644 --- a/public/language/hu/user.json +++ b/public/language/hu/user.json @@ -12,6 +12,7 @@ "delete_account": "Fiók törlése", "delete_account_confirm": "Biztosan törölni szeretnéd a fiókodat?
Ez a művelet nem visszafordítható, így ha folytatod, nem tudod majd visszaállítani az adataidat.

Amennyiben továbbra is törölni szeretnéd a fiókodat, add meg a felhasználónevedet!", "delete_this_account_confirm": "Are you sure you want to delete this account?
This action is irreversible and you will not be able to recover any data

", + "account-deleted": "Account deleted", "fullname": "Teljes Név", "website": "Weboldal", "location": "Lakhely", @@ -52,6 +53,7 @@ "username_taken_workaround": "A kívánt felhasználónév már foglalt, így változtatnunk kellett rajta egy kicsit. Mostantól %1 nicknév alatt vagy ismert.", "upload_picture": "Kép feltöltése", "upload_a_picture": "Egy kép feltöltése", + "remove_uploaded_picture": "Remove Uploaded Picture", "image_spec": "Csak PNG, JPG vagy GIF kiterjesztésű fájlokat tölthetsz fel", "settings": "Beállítások", "show_email": "E-mail címem mutatása", diff --git a/public/language/id/error.json b/public/language/id/error.json index 0df935dd38..e999e2c4b4 100644 --- a/public/language/id/error.json +++ b/public/language/id/error.json @@ -77,6 +77,8 @@ "cant-chat-with-yourself": "Kamu tidak dapat chat dengan akun sendiri", "chat-restricted": "Pengguna ini telah membatasi percakapa mereka. Mereka harus mengikutimu sebelum kamu dapat melakukan percakapan dengan mereka ", "too-many-messages": "You have sent too many messages, please wait awhile.", + "invalid-chat-message": "Invalid chat message", + "chat-message-too-long": "Chat message is too long", "reputation-system-disabled": "Sistem reputasi ditiadakan.", "downvoting-disabled": "Downvoting ditiadakan", "not-enough-reputation-to-downvote": "Tidak cukup reputation untuk downvote post ini", diff --git a/public/language/id/groups.json b/public/language/id/groups.json index b3b60e6d73..7b3441f9df 100644 --- a/public/language/id/groups.json +++ b/public/language/id/groups.json @@ -12,6 +12,9 @@ "invited.none": "There are no invited members at this time", "invited.uninvite": "Rescind Invitation", "invited.search": "Search for a user to invite to this group", + "invited.notification_title": "You have been invited to join %1", + "request.notification_title": "Group Membership Request from %1", + "request.notification_text": "%1 has requested to become a member of %2", "cover-instructions": "Drag and Drop a photo, drag to position, and hit Save", "cover-change": "Change", "cover-save": "Save", diff --git a/public/language/id/user.json b/public/language/id/user.json index f1aa290504..007b820825 100644 --- a/public/language/id/user.json +++ b/public/language/id/user.json @@ -12,6 +12,7 @@ "delete_account": "Hapus Akun", "delete_account_confirm": "Kamu yakin ingin menghapus akunmu?
Tindakan ini tidak dapat dibatalkan dan kamu tidak dapat mengembalikan seluruh data mu

Masukkan nama pengguna kamu untuk memastikan jika kamu benar-benar ingin menghapus akun ini.", "delete_this_account_confirm": "Are you sure you want to delete this account?
This action is irreversible and you will not be able to recover any data

", + "account-deleted": "Account deleted", "fullname": "Nama Lengkap", "website": "Website", "location": "Lokasi", @@ -52,6 +53,7 @@ "username_taken_workaround": "Nama pengguna yang kamu inginkan telah diambil, jadi kami merubahnya sedikit. Kamu saat ini dikenal sebagai %1", "upload_picture": "Unggah gambar/foto", "upload_a_picture": "Unggah sebuah gambar/foto", + "remove_uploaded_picture": "Remove Uploaded Picture", "image_spec": "Kamu hanya diijinkan mengunggah berkas dengan format PNG, JPG, atau GIF", "settings": "Pengaturan", "show_email": "Tampilkan Email Saya", diff --git a/public/language/it/error.json b/public/language/it/error.json index 6ec26dda02..e2710a7863 100644 --- a/public/language/it/error.json +++ b/public/language/it/error.json @@ -77,6 +77,8 @@ "cant-chat-with-yourself": "Non puoi chattare con te stesso!", "chat-restricted": "Questo utente ha ristretto i suoi messaggi in chat alle persone che segue. Per poter chattare con te ti deve prima seguire.", "too-many-messages": "Hai inviato troppi messaggi, aspetta un attimo.", + "invalid-chat-message": "Invalid chat message", + "chat-message-too-long": "Chat message is too long", "reputation-system-disabled": "Il sistema di reputazione è disabilitato.", "downvoting-disabled": "Il Downvoting è disabilitato", "not-enough-reputation-to-downvote": "Non hai i privilegi per votare negativamente questo post", diff --git a/public/language/it/global.json b/public/language/it/global.json index 320a7f9498..4d9ee25eee 100644 --- a/public/language/it/global.json +++ b/public/language/it/global.json @@ -33,7 +33,7 @@ "header.notifications": "Notifiche", "header.search": "Cerca", "header.profile": "Profilo", - "header.navigation": "Navigation", + "header.navigation": "Navigazione", "notifications.loading": "Caricamento delle Notifiche", "chats.loading": "Caricamento Messaggi", "motd.welcome": "Benvenuti in NodeBB, la piattaforma di discussione del futuro.", @@ -68,7 +68,7 @@ "recentposts": "Post Recenti", "recentips": "Indirizzi IP Recentemente Loggati", "away": "Non disponibile", - "dnd": "Do not disturb", + "dnd": "Non disturbare", "invisible": "Invisibile", "offline": "Non in linea", "email": "Email", diff --git a/public/language/it/groups.json b/public/language/it/groups.json index 1f30115a7d..e6da0d3204 100644 --- a/public/language/it/groups.json +++ b/public/language/it/groups.json @@ -12,6 +12,9 @@ "invited.none": "Non ci sono inviti in sospeso", "invited.uninvite": "Revoca invito", "invited.search": "Ricerca un utente da invitare in questo gruppo", + "invited.notification_title": "You have been invited to join %1", + "request.notification_title": "Group Membership Request from %1", + "request.notification_text": "%1 has requested to become a member of %2", "cover-instructions": "Drag and Drop una fotografia, spostarla ad una posizione, e premere Salva", "cover-change": "Cambia", "cover-save": "Salva", diff --git a/public/language/it/modules.json b/public/language/it/modules.json index a9ca9051a0..5d9b4a8e1d 100644 --- a/public/language/it/modules.json +++ b/public/language/it/modules.json @@ -5,7 +5,7 @@ "chat.no_active": "Non hai chat attive.", "chat.user_typing": "%1 sta scrivendo...", "chat.user_has_messaged_you": "%1 ti ha scritto.", - "chat.see_all": "See all chats", + "chat.see_all": "Vedi tutte le chat", "chat.no-messages": "Si prega di selezionare un destinatario per vedere la cronologia dei messaggi", "chat.recent-chats": "Chat Recenti", "chat.contacts": "Contatti", @@ -24,6 +24,6 @@ "composer.submit_and_lock": "Invia e Blocca", "composer.toggle_dropdown": "Mostra/Nascondi menu a discesa", "bootbox.ok": "OK", - "bootbox.cancel": "Cancel", - "bootbox.confirm": "Confirm" + "bootbox.cancel": "Annulla", + "bootbox.confirm": "Conferma" } \ No newline at end of file diff --git a/public/language/it/notifications.json b/public/language/it/notifications.json index b6052d0e92..031859daf3 100644 --- a/public/language/it/notifications.json +++ b/public/language/it/notifications.json @@ -1,7 +1,7 @@ { "title": "Notifiche", "no_notifs": "Non hai nuove notifiche", - "see_all": "See all notifications", + "see_all": "Vedi tutte le notifiche", "mark_all_read": "Segna tutte le notifiche come già lette", "back_to_home": "Indietro a %1", "outgoing_link": "Link in uscita", diff --git a/public/language/it/pages.json b/public/language/it/pages.json index 210feb51fb..fc632584c2 100644 --- a/public/language/it/pages.json +++ b/public/language/it/pages.json @@ -1,37 +1,37 @@ { "home": "Home", "unread": "Discussioni non lette", - "popular-day": "Popular topics today", - "popular-week": "Popular topics this week", - "popular-month": "Popular topics this month", - "popular-alltime": "All time popular topics", + "popular-day": "Discussioni popolari oggi", + "popular-week": "Discussioni popolari questa settimana", + "popular-month": "Discussioni popolari questo mese", + "popular-alltime": "Discussioni più popolari di sempre", "recent": "Discussioni Recenti", - "users/online": "Online Users", - "users/latest": "Latest Users", - "users/sort-posts": "Users with the most posts", - "users/sort-reputation": "Users with the most reputation", - "users/map": "User Map", - "users/search": "User Search", + "users/online": "Utenti Online", + "users/latest": "Ultimi Utenti", + "users/sort-posts": "Utenti maggiori contributori", + "users/sort-reputation": "Utenti con la reputazione più alta", + "users/map": "Mappa Utenti", + "users/search": "Ricerca Utenti", "notifications": "Notifiche", "tags": "Tags", "tag": "Discussioni taggate \"%1\"", - "register": "Register an account", - "login": "Login to your account", - "reset": "Reset your account password", - "categories": "Categories", - "groups": "Groups", - "group": "%1 group", - "chats": "Chats", - "chat": "Chatting with %1", - "account/edit": "Editing \"%1\"", - "account/following": "People %1 follows", - "account/followers": "People who follow %1", - "account/posts": "Posts made by %1", - "account/topics": "Topics created by %1", - "account/groups": "%1's Groups", - "account/favourites": "%1's Favourite Posts", - "account/settings": "User Settings", - "account/watched": "Topics watched by %1", + "register": "Registrati", + "login": "Autenticati", + "reset": "Resetta password", + "categories": "Categorie", + "groups": "Gruppi", + "group": "Gruppo %1", + "chats": "Chat", + "chat": "In chat con %1", + "account/edit": "Modificando \"%1\"", + "account/following": "Persone seguite da %1", + "account/followers": "Persone che seguono %1", + "account/posts": "Post creati da %1", + "account/topics": "Discussioni create da %1", + "account/groups": "Gruppi di %1", + "account/favourites": "Post Favoriti da %1", + "account/settings": "Impostazioni Utente", + "account/watched": "Discussioni osservate da %1", "maintenance.text": "%1 è attualmente in manutenzione. Per favore ritorna più tardi.", "maintenance.messageIntro": "Inoltre, l'amministratore ha lasciato questo messaggio:" } \ No newline at end of file diff --git a/public/language/it/user.json b/public/language/it/user.json index efdfafa7fe..a4e3b5e9e4 100644 --- a/public/language/it/user.json +++ b/public/language/it/user.json @@ -12,6 +12,7 @@ "delete_account": "Elimina Account", "delete_account_confirm": "Sei sicuro di voler cancellare il tuo account?
Questa azione è irreversibile e non potrai recuperare nessun dato

Inserisci il tuo username per confermare che vuoi eliminare questo account.", "delete_this_account_confirm": "Sei sicuro di voler cancellare questo account?
Questa azione è irreversibile e non potrai recuperare nessun dato

", + "account-deleted": "Account deleted", "fullname": "Nome e Cognome", "website": "Sito Internet", "location": "Località", @@ -52,6 +53,7 @@ "username_taken_workaround": "Il nome utente che hai richiesto era già stato utilizzato, quindi lo abbiamo modificato leggermente. Ora il tuo è %1", "upload_picture": "Carica foto", "upload_a_picture": "Carica una foto", + "remove_uploaded_picture": "Remove Uploaded Picture", "image_spec": "Puoi caricare solo file PNG, JPG o GIF", "settings": "Impostazioni", "show_email": "Mostra la mia Email", @@ -85,5 +87,5 @@ "follow_topics_you_create": "Segui le discussioni che hai iniziato", "grouptitle": "Seleziona il titolo del gruppo che vorresti vedere", "no-group-title": "Nessun titolo al gruppo", - "select-skin": "Select a Skin" + "select-skin": "Seleziona uno Skin" } \ No newline at end of file diff --git a/public/language/ja/error.json b/public/language/ja/error.json index 0fead7d402..e268aedd0c 100644 --- a/public/language/ja/error.json +++ b/public/language/ja/error.json @@ -77,6 +77,8 @@ "cant-chat-with-yourself": "自分にチャットすることはできません!", "chat-restricted": "This user has restricted their chat messages. They must follow you before you can chat with them", "too-many-messages": "You have sent too many messages, please wait awhile.", + "invalid-chat-message": "Invalid chat message", + "chat-message-too-long": "Chat message is too long", "reputation-system-disabled": "Reputation system is disabled.", "downvoting-disabled": "Downvoting is disabled", "not-enough-reputation-to-downvote": "You do not have enough reputation to downvote this post", diff --git a/public/language/ja/groups.json b/public/language/ja/groups.json index 3bd63ad94a..1d1a6cf52b 100644 --- a/public/language/ja/groups.json +++ b/public/language/ja/groups.json @@ -12,6 +12,9 @@ "invited.none": "There are no invited members at this time", "invited.uninvite": "Rescind Invitation", "invited.search": "Search for a user to invite to this group", + "invited.notification_title": "You have been invited to join %1", + "request.notification_title": "Group Membership Request from %1", + "request.notification_text": "%1 has requested to become a member of %2", "cover-instructions": "Drag and Drop a photo, drag to position, and hit Save", "cover-change": "Change", "cover-save": "Save", diff --git a/public/language/ja/user.json b/public/language/ja/user.json index 14b547545e..64846ea716 100644 --- a/public/language/ja/user.json +++ b/public/language/ja/user.json @@ -12,6 +12,7 @@ "delete_account": "Delete Account", "delete_account_confirm": "Are you sure you want to delete your account?
This action is irreversible and you will not be able to recover any of your data

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

", + "account-deleted": "Account deleted", "fullname": "フルネーム", "website": "ウェブサイト", "location": "ロケーション", @@ -52,6 +53,7 @@ "username_taken_workaround": "このユーザー名はすでに使用されています。いまのユーザー名は %1 です。", "upload_picture": "画像をアップロード", "upload_a_picture": "画像をアップロード", + "remove_uploaded_picture": "Remove Uploaded Picture", "image_spec": "PNG、JPGかGIFフォーマットの画像をアップロードしてください。", "settings": "設定", "show_email": "メールアドレスを表示", diff --git a/public/language/ko/error.json b/public/language/ko/error.json index 344fca5493..9477533623 100644 --- a/public/language/ko/error.json +++ b/public/language/ko/error.json @@ -77,6 +77,8 @@ "cant-chat-with-yourself": "자신과는 채팅할 수 없습니다.", "chat-restricted": "This user has restricted their chat messages. They must follow you before you can chat with them", "too-many-messages": "You have sent too many messages, please wait awhile.", + "invalid-chat-message": "Invalid chat message", + "chat-message-too-long": "Chat message is too long", "reputation-system-disabled": "인기도 시스템을 사용하지 않습니다.", "downvoting-disabled": "비추천 기능을 사용하지 않습니다.", "not-enough-reputation-to-downvote": "인기도가 낮아 이 게시물에 반대할 수 없습니다.", diff --git a/public/language/ko/groups.json b/public/language/ko/groups.json index b2e8a8ec48..f6c6e5a552 100644 --- a/public/language/ko/groups.json +++ b/public/language/ko/groups.json @@ -12,6 +12,9 @@ "invited.none": "There are no invited members at this time", "invited.uninvite": "Rescind Invitation", "invited.search": "Search for a user to invite to this group", + "invited.notification_title": "You have been invited to join %1", + "request.notification_title": "Group Membership Request from %1", + "request.notification_text": "%1 has requested to become a member of %2", "cover-instructions": "Drag and Drop a photo, drag to position, and hit Save", "cover-change": "Change", "cover-save": "Save", diff --git a/public/language/ko/user.json b/public/language/ko/user.json index 7f55b2c15b..f7716f5d84 100644 --- a/public/language/ko/user.json +++ b/public/language/ko/user.json @@ -12,6 +12,7 @@ "delete_account": "계정 삭제", "delete_account_confirm": "정말 계정을 삭제하시겠습니까?
이 동작은 되돌릴 수 없으며 삭제된 데이터도 복구할 수 없습니다.

계정 삭제를 원하면 사용자 이름을 입력하세요.", "delete_this_account_confirm": "Are you sure you want to delete this account?
This action is irreversible and you will not be able to recover any data

", + "account-deleted": "Account deleted", "fullname": "이름", "website": "웹 사이트", "location": "거주지", @@ -52,6 +53,7 @@ "username_taken_workaround": "새 사용자 이름이 이미 존재하여 %1로 저장되었습니다.", "upload_picture": "사진 업로드", "upload_a_picture": "사진 업로드", + "remove_uploaded_picture": "Remove Uploaded Picture", "image_spec": "PNG, JPG, GIF 파일만 업로드할 수 있습니다.", "settings": "설정", "show_email": "이메일 공개", diff --git a/public/language/lt/error.json b/public/language/lt/error.json index c7b5d1f3ba..28d5e4a7ed 100644 --- a/public/language/lt/error.json +++ b/public/language/lt/error.json @@ -77,6 +77,8 @@ "cant-chat-with-yourself": "Jūs negalite susirašinėti su savimi!", "chat-restricted": "Šis vartotojas apribojo savo žinutes. Jie turi sekti jus kad jūs galėtumėte pradėti bendrauti su jais", "too-many-messages": "Išsiuntėte per daug pranešimų, kurį laiką prašome palaukti.", + "invalid-chat-message": "Invalid chat message", + "chat-message-too-long": "Chat message is too long", "reputation-system-disabled": "Reputacijos sistema išjungta.", "downvoting-disabled": "Downvoting yra išjungtas", "not-enough-reputation-to-downvote": "Jūs neturite pakankamai reputacijos balsuoti prieš šį pranešimą", diff --git a/public/language/lt/groups.json b/public/language/lt/groups.json index bbd2c17770..15c03efcbb 100644 --- a/public/language/lt/groups.json +++ b/public/language/lt/groups.json @@ -12,6 +12,9 @@ "invited.none": "Nėra pakviestu narių šiuo momentu", "invited.uninvite": "Atšaukti pakvietimą", "invited.search": "Ieškoti nario kad pakviesti į šią grupę", + "invited.notification_title": "You have been invited to join %1", + "request.notification_title": "Group Membership Request from %1", + "request.notification_text": "%1 has requested to become a member of %2", "cover-instructions": "Vilkite čia nuotrauką, perkelkite į reikalingą poziciją ir spauskite Save", "cover-change": "Keisti", "cover-save": "Saugoti", diff --git a/public/language/lt/user.json b/public/language/lt/user.json index 3d57aa0174..1d5c62fa2d 100644 --- a/public/language/lt/user.json +++ b/public/language/lt/user.json @@ -12,6 +12,7 @@ "delete_account": "Ištrinti paskyrą", "delete_account_confirm": "Ar tikrai norite ištrinti savo paskyrą?
Šis veiksmas yra negrįžtamas, ir jūs negalėsite susigrąžinti jokių duomenų

Įveskite savo vardą, kad patvirtintumėte, jog norite panaikinti šią paskyrą.", "delete_this_account_confirm": "Ar jūs tikrai norite ištrint šią paskyrą?
Šis veiksmas nebesugražinamas ir jūs nebegalėsite atgauti jokių duomenų

", + "account-deleted": "Account deleted", "fullname": "Vardas ir pavardė", "website": "Tinklalapis", "location": "Vieta", @@ -52,6 +53,7 @@ "username_taken_workaround": "Jūsų norimas vartotojo vardas jau užimtas, todėl mes jį šiek tiek pakeitėme. Dabar jūs esate žinomas kaip %1", "upload_picture": "Įkelti paveikslėlį", "upload_a_picture": "Įkelti paveikslėlį", + "remove_uploaded_picture": "Remove Uploaded Picture", "image_spec": "Galite įkelti tik PNG, JPG arba GIF failus", "settings": "Nustatymai", "show_email": "Rodyti mano el. paštą viešai", diff --git a/public/language/ms/error.json b/public/language/ms/error.json index d346472fff..eae120920b 100644 --- a/public/language/ms/error.json +++ b/public/language/ms/error.json @@ -77,6 +77,8 @@ "cant-chat-with-yourself": "Anda tidak boleh sembang dengan diri sendiri!", "chat-restricted": "Pengguna ini menyekat ruangan sembangnya. Dia hendaklah mengikut anda sebelum kalian dapat bersembang", "too-many-messages": "Anda menghantar terlalu banyak pesanan, sila tunggu seketika.", + "invalid-chat-message": "Invalid chat message", + "chat-message-too-long": "Chat message is too long", "reputation-system-disabled": "Sistem reputasi dilumpuhkan.", "downvoting-disabled": "Undi turun dilumpuhkan", "not-enough-reputation-to-downvote": "Anda tidak mempunyai reputasi mencukupi untuk mengundi turun kiriman ini", diff --git a/public/language/ms/groups.json b/public/language/ms/groups.json index 9a83a75da2..7d93f2b906 100644 --- a/public/language/ms/groups.json +++ b/public/language/ms/groups.json @@ -12,6 +12,9 @@ "invited.none": "Tiada ahli yang dijemput buat masa ini", "invited.uninvite": "Batalkan Jemputan", "invited.search": "Cari pengguna untuk dijemput ke kumpulan ini", + "invited.notification_title": "You have been invited to join %1", + "request.notification_title": "Group Membership Request from %1", + "request.notification_text": "%1 has requested to become a member of %2", "cover-instructions": "Seret dan lepas gambar, lepas ke posisi, dan tekan Simpan", "cover-change": "Ubah", "cover-save": "Simpan", diff --git a/public/language/ms/user.json b/public/language/ms/user.json index 2fa7ba79af..7e7dc8f656 100644 --- a/public/language/ms/user.json +++ b/public/language/ms/user.json @@ -12,6 +12,7 @@ "delete_account": "Padam Akaun", "delete_account_confirm": "Anda yakin untuk padam akaun anda?
Pebuatan ini tidak boleh diundur dan anda tidak boleh memulihkan sebarang data anda.

Masukkan nama pengguna anda untuk memastikan anda benar-benar ingin memadam akaun ini.", "delete_this_account_confirm": "Anda yakin untuk padam akaum ini?
Pebuatan ini tidak boleh diundur dan anda tidak boleh memulihkan sebarang data

", + "account-deleted": "Account deleted", "fullname": "Nama penuh", "website": "Laman Web", "location": "Lokasi", @@ -52,6 +53,7 @@ "username_taken_workaround": "Nama pengguna yang anda minta telah digunakan oleh orang lain, jadi kami telah mengubahsuaikannya sedikit. Anda kini dikenali sebagai %1", "upload_picture": "Muatnaik gambar", "upload_a_picture": "Muatnaik sekeping gambar", + "remove_uploaded_picture": "Remove Uploaded Picture", "image_spec": "Anda hanya boleh memuatnaikkan fail PNG, JPG atau GIF", "settings": "Tetapan", "show_email": "Tunjukkan emel saya", diff --git a/public/language/nb/error.json b/public/language/nb/error.json index fdb5a62f86..315ca61e42 100644 --- a/public/language/nb/error.json +++ b/public/language/nb/error.json @@ -77,6 +77,8 @@ "cant-chat-with-yourself": "Du kan ikke chatte med deg selv!", "chat-restricted": "Denne brukeren har begrenset sine chat-meldinger. De må følge deg før du kan chatte med dem", "too-many-messages": "Du har sendt for mange meldinger, vennligst vent en stund.", + "invalid-chat-message": "Invalid chat message", + "chat-message-too-long": "Chat message is too long", "reputation-system-disabled": "Ryktesystem er deaktivert.", "downvoting-disabled": "Nedstemming av deaktivert", "not-enough-reputation-to-downvote": "Du har ikke nok rykte til å nedstemme det innlegget", diff --git a/public/language/nb/groups.json b/public/language/nb/groups.json index a2028bd35f..6efc44218a 100644 --- a/public/language/nb/groups.json +++ b/public/language/nb/groups.json @@ -12,6 +12,9 @@ "invited.none": "Det er ingen inviterte medlemmer på dette tidspunktet", "invited.uninvite": "Trekk tilbake invitasjon", "invited.search": "Søk etter en bruker å invitere til denne gruppen", + "invited.notification_title": "You have been invited to join %1", + "request.notification_title": "Group Membership Request from %1", + "request.notification_text": "%1 has requested to become a member of %2", "cover-instructions": "Dra og slipp et bilde, dra til posisjon, og trykk Lagre", "cover-change": "Endre", "cover-save": "Lagre", diff --git a/public/language/nb/user.json b/public/language/nb/user.json index 4d56c93279..eb889a8ab3 100644 --- a/public/language/nb/user.json +++ b/public/language/nb/user.json @@ -12,6 +12,7 @@ "delete_account": "Slett konto", "delete_account_confirm": "Er du sikker på at du vil slette kontoen din?
Dette kan ikke reverseres, og du vil ikke kunne hente tilbake dine data.

Skriv inn ditt brukernavn for å bekrefte at du vil tilintetgjøre denne kontoen.", "delete_this_account_confirm": "Er du sikker på at du vil slette denne kontoen?
Denne handlingen kan ikke reverseres og du vil ikke kunne gjenopprette noe data

", + "account-deleted": "Account deleted", "fullname": "Fullt navn", "website": "Nettsted", "location": "Plassering", @@ -52,6 +53,7 @@ "username_taken_workaround": "Brukernavnet du ønsket er opptatt, så vi har endret ditt litt. Du er nå kjent som %1", "upload_picture": "Last opp bilde", "upload_a_picture": "Last opp et bilde", + "remove_uploaded_picture": "Remove Uploaded Picture", "image_spec": "Du kan bare laste opp PNG, JPG eller GIF-filer", "settings": "Innstillinger", "show_email": "Vis min e-post", diff --git a/public/language/nl/error.json b/public/language/nl/error.json index e9840b8bf9..cc4a39e88d 100644 --- a/public/language/nl/error.json +++ b/public/language/nl/error.json @@ -77,6 +77,8 @@ "cant-chat-with-yourself": "Het is niet mogelijk met jezelf een chatgesprek te houden.", "chat-restricted": "Deze gebruiker heeft beperkingen aan de chatfunctie opgelegd waardoor deze eerst iemand moet volgen voordat deze persoon een nieuwe chat mag initiëren.", "too-many-messages": "Er zijn in korte tijd teveel berichten verzonden, een moment geduld.", + "invalid-chat-message": "Invalid chat message", + "chat-message-too-long": "Chat message is too long", "reputation-system-disabled": "Reputatie systeem is uitgeschakeld.", "downvoting-disabled": "Negatief stemmen staat uitgeschakeld.", "not-enough-reputation-to-downvote": "Deze gebruikersaccount beschikt over onvoldoende reputatie om een negatieve stem uit te mogen brengen.", diff --git a/public/language/nl/global.json b/public/language/nl/global.json index 8cdd5286e3..3da3a88d9f 100644 --- a/public/language/nl/global.json +++ b/public/language/nl/global.json @@ -33,7 +33,7 @@ "header.notifications": "Notificaties", "header.search": "Zoeken", "header.profile": "Profiel", - "header.navigation": "Navigation", + "header.navigation": "Navigatie", "notifications.loading": "Notificaties laden", "chats.loading": "Chats laden", "motd.welcome": "Welkom bij NodeBB, het discussie platform van de toekomst.", @@ -68,7 +68,7 @@ "recentposts": "Recente berichten", "recentips": "IP-adressen van recente gebruikers", "away": "Afwezig", - "dnd": "Do not disturb", + "dnd": "Niet Storen", "invisible": "Onzichtbaar", "offline": "Offline", "email": "E-mailadres", diff --git a/public/language/nl/groups.json b/public/language/nl/groups.json index b6e433dfed..f50bc09e5d 100644 --- a/public/language/nl/groups.json +++ b/public/language/nl/groups.json @@ -12,6 +12,9 @@ "invited.none": "Er zijn geen uitgenodigde leden op het moment", "invited.uninvite": "Uitnodiging intrekken", "invited.search": "Zoek naar een gebruiker om uit te nodigen voor deze groep", + "invited.notification_title": "You have been invited to join %1", + "request.notification_title": "Group Membership Request from %1", + "request.notification_text": "%1 has requested to become a member of %2", "cover-instructions": "Sleep een afbeelding, sleep om te positioneren en klik tenslotte op Opslaan", "cover-change": "Bewerken", "cover-save": "Opslaan", diff --git a/public/language/nl/modules.json b/public/language/nl/modules.json index 538c5c9436..2324798d39 100644 --- a/public/language/nl/modules.json +++ b/public/language/nl/modules.json @@ -5,7 +5,7 @@ "chat.no_active": "Er zijn geen actieve chats.", "chat.user_typing": "%1 is aan het typen ...", "chat.user_has_messaged_you": "%1 heeft een bericht gestuurd", - "chat.see_all": "See all chats", + "chat.see_all": "Laat alle chats zien", "chat.no-messages": "Selecteer een ontvanger om de chatgeschiedenis in te zien", "chat.recent-chats": "Recent gevoerde gesprekken", "chat.contacts": "Contacten", @@ -24,6 +24,6 @@ "composer.submit_and_lock": "Plaats een bericht en vergrendel direct het onderwerp", "composer.toggle_dropdown": "Keuzelijst schakelen", "bootbox.ok": "OK", - "bootbox.cancel": "Cancel", - "bootbox.confirm": "Confirm" + "bootbox.cancel": "Anuleren", + "bootbox.confirm": "Bevestig" } \ No newline at end of file diff --git a/public/language/nl/notifications.json b/public/language/nl/notifications.json index a53316b83e..d8ee5b7287 100644 --- a/public/language/nl/notifications.json +++ b/public/language/nl/notifications.json @@ -1,7 +1,7 @@ { "title": "Notificaties", "no_notifs": "Je hebt geen nieuwe notificaties", - "see_all": "See all notifications", + "see_all": "Laat alle notificaties zien", "mark_all_read": "Markeer alles als gelezen", "back_to_home": "Terug naar %1", "outgoing_link": "Uitgaande Link", diff --git a/public/language/nl/pages.json b/public/language/nl/pages.json index bc944d347e..220ea1bc3b 100644 --- a/public/language/nl/pages.json +++ b/public/language/nl/pages.json @@ -1,37 +1,37 @@ { "home": "Home", "unread": "Ongelezen onderwerpen", - "popular-day": "Popular topics today", - "popular-week": "Popular topics this week", - "popular-month": "Popular topics this month", - "popular-alltime": "All time popular topics", + "popular-day": "De populaire onderwerpen van vandaag", + "popular-week": "De populaire onderwerpen van deze week", + "popular-month": "De populaire onderwerpen van deze maand", + "popular-alltime": "De populaire onderwerpen", "recent": "Recente onderwerpen", - "users/online": "Online Users", - "users/latest": "Latest Users", - "users/sort-posts": "Users with the most posts", - "users/sort-reputation": "Users with the most reputation", - "users/map": "User Map", - "users/search": "User Search", + "users/online": "Online Gebruikers", + "users/latest": "Meest recente gebruikers", + "users/sort-posts": "Gebruikers met de meeste berichten", + "users/sort-reputation": "Gebruikers met de meeste reputatie", + "users/map": "Gebruikersmap", + "users/search": "Zoek Gebruiker", "notifications": "Notificaties", "tags": "Tags", "tag": "Onderwerpen geplaatst onder \"%1\"", - "register": "Register an account", - "login": "Login to your account", - "reset": "Reset your account password", - "categories": "Categories", - "groups": "Groups", - "group": "%1 group", + "register": "Registeer een gebruikersaccount", + "login": "Login met u gebruikersaccount in", + "reset": "Gebruikerswachtwoord opnieuw instellen", + "categories": "Categorieën", + "groups": "Groepen", + "group": "%1's groep", "chats": "Chats", - "chat": "Chatting with %1", - "account/edit": "Editing \"%1\"", - "account/following": "People %1 follows", - "account/followers": "People who follow %1", - "account/posts": "Posts made by %1", - "account/topics": "Topics created by %1", - "account/groups": "%1's Groups", - "account/favourites": "%1's Favourite Posts", - "account/settings": "User Settings", - "account/watched": "Topics watched by %1", + "chat": "Chatten met %1", + "account/edit": "\"%1\" aanpassen", + "account/following": "Door %1 gevolgd", + "account/followers": "Die %1 volgen", + "account/posts": "Berichten geplaatst door %1", + "account/topics": "Onderwerpen begonnen door %1", + "account/groups": "%1's groepen", + "account/favourites": "Favoriete berichten van %1", + "account/settings": "Gebruikersinstellingen", + "account/watched": "Berichten die door %1 bekeken worden", "maintenance.text": "%1 is momenteel in onderhoud. Excuses voor het ongemak en probeer het later nog eens", "maintenance.messageIntro": "Daarnaast heeft de beheerder het volgende bericht achtergelaten:" } \ No newline at end of file diff --git a/public/language/nl/user.json b/public/language/nl/user.json index 343498373a..7ccbc8ee09 100644 --- a/public/language/nl/user.json +++ b/public/language/nl/user.json @@ -12,6 +12,7 @@ "delete_account": "Account verwijderen", "delete_account_confirm": "Controleer of dat het zeker is dat deze account verwijderd moet worden.
Deze actie kan niet ongedaan gemaakt worden en herstellen van gebruiker- of profielgegevens is niet mogelijk

Typ hier de gebruikersnaam als extra controle om te bevestigen dat deze account verwijderd moet worden.", "delete_this_account_confirm": "Weet u zeker dat u deze account wilt verwijderen?
Deze actie kan niet ongedaan worden en u kunt niet de informatie herstellen

", + "account-deleted": "Account deleted", "fullname": "Volledige naam", "website": "Website", "location": "Locatie", @@ -52,6 +53,7 @@ "username_taken_workaround": "Helaas, de gewenste gebruikersnaam is al door iemand in gebruik genomen dus vandaar een kleine aanpassing naar %1 doorgevoerd", "upload_picture": "Upload afbeelding", "upload_a_picture": "Upload een afbeelding", + "remove_uploaded_picture": "Remove Uploaded Picture", "image_spec": "Alleen afbeeldingsbestanden van het type PNG, JPG/JPEG, of GIF worden ondersteund.", "settings": "Instellingen", "show_email": "Inschakelen weergave van e-mailadres op profielpagina", @@ -85,5 +87,5 @@ "follow_topics_you_create": "Volg de onderwerpen waarvan ik de oorspronkelijke auteur ben", "grouptitle": "Selecteer de groepstitel voor weergave", "no-group-title": "Geen groepstitel", - "select-skin": "Select a Skin" + "select-skin": "Selecteer een uiterlijk" } \ No newline at end of file diff --git a/public/language/pl/error.json b/public/language/pl/error.json index 198c58b01a..7af713ba97 100644 --- a/public/language/pl/error.json +++ b/public/language/pl/error.json @@ -77,6 +77,8 @@ "cant-chat-with-yourself": "Nie możesz rozmawiać ze sobą", "chat-restricted": "Ten użytkownik ograniczył swoje czaty. Musi Cię śledzić, zanim będziesz mógł z nim czatować.", "too-many-messages": "Wysłałeś zbyt wiele wiadomości, proszę poczekaj chwilę.", + "invalid-chat-message": "Invalid chat message", + "chat-message-too-long": "Chat message is too long", "reputation-system-disabled": "System reputacji został wyłączony", "downvoting-disabled": "Ocena postów jest wyłączona", "not-enough-reputation-to-downvote": "Masz za mało reputacji by ocenić ten post.", diff --git a/public/language/pl/groups.json b/public/language/pl/groups.json index 3efc724871..243f60e65b 100644 --- a/public/language/pl/groups.json +++ b/public/language/pl/groups.json @@ -12,6 +12,9 @@ "invited.none": "There are no invited members at this time", "invited.uninvite": "Rescind Invitation", "invited.search": "Search for a user to invite to this group", + "invited.notification_title": "You have been invited to join %1", + "request.notification_title": "Group Membership Request from %1", + "request.notification_text": "%1 has requested to become a member of %2", "cover-instructions": "Przeciągnij i upuść zdjęcie, ustaw w odpowiedniej pozycji i kliknij Zapisz", "cover-change": "Zmień", "cover-save": "Zapisz", diff --git a/public/language/pl/user.json b/public/language/pl/user.json index c586b46b3e..d15f6ec2ad 100644 --- a/public/language/pl/user.json +++ b/public/language/pl/user.json @@ -12,6 +12,7 @@ "delete_account": "Skasuj konto", "delete_account_confirm": "Jesteś pewny, że chcesz skasować swoje konto?
Tej operacji nie można cofnąć i utracisz wszystkie swoje dane

Podaj swoją nazwę użytkownika, by potwierdzić skasowanie konta.", "delete_this_account_confirm": "Are you sure you want to delete this account?
This action is irreversible and you will not be able to recover any data

", + "account-deleted": "Account deleted", "fullname": "Pełna nazwa", "website": "Strona WWW", "location": "Położenie", @@ -52,6 +53,7 @@ "username_taken_workaround": "Wybrany login jest już zajęty, więc zmieniliśmy go trochę. Proponujemy %1", "upload_picture": "Prześlij zdjęcie", "upload_a_picture": "Prześlij zdjęcie", + "remove_uploaded_picture": "Remove Uploaded Picture", "image_spec": "Możesz przesłać tylko pliki PNG, JPG lub GIF.", "settings": "Ustawienia", "show_email": "Wyświetlaj mój adres e-mail", diff --git a/public/language/pt_BR/category.json b/public/language/pt_BR/category.json index c8802e7429..e459be58a1 100644 --- a/public/language/pt_BR/category.json +++ b/public/language/pt_BR/category.json @@ -1,12 +1,12 @@ { - "category": "Category", - "subcategories": "Subcategories", + "category": "Categoria", + "subcategories": "Subcategorias", "new_topic_button": "Novo Tópico", "guest-login-post": "Logue-se para postar", "no_topics": "Não tem nenhum tópico nesta categoria.
Por que você não tenta postar o algum?", "browsing": "navegando", "no_replies": "Ninguém respondeu", - "no_new_posts": "No new posts.", + "no_new_posts": "Nenhum post novo.", "share_this_category": "Compartilhar esta categoria", "watch": "Assistir", "ignore": "Ignorar", diff --git a/public/language/pt_BR/error.json b/public/language/pt_BR/error.json index 75959666a7..90d808d88c 100644 --- a/public/language/pt_BR/error.json +++ b/public/language/pt_BR/error.json @@ -46,8 +46,8 @@ "too-many-posts-newbie": "Como novo usuário, você pode postar uma vez a cada %1 segundo(s) até que você tenha recebido reputação de %2 - por favor aguarde antes de postar novamente", "tag-too-short": "Por favor digite uma tag maior. Tags devem conter pelo menos %1 caractere(s)", "tag-too-long": "Por favor digite uma tag menor. Tags não podem conter mais que %1 caractere(s)", - "not-enough-tags": "Not enough tags. Topics must have at least %1 tag(s)", - "too-many-tags": "Too many tags. Topics can't have more than %1 tag(s)", + "not-enough-tags": "Sem tags suficientes. Tópicos devem ter no mínimo %1 tag(s)", + "too-many-tags": "Muitas tags. Tópicos não podem ter mais que %1 tag(s)", "file-too-big": "O tamanho máximo permitido de arquivo é %1 kB - por favor faça upload de um arquivo menor", "cant-vote-self-post": "Você não pode votar no seu próprio post", "already-favourited": "Você já adicionou este post aos favoritos", @@ -77,6 +77,8 @@ "cant-chat-with-yourself": "Você não pode iniciar um chat consigo mesmo!", "chat-restricted": "Este usuário restringiu suas mensagens de chat. Eles devem seguir você antes que você possa conversar com eles", "too-many-messages": "Você enviou muitas mensagens, por favor aguarde um momento.", + "invalid-chat-message": "Invalid chat message", + "chat-message-too-long": "Chat message is too long", "reputation-system-disabled": "O sistema de reputação está desabilitado.", "downvoting-disabled": "Negativação está desabilitada", "not-enough-reputation-to-downvote": "Você não possui reputação suficiente para negativar este post", diff --git a/public/language/pt_BR/global.json b/public/language/pt_BR/global.json index 986fd7344b..0b3b2d40ff 100644 --- a/public/language/pt_BR/global.json +++ b/public/language/pt_BR/global.json @@ -22,7 +22,7 @@ "pagination.out_of": "%1 de %2", "pagination.enter_index": "Digite o índice", "header.admin": "Admin", - "header.categories": "Categories", + "header.categories": "Categorias", "header.recent": "Recente", "header.unread": "Não Lido", "header.tags": "Tags", @@ -33,7 +33,7 @@ "header.notifications": "Notificações", "header.search": "Procurar", "header.profile": "Perfil", - "header.navigation": "Navigation", + "header.navigation": "Navegação", "notifications.loading": "Carregando Notificações", "chats.loading": "Carregando Chats", "motd.welcome": "Seja bem-vindo ao NodeBB, a plataforma de discussão do futuro.", @@ -68,7 +68,7 @@ "recentposts": "Posts Recentes", "recentips": "Recentemente Logado nos IPs", "away": "Ausente", - "dnd": "Do not disturb", + "dnd": "Não perturbar", "invisible": "Invisível", "offline": "Offline", "email": "Email", diff --git a/public/language/pt_BR/groups.json b/public/language/pt_BR/groups.json index 04fe94a34b..9f68a4455c 100644 --- a/public/language/pt_BR/groups.json +++ b/public/language/pt_BR/groups.json @@ -12,6 +12,9 @@ "invited.none": "Não há membros convidados no momento", "invited.uninvite": "Anular Convite", "invited.search": "Procure por um usuário para convidar para esse grupo", + "invited.notification_title": "You have been invited to join %1", + "request.notification_title": "Group Membership Request from %1", + "request.notification_text": "%1 has requested to become a member of %2", "cover-instructions": "Arraste uma foto, arraste para a posição correta e clique em Salvar", "cover-change": "Alterar", "cover-save": "Salvar", @@ -38,13 +41,13 @@ "details.private_help": "Se habilitado, a entrada nos grupos requer aprovação de um dos donos do grupo", "details.hidden": "Oculto", "details.hidden_help": "Se habilitado, este grupo não se encontrará na listagem de grupos e os usuários terão de ser convivados manualmente", - "details.delete_group": "Delete Group", + "details.delete_group": "Deletar Grupo", "event.updated": "Os detalhes do grupo foram atualizados", "event.deleted": "O grupo \"%1\" foi deletado", - "membership.accept-invitation": "Accept Invitation", - "membership.invitation-pending": "Invitation Pending", - "membership.join-group": "Join Group", - "membership.leave-group": "Leave Group", - "membership.reject": "Reject", - "new-group.group_name": "Group Name:" + "membership.accept-invitation": "Aceitar Convite", + "membership.invitation-pending": "Convite Pendente", + "membership.join-group": "Entrar no Grupo", + "membership.leave-group": "Deixar Grupo", + "membership.reject": "Rejeitar", + "new-group.group_name": "Nome do Grupo:" } \ No newline at end of file diff --git a/public/language/pt_BR/modules.json b/public/language/pt_BR/modules.json index f9a8e4141e..8b4633e424 100644 --- a/public/language/pt_BR/modules.json +++ b/public/language/pt_BR/modules.json @@ -5,7 +5,7 @@ "chat.no_active": "Você não tem chats ativos.", "chat.user_typing": "%1 está digitando ...", "chat.user_has_messaged_you": "%1 te enviou uma mensagem.", - "chat.see_all": "See all chats", + "chat.see_all": "Ver todos os chats", "chat.no-messages": "Por favor, escolha um destinatário para visualizar o histórico de conversas", "chat.recent-chats": "Conversas Recentes", "chat.contacts": "Contatos", @@ -24,6 +24,6 @@ "composer.submit_and_lock": "Envie e Tranque", "composer.toggle_dropdown": "Alternar Dropdown", "bootbox.ok": "OK", - "bootbox.cancel": "Cancel", - "bootbox.confirm": "Confirm" + "bootbox.cancel": "Cancelar", + "bootbox.confirm": "Confirmar" } \ No newline at end of file diff --git a/public/language/pt_BR/notifications.json b/public/language/pt_BR/notifications.json index f25f1c2ead..11337049c7 100644 --- a/public/language/pt_BR/notifications.json +++ b/public/language/pt_BR/notifications.json @@ -1,7 +1,7 @@ { "title": "Notificações", "no_notifs": "Você não tem nenhuma notificação nova", - "see_all": "See all notifications", + "see_all": "Ver todas as notificações", "mark_all_read": "Marcas todas as notificações como lidas", "back_to_home": "Voltar para %1", "outgoing_link": "Link Externo", diff --git a/public/language/pt_BR/pages.json b/public/language/pt_BR/pages.json index abb3bd3883..565328edb3 100644 --- a/public/language/pt_BR/pages.json +++ b/public/language/pt_BR/pages.json @@ -1,37 +1,37 @@ { "home": "Home", "unread": "Tópicos Não Lidos", - "popular-day": "Popular topics today", - "popular-week": "Popular topics this week", - "popular-month": "Popular topics this month", - "popular-alltime": "All time popular topics", + "popular-day": "Tópicos populares de hoje", + "popular-week": "Tópicos populares esta semana", + "popular-month": "Tópicos populares deste mês", + "popular-alltime": "Tópicos populares de todos os tempos", "recent": "Tópicos Recentes", - "users/online": "Online Users", - "users/latest": "Latest Users", - "users/sort-posts": "Users with the most posts", - "users/sort-reputation": "Users with the most reputation", - "users/map": "User Map", - "users/search": "User Search", + "users/online": "Usuários Online", + "users/latest": "Últimos Usuários", + "users/sort-posts": "Usuários com mais posts", + "users/sort-reputation": "Usuários com maior reputação", + "users/map": "Mapa de Usuários", + "users/search": "Pesquisa de Usuários", "notifications": "Notificações", "tags": "Tags", "tag": "Tópicos com a tag \"%1\"", - "register": "Register an account", - "login": "Login to your account", - "reset": "Reset your account password", - "categories": "Categories", - "groups": "Groups", - "group": "%1 group", + "register": "Registrar uma conta", + "login": "Login na sua conta", + "reset": "Redefinir a senha da sua conta", + "categories": "Categorias", + "groups": "Grupos", + "group": "%1 grupo", "chats": "Chats", - "chat": "Chatting with %1", - "account/edit": "Editing \"%1\"", - "account/following": "People %1 follows", - "account/followers": "People who follow %1", - "account/posts": "Posts made by %1", - "account/topics": "Topics created by %1", - "account/groups": "%1's Groups", - "account/favourites": "%1's Favourite Posts", - "account/settings": "User Settings", - "account/watched": "Topics watched by %1", + "chat": "Conversando com %1", + "account/edit": "Editando \"%1\"", + "account/following": "Pessoas que %1 segue", + "account/followers": "Pessoas que seguem %1", + "account/posts": "Posts feitos por %1", + "account/topics": "Tópicos criados por %1", + "account/groups": "Grupos de %1", + "account/favourites": "Posts Favoritos de %1", + "account/settings": "Configurações de Usuário", + "account/watched": "Tópicos assistidos por %1", "maintenance.text": "%1 está atualmente sob manutenção. Por favor retorne em outro momento.", "maintenance.messageIntro": "Adicionalmente, o administrador deixou esta mensagem:" } \ No newline at end of file diff --git a/public/language/pt_BR/user.json b/public/language/pt_BR/user.json index 1fa01c1d60..9c5aafd37c 100644 --- a/public/language/pt_BR/user.json +++ b/public/language/pt_BR/user.json @@ -12,6 +12,7 @@ "delete_account": "Deletar Conta", "delete_account_confirm": "Você tem certeza que deseja excluir a sua conta?
Esta ação é irreversível e você não poderá recuperar quaisquer dos seus dados

Entre com o seu nome de usuário para confirmar que deseja destruir esta conta.", "delete_this_account_confirm": "Você tem certeza que deseja excluir essa conta?
Essa ação é irreversível e você não poderá recuperar quaisquer dados

", + "account-deleted": "Account deleted", "fullname": "Nome Completo", "website": "Website", "location": "Local", @@ -52,6 +53,7 @@ "username_taken_workaround": "O nome de usuário que você escolheu já existia, então nós o alteramos um pouquinho. Agora você é conhecido como %1", "upload_picture": "Carregar Foto", "upload_a_picture": "Carregue uma Foto", + "remove_uploaded_picture": "Remove Uploaded Picture", "image_spec": "Você pode usar somente arquivos PNG, JPG ou GIF", "settings": "Configurações", "show_email": "Mostrar Meu Email", @@ -85,5 +87,5 @@ "follow_topics_you_create": "Seguir tópicos que você cria", "grouptitle": "Escolha o título do grupo que você deseja exibir", "no-group-title": "Sem título de grupo", - "select-skin": "Select a Skin" + "select-skin": "Escolha uma Skin" } \ No newline at end of file diff --git a/public/language/ro/error.json b/public/language/ro/error.json index ce0c232cf4..e356abf6fb 100644 --- a/public/language/ro/error.json +++ b/public/language/ro/error.json @@ -77,6 +77,8 @@ "cant-chat-with-yourself": "Nu poți conversa cu tine!", "chat-restricted": "This user has restricted their chat messages. They must follow you before you can chat with them", "too-many-messages": "You have sent too many messages, please wait awhile.", + "invalid-chat-message": "Invalid chat message", + "chat-message-too-long": "Chat message is too long", "reputation-system-disabled": "Sistemul de reputație este dezactivat.", "downvoting-disabled": "Votarea negativă este dezactivată", "not-enough-reputation-to-downvote": "Nu ai destulă reputație pentru a vota negativ acest post.", diff --git a/public/language/ro/groups.json b/public/language/ro/groups.json index b8e9bc1c19..14663a8fa4 100644 --- a/public/language/ro/groups.json +++ b/public/language/ro/groups.json @@ -12,6 +12,9 @@ "invited.none": "There are no invited members at this time", "invited.uninvite": "Rescind Invitation", "invited.search": "Search for a user to invite to this group", + "invited.notification_title": "You have been invited to join %1", + "request.notification_title": "Group Membership Request from %1", + "request.notification_text": "%1 has requested to become a member of %2", "cover-instructions": "Drag and Drop a photo, drag to position, and hit Save", "cover-change": "Change", "cover-save": "Save", diff --git a/public/language/ro/user.json b/public/language/ro/user.json index e44b727ea8..fa57fd47cb 100644 --- a/public/language/ro/user.json +++ b/public/language/ro/user.json @@ -12,6 +12,7 @@ "delete_account": "Șterge Cont", "delete_account_confirm": "Ești sigur ca vrei să îți ștergi contul?
Această acțiune este ireversibilă și nu o să mai fie posibil să îți recuperezi datele

Introdu numele tău de utilizator pentru a confirma că dorești să ștergi acest cont.", "delete_this_account_confirm": "Are you sure you want to delete this account?
This action is irreversible and you will not be able to recover any data

", + "account-deleted": "Account deleted", "fullname": "Nume Întreg", "website": "Pagină Web", "location": "Locație", @@ -52,6 +53,7 @@ "username_taken_workaround": "Numele de utilizator pe care l-ai cerut este deja luat, așa că l-am modificat puțin. Acum ești cunoscut ca %1", "upload_picture": "Uploadează poză", "upload_a_picture": "Uploadează o poză", + "remove_uploaded_picture": "Remove Uploaded Picture", "image_spec": "Ai voie să uploadezi doar fișiere PNG, JPG sau GIF", "settings": "Setări", "show_email": "Arată adresa mea de email", diff --git a/public/language/ru/error.json b/public/language/ru/error.json index 3fdf7412e8..7cd398c0e4 100644 --- a/public/language/ru/error.json +++ b/public/language/ru/error.json @@ -77,6 +77,8 @@ "cant-chat-with-yourself": "Вы не можете общаться с самим собой", "chat-restricted": "Пользователь ограничил прием сообщений. Он должен быть подписан на Вас, чтобы Вы могли вести переписку с ним.", "too-many-messages": "Вы отправили слишком много сообщений, подождите немного.", + "invalid-chat-message": "Invalid chat message", + "chat-message-too-long": "Chat message is too long", "reputation-system-disabled": "Система репутации отключена.", "downvoting-disabled": "Понижение оценки отключено", "not-enough-reputation-to-downvote": "У Вас недостаточно репутации для понижения оценки поста", diff --git a/public/language/ru/groups.json b/public/language/ru/groups.json index 16b6277be6..c020c82d39 100644 --- a/public/language/ru/groups.json +++ b/public/language/ru/groups.json @@ -12,6 +12,9 @@ "invited.none": "На данный момент нет приглашенных пользователей", "invited.uninvite": "Аннулировать приглашение", "invited.search": "Поиск пользователя для приглашение в эту группу", + "invited.notification_title": "You have been invited to join %1", + "request.notification_title": "Group Membership Request from %1", + "request.notification_text": "%1 has requested to become a member of %2", "cover-instructions": "Перетяните сюда изображение, переместите на нужную позицию и нажмите Сохранить", "cover-change": "Изменить", "cover-save": "Сохранить", diff --git a/public/language/ru/user.json b/public/language/ru/user.json index f6b9bf49e1..e617baab6a 100644 --- a/public/language/ru/user.json +++ b/public/language/ru/user.json @@ -12,6 +12,7 @@ "delete_account": "Удалить аккаунт", "delete_account_confirm": "Вы уверены, что хотите удалить учетную запись?
Это действие необратимо, Вы не сможете восстановить свои данные

Введите имя пользователя для подтверждения уничтожения аккаунта.", "delete_this_account_confirm": "Вы уверены, что хотите удалить этот аккаунт?
Это действие является необратимым и вы не сможете восстановить любые данные

", + "account-deleted": "Account deleted", "fullname": "Полное имя", "website": "Сайт", "location": "Откуда", @@ -52,6 +53,7 @@ "username_taken_workaround": "Логин, который Вы запросили, уже занят. Мы его немного изменили. Теперь Ваш логин %1", "upload_picture": "Загрузить фотографию", "upload_a_picture": "Загрузить фотографию", + "remove_uploaded_picture": "Remove Uploaded Picture", "image_spec": "Поддерживаются только PNG, JPG или GIF файлы", "settings": "Настройки", "show_email": "Показывать мой Email", diff --git a/public/language/rw/error.json b/public/language/rw/error.json index 93e1a9fedc..7b2ed8373f 100644 --- a/public/language/rw/error.json +++ b/public/language/rw/error.json @@ -77,6 +77,8 @@ "cant-chat-with-yourself": "Ntabwo wakwiganiriza!", "chat-restricted": "Uyu muntu yemerera kuganirira mu gikari n'abantu bamwe na bamwe. Agomba kuba yarahisemo kugukurikira kugirango ube wabasha kumuganiriza uciye mu gikari. ", "too-many-messages": "Wohereje ubutumwa bwinshi cyane. Ba utegerejeho gato. ", + "invalid-chat-message": "Invalid chat message", + "chat-message-too-long": "Chat message is too long", "reputation-system-disabled": "Ibijyanye n'itangwa ry'amanota ntibyemerewe. ", "downvoting-disabled": "Kwambura amanota ntibyemerewe", "not-enough-reputation-to-downvote": "Ntabwo ufite amanota ahagije ngo ube wakwemererwa kugira uwo wambura amanota", diff --git a/public/language/rw/groups.json b/public/language/rw/groups.json index b1d7e5de19..7f757e56d1 100644 --- a/public/language/rw/groups.json +++ b/public/language/rw/groups.json @@ -12,6 +12,9 @@ "invited.none": "Nta banyamuryango batumiwe bahari", "invited.uninvite": "Kuraho Ubutumire", "invited.search": "Shaka umuntu wo gutumira muri iri tsinda", + "invited.notification_title": "You have been invited to join %1", + "request.notification_title": "Group Membership Request from %1", + "request.notification_text": "%1 has requested to become a member of %2", "cover-instructions": "Terura Urekure ifoto aha. Ushobora kuringaniza ifoto uko ushatse ubundi ukande kuri Bika", "cover-change": "Hindura", "cover-save": "Bika", diff --git a/public/language/rw/user.json b/public/language/rw/user.json index d701769fc4..f3ea54fd8a 100644 --- a/public/language/rw/user.json +++ b/public/language/rw/user.json @@ -12,6 +12,7 @@ "delete_account": "Siba Konte", "delete_account_confirm": "Wiringiye neza ko ushaka gusiba konte yawe?
Numara kuyisiba ntabwo urabasha kwisubira kandi nturabasha kugarura ibyo wari ufiteho

Shyiramo izina ryawe kugirango wemeze ko koko ushaka gusenya iyi konte.", "delete_this_account_confirm": "Wiringiye neza ko ushaka gusiba iyi konte?
Ntabwo uri bubashe kwisubira kandi ntabwo urabasha gusubirana ibyo wari ufiteho numara kuyisiba

", + "account-deleted": "Account deleted", "fullname": "Izina Ryuzuye", "website": "Urubuga", "location": "Ahantu", @@ -52,6 +53,7 @@ "username_taken_workaround": "Izina ushaka kujya ukoresha twasanze ryarafashwe. Ntugire impungenge kuko twakuboneye iryo byenda kumera kimwe. Uzaba uzwi ku izina rya %1", "upload_picture": "Gushyiraho ifoto", "upload_a_picture": "Shyiraho ifoto", + "remove_uploaded_picture": "Remove Uploaded Picture", "image_spec": "Wemerewe gushyiraho ifoto iri muri foruma ya PNG, JPG, cyangwa GIF ", "settings": "Itunganya", "show_email": "Hagaragazwe Email Yanjye", diff --git a/public/language/sc/error.json b/public/language/sc/error.json index 065234c18d..f346641522 100644 --- a/public/language/sc/error.json +++ b/public/language/sc/error.json @@ -77,6 +77,8 @@ "cant-chat-with-yourself": "You can't chat with yourself!", "chat-restricted": "This user has restricted their chat messages. They must follow you before you can chat with them", "too-many-messages": "You have sent too many messages, please wait awhile.", + "invalid-chat-message": "Invalid chat message", + "chat-message-too-long": "Chat message is too long", "reputation-system-disabled": "Reputation system is disabled.", "downvoting-disabled": "Downvoting is disabled", "not-enough-reputation-to-downvote": "You do not have enough reputation to downvote this post", diff --git a/public/language/sc/groups.json b/public/language/sc/groups.json index 3bd63ad94a..1d1a6cf52b 100644 --- a/public/language/sc/groups.json +++ b/public/language/sc/groups.json @@ -12,6 +12,9 @@ "invited.none": "There are no invited members at this time", "invited.uninvite": "Rescind Invitation", "invited.search": "Search for a user to invite to this group", + "invited.notification_title": "You have been invited to join %1", + "request.notification_title": "Group Membership Request from %1", + "request.notification_text": "%1 has requested to become a member of %2", "cover-instructions": "Drag and Drop a photo, drag to position, and hit Save", "cover-change": "Change", "cover-save": "Save", diff --git a/public/language/sc/user.json b/public/language/sc/user.json index c46910bbc8..e374fb32e5 100644 --- a/public/language/sc/user.json +++ b/public/language/sc/user.json @@ -12,6 +12,7 @@ "delete_account": "Delete Account", "delete_account_confirm": "Are you sure you want to delete your account?
This action is irreversible and you will not be able to recover any of your data

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

", + "account-deleted": "Account deleted", "fullname": "Nùmene e Sambenadu", "website": "Giassu web", "location": "Logu", @@ -52,6 +53,7 @@ "username_taken_workaround": "The username you requested was already taken, so we have altered it slightly. You are now known as %1", "upload_picture": "Càrriga immàgine", "upload_a_picture": "Càrriga un'immàgine", + "remove_uploaded_picture": "Remove Uploaded Picture", "image_spec": "Podes carrigare isceti files PNG, JPG o GIF", "settings": "Sèberos", "show_email": "Ammustra s'Email Mia", diff --git a/public/language/sk/error.json b/public/language/sk/error.json index 3fa0032b60..4a95e40964 100644 --- a/public/language/sk/error.json +++ b/public/language/sk/error.json @@ -77,6 +77,8 @@ "cant-chat-with-yourself": "Nemôžete chatovat so samým sebou.", "chat-restricted": "This user has restricted their chat messages. They must follow you before you can chat with them", "too-many-messages": "You have sent too many messages, please wait awhile.", + "invalid-chat-message": "Invalid chat message", + "chat-message-too-long": "Chat message is too long", "reputation-system-disabled": "Reputation system is disabled.", "downvoting-disabled": "Downvoting is disabled", "not-enough-reputation-to-downvote": "You do not have enough reputation to downvote this post", diff --git a/public/language/sk/groups.json b/public/language/sk/groups.json index 3bd63ad94a..1d1a6cf52b 100644 --- a/public/language/sk/groups.json +++ b/public/language/sk/groups.json @@ -12,6 +12,9 @@ "invited.none": "There are no invited members at this time", "invited.uninvite": "Rescind Invitation", "invited.search": "Search for a user to invite to this group", + "invited.notification_title": "You have been invited to join %1", + "request.notification_title": "Group Membership Request from %1", + "request.notification_text": "%1 has requested to become a member of %2", "cover-instructions": "Drag and Drop a photo, drag to position, and hit Save", "cover-change": "Change", "cover-save": "Save", diff --git a/public/language/sk/user.json b/public/language/sk/user.json index 1b09d21a94..660d9732cc 100644 --- a/public/language/sk/user.json +++ b/public/language/sk/user.json @@ -12,6 +12,7 @@ "delete_account": "Delete Account", "delete_account_confirm": "Are you sure you want to delete your account?
This action is irreversible and you will not be able to recover any of your data

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

", + "account-deleted": "Account deleted", "fullname": "Meno a priezvisko", "website": "Webová stránka", "location": "Poloha", @@ -52,6 +53,7 @@ "username_taken_workaround": "Vaše požadované prihlasovacie meno je už obsadené, tak sme si ho dovolili mierne upraviť. Budeme Vás evidovať ako %1", "upload_picture": "Nahrať obrázok", "upload_a_picture": "Nahrať obrázok", + "remove_uploaded_picture": "Remove Uploaded Picture", "image_spec": "Môžeš pridať len obrázky typu PNG, JPG alebo GIF", "settings": "Nastavenia", "show_email": "Zobrazovať môj email v profile", diff --git a/public/language/sr/error.json b/public/language/sr/error.json index 4c68c3cf56..4aafa833a4 100644 --- a/public/language/sr/error.json +++ b/public/language/sr/error.json @@ -77,6 +77,8 @@ "cant-chat-with-yourself": "You can't chat with yourself!", "chat-restricted": "This user has restricted their chat messages. They must follow you before you can chat with them", "too-many-messages": "You have sent too many messages, please wait awhile.", + "invalid-chat-message": "Invalid chat message", + "chat-message-too-long": "Chat message is too long", "reputation-system-disabled": "Reputation system is disabled.", "downvoting-disabled": "Downvoting is disabled", "not-enough-reputation-to-downvote": "You do not have enough reputation to downvote this post", diff --git a/public/language/sr/groups.json b/public/language/sr/groups.json index d0d34f1efd..adc5a7ee09 100644 --- a/public/language/sr/groups.json +++ b/public/language/sr/groups.json @@ -12,6 +12,9 @@ "invited.none": "There are no invited members at this time", "invited.uninvite": "Rescind Invitation", "invited.search": "Search for a user to invite to this group", + "invited.notification_title": "You have been invited to join %1", + "request.notification_title": "Group Membership Request from %1", + "request.notification_text": "%1 has requested to become a member of %2", "cover-instructions": "Превуците и испустите фотографију, одвуците до позиције и притисните Сними", "cover-change": "Измени", "cover-save": "Сними", diff --git a/public/language/sr/user.json b/public/language/sr/user.json index b2e8547468..2bfa7f7656 100644 --- a/public/language/sr/user.json +++ b/public/language/sr/user.json @@ -12,6 +12,7 @@ "delete_account": "Брисање налога", "delete_account_confirm": "Are you sure you want to delete your account?
This action is irreversible and you will not be able to recover any of your data

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

", + "account-deleted": "Account deleted", "fullname": "Пуно име", "website": "Веб сајт", "location": "Локација", @@ -52,6 +53,7 @@ "username_taken_workaround": "The username you requested was already taken, so we have altered it slightly. You are now known as %1", "upload_picture": "Upload picture", "upload_a_picture": "Upload a picture", + "remove_uploaded_picture": "Remove Uploaded Picture", "image_spec": "You may only upload PNG, JPG, or GIF files", "settings": "Подешавања", "show_email": "Прикажи моју лозинку", diff --git a/public/language/sv/error.json b/public/language/sv/error.json index 109a7e7c6a..0b316561f5 100644 --- a/public/language/sv/error.json +++ b/public/language/sv/error.json @@ -77,6 +77,8 @@ "cant-chat-with-yourself": "Du kan inte chatta med dig själv.", "chat-restricted": "Denna användaren har begränsat sina chatt-meddelanden. Användaren måste följa dig innan ni kan chatta med varann", "too-many-messages": "Du har skickat för många meddelanden, var god vänta", + "invalid-chat-message": "Invalid chat message", + "chat-message-too-long": "Chat message is too long", "reputation-system-disabled": "Ryktessystemet är inaktiverat.", "downvoting-disabled": "Nedröstning är inaktiverat", "not-enough-reputation-to-downvote": "Du har inte tillräckligt förtroende för att rösta ner det här meddelandet", diff --git a/public/language/sv/groups.json b/public/language/sv/groups.json index 248beda16e..07f9fea159 100644 --- a/public/language/sv/groups.json +++ b/public/language/sv/groups.json @@ -12,6 +12,9 @@ "invited.none": "Det finns inga inbjudna medlemmar just nu", "invited.uninvite": "Dra tillbaka inbjudan", "invited.search": "Sök efter en användare att lägga till i denna grupp", + "invited.notification_title": "You have been invited to join %1", + "request.notification_title": "Group Membership Request from %1", + "request.notification_text": "%1 has requested to become a member of %2", "cover-instructions": "Dra och släpp ett foto, dra till position och tryck Spara", "cover-change": "Ändra", "cover-save": "Spara", diff --git a/public/language/sv/user.json b/public/language/sv/user.json index 8510e69ac9..8f4a8d401a 100644 --- a/public/language/sv/user.json +++ b/public/language/sv/user.json @@ -12,6 +12,7 @@ "delete_account": "Ta bort ämne", "delete_account_confirm": "Är du säker på att du vill radera ditt konto?
Denna åtgärd går inte att ångra och du kommer inte kunna återställa ditt konto

Skriv in ditt användarnamn för att bekräfta att du vill radera ditt konto.", "delete_this_account_confirm": "Är du säker på att du vill ta bort detta konto?
Detta går ej att ångra - data går förlorad för alltid

", + "account-deleted": "Account deleted", "fullname": "Hela namnet", "website": "Webbsida", "location": "Plats", @@ -52,6 +53,7 @@ "username_taken_workaround": "Användarnamnet är redan upptaget, så vi förändrade det lite. Du kallas nu för %1", "upload_picture": "Ladda upp bild", "upload_a_picture": "Ladda upp en bild", + "remove_uploaded_picture": "Remove Uploaded Picture", "image_spec": "Du får bara ladda upp PNG, JPG eller GIF-filer", "settings": "Inställningar", "show_email": "Visa min epost", diff --git a/public/language/th/error.json b/public/language/th/error.json index ad6b9b98a4..8d6b9e42a9 100644 --- a/public/language/th/error.json +++ b/public/language/th/error.json @@ -77,6 +77,8 @@ "cant-chat-with-yourself": "You can't chat with yourself!", "chat-restricted": "This user has restricted their chat messages. They must follow you before you can chat with them", "too-many-messages": "You have sent too many messages, please wait awhile.", + "invalid-chat-message": "Invalid chat message", + "chat-message-too-long": "Chat message is too long", "reputation-system-disabled": "Reputation system is disabled.", "downvoting-disabled": "Downvoting is disabled", "not-enough-reputation-to-downvote": "You do not have enough reputation to downvote this post", diff --git a/public/language/th/groups.json b/public/language/th/groups.json index e1f567f52e..47ba7b8929 100644 --- a/public/language/th/groups.json +++ b/public/language/th/groups.json @@ -12,6 +12,9 @@ "invited.none": "There are no invited members at this time", "invited.uninvite": "Rescind Invitation", "invited.search": "Search for a user to invite to this group", + "invited.notification_title": "You have been invited to join %1", + "request.notification_title": "Group Membership Request from %1", + "request.notification_text": "%1 has requested to become a member of %2", "cover-instructions": "ลากรูปภาพไปวางยังตำแหน่งที่ต้องการแล้วกดที่ปุ่ม บันทึก", "cover-change": "ปรับปรุง", "cover-save": "บันทึก", diff --git a/public/language/th/user.json b/public/language/th/user.json index 104bb9a426..3898e615de 100644 --- a/public/language/th/user.json +++ b/public/language/th/user.json @@ -12,6 +12,7 @@ "delete_account": "ลบบัญชี", "delete_account_confirm": "คุณยืนยันว่าต้องการลบบัญชีของคุณ?
การแก้ไขนี้ไม่สามารถแก้ไขให้ย้อนกลับมาเหมือนเดิมได้ คุณจะไม่สามารถกู้ข้อมูลของคุณกลับมาได้เลย

กรอก ชื่อสมาชิกของคุณ เพื่อยืนยันการลบบัญชีของคุณ", "delete_this_account_confirm": "Are you sure you want to delete this account?
This action is irreversible and you will not be able to recover any data

", + "account-deleted": "Account deleted", "fullname": "ชื่อเต็ม", "website": "เว็บไซต์", "location": "สถานที่", @@ -52,6 +53,7 @@ "username_taken_workaround": "ชื่อนี้มีคนใช้แล้ว เราเลยแก้ไขชื่อคุณ โดยคุณจะถูกรู้จักในชื่อ %1", "upload_picture": "อัปโหลดรูป", "upload_a_picture": "อัปโหลดรูป", + "remove_uploaded_picture": "Remove Uploaded Picture", "image_spec": "คุณสามารถอัปโหลด PNG, JPG หรือ GIF ไฟล์เท่านั้น", "settings": "ตั้งค่า", "show_email": "แสดงอีเมล์", diff --git a/public/language/tr/error.json b/public/language/tr/error.json index 9c02802883..8438da066e 100644 --- a/public/language/tr/error.json +++ b/public/language/tr/error.json @@ -77,6 +77,8 @@ "cant-chat-with-yourself": "Kendinizle sohbet edemezsiniz!", "chat-restricted": "Bu kullanıcı sohbet ayarlarını kısıtlamış. Bu kişiye mesaj gönderebilmeniz için sizi takip etmeleri gerekiyor", "too-many-messages": "Ardı ardına çok fazla mesaj yolladınız, lütfen biraz bekleyiniz.", + "invalid-chat-message": "Invalid chat message", + "chat-message-too-long": "Chat message is too long", "reputation-system-disabled": "Saygınlık sistemi kapatılmış.", "downvoting-disabled": "Aşagı oylama kapatılmış", "not-enough-reputation-to-downvote": "Bu iletiyi aşagı oylamak için yeterince saygınlığınız yok.", diff --git a/public/language/tr/global.json b/public/language/tr/global.json index d6da9c3e05..bcc82ef7c1 100644 --- a/public/language/tr/global.json +++ b/public/language/tr/global.json @@ -33,7 +33,7 @@ "header.notifications": "Bildirimler", "header.search": "Arama", "header.profile": "Profil", - "header.navigation": "Navigation", + "header.navigation": "Navigasyon", "notifications.loading": "Bildirimler Yükleniyor", "chats.loading": "Sohbetler Yükleniyor", "motd.welcome": "NodeBB, geleceğin tartışma platformuna hoş geldiniz.", @@ -68,7 +68,7 @@ "recentposts": "Güncel İletiler", "recentips": "Güncel giriş yapilan IP adresleri", "away": "Dışarıda", - "dnd": "Do not disturb", + "dnd": "Rahatsız etme", "invisible": "Görünmez", "offline": "Çevrimdışı", "email": "E-posta", diff --git a/public/language/tr/groups.json b/public/language/tr/groups.json index c1c65af240..944cd74c2c 100644 --- a/public/language/tr/groups.json +++ b/public/language/tr/groups.json @@ -12,6 +12,9 @@ "invited.none": "Şu anda davet edilmiş üye yok", "invited.uninvite": "Daveti iptal et", "invited.search": "Gruba davet etmek için kullanıcı ara", + "invited.notification_title": "You have been invited to join %1", + "request.notification_title": "Group Membership Request from %1", + "request.notification_text": "%1 has requested to become a member of %2", "cover-instructions": "Bir fotoğrafı Sürükleyin ve Bırakın, uygun yere sürükleyip Kaydet'e tıklayın.", "cover-change": "Değiştir", "cover-save": "Kaydet", diff --git a/public/language/tr/modules.json b/public/language/tr/modules.json index 310c7824e3..bd46d890be 100644 --- a/public/language/tr/modules.json +++ b/public/language/tr/modules.json @@ -5,7 +5,7 @@ "chat.no_active": "Aktif sohbet mevcut değil", "chat.user_typing": "%1 yazıyor ...", "chat.user_has_messaged_you": "%1 size bir mesaj gönderdi.", - "chat.see_all": "See all chats", + "chat.see_all": "Bütün sohbetleri gör", "chat.no-messages": "Lütfen sohbet geçmişini görmek için bir kontak seçin", "chat.recent-chats": "Güncel Sohbetler", "chat.contacts": "Kontaklar", @@ -23,7 +23,7 @@ "composer.discard": "Bu iletiyi iptal etmek istediğinizden eminmisiniz?", "composer.submit_and_lock": "Gönder ve Kitle", "composer.toggle_dropdown": "Menü aç", - "bootbox.ok": "OK", - "bootbox.cancel": "Cancel", - "bootbox.confirm": "Confirm" + "bootbox.ok": "Tamam", + "bootbox.cancel": "İptal", + "bootbox.confirm": "Onayla" } \ No newline at end of file diff --git a/public/language/tr/notifications.json b/public/language/tr/notifications.json index 0ccd12db98..d498a13dbe 100644 --- a/public/language/tr/notifications.json +++ b/public/language/tr/notifications.json @@ -1,7 +1,7 @@ { "title": "Bildirimler", "no_notifs": "Yeni bildirimleriniz yok", - "see_all": "See all notifications", + "see_all": "Bütün bildirimleri gör", "mark_all_read": "Okunmuş bütün bildirimleri işaretle", "back_to_home": "Geri dön %1", "outgoing_link": "Harici Link", diff --git a/public/language/tr/pages.json b/public/language/tr/pages.json index 8a4da32fbe..451dd4de9b 100644 --- a/public/language/tr/pages.json +++ b/public/language/tr/pages.json @@ -1,37 +1,37 @@ { "home": "Ana Sayfa", "unread": "Okunmayan Başlıklar", - "popular-day": "Popular topics today", - "popular-week": "Popular topics this week", - "popular-month": "Popular topics this month", - "popular-alltime": "All time popular topics", + "popular-day": "Bugünkü popüler başlıklar", + "popular-week": "Bu haftaki popüler başlıklar", + "popular-month": "Bu ayki popüler başlıklar", + "popular-alltime": "En popüler başlıklar", "recent": "Güncel Konular", - "users/online": "Online Users", - "users/latest": "Latest Users", - "users/sort-posts": "Users with the most posts", - "users/sort-reputation": "Users with the most reputation", - "users/map": "User Map", - "users/search": "User Search", + "users/online": "Çevrimiçi Kullanıcılar", + "users/latest": "En Yeni Kullanıcılar", + "users/sort-posts": "En çok ileti gönderen kullanıcılar", + "users/sort-reputation": "En çok saygınlığı olan kullanıcılar", + "users/map": "Kullanıcı Haritası", + "users/search": "Kullanıcı Ara", "notifications": "Bildirimler", "tags": "Etiketler", "tag": "“%1“ ile etiketlenmiş konular", - "register": "Register an account", - "login": "Login to your account", - "reset": "Reset your account password", - "categories": "Categories", - "groups": "Groups", - "group": "%1 group", - "chats": "Chats", - "chat": "Chatting with %1", - "account/edit": "Editing \"%1\"", - "account/following": "People %1 follows", - "account/followers": "People who follow %1", - "account/posts": "Posts made by %1", - "account/topics": "Topics created by %1", - "account/groups": "%1's Groups", - "account/favourites": "%1's Favourite Posts", - "account/settings": "User Settings", - "account/watched": "Topics watched by %1", + "register": "Bir hesap aç", + "login": "Hesabına giriş yap", + "reset": "Hesap şifreni baştan yarat", + "categories": "Kategoriler", + "groups": "Gruplar", + "group": "%1 grubu", + "chats": "Sohbetler", + "chat": "%1 ile sohbet", + "account/edit": "\"%1\" düzenleniyor", + "account/following": "%1 tarafından takip edilenler", + "account/followers": "%1 takip edenler", + "account/posts": "%1 tarafından gönderilen iletiler", + "account/topics": "%1 tarafından gönderilen başlıklar", + "account/groups": "%1 Kişisine Ait Gruplar", + "account/favourites": "%1'in Favori İletileri", + "account/settings": "Kullanıcı Ayarları", + "account/watched": "%1 tarafından izlenen konular", "maintenance.text": "%1 şu anda bakımda. Lütfen bir süre sonra tekrar deneyin.", "maintenance.messageIntro": "Ayrıca, yönetici şu mesaji bıraktı:" } \ No newline at end of file diff --git a/public/language/tr/user.json b/public/language/tr/user.json index 51e995af0a..cc63b7b9b7 100644 --- a/public/language/tr/user.json +++ b/public/language/tr/user.json @@ -12,6 +12,7 @@ "delete_account": "Hesabı Sil", "delete_account_confirm": "Hesabınızı silmek istediğinize emin misiniz?
Bu işlem geri çevrilemez ve tüm verileriniz sistemden silinecek.

Eğer hesabınızı silmek istiyorsanız lütfen kullanıcı isminizi girerek işlemi onaylayın.", "delete_this_account_confirm": "Bu hesabı silmek istediğinizden emin misiniz?
Bu işlem geri döndürülemez ve hiç bir veriyi kurtaramazsınız

", + "account-deleted": "Account deleted", "fullname": "Tam Ad", "website": "Websitesi", "location": "Konum", @@ -52,6 +53,7 @@ "username_taken_workaround": "İstediğiniz kullanıcı ismi zaten alınmış, bu yüzden biraz degiştirdik. Şimdiki kullanıcı isminiz %1", "upload_picture": "Resim Yükle", "upload_a_picture": "Bir Resim Yükle", + "remove_uploaded_picture": "Remove Uploaded Picture", "image_spec": "Sadece PNG, JPG veya GIF dosyaları yükleyebilirsiniz", "settings": "Ayarlar", "show_email": "E-postamı göster", @@ -85,5 +87,5 @@ "follow_topics_you_create": "Kendi konularımı takip et", "grouptitle": "Göstermek istediğiniz gurup başlığını seçin", "no-group-title": "Grup başlığı yok", - "select-skin": "Select a Skin" + "select-skin": "Bir deri şeçin" } \ No newline at end of file diff --git a/public/language/vi/error.json b/public/language/vi/error.json index 0a1b14c46b..cdba17f395 100644 --- a/public/language/vi/error.json +++ b/public/language/vi/error.json @@ -77,6 +77,8 @@ "cant-chat-with-yourself": "Bạn không thể chat với chính bạn!", "chat-restricted": "Người dùng này đã bật chế độ hạn chế tin nhắn chat. Bạn phải được anh/cô ta follow thì mới có thể gởi tin nhắn đến họ được.", "too-many-messages": "You have sent too many messages, please wait awhile.", + "invalid-chat-message": "Invalid chat message", + "chat-message-too-long": "Chat message is too long", "reputation-system-disabled": "Hệ thống tín nhiệm đã bị vô hiệu hóa.", "downvoting-disabled": "Downvote đã bị tắt", "not-enough-reputation-to-downvote": "Bạn không có đủ phiếu tín nhiệm để downvote bài này", diff --git a/public/language/vi/groups.json b/public/language/vi/groups.json index b44ae1ecc5..07900486ff 100644 --- a/public/language/vi/groups.json +++ b/public/language/vi/groups.json @@ -12,6 +12,9 @@ "invited.none": "There are no invited members at this time", "invited.uninvite": "Rescind Invitation", "invited.search": "Search for a user to invite to this group", + "invited.notification_title": "You have been invited to join %1", + "request.notification_title": "Group Membership Request from %1", + "request.notification_text": "%1 has requested to become a member of %2", "cover-instructions": "Drag and Drop a photo, drag to position, and hit Save", "cover-change": "Thay đổi", "cover-save": "Lưu", diff --git a/public/language/vi/user.json b/public/language/vi/user.json index 3c062711a1..3f7cda548d 100644 --- a/public/language/vi/user.json +++ b/public/language/vi/user.json @@ -12,6 +12,7 @@ "delete_account": "Xóa tài khoản", "delete_account_confirm": "Bạn có chắc muốn xóa tài khoản của mình?
Hành động này không thể khôi phục và bạn sẽ mất toàn bộ dữ liệu

Hãy nhập tên đăng nhập để xác nhận.", "delete_this_account_confirm": "Are you sure you want to delete this account?
This action is irreversible and you will not be able to recover any data

", + "account-deleted": "Account deleted", "fullname": "Tên đầy đủ", "website": "Website", "location": "Địa điểm", @@ -52,6 +53,7 @@ "username_taken_workaround": "Tên truy cập này đã tồn tại, vì vậy chúng tôi đã sửa đổi nó một chút. Tên truy cập của bạn giờ là %1", "upload_picture": "Tải lên hình ảnh", "upload_a_picture": "Tải lên một hình ảnh", + "remove_uploaded_picture": "Remove Uploaded Picture", "image_spec": "Bạn chỉ có thể tải lên được các file PNG, JPG hoặc GIF", "settings": "Thiết lập", "show_email": "Hiện Email của tôi", diff --git a/public/language/zh_CN/error.json b/public/language/zh_CN/error.json index 4e1ecab7ff..ebca6bc593 100644 --- a/public/language/zh_CN/error.json +++ b/public/language/zh_CN/error.json @@ -77,6 +77,8 @@ "cant-chat-with-yourself": "您不能和自己聊天!", "chat-restricted": "此用户限制了他的聊天消息。必须他先关注您,您才能和他聊天。", "too-many-messages": "您发送了太多消息,请稍等片刻。", + "invalid-chat-message": "Invalid chat message", + "chat-message-too-long": "Chat message is too long", "reputation-system-disabled": "威望系统已禁用。", "downvoting-disabled": "扣分功能已禁用", "not-enough-reputation-to-downvote": "您的威望不足以给此帖扣分", diff --git a/public/language/zh_CN/groups.json b/public/language/zh_CN/groups.json index d127f71963..40e0bd3030 100644 --- a/public/language/zh_CN/groups.json +++ b/public/language/zh_CN/groups.json @@ -12,6 +12,9 @@ "invited.none": "暂时没有接受邀请的成员", "invited.uninvite": "取消邀请", "invited.search": "选择用户加入小组", + "invited.notification_title": "You have been invited to join %1", + "request.notification_title": "Group Membership Request from %1", + "request.notification_text": "%1 has requested to become a member of %2", "cover-instructions": "拖放照片到此位置,然后点击 保存", "cover-change": "更改", "cover-save": "保存", diff --git a/public/language/zh_CN/user.json b/public/language/zh_CN/user.json index 5eabb24fec..b46b6c948a 100644 --- a/public/language/zh_CN/user.json +++ b/public/language/zh_CN/user.json @@ -12,6 +12,7 @@ "delete_account": "删除帐号", "delete_account_confirm": "确认要删除您的帐户吗?
此操作是不可逆转的,您将无法恢复您的任何数据

请输入您的用户名,确认您想要删除此帐户。", "delete_this_account_confirm": "您确定要删除账户吗?
这个操作不可逆,而且您可能不能找回任何数据

", + "account-deleted": "Account deleted", "fullname": "姓名", "website": "网站", "location": "位置", @@ -52,6 +53,7 @@ "username_taken_workaround": "您申请的用户名已被占用,所以我们稍作更改。您现在的用户名是 %1", "upload_picture": "上传头像", "upload_a_picture": "上传头像", + "remove_uploaded_picture": "Remove Uploaded Picture", "image_spec": "您只能上传 PNG, JPG 或者 GIF 图片文件", "settings": "设置", "show_email": "显示我的电子邮箱", diff --git a/public/language/zh_TW/error.json b/public/language/zh_TW/error.json index 8e243ea7ff..2f5ef4565d 100644 --- a/public/language/zh_TW/error.json +++ b/public/language/zh_TW/error.json @@ -77,6 +77,8 @@ "cant-chat-with-yourself": "你不能與自己聊天!", "chat-restricted": "此用戶已限制了他的聊天功能。你要在他關注你之後,才能跟他聊天", "too-many-messages": "You have sent too many messages, please wait awhile.", + "invalid-chat-message": "Invalid chat message", + "chat-message-too-long": "Chat message is too long", "reputation-system-disabled": "信譽系統已停用。", "downvoting-disabled": "Downvoting已停用", "not-enough-reputation-to-downvote": "你沒有足夠的信譽downvote這個帖子", diff --git a/public/language/zh_TW/groups.json b/public/language/zh_TW/groups.json index 17d8092035..93d5ac1b39 100644 --- a/public/language/zh_TW/groups.json +++ b/public/language/zh_TW/groups.json @@ -12,6 +12,9 @@ "invited.none": "There are no invited members at this time", "invited.uninvite": "Rescind Invitation", "invited.search": "Search for a user to invite to this group", + "invited.notification_title": "You have been invited to join %1", + "request.notification_title": "Group Membership Request from %1", + "request.notification_text": "%1 has requested to become a member of %2", "cover-instructions": "拖拉一本相簿,並拖到此位置,以及點擊 儲存", "cover-change": "變更", "cover-save": "儲存", diff --git a/public/language/zh_TW/user.json b/public/language/zh_TW/user.json index 9a71d77b9a..431250f9c7 100644 --- a/public/language/zh_TW/user.json +++ b/public/language/zh_TW/user.json @@ -12,6 +12,7 @@ "delete_account": "刪除帳戶", "delete_account_confirm": "你確定要刪除自己的帳戶?
此操作不能復原,您將無法恢復任何數據

輸入您的使用者名稱,以確認您希望刪除這個帳戶。", "delete_this_account_confirm": "Are you sure you want to delete this account?
This action is irreversible and you will not be able to recover any data

", + "account-deleted": "Account deleted", "fullname": "全名", "website": "網站", "location": "地址", @@ -52,6 +53,7 @@ "username_taken_workaround": "您所註冊的使用者名稱已經被使用了,所以我們將它略微改變。你現在改稱 %1", "upload_picture": "上傳頭像", "upload_a_picture": "上傳一張照片", + "remove_uploaded_picture": "Remove Uploaded Picture", "image_spec": "支援的圖檔格式包含 PNG, JPG, 或是 GIF", "settings": "設定", "show_email": "顯示我的郵箱", diff --git a/public/less/generics.less b/public/less/generics.less index 4ce04c4337..f9ef30469a 100644 --- a/public/less/generics.less +++ b/public/less/generics.less @@ -26,7 +26,7 @@ padding: 0.5rem 1rem; &:hover { - background: @gray-lighter; + background: #eee; } img { @@ -40,4 +40,4 @@ display: inline-block; } } -} \ No newline at end of file +} diff --git a/public/src/admin/admin.js b/public/src/admin/admin.js index a1ac7376f3..a601893e85 100644 --- a/public/src/admin/admin.js +++ b/public/src/admin/admin.js @@ -1,5 +1,5 @@ "use strict"; -/*global componentHandler, define, socket, app, ajaxify, utils, bootbox, Mousetrap, Hammer, RELATIVE_PATH*/ +/*global config, translator, componentHandler, define, socket, app, ajaxify, utils, bootbox, Mousetrap, Hammer, Slideout, RELATIVE_PATH*/ (function() { $(document).ready(function() { @@ -105,7 +105,9 @@ } function launchSnackbar(params) { - translator.translate("" + params.title + "" + params.message, function(html) { + var message = (params.title ? "" + params.title + "" : '') + (params.message ? params.message : ''); + + translator.translate(message, function(html) { var bar = $.snackbar({ content: html, timeout: 3000, diff --git a/public/src/admin/appearance/themes.js b/public/src/admin/appearance/themes.js index 17bae639d7..901f64b688 100644 --- a/public/src/admin/appearance/themes.js +++ b/public/src/admin/appearance/themes.js @@ -76,8 +76,10 @@ define('admin/appearance/themes', function() { templates.parse('admin/partials/theme_list', { themes: themes }, function(html) { - instListEl.html(html); - highlightSelectedTheme(config['theme:id']); + translator.translate(html, function(html) { + instListEl.html(html); + highlightSelectedTheme(config['theme:id']); + }); }); } }); diff --git a/public/src/admin/extend/plugins.js b/public/src/admin/extend/plugins.js index 5944f98f91..c886a56c75 100644 --- a/public/src/admin/extend/plugins.js +++ b/public/src/admin/extend/plugins.js @@ -38,6 +38,8 @@ define('admin/extend/plugins', function() { }); pluginsList.on('click', 'button[data-action="toggleInstall"]', function() { + var btn = $(this); + btn.attr('disabled', true); pluginID = $(this).parents('li').attr('data-plugin-id'); if ($(this).attr('data-installed') === '1') { @@ -49,6 +51,8 @@ define('admin/extend/plugins', function() { bootbox.confirm('

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

Server returned (' + err.status + '): ' + err.responseText + '
', function(confirm) { if (confirm) { Plugins.toggleInstall(pluginID, 'latest'); + } else { + btn.removeAttr('disabled'); } }); return; @@ -58,9 +62,15 @@ define('admin/extend/plugins', function() { if (payload.version !== 'latest') { Plugins.toggleInstall(pluginID, payload.version); } else if (payload.version === 'latest') { - confirmInstall(pluginID, function() { - Plugins.toggleInstall(pluginID, 'latest'); + confirmInstall(pluginID, function(confirm) { + if (confirm) { + Plugins.toggleInstall(pluginID, 'latest'); + } else { + btn.removeAttr('disabled'); + } }); + } else { + btn.removeAttr('disabled'); } }); }); @@ -137,9 +147,7 @@ define('admin/extend/plugins', function() { '

In the event that NodeBB cannot boot properly:

' + '
$ ./nodebb reset plugin="' + pluginID + '"
' + '

Continue installation of latest version of this plugin?

', function(confirm) { - if (confirm) { - callback(); - } + callback(confirm); }); } @@ -176,15 +184,14 @@ define('admin/extend/plugins', function() { Plugins.toggleInstall = function(pluginID, version, callback) { var btn = $('li[data-plugin-id="' + pluginID + '"] button[data-action="toggleInstall"]'); var activateBtn = btn.siblings('[data-action="toggleActive"]'); - btn.html(btn.html() + 'ing') - .attr('disabled', true) - .find('i').attr('class', 'fa fa-refresh fa-spin'); + btn.find('i').attr('class', 'fa fa-refresh fa-spin'); socket.emit('admin.plugins.toggleInstall', { id: pluginID, version: version }, function(err, pluginData) { if (err) { + btn.removeAttr('disabled'); return app.alertError(err.message); } diff --git a/public/src/admin/general/homepage.js b/public/src/admin/general/homepage.js new file mode 100644 index 0000000000..96a6aac0eb --- /dev/null +++ b/public/src/admin/general/homepage.js @@ -0,0 +1,23 @@ +"use strict"; +/*global define*/ + +define('admin/general/homepage', ['admin/settings'], function(Settings) { + + function toggleCustomRoute() { + if ($('[data-field="homePageRoute"]').val()) { + $('#homePageCustom').hide(); + }else{ + $('#homePageCustom').show(); + } + } + + var Homepage = {}; + + Homepage.init = function() { + $('[data-field="homePageRoute"]').on('change', toggleCustomRoute); + + toggleCustomRoute(); + }; + + return Homepage; +}); diff --git a/public/src/admin/manage/category.js b/public/src/admin/manage/category.js index 25832bcac6..c16809e44f 100644 --- a/public/src/admin/manage/category.js +++ b/public/src/admin/manage/category.js @@ -234,6 +234,14 @@ define('admin/manage/category', [ Category.launchParentSelector = function() { socket.emit('categories.get', function(err, categories) { + if (err) { + return app.alertError(err.message); + } + + categories = categories.filter(function(category) { + return category && parseInt(category.cid, 10) !== parseInt(ajaxify.data.category.cid, 10); + }); + templates.parse('partials/category_list', { categories: categories }, function(html) { diff --git a/public/src/admin/manage/flags.js b/public/src/admin/manage/flags.js index 880094af20..5cbf55f7f1 100644 --- a/public/src/admin/manage/flags.js +++ b/public/src/admin/manage/flags.js @@ -2,15 +2,15 @@ /*global define, socket, app, admin, utils, bootbox, RELATIVE_PATH*/ define('admin/manage/flags', [ - 'forum/infinitescroll', - 'admin/modules/selectable', + 'forum/infinitescroll', + 'admin/modules/selectable', 'autocomplete' ], function(infinitescroll, selectable, autocomplete) { var Flags = {}; Flags.init = function() { - $('.post-container .content img').addClass('img-responsive'); + $('.post-container .content img:not(.not-responsive)').addClass('img-responsive'); var params = utils.params(); $('#flag-sort-by').val(params.sortBy); @@ -91,7 +91,7 @@ define('admin/manage/flags', [ infinitescroll.parseAndTranslate('admin/manage/flags', 'posts', {posts: data.posts}, function(html) { $('[data-next]').attr('data-next', data.next); $('.post-container').append(html); - html.find('img').addClass('img-responsive'); + html.find('img:not(.not-responsive)').addClass('img-responsive'); done(); }); } else { diff --git a/public/src/admin/manage/tags.js b/public/src/admin/manage/tags.js index 95133f9836..d85df5c266 100644 --- a/public/src/admin/manage/tags.js +++ b/public/src/admin/manage/tags.js @@ -10,7 +10,6 @@ define('admin/manage/tags', [ timeoutId = 0; Tags.init = function() { - handleColorPickers(); selectable.enable('.tag-management', '.tag-row'); handleSearch(); @@ -53,7 +52,7 @@ define('admin/manage/tags', [ var firstTag = $(tagsToModify[0]), title = tagsToModify.length > 1 ? 'Editing multiple tags' : 'Editing ' + firstTag.find('.tag-item').text() + ' tag'; - bootbox.dialog({ + var modal = bootbox.dialog({ title: title, message: firstTag.find('.tag-modal').html(), buttons: { @@ -79,9 +78,7 @@ define('admin/manage/tags', [ } }); - setTimeout(function() { - handleColorPickers(); - }, 500); + handleColorPickers(modal); }); } @@ -110,21 +107,13 @@ define('admin/manage/tags', [ }); } - function handleColorPickers() { + function handleColorPickers(modal) { function enableColorPicker(idx, inputEl) { - var $inputEl = $(inputEl), - previewEl = $inputEl.parents('.tag-row').find('.tag-item'); - - colorpicker.enable($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); - } - }); + var $inputEl = $(inputEl); + colorpicker.enable($inputEl); } - $('[data-name="bgColor"], [data-name="color"]').each(enableColorPicker); + modal.find('[data-name="bgColor"], [data-name="color"]').each(enableColorPicker); } function save(tag) { diff --git a/public/src/ajaxify.js b/public/src/ajaxify.js index 4412d185b1..d1d7ecb4be 100644 --- a/public/src/ajaxify.js +++ b/public/src/ajaxify.js @@ -110,20 +110,21 @@ $(document).ready(function() { textStatus = err.textStatus; if (data) { - data.responseJSON.config = config; var status = parseInt(data.status, 10); if (status === 403 || status === 404 || status === 500 || status === 502 || status === 503) { if (status === 502) { status = 500; } - + if (data.responseJSON) { + data.responseJSON.config = config; + } $('#footer, #content').removeClass('hide').addClass('ajaxifying'); return renderTemplate(url, status.toString(), data.responseJSON, callback); } else if (status === 401) { app.alertError('[[global:please_log_in]]'); app.previousUrl = url; return ajaxify.go('login'); - } else if (status === 302) { + } else if (status === 302 || status === 308) { if (data.responseJSON.external) { window.location.href = data.responseJSON.external; } else if (typeof data.responseJSON === 'string') { diff --git a/public/src/app.js b/public/src/app.js index a55dc9d2ab..282e76718f 100644 --- a/public/src/app.js +++ b/public/src/app.js @@ -146,19 +146,19 @@ app.cacheBuster = null; var modal = $(dialog.apply(this, arguments)[0]); translate(modal); return modal; - } + }; bootbox.prompt = function() { var modal = $(prompt.apply(this, arguments)[0]); translate(modal); return modal; - } + }; bootbox.confirm = function() { var modal = $(confirm.apply(this, arguments)[0]); translate(modal); return modal; - } + }; } function overrideTimeago() { @@ -434,11 +434,15 @@ app.cacheBuster = null; searchFields = $("#search-fields"), searchInput = $('#search-fields input'); + $('#search-form .advanced-search-link').on('mousedown', function() { + ajaxify.go('/search'); + }); + $('#search-form').on('submit', dismissSearch); searchInput.on('blur', dismissSearch); function dismissSearch(){ - searchFields.hide(); + searchFields.addClass('hide'); searchButton.show(); } @@ -469,7 +473,7 @@ app.cacheBuster = null; }; app.prepareSearch = function() { - $("#search-fields").removeClass('hide').show(); + $("#search-fields").removeClass('hide'); $("#search-button").hide(); $('#search-fields input').focus(); }; @@ -481,7 +485,7 @@ app.cacheBuster = null; if(err) { return app.alertError(err.message); } - $('[component="user/status"]') + $('[data-uid="' + app.user.uid + '"] [component="user/status"], [component="header/profilelink"] [component="user/status"]') .removeClass('away online dnd offline') .addClass(status); @@ -563,14 +567,9 @@ app.cacheBuster = null; app.showEmailConfirmWarning(); 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; + socket.on('event:nodebb.ready', function(data) { + if (!app.cacheBusters || app.cacheBusters['cache-buster'] !== data['cache-buster']) { + app.cacheBusters = data; app.alert({ alert_id: 'forum_updated', diff --git a/public/src/client/account/edit.js b/public/src/client/account/edit.js index c3de1ec9bf..886069a40c 100644 --- a/public/src/client/account/edit.js +++ b/public/src/client/account/edit.js @@ -153,7 +153,7 @@ define('forum/account/edit', ['forum/account/header', 'uploader', 'translator'], function handleAccountDelete() { $('#deleteAccountBtn').on('click', function() { translator.translate('[[user:delete_account_confirm]]', function(translated) { - bootbox.confirm(translated + '

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

', function(confirm) { if (!confirm) { return; } @@ -169,6 +169,10 @@ define('forum/account/edit', ['forum/account/header', 'uploader', 'translator'], }); } }); + + modal.on('shown.bs.modal', function() { + modal.find('input').focus(); + }); }); return false; }); @@ -182,8 +186,16 @@ define('forum/account/edit', ['forum/account/header', 'uploader', 'translator'], $('#user-uploaded-picture').attr('src', urlOnServer); updateHeader(urlOnServer); uploadedPicture = urlOnServer; + $('#removeUploadedPictureBtn').removeClass('hide'); } + function onRemoveComplete(urlOnServer) { + $('#user-current-picture').attr('src', urlOnServer); + $('#user-uploaded-picture').attr('src', ''); + updateHeader(urlOnServer); + uploadedPicture = ''; + $('#removeUploadedPictureBtn').addClass('hide'); + } $('#upload-picture-modal').on('hide', function() { $('#userPhotoInput').val(''); @@ -222,6 +234,16 @@ define('forum/account/edit', ['forum/account/header', 'uploader', 'translator'], }); return false; }); + + $('#removeUploadedPictureBtn').on('click', function() { + socket.emit('user.removeUploadedPicture', {uid: ajaxify.data.theirid}, function(err, imageUrlOnServer) { + if (err) { + return app.alertError(err.message); + } + onRemoveComplete(imageUrlOnServer); + $('#change-picture-modal').modal('hide'); + }); + }) } function handleEmailConfirm() { diff --git a/public/src/client/account/favourites.js b/public/src/client/account/favourites.js index fd5931cc49..9d732418c4 100644 --- a/public/src/client/account/favourites.js +++ b/public/src/client/account/favourites.js @@ -8,7 +8,7 @@ define('forum/account/favourites', ['forum/account/header', 'forum/account/posts Favourites.init = function() { header.init(); - $('[component="post/content"] img').addClass('img-responsive'); + $('[component="post/content"] img:not(.not-responsive)').addClass('img-responsive'); posts.handleInfiniteScroll('posts.loadMoreFavourites', 'account/favourites'); }; diff --git a/public/src/client/account/header.js b/public/src/client/account/header.js index 511f3be5a9..226f6ac3de 100644 --- a/public/src/client/account/header.js +++ b/public/src/client/account/header.js @@ -19,7 +19,7 @@ define('forum/account/header', function() { $('.account-sub-links li').removeClass('active').each(function() { var href = $(this).find('a').attr('href'); - if (href === window.location.pathname) { + if (decodeURIComponent(href) === decodeURIComponent(window.location.pathname)) { $(this).addClass('active'); return false; } diff --git a/public/src/client/account/posts.js b/public/src/client/account/posts.js index 94dac96323..b5b04056e8 100644 --- a/public/src/client/account/posts.js +++ b/public/src/client/account/posts.js @@ -9,7 +9,7 @@ define('forum/account/posts', ['forum/account/header', 'forum/infinitescroll'], AccountPosts.init = function() { header.init(); - $('[component="post/content"] img').addClass('img-responsive'); + $('[component="post/content"] img:not(.not-responsive)').addClass('img-responsive'); AccountPosts.handleInfiniteScroll('posts.loadMoreUserPosts', 'account/posts'); }; @@ -43,7 +43,7 @@ define('forum/account/posts', ['forum/account/header', 'forum/infinitescroll'], function onPostsLoaded(posts, callback) { infinitescroll.parseAndTranslate(template, 'posts', {posts: posts}, function(html) { $('[component="posts"]').append(html); - html.find('img').addClass('img-responsive'); + html.find('img:not(.not-responsive)').addClass('img-responsive'); html.find('.timeago').timeago(); app.createUserTooltips(); utils.makeNumbersHumanReadable(html.find('.human-readable-number')); diff --git a/public/src/client/account/profile.js b/public/src/client/account/profile.js index 5e72605017..5a7756cc93 100644 --- a/public/src/client/account/profile.js +++ b/public/src/client/account/profile.js @@ -44,7 +44,7 @@ define('forum/account/profile', ['forum/account/header', 'forum/infinitescroll', }; function processPage() { - $('[component="posts"] img, [component="aboutme"] img').addClass('img-responsive'); + $('[component="posts"] img:not(.not-responsive), [component="aboutme"] img:not(.not-responsive)').addClass('img-responsive'); } function updateButtons() { @@ -154,6 +154,7 @@ define('forum/account/profile', ['forum/account/header', 'forum/infinitescroll', if (err) { return app.alertError(err.message); } + app.alertSuccess('[[user:account-deleted]]'); history.back(); }); }); diff --git a/public/src/client/account/settings.js b/public/src/client/account/settings.js index 23afc2f5c0..bf535d8ec0 100644 --- a/public/src/client/account/settings.js +++ b/public/src/client/account/settings.js @@ -45,7 +45,7 @@ define('forum/account/settings', ['forum/account/header'], function(header) { config[key] = newSettings[key]; } } - app.exposeConfigToTemplates(); + if (requireReload && parseInt(app.user.uid, 10) === parseInt(ajaxify.data.theirid, 10)) { app.alert({ id: 'setting-change', diff --git a/public/src/client/categories.js b/public/src/client/categories.js index 33db4ef784..32f6af67c3 100644 --- a/public/src/client/categories.js +++ b/public/src/client/categories.js @@ -62,7 +62,7 @@ define('forum/categories', ['components', 'translator'], function(components, tr templates.parse('categories', 'posts', {categories: {posts: posts}}, function(html) { translator.translate(html, function(translatedHTML) { translatedHTML = $(translatedHTML); - translatedHTML.find('img').addClass('img-responsive'); + translatedHTML.find('img:not(.not-responsive)').addClass('img-responsive'); translatedHTML.find('.timeago').timeago(); callback(translatedHTML); }); diff --git a/public/src/client/chats.js b/public/src/client/chats.js index b5413a6de6..8611d36f32 100644 --- a/public/src/client/chats.js +++ b/public/src/client/chats.js @@ -46,6 +46,7 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', inputEl.on('keypress', function(e) { if(e.which === 13 && !e.shiftKey) { Chats.sendMessage(Chats.getRecipientUid(), inputEl); + return false; } }); @@ -130,7 +131,7 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', chatContainer = $('.chat-content'); newMessage.appendTo(chatContainer); newMessage.find('.timeago').timeago(); - newMessage.find('img:not(".chat-user-image")').addClass('img-responsive'); + newMessage.find('img:not(.not-responsive)').addClass('img-responsive'); Chats.scrollToBottom($('.expanded-chat .chat-content')); } @@ -184,25 +185,30 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', }; Chats.sendMessage = function(toUid, inputEl) { - var msg = S(inputEl.val()).stripTags().s; - if (msg.length) { - msg = msg +'\n'; - socket.emit('modules.chats.send', { - touid:toUid, - message:msg - }, function(err) { - if (err) { - if (err.message === '[[error:email-not-confirmed-chat]]') { - return app.showEmailConfirmWarning(err); - } - return app.alertError(err.message); - } - - inputEl.val(''); - sounds.play('chat-outgoing'); - Chats.notifyTyping(toUid, false); - }); + var msg = inputEl.val(); + if (msg.length > config.maximumChatMessageLength) { + return app.alertError('[[error:chat-message-too-long]]'); } + + if (!msg.length) { + return; + } + + inputEl.val(''); + socket.emit('modules.chats.send', { + touid: toUid, + message: msg + }, function(err) { + if (err) { + if (err.message === '[[error:email-not-confirmed-chat]]') { + return app.showEmailConfirmWarning(err); + } + return app.alertError(err.message); + } + + sounds.play('chat-outgoing'); + Chats.notifyTyping(toUid, false); + }); }; Chats.scrollToBottom = function(containerEl) { diff --git a/public/src/client/groups/details.js b/public/src/client/groups/details.js index ae6c46e03c..f5fb69e401 100644 --- a/public/src/client/groups/details.js +++ b/public/src/client/groups/details.js @@ -24,7 +24,7 @@ define('forum/groups/details', ['iconSelect', 'components', 'forum/infinitescrol handleMemberInfiniteScroll(); handleMemberInvitations(); - components.get('groups/activity').find('.content img').addClass('img-responsive'); + components.get('groups/activity').find('.content img:not(.not-responsive)').addClass('img-responsive'); detailsPage.on('click', '[data-action]', function() { var btnEl = $(this), diff --git a/public/src/client/reset.js b/public/src/client/reset.js index fcf4098b68..ef6856555a 100644 --- a/public/src/client/reset.js +++ b/public/src/client/reset.js @@ -16,13 +16,13 @@ define('forum/reset', function() { return app.alertError(err.message); } - errorEl.addClass('hide').hide(); - successEl.removeClass('hide').show(); + errorEl.addClass('hide'); + successEl.removeClass('hide'); inputEl.val(''); }); } else { - successEl.addClass('hide').hide(); - errorEl.removeClass('hide').show(); + successEl.addClass('hide'); + errorEl.removeClass('hide'); } return false; }); diff --git a/public/src/client/reset_code.js b/public/src/client/reset_code.js index abbc4582db..678edf987a 100644 --- a/public/src/client/reset_code.js +++ b/public/src/client/reset_code.js @@ -13,7 +13,7 @@ define('forum/reset_code', function() { noticeEl = $('#notice'); resetEl.on('click', function() { - if (password.val().length < 6) { + if (password.val().length < config.minimumPasswordLength) { app.alertError('[[reset_password:password_too_short]]'); } else if (password.val() !== repeat.val()) { app.alertError('[[reset_password:passwords_do_not_match]]'); diff --git a/public/src/client/search.js b/public/src/client/search.js index 5633884ff5..adee1adaee 100644 --- a/public/src/client/search.js +++ b/public/src/client/search.js @@ -80,9 +80,11 @@ define('forum/search', ['search', 'autocomplete'], function(searchModule, autoco $('#posted-by-user').val(params.by); } - if (params.categories) { - $('#posted-in-categories').val(params.categories); - } + loadCategories(function() { + if (params.categories) { + $('#posted-in-categories').val(params.categories); + } + }); if (params.searchChildren) { $('#search-children').prop('checked', true); @@ -112,6 +114,38 @@ define('forum/search', ['search', 'autocomplete'], function(searchModule, autoco } } + function loadCategories(callback) { + var listEl = $('#posted-in-categories'); + + socket.emit('categories.getCategoriesByPrivilege', 'read', function(err, categories) { + if (err) { + return app.alertError(err.message); + } + + categories = categories.filter(function(category) { + return !category.link && !parseInt(category.parentCid, 10); + }); + + categories.forEach(function(category) { + recursive(category, listEl, ''); + }); + listEl.attr('size', listEl.find('option').length); + callback(); + }); + } + + function recursive(category, listEl, level) { + if (category.link) { + return; + } + var bullet = level ? '• ' : ''; + $('').appendTo(listEl); + + category.children.forEach(function(child) { + recursive(child, listEl, '    ' + level); + }); + } + function highlightMatches(searchQuery) { if (!searchQuery) { return; @@ -125,12 +159,12 @@ define('forum/search', ['search', 'autocomplete'], function(searchModule, autoco var result = $(this); var text = result.html().replace(regex, '$1'); - result.html(text).find('img').addClass('img-responsive').each(function() { + result.html(text).find('img:not(.not-responsive)').addClass('img-responsive').each(function() { $(this).attr('src', $(this).attr('src').replace(/([\s\S]*?)<\/strong>/gi, '$1')); }); result.find('a').each(function() { - $(this).attr('href', $(this).attr('href').replace(/([\s\S]*?)<\/strong>/gi, '$1')); + $(this).attr('href', $(this).attr('href').replace(/([\s\S]*?)<\/strong>/gi, '$1')); }); }); } catch(e) { diff --git a/public/src/client/topic/events.js b/public/src/client/topic/events.js index 2d62781ee6..99564cd50c 100644 --- a/public/src/client/topic/events.js +++ b/public/src/client/topic/events.js @@ -82,12 +82,14 @@ define('forum/topic/events', [ } function onTopicPurged(data) { - ajaxify.go('category/' + ajaxify.data.cid); + if (ajaxify.data.category && ajaxify.data.category.slug) { + ajaxify.go('category/' + ajaxify.data.category.slug, null, true); + } } function onTopicMoved(data) { - if (data && data.tid > 0) { - ajaxify.go('topic/' + data.tid); + if (data && data.slug) { + ajaxify.go('topic/' + data.slug, null, true); } } @@ -110,7 +112,7 @@ define('forum/topic/events', [ editedPostEl.fadeOut(250, function() { editedPostEl.html(data.post.content); - editedPostEl.find('img').addClass('img-responsive'); + editedPostEl.find('img:not(.not-responsive)').addClass('img-responsive'); app.replaceSelfLinks(editedPostEl.find('a')); editedPostEl.fadeIn(250); }); diff --git a/public/src/client/topic/move.js b/public/src/client/topic/move.js index 648f9f3b27..4c932d8a9b 100644 --- a/public/src/client/topic/move.js +++ b/public/src/client/topic/move.js @@ -18,7 +18,7 @@ define('forum/topic/move', function() { Move.moveAll = tids ? false : true; modal.on('shown.bs.modal', onMoveModalShown); - $('#move-confirm').hide(); + $('#move-confirm').addClass('hide'); if (Move.moveAll || (tids && tids.length > 1)) { modal.find('.modal-header h3').translateText('[[topic:move_topics]]'); @@ -52,7 +52,7 @@ define('forum/topic/move', function() { function selectCategory(category) { modal.find('#confirm-category-name').html(category.html()); - $('#move-confirm').show(); + $('#move-confirm').removeClass('hide'); targetCid = category.attr('data-cid'); targetCategoryLabel = category.html(); diff --git a/public/src/client/topic/posts.js b/public/src/client/topic/posts.js index 6532ae648c..dc9825f1dc 100644 --- a/public/src/client/topic/posts.js +++ b/public/src/client/topic/posts.js @@ -18,6 +18,10 @@ define('forum/topic/posts', [ return; } + if (!data || !data.posts || !data.posts.length) { + return; + } + if (config.usePagination) { return onNewPostPagination(data); } @@ -32,12 +36,20 @@ define('forum/topic/posts', [ if (html) { html.addClass('new'); } + scrollToPostIfSelf(data.posts[0]); }); }; + function scrollToPostIfSelf(post) { + var isSelfPost = parseInt(post.uid, 10) === parseInt(app.user.uid, 10); + if (isSelfPost) { + navigator.scrollBottom(post.index); + } + } + function onNewPostPagination(data) { function scrollToPost() { - navigator.scrollBottom(data.posts[0].index); + scrollToPostIfSelf(data.posts[0]); } var posts = data.posts; diff --git a/public/src/client/topic/threadTools.js b/public/src/client/topic/threadTools.js index 95d1e61adb..82ee651ef9 100644 --- a/public/src/client/topic/threadTools.js +++ b/public/src/client/topic/threadTools.js @@ -94,9 +94,15 @@ define('forum/topic/threadTools', ['forum/topic/fork', 'forum/topic/move', 'comp function topicCommand(command, tid) { translator.translate('[[topic:thread_tools.' + command + '_confirm]]', function(msg) { bootbox.confirm(msg, function(confirm) { - if (confirm) { - socket.emit('topics.' + command, {tids: [tid], cid: ajaxify.data.cid}); + if (!confirm) { + return; } + + socket.emit('topics.' + command, {tids: [tid], cid: ajaxify.data.cid}, function(err) { + if (err) { + app.alertError(err.message); + } + }); }); }); } diff --git a/public/src/installer/install.js b/public/src/installer/install.js index 15b3514f03..56645f0301 100644 --- a/public/src/installer/install.js +++ b/public/src/installer/install.js @@ -75,7 +75,7 @@ $('document').ready(function() { } function validateConfirmPassword(field) { - if ($('[name="admin:password"]').val() !== $('[name="admin:password:confirm"]').val()) { + if ($('[name="admin:password"]').val() !== $('[name="admin:passwordConfirm"]').val()) { parent.addClass('error'); help.html('Passwords do not match.'); } else { @@ -101,7 +101,7 @@ $('document').ready(function() { return validateUsername(field); case 'admin:password': return validatePassword(field); - case 'admin:password:confirm': + case 'admin:passwordConfirm': return validateConfirmPassword(field); case 'admin:email': return validateEmail(field); diff --git a/public/src/modules/chat.js b/public/src/modules/chat.js index 4c934b19b8..89fede22aa 100644 --- a/public/src/modules/chat.js +++ b/public/src/modules/chat.js @@ -152,8 +152,7 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra return; } - for(var x = 0; x') .attr('data-uid', userObj.uid) .html(''+ @@ -162,16 +161,15 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra userObj.username + '') .appendTo(chatsListEl); - (function(userObj) { - dropdownEl.click(function() { - if (!ajaxify.currentPage.match(/^chats\//)) { - app.openChat(userObj.username, userObj.uid); - } else { - ajaxify.go('chats/' + utils.slugify(userObj.username)); - } - }); - })(userObj); - } + + dropdownEl.click(function() { + if (!ajaxify.currentPage.match(/^chats\//)) { + app.openChat(userObj.username, userObj.uid); + } else { + ajaxify.go('chats/' + utils.slugify(userObj.username)); + } + }); + }); }); }; @@ -241,7 +239,7 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra chatModal.find('.chat-content').css('height', module.calculateChatListHeight(chatModal)); }); - + chatModal.draggable({ start:function() { module.bringModalToTop(chatModal); @@ -265,7 +263,7 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra $(window).one('action:ajaxify.end', function() { components.get('chat/input').val(text); }); - + ajaxify.go('chats/' + utils.slugify(data.username)); module.close(chatModal); } @@ -442,6 +440,7 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra input.off('keypress').on('keypress', function(e) { if (e.which === 13 && !e.shiftKey) { Chats.sendMessage(chatModal.attr('touid'), input); + return false; } }); @@ -457,6 +456,7 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra chatModal.find('#chat-message-send-btn').off('click').on('click', function(e){ Chats.sendMessage(chatModal.attr('touid'), input); + input.focus(); return false; }); } @@ -471,9 +471,9 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra Chats.parseMessage(data, function(html) { var message = $(html); - message.find('img:not(".chat-user-image")').addClass('img-responsive'); - message.find('.timeago').timeago(); message.appendTo(chatContent); + message.find('img:not(.not-responsive)').addClass('img-responsive'); + message.find('.timeago').timeago(); Chats.scrollToBottom(chatContent); if (typeof done === 'function') { @@ -490,8 +490,11 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra module.sync = function() { socket.emit('modules.chats.sync', function(err, users) { - for(var x=0,numUsers=users.length,user;x 0) { topicSearchEl.find('.count').html((index+1) + ' / ' + Search.current.results.length); topicSearchEl.find('.prev, .next').removeAttr('disabled'); - socket.emit('posts.getPidIndex', Search.current.results[index], function(err, postIndex) { - nav.scrollToPost(postIndex-1, true); // why -1? Ask @barisusakli + var data = { + pid: Search.current.results[index], + tid: Search.current.tid, + topicPostSort: config.topicPostSort + }; + socket.emit('posts.getPidIndex', data, function(err, postIndex) { + nav.scrollToPost(postIndex, true); }); } else { translator.translate('[[search:no-matches]]', function(text) { diff --git a/public/src/variables.js b/public/src/variables.js index 52674df429..6b443caa5d 100644 --- a/public/src/variables.js +++ b/public/src/variables.js @@ -9,6 +9,7 @@ var dataEl = $('#content [ajaxify-data]'); if (dataEl.length) { ajaxify.data = JSON.parse(decodeURIComponent(dataEl.attr('ajaxify-data'))); + dataEl.remove(); } }; }(ajaxify || {})); diff --git a/public/src/widgets.js b/public/src/widgets.js index 56a7927e47..18d593ad1f 100644 --- a/public/src/widgets.js +++ b/public/src/widgets.js @@ -18,7 +18,7 @@ if (template.match(/^admin/)) { return callback(); } - + var widgetLocations = ['sidebar', 'footer', 'header'], numLocations; $('#content [widget-area]').each(function() { @@ -74,7 +74,7 @@ } var widgetAreas = $('#content [widget-area]'); - widgetAreas.find('img:not(.user-img)').addClass('img-responsive'); + widgetAreas.find('img:not(.not-responsive)').addClass('img-responsive'); widgetAreas.find('.timeago').timeago(); widgetAreas.find('img[title].teaser-pic,img[title].user-img').each(function() { $(this).tooltip({ diff --git a/src/categories.js b/src/categories.js index 0626de3349..a7cd08eebe 100644 --- a/src/categories.js +++ b/src/categories.js @@ -333,6 +333,9 @@ var async = require('async'), privileges.categories.filterCids('find', children, uid, next); }, function (children, next) { + children = children.filter(function(cid) { + return parseInt(category.cid, 10) !== parseInt(cid, 10); + }); if (!children.length) { category.children = []; return callback(); diff --git a/src/categories/create.js b/src/categories/create.js index 73a2591714..fbd448407d 100644 --- a/src/categories/create.js +++ b/src/categories/create.js @@ -37,7 +37,7 @@ module.exports = function(Categories) { link: '', numRecentReplies: 1, class: ( data.class ? data.class : 'col-md-3 col-xs-6' ), - imageClass: 'auto' + imageClass: 'cover' }; plugins.fireHook('filter:category.create', {category: category, data: data}, next); diff --git a/src/categories/topics.js b/src/categories/topics.js index 9da0957c8b..ef2c69e068 100644 --- a/src/categories/topics.js +++ b/src/categories/topics.js @@ -53,6 +53,7 @@ module.exports = function(Categories) { topic.title = '[[topic:topic_is_deleted]]'; topic.slug = topic.tid; topic.teaser = null; + topic.noAnchor = true; } }); diff --git a/src/categories/update.js b/src/categories/update.js index 036b405c96..9ad8acf61e 100644 --- a/src/categories/update.js +++ b/src/categories/update.js @@ -68,6 +68,9 @@ module.exports = function(Categories) { } function updateParent(cid, newParent, callback) { + if (parseInt(cid, 10) === parseInt(newParent, 10)) { + return callback(new Error('[[error:cant-set-self-as-parent]]')); + } Categories.getCategoryField(cid, 'parentCid', function(err, oldParent) { if (err) { return callback(err); diff --git a/src/controllers/accounts.js b/src/controllers/accounts.js index 6a1d4f100e..8e2ee930c2 100644 --- a/src/controllers/accounts.js +++ b/src/controllers/accounts.js @@ -175,9 +175,11 @@ accountsController.getAccount = function(req, res, next) { userData.posts = results.posts.posts.filter(function (p) { return p && parseInt(p.deleted, 10) !== 1; }); + userData.aboutme = results.aboutme; userData.nextStart = results.posts.nextStart; userData.isFollowing = results.isFollowing; + userData.breadcrumbs = helpers.buildBreadcrumbs([{text: userData.username}]); if (!userData.profileviews) { userData.profileviews = 1; @@ -224,25 +226,26 @@ function getFollow(tpl, name, req, res, callback) { userData.users = users; userData.nextStart = 50; userData.title = '[[pages:' + tpl + ', ' + userData.username + ']]'; + userData.breadcrumbs = helpers.buildBreadcrumbs([{text: userData.username, url: '/user/' + userData.userslug}, {text: '[[user:' + name + ']]'}]); res.render(tpl, userData); }); } accountsController.getFavourites = function(req, res, next) { - getFromUserSet('account/favourites', 'favourites', posts.getPostSummariesFromSet, 'posts', req, res, next); + getFromUserSet('account/favourites', 'favourites', '[[user:favourites]]', posts.getPostSummariesFromSet, 'posts', req, res, next); }; accountsController.getPosts = function(req, res, next) { - getFromUserSet('account/posts', 'posts', posts.getPostSummariesFromSet, 'posts', req, res, next); + getFromUserSet('account/posts', 'posts', '[[global:posts]]', posts.getPostSummariesFromSet, 'posts', req, res, next); }; accountsController.getWatchedTopics = function(req, res, next) { - getFromUserSet('account/watched', 'followed_tids', topics.getTopicsFromSet, 'topics', req, res, next); + getFromUserSet('account/watched', 'followed_tids', '[[user:watched]]',topics.getTopicsFromSet, 'topics', req, res, next); }; accountsController.getTopics = function(req, res, next) { - getFromUserSet('account/topics', 'topics', topics.getTopicsFromSet, 'topics', req, res, next); + getFromUserSet('account/topics', 'topics', '[[global:topics]]', topics.getTopicsFromSet, 'topics', req, res, next); }; accountsController.getGroups = function(req, res, next) { @@ -259,12 +262,13 @@ accountsController.getGroups = function(req, res, next) { userData.groups = groupsData[0]; userData.groups.forEach(groups.escapeGroupData); userData.title = '[[pages:account/groups, ' + userData.username + ']]'; + userData.breadcrumbs = helpers.buildBreadcrumbs([{text: userData.username, url: '/user/' + userData.userslug}, {text: '[[global:header.groups]]'}]); res.render('account/groups', userData); }); }); }; -function getFromUserSet(tpl, set, method, type, req, res, next) { +function getFromUserSet(tpl, set, crumb, method, type, req, res, next) { async.parallel({ settings: function(next) { user.getSettings(req.uid, next); @@ -310,6 +314,7 @@ function getFromUserSet(tpl, set, method, type, req, res, next) { userData.pagination = pagination.create(page, pageCount); userData.title = '[[pages:' + tpl + ', ' + userData.username + ']]'; + userData.breadcrumbs = helpers.buildBreadcrumbs([{text: userData.username, url: '/user/' + userData.userslug}, {text: crumb}]); res.render(tpl, userData); }); @@ -369,8 +374,11 @@ accountsController.accountEdit = function(req, res, callback) { return callback(err); } + userData['username:disableEdit'] = parseInt(meta.config['username:disableEdit'], 10) === 1; + userData.hasPassword = !!password; userData.title = '[[pages:account/edit, ' + userData.username + ']]'; + userData.breadcrumbs = helpers.buildBreadcrumbs([{text: userData.username, url: '/user/' + userData.userslug}, {text: '[[user:edit]]'}]); res.render('account/edit', userData); }); }; @@ -457,6 +465,7 @@ accountsController.accountSettings = function(req, res, callback) { userData.disableCustomUserSkins = parseInt(meta.config.disableCustomUserSkins, 10) === 1; userData.title = '[[pages:account/settings]]'; + userData.breadcrumbs = helpers.buildBreadcrumbs([{text: userData.username, url: '/user/' + userData.userslug}, {text: '[[user:settings]]'}]); res.render('account/settings', userData); }); @@ -493,7 +502,7 @@ accountsController.uploadPicture = function (req, res, next) { } ], function(err, image) { fs.unlink(userPhoto.path, function(err) { - winston.error('unable to delete picture', err); + winston.error('unable to delete picture ' + userPhoto.path, err); }); if (err) { return next(err); @@ -510,7 +519,8 @@ accountsController.getNotifications = function(req, res, next) { } res.render('notifications', { notifications: notifications, - title: '[[pages:notifications]]' + title: '[[pages:notifications]]', + breadcrumbs: helpers.buildBreadcrumbs([{text: '[[pages:notifications]]'}]) }); }); }; @@ -550,7 +560,8 @@ accountsController.getChats = function(req, res, callback) { nextStart: results.recentChats.nextStart, contacts: results.contacts, allowed: true, - title: '[[pages:chats]]' + title: '[[pages:chats]]', + breadcrumbs: helpers.buildBreadcrumbs([{text: '[[pages:chats]]'}]) }); } @@ -584,7 +595,8 @@ accountsController.getChats = function(req, res, callback) { meta: data.toUser, messages: data.messages, allowed: data.allowed, - title: '[[pages:chat, ' + data.toUser.username + ']]' + title: '[[pages:chat, ' + data.toUser.username + ']]', + breadcrumbs: helpers.buildBreadcrumbs([{text: '[[pages:chats]]', url: '/chats'}, {text: data.toUser.username}]) }); }); }); diff --git a/src/controllers/admin.js b/src/controllers/admin.js index 776b6e9362..023df9c6b1 100644 --- a/src/controllers/admin.js +++ b/src/controllers/admin.js @@ -19,7 +19,7 @@ var async = require('async'), var adminController = { - categories: {}, + categories: require('./admin/categories'), tags: {}, flags: {}, topics: {}, @@ -142,33 +142,6 @@ function getGlobalField(field, callback) { }); } -adminController.categories.get = function(req, res, next) { - async.parallel({ - category: async.apply(categories.getCategories, [req.params.category_id], req.user.uid), - privileges: async.apply(privileges.categories.list, req.params.category_id) - }, function(err, data) { - if (err) { - return next(err); - } - - plugins.fireHook('filter:admin.category.get', {req: req, res: res, category: data.category[0], privileges: data.privileges}, function(err, data) { - if (err) { - return next(err); - } - - res.render('admin/manage/category', { - category: data.category, - privileges: data.privileges - }); - }); - }); -}; - -adminController.categories.getAll = function(req, res, next) { - //Categories list will be rendered on client side with recursion, etc. - res.render('admin/manage/categories', {}); -}; - adminController.tags.get = function(req, res, next) { topics.getTags(0, 199, function(err, tags) { if (err) { @@ -348,21 +321,49 @@ adminController.navigation.get = function(req, res, next) { }; adminController.homepage.get = function(req, res, next) { - plugins.fireHook('filter:homepage.get', {routes: [ - { - route: 'categories', - name: 'Categories' + async.waterfall([ + function(next) { + db.getSortedSetRange('categories:cid', 0, -1, next); }, - { - route: 'recent', - name: 'Recent' + function(cids, next) { + privileges.categories.filterCids('find', cids, 0, next); }, - { - route: 'popular', - name: 'Popular' + function(cids, next) { + categories.getMultipleCategoryFields(cids, ['name', 'slug'], next); + }, + function(categoryData, next) { + categoryData = categoryData.map(function(category) { + return { + route: 'category/' + category.slug, + name: 'Category: ' + category.name + }; + }); + next(null, categoryData); } - ]}, function(err, data) { - res.render('admin/general/homepage', data); + ], function(err, categoryData) { + if (err || !categoryData) categoryData = []; + + plugins.fireHook('filter:homepage.get', {routes: [ + { + route: 'categories', + name: 'Categories' + }, + { + route: 'recent', + name: 'Recent' + }, + { + route: 'popular', + name: 'Popular' + } + ].concat(categoryData)}, function(err, data) { + data.routes.push({ + route: '', + name: 'Custom' + }); + + res.render('admin/general/homepage', data); + }); }); }; diff --git a/src/controllers/admin/categories.js b/src/controllers/admin/categories.js new file mode 100644 index 0000000000..99bda3285b --- /dev/null +++ b/src/controllers/admin/categories.js @@ -0,0 +1,40 @@ +"use strict"; + +var async = require('async'), + + categories = require('../../categories'), + privileges = require('../../privileges'), + plugins = require('../../plugins'); + + +var categoriesController = {}; + +categoriesController.get = function(req, res, next) { + async.parallel({ + category: async.apply(categories.getCategories, [req.params.category_id], req.user.uid), + privileges: async.apply(privileges.categories.list, req.params.category_id) + }, function(err, data) { + if (err) { + return next(err); + } + + plugins.fireHook('filter:admin.category.get', {req: req, res: res, category: data.category[0], privileges: data.privileges}, function(err, data) { + if (err) { + return next(err); + } + + res.render('admin/manage/category', { + category: data.category, + privileges: data.privileges + }); + }); + }); +}; + +categoriesController.getAll = function(req, res, next) { + //Categories list will be rendered on client side with recursion, etc. + res.render('admin/manage/categories', {}); +}; + + +module.exports = categoriesController; diff --git a/src/controllers/admin/uploads.js b/src/controllers/admin/uploads.js index 8cdf5f81f3..cbf6e7f650 100644 --- a/src/controllers/admin/uploads.js +++ b/src/controllers/admin/uploads.js @@ -3,6 +3,7 @@ var fs = require('fs'), path = require('path'), nconf = require('nconf'), + winston = require('winston'), file = require('../../file'), plugins = require('../../plugins'); @@ -17,7 +18,11 @@ uploadsController.uploadCategoryPicture = function(req, res, next) { try { params = JSON.parse(req.body.params); } catch (e) { - fs.unlink(uploadedFile.path); + fs.unlink(uploadedFile.path, function(err) { + if (err) { + winston.error(err); + } + }); return next(e); } @@ -33,7 +38,11 @@ uploadsController.uploadFavicon = function(req, res, next) { if (validateUpload(req, res, next, uploadedFile, allowedTypes)) { file.saveFileToLocal('favicon.ico', 'system', uploadedFile.path, function(err, image) { - fs.unlink(uploadedFile.path); + fs.unlink(uploadedFile.path, function(err) { + if (err) { + winston.error(err); + } + }); if (err) { return next(err); } @@ -62,7 +71,11 @@ function upload(name, req, res, next) { function validateUpload(req, res, next, uploadedFile, allowedTypes) { if (allowedTypes.indexOf(uploadedFile.type) === -1) { - fs.unlink(uploadedFile.path); + fs.unlink(uploadedFile.path, function(err) { + if (err) { + winston.error(err); + } + }); res.json({error: '[[error:invalid-image-type, ' + allowedTypes.join(', ') + ']]'}); return false; @@ -73,7 +86,11 @@ function validateUpload(req, res, next, uploadedFile, allowedTypes) { function uploadImage(filename, folder, uploadedFile, req, res, next) { function done(err, image) { - fs.unlink(uploadedFile.path); + fs.unlink(uploadedFile.path, function(err) { + if (err) { + winston.error(err); + } + }); if (err) { return next(err); } diff --git a/src/controllers/api.js b/src/controllers/api.js index 9bbb6dc5d7..5af74d2ed6 100644 --- a/src/controllers/api.js +++ b/src/controllers/api.js @@ -54,6 +54,7 @@ apiController.getConfig = function(req, res, next) { config.usePagination = parseInt(meta.config.usePagination, 10) === 1; config.disableSocialButtons = parseInt(meta.config.disableSocialButtons, 10) === 1; config.disableChat = parseInt(meta.config.disableChat, 10) === 1; + config.maximumChatMessageLength = parseInt(meta.config.maximumChatMessageLength, 10) || 1000; config.maxReconnectionAttempts = meta.config.maxReconnectionAttempts || 5; config.reconnectionDelay = meta.config.reconnectionDelay || 1500; config.minimumTagsPerTopic = meta.config.minimumTagsPerTopic || 0; @@ -70,8 +71,6 @@ apiController.getConfig = function(req, res, next) { config.environment = process.env.NODE_ENV; config.loggedIn = !!req.user; config['cache-buster'] = meta.config['cache-buster'] || ''; - config['script-buster'] = meta.js.hash || ''; - config['css-buster'] = meta.css.hash || ''; config.requireEmailConfirmation = parseInt(meta.config.requireEmailConfirmation, 10) === 1; config.topicPostSort = meta.config.topicPostSort || 'oldest_to_newest'; config.categoryTopicSort = meta.config.categoryTopicSort || 'newest_to_oldest'; diff --git a/src/controllers/authentication.js b/src/controllers/authentication.js index beb319b04c..043776eda2 100644 --- a/src/controllers/authentication.js +++ b/src/controllers/authentication.js @@ -52,11 +52,7 @@ authenticationController.register = function(req, res, next) { return next(new Error('[[error:username-too-long')); } - if (!userData.password || userData.password.length < meta.config.minimumPasswordLength) { - return next(new Error('[[user:change_password_error_length]]')); - } - - next(); + user.isPasswordValid(userData.password, next); }, function(next) { plugins.fireHook('filter:register.check', {req: req, res: res, userData: userData}, next); diff --git a/src/controllers/categories.js b/src/controllers/categories.js index b770598311..438e970c99 100644 --- a/src/controllers/categories.js +++ b/src/controllers/categories.js @@ -66,6 +66,9 @@ categoriesController.list = function(req, res, next) { } data.title = '[[pages:categories]]'; + if (req.path.startsWith('/api/categories') || req.path.startsWith('/categories')) { + data.breadcrumbs = helpers.buildBreadcrumbs([{text: data.title}]); + } plugins.fireHook('filter:categories.build', {req: req, res: res, templateData: data}, function(err, data) { if (err) { diff --git a/src/controllers/groups.js b/src/controllers/groups.js index 2d4f97bc89..e7602f85e9 100644 --- a/src/controllers/groups.js +++ b/src/controllers/groups.js @@ -2,6 +2,7 @@ var async = require('async'), nconf = require('nconf'), + validator = require('validator'), db = require('../database'), meta = require('../meta'), groups = require('../groups'), @@ -17,6 +18,7 @@ groupsController.list = function(req, res, next) { return next(err); } data.title = '[[pages:groups]]'; + data.breadcrumbs = helpers.buildBreadcrumbs([{text: '[[pages:groups]]'}]); res.render('groups/list', data); }); }; @@ -90,17 +92,20 @@ groupsController.details = function(req, res, callback) { } results.title = '[[pages:group, ' + results.group.displayName + ']]'; + results.breadcrumbs = helpers.buildBreadcrumbs([{text: '[[pages:groups]]', url: '/groups' }, {text: results.group.displayName}]); res.render('groups/details', results); }); }); }; groupsController.members = function(req, res, next) { + var groupName; async.waterfall([ function(next) { groups.getGroupNameByGroupSlug(req.params.slug, next); }, - function(groupName, next) { + function(_groupName, next) { + groupName = _groupName; user.getUsersFromSet('group:' + groupName + ':members', req.uid, 0, 49, next); }, ], function(err, users) { @@ -108,10 +113,17 @@ groupsController.members = function(req, res, next) { return next(err); } + var breadcrumbs = helpers.buildBreadcrumbs([ + {text: '[[pages:groups]]', url: '/groups' }, + {text: validator.escape(groupName), url: '/groups/' + req.params.slug}, + {text: '[[groups:details.members]]'} + ]); + res.render('groups/members', { users: users, nextStart: 50, loadmore_display: users.length > 50 ? 'block' : 'hide', + breadcrumbs: breadcrumbs }); }); }; diff --git a/src/controllers/helpers.js b/src/controllers/helpers.js index 5486bcf009..ce1938ca30 100644 --- a/src/controllers/helpers.js +++ b/src/controllers/helpers.js @@ -39,7 +39,7 @@ helpers.notAllowed = function(req, res, error) { helpers.redirect = function(res, url) { if (res.locals.isAPI) { - res.status(302).json(url); + res.status(308).json(url); } else { res.redirect(nconf.get('relative_path') + url); } diff --git a/src/controllers/index.js b/src/controllers/index.js index 53ae658937..d4d321657b 100644 --- a/src/controllers/index.js +++ b/src/controllers/index.js @@ -33,20 +33,20 @@ var Controllers = { Controllers.home = function(req, res, next) { - var route = meta.config.homePageRoute || 'categories', + var route = meta.config.homePageRoute || meta.config.homePageCustom || 'categories', hook = 'action:homepage.get:' + route; if (plugins.hasListeners(hook)) { plugins.fireHook(hook, {req: req, res: res, next: next}); } else { - if (route === 'categories') { + if (route === 'categories' || route === '/') { Controllers.categories.list(req, res, next); } else if (route === 'recent') { Controllers.recent.get(req, res, next); } else if (route === 'popular') { Controllers.popular.get(req, res, next); } else { - next(); + res.redirect(route); } } }; diff --git a/src/controllers/popular.js b/src/controllers/popular.js index 9a39af45f6..6f80254f47 100644 --- a/src/controllers/popular.js +++ b/src/controllers/popular.js @@ -36,10 +36,13 @@ popularController.get = function(req, res, next) { topics: topics, 'feeds:disableRSS': parseInt(meta.config['feeds:disableRSS'], 10) === 1, rssFeedUrl: nconf.get('relative_path') + '/popular/' + (req.params.term || 'daily') + '.rss', - breadcrumbs: helpers.buildBreadcrumbs([{text: '[[global:header.popular]]'}]), title: '[[pages:popular-' + term + ']]' }; + if (req.path.startsWith('/api/popular') || req.path.startsWith('/popular')) { + data.breadcrumbs = helpers.buildBreadcrumbs([{text: '[[global:header.popular]]'}]); + } + if (!req.uid) { anonCache[term] = data; lastUpdateTime = Date.now(); diff --git a/src/controllers/recent.js b/src/controllers/recent.js index a7f0cfca8a..93cd2a9d09 100644 --- a/src/controllers/recent.js +++ b/src/controllers/recent.js @@ -20,8 +20,10 @@ recentController.get = function(req, res, next) { data['feeds:disableRSS'] = parseInt(meta.config['feeds:disableRSS'], 10) === 1; data.rssFeedUrl = nconf.get('relative_path') + '/recent.rss'; - data.breadcrumbs = helpers.buildBreadcrumbs([{text: '[[recent:title]]'}]); data.title = '[[pages:recent]]'; + if (req.path.startsWith('/api/recent') || req.path.startsWith('/recent')) { + data.breadcrumbs = helpers.buildBreadcrumbs([{text: '[[recent:title]]'}]); + } plugins.fireHook('filter:recent.build', {req: req, res: res, templateData: data}, function(err, data) { if (err) { diff --git a/src/controllers/search.js b/src/controllers/search.js index 5da395c2b8..874cbea1a2 100644 --- a/src/controllers/search.js +++ b/src/controllers/search.js @@ -15,60 +15,46 @@ searchController.search = function(req, res, next) { return next(); } - var breadcrumbs = helpers.buildBreadcrumbs([{text: '[[global:search]]'}]); + req.params.term = validator.escape(req.params.term); + var page = Math.max(1, parseInt(req.query.page, 10)) || 1; + if (req.query.categories && !Array.isArray(req.query.categories)) { + req.query.categories = [req.query.categories]; + } - categories.getCategoriesByPrivilege('categories:cid', req.uid, 'read', function(err, categories) { + var data = { + query: req.params.term, + searchIn: req.query.in || 'posts', + postedBy: req.query.by, + categories: req.query.categories, + searchChildren: req.query.searchChildren, + replies: req.query.replies, + repliesFilter: req.query.repliesFilter, + timeRange: req.query.timeRange, + timeFilter: req.query.timeFilter, + sortBy: req.query.sortBy, + sortDirection: req.query.sortDirection, + page: page, + uid: req.uid + }; + + search.search(data, function(err, results) { if (err) { return next(err); } - categories = categories.filter(function(category) { - return category && !category.link; - }); + results.pagination = pagination.create(page, results.pageCount, req.query); + results.showAsPosts = !req.query.showAs || req.query.showAs === 'posts'; + results.showAsTopics = req.query.showAs === 'topics'; + results.breadcrumbs = helpers.buildBreadcrumbs([{text: '[[global:search]]'}]); + results.expandSearch = !req.params.term; - req.params.term = validator.escape(req.params.term); - var page = Math.max(1, parseInt(req.query.page, 10)) || 1; - if (req.query.categories && !Array.isArray(req.query.categories)) { - req.query.categories = [req.query.categories]; - } - - var data = { - query: req.params.term, - searchIn: req.query.in || 'posts', - postedBy: req.query.by, - categories: req.query.categories, - searchChildren: req.query.searchChildren, - replies: req.query.replies, - repliesFilter: req.query.repliesFilter, - timeRange: req.query.timeRange, - timeFilter: req.query.timeFilter, - sortBy: req.query.sortBy, - sortDirection: req.query.sortDirection, - page: page, - uid: req.uid - }; - - search.search(data, function(err, results) { + plugins.fireHook('filter:search.build', {data: data, results: results}, function(err, data) { if (err) { return next(err); } - - results.pagination = pagination.create(page, results.pageCount, req.query); - results.showAsPosts = !req.query.showAs || req.query.showAs === 'posts'; - results.showAsTopics = req.query.showAs === 'topics'; - results.breadcrumbs = breadcrumbs; - results.categories = categories; - results.expandSearch = !req.params.term; - - plugins.fireHook('filter:search.build', {data: data, results: results}, function(err, data) { - if (err) { - return next(err); - } - res.render('search', data.results); - }); + res.render('search', data.results); }); }); }; - module.exports = searchController; diff --git a/src/controllers/topics.js b/src/controllers/topics.js index c1798d5bb4..93ab192823 100644 --- a/src/controllers/topics.js +++ b/src/controllers/topics.js @@ -81,7 +81,8 @@ topicsController.get = function(req, res, callback) { set = 'tid:' + tid + ':posts:votes'; } - var postIndex = 0; + var postIndex = 0, + index = 0; req.params.post_index = parseInt(req.params.post_index, 10) || 0; if (reverse && req.params.post_index === 1) { @@ -94,7 +95,6 @@ topicsController.get = function(req, res, callback) { postIndex = Math.max(0, (req.params.post_index || 1) - Math.ceil(settings.postsPerPage / 2)); } } else if (!req.query.page) { - var index = 0; if (reverse) { index = Math.max(0, postCount - (req.params.post_index || postCount)); } else { @@ -118,6 +118,7 @@ topicsController.get = function(req, res, callback) { topicData.pageCount = pageCount; topicData.currentPage = page; + topicData.postIndex = postIndex || index || req.params.post_index; if (page > 1) { topicData.posts.splice(0, 1); @@ -147,10 +148,11 @@ topicsController.get = function(req, res, callback) { }); }, function (topicData, next) { - var description = ''; + var description = '', + idx = topicData.postIndex; - if (topicData.posts[0] && topicData.posts[0].content) { - description = S(topicData.posts[0].content).stripTags().decodeHTMLEntities().s; + if (topicData.posts[idx] && topicData.posts[idx].content) { + description = S(topicData.posts[idx].content).stripTags().decodeHTMLEntities().s; } if (description.length > 255) { @@ -163,8 +165,8 @@ topicsController.get = function(req, res, callback) { var ogImageUrl = ''; if (topicData.thumb) { ogImageUrl = topicData.thumb; - } else if(topicData.posts.length && topicData.posts[0] && topicData.posts[0].user && topicData.posts[0].user.picture){ - ogImageUrl = topicData.posts[0].user.picture; + } else if(topicData.posts.length && topicData.posts[idx] && topicData.posts[idx].user && topicData.posts[idx].user.picture){ + ogImageUrl = topicData.posts[idx].user.picture; } else if(meta.config['brand:logo']) { ogImageUrl = meta.config['brand:logo']; } else { diff --git a/src/controllers/uploads.js b/src/controllers/uploads.js index 1c99026463..be5d80bf23 100644 --- a/src/controllers/uploads.js +++ b/src/controllers/uploads.js @@ -136,9 +136,14 @@ function uploadFile(uid, uploadedFile, callback) { } function deleteTempFiles(files) { - for(var i=0; i (stop - start + 1) ? 'block' : 'hide', users: data.users, pagination: pagination.create(1, pageCount), - title: setToTitles[set] || '[[pages:users/latest]]' + title: setToTitles[set] || '[[pages:users/latest]]', + breadcrumbs: helpers.buildBreadcrumbs([{text: setToCrumbs[set]}]) }; userData['route_' + set] = true; render(req, res, userData, next); @@ -127,7 +135,8 @@ usersController.getUsersForSearch = function(req, res, next) { search_display: 'block', loadmore_display: 'hidden', users: data.users, - title: '[[pages:users/search]]' + title: '[[pages:users/search]]', + breadcrumbs: helpers.buildBreadcrumbs([{text: '[[pages:users/search]]'}]) }; render(req, res, userData, next); @@ -203,7 +212,7 @@ usersController.getMap = function(req, res, next) { } }); - res.render('usersMap', {rooms: data, title: '[[pages:users/map]]'}); + res.render('usersMap', {rooms: data, title: '[[pages:users/map]]', breadcrumbs: helpers.buildBreadcrumbs([{text: '[[pages:users/map]]'}])}); }); }; @@ -212,6 +221,7 @@ function render(req, res, data, next) { if (err) { return next(err); } + data.templateData.inviteOnly = meta.config.registrationType === 'invite-only'; res.render('users', data.templateData); }); diff --git a/src/emailer.js b/src/emailer.js index 14c683d640..221c693dab 100644 --- a/src/emailer.js +++ b/src/emailer.js @@ -45,49 +45,64 @@ var fs = require('fs'), }; Emailer.sendToEmail = function(template, email, language, params, callback) { - callback = callback || function() {}; - async.parallel({ - html: function(next) { - app.render('emails/' + template, params, next); - }, - plaintext: function(next) { - app.render('emails/' + template + '_plaintext', params, next); - } - }, function(err, results) { - if (err) { - winston.error('[emailer] Error sending digest : ' + err.stack); - return callback(err); - } - async.map([results.html, results.plaintext, params.subject], function(raw, next) { - translator.translate(raw, language || meta.config.defaultLang || 'en_GB', function(translated) { - next(undefined, translated); - }); - }, function(err, translated) { - if (err) { - return callback(err); - } - - if (Plugins.hasListeners('action:email.send')) { - Plugins.fireHook('action:email.send', { - to: email, - from: meta.config['email:from'] || 'no-reply@localhost.lan', - from_name: meta.config['email:from_name'] || 'NodeBB', - subject: translated[2], - html: translated[0], - plaintext: translated[1], - template: template, - uid: params.uid, - pid: params.pid, - fromUid: params.fromUid + function renderAndTranslate(tpl, params, callback) { + async.waterfall([ + function (next) { + app.render(tpl, params, next); + }, + function (html, next) { + translator.translate(html, lang, function(translated) { + next(null, translated); }); - callback(); - } else { - winston.warn('[emailer] No active email plugin found!'); - callback(); } - }); - }); + ], callback); + } + callback = callback || function() {}; + + if (!Plugins.hasListeners('action:email.send')) { + winston.warn('[emailer] No active email plugin found!'); + return callback(); + } + + var lang = language || meta.config.defaultLang || 'en_GB'; + + async.waterfall([ + function (next) { + async.parallel({ + html: function(next) { + renderAndTranslate('emails/' + template, params, next); + }, + plaintext: function(next) { + renderAndTranslate('emails/' + template + '_plaintext', params, next); + }, + subject: function(next) { + translator.translate(params.subject, lang, function(translated) { + next(null, translated); + }); + } + }, next); + }, + function (results, next) { + var data = { + to: email, + from: meta.config['email:from'] || 'no-reply@localhost.lan', + from_name: meta.config['email:from_name'] || 'NodeBB', + subject: results.subject, + html: results.html, + plaintext: results.plaintext, + template: template, + uid: params.uid, + pid: params.pid, + fromUid: params.fromUid + }; + Plugins.fireHook('filter:email.send', data, next); + }, + function (data, next) { + Plugins.fireHook('action:email.send', data); + next(); + } + ], callback); }; diff --git a/src/groups.js b/src/groups.js index 13bae53e5f..5e5a19b984 100644 --- a/src/groups.js +++ b/src/groups.js @@ -80,28 +80,7 @@ var async = require('async'), }); } - async.parallel({ - groups: function(next) { - Groups.getGroupsData(groupNames, next); - }, - members: function(next) { - Groups.getMemberUsers(groupNames, 0, 3, next); - } - }, function (err, data) { - if (err) { - return callback(err); - } - data.groups.forEach(function(group, index) { - if (!group) { - return; - } - Groups.escapeGroupData(group); - group.members = data.members[index] || []; - group.truncated = group.memberCount > data.members.length; - }); - - callback(null, data.groups); - }); + Groups.getGroupsAndMembers(groupNames, callback); } }; @@ -109,6 +88,31 @@ var async = require('async'), db.getSortedSetRevRange(set, start, stop, callback); }; + Groups.getGroupsAndMembers = function(groupNames, callback) { + async.parallel({ + groups: function(next) { + Groups.getGroupsData(groupNames, next); + }, + members: function(next) { + Groups.getMemberUsers(groupNames, 0, 3, next); + } + }, function (err, data) { + if (err) { + return callback(err); + } + data.groups.forEach(function(group, index) { + if (!group) { + return; + } + Groups.escapeGroupData(group); + group.members = data.members[index] || []; + group.truncated = group.memberCount > data.members.length; + }); + + callback(null, data.groups); + }); + }; + Groups.get = function(groupName, options, callback) { if (!groupName) { return callback(new Error('[[error:invalid-group]]')); @@ -197,6 +201,10 @@ var async = require('async'), }); }; + Groups.getOwners = function(groupName, callback) { + db.getSetMembers('group:' + groupName + ':owners', callback); + }; + Groups.getOwnersAndMembers = function(groupName, uid, start, stop, callback) { async.parallel({ owners: function (next) { @@ -436,7 +444,7 @@ var async = require('async'), } }); - Groups.getGroupsData(memberOf, next); + Groups.getGroupsAndMembers(memberOf, next); }); }, next); } diff --git a/src/groups/create.js b/src/groups/create.js index d8eb4a0ab9..3eeed9a893 100644 --- a/src/groups/create.js +++ b/src/groups/create.js @@ -7,63 +7,80 @@ var async = require('async'), db = require('../database'); module.exports = function(Groups) { + Groups.create = function(data, callback) { - if (data.name.length === 0) { + var system = data.name === 'administrators' || data.name === 'registered-users' || Groups.isPrivilegeGroup(data.name); + var groupData; + var timestamp = data.timestamp || Date.now(); + + async.waterfall([ + function (next) { + validateGroupName(data.name, next); + }, + function (next) { + meta.userOrGroupExists(data.name, next); + }, + function (exists, next) { + if (exists) { + return next(new Error('[[error:group-already-exists]]')); + } + + var memberCount = data.hasOwnProperty('ownerUid') ? 1 : 0; + var slug = utils.slugify(data.name); + groupData = { + name: data.name, + slug: slug, + createtime: timestamp, + userTitle: data.name, + description: data.description || '', + memberCount: memberCount, + deleted: '0', + hidden: data.hidden || '0', + system: system ? '1' : '0', + private: data.private || '1' + }; + plugins.fireHook('filter:group.create', {group: groupData, data: data}, next); + }, + function (results, next) { + var tasks = [ + async.apply(db.sortedSetAdd, 'groups:createtime', groupData.createtime, groupData.name), + async.apply(db.setObject, 'group:' + groupData.name, groupData) + ]; + + if (data.hasOwnProperty('ownerUid')) { + tasks.push(async.apply(db.setAdd, 'group:' + groupData.name + ':owners', data.ownerUid)); + tasks.push(async.apply(db.sortedSetAdd, 'group:' + groupData.name + ':members', timestamp, data.ownerUid)); + + groupData.ownerUid = data.ownerUid; + } + + if (!data.hidden && !system) { + tasks.push(async.apply(db.sortedSetAdd, 'groups:visible:createtime', timestamp, groupData.name)); + tasks.push(async.apply(db.sortedSetAdd, 'groups:visible:memberCount', groupData.memberCount, groupData.name)); + tasks.push(async.apply(db.sortedSetAdd, 'groups:visible:name', 0, groupData.name.toLowerCase() + ':' + groupData.name)); + } + + tasks.push(async.apply(db.setObjectField, 'groupslug:groupname', groupData.slug, groupData.name)); + + async.series(tasks, next); + }, + function (results, next) { + plugins.fireHook('action:group.create', groupData); + next(null, groupData); + } + ], callback); + + }; + + function validateGroupName(name, callback) { + if (!name) { return callback(new Error('[[error:group-name-too-short]]')); } - var system = data.name === 'administrators' || data.name === 'registered-users' || Groups.isPrivilegeGroup(data.name); + if (name.indexOf('/') !== -1) { + return callback(new Error('[[error:invalid-group-name]]')); + } - meta.userOrGroupExists(data.name, function (err, exists) { - if (err) { - return callback(err); - } - - if (exists) { - return callback(new Error('[[error:group-already-exists]]')); - } - var timestamp = data.timestamp || Date.now(); - var memberCount = data.hasOwnProperty('ownerUid') ? 1 : 0; - var slug = utils.slugify(data.name); - var groupData = { - name: data.name, - slug: slug, - createtime: timestamp, - userTitle: data.name, - description: data.description || '', - memberCount: memberCount, - deleted: '0', - hidden: data.hidden || '0', - system: system ? '1' : '0', - private: data.private || '1' - }; - var tasks = [ - async.apply(db.sortedSetAdd, 'groups:createtime', timestamp, data.name), - async.apply(db.setObject, 'group:' + data.name, groupData) - ]; - - if (data.hasOwnProperty('ownerUid')) { - tasks.push(async.apply(db.setAdd, 'group:' + data.name + ':owners', data.ownerUid)); - tasks.push(async.apply(db.sortedSetAdd, 'group:' + data.name + ':members', timestamp, data.ownerUid)); - - groupData.ownerUid = data.ownerUid; - } - - if (!data.hidden && !system) { - tasks.push(async.apply(db.sortedSetAdd, 'groups:visible:createtime', timestamp, data.name)); - tasks.push(async.apply(db.sortedSetAdd, 'groups:visible:memberCount', memberCount, data.name)); - tasks.push(async.apply(db.sortedSetAdd, 'groups:visible:name', 0, data.name.toLowerCase() + ':' + data.name)); - } - - tasks.push(async.apply(db.setObjectField, 'groupslug:groupname', slug, data.name)); - - async.series(tasks, function(err) { - if (!err) { - plugins.fireHook('action:group.create', groupData); - } - - callback(err, groupData); - }); - }); - }; + callback(); + } }; diff --git a/src/groups/membership.js b/src/groups/membership.js index 51ee633787..c8cf9a9871 100644 --- a/src/groups/membership.js +++ b/src/groups/membership.js @@ -5,7 +5,9 @@ var async = require('async'), _ = require('underscore'), user = require('../user'), + utils = require('../../public/src/utils'), plugins = require('../plugins'), + notifications = require('../notifications'), db = require('./../database'); module.exports = function(Groups) { @@ -66,7 +68,29 @@ module.exports = function(Groups) { }; Groups.requestMembership = function(groupName, uid, callback) { - inviteOrRequestMembership(groupName, uid, 'request', callback); + async.waterfall([ + async.apply(inviteOrRequestMembership, groupName, uid, 'request'), + function(next) { + user.getUserField(uid, 'username', function(err, username) { + if (err) { + return next(err); + } + next(null, { + bodyShort: '[[groups:request.notification_title, ' + username + ']]', + bodyLong: '[[groups:request.notification_text, ' + username + ', ' + groupName + ']]', + nid: 'group:' + groupName + ':uid:' + uid + ':request', + path: '/groups/' + utils.slugify(groupName) + }); + }); + }, + async.apply(notifications.create), + function(notification, next) { + Groups.getOwners(groupName, function(err, ownerUids) { + next(null, notification, ownerUids); + }); + }, + async.apply(notifications.push) + ], callback); }; Groups.acceptMembership = function(groupName, uid, callback) { @@ -87,7 +111,19 @@ module.exports = function(Groups) { }; Groups.invite = function(groupName, uid, callback) { - inviteOrRequestMembership(groupName, uid, 'invite', callback); + async.waterfall([ + async.apply(inviteOrRequestMembership, groupName, uid, 'invite'), + async.apply(notifications.create, { + bodyShort: '[[groups:invited.notification_title, ' + groupName + ']]', + bodyLong: '', + nid: 'group:' + groupName + ':uid:' + uid + ':invite', + path: '/groups/' + utils.slugify(groupName) + }), + function(notification, next) { + next(null, notification, [uid]); + }, + async.apply(notifications.push) + ], callback); }; function inviteOrRequestMembership(groupName, uid, type, callback) { diff --git a/src/groups/ownership.js b/src/groups/ownership.js index 4af6126aef..aa085f8213 100644 --- a/src/groups/ownership.js +++ b/src/groups/ownership.js @@ -1,6 +1,8 @@ 'use strict'; -var db = require('../database'); +var async = require('async'), + db = require('../database'), + plugins = require('../plugins'); module.exports = function(Groups) { @@ -23,22 +25,35 @@ module.exports = function(Groups) { Groups.ownership.grant = function(toUid, groupName, callback) { // Note: No ownership checking is done here on purpose! - db.setAdd('group:' + groupName + ':owners', toUid, callback); + async.waterfall([ + function(next) { + db.setAdd('group:' + groupName + ':owners', toUid, next); + }, + function(next) { + plugins.fireHook('action:group.grantOwnership', {uid: toUid, groupName: groupName}); + next(); + } + ], callback); }; Groups.ownership.rescind = function(toUid, groupName, callback) { // Note: No ownership checking is done here on purpose! // If the owners set only contains one member, error out! - db.setCount('group:' + groupName + ':owners', function(err, numOwners) { - if (err) { - return callback(err); + async.waterfall([ + function (next) { + db.setCount('group:' + groupName + ':owners', next); + }, + function (numOwners, next) { + if (numOwners <= 1) { + return next(new Error('[[error:group-needs-owner]]')); + } + db.setRemove('group:' + groupName + ':owners', toUid, next); + }, + function (next) { + plugins.fireHook('action:group.rescindOwnership', {uid: toUid, groupName: groupName}); + next(); } - if (numOwners <= 1) { - return callback(new Error('[[error:group-needs-owner]]')); - } - - db.setRemove('group:' + groupName + ':owners', toUid, callback); - }); + ], callback); }; }; diff --git a/src/install.js b/src/install.js index caafcbfacc..9fa6a1cba0 100644 --- a/src/install.js +++ b/src/install.js @@ -283,11 +283,11 @@ function enableDefaultTheme(next) { process.stdout.write('Previous theme detected, skipping enabling default theme\n'); return next(err); } - - process.stdout.write('Enabling default theme: Persona\n'); + var defaultTheme = nconf.get('defaultTheme') || 'nodebb-theme-persona'; + process.stdout.write('Enabling default theme: ' + defaultTheme + '\n'); meta.themes.set({ type: 'local', - id: 'nodebb-theme-persona' + id: defaultTheme }, next); }); } @@ -347,16 +347,20 @@ function createAdmin(callback) { winston.warn("Passwords did not match, please try again"); return retryPassword(results); } - + var adminUid; async.waterfall([ function(next) { User.create({username: results.username, password: results.password, email: results.email}, next); }, function(uid, next) { + adminUid = uid; Groups.join('administrators', uid, next); }, function(next) { Groups.show('administrators', next); + }, + function(next) { + Groups.ownership.grant(adminUid, 'administrators', next); } ], function(err) { if (err) { @@ -488,6 +492,8 @@ function enableDefaultPlugins(next) { ], customDefaults = nconf.get('defaultPlugins'); + winston.info('[install/defaultPlugins] customDefaults', customDefaults); + if (customDefaults && customDefaults.length) { try { customDefaults = JSON.parse(customDefaults); @@ -502,6 +508,8 @@ function enableDefaultPlugins(next) { return array.indexOf(plugin) === index; }); + winston.info('[install/enableDefaultPlugins] activating default plugins', defaultEnabled); + var db = require('./database'); var order = defaultEnabled.map(function(plugin, index) { return index; diff --git a/src/messaging.js b/src/messaging.js index ab27543f96..f3020ed8f5 100644 --- a/src/messaging.js +++ b/src/messaging.js @@ -28,8 +28,6 @@ var db = require('./database'), } Messaging.addMessage = function(fromuid, touid, content, timestamp, callback) { - var uids = sortUids(fromuid, touid); - if (typeof timestamp === 'function') { callback = timestamp; timestamp = Date.now(); @@ -37,6 +35,16 @@ var db = require('./database'), timestamp = timestamp || Date.now(); } + if (!content) { + return callback(new Error('[[error:invalid-chat-message]]')); + } + + if (content.length > (meta.config.maximumChatMessageLength || 1000)) { + return callback(new Error('[[error:chat-message-too-long]]')); + } + + var uids = sortUids(fromuid, touid); + db.incrObjectField('global', 'nextMid', function(err, mid) { if (err) { return callback(err); @@ -255,20 +263,16 @@ var db = require('./database'), user.getMultipleUserFields(uids, ['uid', 'username', 'picture', 'status'] , next); }, teasers: function(next) { - var teasers = []; - async.each(uids, function(fromuid, next) { + async.map(uids, function(fromuid, next) { Messaging.getMessages({ fromuid: fromuid, touid: uid, isNew: false, count: 1 }, function(err, teaser) { - teasers[uids.indexOf(fromuid)] = teaser[0]; - next(err); + next(err, teaser[0]); }); - }, function(err) { - next(err, teasers); - }); + }, next); } }, function(err, results) { if (err) { @@ -276,8 +280,11 @@ var db = require('./database'), } results.users.forEach(function(user, index) { - if (user && !parseInt(user.uid, 10)) { + if (user && parseInt(user.uid, 10)) { Messaging.markRead(uid, uids[index]); + user.unread = results.unread[index]; + user.status = sockets.isUserOnline(user.uid) ? user.status : 'offline'; + user.teaser = results.teasers[index]; } }); @@ -285,18 +292,6 @@ var db = require('./database'), return user && parseInt(user.uid, 10); }); - if (!results.users.length) { - return callback(null, {users: [], nextStart: stop + 1}); - } - - results.users.forEach(function(user, index) { - if (user) { - user.unread = results.unread[index]; - user.status = sockets.isUserOnline(user.uid) ? user.status : 'offline'; - user.teaser = results.teasers[index]; - } - }); - callback(null, {users: results.users, nextStart: stop + 1}); }); }); diff --git a/src/meta.js b/src/meta.js index a9b276c313..316d6349ce 100644 --- a/src/meta.js +++ b/src/meta.js @@ -10,7 +10,8 @@ var async = require('async'), groups = require('./groups'), emitter = require('./emitter'), pubsub = require('./pubsub'), - auth = require('./routes/authentication'); + auth = require('./routes/authentication'), + utils = require('../public/src/utils'); (function (Meta) { Meta.reloadRequired = false; @@ -63,6 +64,7 @@ var async = require('async'), async.apply(Meta.templates.compile), async.apply(auth.reloadRoutes), function(next) { + Meta.config['cache-buster'] = utils.generateUUID(); templates.flush(); next(); } diff --git a/src/meta/configs.js b/src/meta/configs.js index 65ffb45b11..b7b6a13b33 100644 --- a/src/meta/configs.js +++ b/src/meta/configs.js @@ -4,7 +4,8 @@ var winston = require('winston'), db = require('../database'), pubsub = require('../pubsub'), - nconf = require('nconf'); + nconf = require('nconf'), + utils = require('../../public/src/utils'); module.exports = function(Meta) { @@ -20,6 +21,8 @@ module.exports = function(Meta) { return callback(err); } + config['cache-buster'] = utils.generateUUID(); + Meta.config = config; callback(); }); diff --git a/src/meta/css.js b/src/meta/css.js index bc4ea92352..f3257e624f 100644 --- a/src/meta/css.js +++ b/src/meta/css.js @@ -18,8 +18,6 @@ module.exports = function(Meta) { Meta.css = {}; Meta.css.cache = undefined; Meta.css.acpCache = undefined; - Meta.css.branding = {}; - Meta.css.defaultBranding = {}; Meta.css.minify = function(callback) { callback = callback || function() {}; @@ -94,8 +92,7 @@ module.exports = function(Meta) { process.send({ action: 'css-propagate', cache: minified[0], - acpCache: minified[1], - hash: Meta.css.hash + acpCache: minified[1] }); } @@ -188,14 +185,6 @@ module.exports = function(Meta) { Meta.css[destination] = lessOutput.css; - if (destination === 'cache') { - // Calculate css buster - var hasher = crypto.createHash('md5'); - - hasher.update(lessOutput.css, 'utf-8'); - Meta.css.hash = hasher.digest('hex').slice(0, 8); - } - // Save the compiled CSS in public/ so things like nginx can serve it if (nconf.get('isPrimary') === 'true') { Meta.css.commitToFile(destination); diff --git a/src/meta/js.js b/src/meta/js.js index 21dc1c4cee..c703f5332e 100644 --- a/src/meta/js.js +++ b/src/meta/js.js @@ -18,7 +18,6 @@ module.exports = function(Meta) { Meta.js = { cache: '', map: '', - hash: +new Date(), scripts: { base: [ 'public/vendor/jquery/js/jquery.js', @@ -142,8 +141,7 @@ module.exports = function(Meta) { process.send({ action: 'js-propagate', cache: Meta.js.cache, - map: Meta.js.map, - hash: Meta.js.hash + map: Meta.js.map }); } @@ -161,9 +159,6 @@ module.exports = function(Meta) { Meta.js.map = message.sourceMap; onComplete(); break; - case 'hash': - Meta.js.hash = message.payload; - break; case 'error': winston.error('[meta/js] Could not compile client-side scripts! ' + message.payload.message); minifier.kill(); diff --git a/src/meta/tags.js b/src/meta/tags.js index 022a3ef778..64e183e43e 100644 --- a/src/meta/tags.js +++ b/src/meta/tags.js @@ -1,46 +1,55 @@ +'use strict'; + var nconf = require('nconf'), validator = require('validator'), async = require('async'), + winston = require('winston'), plugins = require('../plugins'); module.exports = function(Meta) { Meta.tags = {}; Meta.tags.parse = function(meta, link, callback) { - async.parallel([ - async.apply(plugins.fireHook, 'filter:meta.getMetaTags', [{ - name: 'viewport', - content: 'width=device-width, initial-scale=1.0, user-scalable=no' - }, { - name: 'content-type', - content: 'text/html; charset=UTF-8' - }, { - name: 'apple-mobile-web-app-capable', - content: 'yes' - }, { - property: 'og:site_name', - content: Meta.config.title || 'NodeBB' - }, { - name: 'keywords', - content: Meta.config.keywords || '' - }, { - name: 'msapplication-badge', - content: 'frequency=30; polling-uri=' + nconf.get('url') + '/sitemap.xml' - }, { - name: 'msapplication-square150x150logo', - content: Meta.config['brand:logo'] || '' - }]), - async.apply(plugins.fireHook, 'filter:meta.getLinkTags', [{ - rel: "icon", - type: "image/x-icon", - href: nconf.get('relative_path') + '/favicon.ico' - }, { - rel: 'apple-touch-icon', - href: nconf.get('relative_path') + '/apple-touch-icon' - }]) - ], function(err, tags) { - meta = tags[0].concat(meta || []).map(function(tag) { - if(!tag || typeof tag.content !== 'string') { + async.parallel({ + tags: function(next) { + var defaultTags = [{ + name: 'viewport', + content: 'width=device-width, initial-scale=1.0, user-scalable=no' + }, { + name: 'content-type', + content: 'text/html; charset=UTF-8' + }, { + name: 'apple-mobile-web-app-capable', + content: 'yes' + }, { + property: 'og:site_name', + content: Meta.config.title || 'NodeBB' + }, { + name: 'keywords', + content: Meta.config.keywords || '' + }, { + name: 'msapplication-badge', + content: 'frequency=30; polling-uri=' + nconf.get('url') + '/sitemap.xml' + }, { + name: 'msapplication-square150x150logo', + content: Meta.config['brand:logo'] || '' + }]; + plugins.fireHook('filter:meta.getMetaTags', defaultTags, next); + }, + links: function(next) { + var defaultLinks = [{ + rel: "icon", + type: "image/x-icon", + href: nconf.get('relative_path') + '/favicon.ico' + }, { + rel: 'apple-touch-icon', + href: nconf.get('relative_path') + '/apple-touch-icon' + }]; + plugins.fireHook('filter:meta.getLinkTags', defaultLinks, next); + } + }, function(err, results) { + meta = results.tags.concat(meta || []).map(function(tag) { + if (!tag || typeof tag.content !== 'string') { winston.warn('Invalid meta tag. ', tag); return tag; } @@ -49,7 +58,9 @@ module.exports = function(Meta) { return tag; }); - link = tags[1].concat(link || []); + addDescription(meta); + + link = results.links.concat(link || []); callback(null, { meta: meta, @@ -57,4 +68,20 @@ module.exports = function(Meta) { }); }); }; + + function addDescription(meta) { + var hasDescription = false; + meta.forEach(function(tag) { + if (tag.name === 'description') { + hasDescription = true; + } + }); + + if (!hasDescription) { + meta.push({ + name: 'description', + content: validator.escape(Meta.config.description || '') + }); + } + } }; \ No newline at end of file diff --git a/src/middleware/middleware.js b/src/middleware/middleware.js index 207356013a..c381acea20 100644 --- a/src/middleware/middleware.js +++ b/src/middleware/middleware.js @@ -320,7 +320,6 @@ middleware.processRender = function(req, res, next) { self.send(str); }; - options = options || {}; if ('function' === typeof options) { @@ -346,13 +345,14 @@ middleware.processRender = function(req, res, next) { return res.json(options); } + var ajaxifyData = encodeURIComponent(JSON.stringify(options)); render.call(self, template, options, function(err, str) { if (err) { winston.error(err); return fn(err); } - str = str + ''; + str = str + ''; str = (res.locals.postHeader ? res.locals.postHeader : '') + str + (res.locals.preFooter ? res.locals.preFooter : ''); if (res.locals.footer) { @@ -457,7 +457,7 @@ function redirectToLogin(req, res) { } function modifyTitle(obj) { - var title = '[[pages:home]] | ' + validator.escape(meta.config.title || 'NodeBB'); + var title = '[[pages:home]] | ' + validator.escape(meta.config.browserTitle || meta.config.title || 'NodeBB'); obj.browserTitle = title; if (obj.metaTags) { diff --git a/src/plugins/install.js b/src/plugins/install.js index 693e90c866..cdc8743b69 100644 --- a/src/plugins/install.js +++ b/src/plugins/install.js @@ -44,8 +44,8 @@ module.exports = function(Plugins) { if (err) { return next(err); } - db.sortedSetAdd('plugins:active', count, id, next); - }); + db.sortedSetAdd('plugins:active', count, id, next); + }); } }, function(next) { diff --git a/src/postTools.js b/src/postTools.js index d702274abf..9458152635 100644 --- a/src/postTools.js +++ b/src/postTools.js @@ -8,10 +8,6 @@ var async = require('async'), (function(PostTools) { - PostTools.edit = function(data, callback) { - posts.edit(data, callback); - }; - PostTools.delete = function(uid, pid, callback) { togglePostDelete(uid, pid, true, callback); }; @@ -60,13 +56,18 @@ var async = require('async'), } PostTools.purge = function(uid, pid, callback) { - privileges.posts.canEdit(pid, uid, function(err, canEdit) { - if (err || !canEdit) { - return callback(err || new Error('[[error:no-privileges]]')); + async.waterfall([ + function (next) { + privileges.posts.canPurge(pid, uid, next); + }, + function (canPurge, next) { + if (!canPurge) { + return next(new Error('[[error:no-privileges]]')); + } + cache.del(pid); + posts.purge(pid, next); } - cache.del(pid); - posts.purge(pid, callback); - }); + ], callback); }; diff --git a/src/posts.js b/src/posts.js index 82be31fea0..24d3371c23 100644 --- a/src/posts.js +++ b/src/posts.js @@ -168,25 +168,13 @@ var async = require('async'), }); }; - Posts.getPidIndex = function(pid, uid, callback) { - async.parallel({ - settings: function(next) { - user.getSettings(uid, next); - }, - tid: function(next) { - Posts.getPostField(pid, 'tid', next); + Posts.getPidIndex = function(pid, tid, topicPostSort, callback) { + var set = topicPostSort === 'most_votes' ? 'tid:' + tid + ':posts:votes' : 'tid:' + tid + ':posts'; + db.sortedSetRank(set, pid, function(err, index) { + if (!utils.isNumber(index)) { + return callback(err, 0); } - }, function(err, results) { - if(err) { - return callback(err); - } - var set = results.settings.topicPostSort === 'most_votes' ? 'tid:' + results.tid + ':posts:votes' : 'tid:' + results.tid + ':posts'; - db.sortedSetRank(set, pid, function(err, index) { - if (!utils.isNumber(index)) { - return callback(err, 1); - } - callback(err, parseInt(index, 10) + 2); - }); + callback(err, parseInt(index, 10) + 1); }); }; diff --git a/src/privileges/categories.js b/src/privileges/categories.js index feb3c53bc6..3e454ccf9f 100644 --- a/src/privileges/categories.js +++ b/src/privileges/categories.js @@ -2,6 +2,7 @@ 'use strict'; var async = require('async'), + _ = require('underscore'), user = require('../user'), categories = require('../categories'), @@ -14,64 +15,61 @@ module.exports = function(privileges) { privileges.categories = {}; privileges.categories.list = function(cid, callback) { - // Method used in admin/category controller to show all users with privs in that given cid + // Method used in admin/category controller to show all users/groups with privs in that given cid + + var privilegeLabels = [ + {name: 'Find category'}, + {name: 'Access & Read'}, + {name: 'Create Topics'}, + {name: 'Reply to Topics'}, + {name: 'Purge'}, + {name: 'Moderate'} + ]; + + var userPrivilegeList = [ + 'find', 'read', 'topics:create', 'topics:reply', 'purge', 'mods' + ]; + var groupPrivilegeList = [ + 'groups:find', 'groups:read', 'groups:topics:create', 'groups:topics:reply', 'groups:purge', 'groups:moderate' + ]; + async.parallel({ labels: function(next) { async.parallel({ - users: async.apply(plugins.fireHook, 'filter:privileges.list_human', - ['Find category', 'Access & Read', 'Create Topics', 'Reply to Topics', 'Moderate'].map(function(name) { - return { - name: name - }; - }) - ), - groups: async.apply(plugins.fireHook, 'filter:privileges.groups.list_human', - ['Find category', 'Access & Read', 'Create Topics', 'Reply to Topics', 'Moderate'].map(function(name) { - return { - name: name - }; - }) - ) + users: async.apply(plugins.fireHook, 'filter:privileges.list_human', privilegeLabels), + groups: async.apply(plugins.fireHook, 'filter:privileges.groups.list_human', privilegeLabels) }, next); }, users: function(next) { var privileges; async.waterfall([ - async.apply(plugins.fireHook, 'filter:privileges.list', [ - 'find', 'read', 'topics:create', 'topics:reply', 'mods' - ]), + async.apply(plugins.fireHook, 'filter:privileges.list', userPrivilegeList), function(privs, next) { privileges = privs; groups.getMembersOfGroups(privs.map(function(privilege) { return 'cid:' + cid + ':privileges:' + privilege; - }), function(err, memberSets) { + }), next); + }, + function(memberSets, next) { + + memberSets = memberSets.map(function(set) { + return set.map(function(uid) { + return parseInt(uid, 10); + }); + }); + + var members = _.unique(_.flatten(memberSets)); + + user.getMultipleUserFields(members, ['picture', 'username'], function(err, memberData) { if (err) { return next(err); } - next(null, memberSets.map(function(set) { - return set.map(function(uid) { - return parseInt(uid, 10); - }); - })); - }); - }, - function(memberSets, next) { - // Reduce into a single array - var members = memberSets.reduce(function(combined, curMembers) { - return combined.concat(curMembers); - }).filter(function(member, index, combined) { - return combined.indexOf(member) === index; - }); - - user.getMultipleUserFields(members, ['picture', 'username'], function(err, memberData) { - memberData = memberData.map(function(member) { + memberData.forEach(function(member) { member.privileges = {}; for(var x=0,numPrivs=privileges.length;x 0) { - plugins.fireHook('action:topic.reply', postData); - } - postData.topic.title = validator.escape(postData.topic.title); + next(null, postData); } ], callback); - }; + } function checkTitleLength(title, callback) { if (!title || title.length < parseInt(meta.config.minimumTitleLength, 10)) { diff --git a/src/topics/follow.js b/src/topics/follow.js index 2dd3e434f4..16d54f04fb 100644 --- a/src/topics/follow.js +++ b/src/topics/follow.js @@ -46,6 +46,9 @@ module.exports = function(Topics) { Topics.follow = function(tid, uid, callback) { callback = callback || function() {}; + if (!parseInt(uid, 10)) { + return callback(); + } async.waterfall([ function (next) { Topics.exists(tid, next); @@ -133,13 +136,14 @@ module.exports = function(Topics) { bodyShort: '[[notifications:user_posted_to, ' + postData.user.username + ', ' + title + ']]', bodyLong: postData.content, pid: postData.pid, - nid: 'tid:' + postData.topic.tid + ':pid:' + postData.pid + ':uid:' + exceptUid, + nid: 'new_post:tid:' + postData.topic.tid + ':pid:' + postData.pid + ':uid:' + exceptUid, tid: postData.topic.tid, from: exceptUid }, function(err, notification) { if (err) { return next(err); } + if (notification) { notifications.push(notification, followers); } diff --git a/src/topics/posts.js b/src/topics/posts.js index f0265e7048..217c91284a 100644 --- a/src/topics/posts.js +++ b/src/topics/posts.js @@ -44,12 +44,7 @@ module.exports = function(Topics) { return callback(err); } - var indices = Topics.calculatePostIndices(start, stop, results.postCount, reverse); - results.posts.forEach(function(post, index) { - if (post) { - post.index = indices[index]; - } - }); + Topics.calculatePostIndices(results.posts, start, stop, results.postCount, reverse); Topics.addPostData(results.posts, uid, callback); }); @@ -151,17 +146,14 @@ module.exports = function(Topics) { }); }; - Topics.calculatePostIndices = function(start, stop, postCount, reverse) { - var indices = []; - var count = stop - start + 1; - for(var i=0; i uploadSize * 1024) { return callback(new Error('[[error:file-too-big, ' + uploadSize + ']]')); } - request.get(url) - .on('error', function(err) { - winston.error(err); - }) - .pipe(fs.createWriteStream(filename)) - .on('close', function(err) { - if (err) { - return callback(err); - } - callback(null, {path: filename, size: size, type: type, name: filename + '.' + extension}); - }); + var picture = {url: url, name: ''}; + plugins.fireHook('filter:uploadImage', {image: picture, uid: uid}, function(err, image) { + if (err) { + return callback(err); + } + User.setUserFields(uid, {uploadedpicture: image.url, picture: image.url}); + callback(null, image); + }); }); - } + }; }; \ No newline at end of file diff --git a/src/user/posts.js b/src/user/posts.js index c7d8e230be..895b5dbc86 100644 --- a/src/user/posts.js +++ b/src/user/posts.js @@ -6,7 +6,7 @@ var async = require('async'), module.exports = function(User) { - User.isReadyToPost = function(uid, callback) { + User.isReadyToPost = function(uid, cid, callback) { if (parseInt(uid, 10) === 0) { return callback(); } @@ -20,6 +20,9 @@ module.exports = function(User) { }, isAdmin: function(next) { User.isAdministrator(uid, next); + }, + isModerator: function(next) { + User.isModerator(uid, cid, next); } }, function(err, results) { if (err) { @@ -30,7 +33,7 @@ module.exports = function(User) { return callback(new Error('[[error:no-user]]')); } - if (results.isAdmin) { + if (results.isAdmin || results.isModerator) { return callback(); } diff --git a/src/user/reset.js b/src/user/reset.js index e9a5e46c1a..26acc5ecbc 100644 --- a/src/user/reset.js +++ b/src/user/reset.js @@ -77,6 +77,9 @@ var async = require('async'), UserReset.commit = function(code, password, callback) { var uid; async.waterfall([ + function(next) { + user.isPasswordValid(password, next); + }, function(next) { UserReset.validate(code, next); }, diff --git a/src/user/settings.js b/src/user/settings.js index 5af9f567c1..17e524bcb7 100644 --- a/src/user/settings.js +++ b/src/user/settings.js @@ -60,27 +60,36 @@ module.exports = function(User) { settings.showemail = parseInt(settings.showemail, 10) === 1; settings.showfullname = parseInt(settings.showfullname, 10) === 1; - settings.openOutgoingLinksInNewTab = parseInt(settings.openOutgoingLinksInNewTab, 10) === 1; - settings.dailyDigestFreq = settings.dailyDigestFreq || 'off'; - settings.usePagination = (settings.usePagination === null || settings.usePagination === undefined) ? parseInt(meta.config.usePagination, 10) === 1 : parseInt(settings.usePagination, 10) === 1; + settings.openOutgoingLinksInNewTab = parseInt(getSetting(settings, 'openOutgoingLinksInNewTab', 0), 10) === 1; + settings.dailyDigestFreq = getSetting(settings, 'dailyDigestFreq', 'off'); + settings.usePagination = parseInt(getSetting(settings, 'usePagination', 0), 10) === 1; settings.topicsPerPage = Math.min(settings.topicsPerPage ? parseInt(settings.topicsPerPage, 10) : defaultTopicsPerPage, defaultTopicsPerPage); settings.postsPerPage = Math.min(settings.postsPerPage ? parseInt(settings.postsPerPage, 10) : defaultPostsPerPage, defaultPostsPerPage); settings.notificationSounds = parseInt(settings.notificationSounds, 10) === 1; settings.userLang = settings.userLang || meta.config.defaultLang || 'en_GB'; - settings.topicPostSort = settings.topicPostSort || meta.config.topicPostSort || 'oldest_to_newest'; - settings.categoryTopicSort = settings.categoryTopicSort || meta.config.categoryTopicSort || 'newest_to_oldest'; - settings.followTopicsOnCreate = (settings.followTopicsOnCreate === null || settings.followTopicsOnCreate === undefined) ? true : parseInt(settings.followTopicsOnCreate, 10) === 1; - settings.followTopicsOnReply = parseInt(settings.followTopicsOnReply, 10) === 1; - settings.sendChatNotifications = parseInt(settings.sendChatNotifications, 10) === 1; - settings.sendPostNotifications = parseInt(settings.sendPostNotifications, 10) === 1; + settings.topicPostSort = getSetting(settings, 'topicPostSort', 'oldest_to_newest'); + settings.categoryTopicSort = getSetting(settings, 'categoryTopicSort', 'newest_to_oldest'); + settings.followTopicsOnCreate = parseInt(getSetting(settings, 'followTopicsOnCreate', 1), 10) === 1; + settings.followTopicsOnReply = parseInt(getSetting(settings, 'followTopicsOnReply', 0), 10) === 1; + settings.sendChatNotifications = parseInt(getSetting(settings, 'sendChatNotifications', 0), 10) === 1; + settings.sendPostNotifications = parseInt(getSetting(settings, 'sendPostNotifications', 0), 10) === 1; settings.restrictChat = parseInt(settings.restrictChat, 10) === 1; - settings.topicSearchEnabled = parseInt(settings.topicSearchEnabled, 10) === 1; + settings.topicSearchEnabled = parseInt(getSetting(settings, 'topicSearchEnabled', 0), 10) === 1; settings.bootswatchSkin = settings.bootswatchSkin || 'default'; callback(null, settings); }); } + function getSetting(settings, key, base) { + if (settings[key] || settings[key] === 0) { + return settings[key]; + } else if (meta.config[key] || meta.config[key] === 0) { + return meta.config[key]; + } + return base; + } + User.saveSettings = function(uid, data, callback) { if (invalidPaginationSettings(data)) { return callback(new Error('[[error:invalid-pagination-value]]')); @@ -90,27 +99,32 @@ module.exports = function(User) { plugins.fireHook('action:user.saveSettings', {uid: uid, settings: data}); + var settings = { + showemail: data.showemail, + showfullname: data.showfullname, + openOutgoingLinksInNewTab: data.openOutgoingLinksInNewTab, + dailyDigestFreq: data.dailyDigestFreq || 'off', + usePagination: data.usePagination, + topicsPerPage: Math.min(data.topicsPerPage, parseInt(meta.config.topicsPerPage, 10) || 20), + postsPerPage: Math.min(data.postsPerPage, parseInt(meta.config.postsPerPage, 10) || 20), + notificationSounds: data.notificationSounds, + userLang: data.userLang || meta.config.defaultLang, + followTopicsOnCreate: data.followTopicsOnCreate, + followTopicsOnReply: data.followTopicsOnReply, + sendChatNotifications: data.sendChatNotifications, + sendPostNotifications: data.sendPostNotifications, + restrictChat: data.restrictChat, + topicSearchEnabled: data.topicSearchEnabled, + groupTitle: data.groupTitle + }; + + if (data.bootswatchSkin) { + settings.bootswatchSkin = data.bootswatchSkin; + } + async.waterfall([ function(next) { - db.setObject('user:' + uid + ':settings', { - showemail: data.showemail, - showfullname: data.showfullname, - openOutgoingLinksInNewTab: data.openOutgoingLinksInNewTab, - dailyDigestFreq: data.dailyDigestFreq || 'off', - usePagination: data.usePagination, - topicsPerPage: Math.min(data.topicsPerPage, parseInt(meta.config.topicsPerPage, 10) || 20), - postsPerPage: Math.min(data.postsPerPage, parseInt(meta.config.postsPerPage, 10) || 20), - notificationSounds: data.notificationSounds, - userLang: data.userLang || meta.config.defaultLang, - followTopicsOnCreate: data.followTopicsOnCreate, - followTopicsOnReply: data.followTopicsOnReply, - sendChatNotifications: data.sendChatNotifications, - sendPostNotifications: data.sendPostNotifications, - restrictChat: data.restrictChat, - topicSearchEnabled: data.topicSearchEnabled, - bootswatchSkin: data.bootswatchSkin, - groupTitle: data.groupTitle - }, next); + db.setObject('user:' + uid + ':settings', settings, next); }, function(next) { updateDigestSetting(uid, data.dailyDigestFreq, next); diff --git a/src/views/admin/general/homepage.tpl b/src/views/admin/general/homepage.tpl index 812b31b077..5148d79da9 100644 --- a/src/views/admin/general/homepage.tpl +++ b/src/views/admin/general/homepage.tpl @@ -12,6 +12,11 @@ +
+ diff --git a/src/views/admin/partials/menu.tpl b/src/views/admin/partials/menu.tpl index 4c483ad1ba..183adbb3e3 100644 --- a/src/views/admin/partials/menu.tpl +++ b/src/views/admin/partials/menu.tpl @@ -18,7 +18,7 @@
  • Users
  • Registration Queue
  • Groups
  • -
  • Flags
  • +
  • Flags
  • @@ -32,6 +32,7 @@
  • Group
  • Guests
  • Post
  • +
  • Chat
  • Pagination
  • Tags
  • Notifications
  • @@ -108,8 +109,8 @@

    - - + +
    • Group
    • Guests
    • Post
    • +
    • Chat
    • Pagination
    • Tags
    • Notifications
    • diff --git a/src/views/admin/settings/chat.tpl b/src/views/admin/settings/chat.tpl new file mode 100644 index 0000000000..828bf339a0 --- /dev/null +++ b/src/views/admin/settings/chat.tpl @@ -0,0 +1,26 @@ + + + +
      +
      Chat Settings
      +
      +
      +
      + +
      +
      +
      + Chat Message Inbox Size
      +
      + +
      + + +
      +
      +
      + + \ No newline at end of file diff --git a/src/views/admin/settings/general.tpl b/src/views/admin/settings/general.tpl index 2d04a9ad89..dda578a529 100644 --- a/src/views/admin/settings/general.tpl +++ b/src/views/admin/settings/general.tpl @@ -26,7 +26,7 @@
      -
      +
      @@ -90,12 +90,7 @@ Disable social buttons -
      - -
      + diff --git a/src/views/admin/settings/post.tpl b/src/views/admin/settings/post.tpl index 8697b81f4b..b669021204 100644 --- a/src/views/admin/settings/post.tpl +++ b/src/views/admin/settings/post.tpl @@ -114,15 +114,6 @@ -
      -
      Chat Settings
      -
      -
      - Chat Message Inbox Size
      -
      -
      -
      -
      Upload Settings
      diff --git a/src/views/admin/settings/user.tpl b/src/views/admin/settings/user.tpl index b0bec269e3..f5cc3884ee 100644 --- a/src/views/admin/settings/user.tpl +++ b/src/views/admin/settings/user.tpl @@ -44,6 +44,12 @@
      Account Settings
      +
      + +
      +
      +
      Default User Settings
      +
      + +
      + +
      + +
      + +
      + +
      + + +
      + +
      + +
      + +
      + +
      + +
      + +
      + +
      + +
      + + +
      +
      + \ No newline at end of file diff --git a/src/webserver.js b/src/webserver.js index abeb367a90..9b6fb24e8c 100644 --- a/src/webserver.js +++ b/src/webserver.js @@ -117,24 +117,7 @@ function cacheStaticFiles(callback) { app.enable('cache'); app.enable('minification'); - - // Configure cache-buster timestamp - require('child_process').exec('git describe --tags', { - cwd: path.join(__dirname, '../') - }, function(err, stdOut) { - if (!err) { - meta.config['cache-buster'] = stdOut.trim(); - callback(); - } else { - fs.stat(path.join(__dirname, '../package.json'), function(err, stats) { - if (err) { - return callback(err); - } - meta.config['cache-buster'] = new Date(stats.mtime).getTime(); - callback(); - }); - } - }); + callback(); } function listen(callback) {