diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..e0fa7609a5 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,13 @@ +# These files are text and should be normalized (convert crlf => lf) +*.json text +*.css text +*.less text +*.tpl text +*.html text +*.js text +*.md text + +# Images should be treated as binary +# (binary is a macro for -text -diff) +*.png binary +*.jpg binary \ No newline at end of file diff --git a/.gitignore b/.gitignore index 2f966c4535..82ed213c66 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,6 @@ public/css/*.css *.sublime-project *.sublime-workspace .project -.idea *.swp Vagrantfile .vagrant @@ -31,4 +30,14 @@ pidfile /public/stylesheet.css /public/admin.css /public/nodebb.min.js -/public/nodebb.min.js.map \ No newline at end of file +/public/nodebb.min.js.map + +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio +*.iml + +## Directory-based project format: +.idea/ + +## File-based project format: +*.ipr +*.iws \ No newline at end of file diff --git a/.tx/config b/.tx/config index 3bebe996b1..7581d243ab 100644 --- a/.tx/config +++ b/.tx/config @@ -9,6 +9,7 @@ trans.ar = public/language/ar/category.json trans.bn = public/language/bn/category.json trans.bg = public/language/bg/category.json trans.cs = public/language/cs/category.json +trans.da = public/language/da/category.json trans.de = public/language/de/category.json trans.el = public/language/el/category.json trans.en_US = public/language/en_US/category.json @@ -34,6 +35,7 @@ trans.ru = public/language/ru/category.json trans.ro = public/language/ro/category.json trans.sc = public/language/sc/category.json trans.sk = public/language/sk/category.json +trans.sr = public/language/sr/category.json trans.sv = public/language/sv/category.json trans.th = public/language/th/category.json trans.tr = public/language/tr/category.json @@ -50,6 +52,7 @@ trans.ar = public/language/ar/login.json trans.bn = public/language/bn/login.json trans.bg = public/language/bg/login.json trans.cs = public/language/cs/login.json +trans.da = public/language/da/login.json trans.de = public/language/de/login.json trans.el = public/language/el/login.json trans.en_US = public/language/en_US/login.json @@ -75,6 +78,7 @@ trans.ru = public/language/ru/login.json trans.ro = public/language/ro/login.json trans.sc = public/language/sc/login.json trans.sk = public/language/sk/login.json +trans.sr = public/language/sr/login.json trans.sv = public/language/sv/login.json trans.th = public/language/th/login.json trans.tr = public/language/tr/login.json @@ -84,12 +88,14 @@ trans.zh_TW = public/language/zh_TW/login.json type = KEYVALUEJSON [nodebb.recent] +file_filter = public/language//recent.json source_file = public/language/en_GB/recent.json source_lang = en_GB trans.ar = public/language/ar/recent.json trans.bn = public/language/bn/recent.json trans.bg = public/language/bg/recent.json trans.cs = public/language/cs/recent.json +trans.da = public/language/da/recent.json trans.de = public/language/de/recent.json trans.el = public/language/el/recent.json trans.en_US = public/language/en_US/recent.json @@ -115,6 +121,7 @@ trans.ru = public/language/ru/recent.json trans.ro = public/language/ro/recent.json trans.sc = public/language/sc/recent.json trans.sk = public/language/sk/recent.json +trans.sr = public/language/sr/recent.json trans.sv = public/language/sv/recent.json trans.th = public/language/th/recent.json trans.tr = public/language/tr/recent.json @@ -124,12 +131,14 @@ trans.zh_TW = public/language/zh_TW/recent.json type = KEYVALUEJSON [nodebb.unread] +file_filter = public/language//unread.json source_file = public/language/en_GB/unread.json source_lang = en_GB trans.ar = public/language/ar/unread.json trans.bn = public/language/bn/unread.json trans.bg = public/language/bg/unread.json trans.cs = public/language/cs/unread.json +trans.da = public/language/da/unread.json trans.de = public/language/de/unread.json trans.el = public/language/el/unread.json trans.en_US = public/language/en_US/unread.json @@ -155,6 +164,7 @@ trans.ru = public/language/ru/unread.json trans.ro = public/language/ro/unread.json trans.sc = public/language/sc/unread.json trans.sk = public/language/sk/unread.json +trans.sr = public/language/sr/unread.json trans.sv = public/language/sv/unread.json trans.th = public/language/th/unread.json trans.tr = public/language/tr/unread.json @@ -164,12 +174,14 @@ trans.zh_TW = public/language/zh_TW/unread.json type = KEYVALUEJSON [nodebb.modules] +file_filter = public/language//modules.json source_file = public/language/en_GB/modules.json source_lang = en_GB trans.ar = public/language/ar/modules.json trans.bn = public/language/bn/modules.json trans.bg = public/language/bg/modules.json trans.cs = public/language/cs/modules.json +trans.da = public/language/da/modules.json trans.de = public/language/de/modules.json trans.el = public/language/el/modules.json trans.en_US = public/language/en_US/modules.json @@ -195,6 +207,7 @@ trans.ru = public/language/ru/modules.json trans.ro = public/language/ro/modules.json trans.sc = public/language/sc/modules.json trans.sk = public/language/sk/modules.json +trans.sr = public/language/sr/modules.json trans.sv = public/language/sv/modules.json trans.th = public/language/th/modules.json trans.tr = public/language/tr/modules.json @@ -204,12 +217,14 @@ trans.zh_TW = public/language/zh_TW/modules.json type = KEYVALUEJSON [nodebb.register] +file_filter = public/language//register.json source_file = public/language/en_GB/register.json source_lang = en_GB trans.ar = public/language/ar/register.json trans.bn = public/language/bn/register.json trans.bg = public/language/bg/register.json trans.cs = public/language/cs/register.json +trans.da = public/language/da/register.json trans.de = public/language/de/register.json trans.el = public/language/el/register.json trans.en_US = public/language/en_US/register.json @@ -235,6 +250,7 @@ trans.ru = public/language/ru/register.json trans.ro = public/language/ro/register.json trans.sc = public/language/sc/register.json trans.sk = public/language/sk/register.json +trans.sr = public/language/sr/register.json trans.sv = public/language/sv/register.json trans.th = public/language/th/register.json trans.tr = public/language/tr/register.json @@ -244,12 +260,14 @@ trans.zh_TW = public/language/zh_TW/register.json type = KEYVALUEJSON [nodebb.user] +file_filter = public/language//user.json source_file = public/language/en_GB/user.json source_lang = en_GB trans.ar = public/language/ar/user.json trans.bn = public/language/bn/user.json trans.bg = public/language/bg/user.json trans.cs = public/language/cs/user.json +trans.da = public/language/da/user.json trans.de = public/language/de/user.json trans.el = public/language/el/user.json trans.en_US = public/language/en_US/user.json @@ -275,6 +293,7 @@ trans.ru = public/language/ru/user.json trans.ro = public/language/ro/user.json trans.sc = public/language/sc/user.json trans.sk = public/language/sk/user.json +trans.sr = public/language/sr/user.json trans.sv = public/language/sv/user.json trans.th = public/language/th/user.json trans.tr = public/language/tr/user.json @@ -284,12 +303,14 @@ trans.zh_TW = public/language/zh_TW/user.json type = KEYVALUEJSON [nodebb.global] +file_filter = public/language//global.json source_file = public/language/en_GB/global.json source_lang = en_GB trans.ar = public/language/ar/global.json trans.bn = public/language/bn/global.json trans.bg = public/language/bg/global.json trans.cs = public/language/cs/global.json +trans.da = public/language/da/global.json trans.de = public/language/de/global.json trans.el = public/language/el/global.json trans.en_US = public/language/en_US/global.json @@ -315,6 +336,7 @@ trans.ru = public/language/ru/global.json trans.ro = public/language/ro/global.json trans.sc = public/language/sc/global.json trans.sk = public/language/sk/global.json +trans.sr = public/language/sr/global.json trans.sv = public/language/sv/global.json trans.th = public/language/th/global.json trans.tr = public/language/tr/global.json @@ -324,12 +346,14 @@ trans.zh_TW = public/language/zh_TW/global.json type = KEYVALUEJSON [nodebb.notifications] +file_filter = public/language//notifications.json source_file = public/language/en_GB/notifications.json source_lang = en_GB trans.ar = public/language/ar/notifications.json trans.bn = public/language/bn/notifications.json trans.bg = public/language/bg/notifications.json trans.cs = public/language/cs/notifications.json +trans.da = public/language/da/notifications.json trans.de = public/language/de/notifications.json trans.el = public/language/el/notifications.json trans.en_US = public/language/en_US/notifications.json @@ -355,6 +379,7 @@ trans.ru = public/language/ru/notifications.json trans.ro = public/language/ro/notifications.json trans.sc = public/language/sc/notifications.json trans.sk = public/language/sk/notifications.json +trans.sr = public/language/sr/notifications.json trans.sv = public/language/sv/notifications.json trans.th = public/language/th/notifications.json trans.tr = public/language/tr/notifications.json @@ -364,12 +389,14 @@ trans.zh_TW = public/language/zh_TW/notifications.json type = KEYVALUEJSON [nodebb.reset_password] +file_filter = public/language//reset_password.json source_file = public/language/en_GB/reset_password.json source_lang = en_GB trans.ar = public/language/ar/reset_password.json trans.bn = public/language/bn/reset_password.json trans.bg = public/language/bg/reset_password.json trans.cs = public/language/cs/reset_password.json +trans.da = public/language/da/reset_password.json trans.de = public/language/de/reset_password.json trans.el = public/language/el/reset_password.json trans.en_US = public/language/en_US/reset_password.json @@ -395,6 +422,7 @@ trans.ru = public/language/ru/reset_password.json trans.ro = public/language/ro/reset_password.json trans.sc = public/language/sc/reset_password.json trans.sk = public/language/sk/reset_password.json +trans.sr = public/language/sr/reset_password.json trans.sv = public/language/sv/reset_password.json trans.th = public/language/th/reset_password.json trans.tr = public/language/tr/reset_password.json @@ -404,12 +432,14 @@ trans.zh_TW = public/language/zh_TW/reset_password.json type = KEYVALUEJSON [nodebb.users] +file_filter = public/language//users.json source_file = public/language/en_GB/users.json source_lang = en_GB trans.ar = public/language/ar/users.json trans.bn = public/language/bn/users.json trans.bg = public/language/bg/users.json trans.cs = public/language/cs/users.json +trans.da = public/language/da/users.json trans.de = public/language/de/users.json trans.el = public/language/el/users.json trans.en_US = public/language/en_US/users.json @@ -435,6 +465,7 @@ trans.ru = public/language/ru/users.json trans.ro = public/language/ro/users.json trans.sc = public/language/sc/users.json trans.sk = public/language/sk/users.json +trans.sr = public/language/sr/users.json trans.sv = public/language/sv/users.json trans.th = public/language/th/users.json trans.tr = public/language/tr/users.json @@ -444,12 +475,14 @@ trans.zh_TW = public/language/zh_TW/users.json type = KEYVALUEJSON [nodebb.language] +file_filter = public/language//language.json source_file = public/language/en_GB/language.json source_lang = en_GB trans.ar = public/language/ar/language.json trans.bn = public/language/bn/language.json trans.bg = public/language/bg/language.json trans.cs = public/language/cs/language.json +trans.da = public/language/da/language.json trans.de = public/language/de/language.json trans.el = public/language/el/language.json trans.en_US = public/language/en_US/language.json @@ -475,6 +508,7 @@ trans.ru = public/language/ru/language.json trans.ro = public/language/ro/language.json trans.sc = public/language/sc/language.json trans.sk = public/language/sk/language.json +trans.sr = public/language/sr/language.json trans.sv = public/language/sv/language.json trans.th = public/language/th/language.json trans.tr = public/language/tr/language.json @@ -484,12 +518,14 @@ trans.zh_TW = public/language/zh_TW/language.json type = KEYVALUEJSON [nodebb.pages] +file_filter = public/language//pages.json source_file = public/language/en_GB/pages.json source_lang = en_GB trans.ar = public/language/ar/pages.json trans.bn = public/language/bn/pages.json trans.bg = public/language/bg/pages.json trans.cs = public/language/cs/pages.json +trans.da = public/language/da/pages.json trans.de = public/language/de/pages.json trans.el = public/language/el/pages.json trans.en_US = public/language/en_US/pages.json @@ -515,6 +551,7 @@ trans.ru = public/language/ru/pages.json trans.ro = public/language/ro/pages.json trans.sc = public/language/sc/pages.json trans.sk = public/language/sk/pages.json +trans.sr = public/language/sr/pages.json trans.sv = public/language/sv/pages.json trans.th = public/language/th/pages.json trans.tr = public/language/tr/pages.json @@ -524,12 +561,14 @@ trans.zh_TW = public/language/zh_TW/pages.json type = KEYVALUEJSON [nodebb.topic] +file_filter = public/language//topic.json source_file = public/language/en_GB/topic.json source_lang = en_GB trans.ar = public/language/ar/topic.json trans.bn = public/language/bn/topic.json trans.bg = public/language/bg/topic.json trans.cs = public/language/cs/topic.json +trans.da = public/language/da/topic.json trans.de = public/language/de/topic.json trans.el = public/language/el/topic.json trans.en_US = public/language/en_US/topic.json @@ -555,6 +594,7 @@ trans.ru = public/language/ru/topic.json trans.ro = public/language/ro/topic.json trans.sc = public/language/sc/topic.json trans.sk = public/language/sk/topic.json +trans.sr = public/language/sr/topic.json trans.sv = public/language/sv/topic.json trans.th = public/language/th/topic.json trans.tr = public/language/tr/topic.json @@ -564,12 +604,14 @@ trans.zh_TW = public/language/zh_TW/topic.json type = KEYVALUEJSON [nodebb.success] +file_filter = public/language//success.json source_file = public/language/en_GB/success.json source_lang = en_GB trans.ar = public/language/ar/success.json trans.bn = public/language/bn/success.json trans.bg = public/language/bg/success.json trans.cs = public/language/cs/success.json +trans.da = public/language/da/success.json trans.de = public/language/de/success.json trans.el = public/language/el/success.json trans.en_US = public/language/en_US/success.json @@ -595,6 +637,7 @@ trans.ru = public/language/ru/success.json trans.ro = public/language/ro/success.json trans.sc = public/language/sc/success.json trans.sk = public/language/sk/success.json +trans.sr = public/language/sr/success.json trans.sv = public/language/sv/success.json trans.th = public/language/th/success.json trans.tr = public/language/tr/success.json @@ -604,12 +647,14 @@ trans.zh_TW = public/language/zh_TW/success.json type = KEYVALUEJSON [nodebb.error] +file_filter = public/language//error.json source_file = public/language/en_GB/error.json source_lang = en_GB trans.ar = public/language/ar/error.json trans.bn = public/language/bn/error.json trans.bg = public/language/bg/error.json trans.cs = public/language/cs/error.json +trans.da = public/language/da/error.json trans.de = public/language/de/error.json trans.el = public/language/el/error.json trans.en_US = public/language/en_US/error.json @@ -635,6 +680,7 @@ trans.ru = public/language/ru/error.json trans.ro = public/language/ro/error.json trans.sc = public/language/sc/error.json trans.sk = public/language/sk/error.json +trans.sr = public/language/sr/error.json trans.sv = public/language/sv/error.json trans.th = public/language/th/error.json trans.tr = public/language/tr/error.json @@ -644,12 +690,14 @@ trans.zh_TW = public/language/zh_TW/error.json type = KEYVALUEJSON [nodebb.tags] +file_filter = public/language//tags.json source_file = public/language/en_GB/tags.json source_lang = en_GB trans.ar = public/language/ar/tags.json trans.bn = public/language/bn/tags.json trans.bg = public/language/bg/tags.json trans.cs = public/language/cs/tags.json +trans.da = public/language/da/tags.json trans.de = public/language/de/tags.json trans.el = public/language/el/tags.json trans.en_US = public/language/en_US/tags.json @@ -675,6 +723,7 @@ trans.ru = public/language/ru/tags.json trans.ro = public/language/ro/tags.json trans.sc = public/language/sc/tags.json trans.sk = public/language/sk/tags.json +trans.sr = public/language/sr/tags.json trans.sv = public/language/sv/tags.json trans.th = public/language/th/tags.json trans.tr = public/language/tr/tags.json @@ -684,12 +733,14 @@ trans.zh_TW = public/language/zh_TW/tags.json type = KEYVALUEJSON [nodebb.email] +file_filter = public/language//email.json source_file = public/language/en_GB/email.json source_lang = en_GB trans.ar = public/language/ar/email.json trans.bn = public/language/bn/email.json trans.bg = public/language/bg/email.json trans.cs = public/language/cs/email.json +trans.da = public/language/da/email.json trans.de = public/language/de/email.json trans.el = public/language/el/email.json trans.en_US = public/language/en_US/email.json @@ -715,6 +766,7 @@ trans.ru = public/language/ru/email.json trans.ro = public/language/ro/email.json trans.sc = public/language/sc/email.json trans.sk = public/language/sk/email.json +trans.sr = public/language/sr/email.json trans.sv = public/language/sv/email.json trans.th = public/language/th/email.json trans.tr = public/language/tr/email.json @@ -724,12 +776,14 @@ trans.zh_TW = public/language/zh_TW/email.json type = KEYVALUEJSON [nodebb.search] +file_filter = public/language//search.json source_file = public/language/en_GB/search.json source_lang = en_GB trans.ar = public/language/ar/search.json trans.bn = public/language/bn/search.json trans.bg = public/language/bg/search.json trans.cs = public/language/cs/search.json +trans.da = public/language/da/search.json trans.de = public/language/de/search.json trans.el = public/language/el/search.json trans.en_US = public/language/en_US/search.json @@ -755,6 +809,7 @@ trans.ru = public/language/ru/search.json trans.ro = public/language/ro/search.json trans.sc = public/language/sc/search.json trans.sk = public/language/sk/search.json +trans.sr = public/language/sr/search.json trans.sv = public/language/sv/search.json trans.th = public/language/th/search.json trans.tr = public/language/tr/search.json @@ -764,12 +819,14 @@ trans.zh_TW = public/language/zh_TW/search.json type = KEYVALUEJSON [nodebb.groups] +file_filter = public/language//groups.json source_file = public/language/en_GB/groups.json source_lang = en_GB trans.ar = public/language/ar/groups.json trans.bn = public/language/bn/groups.json trans.bg = public/language/bg/groups.json trans.cs = public/language/cs/groups.json +trans.da = public/language/da/groups.json trans.de = public/language/de/groups.json trans.el = public/language/el/groups.json trans.en_US = public/language/en_US/groups.json @@ -795,6 +852,7 @@ trans.ru = public/language/ru/groups.json trans.ro = public/language/ro/groups.json trans.sc = public/language/sc/groups.json trans.sk = public/language/sk/groups.json +trans.sr = public/language/sr/groups.json trans.sv = public/language/sv/groups.json trans.th = public/language/th/groups.json trans.tr = public/language/tr/groups.json diff --git a/README.md b/README.md index d8b5435dbc..9062b049ae 100644 --- a/README.md +++ b/README.md @@ -1,65 +1,75 @@ -# NodeBB - -[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/NodeBB/NodeBB?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -[![Build Status](https://travis-ci.org/NodeBB/NodeBB.svg?branch=master)](https://travis-ci.org/NodeBB/NodeBB) -[![Dependency Status](https://david-dm.org/nodebb/nodebb.svg)](https://david-dm.org/nodebb/nodebb) -[![Code Climate](https://codeclimate.com/github/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. - -Additional functionality is enabled through the use of third-party plugins. - -* [Get NodeBB](http://www.nodebb.org/ "NodeBB") -* [Demo & Meta Discussion](http://community.nodebb.org) -* [NodeBB Blog](http://blog.nodebb.org) -* [Documentation & Installation Instructions](http://docs.nodebb.org) -* [Join us on IRC](https://kiwiirc.com/client/irc.freenode.net/nodebb) - #nodebb on Freenode -* [Follow us on Twitter](http://www.twitter.com/NodeBB/ "NodeBB Twitter") -* [Like us on Facebook](http://www.facebook.com/NodeBB/ "NodeBB Facebook") -* [Get Plugins](http://community.nodebb.org/category/7/nodebb-plugins "NodeBB Plugins") -* [Get Themes](http://community.nodebb.org/category/10/nodebb-themes "NodeBB Themes") -* [Help translate NodeBB](https://www.transifex.com/projects/p/nodebb/) - -## Screenshots - -[](http://i.imgur.com/FLOUuIq.png) [](http://i.imgur.com/Ud1LrfI.png) [](http://i.imgur.com/ZC8W39a.png) [](http://i.imgur.com/o90kVPi.png) [](http://i.imgur.com/AaRRrU2.png) [](http://i.imgur.com/LmHtPho.png) [](http://i.imgur.com/paiJPJk.jpg) [](http://i.imgur.com/ZfavPHD.png) [](http://i.imgur.com/8OLssij.png) [](http://i.imgur.com/JKOc0LZ.png) - -## How can I follow along/contribute? - -* Our feature roadmap is hosted on the project wiki's [Version History / Roadmap](https://github.com/NodeBB/NodeBB/wiki/Version-History-%26-Roadmap) -* If you are a developer, feel free to check out the source and submit pull requests. We also have a wide array of [plugins](http://community.nodebb.org/category/7/nodebb-plugins) which would be a great starting point for learning the codebase. -* If you are a designer, [NodeBB needs themes](http://community.nodebb.org/category/10/nodebb-themes)! NodeBB's theming system allows extention of the base templates as well as styling via LESS or CSS. NodeBB's base theme utilizes [Bootstrap 3](http://getbootstrap.com/) but themes can choose to use a different framework altogether. -* If you know languages other than English you can help us translate NodeBB. We use [Transifex](https://www.transifex.com/projects/p/nodebb/) for internationalization. -* Please don't forget to **like**, **follow**, and **star our repo**! Join our growing [community](http://community.nodebb.org) to keep up to date with the latest NodeBB development. - -## Requirements - -NodeBB requires the following software to be installed: - -* A version of Node.js at least 0.10 or greater -* Redis, version 2.8.9 or greater **or** MongoDB, version 2.6 or greater -* nginx, version 1.3.13 or greater (**only if** intending to use nginx to proxy requests to a NodeBB) - -## Installation - -[Please refer to platform-specific installation documentation](http://docs.nodebb.org/en/latest/installing/os.html) - -## Securing NodeBB - -It is important to ensure that your NodeBB and database servers are secured. Bear these points in mind: - -1. While some distributions set up Redis with a more restrictive configuration, Redis by default listens to all interfaces, which is especially dangerous when a server is open to the public. Some suggestions: - * Set `bind_address` to `127.0.0.1` so as to restrict access to the local machine only - * Use `requirepass` to secure Redis behind a password (preferably a long one) - * Familiarise yourself with [Redis Security](http://redis.io/topics/security) -2. Use `iptables` to secure your server from unintended open ports. In Ubuntu, `ufw` provides a friendlier interface to working with `iptables`. - * e.g. If your NodeBB is proxied, no ports should be open except 80 (and possibly 22, for SSH access) - -## Upgrading NodeBB - -Detailed upgrade instructions are listed in [Upgrading NodeBB](https://docs.nodebb.org/en/latest/upgrading/index.html) - -## License - -NodeBB is licensed under the **GNU General Public License v3 (GPL-3)** (http://www.gnu.org/copyleft/gpl.html) +# NodeBB + +[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/NodeBB/NodeBB?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![Build Status](https://travis-ci.org/NodeBB/NodeBB.svg?branch=master)](https://travis-ci.org/NodeBB/NodeBB) +[![Dependency Status](https://david-dm.org/nodebb/nodebb.svg)](https://david-dm.org/nodebb/nodebb) +[![Code Climate](https://codeclimate.com/github/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. + +Additional functionality is enabled through the use of third-party plugins. + +* [Get NodeBB](http://www.nodebb.org/ "NodeBB") +* [Demo & Meta Discussion](http://community.nodebb.org) +* [Documentation & Installation Instructions](http://docs.nodebb.org) +* [Help translate NodeBB](https://www.transifex.com/projects/p/nodebb/) +* [NodeBB Blog](http://blog.nodebb.org) +* [Join us on IRC](https://kiwiirc.com/client/irc.freenode.net/nodebb) - #nodebb on Freenode +* [Follow us on Twitter](http://www.twitter.com/NodeBB/ "NodeBB Twitter") +* [Like us on Facebook](http://www.facebook.com/NodeBB/ "NodeBB Facebook") + +## Screenshots + +[![](http://i.imgur.com/VCoOFyqb.png)](http://i.imgur.com/VCoOFyq.png) +[![](http://i.imgur.com/FLOUuIqb.png)](http://i.imgur.com/FLOUuIq.png) +[![](http://i.imgur.com/Ud1LrfIb.png)](http://i.imgur.com/Ud1LrfI.png) +[![](http://i.imgur.com/h6yZ66sb.png)](http://i.imgur.com/h6yZ66s.png) +[![](http://i.imgur.com/o90kVPib.png)](http://i.imgur.com/o90kVPi.png) +[![](http://i.imgur.com/AaRRrU2b.png)](http://i.imgur.com/AaRRrU2.png) +[![](http://i.imgur.com/LmHtPhob.png)](http://i.imgur.com/LmHtPho.png) +[![](http://i.imgur.com/paiJPJkb.jpg)](http://i.imgur.com/paiJPJk.jpg) + +[![](http://i.imgur.com/8OLssij.png)](http://i.imgur.com/8OLssij.png) +[![](http://i.imgur.com/JKOc0LZ.png)](http://i.imgur.com/JKOc0LZ.png) + +## How can I follow along/contribute? + +* Our feature roadmap is hosted on the project wiki's [Version History / Roadmap](https://github.com/NodeBB/NodeBB/wiki/Version-History-%26-Roadmap) +* If you are a developer, feel free to check out the source and submit pull requests. We also have a wide array of [plugins](http://community.nodebb.org/category/7/nodebb-plugins) which would be a great starting point for learning the codebase. +* If you are a designer, [NodeBB needs themes](http://community.nodebb.org/category/10/nodebb-themes)! NodeBB's theming system allows extention of the base templates as well as styling via LESS or CSS. NodeBB's base theme utilizes [Bootstrap 3](http://getbootstrap.com/) but themes can choose to use a different framework altogether. +* If you know languages other than English you can help us translate NodeBB. We use [Transifex](https://www.transifex.com/projects/p/nodebb/) for internationalization. +* Please don't forget to **like**, **follow**, and **star our repo**! Join our growing [community](http://community.nodebb.org) to keep up to date with the latest NodeBB development. + +## Requirements + +NodeBB requires the following software to be installed: + +* A version of Node.js at least 0.10 or greater +* Redis, version 2.8.9 or greater **or** MongoDB, version 2.6 or greater +* nginx, version 1.3.13 or greater (**only if** intending to use nginx to proxy requests to a NodeBB) + +## Installation + +[Please refer to platform-specific installation documentation](http://docs.nodebb.org/en/latest/installing/os.html) + +## Securing NodeBB + +It is important to ensure that your NodeBB and database servers are secured. Bear these points in mind: + +1. While some distributions set up Redis with a more restrictive configuration, Redis by default listens to all interfaces, which is especially dangerous when a server is open to the public. Some suggestions: + * Set `bind_address` to `127.0.0.1` so as to restrict access to the local machine only + * Use `requirepass` to secure Redis behind a password (preferably a long one) + * Familiarise yourself with [Redis Security](http://redis.io/topics/security) +2. Use `iptables` to secure your server from unintended open ports. In Ubuntu, `ufw` provides a friendlier interface to working with `iptables`. + * e.g. If your NodeBB is proxied, no ports should be open except 80 (and possibly 22, for SSH access) + +## Upgrading NodeBB + +Detailed upgrade instructions are listed in [Upgrading NodeBB](https://docs.nodebb.org/en/latest/upgrading/index.html) + +## License + +NodeBB is licensed under the **GNU General Public License v3 (GPL-3)** (http://www.gnu.org/copyleft/gpl.html). + +Interested in a sublicense agreement for use of NodeBB in a non-free/restrictive environment? Contact us at sales@nodebb.org. \ No newline at end of file diff --git a/app.js b/app.js index 7c9ac8f4d8..0f5de4bfe9 100644 --- a/app.js +++ b/app.js @@ -1,396 +1,416 @@ -/* - NodeBB - A better forum platform for the modern web - https://github.com/NodeBB/NodeBB/ - Copyright (C) 2013-2014 NodeBB Inc. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -"use strict"; -/*global require, global, process*/ - -var nconf = require('nconf'); -nconf.argv().env('__'); - -var fs = require('fs'), - os = require('os'), - url = require('url'), - async = require('async'), - semver = require('semver'), - winston = require('winston'), - path = require('path'), - pkg = require('./package.json'), - utils = require('./public/src/utils.js'); - - -global.env = process.env.NODE_ENV || 'production'; - -winston.remove(winston.transports.Console); -winston.add(winston.transports.Console, { - colorize: true, - timestamp: function() { - var date = new Date(); - return date.getDate() + '/' + (date.getMonth() + 1) + ' ' + date.toTimeString().substr(0,5) + ' [' + global.process.pid + ']'; - }, - level: (global.env === 'production' || nconf.get('log-level') === 'info') ? 'info' : 'verbose' -}); - -if(os.platform() === 'linux') { - require('child_process').exec('/usr/bin/which convert', function(err, stdout, stderr) { - if(err || !stdout) { - winston.warn('Couldn\'t find convert. Did you install imagemagick?'); - } - }); -} - -// Alternate configuration file support -var configFile = path.join(__dirname, '/config.json'), - configExists; - -if (nconf.get('config')) { - configFile = path.resolve(__dirname, nconf.get('config')); -} -configExists = fs.existsSync(configFile); - -if (!nconf.get('setup') && !nconf.get('install') && !nconf.get('upgrade') && !nconf.get('reset') && configExists) { - start(); -} else if (nconf.get('setup') || nconf.get('install')) { - setup(); -} else if (!configExists) { - require('./install/web').install(nconf.get('port')); -} else if (nconf.get('upgrade')) { - upgrade(); -} else if (nconf.get('reset')) { - reset(); -} - -function loadConfig() { - nconf.file({ - file: configFile - }); - - nconf.defaults({ - base_dir: __dirname, - themes_path: path.join(__dirname, 'node_modules'), - views_dir: path.join(__dirname, 'public/templates'), - version: pkg.version - }); - - if (!nconf.get('isCluster')) { - nconf.set('isPrimary', 'true'); - nconf.set('isCluster', 'false'); - } - - // Ensure themes_path is a full filepath - nconf.set('themes_path', path.resolve(__dirname, nconf.get('themes_path'))); - nconf.set('core_templates_path', path.join(__dirname, 'src/views')); - nconf.set('base_templates_path', path.join(nconf.get('themes_path'), 'nodebb-theme-vanilla/templates')); - - if (!process.send) { - // If run using `node app`, log GNU copyright info along with server info - winston.info('NodeBB v' + nconf.get('version') + ' Copyright (C) 2013-2014 NodeBB Inc.'); - winston.info('This program comes with ABSOLUTELY NO WARRANTY.'); - winston.info('This is free software, and you are welcome to redistribute it under certain conditions.'); - winston.info(''); - } -} - -function start() { - loadConfig(); - var db = require('./src/database'); - - // nconf defaults, if not set in config - if (!nconf.get('upload_path')) { - nconf.set('upload_path', '/public/uploads'); - } - // Parse out the relative_url and other goodies from the configured URL - var urlObject = url.parse(nconf.get('url')); - var relativePath = urlObject.pathname !== '/' ? urlObject.pathname : ''; - nconf.set('use_port', !!urlObject.port); - nconf.set('relative_path', relativePath); - nconf.set('port', urlObject.port || nconf.get('port') || nconf.get('PORT') || 4567); - nconf.set('upload_url', '/uploads/'); - - if (nconf.get('isPrimary') === 'true') { - winston.info('Time: %s', (new Date()).toString()); - winston.info('Initializing NodeBB v%s', nconf.get('version')); - winston.verbose('* using configuration stored in: %s', configFile); - - var host = nconf.get(nconf.get('database') + ':host'), - storeLocation = host ? 'at ' + host + (host.indexOf('/') === -1 ? ':' + nconf.get(nconf.get('database') + ':port') : '') : ''; - - winston.verbose('* using %s store %s', nconf.get('database'), storeLocation); - winston.verbose('* using themes stored in: %s', nconf.get('themes_path')); - } - - process.on('SIGTERM', shutdown); - process.on('SIGINT', shutdown); - process.on('SIGHUP', restart); - process.on('message', function(message) { - if (typeof message !== 'object') { - return; - } - var meta = require('./src/meta'); - var emitter = require('./src/emitter'); - switch (message.action) { - case 'reload': - meta.reload(); - break; - 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; - case 'templates:compiled': - emitter.emit('templates:compiled'); - break; - } - }); - - process.on('uncaughtException', function(err) { - winston.error(err.stack); - console.log(err.stack); - - require('./src/meta').js.killMinifier(); - shutdown(1); - }); - - async.waterfall([ - async.apply(db.init), - async.apply(db.checkCompatibility), - function(next) { - require('./src/meta').configs.init(next); - }, - function(next) { - require('./src/upgrade').check(next); - }, - function(schema_ok, next) { - if (!schema_ok && nconf.get('check-schema') !== false) { - winston.warn('Your NodeBB schema is out-of-date. Please run the following command to bring your dataset up to spec:'); - winston.warn(' ./nodebb upgrade'); - process.exit(); - return; - } - var webserver = require('./src/webserver'); - require('./src/socket.io').init(webserver.server); - - if (nconf.get('isPrimary') === 'true' && !nconf.get('jobsDisabled')) { - require('./src/notifications').init(); - require('./src/user').startJobs(); - } - - webserver.listen(); - } - ], function(err) { - if (err) { - if (err.stacktrace !== false) { - winston.error(err.stack); - } else { - winston.error(err.message); - } - - process.exit(); - } - }); -} - -function setup() { - loadConfig(); - - winston.info('NodeBB Setup Triggered via Command Line'); - - var install = require('./src/install'); - - process.stdout.write('\nWelcome to NodeBB!\n'); - process.stdout.write('\nThis looks like a new installation, so you\'ll have to answer a few questions about your environment before we can proceed.\n'); - process.stdout.write('Press enter to accept the default setting (shown in brackets).\n'); - - install.setup(function (err, data) { - var separator = ' '; - if (process.stdout.columns > 10) { - for(var x=0,cols=process.stdout.columns-10;x. +*/ + +"use strict"; +/*global require, global, process*/ + +var nconf = require('nconf'); +nconf.argv().env('__'); + +var fs = require('fs'), + os = require('os'), + url = require('url'), + async = require('async'), + semver = require('semver'), + winston = require('winston'), + colors = require('colors'), + path = require('path'), + pkg = require('./package.json'), + utils = require('./public/src/utils.js'); + +global.env = process.env.NODE_ENV || 'production'; + +winston.remove(winston.transports.Console); +winston.add(winston.transports.Console, { + colorize: true, + timestamp: function() { + var date = new Date(); + return date.getDate() + '/' + (date.getMonth() + 1) + ' ' + date.toTimeString().substr(0,5) + ' [' + global.process.pid + ']'; + }, + level: nconf.get('log-level') || (global.env === 'production' ? 'info' : 'verbose') +}); + +if(os.platform() === 'linux') { + require('child_process').exec('/usr/bin/which convert', function(err, stdout, stderr) { + if(err || !stdout) { + winston.warn('Couldn\'t find convert. Did you install imagemagick?'); + } + }); +} + +// Alternate configuration file support +var configFile = path.join(__dirname, '/config.json'), + configExists; + +if (nconf.get('config')) { + configFile = path.resolve(__dirname, nconf.get('config')); +} +configExists = fs.existsSync(configFile); + +if (!nconf.get('setup') && !nconf.get('install') && !nconf.get('upgrade') && !nconf.get('reset') && configExists) { + start(); +} else if (nconf.get('setup') || nconf.get('install')) { + setup(); +} else if (!configExists) { + require('./install/web').install(nconf.get('port')); +} else if (nconf.get('upgrade')) { + upgrade(); +} else if (nconf.get('reset')) { + reset(); +} + +function loadConfig() { + nconf.file({ + file: configFile + }); + + nconf.defaults({ + base_dir: __dirname, + themes_path: path.join(__dirname, 'node_modules'), + views_dir: path.join(__dirname, 'public/templates'), + version: pkg.version + }); + + if (!nconf.get('isCluster')) { + nconf.set('isPrimary', 'true'); + nconf.set('isCluster', 'false'); + } + + // Ensure themes_path is a full filepath + nconf.set('themes_path', path.resolve(__dirname, nconf.get('themes_path'))); + nconf.set('core_templates_path', path.join(__dirname, 'src/views')); + nconf.set('base_templates_path', path.join(nconf.get('themes_path'), 'nodebb-theme-vanilla/templates')); + + if (!process.send) { + // If run using `node app`, log GNU copyright info along with server info + winston.info('NodeBB v' + nconf.get('version') + ' Copyright (C) 2013-2014 NodeBB Inc.'); + winston.info('This program comes with ABSOLUTELY NO WARRANTY.'); + winston.info('This is free software, and you are welcome to redistribute it under certain conditions.'); + winston.info(''); + } +} + +function start() { + loadConfig(); + var db = require('./src/database'); + + // nconf defaults, if not set in config + if (!nconf.get('upload_path')) { + nconf.set('upload_path', '/public/uploads'); + } + // Parse out the relative_url and other goodies from the configured URL + var urlObject = url.parse(nconf.get('url')); + var relativePath = urlObject.pathname !== '/' ? urlObject.pathname : ''; + nconf.set('base_url', urlObject.protocol + '//' + urlObject.host); + nconf.set('use_port', !!urlObject.port); + nconf.set('relative_path', relativePath); + nconf.set('port', urlObject.port || nconf.get('port') || nconf.get('PORT') || 4567); + nconf.set('upload_url', '/uploads/'); + + if (nconf.get('isPrimary') === 'true') { + winston.info('Time: %s', (new Date()).toString()); + winston.info('Initializing NodeBB v%s', nconf.get('version')); + winston.verbose('* using configuration stored in: %s', configFile); + + var host = nconf.get(nconf.get('database') + ':host'), + storeLocation = host ? 'at ' + host + (host.indexOf('/') === -1 ? ':' + nconf.get(nconf.get('database') + ':port') : '') : ''; + + winston.verbose('* using %s store %s', nconf.get('database'), storeLocation); + winston.verbose('* using themes stored in: %s', nconf.get('themes_path')); + } + + process.on('SIGTERM', shutdown); + process.on('SIGINT', shutdown); + process.on('SIGHUP', restart); + process.on('message', function(message) { + if (typeof message !== 'object') { + return; + } + var meta = require('./src/meta'); + var emitter = require('./src/emitter'); + switch (message.action) { + case 'reload': + meta.reload(); + break; + 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; + case 'templates:compiled': + emitter.emit('templates:compiled'); + break; + } + }); + + process.on('uncaughtException', function(err) { + winston.error(err.stack); + console.log(err.stack); + + require('./src/meta').js.killMinifier(); + shutdown(1); + }); + + async.waterfall([ + async.apply(db.init), + async.apply(db.checkCompatibility), + function(next) { + require('./src/meta').configs.init(next); + }, + function(next) { + require('./src/meta').dependencies.check(next); + }, + function(next) { + require('./src/upgrade').check(next); + }, + function(next) { + var webserver = require('./src/webserver'); + require('./src/socket.io').init(webserver.server); + + if (nconf.get('isPrimary') === 'true' && !nconf.get('jobsDisabled')) { + require('./src/notifications').init(); + require('./src/user').startJobs(); + } + + webserver.listen(); + } + ], function(err) { + if (err) { + switch(err.message) { + case 'schema-out-of-date': + winston.warn('Your NodeBB schema is out-of-date. Please run the following command to bring your dataset up to spec:'); + winston.warn(' ./nodebb upgrade'); + break; + case 'dependencies-out-of-date': + winston.warn('One or more of NodeBB\'s dependent packages are out-of-date. Please run the following command to update them:'); + winston.warn(' ./nodebb upgrade'); + break; + default: + if (err.stacktrace !== false) { + winston.error(err.stack); + } else { + winston.error(err.message); + } + break; + } + + // Either way, bad stuff happened. Abort start. + process.exit(); + } + }); +} + +function setup() { + loadConfig(); + + winston.info('NodeBB Setup Triggered via Command Line'); + + var install = require('./src/install'); + + process.stdout.write('\nWelcome to NodeBB!\n'); + process.stdout.write('\nThis looks like a new installation, so you\'ll have to answer a few questions about your environment before we can proceed.\n'); + process.stdout.write('Press enter to accept the default setting (shown in brackets).\n'); + + install.setup(function (err, data) { + var separator = ' '; + if (process.stdout.columns > 10) { + for(var x=0,cols=process.stdout.columns-10;x /dev/null - then return 1; - else - rm ./pidfile; - return 0; - fi - else - return 0; - fi + try { + process.kill(parseInt(pid, 10), 0); + callback(null, parseInt(pid, 10)); + } catch(e) { + callback(e); + } + }); + }; + +switch(process.argv[2]) { + case 'status': + getRunningPid(function(err, pid) { + if (!err) { + process.stdout.write('\nNodeBB Running '.bold + '(pid '.cyan + pid.toString().cyan + ')\n'.cyan); + process.stdout.write('\t"' + './nodebb stop'.yellow + '" to stop the NodeBB server\n'); + process.stdout.write('\t"' + './nodebb log'.yellow + '" to view server output\n'); + process.stdout.write('\t"' + './nodebb restart'.yellow + '" to restart NodeBB\n\n'); + } else { + process.stdout.write('\nNodeBB is not running\n'.bold); + process.stdout.write('\t"' + './nodebb start'.yellow + '" to launch the NodeBB server\n\n'.reset); + } + }) + break; + + case 'start': + process.stdout.write('\nStarting NodeBB\n'.bold); + process.stdout.write(' "' + './nodebb stop'.yellow + '" to stop the NodeBB server\n'); + process.stdout.write(' "' + './nodebb log'.yellow + '" to view server output\n'); + process.stdout.write(' "' + './nodebb restart'.yellow + '" to restart NodeBB\n\n'.reset); + + // Spawn a new NodeBB process + cproc.fork(__dirname + '/loader.js', { + env: process.env + }); + break; + + case 'stop': + getRunningPid(function(err, pid) { + if (!err) { + process.kill(pid, 'SIGTERM'); + process.stdout.write('Stopping NodeBB. Goodbye!\n') + } else { + process.stdout.write('NodeBB is already stopped.\n'); + } + }); + break; + + case 'restart': + getRunningPid(function(err, pid) { + if (!err) { + process.kill(pid, 'SIGHUP'); + } else { + process.stdout.write('NodeBB could not be restarted, as a running instance could not be found.'); + } + }); + break; + + case 'reload': + getRunningPid(function(err, pid) { + if (!err) { + process.kill(pid, 'SIGUSR2'); + } else { + process.stdout.write('NodeBB could not be reloaded, as a running instance could not be found.'); + } + }); + break; + + case 'dev': + process.env.NODE_ENV = 'development'; + cproc.fork(__dirname + '/loader.js', ['--no-daemon', '--no-silent'], { + env: process.env + }); + break; + + case 'log': + process.stdout.write('\nType '.red + 'Ctrl-C '.bold + 'to exit'.red); + process.stdout.write('\n\n'.reset); + cproc.spawn('tail', ['-F', './logs/output.log'], { + cwd: __dirname, + stdio: 'inherit' + }); + break; + + case 'setup': + cproc.fork('app.js', ['--setup'], { + cwd: __dirname, + silent: false + }); + break; + + case 'reset': + var args = process.argv.slice(0); + args.unshift('--reset'); + + cproc.fork('app.js', args, { + cwd: __dirname, + silent: false + }); + break; + + case 'upgrade': + async.series([ + function(next) { + process.stdout.write('1. '.bold + 'Bringing base dependencies up to date\n'.yellow); + npm.load({ + loglevel: 'silent' + }, function() { + npm.commands.install(next); + }); + }, + function(next) { + process.stdout.write('2. '.bold + 'Updating NodeBB data store schema\n'.yellow); + var upgradeProc = cproc.fork('app.js', ['--upgrade'], { + cwd: __dirname, + silent: false + }); + + upgradeProc.on('close', next) + }, + function(next) { + process.stdout.write('3. '.bold + 'Storing upgrade date in "package.json"\n'.yellow); + touch(__dirname + '/package.json', {}, next); + } + ], function(err) { + if (err) { + process.stdout.write('\nError'.red + ': ' + err.message + '\n'); + } else { + var message = 'NodeBB Upgrade Complete!', + spaces = new Array(Math.floor(process.stdout.columns / 2) - (message.length / 2) + 1).join(' '); + process.stdout.write('\n' + spaces + message.green.bold + '\n\n'.reset); + } + }); + break; + + default: + process.stdout.write('\nWelcome to NodeBB\n\n'.bold); + process.stdout.write('Usage: ./nodebb {start|stop|reload|restart|log|setup|reset|upgrade|dev}\n\n'); + process.stdout.write('\t' + 'start'.yellow + '\tStart the NodeBB server\n'); + process.stdout.write('\t' + 'stop'.yellow + '\tStops the NodeBB server\n'); + process.stdout.write('\t' + 'reload'.yellow + '\tRestarts NodeBB\n'); + process.stdout.write('\t' + 'restart'.yellow + '\tRestarts NodeBB\n'); + process.stdout.write('\t' + 'log'.yellow + '\tOpens the logging interface (useful for debugging)\n'); + process.stdout.write('\t' + 'setup'.yellow + '\tRuns the NodeBB setup script\n'); + process.stdout.write('\t' + 'reset'.yellow + '\tDisables all plugins, restores the default theme.\n'); + process.stdout.write('\t' + 'upgrade'.yellow + '\tRun NodeBB upgrade scripts, ensure packages are up-to-date\n'); + process.stdout.write('\t' + 'dev'.yellow + '\tStart NodeBB in interactive development mode\n'); + process.stdout.write('\t' + 'watch'.yellow + '\tStart NodeBB in development mode and watch for changes\n'); + process.stdout.write('\n'.reset); + break; } - -case "$1" in - start) - echo "Starting NodeBB"; - echo " \"./nodebb stop\" to stop the NodeBB server"; - echo " \"./nodebb log\" to view server output"; - - # Start the loader daemon - "$node" loader "$@" - ;; - - stop) - pidExists; - if [ 0 -eq $? ]; - then - echo "NodeBB is already stopped."; - else - echo "Stopping NodeBB. Goodbye!"; - kill $(cat pidfile); - fi - ;; - - restart) - pidExists; - if [ 0 -eq $? ]; - then - echo "NodeBB could not be restarted, as a running instance could not be found."; - else - echo "Restarting NodeBB."; - kill -1 $(cat pidfile); - fi - ;; - - reload) - pidExists; - if [ 0 -eq $? ]; - then - echo "NodeBB could not be reloaded, as a running instance could not be found."; - else - echo "Reloading NodeBB."; - kill -12 $(cat pidfile); - fi - ;; - - status) - pidExists; - if [ 0 -eq $? ]; - then - echo "NodeBB is not running"; - echo " \"./nodebb start\" to launch the NodeBB server"; - else - echo "NodeBB Running (pid $(cat pidfile))"; - echo " \"./nodebb stop\" to stop the NodeBB server"; - echo " \"./nodebb log\" to view server output"; - echo " \"./nodebb restart\" to restart NodeBB"; - fi - ;; - - log) - clear; - tail -F ./logs/output.log; - ;; - - upgrade) - npm install - # ls -d node_modules/nodebb* | xargs -n1 basename | xargs npm install - # ls -d node_modules/nodebb* | xargs -n1 basename | xargs npm update - npm i nodebb-theme-vanilla nodebb-theme-lavender nodebb-widget-essentials - "$node" app --upgrade - touch package.json - ;; - - setup) - "$node" app --setup "$@" - ;; - - reset) - "$node" app --reset --$2 - ;; - - dev) - echo "Launching NodeBB in \"development\" mode." - echo "To run the production build of NodeBB, please use \"forever\"." - echo "More Information: https://docs.nodebb.org/en/latest/running/index.html" - NODE_ENV=development "$node" loader --no-daemon --no-silent "$@" - ;; - - watch) - echo "***************************************************************************" - echo "WARNING: ./nodebb watch will be deprecated soon. Please use grunt: " - echo "https://docs.nodebb.org/en/latest/running/index.html#grunt-development" - echo "***************************************************************************" - NODE_ENV=development supervisor -q --ignore public/templates,public/nodebb.min.js,public/nodebb.min.js.map --extensions 'node|js|tpl|less' -- app "$@" - ;; - - *) - echo "Welcome to NodeBB" - echo $"Usage: $0 {start|stop|reload|restart|log|setup|reset|upgrade|dev|watch}" - echo '' - column -s ' ' -t <<< ' - start Start the NodeBB server - stop Stops the NodeBB server - reload Restarts NodeBB - restart Restarts NodeBB - log Opens the logging interface (useful for debugging) - setup Runs the NodeBB setup script - reset Disables all plugins, restores the default theme. - upgrade Run NodeBB upgrade scripts, ensure packages are up-to-date - dev Start NodeBB in interactive development mode - watch Start NodeBB in development mode and watch for changes - ' - exit 1 -esac diff --git a/package.json b/package.json index 5d2ff794e7..a1be6ec4f3 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "name": "nodebb", - "license": "GPLv3 or later", + "license": "GPL-3.0", "description": "NodeBB Forum", - "version": "0.7.0", + "version": "0.7.1-dev", "homepage": "http://www.nodebb.org", "repository": { "type": "git", @@ -17,6 +17,7 @@ "async": "~0.9.0", "bcryptjs": "~2.1.0", "body-parser": "^1.9.0", + "colors": "^1.1.0", "compression": "^1.1.0", "connect-ensure-login": "^0.1.1", "connect-flash": "^0.1.1", @@ -27,28 +28,30 @@ "daemon": "~1.1.0", "express": "^4.9.5", "express-session": "^1.8.2", - "gm": "1.17.0", "gravatar": "^1.1.0", "heapdump": "^0.3.0", "less": "^2.0.0", "logrotate-stream": "^0.2.3", "lru-cache": "^2.6.1", + "lwip": "0.0.7", "mime": "^1.3.4", + "minimist": "^1.1.1", "mkdirp": "~0.5.0", "mmmagic": "^0.3.13", "morgan": "^1.3.2", "nconf": "~0.7.1", + "nodebb-plugin-composer-default": "^1.0.7", "nodebb-plugin-dbsearch": "^0.2.12", "nodebb-plugin-emoji-extended": "^0.4.8", - "nodebb-plugin-markdown": "^2.1.7", - "nodebb-plugin-mentions": "^0.11.4", + "nodebb-plugin-markdown": "^4.0.0", + "nodebb-plugin-mentions": "^0.11.7", "nodebb-plugin-soundpack-default": "^0.1.1", "nodebb-plugin-spam-be-gone": "^0.4.0", - "nodebb-theme-lavender": "^1.0.42", - "nodebb-theme-vanilla": "^1.0.130", - "nodebb-theme-persona": "^0.1.55", - "nodebb-widget-essentials": "^1.0.2", "nodebb-rewards-essentials": "^0.0.1", + "nodebb-theme-lavender": "^1.0.47", + "nodebb-theme-persona": "^1.0.15", + "nodebb-theme-vanilla": "^2.0.13", + "nodebb-widget-essentials": "^1.0.2", "npm": "^2.1.4", "passport": "^0.2.1", "passport-local": "1.0.0", @@ -64,9 +67,11 @@ "socket.io-redis": "^0.1.3", "socketio-wildcard": "~0.1.1", "string": "^3.0.0", - "templates.js": "^0.2.3", - "uglify-js": "git+https://github.com/julianlam/UglifyJS2.git", + "templates.js": "^0.2.6", + "touch": "0.0.3", + "uglify-js": "^2.4.23", "underscore": "~1.8.3", + "underscore.deep": "^0.5.1", "validator": "^3.30.0", "winston": "^0.9.0", "xregexp": "~2.0.0" @@ -85,17 +90,17 @@ "maintainers": [ { "name": "Andrew Rodrigues", - "email": "andrew@designcreateplay.com", + "email": "andrew@nodebb.org", "url": "https://github.com/psychobunny" }, { "name": "Julian Lam", - "email": "julian@designcreateplay.com", + "email": "julian@nodebb.org", "url": "https://github.com/julianlam" }, { "name": "Barış Soner Uşaklı", - "email": "baris@designcreateplay.com", + "email": "baris@nodebb.org", "url": "https://github.com/barisusakli" } ] diff --git a/public/language/ar/category.json b/public/language/ar/category.json index 6a975425c0..430a82390d 100644 --- a/public/language/ar/category.json +++ b/public/language/ar/category.json @@ -1,12 +1,12 @@ { "new_topic_button": "موضوع جديد", - "guest-login-post": "المرجو تسجيل الدخول أوَّلا", + "guest-login-post": "يجب عليك تسجيل الدخول للرد", "no_topics": "لا توجد مواضيع في هذه الفئةلم لا تحاول إنشاء موضوع؟
", "browsing": "تصفح", - "no_replies": "لم يرد أحد", + "no_replies": "لا توجد ردود.", "share_this_category": "انشر هذه الفئة", - "watch": "Watch", + "watch": "متابعة", "ignore": "تجاهل", - "watch.message": "You are now watching updates from this category", - "ignore.message": "You are now ignoring updates from this category" + "watch.message": "أنت اﻷن متابع لتحديثات هذه الفئة", + "ignore.message": "أنت اﻷن تتجاهل تحديثات هذه الفئة" } \ No newline at end of file diff --git a/public/language/ar/email.json b/public/language/ar/email.json index d295a3a70f..9126d389fa 100644 --- a/public/language/ar/email.json +++ b/public/language/ar/email.json @@ -1,13 +1,17 @@ { - "password-reset-requested": "تم طلب إعادة تعيين كلمة السر - %1!", + "password-reset-requested": "تم طلب إعادة تعيين كلمة المرور - %1!", "welcome-to": "مرحبًا بك في %1", + "invite": "Invitation from %1", "greeting_no_name": "مرحبًا", "greeting_with_name": "مرحبًا بك يا %1", "welcome.text1": "شكرًا على تسجيلك في %1!", "welcome.text2": "لتفعيل حسابك، نحتاج إلى التأكد من صحة عنوان البريد الإلكتروني الذي تسجلت به.", + "welcome.text3": "An administrator has accepted your registration application. You can login with your username/password now.", "welcome.cta": "انقر هنا لتفعيل عنوان بريدك الإلكتروني", - "reset.text1": "لقد توصلنا بطلب إعادة تعيين كلمة السرالخاصة بك، ربما لكونك قد نسيتها, إن لم يكن الأمر كذلك، المرجو تجاهل هذه الرسالة.", - "reset.text2": "لمواصلة طلب إعاة تعيين كلمة السر، المرجو تتبع هذا الرابط.", + "invitation.text1": "%1 has invited you to join %2", + "invitation.ctr": "Click here to create your account.", + "reset.text1": "لقد توصلنا بطلب إعادة تعيين كلمة المرور الخاصة بك، ربما لكونك قد نسيتها, إن لم يكن الأمر كذلك، المرجو تجاهل هذه الرسالة.", + "reset.text2": "لمواصلة طلب إعاة تعيين كلمة المرور، الرجاء تتبع هذا الرابط.", "reset.cta": "انقر هنا لإعادة تعيين كلمة السر الخاصة بك.", "reset.notify.subject": "تم تغيير كلمة المرور بنجاح", "reset.notify.text1": "نحيطك علما أن كلمة مرورك قد تم تغييرها في %1", diff --git a/public/language/ar/error.json b/public/language/ar/error.json index 4c10a8cf38..9038d1d735 100644 --- a/public/language/ar/error.json +++ b/public/language/ar/error.json @@ -1,8 +1,8 @@ { "invalid-data": "بيانات غير صالحة", "not-logged-in": "لم تقم بتسجيل الدخول", - "account-locked": "تم إقفال حسابكم مؤقتًا.", - "search-requires-login": "Searching requires an account - please login or register.", + "account-locked": "تم حظر حسابك مؤقتًا.", + "search-requires-login": "البحث في المنتدى يتطلب حساب - الرجاء تسجيل الدخول أو التسجيل", "invalid-cid": "قائمة غير موجودة", "invalid-tid": "موضوع غير متواجد", "invalid-pid": "رد غير موجود", @@ -18,10 +18,10 @@ "username-taken": "اسم المستخدم مأخوذ", "email-taken": "البريد الالكتروني مأخوذ", "email-not-confirmed": "عنوان بريدك الإلكتروني غير مفعل بعد. انقر هنا لتفعيله من فضلك.", - "email-not-confirmed-chat": "You are unable to chat until your email is confirmed, please click here to confirm your email.", + "email-not-confirmed-chat": "لا يمكنك الدردشة حتى تقوم بتأكيد بريدك الإلكتروني، الرجاء إضغط هنا لتأكيد بريدك اﻹلكتروني.", "no-email-to-confirm": "هذا المنتدى يستلزم تفعيل بريدك الإلكتروني، انقر هنا من فضلك لإدخاله.", "email-confirm-failed": "لم نستطع تفعيل بريدك الإلكتروني، المرجو المحاولة لاحقًا.", - "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", + "confirm-email-already-sent": "لقد تم ارسال بريد التأكيد، الرجاء اﻹنتظار 1% دقائق لإعادة اﻹرسال", "username-too-short": "اسم المستخدم قصير.", "username-too-long": "اسم المستخدم طويل", "user-banned": "المستخدم محظور", @@ -51,14 +51,17 @@ "already-favourited": "لقد سبق وأضفت هذا الرد إلى المفضلة", "already-unfavourited": "لقد سبق وحذفت هذا الرد من المفضلة", "cant-ban-other-admins": "لايمكن حظر مدبر نظام آخر.", + "cant-remove-last-admin": "You are the only administrator. Add another user as an administrator before removing yourself as admin", "invalid-image-type": "Invalid image type. Allowed types are: %1", "invalid-image-extension": "Invalid image extension", "invalid-file-type": "Invalid file type. Allowed types are: %1", "group-name-too-short": "اسم المجموعة قصير", "group-already-exists": "المجموعة موجودة مسبقا", "group-name-change-not-allowed": "لايسمح بتغيير أسماء المجموعات", - "group-already-member": "You are already part of this group", - "group-needs-owner": "This group requires at least one owner", + "group-already-member": "أنت بالفعل عضو في هذه المجموعة ", + "group-needs-owner": "هذه المجموعة تتطلب مالك واحد على اﻷقل", + "group-already-invited": "This user has already been invited", + "group-already-requested": "Your membership request has already been submitted", "post-already-deleted": "سبق وتم حذف هذا الرد", "post-already-restored": "سبق وتم إلغاء حذف هذا الرد", "topic-already-deleted": "سبق وتم حذف هذا الموضوع", @@ -67,18 +70,19 @@ "topic-thumbnails-are-disabled": "الصور المصغرة غير مفعلة.", "invalid-file": "ملف غير مقبول", "uploads-are-disabled": "رفع الملفات غير مفعل", - "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", + "signature-too-long": "عذرا، توقيعك يجب ألا يتجاوز %1 حرفًا.", "about-me-too-long": "Sorry, your about me cannot be longer than %1 character(s).", "cant-chat-with-yourself": "لايمكنك فتح محادثة مع نفسك", "chat-restricted": "هذا المستخدم عطل المحادثات الواردة عليه. يجب أن يتبعك حتى تتمكن من فتح محادثة معه.", - "too-many-messages": "You have sent too many messages, please wait awhile.", + "too-many-messages": "لقد أرسلت الكثير من الرسائل، الرجاء اﻹنتظار قليلاً", "reputation-system-disabled": "نظام السمعة معطل", "downvoting-disabled": "التصويتات السلبية معطلة", "not-enough-reputation-to-downvote": "ليس لديك سمعة تكفي لإضافة صوت سلبي لهذا الموضوع", "not-enough-reputation-to-flag": "ليس لديك سمعة تكفي للإشعار بموضوع مخل", + "already-flagged": "You have already flagged this post", "reload-failed": "المنتدى واجه مشكلة أثناء إعادة التحميل: \"%1\". سيواصل المنتدى خدمة العملاء السابقين لكن يجب عليك إلغاء أي تغيير قمت به قبل إعادة التحميل.", "registration-error": "حدث خطأ أثناء التسجيل", - "parse-error": "Something went wrong while parsing server response", + "parse-error": "حدث خطأ ما أثناء تحليل استجابة الخادم", "wrong-login-type-email": "الرجاء استعمال بريدك اﻹلكتروني للدخول", "wrong-login-type-username": "الرجاء استعمال اسم المستخدم الخاص بك للدخول" } \ No newline at end of file diff --git a/public/language/ar/global.json b/public/language/ar/global.json index 15275ddc39..3f6004b948 100644 --- a/public/language/ar/global.json +++ b/public/language/ar/global.json @@ -3,7 +3,7 @@ "search": "بحث", "buttons.close": "أغلق", "403.title": "غير مسموح بالدخول", - "403.message": "You seem to have stumbled upon a page that you do not have access to.", + "403.message": "يبدو أنك قد تعثرت على صفحة لا تمتلك الصلاحية للدخول إليها", "403.login": "Perhaps you should try logging in?", "404.title": "لم يتم العثور", "404.message": "You seem to have stumbled upon a page that does not exist. Return to the home page.", @@ -50,6 +50,7 @@ "views": "المشاهدات", "reputation": "السمعة", "read_more": "اقرأ المزيد", + "more": "More", "posted_ago_by_guest": "كتب %1 من طرف زائر", "posted_ago_by": "كتب %1 من طرف %2", "posted_ago": "كتب %1", diff --git a/public/language/ar/groups.json b/public/language/ar/groups.json index c67f95ef8b..04e4f9b39f 100644 --- a/public/language/ar/groups.json +++ b/public/language/ar/groups.json @@ -6,6 +6,12 @@ "no_groups_found": "لاوجدود لمجموعات يمكن معاينتها", "pending.accept": "موافق", "pending.reject": "رفض", + "pending.accept_all": "Accept All", + "pending.reject_all": "Reject All", + "pending.none": "There are no pending members at this time", + "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", "cover-instructions": "اسحب وأسقِط صورة، اسحبها للموضع المرغوب، وانقر على حفظ", "cover-change": "تغيير", "cover-save": "حفظ", @@ -13,6 +19,7 @@ "details.title": "تفاصيل المجموعة", "details.members": "لائحة الأعضاء", "details.pending": "المستخدمون في الانتظار", + "details.invited": "Invited Members", "details.has_no_posts": "أعضاء هذه المجموعة لم يضيفوا أية مشاركة", "details.latest_posts": "آخر المشاركات", "details.private": "خاص", diff --git a/public/language/ar/login.json b/public/language/ar/login.json index 878a32533c..29d256203f 100644 --- a/public/language/ar/login.json +++ b/public/language/ar/login.json @@ -7,5 +7,5 @@ "alternative_logins": "تسجيلات الدخول البديلة", "failed_login_attempt": "فشلت محاولة تسجيل الدخول، يرجى المحاولة مرة أخرى.", "login_successful": "قمت بتسجيل الدخول بنجاح!", - "dont_have_account": "لم تفتح حسابك بعد؟" + "dont_have_account": "لا تملك حساب؟" } \ No newline at end of file diff --git a/public/language/ar/modules.json b/public/language/ar/modules.json index 061fc4430c..56ff907b18 100644 --- a/public/language/ar/modules.json +++ b/public/language/ar/modules.json @@ -15,7 +15,7 @@ "chat.seven_days": "7 أيام", "chat.thirty_days": "30 يومًا", "chat.three_months": "3 أشهر", - "composer.compose": "Compose", + "composer.compose": "اكتب", "composer.show_preview": "عرض المعاينة", "composer.hide_preview": "إخفاء المعاينة", "composer.user_said_in": "%1 كتب في %2", diff --git a/public/language/ar/notifications.json b/public/language/ar/notifications.json index 2e87be20cd..5a26c90719 100644 --- a/public/language/ar/notifications.json +++ b/public/language/ar/notifications.json @@ -20,6 +20,7 @@ "user_posted_topic": "%1 أنشأ موضوعًا جديدًا: %2", "user_mentioned_you_in": "%1 ذكرَ اسمك في %2", "user_started_following_you": "%1 صار يتابعك.", + "new_register": "%1 sent a registration request.", "email-confirmed": "تم التحقق من عنوان البريد الإلكتروني", "email-confirmed-message": "شكرًا على إثبات صحة عنوان بريدك الإلكتروني. صار حسابك مفعلًا بالكامل.", "email-confirm-error-message": "حدث خطأ أثناء التحقق من عنوان بريدك الإلكتروني. ربما رمز التفعيل خاطئ أو انتهت صلاحيته.", diff --git a/public/language/ar/pages.json b/public/language/ar/pages.json index a767ba893f..cf230fc28a 100644 --- a/public/language/ar/pages.json +++ b/public/language/ar/pages.json @@ -5,17 +5,17 @@ "recent": "المواضيع الحديثة", "users": "اﻷعضاء المسجلون", "notifications": "التنبيهات", - "tags": "Tags", + "tags": "الكلمات الدلالية", "tag": "Topics tagged under \"%1\"", "user.edit": "تعديل \"%1\"", "user.following": "المستخدمون الذين يتبعهم %1", "user.followers": "المستخدمون الذين يتبعون %1", "user.posts": "ردود %1", "user.topics": "مواضيع %1", - "user.groups": "%1's Groups", + "user.groups": "مجموعات %1", "user.favourites": "مفضلات %1", "user.settings": "خيارات المستخدم", - "user.watched": "Topics watched by %1", + "user.watched": "المواضيع المتابعة من قبل %1 ", "maintenance.text": "جاري صيانة %1. المرجو العودة لاحقًا.", "maintenance.messageIntro": "بالإضافة إلى ذلك، قام مدبر النظام بترك هذه الرسالة:" } \ No newline at end of file diff --git a/public/language/ar/register.json b/public/language/ar/register.json index d456c82572..9960bcace0 100644 --- a/public/language/ar/register.json +++ b/public/language/ar/register.json @@ -1,18 +1,19 @@ { "register": "تسجيل", - "help.email": "افتراضيا، سيتم إخفاء بريدك الإلكتروني من الجمهور.", + "help.email": "افتراضيا، سيتم إخفاء بريدك الإلكتروني من العامة.", "help.username_restrictions": "اسم مستخدم فريدة من نوعها بين1% و2% حرفا. يمكن للآخرين ذكرك @ <'span id='your-username> اسم المستخدم .", - "help.minimum_password_length": "كلمتك السر يجب أن تكون على الأقل متألفة من 1% أحرف", + "help.minimum_password_length": "كلمة المرور يجب أن تكون على الأقل بها 1% أحرف", "email_address": "عنوان البريد الإلكتروني", "email_address_placeholder": "ادخل عنوان البريد الإلكتروني", "username": "اسم المستخدم", "username_placeholder": "أدخل اسم المستخدم", - "password": "كلمة السر", - "password_placeholder": "أدخل كلمة السر", - "confirm_password": "تأكيد كلمة السر", - "confirm_password_placeholder": "تأكيد كلمة السر", + "password": "كلمة المرور", + "password_placeholder": "أدخل كلمة المرور", + "confirm_password": "تأكيد كلمة المرور", + "confirm_password_placeholder": "تأكيد كلمة المرور", "register_now_button": "قم بالتسجيل الآن", "alternative_registration": "طريقة تسجيل بديلة", "terms_of_use": "شروط الاستخدام", - "agree_to_terms_of_use": "أوافق على شروط الاستخدام" + "agree_to_terms_of_use": "أوافق على شروط الاستخدام", + "registration-added-to-queue": "Your registration has been added to the approval queue. You will receive an email when it is accepted by an administrator." } \ No newline at end of file diff --git a/public/language/ar/reset_password.json b/public/language/ar/reset_password.json index cdd2b378a0..fe35f3569c 100644 --- a/public/language/ar/reset_password.json +++ b/public/language/ar/reset_password.json @@ -1,17 +1,17 @@ { - "reset_password": "إعادة تعيين كلمة السر", - "update_password": "تحديث كلمة السر", - "password_changed.title": "تم تغير كلمة السر", - "password_changed.message": "

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

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

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

", "wrong_reset_code.title": "رمز إعادة التعيين غير صحيح", "wrong_reset_code.message": "رمز إعادة التعين غير صحيح، يرجى المحاولة مرة أخرى أو اطلب رمزا جديدا", - "new_password": "كلمة السر الجديدة", - "repeat_password": "تأكيد كلمة السر", + "new_password": "كلمة المرور الجديدة", + "repeat_password": "تأكيد كلمة المرور", "enter_email": "يرجى إدخال عنوان البريد الإلكتروني الخاص بك وسوف نرسل لك رسالة بالبريد الالكتروني مع تعليمات حول كيفية إستعادة حسابك.", "enter_email_address": "ادخل عنوان البريد الإلكتروني", "password_reset_sent": "إعادة تعيين كلمة السر أرسلت", "invalid_email": "بريد إلكتروني غير صالح أو غير موجود", - "password_too_short": "The password entered is too short, please pick a different password.", - "passwords_do_not_match": "The two passwords you've entered do not match.", - "password_expired": "Your password has expired, please choose a new password" + "password_too_short": "كلمة المرور التي أدخلتها قصيرة، الرجاء اختر كلمة مرور مختلفة", + "passwords_do_not_match": "كلمتا السر التي أدخلتهما غير متطابقتان", + "password_expired": "لقد انتهت صلاحية كلمة المرور الخاصة بك، الرجاء اختيار كلمة مرور جديدة" } \ No newline at end of file diff --git a/public/language/ar/search.json b/public/language/ar/search.json index 8951e32dc3..39a6b89927 100644 --- a/public/language/ar/search.json +++ b/public/language/ar/search.json @@ -1,16 +1,16 @@ { - "results_matching": "%1 نتيجة (نتائج) موافقة ل \"%2\", (%3 ثواني)", - "no-matches": "No matches found", + "results_matching": "%1 نتيجة (نتائج) موافقة لـ \"%2\", (%3 ثواني)", + "no-matches": "لم يتم العثور على نتائج.", "advanced-search": "بحث متقدم", "in": "في", "titles": "العناوين", "titles-posts": "العناوين والمشاركات", - "posted-by": "Posted by", + "posted-by": "مشاركة من طرف", "in-categories": "في الفئات", "search-child-categories": "بحث في الفئات الفرعية", - "reply-count": "Reply Count", - "at-least": "At least", - "at-most": "At most", + "reply-count": "عدد المشاركات", + "at-least": "على اﻷقل", + "at-most": "على اﻷكثر", "post-time": "تاريخ المشاركة", "newer-than": "أحدث من", "older-than": "أقدم من", @@ -22,7 +22,7 @@ "three-months": "ثلاثة أشهر", "six-months": "ستة أشهر", "one-year": "عام", - "sort-by": "Sort by", + "sort-by": "عرض حسب", "last-reply-time": "تاريخ آخر رد", "topic-title": "عنوان الموضوع", "number-of-replies": "عدد الردود", @@ -30,11 +30,11 @@ "topic-start-date": "تاريخ بدأ الموضوع", "username": "اسم المستخدم", "category": "فئة", - "descending": "In descending order", - "ascending": "In ascending order", + "descending": "في ترتيب تنازلي", + "ascending": "في ترتيب تصاعدي", "save-preferences": "حفظ التفضيلات", - "clear-preferences": "Clear preferences", + "clear-preferences": "ازالة التفضيلات", "search-preferences-saved": "تم حفظ تفضيلات البحث", - "search-preferences-cleared": "Search preferences cleared", + "search-preferences-cleared": "تم ازالة تفضيلات البحث", "show-results-as": "عرض النتائج كـ" } \ No newline at end of file diff --git a/public/language/ar/tags.json b/public/language/ar/tags.json index f2eccbd1c0..2798a0c8c8 100644 --- a/public/language/ar/tags.json +++ b/public/language/ar/tags.json @@ -1,7 +1,7 @@ { - "no_tag_topics": "لاوجود لمواضيع تحمل هذا الوسم.", - "tags": "بطاقات", + "no_tag_topics": "لا يوجد مواضيع بهذه الكلمة الدلالية.", + "tags": "الكلمات الدلالية", "enter_tags_here": "Enter tags here, between %1 and %2 characters each.", - "enter_tags_here_short": "أدخل البطاقات...", - "no_tags": "لاتوجد هناك بطاقات بعد." + "enter_tags_here_short": "أدخل الكلمات الدلالية...", + "no_tags": "لا يوجد كلمات دلالية بعد." } \ No newline at end of file diff --git a/public/language/ar/topic.json b/public/language/ar/topic.json index 5eb88f22d0..783a84a5fa 100644 --- a/public/language/ar/topic.json +++ b/public/language/ar/topic.json @@ -76,7 +76,7 @@ "fork_no_pids": "لم تختر أي مشاركة", "fork_success": "تم إنشاء فرع للموضوع بنجاح! إضغط هنا لمعاينة الفرع.", "composer.title_placeholder": "أدخل عنوان موضوعك هنا...", - "composer.handle_placeholder": "Name", + "composer.handle_placeholder": "اﻹسم", "composer.discard": "نبذ التغييرات", "composer.submit": "حفظ", "composer.replying_to": "الرد على %1", diff --git a/public/language/ar/unread.json b/public/language/ar/unread.json index 9abf80b1e9..365303bf35 100644 --- a/public/language/ar/unread.json +++ b/public/language/ar/unread.json @@ -5,5 +5,6 @@ "mark_as_read": "حدد غير مقروء", "selected": "المحددة", "all": "الكل", + "all_categories": "All categories", "topics_marked_as_read.success": "تم تحديد المواضيع على أنها مقروءة!" } \ No newline at end of file diff --git a/public/language/ar/user.json b/public/language/ar/user.json index acc500eabf..d4c64f1ecd 100644 --- a/public/language/ar/user.json +++ b/public/language/ar/user.json @@ -6,8 +6,12 @@ "postcount": "عدد المشاركات", "email": "البريد الإلكتروني", "confirm_email": "تأكيد عنوان البريد الإلكتروني", + "ban_account": "Ban Account", + "ban_account_confirm": "Do you really want to ban this user?", + "unban_account": "Unban Account", "delete_account": "حذف الحساب", "delete_account_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

", "fullname": "الاسم الكامل", "website": "الموقع الإلكتروني", "location": "الموقع", @@ -21,7 +25,7 @@ "watched": "متابع", "followers": "المتابعون", "following": "يتابع", - "aboutme": "About me", + "aboutme": "معلومة عنك او السيرة الذاتية", "signature": "توقيع", "gravatar": "Gravatar", "birthday": "عيد ميلاد", @@ -64,9 +68,9 @@ "settings-require-reload": "تغيير بعض اﻹعدادات يتطلب تحديث الصفحة. إضغط هنا لتحديث الصفحة", "has_no_follower": "هذا المستخدم ليس لديه أي متابع :(", "follows_no_one": "هذا المستخدم لا يتابع أحد :(", - "has_no_posts": "هذا المستخدم لم يكتب أي شيء بعد.", - "has_no_topics": "هذا المستخدم لم ينشئ أي موضوع بعد.", - "has_no_watched_topics": "This user didn't watch any topics yet.", + "has_no_posts": "This user hasn't posted anything yet.", + "has_no_topics": "This user hasn't posted any topics yet.", + "has_no_watched_topics": "This user hasn't watched any topics yet.", "email_hidden": "البريد الإلكتروني مخفي", "hidden": "مخفي", "paginate_description": "Paginate topics and posts instead of using infinite scroll", @@ -79,6 +83,6 @@ "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen", "follow_topics_you_reply_to": "متابعة المواضيع التي تقوم بالرد فيها", "follow_topics_you_create": "متابعة المواضيع التي تنشئها", - "grouptitle": "Select the group title you would like to display", - "no-group-title": "No group title" + "grouptitle": "حدد عنوان المجموعة الذي تريد عرضه", + "no-group-title": "لا يوجد عنوان للمجموعة" } \ No newline at end of file diff --git a/public/language/ar/users.json b/public/language/ar/users.json index e49f5e1b79..d90805ee6b 100644 --- a/public/language/ar/users.json +++ b/public/language/ar/users.json @@ -5,8 +5,17 @@ "search": "بحث", "enter_username": "أدخل اسم مستخدم للبحث", "load_more": "حمل المزيد", - "users-found-search-took": "%1 user(s) found! Search took %2 seconds.", + "users-found-search-took": "تم إيجاد %1 مستخدمـ(ين)! استغرق البحث %2 ثانية.", "filter-by": "Filter By", "online-only": "المتصلون فقط", - "picture-only": "Picture only" + "picture-only": "صورة فقط", + "invite": "Invite", + "invitation-email-sent": "An invitation email has been sent to %1", + "user_list": "User List", + "recent_topics": "Recent Topics", + "popular_topics": "Popular Topics", + "unread_topics": "Unread Topics", + "categories": "Categories", + "tags": "Tags", + "map": "Map" } \ No newline at end of file diff --git a/public/language/bg/email.json b/public/language/bg/email.json index 3ee542969f..f674ebd3f6 100644 --- a/public/language/bg/email.json +++ b/public/language/bg/email.json @@ -1,11 +1,15 @@ { "password-reset-requested": "Изпратена е заявка за подновяване на паролата – %1!", "welcome-to": "Добре дошли в %1", + "invite": "Покана от %1", "greeting_no_name": "Здравейте", "greeting_with_name": "Здравейте, %1", "welcome.text1": "Благодарим Ви, че се регистрирахте с %1", "welcome.text2": "За да активирате напълно Вашия акаунт, трябва да потвърдите е-пощата, с която сте се регистрирали.", + "welcome.text3": "Вашата заявка за регистрация беше приета от администратор. Вече можете да се впишете с Вашето потребителско име и парола.", "welcome.cta": "Натиснете тук, за да потвърдите Вашата е-поща.", + "invitation.text1": "%1 Ви покани да се присъедините към %2", + "invitation.ctr": "Натиснете тук, за да си създадете акаунт.", "reset.text1": "Получихме заявка за подновяване на Вашата парола, най-вероятно защото сте я забравили. Ако това не е така, моля не обръщайте внимание на това е-писмо.", "reset.text2": "За да продължите с процедурата по подновяване на паролата, моля последвайте следната връзка:", "reset.cta": "Натиснете тук, за да подновите паролата си", diff --git a/public/language/bg/error.json b/public/language/bg/error.json index ce39acb82d..fb4ca7f82d 100644 --- a/public/language/bg/error.json +++ b/public/language/bg/error.json @@ -51,6 +51,7 @@ "already-favourited": "Вече сте отбелязали тази публикация като любима", "already-unfavourited": "Вече сте премахнали тази публикация от любимите си", "cant-ban-other-admins": "Не можете да блокирате другите администратори!", + "cant-remove-last-admin": "Вие сте единственият администратор. Добавете друг потребител като администратор, преди да премахнете себе си като администратор", "invalid-image-type": "Грешен тип на изображение. Позволените типове са: %1", "invalid-image-extension": "Грешно разширение на изображението", "invalid-file-type": "Грешен тип на файл. Позволените типове са: %1", @@ -59,6 +60,8 @@ "group-name-change-not-allowed": "Промяната на името на групата не е разрешено", "group-already-member": "Вече сте част от тази група", "group-needs-owner": "Тази група се нуждае от поне един собственик", + "group-already-invited": "Този потребител вече е бил поканен", + "group-already-requested": "Вашата заявка за членство вече е била изпратена", "post-already-deleted": "Тази публикация вече е изтрита", "post-already-restored": "Тази публикация вече е възстановена", "topic-already-deleted": "Тази тема вече е изтрита", @@ -67,8 +70,8 @@ "topic-thumbnails-are-disabled": "Иконките на темите са изключени.", "invalid-file": "Грешен файл", "uploads-are-disabled": "Качването не е разрешено", - "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", - "about-me-too-long": "Sorry, your about me cannot be longer than %1 character(s).", + "signature-too-long": "Съжаляваме, но подписът Ви трябва да съдържа не повече от %1 символ(а).", + "about-me-too-long": "Съжаляваме, но информацията за Вас трябва да съдържа не повече от %1 символ(а).", "cant-chat-with-yourself": "Не можете да пишете чат съобщение на себе си!", "chat-restricted": "Този потребител е ограничил чат съобщенията до себе си. Той трябва първо да Ви последва, преди да можете да си пишете с него.", "too-many-messages": "Изпратили сте твърде много съобщения. Моля, изчакайте малко.", @@ -76,6 +79,7 @@ "downvoting-disabled": "Отрицателното гласуване е изключено", "not-enough-reputation-to-downvote": "Нямате достатъчно репутация, за да гласувате отрицателно за тази публикация", "not-enough-reputation-to-flag": "Нямате достатъчно репутация, за да докладвате тази публикация", + "already-flagged": "Вече сте докладвали тази публикация", "reload-failed": "NodeBB срещна проблем при презареждането: „%1“. NodeBB ще продължи да поддържа съществуващите клиентски ресурси, но Вие трябва да отмените последните си действия преди презареждането.", "registration-error": "Грешка при регистрацията", "parse-error": "Нещо се обърка при прочитането на отговора на сървъра", diff --git a/public/language/bg/global.json b/public/language/bg/global.json index a0b9ca9e26..41f4d35262 100644 --- a/public/language/bg/global.json +++ b/public/language/bg/global.json @@ -50,6 +50,7 @@ "views": "Преглеждания", "reputation": "Репутация", "read_more": "още", + "more": "Още", "posted_ago_by_guest": "публикувано %1 от гост", "posted_ago_by": "публикувано %1 от %2", "posted_ago": "публикувано %1", diff --git a/public/language/bg/groups.json b/public/language/bg/groups.json index 747b79d8a0..a3f89e69fc 100644 --- a/public/language/bg/groups.json +++ b/public/language/bg/groups.json @@ -6,6 +6,12 @@ "no_groups_found": "Няма групи", "pending.accept": "Приемане", "pending.reject": "Отхвърляне", + "pending.accept_all": "Приемане на всички", + "pending.reject_all": "Отхвърляне на всички", + "pending.none": "В момента няма чакащи членове", + "invited.none": "В момента няма поканени членове", + "invited.uninvite": "Отмяна на поканата", + "invited.search": "Потърсете потребител, когото да поканите в тази група", "cover-instructions": "Плъзнете снимка, наместете я в предпочитаната позиция и натистнете Запазване", "cover-change": "Промяна", "cover-save": "Запазване", @@ -13,6 +19,7 @@ "details.title": "Подробности за групата", "details.members": "Списък на членовете", "details.pending": "Кандидатстващи членове", + "details.invited": "Поканени членове", "details.has_no_posts": "Членовете на тази група не са публикували нищо.", "details.latest_posts": "Скорошни публикации", "details.private": "Частна", diff --git a/public/language/bg/notifications.json b/public/language/bg/notifications.json index c646a60206..ac6145c1e5 100644 --- a/public/language/bg/notifications.json +++ b/public/language/bg/notifications.json @@ -20,6 +20,7 @@ "user_posted_topic": "%1 публикува нова тема: %2", "user_mentioned_you_in": "%1 Ви спомена в %2", "user_started_following_you": "%1 започна да Ви следва.", + "new_register": "%1 изпрати заявка за регистрация.", "email-confirmed": "Е-пощата беше потвърдена", "email-confirmed-message": "Благодарим Ви, че потвърдихте е-пощата си. Акаунтът Ви е вече напълно активиран.", "email-confirm-error-message": "Възникна проблем при потвърждаването на е-пощата Ви. Може кодът да е грешен или давността му да е изтекла.", diff --git a/public/language/bg/register.json b/public/language/bg/register.json index 3f83b192e3..c62a14b3e9 100644 --- a/public/language/bg/register.json +++ b/public/language/bg/register.json @@ -14,5 +14,6 @@ "register_now_button": "Регистриране", "alternative_registration": "Друг начин за регистриране", "terms_of_use": "Условия за ползване", - "agree_to_terms_of_use": "Съгласен съм с условията за ползване" + "agree_to_terms_of_use": "Съгласен съм с условията за ползване", + "registration-added-to-queue": "Вашата регистрация беше добавена в опашката за одобрение. Ще получите е-писмо, когато тя бъде одобрена от администратор." } \ No newline at end of file diff --git a/public/language/bg/unread.json b/public/language/bg/unread.json index cff5d865ba..b80ddf87ef 100644 --- a/public/language/bg/unread.json +++ b/public/language/bg/unread.json @@ -5,5 +5,6 @@ "mark_as_read": "Отбелязване като прочетени", "selected": "Избраните", "all": "Всички", + "all_categories": "Всички категории", "topics_marked_as_read.success": "Темите бяха отбелязани като прочетени!" } \ No newline at end of file diff --git a/public/language/bg/user.json b/public/language/bg/user.json index dee62ac943..eebc5bdc93 100644 --- a/public/language/bg/user.json +++ b/public/language/bg/user.json @@ -6,8 +6,12 @@ "postcount": "Брой публикации", "email": "Е-поща", "confirm_email": "Потвърдете е-пощата", + "ban_account": "Блокиране на акаунта", + "ban_account_confirm": "Наистина ли искате да блокирате този потребител?", + "unban_account": "Отблокиране на акаунта", "delete_account": "Изтриване на акаунта", "delete_account_confirm": "Сигурни ли сте, че искате да изтриете акаунта си?
Това действие е необратимо и няма да можете да възстановите нищо от данните си

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

", "fullname": "Цяло име", "website": "Уеб сайт", "location": "Местоположение", @@ -64,9 +68,9 @@ "settings-require-reload": "Някои промени в настройките изискват презареждане. Натиснете тук, за да презаредите страницата.", "has_no_follower": "Този потребител няма последователи :(", "follows_no_one": "Този потребител не следва никого :(", - "has_no_posts": "Този потребител не е публикувал нищо все още.", + "has_no_posts": "Този потребител не е публикувал нищо досега.", "has_no_topics": "Този потребител не е създавал теми досега.", - "has_no_watched_topics": "Този потребител не е наблюдавал нито една тема все още.", + "has_no_watched_topics": "Този потребител не е следил нито една тема досега.", "email_hidden": "Е-пощата е скрита", "hidden": "скрито", "paginate_description": "Разделяне на темите и публикациите на страници, вместо да се превърта безкрайно", diff --git a/public/language/bg/users.json b/public/language/bg/users.json index ec375fb01e..9a442eed88 100644 --- a/public/language/bg/users.json +++ b/public/language/bg/users.json @@ -8,5 +8,14 @@ "users-found-search-took": "Намерени са %1 потребител(и)! Търсенето отне %2 секунди.", "filter-by": "Филтриране", "online-only": "Само тези на линия", - "picture-only": "Само със снимка" + "picture-only": "Само със снимка", + "invite": "Канене", + "invitation-email-sent": "Беше изпратено е-писмо за потвърждение до %1", + "user_list": "Списък от потребители", + "recent_topics": "Скорошни теми", + "popular_topics": "Популярни теми", + "unread_topics": "Непрочетени теми", + "categories": "Категории", + "tags": "Етикети", + "map": "Карта" } \ No newline at end of file diff --git a/public/language/bn/email.json b/public/language/bn/email.json index 91169e5549..2ac4ae8057 100644 --- a/public/language/bn/email.json +++ b/public/language/bn/email.json @@ -1,11 +1,15 @@ { "password-reset-requested": "পাসওয়ার্ড রিসেটের জন্য অনুরোধ করা হয়েছে - %1!", "welcome-to": "%1 এ স্বাগতম", + "invite": "Invitation from %1", "greeting_no_name": "স্বাগতম", "greeting_with_name": "স্বাগতম %1", "welcome.text1": "%1 এ নিবন্ধন করার জন্য আপনাকে ধন্যবাদ!", "welcome.text2": "আপনার একাউন্ট এ্যাক্টিভেট করার জন্য, আপনি যে ইমেইল এড্রেস ব্যাবহার করে নিবন্ধন করেছেন তা যাচাই করতে হবে", + "welcome.text3": "An administrator has accepted your registration application. You can login with your username/password now.", "welcome.cta": "আপনার ইমেইল এড্রেস নিশ্চিত করার জন্য এখানে ক্লিক করুন", + "invitation.text1": "%1 has invited you to join %2", + "invitation.ctr": "Click here to create your account.", "reset.text1": "আমরা আপনার পাসওয়ার্ড রিসেট করার অনুরোধ পেয়েছি, সম্ভবত আপনি আপনার পাসওয়ার্ড ভুলে গিয়েছেন বলেই। তবে যদি তা না হয়ে থাকে, তাহলে এই মেইলকে উপেক্ষা করতে পারেন।", "reset.text2": "পাসওয়ার্ড রিসেট করতে নিচের লিংকে ক্লিক করুন", "reset.cta": "পাসওয়ার্ড রিসেট করতে এখানে ক্লিক করুন", diff --git a/public/language/bn/error.json b/public/language/bn/error.json index 979dca250b..48c5ac1f87 100644 --- a/public/language/bn/error.json +++ b/public/language/bn/error.json @@ -51,6 +51,7 @@ "already-favourited": "আপনি ইতিমধ্যে এই পোষ্টটি পছন্দের তালিকায় যোগ করেছেন", "already-unfavourited": "আপনি ইতিমধ্যে এই পোষ্টটি আপনার পছন্দের তালিকা থেকে সরিয়ে ফেলেছেন", "cant-ban-other-admins": "আপনি অন্য এ্যাডমিনদের নিষিদ্ধ করতে পারেন না!", + "cant-remove-last-admin": "You are the only administrator. Add another user as an administrator before removing yourself as admin", "invalid-image-type": "Invalid image type. Allowed types are: %1", "invalid-image-extension": "Invalid image extension", "invalid-file-type": "Invalid file type. Allowed types are: %1", @@ -59,6 +60,8 @@ "group-name-change-not-allowed": "গ্রুপের নাম পরিবর্তনের অনুমতি নেই", "group-already-member": "You are already part of this group", "group-needs-owner": "This group requires at least one owner", + "group-already-invited": "This user has already been invited", + "group-already-requested": "Your membership request has already been submitted", "post-already-deleted": "এই পোস্টটি ইতিমধ্যে ডিলিট করা হয়ে গিয়েছে", "post-already-restored": "এই পোষ্টটি ইতিমধ্যে পুনরোদ্ধার করা হয়েছে", "topic-already-deleted": "এই টপিকটি ইতিমধ্যে ডিলিট করা হয়েছে", @@ -76,6 +79,7 @@ "downvoting-disabled": "ঋণাত্মক ভোট নিস্ক্রীয় রাখা হয়েছে।", "not-enough-reputation-to-downvote": "আপনার এই পোস্ট downvote করার জন্য পর্যাপ্ত সম্মাননা নেই", "not-enough-reputation-to-flag": "এই পোষ্টকে ফ্লাগ করার জন্য আপনার পর্যাপ্ত সম্মাননা নেই", + "already-flagged": "You have already flagged this post", "reload-failed": "\"%1\" রিলোড করতে সমস্যা হয়েছে। রিলোডের পূর্বে যা করা হয়েছিল সেটি আনডু করা সমীচীন। ", "registration-error": "নিবন্ধন এরর!", "parse-error": "Something went wrong while parsing server response", diff --git a/public/language/bn/global.json b/public/language/bn/global.json index 64d9b71ff3..e21604d309 100644 --- a/public/language/bn/global.json +++ b/public/language/bn/global.json @@ -50,6 +50,7 @@ "views": "দেখেছেন", "reputation": "সন্মাননা", "read_more": "আরো পড়ুন", + "more": "More", "posted_ago_by_guest": "অতিথি পোস্ট করেছেন %1", "posted_ago_by": " %1 %2 দ্বারা পোস্টকৃত", "posted_ago": "পোস্ট করেছেন %1", diff --git a/public/language/bn/groups.json b/public/language/bn/groups.json index d87b5bc5d0..71b932f26f 100644 --- a/public/language/bn/groups.json +++ b/public/language/bn/groups.json @@ -6,6 +6,12 @@ "no_groups_found": "There are no groups to see", "pending.accept": "Accept", "pending.reject": "Reject", + "pending.accept_all": "Accept All", + "pending.reject_all": "Reject All", + "pending.none": "There are no pending members at this time", + "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", "cover-instructions": "Drag and Drop a photo, drag to position, and hit Save", "cover-change": "Change", "cover-save": "Save", @@ -13,6 +19,7 @@ "details.title": "গ্রুপের বিস্তারিত", "details.members": "সদস্য তালিকা", "details.pending": "Pending Members", + "details.invited": "Invited Members", "details.has_no_posts": "এই গ্রুপের সদস্যরা এখনো কোন পোষ্ট করেন নি", "details.latest_posts": "সর্বশেষ পোষ্টসমূহ", "details.private": "Private", diff --git a/public/language/bn/notifications.json b/public/language/bn/notifications.json index 2c29c68362..78771cb47c 100644 --- a/public/language/bn/notifications.json +++ b/public/language/bn/notifications.json @@ -20,6 +20,7 @@ "user_posted_topic": "%1 has posted a new topic: %2", "user_mentioned_you_in": "%1, %2 এ আপনার নাম উল্লেখ করেছেন", "user_started_following_you": "%1 আপনাকে অনুসরন করা শুরু করেছেন।", + "new_register": "%1 sent a registration request.", "email-confirmed": "ইমেইল নিশ্চিত করা হয়েছে", "email-confirmed-message": "আপনার ইমেইল যাচাই করার জন্য আপনাকে ধন্যবাদ। আপনার অ্যাকাউন্টটি এখন সম্পূর্ণরূপে সক্রিয়।", "email-confirm-error-message": "আপনার ইমেল ঠিকানার বৈধতা যাচাইয়ে একটি সমস্যা হয়েছে। সম্ভবত কোডটি ভুল ছিল অথবা কোডের মেয়াদ শেষ হয়ে গিয়েছে।", diff --git a/public/language/bn/register.json b/public/language/bn/register.json index 2361fe7c2b..46676f7ba8 100644 --- a/public/language/bn/register.json +++ b/public/language/bn/register.json @@ -14,5 +14,6 @@ "register_now_button": "নিবন্ধন করুন", "alternative_registration": "বিকল্প নিবন্ধন", "terms_of_use": "নিয়মাবলী", - "agree_to_terms_of_use": "আমি নিয়মাবলী মেনে চলতে সম্মতি জানালাম" + "agree_to_terms_of_use": "আমি নিয়মাবলী মেনে চলতে সম্মতি জানালাম", + "registration-added-to-queue": "Your registration has been added to the approval queue. You will receive an email when it is accepted by an administrator." } \ No newline at end of file diff --git a/public/language/bn/unread.json b/public/language/bn/unread.json index c5541d28e9..d7e42fb61b 100644 --- a/public/language/bn/unread.json +++ b/public/language/bn/unread.json @@ -5,5 +5,6 @@ "mark_as_read": "পঠিত হিসেবে চিহ্নিত করুন", "selected": "নির্বাচিত", "all": "সবগুলো", + "all_categories": "All categories", "topics_marked_as_read.success": "পঠিত হিসেবে চিহ্নিত টপিকসমূহ" } \ No newline at end of file diff --git a/public/language/bn/user.json b/public/language/bn/user.json index 96b8943a2d..b348b91866 100644 --- a/public/language/bn/user.json +++ b/public/language/bn/user.json @@ -6,8 +6,12 @@ "postcount": "Post Count", "email": "ইমেইল", "confirm_email": "ইমেইল নিশ্চিত করুন", + "ban_account": "Ban Account", + "ban_account_confirm": "Do you really want to ban this user?", + "unban_account": "Unban Account", "delete_account": "একাউন্ট মুছে ফেলুন", "delete_account_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

", "fullname": "পুর্ণ নাম", "website": "ওয়েবসাইট", "location": "স্থান", @@ -64,9 +68,9 @@ "settings-require-reload": "Some setting changes require a reload. Click here to reload the page.", "has_no_follower": "এই সদস্যের কোন ফলোয়ার নেই :(", "follows_no_one": "এই সদস্য কাউকে ফলো করছেন না :(", - "has_no_posts": "এই সদস্য এখনো কোন পোষ্ট করেন নি", - "has_no_topics": "এই সদস্য এখনো কোন টপিক পোষ্ট করেন নি", - "has_no_watched_topics": "This user didn't watch any topics yet.", + "has_no_posts": "This user hasn't posted anything yet.", + "has_no_topics": "This user hasn't posted any topics yet.", + "has_no_watched_topics": "This user hasn't watched any topics yet.", "email_hidden": "ইমেইল গোপন রাখা হয়েছে", "hidden": "গোপন করা হয়েছে", "paginate_description": "Paginate topics and posts instead of using infinite scroll", diff --git a/public/language/bn/users.json b/public/language/bn/users.json index 402aa33c27..f6fab836dc 100644 --- a/public/language/bn/users.json +++ b/public/language/bn/users.json @@ -8,5 +8,14 @@ "users-found-search-took": "%1 user(s) found! Search took %2 seconds.", "filter-by": "Filter By", "online-only": "Online only", - "picture-only": "Picture only" + "picture-only": "Picture only", + "invite": "Invite", + "invitation-email-sent": "An invitation email has been sent to %1", + "user_list": "User List", + "recent_topics": "Recent Topics", + "popular_topics": "Popular Topics", + "unread_topics": "Unread Topics", + "categories": "Categories", + "tags": "Tags", + "map": "Map" } \ No newline at end of file diff --git a/public/language/cs/email.json b/public/language/cs/email.json index a259648e70..2001c34a36 100644 --- a/public/language/cs/email.json +++ b/public/language/cs/email.json @@ -1,11 +1,15 @@ { "password-reset-requested": "Požadována obnova hesla - %1!", "welcome-to": "Vítejte v %1", + "invite": "Invitation from %1", "greeting_no_name": "Dobrý den", "greeting_with_name": "Dobrý den %1", "welcome.text1": "Děkujeme vám za registraci s %1!", "welcome.text2": "Pro úplnou aktivaci vašeho účtu potřebujeme ověřit vaší emailovou adresu.", + "welcome.text3": "An administrator has accepted your registration application. You can login with your username/password now.", "welcome.cta": "Klikněte zde pro potvrzení vaší emailové adresy", + "invitation.text1": "%1 has invited you to join %2", + "invitation.ctr": "Click here to create your account.", "reset.text1": "Obdrželi jsme požadavek na obnovu hesla, pravděpodobně kvůli tomu, že jste ho zapomněli. Pokud to není tento případ, ignorujte, prosím, tento email.", "reset.text2": "Přejete-li si pokračovat v obnově vašeho hesla, klikněte, prosím, na následující odkaz:", "reset.cta": "Klikněte zde, chcete-li obnovit vaše heslo", diff --git a/public/language/cs/error.json b/public/language/cs/error.json index 60418d0ef0..2d441ce0db 100644 --- a/public/language/cs/error.json +++ b/public/language/cs/error.json @@ -51,6 +51,7 @@ "already-favourited": "You have already favourited this post", "already-unfavourited": "You have already unfavourited this post", "cant-ban-other-admins": "Nemůžete zakazovat ostatní administrátory!", + "cant-remove-last-admin": "You are the only administrator. Add another user as an administrator before removing yourself as admin", "invalid-image-type": "Invalid image type. Allowed types are: %1", "invalid-image-extension": "Invalid image extension", "invalid-file-type": "Invalid file type. Allowed types are: %1", @@ -59,6 +60,8 @@ "group-name-change-not-allowed": "Změna názvu skupiny není povolena", "group-already-member": "You are already part of this group", "group-needs-owner": "This group requires at least one owner", + "group-already-invited": "This user has already been invited", + "group-already-requested": "Your membership request has already been submitted", "post-already-deleted": "Tento příspěvek byl již vymazán", "post-already-restored": "Tento příspěvek byl již obnoven", "topic-already-deleted": "Toto téma bylo již vymazáno", @@ -76,6 +79,7 @@ "downvoting-disabled": "Downvoting is disabled", "not-enough-reputation-to-downvote": "You do not have enough reputation to downvote this post", "not-enough-reputation-to-flag": "You do not have enough reputation to flag this post", + "already-flagged": "You have already flagged this post", "reload-failed": "NodeBB encountered a problem while reloading: \"%1\". NodeBB will continue to serve the existing client-side assets, although you should undo what you did just prior to reloading.", "registration-error": "Chyba při registraci", "parse-error": "Something went wrong while parsing server response", diff --git a/public/language/cs/global.json b/public/language/cs/global.json index 8fa5d860bb..a82e94c56e 100644 --- a/public/language/cs/global.json +++ b/public/language/cs/global.json @@ -50,6 +50,7 @@ "views": "Zobrazení", "reputation": "Reputation", "read_more": "read more", + "more": "More", "posted_ago_by_guest": "posted %1 by Guest", "posted_ago_by": "posted %1 by %2", "posted_ago": "posted %1", diff --git a/public/language/cs/groups.json b/public/language/cs/groups.json index 9a52faf4f7..6f4a368227 100644 --- a/public/language/cs/groups.json +++ b/public/language/cs/groups.json @@ -6,6 +6,12 @@ "no_groups_found": "There are no groups to see", "pending.accept": "Accept", "pending.reject": "Reject", + "pending.accept_all": "Accept All", + "pending.reject_all": "Reject All", + "pending.none": "There are no pending members at this time", + "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", "cover-instructions": "Drag and Drop a photo, drag to position, and hit Save", "cover-change": "Change", "cover-save": "Save", @@ -13,6 +19,7 @@ "details.title": "podrobnosti skupiny", "details.members": "Seznam členů", "details.pending": "Pending Members", + "details.invited": "Invited Members", "details.has_no_posts": "Členové této skupiny dosud neodeslali ani jeden příspěvek.", "details.latest_posts": "Nejnovější příspěvky", "details.private": "Private", diff --git a/public/language/cs/notifications.json b/public/language/cs/notifications.json index 791c3dc948..8265bb48da 100644 --- a/public/language/cs/notifications.json +++ b/public/language/cs/notifications.json @@ -20,6 +20,7 @@ "user_posted_topic": "%1 has posted a new topic: %2", "user_mentioned_you_in": "%1 mentioned you in %2", "user_started_following_you": "%1 started following you.", + "new_register": "%1 sent a registration request.", "email-confirmed": "Email Confirmed", "email-confirmed-message": "Thank you for validating your email. Your account is now fully activated.", "email-confirm-error-message": "There was a problem validating your email address. Perhaps the code was invalid or has expired.", diff --git a/public/language/cs/register.json b/public/language/cs/register.json index 1e7b9fc301..db7a689039 100644 --- a/public/language/cs/register.json +++ b/public/language/cs/register.json @@ -14,5 +14,6 @@ "register_now_button": "Zaregistrovat se", "alternative_registration": "Jiný způsob registrace", "terms_of_use": "Podmínky", - "agree_to_terms_of_use": "Souhlasím s Podmínkami" + "agree_to_terms_of_use": "Souhlasím s Podmínkami", + "registration-added-to-queue": "Your registration has been added to the approval queue. You will receive an email when it is accepted by an administrator." } \ No newline at end of file diff --git a/public/language/cs/unread.json b/public/language/cs/unread.json index 00107b5419..1ded2636c9 100644 --- a/public/language/cs/unread.json +++ b/public/language/cs/unread.json @@ -5,5 +5,6 @@ "mark_as_read": "Označit jako přeštené", "selected": "Vybrané", "all": "Vše", + "all_categories": "All categories", "topics_marked_as_read.success": "Téma bylo označeno jako přečtené!" } \ No newline at end of file diff --git a/public/language/cs/user.json b/public/language/cs/user.json index b41192c56f..3089a0e14f 100644 --- a/public/language/cs/user.json +++ b/public/language/cs/user.json @@ -6,8 +6,12 @@ "postcount": "Post Count", "email": "Email", "confirm_email": "Potvrdit email", + "ban_account": "Ban Account", + "ban_account_confirm": "Do you really want to ban this user?", + "unban_account": "Unban Account", "delete_account": "Vymazat účet", "delete_account_confirm": "Are you sure you want to delete your account?
This action is irreversible and you will not be able to recover any of your data

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

", "fullname": "Jméno a příjmení", "website": "Webové stránky", "location": "Poloha", @@ -64,9 +68,9 @@ "settings-require-reload": "Some setting changes require a reload. Click here to reload the page.", "has_no_follower": "Tohoto uživatele nikdo nesleduje :(", "follows_no_one": "Tento uživatel nikoho nesleduje :(", - "has_no_posts": "This user didn't post anything yet.", - "has_no_topics": "This user didn't post any topics yet.", - "has_no_watched_topics": "This user didn't watch any topics yet.", + "has_no_posts": "This user hasn't posted anything yet.", + "has_no_topics": "This user hasn't posted any topics yet.", + "has_no_watched_topics": "This user hasn't watched any topics yet.", "email_hidden": "Skrytý email", "hidden": "skrytý", "paginate_description": "Paginate topics and posts instead of using infinite scroll", diff --git a/public/language/cs/users.json b/public/language/cs/users.json index 22b698a8c0..4d53504d8e 100644 --- a/public/language/cs/users.json +++ b/public/language/cs/users.json @@ -8,5 +8,14 @@ "users-found-search-took": "%1 user(s) found! Search took %2 seconds.", "filter-by": "Filter By", "online-only": "Online only", - "picture-only": "Picture only" + "picture-only": "Picture only", + "invite": "Invite", + "invitation-email-sent": "An invitation email has been sent to %1", + "user_list": "User List", + "recent_topics": "Recent Topics", + "popular_topics": "Popular Topics", + "unread_topics": "Unread Topics", + "categories": "Categories", + "tags": "Tags", + "map": "Map" } \ No newline at end of file diff --git a/public/language/da/category.json b/public/language/da/category.json new file mode 100644 index 0000000000..44a6bed071 --- /dev/null +++ b/public/language/da/category.json @@ -0,0 +1,12 @@ +{ + "new_topic_button": "Nyt emne", + "guest-login-post": "Log ind", + "no_topics": "Der er ikke nogen nye emner i denne kategori.
Hvorfor prøver du ikke at lave et?", + "browsing": "browse", + "no_replies": "Ingen har svaret", + "share_this_category": "Del denne kategori", + "watch": "Overvåg", + "ignore": "Ignorer", + "watch.message": "Du overvåger nu opdateringer fra denne kategori", + "ignore.message": "Du ignorere nu opdateringer fra denne kategori" +} \ No newline at end of file diff --git a/public/language/da/email.json b/public/language/da/email.json new file mode 100644 index 0000000000..27d8aebc22 --- /dev/null +++ b/public/language/da/email.json @@ -0,0 +1,32 @@ +{ + "password-reset-requested": "Vi skal anmode dig om at nulstille dit kodeord - %1!", + "welcome-to": "Velkommen til %1", + "invite": "Invitation fra %1", + "greeting_no_name": "Hej", + "greeting_with_name": "Hej %1", + "welcome.text1": "Tak for at du registrerede dig hos %1!", + "welcome.text2": "For at færdiggøre din konto, har vi brug for at verificere at du ejer den email adresse du registerede med.", + "welcome.text3": "En administrator har accepteret din registreringsansøgning. Du kan logge ind med dit brugernavn og adgangskode nu.", + "welcome.cta": "Klik her for at bekræfte din email adresse.", + "invitation.text1": "%1 har inviteret dig til at deltage i %2", + "invitation.ctr": "Klik her for at oprette din konto.", + "reset.text1": "Vi har modtaget en anmodning om at nulstille dit kodeord, måske fordi du har glemt det. Hvis det ikke er tilfældet, venligst ignorer denne email.", + "reset.text2": "For at fortsætte med at nulstille kodeordet, venligst klik på dette link:", + "reset.cta": "Klik her for at nulstille dit kodeord. ", + "reset.notify.subject": "Dit kodeord er nu ændret", + "reset.notify.text1": "Bemærk: %1 gang blev dit kodeord ændret.", + "reset.notify.text2": "Hvis du ikke godkendte dette, kontakt straks en administrator.", + "digest.notifications": "Du har ulæste notifikationer fra %1:", + "digest.latest_topics": "Nyeste emne fra %1", + "digest.cta": "Klik her for at gå til %1", + "digest.unsub.info": "Du har fået tilsendt dette sammendrag pga. indstillingerne i dit abonnement.", + "digest.no_topics": "Der har ikke været nogen aktive emner de/den sidste %1", + "notif.chat.subject": "Ny chat besked modtaget fra %1", + "notif.chat.cta": "Klik her for at forsætte med samtalen", + "notif.chat.unsub.info": "Denne chat notifikation blev sendt til dig pga. indstillingerne i dit abonnement.", + "notif.post.cta": "Klik her for a læse hele emnet", + "notif.post.unsub.info": "Denne indlægs notifikation var sendt pga. dine abonnering indstillinger.", + "test.text1": "Dette er en test email for at kontrollere, at den udgående email server er opsat korrekt i forhold til din NodeBB installation.", + "unsub.cta": "Klik her for at ændre disse indstillinger", + "closing": "Tak!" +} \ No newline at end of file diff --git a/public/language/da/error.json b/public/language/da/error.json new file mode 100644 index 0000000000..e976631c35 --- /dev/null +++ b/public/language/da/error.json @@ -0,0 +1,88 @@ +{ + "invalid-data": "Ugyldig Data", + "not-logged-in": "Det ser ikke ud til at du er logget ind.", + "account-locked": "Din konto er blevet blokeret midlertidigt.", + "search-requires-login": "Du skal have en konto for at søge - log venligst ind eller registrer dig.", + "invalid-cid": "Ugyldig Kategori ID", + "invalid-tid": "Ugyldig Tråd ID", + "invalid-pid": "Ugyldig Indlæg ID", + "invalid-uid": "Ugyldig Bruger ID", + "invalid-username": "Ugyldig Brugernavn", + "invalid-email": "Ugyldig Email", + "invalid-title": "Ugylidt titel", + "invalid-user-data": "Ugyldig Bruger Data", + "invalid-password": "Ugyldig Adgangskode", + "invalid-username-or-password": "Venligst angiv både brugernavn og adgangskode", + "invalid-search-term": "Ugyldig søgeterm", + "invalid-pagination-value": "Ugyldig sidetalsværdi", + "username-taken": "Brugernavn optaget", + "email-taken": "Emailadresse allerede i brug", + "email-not-confirmed": "Din email adresse er ikke blevet bekræftet endnu, venligst klik her for at bekrætige den.", + "email-not-confirmed-chat": "Du kan ikke chatte før din email er bekræftet, klik her for at bekræfte din email.", + "no-email-to-confirm": "Dette forum kræver bekræftelse af din email, klik her for at indtaste en email", + "email-confirm-failed": "Vi kunne ikke bekræfte din email, prøv igen senere.", + "confirm-email-already-sent": "Bekræftelses email er allerede afsendt, vent venligt %1 minut(ter) for at sende endnu en.", + "username-too-short": "Brugernavn er for kort", + "username-too-long": "Brugernavn er for langt", + "user-banned": "Bruger er bortvist", + "user-too-new": "Beklager, du er nødt til at vente %1 sekund(er) før du opretter dit indlæg", + "no-category": "Kategorien eksisterer ikke", + "no-topic": "Tråden eksisterer ikke", + "no-post": "Indlægget eksisterer ikke", + "no-group": "Gruppen eksisterer ikke", + "no-user": "Brugeren eksisterer ikke", + "no-teaser": "Teaser eksisterer ikke", + "no-privileges": "Du har ikke nok rettigheder til at udføre denne handling", + "no-emailers-configured": "Ingen email plugin er blevet indlæst, så en test email kunne ikke sendes", + "category-disabled": "Kategorien er deaktiveret", + "topic-locked": "Tråden er låst", + "post-edit-duration-expired": "Du kan kun redigere indlæg i %1 sekund(er) efter indlæg", + "still-uploading": "Venligst vent til overførslen er færdig", + "content-too-short": "Venligst indtast et længere indlæg. Indlægget skal mindst indeholde %1 karakter(er).", + "content-too-long": "Venligt indtast et kortere indlæg. Indlæg kan ikke være længere end %1 karakter(er).", + "title-too-short": "Venligst indtast en længere titel. Titlen skal mindst indeholde %1 karakter(er).", + "title-too-long": "Venligst indtast en kortere titel. Titlen kan ikke indeholde flere end %1 karakter(er).", + "too-many-posts": "Du kan højest skrive et indlæg hver %1 sekund(er) - venligst vent et øjeblik før næste indlæg", + "too-many-posts-newbie": "Som ny bruger kan du kun skrive et indlæg engang hvert %1. sekund() indtil du har optjent %2 omdømme point - venligst vent et øjeblik før næste indlæg.", + "tag-too-short": "Indtast et længere tag. Tags skal indeholde mindst %1 karakter(er).", + "tag-too-long": "Indtast et længere tag. Tags kan ikke være længere end %1 karakter(er).", + "file-too-big": "Maksimum filstørrelse er %1 kB - venligst overfør en mindre fil", + "cant-vote-self-post": "Du kan ikke stemme på dit eget indlæg", + "already-favourited": "Du har allerede føjet dette indlæg til dine favoritter", + "already-unfavourited": "Du har allerede fjernet dette indlæg fra dine favoritter", + "cant-ban-other-admins": "Du kan ikke udlukke andre administatrorer!", + "cant-remove-last-admin": "Du er den eneste administrator. Tilføj en anden bruger som administrator før du fjerner dig selv som administrator", + "invalid-image-type": "Invalid billed type. De tilladte typer er: %1", + "invalid-image-extension": "Forkert billede filnavnsendelse", + "invalid-file-type": "Invalid fil type. Tilladte typer er: %1", + "group-name-too-short": "Gruppe navn for kort", + "group-already-exists": "Gruppen eksisterer allerede", + "group-name-change-not-allowed": "Ændring af gruppe navn er ikke tilladt", + "group-already-member": "Du er allerede en del af denne gruppe", + "group-needs-owner": "Denne grupper kræver mindst én ejer", + "group-already-invited": "Denne bruger er allerede blevet inviteret", + "group-already-requested": "Din medlemskabs anmodning er allerede blevet afsendt", + "post-already-deleted": "Dette indlæg er allerede blevet slettet", + "post-already-restored": "Dette indlæg er allerede blevet genskabt", + "topic-already-deleted": "Denne tråd er allerede blevet slettet", + "topic-already-restored": "Denne tråd er allerede blevet genskabt", + "cant-purge-main-post": "Du kan ikke udradere hoved indlægget, fjern venligt tråden istedet", + "topic-thumbnails-are-disabled": "Tråd miniaturebilleder er slået fra.", + "invalid-file": "Ugyldig fil", + "uploads-are-disabled": "Overførsel er slået fra", + "signature-too-long": "Beklager, din signatur kan ikke være længere end %1 karakter(er).", + "about-me-too-long": "Beklager, men din om mig side kan ikke være længere end %1 karakter(er).", + "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.", + "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", + "not-enough-reputation-to-flag": "Du har ikke nok omdømme til at vurdere dette indlæg", + "already-flagged": "Du har allerede vurderet dette indlæg", + "reload-failed": "NodeBB stødte på et problem under genindlæsningen : \"%1\". NodeBB vil fortsætte med en ældre version, og det er nok god ide at genoptage fra lige før du genindlæste siden.", + "registration-error": "Registeringsfejl", + "parse-error": "Noget gik galt under fortolknings er serverens respons", + "wrong-login-type-email": "Brug venligt din email til login", + "wrong-login-type-username": "Brug venligt dit brugernavn til login" +} \ No newline at end of file diff --git a/public/language/da/global.json b/public/language/da/global.json new file mode 100644 index 0000000000..16164668d1 --- /dev/null +++ b/public/language/da/global.json @@ -0,0 +1,82 @@ +{ + "home": "Forside", + "search": "Søg", + "buttons.close": "Luk", + "403.title": "Adgang nægtet", + "403.message": "Det ser ud til du er stødt på en side du ikke har adgang til.", + "403.login": "Måske du skulle prøve og logge ind?", + "404.title": "Ikke fundet", + "404.message": "Det ser ud til du er stødt på en side der ikke finder. Retuner til forsiden.", + "500.title": "Intern fejl.", + "500.message": "Ups! Ser ud til at noget gik galt!", + "register": "Tilmeld", + "login": "Log ind", + "please_log_in": "Venligst log ind", + "logout": "Log ud", + "posting_restriction_info": "Det er i øjeblikket kun muligt at skrive indlæg som registeret medlem, klik her for at logge ind.", + "welcome_back": "Velkommen tilbage", + "you_have_successfully_logged_in": "Du er nu logget ind", + "save_changes": "Gem ændringer", + "close": "Luk", + "pagination": "Sidetal", + "pagination.out_of": "%1 ud af %2", + "pagination.enter_index": "Indtast indeks", + "header.admin": "Administrator", + "header.recent": "Seneste", + "header.unread": "Ulæst", + "header.tags": "Etiket", + "header.popular": "Populære", + "header.users": "Bruger", + "header.groups": "Grupper", + "header.chats": "Chats", + "header.notifications": "Notifikationer", + "header.search": "Søg", + "header.profile": "Profil", + "notifications.loading": "Indlæser notifikationer", + "chats.loading": "Indlæser chats", + "motd.welcome": "Velkommen til NodeBB, fremtidens diskussion platform. ", + "previouspage": "Forrige side", + "nextpage": "Næste side", + "alert.success": "Succes", + "alert.error": "Fejl", + "alert.banned": "Forment adgang", + "alert.banned.message": "Du er lige blevet udelukket, du vil nu blive logget ud.", + "alert.unfollow": "Du følger ikke længere %1!", + "alert.follow": "Du følger nu %1!", + "online": "Online", + "users": "Bruger", + "topics": "Emner", + "posts": "Indlæg", + "views": "Visninger", + "reputation": "Omdømme", + "read_more": "læs mere", + "more": "Mere", + "posted_ago_by_guest": "indsendt %1 af gæst", + "posted_ago_by": "indsendt %1 siden af %2", + "posted_ago": "Indsendt %1 siden", + "posted_in_ago_by_guest": "insendt i %1 %2 siden af gæst", + "posted_in_ago_by": "skrevet i %1 %2 af %3", + "posted_in_ago": "skrivet i %1 %2", + "replied_ago": "svaret for %1", + "user_posted_ago": "%1 skrev for %2", + "guest_posted_ago": "Gæst skrev for %1", + "last_edited_by_ago": "sidst redigeret af %1 for %2", + "norecentposts": "Ingen seneste indlæg", + "norecenttopics": "Ingen seneste tråde", + "recentposts": "Seneste indlæg", + "recentips": "Seneste loggede ind IPer", + "away": "Væk", + "dnd": "Vil ikke forstyres", + "invisible": "Usynlig", + "offline": "Offline", + "email": "Email", + "language": "Sprog", + "guest": "Gæst", + "guests": "Gæster", + "updated.title": "Forum opdateret", + "updated.message": "Dette form er lige blevet opdateret til den seneste version. Klik her for at genindlæse siden.", + "privacy": "Privatliv", + "follow": "Følg", + "unfollow": "Følg ikke længere", + "delete_all": "Slet alt" +} \ No newline at end of file diff --git a/public/language/da/groups.json b/public/language/da/groups.json new file mode 100644 index 0000000000..41478b7ba0 --- /dev/null +++ b/public/language/da/groups.json @@ -0,0 +1,43 @@ +{ + "groups": "grupper", + "view_group": "se gruppe", + "owner": "Gruppe ejer", + "new_group": "Opret ny gruppe", + "no_groups_found": "Der er ingen grupper at se", + "pending.accept": "Accepter", + "pending.reject": "Afvis", + "pending.accept_all": "Acceptér Alle", + "pending.reject_all": "Afvis Alle", + "pending.none": "Der er ikke nogen afventene medlemmer i øjeblikket", + "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", + "cover-instructions": "Træk og slip et billede, træk til positionen og tryk Gem", + "cover-change": "Ændre", + "cover-save": "Gem", + "cover-saving": "Gemmer", + "details.title": "gruppe detaljer", + "details.members": "liste over medlemmer", + "details.pending": "Verserende medlemmer", + "details.invited": "Inviterede Medlemmer", + "details.has_no_posts": "Medlemmer af denne gruppe har ikke oprettet indlæg.", + "details.latest_posts": "seneste indlæg", + "details.private": "Privat", + "details.grant": "Giv/ophæv ejerskab", + "details.kick": "Spark", + "details.owner_options": "Gruppe administration", + "details.group_name": "Gruppe navn", + "details.member_count": "Medlemsantal", + "details.creation_date": "Oprettelsesdato", + "details.description": "Beskrivelse", + "details.badge_preview": "Mærke forhåndsvisning", + "details.change_icon": "Skift ikon", + "details.change_colour": "Skift farve", + "details.badge_text": "Mærke tekst", + "details.userTitleEnabled": "Vis mærke", + "details.private_help": "Hvis aktiveret, så vil det kræve godkendelse af gruppe ejeren for at tilslutte sig en gruppe", + "details.hidden": "Skjult", + "details.hidden_help": "Hvis aktiveret, så vil denne gruppe ikke kunne ses i gruppelisten og bruhere skal inviteres manuelt", + "event.updated": "Gruppe detaljer er blevet opdateret", + "event.deleted": "Gruppen \"%1\" er blevet slettet" +} \ No newline at end of file diff --git a/public/language/da/language.json b/public/language/da/language.json new file mode 100644 index 0000000000..f831129ee9 --- /dev/null +++ b/public/language/da/language.json @@ -0,0 +1,5 @@ +{ + "name": "Danish", + "code": "da", + "dir": "ltr" +} \ No newline at end of file diff --git a/public/language/da/login.json b/public/language/da/login.json new file mode 100644 index 0000000000..1aabf6ec75 --- /dev/null +++ b/public/language/da/login.json @@ -0,0 +1,11 @@ +{ + "username-email": "Brugernavn / Email", + "username": "Brugernavn", + "email": "Email", + "remember_me": "Husk mig?", + "forgot_password": "Glemt kodeord?", + "alternative_logins": "alternative logins", + "failed_login_attempt": "Login mislykkedes, venligt prøv igen.", + "login_successful": "Du har successfuldt logged in!", + "dont_have_account": "Har du ikke en konto?" +} \ No newline at end of file diff --git a/public/language/da/modules.json b/public/language/da/modules.json new file mode 100644 index 0000000000..8f178892ee --- /dev/null +++ b/public/language/da/modules.json @@ -0,0 +1,26 @@ +{ + "chat.chatting_with": "Chat med ", + "chat.placeholder": "Skriv din chatbesked her, tryk enter for at sende", + "chat.send": "Send", + "chat.no_active": "Du har ingen aktive chats.", + "chat.user_typing": "%1 skriver ...", + "chat.user_has_messaged_you": "1% har skrevet til dig.", + "chat.see_all": "Se alle chats.", + "chat.no-messages": "Vælg en modtager for at se beskedhistorikken", + "chat.recent-chats": "Seneste chats", + "chat.contacts": "Kontakter", + "chat.message-history": "Beskedhistorik", + "chat.pop-out": "Pop ud chatten", + "chat.maximize": "Maximer", + "chat.seven_days": "7 dage", + "chat.thirty_days": "30 dage", + "chat.three_months": "3 måneder", + "composer.compose": "Skriv", + "composer.show_preview": "Vis forhåndsvisning", + "composer.hide_preview": "Fjern forhåndsvisning", + "composer.user_said_in": "%1 sagde i %2:", + "composer.user_said": "%1 sagde:", + "composer.discard": "Er du sikker på at du vil kassere dette indlæg?", + "composer.submit_and_lock": "Send og lås", + "composer.toggle_dropdown": "Skift mellem dropdown" +} \ No newline at end of file diff --git a/public/language/da/notifications.json b/public/language/da/notifications.json new file mode 100644 index 0000000000..a6a810dc87 --- /dev/null +++ b/public/language/da/notifications.json @@ -0,0 +1,28 @@ +{ + "title": "Notifikationer", + "no_notifs": "Du har ingen nye notifkationer", + "see_all": "Se alle notifikationer", + "mark_all_read": "Marker alle notifikationer læst", + "back_to_home": "Tilbage til %1", + "outgoing_link": "Udgående link", + "outgoing_link_message": "Du forlader nu %1.", + "continue_to": "Fortsæt til %1", + "return_to": "Returnere til %t", + "new_notification": "Ny notifikation", + "you_have_unread_notifications": "Du har ulæste notifikationer.", + "new_message_from": "Ny besked fra %1", + "upvoted_your_post_in": "%1 har upvotet dit indlæg i %2.", + "moved_your_post": "%1 har flyttet dit indlæg.", + "moved_your_topic": "%1 har flyttet din tråd.", + "favourited_your_post_in": "%1 har favoriseret dit indlæg i %2.", + "user_flagged_post_in": "%1 har anmeldt et indlæg i %2", + "user_posted_to": "%1 har skrevet et svar til: %2", + "user_posted_topic": "%1 har oprettet en ny tråd: %2", + "user_mentioned_you_in": "%1 nævnte dig i %2", + "user_started_following_you": "%1 har valgt at følge dig.", + "new_register": "%1 har sendt en registrerings anmodning.", + "email-confirmed": "Email bekræftet", + "email-confirmed-message": "Tak fordi du validerede din email. Din konto er nu fuldt ud aktiveret.", + "email-confirm-error-message": "Der var et problem med valideringen af din emailadresse. Bekræftelses koden var muligvis forkert eller udløbet.", + "email-confirm-sent": "Bekræftelses email afsendt." +} \ No newline at end of file diff --git a/public/language/da/pages.json b/public/language/da/pages.json new file mode 100644 index 0000000000..6843d45062 --- /dev/null +++ b/public/language/da/pages.json @@ -0,0 +1,21 @@ +{ + "home": "Forside", + "unread": "Ulæste tråde", + "popular": "Populære tråde", + "recent": "Seneste tråde", + "users": "Registrerede brugere", + "notifications": "Notifikationer", + "tags": "Tags", + "tag": "Tråde tagget under \"%1\"", + "user.edit": "Redigere \"%1\"", + "user.following": "Folk som %1 følger", + "user.followers": "Folk der følger %1", + "user.posts": "Indlæg skrevet af %1", + "user.topics": "Tråde oprettet af %1", + "user.groups": "%1s grupper", + "user.favourites": "%1s favorit indlæg", + "user.settings": "Brugerindstillinger", + "user.watched": "Tråde overvåget af %1", + "maintenance.text": "%1 er under vedligeholdelse. Kom venligst tilbage senere.", + "maintenance.messageIntro": "Administratoren har yderligere vedlagt denne besked:" +} \ No newline at end of file diff --git a/public/language/da/recent.json b/public/language/da/recent.json new file mode 100644 index 0000000000..102e609bdc --- /dev/null +++ b/public/language/da/recent.json @@ -0,0 +1,19 @@ +{ + "title": "Seneste", + "day": "Dag", + "week": "Uge", + "month": "Måned", + "year": "År", + "alltime": "Al tid", + "no_recent_topics": "Der er ingen seneste tråde", + "no_popular_topics": "Der er ingen populære tråde.", + "there-is-a-new-topic": "Der er en ny tråd.", + "there-is-a-new-topic-and-a-new-post": "Der er en ny tråd og et nyt indlæg.", + "there-is-a-new-topic-and-new-posts": "Der er en tråd og %1 nye indlæg", + "there-are-new-topics": "Der er %1 nye indlæg.", + "there-are-new-topics-and-a-new-post": "Der er %1 nye indlæg og et nyt indlæg.", + "there-are-new-topics-and-new-posts": "Der er %1 nye tråde og %2 nye indlæg.", + "there-is-a-new-post": "Der er et nyt indlæg.", + "there-are-new-posts": "Der er %1 nye indlæg.", + "click-here-to-reload": "Klik her for at genindlæse." +} \ No newline at end of file diff --git a/public/language/da/register.json b/public/language/da/register.json new file mode 100644 index 0000000000..b1f69c4245 --- /dev/null +++ b/public/language/da/register.json @@ -0,0 +1,19 @@ +{ + "register": "Tilmeld", + "help.email": "Den email er skjult som standard.", + "help.username_restrictions": "Et unikt brugernavn mellem %1 og %2 karakterer. Andre kan nævne dig med @brugernavn.", + "help.minimum_password_length": "Din adgangskode skal være på mindst %1 karakterer.", + "email_address": "Emailadresse", + "email_address_placeholder": "Indtast emailadresse", + "username": "Brugernavn", + "username_placeholder": "Indtast brugernavn", + "password": "Adgangskode", + "password_placeholder": "Indtast adgangskode", + "confirm_password": "Bekræft adgangskode", + "confirm_password_placeholder": "Bekræft adgangskode", + "register_now_button": "Registrer nu", + "alternative_registration": "Alternativ registrering", + "terms_of_use": "Betingelser for brug", + "agree_to_terms_of_use": "Jeg acceptere betingelserne for brug", + "registration-added-to-queue": "Din registrering er blevet tilføjet til godkendelses køen. Du vil mostage en email når du er blevet accepteret af en administrator." +} \ No newline at end of file diff --git a/public/language/da/reset_password.json b/public/language/da/reset_password.json new file mode 100644 index 0000000000..1feb80736e --- /dev/null +++ b/public/language/da/reset_password.json @@ -0,0 +1,17 @@ +{ + "reset_password": "Nulstil adgangskode", + "update_password": "Opdater adgangskode", + "password_changed.title": "Adgangskode ændret", + "password_changed.message": "

Adgangskode er blevet ændret, log venligt ind igen.", + "wrong_reset_code.title": "Forkert nulstillingskode", + "wrong_reset_code.message": "Nulstillingskoden var forkert. Prøv venligst igen, eller anmod om en ny nultillingskode.", + "new_password": "Ny adgangskode", + "repeat_password": "Bekræft adgangskode", + "enter_email": "Indtast venligst din emailadresse så vi kan sende dig instrukser til at nulstille din konto.", + "enter_email_address": "Indtast emailadresse", + "password_reset_sent": "Adgangskode nulstilling afsendt", + "invalid_email": "Ugyldig emailadresse / Emailadresse findes ikke", + "password_too_short": "Den indtastede adgangskode er for kort, vælg venligt en anden adgangskode.", + "passwords_do_not_match": "De to indtastede adgangskoder er ikke ens.", + "password_expired": "Din adgangskode er udløbet, vælg venligst en ny adgangskode" +} \ No newline at end of file diff --git a/public/language/da/search.json b/public/language/da/search.json new file mode 100644 index 0000000000..d96fd1a4f8 --- /dev/null +++ b/public/language/da/search.json @@ -0,0 +1,40 @@ +{ + "results_matching": "%1 resultat(er) matcher \"%2\", (%3 sekunder)", + "no-matches": "Ingen resultatet fundet", + "advanced-search": "Advanceret søgning", + "in": "I", + "titles": "Titler", + "titles-posts": "Titler og indlæg", + "posted-by": "Skrevet af", + "in-categories": "I katagorierne", + "search-child-categories": "Søg underkategorier", + "reply-count": "Svar antal", + "at-least": "Mindst", + "at-most": "Højst", + "post-time": "Skrevet", + "newer-than": "Nyere end", + "older-than": "Ældre end", + "any-date": "Enhver dato", + "yesterday": "Igår", + "one-week": "En uge", + "two-weeks": "To uger", + "one-month": "En måned", + "three-months": "Tre måneder", + "six-months": "Seks måneder", + "one-year": "Et år", + "sort-by": "Sorter efter", + "last-reply-time": "Sidste svar tid", + "topic-title": "Tråd titel", + "number-of-replies": "Antal svar", + "number-of-views": "Antal visninger", + "topic-start-date": "Tråd starts dato", + "username": "Brugernavn", + "category": "Kategori", + "descending": "I faldende rækkefølge", + "ascending": "I stigende rækkefølge", + "save-preferences": "Gem præferencer", + "clear-preferences": "Slet præferencer", + "search-preferences-saved": "Søgnings præferencer gemt", + "search-preferences-cleared": "Søgnings præferencer slettet", + "show-results-as": "Vis resultater som" +} \ No newline at end of file diff --git a/public/language/da/success.json b/public/language/da/success.json new file mode 100644 index 0000000000..312e34d293 --- /dev/null +++ b/public/language/da/success.json @@ -0,0 +1,6 @@ +{ + "success": "Udført", + "topic-post": "Du har indsendt et indlæg.", + "authentication-successful": "Du blev autentificeret", + "settings-saved": "Indstillinger gemt!" +} \ No newline at end of file diff --git a/public/language/da/tags.json b/public/language/da/tags.json new file mode 100644 index 0000000000..97b3faa145 --- /dev/null +++ b/public/language/da/tags.json @@ -0,0 +1,7 @@ +{ + "no_tag_topics": "Der er ikke indlæg med dette tag.", + "tags": "Tags", + "enter_tags_here": "Indsæt tags her, hver på mellem %1 og %2 karakterer.", + "enter_tags_here_short": "Skriv tags", + "no_tags": "Der er ingen tags endnu." +} \ No newline at end of file diff --git a/public/language/da/topic.json b/public/language/da/topic.json new file mode 100644 index 0000000000..395e3e162e --- /dev/null +++ b/public/language/da/topic.json @@ -0,0 +1,100 @@ +{ + "topic": "Tråd", + "topic_id": "Tråd-id", + "topic_id_placeholder": "Angiv tråd-id", + "no_topics_found": "Ingen tråde fundet", + "no_posts_found": "Ingen indlæg fundet!", + "post_is_deleted": "Dette indlæg er slettet!", + "topic_is_deleted": "Denne tråd er blevet slettet!", + "profile": "Profil", + "posted_by": "Skrevet af %1", + "posted_by_guest": "Skrevet af Gæst", + "chat": "Chat", + "notify_me": "Bliv notificeret ved nye svar i dette emne", + "quote": "Citer", + "reply": "Svar", + "guest-login-reply": "Login for at svare", + "edit": "Rediger", + "delete": "Slet", + "purge": "Udrader", + "restore": "Gendan", + "move": "Flyt", + "fork": "Fork", + "link": "Link", + "share": "Del", + "tools": "Værktøjer", + "flag": "Marker", + "locked": "Låst", + "bookmark_instructions": "Klik her for at returnere til den sidste position eller luk for at kassere.", + "flag_title": "Meld dette indlæg til moderation", + "flag_confirm": "Er du sikker på du ænsker at melde dette indlæg?", + "flag_success": "Dette indlæg er blevet meldt til moderation.", + "deleted_message": "Denne tråd er blevet slettet. Kun brugere med emne behandlings privilegier kan se den.", + "following_topic.message": "Du vil nu modtage notifikationer når nogle skriver et indlæg i dette emne.", + "not_following_topic.message": "Du vil ikke længere modtage notifikationer fra dette emne.", + "login_to_subscribe": "Venligt registrer eller login for at abbonere på dette emne.", + "markAsUnreadForAll.success": "Emnet er market ulæst for alle.", + "watch": "Overvåg", + "unwatch": "Fjern overvågning", + "watch.title": "Bliv notificeret ved nye indlæg i dette emne", + "unwatch.title": "Fjern overvågning af dette emne", + "share_this_post": "Del dette indlæg", + "thread_tools.title": "Emne værktøjer", + "thread_tools.markAsUnreadForAll": "Marker som ulæst", + "thread_tools.pin": "Fastgør tråd", + "thread_tools.unpin": "Frigør tråd", + "thread_tools.lock": "Lås tråd", + "thread_tools.unlock": "Lås tråd op", + "thread_tools.move": "Flyt tråd", + "thread_tools.move_all": "Flyt alt", + "thread_tools.fork": "Fraskil tråd", + "thread_tools.delete": "Slet tråd", + "thread_tools.delete_confirm": "Er du sikker på at du vil slette dette emne?", + "thread_tools.restore": "Gendan tråd", + "thread_tools.restore_confirm": "Er du sikker på at du ønsker at genoprette denne tråd?", + "thread_tools.purge": "Udrader tråd", + "thread_tools.purge_confirm": "Er du sikker på at du vil udrense denne tråd?", + "topic_move_success": "Denne tråd blev flyttet til %1", + "post_delete_confirm": "Er du sikker på at du vil slette dette indlæg?", + "post_restore_confirm": "Er du sikker på at du vil gendanne dette indlæg?", + "post_purge_confirm": "Er du sikker på at du vil udradere dette indlæg?", + "load_categories": "Indlæser kategorier", + "disabled_categories_note": "Deaktiverede kategorier er nedtonede", + "confirm_move": "Flyt", + "confirm_fork": "Fraskil", + "favourite": "Favoriser", + "favourites": "Favoritter", + "favourites.has_no_favourites": "Du har ingen favoritter, favoriser nogle indlæg for at se dem her!", + "loading_more_posts": "Indlæser flere indlæg", + "move_topic": "Flyt tråd", + "move_topics": "Flyt tråde", + "move_post": "Flyt indlæg", + "post_moved": "Indlæg flyttet!", + "fork_topic": "Fraskil tråd", + "topic_will_be_moved_to": "Denne tråd vil blive flyttet til katagorien", + "fork_topic_instruction": "Klik på indlæg du ønsker at fraskille", + "fork_no_pids": "Ingen indlæg valgt", + "fork_success": "Tråden blev fraskilt! Klik her for at gå til den fraskilte tråd.", + "composer.title_placeholder": "Angiv din trådtittel her ...", + "composer.handle_placeholder": "Navn", + "composer.discard": "Fortryd", + "composer.submit": "Send", + "composer.replying_to": "Svare til %1", + "composer.new_topic": "Ny tråd", + "composer.uploading": "uploader...", + "composer.thumb_url_label": "Indsæt en tråd miniature URL", + "composer.thumb_title": "Tilføj en miniature til denne tråd", + "composer.thumb_url_placeholder": "http://example.com/thumb.png", + "composer.thumb_file_label": "Eller upload en fil", + "composer.thumb_remove": "Slet felter", + "composer.drag_and_drop_images": "Træk og slip billeder her", + "more_users_and_guests": "%1 flere bruger(e) og %2 gæst(er)", + "more_users": "%1 flere bruger(e)", + "more_guests": "%1 flere gæst(er)", + "users_and_others": "%1 og %2 andre", + "sort_by": "Sorter efter", + "oldest_to_newest": "Ældste til nyeste", + "newest_to_oldest": "Nyeste til ældste", + "most_votes": "Flest stemmer", + "most_posts": "Flest indlæg" +} \ No newline at end of file diff --git a/public/language/da/unread.json b/public/language/da/unread.json new file mode 100644 index 0000000000..e44b6e42fd --- /dev/null +++ b/public/language/da/unread.json @@ -0,0 +1,10 @@ +{ + "title": "Ulæst", + "no_unread_topics": "Der er ingen ulæste emner.", + "load_more": "Indlæs mere", + "mark_as_read": "Marker som læst", + "selected": "Valgte", + "all": "Alle", + "all_categories": "Alle kategorier", + "topics_marked_as_read.success": "Emner markeret som læst!" +} \ No newline at end of file diff --git a/public/language/da/user.json b/public/language/da/user.json new file mode 100644 index 0000000000..421f00b20e --- /dev/null +++ b/public/language/da/user.json @@ -0,0 +1,88 @@ +{ + "banned": "Banlyst", + "offline": "Offline", + "username": "Brugernavn", + "joindate": "Oprettet", + "postcount": "Antal indlæg", + "email": "Email", + "confirm_email": "Bekræft email", + "ban_account": "Ban Konto", + "ban_account_confirm": "Ønsker du virkelig at banne denne konto?", + "unban_account": "Afban Konto", + "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.

", + "fullname": "Fulde navn", + "website": "Webside", + "location": "Lokation", + "age": "Alder", + "joined": "Oprettet", + "lastonline": "Sidst online", + "profile": "Profil", + "profile_views": "Profil visninger", + "reputation": "Omdømme", + "favourites": "Favoritter", + "watched": "Set", + "followers": "Followers", + "following": "Følger", + "aboutme": "Om mig", + "signature": "Signatur", + "gravatar": "Gravatar", + "birthday": "Fødselsdag", + "chat": "Chat", + "follow": "Følg", + "unfollow": "Følg ikke", + "more": "Mere", + "profile_update_success": "Din profil blev opdateret", + "change_picture": "Skift billede", + "edit": "Rediger", + "uploaded_picture": "Upload billede", + "upload_new_picture": "Upload nyt billede", + "upload_new_picture_from_url": "Upload nyt billede fra URL", + "current_password": "Nuværende kodeord", + "change_password": "Skift kodeord", + "change_password_error": "Ukorrekt kodeord", + "change_password_error_wrong_current": "Nuværende kodeord er ikke korrekt", + "change_password_error_length": "Kodeord er for kort!", + "change_password_error_match": "Passwords matcher ikke!", + "change_password_error_privileges": "Du har ikke rettigheder til at ændre dette password.", + "change_password_success": "Dit password er opdateret!", + "confirm_password": "Bekræft kodeord", + "password": "Kodeord", + "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", + "image_spec": "Du kan kun uploade PNG, JPG eller GIF billeder", + "settings": "Indstillinger", + "show_email": "Vis min emailaddresse", + "show_fullname": "Vis mit fulde navn", + "restrict_chats": "Tillad kun chat beskeder fra brugere jeg følger", + "digest_label": "Abonner på sammendrag", + "digest_description": "Abonner på email opdateringer for detta forum (nye notifikationer og indlæg) efter en bestemt køreplan", + "digest_off": "Slukket", + "digest_daily": "Daglig", + "digest_weekly": "Ugentlig", + "digest_monthly": "Månedlig", + "send_chat_notifications": "Send en email hvis en ny chat besked er modtaget og jeg ikke er online", + "send_post_notifications": "Send en email når der er skrevet svar til indlæg jeg abonnere på", + "settings-require-reload": "Nogle indstillinger kræver en genindlæsning. Klik her for at genindlæse siden.", + "has_no_follower": "Denne bruger har ingen følgere :(", + "follows_no_one": "Denne bruger følger ikke nogen :(", + "has_no_posts": "Denne bruger har ikke skrevet noget endnu.", + "has_no_topics": "Denne bruger har ikke skrævet nogle tråde endnu.", + "has_no_watched_topics": "Denne bruger har ikke fulgt nogle tråde endnu.", + "email_hidden": "Email Skjult", + "hidden": "skjult", + "paginate_description": "Sideinddel emner og indlæg istedet for uendeligt rul", + "topics_per_page": "Emner per side", + "posts_per_page": "Indlæg per side", + "notification_sounds": "Afspil en lyd når du modtager en notifikation", + "browsing": "Gennemsenings indstillinger", + "open_links_in_new_tab": "Åben udgående link i en ny tab", + "enable_topic_searching": "Slå In-Topic søgning til", + "topic_search_help": "Hvis slået til, så vil in-topic søgning overskrive browserens almindelige søge function og tillade dig at søge hele emnet, istedet for kun det der er vist på skærmen", + "follow_topics_you_reply_to": "Følg emner du har skrevet indlæg i", + "follow_topics_you_create": "Følg emner du opretter", + "grouptitle": "Vælg gruppe titlen du gerne vil fremvise", + "no-group-title": "Ingen gruppe titel" +} \ No newline at end of file diff --git a/public/language/da/users.json b/public/language/da/users.json new file mode 100644 index 0000000000..9facf52cea --- /dev/null +++ b/public/language/da/users.json @@ -0,0 +1,21 @@ +{ + "latest_users": "Seneste brugere", + "top_posters": "Top Postere", + "most_reputation": "Mest Omdømme", + "search": "Søg", + "enter_username": "Indtast brugernavn for at søge", + "load_more": "Indlæs mere", + "users-found-search-took": "%1 bruger(e) fundet! Søgning tog %2 sekunder.", + "filter-by": "Filtre Efter", + "online-only": "Kun online", + "picture-only": "Kun billeder", + "invite": "Invitér", + "invitation-email-sent": "En invitations email er blevet sendt til %1", + "user_list": "Bruger Liste", + "recent_topics": "Seneste Tråde", + "popular_topics": "Populærer Tråde", + "unread_topics": "Ulæste Tråde", + "categories": "Kategorier", + "tags": "Tags", + "map": "Kort" +} \ No newline at end of file diff --git a/public/language/de/email.json b/public/language/de/email.json index 62c835cefd..923e3000e7 100644 --- a/public/language/de/email.json +++ b/public/language/de/email.json @@ -1,11 +1,15 @@ { "password-reset-requested": "Zurücksetzung des Passworts beantragt - %1!", "welcome-to": "Willkommen bei %1", + "invite": "Einladung von %1", "greeting_no_name": "Hallo", "greeting_with_name": "Hallo %1", "welcome.text1": "Vielen Dank für die Registrierung bei %1!", "welcome.text2": "Um dein Konto vollständig zu aktivieren, müssen wir überprüfen, ob du Besitzer der E-Mail-Adresse bist, mit der du dich registriert hast.", + "welcome.text3": "Ein Administrator hat deine Registration aktzeptiert. Du kannst dich jetzt mit deinem Benutzernamen/Passwort einloggen.", "welcome.cta": "Klicke hier, um deine E-Mail-Adresse zu bestätigen.", + "invitation.text1": "%1 hat dich eingeladen %2 beizutreten", + "invitation.ctr": "Klicke hier, um ein Konto zu erstellen.", "reset.text1": "Wir haben eine Anfrage auf Zurücksetzung deines Passworts erhalten, wahrscheinlich, weil du es vergessen hast. Falls dies nicht der Fall ist, ignoriere bitte diese E-Mail.", "reset.text2": "Klicke bitte auf den folgenden Link, um mit der Zurücksetzung deines Passworts fortzufahren:", "reset.cta": "Klicke hier, um dein Passwort zurückzusetzen", diff --git a/public/language/de/error.json b/public/language/de/error.json index 61cb1fa3f9..210d0067c9 100644 --- a/public/language/de/error.json +++ b/public/language/de/error.json @@ -2,7 +2,7 @@ "invalid-data": "Daten ungültig", "not-logged-in": "Du bist nicht angemeldet.", "account-locked": "Dein Account wurde vorübergehend gesperrt.", - "search-requires-login": "Searching requires an account - please login or register.", + "search-requires-login": "Die Suche erfordert ein Konto, bitte einloggen oder registrieren.", "invalid-cid": "Ungültige Kategorie-ID", "invalid-tid": "Ungültige Themen-ID", "invalid-pid": "Ungültige Beitrags-ID", @@ -21,11 +21,11 @@ "email-not-confirmed-chat": "Deine E-Mail wurde noch nicht bestätigt. Bitte klicke hier, um deine E-Mail zu bestätigen.", "no-email-to-confirm": "Dieses Forum setzt E-Mail-Bestätigung voraus, bitte klick hier um eine E-Mail-Adresse einzugeben", "email-confirm-failed": "Wir konnten deine E-Mail-Adresse nicht bestätigen, bitte versuch es später noch einmal", - "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", + "confirm-email-already-sent": "Bestätigungsmail wurde verschickt, bitte warten %1 Minute(n) warten um eine weitere zu verschicken.", "username-too-short": "Benutzername ist zu kurz", "username-too-long": "Der Benutzername ist zu lang", "user-banned": "Der Benutzer ist gesperrt", - "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", + "user-too-new": "Entschuldigung, Sie müssen %1 Sekunde(n) warten, bevor Sie ihren ersten Beitrag schreiben können.", "no-category": "Die Kategorie existiert nicht", "no-topic": "Das Thema existiert nicht", "no-post": "Der Beitrag existiert nicht", @@ -36,21 +36,22 @@ "no-emailers-configured": "Es wurde keine E-Mail-Plugins geladen, weshalb eine Test-E-Mail nicht gesendet werden konnte.", "category-disabled": "Kategorie ist deaktiviert", "topic-locked": "Thema ist gesperrt", - "post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting", + "post-edit-duration-expired": "Entschuldigung, Sie dürfen Beiträge nur %1 Sekunde(n) nach dem veröffentlichen editieren.", "still-uploading": "Bitte warte bis der Vorgang abgeschlossen ist.", - "content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).", - "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).", - "title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).", - "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).", - "too-many-posts": "You can only post once every %1 second(s) - please wait before posting again", - "too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again", - "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)", - "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)", - "file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file", + "content-too-short": "Bitte schreiben Sie einen längeren Beitrag. Beiträge sollten mindestens %1 Zeichen enthalten.", + "content-too-long": "Bitte schreiben Sie einen kürzeren Beitrag. Beiträge können nicht länger als %1 Zeichen sein.", + "title-too-short": "Bitte geben Sie einen längeren Titel ein. Ein Titel muss mindestens %1 Zeichen enthalten.", + "title-too-long": "Bitten geben Sie einen kürzeren Titel ein. Ein Titel darf nicht mehr als %1 Zeichen enthalten.", + "too-many-posts": "Sie können nur einen Beitrag innerhalb von %1 Sekunden erstellen - Bitte warten Sie bevor Sie erneut einen Beitrag erstellen.", + "too-many-posts-newbie": "Als neuer Benutzer können Sie nur einen Beitrag innerhalb von %1 Sekunden erstellen - Bitte warten Sie bevor Sie erneut einen Beitrag erstellen.", + "tag-too-short": "Bitte geben Sie ein längeres Schlagwort ein. Tags sollten mindestens %1 Zeichen enthalten.", + "tag-too-long": "Bitte geben Sie ein kürzeres Schlagwort ein. Tags können nicht länger als %1 Zeichen sein.", + "file-too-big": "Die maximale Dateigröße ist %1 kB, bitte laden Sie eine kleinere Datei hoch.", "cant-vote-self-post": "Du kannst deinen eigenen Beitrag nicht bewerten", "already-favourited": "Dieser Beitrag ist bereits in deinen Favoriten enthalten", "already-unfavourited": "Du hast diesen Beitrag bereits aus deinen Favoriten entfernt", "cant-ban-other-admins": "Du kannst andere Administratoren nicht sperren!", + "cant-remove-last-admin": "Du bist der einzige Administrator. Füge zuerst einen anderen Administrator hinzu, bevor du dich selbst als Administrator entfernst", "invalid-image-type": "Falsche Bildart. Erlaubte Arten sind: %1", "invalid-image-extension": "Ungültige Dateinamenerweiterung", "invalid-file-type": "Ungültiger Dateityp. Erlaubte Typen sind: %1", @@ -59,6 +60,8 @@ "group-name-change-not-allowed": "Du kannst den Namen der Gruppe nicht ändern", "group-already-member": "Du bist bereits Teil dieser Gruppe", "group-needs-owner": "Diese Gruppe muss mindestens einen Besitzer vorweisen", + "group-already-invited": "Dieser Benutzer wurde bereits eingeladen", + "group-already-requested": "Deine Mitgliedsanfrage wurde bereits eingereicht", "post-already-deleted": "Dieser Beitrag ist bereits gelöscht worden", "post-already-restored": "Dieser Beitrag ist bereits wiederhergestellt worden", "topic-already-deleted": "Dieses Thema ist bereits gelöscht worden", @@ -67,8 +70,8 @@ "topic-thumbnails-are-disabled": "Vorschaubilder für Themen sind deaktiviert", "invalid-file": "Datei ungültig", "uploads-are-disabled": "Uploads sind deaktiviert", - "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", - "about-me-too-long": "Sorry, your about me cannot be longer than %1 character(s).", + "signature-too-long": "Entschuldigung, Ihre Signatur kann nicht länger als %1 Zeichen sein.", + "about-me-too-long": "Entschuldigung, Ihr \"über mich\" kann nicht länger als %1 Zeichen sein.", "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.", @@ -76,6 +79,7 @@ "downvoting-disabled": "Downvotes sind deaktiviert.", "not-enough-reputation-to-downvote": "Deine Reputation ist zu niedrig, um diesen Beitrag negativ zu bewerten.", "not-enough-reputation-to-flag": "Deine Reputation ist nicht gut genug, um diesen Beitrag zu melden", + "already-flagged": "Du hast diesen Beitrag bereits gemeldet", "reload-failed": "Es ist ein Problem während des Reloads von NodeBB aufgetreten: \"%1\". NodeBB wird weiterhin clientseitige Assets bereitstellen, allerdings solltest du das, was du vor dem Reload gemacht hast, rückgängig machen.", "registration-error": "Registrierungsfehler", "parse-error": "Beim auswerten der Serverantwort ist etwas schiefgegangen", diff --git a/public/language/de/global.json b/public/language/de/global.json index 8da4b23a06..ace27161a1 100644 --- a/public/language/de/global.json +++ b/public/language/de/global.json @@ -50,6 +50,7 @@ "views": "Aufrufe", "reputation": "Reputation", "read_more": "weiterlesen", + "more": "Mehr", "posted_ago_by_guest": "%1 von einem Gast geschrieben", "posted_ago_by": "%1 von %2 geschrieben", "posted_ago": "%1 geschrieben", diff --git a/public/language/de/groups.json b/public/language/de/groups.json index c2328959aa..a99d4ebc36 100644 --- a/public/language/de/groups.json +++ b/public/language/de/groups.json @@ -6,6 +6,12 @@ "no_groups_found": "Es sind keine Gruppen vorhanden", "pending.accept": "Annehmen", "pending.reject": "Abweisen", + "pending.accept_all": "Alle annehmen", + "pending.reject_all": "Alle ablehnen", + "pending.none": "Es sind zur Zeit keine unvearbeiteten Mitglieder vorhanden", + "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", "cover-instructions": "Foto auf eine Position bewegen, und Speichern drücken", "cover-change": "Ändern", "cover-save": "Speichern", @@ -13,6 +19,7 @@ "details.title": "Gruppendetails", "details.members": "Mitgliederliste", "details.pending": "Mitglieder in Schwebe", + "details.invited": "Eingeladene Mitglieder", "details.has_no_posts": "Die Mitglieder dieser Gruppe haben keine Beiträge verfasst.", "details.latest_posts": "Neueste Beiträge", "details.private": "Privat", diff --git a/public/language/de/notifications.json b/public/language/de/notifications.json index f500eefe5b..0dcc1b825d 100644 --- a/public/language/de/notifications.json +++ b/public/language/de/notifications.json @@ -20,6 +20,7 @@ "user_posted_topic": "%1 hat ein neues Thema erstellt: %2", "user_mentioned_you_in": "%1 erwähnte dich in %2", "user_started_following_you": "%1 folgt dir jetzt.", + "new_register": "%1 hat eine Registrationsanfrage geschickt.", "email-confirmed": "E-Mail bestätigt", "email-confirmed-message": "Vielen Dank für Ihre E-Mail-Validierung. Ihr Konto ist nun vollständig aktiviert.", "email-confirm-error-message": "Es gab ein Problem bei der Validierung Ihrer E-Mail-Adresse. Möglicherweise ist der Code ungültig oder abgelaufen.", diff --git a/public/language/de/register.json b/public/language/de/register.json index 7655601d98..9314539d46 100644 --- a/public/language/de/register.json +++ b/public/language/de/register.json @@ -14,5 +14,6 @@ "register_now_button": "Jetzt registrieren", "alternative_registration": "Alternative Registrierung", "terms_of_use": "Nutzungsbedingungen", - "agree_to_terms_of_use": "Ich stimme den Nutzungsbedingungen zu" + "agree_to_terms_of_use": "Ich stimme den Nutzungsbedingungen zu", + "registration-added-to-queue": "Deine Registration wurde abgeschickt. Du wirst eine E-Mail erhalten, sobald sie von einem Administrator akzeptiert wird." } \ No newline at end of file diff --git a/public/language/de/topic.json b/public/language/de/topic.json index 43d59ecb73..5334700f39 100644 --- a/public/language/de/topic.json +++ b/public/language/de/topic.json @@ -1,37 +1,37 @@ { "topic": "Thema", - "topic_id": "Topic ID", - "topic_id_placeholder": "Topic ID eingeben", - "no_topics_found": "Keine passenden Themen gefunden.", + "topic_id": "Themen-ID", + "topic_id_placeholder": "Themen-ID eingeben", + "no_topics_found": "Keine passenden Themen gefunden!", "no_posts_found": "Keine Beiträge gefunden!", "post_is_deleted": "Dieser Beitrag wurde gelöscht!", - "topic_is_deleted": "This topic is deleted!", + "topic_is_deleted": "Dieses Thema wurde gelöscht!", "profile": "Profil", - "posted_by": "Geschrieben von %1", + "posted_by": "Verfasst von %1", "posted_by_guest": "Verfasst von einem Gast", "chat": "Chat", "notify_me": "Erhalte eine Benachrichtigung bei neuen Antworten zu diesem Thema.", - "quote": "zitieren", - "reply": "antworten", + "quote": "Zitieren", + "reply": "Antworten", "guest-login-reply": "Anmelden zum Antworten", - "edit": "bearbeiten", - "delete": "löschen", - "purge": "säubern", + "edit": "Bearbeiten", + "delete": "Löschen", + "purge": "Bereinigen", "restore": "Wiederherstellen", - "move": "verschieben", + "move": "Verschieben", "fork": "Aufspalten", "link": "Link", "share": "Teilen", - "tools": "Tools", + "tools": "Werkzeuge", "flag": "Markieren", "locked": "Gesperrt", - "bookmark_instructions": "Klicke hier um zur letzten Position zurückzukehren oder schließe zum Abbrechen.", + "bookmark_instructions": "Klicke hier, um zur letzten Position zurückzukehren oder schließe zum Abbrechen.", "flag_title": "Diesen Beitrag zur Moderation markieren", "flag_confirm": "Sind Sie sicher, dass Sie diesen Post markieren möchten?", "flag_success": "Dieser Beitrag wurde erfolgreich für die Moderation markiert.", "deleted_message": "Dieses Thema wurde gelöscht. Nur Nutzer mit entsprechenden Rechten können es sehen.", "following_topic.message": "Du erhälst nun eine Benachrichtigung, wenn jemand einen Beitrag zu diesem Thema verfasst.", - "not_following_topic.message": "Du erhälst keine weiteren Benachrichtigungen zu diesem Thema.", + "not_following_topic.message": "Du erhälst keine weiteren Benachrichtigungen zu diesem Thema mehr.", "login_to_subscribe": "Bitte registrieren oder einloggen um dieses Thema zu abonnieren", "markAsUnreadForAll.success": "Thema für Alle als ungelesen markiert.", "watch": "Beobachten", @@ -39,10 +39,10 @@ "watch.title": "Bei neuen Antworten benachrichtigen", "unwatch.title": "Dieses Thema nicht mehr beobachten", "share_this_post": "Diesen Beitrag teilen", - "thread_tools.title": "Themen-Tools", + "thread_tools.title": "Themen-Werkzeuge", "thread_tools.markAsUnreadForAll": "Als ungelesen markieren", - "thread_tools.pin": "Thema anpinnen", - "thread_tools.unpin": "Thema nicht mehr anpinnen", + "thread_tools.pin": "Thema anheften", + "thread_tools.unpin": "Thema nicht mehr anheften", "thread_tools.lock": "Thema schließen", "thread_tools.unlock": "Thema öffnen", "thread_tools.move": "Thema verschieben", @@ -53,11 +53,11 @@ "thread_tools.restore": "Thema wiederherstellen", "thread_tools.restore_confirm": "Bist du sicher, dass du dieses Thema wiederherstellen möchtest?", "thread_tools.purge": "Thema säubern", - "thread_tools.purge_confirm": "Bist du sicher, dass du dieses Thema säubern möchtest?", - "topic_move_success": "Thema wurde erfolgreich zu %1 verschoben.", + "thread_tools.purge_confirm": "Bist du sicher, dass du dieses Thema bereinigen möchtest?", + "topic_move_success": "Thema wurde erfolgreich nach %1 verschoben.", "post_delete_confirm": "Sind Sie sicher, dass Sie diesen Beitrag löschen möchten?", "post_restore_confirm": "Sind Sie sicher, dass Sie diesen Beitrag wiederherstellen möchten?", - "post_purge_confirm": "Sind Sie sicher, das Sie diesen Beitrag säubern möchten?", + "post_purge_confirm": "Sind Sie sicher, das Sie diesen Beitrag bereinigen möchten?", "load_categories": "Kategorien laden", "disabled_categories_note": "Deaktivierte Kategorien sind ausgegraut.", "confirm_move": "Verschieben", @@ -72,29 +72,29 @@ "post_moved": "Beitrag wurde verschoben!", "fork_topic": "Thema aufspalten", "topic_will_be_moved_to": "Dieses Thema wird verschoben nach", - "fork_topic_instruction": "Klicke auf die Beiträge, die du aufspalten willst", + "fork_topic_instruction": "Klicke auf die Beiträge, die aufgespaltet werden sollen", "fork_no_pids": "Keine Beiträge ausgewählt!", - "fork_success": "Thema erfolgreich abgespalten! Klicke hier, um zum abgespalteten Thema zu gelangen.", + "fork_success": "Thema erfolgreich aufgespalten! Klicke hier, um zum aufgespalteten Thema zu gelangen.", "composer.title_placeholder": "Hier den Titel des Themas eingeben...", "composer.handle_placeholder": "Name", "composer.discard": "Verwerfen", "composer.submit": "Absenden", "composer.replying_to": "Antworte auf %1", "composer.new_topic": "Neues Thema", - "composer.uploading": "Upload läuft...", + "composer.uploading": "Lade hoch...", "composer.thumb_url_label": "Vorschaubild-URL hier einfügen", "composer.thumb_title": "Vorschaubild zu diesem Thema hinzufügen", "composer.thumb_url_placeholder": "http://example.com/thumb.png", "composer.thumb_file_label": "Oder eine Datei hochladen", "composer.thumb_remove": "Felder leeren", - "composer.drag_and_drop_images": "Bilder hier reinziehen", + "composer.drag_and_drop_images": "Bilder hierher ziehen", "more_users_and_guests": "%1 weitere(r) Nutzer und %2 Gäste", "more_users": "%1 weitere(r) Nutzer", "more_guests": "%1 weitere Gäste", "users_and_others": "%1 und %2 andere", "sort_by": "Sortieren nach", "oldest_to_newest": "Älteste zuerst", - "newest_to_oldest": "Neuster zuerst", + "newest_to_oldest": "Neuste zuerst", "most_votes": "Die meisten Stimmen", "most_posts": "Die meisten Beiträge" } \ No newline at end of file diff --git a/public/language/de/unread.json b/public/language/de/unread.json index 83c336cf72..b9796b7c41 100644 --- a/public/language/de/unread.json +++ b/public/language/de/unread.json @@ -5,5 +5,6 @@ "mark_as_read": "Als gelesen markieren", "selected": "Ausgewählte", "all": "Alle", + "all_categories": "Alle Kategorien", "topics_marked_as_read.success": "Themen als gelesen markiert!" } \ No newline at end of file diff --git a/public/language/de/user.json b/public/language/de/user.json index d1677c7609..8c96e203d3 100644 --- a/public/language/de/user.json +++ b/public/language/de/user.json @@ -6,8 +6,12 @@ "postcount": "Beiträge", "email": "E-Mail", "confirm_email": "E-Mail bestätigen", + "ban_account": "Konto sperren", + "ban_account_confirm": "Sind Sie sicher, dass Sie diesen Benutzer sperren möchten?", + "unban_account": "Konto entsperren", "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

", "fullname": "Kompletter Name", "website": "Homepage", "location": "Wohnort", @@ -21,7 +25,7 @@ "watched": "Beobachtet", "followers": "Folger", "following": "Folgt", - "aboutme": "About me", + "aboutme": "Über mich", "signature": "Signatur", "gravatar": "Gravatar", "birthday": "Geburtstag", @@ -66,7 +70,7 @@ "follows_no_one": "Dieser User folgt noch niemandem :(", "has_no_posts": "Dieser Nutzer hat noch nichts gepostet.", "has_no_topics": "Dieser Nutzer hat noch keine Themen gepostet.", - "has_no_watched_topics": "Diese Person beobachtet keine Themen", + "has_no_watched_topics": "Dieser Nutzer beobachtet keine Themen.", "email_hidden": "E-Mail Adresse versteckt", "hidden": "versteckt", "paginate_description": "Themen und Beiträge in Seiten aufteilen, anstelle unendlich zu scrollen", diff --git a/public/language/de/users.json b/public/language/de/users.json index 99b17f79f6..ebc7f42727 100644 --- a/public/language/de/users.json +++ b/public/language/de/users.json @@ -8,5 +8,14 @@ "users-found-search-took": "%1 Benutzer gefunden! Die Suche dauerte %2 ms.", "filter-by": "Filtern nach", "online-only": "Nur Online", - "picture-only": "Nur mit Bildern" + "picture-only": "Nur mit Bildern", + "invite": "Einladen", + "invitation-email-sent": "Eine Einladungsemail wurde an %1 verschickt", + "user_list": "Nutzerliste", + "recent_topics": "Neueste Themen", + "popular_topics": "Beliebte Themen", + "unread_topics": "Ungelesen Themen", + "categories": "Kategorien", + "tags": "Stichwörter", + "map": "Karte" } \ No newline at end of file diff --git a/public/language/el/email.json b/public/language/el/email.json index c4a52f1ffa..eefd729f7a 100644 --- a/public/language/el/email.json +++ b/public/language/el/email.json @@ -1,11 +1,15 @@ { "password-reset-requested": "Ζητήθηκε Επαναφορά Κωδικου - %1!", "welcome-to": "Καλωσόρισες στο %1", + "invite": "Invitation from %1", "greeting_no_name": "Γειά σου", "greeting_with_name": "Γειά σου %1", "welcome.text1": "Ευχαριστούμε που γράφτηκες στο %1!", "welcome.text2": "Για να ενεργοποιήσεις πλήρως τον λογαριασμό σου, πρέπει να επιβεβαιώσουμε πως η διεύθυνση email με την οποια γράφτηκες σου ανήκει.", + "welcome.text3": "An administrator has accepted your registration application. You can login with your username/password now.", "welcome.cta": "Κάνε κλικ εδώ για να επιβεβαιώσεις την διεύθυνσή σου", + "invitation.text1": "%1 has invited you to join %2", + "invitation.ctr": "Click here to create your account.", "reset.text1": "Λάβαμε ένα αίτημα για επαναφορά του κωδικού σου, πιθανότατα γιατί τον ξέχασες. Αν δεν έκανες εσύ αυτό το αίτημα, αγνόησε αυτό το email.", "reset.text2": "Για να κάνεις την επαναφορά του κωδικού σου, παρακαλώ πάτα στο παρακάτω σύνδεσμο:", "reset.cta": "Κάνε κλικ εδώ για να επαναφέρεις τον κωδικό σου", diff --git a/public/language/el/error.json b/public/language/el/error.json index 2cb627fbc4..bb7745dc46 100644 --- a/public/language/el/error.json +++ b/public/language/el/error.json @@ -51,6 +51,7 @@ "already-favourited": "You have already favourited this post", "already-unfavourited": "You have already unfavourited this post", "cant-ban-other-admins": "Δεν μπορείς να αποκλείσεις άλλους διαχειριστές!", + "cant-remove-last-admin": "You are the only administrator. Add another user as an administrator before removing yourself as admin", "invalid-image-type": "Invalid image type. Allowed types are: %1", "invalid-image-extension": "Invalid image extension", "invalid-file-type": "Invalid file type. Allowed types are: %1", @@ -59,6 +60,8 @@ "group-name-change-not-allowed": "Αλλαγή του ονόματος της ομάδας δεν επιτρέπεται", "group-already-member": "You are already part of this group", "group-needs-owner": "This group requires at least one owner", + "group-already-invited": "This user has already been invited", + "group-already-requested": "Your membership request has already been submitted", "post-already-deleted": "This post has already been deleted", "post-already-restored": "This post has already been restored", "topic-already-deleted": "This topic has already been deleted", @@ -76,6 +79,7 @@ "downvoting-disabled": "Η καταψήφιση έχει απενεργοποιηθεί", "not-enough-reputation-to-downvote": "Δεν έχεις αρκετή φήμη για να καταψηφίσεις αυτή την δημοσίευση", "not-enough-reputation-to-flag": "You do not have enough reputation to flag this post", + "already-flagged": "You have already flagged this post", "reload-failed": "Το NodeBB συνάντησε ένα πρόβλημα καθώς γινόταν η ανανέωση: \"%1\". Το NodeBB θα συνεχίσει να προσφέρει τα στοιχεία του χρήστη, αν και θα ήταν καλή ιδέα να επαναφέρεις ότι έκανες πριν την ανανέωση.", "registration-error": "Registration Error", "parse-error": "Something went wrong while parsing server response", diff --git a/public/language/el/global.json b/public/language/el/global.json index e7ce30b6a7..4c7e46eb74 100644 --- a/public/language/el/global.json +++ b/public/language/el/global.json @@ -50,6 +50,7 @@ "views": "Εμφανίσεις", "reputation": "Φήμη", "read_more": "διάβασε περισσότερα", + "more": "More", "posted_ago_by_guest": "δημοσιεύτηκε πριν από %1 από Επισκέπτη", "posted_ago_by": "δημοσιεύτηκε πριν από %1 από τον/την %2", "posted_ago": "δημοσιεύτηκε πρίν από %1", diff --git a/public/language/el/groups.json b/public/language/el/groups.json index 28c9fbe159..a8496beb01 100644 --- a/public/language/el/groups.json +++ b/public/language/el/groups.json @@ -6,6 +6,12 @@ "no_groups_found": "There are no groups to see", "pending.accept": "Accept", "pending.reject": "Reject", + "pending.accept_all": "Accept All", + "pending.reject_all": "Reject All", + "pending.none": "There are no pending members at this time", + "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", "cover-instructions": "Drag and Drop a photo, drag to position, and hit Save", "cover-change": "Change", "cover-save": "Save", @@ -13,6 +19,7 @@ "details.title": "Λεπτομέρειες Ομάδας", "details.members": "Λίστα Μελών", "details.pending": "Pending Members", + "details.invited": "Invited Members", "details.has_no_posts": "Τα μέλη αυτής της ομάδας δεν έχουν δημοσιεύσει τίποτα.", "details.latest_posts": "Τελευταίες δημοσιεύσεις.", "details.private": "Private", diff --git a/public/language/el/notifications.json b/public/language/el/notifications.json index 68fe73ee7f..7b21d83f53 100644 --- a/public/language/el/notifications.json +++ b/public/language/el/notifications.json @@ -20,6 +20,7 @@ "user_posted_topic": "%1 has posted a new topic: %2", "user_mentioned_you_in": "Ο/Η %1 σε ανέφερε στο %2", "user_started_following_you": "Ο/Η %1 σε ακολουθεί.", + "new_register": "%1 sent a registration request.", "email-confirmed": "Το Εmail Επιβεβαιώθηκε", "email-confirmed-message": "Ευχαριστούμε που επιβεβαίωσες το email σου. Ο λογαριασμός σου είναι πλέον πλήρως ενεργοποιημένος.", "email-confirm-error-message": "Υπήρξε κάποιο πρόβλημα με την επιβεβαίωση της διεύθυνσής email σου. Ίσως ο κώδικας να είναι άκυρος ή να έχει λήξει.", diff --git a/public/language/el/register.json b/public/language/el/register.json index 15a6661368..875a507d52 100644 --- a/public/language/el/register.json +++ b/public/language/el/register.json @@ -14,5 +14,6 @@ "register_now_button": "Εγγραφή Τώρα", "alternative_registration": "Εναλλακτική Εγγραφή", "terms_of_use": "Όροι Χρήσης", - "agree_to_terms_of_use": "Συμφωνώ με τους Όρους Χρήσης" + "agree_to_terms_of_use": "Συμφωνώ με τους Όρους Χρήσης", + "registration-added-to-queue": "Your registration has been added to the approval queue. You will receive an email when it is accepted by an administrator." } \ No newline at end of file diff --git a/public/language/el/unread.json b/public/language/el/unread.json index e4d69991a3..98aea93e27 100644 --- a/public/language/el/unread.json +++ b/public/language/el/unread.json @@ -5,5 +5,6 @@ "mark_as_read": "Σημείωση ώς Αναγνωσμένα", "selected": "Επιλεγμένα", "all": "Όλα", + "all_categories": "All categories", "topics_marked_as_read.success": "Τα θέματα σημειώθηκαν ως αναγνωσμένα!" } \ No newline at end of file diff --git a/public/language/el/user.json b/public/language/el/user.json index 9524d5a529..c58b3dfb4a 100644 --- a/public/language/el/user.json +++ b/public/language/el/user.json @@ -6,8 +6,12 @@ "postcount": "Post Count", "email": "Emai", "confirm_email": "Επιβεβαίωση Email", + "ban_account": "Ban Account", + "ban_account_confirm": "Do you really want to ban this user?", + "unban_account": "Unban Account", "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

", "fullname": "Πλήρες Όνομα", "website": "Ιστοσελίδα", "location": "Τοποθεσία", @@ -64,9 +68,9 @@ "settings-require-reload": "Some setting changes require a reload. Click here to reload the page.", "has_no_follower": "Αυτός ο χρήστης δεν έχει κανέναν ακόλουθο :(", "follows_no_one": "Αυτός ο χρήστης δεν ακολουθεί κανέναν :(", - "has_no_posts": "Αυτός ο χρήστης δεν έχει δημοσιεύσει τίποτα ακόμη.", - "has_no_topics": "Αυτός ο χρήστης δεν έχει δημοσιεύσει κανένα θέμα ακόμη.", - "has_no_watched_topics": "This user didn't watch any topics yet.", + "has_no_posts": "This user hasn't posted anything yet.", + "has_no_topics": "This user hasn't posted any topics yet.", + "has_no_watched_topics": "This user hasn't watched any topics yet.", "email_hidden": "Κρυμμένο Emai", "hidden": "κρυμμένο", "paginate_description": "Paginate topics and posts instead of using infinite scroll", diff --git a/public/language/el/users.json b/public/language/el/users.json index aae33e825c..3eda51f303 100644 --- a/public/language/el/users.json +++ b/public/language/el/users.json @@ -8,5 +8,14 @@ "users-found-search-took": "%1 user(s) found! Search took %2 seconds.", "filter-by": "Filter By", "online-only": "Online only", - "picture-only": "Picture only" + "picture-only": "Picture only", + "invite": "Invite", + "invitation-email-sent": "An invitation email has been sent to %1", + "user_list": "User List", + "recent_topics": "Recent Topics", + "popular_topics": "Popular Topics", + "unread_topics": "Unread Topics", + "categories": "Categories", + "tags": "Tags", + "map": "Map" } \ No newline at end of file diff --git a/public/language/en@pirate/email.json b/public/language/en@pirate/email.json index f290435e75..d8114d0231 100644 --- a/public/language/en@pirate/email.json +++ b/public/language/en@pirate/email.json @@ -1,11 +1,15 @@ { "password-reset-requested": "Password Reset Requested - %1!", "welcome-to": "Welcome to %1", + "invite": "Invitation from %1", "greeting_no_name": "Hello", "greeting_with_name": "Hello %1", "welcome.text1": "Thank you for registering with %1!", "welcome.text2": "To fully activate your account, we need to verify that you own the email address you registered with.", + "welcome.text3": "An administrator has accepted your registration application. You can login with your username/password now.", "welcome.cta": "Click here to confirm your email address", + "invitation.text1": "%1 has invited you to join %2", + "invitation.ctr": "Click here to create your account.", "reset.text1": "We received a request to reset your password, possibly because you have forgotten it. If this is not the case, please ignore this email.", "reset.text2": "To continue with the password reset, please click on the following link:", "reset.cta": "Click here to reset your password", diff --git a/public/language/en@pirate/error.json b/public/language/en@pirate/error.json index ac2457e250..56f9f626ff 100644 --- a/public/language/en@pirate/error.json +++ b/public/language/en@pirate/error.json @@ -51,6 +51,7 @@ "already-favourited": "You have already favourited this post", "already-unfavourited": "You have already unfavourited this post", "cant-ban-other-admins": "You can't ban other admins!", + "cant-remove-last-admin": "You are the only administrator. Add another user as an administrator before removing yourself as admin", "invalid-image-type": "Invalid image type. Allowed types are: %1", "invalid-image-extension": "Invalid image extension", "invalid-file-type": "Invalid file type. Allowed types are: %1", @@ -59,6 +60,8 @@ "group-name-change-not-allowed": "Group name change not allowed", "group-already-member": "You are already part of this group", "group-needs-owner": "This group requires at least one owner", + "group-already-invited": "This user has already been invited", + "group-already-requested": "Your membership request has already been submitted", "post-already-deleted": "This post has already been deleted", "post-already-restored": "This post has already been restored", "topic-already-deleted": "This topic has already been deleted", @@ -76,6 +79,7 @@ "downvoting-disabled": "Downvoting is disabled", "not-enough-reputation-to-downvote": "You do not have enough reputation to downvote this post", "not-enough-reputation-to-flag": "You do not have enough reputation to flag this post", + "already-flagged": "You have already flagged this post", "reload-failed": "NodeBB encountered a problem while reloading: \"%1\". NodeBB will continue to serve the existing client-side assets, although you should undo what you did just prior to reloading.", "registration-error": "Registration Error", "parse-error": "Something went wrong while parsing server response", diff --git a/public/language/en@pirate/global.json b/public/language/en@pirate/global.json index f331b79472..9c839710b0 100644 --- a/public/language/en@pirate/global.json +++ b/public/language/en@pirate/global.json @@ -50,6 +50,7 @@ "views": "Views", "reputation": "Reputation", "read_more": "read more", + "more": "More", "posted_ago_by_guest": "posted %1 by Guest", "posted_ago_by": "posted %1 by %2", "posted_ago": "posted %1", diff --git a/public/language/en@pirate/groups.json b/public/language/en@pirate/groups.json index 5e301aa46a..c9ac036475 100644 --- a/public/language/en@pirate/groups.json +++ b/public/language/en@pirate/groups.json @@ -6,6 +6,12 @@ "no_groups_found": "There are no groups to see", "pending.accept": "Accept", "pending.reject": "Reject", + "pending.accept_all": "Accept All", + "pending.reject_all": "Reject All", + "pending.none": "There are no pending members at this time", + "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", "cover-instructions": "Drag and Drop a photo, drag to position, and hit Save", "cover-change": "Change", "cover-save": "Save", @@ -13,6 +19,7 @@ "details.title": "Group Details", "details.members": "Member List", "details.pending": "Pending Members", + "details.invited": "Invited Members", "details.has_no_posts": "This group's members have not made any posts.", "details.latest_posts": "Latest Posts", "details.private": "Private", diff --git a/public/language/en@pirate/notifications.json b/public/language/en@pirate/notifications.json index 3eb4fba74d..fd89bbf6f5 100644 --- a/public/language/en@pirate/notifications.json +++ b/public/language/en@pirate/notifications.json @@ -20,6 +20,7 @@ "user_posted_topic": "%1 has posted a new topic: %2", "user_mentioned_you_in": "%1 mentioned you in %2", "user_started_following_you": "%1 started following you.", + "new_register": "%1 sent a registration request.", "email-confirmed": "Email Confirmed", "email-confirmed-message": "Thank you for validating your email. Your account is now fully activated.", "email-confirm-error-message": "There was a problem validating your email address. Perhaps the code was invalid or has expired.", diff --git a/public/language/en@pirate/register.json b/public/language/en@pirate/register.json index a063a896ef..381aaef8b5 100644 --- a/public/language/en@pirate/register.json +++ b/public/language/en@pirate/register.json @@ -14,5 +14,6 @@ "register_now_button": "Register Now", "alternative_registration": "Alternative Registration", "terms_of_use": "Terms of Use", - "agree_to_terms_of_use": "I agree to the Terms of Use" + "agree_to_terms_of_use": "I agree to the Terms of Use", + "registration-added-to-queue": "Your registration has been added to the approval queue. You will receive an email when it is accepted by an administrator." } \ No newline at end of file diff --git a/public/language/en@pirate/unread.json b/public/language/en@pirate/unread.json index 17e7089fdf..994ee938f1 100644 --- a/public/language/en@pirate/unread.json +++ b/public/language/en@pirate/unread.json @@ -5,5 +5,6 @@ "mark_as_read": "Mark as Read", "selected": "Selected", "all": "All", + "all_categories": "All categories", "topics_marked_as_read.success": "Topics marked as read!" } \ No newline at end of file diff --git a/public/language/en@pirate/user.json b/public/language/en@pirate/user.json index 49ecb754ac..a35bdf9aaa 100644 --- a/public/language/en@pirate/user.json +++ b/public/language/en@pirate/user.json @@ -6,8 +6,12 @@ "postcount": "Post Count", "email": "Email", "confirm_email": "Confirm Email", + "ban_account": "Ban Account", + "ban_account_confirm": "Do you really want to ban this user?", + "unban_account": "Unban Account", "delete_account": "Delete Account", "delete_account_confirm": "Are you sure you want to delete your account?
This action is irreversible and you will not be able to recover any of your data

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

", "fullname": "Full Name", "website": "Website", "location": "Location", @@ -64,9 +68,9 @@ "settings-require-reload": "Some setting changes require a reload. Click here to reload the page.", "has_no_follower": "This user doesn't have any followers :(", "follows_no_one": "This user isn't following anyone :(", - "has_no_posts": "This user didn't post anything yet.", - "has_no_topics": "This user didn't post any topics yet.", - "has_no_watched_topics": "This user didn't watch any topics yet.", + "has_no_posts": "This user hasn't posted anything yet.", + "has_no_topics": "This user hasn't posted any topics yet.", + "has_no_watched_topics": "This user hasn't watched any topics yet.", "email_hidden": "Email Hidden", "hidden": "hidden", "paginate_description": "Paginate topics and posts instead of using infinite scroll", diff --git a/public/language/en@pirate/users.json b/public/language/en@pirate/users.json index daabbb899c..41f51b97a4 100644 --- a/public/language/en@pirate/users.json +++ b/public/language/en@pirate/users.json @@ -8,5 +8,14 @@ "users-found-search-took": "%1 user(s) found! Search took %2 seconds.", "filter-by": "Filter By", "online-only": "Online only", - "picture-only": "Picture only" + "picture-only": "Picture only", + "invite": "Invite", + "invitation-email-sent": "An invitation email has been sent to %1", + "user_list": "User List", + "recent_topics": "Recent Topics", + "popular_topics": "Popular Topics", + "unread_topics": "Unread Topics", + "categories": "Categories", + "tags": "Tags", + "map": "Map" } \ No newline at end of file diff --git a/public/language/en_GB/email.json b/public/language/en_GB/email.json index 33fd28377b..1aa66835b8 100644 --- a/public/language/en_GB/email.json +++ b/public/language/en_GB/email.json @@ -2,13 +2,19 @@ "password-reset-requested": "Password Reset Requested - %1!", "welcome-to": "Welcome to %1", + "invite": "Invitation from %1", + "greeting_no_name": "Hello", "greeting_with_name": "Hello %1", "welcome.text1": "Thank you for registering with %1!", "welcome.text2": "To fully activate your account, we need to verify that you own the email address you registered with.", + "welcome.text3": "An administrator has accepted your registration application. You can login with your username/password now.", "welcome.cta": "Click here to confirm your email address", + "invitation.text1": "%1 has invited you to join %2", + "invitation.ctr": "Click here to create your account.", + "reset.text1": "We received a request to reset your password, possibly because you have forgotten it. If this is not the case, please ignore this email.", "reset.text2": "To continue with the password reset, please click on the following link:", "reset.cta": "Click here to reset your password", diff --git a/public/language/en_GB/error.json b/public/language/en_GB/error.json index 73bd2ce7db..3419301602 100644 --- a/public/language/en_GB/error.json +++ b/public/language/en_GB/error.json @@ -58,6 +58,9 @@ "too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again", "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)", "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)", + "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)", + "file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file", "cant-vote-self-post": "You cannot vote for your own post", @@ -65,6 +68,7 @@ "already-unfavourited": "You have already unfavourited this post", "cant-ban-other-admins": "You can't ban other admins!", + "cant-remove-last-admin": "You are the only administrator. Add another user as an administrator before removing yourself as admin", "invalid-image-type": "Invalid image type. Allowed types are: %1", "invalid-image-extension": "Invalid image extension", @@ -75,6 +79,8 @@ "group-name-change-not-allowed": "Group name change not allowed", "group-already-member": "You are already part of this group", "group-needs-owner": "This group requires at least one owner", + "group-already-invited": "This user has already been invited", + "group-already-requested": "Your membership request has already been submitted", "post-already-deleted": "This post has already been deleted", "post-already-restored": "This post has already been restored", @@ -99,6 +105,7 @@ "downvoting-disabled": "Downvoting is disabled", "not-enough-reputation-to-downvote": "You do not have enough reputation to downvote this post", "not-enough-reputation-to-flag": "You do not have enough reputation to flag this post", + "already-flagged": "You have already flagged this post", "reload-failed": "NodeBB encountered a problem while reloading: \"%1\". NodeBB will continue to serve the existing client-side assets, although you should undo what you did just prior to reloading.", diff --git a/public/language/en_GB/global.json b/public/language/en_GB/global.json index 92ef56998f..d6c0a71e52 100644 --- a/public/language/en_GB/global.json +++ b/public/language/en_GB/global.json @@ -28,6 +28,7 @@ "pagination.enter_index": "Enter index", "header.admin": "Admin", + "header.categories": "Categories", "header.recent": "Recent", "header.unread": "Unread", "header.tags": "Tags", @@ -64,6 +65,7 @@ "reputation": "Reputation", "read_more": "read more", + "more": "More", "posted_ago_by_guest": "posted %1 by Guest", "posted_ago_by": "posted %1 by %2", diff --git a/public/language/en_GB/groups.json b/public/language/en_GB/groups.json index 644bd6a4cb..dd8b795bcb 100644 --- a/public/language/en_GB/groups.json +++ b/public/language/en_GB/groups.json @@ -7,6 +7,13 @@ "pending.accept": "Accept", "pending.reject": "Reject", + "pending.accept_all": "Accept All", + "pending.reject_all": "Reject All", + "pending.none": "There are no pending members at this time", + + "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", "cover-instructions": "Drag and Drop a photo, drag to position, and hit Save", "cover-change": "Change", @@ -16,6 +23,7 @@ "details.title": "Group Details", "details.members": "Member List", "details.pending": "Pending Members", + "details.invited": "Invited Members", "details.has_no_posts": "This group's members have not made any posts.", "details.latest_posts": "Latest Posts", "details.private": "Private", diff --git a/public/language/en_GB/notifications.json b/public/language/en_GB/notifications.json index 19de8e2c28..1c256bebb4 100644 --- a/public/language/en_GB/notifications.json +++ b/public/language/en_GB/notifications.json @@ -22,6 +22,7 @@ "user_posted_topic": "%1 has posted a new topic: %2", "user_mentioned_you_in": "%1 mentioned you in %2", "user_started_following_you": "%1 started following you.", + "new_register": "%1 sent a registration request.", "email-confirmed": "Email Confirmed", "email-confirmed-message": "Thank you for validating your email. Your account is now fully activated.", diff --git a/public/language/en_GB/register.json b/public/language/en_GB/register.json index 26196c765d..dcbd4bb03a 100644 --- a/public/language/en_GB/register.json +++ b/public/language/en_GB/register.json @@ -14,5 +14,6 @@ "register_now_button": "Register Now", "alternative_registration": "Alternative Registration", "terms_of_use": "Terms of Use", - "agree_to_terms_of_use": "I agree to the Terms of Use" + "agree_to_terms_of_use": "I agree to the Terms of Use", + "registration-added-to-queue": "Your registration has been added to the approval queue. You will receive an email when it is accepted by an administrator." } \ No newline at end of file diff --git a/public/language/en_GB/unread.json b/public/language/en_GB/unread.json index 8bd7a20711..0af9a2cdf6 100644 --- a/public/language/en_GB/unread.json +++ b/public/language/en_GB/unread.json @@ -5,5 +5,6 @@ "mark_as_read": "Mark as Read", "selected": "Selected", "all": "All", + "all_categories": "All categories", "topics_marked_as_read.success": "Topics marked as read!" } \ No newline at end of file diff --git a/public/language/en_GB/user.json b/public/language/en_GB/user.json index 99f118ad5a..50d5dbf6c0 100644 --- a/public/language/en_GB/user.json +++ b/public/language/en_GB/user.json @@ -7,8 +7,12 @@ "email": "Email", "confirm_email": "Confirm Email", + "ban_account": "Ban Account", + "ban_account_confirm": "Do you really want to ban this user?", + "unban_account": "Unban Account", "delete_account": "Delete Account", "delete_account_confirm": "Are you sure you want to delete your account?
This action is irreversible and you will not be able to recover any of your data

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

", "fullname": "Full Name", "website": "Website", @@ -70,9 +74,9 @@ "has_no_follower": "This user doesn't have any followers :(", "follows_no_one": "This user isn't following anyone :(", - "has_no_posts": "This user didn't post anything yet.", - "has_no_topics": "This user didn't post any topics yet.", - "has_no_watched_topics": "This user didn't watch any topics yet.", + "has_no_posts": "This user hasn't posted anything yet.", + "has_no_topics": "This user hasn't posted any topics yet.", + "has_no_watched_topics": "This user hasn't watched any topics yet.", "email_hidden": "Email Hidden", "hidden": "hidden", diff --git a/public/language/en_GB/users.json b/public/language/en_GB/users.json index 0f3687c9ed..5616cda575 100644 --- a/public/language/en_GB/users.json +++ b/public/language/en_GB/users.json @@ -8,5 +8,14 @@ "users-found-search-took": "%1 user(s) found! Search took %2 seconds.", "filter-by": "Filter By", "online-only": "Online only", - "picture-only": "Picture only" + "picture-only": "Picture only", + "invite": "Invite", + "invitation-email-sent": "An invitation email has been sent to %1", + "user_list": "User List", + "recent_topics": "Recent Topics", + "popular_topics": "Popular Topics", + "unread_topics": "Unread Topics", + "categories": "Categories", + "tags": "Tags", + "map": "Map" } \ No newline at end of file diff --git a/public/language/en_US/email.json b/public/language/en_US/email.json index f290435e75..d8114d0231 100644 --- a/public/language/en_US/email.json +++ b/public/language/en_US/email.json @@ -1,11 +1,15 @@ { "password-reset-requested": "Password Reset Requested - %1!", "welcome-to": "Welcome to %1", + "invite": "Invitation from %1", "greeting_no_name": "Hello", "greeting_with_name": "Hello %1", "welcome.text1": "Thank you for registering with %1!", "welcome.text2": "To fully activate your account, we need to verify that you own the email address you registered with.", + "welcome.text3": "An administrator has accepted your registration application. You can login with your username/password now.", "welcome.cta": "Click here to confirm your email address", + "invitation.text1": "%1 has invited you to join %2", + "invitation.ctr": "Click here to create your account.", "reset.text1": "We received a request to reset your password, possibly because you have forgotten it. If this is not the case, please ignore this email.", "reset.text2": "To continue with the password reset, please click on the following link:", "reset.cta": "Click here to reset your password", diff --git a/public/language/en_US/error.json b/public/language/en_US/error.json index ac2457e250..56f9f626ff 100644 --- a/public/language/en_US/error.json +++ b/public/language/en_US/error.json @@ -51,6 +51,7 @@ "already-favourited": "You have already favourited this post", "already-unfavourited": "You have already unfavourited this post", "cant-ban-other-admins": "You can't ban other admins!", + "cant-remove-last-admin": "You are the only administrator. Add another user as an administrator before removing yourself as admin", "invalid-image-type": "Invalid image type. Allowed types are: %1", "invalid-image-extension": "Invalid image extension", "invalid-file-type": "Invalid file type. Allowed types are: %1", @@ -59,6 +60,8 @@ "group-name-change-not-allowed": "Group name change not allowed", "group-already-member": "You are already part of this group", "group-needs-owner": "This group requires at least one owner", + "group-already-invited": "This user has already been invited", + "group-already-requested": "Your membership request has already been submitted", "post-already-deleted": "This post has already been deleted", "post-already-restored": "This post has already been restored", "topic-already-deleted": "This topic has already been deleted", @@ -76,6 +79,7 @@ "downvoting-disabled": "Downvoting is disabled", "not-enough-reputation-to-downvote": "You do not have enough reputation to downvote this post", "not-enough-reputation-to-flag": "You do not have enough reputation to flag this post", + "already-flagged": "You have already flagged this post", "reload-failed": "NodeBB encountered a problem while reloading: \"%1\". NodeBB will continue to serve the existing client-side assets, although you should undo what you did just prior to reloading.", "registration-error": "Registration Error", "parse-error": "Something went wrong while parsing server response", diff --git a/public/language/en_US/global.json b/public/language/en_US/global.json index 6de9e8b364..e47670ae9a 100644 --- a/public/language/en_US/global.json +++ b/public/language/en_US/global.json @@ -50,6 +50,7 @@ "views": "Views", "reputation": "Reputation", "read_more": "read more", + "more": "More", "posted_ago_by_guest": "posted %1 by Guest", "posted_ago_by": "posted %1 by %2", "posted_ago": "posted %1", diff --git a/public/language/en_US/groups.json b/public/language/en_US/groups.json index 5e301aa46a..c9ac036475 100644 --- a/public/language/en_US/groups.json +++ b/public/language/en_US/groups.json @@ -6,6 +6,12 @@ "no_groups_found": "There are no groups to see", "pending.accept": "Accept", "pending.reject": "Reject", + "pending.accept_all": "Accept All", + "pending.reject_all": "Reject All", + "pending.none": "There are no pending members at this time", + "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", "cover-instructions": "Drag and Drop a photo, drag to position, and hit Save", "cover-change": "Change", "cover-save": "Save", @@ -13,6 +19,7 @@ "details.title": "Group Details", "details.members": "Member List", "details.pending": "Pending Members", + "details.invited": "Invited Members", "details.has_no_posts": "This group's members have not made any posts.", "details.latest_posts": "Latest Posts", "details.private": "Private", diff --git a/public/language/en_US/notifications.json b/public/language/en_US/notifications.json index 32ddb12eb7..b7a0ad2b2a 100644 --- a/public/language/en_US/notifications.json +++ b/public/language/en_US/notifications.json @@ -20,6 +20,7 @@ "user_posted_topic": "%1 has posted a new topic: %2", "user_mentioned_you_in": "%1 mentioned you in %2", "user_started_following_you": "%1 started following you.", + "new_register": "%1 sent a registration request.", "email-confirmed": "Email Confirmed", "email-confirmed-message": "Thank you for validating your email. Your account is now fully activated.", "email-confirm-error-message": "There was a problem validating your email address. Perhaps the code was invalid or has expired.", diff --git a/public/language/en_US/register.json b/public/language/en_US/register.json index e553561c0e..72cd580910 100644 --- a/public/language/en_US/register.json +++ b/public/language/en_US/register.json @@ -14,5 +14,6 @@ "register_now_button": "Register Now", "alternative_registration": "Alternative Registration", "terms_of_use": "Terms of Use", - "agree_to_terms_of_use": "I agree to the Terms of Use" + "agree_to_terms_of_use": "I agree to the Terms of Use", + "registration-added-to-queue": "Your registration has been added to the approval queue. You will receive an email when it is accepted by an administrator." } \ No newline at end of file diff --git a/public/language/en_US/unread.json b/public/language/en_US/unread.json index d682c0541f..d723f6e8c9 100644 --- a/public/language/en_US/unread.json +++ b/public/language/en_US/unread.json @@ -5,5 +5,6 @@ "mark_as_read": "Mark as Read", "selected": "Selected", "all": "All", + "all_categories": "All categories", "topics_marked_as_read.success": "Topics marked as read!" } \ No newline at end of file diff --git a/public/language/en_US/user.json b/public/language/en_US/user.json index c3ebec37a2..be5da7fc6c 100644 --- a/public/language/en_US/user.json +++ b/public/language/en_US/user.json @@ -6,8 +6,12 @@ "postcount": "Post Count", "email": "Email", "confirm_email": "Confirm Email", + "ban_account": "Ban Account", + "ban_account_confirm": "Do you really want to ban this user?", + "unban_account": "Unban Account", "delete_account": "Delete Account", "delete_account_confirm": "Are you sure you want to delete your account?
This action is irreversible and you will not be able to recover any of your data

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

", "fullname": "Full Name", "website": "Website", "location": "Location", @@ -64,9 +68,9 @@ "settings-require-reload": "Some setting changes require a reload. Click here to reload the page.", "has_no_follower": "This user doesn't have any followers :(", "follows_no_one": "This user isn't following anyone :(", - "has_no_posts": "This user didn't post anything yet.", - "has_no_topics": "This user didn't post any topics yet.", - "has_no_watched_topics": "This user didn't watch any topics yet.", + "has_no_posts": "This user hasn't posted anything yet.", + "has_no_topics": "This user hasn't posted any topics yet.", + "has_no_watched_topics": "This user hasn't watched any topics yet.", "email_hidden": "Email Hidden", "hidden": "hidden", "paginate_description": "Paginate topics and posts instead of using infinite scroll", diff --git a/public/language/en_US/users.json b/public/language/en_US/users.json index 0038dcffc4..52e4c7dc46 100644 --- a/public/language/en_US/users.json +++ b/public/language/en_US/users.json @@ -8,5 +8,14 @@ "users-found-search-took": "%1 user(s) found! Search took %2 seconds.", "filter-by": "Filter By", "online-only": "Online only", - "picture-only": "Picture only" + "picture-only": "Picture only", + "invite": "Invite", + "invitation-email-sent": "An invitation email has been sent to %1", + "user_list": "User List", + "recent_topics": "Recent Topics", + "popular_topics": "Popular Topics", + "unread_topics": "Unread Topics", + "categories": "Categories", + "tags": "Tags", + "map": "Map" } \ No newline at end of file diff --git a/public/language/es/category.json b/public/language/es/category.json index dcb03b8ef3..edf0773adc 100644 --- a/public/language/es/category.json +++ b/public/language/es/category.json @@ -1,7 +1,7 @@ { "new_topic_button": "Nuevo tema", "guest-login-post": "Acceder para poder escribir un mensaje", - "no_topics": "No hay temas en esta categoría.
¿Por que no te animas y publicas uno?", + "no_topics": "No hay temas en esta categoría.
¿Por qué no te animas y publicas uno?", "browsing": "viendo ahora", "no_replies": "Nadie ha respondido aún", "share_this_category": "Compartir esta categoría", diff --git a/public/language/es/email.json b/public/language/es/email.json index fd42293e1b..b69f339719 100644 --- a/public/language/es/email.json +++ b/public/language/es/email.json @@ -1,22 +1,26 @@ { "password-reset-requested": "Reinicio de contraseña solicitado - %1!", "welcome-to": "Bienvenido a %1", + "invite": "Invitación de %1", "greeting_no_name": "Hola", "greeting_with_name": "Hola %1", "welcome.text1": "Gracias por registrarte con %1!", "welcome.text2": "Para activar completamente tu cuenta, necesitamos verificar que la dirección email con la que te registraste te pertenece.", - "welcome.cta": "Cliquea aquí para confirmar tu dirección email.", - "reset.text1": "Recibimos una solicitud para reiniciar tu contraseña, posiblemente porque la olvidaste. Si no es así, por favor ignora este email.", + "welcome.text3": "El administrador ha aceptado tu registro. Puedes acceder con tu usuario/contraseña ahora.", + "welcome.cta": "Cliquea aquí para confirmar tu dirección de email.", + "invitation.text1": "%1 te ha invitado a unirte a %2", + "invitation.ctr": "Haz click aquí para crear tu cuenta", + "reset.text1": "Hemos recibido una solicitud para reiniciar tu contraseña, posiblemente porque la olvidaste. Si no es así, por favor, ignora este email.", "reset.text2": "Para continuar con el reinicio de contraseña, por favor cliquea en el siguiente vínculo:", "reset.cta": "Cliquea aquí para reiniciar tu contraseña", "reset.notify.subject": "Se ha modificado correctamente la contraseña.", - "reset.notify.text1": "Te estamos notificando que a a %1, tu contraseña ha sido cambiado correctamente.", - "reset.notify.text2": "Si no has sido tu, por favor notifica al administrador inmediatamente.", + "reset.notify.text1": "Te estamos notificando que en %1, tu contraseña ha sido cambiada correctamente.", + "reset.notify.text2": "Si no has sido tú, por favor notifica al administrador inmediatamente.", "digest.notifications": "Tiene notificaciones sin leer de %1:", "digest.latest_topics": "Últimos temas de %1", "digest.cta": "Cliquea aquí para visitar %1", "digest.unsub.info": "Este compendio te fue enviado debido a tus ajustes de subscripción.", - "digest.no_topics": "No han habido temas activos en el pasado %1", + "digest.no_topics": "No han habido temas activos anteriores %1", "notif.chat.subject": "Nuevo mensaje de chat recibido de %1", "notif.chat.cta": "Haz click aquí para continuar la conversación", "notif.chat.unsub.info": "Esta notificación de chat se te envió debido a tus ajustes de suscripción.", diff --git a/public/language/es/error.json b/public/language/es/error.json index cd90a8470b..f049c9598f 100644 --- a/public/language/es/error.json +++ b/public/language/es/error.json @@ -2,7 +2,7 @@ "invalid-data": "Datos no válidos", "not-logged-in": "No has iniciado sesión.", "account-locked": "Tu cuenta ha sido bloqueada temporalmente.", - "search-requires-login": "Searching requires an account - please login or register.", + "search-requires-login": "¡Buscar requiere estar registrado! Por favor, entra o regístrate.", "invalid-cid": "Identificador de categoría no válido", "invalid-tid": "Identificador de tema no válido", "invalid-pid": "Identificador de publicación no válido", @@ -21,11 +21,11 @@ "email-not-confirmed-chat": "No puedes usar el chat hasta que confirmes tu dirección de correo electrónico, por favor haz click aquí para confirmar tu correo.", "no-email-to-confirm": "Este foro requiere confirmación de su email, por favor pulse aquí para introducir un email", "email-confirm-failed": "No se ha podido confirmar su email, por favor inténtelo de nuevo más tarde.", - "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", + "confirm-email-already-sent": "El email de confirmación ya ha sido enviado, por favor espera %1 minuto(s) para enviar otro.", "username-too-short": "Nombre de usuario es demasiado corto", "username-too-long": "Nombre de usuario demasiado largo", "user-banned": "Usuario baneado", - "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", + "user-too-new": "Lo sentimos, es necesario que esperes %1 segundo(s) antes poder hacer tu primera publicación", "no-category": "La categoría no existe", "no-topic": "El tema no existe", "no-post": "La publicación no existe", @@ -36,21 +36,22 @@ "no-emailers-configured": "No se ha cargado ningún plugin de email, así que no se pudo enviar el email de prueba.", "category-disabled": "Categoría deshabilitada", "topic-locked": "Tema bloqueado", - "post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting", + "post-edit-duration-expired": "Sólo puedes editar mensajes durante %1 segundo(s) después de haberlo escrito", "still-uploading": "Por favor, espera a que terminen las subidas.", - "content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).", - "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).", - "title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).", - "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).", - "too-many-posts": "You can only post once every %1 second(s) - please wait before posting again", - "too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again", - "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)", - "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)", - "file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file", + "content-too-short": "Por favor introduzca una publicación más larga. Las publicaciones deben contener al menos %1 caractere(s).", + "content-too-long": "Por favor introduzca un mensaje más corto. Los mensajes no pueden exceder los %1 caractere(s).", + "title-too-short": "Por favor introduzca un título más largo. Los títulos deben contener al menos %1 caractere(s).", + "title-too-long": "Por favor, introduce un título más corto, que no sobrepase los %1 caractere(s).", + "too-many-posts": "Solo puedes publicar una vez cada %1 segundo(s) - por favor espere antes de volver a publicar", + "too-many-posts-newbie": "Como nuevo usuario, solo puedes publicar una vez cada %1 segundo(s) hasta hayas ganado una reputación de %2 - por favor espera antes de volver a publicar", + "tag-too-short": "Por favor introduce una etiqueta más larga. Las etiquetas deben contener por lo menos %1 caractere(s)", + "tag-too-long": "Por favor introduce una etiqueta más corta. Las etiquetas no pueden exceder los %1 caractere(s)", + "file-too-big": "El tamaño de fichero máximo es de %1 kB - por favor, suba un fichero más pequeño", "cant-vote-self-post": "No puedes votar tus propios posts", "already-favourited": "Ya ha marcado esta publicación como favorita", "already-unfavourited": "Ya ha desmarcado esta publicación como favorita", "cant-ban-other-admins": "¡No puedes expulsar a otros administradores!", + "cant-remove-last-admin": "Tu eres el unico administrador. Añade otro usuario como administrador antes de eliminarte a ti mismo.", "invalid-image-type": "Tipo de imagen inválido. Los tipos permitidos son: %1", "invalid-image-extension": "Extensión de imagen inválida", "invalid-file-type": "Tipo de fichero inválido. Los tipos permitidos son: %1", @@ -59,16 +60,18 @@ "group-name-change-not-allowed": "El nombre del grupo deseado no está permitido.", "group-already-member": "Ya formas parte de este grupo", "group-needs-owner": "Este grupo requiere al menos un propietario", + "group-already-invited": "Este usuario ya ha sido invitado", + "group-already-requested": "Tu solicitud de miembro ya ha sido enviada", "post-already-deleted": "Este publicación ya ha sido borrada", "post-already-restored": "Esta publicación ya ha sido restaurada", "topic-already-deleted": "Este tema ya ha sido borrado", "topic-already-restored": "Este tema ya ha sido restaurado", - "cant-purge-main-post": "You can't purge the main post, please delete the topic instead", + "cant-purge-main-post": "No puedes purgar el mensaje principal, por favor utiliza borrar tema", "topic-thumbnails-are-disabled": "Las miniaturas de los temas están deshabilitadas.", "invalid-file": "Archivo no válido", "uploads-are-disabled": "Las subidas están deshabilitadas.", - "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", - "about-me-too-long": "Sorry, your about me cannot be longer than %1 character(s).", + "signature-too-long": "Lo sentimos, pero tu firma no puede ser más larga de %1 caractere(s).", + "about-me-too-long": "Lo sentimos, pero tu descripción no puede ser más larga de %1 caractere(s).", "cant-chat-with-yourself": "¡No puedes conversar contigo mismo!", "chat-restricted": "Este usuario tiene restringidos los mensajes de chat. Los usuarios deben seguirte antes de que pueda charlar con ellos", "too-many-messages": "Has enviado demasiados mensajes, por favor espera un poco.", @@ -76,6 +79,7 @@ "downvoting-disabled": "La votación negativa está deshabilitada.", "not-enough-reputation-to-downvote": "No tienes suficiente reputación para votar negativo este post", "not-enough-reputation-to-flag": "No tiene suficiente reputación para poner reportar esta publicación", + "already-flagged": "Ya reportaste este mensaje anteriormente", "reload-failed": "NodeBB encontró un problema al refrescar: \"%1\". NodeBB intentará cargar el resto de contenido, aunque deberías deshacer lo que hiciste justo antes.", "registration-error": "Error de registro", "parse-error": "Algo ha ido mal mientras se parseaba la respuesta del servidor", diff --git a/public/language/es/global.json b/public/language/es/global.json index 4e2b07be05..6047e4413e 100644 --- a/public/language/es/global.json +++ b/public/language/es/global.json @@ -50,6 +50,7 @@ "views": "Visitas", "reputation": "Reputación", "read_more": "leer más", + "more": "Más", "posted_ago_by_guest": "publicado %1 por Invitado", "posted_ago_by": "publicado %1 por %2", "posted_ago": "publicado %1", diff --git a/public/language/es/groups.json b/public/language/es/groups.json index 70aaf339a1..48bece926e 100644 --- a/public/language/es/groups.json +++ b/public/language/es/groups.json @@ -6,6 +6,12 @@ "no_groups_found": "No hay grupos que ver", "pending.accept": "Aceptar", "pending.reject": "Rechazar", + "pending.accept_all": "Aceptar todo", + "pending.reject_all": "Rechazar todo", + "pending.none": "No hay miembros pendientes en este momento", + "invited.none": "No hay miembros invitados en este momento", + "invited.uninvite": "Cancelar invitación", + "invited.search": "Buscar un usuario para invitar a este grupo", "cover-instructions": "Arrastra y suelta una foto, arrastra a la posición, y pulsa Guardar ", "cover-change": "Cambiar", "cover-save": "Guardar", @@ -13,6 +19,7 @@ "details.title": "Detalles de Grupo", "details.members": "Lista de Miembros", "details.pending": "Miembros Pendientes", + "details.invited": "Miembros Invitados", "details.has_no_posts": "Los miembros de este grupo no han hecho ninguna publicación.", "details.latest_posts": "Últimas Publicaciones", "details.private": "Privado", diff --git a/public/language/es/notifications.json b/public/language/es/notifications.json index 03a9b10b9b..296d619a14 100644 --- a/public/language/es/notifications.json +++ b/public/language/es/notifications.json @@ -20,6 +20,7 @@ "user_posted_topic": "%1 ha publicado un nuevo tema: %2", "user_mentioned_you_in": "%1 te mencionó en %2", "user_started_following_you": "%1 comenzó a seguirte.", + "new_register": "%1 envió una solicitud de registro.", "email-confirmed": "Correo electrónico confirmado", "email-confirmed-message": "Gracias por validar tu correo electrónico. Tu cuenta ya está completamente activa.", "email-confirm-error-message": "Hubo un problema al validar tu cuenta de correo electrónico. Quizá el código era erróneo o expiró...", diff --git a/public/language/es/register.json b/public/language/es/register.json index 4ffa4d8bdd..ef1102ea8a 100644 --- a/public/language/es/register.json +++ b/public/language/es/register.json @@ -1,5 +1,5 @@ { - "register": "Registrase", + "register": "Registrarse", "help.email": "Por defecto, tu cuenta de correo electrónico estará oculta al publico.", "help.username_restrictions": "El nombre de usuario debe tener entre %1 y %2 carácteres. Los miembros pueden responderte escribiendo @usuario.", "help.minimum_password_length": "Tu contraseña debe tener al menos %1 carácteres.", @@ -11,8 +11,9 @@ "password_placeholder": "Introduce tu contraseña", "confirm_password": "Confirmar contraseña", "confirm_password_placeholder": "Confirmar contraseña", - "register_now_button": "Registrarme ahora", + "register_now_button": "Registrarse ahora", "alternative_registration": "Métodos de registro alternativos", "terms_of_use": "Términos y Condiciones de uso", - "agree_to_terms_of_use": "Acepto los Términos y Condiciones de uso" + "agree_to_terms_of_use": "Acepto los Términos y Condiciones de uso", + "registration-added-to-queue": "Tu registro se ha añadido a la cola de aprobación,. Recibirás un correo cuando este sea aceptado por un administrador. " } \ No newline at end of file diff --git a/public/language/es/topic.json b/public/language/es/topic.json index edf271b351..c90bccb9d0 100644 --- a/public/language/es/topic.json +++ b/public/language/es/topic.json @@ -5,7 +5,7 @@ "no_topics_found": "¡No se encontraron temas!", "no_posts_found": "¡No se encontraron publicaciones!", "post_is_deleted": "¡Esta publicación está eliminada!", - "topic_is_deleted": "This topic is deleted!", + "topic_is_deleted": "¡Este tema ha sido eliminado!", "profile": "Perfil", "posted_by": "Publicado por %1", "posted_by_guest": "Publicado por Invitado", diff --git a/public/language/es/unread.json b/public/language/es/unread.json index 95bd38fd77..d77a5f7979 100644 --- a/public/language/es/unread.json +++ b/public/language/es/unread.json @@ -5,5 +5,6 @@ "mark_as_read": "Marcar como leído", "selected": "Seleccionados", "all": "Todos", + "all_categories": "Todos los foros", "topics_marked_as_read.success": "¡Temas marcados como leídos!" } \ No newline at end of file diff --git a/public/language/es/user.json b/public/language/es/user.json index 9f280f320a..35dd4905dd 100644 --- a/public/language/es/user.json +++ b/public/language/es/user.json @@ -6,8 +6,12 @@ "postcount": "Número De Publicaciones", "email": "Correo electrónico", "confirm_email": "Confirmar correo electrónico", + "ban_account": "Banear Cuenta", + "ban_account_confirm": "Quieres confirmar el baneo de este usuario?", + "unban_account": "Desbanear Cuenta", "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

", "fullname": "Nombre completo", "website": "Sitio web", "location": "Ubicación", @@ -64,9 +68,9 @@ "settings-require-reload": "Algunos cambios de configuración requieren una recarga la página. Haz click aquí para recargar la página.", "has_no_follower": "Este usuario no tiene seguidores :(", "follows_no_one": "Este miembro no sigue a nadie :(", - "has_no_posts": "Este usuario aún no ha publicado nada.", - "has_no_topics": "Este usuario aún no ha publicado nada.", - "has_no_watched_topics": "Este usuario todavía no ha visto ninguna publicación.", + "has_no_posts": "Este usuario no ha publicado nada aún.", + "has_no_topics": "Este usuario no ha publicado ninguna tema todavía.", + "has_no_watched_topics": "Este usuario no esta suscrito a ningún tema aún.", "email_hidden": "Correo electrónico oculto", "hidden": "oculto", "paginate_description": "Paginar hilos y mensajes en lugar de usar desplazamiento infinito", @@ -77,7 +81,7 @@ "open_links_in_new_tab": "Abrir los enlaces externos en una nueva pestaña", "enable_topic_searching": "Activar la búsqueda \"in-topic\"", "topic_search_help": "Si está activada, la búsqueda 'in-topic' sustituirá el comportamiento por defecto del navegador y le permitirá buscar en el tema al completo, en vez de hacer una búsqueda únicamente sobre el contenido mostrado en pantalla", - "follow_topics_you_reply_to": "Seguir los temas en las que respondes", + "follow_topics_you_reply_to": "Seguir los temas en los que respondes", "follow_topics_you_create": "Seguir publicaciones que creas", "grouptitle": "Selecciona el título del grupo que deseas visualizar", "no-group-title": "Sin título de grupo" diff --git a/public/language/es/users.json b/public/language/es/users.json index 81668516cf..e329a4352c 100644 --- a/public/language/es/users.json +++ b/public/language/es/users.json @@ -8,5 +8,14 @@ "users-found-search-took": "¡%1 usuario(s) encontrado! La búsqueda ha llevado %2 segundos.", "filter-by": "Filtrar Por", "online-only": "Sólo en línea", - "picture-only": "Sólo imagen" + "picture-only": "Sólo imagen", + "invite": "Invitar", + "invitation-email-sent": "Un correo de invitación ha sido enviado a %1", + "user_list": "Lista de Usuarios", + "recent_topics": "Temas Recientes", + "popular_topics": "Temas Populares", + "unread_topics": "Temas no leídos", + "categories": "Categorías ", + "tags": "Etiquetas", + "map": "Actividad" } \ No newline at end of file diff --git a/public/language/et/email.json b/public/language/et/email.json index 9f3a49318e..539e2f4eed 100644 --- a/public/language/et/email.json +++ b/public/language/et/email.json @@ -1,28 +1,32 @@ { - "password-reset-requested": "Parooli muutmise taotlus - %1!", - "welcome-to": "Tere tulemast %1", + "password-reset-requested": "Parooli muutmise taotlus - %1", + "welcome-to": "Tere tulemast %1 foorumisse", + "invite": "Invitation from %1", "greeting_no_name": "Tere", "greeting_with_name": "Tere %1", - "welcome.text1": "Thank you for registering with %1!", - "welcome.text2": "To fully activate your account, we need to verify that you own the email address you registered with.", - "welcome.cta": "Click here to confirm your email address", - "reset.text1": "We received a request to reset your password, possibly because you have forgotten it. If this is not the case, please ignore this email.", - "reset.text2": "To continue with the password reset, please click on the following link:", - "reset.cta": "Click here to reset your password", - "reset.notify.subject": "Password successfully changed", - "reset.notify.text1": "We are notifying you that on %1, your password was changed successfully.", - "reset.notify.text2": "If you did not authorise this, please notify an administrator immediately.", - "digest.notifications": "You have unread notifications from %1:", - "digest.latest_topics": "Latest topics from %1", - "digest.cta": "Click here to visit %1", - "digest.unsub.info": "This digest was sent to you due to your subscription settings.", - "digest.no_topics": "There have been no active topics in the past %1", - "notif.chat.subject": "New chat message received from %1", - "notif.chat.cta": "Click here to continue the conversation", - "notif.chat.unsub.info": "This chat notification was sent to you due to your subscription settings.", - "notif.post.cta": "Click here to read the full topic", - "notif.post.unsub.info": "This post notification was sent to you due to your subscription settings.", - "test.text1": "This is a test email to verify that the emailer is set up correctly for your NodeBB.", - "unsub.cta": "Click here to alter those settings", - "closing": "Thanks!" + "welcome.text1": "Täname et oled registreerinud %1 foorumisse!", + "welcome.text2": "Konto täielikuks aktiveerimiseks peame me kinnitama, et registreerimisel kasutatud e-mail kuulub teile.", + "welcome.text3": "An administrator has accepted your registration application. You can login with your username/password now.", + "welcome.cta": "Vajuta siia, et kinnitada oma e-maili aadress", + "invitation.text1": "%1 has invited you to join %2", + "invitation.ctr": "Click here to create your account.", + "reset.text1": "Meile laekus päring parooli muutmiseks. Kui päring ei ole teie poolt esitatud või te ei soovi parooli muuta, siis võite antud kirja ignoreerida.", + "reset.text2": "Selleks, et jätkata parooli muutmisega vajuta järgnevale lingile:", + "reset.cta": "Vajuta siia, et taotleda uut parooli", + "reset.notify.subject": "Parool edukalt muudetud", + "reset.notify.text1": "Teatame, et sinu parooli muutmine kuupäeval %1 oli edukas.", + "reset.notify.text2": "Kui te ei ole lubanud seda, siis teavitage koheselt administraatorit.", + "digest.notifications": "Sul on lugemata teateid %1 poolt:", + "digest.latest_topics": "Viimased teemad %1 poolt", + "digest.cta": "Vajuta siia et külastada %1", + "digest.unsub.info": "See uudiskiri on saadetud teile tellimuse seadistuse tõttu.", + "digest.no_topics": "Viimase %1 jooksul ei ole olnud ühtegi aktiivset teemat", + "notif.chat.subject": "Sulle on saabunud uus sõnum kasutajalt %1", + "notif.chat.cta": "Vajuta siia, et jätkata vestlusega", + "notif.chat.unsub.info": "See chat teavitus on saadetud teile tellimuse seadistuse tõttu.", + "notif.post.cta": "Vajuta siia, et lugeda teemat täies mahus", + "notif.post.unsub.info": "See postituse teavitus on saadetud teile tellimuse seadistuse tõttu.", + "test.text1": "See on test e-mail kinnitamaks, et emailer on korrektselt seadistatud sinu NodeBB jaoks.", + "unsub.cta": "Vajuta siia, et muuta neid seadeid", + "closing": "Aitäh!" } \ No newline at end of file diff --git a/public/language/et/error.json b/public/language/et/error.json index bceffa41e1..e41b323cfe 100644 --- a/public/language/et/error.json +++ b/public/language/et/error.json @@ -51,6 +51,7 @@ "already-favourited": "Sa juba märkisid selle postituse lemmikuks", "already-unfavourited": "Sa juba eemaldasid selle postituse lemmikute hulgast", "cant-ban-other-admins": "Sa ei saa bannida teisi administraatoreid!", + "cant-remove-last-admin": "You are the only administrator. Add another user as an administrator before removing yourself as admin", "invalid-image-type": "Vigane pildi formaat. Lubatud formaadid on: %1", "invalid-image-extension": "Vigane pildi formaat", "invalid-file-type": "Vigane faili formaat. Lubatud formaadid on: %1", @@ -59,6 +60,8 @@ "group-name-change-not-allowed": "Grupi nimevahetus ei ole lubatud", "group-already-member": "Sa oled juba selle grupi liige", "group-needs-owner": "See grupp nõuab vähemalt ühte omanikku", + "group-already-invited": "This user has already been invited", + "group-already-requested": "Your membership request has already been submitted", "post-already-deleted": "Postitus on juba kustutatud", "post-already-restored": "Postitus on juba taastatud", "topic-already-deleted": "Teema on juba kustutatud", @@ -76,6 +79,7 @@ "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.", "not-enough-reputation-to-flag": "Sul ei ole piisavalt reputatsiooni, et seda postitust raporteerida", + "already-flagged": "You have already flagged this post", "reload-failed": "\"%1\" värskendamisel tekkis süsteemne viga. Foorum ei lakka töötamast, kuid peaksid kindlasti eemaldama enne värskendamist tehtud muudatused.", "registration-error": "Viga registreerimisel", "parse-error": "Midagi läks valesti...", diff --git a/public/language/et/global.json b/public/language/et/global.json index b383959064..25c37dbd4c 100644 --- a/public/language/et/global.json +++ b/public/language/et/global.json @@ -45,11 +45,12 @@ "alert.follow": "Sa jälgid nüüd %1!", "online": "Sees", "users": "Kasutajad", - "topics": "Teemad", + "topics": "Teemat", "posts": "Postitust", "views": "Vaatamist", "reputation": "Reputatsioon", "read_more": "loe veel", + "more": "More", "posted_ago_by_guest": "postitas %1 külaline", "posted_ago_by": "postitas %1 kasutaja %2", "posted_ago": "postitatud %1", diff --git a/public/language/et/groups.json b/public/language/et/groups.json index 56642324bd..a94a4f28af 100644 --- a/public/language/et/groups.json +++ b/public/language/et/groups.json @@ -6,6 +6,12 @@ "no_groups_found": "Ei ole ühtegi gruppi", "pending.accept": "Aktsepteeri", "pending.reject": "Lükka tagasi", + "pending.accept_all": "Accept All", + "pending.reject_all": "Reject All", + "pending.none": "There are no pending members at this time", + "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", "cover-instructions": "Lohista kaanepilt siia ning vajuta salvesta", "cover-change": "Muuda", "cover-save": "Salvesta", @@ -13,6 +19,7 @@ "details.title": "Grupi detailid", "details.members": "Liikmete nimekiri", "details.pending": "Otsust ootavad liikmed", + "details.invited": "Invited Members", "details.has_no_posts": "Selle grupi liikmed ei ole teinud ühtegi postitust.", "details.latest_posts": "Viimased postitused", "details.private": "Privaatne", diff --git a/public/language/et/notifications.json b/public/language/et/notifications.json index 24c90fce91..fc4f6a9ea9 100644 --- a/public/language/et/notifications.json +++ b/public/language/et/notifications.json @@ -20,8 +20,9 @@ "user_posted_topic": "%1 on postitanud uue teema: %2", "user_mentioned_you_in": "%1 mainis sind postituses %2", "user_started_following_you": "%1 hakkas sind jälgima.", + "new_register": "%1 sent a registration request.", "email-confirmed": "Emaili aadress kinnitatud", - "email-confirmed-message": "Täname, et kinnitasite oma emaili aadressi. Teie kasutaja omn nüüd täielikult aktiveeritud.", + "email-confirmed-message": "Täname, et kinnitasite oma emaili aadressi. Teie kasutaja on nüüd täielikult aktiveeritud.", "email-confirm-error-message": "Emaili aadressi kinnitamisel tekkis viga. Võibolla kinnituskood oli vale või aegunud.", "email-confirm-sent": "Kinnituskiri on saadetud." } \ No newline at end of file diff --git a/public/language/et/register.json b/public/language/et/register.json index 0305eddc67..922c163537 100644 --- a/public/language/et/register.json +++ b/public/language/et/register.json @@ -14,5 +14,6 @@ "register_now_button": "Registreeri", "alternative_registration": "Alternatiivne registreerimismeetod", "terms_of_use": "Foorumi reeglid", - "agree_to_terms_of_use": "Nõustun foorumi reeglitega" + "agree_to_terms_of_use": "Nõustun foorumi reeglitega", + "registration-added-to-queue": "Your registration has been added to the approval queue. You will receive an email when it is accepted by an administrator." } \ No newline at end of file diff --git a/public/language/et/unread.json b/public/language/et/unread.json index e938c97b5a..f439b6db35 100644 --- a/public/language/et/unread.json +++ b/public/language/et/unread.json @@ -5,5 +5,6 @@ "mark_as_read": "Märgi loetuks", "selected": "Valitud", "all": "Kõik", + "all_categories": "All categories", "topics_marked_as_read.success": "Teemad märgitud loetuks!" } \ No newline at end of file diff --git a/public/language/et/user.json b/public/language/et/user.json index 5cd87ac9a2..d28d82ad70 100644 --- a/public/language/et/user.json +++ b/public/language/et/user.json @@ -6,8 +6,12 @@ "postcount": "Postitusi", "email": "Email", "confirm_email": "Kinnita email", + "ban_account": "Ban Account", + "ban_account_confirm": "Do you really want to ban this user?", + "unban_account": "Unban Account", "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": "Are you sure you want to delete this account?
This action is irreversible and you will not be able to recover any data

", "fullname": "Täisnimi", "website": "Koduleht", "location": "Asukoht", @@ -64,9 +68,9 @@ "settings-require-reload": "Muudatused seadetes nõuavad lehe uuesti laadimist. Lehe värskendamiseks vajuta siia.", "has_no_follower": "Sellel kasutajal pole ühtegi jälgijat :(", "follows_no_one": "See kasutaja ei jälgi kedagi :(", - "has_no_posts": "See kasutaja pole midagi postitanud veel.", - "has_no_topics": "See kasutaja pole vele ühtegi teemat postitanud.", - "has_no_watched_topics": "See kasutaja pole vaadanud ühtegi teemat veel.", + "has_no_posts": "This user hasn't posted anything yet.", + "has_no_topics": "This user hasn't posted any topics yet.", + "has_no_watched_topics": "This user hasn't watched any topics yet.", "email_hidden": "Peidetud email", "hidden": "peidetud", "paginate_description": "Paginate topics and posts instead of using infinite scroll", diff --git a/public/language/et/users.json b/public/language/et/users.json index 40d9110d57..d8d7342f4e 100644 --- a/public/language/et/users.json +++ b/public/language/et/users.json @@ -8,5 +8,14 @@ "users-found-search-took": "%1 kasutaja(t) leiti! Otsing kestis %2 sekundit.", "filter-by": "Filtreeri", "online-only": "Ainult seesolevad", - "picture-only": "Ainult pilt" + "picture-only": "Ainult pilt", + "invite": "Invite", + "invitation-email-sent": "An invitation email has been sent to %1", + "user_list": "User List", + "recent_topics": "Recent Topics", + "popular_topics": "Popular Topics", + "unread_topics": "Unread Topics", + "categories": "Categories", + "tags": "Tags", + "map": "Map" } \ No newline at end of file diff --git a/public/language/fa_IR/email.json b/public/language/fa_IR/email.json index 146d6ebb0d..4b0a656909 100644 --- a/public/language/fa_IR/email.json +++ b/public/language/fa_IR/email.json @@ -1,11 +1,15 @@ { "password-reset-requested": "درخواست بازیابی گذرواژه- %1!", "welcome-to": "به 1% خوش آمدید", + "invite": "دعوتنامه از %1", "greeting_no_name": "سلام", "greeting_with_name": "سلام 1%", "welcome.text1": "متشکر بابت ثبت نام در %1!", "welcome.text2": "برای فعال کردن کامل اکانت شما، ما نیاز داریم تا اطمینان حاصل کنیم که شما مالک ایمیلی که با ان ثبت نام کردید هستید.", + "welcome.text3": "ِک مدیر درخواست ثبت نام شما را قبول کرده. اکنون میتوانید با نام کاربری/رمز عبور خود وارد شوید", "welcome.cta": "برای تأیید آدرس ایمیل خود اینجا کلیک کنید", + "invitation.text1": "%1 شما را برای پیوستن به %2 دعوت کرده", + "invitation.ctr": "برای ساخت حسابتان اینجا را کلیک کنید", "reset.text1": "ما یک درخواست برای بازنشانی رمزعبور شما دریافت کرده ایم، احتمالا به این دلیل که شما آن را فراموش کرده اید. اگر این مورد نیست و شما رمز خود را به یاد دارید، لطفا این ایمیل را نادیده بگیرید.", "reset.text2": "برای ادامه بازنشانی رمز، لطفابر روی این لینک کلیک کنید:", "reset.cta": "برای تنظیم مجدد گذرواژه‌ی خود اینجا کلیک کنید", diff --git a/public/language/fa_IR/error.json b/public/language/fa_IR/error.json index 37e2a353b3..0ad12b9ee7 100644 --- a/public/language/fa_IR/error.json +++ b/public/language/fa_IR/error.json @@ -51,6 +51,7 @@ "already-favourited": "شما قبلا این دیدگاه را محبوب کرده اید", "already-unfavourited": "شما قبلا این دیدگاه را نامحبوب کرده اید", "cant-ban-other-admins": "شما نمی‌توانید دیگر مدیران را محروم کنید!", + "cant-remove-last-admin": "You are the only administrator. Add another user as an administrator before removing yourself as admin", "invalid-image-type": "نوع تصویر نامعتبر است. نوعهای قابل قبول اینها هستند: %1", "invalid-image-extension": "پسوند عکس نامعتبر است", "invalid-file-type": "نوع پرونده نامعتبر است. نوعهای قابل قبول اینها هستند: %1", @@ -59,6 +60,8 @@ "group-name-change-not-allowed": "تغیر نام گروه نیاز به دسترسی دارد.", "group-already-member": "شما الان هم عضوی از این گروه هستید", "group-needs-owner": "این گروه حداقل یک مالک باید داشته باشد", + "group-already-invited": "This user has already been invited", + "group-already-requested": "Your membership request has already been submitted", "post-already-deleted": "این دیدگاه پیش‌تر پاک شده است", "post-already-restored": "دیدگاه پیش‌تر بازگردانی شده است.", "topic-already-deleted": "جستار پیش‌تر حذف شده است", @@ -76,6 +79,7 @@ "downvoting-disabled": "رای منفی غیر فعال شده است", "not-enough-reputation-to-downvote": "شما اعتبار کافی برای دادن رای منفی به این دیدگاه را ندارید.", "not-enough-reputation-to-flag": "شما اعتبار کافی برای نشاندار کردن این دیدگاه ندارید", + "already-flagged": "You have already flagged this post", "reload-failed": "NodeBB در هنگام بارگذاری مجدد با یک مشکل مواجه شده است: \"%1\". NodeBB سرویس رسانی به کلاینت های سرویس گیرنده را ادامه خواهد داد، اگرچه شما کاری را قبل از بارگیری مجدد انجام دادید بازگردانی کنید", "registration-error": "خطای ثبت نام", "parse-error": "هنگام تجزیه پاسخ سرور اشتباهی پیش امد", diff --git a/public/language/fa_IR/global.json b/public/language/fa_IR/global.json index f3594f29d3..aff826fca3 100644 --- a/public/language/fa_IR/global.json +++ b/public/language/fa_IR/global.json @@ -50,6 +50,7 @@ "views": "بازدیدها", "reputation": "اعتبار", "read_more": "بیشتر بخوانید", + "more": "More", "posted_ago_by_guest": "ارسال شده در %1 توسط مهمان", "posted_ago_by": "ارسال شده در %1 توسط %2", "posted_ago": "ارسال شده در %1", diff --git a/public/language/fa_IR/groups.json b/public/language/fa_IR/groups.json index 3f4d7d1c2c..af5126d08a 100644 --- a/public/language/fa_IR/groups.json +++ b/public/language/fa_IR/groups.json @@ -6,6 +6,12 @@ "no_groups_found": "گروهی برای دیدن وجود ندارد", "pending.accept": "قبول", "pending.reject": "رد", + "pending.accept_all": "Accept All", + "pending.reject_all": "Reject All", + "pending.none": "There are no pending members at this time", + "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", "cover-instructions": "کشیدن و انداختن عکس، به محل بکشید، و ذخیره را بزنید", "cover-change": "تغییر", "cover-save": "ذخیره", @@ -13,6 +19,7 @@ "details.title": "جزئیات گروه", "details.members": "لیست اعضا", "details.pending": "اعضای در انتظار", + "details.invited": "Invited Members", "details.has_no_posts": "اعضای این گروه هیچ دیدگاهی ایجاد نکرده اند", "details.latest_posts": "آخرین دیدگاه ها", "details.private": "خصوصی", diff --git a/public/language/fa_IR/notifications.json b/public/language/fa_IR/notifications.json index 0775c24103..8a5cbf8863 100644 --- a/public/language/fa_IR/notifications.json +++ b/public/language/fa_IR/notifications.json @@ -20,6 +20,7 @@ "user_posted_topic": "%1 یک جستار جدید ارسال کرده: %2", "user_mentioned_you_in": "%1 در \n%1 mentioned you in %2 از شما نام برده", "user_started_following_you": "%1 شروع به دنبال کردن شما کرده", + "new_register": "%1 یک درخواست ثبت نام ارسال کرده است", "email-confirmed": "رایانامه تایید شد", "email-confirmed-message": "بابت تایید ایمیلتان سپاس‌گزاریم. حساب کاربری شما اکنون به صورت کامل فعال شده است.", "email-confirm-error-message": "خطایی در تایید آدرس ایمیل شما پیش آمده است. ممکن است کد نا‌معتبر و یا منقضی شده باشد.", diff --git a/public/language/fa_IR/register.json b/public/language/fa_IR/register.json index 4cd1d1cd9f..f1306c175b 100644 --- a/public/language/fa_IR/register.json +++ b/public/language/fa_IR/register.json @@ -14,5 +14,6 @@ "register_now_button": "اکنون نام‌نویسی کنید", "alternative_registration": "روش نام‌نویسی جایگزین", "terms_of_use": "شرایط استفاده", - "agree_to_terms_of_use": "با شرایط استفاده موافقم" + "agree_to_terms_of_use": "با شرایط استفاده موافقم", + "registration-added-to-queue": "ثبت نام شما به صف تایید اضافه شد. وقتی توسط یک مدیر تایید شد شما رایانامه ای دریافت خواهید کرد." } \ No newline at end of file diff --git a/public/language/fa_IR/topic.json b/public/language/fa_IR/topic.json index 3a26922060..849d261a67 100644 --- a/public/language/fa_IR/topic.json +++ b/public/language/fa_IR/topic.json @@ -5,7 +5,7 @@ "no_topics_found": "هیچ جستاری یافت نشد!", "no_posts_found": "دیدگاهی یافت نشد!", "post_is_deleted": "این دیدگاه پاک شده!", - "topic_is_deleted": "This topic is deleted!", + "topic_is_deleted": "جستار حذف شده است!", "profile": "نمایه", "posted_by": "ارسال شده توسط %1", "posted_by_guest": "ارسال شده توسط مهمان", diff --git a/public/language/fa_IR/unread.json b/public/language/fa_IR/unread.json index 4668db5f62..ae9ce77968 100644 --- a/public/language/fa_IR/unread.json +++ b/public/language/fa_IR/unread.json @@ -5,5 +5,6 @@ "mark_as_read": "خوانده شده بگیر", "selected": "برگزیده", "all": "همه", + "all_categories": "تمام دسته ها", "topics_marked_as_read.success": "همه جستارها خوانده شدند" } \ No newline at end of file diff --git a/public/language/fa_IR/user.json b/public/language/fa_IR/user.json index 2758770015..3f6f0a27ee 100644 --- a/public/language/fa_IR/user.json +++ b/public/language/fa_IR/user.json @@ -6,8 +6,12 @@ "postcount": "تعداد دیدگاه ها", "email": "رایانامه", "confirm_email": "تأیید رایانامه", + "ban_account": "Ban Account", + "ban_account_confirm": "Do you really want to ban this user?", + "unban_account": "Unban Account", "delete_account": "حذف حساب کاربری", "delete_account_confirm": "آیا مطمئنید که میخواهید حساب کاربری خود را حذف کنید؟
این عمل غیر قابل بازگشت است و شما قادر نخواهید بود هیچ کدام از اطلاعات خود را بازیابی کنید./strong>

برای تایید حذف این حساب کاربری، نام کاربری خود را وارد کنید", + "delete_this_account_confirm": "Are you sure you want to delete this account?
This action is irreversible and you will not be able to recover any data

", "fullname": "نام کامل", "website": "تارنما", "location": "محل سکونت", @@ -21,7 +25,7 @@ "watched": "پاییده شده", "followers": "دنبال‌کننده‌ها", "following": "دنبال‌شونده‌ها", - "aboutme": "About me", + "aboutme": "درباره ی من", "signature": "امضا", "gravatar": "گراواتار", "birthday": "روز تولد", @@ -64,21 +68,21 @@ "settings-require-reload": "تغییر برخی تنظیمات مستلزم بارگذاری مجدد هستند. برای بارگذاری مجدد صفحه اینجا کلیک کنید.", "has_no_follower": "این کاربر هیچ دنبال‌کننده‌ای ندارد :(", "follows_no_one": "این کاربر هیچ کسی را دنبال نمی‌کند :(", - "has_no_posts": "این کاربر هنوز هیچ دیدگاهی نگذاشته است.", - "has_no_topics": "این کاربر هنوز هیچ جستاری نفرستاده است.", - "has_no_watched_topics": "این کاربر هنوز هیچ جستاری را نپاییده است.", + "has_no_posts": "این کاربر تا به حال هیچ چیزی ارسال نکرده است.", + "has_no_topics": "این کاربر تا به حال هیچ جستاری ارسال نکرده است", + "has_no_watched_topics": "این کاربر تا به حال هیچ جستاری را نپاییده است", "email_hidden": "رایانامه پنهان شده", "hidden": "پنهان", "paginate_description": "Paginate topics and posts instead of using infinite scroll", "topics_per_page": "شمار جستارها در هر برگه", "posts_per_page": "شمار دیدگاه‌ها در هر برگه", - "notification_sounds": "Play a sound when you receive a notification", + "notification_sounds": "پخش صدا زمانی که یک آگاه سازی دریافت میکنید", "browsing": "تنظیمات مرور", - "open_links_in_new_tab": "Open outgoing links in new tab", + "open_links_in_new_tab": "پیوندهای به بیرون را در برگ جدید باز کن", "enable_topic_searching": "فعال کردن جستجوی داخل-جستار ", "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen", - "follow_topics_you_reply_to": "Follow topics that you reply to", - "follow_topics_you_create": "Follow topics you create", + "follow_topics_you_reply_to": "تاپیک هایی که پاسخ داده ای را دنبال کن", + "follow_topics_you_create": "جستارهایی که ایجاد کرده ای را دنبال کن", "grouptitle": "عنوان گروهی که میخواهید نشان داده شود را انتخاب کنید.", "no-group-title": "عنوان گروه ای نیست" } \ No newline at end of file diff --git a/public/language/fa_IR/users.json b/public/language/fa_IR/users.json index c313b79dd8..cb506c48bf 100644 --- a/public/language/fa_IR/users.json +++ b/public/language/fa_IR/users.json @@ -8,5 +8,14 @@ "users-found-search-took": "%1 کاربر(ها) یافت شد! جستجو %2 ثانیه طولید", "filter-by": "غربال با", "online-only": "فقط آنلاین", - "picture-only": "عکس فقط" + "picture-only": "عکس فقط", + "invite": "دعوت", + "invitation-email-sent": "رایانامه ی دعوتنامه به %1 ارسال شد", + "user_list": "فهرست کاربران", + "recent_topics": "جستارهای اخیر", + "popular_topics": "جستارهای محبوب", + "unread_topics": "جستارهای خوانده نشده", + "categories": "دسته ها", + "tags": "برچسب‌ها", + "map": "نقشه" } \ No newline at end of file diff --git a/public/language/fi/email.json b/public/language/fi/email.json index 50237b0d03..325a17f6be 100644 --- a/public/language/fi/email.json +++ b/public/language/fi/email.json @@ -1,11 +1,15 @@ { "password-reset-requested": "Pyydetty salasanan palautuskoodia - %1!", "welcome-to": "%1 - Tervetuloa", + "invite": "Invitation from %1", "greeting_no_name": "Hei", "greeting_with_name": "Hei %1", "welcome.text1": "Kiitos rekisteröitymisestäsi sivustolle %1!", "welcome.text2": "Meidän täytyy varmistaa, että omistat sen sähköpostiosoitteen, jolla rekisteröidyit, ennen kuin tilisi voidaan aktivoida kokonaan.", + "welcome.text3": "An administrator has accepted your registration application. You can login with your username/password now.", "welcome.cta": "Napsauta tästä vahvistaaksesi sähköpostiosoitteesi", + "invitation.text1": "%1 has invited you to join %2", + "invitation.ctr": "Click here to create your account.", "reset.text1": "We received a request to reset your password, possibly because you have forgotten it. If this is not the case, please ignore this email.", "reset.text2": "To continue with the password reset, please click on the following link:", "reset.cta": "Click here to reset your password", diff --git a/public/language/fi/error.json b/public/language/fi/error.json index 0c38939c8c..076978796a 100644 --- a/public/language/fi/error.json +++ b/public/language/fi/error.json @@ -51,6 +51,7 @@ "already-favourited": "Tämä viesti on jo suosikeissasi", "already-unfavourited": "Olet jo poistanut tämän viestin suosikeistasi", "cant-ban-other-admins": "Et voi estää muita ylläpitäjiä!", + "cant-remove-last-admin": "You are the only administrator. Add another user as an administrator before removing yourself as admin", "invalid-image-type": "Invalid image type. Allowed types are: %1", "invalid-image-extension": "Invalid image extension", "invalid-file-type": "Invalid file type. Allowed types are: %1", @@ -59,6 +60,8 @@ "group-name-change-not-allowed": "Et voi vaihtaa ryhmän nimeä", "group-already-member": "You are already part of this group", "group-needs-owner": "This group requires at least one owner", + "group-already-invited": "This user has already been invited", + "group-already-requested": "Your membership request has already been submitted", "post-already-deleted": "This post has already been deleted", "post-already-restored": "This post has already been restored", "topic-already-deleted": "This topic has already been deleted", @@ -76,6 +79,7 @@ "downvoting-disabled": "Downvoting is disabled", "not-enough-reputation-to-downvote": "You do not have enough reputation to downvote this post", "not-enough-reputation-to-flag": "You do not have enough reputation to flag this post", + "already-flagged": "You have already flagged this post", "reload-failed": "NodeBB encountered a problem while reloading: \"%1\". NodeBB will continue to serve the existing client-side assets, although you should undo what you did just prior to reloading.", "registration-error": "Registration Error", "parse-error": "Something went wrong while parsing server response", diff --git a/public/language/fi/global.json b/public/language/fi/global.json index b5823b4f45..fec566c64b 100644 --- a/public/language/fi/global.json +++ b/public/language/fi/global.json @@ -50,6 +50,7 @@ "views": "Katsottu", "reputation": "Maine", "read_more": "lue lisää", + "more": "More", "posted_ago_by_guest": "posted %1 by Guest", "posted_ago_by": "posted %1 by %2", "posted_ago": "lähetetty %1", diff --git a/public/language/fi/groups.json b/public/language/fi/groups.json index b320947545..019bc0bf24 100644 --- a/public/language/fi/groups.json +++ b/public/language/fi/groups.json @@ -6,6 +6,12 @@ "no_groups_found": "Ei ryhmiä", "pending.accept": "Accept", "pending.reject": "Reject", + "pending.accept_all": "Accept All", + "pending.reject_all": "Reject All", + "pending.none": "There are no pending members at this time", + "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", "cover-instructions": "Vedä ja pudota kuva, raahaa oikeaan kohtaan ja paina Tallenna", "cover-change": "Change", "cover-save": "Tallenna", @@ -13,6 +19,7 @@ "details.title": "Ryhmän tiedot", "details.members": "Jäsenluettelo", "details.pending": "Pending Members", + "details.invited": "Invited Members", "details.has_no_posts": "This group's members have not made any posts.", "details.latest_posts": "Uusimmat viestit", "details.private": "Private", diff --git a/public/language/fi/notifications.json b/public/language/fi/notifications.json index 2b45a426b6..6a7b3e01ad 100644 --- a/public/language/fi/notifications.json +++ b/public/language/fi/notifications.json @@ -20,6 +20,7 @@ "user_posted_topic": "%1 on kirjoittanut uuden aiheen: %2", "user_mentioned_you_in": "%1 mainitsi sinut viestissä %2", "user_started_following_you": "%1 alkoi seurata sinua.", + "new_register": "%1 sent a registration request.", "email-confirmed": "Sähköpostiosoite vahvistettu", "email-confirmed-message": "Kiitos sähköpostiosoitteesi vahvistamisesta. Käyttäjätilisi on nyt täysin aktivoitu.", "email-confirm-error-message": "Ongelma sähköpostiosoitteen vahvistamisessa. Ehkäpä koodi oli virheellinen tai vanhentunut.", diff --git a/public/language/fi/register.json b/public/language/fi/register.json index a07f0d311e..f7d7ab7e08 100644 --- a/public/language/fi/register.json +++ b/public/language/fi/register.json @@ -14,5 +14,6 @@ "register_now_button": "Rekisteröidy nyt", "alternative_registration": "Vaihtoehtoiset rekisteröitymistavat", "terms_of_use": "Käyttöehdot", - "agree_to_terms_of_use": "Hyväksyn käyttöehdot" + "agree_to_terms_of_use": "Hyväksyn käyttöehdot", + "registration-added-to-queue": "Your registration has been added to the approval queue. You will receive an email when it is accepted by an administrator." } \ No newline at end of file diff --git a/public/language/fi/unread.json b/public/language/fi/unread.json index 73a53aa684..5588fce2c3 100644 --- a/public/language/fi/unread.json +++ b/public/language/fi/unread.json @@ -5,5 +5,6 @@ "mark_as_read": "Merkitse luetuksi", "selected": "Valitut", "all": "Kaikki", + "all_categories": "All categories", "topics_marked_as_read.success": "Aihe merkitty luetuksi!" } \ No newline at end of file diff --git a/public/language/fi/user.json b/public/language/fi/user.json index 302f503beb..3ffc13dfe0 100644 --- a/public/language/fi/user.json +++ b/public/language/fi/user.json @@ -6,8 +6,12 @@ "postcount": "Viestien määrä", "email": "Sähköposti", "confirm_email": "Vahvista sähköpostiosoite", + "ban_account": "Ban Account", + "ban_account_confirm": "Do you really want to ban this user?", + "unban_account": "Unban Account", "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

", "fullname": "Koko nimi", "website": "Kotisivu", "location": "Sijainti", @@ -64,9 +68,9 @@ "settings-require-reload": "Some setting changes require a reload. Click here to reload the page.", "has_no_follower": "Kukaan ei seuraa tätä käyttäjää :(", "follows_no_one": "Tämä käyttäjä ei seuraa ketään :(", - "has_no_posts": "Tämä käyttäjä ei ole kirjoittanut vielä mitään.", - "has_no_topics": "Tämä käyttäjä ei ole kirjoittanut vielä yhtään aihetta.", - "has_no_watched_topics": "Tämä käyttäjä ei seuraa vielä yhtään aihetta.", + "has_no_posts": "This user hasn't posted anything yet.", + "has_no_topics": "This user hasn't posted any topics yet.", + "has_no_watched_topics": "This user hasn't watched any topics yet.", "email_hidden": "Sähköposti piilotettu", "hidden": "piilotettu", "paginate_description": "Paginate topics and posts instead of using infinite scroll", diff --git a/public/language/fi/users.json b/public/language/fi/users.json index fe90faeaeb..f17d4c996b 100644 --- a/public/language/fi/users.json +++ b/public/language/fi/users.json @@ -8,5 +8,14 @@ "users-found-search-took": "%1 käyttäjä(ä) löytyi! Haku kesti %2 sekuntia.", "filter-by": "Suodata", "online-only": "Vain verkossa olevat", - "picture-only": "Picture only" + "picture-only": "Picture only", + "invite": "Invite", + "invitation-email-sent": "An invitation email has been sent to %1", + "user_list": "User List", + "recent_topics": "Recent Topics", + "popular_topics": "Popular Topics", + "unread_topics": "Unread Topics", + "categories": "Categories", + "tags": "Tags", + "map": "Map" } \ No newline at end of file diff --git a/public/language/fr/email.json b/public/language/fr/email.json index a3b80ecd21..6786d4dee8 100644 --- a/public/language/fr/email.json +++ b/public/language/fr/email.json @@ -1,11 +1,15 @@ { "password-reset-requested": "Demande de réinitialisation du mot de passe - %1!", "welcome-to": "Bienvenue sur %1", + "invite": "Invitation de %1", "greeting_no_name": "Bonjour", "greeting_with_name": "Bonjour %1", "welcome.text1": "Merci de vous être inscrit sur %1!", "welcome.text2": "Pour activer totalement votre compte, nous devons vérifier que vous êtes bien propriétaire de l'adresse email que vous avez utilisé pour vous inscrire.", + "welcome.text3": "Un administrateur a accepté votre demande d'inscription. Vous pouvez maintenant vous connecter avec vos identifiants/mots de passe.", "welcome.cta": "Cliquez ici pour confirmer votre adresse email", + "invitation.text1": "%1 vous a invité à joindre %2", + "invitation.ctr": "Cliquer ici pour créer votre compte.", "reset.text1": "Nous avons reçu une demande de réinitialisation de votre mot de passe, probablement parce que vous l'avez oublié. Si ce n'est pas le cas, veuillez ignorer cet email.", "reset.text2": "Pour confirmer la réinitialisation de votre mot de passe, veuillez cliquer sur le lien suivant :", "reset.cta": "Cliquez ici pour réinitialiser votre mot de passe", diff --git a/public/language/fr/error.json b/public/language/fr/error.json index dd79c11b72..ac1ac582aa 100644 --- a/public/language/fr/error.json +++ b/public/language/fr/error.json @@ -51,6 +51,7 @@ "already-favourited": "Vous avez déjà mis ce message en favoris", "already-unfavourited": "Vous avez déjà retiré ce message des favoris", "cant-ban-other-admins": "Vous ne pouvez pas bannir les autres administrateurs !", + "cant-remove-last-admin": "Vous seul êtes administrateur. Ajouter un autre utilisateur en tant qu'administrateur avant de vous en retirer.", "invalid-image-type": "Type d'image invalide. Les types autorisés sont: %1", "invalid-image-extension": "Extension d'image invalide", "invalid-file-type": "Type de fichier non valide. Les types autorisés sont : %1", @@ -59,6 +60,8 @@ "group-name-change-not-allowed": "Modification du nom de groupe non permise", "group-already-member": "Vous faites déjà parti de ce groupe", "group-needs-owner": "Ce groupe nécessite au moins un propriétaire", + "group-already-invited": "Cet utilisateur a déjà été invité.", + "group-already-requested": "Votre demande d'adhésion a déjà été envoyée.", "post-already-deleted": "Message déjà supprimé", "post-already-restored": "Message déjà restauré", "topic-already-deleted": "Sujet déjà supprimé", @@ -67,8 +70,8 @@ "topic-thumbnails-are-disabled": "Les miniatures de sujet sont désactivés", "invalid-file": "Fichier invalide", "uploads-are-disabled": "Les envois sont désactivés", - "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", - "about-me-too-long": "Sorry, your about me cannot be longer than %1 character(s).", + "signature-too-long": "La signature ne peut dépasser %1 caractère(s).", + "about-me-too-long": "Votre texte \"à propos de moi\" ne peut dépasser %1 caractère(s).", "cant-chat-with-yourself": "Vous ne pouvez chatter avec vous même !", "chat-restricted": "Cet utilisateur a restreint les ses messages de chat. Il doit d'abord vous suivre avant de pouvoir discuter avec lui.", "too-many-messages": "Vous avez envoyé trop de messages, veuillez patienter un instant.", @@ -76,6 +79,7 @@ "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", "not-enough-reputation-to-flag": "Vous n'avez pas une réputation assez élevée pour signaler ce message", + "already-flagged": "Vous avez déjà signalé ce message", "reload-failed": "NodeBB a rencontré un problème lors du rechargement : \"% 1\" . NodeBB continuera de fonctionner côté client, même si vous devez annuler ce que vous avez fait juste avant de recharger .", "registration-error": "Erreur d'enregistrement", "parse-error": "Une erreur est survenue en analysant la réponse du serveur", diff --git a/public/language/fr/global.json b/public/language/fr/global.json index d09ab26cfc..713ae56634 100644 --- a/public/language/fr/global.json +++ b/public/language/fr/global.json @@ -50,6 +50,7 @@ "views": "Vues", "reputation": "Réputation", "read_more": "En lire plus", + "more": "Plus", "posted_ago_by_guest": "posté %1 par un invité", "posted_ago_by": "posté %1 par %2", "posted_ago": "posté %1", diff --git a/public/language/fr/groups.json b/public/language/fr/groups.json index 39b963ae57..0315245c6b 100644 --- a/public/language/fr/groups.json +++ b/public/language/fr/groups.json @@ -6,6 +6,12 @@ "no_groups_found": "Il n'y a aucun groupe", "pending.accept": "Accepter", "pending.reject": "Refuser", + "pending.accept_all": "Tout accepter", + "pending.reject_all": "Tout rejeter", + "pending.none": "Il n'y a aucun membre en attente pour le moment", + "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", "cover-instructions": "Glissez-déposez une image, ajustez la position, et cliquez sur Enregistrer", "cover-change": "Modifier", "cover-save": "Enregistrer", @@ -13,6 +19,7 @@ "details.title": "Informations du groupe", "details.members": "Liste des membres", "details.pending": "Membres en attente", + "details.invited": "Inviter des Membres", "details.has_no_posts": "Les membres de ce groupe n'ont envoyé aucun message.", "details.latest_posts": "Derniers messages", "details.private": "Privé", diff --git a/public/language/fr/notifications.json b/public/language/fr/notifications.json index e6c533a20e..217f84ecc6 100644 --- a/public/language/fr/notifications.json +++ b/public/language/fr/notifications.json @@ -20,6 +20,7 @@ "user_posted_topic": "%1 a posté un nouveau sujet: %2.", "user_mentioned_you_in": "%1 vous a mentionné dans %2", "user_started_following_you": "%1 vous suit.", + "new_register": "%1 a envoyé une demande d'incription.", "email-confirmed": "Email vérifié", "email-confirmed-message": "Merci pour la validation de votre adresse email. Votre compte est désormais activé.", "email-confirm-error-message": "Il y a un un problème dans la vérification de votre adresse email. Le code est peut être invalide ou a expiré.", diff --git a/public/language/fr/register.json b/public/language/fr/register.json index 1c2c310e03..82a8eb27f5 100644 --- a/public/language/fr/register.json +++ b/public/language/fr/register.json @@ -14,5 +14,6 @@ "register_now_button": "S'inscrire", "alternative_registration": "Autres méthodes d'inscription", "terms_of_use": "Conditions d'utilisation", - "agree_to_terms_of_use": "J'accepte les Conditions d'utilisation" + "agree_to_terms_of_use": "J'accepte les Conditions d'utilisation", + "registration-added-to-queue": "Votre inscription a été ajoutée à la liste d'approbation. Vous recevrez un email quand celle-ci sera acceptée par un administrateur." } \ No newline at end of file diff --git a/public/language/fr/unread.json b/public/language/fr/unread.json index 187407f3b2..9432bf9d36 100644 --- a/public/language/fr/unread.json +++ b/public/language/fr/unread.json @@ -5,5 +5,6 @@ "mark_as_read": "Marquer comme lu", "selected": "Sélectionnés", "all": "Tous", + "all_categories": "Toutes Catégories", "topics_marked_as_read.success": "Sujets marqués comme lus !" } \ No newline at end of file diff --git a/public/language/fr/user.json b/public/language/fr/user.json index 486ee48178..c4f02fb5a3 100644 --- a/public/language/fr/user.json +++ b/public/language/fr/user.json @@ -6,8 +6,12 @@ "postcount": "Nombre de messages", "email": "Email", "confirm_email": "Confirmer l'adresse email", + "ban_account": "Bannir", + "ban_account_confirm": "Êtes-vous sûr de bien vouloir bannir cet utilisateur ?", + "unban_account": "Restaurer le Compte", "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.", "fullname": "Nom", "website": "Site web", "location": "Emplacement", @@ -64,9 +68,9 @@ "settings-require-reload": "Certains réglages nécessitent un rechargement. Cliquez ici pour recharger la page.", "has_no_follower": "Cet utilisateur n'est suivi par personne :(", "follows_no_one": "Cet utilisateur ne suit personne :(", - "has_no_posts": "Ce membre n'a rien posté pour le moment", - "has_no_topics": "L’utilisateur n'a pas encore créé de sujet.", - "has_no_watched_topics": "Cet utilisateur ne suis encore aucun sujet.", + "has_no_posts": "Cet utilisateur n'a encore rien posté.", + "has_no_topics": "Cet utilisateur n'a encore créé aucun sujet.", + "has_no_watched_topics": "Cet utilisateur n'a encore consulté aucun sujet.", "email_hidden": "Email masqué", "hidden": "masqué", "paginate_description": "Utiliser la pagination des sujets et des messages à la place du défilement infini.", diff --git a/public/language/fr/users.json b/public/language/fr/users.json index 3467b5b864..fdcd16a1d8 100644 --- a/public/language/fr/users.json +++ b/public/language/fr/users.json @@ -8,5 +8,14 @@ "users-found-search-took": "%1 utilisateur(s) trouvé(s)! La recherche a pris %2 secondes.", "filter-by": "Filtrer par", "online-only": "En ligne uniquement", - "picture-only": "Avec image uniquement" + "picture-only": "Avec image uniquement", + "invite": "Invitation", + "invitation-email-sent": "Un email d'invitation a été envoyé à %1", + "user_list": "Liste d'Utilisateurs", + "recent_topics": "Sujets Récents", + "popular_topics": "Sujets Populaires", + "unread_topics": "Sujets Non-Lus", + "categories": "Catégories", + "tags": "Mots-clés", + "map": "Carte" } \ No newline at end of file diff --git a/public/language/he/email.json b/public/language/he/email.json index 8743ba21d8..25d4bc782f 100644 --- a/public/language/he/email.json +++ b/public/language/he/email.json @@ -1,11 +1,15 @@ { "password-reset-requested": "בקשת איפוס הסיסמה נשלחה - %1!", "welcome-to": "ברוכים הבאים ל%1", + "invite": "Invitation from %1", "greeting_no_name": "שלום", "greeting_with_name": "שלום %1", "welcome.text1": "תודה שנרשמת ל%1!", "welcome.text2": "על מנת להפעיל את החשבון שלך, אנו צריכים לוודא שאתה בעל חשבון המייל שנרשמת איתו.", + "welcome.text3": "An administrator has accepted your registration application. You can login with your username/password now.", "welcome.cta": "לחץ כאן על מנת לאשר את כתובת המייל שלך.", + "invitation.text1": "%1 has invited you to join %2", + "invitation.ctr": "Click here to create your account.", "reset.text1": "קיבלנו בקשה לאפס את הסיסמה לחשבון שלך, כנראה מפני ששכחת אותה. אם לא ביקשת לאפס את הסיסמה, אנא התעלם ממייל זה.", "reset.text2": "על מנת להמשיך עם תהליך איפוס הסיסמה, אנא לחץ על הלינק הבא:", "reset.cta": "לחץ כאן לאפס את הסיסמה שלך.", diff --git a/public/language/he/error.json b/public/language/he/error.json index ebe39d885d..9086b3bd09 100644 --- a/public/language/he/error.json +++ b/public/language/he/error.json @@ -51,6 +51,7 @@ "already-favourited": "כבר הוספת פוסט זה למועדפים", "already-unfavourited": "כבר הסרת פוסט זה מהמועדפים", "cant-ban-other-admins": "אינך יכול לחסום מנהלים אחרים!", + "cant-remove-last-admin": "You are the only administrator. Add another user as an administrator before removing yourself as admin", "invalid-image-type": "פורמט תמונה לא תקין. הפורמטים המורשים הם: %1", "invalid-image-extension": "פורמט תמונה לא תקין", "invalid-file-type": "Invalid file type. Allowed types are: %1", @@ -59,6 +60,8 @@ "group-name-change-not-allowed": "לא ניתן לשנות את שם הקבוצה", "group-already-member": "אתה כבר חבר בקבוצה זו", "group-needs-owner": "קבוצה זו חייבת לפחות מנהל אחד", + "group-already-invited": "This user has already been invited", + "group-already-requested": "Your membership request has already been submitted", "post-already-deleted": "פוסט זה כבר נמחק", "post-already-restored": "פוסט זה כבר שוחזר", "topic-already-deleted": "נושא זה כבר נמחק", @@ -76,6 +79,7 @@ "downvoting-disabled": "היכולת להצביע נגד לא פעילה", "not-enough-reputation-to-downvote": "אין לך מספיק מוניטין כדי להוריד את הדירוג של פוסט זה", "not-enough-reputation-to-flag": "אין לך מוניטין מספק על מנת לסמן את הפוסט הזה", + "already-flagged": "You have already flagged this post", "reload-failed": "אירעה תקלה ב NodeBB בזמן הטעינה של: \"%1\". המערכת תמשיך להגיש דפים קיימים, אבל כדאי שתשחזר את הפעולות שלך מהפעם האחרונה שהמערכת עבדה כראוי.", "registration-error": "שגיאה בהרשמה", "parse-error": "אירעה שגיאה בעת בעת ניתוח תגובת השרת", diff --git a/public/language/he/global.json b/public/language/he/global.json index da7f284644..2769a2447a 100644 --- a/public/language/he/global.json +++ b/public/language/he/global.json @@ -50,6 +50,7 @@ "views": "צפיות", "reputation": "מוניטין", "read_more": "קרא עוד", + "more": "More", "posted_ago_by_guest": "הפוסט הועלה ב %1 על ידי אורח", "posted_ago_by": "הפוסט עלה ב %1 על ידי %2", "posted_ago": "הפוסט הועלה ב %1", diff --git a/public/language/he/groups.json b/public/language/he/groups.json index bb4e535bb8..ea3f3712c6 100644 --- a/public/language/he/groups.json +++ b/public/language/he/groups.json @@ -6,6 +6,12 @@ "no_groups_found": "אין קבוצות לצפייה", "pending.accept": "Accept", "pending.reject": "Reject", + "pending.accept_all": "Accept All", + "pending.reject_all": "Reject All", + "pending.none": "There are no pending members at this time", + "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", "cover-instructions": "גרור תמונה, הזז למיקום הרצוי ואז לחץ על שמור", "cover-change": "שנה", "cover-save": "שמור", @@ -13,6 +19,7 @@ "details.title": "פרטי הקבוצה", "details.members": "רשימת חברי הקבוצה", "details.pending": "חברי קבוצה הממתינים לאישור", + "details.invited": "Invited Members", "details.has_no_posts": "חברי הקבוצה הזו לא העלו אף פוסט.", "details.latest_posts": "פוסטים אחרונים", "details.private": "Private", diff --git a/public/language/he/notifications.json b/public/language/he/notifications.json index 215356f8e4..358c7585ca 100644 --- a/public/language/he/notifications.json +++ b/public/language/he/notifications.json @@ -20,6 +20,7 @@ "user_posted_topic": "%1 העלה נושא חדש: %2", "user_mentioned_you_in": "%1 הזכיר אותך ב %2", "user_started_following_you": "%1 התחיל לעקוב אחריך.", + "new_register": "%1 sent a registration request.", "email-confirmed": "כתובת המייל אושרה", "email-confirmed-message": "תודה שאישרת את כתובת המייל שלך. החשבון שלך פעיל כעת.", "email-confirm-error-message": "אירעה שגיאה בעת אישור המייל שלך. ייתכן כי הקוד היה שגוי או פג תוקף.", diff --git a/public/language/he/register.json b/public/language/he/register.json index 833a93f81b..83d7581079 100644 --- a/public/language/he/register.json +++ b/public/language/he/register.json @@ -14,5 +14,6 @@ "register_now_button": "הרשם עכשיו", "alternative_registration": "הרשם באמצעות...", "terms_of_use": "תנאי שימוש", - "agree_to_terms_of_use": "אני מסכים לתנאי השימוש" + "agree_to_terms_of_use": "אני מסכים לתנאי השימוש", + "registration-added-to-queue": "Your registration has been added to the approval queue. You will receive an email when it is accepted by an administrator." } \ No newline at end of file diff --git a/public/language/he/unread.json b/public/language/he/unread.json index e5847601dc..5b4fd51ead 100644 --- a/public/language/he/unread.json +++ b/public/language/he/unread.json @@ -5,5 +5,6 @@ "mark_as_read": "סמן כנקרא", "selected": "נבחר", "all": "הכל", + "all_categories": "All categories", "topics_marked_as_read.success": "נושאים שמסומנים כנקרא!" } \ No newline at end of file diff --git a/public/language/he/user.json b/public/language/he/user.json index 6011356044..0481813d4b 100644 --- a/public/language/he/user.json +++ b/public/language/he/user.json @@ -6,8 +6,12 @@ "postcount": "כמות פוסטים", "email": "כתובת אימייל", "confirm_email": "אשר מייל", + "ban_account": "Ban Account", + "ban_account_confirm": "Do you really want to ban this user?", + "unban_account": "Unban Account", "delete_account": "מחק חשבון", "delete_account_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

", "fullname": "שם מלא", "website": "אתר", "location": "מיקום", @@ -64,9 +68,9 @@ "settings-require-reload": "Some setting changes require a reload. Click here to reload the page.", "has_no_follower": "למשתמש זה אין עוקבים :(", "follows_no_one": "משתמש זה אינו עוקב אחרי אחרים :(", - "has_no_posts": "המשתמש הזה עוד לא פרסם כלום.", - "has_no_topics": "המשתמש טרם יצר נושאים כלשהם.", - "has_no_watched_topics": "משתמש זה לא עקב אחרי אף נושא עדיין.", + "has_no_posts": "This user hasn't posted anything yet.", + "has_no_topics": "This user hasn't posted any topics yet.", + "has_no_watched_topics": "This user hasn't watched any topics yet.", "email_hidden": "כתובת אימייל מוסתרת", "hidden": "מוסתר", "paginate_description": "Paginate topics and posts instead of using infinite scroll", diff --git a/public/language/he/users.json b/public/language/he/users.json index 5b996d5f5d..e21b577d55 100644 --- a/public/language/he/users.json +++ b/public/language/he/users.json @@ -8,5 +8,14 @@ "users-found-search-took": "%1 משתמשים נמצאו! החיפוש ערך %2 שניות.", "filter-by": "פלטר על-פי", "online-only": "אונליין בלבד", - "picture-only": "תמונה בלבד" + "picture-only": "תמונה בלבד", + "invite": "Invite", + "invitation-email-sent": "An invitation email has been sent to %1", + "user_list": "User List", + "recent_topics": "Recent Topics", + "popular_topics": "Popular Topics", + "unread_topics": "Unread Topics", + "categories": "Categories", + "tags": "Tags", + "map": "Map" } \ No newline at end of file diff --git a/public/language/hu/email.json b/public/language/hu/email.json index 45c6be2630..408167ce27 100644 --- a/public/language/hu/email.json +++ b/public/language/hu/email.json @@ -1,11 +1,15 @@ { "password-reset-requested": "Jelszó-visszaállítási kérelem - %1!", "welcome-to": "Üdvözlet a %1-on", + "invite": "Invitation from %1", "greeting_no_name": "Üdvözlet", "greeting_with_name": "Üdvözlet %1", "welcome.text1": "Köszönjük a regisztrációt %1!", "welcome.text2": "A fiók aktiválásához ellenőriznünk kell, hogy valós e-mail cím lett e megadva.", + "welcome.text3": "An administrator has accepted your registration application. You can login with your username/password now.", "welcome.cta": "Kattintsunk ide az e-mail cím megerősítéséhez", + "invitation.text1": "%1 has invited you to join %2", + "invitation.ctr": "Click here to create your account.", "reset.text1": "Kaptunk egy kérést jelszava visszaállításához, valószínűleg azért, mert elfelejtette azt. Ha ez nem így van, hagyja figyelmen kívül ezt a levelet.", "reset.text2": "Ha szeretné, hogy továbbra alaphelyzetbe a jelszavát, kérjük kattintson az alábbi linkre:", "reset.cta": "Kattints ide a jelszavad visszaállításához", diff --git a/public/language/hu/error.json b/public/language/hu/error.json index 4f6a148af5..8ca06e3d51 100644 --- a/public/language/hu/error.json +++ b/public/language/hu/error.json @@ -51,6 +51,7 @@ "already-favourited": "Már bejelölted Kedvencnek ezt a hozzászólást", "already-unfavourited": "Már kivetted a Kedvenceid közül ezt a hozzászólást", "cant-ban-other-admins": "Nem tilthatsz ki másik adminisztrátort!", + "cant-remove-last-admin": "You are the only administrator. Add another user as an administrator before removing yourself as admin", "invalid-image-type": "Érvénytelen a kép típusa. Engedett kiterjesztések: %1", "invalid-image-extension": "Érvénytelen a kép kiterjesztése", "invalid-file-type": "Érvénytelen a fájl típusa. Engedélyezett kiterjesztések: %1", @@ -59,6 +60,8 @@ "group-name-change-not-allowed": "A csoport névváltoztatás nem engedélyezett", "group-already-member": "Már a tagja vagy ennek a csoportnak", "group-needs-owner": "Ennek a csoportnak lennie kell legalább egy tulajdonosnak.", + "group-already-invited": "This user has already been invited", + "group-already-requested": "Your membership request has already been submitted", "post-already-deleted": "Ez a bejegyzés mát törlésre került", "post-already-restored": "Ez a bejegyzés már visszaállításra került", "topic-already-deleted": "Ezt a témakör már törlésre került", @@ -76,6 +79,7 @@ "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", "not-enough-reputation-to-flag": "Nem rendelkezel elég Hírnév ponttal, hogy jelentsd ezt a hozzászólást", + "already-flagged": "You have already flagged this post", "reload-failed": "NodeBB egy hibát észlelt újratöltés közben: \"% 1\". A fórum továbbra is kiszolgálja a kliens-oldali eszközöket, bár vissza kellene csinálnod amit az újratöltés előtt elállítottál.", "registration-error": "Regisztrációs hiba", "parse-error": "Hiba történt a szerver válaszának feldolgozása közben", diff --git a/public/language/hu/global.json b/public/language/hu/global.json index ef775f0510..1411d5d09f 100644 --- a/public/language/hu/global.json +++ b/public/language/hu/global.json @@ -50,6 +50,7 @@ "views": "Megtekintések", "reputation": "Hírnév", "read_more": "tovább olvas", + "more": "More", "posted_ago_by_guest": "posted %1 by Guest", "posted_ago_by": "posted %1 by %2", "posted_ago": "posted %1", diff --git a/public/language/hu/groups.json b/public/language/hu/groups.json index 4fff97f77a..1865ef5e5d 100644 --- a/public/language/hu/groups.json +++ b/public/language/hu/groups.json @@ -6,6 +6,12 @@ "no_groups_found": "There are no groups to see", "pending.accept": "Accept", "pending.reject": "Reject", + "pending.accept_all": "Accept All", + "pending.reject_all": "Reject All", + "pending.none": "There are no pending members at this time", + "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", "cover-instructions": "Drag and Drop a photo, drag to position, and hit Save", "cover-change": "Change", "cover-save": "Save", @@ -13,6 +19,7 @@ "details.title": "Csoport részletek", "details.members": "Felhasználók", "details.pending": "Pending Members", + "details.invited": "Invited Members", "details.has_no_posts": "A csoport felhasználói nem tettek még közzé bejegyzést.", "details.latest_posts": "Legújabb bejegyzések", "details.private": "Private", diff --git a/public/language/hu/notifications.json b/public/language/hu/notifications.json index f991c637f6..dd364e0426 100644 --- a/public/language/hu/notifications.json +++ b/public/language/hu/notifications.json @@ -20,6 +20,7 @@ "user_posted_topic": "%1 has posted a new topic: %2", "user_mentioned_you_in": "%1 mentioned you in %2", "user_started_following_you": "%1 started following you.", + "new_register": "%1 sent a registration request.", "email-confirmed": "Email Confirmed", "email-confirmed-message": "Thank you for validating your email. Your account is now fully activated.", "email-confirm-error-message": "There was a problem validating your email address. Perhaps the code was invalid or has expired.", diff --git a/public/language/hu/register.json b/public/language/hu/register.json index 90fdd246b9..a14ce476ca 100644 --- a/public/language/hu/register.json +++ b/public/language/hu/register.json @@ -14,5 +14,6 @@ "register_now_button": "Regisztrálás Most", "alternative_registration": "Alternatív Regisztráció", "terms_of_use": "Használtai feltételek", - "agree_to_terms_of_use": "Egyetértek a Használtai Feltételekkel" + "agree_to_terms_of_use": "Egyetértek a Használtai Feltételekkel", + "registration-added-to-queue": "Your registration has been added to the approval queue. You will receive an email when it is accepted by an administrator." } \ No newline at end of file diff --git a/public/language/hu/unread.json b/public/language/hu/unread.json index ea769c0960..8bf28e0be6 100644 --- a/public/language/hu/unread.json +++ b/public/language/hu/unread.json @@ -5,5 +5,6 @@ "mark_as_read": "Olvasottnak jelölés", "selected": "Kiválasztva", "all": "Összes", + "all_categories": "All categories", "topics_marked_as_read.success": "Témakör olvasottnak jelölve!" } \ No newline at end of file diff --git a/public/language/hu/user.json b/public/language/hu/user.json index b6f9906710..64f60f4a44 100644 --- a/public/language/hu/user.json +++ b/public/language/hu/user.json @@ -6,8 +6,12 @@ "postcount": "Bejegyzés megtekintés", "email": "E-mail", "confirm_email": "E-mail megerősítése", + "ban_account": "Ban Account", + "ban_account_confirm": "Do you really want to ban this user?", + "unban_account": "Unban Account", "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

", "fullname": "Teljes Név", "website": "Weboldal", "location": "Lakhely", @@ -64,9 +68,9 @@ "settings-require-reload": "Some setting changes require a reload. Click here to reload the page.", "has_no_follower": "Ezt a felhasználót nem követi senki :(", "follows_no_one": "Ez a felhasználó nem követ senkit :(", - "has_no_posts": "Ennek a felhasználónak még nincsen hozzászólása.", - "has_no_topics": "Ez a felhasználó nem nyitott egyetlen témát sem még.", - "has_no_watched_topics": "Ez a felhasználó nem figyel egyetlen témát sem még.", + "has_no_posts": "This user hasn't posted anything yet.", + "has_no_topics": "This user hasn't posted any topics yet.", + "has_no_watched_topics": "This user hasn't watched any topics yet.", "email_hidden": "E-mail rejtett", "hidden": "rejtett", "paginate_description": "Paginate topics and posts instead of using infinite scroll", diff --git a/public/language/hu/users.json b/public/language/hu/users.json index 95e721a4d7..cbaabd7015 100644 --- a/public/language/hu/users.json +++ b/public/language/hu/users.json @@ -8,5 +8,14 @@ "users-found-search-took": "%1 user(s) found! Search took %2 seconds.", "filter-by": "Filter By", "online-only": "Online only", - "picture-only": "Picture only" + "picture-only": "Picture only", + "invite": "Invite", + "invitation-email-sent": "An invitation email has been sent to %1", + "user_list": "User List", + "recent_topics": "Recent Topics", + "popular_topics": "Popular Topics", + "unread_topics": "Unread Topics", + "categories": "Categories", + "tags": "Tags", + "map": "Map" } \ No newline at end of file diff --git a/public/language/id/email.json b/public/language/id/email.json index 481f412f23..5d47a185d0 100644 --- a/public/language/id/email.json +++ b/public/language/id/email.json @@ -1,11 +1,15 @@ { "password-reset-requested": "Permintaan atur ulang Kata Sandi - %1!", "welcome-to": "Selamat datang di %1", + "invite": "Invitation from %1", "greeting_no_name": "Hai", "greeting_with_name": "Hai %1", "welcome.text1": "Terima kasih anda telah mendaftarkan diri anda dengan %1!", "welcome.text2": "Untuk mengaktifkan akun anda sepenuhnya, kami perlu memverifkasi bahwa anda adalah pemilik email yang terdaftar.", + "welcome.text3": "An administrator has accepted your registration application. You can login with your username/password now.", "welcome.cta": "Klik disini untuk mengkonfirmasi alamat email anda.", + "invitation.text1": "%1 has invited you to join %2", + "invitation.ctr": "Click here to create your account.", "reset.text1": "Kami menerima permintan untuk mengatur ulang kata sandi anda, Ini dikarenakan anda telah lupa akan kata sandi anda. Tolong abaikan email ini jika sebaliknya.", "reset.text2": "Mohon klik link berikut untuk mengatur ulang kata sandi anda.", "reset.cta": "Klik di sini untuk mengatur ulang kata sandi anda", diff --git a/public/language/id/error.json b/public/language/id/error.json index 531b335a78..c324329b12 100644 --- a/public/language/id/error.json +++ b/public/language/id/error.json @@ -51,6 +51,7 @@ "already-favourited": "Post ini sudah kamu favorit", "already-unfavourited": "Postingan ini sudah kamu unfavorit", "cant-ban-other-admins": "Kamu tidak dapat ban admin lainnya!", + "cant-remove-last-admin": "You are the only administrator. Add another user as an administrator before removing yourself as admin", "invalid-image-type": "Invalid image type. Allowed types are: %1", "invalid-image-extension": "Invalid image extension", "invalid-file-type": "Invalid file type. Allowed types are: %1", @@ -59,6 +60,8 @@ "group-name-change-not-allowed": "Perubahan nama grup tidak dibolehkan", "group-already-member": "You are already part of this group", "group-needs-owner": "This group requires at least one owner", + "group-already-invited": "This user has already been invited", + "group-already-requested": "Your membership request has already been submitted", "post-already-deleted": "Postingan ini sudah dihapus", "post-already-restored": "Postingan ini sudah direstore", "topic-already-deleted": "Topik ini sudah dihapus", @@ -76,6 +79,7 @@ "downvoting-disabled": "Downvoting ditiadakan", "not-enough-reputation-to-downvote": "Tidak cukup reputation untuk downvote post ini", "not-enough-reputation-to-flag": "Tidak cukup reputation untuk flag post ini", + "already-flagged": "You have already flagged this post", "reload-failed": "NodeBB mengalami masalah saat memuat \"%1\". NodeBB akan melanjutkan pemuatan, kamu harus membatalkan tindakanmu sebelum pemuatan kembali dilakukan.", "registration-error": "Registrasti Error", "parse-error": "Something went wrong while parsing server response", diff --git a/public/language/id/global.json b/public/language/id/global.json index 738fa81207..2916e6b6a8 100644 --- a/public/language/id/global.json +++ b/public/language/id/global.json @@ -50,6 +50,7 @@ "views": "Views", "reputation": "Reputasi", "read_more": "selengkapnya", + "more": "More", "posted_ago_by_guest": "dibuat %1 oleh Guest", "posted_ago_by": "dibuat %1 oleh %2", "posted_ago": "dibuat %1", diff --git a/public/language/id/groups.json b/public/language/id/groups.json index 8cf87045b7..afc68e933b 100644 --- a/public/language/id/groups.json +++ b/public/language/id/groups.json @@ -6,6 +6,12 @@ "no_groups_found": "There are no groups to see", "pending.accept": "Accept", "pending.reject": "Reject", + "pending.accept_all": "Accept All", + "pending.reject_all": "Reject All", + "pending.none": "There are no pending members at this time", + "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", "cover-instructions": "Drag and Drop a photo, drag to position, and hit Save", "cover-change": "Change", "cover-save": "Save", @@ -13,6 +19,7 @@ "details.title": "Rincian Grup", "details.members": "Daftar Anggota", "details.pending": "Pending Members", + "details.invited": "Invited Members", "details.has_no_posts": "Anggota grup ini belum membuat posting satupun.", "details.latest_posts": "Posting Terkini", "details.private": "Private", diff --git a/public/language/id/notifications.json b/public/language/id/notifications.json index ee609afb0b..84f8c41c85 100644 --- a/public/language/id/notifications.json +++ b/public/language/id/notifications.json @@ -20,6 +20,7 @@ "user_posted_topic": "%1 telah membuat topik baru: %2", "user_mentioned_you_in": "%1 menyebut mu di %2", "user_started_following_you": "%1 mulai mengikutimu.", + "new_register": "%1 sent a registration request.", "email-confirmed": "Email telah Dikonfirmasi", "email-confirmed-message": "Terimakasih telah melakukan validasi email. Akunmu saat ini telah aktif sepenuhnya.", "email-confirm-error-message": "Terjadi masalah saat melakukan validasi emailmu. Mungkin terjadi kesalahan kode atau waktu habis.", diff --git a/public/language/id/register.json b/public/language/id/register.json index f38cb7a6d6..646f80bdfc 100644 --- a/public/language/id/register.json +++ b/public/language/id/register.json @@ -14,5 +14,6 @@ "register_now_button": "Daftar Sekarang", "alternative_registration": "Pendaftaran Alternatif", "terms_of_use": "Aturan Penggunaan", - "agree_to_terms_of_use": "Saya menyetujui Aturan Penggunaan" + "agree_to_terms_of_use": "Saya menyetujui Aturan Penggunaan", + "registration-added-to-queue": "Your registration has been added to the approval queue. You will receive an email when it is accepted by an administrator." } \ No newline at end of file diff --git a/public/language/id/unread.json b/public/language/id/unread.json index 326f9d732b..23409f6be1 100644 --- a/public/language/id/unread.json +++ b/public/language/id/unread.json @@ -5,5 +5,6 @@ "mark_as_read": "Tandai Sudah Dibaca", "selected": "Terpilih", "all": "Semua", + "all_categories": "All categories", "topics_marked_as_read.success": "Topik ditandai sudah dibaca!" } \ No newline at end of file diff --git a/public/language/id/user.json b/public/language/id/user.json index 74cfa9f7d6..6b942f25e5 100644 --- a/public/language/id/user.json +++ b/public/language/id/user.json @@ -6,8 +6,12 @@ "postcount": "Post Count", "email": "Email", "confirm_email": "Konfirmasi Email", + "ban_account": "Ban Account", + "ban_account_confirm": "Do you really want to ban this user?", + "unban_account": "Unban Account", "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

", "fullname": "Nama Lengkap", "website": "Website", "location": "Lokasi", @@ -64,9 +68,9 @@ "settings-require-reload": "Some setting changes require a reload. Click here to reload the page.", "has_no_follower": "User ini tidak memiliki pengikut :(", "follows_no_one": "User ini tidak mengikuti seorangpun :(", - "has_no_posts": "User ini belum pernah membuat posting.", - "has_no_topics": "User ini belum pernah membuat topik.", - "has_no_watched_topics": "This user didn't watch any topics yet.", + "has_no_posts": "This user hasn't posted anything yet.", + "has_no_topics": "This user hasn't posted any topics yet.", + "has_no_watched_topics": "This user hasn't watched any topics yet.", "email_hidden": "Email Disembunyikan", "hidden": "disembunyikan", "paginate_description": "Paginate topics and posts instead of using infinite scroll", diff --git a/public/language/id/users.json b/public/language/id/users.json index 783d9478f3..2c6a6c0b40 100644 --- a/public/language/id/users.json +++ b/public/language/id/users.json @@ -8,5 +8,14 @@ "users-found-search-took": "%1 user(s) found! Search took %2 seconds.", "filter-by": "Filter By", "online-only": "Online only", - "picture-only": "Picture only" + "picture-only": "Picture only", + "invite": "Invite", + "invitation-email-sent": "An invitation email has been sent to %1", + "user_list": "User List", + "recent_topics": "Recent Topics", + "popular_topics": "Popular Topics", + "unread_topics": "Unread Topics", + "categories": "Categories", + "tags": "Tags", + "map": "Map" } \ No newline at end of file diff --git a/public/language/it/email.json b/public/language/it/email.json index e351a25036..3593b754ed 100644 --- a/public/language/it/email.json +++ b/public/language/it/email.json @@ -1,11 +1,15 @@ { "password-reset-requested": "Richiesta di reset della password - %1!", "welcome-to": "Benvenuto in %1", + "invite": "Invitation from %1", "greeting_no_name": "Ciao", "greeting_with_name": "Ciao %1", "welcome.text1": "Grazie per esserti registrato su %1!", "welcome.text2": "Per attivare completamente il tuo account dobbiamo verificare che sei il proprietario dell'indiritto email con cui ti sei registrato.", + "welcome.text3": "An administrator has accepted your registration application. You can login with your username/password now.", "welcome.cta": "Clicca qui per confermare il tuo indirizzo email", + "invitation.text1": "%1 has invited you to join %2", + "invitation.ctr": "Click here to create your account.", "reset.text1": "Abbiamo ricevuto una richiesta di reset della tua password, probabilmente perché l'hai dimenticata. Se non è così si prega di ignorare questa email.", "reset.text2": "Per confermare il reset della password per favore clicca il seguente link:", "reset.cta": "Clicca qui per resettare la tua password", diff --git a/public/language/it/error.json b/public/language/it/error.json index 7577b34f43..1426a8531b 100644 --- a/public/language/it/error.json +++ b/public/language/it/error.json @@ -51,6 +51,7 @@ "already-favourited": "Hai già inserito tra i preferiti questo post", "already-unfavourited": "Hai già inserito tra i Non Preferiti questo post", "cant-ban-other-admins": "Non puoi bannare altri amministratori!", + "cant-remove-last-admin": "You are the only administrator. Add another user as an administrator before removing yourself as admin", "invalid-image-type": "Tipo dell'immagine non valido. I tipi permessi sono: %1", "invalid-image-extension": "Estensione immagine non valida", "invalid-file-type": "Tipo di file non valido. I formati consentiti sono: %1", @@ -59,6 +60,8 @@ "group-name-change-not-allowed": "Il cambio di nome al Gruppo non è consentito", "group-already-member": "Fai già parte di questo gruppo", "group-needs-owner": "Questo gruppo richiede almeno un proprietario.", + "group-already-invited": "This user has already been invited", + "group-already-requested": "Your membership request has already been submitted", "post-already-deleted": "Questo Post è già stato cancellato", "post-already-restored": "Questo Post è già stato ripristinato", "topic-already-deleted": "Questo Topic è già stato cancellato", @@ -76,6 +79,7 @@ "downvoting-disabled": "Il Downvoting è disabilitato", "not-enough-reputation-to-downvote": "Non hai i privilegi per votare negativamente questo post", "not-enough-reputation-to-flag": "Tu non hai abbastanza reputazione per segnalare questo Post", + "already-flagged": "You have already flagged this post", "reload-failed": "NodeBB ha incontrato un problema durante il ricaricamento: \"%1\". NodeBB continuerà a servire gli assets esistenti lato client, così puoi annullare quello che hai fatto prima di ricaricare.", "registration-error": "Errore nella registrazione", "parse-error": "Qualcosa è andato storto durante l'analisi della risposta proveniente dal server", diff --git a/public/language/it/global.json b/public/language/it/global.json index ed27a9340d..1f5418e698 100644 --- a/public/language/it/global.json +++ b/public/language/it/global.json @@ -50,6 +50,7 @@ "views": "Visualizzazioni", "reputation": "Reputazione", "read_more": "per saperne di più", + "more": "More", "posted_ago_by_guest": "scritto %1 di Ospite", "posted_ago_by": "scritto %1 di %2", "posted_ago": "postato %1", diff --git a/public/language/it/groups.json b/public/language/it/groups.json index 4b96975fac..2daecdf16b 100644 --- a/public/language/it/groups.json +++ b/public/language/it/groups.json @@ -6,6 +6,12 @@ "no_groups_found": "Non ci sono gruppi da vedere", "pending.accept": "Accetta", "pending.reject": "Rigetta", + "pending.accept_all": "Accept All", + "pending.reject_all": "Reject All", + "pending.none": "There are no pending members at this time", + "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", "cover-instructions": "Drag and Drop una fotografia, spostarla ad una posizione, e premere Salva", "cover-change": "Cambia", "cover-save": "Salva", @@ -13,6 +19,7 @@ "details.title": "Dettagli Gruppo", "details.members": "Lista Membri", "details.pending": "Membri in attesa", + "details.invited": "Invited Members", "details.has_no_posts": "I membri di questo gruppo non hanno ancora postato.", "details.latest_posts": "Ultimi Post", "details.private": "Privato", diff --git a/public/language/it/notifications.json b/public/language/it/notifications.json index c3f067b0cf..7e40b19b11 100644 --- a/public/language/it/notifications.json +++ b/public/language/it/notifications.json @@ -20,6 +20,7 @@ "user_posted_topic": "%1 ha postato un nuovo Topic: %2", "user_mentioned_you_in": "%1 ti ha menzionato in %2", "user_started_following_you": "%1 ha iniziato a seguirti.", + "new_register": "%1 ha inviato una richiesta di registrazione.", "email-confirmed": "Email Confermata", "email-confirmed-message": "Grazie per aver validato la tua email. Il tuo account è ora completamente attivato.", "email-confirm-error-message": "C'è stato un problema nella validazione del tuo indirizzo email. Potrebbe essere il codice non valido o scaduto.", diff --git a/public/language/it/register.json b/public/language/it/register.json index 84ca2cbdab..b345928150 100644 --- a/public/language/it/register.json +++ b/public/language/it/register.json @@ -14,5 +14,6 @@ "register_now_button": "Registrati Ora", "alternative_registration": "Altri metodi di registrazione", "terms_of_use": "Termini di Utilizzo", - "agree_to_terms_of_use": "Accetto i Termini di Utilizzo" + "agree_to_terms_of_use": "Accetto i Termini di Utilizzo", + "registration-added-to-queue": "Your registration has been added to the approval queue. You will receive an email when it is accepted by an administrator." } \ No newline at end of file diff --git a/public/language/it/topic.json b/public/language/it/topic.json index d442472c00..26b6353ae1 100644 --- a/public/language/it/topic.json +++ b/public/language/it/topic.json @@ -5,9 +5,9 @@ "no_topics_found": "Nessuna discussione trovata!", "no_posts_found": "Nessun post trovato!", "post_is_deleted": "Questo post è eliminato!", - "topic_is_deleted": "This topic is deleted!", + "topic_is_deleted": "Questa discussione é stata eliminata", "profile": "Profilo", - "posted_by": "scritto da %1", + "posted_by": "Pubblicato da: %1", "posted_by_guest": "Scritto da Ospite", "chat": "Chat", "notify_me": "Ricevi notifiche di nuove risposte in questa discussione", diff --git a/public/language/it/unread.json b/public/language/it/unread.json index 9eb5d5a578..415f8b5d3c 100644 --- a/public/language/it/unread.json +++ b/public/language/it/unread.json @@ -5,5 +5,6 @@ "mark_as_read": "Segna come Letto", "selected": "Selezionati", "all": "Tutti", + "all_categories": "All categories", "topics_marked_as_read.success": "Discussione marcata come letta!" } \ No newline at end of file diff --git a/public/language/it/user.json b/public/language/it/user.json index 38e2565652..692831ff5c 100644 --- a/public/language/it/user.json +++ b/public/language/it/user.json @@ -6,8 +6,12 @@ "postcount": "Numero post", "email": "Email", "confirm_email": "Conferma Email", + "ban_account": "Ban Account", + "ban_account_confirm": "Do you really want to ban this user?", + "unban_account": "Unban Account", "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": "Are you sure you want to delete this account?
This action is irreversible and you will not be able to recover any data

", "fullname": "Nome e Cognome", "website": "Sito Internet", "location": "Località", @@ -64,9 +68,9 @@ "settings-require-reload": "Alcuni cambiamenti di impostazioni richiedono un ricaricamento. Clicca qui per ricaricare la pagina.", "has_no_follower": "Questo utente non è seguito da nessuno :(", "follows_no_one": "Questo utente non segue nessuno :(", - "has_no_posts": "Questo utente non ha ancora postato nulla.", - "has_no_topics": "Questo utente non ha ancora avviato discussioni.", - "has_no_watched_topics": "Questo utente non sta ancora osservando alcuna discussione.", + "has_no_posts": "This user hasn't posted anything yet.", + "has_no_topics": "This user hasn't posted any topics yet.", + "has_no_watched_topics": "This user hasn't watched any topics yet.", "email_hidden": "Email Nascosta", "hidden": "nascosta", "paginate_description": "Paginate topics and posts instead of using infinite scroll", diff --git a/public/language/it/users.json b/public/language/it/users.json index 4774dd2909..d9a9e68427 100644 --- a/public/language/it/users.json +++ b/public/language/it/users.json @@ -8,5 +8,14 @@ "users-found-search-took": "%1 utente(i) trovati! La ricerca ha impiegato %2 secondi.", "filter-by": "Filtra per", "online-only": "Solo online", - "picture-only": "Solo con foto" + "picture-only": "Solo con foto", + "invite": "Invite", + "invitation-email-sent": "An invitation email has been sent to %1", + "user_list": "User List", + "recent_topics": "Recent Topics", + "popular_topics": "Popular Topics", + "unread_topics": "Unread Topics", + "categories": "Categories", + "tags": "Tags", + "map": "Map" } \ No newline at end of file diff --git a/public/language/ja/email.json b/public/language/ja/email.json index f290435e75..d8114d0231 100644 --- a/public/language/ja/email.json +++ b/public/language/ja/email.json @@ -1,11 +1,15 @@ { "password-reset-requested": "Password Reset Requested - %1!", "welcome-to": "Welcome to %1", + "invite": "Invitation from %1", "greeting_no_name": "Hello", "greeting_with_name": "Hello %1", "welcome.text1": "Thank you for registering with %1!", "welcome.text2": "To fully activate your account, we need to verify that you own the email address you registered with.", + "welcome.text3": "An administrator has accepted your registration application. You can login with your username/password now.", "welcome.cta": "Click here to confirm your email address", + "invitation.text1": "%1 has invited you to join %2", + "invitation.ctr": "Click here to create your account.", "reset.text1": "We received a request to reset your password, possibly because you have forgotten it. If this is not the case, please ignore this email.", "reset.text2": "To continue with the password reset, please click on the following link:", "reset.cta": "Click here to reset your password", diff --git a/public/language/ja/error.json b/public/language/ja/error.json index 631406319a..483afca720 100644 --- a/public/language/ja/error.json +++ b/public/language/ja/error.json @@ -51,6 +51,7 @@ "already-favourited": "You have already favourited this post", "already-unfavourited": "You have already unfavourited this post", "cant-ban-other-admins": "ほかの管理者を停止することはできません!", + "cant-remove-last-admin": "You are the only administrator. Add another user as an administrator before removing yourself as admin", "invalid-image-type": "Invalid image type. Allowed types are: %1", "invalid-image-extension": "Invalid image extension", "invalid-file-type": "Invalid file type. Allowed types are: %1", @@ -59,6 +60,8 @@ "group-name-change-not-allowed": "グループ名の修正はできません", "group-already-member": "You are already part of this group", "group-needs-owner": "This group requires at least one owner", + "group-already-invited": "This user has already been invited", + "group-already-requested": "Your membership request has already been submitted", "post-already-deleted": "This post has already been deleted", "post-already-restored": "This post has already been restored", "topic-already-deleted": "This topic has already been deleted", @@ -76,6 +79,7 @@ "downvoting-disabled": "Downvoting is disabled", "not-enough-reputation-to-downvote": "You do not have enough reputation to downvote this post", "not-enough-reputation-to-flag": "You do not have enough reputation to flag this post", + "already-flagged": "You have already flagged this post", "reload-failed": "NodeBB encountered a problem while reloading: \"%1\". NodeBB will continue to serve the existing client-side assets, although you should undo what you did just prior to reloading.", "registration-error": "Registration Error", "parse-error": "Something went wrong while parsing server response", diff --git a/public/language/ja/global.json b/public/language/ja/global.json index f0f54776bb..682771cc8f 100644 --- a/public/language/ja/global.json +++ b/public/language/ja/global.json @@ -50,6 +50,7 @@ "views": "閲覧数", "reputation": "評価", "read_more": "続きを読む", + "more": "More", "posted_ago_by_guest": "%1にゲストが投稿", "posted_ago_by": "%1に %2 が投稿", "posted_ago": "%1に投稿された", diff --git a/public/language/ja/groups.json b/public/language/ja/groups.json index 5e301aa46a..c9ac036475 100644 --- a/public/language/ja/groups.json +++ b/public/language/ja/groups.json @@ -6,6 +6,12 @@ "no_groups_found": "There are no groups to see", "pending.accept": "Accept", "pending.reject": "Reject", + "pending.accept_all": "Accept All", + "pending.reject_all": "Reject All", + "pending.none": "There are no pending members at this time", + "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", "cover-instructions": "Drag and Drop a photo, drag to position, and hit Save", "cover-change": "Change", "cover-save": "Save", @@ -13,6 +19,7 @@ "details.title": "Group Details", "details.members": "Member List", "details.pending": "Pending Members", + "details.invited": "Invited Members", "details.has_no_posts": "This group's members have not made any posts.", "details.latest_posts": "Latest Posts", "details.private": "Private", diff --git a/public/language/ja/notifications.json b/public/language/ja/notifications.json index 6677d832e2..0569e4cf80 100644 --- a/public/language/ja/notifications.json +++ b/public/language/ja/notifications.json @@ -20,6 +20,7 @@ "user_posted_topic": "%1 has posted a new topic: %2", "user_mentioned_you_in": "%1 mentioned you in %2", "user_started_following_you": "%1 started following you.", + "new_register": "%1 sent a registration request.", "email-confirmed": "Email Confirmed", "email-confirmed-message": "Thank you for validating your email. Your account is now fully activated.", "email-confirm-error-message": "There was a problem validating your email address. Perhaps the code was invalid or has expired.", diff --git a/public/language/ja/register.json b/public/language/ja/register.json index 7617407aa2..a6c43fa915 100644 --- a/public/language/ja/register.json +++ b/public/language/ja/register.json @@ -14,5 +14,6 @@ "register_now_button": "今すぐ登録する", "alternative_registration": "ほかの登録方法", "terms_of_use": "利用規約", - "agree_to_terms_of_use": "利用規約に同意する" + "agree_to_terms_of_use": "利用規約に同意する", + "registration-added-to-queue": "Your registration has been added to the approval queue. You will receive an email when it is accepted by an administrator." } \ No newline at end of file diff --git a/public/language/ja/unread.json b/public/language/ja/unread.json index 8b67da2a31..f35c0c4d4c 100644 --- a/public/language/ja/unread.json +++ b/public/language/ja/unread.json @@ -5,5 +5,6 @@ "mark_as_read": "既読にする", "selected": "選択済み", "all": "すべて", + "all_categories": "All categories", "topics_marked_as_read.success": "すべてのスレッドを既読にしました。" } \ No newline at end of file diff --git a/public/language/ja/user.json b/public/language/ja/user.json index 55e52d7cb0..5100ffaaae 100644 --- a/public/language/ja/user.json +++ b/public/language/ja/user.json @@ -6,8 +6,12 @@ "postcount": "Post Count", "email": "メール", "confirm_email": "Confirm Email", + "ban_account": "Ban Account", + "ban_account_confirm": "Do you really want to ban this user?", + "unban_account": "Unban Account", "delete_account": "Delete Account", "delete_account_confirm": "Are you sure you want to delete your account?
This action is irreversible and you will not be able to recover any of your data

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

", "fullname": "フルネーム", "website": "ウェブサイト", "location": "ロケーション", @@ -64,9 +68,9 @@ "settings-require-reload": "Some setting changes require a reload. Click here to reload the page.", "has_no_follower": "フォロワーはまだいません :(", "follows_no_one": "フォロー中のユーザーはまだいません :(", - "has_no_posts": "まだポストを投稿したことありません。", - "has_no_topics": "まだスレッドを作成したありません。", - "has_no_watched_topics": "This user didn't watch any topics yet.", + "has_no_posts": "This user hasn't posted anything yet.", + "has_no_topics": "This user hasn't posted any topics yet.", + "has_no_watched_topics": "This user hasn't watched any topics yet.", "email_hidden": "メールアドレスを非表示", "hidden": "非表示", "paginate_description": "Paginate topics and posts instead of using infinite scroll", diff --git a/public/language/ja/users.json b/public/language/ja/users.json index 6d73cf0513..0629269a9b 100644 --- a/public/language/ja/users.json +++ b/public/language/ja/users.json @@ -8,5 +8,14 @@ "users-found-search-took": "%1 user(s) found! Search took %2 seconds.", "filter-by": "Filter By", "online-only": "Online only", - "picture-only": "Picture only" + "picture-only": "Picture only", + "invite": "Invite", + "invitation-email-sent": "An invitation email has been sent to %1", + "user_list": "User List", + "recent_topics": "Recent Topics", + "popular_topics": "Popular Topics", + "unread_topics": "Unread Topics", + "categories": "Categories", + "tags": "Tags", + "map": "Map" } \ No newline at end of file diff --git a/public/language/ko/category.json b/public/language/ko/category.json index f7ccdba720..e2a4059181 100644 --- a/public/language/ko/category.json +++ b/public/language/ko/category.json @@ -1,11 +1,11 @@ { "new_topic_button": "새 주제 생성", - "guest-login-post": "Log in to post", + "guest-login-post": "로그인", "no_topics": "이 카테고리에는 생성된 주제가 없습니다.
먼저 주제를 생성해 보세요.", "browsing": "이 주제를 읽고 있는 사용자", "no_replies": "답글이 없습니다.", "share_this_category": "이 카테고리를 공유", - "watch": "Watch", + "watch": "관심 주제", "ignore": "관심 해제", "watch.message": "You are now watching updates from this category", "ignore.message": "You are now ignoring updates from this category" diff --git a/public/language/ko/email.json b/public/language/ko/email.json index 1c5032db35..4a2db8addd 100644 --- a/public/language/ko/email.json +++ b/public/language/ko/email.json @@ -1,11 +1,15 @@ { "password-reset-requested": "패스워드 재설정을 요청했습니다 - %1!", "welcome-to": "%1에 오신 것을 환영합니다.", + "invite": "Invitation from %1", "greeting_no_name": "안녕하세요", "greeting_with_name": "안녕하세요 %1님", "welcome.text1": "%1님 가입해주셔서 감사합니다.", "welcome.text2": "계정을 활성화하려면 등록하신 메일 주소가 올바른지 확인이 필요합니다.", + "welcome.text3": "An administrator has accepted your registration application. You can login with your username/password now.", "welcome.cta": "메일 주소를 확인하려면 여기를 클릭하세요.", + "invitation.text1": "%1 has invited you to join %2", + "invitation.ctr": "Click here to create your account.", "reset.text1": "패스워드 재설정 요청을 받았습니다. 패스워드를 분실해서 요청한 것이 아니라면 이 메일을 무시하셔도 좋습니다.", "reset.text2": "패스워드를 재설정하려면 다음 링크를 클릭하세요.", "reset.cta": "패스워드를 재설정하려면 여기를 클릭하세요.", diff --git a/public/language/ko/error.json b/public/language/ko/error.json index 5bfcca4262..715f932578 100644 --- a/public/language/ko/error.json +++ b/public/language/ko/error.json @@ -51,6 +51,7 @@ "already-favourited": "You have already favourited this post", "already-unfavourited": "You have already unfavourited this post", "cant-ban-other-admins": "다른 관리자를 차단할 수 없습니다.", + "cant-remove-last-admin": "You are the only administrator. Add another user as an administrator before removing yourself as admin", "invalid-image-type": "Invalid image type. Allowed types are: %1", "invalid-image-extension": "Invalid image extension", "invalid-file-type": "Invalid file type. Allowed types are: %1", @@ -59,6 +60,8 @@ "group-name-change-not-allowed": "금지된 그룹 이름입니다.", "group-already-member": "You are already part of this group", "group-needs-owner": "This group requires at least one owner", + "group-already-invited": "This user has already been invited", + "group-already-requested": "Your membership request has already been submitted", "post-already-deleted": "이미 삭제된 게시물입니다.", "post-already-restored": "이미 복원된 게시물입니다.", "topic-already-deleted": "이미 삭제된 주제입니다.", @@ -76,6 +79,7 @@ "downvoting-disabled": "비추천 기능을 사용하지 않습니다.", "not-enough-reputation-to-downvote": "인기도가 낮아 이 게시물에 반대할 수 없습니다.", "not-enough-reputation-to-flag": "인기도가 낮아 이 게시물을 신고할 수 없습니다.", + "already-flagged": "You have already flagged this post", "reload-failed": "NodeBB를 다시 읽어들이는 중 다음과 같은 문제가 발생했습니다. \"%1\" 다시 읽어들이기 직전에 했던 동작은 되돌리세요. 클라이언트측 자원은 문제없이 계속 전송됩니다.", "registration-error": "Registration Error", "parse-error": "Something went wrong while parsing server response", diff --git a/public/language/ko/global.json b/public/language/ko/global.json index d5c5787e33..601c60aa19 100644 --- a/public/language/ko/global.json +++ b/public/language/ko/global.json @@ -50,6 +50,7 @@ "views": "조회 수", "reputation": "인기도", "read_more": "전체 보기", + "more": "More", "posted_ago_by_guest": "익명 사용자가 %1에 작성했습니다.", "posted_ago_by": "%2님이 %1에 작성했습니다.", "posted_ago": "%1에 작성되었습니다.", diff --git a/public/language/ko/groups.json b/public/language/ko/groups.json index 6bc42b54fa..f2327c674d 100644 --- a/public/language/ko/groups.json +++ b/public/language/ko/groups.json @@ -6,6 +6,12 @@ "no_groups_found": "There are no groups to see", "pending.accept": "Accept", "pending.reject": "Reject", + "pending.accept_all": "Accept All", + "pending.reject_all": "Reject All", + "pending.none": "There are no pending members at this time", + "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", "cover-instructions": "Drag and Drop a photo, drag to position, and hit Save", "cover-change": "Change", "cover-save": "Save", @@ -13,6 +19,7 @@ "details.title": "그룹 상세정보", "details.members": "구성원 목록", "details.pending": "Pending Members", + "details.invited": "Invited Members", "details.has_no_posts": "이 그룹의 구성원이 작성한 게시물이 없습니다.", "details.latest_posts": "최근 게시물", "details.private": "Private", diff --git a/public/language/ko/notifications.json b/public/language/ko/notifications.json index d0240f52ae..c65552e3fb 100644 --- a/public/language/ko/notifications.json +++ b/public/language/ko/notifications.json @@ -20,6 +20,7 @@ "user_posted_topic": "%1님이 새 주제를 작성했습니다: %2", "user_mentioned_you_in": "%1님이 %2에서 나를 언급했습니다.", "user_started_following_you": "%1님이 나를 팔로우합니다.", + "new_register": "%1 sent a registration request.", "email-confirmed": "확인된 이메일", "email-confirmed-message": "이메일을 확인해주셔서 감사합니다. 계정이 완전히 활성화되었습니다.", "email-confirm-error-message": "이메일 주소를 검증하지 못했습니다. 코드가 올바르지 않거나 만료되었을 수 있습니다.", diff --git a/public/language/ko/register.json b/public/language/ko/register.json index 4d531f7cb4..d61d9ae243 100644 --- a/public/language/ko/register.json +++ b/public/language/ko/register.json @@ -14,5 +14,6 @@ "register_now_button": "회원가입", "alternative_registration": "다른 방법으로 회원가입", "terms_of_use": "이용약관", - "agree_to_terms_of_use": "이용약관에 동의합니다." + "agree_to_terms_of_use": "이용약관에 동의합니다.", + "registration-added-to-queue": "Your registration has been added to the approval queue. You will receive an email when it is accepted by an administrator." } \ No newline at end of file diff --git a/public/language/ko/unread.json b/public/language/ko/unread.json index b7ffd0ad4e..0e327f15db 100644 --- a/public/language/ko/unread.json +++ b/public/language/ko/unread.json @@ -5,5 +5,6 @@ "mark_as_read": "읽음으로 표시", "selected": "선택된 주제", "all": "전체", + "all_categories": "All categories", "topics_marked_as_read.success": "성공적으로 읽음으로 표시했습니다." } \ No newline at end of file diff --git a/public/language/ko/user.json b/public/language/ko/user.json index 324eccb26f..7d34664421 100644 --- a/public/language/ko/user.json +++ b/public/language/ko/user.json @@ -6,8 +6,12 @@ "postcount": "Post Count", "email": "이메일", "confirm_email": "이메일 확인", + "ban_account": "Ban Account", + "ban_account_confirm": "Do you really want to ban this user?", + "unban_account": "Unban Account", "delete_account": "계정 삭제", "delete_account_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

", "fullname": "이름", "website": "웹 사이트", "location": "거주지", @@ -64,9 +68,9 @@ "settings-require-reload": "Some setting changes require a reload. Click here to reload the page.", "has_no_follower": "아무도 이 사용자를 팔로우하지 않습니다.", "follows_no_one": "이 사용자는 아무도 팔로우하지 않습니다.", - "has_no_posts": "이 사용자는 게시물을 작성하지 않았습니다.", - "has_no_topics": "이 사용자는 주제를 생성하지 않았습니다.", - "has_no_watched_topics": "This user didn't watch any topics yet.", + "has_no_posts": "This user hasn't posted anything yet.", + "has_no_topics": "This user hasn't posted any topics yet.", + "has_no_watched_topics": "This user hasn't watched any topics yet.", "email_hidden": "이메일 비공개", "hidden": "비공개", "paginate_description": "Paginate topics and posts instead of using infinite scroll", diff --git a/public/language/ko/users.json b/public/language/ko/users.json index 9135621eb6..8ba619fa8d 100644 --- a/public/language/ko/users.json +++ b/public/language/ko/users.json @@ -8,5 +8,14 @@ "users-found-search-took": "%1 user(s) found! Search took %2 seconds.", "filter-by": "Filter By", "online-only": "Online only", - "picture-only": "Picture only" + "picture-only": "Picture only", + "invite": "Invite", + "invitation-email-sent": "An invitation email has been sent to %1", + "user_list": "User List", + "recent_topics": "Recent Topics", + "popular_topics": "Popular Topics", + "unread_topics": "Unread Topics", + "categories": "Categories", + "tags": "Tags", + "map": "Map" } \ No newline at end of file diff --git a/public/language/lt/email.json b/public/language/lt/email.json index af0968b55e..12fd68ced9 100644 --- a/public/language/lt/email.json +++ b/public/language/lt/email.json @@ -1,11 +1,15 @@ { "password-reset-requested": "Password Reset Requested - %1!", "welcome-to": "Sveiki atvykę į %1", + "invite": "Invitation from %1", "greeting_no_name": "Sveiki", "greeting_with_name": "Sveiki %1", "welcome.text1": "Thank you for registering with %1!", "welcome.text2": "To fully activate your account, we need to verify that you own the email address you registered with.", + "welcome.text3": "An administrator has accepted your registration application. You can login with your username/password now.", "welcome.cta": "El. adreso patvirtinimui spauskite čia", + "invitation.text1": "%1 has invited you to join %2", + "invitation.ctr": "Click here to create your account.", "reset.text1": "We received a request to reset your password, possibly because you have forgotten it. If this is not the case, please ignore this email.", "reset.text2": "To continue with the password reset, please click on the following link:", "reset.cta": "Slaptažodžio atstatymui spauskite čia", diff --git a/public/language/lt/error.json b/public/language/lt/error.json index 49023f88aa..4b42d4b09c 100644 --- a/public/language/lt/error.json +++ b/public/language/lt/error.json @@ -51,6 +51,7 @@ "already-favourited": "You have already favourited this post", "already-unfavourited": "You have already unfavourited this post", "cant-ban-other-admins": "Jūs negalite užblokuoti kitų administratorių!", + "cant-remove-last-admin": "You are the only administrator. Add another user as an administrator before removing yourself as admin", "invalid-image-type": "Neteisingas vaizdo tipas. Leidžiami tipai :%1", "invalid-image-extension": "Neteisingas vaizdo plėtinys", "invalid-file-type": "Neteisingas failo tipas. Leidžiami tipai: %1", @@ -59,6 +60,8 @@ "group-name-change-not-allowed": "Grupės pavadinimas keitimas neleidžiamas", "group-already-member": "Jūs jau priklausote šiai grupei", "group-needs-owner": "This group requires at least one owner", + "group-already-invited": "This user has already been invited", + "group-already-requested": "Your membership request has already been submitted", "post-already-deleted": "Šis įrašas jau buvo ištrintas", "post-already-restored": "Šis įrašas jau atstatytas", "topic-already-deleted": "Ši tema jau ištrinta", @@ -76,6 +79,7 @@ "downvoting-disabled": "Downvoting is disabled", "not-enough-reputation-to-downvote": "Jūs neturite pakankamai reputacijos balsuoti prieš šį pranešimą", "not-enough-reputation-to-flag": "You do not have enough reputation to flag this post", + "already-flagged": "You have already flagged this post", "reload-failed": "NodeBB encountered a problem while reloading: \"%1\". NodeBB will continue to serve the existing client-side assets, although you should undo what you did just prior to reloading.", "registration-error": "Registracijos klaida", "parse-error": "Something went wrong while parsing server response", diff --git a/public/language/lt/global.json b/public/language/lt/global.json index 8301db2346..3739fcdce6 100644 --- a/public/language/lt/global.json +++ b/public/language/lt/global.json @@ -50,6 +50,7 @@ "views": "Peržiūros", "reputation": "Reputacija", "read_more": "skaityti plačiau", + "more": "More", "posted_ago_by_guest": "parašyta %2 nuo svečio", "posted_ago_by": "parašyta %1 nuo %2", "posted_ago": "parašyta %1", diff --git a/public/language/lt/groups.json b/public/language/lt/groups.json index cc1b781426..00eb1d9582 100644 --- a/public/language/lt/groups.json +++ b/public/language/lt/groups.json @@ -6,6 +6,12 @@ "no_groups_found": "There are no groups to see", "pending.accept": "Priimti", "pending.reject": "Atmesti", + "pending.accept_all": "Accept All", + "pending.reject_all": "Reject All", + "pending.none": "There are no pending members at this time", + "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", "cover-instructions": "Vilkite čia nuotrauką, perkelkite į reikalingą poziciją ir spauskite Save", "cover-change": "Keisti", "cover-save": "Saugoti", @@ -13,6 +19,7 @@ "details.title": "Grupės detalės", "details.members": "Narių sąrašas", "details.pending": "Laukiantys nariai", + "details.invited": "Invited Members", "details.has_no_posts": "Šios grupės nariai neatliko jokių įrašų.", "details.latest_posts": "Vėliausi įrašai", "details.private": "Asmeniška", diff --git a/public/language/lt/notifications.json b/public/language/lt/notifications.json index e7deae258e..11589e9fc5 100644 --- a/public/language/lt/notifications.json +++ b/public/language/lt/notifications.json @@ -20,6 +20,7 @@ "user_posted_topic": "%1 paskelbė naują temą: %2", "user_mentioned_you_in": "%1 paminėjo Jus %2", "user_started_following_you": "%1 started following you.", + "new_register": "%1 sent a registration request.", "email-confirmed": "El. paštas patvirtintas", "email-confirmed-message": "Dėkojame už el. pašto patvirtinimą. Jūsų paskyra pilnai aktyvuota.", "email-confirm-error-message": "Įvyko klaida mėginant patvirtinti Jūsų el. pašto adresą. Galbūt kodas yra neteisingas, arba nebegalioajantis.", diff --git a/public/language/lt/register.json b/public/language/lt/register.json index 37237e59a9..a4c931e7d7 100644 --- a/public/language/lt/register.json +++ b/public/language/lt/register.json @@ -14,5 +14,6 @@ "register_now_button": "Registruotis", "alternative_registration": "Alternatyvūs registracijos būdai", "terms_of_use": "Naudojimo sąlygos", - "agree_to_terms_of_use": "Aš sutinku su vartojimo sąlygomis" + "agree_to_terms_of_use": "Aš sutinku su vartojimo sąlygomis", + "registration-added-to-queue": "Your registration has been added to the approval queue. You will receive an email when it is accepted by an administrator." } \ No newline at end of file diff --git a/public/language/lt/unread.json b/public/language/lt/unread.json index bbfd49830c..56db7f7d15 100644 --- a/public/language/lt/unread.json +++ b/public/language/lt/unread.json @@ -5,5 +5,6 @@ "mark_as_read": "Pažymėti kaip perskaitytus", "selected": "Pasirinkti", "all": "Visi", + "all_categories": "All categories", "topics_marked_as_read.success": "Temos pažymėtos kaip perskaitytos." } \ No newline at end of file diff --git a/public/language/lt/user.json b/public/language/lt/user.json index 147e525aa5..70f6a75bc2 100644 --- a/public/language/lt/user.json +++ b/public/language/lt/user.json @@ -6,8 +6,12 @@ "postcount": "Įrašų kiekis", "email": "El. paštas", "confirm_email": "Patvirtinti el. paštą", + "ban_account": "Ban Account", + "ban_account_confirm": "Do you really want to ban this user?", + "unban_account": "Unban Account", "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": "Are you sure you want to delete this account?
This action is irreversible and you will not be able to recover any data

", "fullname": "Vardas ir pavardė", "website": "Tinklalapis", "location": "Vieta", @@ -64,9 +68,9 @@ "settings-require-reload": "Some setting changes require a reload. Click here to reload the page.", "has_no_follower": "Šis vartotojas neturi jokių sekėjų :(", "follows_no_one": "Šis vartotojas nieko neseka :(", - "has_no_posts": "Šis vartotojas dar neparašė nė vieno pranešimo.", - "has_no_topics": "Šis vartotojas dar nesukūrė nė vienos temos.", - "has_no_watched_topics": "Šis vartotojas dar neseka jokių temų", + "has_no_posts": "This user hasn't posted anything yet.", + "has_no_topics": "This user hasn't posted any topics yet.", + "has_no_watched_topics": "This user hasn't watched any topics yet.", "email_hidden": "El. paštas paslėptas", "hidden": "paslėptas", "paginate_description": "Paginate topics and posts instead of using infinite scroll", diff --git a/public/language/lt/users.json b/public/language/lt/users.json index f4f4360022..ddfb31c12b 100644 --- a/public/language/lt/users.json +++ b/public/language/lt/users.json @@ -8,5 +8,14 @@ "users-found-search-took": "Rasta %1 vartotojas(-ai)! Paieška užtruko %2 sekundes.", "filter-by": "Filtruoti pagal", "online-only": "Online only", - "picture-only": "Tik paveikslėlis" + "picture-only": "Tik paveikslėlis", + "invite": "Invite", + "invitation-email-sent": "An invitation email has been sent to %1", + "user_list": "User List", + "recent_topics": "Recent Topics", + "popular_topics": "Popular Topics", + "unread_topics": "Unread Topics", + "categories": "Categories", + "tags": "Tags", + "map": "Map" } \ No newline at end of file diff --git a/public/language/ms/email.json b/public/language/ms/email.json index 385b7b84a2..5d55029bf5 100644 --- a/public/language/ms/email.json +++ b/public/language/ms/email.json @@ -1,11 +1,15 @@ { "password-reset-requested": "Permintaan set semula kata lalaun - %1!", "welcome-to": "Selamat datang ke %1", + "invite": "Invitation from %1", "greeting_no_name": "Salam", "greeting_with_name": "Salam %1", "welcome.text1": "Terima kasih kerana mendaftar dengan %1!", "welcome.text2": "Untuk mengaktifkan akaun anda sepenuhnya, kami perlu mengesahkan bahawa anda memiliki alamat emel yang didaftarkan.", + "welcome.text3": "An administrator has accepted your registration application. You can login with your username/password now.", "welcome.cta": "Klik sini untuk sahkan emel anda", + "invitation.text1": "%1 has invited you to join %2", + "invitation.ctr": "Click here to create your account.", "reset.text1": "Kami menerima permintaan set semula kata laluan anda, kemungkinan kerana anda terlupa. Sekiranya tidak, sila abaikan emel ini.", "reset.text2": "Untuk meneruskan dengan set semula kata laluan, sila klik pautan berikut:", "reset.cta": "Klik sini untuk set semula kata laluan anda", diff --git a/public/language/ms/error.json b/public/language/ms/error.json index e9eefc19a6..cde4c21a5f 100644 --- a/public/language/ms/error.json +++ b/public/language/ms/error.json @@ -51,6 +51,7 @@ "already-favourited": "Anda telah pun menggemari kiriman ini", "already-unfavourited": "Anda telah pun nyah-gemar kiriman ini", "cant-ban-other-admins": "Anda tidak boleh haramkan admin / pentadbir!", + "cant-remove-last-admin": "You are the only administrator. Add another user as an administrator before removing yourself as admin", "invalid-image-type": "Jenis imej tak sah. Jenis yang dibenarkan ialah: %1", "invalid-image-extension": "Sambungan imej tak sah", "invalid-file-type": "Jenis fail tak sah. Jenis fail yang dibenarkan ialah: %1", @@ -59,6 +60,8 @@ "group-name-change-not-allowed": "Pengubahan nama kumpulan tidak dibenarkan", "group-already-member": "Anda telah pun menjadi ahli kumpulan ini", "group-needs-owner": "Kumpulan ini memerlukan sekurang-kurangnya seorang pemilik", + "group-already-invited": "This user has already been invited", + "group-already-requested": "Your membership request has already been submitted", "post-already-deleted": "Kiriman ini telah dipadam", "post-already-restored": "Kiriman ini telah dipulihkan", "topic-already-deleted": "Topik ini telah dipadam", @@ -76,6 +79,7 @@ "downvoting-disabled": "Undi turun dilumpuhkan", "not-enough-reputation-to-downvote": "Anda tidak mempunyai reputasi mencukupi untuk mengundi turun kiriman ini", "not-enough-reputation-to-flag": "Anda tidak mempunyai reputasi mencukupi untuk menanda kiriman ini", + "already-flagged": "You have already flagged this post", "reload-failed": "NodeBB menemui masalah ketika muat semula: \"%1\". NodeBB akan terus melayan aset pelanggan sedia ada, tapi anda seharusnya undur perbuatan yang dilakukan sebelum muat semula.", "registration-error": "Ralat pendaftaran.", "parse-error": "Sesuatu tidak kena berlaku ketika menghuraikan repson pelayan (server)", diff --git a/public/language/ms/global.json b/public/language/ms/global.json index 1f8a8ffba5..c360234332 100644 --- a/public/language/ms/global.json +++ b/public/language/ms/global.json @@ -50,6 +50,7 @@ "views": "Lihat", "reputation": "Reputasi", "read_more": "baca lagi", + "more": "More", "posted_ago_by_guest": "dikirim %1 oleh pelawat", "posted_ago_by": "dikirim %1 oleh %2", "posted_ago": "dikirim %1", diff --git a/public/language/ms/groups.json b/public/language/ms/groups.json index 767f1e778f..bb12dd6325 100644 --- a/public/language/ms/groups.json +++ b/public/language/ms/groups.json @@ -6,6 +6,12 @@ "no_groups_found": "Tiada kumpulan untuk dilihat", "pending.accept": "Terima", "pending.reject": "Tolak", + "pending.accept_all": "Accept All", + "pending.reject_all": "Reject All", + "pending.none": "There are no pending members at this time", + "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", "cover-instructions": "Seret dan lepas gambar, lepas ke posisi, dan tekan Simpan", "cover-change": "Ubah", "cover-save": "Simpan", @@ -13,6 +19,7 @@ "details.title": "Perincian Kumpulan", "details.members": "Senarai Ahli", "details.pending": "Ahli Menunggu", + "details.invited": "Invited Members", "details.has_no_posts": "Kumpulan ahli kumpulan ini belum membuat sebarang kiriman.", "details.latest_posts": "Kiriman Terkini", "details.private": "Privasi", diff --git a/public/language/ms/notifications.json b/public/language/ms/notifications.json index 0d8fccbef6..2da026aa39 100644 --- a/public/language/ms/notifications.json +++ b/public/language/ms/notifications.json @@ -20,6 +20,7 @@ "user_posted_topic": "%1 membuka topik baru : %2", "user_mentioned_you_in": "%1 sebut anda di %2", "user_started_following_you": "%1 mula mengikut anda.", + "new_register": "%1 sent a registration request.", "email-confirmed": "EMel Disahkan", "email-confirmed-message": "Terima kasih kerana mengesahkan emel anda. Akaun anda telah diaktifkan sepenuhnya.", "email-confirm-error-message": "Berlaku masalah semasa mengesahkan emel anda. Mungkin kod tidak sah atau tamat tempoh.", diff --git a/public/language/ms/register.json b/public/language/ms/register.json index 1ab7f35d73..633ff4703d 100644 --- a/public/language/ms/register.json +++ b/public/language/ms/register.json @@ -14,5 +14,6 @@ "register_now_button": "Daftar Sekarang", "alternative_registration": "Pendaftaran Alternatif", "terms_of_use": "Terma Penggunaan", - "agree_to_terms_of_use": "Saya bersetuju dengan Terma Penggunaan" + "agree_to_terms_of_use": "Saya bersetuju dengan Terma Penggunaan", + "registration-added-to-queue": "Your registration has been added to the approval queue. You will receive an email when it is accepted by an administrator." } \ No newline at end of file diff --git a/public/language/ms/unread.json b/public/language/ms/unread.json index 05db3cbf70..d12320a5c9 100644 --- a/public/language/ms/unread.json +++ b/public/language/ms/unread.json @@ -5,5 +5,6 @@ "mark_as_read": "Tanda sebagai sudah dibaca", "selected": "Dipilih", "all": "Semua", + "all_categories": "All categories", "topics_marked_as_read.success": "Topik ditandakan sebagai sudah dibaca" } \ No newline at end of file diff --git a/public/language/ms/user.json b/public/language/ms/user.json index a9f7b41dd1..21a7c0a504 100644 --- a/public/language/ms/user.json +++ b/public/language/ms/user.json @@ -6,8 +6,12 @@ "postcount": "Jumlah Kiriman", "email": "Emel", "confirm_email": "Pastikan Emel", + "ban_account": "Ban Account", + "ban_account_confirm": "Do you really want to ban this user?", + "unban_account": "Unban Account", "delete_account": "Padam Akaun", "delete_account_confirm": "Anda yakin untuk paddam 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": "Are you sure you want to delete this account?
This action is irreversible and you will not be able to recover any data

", "fullname": "Nama penuh", "website": "Laman Web", "location": "Lokasi", @@ -64,9 +68,9 @@ "settings-require-reload": "Sebahagian perubahan tetapan memerlukan segar semula. Klik sini untuk segar semula halaman ini.", "has_no_follower": "Pengguna ini tiada pengikut :(", "follows_no_one": "Pengguna ini tidak mengikuti sesiapa :(", - "has_no_posts": "Pengguna ini masih belum mengirim sebarang kiriman", - "has_no_topics": "Pengguna ini masih belum menghantar sebarang topik", - "has_no_watched_topics": "Pengguna ini masih belum melihat sebarang topik", + "has_no_posts": "This user hasn't posted anything yet.", + "has_no_topics": "This user hasn't posted any topics yet.", + "has_no_watched_topics": "This user hasn't watched any topics yet.", "email_hidden": "Emel disembunyikan", "hidden": "disembunyikan", "paginate_description": "Gunakan muka surat untuk topik dan kiriman daripada penggunaan skroll infiniti", diff --git a/public/language/ms/users.json b/public/language/ms/users.json index 5f373391d5..f66effd754 100644 --- a/public/language/ms/users.json +++ b/public/language/ms/users.json @@ -8,5 +8,14 @@ "users-found-search-took": "%1 pengguna dijumpai! Pencarian ambil masa %2 saat.", "filter-by": "Saring dengan", "online-only": "Atas talian sahaja", - "picture-only": "Gambar sahaja" + "picture-only": "Gambar sahaja", + "invite": "Invite", + "invitation-email-sent": "An invitation email has been sent to %1", + "user_list": "User List", + "recent_topics": "Recent Topics", + "popular_topics": "Popular Topics", + "unread_topics": "Unread Topics", + "categories": "Categories", + "tags": "Tags", + "map": "Map" } \ No newline at end of file diff --git a/public/language/nb/category.json b/public/language/nb/category.json index 673aec04c3..f4b2298a0d 100644 --- a/public/language/nb/category.json +++ b/public/language/nb/category.json @@ -1,7 +1,7 @@ { "new_topic_button": "Nytt emne", "guest-login-post": "Logg inn til innlegg", - "no_topics": "Det er ingen emner i denne kategorien
Hvorfor ikke lage ett?", + "no_topics": "Det er ingen emner i denne kategorien
Hvorfor ikke lage et?", "browsing": "leser", "no_replies": "Ingen har svart", "share_this_category": "Del denne kategorien", diff --git a/public/language/nb/email.json b/public/language/nb/email.json index 37b5df85d0..21deb6781c 100644 --- a/public/language/nb/email.json +++ b/public/language/nb/email.json @@ -1,11 +1,15 @@ { "password-reset-requested": "Tilbakestilling av passord - %1!", "welcome-to": "Velkommen til %1", + "invite": "Invitation from %1", "greeting_no_name": "Hallo", "greeting_with_name": "Hallo, %1", "welcome.text1": "Takk for at du registrerte deg hos %1!", - "welcome.text2": "For å aktivere kontoen din må vi verifisere at du eier epost-adressen du registrerte med.", - "welcome.cta": "Klikk her for å verifisere epost-adressen din", + "welcome.text2": "For å aktivere kontoen din må vi verifisere at du eier e-postadressen du registrerte med.", + "welcome.text3": "An administrator has accepted your registration application. You can login with your username/password now.", + "welcome.cta": "Klikk her for å verifisere e-postadressen din", + "invitation.text1": "%1 has invited you to join %2", + "invitation.ctr": "Click here to create your account.", "reset.text1": "Vi har blir bedt om å tilbakestille passordet ditt, muligens fordi du har glemt det. Hvis dette ikke stemmer kan du ignorere denne eposten.", "reset.text2": "For å fortsette med tilbakestillingen, vennligst klikk på følgende lenke:", "reset.cta": "Klikk her for å tilbakestille passordet ditt", diff --git a/public/language/nb/error.json b/public/language/nb/error.json index fad46d11e6..e530bc925d 100644 --- a/public/language/nb/error.json +++ b/public/language/nb/error.json @@ -1,8 +1,8 @@ { - "invalid-data": "Ugyldig data", + "invalid-data": "Ugyldige data", "not-logged-in": "Du ser ikke ut til å være logget inn.", "account-locked": "Kontoen din har blitt midlertidig låst", - "search-requires-login": "Searching requires an account - please login or register.", + "search-requires-login": "Søking krever en konto - vennligst logg inn eller registrer deg.", "invalid-cid": "Ugyldig kategori-ID", "invalid-tid": "Ugyldig emne-ID", "invalid-pid": "Ugyldig innlegg-ID", @@ -21,11 +21,11 @@ "email-not-confirmed-chat": "Du kan ikke chatte før e-posten din er bekreftet, vennligst klikk her for å bekrefte e-postadressen.", "no-email-to-confirm": "Dette forumet krever at e-postbekreftelse, vennligst klikk her for å skrive inn en e-post", "email-confirm-failed": "Vi kunne ikke godkjenne e-posten din, vennligst prøv igjen senere.", - "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", + "confirm-email-already-sent": "Bekreftelsesepost allerede sendt, vennligst vent %1 minutt(er) for å sende enn til.", "username-too-short": "Brukernavnet er for kort", "username-too-long": "Brukernavnet er for langt", "user-banned": "Bruker utestengt", - "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", + "user-too-new": "Beklager, du må vente %1 sekund(er) før du lager ditt første innlegg", "no-category": "Kategorien eksisterer ikke.", "no-topic": "Emne eksisterer ikke", "no-post": "Innlegg eksisterer ikke", @@ -36,39 +36,42 @@ "no-emailers-configured": "Ingen e-post-tillegg er lastet, så ingen test e-post kunne bli sendt", "category-disabled": "Kategori deaktivert", "topic-locked": "Emne låst", - "post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting", + "post-edit-duration-expired": "Du har bare lov til å redigere innlegg i %1 sekund(er) etter posting", "still-uploading": "Vennligst vent til opplastingene blir fullført.", - "content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).", - "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).", - "title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).", - "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).", - "too-many-posts": "You can only post once every %1 second(s) - please wait before posting again", - "too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again", - "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)", - "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)", - "file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file", + "content-too-short": "Vennligst skriv et lengre innlegg. Innlegg må inneholde minst %1 tegn.", + "content-too-long": "Vennligst skriv et kortere innlegg. Innlegg kan ikke være lengre enn %1 tegn.", + "title-too-short": "Vennligst skriv en lengre tittel. Titler må inneholde minst %1 tegn.", + "title-too-long": "Vennligst skriv en kortere tittel. Tittel kan ikke være lengre enn %1 tegn.", + "too-many-posts": "Du kan bare poste hvert %1 sekund – vennligst vent før du poster igjen", + "too-many-posts-newbie": "Som en ny bruker kan du bare poste en gang hvert %1. sekund, før du har opparbeidet %2 rykte – vennligst vent før du poster igjen", + "tag-too-short": "Vennligst skriv en lengre tag. Tagger må være på minst %1 tegn", + "tag-too-long": "Vennligst skriv en kortere tag. Tagger kan ikke være lengre enn %1 tegn", + "file-too-big": "Største tillatte filstørrelse er %1 kB – vennligst last opp en mindre fil", "cant-vote-self-post": "Du kan ikke stemme på ditt eget innlegg", "already-favourited": "Du har allerede favorittmerket dette innlegget", "already-unfavourited": "Du har allerede avfavorisert dette innlegget", "cant-ban-other-admins": "Du kan ikke utestenge andre administratorer!", + "cant-remove-last-admin": "You are the only administrator. Add another user as an administrator before removing yourself as admin", "invalid-image-type": "Ugyldig bildetype. Tilatte typer er: %1", "invalid-image-extension": "Ugyldig bildefiltype", - "invalid-file-type": "Invalid file type. Allowed types are: %1", + "invalid-file-type": "Ugyldig filtype. Tillatte typer er: %1", "group-name-too-short": "Gruppenavnet er for kort", "group-already-exists": "Gruppe eksisterer allerede", "group-name-change-not-allowed": "Gruppenavn ikke tillatt", "group-already-member": "Du er allerede en del av denne gruppen", "group-needs-owner": "Denne gruppen kreves minst en eier", + "group-already-invited": "This user has already been invited", + "group-already-requested": "Your membership request has already been submitted", "post-already-deleted": "Dette innlegget har blitt slettet", "post-already-restored": "Dette innlegget har allerede blitt gjenopprettet", "topic-already-deleted": "Dette emnet har allerede blitt slettet", "topic-already-restored": "Dette emnet har allerede blitt gjenopprettet", - "cant-purge-main-post": "You can't purge the main post, please delete the topic instead", + "cant-purge-main-post": "Du kan ikke slette hovedinnlegget. Vennligst slett emnet i stedet.", "topic-thumbnails-are-disabled": "Emne-minatyrbilder har blitt deaktivert", "invalid-file": "Ugyldig fil", "uploads-are-disabled": "Opplastninger er deaktivert", - "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", - "about-me-too-long": "Sorry, your about me cannot be longer than %1 character(s).", + "signature-too-long": "Beklager, din signatur kan ikke være lengre enn %1 tegn", + "about-me-too-long": "Beklager, din om meg kan ikke være lengre enn %1 tegn.", "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.", @@ -76,6 +79,7 @@ "downvoting-disabled": "Nedstemming av deaktivert", "not-enough-reputation-to-downvote": "Du har ikke nok rykte til å nedstemme det innlegget", "not-enough-reputation-to-flag": "Du har ikke nok rykte til å flagge dette innlegget", + "already-flagged": "You have already flagged this post", "reload-failed": "NodeBB støtte på et problem under lasting på nytt: \"%1\". NodeBB vil fortsette å servere eksisterende klientside ressureser, selv om du burde angre endringene du gjorde før du lastet på nytt.", "registration-error": "Feil under registrering", "parse-error": "Noe gikk feil under analysering av serversvar", diff --git a/public/language/nb/global.json b/public/language/nb/global.json index 9528d360c5..4576a39c21 100644 --- a/public/language/nb/global.json +++ b/public/language/nb/global.json @@ -50,6 +50,7 @@ "views": "Visninger", "reputation": "Rykte", "read_more": "les mer", + "more": "More", "posted_ago_by_guest": "skrevet %1 av Gjest", "posted_ago_by": "skrevet %1 av %2", "posted_ago": "skrevet %1", diff --git a/public/language/nb/groups.json b/public/language/nb/groups.json index 0668648206..99fee661d7 100644 --- a/public/language/nb/groups.json +++ b/public/language/nb/groups.json @@ -6,6 +6,12 @@ "no_groups_found": "Det er ingen grupper å se", "pending.accept": "Aksepter", "pending.reject": "Avslå", + "pending.accept_all": "Accept All", + "pending.reject_all": "Reject All", + "pending.none": "There are no pending members at this time", + "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", "cover-instructions": "Dra og slipp et bilde, dra til posisjon, og trykk Lagre", "cover-change": "Endre", "cover-save": "Lagre", @@ -13,6 +19,7 @@ "details.title": "Gruppedetaljer", "details.members": "Medlemsliste", "details.pending": "Ventende meldemmer", + "details.invited": "Invited Members", "details.has_no_posts": "Medlemmene i denne gruppen har ikke skrevet noen innlegg.", "details.latest_posts": "Seneste innlegg", "details.private": "Privat", @@ -20,8 +27,8 @@ "details.kick": "Kast ut", "details.owner_options": "Gruppeadministrasjon", "details.group_name": "Gruppenavn", - "details.member_count": "Member Count", - "details.creation_date": "Creation Date", + "details.member_count": "Antall medlemmer", + "details.creation_date": "Opprettelsesdato", "details.description": "Beskrivelse", "details.badge_preview": "Forhåndsvisning av skilt", "details.change_icon": "Endre ikon", diff --git a/public/language/nb/notifications.json b/public/language/nb/notifications.json index 33ff86eff6..5c58b3d7f5 100644 --- a/public/language/nb/notifications.json +++ b/public/language/nb/notifications.json @@ -2,7 +2,7 @@ "title": "Varsler", "no_notifs": "Du har ingen nye varsler", "see_all": "Se alle varsler", - "mark_all_read": "Mark all notifications read", + "mark_all_read": "Merk alle varsler som lest", "back_to_home": "Tilbake til %1", "outgoing_link": "Utgående link", "outgoing_link_message": "Du forlater nå %1.", @@ -20,6 +20,7 @@ "user_posted_topic": "%1 har skrevet et nytt emne: %2", "user_mentioned_you_in": "%1 nevnte deg i %2", "user_started_following_you": "%1 begynte å følge deg.", + "new_register": "%1 sent a registration request.", "email-confirmed": "E-post bekreftet", "email-confirmed-message": "Thank for at du har validert din e-post. Kontoen din er nå fullstendig aktivert.", "email-confirm-error-message": "Det oppsto et problem under valdiering av din e-post. Koden kan ha vært ugyldig eller ha utløpt.", diff --git a/public/language/nb/recent.json b/public/language/nb/recent.json index 3b980f6f02..bdee8a1f58 100644 --- a/public/language/nb/recent.json +++ b/public/language/nb/recent.json @@ -6,7 +6,7 @@ "year": "År", "alltime": "All tid", "no_recent_topics": "Det er ingen nye tråder.", - "no_popular_topics": "There are no popular topics.", + "no_popular_topics": "Det er ingen populære emner.", "there-is-a-new-topic": "Det finnes et nytt emne.", "there-is-a-new-topic-and-a-new-post": "Det finnes et nytt emne og et nytt innlegg.", "there-is-a-new-topic-and-new-posts": "Det finnes et nytt emne og %1 nye innlegg.", diff --git a/public/language/nb/register.json b/public/language/nb/register.json index 0ba98db455..68a0b8a9f2 100644 --- a/public/language/nb/register.json +++ b/public/language/nb/register.json @@ -14,5 +14,6 @@ "register_now_button": "Registrer nå", "alternative_registration": "Alternativ registrering", "terms_of_use": "Vilkårene for bruk", - "agree_to_terms_of_use": "Jeg godtar vilkårene for bruk" + "agree_to_terms_of_use": "Jeg godtar vilkårene for bruk", + "registration-added-to-queue": "Your registration has been added to the approval queue. You will receive an email when it is accepted by an administrator." } \ No newline at end of file diff --git a/public/language/nb/reset_password.json b/public/language/nb/reset_password.json index f9cd7bdf72..bf51d99140 100644 --- a/public/language/nb/reset_password.json +++ b/public/language/nb/reset_password.json @@ -11,7 +11,7 @@ "enter_email_address": "Skriv e-post", "password_reset_sent": "Passord-tilbakestilling sendt", "invalid_email": "Ugyldig e-post / e-post eksisterer ikke", - "password_too_short": "The password entered is too short, please pick a different password.", - "passwords_do_not_match": "The two passwords you've entered do not match.", - "password_expired": "Your password has expired, please choose a new password" + "password_too_short": "Passordet du skrev inn er for kort, velg et lengre passord.", + "passwords_do_not_match": "Passordene du har skrevet inn stemmer ikke overens.", + "password_expired": "Passordet ditt er utgått, velg et nytt passord" } \ No newline at end of file diff --git a/public/language/nb/search.json b/public/language/nb/search.json index 51c3d113da..a62f2d9cb5 100644 --- a/public/language/nb/search.json +++ b/public/language/nb/search.json @@ -1,7 +1,7 @@ { "results_matching": "%1 resultat(er) samsvarer med \"%2\", (%3 sekunder)", "no-matches": "Ingen matcher funnet", - "advanced-search": "Advanced Search", + "advanced-search": "Avansert søk", "in": "I", "titles": "Titler", "titles-posts": "Titler og innlegg", @@ -30,11 +30,11 @@ "topic-start-date": "Starttid for emne", "username": "Brukernavn", "category": "Kategori", - "descending": "In descending order", - "ascending": "In ascending order", + "descending": "I synkende rekkefølge", + "ascending": "I stigende rekkefølge", "save-preferences": "Lagre innstillinger", "clear-preferences": "Tøm innstillinnger", "search-preferences-saved": "Søkeinnstillinger lagret", "search-preferences-cleared": "Søkeinnstillinger tømt", - "show-results-as": "Vis resultateter som" + "show-results-as": "Vis resultater som" } \ No newline at end of file diff --git a/public/language/nb/tags.json b/public/language/nb/tags.json index 5287b6125a..c9a6efbab7 100644 --- a/public/language/nb/tags.json +++ b/public/language/nb/tags.json @@ -1,7 +1,7 @@ { "no_tag_topics": "Det er ingen emnet med denne taggen.", "tags": "Tagger", - "enter_tags_here": "Enter tags here, between %1 and %2 characters each.", + "enter_tags_here": "Skriv tagger her, mellom %1 og %2 tegn hver.", "enter_tags_here_short": "Skriv tagger...", "no_tags": "Det finnes ingen tagger enda." } \ No newline at end of file diff --git a/public/language/nb/topic.json b/public/language/nb/topic.json index 3863fd48ae..49afb62941 100644 --- a/public/language/nb/topic.json +++ b/public/language/nb/topic.json @@ -5,7 +5,7 @@ "no_topics_found": "Ingen emner funnet!", "no_posts_found": "Ingen innlegg funnet!", "post_is_deleted": "Dette innlegget er slettet!", - "topic_is_deleted": "This topic is deleted!", + "topic_is_deleted": "Dette emnet er slettet!", "profile": "Profil", "posted_by": "Skapt av %1", "posted_by_guest": "Skapt av Gjest", @@ -13,7 +13,7 @@ "notify_me": "Bli varslet om nye svar i dette emnet", "quote": "Siter", "reply": "Svar", - "guest-login-reply": "Log in to reply", + "guest-login-reply": "Logg inn for å besvare", "edit": "Endre", "delete": "Slett", "purge": "Tøm", diff --git a/public/language/nb/unread.json b/public/language/nb/unread.json index 34d57eb1d8..2abd16760f 100644 --- a/public/language/nb/unread.json +++ b/public/language/nb/unread.json @@ -5,5 +5,6 @@ "mark_as_read": "Marker som lest", "selected": "Valgte", "all": "Alle", + "all_categories": "All categories", "topics_marked_as_read.success": "Emner merket som lest!" } \ No newline at end of file diff --git a/public/language/nb/user.json b/public/language/nb/user.json index be80c50470..85141201a5 100644 --- a/public/language/nb/user.json +++ b/public/language/nb/user.json @@ -6,8 +6,12 @@ "postcount": "Antall innlegg", "email": "E-post", "confirm_email": "Bekfreft e-post", + "ban_account": "Ban Account", + "ban_account_confirm": "Do you really want to ban this user?", + "unban_account": "Unban Account", "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": "Are you sure you want to delete this account?
This action is irreversible and you will not be able to recover any data

", "fullname": "Fullt navn", "website": "Nettsted", "location": "Plassering", @@ -21,14 +25,14 @@ "watched": "Overvåkede", "followers": "Følgere", "following": "Følger", - "aboutme": "About me", + "aboutme": "Om meg", "signature": "Signatur", "gravatar": "Gravatar", "birthday": "Bursdag", - "chat": "Chatt", + "chat": "Chat", "follow": "Følg", "unfollow": "Avfølg", - "more": "More", + "more": "Mer", "profile_update_success": "Profilen ble oppdatert!", "change_picture": "Bytt bilde", "edit": "Endre", @@ -60,25 +64,25 @@ "digest_weekly": "Ukentlig", "digest_monthly": "Månedlig", "send_chat_notifications": "Send en epost hvis jeg mottar en chat-melding når jeg ikke er pålogget", - "send_post_notifications": "Send an email when replies are made to topics I am subscribed to", - "settings-require-reload": "Some setting changes require a reload. Click here to reload the page.", + "send_post_notifications": "Send en e-post når svar postes til emner jeg abonnerer på", + "settings-require-reload": "Noen innstillingsendringer krever at du laster siden på nytt. Klikk her for å laste på nytt.", "has_no_follower": "Denne brukeren har ingen følgere :(", "follows_no_one": "Denne brukeren følger ingen :(", - "has_no_posts": "Denne brukeren har ikke skrevet noe enda.", - "has_no_topics": "Denne brukeren har ikke skrevet noen emner enda.", - "has_no_watched_topics": "Denne brukeren overvåker ingen innlegg foreløpig.", + "has_no_posts": "This user hasn't posted anything yet.", + "has_no_topics": "This user hasn't posted any topics yet.", + "has_no_watched_topics": "This user hasn't watched any topics yet.", "email_hidden": "E-post skjult", "hidden": "skjult", - "paginate_description": "Paginate topics and posts instead of using infinite scroll", + "paginate_description": "Bruk sidevelger for emner og innlegg istedet for uendelig scrolling", "topics_per_page": "Tråd per side", "posts_per_page": "Innlegg per side", - "notification_sounds": "Play a sound when you receive a notification", + "notification_sounds": "Spill en lyd når du mottar et varsel", "browsing": "Surfeinnstillinger", - "open_links_in_new_tab": "Open outgoing links in new tab", + "open_links_in_new_tab": "Åpne utgående lenker i en ny fane", "enable_topic_searching": "Aktiver søk-i-emne", - "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen", - "follow_topics_you_reply_to": "Follow topics that you reply to", - "follow_topics_you_create": "Follow topics you create", - "grouptitle": "Select the group title you would like to display", - "no-group-title": "No group title" + "topic_search_help": "Hvis søk-i-emne er aktivert, overstyres nettleserens standard sidesøk og gir mulighet til å søke gjennom hele emnet, ikke bare det som vises på skjermen", + "follow_topics_you_reply_to": "Følg emner du besvarer", + "follow_topics_you_create": "Følg emner du oppretter", + "grouptitle": "Velg gruppetittelen du vil vise", + "no-group-title": "Ingen gruppetittel" } \ No newline at end of file diff --git a/public/language/nb/users.json b/public/language/nb/users.json index 661479fbf1..1b379cc07f 100644 --- a/public/language/nb/users.json +++ b/public/language/nb/users.json @@ -5,8 +5,17 @@ "search": "Søk", "enter_username": "Skriv ett brukernavn for å søke", "load_more": "Last flere", - "users-found-search-took": "%1 user(s) found! Search took %2 seconds.", + "users-found-search-took": "%1 bruker(e) funnet. Søket tok %2 sekunder.", "filter-by": "Filtrer etter", "online-only": "Bare påloggede", - "picture-only": "Bare bilde" + "picture-only": "Bare bilde", + "invite": "Invite", + "invitation-email-sent": "An invitation email has been sent to %1", + "user_list": "User List", + "recent_topics": "Recent Topics", + "popular_topics": "Popular Topics", + "unread_topics": "Unread Topics", + "categories": "Categories", + "tags": "Tags", + "map": "Map" } \ No newline at end of file diff --git a/public/language/nl/category.json b/public/language/nl/category.json index 9070eb6ba4..7421cee928 100644 --- a/public/language/nl/category.json +++ b/public/language/nl/category.json @@ -2,7 +2,7 @@ "new_topic_button": "Nieuw onderwerp", "guest-login-post": "Log in om een reactie te plaatsen", "no_topics": "Er zijn geen onderwerpen in deze categorie.
Waarom maak je er niet een aan?", - "browsing": "verkennen", + "browsing": "browsing", "no_replies": "Niemand heeft gereageerd", "share_this_category": "Deel deze categorie", "watch": "Volgen", diff --git a/public/language/nl/email.json b/public/language/nl/email.json index 6eeb3de7f6..c147fdaf92 100644 --- a/public/language/nl/email.json +++ b/public/language/nl/email.json @@ -1,11 +1,15 @@ { - "password-reset-requested": "Om wachtwoordherstel verzocht - %1!", + "password-reset-requested": "Wachtwoord reset gevraagd - %1!", "welcome-to": "Welkom bij %1", + "invite": "Uitnodiging van %1 ", "greeting_no_name": "Hallo", "greeting_with_name": "Hallo %1", "welcome.text1": "Bedank voor het registreren bij %1!", "welcome.text2": "Om de account volledig te activeren, dienen de instructies uit het bevestigingsbericht opgevolgd te worden. Controleer daarom nu eerst de e-mail inbox voor de activeringscode en volg de link in het bericht.", + "welcome.text3": "Een administrator heeft uw registratie geaccepteerd. U kan nu inloggen met uw gebruikersnaam en wachtwoord.", "welcome.cta": "Klik hier voor bevestigen van het e-mailadres", + "invitation.text1": "%1 heeft u uitgenodigd voor %2 ", + "invitation.ctr": "Klik hier om uw account aan te maken.", "reset.text1": "Wij ontvingen zojuist een verzoek om het wachtwoord van de account, bij onze website bekend als gekoppeld aan dit e-mailadres, te herstellen. Is dit verzoek niet bekend en geautoriseerd, dan kan dit bericht genegeerd worden.", "reset.text2": "Om het wachtwoord opnieuw in te stellen, klik op deze link:", "reset.cta": "Klik hier voor wachtwoordherstel", diff --git a/public/language/nl/error.json b/public/language/nl/error.json index 95f7d04f1d..0c9de298df 100644 --- a/public/language/nl/error.json +++ b/public/language/nl/error.json @@ -2,7 +2,7 @@ "invalid-data": "Ongeldige Data", "not-logged-in": "De account lijkt op dit moment niet aangemeld te zijn.", "account-locked": "De account is tijdelijk vergrendeld", - "search-requires-login": "Searching requires an account - please login or register.", + "search-requires-login": "Zoeken vereist een account - gelieve aan te melden of te registreren.", "invalid-cid": "Ongeldig categoriesleutel", "invalid-tid": "Ongeldig id voor onderwerp", "invalid-pid": "Ongeldig berichtkenmerk", @@ -51,6 +51,7 @@ "already-favourited": "Dit bericht staat al tussen de favorieten", "already-unfavourited": "Dit bericht is al uit favorieten verwijderd", "cant-ban-other-admins": "Het is niet toegestaan andere beheerders te verbannen!", + "cant-remove-last-admin": "U bent de enigen administrator. Voeg een andere gebruiker toe als administrator voordat u uw zelf verweiderd als admin", "invalid-image-type": "Ongeldig bestandstype afbeelding. Deze afbeelding is van een bestandstype dat niet ondersteund wordt. Toegestane bestandstypes voor afbeeldingsbestanden zijn: %1", "invalid-image-extension": "Ongeldige bestandstype afbeelding", "invalid-file-type": "Dit bestandstype wordt niet ondersteund. Toegestane bestandstypen zijn: %1", @@ -59,6 +60,8 @@ "group-name-change-not-allowed": "Het veranderen van de groepsnaam is niet toegestaan!", "group-already-member": "Groepslidmaatschap al aanwezig", "group-needs-owner": "De groep vereist ten minste 1 eigenaar", + "group-already-invited": "Deze gebruiker is all uitgenodigt ", + "group-already-requested": "Uw lidmaatschap aanvraag is all verstuurd", "post-already-deleted": "Dit bericht is al verwijderd", "post-already-restored": "Dit bericht is al hersteld", "topic-already-deleted": "Dit onderwerp is al verwijderd", @@ -67,8 +70,8 @@ "topic-thumbnails-are-disabled": "Miniatuurweergaven bij onderwerpen uitgeschakeld. ", "invalid-file": "Ongeldig bestand", "uploads-are-disabled": "Uploads zijn uitgeschakeld", - "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", - "about-me-too-long": "Sorry, your about me cannot be longer than %1 character(s).", + "signature-too-long": "Sorry, je onderschrift kan niet langer zijn da %1 karakter(s).", + "about-me-too-long": "Sorry, je over mij kan niet langer zijn da %1 karakter(s).", "cant-chat-with-yourself": "Het is niet mogelijk met jezelf een chatgesprek te houden.", "chat-restricted": "Deze gebruiker heeft beperkingen aan de chatfunctie opgelegd waardoor deze eerst iemand moet volgen voordat deze persoon een nieuwe chat mag initiëren.", "too-many-messages": "Er zijn in korte tijd teveel berichten verzonden, een moment geduld.", @@ -76,6 +79,7 @@ "downvoting-disabled": "Negatief stemmen staat uitgeschakeld.", "not-enough-reputation-to-downvote": "Deze gebruikersaccount beschikt over onvoldoende reputatie om een negatieve stem uit te mogen brengen.", "not-enough-reputation-to-flag": "Onvoldoende reputatie om dit bericht aan beheerders te mogen melden.", + "already-flagged": "U heeft deze post all gerapporteerd ", "reload-failed": "Tijdens het herladen van \"%1\" is NodeBB een fout of probleem tegen gekomen. NodeBB blijft operationeel echter het is verstandig om de oorzaak te onderzoeken en wellicht de vorige actie, voor het herladen, ongedaan te maken.", "registration-error": "Fout tijdens registratie", "parse-error": "Tijdens het verwerken van het antwoord van de server is iets misgegaan.", diff --git a/public/language/nl/global.json b/public/language/nl/global.json index 9d0b5c6b2a..a12b42d659 100644 --- a/public/language/nl/global.json +++ b/public/language/nl/global.json @@ -50,6 +50,7 @@ "views": "Gezien", "reputation": "Reputatie", "read_more": "Lees meer", + "more": "Meer", "posted_ago_by_guest": "geplaatst %1 door gast", "posted_ago_by": "geplaatst %1 door %2", "posted_ago": "geplaatst door %1", diff --git a/public/language/nl/groups.json b/public/language/nl/groups.json index 3ee0fccb73..ac39b79886 100644 --- a/public/language/nl/groups.json +++ b/public/language/nl/groups.json @@ -6,6 +6,12 @@ "no_groups_found": "Geen groepen voor weergave", "pending.accept": "Accepteer", "pending.reject": "Afwijzen", + "pending.accept_all": "Iedereen accepteren", + "pending.reject_all": "Iedereen afwijzen", + "pending.none": "Er zijn geen afwachtende leden op het moment", + "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", "cover-instructions": "Sleep een afbeelding, sleep om te positioneren en klik tenslotte op Opslaan", "cover-change": "Bewerken", "cover-save": "Opslaan", @@ -13,21 +19,22 @@ "details.title": "Groepsdetails", "details.members": "Ledenlijst", "details.pending": "Nog niet geaccepteerde leden", + "details.invited": "Uitgenodigde leden", "details.has_no_posts": "Deze groepleden hebben nog geen berichten geplaatst", "details.latest_posts": "Meest recente berichten", "details.private": "Prive", "details.grant": "Toekennen/herroepen van eigendom", - "details.kick": "Schoppen", + "details.kick": "Kick", "details.owner_options": "Groepsadministratie", "details.group_name": "Groepsnaam", "details.member_count": "Ledentelling", "details.creation_date": "Aangemaakt op", "details.description": "Beschrijving", - "details.badge_preview": "Draaginsigne voorvertoning", + "details.badge_preview": "Badge Voorbeeld", "details.change_icon": "Wijzig icoon", "details.change_colour": "Wijzig kleur", - "details.badge_text": "Draaginsigne tekst", - "details.userTitleEnabled": "Draaginsignes weergeven", + "details.badge_text": "Badge Tekst", + "details.userTitleEnabled": "Badge Weergeven", "details.private_help": "Wanneer ingeschakeld, zal eerst een groepseigenaar goedkeuring moeten verlenen voordat nieuwe leden kunnen toetreden", "details.hidden": "Niet getoond", "details.hidden_help": "Indien geactiveerd zal deze groep niet getoond worden in de groepslijst en zullen gebruikers handmatig uitgenodigd moeten worden.", diff --git a/public/language/nl/notifications.json b/public/language/nl/notifications.json index fc2e904d90..f813085261 100644 --- a/public/language/nl/notifications.json +++ b/public/language/nl/notifications.json @@ -20,6 +20,7 @@ "user_posted_topic": "%1 heeft een nieuw onderwerp geplaatst: %2", "user_mentioned_you_in": "Onze naam is genoemd door %1 in %2.", "user_started_following_you": "%1 volgt ons nu.", + "new_register": "%1 heeft een registratie verzoek aangevraagd.", "email-confirmed": "E-mailadres bevestigd", "email-confirmed-message": "Bedankt voor het bevestigen van het e-mailadres. Deze account is nu volledig geactiveerd.", "email-confirm-error-message": "Er was een probleem met het bevestigen van dit e-mailadres. Misschien is de code niet goed ingevoerd of was de beschikbare tijd inmiddels verstreken.", diff --git a/public/language/nl/recent.json b/public/language/nl/recent.json index 887a149da0..229a8c5e69 100644 --- a/public/language/nl/recent.json +++ b/public/language/nl/recent.json @@ -4,7 +4,7 @@ "week": "Week", "month": "Maand", "year": "Jaar", - "alltime": "Intussen", + "alltime": "altijd", "no_recent_topics": "Er zijn geen recente reacties.", "no_popular_topics": "Er zijn geen populaire onderwerpen.", "there-is-a-new-topic": "Er is een nieuw onderwerp", diff --git a/public/language/nl/register.json b/public/language/nl/register.json index 09dc7f786a..ed4b13a411 100644 --- a/public/language/nl/register.json +++ b/public/language/nl/register.json @@ -14,5 +14,6 @@ "register_now_button": "Nu Registreren", "alternative_registration": "Alternatieve Registratie", "terms_of_use": "Gebruiksvoorwaarden", - "agree_to_terms_of_use": "Ik ga akkoord van de Gebruiksvoorwaarden" + "agree_to_terms_of_use": "Ik ga akkoord van de Gebruiksvoorwaarden", + "registration-added-to-queue": "Uw registratie is toegevoegd aan de wachtrij. U krijgt een email wanneer uw registratie is geaccepteerd " } \ No newline at end of file diff --git a/public/language/nl/search.json b/public/language/nl/search.json index 7b558be954..da9e793e8b 100644 --- a/public/language/nl/search.json +++ b/public/language/nl/search.json @@ -9,8 +9,8 @@ "in-categories": "In categorieën", "search-child-categories": "Doorzoek subcategorieën ", "reply-count": "Aantal reacties", - "at-least": "Minimaal", - "at-most": "Maximaal", + "at-least": "op zijn minst", + "at-most": "op zijn meest", "post-time": "Geplaatst op", "newer-than": "Nieuwer dan", "older-than": "Ouder dan", diff --git a/public/language/nl/topic.json b/public/language/nl/topic.json index 38593ecca8..90ac37510d 100644 --- a/public/language/nl/topic.json +++ b/public/language/nl/topic.json @@ -5,7 +5,7 @@ "no_topics_found": "Geen onderwerpen gevonden!", "no_posts_found": "Geen berichten gevonden!", "post_is_deleted": "Dit bericht is verwijderd!", - "topic_is_deleted": "This topic is deleted!", + "topic_is_deleted": "Dit onderwerp is verwijderd!", "profile": "Profiel", "posted_by": "Geplaatst door %1", "posted_by_guest": "Geplaatst door gast", @@ -27,15 +27,15 @@ "locked": "Gesloten", "bookmark_instructions": "Klik hier om naar de vorige positie terug te keren of sluit af om te verwerpen.", "flag_title": "Bericht aan beheerders melden", - "flag_confirm": "Is het echt de bedoeling dit bericht aan beheerders te rapporteren?", + "flag_confirm": "Weet u het zeker dat u dit bericht wilt rapporteren?", "flag_success": "Het bericht is gerapporteerd aan beheer.", "deleted_message": "Dit onderwerp is verwijderd. Alleen gebruikers met beheerrechten op onderwerpniveau kunnen dit inzien.", "following_topic.message": "Vanaf nu worden meldingen ontvangen zodra iemand een reactie op dit onderwerp geeft.", - "not_following_topic.message": "Er worden niet langer meldingen ontvangen over dit onderwerp.", - "login_to_subscribe": "Aanmelden om op dit onderwerp te abonneren", + "not_following_topic.message": "U ontvangt geen notificaties over dit onderwerp.", + "login_to_subscribe": "Log in or registreer om dit onderwerp te volgen.", "markAsUnreadForAll.success": "Onderwerp is voor iedereen als 'gelezen' gemarkeerd.", "watch": "Volgen", - "unwatch": "Niet volgen", + "unwatch": "Unfollow", "watch.title": "Krijg meldingen van nieuwe reacties op dit onderwerp", "unwatch.title": "Dit onderwerp niet langer volgen", "share_this_post": "Deel dit bericht", @@ -49,8 +49,8 @@ "thread_tools.move_all": "Verplaats alles", "thread_tools.fork": "Onderwerp afsplitsen", "thread_tools.delete": "Onderwerp verwijderen", - "thread_tools.delete_confirm": "Is het echt de bedoeling dit onderwerp te verwijderen?", - "thread_tools.restore": "Onderwerp erstellen", + "thread_tools.delete_confirm": "Weet u het zeker dat u dit onderwerp wilt verwijderen?", + "thread_tools.restore": "Onderwerp herstellen", "thread_tools.restore_confirm": "Zeker weten dit onderwerp te herstellen?", "thread_tools.purge": "Wis onderwerp ", "thread_tools.purge_confirm": "Is het echt de bedoeling dit onderwerp definitief te wissen?", @@ -59,7 +59,7 @@ "post_restore_confirm": "Is het de bedoeling dit bericht te herstellen?", "post_purge_confirm": "Is het absoluut zeker dat dit bericht volledig verwijderd kan worden?", "load_categories": "Categorieën laden", - "disabled_categories_note": "Uitgeschakelde categorieën zijn grijs", + "disabled_categories_note": "Uitgeschakelde Categorieën zijn grijs", "confirm_move": "Verplaatsen", "confirm_fork": "Splits", "favourite": "Favoriet", @@ -86,7 +86,7 @@ "composer.thumb_title": "Voeg een miniatuurweergave toe aan dit onderwerp", "composer.thumb_url_placeholder": "http://example.com/thumb.png", "composer.thumb_file_label": "Of upload een bestand", - "composer.thumb_remove": "Velden legen", + "composer.thumb_remove": "Velden leegmaken", "composer.drag_and_drop_images": "Sleep en zet afbeeldingen hier", "more_users_and_guests": "%1 of meerdere gebruiker(s) en %2 gast(en)", "more_users": "%1 meer gebruiker(s)", diff --git a/public/language/nl/unread.json b/public/language/nl/unread.json index 3149f079ad..3624cddce6 100644 --- a/public/language/nl/unread.json +++ b/public/language/nl/unread.json @@ -5,5 +5,6 @@ "mark_as_read": "Markeer als gelezen", "selected": "Geselecteerd", "all": "Alles", + "all_categories": "Alle categorieën", "topics_marked_as_read.success": "Onderwerp gemarkeerd als gelezen!" } \ No newline at end of file diff --git a/public/language/nl/user.json b/public/language/nl/user.json index 4822209b05..60b7a9b279 100644 --- a/public/language/nl/user.json +++ b/public/language/nl/user.json @@ -6,8 +6,12 @@ "postcount": "Aantal geplaatste berichten", "email": "E-mail", "confirm_email": "Bevestig e-mail", + "ban_account": "Verban Account", + "ban_account_confirm": "Weet u zeker dat u deze gebruiker wilt verbannen", + "unban_account": "Unban Account", "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

", "fullname": "Volledige naam", "website": "Website", "location": "Locatie", @@ -21,7 +25,7 @@ "watched": "Bekeken", "followers": "Volgers", "following": "Volgend", - "aboutme": "About me", + "aboutme": "Over mij", "signature": "Handtekening", "gravatar": "Gravatar", "birthday": "Verjaardag", @@ -65,8 +69,8 @@ "has_no_follower": "Deze gebruiker heeft geen volgers :(", "follows_no_one": "Deze gebruiker volgt niemand :(", "has_no_posts": "Deze gebruiker heeft nog geen berichten geplaatst", - "has_no_topics": "Deze gebruiker heeft nog geen berichten geplaatst", - "has_no_watched_topics": "Deze gebruiker heeft nog geen berichten bekeken", + "has_no_topics": "Deze gebruiker heeft nog geen onderwerpen gestart.", + "has_no_watched_topics": "Deze gebruiker heeft nog geen onderwerpen gevolgd.", "email_hidden": "E-mail niet beschikbaar", "hidden": "verborgen", "paginate_description": "Blader door onderwerpen en berichten in plaats van oneindig scrollen.", diff --git a/public/language/nl/users.json b/public/language/nl/users.json index 32692839eb..275b0b4d5f 100644 --- a/public/language/nl/users.json +++ b/public/language/nl/users.json @@ -8,5 +8,14 @@ "users-found-search-took": "%1 gebruiker(s) gevonden! Zoekactie duurde %2 seconden.", "filter-by": "Filter op", "online-only": "Online ", - "picture-only": "Alleen een afbeelding" + "picture-only": "Alleen een afbeelding", + "invite": "Uitnodigen", + "invitation-email-sent": "Een uitnodiging email is verstuurd naar %1 ", + "user_list": "Ledenlijst", + "recent_topics": "Recente onderwerpen", + "popular_topics": "Populaire onderwerpen", + "unread_topics": "Ongelezen onderwerpen", + "categories": "Categorieën", + "tags": "Tags", + "map": "Map" } \ No newline at end of file diff --git a/public/language/pl/email.json b/public/language/pl/email.json index da76caf73c..9814604709 100644 --- a/public/language/pl/email.json +++ b/public/language/pl/email.json @@ -1,11 +1,15 @@ { "password-reset-requested": "Wybrano przywrócenie hasła - %1", "welcome-to": "Witaj w %1", + "invite": "Invitation from %1", "greeting_no_name": "Witaj", "greeting_with_name": "Witaj %1", "welcome.text1": "Dziękujemy za rejestrację w %1", "welcome.text2": "Aby aktywować swoje konto, musisz potwierdzić, że skorzystałeś z własnego adresu e-mail.", + "welcome.text3": "An administrator has accepted your registration application. You can login with your username/password now.", "welcome.cta": "Kliknij tutaj, by potwierdzić swój adres", + "invitation.text1": "%1 has invited you to join %2", + "invitation.ctr": "Click here to create your account.", "reset.text1": "Otrzymaliśmy żądanie przywrócenia Twojego hasła. Jeśli nie żądałeś przywrócenia hasła, zignoruj ten e-mail.", "reset.text2": "Aby przywrócić swoje hasło, skorzystaj z poniższego linku:", "reset.cta": "Kliknij tu, by przywrócić swoje hasło", diff --git a/public/language/pl/error.json b/public/language/pl/error.json index 80678e8121..8d3eb4ac75 100644 --- a/public/language/pl/error.json +++ b/public/language/pl/error.json @@ -51,6 +51,7 @@ "already-favourited": "Już polubiłeś ten post", "already-unfavourited": "Już przestałeś lubić ten post", "cant-ban-other-admins": "Nie możesz zbanować innych adminów!", + "cant-remove-last-admin": "You are the only administrator. Add another user as an administrator before removing yourself as admin", "invalid-image-type": "Błędny typ pliku. Dozwolone typy to: %1", "invalid-image-extension": "Błędne rozszerzenie pliku", "invalid-file-type": "Błędny typ pliku. Dozwolone typy to: %1", @@ -59,6 +60,8 @@ "group-name-change-not-allowed": "Nie można zmieniać nazwy tej grupy.", "group-already-member": "Już należysz do tej grupy", "group-needs-owner": "Ta grupa musi mieć przynajmniej jednego właściciela", + "group-already-invited": "This user has already been invited", + "group-already-requested": "Your membership request has already been submitted", "post-already-deleted": "Ten post został już skasowany", "post-already-restored": "Ten post został już przywrócony", "topic-already-deleted": "Ten temat został już skasowany", @@ -76,6 +79,7 @@ "downvoting-disabled": "Ocena postów jest wyłączona", "not-enough-reputation-to-downvote": "Masz za mało reputacji by ocenić ten post.", "not-enough-reputation-to-flag": "Nie masz dość reputacji, by flagować ten post", + "already-flagged": "You have already flagged this post", "reload-failed": "NodeBB napotkał problem w czasie ładowania \"%1\". Forum będzie nadal dostarczać zasoby dostępne w kliencie, jednak powinieneś cofnąć ostatnią akcję.", "registration-error": "Błąd rejestracji", "parse-error": "Coś poszło nie tak podczas parsingu odpowiedzi serwera", diff --git a/public/language/pl/global.json b/public/language/pl/global.json index a12b45bcdf..5b6249229e 100644 --- a/public/language/pl/global.json +++ b/public/language/pl/global.json @@ -50,6 +50,7 @@ "views": "wyświetleń", "reputation": "Punkty reputacji", "read_more": "czytaj więcej", + "more": "More", "posted_ago_by_guest": "wysłany %1 przez Gościa", "posted_ago_by": "wysłany %1 przez %2", "posted_ago": "wysłany %1", diff --git a/public/language/pl/groups.json b/public/language/pl/groups.json index 89838d1bc4..d20f679fd7 100644 --- a/public/language/pl/groups.json +++ b/public/language/pl/groups.json @@ -6,6 +6,12 @@ "no_groups_found": "Brak grup do wyświetlenia", "pending.accept": "Przyjmij", "pending.reject": "Odrzuć", + "pending.accept_all": "Accept All", + "pending.reject_all": "Reject All", + "pending.none": "There are no pending members at this time", + "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", "cover-instructions": "Przeciągnij i upuść zdjęcie, ustaw w odpowiedniej pozycji i kliknij Zapisz", "cover-change": "Zmień", "cover-save": "Zapisz", @@ -13,6 +19,7 @@ "details.title": "Szczegóły grupy", "details.members": "Lista członków", "details.pending": "Członkowie oczekujący", + "details.invited": "Invited Members", "details.has_no_posts": "Członkowie tej grupy nie napisali żadnych postów.", "details.latest_posts": "Ostatnie posty", "details.private": "Prywatna", diff --git a/public/language/pl/notifications.json b/public/language/pl/notifications.json index 2a24b7c122..789a871d02 100644 --- a/public/language/pl/notifications.json +++ b/public/language/pl/notifications.json @@ -20,6 +20,7 @@ "user_posted_topic": "%1 wysłał nowy temat: %2", "user_mentioned_you_in": "%1 wspomniał cię w %2", "user_started_following_you": "%1
zaczął Cię śledzić.", + "new_register": "%1 sent a registration request.", "email-confirmed": "E-mail potwierdzony", "email-confirmed-message": "Dziękujemy za potwierdzenie maila. Twoje konto zostało aktywowane.", "email-confirm-error-message": "Wystąpił problem przy aktywacji, - kod jest błędny lub przestarzały", diff --git a/public/language/pl/register.json b/public/language/pl/register.json index 53814180c4..0a25b6be19 100644 --- a/public/language/pl/register.json +++ b/public/language/pl/register.json @@ -14,5 +14,6 @@ "register_now_button": "Zarejestruj się", "alternative_registration": "Alternatywna rejestracja", "terms_of_use": "Warunki korzystania z serwisu", - "agree_to_terms_of_use": "Zgadzam się na powyższe warunki" + "agree_to_terms_of_use": "Zgadzam się na powyższe warunki", + "registration-added-to-queue": "Your registration has been added to the approval queue. You will receive an email when it is accepted by an administrator." } \ No newline at end of file diff --git a/public/language/pl/unread.json b/public/language/pl/unread.json index d648c84e98..49c583d8a8 100644 --- a/public/language/pl/unread.json +++ b/public/language/pl/unread.json @@ -5,5 +5,6 @@ "mark_as_read": "Oznacz jako przeczytane", "selected": "Wybrane", "all": "Wszystkie", + "all_categories": "All categories", "topics_marked_as_read.success": "Wątki zostały oznaczone jako przeczytane!" } \ No newline at end of file diff --git a/public/language/pl/user.json b/public/language/pl/user.json index 30593edb85..af5cebafa6 100644 --- a/public/language/pl/user.json +++ b/public/language/pl/user.json @@ -6,8 +6,12 @@ "postcount": "Liczba postów", "email": "Adres e-mail", "confirm_email": "Potwierdź e-mail", + "ban_account": "Ban Account", + "ban_account_confirm": "Do you really want to ban this user?", + "unban_account": "Unban Account", "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

", "fullname": "Pełna nazwa", "website": "Strona WWW", "location": "Położenie", @@ -64,9 +68,9 @@ "settings-require-reload": "Some setting changes require a reload. Click here to reload the page.", "has_no_follower": "Ten użytkownik nie ma jeszcze żadnych obserwujących", "follows_no_one": "Użytkownik jeszcze nikogo nie obsweruje.", - "has_no_posts": "Użytkownik nie napisał jeszcze żadnych postów.", - "has_no_topics": "Ten użytkownik nie napisał jeszcze żadnego wątku.", - "has_no_watched_topics": "Ten użytkownik nie obserwował jeszcze żadnego wątku.", + "has_no_posts": "This user hasn't posted anything yet.", + "has_no_topics": "This user hasn't posted any topics yet.", + "has_no_watched_topics": "This user hasn't watched any topics yet.", "email_hidden": "Adres e-mail ukryty", "hidden": "ukryty", "paginate_description": "Paginate topics and posts instead of using infinite scroll", diff --git a/public/language/pl/users.json b/public/language/pl/users.json index b41b99309a..4644860663 100644 --- a/public/language/pl/users.json +++ b/public/language/pl/users.json @@ -8,5 +8,14 @@ "users-found-search-took": "Znaleziono %1 użytkownik(ów). Szukanie zajęło %2 sek.", "filter-by": "Filtruj", "online-only": "Tylko dostępny", - "picture-only": "Tylko ze zdjęciem" + "picture-only": "Tylko ze zdjęciem", + "invite": "Invite", + "invitation-email-sent": "An invitation email has been sent to %1", + "user_list": "User List", + "recent_topics": "Recent Topics", + "popular_topics": "Popular Topics", + "unread_topics": "Unread Topics", + "categories": "Categories", + "tags": "Tags", + "map": "Map" } \ No newline at end of file diff --git a/public/language/pt_BR/email.json b/public/language/pt_BR/email.json index cca995c0a5..76211513c9 100644 --- a/public/language/pt_BR/email.json +++ b/public/language/pt_BR/email.json @@ -1,11 +1,15 @@ { "password-reset-requested": "Reconfiguração de Senha Solicitada - %1!", "welcome-to": "Bem vindo a %1", + "invite": "Convite de %1", "greeting_no_name": "Olá", "greeting_with_name": "Olà %1", "welcome.text1": "Obrigado por se registrar com %1!", "welcome.text2": "Para terminar de ativar sua conta, nós precisamos verificar que você é o dono do endereço de email com o que você se registrou.", + "welcome.text3": "Um administrador aceitou teu pedido de registro. Você pode se logar agora com teu nome de usuário/senha.", "welcome.cta": "Clique aqui para confirmar seu endereço de email", + "invitation.text1": "%1 convidou você para participar de %2", + "invitation.ctr": "Clique aqui para criar tua conta.", "reset.text1": "Nós recebemos um pedido para reconfigurar sua senha, possivelmente porque você a esqueceu. Se este não é o caso, por favor ignore este email.", "reset.text2": "Para continuar com a reconfiguração de senha, por favor clique no seguinte link:", "reset.cta": "Clique aqui para reconfigurar sua senha", diff --git a/public/language/pt_BR/error.json b/public/language/pt_BR/error.json index 2e4890c900..7ef6b21504 100644 --- a/public/language/pt_BR/error.json +++ b/public/language/pt_BR/error.json @@ -51,6 +51,7 @@ "already-favourited": "Você já adicionou este post aos favoritos", "already-unfavourited": "Você já removeu este post dos favoritos", "cant-ban-other-admins": "Você não pode banir outros administradores!", + "cant-remove-last-admin": "Você é o único administrador. Adicione outro usuário como administrador antes de remover a si mesmo como admin", "invalid-image-type": "Tipo inválido de imagem. Os tipos permitidos são: %1", "invalid-image-extension": "Extensão de imagem inválida", "invalid-file-type": "Tipo de arquivo inválido. Os tipos permitidos são: %1", @@ -59,6 +60,8 @@ "group-name-change-not-allowed": "Sem permissão para alterar nome do grupo", "group-already-member": "Você já faz parte deste grupo", "group-needs-owner": "Este grupo requer ao menos um dono", + "group-already-invited": "Esse usuário já foi convidado", + "group-already-requested": "Seu pedido de filiação já foi enviado", "post-already-deleted": "Este post já foi deletado", "post-already-restored": "Este post já foi restaurado", "topic-already-deleted": "Esté tópico já foi deletado", @@ -76,6 +79,7 @@ "downvoting-disabled": "Negativação está desabilitada", "not-enough-reputation-to-downvote": "Você não possui reputação suficiente para negativar este post", "not-enough-reputation-to-flag": "Você não possui reputação suficiente para sinalizar este post", + "already-flagged": "Você já sinalizou esse post", "reload-failed": "O NodeBB encontrou um problema ao recarregar: \"%1\". O NodeBB continuará a servir os assets existentes no lado do cliente, apesar de que você deve desfazer o que você fez antes de recarregar.", "registration-error": "Erro de Cadastro", "parse-error": "Algo deu errado ao conseguir resposta do servidor", diff --git a/public/language/pt_BR/global.json b/public/language/pt_BR/global.json index d9bb14a846..01554381df 100644 --- a/public/language/pt_BR/global.json +++ b/public/language/pt_BR/global.json @@ -50,6 +50,7 @@ "views": "Visualizações", "reputation": "Reputação", "read_more": "ler mais", + "more": "Mais", "posted_ago_by_guest": "postado %1 por Visitante", "posted_ago_by": "postado %1 por %2", "posted_ago": "postou %1", diff --git a/public/language/pt_BR/groups.json b/public/language/pt_BR/groups.json index d7e2aa0d32..1dde957248 100644 --- a/public/language/pt_BR/groups.json +++ b/public/language/pt_BR/groups.json @@ -6,6 +6,12 @@ "no_groups_found": "Não há grupos para ver", "pending.accept": "Aceitar", "pending.reject": "Rejeitar", + "pending.accept_all": "Aceitar Todos", + "pending.reject_all": "Rejeitar Todos", + "pending.none": "Não há membros pendentes no momento", + "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", "cover-instructions": "Arraste uma foto, arraste para a posição correta e clique em Salvar", "cover-change": "Alterar", "cover-save": "Salvar", @@ -13,6 +19,7 @@ "details.title": "Detalhes do Grupo", "details.members": "Lista de Membros", "details.pending": "Membros Pendentes", + "details.invited": "Membros Convidados", "details.has_no_posts": "Os membros deste grupo não fizeram quaisquer posts.", "details.latest_posts": "Últimos Posts", "details.private": "Particular", diff --git a/public/language/pt_BR/notifications.json b/public/language/pt_BR/notifications.json index cb3f217b47..15c756b7dd 100644 --- a/public/language/pt_BR/notifications.json +++ b/public/language/pt_BR/notifications.json @@ -20,6 +20,7 @@ "user_posted_topic": "%1 postou um novo tópico: %2", "user_mentioned_you_in": "%1 mencionou você em %2", "user_started_following_you": "%1 começou a seguir você.", + "new_register": "%1 lhe enviou um pedido de cadastro.", "email-confirmed": "Email Confirmado", "email-confirmed-message": "Obrigado por validar o seu email. Agora sua conta está plenamente ativada.", "email-confirm-error-message": "Houve um problema ao validar o seu endereço de email. Talvez o código era invalido ou tenha expirado.", diff --git a/public/language/pt_BR/register.json b/public/language/pt_BR/register.json index 1f3db7e7db..f3e9ef7859 100644 --- a/public/language/pt_BR/register.json +++ b/public/language/pt_BR/register.json @@ -14,5 +14,6 @@ "register_now_button": "Registrar Agora", "alternative_registration": "Cadastro Alternativo", "terms_of_use": "Termos de Uso", - "agree_to_terms_of_use": "Eu concordo com os Termos de Uso" + "agree_to_terms_of_use": "Eu concordo com os Termos de Uso", + "registration-added-to-queue": "Teu cadastro foi adicionado à fila de aprovação. Você receberá um email quando ele for aceito por um administrador." } \ No newline at end of file diff --git a/public/language/pt_BR/unread.json b/public/language/pt_BR/unread.json index 1bb519b0cb..bba5086d93 100644 --- a/public/language/pt_BR/unread.json +++ b/public/language/pt_BR/unread.json @@ -5,5 +5,6 @@ "mark_as_read": "Marcar como Lido", "selected": "Selecionado", "all": "Todos", + "all_categories": "Todas as categorias", "topics_marked_as_read.success": "Tópicos marcados como lidos!" } \ No newline at end of file diff --git a/public/language/pt_BR/user.json b/public/language/pt_BR/user.json index 01b3b1d9a9..5c5cdef3e4 100644 --- a/public/language/pt_BR/user.json +++ b/public/language/pt_BR/user.json @@ -6,8 +6,12 @@ "postcount": "Número de Posts", "email": "Email", "confirm_email": "Confirmar Email", + "ban_account": "Banir Conta", + "ban_account_confirm": "Você realmente quer banir esse usuario?", + "unban_account": "Desbanir Conta", "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

", "fullname": "Nome Completo", "website": "Website", "location": "Local", @@ -64,9 +68,9 @@ "settings-require-reload": "Algumas mudanças de configuração exigem atualizar o navegador. Clique aqui para atualizar a página.", "has_no_follower": "Este usuário não possui seguidores :(", "follows_no_one": "Este usuário não está seguindo ninguém :(", - "has_no_posts": "Este usuário não postou nada ainda.", - "has_no_topics": "Este usuário ainda não criou nenhum tópico.", - "has_no_watched_topics": "Este usuário ainda não acompanhou quaisquer tópicos.", + "has_no_posts": "Esse usuário ainda não postou nada.", + "has_no_topics": "Esse usuário não postou quaisquer tópicos ainda.", + "has_no_watched_topics": "Esse usuário ainda não seguiu quaisquer tópicos.", "email_hidden": "Email Escondido", "hidden": "escondido", "paginate_description": "Paginar tópicos ao invés de utilizar em vez de usar rolagem infinita.", diff --git a/public/language/pt_BR/users.json b/public/language/pt_BR/users.json index cc49b0fe57..2f0f52cd51 100644 --- a/public/language/pt_BR/users.json +++ b/public/language/pt_BR/users.json @@ -8,5 +8,14 @@ "users-found-search-took": "%1 usuário(s) encontrado(s)! A busca levou %2 segundos.", "filter-by": "Filtrar Por", "online-only": "Apenas Online", - "picture-only": "Apenas foto" + "picture-only": "Apenas foto", + "invite": "Convidar", + "invitation-email-sent": "Um email de convite foi enviado para %1", + "user_list": "Lista de Usuários", + "recent_topics": "Tópicos Recentes", + "popular_topics": "Tópicos Populares", + "unread_topics": "Topicos Não-Lidos", + "categories": "Categorias", + "tags": "Tags", + "map": "Mapa" } \ No newline at end of file diff --git a/public/language/ro/email.json b/public/language/ro/email.json index 25f9723466..187de72f55 100644 --- a/public/language/ro/email.json +++ b/public/language/ro/email.json @@ -1,11 +1,15 @@ { "password-reset-requested": "Cererea de resetare a parolei a fost efectuată - %1!", "welcome-to": "Salutare lui %1", + "invite": "Invitation from %1", "greeting_no_name": "Salut", "greeting_with_name": "Salut %1", "welcome.text1": "îți mulțumim că te-ai Înregistrat cu %1!", "welcome.text2": "Pentru a-ți activa cu success contul trebuie să verificăm adresa de email pe care ai folosit-o la înregistrare.", + "welcome.text3": "An administrator has accepted your registration application. You can login with your username/password now.", "welcome.cta": "Apasă aici pentru a confirma adresa ta de email", + "invitation.text1": "%1 has invited you to join %2", + "invitation.ctr": "Click here to create your account.", "reset.text1": "We received a request to reset your password, possibly because you have forgotten it. If this is not the case, please ignore this email.", "reset.text2": "Pentru a continua cu resetarea parolei, te rugăm sa apeși pe următorul link:", "reset.cta": "Apasă aici pentru a-ți reseta parola", diff --git a/public/language/ro/error.json b/public/language/ro/error.json index c7595f1509..94e87686d6 100644 --- a/public/language/ro/error.json +++ b/public/language/ro/error.json @@ -51,6 +51,7 @@ "already-favourited": "You have already favourited this post", "already-unfavourited": "You have already unfavourited this post", "cant-ban-other-admins": "Nu poți bana alți administratori!", + "cant-remove-last-admin": "You are the only administrator. Add another user as an administrator before removing yourself as admin", "invalid-image-type": "Invalid image type. Allowed types are: %1", "invalid-image-extension": "Invalid image extension", "invalid-file-type": "Invalid file type. Allowed types are: %1", @@ -59,6 +60,8 @@ "group-name-change-not-allowed": "Schimbarea numelui grupului este interzisă", "group-already-member": "You are already part of this group", "group-needs-owner": "This group requires at least one owner", + "group-already-invited": "This user has already been invited", + "group-already-requested": "Your membership request has already been submitted", "post-already-deleted": "This post has already been deleted", "post-already-restored": "This post has already been restored", "topic-already-deleted": "This topic has already been deleted", @@ -76,6 +79,7 @@ "downvoting-disabled": "Votarea negativă este dezactivată", "not-enough-reputation-to-downvote": "Nu ai destulă reputație pentru a vota negativ acest post.", "not-enough-reputation-to-flag": "You do not have enough reputation to flag this post", + "already-flagged": "You have already flagged this post", "reload-failed": "NodeBB a întâmpinat o problemă la reîncarcare: \"%1\". NodeBB va continua să servească fișierele existente pentru partea-client, dar tu va trebuie să refaci modificările pe care le-ai facut înainte de reîncarcare.", "registration-error": "Registration Error", "parse-error": "Something went wrong while parsing server response", diff --git a/public/language/ro/global.json b/public/language/ro/global.json index 45939015de..2085d160ab 100644 --- a/public/language/ro/global.json +++ b/public/language/ro/global.json @@ -50,6 +50,7 @@ "views": "Vizualizări", "reputation": "Reputație", "read_more": "citește mai mult", + "more": "More", "posted_ago_by_guest": "postat %1 de Vizitator", "posted_ago_by": "postat %1 de %2", "posted_ago": "postat %1", diff --git a/public/language/ro/groups.json b/public/language/ro/groups.json index 2e45c9368c..61871c5fa0 100644 --- a/public/language/ro/groups.json +++ b/public/language/ro/groups.json @@ -6,6 +6,12 @@ "no_groups_found": "There are no groups to see", "pending.accept": "Accept", "pending.reject": "Reject", + "pending.accept_all": "Accept All", + "pending.reject_all": "Reject All", + "pending.none": "There are no pending members at this time", + "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", "cover-instructions": "Drag and Drop a photo, drag to position, and hit Save", "cover-change": "Change", "cover-save": "Save", @@ -13,6 +19,7 @@ "details.title": "Detalii Grup", "details.members": "Listă Membrii", "details.pending": "Pending Members", + "details.invited": "Invited Members", "details.has_no_posts": "Membrii acestui grup nu au facut nici o postare.", "details.latest_posts": "Ultimele Mesaje", "details.private": "Private", diff --git a/public/language/ro/notifications.json b/public/language/ro/notifications.json index 46bdd5fa7a..d98258e2c0 100644 --- a/public/language/ro/notifications.json +++ b/public/language/ro/notifications.json @@ -20,6 +20,7 @@ "user_posted_topic": "%1 has posted a new topic: %2", "user_mentioned_you_in": "%1 te-a menționat în %2", "user_started_following_you": "%1 a început să te urmărească.", + "new_register": "%1 sent a registration request.", "email-confirmed": "Email confirmat", "email-confirmed-message": "Îți mulțumim pentru validarea emailului. Contul tău este acuma activat.", "email-confirm-error-message": "A fost o problemă cu activarea adresei tale de email. Poate codul de activare a fost invalid sau expirat.", diff --git a/public/language/ro/register.json b/public/language/ro/register.json index a6415d70a5..99b423716d 100644 --- a/public/language/ro/register.json +++ b/public/language/ro/register.json @@ -14,5 +14,6 @@ "register_now_button": "Înregistrează-te", "alternative_registration": "Înregistrare Alternativă", "terms_of_use": "Termeni de utilizare", - "agree_to_terms_of_use": "Sunt de acord cu termenii de utilizare" + "agree_to_terms_of_use": "Sunt de acord cu termenii de utilizare", + "registration-added-to-queue": "Your registration has been added to the approval queue. You will receive an email when it is accepted by an administrator." } \ No newline at end of file diff --git a/public/language/ro/unread.json b/public/language/ro/unread.json index 9c7974269d..5cff8c6546 100644 --- a/public/language/ro/unread.json +++ b/public/language/ro/unread.json @@ -5,5 +5,6 @@ "mark_as_read": "Marchează ca citit", "selected": "Selectate", "all": "Toate", + "all_categories": "All categories", "topics_marked_as_read.success": "Subiectele au fost marcate ca citite!" } \ No newline at end of file diff --git a/public/language/ro/user.json b/public/language/ro/user.json index 90724690b1..e923453024 100644 --- a/public/language/ro/user.json +++ b/public/language/ro/user.json @@ -6,8 +6,12 @@ "postcount": "Post Count", "email": "Adresă Email", "confirm_email": "Confirmă Email", + "ban_account": "Ban Account", + "ban_account_confirm": "Do you really want to ban this user?", + "unban_account": "Unban Account", "delete_account": "Ș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

", "fullname": "Nume Întreg", "website": "Pagină Web", "location": "Locație", @@ -64,9 +68,9 @@ "settings-require-reload": "Some setting changes require a reload. Click here to reload the page.", "has_no_follower": "Pe acest utilizator nu îl urmărește nimeni :(", "follows_no_one": "Acest utilizator nu urmărește pe nimeni :(", - "has_no_posts": "Acest utilizator nu a postat nici un mesaj până acuma.", - "has_no_topics": "Acest utilizator nu a postat nici un subiect.", - "has_no_watched_topics": "This user didn't watch any topics yet.", + "has_no_posts": "This user hasn't posted anything yet.", + "has_no_topics": "This user hasn't posted any topics yet.", + "has_no_watched_topics": "This user hasn't watched any topics yet.", "email_hidden": "Adresă de email ascunsă", "hidden": "ascuns", "paginate_description": "Paginate topics and posts instead of using infinite scroll", diff --git a/public/language/ro/users.json b/public/language/ro/users.json index 4ef70026a8..238636e567 100644 --- a/public/language/ro/users.json +++ b/public/language/ro/users.json @@ -8,5 +8,14 @@ "users-found-search-took": "%1 user(s) found! Search took %2 seconds.", "filter-by": "Filter By", "online-only": "Online only", - "picture-only": "Picture only" + "picture-only": "Picture only", + "invite": "Invite", + "invitation-email-sent": "An invitation email has been sent to %1", + "user_list": "User List", + "recent_topics": "Recent Topics", + "popular_topics": "Popular Topics", + "unread_topics": "Unread Topics", + "categories": "Categories", + "tags": "Tags", + "map": "Map" } \ No newline at end of file diff --git a/public/language/ru/email.json b/public/language/ru/email.json index 4353d9101e..81e8077779 100644 --- a/public/language/ru/email.json +++ b/public/language/ru/email.json @@ -1,11 +1,15 @@ { "password-reset-requested": "Пароль сброшен - %1!", "welcome-to": "Добро пожаловать на %1", + "invite": "Invitation from %1", "greeting_no_name": "Здравствуйте!", "greeting_with_name": "Здравствуйте, %1!", "welcome.text1": "Благодарим за регистрацию %1! ", "welcome.text2": "Для активации Вашей учетной записи мы должны убедиться, что Вы указали верный email адрес.", + "welcome.text3": "An administrator has accepted your registration application. You can login with your username/password now.", "welcome.cta": "Перейдите по ссылке для подтверждения Вашего email", + "invitation.text1": "%1 has invited you to join %2", + "invitation.ctr": "Click here to create your account.", "reset.text1": "Мы получили запрос на сброс Вашего пароля. Если Вы не подавали запрос, пожалуйста, проигнорируйте это сообщение.", "reset.text2": "Для продолжения процедуры изменения пароля, пожалуйста, перейдите по ссылке:", "reset.cta": "Кликните здесь для изменения пароля", diff --git a/public/language/ru/error.json b/public/language/ru/error.json index b4668f3588..16f729678e 100644 --- a/public/language/ru/error.json +++ b/public/language/ru/error.json @@ -2,7 +2,7 @@ "invalid-data": "Неверные данные", "not-logged-in": "Вы не вошли в свой аккаунт.", "account-locked": "Ваш аккаунт временно заблокирован", - "search-requires-login": "Searching requires an account - please login or register.", + "search-requires-login": "Поиск требует аккаунта - пожалуйста, войдите или зарегистрируйтесь.", "invalid-cid": "Неверный ID категории", "invalid-tid": "Неверный ID темы", "invalid-pid": "Неверный ID поста", @@ -51,6 +51,7 @@ "already-favourited": "Вы уже добавили этот пост в избранное", "already-unfavourited": "Вы уже удалили этот пост из избранного", "cant-ban-other-admins": "Вы не можете забанить других администраторов!", + "cant-remove-last-admin": "You are the only administrator. Add another user as an administrator before removing yourself as admin", "invalid-image-type": "Неверный формат изображения. Поддерживаемые форматы: %1", "invalid-image-extension": "Недопустимое расширение файла", "invalid-file-type": "Неверный формат файла. Поддерживаемые форматы: %1", @@ -59,6 +60,8 @@ "group-name-change-not-allowed": "Изменение названия группы запрещено", "group-already-member": "Вы уже состоите в этой группе", "group-needs-owner": "У группы должен быть как минимум один владелец", + "group-already-invited": "This user has already been invited", + "group-already-requested": "Your membership request has already been submitted", "post-already-deleted": "Этот пост уже удалён", "post-already-restored": "Этот пост уже восстановлен", "topic-already-deleted": "Тема уже удалена", @@ -67,8 +70,8 @@ "topic-thumbnails-are-disabled": "Иконки для темы запрещены", "invalid-file": "Неверный файл", "uploads-are-disabled": "Загрузка запрещена", - "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", - "about-me-too-long": "Sorry, your about me cannot be longer than %1 character(s).", + "signature-too-long": "К сожалению Ваша подпись не может быть длиннее %1 символа(ов).", + "about-me-too-long": "К сожалению поле \"О себе\" не может быть длиннее чем %1 символа(ов).", "cant-chat-with-yourself": "Вы не можете общаться с самим собой", "chat-restricted": "Пользователь ограничил прием сообщений. Он должен быть подписан на Вас, чтобы Вы могли вести переписку с ним.", "too-many-messages": "Вы отправили слишком много сообщений, подождите немного.", @@ -76,6 +79,7 @@ "downvoting-disabled": "Понижение оценки отключено", "not-enough-reputation-to-downvote": "У Вас недостаточно репутации для понижения оценки поста", "not-enough-reputation-to-flag": "У Вас недостаточно репутации, чтобы пометить этот пост.", + "already-flagged": "You have already flagged this post", "reload-failed": "NodeBB обнаружил проблему при перезагрузке: \"%1\". NodeBB продолжит работать с существующими ресурсами клиента, но Вы должны отменить то, что сделали перед перезагрузкой.", "registration-error": "Ошибка при регистрации", "parse-error": "Похоже, что-то пошло не так в процессе обработки ответа сервера.", diff --git a/public/language/ru/global.json b/public/language/ru/global.json index 700ed17127..db2015144b 100644 --- a/public/language/ru/global.json +++ b/public/language/ru/global.json @@ -50,6 +50,7 @@ "views": "Просмотры", "reputation": "Репутация", "read_more": "читать дальше", + "more": "More", "posted_ago_by_guest": "создано %1 Гостем", "posted_ago_by": "сообщений %1 от %2", "posted_ago": "написал %1", diff --git a/public/language/ru/groups.json b/public/language/ru/groups.json index b50974212e..557e8eff81 100644 --- a/public/language/ru/groups.json +++ b/public/language/ru/groups.json @@ -6,6 +6,12 @@ "no_groups_found": "Нет групп для отображения", "pending.accept": "Принять", "pending.reject": "Отклонить", + "pending.accept_all": "Accept All", + "pending.reject_all": "Reject All", + "pending.none": "There are no pending members at this time", + "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", "cover-instructions": "Перетяните сюда изображение, переместите на нужную позицию и нажмите Сохранить", "cover-change": "Изменить", "cover-save": "Сохранить", @@ -13,6 +19,7 @@ "details.title": "Информация о группе", "details.members": "Список пользователей", "details.pending": "Заявки в группу", + "details.invited": "Invited Members", "details.has_no_posts": "Пользователями этой группы не публиковали никаких записей", "details.latest_posts": "Последние записи", "details.private": "Приватная", diff --git a/public/language/ru/modules.json b/public/language/ru/modules.json index 770b9446f5..ec246f2652 100644 --- a/public/language/ru/modules.json +++ b/public/language/ru/modules.json @@ -22,5 +22,5 @@ "composer.user_said": "%1 сказал:", "composer.discard": "Вы уверены, что хотите отказаться от этого поста?", "composer.submit_and_lock": "Отправить и закрыть", - "composer.toggle_dropdown": "Toggle Dropdown" + "composer.toggle_dropdown": "Показать выпадающий список" } \ No newline at end of file diff --git a/public/language/ru/notifications.json b/public/language/ru/notifications.json index c3d1fa2bfc..7f0e5b9bfa 100644 --- a/public/language/ru/notifications.json +++ b/public/language/ru/notifications.json @@ -20,6 +20,7 @@ "user_posted_topic": "%1 открыл новую тему: %2", "user_mentioned_you_in": "%1 упомянул Вас в %2", "user_started_following_you": "%1 подписался на Вас.", + "new_register": "%1 sent a registration request.", "email-confirmed": "Email подтвержден", "email-confirmed-message": "Спасибо за подтверждение Вашего Email-адреса. Ваш аккаунт активирован.", "email-confirm-error-message": "Ошибка проверки Email-адреса. Возможно, код неверен, либо у него истек срок действия.", diff --git a/public/language/ru/register.json b/public/language/ru/register.json index eae56a5403..3fdd181066 100644 --- a/public/language/ru/register.json +++ b/public/language/ru/register.json @@ -14,5 +14,6 @@ "register_now_button": "Зарегистрироваться", "alternative_registration": "Альтернативная регистрация", "terms_of_use": "Условия использования", - "agree_to_terms_of_use": "Я согласен с условиями" + "agree_to_terms_of_use": "Я согласен с условиями", + "registration-added-to-queue": "Your registration has been added to the approval queue. You will receive an email when it is accepted by an administrator." } \ No newline at end of file diff --git a/public/language/ru/topic.json b/public/language/ru/topic.json index 1a449965b2..a2a50aced2 100644 --- a/public/language/ru/topic.json +++ b/public/language/ru/topic.json @@ -5,7 +5,7 @@ "no_topics_found": "Тем не найдено!", "no_posts_found": "Посты не найдены!", "post_is_deleted": "Этот пост удален!", - "topic_is_deleted": "This topic is deleted!", + "topic_is_deleted": "Эта тема удалена!", "profile": "Профиль", "posted_by": "Создано %1", "posted_by_guest": "Опубликовано гостем", diff --git a/public/language/ru/unread.json b/public/language/ru/unread.json index 0bdf6c4933..1063677089 100644 --- a/public/language/ru/unread.json +++ b/public/language/ru/unread.json @@ -5,5 +5,6 @@ "mark_as_read": "Пометить прочитанным", "selected": "Выбраны", "all": "Все", + "all_categories": "All categories", "topics_marked_as_read.success": "Темы помечены как прочитанные!" } \ No newline at end of file diff --git a/public/language/ru/user.json b/public/language/ru/user.json index be8dea688f..072ec73fa4 100644 --- a/public/language/ru/user.json +++ b/public/language/ru/user.json @@ -6,8 +6,12 @@ "postcount": "Сообщений", "email": "Email", "confirm_email": "Подтвердить Email", + "ban_account": "Ban Account", + "ban_account_confirm": "Do you really want to ban this user?", + "unban_account": "Unban Account", "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

", "fullname": "Полное имя", "website": "Сайт", "location": "Откуда", @@ -21,7 +25,7 @@ "watched": "Просмотров", "followers": "Читателей", "following": "Читаемых", - "aboutme": "About me", + "aboutme": "Обо мне", "signature": "Подпись", "gravatar": "Gravatar", "birthday": "День рождения", @@ -64,9 +68,9 @@ "settings-require-reload": "Для отображения некоторых изменений необходимо обновить страницу. Нажмите здесь чтобы продолжить.", "has_no_follower": "Этого пользователя никто не читает :(", "follows_no_one": "Этот пользователь никого не читает :(", - "has_no_posts": "Этот пользователь еще ничего не написал.", - "has_no_topics": "Этот пользователь еще не создал ни одной темы", - "has_no_watched_topics": "Этот пользователь еще не просмотрел ни одной темы", + "has_no_posts": "This user hasn't posted anything yet.", + "has_no_topics": "This user hasn't posted any topics yet.", + "has_no_watched_topics": "This user hasn't watched any topics yet.", "email_hidden": "Email скрыт", "hidden": "скрыто", "paginate_description": "Разбивать по страницам топики и посты вместо того чтобы выводить бесконечным списком", diff --git a/public/language/ru/users.json b/public/language/ru/users.json index ffc028bcf3..7cc44c39fc 100644 --- a/public/language/ru/users.json +++ b/public/language/ru/users.json @@ -8,5 +8,14 @@ "users-found-search-took": "Найдено %1 пользователя(ей). Поиск занял %2 секунд. ", "filter-by": "Сортировать по", "online-only": "Только онлайн", - "picture-only": "Только с аватаром" + "picture-only": "Только с аватаром", + "invite": "Invite", + "invitation-email-sent": "An invitation email has been sent to %1", + "user_list": "User List", + "recent_topics": "Recent Topics", + "popular_topics": "Popular Topics", + "unread_topics": "Unread Topics", + "categories": "Categories", + "tags": "Tags", + "map": "Map" } \ No newline at end of file diff --git a/public/language/sc/email.json b/public/language/sc/email.json index f290435e75..d8114d0231 100644 --- a/public/language/sc/email.json +++ b/public/language/sc/email.json @@ -1,11 +1,15 @@ { "password-reset-requested": "Password Reset Requested - %1!", "welcome-to": "Welcome to %1", + "invite": "Invitation from %1", "greeting_no_name": "Hello", "greeting_with_name": "Hello %1", "welcome.text1": "Thank you for registering with %1!", "welcome.text2": "To fully activate your account, we need to verify that you own the email address you registered with.", + "welcome.text3": "An administrator has accepted your registration application. You can login with your username/password now.", "welcome.cta": "Click here to confirm your email address", + "invitation.text1": "%1 has invited you to join %2", + "invitation.ctr": "Click here to create your account.", "reset.text1": "We received a request to reset your password, possibly because you have forgotten it. If this is not the case, please ignore this email.", "reset.text2": "To continue with the password reset, please click on the following link:", "reset.cta": "Click here to reset your password", diff --git a/public/language/sc/error.json b/public/language/sc/error.json index ac2457e250..56f9f626ff 100644 --- a/public/language/sc/error.json +++ b/public/language/sc/error.json @@ -51,6 +51,7 @@ "already-favourited": "You have already favourited this post", "already-unfavourited": "You have already unfavourited this post", "cant-ban-other-admins": "You can't ban other admins!", + "cant-remove-last-admin": "You are the only administrator. Add another user as an administrator before removing yourself as admin", "invalid-image-type": "Invalid image type. Allowed types are: %1", "invalid-image-extension": "Invalid image extension", "invalid-file-type": "Invalid file type. Allowed types are: %1", @@ -59,6 +60,8 @@ "group-name-change-not-allowed": "Group name change not allowed", "group-already-member": "You are already part of this group", "group-needs-owner": "This group requires at least one owner", + "group-already-invited": "This user has already been invited", + "group-already-requested": "Your membership request has already been submitted", "post-already-deleted": "This post has already been deleted", "post-already-restored": "This post has already been restored", "topic-already-deleted": "This topic has already been deleted", @@ -76,6 +79,7 @@ "downvoting-disabled": "Downvoting is disabled", "not-enough-reputation-to-downvote": "You do not have enough reputation to downvote this post", "not-enough-reputation-to-flag": "You do not have enough reputation to flag this post", + "already-flagged": "You have already flagged this post", "reload-failed": "NodeBB encountered a problem while reloading: \"%1\". NodeBB will continue to serve the existing client-side assets, although you should undo what you did just prior to reloading.", "registration-error": "Registration Error", "parse-error": "Something went wrong while parsing server response", diff --git a/public/language/sc/global.json b/public/language/sc/global.json index 6fa43c2623..39432aa228 100644 --- a/public/language/sc/global.json +++ b/public/language/sc/global.json @@ -50,6 +50,7 @@ "views": "Bìsitas", "reputation": "Reputation", "read_more": "read more", + "more": "More", "posted_ago_by_guest": "posted %1 by Guest", "posted_ago_by": "posted %1 by %2", "posted_ago": "posted %1", diff --git a/public/language/sc/groups.json b/public/language/sc/groups.json index 5e301aa46a..c9ac036475 100644 --- a/public/language/sc/groups.json +++ b/public/language/sc/groups.json @@ -6,6 +6,12 @@ "no_groups_found": "There are no groups to see", "pending.accept": "Accept", "pending.reject": "Reject", + "pending.accept_all": "Accept All", + "pending.reject_all": "Reject All", + "pending.none": "There are no pending members at this time", + "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", "cover-instructions": "Drag and Drop a photo, drag to position, and hit Save", "cover-change": "Change", "cover-save": "Save", @@ -13,6 +19,7 @@ "details.title": "Group Details", "details.members": "Member List", "details.pending": "Pending Members", + "details.invited": "Invited Members", "details.has_no_posts": "This group's members have not made any posts.", "details.latest_posts": "Latest Posts", "details.private": "Private", diff --git a/public/language/sc/notifications.json b/public/language/sc/notifications.json index a3a210afe8..b65573fca1 100644 --- a/public/language/sc/notifications.json +++ b/public/language/sc/notifications.json @@ -20,6 +20,7 @@ "user_posted_topic": "%1 has posted a new topic: %2", "user_mentioned_you_in": "%1 mentioned you in %2", "user_started_following_you": "%1 started following you.", + "new_register": "%1 sent a registration request.", "email-confirmed": "Email Confirmed", "email-confirmed-message": "Thank you for validating your email. Your account is now fully activated.", "email-confirm-error-message": "There was a problem validating your email address. Perhaps the code was invalid or has expired.", diff --git a/public/language/sc/register.json b/public/language/sc/register.json index 9d363000e0..7b4bb97cd7 100644 --- a/public/language/sc/register.json +++ b/public/language/sc/register.json @@ -14,5 +14,6 @@ "register_now_button": "Registra·ti Immoe", "alternative_registration": "Registratziones Alternativas", "terms_of_use": "Tèrmines de Impreu", - "agree_to_terms_of_use": "So de acòrdiu cun is Tèrmines de Impreu" + "agree_to_terms_of_use": "So de acòrdiu cun is Tèrmines de Impreu", + "registration-added-to-queue": "Your registration has been added to the approval queue. You will receive an email when it is accepted by an administrator." } \ No newline at end of file diff --git a/public/language/sc/unread.json b/public/language/sc/unread.json index db7e78b8c9..692bb12f14 100644 --- a/public/language/sc/unread.json +++ b/public/language/sc/unread.json @@ -5,5 +5,6 @@ "mark_as_read": "Mark as Read", "selected": "Selected", "all": "All", + "all_categories": "All categories", "topics_marked_as_read.success": "Topics marked as read!" } \ No newline at end of file diff --git a/public/language/sc/user.json b/public/language/sc/user.json index 70097b829d..3137bbd954 100644 --- a/public/language/sc/user.json +++ b/public/language/sc/user.json @@ -6,8 +6,12 @@ "postcount": "Post Count", "email": "Email", "confirm_email": "Confirm Email", + "ban_account": "Ban Account", + "ban_account_confirm": "Do you really want to ban this user?", + "unban_account": "Unban Account", "delete_account": "Delete Account", "delete_account_confirm": "Are you sure you want to delete your account?
This action is irreversible and you will not be able to recover any of your data

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

", "fullname": "Nùmene e Sambenadu", "website": "Giassu web", "location": "Logu", @@ -64,9 +68,9 @@ "settings-require-reload": "Some setting changes require a reload. Click here to reload the page.", "has_no_follower": "Custu impitadore non tenet perunu sighidore :(", "follows_no_one": "Custu impitadore no est sighende nissunu :(", - "has_no_posts": "Custu impitadore no at ancora publicadu nudda.", - "has_no_topics": "This user didn't post any topics yet.", - "has_no_watched_topics": "This user didn't watch any topics yet.", + "has_no_posts": "This user hasn't posted anything yet.", + "has_no_topics": "This user hasn't posted any topics yet.", + "has_no_watched_topics": "This user hasn't watched any topics yet.", "email_hidden": "Email Cuada", "hidden": "cuadu", "paginate_description": "Paginate topics and posts instead of using infinite scroll", diff --git a/public/language/sc/users.json b/public/language/sc/users.json index 19e6cfe95f..d96c53ea9c 100644 --- a/public/language/sc/users.json +++ b/public/language/sc/users.json @@ -8,5 +8,14 @@ "users-found-search-took": "%1 user(s) found! Search took %2 seconds.", "filter-by": "Filter By", "online-only": "Online only", - "picture-only": "Picture only" + "picture-only": "Picture only", + "invite": "Invite", + "invitation-email-sent": "An invitation email has been sent to %1", + "user_list": "User List", + "recent_topics": "Recent Topics", + "popular_topics": "Popular Topics", + "unread_topics": "Unread Topics", + "categories": "Categories", + "tags": "Tags", + "map": "Map" } \ No newline at end of file diff --git a/public/language/sk/email.json b/public/language/sk/email.json index f290435e75..d8114d0231 100644 --- a/public/language/sk/email.json +++ b/public/language/sk/email.json @@ -1,11 +1,15 @@ { "password-reset-requested": "Password Reset Requested - %1!", "welcome-to": "Welcome to %1", + "invite": "Invitation from %1", "greeting_no_name": "Hello", "greeting_with_name": "Hello %1", "welcome.text1": "Thank you for registering with %1!", "welcome.text2": "To fully activate your account, we need to verify that you own the email address you registered with.", + "welcome.text3": "An administrator has accepted your registration application. You can login with your username/password now.", "welcome.cta": "Click here to confirm your email address", + "invitation.text1": "%1 has invited you to join %2", + "invitation.ctr": "Click here to create your account.", "reset.text1": "We received a request to reset your password, possibly because you have forgotten it. If this is not the case, please ignore this email.", "reset.text2": "To continue with the password reset, please click on the following link:", "reset.cta": "Click here to reset your password", diff --git a/public/language/sk/error.json b/public/language/sk/error.json index b86131117b..7241fdcd59 100644 --- a/public/language/sk/error.json +++ b/public/language/sk/error.json @@ -51,6 +51,7 @@ "already-favourited": "You have already favourited this post", "already-unfavourited": "You have already unfavourited this post", "cant-ban-other-admins": "Nemožte zakázať druhých adminov.", + "cant-remove-last-admin": "You are the only administrator. Add another user as an administrator before removing yourself as admin", "invalid-image-type": "Invalid image type. Allowed types are: %1", "invalid-image-extension": "Invalid image extension", "invalid-file-type": "Invalid file type. Allowed types are: %1", @@ -59,6 +60,8 @@ "group-name-change-not-allowed": "Nepovolená zmena mena skupiny", "group-already-member": "You are already part of this group", "group-needs-owner": "This group requires at least one owner", + "group-already-invited": "This user has already been invited", + "group-already-requested": "Your membership request has already been submitted", "post-already-deleted": "This post has already been deleted", "post-already-restored": "This post has already been restored", "topic-already-deleted": "This topic has already been deleted", @@ -76,6 +79,7 @@ "downvoting-disabled": "Downvoting is disabled", "not-enough-reputation-to-downvote": "You do not have enough reputation to downvote this post", "not-enough-reputation-to-flag": "You do not have enough reputation to flag this post", + "already-flagged": "You have already flagged this post", "reload-failed": "NodeBB encountered a problem while reloading: \"%1\". NodeBB will continue to serve the existing client-side assets, although you should undo what you did just prior to reloading.", "registration-error": "Registration Error", "parse-error": "Something went wrong while parsing server response", diff --git a/public/language/sk/global.json b/public/language/sk/global.json index d8d8a87509..393e734bf7 100644 --- a/public/language/sk/global.json +++ b/public/language/sk/global.json @@ -50,6 +50,7 @@ "views": "Zhliadnutí", "reputation": "Reputácia", "read_more": "čítaj viac", + "more": "More", "posted_ago_by_guest": "publikované %1 od hosťa", "posted_ago_by": "publikované %1 od %2", "posted_ago": "publikované %1", diff --git a/public/language/sk/groups.json b/public/language/sk/groups.json index 5e301aa46a..c9ac036475 100644 --- a/public/language/sk/groups.json +++ b/public/language/sk/groups.json @@ -6,6 +6,12 @@ "no_groups_found": "There are no groups to see", "pending.accept": "Accept", "pending.reject": "Reject", + "pending.accept_all": "Accept All", + "pending.reject_all": "Reject All", + "pending.none": "There are no pending members at this time", + "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", "cover-instructions": "Drag and Drop a photo, drag to position, and hit Save", "cover-change": "Change", "cover-save": "Save", @@ -13,6 +19,7 @@ "details.title": "Group Details", "details.members": "Member List", "details.pending": "Pending Members", + "details.invited": "Invited Members", "details.has_no_posts": "This group's members have not made any posts.", "details.latest_posts": "Latest Posts", "details.private": "Private", diff --git a/public/language/sk/notifications.json b/public/language/sk/notifications.json index 31f981cbee..26bb41cb32 100644 --- a/public/language/sk/notifications.json +++ b/public/language/sk/notifications.json @@ -20,6 +20,7 @@ "user_posted_topic": "%1 has posted a new topic: %2", "user_mentioned_you_in": "%1 mentioned you in %2", "user_started_following_you": "%1 started following you.", + "new_register": "%1 sent a registration request.", "email-confirmed": "Email bol potvrdený", "email-confirmed-message": "Ďakujeme za potvrdenie tvojho emailu. Účet je plne aktivovaný.", "email-confirm-error-message": "Vyskytla sa chyba pri overení tvojej emailovej adresy. ", diff --git a/public/language/sk/register.json b/public/language/sk/register.json index bb419eb305..f3d6e5db2e 100644 --- a/public/language/sk/register.json +++ b/public/language/sk/register.json @@ -14,5 +14,6 @@ "register_now_button": "Zaregistrovať sa", "alternative_registration": "Iný spôsob registrácie", "terms_of_use": "Podmienky používania", - "agree_to_terms_of_use": "Súhlasím s podmienkami používania" + "agree_to_terms_of_use": "Súhlasím s podmienkami používania", + "registration-added-to-queue": "Your registration has been added to the approval queue. You will receive an email when it is accepted by an administrator." } \ No newline at end of file diff --git a/public/language/sk/unread.json b/public/language/sk/unread.json index b3fd4a1c72..cd254ebd4a 100644 --- a/public/language/sk/unread.json +++ b/public/language/sk/unread.json @@ -5,5 +5,6 @@ "mark_as_read": "Označiť ako prečítan'é", "selected": "Vybrané", "all": "Všetko", + "all_categories": "All categories", "topics_marked_as_read.success": "Témy označiť ako prečítané!" } \ No newline at end of file diff --git a/public/language/sk/user.json b/public/language/sk/user.json index 957dde36e1..0133f70a97 100644 --- a/public/language/sk/user.json +++ b/public/language/sk/user.json @@ -6,8 +6,12 @@ "postcount": "Post Count", "email": "Email", "confirm_email": "Confirm Email", + "ban_account": "Ban Account", + "ban_account_confirm": "Do you really want to ban this user?", + "unban_account": "Unban Account", "delete_account": "Delete Account", "delete_account_confirm": "Are you sure you want to delete your account?
This action is irreversible and you will not be able to recover any of your data

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

", "fullname": "Meno a priezvisko", "website": "Webová stránka", "location": "Poloha", @@ -64,9 +68,9 @@ "settings-require-reload": "Some setting changes require a reload. Click here to reload the page.", "has_no_follower": "Tohoto užívatela nikto nesleduje :(", "follows_no_one": "Tento užívateľ nikoho nesleduje :(", - "has_no_posts": "Tento používateľ ešte nespravil príspevok", - "has_no_topics": "Tento užívateľ ešte nepublikoval žiadnu tému.", - "has_no_watched_topics": "This user didn't watch any topics yet.", + "has_no_posts": "This user hasn't posted anything yet.", + "has_no_topics": "This user hasn't posted any topics yet.", + "has_no_watched_topics": "This user hasn't watched any topics yet.", "email_hidden": "Skrytý email", "hidden": "skrytý", "paginate_description": "Paginate topics and posts instead of using infinite scroll", diff --git a/public/language/sk/users.json b/public/language/sk/users.json index 819f861de7..0b4cad215b 100644 --- a/public/language/sk/users.json +++ b/public/language/sk/users.json @@ -8,5 +8,14 @@ "users-found-search-took": "%1 user(s) found! Search took %2 seconds.", "filter-by": "Filter By", "online-only": "Online only", - "picture-only": "Picture only" + "picture-only": "Picture only", + "invite": "Invite", + "invitation-email-sent": "An invitation email has been sent to %1", + "user_list": "User List", + "recent_topics": "Recent Topics", + "popular_topics": "Popular Topics", + "unread_topics": "Unread Topics", + "categories": "Categories", + "tags": "Tags", + "map": "Map" } \ No newline at end of file diff --git a/public/language/sr/category.json b/public/language/sr/category.json new file mode 100644 index 0000000000..57bcb77b34 --- /dev/null +++ b/public/language/sr/category.json @@ -0,0 +1,12 @@ +{ + "new_topic_button": "Nova Tema", + "guest-login-post": "Пријавите се за слање порука", + "no_topics": "Ne postoji nijedna tema u ovoj kategoriji.
Zasto ne bi postavio jednu?", + "browsing": "gleda", + "no_replies": "Jos uvek nema odgovora", + "share_this_category": "Podeli ovu kategoriju", + "watch": "Прати", + "ignore": "Игнориши", + "watch.message": "Сада пратите ажурирања ове категорије", + "ignore.message": "Сада игноришете ажурирања ове категорије" +} \ No newline at end of file diff --git a/public/language/sr/email.json b/public/language/sr/email.json new file mode 100644 index 0000000000..8544a770ea --- /dev/null +++ b/public/language/sr/email.json @@ -0,0 +1,32 @@ +{ + "password-reset-requested": "Захтевано је ресетовање лозинке - %1!", + "welcome-to": "Добродошли на %1", + "invite": "Invitation from %1", + "greeting_no_name": "Здраво", + "greeting_with_name": "Здраво %1", + "welcome.text1": "Хвала што сте се рагистровали на %1!", + "welcome.text2": "Да би у потпуности активирали ваш налог, потребно је да проверимо да ли стварно поседујете адресу е-поште којом сте се регистровали. ", + "welcome.text3": "An administrator has accepted your registration application. You can login with your username/password now.", + "welcome.cta": "Кликните овде за потврду веше адресе е-поште", + "invitation.text1": "%1 has invited you to join %2", + "invitation.ctr": "Click here to create your account.", + "reset.text1": "Примили смо захтев за ресетовање ваше лозинке, највероватније јер сте је заборавили. Уколико то није случај, молимо да занемарите ово писмо.", + "reset.text2": "Да би наставили ас ресетовањем лозинке, кликните на следећу везу:", + "reset.cta": "Кликните овде да ресетујете лозинку", + "reset.notify.subject": "Лозика је успешно змењена", + "reset.notify.text1": "Обавештавамо вас да вам је лозинка на %1 успешно ресетована.", + "reset.notify.text2": "Уколико нисте ви ово одобрили, молимо одмах контактирајте администратора.", + "digest.notifications": "Имате непрочитана обавештења од %1:", + "digest.latest_topics": "Недавне теме од %1", + "digest.cta": "Кликните овде да посетите %1", + "digest.unsub.info": "This digest was sent to you due to your subscription settings.", + "digest.no_topics": "Није било активних тема у последњих %1", + "notif.chat.subject": "Примљена је нова порука ћаскања од %1", + "notif.chat.cta": "Кликните овде да наставите са разговором", + "notif.chat.unsub.info": "This chat notification was sent to you due to your subscription settings.", + "notif.post.cta": "Кликните овде за приказ целе теме", + "notif.post.unsub.info": "This post notification was sent to you due to your subscription settings.", + "test.text1": "Ово је пробно е-писмо за проверу исправности поставки е-поштара у NodeBB.", + "unsub.cta": "Кликните овде да измените те поставке", + "closing": "Хвала!" +} \ No newline at end of file diff --git a/public/language/sr/error.json b/public/language/sr/error.json new file mode 100644 index 0000000000..4600fa0a21 --- /dev/null +++ b/public/language/sr/error.json @@ -0,0 +1,88 @@ +{ + "invalid-data": "Неисправни подаци", + "not-logged-in": "Изгледа да нисте пријављени.", + "account-locked": "Ваш налог је привремено закључан", + "search-requires-login": "Претраживање захтева налог — пријавите се или се региструјте.", + "invalid-cid": "Неисправан ИД категорије", + "invalid-tid": "Неисправан ИД теме", + "invalid-pid": "Неисправан ИД поруке", + "invalid-uid": "Неисправан ИД корисника", + "invalid-username": "Неисправно корисничко име", + "invalid-email": "Неисправна е-пошта", + "invalid-title": "Неисправан наслов!", + "invalid-user-data": "Неисравни корсничи подаци", + "invalid-password": "Неисправна лозинка", + "invalid-username-or-password": "Молимо наведите и корисничко име и лозинку", + "invalid-search-term": "Неисправан упит за претрагу", + "invalid-pagination-value": "Invalid pagination value", + "username-taken": "Корисничко име је заузето", + "email-taken": "Адреса е-поште је заусета", + "email-not-confirmed": "Ваша адреса е-поште жоуш увек није оверена, кликните овде да би сте то учинили.", + "email-not-confirmed-chat": "Није вам дозвољено да ћаскате док не оверите вашу е-пошту, клкните овде да то учините.", + "no-email-to-confirm": "This forum requires email confirmation, please click here to enter an email", + "email-confirm-failed": "We could not confirm your email, please try again later.", + "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", + "username-too-short": "Username too short", + "username-too-long": "Username too long", + "user-banned": "User banned", + "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", + "no-category": "Категорија не постоји", + "no-topic": "Тема не постоји", + "no-post": "Порука не постоји", + "no-group": "Група не постоји", + "no-user": "Корисник не постоји", + "no-teaser": "Исечак не постоји", + "no-privileges": "Немате довољне привилегије за обављање ове радње.", + "no-emailers-configured": "No email plugins were loaded, so a test email could not be sent", + "category-disabled": "Category disabled", + "topic-locked": "Тема је закључана", + "post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting", + "still-uploading": "Please wait for uploads to complete.", + "content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).", + "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).", + "title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).", + "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).", + "too-many-posts": "You can only post once every %1 second(s) - please wait before posting again", + "too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again", + "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)", + "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)", + "file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file", + "cant-vote-self-post": "You cannot vote for your own post", + "already-favourited": "You have already favourited this post", + "already-unfavourited": "You have already unfavourited this post", + "cant-ban-other-admins": "You can't ban other admins!", + "cant-remove-last-admin": "You are the only administrator. Add another user as an administrator before removing yourself as admin", + "invalid-image-type": "Invalid image type. Allowed types are: %1", + "invalid-image-extension": "Invalid image extension", + "invalid-file-type": "Invalid file type. Allowed types are: %1", + "group-name-too-short": "Group name too short", + "group-already-exists": "Group already exists", + "group-name-change-not-allowed": "Group name change not allowed", + "group-already-member": "You are already part of this group", + "group-needs-owner": "This group requires at least one owner", + "group-already-invited": "This user has already been invited", + "group-already-requested": "Your membership request has already been submitted", + "post-already-deleted": "This post has already been deleted", + "post-already-restored": "This post has already been restored", + "topic-already-deleted": "This topic has already been deleted", + "topic-already-restored": "This topic has already been restored", + "cant-purge-main-post": "You can't purge the main post, please delete the topic instead", + "topic-thumbnails-are-disabled": "Topic thumbnails are disabled.", + "invalid-file": "Неисправан фајл", + "uploads-are-disabled": "Uploads are disabled", + "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", + "about-me-too-long": "Sorry, your about me cannot be longer than %1 character(s).", + "cant-chat-with-yourself": "You can't chat with yourself!", + "chat-restricted": "This user has restricted their chat messages. They must follow you before you can chat with them", + "too-many-messages": "You have sent too many messages, please wait awhile.", + "reputation-system-disabled": "Reputation system is disabled.", + "downvoting-disabled": "Downvoting is disabled", + "not-enough-reputation-to-downvote": "You do not have enough reputation to downvote this post", + "not-enough-reputation-to-flag": "You do not have enough reputation to flag this post", + "already-flagged": "You have already flagged this post", + "reload-failed": "NodeBB encountered a problem while reloading: \"%1\". NodeBB will continue to serve the existing client-side assets, although you should undo what you did just prior to reloading.", + "registration-error": "Грешка при регистрацији", + "parse-error": "Something went wrong while parsing server response", + "wrong-login-type-email": "Please use your email to login", + "wrong-login-type-username": "Please use your username to login" +} \ No newline at end of file diff --git a/public/language/sr/global.json b/public/language/sr/global.json new file mode 100644 index 0000000000..013e502c80 --- /dev/null +++ b/public/language/sr/global.json @@ -0,0 +1,82 @@ +{ + "home": "Почетна", + "search": "Претрага", + "buttons.close": "Затвори", + "403.title": "Приступ одбијен", + "403.message": "Изгледа да сте набасали на страницу којој немате дозвољен приступ.", + "403.login": "Можда би требало да се пријавите?", + "404.title": "Не постоји", + "404.message": "You seem to have stumbled upon a page that does not exist. Return to the home page.", + "500.title": "Унутрашња грешка.", + "500.message": "Упс! Изгледа да нешто ије како треба!", + "register": "Регистрација", + "login": "Пријава", + "please_log_in": "Молимо, пријавите се", + "logout": "Одјава", + "posting_restriction_info": "Слање порука је тренутни ограничено само на пријављене кориснике, кликните овде да се пријавите.", + "welcome_back": "Добродошли поново", + "you_have_successfully_logged_in": "Успешно сте се пријавили", + "save_changes": "Сними измене", + "close": "Затвори", + "pagination": "Обележавање страна", + "pagination.out_of": "%1 од %2", + "pagination.enter_index": "Enter index", + "header.admin": "Админ", + "header.recent": "Недавно", + "header.unread": "Непрочитано", + "header.tags": "Ознаке", + "header.popular": "Популарно", + "header.users": "Корисници", + "header.groups": "Групе", + "header.chats": "Ћаскања", + "header.notifications": "Опавештења", + "header.search": "Претрага", + "header.profile": "Профил", + "notifications.loading": "Учитавам обавештења", + "chats.loading": "Учитавам ћаскања", + "motd.welcome": "Добродошли на NodeBB, дискусиону платформу будућности.", + "previouspage": "Претходна стран", + "nextpage": "Следећа страна", + "alert.success": "Успех", + "alert.error": "Грешка", + "alert.banned": "Забрањен", + "alert.banned.message": "You have just been banned, you will now be logged out.", + "alert.unfollow": "You are no longer following %1!", + "alert.follow": "You are now following %1!", + "online": "На вези", + "users": "Корисници", + "topics": "Теме", + "posts": "Поруке", + "views": "Прегледи", + "reputation": "Репутација", + "read_more": "прочитајте више", + "more": "More", + "posted_ago_by_guest": "posted %1 by Guest", + "posted_ago_by": "posted %1 by %2", + "posted_ago": "posted %1", + "posted_in_ago_by_guest": "posted in %1 %2 by Guest", + "posted_in_ago_by": "posted in %1 %2 by %3", + "posted_in_ago": "posted in %1 %2", + "replied_ago": "replied %1", + "user_posted_ago": "%1 posted %2", + "guest_posted_ago": "Guest posted %1", + "last_edited_by_ago": "last edited by %1 %2", + "norecentposts": "Нема недавних порука", + "norecenttopics": "Нема скорашњих тема", + "recentposts": "Скорашње теме", + "recentips": "Недавно забележени ИП-ови", + "away": "Одсутан", + "dnd": "Не узнемиравај", + "invisible": "Невидљив", + "offline": "Ван везе", + "email": "Е-пошта", + "language": "Језик", + "guest": "Гост", + "guests": "Гости", + "updated.title": "Форум је ажуриран", + "updated.message": "Форум је управо ажуриран на последњу верзију. Кликните овде да би освежили страницу.", + "privacy": "Приватност", + "follow": "Прати", + "unfollow": "Не прати", + "delete_all": "Обриши све" +} \ No newline at end of file diff --git a/public/language/sr/groups.json b/public/language/sr/groups.json new file mode 100644 index 0000000000..d8bf883bd3 --- /dev/null +++ b/public/language/sr/groups.json @@ -0,0 +1,43 @@ +{ + "groups": "Групе", + "view_group": "Преглед групе", + "owner": "Власник групе", + "new_group": "Направи нову групу", + "no_groups_found": "Нема група за преглед", + "pending.accept": "Прихвати", + "pending.reject": "Одбиј", + "pending.accept_all": "Accept All", + "pending.reject_all": "Reject All", + "pending.none": "There are no pending members at this time", + "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", + "cover-instructions": "Превуците и испустите фотографију, одвуците до позиције и притисните Сними", + "cover-change": "Измени", + "cover-save": "Сними", + "cover-saving": "Снимање", + "details.title": "Детаљи о групи", + "details.members": "Списак чланова", + "details.pending": "Чланови на чекању", + "details.invited": "Invited Members", + "details.has_no_posts": "Чланови ове групе нису написали ниједну поруку.", + "details.latest_posts": "Последње поруке", + "details.private": "Приватна", + "details.grant": "Grant/Rescind Ownership", + "details.kick": "Избаци", + "details.owner_options": "Администрација групе", + "details.group_name": "Име групе", + "details.member_count": "Број чланова", + "details.creation_date": "Датум настанка", + "details.description": "Опис", + "details.badge_preview": "Приказ беџа", + "details.change_icon": "Промена иконе", + "details.change_colour": "Промена боје", + "details.badge_text": "Текст беџа", + "details.userTitleEnabled": "Приказ беџа", + "details.private_help": "Уколико је укључено, приступање групи захтева одобрење власника групе.", + "details.hidden": "Скривена", + "details.hidden_help": "Уколико је укључено, група неће бити видљива на списку група, и корисницима се позивнице морају слати ручно.", + "event.updated": "Детаљи групе су ажурирани", + "event.deleted": "Група „%1“ је обрисана" +} \ No newline at end of file diff --git a/public/language/sr/language.json b/public/language/sr/language.json new file mode 100644 index 0000000000..f1a8e3dfc3 --- /dev/null +++ b/public/language/sr/language.json @@ -0,0 +1,5 @@ +{ + "name": "Српски", + "code": "sr", + "dir": "ltr" +} \ No newline at end of file diff --git a/public/language/sr/login.json b/public/language/sr/login.json new file mode 100644 index 0000000000..8fefdb9068 --- /dev/null +++ b/public/language/sr/login.json @@ -0,0 +1,11 @@ +{ + "username-email": "Корисничко име / Лозинка", + "username": "Корисничко име", + "email": "Е-пошта", + "remember_me": "Памти ме?", + "forgot_password": "Заборављена лозинка?", + "alternative_logins": "Алтернативно пријављивање", + "failed_login_attempt": "Неуспело пријављивање, покушајте поново.", + "login_successful": "Успешно сте се пријавили!", + "dont_have_account": "Немате налог?" +} \ No newline at end of file diff --git a/public/language/sr/modules.json b/public/language/sr/modules.json new file mode 100644 index 0000000000..f9c76f5b5f --- /dev/null +++ b/public/language/sr/modules.json @@ -0,0 +1,26 @@ +{ + "chat.chatting_with": "Ћаскај са ", + "chat.placeholder": "Унесите текст поруке овде, притисните ентер за слање", + "chat.send": "Пошаљи", + "chat.no_active": "Немате активних ћаскања.", + "chat.user_typing": "%1 куца ...", + "chat.user_has_messaged_you": "%1 вам посла поруку.", + "chat.see_all": "Прикажи сва ћаскања", + "chat.no-messages": "Please select a recipient to view chat message history", + "chat.recent-chats": "Недавна ћаскања", + "chat.contacts": "Контакти", + "chat.message-history": "Историјат порука", + "chat.pop-out": "Pop out chat", + "chat.maximize": "Максимизуј", + "chat.seven_days": "7 дана", + "chat.thirty_days": "30 дана", + "chat.three_months": "3 месеца", + "composer.compose": "Састави", + "composer.show_preview": "Прикажи преглед", + "composer.hide_preview": "Сакриј преглед", + "composer.user_said_in": "%1 рече у %2", + "composer.user_said": "%1 рече:", + "composer.discard": "Желите ли да одбаците ову поруку?", + "composer.submit_and_lock": "Пошаљи и закључај", + "composer.toggle_dropdown": "Toggle Dropdown" +} \ No newline at end of file diff --git a/public/language/sr/notifications.json b/public/language/sr/notifications.json new file mode 100644 index 0000000000..e5ebacb691 --- /dev/null +++ b/public/language/sr/notifications.json @@ -0,0 +1,28 @@ +{ + "title": "Обавештења", + "no_notifs": "Немате нових обавештења", + "see_all": "Прикажи сва обавештења", + "mark_all_read": "Означи сва обавештења као прочитана", + "back_to_home": "Назад на %1", + "outgoing_link": "Одлазна веза", + "outgoing_link_message": "Сада напуштате %1", + "continue_to": "Продужи на %1", + "return_to": "Врати се на %1", + "new_notification": "Ново обавештење", + "you_have_unread_notifications": "Имате непрочитаних обавештења.", + "new_message_from": "Нова порука од %1", + "upvoted_your_post_in": "%1 дода глас вашој поруци у %2", + "moved_your_post": "%1 премести вашу поруку.", + "moved_your_topic": "%1 премести вашу тему.", + "favourited_your_post_in": "%1 доду вашу поруку из %2 у омиљене.", + "user_flagged_post_in": "%1 означи поруку у %2", + "user_posted_to": "%1 посла нови одговор за: %2", + "user_posted_topic": "%1 постави нову тему:", + "user_mentioned_you_in": "%1 вас помену у %2", + "user_started_following_you": "%1 поче да вас прати.", + "new_register": "%1 sent a registration request.", + "email-confirmed": "Е-пошта је је отврђена.", + "email-confirmed-message": "Хвала на овери ваше е-поште. Ваш налог је сада у потпуности активан.", + "email-confirm-error-message": "Дошло је до проблема са овером ваше е-поште. Можда је код неисправан или истекао.", + "email-confirm-sent": "Е-писмо за потврду је послато." +} \ No newline at end of file diff --git a/public/language/sr/pages.json b/public/language/sr/pages.json new file mode 100644 index 0000000000..531561eb89 --- /dev/null +++ b/public/language/sr/pages.json @@ -0,0 +1,21 @@ +{ + "home": "Почетна", + "unread": "Непрочитане теме", + "popular": "Полуларне теме", + "recent": "Скорашње теме", + "users": "Регистровани корисници", + "notifications": "Обавештења", + "tags": "Ознаке", + "tag": "Теме са ознаком „%1“", + "user.edit": "Уређивање „%1“", + "user.following": "Особе које %1 прати", + "user.followers": "Особе које прате %1", + "user.posts": "Поруке од %1", + "user.topics": "Теме од %1", + "user.groups": "Групе корисника %1", + "user.favourites": "Омиљене поруке корисника $1", + "user.settings": "Корисничке поставке", + "user.watched": "Теме које %1 прати", + "maintenance.text": "%1 is currently undergoing maintenance. Please come back another time.", + "maintenance.messageIntro": "Additionally, the administrator has left this message:" +} \ No newline at end of file diff --git a/public/language/sr/recent.json b/public/language/sr/recent.json new file mode 100644 index 0000000000..e82afb2a35 --- /dev/null +++ b/public/language/sr/recent.json @@ -0,0 +1,19 @@ +{ + "title": "Недавно", + "day": "Дан", + "week": "Седмица", + "month": "Месец", + "year": "Година", + "alltime": "Одувек", + "no_recent_topics": "Нема скорашњих тема.", + "no_popular_topics": "Нема популарних тема.", + "there-is-a-new-topic": "Доступна је нова тема.", + "there-is-a-new-topic-and-a-new-post": "Доступна је нова тема у нова порука.", + "there-is-a-new-topic-and-new-posts": "Доступна је нова тема и %1 нових порука.", + "there-are-new-topics": "Доступне су %1 нове теме.", + "there-are-new-topics-and-a-new-post": "Доступне су %1 нове теме и нова порука.", + "there-are-new-topics-and-new-posts": "Доступне су %1 нове теме и %2 нових порука.", + "there-is-a-new-post": "Доступна је нова порука.", + "there-are-new-posts": "Доступне су %1 нове поруке.", + "click-here-to-reload": "Кликните овде за поновно учитавање." +} \ No newline at end of file diff --git a/public/language/sr/register.json b/public/language/sr/register.json new file mode 100644 index 0000000000..9f802086eb --- /dev/null +++ b/public/language/sr/register.json @@ -0,0 +1,19 @@ +{ + "register": "Регистрација", + "help.email": "Ваша е-пошта ће подразумевано бити скривена од јавности.", + "help.username_restrictions": "Јединствено корисничко име између %1 и %2 знакова. Остали вас могу спомињати путем @username.", + "help.minimum_password_length": "Ваша лозинке мора имати најмање %1 знакова.", + "email_address": "Адреса е-поште", + "email_address_placeholder": "Унесите адресу е-поште", + "username": "Корисничко име", + "username_placeholder": "Унесите корисничко име", + "password": "Лозинка", + "password_placeholder": "Унесите лознку", + "confirm_password": "Потврда лозинке", + "confirm_password_placeholder": "Потврдите лозинку", + "register_now_button": "Региструјте се", + "alternative_registration": "Алтернативно регистровање", + "terms_of_use": "Услови коришћења", + "agree_to_terms_of_use": "Слажем се са условима коришћења", + "registration-added-to-queue": "Your registration has been added to the approval queue. You will receive an email when it is accepted by an administrator." +} \ No newline at end of file diff --git a/public/language/sr/reset_password.json b/public/language/sr/reset_password.json new file mode 100644 index 0000000000..4016b8eb3c --- /dev/null +++ b/public/language/sr/reset_password.json @@ -0,0 +1,17 @@ +{ + "reset_password": "Ресетовање лозинке", + "update_password": "Ажурурај лозинку", + "password_changed.title": "Лозинка је промењена", + "password_changed.message": "

Лозинка је успешно ресетована, молимо да се поново пријавите.", + "wrong_reset_code.title": "Неисправан код за ресетовање", + "wrong_reset_code.message": "Примљени код за ресетовање је неисправан. Пробајте поново, или затражите нови код за ресетовањe.", + "new_password": "Нова лозинка", + "repeat_password": "Потврда нове лозинке", + "enter_email": "Унесите вашу адресу е-поште и послаћемо вам писмо за упутством за ресетовање налога.", + "enter_email_address": "Унесите адресу е-поште", + "password_reset_sent": "Порука за ерсетовање лозинке је послата", + "invalid_email": "Неисправна е-пошта / е-пошта не постоји!", + "password_too_short": "Унета лозинка је прекратка, молимо изаберите другу лозинку.", + "passwords_do_not_match": "Унете лозинке се не подударају.", + "password_expired": "Ваша лозинка је истекла, молимо изаберите нову" +} \ No newline at end of file diff --git a/public/language/sr/search.json b/public/language/sr/search.json new file mode 100644 index 0000000000..64803b8d79 --- /dev/null +++ b/public/language/sr/search.json @@ -0,0 +1,40 @@ +{ + "results_matching": "%1 резултат(а) се поклапају са „%2“, (%3 секунди)", + "no-matches": "Нема поклапања", + "advanced-search": "Напредна претрага", + "in": "У", + "titles": "Наслови", + "titles-posts": "Наслови и поруке", + "posted-by": "Написа", + "in-categories": "У категоријама", + "search-child-categories": "Претражи подкатегорије", + "reply-count": "Број одговора", + "at-least": "Најмање", + "at-most": "Највише", + "post-time": "Време писања", + "newer-than": "Новије од", + "older-than": "Старије од", + "any-date": "Било који датум", + "yesterday": "Јуче", + "one-week": "Једне седмице", + "two-weeks": "Две седмице", + "one-month": "Један месец", + "three-months": "Три месеца", + "six-months": "Шест месеци", + "one-year": "Једне године", + "sort-by": "Сложи према", + "last-reply-time": "Вермену последњег одговора", + "topic-title": "Наслову теме", + "number-of-replies": "Броју одговора", + "number-of-views": "Броју прегледа", + "topic-start-date": "Датуму настанка теме", + "username": "Корисничном имену", + "category": "Категорији", + "descending": "У опадајућем низу", + "ascending": "У растућем низу", + "save-preferences": "Сними поставке", + "clear-preferences": "Очисти поставке", + "search-preferences-saved": "Поставке претраге су снимљене", + "search-preferences-cleared": "Поставке претраге су очишћене", + "show-results-as": "Прикажи резултате као" +} \ No newline at end of file diff --git a/public/language/sr/success.json b/public/language/sr/success.json new file mode 100644 index 0000000000..896b19239d --- /dev/null +++ b/public/language/sr/success.json @@ -0,0 +1,6 @@ +{ + "success": "Успех", + "topic-post": "Успешно сте послали поруку.", + "authentication-successful": "Успешна аутентификација", + "settings-saved": "Поставке су сачуване!" +} \ No newline at end of file diff --git a/public/language/sr/tags.json b/public/language/sr/tags.json new file mode 100644 index 0000000000..1fb87cf29b --- /dev/null +++ b/public/language/sr/tags.json @@ -0,0 +1,7 @@ +{ + "no_tag_topics": "Нема тема са овом ознаком.", + "tags": "Ознаке", + "enter_tags_here": "Овде унесите ознаке, између %1 и %2 знакова за сваку.", + "enter_tags_here_short": "Унесите ознаке...", + "no_tags": "Још увек нема ознака." +} \ No newline at end of file diff --git a/public/language/sr/topic.json b/public/language/sr/topic.json new file mode 100644 index 0000000000..76098c8a82 --- /dev/null +++ b/public/language/sr/topic.json @@ -0,0 +1,100 @@ +{ + "topic": "Тема", + "topic_id": "ИД теме", + "topic_id_placeholder": "Унесите ИД теме", + "no_topics_found": "Нису нађене теме!", + "no_posts_found": "Нису нађене поруке!", + "post_is_deleted": "Ова порука је обрисана!", + "topic_is_deleted": "Ова тема је обрисана!", + "profile": "Профил", + "posted_by": "Постави %1", + "posted_by_guest": "Постави Гост", + "chat": "Ћаскање", + "notify_me": "Будите обавештени о ноцим порукама у овој теми", + "quote": "Цитат", + "reply": "Одговор", + "guest-login-reply": "Пријавите се да би одговорили", + "edit": "Уређивање", + "delete": "Брисање", + "purge": "Чишћење", + "restore": "Restore", + "move": "Премештање", + "fork": "Рачвање", + "link": "Веза", + "share": "Дели", + "tools": "Алатке", + "flag": "Означи", + "locked": "Закључан", + "bookmark_instructions": "Click here to return to your last position or close to discard.", + "flag_title": "Flag this post for moderation", + "flag_confirm": "Are you sure you want to flag this post?", + "flag_success": "This post has been flagged for moderation.", + "deleted_message": "This topic has been deleted. Only users with topic management privileges can see it.", + "following_topic.message": "You will now be receiving notifications when somebody posts to this topic.", + "not_following_topic.message": "You will no longer receive notifications from this topic.", + "login_to_subscribe": "Please register or log in in order to subscribe to this topic.", + "markAsUnreadForAll.success": "Topic marked as unread for all.", + "watch": "Прати", + "unwatch": "Не прати", + "watch.title": "Be notified of new replies in this topic", + "unwatch.title": "Stop watching this topic", + "share_this_post": "Share this Post", + "thread_tools.title": "Алатке теме", + "thread_tools.markAsUnreadForAll": "Означи као непрочитано", + "thread_tools.pin": "Pin Topic", + "thread_tools.unpin": "Unpin Topic", + "thread_tools.lock": "Закључај тему", + "thread_tools.unlock": "Откључај тему", + "thread_tools.move": "Премести тему", + "thread_tools.move_all": "Премести све", + "thread_tools.fork": "Рачвај тему", + "thread_tools.delete": "Обриши тему", + "thread_tools.delete_confirm": "Are you sure you want to delete this topic?", + "thread_tools.restore": "Restore Topic", + "thread_tools.restore_confirm": "Are you sure you want to restore this topic?", + "thread_tools.purge": "Purge Topic", + "thread_tools.purge_confirm": "Are you sure you want to purge this topic?", + "topic_move_success": "This topic has been successfully moved to %1", + "post_delete_confirm": "Are you sure you want to delete this post?", + "post_restore_confirm": "Are you sure you want to restore this post?", + "post_purge_confirm": "Are you sure you want to purge this post?", + "load_categories": "Учитавам категорије", + "disabled_categories_note": "Disabled Categories are greyed out", + "confirm_move": "Move", + "confirm_fork": "Fork", + "favourite": "Favourite", + "favourites": "Favourites", + "favourites.has_no_favourites": "You don't have any favourites, favourite some posts to see them here!", + "loading_more_posts": "Loading More Posts", + "move_topic": "Move Topic", + "move_topics": "Move Topics", + "move_post": "Move Post", + "post_moved": "Post moved!", + "fork_topic": "Fork Topic", + "topic_will_be_moved_to": "This topic will be moved to the category", + "fork_topic_instruction": "Click the posts you want to fork", + "fork_no_pids": "No posts selected!", + "fork_success": "Successfully forked topic! Click here to go to the forked topic.", + "composer.title_placeholder": "Enter your topic title here...", + "composer.handle_placeholder": "Name", + "composer.discard": "Discard", + "composer.submit": "Submit", + "composer.replying_to": "Replying to %1", + "composer.new_topic": "New Topic", + "composer.uploading": "uploading...", + "composer.thumb_url_label": "Paste a topic thumbnail URL", + "composer.thumb_title": "Add a thumbnail to this topic", + "composer.thumb_url_placeholder": "http://example.com/thumb.png", + "composer.thumb_file_label": "Or upload a file", + "composer.thumb_remove": "Clear fields", + "composer.drag_and_drop_images": "Drag and Drop Images Here", + "more_users_and_guests": "%1 more user(s) and %2 guest(s)", + "more_users": "%1 more user(s)", + "more_guests": "%1 more guest(s)", + "users_and_others": "%1 and %2 others", + "sort_by": "Sort by", + "oldest_to_newest": "Oldest to Newest", + "newest_to_oldest": "Newest to Oldest", + "most_votes": "Most votes", + "most_posts": "Most posts" +} \ No newline at end of file diff --git a/public/language/sr/unread.json b/public/language/sr/unread.json new file mode 100644 index 0000000000..379ae00f4b --- /dev/null +++ b/public/language/sr/unread.json @@ -0,0 +1,10 @@ +{ + "title": "Непрочитано", + "no_unread_topics": "Нема непрочитаних тема.", + "load_more": "Учитај више", + "mark_as_read": "Означи као прочитано", + "selected": "Изабране", + "all": "Све", + "all_categories": "All categories", + "topics_marked_as_read.success": "Теме означене као прочитане!" +} \ No newline at end of file diff --git a/public/language/sr/user.json b/public/language/sr/user.json new file mode 100644 index 0000000000..113cb7f8d3 --- /dev/null +++ b/public/language/sr/user.json @@ -0,0 +1,88 @@ +{ + "banned": "Забрањен", + "offline": "Ван везе", + "username": "Корисничко име", + "joindate": "Датум регистрације", + "postcount": "Број порука", + "email": "Е-пошта", + "confirm_email": "Потврда е-поште", + "ban_account": "Ban Account", + "ban_account_confirm": "Do you really want to ban this user?", + "unban_account": "Unban Account", + "delete_account": "Брисање налога", + "delete_account_confirm": "Are you sure you want to delete your account?
This action is irreversible and you will not be able to recover any of your data

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

", + "fullname": "Пуно име", + "website": "Веб сајт", + "location": "Локација", + "age": "Старост", + "joined": "Датум регстрације", + "lastonline": "Последњи пут на вези", + "profile": "Профил", + "profile_views": "Прикази профила", + "reputation": "Репутација", + "favourites": "Омиљени", + "watched": "Надгледани", + "followers": "Пратиоци", + "following": "Прати", + "aboutme": "О мени", + "signature": "Потпис", + "gravatar": "Граватар", + "birthday": "Рођендан", + "chat": "Ђаскање", + "follow": "Прати", + "unfollow": "Не прати", + "more": "Више", + "profile_update_success": "Профил је успешно ажуриран!", + "change_picture": "Промена слике", + "edit": "Уређивање", + "uploaded_picture": "Послата слика", + "upload_new_picture": "Слање нове слике", + "upload_new_picture_from_url": "Слање нове слике са УРЛ-а", + "current_password": "Тренутна лозинка", + "change_password": "Промена лозинке", + "change_password_error": "Неисправна лозинка", + "change_password_error_wrong_current": "Ваша тренутна лозинка није исправна!", + "change_password_error_length": "Лозинка је прекратка!", + "change_password_error_match": "Лозинке се морају поклапати!", + "change_password_error_privileges": "Немате дозволе за мењање ове лозинке.", + "change_password_success": "Ваша лозинка је ажурирана!", + "confirm_password": "Потврда лозинке", + "password": "Лозинка", + "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", + "image_spec": "You may only upload PNG, JPG, or GIF files", + "settings": "Подешавања", + "show_email": "Прикажи моју лозинку", + "show_fullname": "Прикажи моје пуно име", + "restrict_chats": "Only allow chat messages from users I follow", + "digest_label": "Subscribe to Digest", + "digest_description": "Subscribe to email updates for this forum (new notifications and topics) according to a set schedule", + "digest_off": "Off", + "digest_daily": "Daily", + "digest_weekly": "Weekly", + "digest_monthly": "Monthly", + "send_chat_notifications": "Send an email if a new chat message arrives and I am not online", + "send_post_notifications": "Send an email when replies are made to topics I am subscribed to", + "settings-require-reload": "Some setting changes require a reload. Click here to reload the page.", + "has_no_follower": "This user doesn't have any followers :(", + "follows_no_one": "This user isn't following anyone :(", + "has_no_posts": "This user hasn't posted anything yet.", + "has_no_topics": "This user hasn't posted any topics yet.", + "has_no_watched_topics": "This user hasn't watched any topics yet.", + "email_hidden": "Email Hidden", + "hidden": "hidden", + "paginate_description": "Paginate topics and posts instead of using infinite scroll", + "topics_per_page": "Topics per Page", + "posts_per_page": "Posts per Page", + "notification_sounds": "Play a sound when you receive a notification", + "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", + "follow_topics_you_reply_to": "Follow topics that you reply to", + "follow_topics_you_create": "Follow topics you create", + "grouptitle": "Select the group title you would like to display", + "no-group-title": "No group title" +} \ No newline at end of file diff --git a/public/language/sr/users.json b/public/language/sr/users.json new file mode 100644 index 0000000000..ce477ba0ef --- /dev/null +++ b/public/language/sr/users.json @@ -0,0 +1,21 @@ +{ + "latest_users": "Последњи корисник", + "top_posters": "Највише порука", + "most_reputation": "Највећи углед", + "search": "Претрага", + "enter_username": "Унесите корисничко име за претрагу", + "load_more": "Учитај више", + "users-found-search-took": "Нађено је %1 корисника! Претрага је завршена за %2 секунде.", + "filter-by": "Филтрирај према", + "online-only": "Само корисници на вези", + "picture-only": "Само слике", + "invite": "Invite", + "invitation-email-sent": "An invitation email has been sent to %1", + "user_list": "User List", + "recent_topics": "Recent Topics", + "popular_topics": "Popular Topics", + "unread_topics": "Unread Topics", + "categories": "Categories", + "tags": "Tags", + "map": "Map" +} \ No newline at end of file diff --git a/public/language/sv/category.json b/public/language/sv/category.json index 722a8d0c45..d82abe3a24 100644 --- a/public/language/sv/category.json +++ b/public/language/sv/category.json @@ -1,6 +1,6 @@ { "new_topic_button": "Nytt ämne", - "guest-login-post": "Log in to post", + "guest-login-post": "Logga in för att posta", "no_topics": "Det finns inga ämnen i denna kategori.
Varför skapar inte du ett ämne?", "browsing": "läser", "no_replies": "Ingen har svarat", diff --git a/public/language/sv/email.json b/public/language/sv/email.json index 808c73caa8..f71376777c 100644 --- a/public/language/sv/email.json +++ b/public/language/sv/email.json @@ -1,17 +1,21 @@ { "password-reset-requested": "Återställning av lösenord efterfrågat - %1!", "welcome-to": "Välkommen till %1", + "invite": "Invitation from %1", "greeting_no_name": "Hej", "greeting_with_name": "Hej %1", "welcome.text1": "Tack för att du registerar dig på %1!", "welcome.text2": "För att slutföra aktiveringen av ditt konto, behöver vi verifiera att du har tillgång till den epostadress du registrerade dig med.", + "welcome.text3": "An administrator has accepted your registration application. You can login with your username/password now.", "welcome.cta": "Klicka här för att bekräfta din epostadress ", + "invitation.text1": "%1 has invited you to join %2", + "invitation.ctr": "Click here to create your account.", "reset.text1": "Vi fick en förfrågan om att återställa ditt lösenord, möjligen för att du har glömt det. Om detta inte är fallet, så kan du bortse från det här epostmeddelandet. ", "reset.text2": "För att fortsätta med återställning av lösenordet så kan du klicka på följande länk:", "reset.cta": "Klicka här för att återställa ditt lösenord", - "reset.notify.subject": "Password successfully changed", - "reset.notify.text1": "We are notifying you that on %1, your password was changed successfully.", - "reset.notify.text2": "If you did not authorise this, please notify an administrator immediately.", + "reset.notify.subject": "Lösenordet ändrat", + "reset.notify.text1": "Vi vill uppmärksamma dig på att ditt lösenord ändrades den %1", + "reset.notify.text2": "Om du inte godkänt det här så vänligen kontakta en admin snarast. ", "digest.notifications": "Du har olästa notiser från %1:", "digest.latest_topics": "Senaste ämnen från %1", "digest.cta": "Klicka här för att besöka %1", @@ -20,8 +24,8 @@ "notif.chat.subject": "Nytt chatt-meddelande från %1", "notif.chat.cta": "Klicka här för att fortsätta konversationen", "notif.chat.unsub.info": "Denna chatt-notifikation skickades till dig på grund av dina inställningar för prenumerationer.", - "notif.post.cta": "Click here to read the full topic", - "notif.post.unsub.info": "This post notification was sent to you due to your subscription settings.", + "notif.post.cta": "Klicka här för att läsa hela ämnet", + "notif.post.unsub.info": "Det här meddelandet fick du på grund av dina inställningar för prenumeration. ", "test.text1": "\nDet här är ett textmeddelande som verifierar att eposten är korrekt installerat för din NodeBB. ", "unsub.cta": "Klicka här för att ändra dom inställningarna", "closing": "Tack!" diff --git a/public/language/sv/error.json b/public/language/sv/error.json index ca8dd6f96e..c2acdc858c 100644 --- a/public/language/sv/error.json +++ b/public/language/sv/error.json @@ -51,14 +51,17 @@ "already-favourited": "Du har redan favoriserat det här inlägget", "already-unfavourited": "Du har redan avfavoriserat det här inlägget", "cant-ban-other-admins": "Du kan inte bannlysa andra administratörer.", - "invalid-image-type": "Invalid image type. Allowed types are: %1", - "invalid-image-extension": "Invalid image extension", - "invalid-file-type": "Invalid file type. Allowed types are: %1", + "cant-remove-last-admin": "You are the only administrator. Add another user as an administrator before removing yourself as admin", + "invalid-image-type": "Ogiltig bildtyp. Tillåtna typer är: % 1", + "invalid-image-extension": "Ogiltigt bildformat", + "invalid-file-type": "Ogiltig filtyp. Tillåtna typer är: % 1", "group-name-too-short": "Gruppnamnet är för kort", "group-already-exists": "Gruppen existerar redan", "group-name-change-not-allowed": "Gruppnamnet får inte ändras", "group-already-member": "You are already part of this group", "group-needs-owner": "This group requires at least one owner", + "group-already-invited": "This user has already been invited", + "group-already-requested": "Your membership request has already been submitted", "post-already-deleted": "Inlägget är redan raderat", "post-already-restored": "Inlägget är redan återställt", "topic-already-deleted": "Ämnet är redan raderat", @@ -76,9 +79,10 @@ "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", "not-enough-reputation-to-flag": "Du har inte tillräckligt förtroende för att flagga det här inlägget.", + "already-flagged": "You have already flagged this post", "reload-failed": "NodeBB stötte på problem med att ladda om: \"%1\". NodeBB kommer fortsätta servera den befintliga resurser till klienten, men du borde återställa det du gjorde alldeles innan du försökte ladda om.", "registration-error": "Registreringsfel", - "parse-error": "Something went wrong while parsing server response", - "wrong-login-type-email": "Please use your email to login", - "wrong-login-type-username": "Please use your username to login" + "parse-error": "Något gick fel vid tolkning av svar från servern", + "wrong-login-type-email": "Använd din e-post adress för att logga in", + "wrong-login-type-username": "Använd ditt användarnamn för att logga in" } \ No newline at end of file diff --git a/public/language/sv/global.json b/public/language/sv/global.json index ca623c2dbb..bb3909ab6a 100644 --- a/public/language/sv/global.json +++ b/public/language/sv/global.json @@ -50,6 +50,7 @@ "views": "Visningar", "reputation": "Rykte", "read_more": "läs mer", + "more": "More", "posted_ago_by_guest": "inskickad %1 av anonym", "posted_ago_by": "inskickad %1 av %2", "posted_ago": "inskickad %1", diff --git a/public/language/sv/groups.json b/public/language/sv/groups.json index bf3aaa470d..c1daef3aec 100644 --- a/public/language/sv/groups.json +++ b/public/language/sv/groups.json @@ -6,6 +6,12 @@ "no_groups_found": "There are no groups to see", "pending.accept": "Accept", "pending.reject": "Reject", + "pending.accept_all": "Accept All", + "pending.reject_all": "Reject All", + "pending.none": "There are no pending members at this time", + "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", "cover-instructions": "Drag and Drop a photo, drag to position, and hit Save", "cover-change": "Change", "cover-save": "Save", @@ -13,6 +19,7 @@ "details.title": "Detaljer för gruppen ", "details.members": "Medlemmar", "details.pending": "Pending Members", + "details.invited": "Invited Members", "details.has_no_posts": "Den här gruppens medlemmar har inte skrivit några inlägg.", "details.latest_posts": "Senaste inlägg", "details.private": "Private", diff --git a/public/language/sv/login.json b/public/language/sv/login.json index c96dd64485..363da51e9b 100644 --- a/public/language/sv/login.json +++ b/public/language/sv/login.json @@ -1,6 +1,6 @@ { - "username-email": "Username / Email", - "username": "Username", + "username-email": "Användarnamn eller epostadress", + "username": "Användarnamn", "email": "Email", "remember_me": "Kom ihåg mig?", "forgot_password": "Glömt lösenord?", diff --git a/public/language/sv/notifications.json b/public/language/sv/notifications.json index 1be16f89db..c95db1b8ee 100644 --- a/public/language/sv/notifications.json +++ b/public/language/sv/notifications.json @@ -20,6 +20,7 @@ "user_posted_topic": "%1 har skapat ett nytt ämne: %2", "user_mentioned_you_in": "%1 nämnde dig i %2", "user_started_following_you": "%1 började följa dig.", + "new_register": "%1 sent a registration request.", "email-confirmed": "Epost bekräftad", "email-confirmed-message": "Tack för att du bekräftat din epostadress. Ditt konto är nu fullt ut aktiverat.", "email-confirm-error-message": "Det uppstod ett fel med att bekräfta din epostadress. Kanske var koden ogiltig eller har gått ut.", diff --git a/public/language/sv/register.json b/public/language/sv/register.json index e51e3bf74e..750db5cc62 100644 --- a/public/language/sv/register.json +++ b/public/language/sv/register.json @@ -14,5 +14,6 @@ "register_now_button": "Registrera nu", "alternative_registration": "Alternativ registrering", "terms_of_use": "Användarvillkor", - "agree_to_terms_of_use": "Jag godkänner användarvillkoren" + "agree_to_terms_of_use": "Jag godkänner användarvillkoren", + "registration-added-to-queue": "Your registration has been added to the approval queue. You will receive an email when it is accepted by an administrator." } \ No newline at end of file diff --git a/public/language/sv/search.json b/public/language/sv/search.json index 7822eeae96..c023bf5927 100644 --- a/public/language/sv/search.json +++ b/public/language/sv/search.json @@ -1,6 +1,6 @@ { "results_matching": "%1 resultat matchar \"%2\", (%3 sekunder)", - "no-matches": "No matches found", + "no-matches": "Inga träffar", "advanced-search": "Advanced Search", "in": "In", "titles": "Titles", @@ -16,9 +16,9 @@ "older-than": "Older than", "any-date": "Any date", "yesterday": "Yesterday", - "one-week": "One week", - "two-weeks": "Two weeks", - "one-month": "One month", + "one-week": "En vecka", + "two-weeks": "Två veckor", + "one-month": "En månad", "three-months": "Three months", "six-months": "Six months", "one-year": "One year", @@ -32,7 +32,7 @@ "category": "Category", "descending": "In descending order", "ascending": "In ascending order", - "save-preferences": "Save preferences", + "save-preferences": "Spara inställningar", "clear-preferences": "Clear preferences", "search-preferences-saved": "Search preferences saved", "search-preferences-cleared": "Search preferences cleared", diff --git a/public/language/sv/topic.json b/public/language/sv/topic.json index b581cea269..d589412c3a 100644 --- a/public/language/sv/topic.json +++ b/public/language/sv/topic.json @@ -4,8 +4,8 @@ "topic_id_placeholder": "Skriv in ID för ämne", "no_topics_found": "Inga ämnen hittades!", "no_posts_found": "Inga inlägg hittades!", - "post_is_deleted": "Det här inlägget är raderat.", - "topic_is_deleted": "This topic is deleted!", + "post_is_deleted": "Detta inlägg är raderat!", + "topic_is_deleted": "Detta ämne är raderat!", "profile": "Profil", "posted_by": "Skapat av %1", "posted_by_guest": "Inlägg av anonym", @@ -13,7 +13,7 @@ "notify_me": "Få notiser om nya svar i detta ämne", "quote": "Citera", "reply": "Svara", - "guest-login-reply": "Log in to reply", + "guest-login-reply": "Logga in för att posta", "edit": "Ändra", "delete": "Ta bort", "purge": "Rensa", @@ -76,9 +76,9 @@ "fork_no_pids": "Inga inlägg valda!", "fork_success": "Ämnet har blivit förgrenat. Klicka här för att gå till det förgrenade ämnet.", "composer.title_placeholder": "Skriv in ämnets titel här...", - "composer.handle_placeholder": "Name", + "composer.handle_placeholder": "Namn", "composer.discard": "Avbryt", - "composer.submit": "Spara", + "composer.submit": "Skicka", "composer.replying_to": "Svarar till %1", "composer.new_topic": "Nytt ämne", "composer.uploading": "laddar upp...", @@ -96,5 +96,5 @@ "oldest_to_newest": "Äldst till nyaste", "newest_to_oldest": "Nyaste till äldst", "most_votes": "Mest röster", - "most_posts": "Most posts" + "most_posts": "Felst inlägg" } \ No newline at end of file diff --git a/public/language/sv/unread.json b/public/language/sv/unread.json index 583d912781..b6a20d21ca 100644 --- a/public/language/sv/unread.json +++ b/public/language/sv/unread.json @@ -1,9 +1,10 @@ { "title": "Olästa", - "no_unread_topics": "Det finns inga ilästa ämnen.", + "no_unread_topics": "Det finns inga olästa ämnen.", "load_more": "Ladda fler", "mark_as_read": "Markerad som läst", "selected": "Vald", "all": "Alla", + "all_categories": "All categories", "topics_marked_as_read.success": "Ämnet markerat som läst." } \ No newline at end of file diff --git a/public/language/sv/user.json b/public/language/sv/user.json index dd8f04d712..c76476de52 100644 --- a/public/language/sv/user.json +++ b/public/language/sv/user.json @@ -2,12 +2,16 @@ "banned": "Bannad", "offline": "Offline", "username": "Användarnamn", - "joindate": "Join Date", - "postcount": "Post Count", + "joindate": "Gick med", + "postcount": "Antal inlägg", "email": "Epost", "confirm_email": "Bekräfta epostadress ", + "ban_account": "Ban Account", + "ban_account_confirm": "Do you really want to ban this user?", + "unban_account": "Unban Account", "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": "Are you sure you want to delete this account?
This action is irreversible and you will not be able to recover any data

", "fullname": "Hela namnet", "website": "Webbsida", "location": "Plats", @@ -18,17 +22,17 @@ "profile_views": "Profil-visningar", "reputation": "Rykte", "favourites": "Favoriter", - "watched": "Watched", + "watched": "Bevakad", "followers": "Följare", "following": "Följer", - "aboutme": "About me", + "aboutme": "Om mig", "signature": "Signatur", "gravatar": "Gravatar", "birthday": "Födelsedag", "chat": "Chatta", "follow": "Följ", "unfollow": "Sluta följ", - "more": "More", + "more": "Mer", "profile_update_success": "Profilen uppdaterades.", "change_picture": "Ändra bild", "edit": "Ändra", @@ -60,25 +64,25 @@ "digest_weekly": "Veckovis", "digest_monthly": "Månadsvis", "send_chat_notifications": "Skicka ett epostmeddelande om nya chatt-meddelanden tas emot när jag inte är online.", - "send_post_notifications": "Send an email when replies are made to topics I am subscribed to", - "settings-require-reload": "Some setting changes require a reload. Click here to reload the page.", + "send_post_notifications": "Skicka ett epost när svar kommit på ämnen jag prenumererar på till", + "settings-require-reload": "Vissa inställningar som ändrades kräver att sidan laddas om. Klicka här för att ladda om sidan.", "has_no_follower": "Denna användare har inga följare :(", "follows_no_one": "Denna användare följer ingen :(", - "has_no_posts": "Denna användare har inte gjort några inlägg än.", - "has_no_topics": "Den här användaren har inte skrivit något inlägg ännu.", - "has_no_watched_topics": "This user didn't watch any topics yet.", + "has_no_posts": "This user hasn't posted anything yet.", + "has_no_topics": "This user hasn't posted any topics yet.", + "has_no_watched_topics": "This user hasn't watched any topics yet.", "email_hidden": "Epost dold", "hidden": "dold", - "paginate_description": "Paginate topics and posts instead of using infinite scroll", + "paginate_description": "Gör så att ämnen och inlägg visas som sidor istället för oändlig skroll", "topics_per_page": "Ämnen per sida", "posts_per_page": "Inlägg per sida", - "notification_sounds": "Play a sound when you receive a notification", + "notification_sounds": "Spela ett ljud när du får en notis", "browsing": "Inställning för bläddring", - "open_links_in_new_tab": "Open outgoing links in new tab", + "open_links_in_new_tab": "Öppna utgående länkar på ny flik", "enable_topic_searching": "Aktivera Sökning Inom Ämne", - "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen", - "follow_topics_you_reply_to": "Follow topics that you reply to", - "follow_topics_you_create": "Follow topics you create", - "grouptitle": "Select the group title you would like to display", - "no-group-title": "No group title" + "topic_search_help": "Om aktiverat kommer sökning inom ämne överskrida webbläsarens vanliga funktionen för sökning bland sidor och tillåta dig att söka genom hela ämnet istället för det som endast visas på skärmen.", + "follow_topics_you_reply_to": "Följ ämnen som du svarat på", + "follow_topics_you_create": "Följ ämnen du skapat", + "grouptitle": "Välj tittel för gruppen så som du vill att den ska visas", + "no-group-title": "Ingen titel på gruppen" } \ No newline at end of file diff --git a/public/language/sv/users.json b/public/language/sv/users.json index 591a32c344..1e364986af 100644 --- a/public/language/sv/users.json +++ b/public/language/sv/users.json @@ -8,5 +8,14 @@ "users-found-search-took": "%1 user(s) found! Search took %2 seconds.", "filter-by": "Filter By", "online-only": "Online only", - "picture-only": "Picture only" + "picture-only": "Picture only", + "invite": "Invite", + "invitation-email-sent": "An invitation email has been sent to %1", + "user_list": "User List", + "recent_topics": "Recent Topics", + "popular_topics": "Popular Topics", + "unread_topics": "Unread Topics", + "categories": "Categories", + "tags": "Tags", + "map": "Map" } \ No newline at end of file diff --git a/public/language/th/email.json b/public/language/th/email.json index d91ba43963..1c6a8c3296 100644 --- a/public/language/th/email.json +++ b/public/language/th/email.json @@ -1,11 +1,15 @@ { "password-reset-requested": "Password Reset Requested - %1!", "welcome-to": "ยินดีต้อนรับ %1", + "invite": "Invitation from %1", "greeting_no_name": "สวัสดี", "greeting_with_name": "สวัสดี %1", "welcome.text1": "ขอบคุณที่ลงทะเบียนกับ %1", "welcome.text2": "To fully activate your account, we need to verify that you own the email address you registered with.", + "welcome.text3": "An administrator has accepted your registration application. You can login with your username/password now.", "welcome.cta": "กดตรงนี้เพื่อยืนยันอีเมลของคุณ", + "invitation.text1": "%1 has invited you to join %2", + "invitation.ctr": "Click here to create your account.", "reset.text1": "We received a request to reset your password, possibly because you have forgotten it. If this is not the case, please ignore this email.", "reset.text2": "เพื่อดำเนินการตั้งรหัสผ่านใหม่ต่อไป, โปรดกดที่ลิ้งค์นี้:", "reset.cta": "กดตรงนี้เพื่อตั้งรหัสผ่านใหม่", diff --git a/public/language/th/error.json b/public/language/th/error.json index a962e365f4..4d4505ad2e 100644 --- a/public/language/th/error.json +++ b/public/language/th/error.json @@ -51,6 +51,7 @@ "already-favourited": "You have already favourited this post", "already-unfavourited": "You have already unfavourited this post", "cant-ban-other-admins": "You can't ban other admins!", + "cant-remove-last-admin": "You are the only administrator. Add another user as an administrator before removing yourself as admin", "invalid-image-type": "Invalid image type. Allowed types are: %1", "invalid-image-extension": "Invalid image extension", "invalid-file-type": "Invalid file type. Allowed types are: %1", @@ -59,6 +60,8 @@ "group-name-change-not-allowed": "Group name change not allowed", "group-already-member": "You are already part of this group", "group-needs-owner": "This group requires at least one owner", + "group-already-invited": "This user has already been invited", + "group-already-requested": "Your membership request has already been submitted", "post-already-deleted": "This post has already been deleted", "post-already-restored": "This post has already been restored", "topic-already-deleted": "This topic has already been deleted", @@ -76,6 +79,7 @@ "downvoting-disabled": "Downvoting is disabled", "not-enough-reputation-to-downvote": "You do not have enough reputation to downvote this post", "not-enough-reputation-to-flag": "You do not have enough reputation to flag this post", + "already-flagged": "You have already flagged this post", "reload-failed": "NodeBB encountered a problem while reloading: \"%1\". NodeBB will continue to serve the existing client-side assets, although you should undo what you did just prior to reloading.", "registration-error": "Registration Error", "parse-error": "Something went wrong while parsing server response", diff --git a/public/language/th/global.json b/public/language/th/global.json index d81b58b8eb..cec8d6cadc 100644 --- a/public/language/th/global.json +++ b/public/language/th/global.json @@ -50,6 +50,7 @@ "views": "ดู", "reputation": "Reputation", "read_more": "read more", + "more": "More", "posted_ago_by_guest": "posted %1 by Guest", "posted_ago_by": "posted %1 by %2", "posted_ago": "posted %1", diff --git a/public/language/th/groups.json b/public/language/th/groups.json index 089ba7fa93..d80f70d18e 100644 --- a/public/language/th/groups.json +++ b/public/language/th/groups.json @@ -6,6 +6,12 @@ "no_groups_found": "ยังไม่มี Group", "pending.accept": "ยอมรับ", "pending.reject": "ไม่ยอมรับ", + "pending.accept_all": "Accept All", + "pending.reject_all": "Reject All", + "pending.none": "There are no pending members at this time", + "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", "cover-instructions": "ลากรูปภาพไปวางยังตำแหน่งที่ต้องการแล้วกดที่ปุ่ม บันทึก", "cover-change": "ปรับปรุง", "cover-save": "บันทึก", @@ -13,6 +19,7 @@ "details.title": "ข้อมูล Group", "details.members": "รายชื่อสมาชิก", "details.pending": "สมาชิกที่กำลังรอการตอบรับ", + "details.invited": "Invited Members", "details.has_no_posts": "Group นี้ยังไม่มีโพสจากสมาชิก", "details.latest_posts": "โพสล่าสุด", "details.private": "ส่วนตัว", diff --git a/public/language/th/notifications.json b/public/language/th/notifications.json index f7383690c5..bc03b9bad0 100644 --- a/public/language/th/notifications.json +++ b/public/language/th/notifications.json @@ -20,6 +20,7 @@ "user_posted_topic": "%1 has posted a new topic: %2", "user_mentioned_you_in": "%1 mentioned you in %2", "user_started_following_you": "%1 started following you.", + "new_register": "%1 sent a registration request.", "email-confirmed": "Email ได้รับการยืนยันแล้ว", "email-confirmed-message": "ขอบคุณที่ยืนยัน Email ของคุณ บัญชีของคุณสามารถใช้งานได้แล้ว", "email-confirm-error-message": "มีปัญหาในการยืนยัน Email ของคุณ บางทีรหัสไม่ถูกต้องหรือหมดอายุแล้ว", diff --git a/public/language/th/register.json b/public/language/th/register.json index 5a02573eee..333898d689 100644 --- a/public/language/th/register.json +++ b/public/language/th/register.json @@ -14,5 +14,6 @@ "register_now_button": "ลงทะเบียน", "alternative_registration": "เข้าสู่ระบบโดยทางอื่น", "terms_of_use": "เงื่อนไขการใช้งาน", - "agree_to_terms_of_use": "ยอมรับข้อตกลงในการใช้งาน" + "agree_to_terms_of_use": "ยอมรับข้อตกลงในการใช้งาน", + "registration-added-to-queue": "Your registration has been added to the approval queue. You will receive an email when it is accepted by an administrator." } \ No newline at end of file diff --git a/public/language/th/unread.json b/public/language/th/unread.json index 2595955dc7..6928cb1366 100644 --- a/public/language/th/unread.json +++ b/public/language/th/unread.json @@ -5,5 +5,6 @@ "mark_as_read": "ทำเครื่องหมายว่าอ่านแล้ว", "selected": "เลือก", "all": "ทั้งหมด", + "all_categories": "All categories", "topics_marked_as_read.success": "Topic ถูกทำเครื่องหมายว่าอ่านแล้วเรียบร้อย" } \ No newline at end of file diff --git a/public/language/th/user.json b/public/language/th/user.json index 15cd6fb01b..697b13ec9e 100644 --- a/public/language/th/user.json +++ b/public/language/th/user.json @@ -6,8 +6,12 @@ "postcount": "จำนวนโพส", "email": "อีเมล์", "confirm_email": "ยืนยันอีเมล", + "ban_account": "Ban Account", + "ban_account_confirm": "Do you really want to ban this user?", + "unban_account": "Unban Account", "delete_account": "ลบบัญชี", "delete_account_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

", "fullname": "ชื่อเต็ม", "website": "เว็บไซต์", "location": "สถานที่", @@ -64,9 +68,9 @@ "settings-require-reload": "Some setting changes require a reload. Click here to reload the page.", "has_no_follower": "ผู้ใช้รายนี้ไม่มีใครติดตาม :(", "follows_no_one": "ผู้ใช้รายนี้ไม่ติดตามใคร :(", - "has_no_posts": "ผู้ใช้รายนี้ไม่ได้โพสต์อะไรเลย", - "has_no_topics": "สมาชิกรายนี้ยังไม่ได้มีการโพสต์ข้อความ", - "has_no_watched_topics": "ผู้ใช้นี้ยังไม่เคยเข้าชมในหัวข้อใดๆ", + "has_no_posts": "This user hasn't posted anything yet.", + "has_no_topics": "This user hasn't posted any topics yet.", + "has_no_watched_topics": "This user hasn't watched any topics yet.", "email_hidden": "ซ่อนอีเมล์", "hidden": "ซ่อน", "paginate_description": "Paginate topics and posts instead of using infinite scroll", diff --git a/public/language/th/users.json b/public/language/th/users.json index 542689e182..cdeb6d897a 100644 --- a/public/language/th/users.json +++ b/public/language/th/users.json @@ -8,5 +8,14 @@ "users-found-search-took": "%1 user(s) found! Search took %2 seconds.", "filter-by": "Filter By", "online-only": "Online only", - "picture-only": "Picture only" + "picture-only": "Picture only", + "invite": "Invite", + "invitation-email-sent": "An invitation email has been sent to %1", + "user_list": "User List", + "recent_topics": "Recent Topics", + "popular_topics": "Popular Topics", + "unread_topics": "Unread Topics", + "categories": "Categories", + "tags": "Tags", + "map": "Map" } \ No newline at end of file diff --git a/public/language/tr/email.json b/public/language/tr/email.json index 350df09f81..cca2d80594 100644 --- a/public/language/tr/email.json +++ b/public/language/tr/email.json @@ -1,11 +1,15 @@ { "password-reset-requested": "Parola Değiştirme İsteği Gönderildi", "welcome-to": "Hoşgeldiniz", + "invite": "Invitation from %1", "greeting_no_name": "Merhaba", "greeting_with_name": "Merhaba %1", "welcome.text1": "Kaydolduğunuz için teşekkürler!", "welcome.text2": "Hesabınızı aktif hale getirmek için, kaydolduğunuz e-posta adresinin size ait olduğunu onaylamamız gerekiyor.", + "welcome.text3": "An administrator has accepted your registration application. You can login with your username/password now.", "welcome.cta": "E-posta adresinizi onaylamak için buraya tıklayın", + "invitation.text1": "%1 has invited you to join %2", + "invitation.ctr": "Click here to create your account.", "reset.text1": "Şifrenizi değiştirmek istediğinize dair bir ileti aldık. Eğer böyle bir istek göndermediyseniz, lütfen bu e-postayı görmezden gelin.", "reset.text2": "Parola değiştirme işlemine devam etmek için aşağıdaki bağlantıya tıklayın:", "reset.cta": "Parolanızı değiştirmek için buraya tıklayın", diff --git a/public/language/tr/error.json b/public/language/tr/error.json index b34823ccfd..c846b60bfd 100644 --- a/public/language/tr/error.json +++ b/public/language/tr/error.json @@ -2,7 +2,7 @@ "invalid-data": "Geçersiz Veri", "not-logged-in": "Giriş yapmamış görünüyorsunuz.", "account-locked": "Hesabınız geçici olarak kitlendi", - "search-requires-login": "Searching requires an account - please login or register.", + "search-requires-login": "Arama hesap gerektiriyor. Lütfen giriş yapın ya da kaydolun.", "invalid-cid": "Geçersiz Kategori ID", "invalid-tid": "Geçersiz Başlık ID", "invalid-pid": "Geçersiz İleti ID", @@ -25,7 +25,7 @@ "username-too-short": "Kullanıcı ismi çok kısa", "username-too-long": "Kullanıcı ismi çok uzun.", "user-banned": "Kullanıcı Yasaklı", - "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", + "user-too-new": "Özür dileriz, ilk iletinizi yapmadan önce %1 saniye beklemeniz gerekiyor", "no-category": "Kategori Yok", "no-topic": "Başlık Yok", "no-post": "İleti Yok", @@ -36,21 +36,22 @@ "no-emailers-configured": "E-posta eklentisi kurulu değil bu yüzden test e-postası gönderilemedi", "category-disabled": "Kategori aktif değil", "topic-locked": "Başlık Kilitli", - "post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting", + "post-edit-duration-expired": "Gönderilen iletiler %1 saniyeden sonra değiştirilemez", "still-uploading": "Lütfen yüklemelerin bitmesini bekleyin.", - "content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).", - "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).", - "title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).", - "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).", - "too-many-posts": "You can only post once every %1 second(s) - please wait before posting again", - "too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again", - "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)", - "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)", - "file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file", + "content-too-short": "Lütfen daha uzun bir ileti girin. En az %1 karakter.", + "content-too-long": "Lütfen daha kısa bir ileti girin. İletiler %1 karakterden uzun olamaz.", + "title-too-short": "Lütfen daha uzun bir başlık girin. Başlıklar en az %1 karakter içermelidir.", + "title-too-long": "Lütfen daha kısa bir başlık girin. Başlıklar %1 karakterden uzun olamaz.", + "too-many-posts": "%1 saniye içinde yalnızca bir ileti gönderebilirsiniz - tekrar ileti göndermeden önce lütfen bekleyin.", + "too-many-posts-newbie": "Yeni bir kullanıcı olarak, %2 saygınlık kazanana kadar %1 saniye içinde bir ileti gönderebilirsiniz - tekrar ileti göndermeden önce lütfen bekleyin.", + "tag-too-short": "Lütfen daha uzun bir etiket girin. Etiketler en az %1 karakter içermelidir.", + "tag-too-long": "Lütfen daha kısa bir etiket girin. Etiketler %1 karakterden uzun olamaz.", + "file-too-big": "İzin verilen en büyük dosya boyutu %1 kb - lütfen daha küçük bir dosya yükleyin", "cant-vote-self-post": "Kendi iletinize oy veremezsiniz", "already-favourited": "Bu iletiyi zaten favorilerinize eklediniz", "already-unfavourited": "Bu iletiyi zaten favorilerinizden çıkardınız", "cant-ban-other-admins": "Başka yöneticileri yasaklayamazsınız!", + "cant-remove-last-admin": "You are the only administrator. Add another user as an administrator before removing yourself as admin", "invalid-image-type": "Geçersiz resim uzantısı. Izin verilen uzantılar: %1", "invalid-image-extension": "Geçersiz resim uzantısı", "invalid-file-type": "Geçersiz dosya türü. İzin verilenler şunlar : %1", @@ -59,6 +60,8 @@ "group-name-change-not-allowed": "Grup ismini değiştiremezsiniz", "group-already-member": "Bu grubun zaten bir parçasısınız.", "group-needs-owner": "Bu grubu en az bir kişi sahiplenmesi gerekiyor", + "group-already-invited": "This user has already been invited", + "group-already-requested": "Your membership request has already been submitted", "post-already-deleted": "İleti zaten silinmiş", "post-already-restored": "İleti zaten geri getirilmiş", "topic-already-deleted": "Başlık zaten silinmiş", @@ -67,8 +70,8 @@ "topic-thumbnails-are-disabled": "Başlık resimleri kapalı.", "invalid-file": "Geçersiz Dosya", "uploads-are-disabled": "Yüklemeler kapalı", - "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", - "about-me-too-long": "Sorry, your about me cannot be longer than %1 character(s).", + "signature-too-long": "Üzgünüm, imzanız %1 karakterden uzun olamaz.", + "about-me-too-long": "Hakkınızda kısmı en fazla %1 karakter olabilir.", "cant-chat-with-yourself": "Kendinizle sohbet edemezsiniz!", "chat-restricted": "Bu kullanıcı sohbet ayarlarını kısıtlamış. Bu kişiye mesaj gönderebilmeniz için sizi takip etmeleri gerekiyor", "too-many-messages": "Ardı ardına çok fazla mesaj yolladınız, lütfen biraz bekleyiniz.", @@ -76,6 +79,7 @@ "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.", "not-enough-reputation-to-flag": "Bu iletiyi bayraklamak için yeterince saygınlığınız yok", + "already-flagged": "You have already flagged this post", "reload-failed": "NodeBB tekrar yüklenirken bir sorunla karşılaştı: “%1“. NodeBB varolan dosyaları servis etmeye devam edecek.", "registration-error": "Kayıt Hatası", "parse-error": "Sunucu yanıtı çözümlemesi sırasında bir şeyler ters gitti", diff --git a/public/language/tr/global.json b/public/language/tr/global.json index cd2e8e18c9..f2541870fe 100644 --- a/public/language/tr/global.json +++ b/public/language/tr/global.json @@ -50,6 +50,7 @@ "views": "Görüntülemeler", "reputation": "Saygınlık", "read_more": "daha fazla oku", + "more": "More", "posted_ago_by_guest": "Ziyaretçi tarafından %1 yayımlandı", "posted_ago_by": "%2 tarafından %1 yayımlandı", "posted_ago": "%1 yayımlandı", diff --git a/public/language/tr/groups.json b/public/language/tr/groups.json index 4c92ecd5d1..bfec944413 100644 --- a/public/language/tr/groups.json +++ b/public/language/tr/groups.json @@ -6,6 +6,12 @@ "no_groups_found": "Henüz hiç grup yok", "pending.accept": "Onayla", "pending.reject": "Reddet", + "pending.accept_all": "Accept All", + "pending.reject_all": "Reject All", + "pending.none": "There are no pending members at this time", + "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", "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", @@ -13,6 +19,7 @@ "details.title": "Grup Detayları", "details.members": "Üye Listesi", "details.pending": "Üyeler bekleniyor", + "details.invited": "Invited Members", "details.has_no_posts": "Bu grubun üyeleri henüz bir ileti göndermedi.", "details.latest_posts": "En son iletiler", "details.private": "Özel", diff --git a/public/language/tr/notifications.json b/public/language/tr/notifications.json index 64ca790352..948a1dfeca 100644 --- a/public/language/tr/notifications.json +++ b/public/language/tr/notifications.json @@ -20,6 +20,7 @@ "user_posted_topic": "%1 yeni bir konu yarattı: %2", "user_mentioned_you_in": "%1 %2 başlığında sizden bahsetti.", "user_started_following_you": "%1 sizi takip etmeye başladı.", + "new_register": "%1 sent a registration request.", "email-confirmed": "E-posta onaylandı", "email-confirmed-message": "E-postanızı onaylandığınız için teşekkürler. Hesabınız tamamen aktive edildi.", "email-confirm-error-message": "E-posta adresinizi onaylarken bir hata oluştu. Kodunuz geçersiz ya da eski olabilir.", diff --git a/public/language/tr/register.json b/public/language/tr/register.json index 9ad1efddf1..cd4d6c67c9 100644 --- a/public/language/tr/register.json +++ b/public/language/tr/register.json @@ -14,5 +14,6 @@ "register_now_button": "Hemen Kayıt Ol", "alternative_registration": "Alternatif Kayıt", "terms_of_use": "Kullanım Şartları", - "agree_to_terms_of_use": "Kullanım Şartlarını kabul ediyorum" + "agree_to_terms_of_use": "Kullanım Şartlarını kabul ediyorum", + "registration-added-to-queue": "Your registration has been added to the approval queue. You will receive an email when it is accepted by an administrator." } \ No newline at end of file diff --git a/public/language/tr/topic.json b/public/language/tr/topic.json index 9700a29663..f5a42a48d2 100644 --- a/public/language/tr/topic.json +++ b/public/language/tr/topic.json @@ -5,7 +5,7 @@ "no_topics_found": "Hiç konu bulunamadı!", "no_posts_found": "Hiç bir ileti bulunamadı!", "post_is_deleted": "Bu ileti silinmiş!", - "topic_is_deleted": "This topic is deleted!", + "topic_is_deleted": "Bu başlık silindi!", "profile": "Profil", "posted_by": "%1 tarafından gönderildi", "posted_by_guest": "Ziyaretçi tarafından yayımlandı", diff --git a/public/language/tr/unread.json b/public/language/tr/unread.json index 93202b1f5e..95268cf85c 100644 --- a/public/language/tr/unread.json +++ b/public/language/tr/unread.json @@ -5,5 +5,6 @@ "mark_as_read": "Okundu Olarak İşaretle", "selected": "Seçili", "all": "Hepsi", + "all_categories": "All categories", "topics_marked_as_read.success": "Başlıklar okundu olarak işaretlendi!" } \ No newline at end of file diff --git a/public/language/tr/user.json b/public/language/tr/user.json index b5edba6bbd..724c026ad3 100644 --- a/public/language/tr/user.json +++ b/public/language/tr/user.json @@ -6,8 +6,12 @@ "postcount": "Yayın Sayısı", "email": "E-posta", "confirm_email": "E-posta onayla", + "ban_account": "Ban Account", + "ban_account_confirm": "Do you really want to ban this user?", + "unban_account": "Unban Account", "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": "Are you sure you want to delete this account?
This action is irreversible and you will not be able to recover any data

", "fullname": "Tam Ad", "website": "Websitesi", "location": "Konum", @@ -21,7 +25,7 @@ "watched": "İzlendi", "followers": "Takipçiler", "following": "Takip Ediyor", - "aboutme": "About me", + "aboutme": "Hakkımda", "signature": "İmza", "gravatar": "Avatar", "birthday": "Doğum Tarihi", @@ -64,9 +68,9 @@ "settings-require-reload": "Bazı ayar değişiklikleri sayfayı tekrar yüklemenizi gerektirir. Buraya tıklayarak sayfayı tekrar yükleyebilirsiniz.", "has_no_follower": "Bu kullanıcının hiç takipçisi yok :(", "follows_no_one": "Bu kullanıcı kimseyi takip etmiyor :(", - "has_no_posts": "Bu kullanıcı henüz birşey göndermedi.", - "has_no_topics": "Bu kullanıcı henüz bir konu yaratmadı.", - "has_no_watched_topics": "Bu kullanıcı henüz hiçbir konuyu izlemedi.", + "has_no_posts": "This user hasn't posted anything yet.", + "has_no_topics": "This user hasn't posted any topics yet.", + "has_no_watched_topics": "This user hasn't watched any topics yet.", "email_hidden": "E-posta gizli", "hidden": "gizli", "paginate_description": "Sonsuz yükleme yerine konu ve iletileri sayfalara böl", diff --git a/public/language/tr/users.json b/public/language/tr/users.json index ed6fd922fe..cee4c4445a 100644 --- a/public/language/tr/users.json +++ b/public/language/tr/users.json @@ -8,5 +8,14 @@ "users-found-search-took": "%1 kullanıcı(lar) bulundu! Arama %2 saniye sürdü.", "filter-by": "Şu şekilde filtrele", "online-only": "Sadece çevrimiçi", - "picture-only": "Sadece resim" + "picture-only": "Sadece resim", + "invite": "Invite", + "invitation-email-sent": "An invitation email has been sent to %1", + "user_list": "User List", + "recent_topics": "Recent Topics", + "popular_topics": "Popular Topics", + "unread_topics": "Unread Topics", + "categories": "Categories", + "tags": "Tags", + "map": "Map" } \ No newline at end of file diff --git a/public/language/vi/email.json b/public/language/vi/email.json index c7f5fd5b54..5dd2385df7 100644 --- a/public/language/vi/email.json +++ b/public/language/vi/email.json @@ -1,11 +1,15 @@ { "password-reset-requested": "Yêu cầu tạo lại mật khẩu - %1!", "welcome-to": "Chào mừng đến với %1", + "invite": "Invitation from %1", "greeting_no_name": "Xin chào", "greeting_with_name": "Xin chào %1", "welcome.text1": "Cảm ơn bạn đã đăng ký tại %1!", "welcome.text2": "Để kích hoạt đầy đủ tính năng của tài khoản, chúng tôi cần xác định bạn là chủ của địa chỉ email mà bạn đã đăng ký.", + "welcome.text3": "An administrator has accepted your registration application. You can login with your username/password now.", "welcome.cta": "Nhấn vào đây để xác nhận địa chỉ email", + "invitation.text1": "%1 has invited you to join %2", + "invitation.ctr": "Click here to create your account.", "reset.text1": "Chúng tôi nhận được yêu cầu khởi tạo lại mật khẩu của bạn, rất có thể vì bạn đã quên mất nó. Nếu bạn không gởi yêu cầu, hãy bỏ qua email này.", "reset.text2": "Để đặt lại mật khẩu, hãy click vào liên kết sau:", "reset.cta": "Click vào đây để khởi tạo lại mật khẩu", diff --git a/public/language/vi/error.json b/public/language/vi/error.json index 33da9ba7ce..7d1a530248 100644 --- a/public/language/vi/error.json +++ b/public/language/vi/error.json @@ -51,6 +51,7 @@ "already-favourited": "Bạn đã bấm yêu thích cho bài viết này rồi", "already-unfavourited": "Bạn đã bỏ thích bài này rồi", "cant-ban-other-admins": "Bạn không thể ban được các admin khác", + "cant-remove-last-admin": "You are the only administrator. Add another user as an administrator before removing yourself as admin", "invalid-image-type": "Invalid image type. Allowed types are: %1", "invalid-image-extension": "Invalid image extension", "invalid-file-type": "Invalid file type. Allowed types are: %1", @@ -59,6 +60,8 @@ "group-name-change-not-allowed": "Không cho phép đổi tên nhóm", "group-already-member": "You are already part of this group", "group-needs-owner": "This group requires at least one owner", + "group-already-invited": "This user has already been invited", + "group-already-requested": "Your membership request has already been submitted", "post-already-deleted": "Bài viết này đã bị xóa", "post-already-restored": "Bài viết này đã được phục hồi", "topic-already-deleted": "Chủ đề này đã bị xóa", @@ -76,6 +79,7 @@ "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", "not-enough-reputation-to-flag": "Bạn không đủ tín nhiệm để đánh dấu bài viết này", + "already-flagged": "You have already flagged this post", "reload-failed": "NodeBB gặp lỗi trong khi tải lại: \"%1\". NodeBB sẽ tiếp tục hoạt động với dữ liệu trước đó, tuy nhiên bạn nên tháo gỡ những gì bạn vừa thực hiện trước khi tải lại.", "registration-error": "Lỗi đăng kí", "parse-error": "Something went wrong while parsing server response", diff --git a/public/language/vi/global.json b/public/language/vi/global.json index a1a8420d6e..f3dc17b93d 100644 --- a/public/language/vi/global.json +++ b/public/language/vi/global.json @@ -50,6 +50,7 @@ "views": "Số lượt view", "reputation": "Độ uy tín", "read_more": "Đọc thêm", + "more": "More", "posted_ago_by_guest": "Đã viết %1 bởi Khách", "posted_ago_by": "Đã viết %1 bởi %2", "posted_ago": "Đã viết %1", diff --git a/public/language/vi/groups.json b/public/language/vi/groups.json index 8122f449cd..c8bdd3bdc9 100644 --- a/public/language/vi/groups.json +++ b/public/language/vi/groups.json @@ -6,6 +6,12 @@ "no_groups_found": "Không có nhóm nào để hiển thị", "pending.accept": "Chấp nhận", "pending.reject": "Từ chối", + "pending.accept_all": "Accept All", + "pending.reject_all": "Reject All", + "pending.none": "There are no pending members at this time", + "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", "cover-instructions": "Drag and Drop a photo, drag to position, and hit Save", "cover-change": "Thay đổi", "cover-save": "Lưu", @@ -13,6 +19,7 @@ "details.title": "Thông tin nhóm", "details.members": "Danh sách thành viên", "details.pending": "Pending Members", + "details.invited": "Invited Members", "details.has_no_posts": "Nhóm thành viên này chưa viết bài viết nào.", "details.latest_posts": "Bài mới nhất", "details.private": "Private", diff --git a/public/language/vi/notifications.json b/public/language/vi/notifications.json index d218247ba7..d4fa621186 100644 --- a/public/language/vi/notifications.json +++ b/public/language/vi/notifications.json @@ -20,6 +20,7 @@ "user_posted_topic": "%1 đã gởi chủ đề mới ở %2", "user_mentioned_you_in": "%1 nhắc đến bạn trong %2", "user_started_following_you": "%1 đã theo dõi bạn.", + "new_register": "%1 sent a registration request.", "email-confirmed": "Đã xác nhận email", "email-confirmed-message": "Cảm ơn bạn đã xác nhận địa chỉ email của bạn. Tài khoản của bạn đã được kích hoạt đầy đủ.", "email-confirm-error-message": "Đã có lỗi khi xác nhận địa chỉ email. Có thể đoạn mã không đúng hoặc đã hết hạn.", diff --git a/public/language/vi/register.json b/public/language/vi/register.json index 848cfdafa5..b24f67d78e 100644 --- a/public/language/vi/register.json +++ b/public/language/vi/register.json @@ -14,5 +14,6 @@ "register_now_button": "Đăng ký ngay", "alternative_registration": "Đăng ký tài khoản khác", "terms_of_use": "Điều khoản sử dụng", - "agree_to_terms_of_use": "Tôi đồng ý với các điều khoản sử dụng" + "agree_to_terms_of_use": "Tôi đồng ý với các điều khoản sử dụng", + "registration-added-to-queue": "Your registration has been added to the approval queue. You will receive an email when it is accepted by an administrator." } \ No newline at end of file diff --git a/public/language/vi/unread.json b/public/language/vi/unread.json index 634efe34d4..049d36dba3 100644 --- a/public/language/vi/unread.json +++ b/public/language/vi/unread.json @@ -5,5 +5,6 @@ "mark_as_read": "Đánh dấu đã đọc", "selected": "Đã chọn", "all": "Tất cả", + "all_categories": "All categories", "topics_marked_as_read.success": "Chủ đề được đánh dấu đã đọc" } \ No newline at end of file diff --git a/public/language/vi/user.json b/public/language/vi/user.json index 5d60c007a1..bef9123115 100644 --- a/public/language/vi/user.json +++ b/public/language/vi/user.json @@ -6,8 +6,12 @@ "postcount": "Số bài viết", "email": "Email", "confirm_email": "Xác nhận email", + "ban_account": "Ban Account", + "ban_account_confirm": "Do you really want to ban this user?", + "unban_account": "Unban Account", "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

", "fullname": "Tên đầy đủ", "website": "Website", "location": "Địa điểm", @@ -64,9 +68,9 @@ "settings-require-reload": "Một số thay đổi trong cài đặt đòi hỏi tải lại. Nhấn vào đây để tải lại trang.", "has_no_follower": "Người dùng này hiện chưa có ai theo dõi :(", "follows_no_one": "Người dùng này hiện chưa theo dõi ai :(", - "has_no_posts": "Người dùng này chưa viết bài nào", - "has_no_topics": "Người dùng này chưa tạo một chủ đề nào", - "has_no_watched_topics": "Thành viên này chưa theo dõi chủ đề nào. ", + "has_no_posts": "This user hasn't posted anything yet.", + "has_no_topics": "This user hasn't posted any topics yet.", + "has_no_watched_topics": "This user hasn't watched any topics yet.", "email_hidden": "Ẩn Email", "hidden": "Đã ẩn", "paginate_description": "Paginate topics and posts instead of using infinite scroll", diff --git a/public/language/vi/users.json b/public/language/vi/users.json index 5e08193fd4..9da6a2e3f1 100644 --- a/public/language/vi/users.json +++ b/public/language/vi/users.json @@ -8,5 +8,14 @@ "users-found-search-took": "%1 user(s) found! Search took %2 seconds.", "filter-by": "Filter By", "online-only": "Online only", - "picture-only": "Picture only" + "picture-only": "Picture only", + "invite": "Invite", + "invitation-email-sent": "An invitation email has been sent to %1", + "user_list": "User List", + "recent_topics": "Recent Topics", + "popular_topics": "Popular Topics", + "unread_topics": "Unread Topics", + "categories": "Categories", + "tags": "Tags", + "map": "Map" } \ No newline at end of file diff --git a/public/language/zh_CN/category.json b/public/language/zh_CN/category.json index 14f4a36667..8024d249a1 100644 --- a/public/language/zh_CN/category.json +++ b/public/language/zh_CN/category.json @@ -1,12 +1,12 @@ { "new_topic_button": "新主题", - "guest-login-post": "登陆后发表", + "guest-login-post": "登录后发表", "no_topics": "此版块还没有任何内容。
赶紧来发帖吧!", "browsing": "正在浏览", "no_replies": "尚无回复", "share_this_category": "分享此版块", - "watch": "订阅", + "watch": "关注", "ignore": "忽略", - "watch.message": "您现在已经订阅了此版块", - "ignore.message": "您现在已经取消订阅了此版块" + "watch.message": "您现在已经关注了此版块", + "ignore.message": "您现在已经取消了此版块的关注" } \ No newline at end of file diff --git a/public/language/zh_CN/email.json b/public/language/zh_CN/email.json index f25a53cd83..6237ef225a 100644 --- a/public/language/zh_CN/email.json +++ b/public/language/zh_CN/email.json @@ -1,11 +1,15 @@ { "password-reset-requested": "密码重置申请 - %1!", "welcome-to": "欢迎来到 %1", + "invite": "Invitation from %1", "greeting_no_name": "您好", "greeting_with_name": "%1,您好", "welcome.text1": "感谢您注册 %1 帐户!", "welcome.text2": "需要在校验您注册时填写的电子邮箱地址后,才能全面激活您的帐户。", + "welcome.text3": "An administrator has accepted your registration application. You can login with your username/password now.", "welcome.cta": "点击这里确认您的电子邮箱地址", + "invitation.text1": "%1 has invited you to join %2", + "invitation.ctr": "Click here to create your account.", "reset.text1": "可能由于您忘记了密码,我们收到了重置您帐户密码的申请。 如果您没有提交密码重置的请求,请忽略这封邮件。", "reset.text2": "如需继续重置密码,请点击下面的链接:", "reset.cta": "点击这里重置您的密码", diff --git a/public/language/zh_CN/error.json b/public/language/zh_CN/error.json index 2aaa30f215..2b9c49f96a 100644 --- a/public/language/zh_CN/error.json +++ b/public/language/zh_CN/error.json @@ -1,6 +1,6 @@ { "invalid-data": "无效数据", - "not-logged-in": "您还没有登陆。", + "not-logged-in": "您还没有登录。", "account-locked": "您的帐号已被临时锁定", "search-requires-login": "搜索功能仅限会员使用 - 请先登录或者注册。", "invalid-cid": "无效版块 ID", @@ -15,51 +15,54 @@ "invalid-username-or-password": "请确认用户名和密码", "invalid-search-term": "无效的搜索关键字", "invalid-pagination-value": "无效页码", - "username-taken": "用户名已被占用", - "email-taken": "电子邮箱已被占用", + "username-taken": "此用户名已被占用", + "email-taken": "此电子邮箱已被占用", "email-not-confirmed": "您的电子邮箱尚未确认,请点击这里确认您的电子邮箱。", "email-not-confirmed-chat": "您的电子邮箱尚未确认,无法聊天,请点击这里确认您的电子邮箱。", - "no-email-to-confirm": "本论坛需要电子邮箱确认,请点击这里输入一个电子邮箱地址", + "no-email-to-confirm": "本论坛需要电子邮箱确认,请点击这里输入电子邮箱地址", "email-confirm-failed": "我们无法确认您的电子邮箱,请重试", "confirm-email-already-sent": "确认邮件已发出,如需重新发送请等待 %1 分钟后再试。", "username-too-short": "用户名太短", "username-too-long": "用户名太长", "user-banned": "用户已禁止", "user-too-new": "抱歉,您需要等待 %1 秒后,才可以发帖!", - "no-category": "版面不存在", + "no-category": "版块不存在", "no-topic": "主题不存在", "no-post": "帖子不存在", "no-group": "用户组不存在", "no-user": "用户不存在", "no-teaser": "主题预览不存在", - "no-privileges": "您没有足够的权限执行此操作。", - "no-emailers-configured": "未加载任何电子邮箱插件,无法发送测试邮件", + "no-privileges": "您没有权限执行此操作。", + "no-emailers-configured": "因未配置电子邮箱插件,无法发送测试邮件", "category-disabled": "版块已禁用", "topic-locked": "主题已锁定", "post-edit-duration-expired": "您必须在发表 %1 秒后才能修改内容", "still-uploading": "请等待上传完成", - "content-too-short": "请再输入一些内容,帖子至少要有 %1 个字符。", - "content-too-long": "请减少帖子字数。字数不能超过 %1 个字符。", - "title-too-short": "请再输入一些内容,标题至少要有 %1 个字符。", - "title-too-long": "请输入更短的标题。不超过 %1 字。", - "too-many-posts": "发帖间隔至少要 %1 秒 - 请稍候再发帖", - "too-many-posts-newbie": "作为新用户,您必须每隔 %1 秒才能发帖一次,直到您有 %2 点威望为止 —— 请稍候再发帖", - "tag-too-short": "标签长度过短。标签长度不能少于 %1 个字符", - "tag-too-long": "标签长度过长,标签长度不能超过 %1 个字符", - "file-too-big": "上传文件的大小限制为 %1 kb - 请上传更小的文件", + "content-too-short": "请增添发帖内容,不能少于 %1 个字符。", + "content-too-long": "请删减发帖内容,不能超过 %1 个字符。", + "title-too-short": "请增加标题,不能少于 %1 个字符。", + "title-too-long": "请缩减标题,不超过 %1 个字符。", + "too-many-posts": "发帖需要间隔 %1 秒以上 - 请稍候再发帖", + "too-many-posts-newbie": "因为您是新用户,所以限制每隔 %1 秒才能发帖一次,直到您有 %2 点威望为止 —— 请稍候再发帖", + "tag-too-short": "话题太短,不能少于 %1 个字符", + "tag-too-long": "话题太长,不能超过 %1 个字符", + "file-too-big": "上传文件的大小限制为 %1 KB - 请缩减文件大小", "cant-vote-self-post": "您不能给自己的帖子投票。", "already-favourited": "您已收藏该帖", "already-unfavourited": "您已取消收藏此帖", - "cant-ban-other-admins": "您不能禁止其他管理员!", + "cant-ban-other-admins": "您不能封禁其他管理员!", + "cant-remove-last-admin": "You are the only administrator. Add another user as an administrator before removing yourself as admin", "invalid-image-type": "无效的图像类型。允许的类型有:%1", "invalid-image-extension": "无效的图像扩展", "invalid-file-type": "无效文件格式,允许的格式有:%1", - "group-name-too-short": "用户组名称太短", - "group-already-exists": "用户组已存在", - "group-name-change-not-allowed": "不允许更改用户组名称", - "group-already-member": "您已经是此群组的成员", - "group-needs-owner": "此群组需要设立至少一名组长", - "post-already-deleted": "此帖子已被删除", + "group-name-too-short": "小组名太短", + "group-already-exists": "小组已存在", + "group-name-change-not-allowed": "不允许更改小组名称", + "group-already-member": "您已经是此小组的成员", + "group-needs-owner": "小组需要指定至少一名组长", + "group-already-invited": "This user has already been invited", + "group-already-requested": "Your membership request has already been submitted", + "post-already-deleted": "此帖已被删除", "post-already-restored": "此帖已经恢复", "topic-already-deleted": "此主题已被删除", "topic-already-restored": "此主题已恢复", @@ -73,12 +76,13 @@ "chat-restricted": "此用户限制了他的聊天消息。必须他先关注您,您才能和他聊天。", "too-many-messages": "您发送了太多消息,请稍等片刻。", "reputation-system-disabled": "威望系统已禁用。", - "downvoting-disabled": "反对功能已禁用", - "not-enough-reputation-to-downvote": "您还没有足够的威望为此帖扣分", - "not-enough-reputation-to-flag": "您没有足够的威望标记此帖", - "reload-failed": "NodeBB 重新加载时遇到问题: \"%1\"。NodeBB 会继续给已存在的客户端组件服务,虽然您应该撤销在重新加载前执行的操作。", + "downvoting-disabled": "扣分功能已禁用", + "not-enough-reputation-to-downvote": "您的威望不足以给此帖扣分", + "not-enough-reputation-to-flag": "您的威望不足以举报此帖", + "already-flagged": "You have already flagged this post", + "reload-failed": "刷新 NodeBB 时遇到问题: \"%1\"。NodeBB 保持给已连接的客户端服务,您应该撤销刷新前做的更改。", "registration-error": "注册错误", - "parse-error": "解析服务器响应时出错", + "parse-error": "服务器响应解析出错", "wrong-login-type-email": "请输入您的电子邮箱地址登录", "wrong-login-type-username": "请输入您的用户名登录" } \ No newline at end of file diff --git a/public/language/zh_CN/global.json b/public/language/zh_CN/global.json index ad4569e538..f60b836187 100644 --- a/public/language/zh_CN/global.json +++ b/public/language/zh_CN/global.json @@ -39,10 +39,10 @@ "nextpage": "下一页", "alert.success": "成功", "alert.error": "错误", - "alert.banned": "禁止", - "alert.banned.message": "您刚刚被禁止,现在您将退出登录。", + "alert.banned": "封禁", + "alert.banned.message": "您刚刚被封,现在您将退出登录。", "alert.unfollow": "您已取消关注 %1!", - "alert.follow": "您正在关注 %1!", + "alert.follow": "您已关注 %1!", "online": "在线", "users": "会员", "topics": "主题", @@ -50,6 +50,7 @@ "views": "浏览", "reputation": "威望", "read_more": "阅读更多", + "more": "More", "posted_ago_by_guest": "游客发布于 %1", "posted_ago_by": "%2 发布于 %1", "posted_ago": "发布于 %1", @@ -73,7 +74,7 @@ "guest": "游客", "guests": "游客", "updated.title": "论坛已更新", - "updated.message": "论坛已更新到最新版本。点这里刷新页面。", + "updated.message": "论坛已更新。请点这里刷新页面。", "privacy": "隐私", "follow": "关注", "unfollow": "取消关注", diff --git a/public/language/zh_CN/groups.json b/public/language/zh_CN/groups.json index 821707c77f..ff95775343 100644 --- a/public/language/zh_CN/groups.json +++ b/public/language/zh_CN/groups.json @@ -1,36 +1,43 @@ { - "groups": "用户组", - "view_group": "查看用户组", - "owner": "用户组组长", - "new_group": "创建新用户组", - "no_groups_found": "还没有用户组", + "groups": "小组", + "view_group": "查看小组", + "owner": "组长", + "new_group": "创建小组", + "no_groups_found": "尚无小组信息", "pending.accept": "接受", "pending.reject": "拒绝", - "cover-instructions": "拖放照片,拖动位置,然后点击 保存", - "cover-change": "变更", + "pending.accept_all": "Accept All", + "pending.reject_all": "Reject All", + "pending.none": "There are no pending members at this time", + "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", + "cover-instructions": "拖放照片到此位置,然后点击 保存", + "cover-change": "更改", "cover-save": "保存", "cover-saving": "正在保存", - "details.title": "用户组详情", - "details.members": "会员列表", - "details.pending": "预备成员", - "details.has_no_posts": "此用户组的会员尚未发表任何帖子。", + "details.title": "小组信息", + "details.members": "成员列表", + "details.pending": "待加入成员", + "details.invited": "Invited Members", + "details.has_no_posts": "此小组的会员尚未发表任何帖子。", "details.latest_posts": "最新帖子", "details.private": "私有", - "details.grant": "授予/取消所有权", - "details.kick": "踢", - "details.owner_options": "用户组管理", - "details.group_name": "用户组名", - "details.member_count": "会员总数", + "details.grant": "授予/取消管理权", + "details.kick": "踢出小组", + "details.owner_options": "小组管理", + "details.group_name": "小组名", + "details.member_count": "小组成员数", "details.creation_date": "创建时间", "details.description": "描述", - "details.badge_preview": "标志预览", + "details.badge_preview": "徽章预览", "details.change_icon": "更改图标", - "details.change_colour": "更新颜色", - "details.badge_text": "标志文字", - "details.userTitleEnabled": "显示标志", - "details.private_help": "如果条件允许,必须得到群主批准才能加入该群。", + "details.change_colour": "更改颜色", + "details.badge_text": "徽章文本", + "details.userTitleEnabled": "显示组内称号", + "details.private_help": "启用此选项后,加入小组需要组长审批。", "details.hidden": "隐藏", - "details.hidden_help": "如果条件允许,这个群不再出现在此列表,所有用户要人工邀请加入。", - "event.updated": "用户组信息已更新", - "event.deleted": "用户组 \"%1\" 已被删除" + "details.hidden_help": "启用此选项后,小组将不在小组列表中展现,成员只能通过邀请加入。", + "event.updated": "小组信息已更新", + "event.deleted": "小组 \"%1\" 已被删除" } \ No newline at end of file diff --git a/public/language/zh_CN/notifications.json b/public/language/zh_CN/notifications.json index f20ee0f1e4..b9e8681302 100644 --- a/public/language/zh_CN/notifications.json +++ b/public/language/zh_CN/notifications.json @@ -20,6 +20,7 @@ "user_posted_topic": "%1 发表了新主题:%2", "user_mentioned_you_in": "%1%2 中提到了您", "user_started_following_you": "%1关注了您。", + "new_register": "%1 sent a registration request.", "email-confirmed": "电子邮箱已确认", "email-confirmed-message": "感谢您验证您的电子邮箱。您的帐户现已全面激活。", "email-confirm-error-message": "验证您电子邮箱地址时出现了问题。可能是因为验证码无效或已过期。", diff --git a/public/language/zh_CN/register.json b/public/language/zh_CN/register.json index f5ebf122e3..f258edac2b 100644 --- a/public/language/zh_CN/register.json +++ b/public/language/zh_CN/register.json @@ -14,5 +14,6 @@ "register_now_button": "马上注册", "alternative_registration": "其他方式注册", "terms_of_use": "使用条款", - "agree_to_terms_of_use": "我同意使用条款" + "agree_to_terms_of_use": "我同意使用条款", + "registration-added-to-queue": "Your registration has been added to the approval queue. You will receive an email when it is accepted by an administrator." } \ No newline at end of file diff --git a/public/language/zh_CN/topic.json b/public/language/zh_CN/topic.json index 2c12d8949c..655bf46f55 100644 --- a/public/language/zh_CN/topic.json +++ b/public/language/zh_CN/topic.json @@ -26,19 +26,19 @@ "flag": "标记", "locked": "已锁定", "bookmark_instructions": "点击这里返回您最后浏览的位置。", - "flag_title": "对此帖设置管理标志", - "flag_confirm": "确认给此帖设置标记吗?", - "flag_success": "此回帖已设置管理标记。", + "flag_title": "举报", + "flag_confirm": "您确认要举报此帖吗?", + "flag_success": "已举报此回帖。", "deleted_message": "此主题已被删除。只有拥有主题管理权限的用户可以查看。", - "following_topic.message": "当有人回复此主题时您会收到通知。", + "following_topic.message": "当有人回复此主题时,您会收到通知。", "not_following_topic.message": "您已停止接收此主题的通知。", - "login_to_subscribe": "请注册或登录后再订阅此主题。", + "login_to_subscribe": "请注册或登录后,再订阅此主题。", "markAsUnreadForAll.success": "将全部主题标为未读。", "watch": "关注", "unwatch": "取消关注", - "watch.title": "此主题有新回复时通知我", + "watch.title": "当此主题有新回复时,通知我", "unwatch.title": "取消关注此主题", - "share_this_post": "分享此帖", + "share_this_post": "分享", "thread_tools.title": "主题工具", "thread_tools.markAsUnreadForAll": "标记为未读", "thread_tools.pin": "置顶主题", @@ -64,7 +64,7 @@ "confirm_fork": "分割", "favourite": "收藏", "favourites": "收藏", - "favourites.has_no_favourites": "您还没有任何收藏,收藏的帖子会在这里展示!", + "favourites.has_no_favourites": "您还没有任何收藏,收藏后的帖子会显示在这里!", "loading_more_posts": "正在加载更多帖子", "move_topic": "移动主题", "move_topics": "移动主题", diff --git a/public/language/zh_CN/unread.json b/public/language/zh_CN/unread.json index d6ccb65baa..8add96ac03 100644 --- a/public/language/zh_CN/unread.json +++ b/public/language/zh_CN/unread.json @@ -5,5 +5,6 @@ "mark_as_read": "标为已读", "selected": "已选", "all": "全部", + "all_categories": "All categories", "topics_marked_as_read.success": "主题被标为已读!" } \ No newline at end of file diff --git a/public/language/zh_CN/user.json b/public/language/zh_CN/user.json index ea8944d323..8c00784aa7 100644 --- a/public/language/zh_CN/user.json +++ b/public/language/zh_CN/user.json @@ -6,8 +6,12 @@ "postcount": "发帖数", "email": "电子邮件", "confirm_email": "确认电子邮箱", + "ban_account": "Ban Account", + "ban_account_confirm": "Do you really want to ban this user?", + "unban_account": "Unban Account", "delete_account": "删除帐号", - "delete_account_confirm": "确认要删除您的帐户吗?
此操作是不可逆转的,您会无法恢复您的任何数据

请输入您的用户名,确认您想要删除此帐户。", + "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

", "fullname": "姓名", "website": "网站", "location": "位置", @@ -59,14 +63,14 @@ "digest_daily": "每天", "digest_weekly": "每周", "digest_monthly": "每月", - "send_chat_notifications": "当我不在线,并受到新的聊天消息时给我发邮件", - "send_post_notifications": "我订阅的主题有回复时发送邮件", - "settings-require-reload": "一些设置变更需要刷新页面。点击这里刷新页面。", + "send_chat_notifications": "当我不在线并收到新的聊天消息时,给我发送邮件通知", + "send_post_notifications": "当我订阅的主题有新回复时,给我发送邮件通知", + "settings-require-reload": "某些设置变更需要刷新页面。点击这里刷新页面。", "has_no_follower": "此用户还没有粉丝 :(", "follows_no_one": "此用户尚未关注任何人 :(", - "has_no_posts": "此用户尚未发布任何帖子。", - "has_no_topics": "此用户还未发布任何主题。", - "has_no_watched_topics": "此用户还未订阅任何主题", + "has_no_posts": "This user hasn't posted anything yet.", + "has_no_topics": "This user hasn't posted any topics yet.", + "has_no_watched_topics": "This user hasn't watched any topics yet.", "email_hidden": "电子邮箱已隐藏", "hidden": "隐藏", "paginate_description": "使用分页式版块浏览", @@ -76,9 +80,9 @@ "browsing": "浏览设置", "open_links_in_new_tab": "在新标签打开外部链接", "enable_topic_searching": "启用主题内搜索", - "topic_search_help": "如果启用,主题内搜索会替代浏览器默认的网页搜索,使你可以在整个主题内搜索,而不仅仅时页面上展现的内容。", - "follow_topics_you_reply_to": "关注你回复的主题", - "follow_topics_you_create": "关注你创建的主题", - "grouptitle": "选择展现的组内称号", - "no-group-title": "无组内头衔" + "topic_search_help": "如果启用此项,主题内搜索会替代浏览器默认的页面搜索,您将可以在整个主题内搜索,而不仅仅只搜索页面上展现的内容。", + "follow_topics_you_reply_to": "关注您回复的主题", + "follow_topics_you_create": "关注您创建的主题", + "grouptitle": "选择展示的小组称号", + "no-group-title": "不展示小组称号" } \ No newline at end of file diff --git a/public/language/zh_CN/users.json b/public/language/zh_CN/users.json index ef7d6dd15b..9a4c25724e 100644 --- a/public/language/zh_CN/users.json +++ b/public/language/zh_CN/users.json @@ -8,5 +8,14 @@ "users-found-search-took": "找到 %1 位用户!搜索耗时 %2 毫秒。", "filter-by": "过滤选项", "online-only": "只看在线", - "picture-only": "只看图片" + "picture-only": "只看图片", + "invite": "邀请注册", + "invitation-email-sent": "已发送邀请给 %1", + "user_list": "会员列表", + "recent_topics": "最新主题", + "popular_topics": "热门主题", + "unread_topics": "未读主题", + "categories": "版面", + "tags": "话题", + "map": "地图" } \ No newline at end of file diff --git a/public/language/zh_TW/email.json b/public/language/zh_TW/email.json index 13f19de484..e32d965ecf 100644 --- a/public/language/zh_TW/email.json +++ b/public/language/zh_TW/email.json @@ -1,11 +1,15 @@ { "password-reset-requested": "已要求重設密碼 - %1!", "welcome-to": "歡迎來到%1", + "invite": "Invitation from %1", "greeting_no_name": "您好", "greeting_with_name": "您好,%1", "welcome.text1": "多謝登記%1!", "welcome.text2": "要啟用你的帳戶,我們先要驗證你用作登記的電郵地址", + "welcome.text3": "An administrator has accepted your registration application. You can login with your username/password now.", "welcome.cta": "請點擊此處確認您的電郵地址", + "invitation.text1": "%1 has invited you to join %2", + "invitation.ctr": "Click here to create your account.", "reset.text1": "我們收到一個重設密碼的請求,你忘掉了密碼嗎?如果不是,請忽略這封郵件。", "reset.text2": "要繼續重置密碼,請點擊以下鏈接:", "reset.cta": "點擊這裡重置密碼", diff --git a/public/language/zh_TW/error.json b/public/language/zh_TW/error.json index d2f4f0806a..638b6cdf27 100644 --- a/public/language/zh_TW/error.json +++ b/public/language/zh_TW/error.json @@ -51,6 +51,7 @@ "already-favourited": "你已經收藏了這篇文章", "already-unfavourited": "你已放棄收藏這篇文章", "cant-ban-other-admins": "您無法禁止其他的管理員!", + "cant-remove-last-admin": "You are the only administrator. Add another user as an administrator before removing yourself as admin", "invalid-image-type": "無效的圖像類型。允許的類型:%1", "invalid-image-extension": "無效的圖像擴充元件", "invalid-file-type": "無效的檔案類型。允許的類型:%1", @@ -59,6 +60,8 @@ "group-name-change-not-allowed": "變更群組名稱不被允許", "group-already-member": "You are already part of this group", "group-needs-owner": "This group requires at least one owner", + "group-already-invited": "This user has already been invited", + "group-already-requested": "Your membership request has already been submitted", "post-already-deleted": "此文章已經被刪除", "post-already-restored": "此文章已還原", "topic-already-deleted": "此主題已經被刪除", @@ -76,6 +79,7 @@ "downvoting-disabled": "Downvoting已停用", "not-enough-reputation-to-downvote": "你沒有足夠的信譽downvote這個帖子", "not-enough-reputation-to-flag": "你沒有足夠的信譽來舉報這個帖子", + "already-flagged": "You have already flagged this post", "reload-failed": "NodeBB重載\"%1\"時遇到了問題。 NodeBB將繼續提供現有的客戶端資源,但請你撤消重載前的動作。", "registration-error": "註冊錯誤", "parse-error": "Something went wrong while parsing server response", diff --git a/public/language/zh_TW/global.json b/public/language/zh_TW/global.json index 6014f8c4ac..2097e3bb26 100644 --- a/public/language/zh_TW/global.json +++ b/public/language/zh_TW/global.json @@ -50,6 +50,7 @@ "views": "Views", "reputation": "聲譽", "read_more": "閱讀更多...", + "more": "More", "posted_ago_by_guest": "posted %1 by Guest", "posted_ago_by": "posted %1 by %2", "posted_ago": "posted %1", diff --git a/public/language/zh_TW/groups.json b/public/language/zh_TW/groups.json index d4ac57f556..590e2118da 100644 --- a/public/language/zh_TW/groups.json +++ b/public/language/zh_TW/groups.json @@ -6,6 +6,12 @@ "no_groups_found": "這裡看不到任何群組", "pending.accept": "接受", "pending.reject": "拒絕", + "pending.accept_all": "Accept All", + "pending.reject_all": "Reject All", + "pending.none": "There are no pending members at this time", + "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", "cover-instructions": "拖拉一本相簿,並拖到此位置,以及點擊 儲存", "cover-change": "變更", "cover-save": "儲存", @@ -13,6 +19,7 @@ "details.title": "群組詳細信息", "details.members": "成員列表", "details.pending": "待審成員", + "details.invited": "Invited Members", "details.has_no_posts": "這個群組的成員還未發出任何帖子。", "details.latest_posts": "最新文章", "details.private": "私人", diff --git a/public/language/zh_TW/notifications.json b/public/language/zh_TW/notifications.json index 660e984111..0bd309b17f 100644 --- a/public/language/zh_TW/notifications.json +++ b/public/language/zh_TW/notifications.json @@ -20,6 +20,7 @@ "user_posted_topic": "%1 發布了一個新的主題: %2", "user_mentioned_you_in": "%1%2提到你", "user_started_following_you": "%1 開始關注你。", + "new_register": "%1 sent a registration request.", "email-confirmed": "已確認電郵", "email-confirmed-message": "感謝您驗證您的電郵。您的帳戶現已全面啟用。", "email-confirm-error-message": "驗證您的電郵地址時出現問題。也許啟動碼無效或已過期。", diff --git a/public/language/zh_TW/register.json b/public/language/zh_TW/register.json index f1bb3b993d..e690779ae8 100644 --- a/public/language/zh_TW/register.json +++ b/public/language/zh_TW/register.json @@ -14,5 +14,6 @@ "register_now_button": "現在註冊", "alternative_registration": "其他註冊方式", "terms_of_use": "使用條款", - "agree_to_terms_of_use": "同意遵守使用條款" + "agree_to_terms_of_use": "同意遵守使用條款", + "registration-added-to-queue": "Your registration has been added to the approval queue. You will receive an email when it is accepted by an administrator." } \ No newline at end of file diff --git a/public/language/zh_TW/unread.json b/public/language/zh_TW/unread.json index ed401a8fb3..b8e2086168 100644 --- a/public/language/zh_TW/unread.json +++ b/public/language/zh_TW/unread.json @@ -5,5 +5,6 @@ "mark_as_read": "標記成已讀", "selected": "已選擇", "all": "全部", + "all_categories": "All categories", "topics_marked_as_read.success": "標記主題成已讀!" } \ No newline at end of file diff --git a/public/language/zh_TW/user.json b/public/language/zh_TW/user.json index 7296da0c9f..e8532b0d58 100644 --- a/public/language/zh_TW/user.json +++ b/public/language/zh_TW/user.json @@ -6,8 +6,12 @@ "postcount": "文章數量", "email": "電子郵件", "confirm_email": "確認電郵", + "ban_account": "Ban Account", + "ban_account_confirm": "Do you really want to ban this user?", + "unban_account": "Unban Account", "delete_account": "刪除帳戶", "delete_account_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

", "fullname": "全名", "website": "網站", "location": "地址", @@ -64,9 +68,9 @@ "settings-require-reload": "Some setting changes require a reload. Click here to reload the page.", "has_no_follower": "該用戶還沒有被任何人關注。", "follows_no_one": "該用戶還沒有關注過任何人。", - "has_no_posts": "尚未有任何貼文.", - "has_no_topics": "這位使用者尚未發表任何主題。", - "has_no_watched_topics": "這位使用者尚未發表任何主題", + "has_no_posts": "This user hasn't posted anything yet.", + "has_no_topics": "This user hasn't posted any topics yet.", + "has_no_watched_topics": "This user hasn't watched any topics yet.", "email_hidden": "郵箱被隱藏", "hidden": "隱藏", "paginate_description": "Paginate topics and posts instead of using infinite scroll", diff --git a/public/language/zh_TW/users.json b/public/language/zh_TW/users.json index 34f7db93c9..783eb62781 100644 --- a/public/language/zh_TW/users.json +++ b/public/language/zh_TW/users.json @@ -8,5 +8,14 @@ "users-found-search-took": "發現 %1 用戶!搜尋只用 %2 秒。", "filter-by": "Filter By", "online-only": "線上僅有", - "picture-only": "圖片僅有" + "picture-only": "圖片僅有", + "invite": "Invite", + "invitation-email-sent": "An invitation email has been sent to %1", + "user_list": "User List", + "recent_topics": "Recent Topics", + "popular_topics": "Popular Topics", + "unread_topics": "Unread Topics", + "categories": "Categories", + "tags": "Tags", + "map": "Map" } \ No newline at end of file diff --git a/public/less/admin/admin.less b/public/less/admin/admin.less index 40ddcf08e3..429a37de86 100644 --- a/public/less/admin/admin.less +++ b/public/less/admin/admin.less @@ -1,9 +1,10 @@ @import "./bootstrap/bootstrap"; @import "./mixins"; +@import "./vars"; @import "./general/dashboard"; @import "./general/navigation"; -@import "./manage/groups"; +@import "./manage/categories"; @import "./manage/tags"; @import "./manage/flags"; @import "./manage/users"; @@ -15,6 +16,7 @@ @import "./modules/alerts"; @import "./modules/selectable"; +@import "./modules/checkboxes"; .admin { padding-top: 70px; @@ -25,6 +27,14 @@ padding: 0px 15px; } + .jumbotron { + padding: @jumbotron-padding; + } + + .btn { + border-radius: 0; + } + .user-img { width:24px; height:24px; @@ -135,10 +145,6 @@ padding-left: 11px; outline: 0; - &:focus { - background-color: transparent; - } - span { opacity: 0; margin-right: -8px; @@ -169,6 +175,12 @@ overflow-y: hidden; } + .acp-panel-heading { + padding: 7px 14px; + border: 0; + .box-header-font; + } + .panel { background-color: #FFF; box-sizing: border-box; @@ -177,11 +189,13 @@ margin-bottom: 20px; &.panel-default .panel-heading { + .acp-panel-heading; background: #fefefe; color: #333; - padding: 7px 14px; - border: 0; - .box-header-font; + } + + &.panel-danger .panel-heading { + .acp-panel-heading; } } @@ -213,6 +227,7 @@ .pointer; line-height: 20px; text-align: center; + color: @gray-dark; &:hover, &.selected { background: black; @@ -283,6 +298,11 @@ #taskbar { display: none; /* not sure why I have to do this, but it only seems to show up on prod */ } + + /* Allows the autocomplete dropbox to appear on top of a modal's backdrop */ + .ui-autocomplete { + z-index: @zindex-popover; + } } // Allowing text to the right of an image-type brand @@ -344,4 +364,4 @@ max-width: 24px; max-height: 24px; } -} +} \ No newline at end of file diff --git a/public/less/admin/general/dashboard.less b/public/less/admin/general/dashboard.less index cf44ceb176..c8b704707b 100644 --- a/public/less/admin/general/dashboard.less +++ b/public/less/admin/general/dashboard.less @@ -102,6 +102,10 @@ border-color: #949FB1; background-color: #A8B3C5; } + &.unread { + border-color: #949FB1; + background-color: #9FB194; + } } } } diff --git a/public/less/admin/manage/categories.less b/public/less/admin/manage/categories.less new file mode 100644 index 0000000000..5f8cc4fcd0 --- /dev/null +++ b/public/less/admin/manage/categories.less @@ -0,0 +1,78 @@ +div.categories { + ul { + .no-select; + list-style-type: none; + margin: 0; + padding: 0; + + > li > ul > li { + margin-left: 4.5rem; + } + + .row { + margin-left: -15px; + margin-right: -15px; + border-bottom: 1px dashed #ddd; + padding-bottom: 12px; + margin-bottom: 12px; + } + } + + .stats { + display: inline-block; + + li { + min-height: 0; + display: inline; + margin: 0 @acp-margin 0 0; + left: 0; + } + } + + li { + min-height: @acp-line-height; + margin: @acp-base-line 0; + + &.placeholder { + border: 1px dashed #2196F3; + background-color: #E1F5FE; + } + } + + .disabled { + + .icon, .header, .description { + opacity: 0.5; + } + + .stats { + opacity: 0.3; + } + } + + .icon { + width: @acp-line-height; + height: @acp-line-height; + border-radius: 50%; + line-height: @acp-line-height; + text-align: center; + vertical-align: bottom; + background-size: cover; + float: left; + margin-right: @acp-margin; + cursor: move; + } + + .information { + float: left; + } + + .header { + margin-top: 0; + margin-bottom: @acp-base-line; + } + + .description { + margin: 0; + } +} \ No newline at end of file diff --git a/public/less/admin/manage/groups.less b/public/less/admin/manage/groups.less deleted file mode 100644 index 03f42694d9..0000000000 --- a/public/less/admin/manage/groups.less +++ /dev/null @@ -1,54 +0,0 @@ -.groups { - #groups-list { - padding-left: 0; - - > li { - list-style-type: none; - margin-bottom: 1em; - padding: 1em; - .zebra; - - h2 { - margin-top: 0; - font-size: 26px; - } - } - } - - .members { - padding: 1em; - - &.current_members { - li { - margin-right: 1em; - } - } - - li { - display: inline-block; - border: 1px solid rgb(200, 200, 200); - - img { - width: 32px; - } - - span { - padding: 0 1em; - } - - &:hover { - .pointer; - background: rgba(192, 192, 192, 0.2); - } - - &.more { - width: 34px; - height: 34px; - vertical-align: top; - padding-top: 3px; - position: relative; - left: -4px; - } - } - } -} \ No newline at end of file diff --git a/public/less/admin/modules/checkboxes.less b/public/less/admin/modules/checkboxes.less new file mode 100644 index 0000000000..2abc51d118 --- /dev/null +++ b/public/less/admin/modules/checkboxes.less @@ -0,0 +1,18 @@ +label { + cursor: pointer; +} + +.checkbox label { + padding-left: 2px; +} + +.admin { + .fa-toggle-on, .fa-toggle-off { + font-size: 21px; + vertical-align: -3px; + } + + .fa-toggle-on { + color: @brand-success; + } +} \ No newline at end of file diff --git a/public/less/admin/vars.less b/public/less/admin/vars.less new file mode 100644 index 0000000000..8e7bc2bb30 --- /dev/null +++ b/public/less/admin/vars.less @@ -0,0 +1,3 @@ +@acp-base-line: 8px; +@acp-line-height: @acp-base-line * 6; +@acp-margin: @acp-base-line * 2; \ No newline at end of file diff --git a/public/less/generics.less b/public/less/generics.less index e44d5f0d72..4ce04c4337 100644 --- a/public/less/generics.less +++ b/public/less/generics.less @@ -13,4 +13,31 @@ .opacity(0.5); } } +} + +.user-list { + padding-left: 2rem; + padding-top: 1rem; + + li { + .pointer; + display: inline-block; + list-style-type: none; + padding: 0.5rem 1rem; + + &:hover { + background: @gray-lighter; + } + + img { + float: left; + max-width: 36px; + padding-right: 1rem; + } + + span { + vertical-align: middle; + display: inline-block; + } + } } \ No newline at end of file diff --git a/public/src/admin/admin.js b/public/src/admin/admin.js index f7b7817591..bc6d81d747 100644 --- a/public/src/admin/admin.js +++ b/public/src/admin/admin.js @@ -1,5 +1,5 @@ "use strict"; -/*global define, socket, app, ajaxify, utils, Mousetrap, Hammer, RELATIVE_PATH*/ +/*global define, socket, app, ajaxify, utils, bootbox, Mousetrap, Hammer, RELATIVE_PATH*/ (function() { $(document).ready(function() { @@ -14,7 +14,7 @@ activateMobile(); } - $(window).on('action:ajaxify.end', function(ev, data) { + $(window).on('action:ajaxify.contentLoaded', function(ev, data) { var url = data.url; selectMenuItem(data.url); @@ -22,6 +22,10 @@ setupRestartLinks(); }); + $(window).on('action:admin.settingsLoaded', setupCheckboxes); + + $('[component="logout"]').on('click', app.logout); + $(window).resize(setupHeaderMenu); }); @@ -38,6 +42,7 @@ // move this to admin.config app.config = config; + $(window).trigger('action:config.loaded'); }); function setupMenu() { @@ -63,8 +68,9 @@ function setupKeybindings() { Mousetrap.bind('ctrl+shift+a r', function() { - console.log('[admin] Reloading NodeBB...'); - socket.emit('admin.reload'); + require(['admin/modules/instance'], function(instance) { + instance.reload(); + }); }); Mousetrap.bind('ctrl+shift+a R', function() { @@ -72,15 +78,6 @@ socket.emit('admin.restart'); }); - Mousetrap.bind('ctrl+shift+a d', function() { - var tid = ajaxify.variables.get('topic_id'), - cid = ajaxify.variables.get('category_id'); - - if (tid && cid) { - socket.emit('topics.delete', { tids: [tid], cid: cid }); - } - }); - Mousetrap.bind('/', function(e) { $('#acp-search input').focus(); @@ -161,56 +158,51 @@ $('.restart').off('click').on('click', function() { bootbox.confirm('Are you sure you wish to restart NodeBB?', function(confirm) { if (confirm) { - app.alert({ - alert_id: 'instance_restart', - type: 'info', - title: 'Restarting... ', - message: 'NodeBB is restarting.', - timeout: 5000 + require(['admin/modules/instance'], function(instance) { + instance.restart(); }); - - $(window).one('action:reconnected', function() { - app.alert({ - alert_id: 'instance_restart', - type: 'success', - title: ' Success', - message: 'NodeBB has successfully restarted.', - timeout: 5000 - }); - }); - - socket.emit('admin.restart'); } }); }); $('.reload').off('click').on('click', function() { - app.alert({ - alert_id: 'instance_reload', - type: 'info', - title: 'Reloading... ', - message: 'NodeBB is reloading.', - timeout: 5000 - }); - - socket.emit('admin.reload', function(err) { - if (!err) { - app.alert({ - alert_id: 'instance_reload', - type: 'success', - title: ' Success', - message: 'NodeBB has successfully reloaded.', - timeout: 5000 - }); - } else { - app.alert({ - alert_id: 'instance_reload', - type: 'danger', - title: '[[global:alert.error]]', - message: '[[error:reload-failed, ' + err.message + ']]' - }); - } + require(['admin/modules/instance'], function(instance) { + instance.reload(); }); }); } + + function setupCheckboxes() { + if (ajaxify.currentPage.match(/^admin\/manage\/categories/)) { + return $('[type=checkbox]').show(); + } + + $('[type=checkbox]').change(function() { + var checked = $(this).is(':checked'); + + $(this).siblings('[class*=fa-]').toggleClass('fa-toggle-off', !checked) + .toggleClass('fa-toggle-on', checked); + }); + + $('[type=checkbox]').each(function() { + var checkbox = $(this), + checked = checkbox.is(':checked'); + + if (checkbox.attr('data-toggle-added')) { + return; + } + + checkbox.hide(); + + if (checked) { + checkbox.after(''); + } + else { + checkbox.after(''); + } + + checkbox.attr('data-toggle-added', true); + }); + } + }()); \ No newline at end of file diff --git a/public/src/admin/appearance/skins.js b/public/src/admin/appearance/skins.js index 374b88a7b4..1a24b80ef5 100644 --- a/public/src/admin/appearance/skins.js +++ b/public/src/admin/appearance/skins.js @@ -33,7 +33,7 @@ define('admin/appearance/skins', function() { alert_id: 'admin:theme', type: 'info', title: 'Skin Updated', - message: themeId + ' skin was successfully applied', + message: themeId ? (themeId + ' skin was successfully applied') : 'Skin reverted to base colours', timeout: 5000 }); }); diff --git a/public/src/admin/extend/plugins.js b/public/src/admin/extend/plugins.js index e2484b1f07..c12e9f02d7 100644 --- a/public/src/admin/extend/plugins.js +++ b/public/src/admin/extend/plugins.js @@ -27,7 +27,9 @@ define('admin/extend/plugins', function() { type: status.active ? 'warning' : 'success', timeout: 5000, clickfn: function() { - socket.emit('admin.reload'); + require(['admin/modules/instance'], function(instance) { + instance.reload(); + }); } }); }); @@ -160,7 +162,9 @@ define('admin/extend/plugins', function() { type: 'warning', timeout: 5000, clickfn: function() { - socket.emit('admin.reload'); + require(['admin/modules/instance'], function(instance) { + instance.reload(); + }); } }); } @@ -177,28 +181,32 @@ define('admin/extend/plugins', function() { socket.emit('admin.plugins.toggleInstall', { id: pluginID, version: version - }, function(err, status) { + }, function(err, pluginData) { if (err) { return app.alertError(err.message); } - if (status.installed) { - btn.html(' Uninstall'); - } else { - btn.html(' Install'); - } + var targetList = (pluginData.installed ? 'installed' : 'download'), + otherList = (pluginData.installed ? 'download' : 'installed'), + payload = {}; - activateBtn.toggleClass('hidden', !status.installed); + payload[targetList] = pluginData; + templates.parse('admin/partials/' + targetList + '_plugin_item', payload, function(html) { + var pluginList = $('ul.' + targetList); - btn.toggleClass('btn-danger', status.installed) - .toggleClass('btn-success', !status.installed) - .attr('disabled', false) - .attr('data-installed', status.installed ? 1 : 0); + pluginList.append(html); + $('ul.' + otherList).find('li[data-plugin-id="' + pluginID + '"]').slideUp('slow', function() { + $(this).remove(); + $('html,body').animate({ + scrollTop: pluginList.find('li').last().offset().top - 48 + }, 1000); + }); + }); app.alert({ alert_id: 'plugin_toggled', - title: 'Plugin ' + (status.installed ? 'Installed' : 'Uninstalled'), - message: status.installed ? 'Plugin successfully installed, please activate the plugin.' : 'The plugin has been successfully deactivated and uninstalled.', + title: 'Plugin ' + (pluginData.installed ? 'Installed' : 'Uninstalled'), + message: pluginData.installed ? 'Plugin successfully installed, please activate the plugin.' : 'The plugin has been successfully deactivated and uninstalled.', type: 'info', timeout: 5000 }); diff --git a/public/src/admin/extend/rewards.js b/public/src/admin/extend/rewards.js index 967e098bf7..d93741d76b 100644 --- a/public/src/admin/extend/rewards.js +++ b/public/src/admin/extend/rewards.js @@ -11,10 +11,10 @@ define('admin/extend/rewards', function() { conditionals; rewards.init = function() { - available = JSON.parse(ajaxify.variables.get('rewards')); - active = JSON.parse(ajaxify.variables.get('active')); - conditions = JSON.parse(ajaxify.variables.get('conditions')); - conditionals = JSON.parse(ajaxify.variables.get('conditionals')); + available = ajaxify.data.rewards; + active = ajaxify.data.active; + conditions = ajaxify.data.conditions; + conditionals = ajaxify.data.conditionals; $('[data-selected]').each(function() { select($(this)); @@ -96,7 +96,7 @@ define('admin/extend/rewards', function() { inputs.forEach(function(input) { html += '

  • '); + foundUser + .attr({title: results.users[x].username, 'data-uid': results.users[x].uid}) + .append($('').attr('src', results.users[x].picture)) + .append($('').html(results.users[x].username)); + + groupDetailsSearchResults.append(foundUser); + } + } else { + groupDetailsSearchResults.html('
  • No Users Found
  • '); + } + }); + }, 200); + }); + + groupDetailsSearchResults.on('click', 'li[data-uid]', function() { + var userLabel = $(this), + uid = parseInt(userLabel.attr('data-uid'), 10), + members = []; + + groupMembersEl.find('li[data-uid]').each(function() { + members.push(parseInt($(this).attr('data-uid'), 10)); + }); + + if (members.indexOf(uid) === -1) { + socket.emit('admin.groups.join', { + groupName: groupName, + uid: uid + }, function(err, data) { + if (!err) { + groupMembersEl.append(userLabel.clone(true)); + } + }); + } + }); + + groupMembersEl.on('click', 'li[data-uid]', function() { + var uid = $(this).attr('data-uid'); + + socket.emit('admin.groups.get', groupName, function(err, groupObj){ + if (err) { + return app.alertError(err.message); + } + + bootbox.confirm('Are you sure you want to remove this user?', function(confirm) { + if (!confirm) { + return; + } + + socket.emit('admin.groups.leave', { + groupName: groupName, + uid: uid + }, function(err, data) { + if (err) { + return app.alertError(err.message); + } + groupMembersEl.find('li[data-uid="' + uid + '"]').remove(); + }); + }); + }); + }); + + $('#group-icon').on('click', function() { + iconSelect.init(groupIcon); + }); + + colorpicker.enable(changeGroupLabelColor, function(hsb, hex) { + groupLabelPreview.css('background-color', '#' + hex); + }); + + $('.save').on('click', function() { + socket.emit('admin.groups.update', { + groupName: groupName, + values: { + name: $('#change-group-name').val(), + userTitle: changeGroupUserTitle.val(), + description: $('#change-group-desc').val(), + icon: groupIcon.attr('value'), + labelColor: changeGroupLabelColor.val(), + private: $('#group-private').is(':checked'), + hidden: $('#group-hidden').is(':checked') + } + }, function(err) { + if (err) { + return app.alertError(err.message); + } + app.alertSuccess('Changes saved!'); + }); + return false; + }); + + }; + + return Groups; +}); diff --git a/public/src/admin/manage/groups.js b/public/src/admin/manage/groups.js index f604bc9387..8c68837403 100644 --- a/public/src/admin/manage/groups.js +++ b/public/src/admin/manage/groups.js @@ -2,32 +2,20 @@ /*global define, templates, socket, ajaxify, app, admin, bootbox, utils, config */ define('admin/manage/groups', [ - 'iconSelect', - 'admin/modules/colorpicker', - 'translator' -], function(iconSelect, colorpicker, translator) { + 'translator', + 'components' +], function(translator, components) { var Groups = {}; + var intervalId = 0; + Groups.init = function() { var createModal = $('#create-modal'), createGroupName = $('#create-group-name'), createModalGo = $('#create-modal-go'), - createModalError = $('#create-modal-error'), - groupDetailsModal = $('#group-details-modal'), - groupDetailsSearch = $('#group-details-search'), - groupDetailsSearchResults = $('#group-details-search-results'), - groupMembersEl = $('ul.current_members'), - detailsModalSave = $('#details-modal-save'), - groupIcon = $('#group-icon'), - changeGroupName = $('#change-group-name'), - changeGroupDesc = $('#change-group-desc'), - changeGroupUserTitle = $('#change-group-user-title'), - changeGroupLabelColor = $('#change-group-label-color'), - groupLabelPreview = $('#group-label-preview'), - searchDelay; + createModalError = $('#create-modal-error'); - // Tooltips - $('#groups-list .members li').tooltip(); + handleSearch(); createModal.on('keypress', function(e) { if (e.keyCode === 13) { @@ -69,28 +57,10 @@ define('admin/manage/groups', [ }); }); - groupDetailsModal.find('form').keypress(function(e) { - if (e.keyCode === 13) { - detailsModalSave.click(); - } - }); - - changeGroupUserTitle.keydown(function() { - setTimeout(function() { - groupLabelPreview.text(changeGroupUserTitle.val()); - }, 0); - }); - - changeGroupLabelColor.keydown(function() { - setTimeout(function() { - groupLabelPreview.css('background', changeGroupLabelColor.val() || '#000000'); - }, 0); - }); - - $('#groups-list').on('click', 'button[data-action]', function() { + $('.groups-list').on('click', 'button[data-action]', function() { var el = $(this), action = el.attr('data-action'), - groupName = el.parents('li[data-groupname]').attr('data-groupname'); + groupName = el.parents('tr[data-groupname]').attr('data-groupname'); switch (action) { case 'delete': @@ -108,141 +78,43 @@ define('admin/manage/groups', [ } }); break; - case 'members': - socket.emit('admin.groups.get', groupName, function(err, groupObj) { - - changeGroupName.val(groupObj.name).prop('readonly', groupObj.system); - changeGroupDesc.val(groupObj.description); - changeGroupUserTitle.val(groupObj.userTitle); - groupIcon.attr('class', 'fa fa-2x ' + groupObj.icon).attr('value', groupObj.icon); - changeGroupLabelColor.val(groupObj.labelColor); - groupLabelPreview.css('background', groupObj.labelColor || '#000000').text(groupObj.userTitle); - groupMembersEl.empty(); - - if (groupObj.members.length > 0) { - for (var x = 0; x < groupObj.members.length; x++) { - var memberIcon = $('
  • ') - .attr('data-uid', groupObj.members[x].uid) - .append($('').attr('src', groupObj.members[x].picture)) - .append($('').html(groupObj.members[x].username)); - groupMembersEl.append(memberIcon); - } - } - - groupDetailsModal.attr('data-groupname', groupObj.name); - groupDetailsModal.modal('show'); - }); - break; } }); - - groupDetailsSearch.on('keyup', function() { - - if (searchDelay) { - clearTimeout(searchDelay); - } - - searchDelay = setTimeout(function() { - var searchText = groupDetailsSearch.val(), - foundUser; - - socket.emit('admin.user.search', {query: searchText}, function(err, results) { - if (!err && results && results.users.length > 0) { - var numResults = results.users.length, x; - if (numResults > 20) { - numResults = 20; - } - - groupDetailsSearchResults.empty(); - for (x = 0; x < numResults; x++) { - foundUser = $('
  • '); - foundUser - .attr({title: results.users[x].username, 'data-uid': results.users[x].uid}) - .append($('').attr('src', results.users[x].picture)) - .append($('').html(results.users[x].username)); - - groupDetailsSearchResults.append(foundUser); - } - } else { - groupDetailsSearchResults.html('
  • No Users Found
  • '); - } - }); - }, 200); - }); - - groupDetailsSearchResults.on('click', 'li[data-uid]', function() { - var userLabel = $(this), - uid = parseInt(userLabel.attr('data-uid'), 10), - groupName = groupDetailsModal.attr('data-groupname'), - members = []; - - groupMembersEl.find('li[data-uid]').each(function() { - members.push(parseInt($(this).attr('data-uid'), 10)); - }); - - if (members.indexOf(uid) === -1) { - socket.emit('admin.groups.join', { - groupName: groupName, - uid: uid - }, function(err, data) { - if (!err) { - groupMembersEl.append(userLabel.clone(true)); - } - }); - } - }); - - groupMembersEl.on('click', 'li[data-uid]', function() { - var uid = $(this).attr('data-uid'), - groupName = groupDetailsModal.attr('data-groupname'); - - socket.emit('admin.groups.get', groupName, function(err, groupObj){ - if (!err){ - bootbox.confirm('Are you sure you want to remove this user?', function(confirm) { - if (confirm){ - socket.emit('admin.groups.leave', { - groupName: groupName, - uid: uid - }, function(err, data) { - if (!err) { - groupMembersEl.find('li[data-uid="' + uid + '"]').remove(); - } - }); - } - }); - } - }); - }); - - $('#change-group-icon').on('click', function() { - iconSelect.init(groupIcon); - }); - - colorpicker.enable(changeGroupLabelColor, function(hsb, hex) { - groupLabelPreview.css('background-color', '#' + hex); - }); - - detailsModalSave.on('click', function() { - socket.emit('admin.groups.update', { - groupName: groupDetailsModal.attr('data-groupname'), - values: { - name: changeGroupName.val(), - userTitle: changeGroupUserTitle.val(), - description: changeGroupDesc.val(), - icon: groupIcon.attr('value'), - labelColor: changeGroupLabelColor.val() - } - }, function(err) { - if (!err) { - groupDetailsModal.on('hidden.bs.modal', function() { - ajaxify.go('admin/manage/groups'); - }); - groupDetailsModal.modal('hide'); - } - }); - }); - }; + function handleSearch() { + function doSearch() { + if (!queryEl.val()) { + return ajaxify.refresh(); + } + $('.pagination').addClass('hide'); + var groupsEl = $('.groups-list'); + socket.emit('groups.search', { + query: queryEl.val(), + options: { + sort: 'date' + } + }, function(err, groups) { + templates.parse('admin/manage/groups', 'groups', { + groups: groups + }, function(html) { + groupsEl.find('[data-groupname]').remove(); + groupsEl.find('tr').after(html); + }); + }); + } + + var queryEl = $('#group-search'); + + queryEl.on('keyup', function() { + if (intervalId) { + clearTimeout(intervalId); + intervalId = 0; + } + intervalId = setTimeout(doSearch, 200); + }); + } + + return Groups; }); diff --git a/public/src/admin/manage/registration.js b/public/src/admin/manage/registration.js new file mode 100644 index 0000000000..0592fc02a9 --- /dev/null +++ b/public/src/admin/manage/registration.js @@ -0,0 +1,28 @@ +"use strict"; + +/* global config, socket, define, templates, bootbox, app, ajaxify, */ + +define('admin/manage/registration', function() { + var Registration = {}; + + Registration.init = function() { + + $('.users-list').on('click', '[data-action]', function(ev) { + var $this = this; + var parent = $(this).parents('[data-username]'); + var action = $(this).attr('data-action'); + var username = parent.attr('data-username'); + var method = action === 'accept' ? 'admin.user.acceptRegistration' : 'admin.user.rejectRegistration'; + + socket.emit(method, {username: username}, function(err) { + if (err) { + return app.alertError(err.message); + } + parent.remove(); + }); + return false; + }); + }; + + return Registration; +}); diff --git a/public/src/admin/manage/users.js b/public/src/admin/manage/users.js index 5909522f8c..ece62335cc 100644 --- a/public/src/admin/manage/users.js +++ b/public/src/admin/manage/users.js @@ -4,7 +4,7 @@ define('admin/manage/users', ['admin/modules/selectable'], function(selectable) var Users = {}; Users.init = function() { - var yourid = ajaxify.variables.get('yourid'); + var yourid = ajaxify.data.yourid; selectable.enable('#users-container', '.user-selectable'); @@ -250,7 +250,7 @@ define('admin/manage/users', ['admin/modules/selectable'], function(selectable) timeoutId = setTimeout(function() { $('.fa-spinner').removeClass('hidden'); - socket.emit('admin.user.search', {searchBy: [type], query: $this.val()}, function(err, data) { + socket.emit('admin.user.search', {searchBy: type, query: $this.val()}, function(err, data) { if (err) { return app.alertError(err.message); } diff --git a/public/src/admin/modules/instance.js b/public/src/admin/modules/instance.js new file mode 100644 index 0000000000..edad60b80e --- /dev/null +++ b/public/src/admin/modules/instance.js @@ -0,0 +1,68 @@ +"use strict"; + +/*globals define, app, socket*/ + +define('admin/modules/instance', function() { + var instance = {}; + + instance.reload = function(callback) { + app.alert({ + alert_id: 'instance_reload', + type: 'info', + title: 'Reloading... ', + message: 'NodeBB is reloading.', + timeout: 5000 + }); + + socket.emit('admin.reload', function(err) { + if (!err) { + app.alert({ + alert_id: 'instance_reload', + type: 'success', + title: ' Success', + message: 'NodeBB has successfully reloaded.', + timeout: 5000 + }); + } else { + app.alert({ + alert_id: 'instance_reload', + type: 'danger', + title: '[[global:alert.error]]', + message: '[[error:reload-failed, ' + err.message + ']]' + }); + } + + if (typeof callback === 'function') { + callback(); + } + }); + }; + + instance.restart = function(callback) { + app.alert({ + alert_id: 'instance_restart', + type: 'info', + title: 'Restarting... ', + message: 'NodeBB is restarting.', + timeout: 5000 + }); + + $(window).one('action:reconnected', function() { + app.alert({ + alert_id: 'instance_restart', + type: 'success', + title: ' Success', + message: 'NodeBB has successfully restarted.', + timeout: 5000 + }); + + if (typeof callback === 'function') { + callback(); + } + }); + + socket.emit('admin.restart'); + }; + + return instance; +}); diff --git a/public/src/admin/settings.js b/public/src/admin/settings.js index 98ab422b14..1eee16a353 100644 --- a/public/src/admin/settings.js +++ b/public/src/admin/settings.js @@ -5,18 +5,10 @@ define('admin/settings', ['uploader', 'sounds'], function(uploader, sounds) { var Settings = {}; Settings.init = function() { - Settings.prepare(); + $(window).on('action:config.loaded', Settings.prepare); }; Settings.prepare = function(callback) { - // Come back in 125ms if the config isn't ready yet - if (!app.config) { - setTimeout(function() { - Settings.prepare(callback); - }, 125); - return; - } - // Populate the fields on the page from the config var fields = $('#content [data-field]'), numFields = fields.length, @@ -104,6 +96,8 @@ define('admin/settings', ['uploader', 'sounds'], function(uploader, sounds) { if (typeof callback === 'function') { callback(); } + + $(window).trigger('action:admin.settingsLoaded'); }; function handleUploads() { diff --git a/public/src/ajaxify.js b/public/src/ajaxify.js index b155efdaf3..f048720e44 100644 --- a/public/src/ajaxify.js +++ b/public/src/ajaxify.js @@ -1,301 +1,312 @@ -"use strict"; - -var ajaxify = ajaxify || {}; - -$(document).ready(function() { - - /*global app, templates, utils, socket, config, RELATIVE_PATH*/ - - var location = document.location || window.location, - rootUrl = location.protocol + '//' + (location.hostname || location.host) + (location.port ? ':' + location.port : ''), - apiXHR = null, - - translator; - - // Dumb hack to fool ajaxify into thinking translator is still a global - // When ajaxify is migrated to a require.js module, then this can be merged into the "define" call - require(['translator'], function(_translator) { - translator = _translator; - }); - - $(window).on('popstate', function (ev) { - ev = ev.originalEvent; - - if (ev !== null && ev.state && ev.state.url !== undefined) { - ajaxify.go(ev.state.url, function() { - $(window).trigger('action:popstate', {url: ev.state.url}); - }, true); - } - }); - - ajaxify.currentPage = null; - - ajaxify.go = function (url, callback, quiet) { - if (ajaxify.handleRedirects(url)) { - return true; - } - - app.enterRoom(''); - - $(window).off('scroll'); - - if ($('#content').hasClass('ajaxifying') && apiXHR) { - apiXHR.abort(); - } - - url = ajaxify.start(url, quiet); - - $('#footer, #content').removeClass('hide').addClass('ajaxifying'); - - ajaxify.variables.flush(); - ajaxify.loadData(url, function(err, data) { - if (err) { - return onAjaxError(err, url, callback, quiet); - } - - app.template = data.template.name; - - require(['translator'], function(translator) { - translator.load(config.defaultLang, data.template.name); - renderTemplate(url, data.template.name, data, callback); - }); - }); - - require(['search'], function(search) { - if (search.topicDOM.active && !url.startsWith('topic/')) { - search.topicDOM.end(); - } - }); - - return true; - }; - - ajaxify.handleRedirects = function(url) { - url = ajaxify.removeRelativePath(url.replace(/\/$/, '')).toLowerCase(); - var isAdminRoute = url.startsWith('admin') && window.location.pathname.indexOf(RELATIVE_PATH + '/admin') !== 0; - var uploadsOrApi = url.startsWith('uploads') || url.startsWith('api'); - if (isAdminRoute || uploadsOrApi) { - window.open(RELATIVE_PATH + '/' + url, '_blank'); - return true; - } - return false; - }; - - - ajaxify.start = function(url, quiet, search) { - url = ajaxify.removeRelativePath(url.replace(/^\/|\/$/g, '')); - var hash = window.location.hash; - search = search || ''; - - $(window).trigger('action:ajaxify.start', {url: url}); - - if (!window.location.pathname.match(/\/(403|404)$/g)) { - app.previousUrl = window.location.href; - } - - ajaxify.currentPage = url; - - if (window.history && window.history.pushState) { - window.history[!quiet ? 'pushState' : 'replaceState']({ - url: url + search + hash - }, url, RELATIVE_PATH + '/' + url + search + hash); - } - return url; - }; - - function onAjaxError(err, url, callback, quiet) { - var data = err.data, - textStatus = err.textStatus; - - if (data) { - var status = parseInt(data.status, 10); - - if (status === 403 || status === 404 || status === 500) { - $('#footer, #content').removeClass('hide').addClass('ajaxifying'); - return renderTemplate(url, status.toString(), data.responseJSON, (new Date()).getTime(), callback); - } else if (status === 401) { - app.alertError('[[global:please_log_in]]'); - app.previousUrl = url; - return ajaxify.go('login'); - } else if (status === 302) { - if (data.responseJSON.external) { - window.location.href = data.responseJSON.external; - } else if (typeof data.responseJSON === 'string') { - ajaxify.go(data.responseJSON.slice(1), callback, quiet); - } - } - } else if (textStatus !== 'abort') { - app.alertError(data.responseJSON.error); - } - } - - function renderTemplate(url, tpl_url, data, callback) { - $(window).trigger('action:ajaxify.loadingTemplates', {}); - - templates.parse(tpl_url, data, function(template) { - translator.translate(template, function(translatedTemplate) { - $('#content').html(translatedTemplate); - - ajaxify.end(url, tpl_url); - - if (typeof callback === 'function') { - callback(); - } - - $('#content, #footer').removeClass('ajaxifying'); - - app.refreshTitle(url); - }); - }); - } - - ajaxify.end = function(url, tpl_url) { - ajaxify.variables.parse(); - - ajaxify.loadScript(tpl_url); - - ajaxify.widgets.render(tpl_url, url, function() { - $(window).trigger('action:ajaxify.end', {url: url}); - }); - - $(window).trigger('action:ajaxify.contentLoaded', {url: url, tpl: tpl_url}); - - app.processPage(); - }; - - ajaxify.removeRelativePath = function(url) { - if (url.startsWith(RELATIVE_PATH.slice(1))) { - url = url.slice(RELATIVE_PATH.length); - } - return url; - }; - - ajaxify.refresh = function(e) { - if (e && e instanceof jQuery.Event) { - e.preventDefault(); - } - - ajaxify.go(ajaxify.currentPage); - }; - - ajaxify.loadScript = function(tpl_url, callback) { - var location = !app.inAdmin ? 'forum/' : ''; - - require([location + tpl_url], function(script) { - if (script && script.init) { - script.init(); - } - - if (callback) { - callback(); - } - }); - }; - - ajaxify.loadData = function(url, callback) { - url = ajaxify.removeRelativePath(url); - - $(window).trigger('action:ajaxify.loadingData', {url: url}); - - apiXHR = $.ajax({ - url: RELATIVE_PATH + '/api/' + url, - cache: false, - success: function(data) { - if (!data) { - return; - } - - data.relative_path = RELATIVE_PATH; - $(window).trigger('action:ajaxify.dataLoaded', {url: url, data: data}); - - if (callback) { - callback(null, data); - } - }, - error: function(data, textStatus) { - if (data.status === 0 && textStatus === 'error') { - data.status = 500; - } - callback({ - data: data, - textStatus: textStatus - }); - } - }); - }; - - ajaxify.loadTemplate = function(template, callback) { - if (templates.cache[template]) { - callback(templates.cache[template]); - } else { - $.ajax({ - url: RELATIVE_PATH + '/templates/' + template + '.tpl' + (config['cache-buster'] ? '?v=' + config['cache-buster'] : ''), - type: 'GET', - success: function(data) { - callback(data.toString()); - }, - error: function(error) { - throw new Error("Unable to load template: " + template + " (" + error.statusText + ")"); - } - }); - } - }; - - function ajaxifyAnchors() { - templates.registerLoader(ajaxify.loadTemplate); - - if (!window.history || !window.history.pushState) { - return; // no ajaxification for old browsers - } - - function hrefEmpty(href) { - return href === undefined || href === '' || href === 'javascript:;' || href === window.location.href + "#" || href.slice(0, 1) === "#"; - } - - // Enhancing all anchors to ajaxify... - $(document.body).on('click', 'a', function (e) { - if (this.target !== '') { - return; - } else if (hrefEmpty(this.href) || this.protocol === 'javascript:' || $(this).attr('data-ajaxify') === 'false') { - return e.preventDefault(); - } - - if (!e.ctrlKey && !e.shiftKey && !e.metaKey && e.which === 1) { - if ( - this.host === '' || // Relative paths are always internal links... - (this.host === window.location.host && this.protocol === window.location.protocol && // Otherwise need to check that protocol and host match - (RELATIVE_PATH.length > 0 ? this.pathname.indexOf(RELATIVE_PATH) === 0 : true)) // Subfolder installs need this additional check - ) { - // Internal link - var url = this.href.replace(rootUrl + '/', ''); - - if(window.location.pathname === this.pathname && this.hash) { - if (this.hash !== window.location.hash) { - window.location.hash = this.hash; - } - - e.preventDefault(); - } else { - if (ajaxify.go(url)) { - e.preventDefault(); - } - } - } else if (window.location.pathname !== '/outgoing') { - // External Link - if (config.openOutgoingLinksInNewTab) { - window.open(this.href, '_blank'); - e.preventDefault(); - } else if (config.useOutgoingLinksPage) { - ajaxify.go('outgoing?url=' + encodeURIComponent(this.href)); - e.preventDefault(); - } - } - } - }); - } - - ajaxifyAnchors(); - app.load(); - templates.cache['500'] = $('.tpl-500').html(); - +"use strict"; + +var ajaxify = ajaxify || {}; + +$(document).ready(function() { + + /*global app, templates, utils, socket, config, RELATIVE_PATH*/ + + var location = document.location || window.location, + rootUrl = location.protocol + '//' + (location.hostname || location.host) + (location.port ? ':' + location.port : ''), + apiXHR = null, + + translator; + + // Dumb hack to fool ajaxify into thinking translator is still a global + // When ajaxify is migrated to a require.js module, then this can be merged into the "define" call + require(['translator'], function(_translator) { + translator = _translator; + }); + + $(window).on('popstate', function (ev) { + ev = ev.originalEvent; + + if (ev !== null && ev.state && ev.state.url !== undefined) { + ajaxify.go(ev.state.url, function() { + $(window).trigger('action:popstate', {url: ev.state.url}); + }, true); + } + }); + + ajaxify.currentPage = null; + + ajaxify.go = function (url, callback, quiet, search) { + if (!socket.connected) { + if (ajaxify.reconnectAction) { + $(window).off('action:reconnected', ajaxify.reconnectAction); + } + ajaxify.reconnectAction = function(e) { + ajaxify.go(url, callback, quiet, search); + $(window).off(e); + } + $(window).on('action:reconnected', ajaxify.reconnectAction); + } + + if (ajaxify.handleRedirects(url)) { + return true; + } + + app.enterRoom(''); + + $(window).off('scroll'); + + if ($('#content').hasClass('ajaxifying') && apiXHR) { + apiXHR.abort(); + } + + url = ajaxify.start(url, quiet, search); + + $('#footer, #content').removeClass('hide').addClass('ajaxifying'); + + ajaxify.variables.flush(); + ajaxify.loadData(url, function(err, data) { + if (err) { + return onAjaxError(err, url, callback, quiet); + } + + app.template = data.template.name; + + require(['translator'], function(translator) { + translator.load(config.defaultLang, data.template.name); + renderTemplate(url, data.template.name, data, callback); + }); + }); + + return true; + }; + + ajaxify.handleRedirects = function(url) { + url = ajaxify.removeRelativePath(url.replace(/\/$/, '')).toLowerCase(); + var isAdminRoute = url.startsWith('admin') && window.location.pathname.indexOf(RELATIVE_PATH + '/admin') !== 0; + var uploadsOrApi = url.startsWith('uploads') || url.startsWith('api'); + if (isAdminRoute || uploadsOrApi) { + window.open(RELATIVE_PATH + '/' + url, '_top'); + return true; + } + return false; + }; + + + ajaxify.start = function(url, quiet, search) { + url = ajaxify.removeRelativePath(url.replace(/^\/|\/$/g, '')); + var hash = window.location.hash; + search = search || ''; + + $(window).trigger('action:ajaxify.start', {url: url}); + + if (!window.location.pathname.match(/\/(403|404)$/g)) { + app.previousUrl = window.location.href; + } + + ajaxify.currentPage = url; + + if (window.history && window.history.pushState) { + window.history[!quiet ? 'pushState' : 'replaceState']({ + url: url + search + hash + }, url, RELATIVE_PATH + '/' + url + search + hash); + } + return url; + }; + + function onAjaxError(err, url, callback, quiet) { + var data = err.data, + textStatus = err.textStatus; + + if (data) { + var status = parseInt(data.status, 10); + if (status === 403 || status === 404 || status === 500 || status === 502) { + if (status === 502) { + status = 500; + } + + $('#footer, #content').removeClass('hide').addClass('ajaxifying'); + return renderTemplate(url, status.toString(), data.responseJSON, (new Date()).getTime(), callback); + } else if (status === 401) { + app.alertError('[[global:please_log_in]]'); + app.previousUrl = url; + return ajaxify.go('login'); + } else if (status === 302) { + if (data.responseJSON.external) { + window.location.href = data.responseJSON.external; + } else if (typeof data.responseJSON === 'string') { + ajaxify.go(data.responseJSON.slice(1), callback, quiet); + } + } + } else if (textStatus !== 'abort') { + app.alertError(data.responseJSON.error); + } + } + + function renderTemplate(url, tpl_url, data, callback) { + $(window).trigger('action:ajaxify.loadingTemplates', {}); + + templates.parse(tpl_url, data, function(template) { + translator.translate(template, function(translatedTemplate) { + $('#content').html(translatedTemplate); + + ajaxify.end(url, tpl_url); + + if (typeof callback === 'function') { + callback(); + } + + $('#content, #footer').removeClass('ajaxifying'); + + app.refreshTitle(url); + }); + }); + } + + ajaxify.end = function(url, tpl_url) { + function done() { + if (--count === 0) { + $(window).trigger('action:ajaxify.end', {url: url}); + } + } + var count = 2; + + ajaxify.variables.parse(); + + ajaxify.loadScript(tpl_url, done); + + ajaxify.widgets.render(tpl_url, url, done); + + $(window).trigger('action:ajaxify.contentLoaded', {url: url, tpl: tpl_url}); + + app.processPage(); + }; + + ajaxify.removeRelativePath = function(url) { + if (url.startsWith(RELATIVE_PATH.slice(1))) { + url = url.slice(RELATIVE_PATH.length); + } + return url; + }; + + ajaxify.refresh = function(e) { + if (e && e instanceof jQuery.Event) { + e.preventDefault(); + } + + ajaxify.go(ajaxify.currentPage, null, true); + }; + + ajaxify.loadScript = function(tpl_url, callback) { + var location = !app.inAdmin ? 'forum/' : ''; + + require([location + tpl_url], function(script) { + if (script && script.init) { + script.init(); + } + + if (callback) { + callback(); + } + }); + }; + + ajaxify.loadData = function(url, callback) { + url = ajaxify.removeRelativePath(url); + + $(window).trigger('action:ajaxify.loadingData', {url: url}); + + apiXHR = $.ajax({ + url: RELATIVE_PATH + '/api/' + url, + cache: false, + success: function(data) { + if (!data) { + return; + } + ajaxify.data = data; + data.relative_path = RELATIVE_PATH; + $(window).trigger('action:ajaxify.dataLoaded', {url: url, data: data}); + + if (callback) { + callback(null, data); + } + }, + error: function(data, textStatus) { + if (data.status === 0 && textStatus === 'error') { + data.status = 500; + } + callback({ + data: data, + textStatus: textStatus + }); + } + }); + }; + + ajaxify.loadTemplate = function(template, callback) { + if (templates.cache[template]) { + callback(templates.cache[template]); + } else { + $.ajax({ + url: RELATIVE_PATH + '/templates/' + template + '.tpl' + (config['cache-buster'] ? '?v=' + config['cache-buster'] : ''), + type: 'GET', + success: function(data) { + callback(data.toString()); + }, + error: function(error) { + throw new Error("Unable to load template: " + template + " (" + error.statusText + ")"); + } + }); + } + }; + + function ajaxifyAnchors() { + templates.registerLoader(ajaxify.loadTemplate); + + function hrefEmpty(href) { + return href === undefined || href === '' || href === 'javascript:;'; + } + + // Enhancing all anchors to ajaxify... + $(document.body).on('click', 'a', function (e) { + if (this.target !== '' || (this.protocol !== 'http:' && this.protocol !== 'https:')) { + return; + } else if (hrefEmpty(this.href) || this.protocol === 'javascript:' || $(this).attr('data-ajaxify') === 'false' || $(this).attr('href') === '#') { + return e.preventDefault(); + } + + if (!e.ctrlKey && !e.shiftKey && !e.metaKey && e.which === 1) { + if ( + this.host === '' || // Relative paths are always internal links... + (this.host === window.location.host && this.protocol === window.location.protocol && // Otherwise need to check that protocol and host match + (RELATIVE_PATH.length > 0 ? this.pathname.indexOf(RELATIVE_PATH) === 0 : true)) // Subfolder installs need this additional check + ) { + // Internal link + var url = this.pathname.replace(RELATIVE_PATH + '/', ''); + + // Special handling for urls with hashes + if (window.location.pathname === this.pathname && this.hash.length) { + window.location.hash = this.hash; + } else { + window.location.hash = ''; + if (ajaxify.go(url)) { + e.preventDefault(); + } + } + } else if (window.location.pathname !== '/outgoing') { + // External Link + if (config.openOutgoingLinksInNewTab) { + window.open(this.href, '_blank'); + e.preventDefault(); + } else if (config.useOutgoingLinksPage) { + ajaxify.go('outgoing?url=' + encodeURIComponent(this.href)); + e.preventDefault(); + } + } + } + }); + } + + if (window.history && window.history.pushState) { + // Progressive Enhancement, ajaxify available only to modern browsers + ajaxifyAnchors(); + } + + app.load(); + templates.cache['500'] = $('.tpl-500').html(); + }); \ No newline at end of file diff --git a/public/src/app.js b/public/src/app.js index 5484d9de50..3254c7a51f 100644 --- a/public/src/app.js +++ b/public/src/app.js @@ -1,620 +1,613 @@ -"use strict"; -/*global io, templates, ajaxify, utils, bootbox, RELATIVE_PATH, config, Visibility*/ - -var socket, - app = app || {}; - -app.isFocused = true; -app.isConnected = false; -app.currentRoom = null; -app.widgets = {}; -app.cacheBuster = null; - -(function () { - var showWelcomeMessage = false; - var reconnecting = false; - - function socketIOConnect() { - var ioParams = { - reconnectionAttempts: config.maxReconnectionAttempts, - reconnectionDelay: config.reconnectionDelay, - transports: config.socketioTransports, - path: config.relative_path + '/socket.io' - }; - - socket = io(config.websocketAddress, ioParams); - reconnecting = false; - - socket.on('event:connect', function () { - app.showLoginMessage(); - app.replaceSelfLinks(); - $(window).trigger('action:connected'); - app.isConnected = true; - }); - - socket.on('connect', onSocketConnect); - - socket.on('event:disconnect', function() { - $(window).trigger('action:disconnected'); - app.isConnected = false; - socket.connect(); - }); - - socket.on('reconnecting', function (attempt) { - reconnecting = true; - var reconnectEl = $('#reconnect'); - - if (!reconnectEl.hasClass('active')) { - reconnectEl.html(''); - } - - reconnectEl.addClass('active').removeClass("hide").tooltip({ - placement: 'bottom' - }); - }); - - socket.on('event:banned', function() { - app.alert({ - title: '[[global:alert.banned]]', - message: '[[global:alert.banned.message]]', - type: 'danger', - timeout: 1000 - }); - - setTimeout(function() { - window.location.href = config.relative_path + '/'; - }, 1000); - }); - - socket.on('event:logout', app.logout); - - socket.on('event:alert', function(data) { - app.alert(data); - }); - - socket.on('reconnect_failed', function() { - // Wait ten times the reconnection delay and then start over - setTimeout(socket.connect.bind(socket), parseInt(config.reconnectionDelay, 10) * 10); - }); - } - - function onSocketConnect(data) { - if (reconnecting) { - var reconnectEl = $('#reconnect'); - - reconnectEl.tooltip('destroy'); - reconnectEl.html(''); - reconnecting = false; - - // Rejoin room that was left when we disconnected - var url_parts = window.location.pathname.slice(RELATIVE_PATH.length).split('/').slice(1); - var room; - - switch(url_parts[0]) { - case 'user': - room = 'user/' + ajaxify.variables.get('theirid'); - break; - case 'topic': - room = 'topic_' + url_parts[1]; - break; - case 'category': - room = 'category_' + url_parts[1]; - break; - case 'recent': // intentional fall-through - case 'unread': - room = 'recent_posts'; - break; - case 'admin': - room = 'admin'; - break; - case 'categories': - room = 'categories'; - break; - } - app.currentRoom = ''; - app.enterRoom(room); - - socket.emit('meta.reconnected'); - - app.isConnected = true; - $(window).trigger('action:reconnected'); - - setTimeout(function() { - reconnectEl.removeClass('active').addClass('hide'); - }, 3000); - } - } - - app.logout = function() { - require(['csrf'], function(csrf) { - $.ajax(RELATIVE_PATH + '/logout', { - type: 'POST', - headers: { - 'x-csrf-token': csrf.get() - }, - success: function() { - window.location.href = RELATIVE_PATH + '/'; - } - }); - }); - }; - - app.alert = function (params) { - require(['alerts'], function(alerts) { - alerts.alert(params); - }); - }; - - app.removeAlert = function(id) { - require(['alerts'], function(alerts) { - alerts.remove(id); - }); - }; - - app.alertSuccess = function (message, timeout) { - app.alert({ - title: '[[global:alert.success]]', - message: message, - type: 'success', - timeout: timeout ? timeout : 2000 - }); - }; - - app.alertError = function (message, timeout) { - app.alert({ - title: '[[global:alert.error]]', - message: message, - type: 'danger', - timeout: timeout ? timeout : 5000 - }); - }; - - app.enterRoom = function (room, callback) { - callback = callback || function() {}; - if (socket) { - if (app.currentRoom === room) { - return; - } - - socket.emit('meta.rooms.enter', { - enter: room, - username: app.user.username, - userslug: app.user.userslug, - picture: app.user.picture - }, function(err) { - if (err) { - app.alertError(err.message); - return; - } - app.currentRoom = room; - }); - } - }; - - function highlightNavigationLink() { - var path = window.location.pathname; - $('#main-nav li').removeClass('active'); - if (path) { - $('#main-nav li a').each(function () { - var href = $(this).attr('href'); - - if (href && path.startsWith(href)) { - $(this.parentNode).addClass('active'); - return false; - } - }); - } - } - - app.createUserTooltips = function() { - $('img[title].teaser-pic,img[title].user-img').each(function() { - $(this).tooltip({ - placement: 'top', - title: $(this).attr('title') - }); - }); - }; - - app.createStatusTooltips = function() { - $('body').tooltip({ - selector:'.fa-circle.status', - placement: 'top' - }); - }; - - app.replaceSelfLinks = function(selector) { - selector = selector || $('a'); - selector.each(function() { - var href = $(this).attr('href'); - if (href && app.user.userslug && href.indexOf('user/_self_') !== -1) { - $(this).attr('href', href.replace(/user\/_self_/g, 'user/' + app.user.userslug)); - } - }); - }; - - app.processPage = function () { - highlightNavigationLink(); - - $('.timeago').timeago(); - - utils.makeNumbersHumanReadable($('.human-readable-number')); - - utils.addCommasToNumbers($('.formatted-number')); - - app.createUserTooltips(); - - app.createStatusTooltips(); - - app.replaceSelfLinks(); - - // Scroll back to top of page - window.scrollTo(0, 0); - }; - - app.showLoginMessage = function () { - function showAlert() { - app.alert({ - type: 'success', - title: '[[global:welcome_back]] ' + app.user.username + '!', - message: '[[global:you_have_successfully_logged_in]]', - timeout: 5000 - }); - } - - if (showWelcomeMessage) { - showWelcomeMessage = false; - if (document.readyState !== 'complete') { - $(document).ready(showAlert); - } else { - showAlert(); - } - } - }; - - app.openChat = function (username, touid) { - if (username === app.user.username) { - return app.alertError('[[error:cant-chat-with-yourself]]'); - } - - if (!app.user.uid) { - return app.alertError('[[error:not-logged-in]]'); - } - - require(['chat'], function (chat) { - function loadAndCenter(chatModal) { - chat.load(chatModal.attr('UUID')); - chat.center(chatModal); - chat.focusInput(chatModal); - } - - if (!chat.modalExists(touid)) { - chat.createModal(username, touid, loadAndCenter); - } else { - loadAndCenter(chat.getModal(touid)); - } - }); - }; - - var titleObj = { - active: false, - interval: undefined, - titles: [] - }; - - app.alternatingTitle = function (title) { - if (typeof title !== 'string') { - return; - } - - if (title.length > 0 && !app.isFocused) { - if (!titleObj.titles[0]) { - titleObj.titles[0] = window.document.title; - } - - require(['translator'], function(translator) { - translator.translate(title, function(translated) { - titleObj.titles[1] = translated; - if (titleObj.interval) { - clearInterval(titleObj.interval); - } - - titleObj.interval = setInterval(function() { - var title = titleObj.titles[titleObj.titles.indexOf(window.document.title) ^ 1]; - if (title) { - window.document.title = $('
    ').html(title).text(); - } - }, 2000); - }); - }); - } else { - if (titleObj.interval) { - clearInterval(titleObj.interval); - } - if (titleObj.titles[0]) { - window.document.title = $('
    ').html(titleObj.titles[0]).text(); - } - } - }; - - app.refreshTitle = function(url) { - if (!url) { - var a = document.createElement('a'); - a.href = document.location; - url = a.pathname.slice(1); - } - - socket.emit('meta.buildTitle', url, function(err, title, numNotifications) { - if (err) { - return; - } - titleObj.titles[0] = (numNotifications > 0 ? '(' + numNotifications + ') ' : '') + title; - app.alternatingTitle(''); - }); - }; - - app.toggleNavbar = function(state) { - var navbarEl = $('.navbar'); - if (navbarEl) { - navbarEl.toggleClass('hidden', !!!state); - } - }; - - app.exposeConfigToTemplates = function() { - $(document).ready(function() { - templates.setGlobal('loggedIn', config.loggedIn); - templates.setGlobal('relative_path', RELATIVE_PATH); - for(var key in config) { - if (config.hasOwnProperty(key)) { - templates.setGlobal('config.' + key, config[key]); - } - } - }); - }; - - function createHeaderTooltips() { - if (utils.findBootstrapEnvironment() === 'xs') { - return; - } - $('#header-menu li a[title]').each(function() { - $(this).tooltip({ - placement: 'bottom', - title: $(this).attr('title') - }); - }); - - $('#search-form').parent().tooltip({ - placement: 'bottom', - title: $('#search-button i').attr('title') - }); - - $('#user_dropdown').tooltip({ - placement: 'bottom', - title: $('#user_dropdown').attr('title') - }); - } - - app.handleSearch = function () { - var searchButton = $("#search-button"), - searchFields = $("#search-fields"), - searchInput = $('#search-fields input'); - - $('#search-form').on('submit', dismissSearch); - searchInput.on('blur', dismissSearch); - - function dismissSearch(){ - searchFields.hide(); - searchButton.show(); - } - - function prepareSearch() { - searchFields.removeClass('hide').show(); - searchButton.hide(); - searchInput.focus(); - } - - searchButton.on('click', function(e) { - if (!config.loggedIn && !config.allowGuestSearching) { - app.alert({ - message:'[[error:search-requires-login]]', - timeout: 3000 - }); - ajaxify.go('login'); - return false; - } - e.stopPropagation(); - - prepareSearch(); - return false; - }); - - require(['search', 'mousetrap'], function(search, Mousetrap) { - $('#search-form').on('submit', function (e) { - e.preventDefault(); - var input = $(this).find('input'); - - search.query({term: input.val()}, function() { - input.val(''); - }); - }); - - $('.topic-search') - .on('click', '.prev', function() { - search.topicDOM.prev(); - }) - .on('click', '.next', function() { - search.topicDOM.next(); - }); - - Mousetrap.bind('ctrl+f', function(e) { - if (config.topicSearchEnabled) { - // If in topic, open search window and populate, otherwise regular behaviour - var match = ajaxify.currentPage.match(/^topic\/([\d]+)/), - tid; - if (match) { - e.preventDefault(); - tid = match[1]; - searchInput.val('in:topic-' + tid + ' '); - prepareSearch(); - } - } - }); - }); - } - - function handleStatusChange() { - $('#user-control-list .user-status').off('click').on('click', function(e) { - var status = $(this).attr('data-status'); - socket.emit('user.setStatus', status, function(err, data) { - if(err) { - return app.alertError(err.message); - } - $('#logged-in-menu #user_label #user-profile-link>i').attr('class', 'fa fa-circle status ' + status); - }); - e.preventDefault(); - }); - } - - app.updateUserStatus = function(el, status) { - if (!el.length) { - return; - } - - translator.translate('[[global:' + status + ']]', function(translated) { - el.removeClass('online offline dnd away') - .addClass(status) - .attr('title', translated) - .attr('data-original-title', translated); - }); - }; - - function handleNewTopic() { - $('#content').on('click', '#new_topic', function() { - require(['composer'], function(composer) { - var cid = ajaxify.variables.get('category_id'); - if (cid) { - composer.newTopic(cid); - } else { - socket.emit('categories.getCategoriesByPrivilege', 'topics:create', function(err, categories) { - if (err) { - return app.alertError(err.message); - } - if (categories.length) { - composer.newTopic(categories[0].cid); - } - }); - } - }); - }); - } - - app.load = function() { - $('document').ready(function () { - var url = ajaxify.start(window.location.pathname.slice(1), true, window.location.search); - ajaxify.end(url, app.template); - - handleStatusChange(); - - if (config.searchEnabled) { - app.handleSearch(); - } - - handleNewTopic(); - - $('#logout-link').on('click', app.logout); - - Visibility.change(function(e, state){ - if (state === 'visible') { - app.isFocused = true; - app.alternatingTitle(''); - } else if (state === 'hidden') { - app.isFocused = false; - } - }); - - createHeaderTooltips(); - 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; - - app.alert({ - alert_id: 'forum_updated', - title: '[[global:updated.title]]', - message: '[[global:updated.message]]', - clickfn: function() { - window.location.reload(); - }, - type: 'warning' - }); - } - }); - - require(['taskbar', 'helpers'], function(taskbar, helpers) { - taskbar.init(); - - // templates.js helpers - helpers.register(); - }); - }); - }; - - app.showEmailConfirmWarning = function(err) { - if (!config.requireEmailConfirmation || !app.user.uid) { - return; - } - if (!app.user.email) { - app.alert({ - alert_id: 'email_confirm', - message: '[[error:no-email-to-confirm]]', - type: 'warning', - timeout: 0, - clickfn: function() { - app.removeAlert('email_confirm'); - ajaxify.go('user/' + app.user.userslug + '/edit'); - } - }); - } else if (!app.user['email:confirmed']) { - app.alert({ - alert_id: 'email_confirm', - message: err ? err.message : '[[error:email-not-confirmed]]', - type: 'warning', - timeout: 0, - clickfn: function() { - app.removeAlert('email_confirm'); - socket.emit('user.emailConfirm', {}, function(err) { - if (err) { - return app.alertError(err.message); - } - app.alertSuccess('[[notifications:email-confirm-sent]]'); - }); - } - }); - } - }; - - showWelcomeMessage = window.location.href.indexOf('loggedin') !== -1; - - app.exposeConfigToTemplates(); - - socketIOConnect(); - - app.cacheBuster = config['cache-buster']; - - require(['csrf'], function(csrf) { - csrf.set(config.csrf_token); - }); - - bootbox.setDefaults({ - locale: config.userLang - }); - - app.alternatingTitle(''); - -}()); +"use strict"; +/*global io, templates, ajaxify, utils, bootbox, RELATIVE_PATH, config, Visibility*/ + +var socket, + app = app || {}; + +app.isFocused = true; +app.isConnected = false; +app.currentRoom = null; +app.widgets = {}; +app.cacheBuster = null; + +(function () { + var showWelcomeMessage = false; + var reconnecting = false; + + function socketIOConnect() { + var ioParams = { + reconnectionAttempts: config.maxReconnectionAttempts, + reconnectionDelay: config.reconnectionDelay, + transports: config.socketioTransports, + path: config.relative_path + '/socket.io' + }; + + socket = io(config.websocketAddress, ioParams); + reconnecting = false; + + socket.on('event:connect', function () { + app.showLoginMessage(); + app.replaceSelfLinks(); + $(window).trigger('action:connected'); + app.isConnected = true; + }); + + socket.on('connect', onSocketConnect); + + socket.on('event:disconnect', function() { + $(window).trigger('action:disconnected'); + app.isConnected = false; + socket.connect(); + }); + + socket.on('reconnecting', function (attempt) { + reconnecting = true; + var reconnectEl = $('#reconnect'); + + if (!reconnectEl.hasClass('active')) { + reconnectEl.html(''); + } + + reconnectEl.addClass('active').removeClass("hide").tooltip({ + placement: 'bottom' + }); + }); + + socket.on('event:banned', function() { + app.alert({ + title: '[[global:alert.banned]]', + message: '[[global:alert.banned.message]]', + type: 'danger', + timeout: 1000 + }); + + setTimeout(function() { + window.location.href = config.relative_path + '/'; + }, 1000); + }); + + socket.on('event:logout', app.logout); + + socket.on('event:alert', function(data) { + app.alert(data); + }); + + socket.on('reconnect_failed', function() { + // Wait ten times the reconnection delay and then start over + setTimeout(socket.connect.bind(socket), parseInt(config.reconnectionDelay, 10) * 10); + }); + } + + function onSocketConnect(data) { + if (reconnecting) { + var reconnectEl = $('#reconnect'); + + reconnectEl.tooltip('destroy'); + reconnectEl.html(''); + reconnecting = false; + + // Rejoin room that was left when we disconnected + var url_parts = window.location.pathname.slice(RELATIVE_PATH.length).split('/').slice(1); + var room; + + switch(url_parts[0]) { + case 'user': + room = 'user/' + ajaxify.data.theirid; + break; + case 'topic': + room = 'topic_' + url_parts[1]; + break; + case 'category': + room = 'category_' + url_parts[1]; + break; + case 'recent': + room = 'recent_topics'; + break; + case 'unread': + room = 'unread_topics'; + break; + case 'popular': + room = 'popular_topics'; + break; + case 'admin': + room = 'admin'; + break; + case 'categories': + room = 'categories'; + break; + } + app.currentRoom = ''; + app.enterRoom(room); + + socket.emit('meta.reconnected'); + + app.isConnected = true; + $(window).trigger('action:reconnected'); + + setTimeout(function() { + reconnectEl.removeClass('active').addClass('hide'); + }, 3000); + } + } + + app.logout = function() { + require(['csrf'], function(csrf) { + $.ajax(RELATIVE_PATH + '/logout', { + type: 'POST', + headers: { + 'x-csrf-token': csrf.get() + }, + success: function() { + window.location.href = RELATIVE_PATH + '/'; + } + }); + }); + }; + + app.alert = function (params) { + require(['alerts'], function(alerts) { + alerts.alert(params); + }); + }; + + app.removeAlert = function(id) { + require(['alerts'], function(alerts) { + alerts.remove(id); + }); + }; + + app.alertSuccess = function (message, timeout) { + app.alert({ + title: '[[global:alert.success]]', + message: message, + type: 'success', + timeout: timeout ? timeout : 2000 + }); + }; + + app.alertError = function (message, timeout) { + app.alert({ + title: '[[global:alert.error]]', + message: message, + type: 'danger', + timeout: timeout ? timeout : 5000 + }); + }; + + app.enterRoom = function (room, callback) { + callback = callback || function() {}; + if (socket) { + if (app.currentRoom === room) { + return; + } + + socket.emit('meta.rooms.enter', { + enter: room, + username: app.user.username, + userslug: app.user.userslug, + picture: app.user.picture + }, function(err) { + if (err) { + app.alertError(err.message); + return; + } + app.currentRoom = room; + }); + } + }; + + function highlightNavigationLink() { + var path = window.location.pathname; + $('#main-nav li').removeClass('active'); + if (path) { + $('#main-nav li a').each(function () { + var href = $(this).attr('href'); + + if (href && path.startsWith(href)) { + $(this.parentNode).addClass('active'); + return false; + } + }); + } + } + + app.createUserTooltips = function() { + $('img[title].teaser-pic,img[title].user-img').each(function() { + $(this).tooltip({ + placement: 'top', + title: $(this).attr('title') + }); + }); + }; + + app.createStatusTooltips = function() { + $('body').tooltip({ + selector:'.fa-circle.status', + placement: 'top' + }); + }; + + app.replaceSelfLinks = function(selector) { + selector = selector || $('a'); + selector.each(function() { + var href = $(this).attr('href'); + if (href && app.user.userslug && href.indexOf('user/_self_') !== -1) { + $(this).attr('href', href.replace(/user\/_self_/g, 'user/' + app.user.userslug)); + } + }); + }; + + app.processPage = function () { + highlightNavigationLink(); + + $('.timeago').timeago(); + + utils.makeNumbersHumanReadable($('.human-readable-number')); + + utils.addCommasToNumbers($('.formatted-number')); + + app.createUserTooltips(); + + app.createStatusTooltips(); + + app.replaceSelfLinks(); + + // Scroll back to top of page + window.scrollTo(0, 0); + }; + + app.showLoginMessage = function () { + function showAlert() { + app.alert({ + type: 'success', + title: '[[global:welcome_back]] ' + app.user.username + '!', + message: '[[global:you_have_successfully_logged_in]]', + timeout: 5000 + }); + } + + if (showWelcomeMessage) { + showWelcomeMessage = false; + if (document.readyState !== 'complete') { + $(document).ready(showAlert); + } else { + showAlert(); + } + } + }; + + app.openChat = function (username, touid) { + if (username === app.user.username) { + return app.alertError('[[error:cant-chat-with-yourself]]'); + } + + if (!app.user.uid) { + return app.alertError('[[error:not-logged-in]]'); + } + + require(['chat'], function (chat) { + function loadAndCenter(chatModal) { + chat.load(chatModal.attr('UUID')); + chat.center(chatModal); + chat.focusInput(chatModal); + } + + if (!chat.modalExists(touid)) { + chat.createModal(username, touid, loadAndCenter); + } else { + loadAndCenter(chat.getModal(touid)); + } + }); + }; + + var titleObj = { + active: false, + interval: undefined, + titles: [] + }; + + app.alternatingTitle = function (title) { + if (typeof title !== 'string') { + return; + } + + if (title.length > 0 && !app.isFocused) { + if (!titleObj.titles[0]) { + titleObj.titles[0] = window.document.title; + } + + require(['translator'], function(translator) { + translator.translate(title, function(translated) { + titleObj.titles[1] = translated; + if (titleObj.interval) { + clearInterval(titleObj.interval); + } + + titleObj.interval = setInterval(function() { + var title = titleObj.titles[titleObj.titles.indexOf(window.document.title) ^ 1]; + if (title) { + window.document.title = $('
    ').html(title).text(); + } + }, 2000); + }); + }); + } else { + if (titleObj.interval) { + clearInterval(titleObj.interval); + } + if (titleObj.titles[0]) { + window.document.title = $('
    ').html(titleObj.titles[0]).text(); + } + } + }; + + app.refreshTitle = function(url) { + if (!url) { + var a = document.createElement('a'); + a.href = document.location; + url = a.pathname.slice(1); + } + + socket.emit('meta.buildTitle', url, function(err, title, numNotifications) { + if (err) { + return; + } + titleObj.titles[0] = (numNotifications > 0 ? '(' + numNotifications + ') ' : '') + title; + app.alternatingTitle(''); + }); + }; + + app.toggleNavbar = function(state) { + var navbarEl = $('.navbar'); + if (navbarEl) { + navbarEl.toggleClass('hidden', !!!state); + } + }; + + app.exposeConfigToTemplates = function() { + $(document).ready(function() { + templates.setGlobal('loggedIn', config.loggedIn); + templates.setGlobal('relative_path', RELATIVE_PATH); + for(var key in config) { + if (config.hasOwnProperty(key)) { + templates.setGlobal('config.' + key, config[key]); + } + } + }); + }; + + function createHeaderTooltips() { + if (utils.findBootstrapEnvironment() === 'xs') { + return; + } + $('#header-menu li a[title]').each(function() { + $(this).tooltip({ + placement: 'bottom', + title: $(this).attr('title') + }); + }); + + $('#search-form').parent().tooltip({ + placement: 'bottom', + title: $('#search-button i').attr('title') + }); + + $('#user_dropdown').tooltip({ + placement: 'bottom', + title: $('#user_dropdown').attr('title') + }); + } + + app.handleSearch = function () { + var searchButton = $("#search-button"), + searchFields = $("#search-fields"), + searchInput = $('#search-fields input'); + + $('#search-form').on('submit', dismissSearch); + searchInput.on('blur', dismissSearch); + + function dismissSearch(){ + searchFields.hide(); + searchButton.show(); + } + + searchButton.on('click', function(e) { + if (!config.loggedIn && !config.allowGuestSearching) { + app.alert({ + message:'[[error:search-requires-login]]', + timeout: 3000 + }); + ajaxify.go('login'); + return false; + } + e.stopPropagation(); + + app.prepareSearch(); + return false; + }); + + $('#search-form').on('submit', function () { + var input = $(this).find('input'); + require(['search'], function(search) { + search.query({term: input.val()}, function() { + input.val(''); + }); + }); + return false; + }); + }; + + app.prepareSearch = function() { + $("#search-fields").removeClass('hide').show(); + $("#search-button").hide(); + $('#search-fields input').focus(); + }; + + function handleStatusChange() { + $('#user-control-list .user-status').off('click').on('click', function(e) { + var status = $(this).attr('data-status'); + socket.emit('user.setStatus', status, function(err, data) { + if(err) { + return app.alertError(err.message); + } + $('#logged-in-menu #user_label #user-profile-link>i').attr('class', 'fa fa-circle status ' + status); + }); + e.preventDefault(); + }); + } + + app.updateUserStatus = function(el, status) { + if (!el.length) { + return; + } + + translator.translate('[[global:' + status + ']]', function(translated) { + el.removeClass('online offline dnd away') + .addClass(status) + .attr('title', translated) + .attr('data-original-title', translated); + }); + }; + + function handleNewTopic() { + $('#content').on('click', '#new_topic', function() { + var cid = ajaxify.data.cid; + if (cid) { + $(window).trigger('action:composer.topic.new', { + cid: cid + }); + } else { + socket.emit('categories.getCategoriesByPrivilege', 'topics:create', function(err, categories) { + if (err) { + return app.alertError(err.message); + } + if (categories.length) { + $(window).trigger('action:composer.topic.new', { + cid: categories[0].cid + }); + } + }); + } + }); + } + + app.load = function() { + $('document').ready(function () { + var url = ajaxify.start(window.location.pathname.slice(1), true, window.location.search); + ajaxify.end(url, app.template); + + handleStatusChange(); + + if (config.searchEnabled) { + app.handleSearch(); + } + + handleNewTopic(); + + $('#logout-link').on('click', app.logout); + + Visibility.change(function(e, state){ + if (state === 'visible') { + app.isFocused = true; + app.alternatingTitle(''); + } else if (state === 'hidden') { + app.isFocused = false; + } + }); + + createHeaderTooltips(); + 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; + + app.alert({ + alert_id: 'forum_updated', + title: '[[global:updated.title]]', + message: '[[global:updated.message]]', + clickfn: function() { + window.location.reload(); + }, + type: 'warning' + }); + } + }); + + require(['taskbar', 'helpers'], function(taskbar, helpers) { + taskbar.init(); + + // templates.js helpers + helpers.register(); + + $(window).trigger('action:app.load'); + }); + }); + }; + + app.loadJQueryUI = function(callback) { + if (typeof $().autocomplete === 'function') { + return callback(); + } + + $.getScript(RELATIVE_PATH + '/vendor/jquery/js/jquery-ui-1.10.4.custom.js', callback); + }; + + app.showEmailConfirmWarning = function(err) { + if (!config.requireEmailConfirmation || !app.user.uid) { + return; + } + if (!app.user.email) { + app.alert({ + alert_id: 'email_confirm', + message: '[[error:no-email-to-confirm]]', + type: 'warning', + timeout: 0, + clickfn: function() { + app.removeAlert('email_confirm'); + ajaxify.go('user/' + app.user.userslug + '/edit'); + } + }); + } else if (!app.user['email:confirmed']) { + app.alert({ + alert_id: 'email_confirm', + message: err ? err.message : '[[error:email-not-confirmed]]', + type: 'warning', + timeout: 0, + clickfn: function() { + app.removeAlert('email_confirm'); + socket.emit('user.emailConfirm', {}, function(err) { + if (err) { + return app.alertError(err.message); + } + app.alertSuccess('[[notifications:email-confirm-sent]]'); + }); + } + }); + } + }; + + showWelcomeMessage = window.location.href.indexOf('loggedin') !== -1; + + app.exposeConfigToTemplates(); + + socketIOConnect(); + + app.cacheBuster = config['cache-buster']; + + require(['csrf'], function(csrf) { + csrf.set(config.csrf_token); + }); + + bootbox.setDefaults({ + locale: config.userLang + }); + + app.alternatingTitle(''); +}()); diff --git a/public/src/client/account/edit.js b/public/src/client/account/edit.js index e7ed54cbbe..d57443d631 100644 --- a/public/src/client/account/edit.js +++ b/public/src/client/account/edit.js @@ -10,17 +10,19 @@ define('forum/account/edit', ['forum/account/header', 'uploader', 'translator'], currentEmail; AccountEdit.init = function() { - gravatarPicture = ajaxify.variables.get('gravatarpicture'); - uploadedPicture = ajaxify.variables.get('uploadedpicture'); + gravatarPicture = ajaxify.data.gravatarpicture; + uploadedPicture = ajaxify.data.uploadedpicture; header.init(); $('#submitBtn').on('click', updateProfile); - $('#inputBirthday').datepicker({ - changeMonth: true, - changeYear: true, - yearRange: '1900:+0' + app.loadJQueryUI(function() { + $('#inputBirthday').datepicker({ + changeMonth: true, + changeYear: true, + yearRange: '1900:+0' + }); }); currentEmail = $('#inputEmail').val(); @@ -85,7 +87,7 @@ define('forum/account/edit', ['forum/account/header', 'uploader', 'translator'], } function updateHeader(picture, username, userslug) { - if (parseInt(ajaxify.variables.get('theirid'), 10) !== parseInt(ajaxify.variables.get('yourid'), 10)) { + if (parseInt(ajaxify.data.theirid, 10) !== parseInt(ajaxify.data.yourid, 10)) { return; } @@ -188,7 +190,7 @@ define('forum/account/edit', ['forum/account/header', 'uploader', 'translator'], $('#uploadPictureBtn').on('click', function() { $('#change-picture-modal').modal('hide'); - uploader.open(config.relative_path + '/api/user/' + ajaxify.variables.get('userslug') + '/uploadpicture', {}, config.maximumProfileImageSize, function(imageUrlOnServer) { + uploader.open(config.relative_path + '/api/user/' + ajaxify.data.userslug + '/uploadpicture', {}, config.maximumProfileImageSize, function(imageUrlOnServer) { onUploadComplete(imageUrlOnServer); }); @@ -205,7 +207,7 @@ define('forum/account/edit', ['forum/account/header', 'uploader', 'translator'], if (!url) { return; } - socket.emit('user.uploadProfileImageFromUrl', {url: url, uid: ajaxify.variables.get('theirid')}, function(err, imageUrlOnServer) { + socket.emit('user.uploadProfileImageFromUrl', {url: url, uid: ajaxify.data.theirid}, function(err, imageUrlOnServer) { if (err) { return app.alertError(err.message); } @@ -284,7 +286,7 @@ define('forum/account/edit', ['forum/account/header', 'uploader', 'translator'], socket.emit('user.changePassword', { 'currentPassword': currentPassword.val(), 'newPassword': password.val(), - 'uid': ajaxify.variables.get('theirid') + 'uid': ajaxify.data.theirid }, function(err) { btn.removeClass('disabled').find('i').addClass('hide'); currentPassword.val(''); @@ -317,7 +319,7 @@ define('forum/account/edit', ['forum/account/header', 'uploader', 'translator'], function changeUserPicture(type, callback) { socket.emit('user.changePicture', { type: type, - uid: ajaxify.variables.get('theirid') + uid: ajaxify.data.theirid }, callback); } diff --git a/public/src/client/account/header.js b/public/src/client/account/header.js index 5db1d86723..511f3be5a9 100644 --- a/public/src/client/account/header.js +++ b/public/src/client/account/header.js @@ -10,7 +10,7 @@ define('forum/account/header', function() { }; function hidePrivateLinks() { - if (!app.user.uid || app.user.uid !== parseInt(ajaxify.variables.get('theirid'), 10)) { + if (!app.user.uid || app.user.uid !== parseInt(ajaxify.data.theirid, 10)) { $('.account-sub-links .plugin-link.private').addClass('hide'); } } diff --git a/public/src/client/account/posts.js b/public/src/client/account/posts.js index e6ce44c9d4..94dac96323 100644 --- a/public/src/client/account/posts.js +++ b/public/src/client/account/posts.js @@ -28,7 +28,7 @@ define('forum/account/posts', ['forum/account/header', 'forum/infinitescroll'], } infinitescroll.loadMore(method, { - uid: ajaxify.variables.get('theirid'), + uid: ajaxify.data.theirid, after: $('[component="posts"]').attr('data-nextstart') }, function(data, done) { if (data.posts && data.posts.length) { diff --git a/public/src/client/account/profile.js b/public/src/client/account/profile.js index 00be98d99f..5e72605017 100644 --- a/public/src/client/account/profile.js +++ b/public/src/client/account/profile.js @@ -1,6 +1,6 @@ 'use strict'; -/* globals define, ajaxify, app, utils, socket */ +/* globals define, ajaxify, app, utils, socket, bootbox */ define('forum/account/profile', ['forum/account/header', 'forum/infinitescroll', 'translator'], function(header, infinitescroll, translator) { var Account = {}, @@ -11,9 +11,9 @@ define('forum/account/profile', ['forum/account/header', 'forum/infinitescroll', Account.init = function() { header.init(); - yourid = ajaxify.variables.get('yourid'); - theirid = ajaxify.variables.get('theirid'); - isFollowing = ajaxify.variables.get('isFollowing'); + yourid = ajaxify.data.yourid; + theirid = ajaxify.data.theirid; + isFollowing = ajaxify.data.isFollowing; app.enterRoom('user/' + theirid); @@ -33,6 +33,10 @@ define('forum/account/profile', ['forum/account/header', 'forum/infinitescroll', app.openChat($('.account-username').html(), theirid); }); + $('#banAccountBtn').on('click', banAccount); + $('#unbanAccountBtn').on('click', unbanAccount); + $('#deleteAccountBtn').on('click', deleteAccount); + socket.removeListener('event:user_status_change', onUserStatusChange); socket.on('event:user_status_change', onUserStatusChange); @@ -66,7 +70,7 @@ define('forum/account/profile', ['forum/account/header', 'forum/infinitescroll', } function onUserStatusChange(data) { - if (parseInt(ajaxify.variables.get('theirid'), 10) !== parseInt(data.uid, 10)) { + if (parseInt(ajaxify.data.theirid, 10) !== parseInt(data.uid, 10)) { return; } @@ -112,5 +116,49 @@ define('forum/account/profile', ['forum/account/header', 'forum/infinitescroll', }); } + function banAccount() { + translator.translate('[[user:ban_account_confirm]]', function(translated) { + bootbox.confirm(translated, function(confirm) { + if (!confirm) { + return; + } + socket.emit('admin.user.banUsers', [ajaxify.data.theirid], function(err) { + if (err) { + return app.alertError(err.message); + } + $('#banAccountBtn').toggleClass('hide', true); + $('#banLabel, #unbanAccountBtn').toggleClass('hide', false); + }); + }); + }); + } + + function unbanAccount() { + socket.emit('admin.user.unbanUsers', [ajaxify.data.theirid], function(err) { + if (err) { + return app.alertError(err.message); + } + $('#banAccountBtn').toggleClass('hide', false); + $('#banLabel, #unbanAccountBtn').toggleClass('hide', true); + }); + } + + function deleteAccount() { + translator.translate('[[user:delete_this_account_confirm]]', function(translated) { + bootbox.confirm(translated, function(confirm) { + if (!confirm) { + return; + } + + socket.emit('admin.user.deleteUsers', [ajaxify.data.theirid], function(err) { + if (err) { + return app.alertError(err.message); + } + history.back(); + }); + }); + }); + } + return Account; }); diff --git a/public/src/client/account/settings.js b/public/src/client/account/settings.js index 848bf8c1ef..434072a0b0 100644 --- a/public/src/client/account/settings.js +++ b/public/src/client/account/settings.js @@ -30,7 +30,7 @@ define('forum/account/settings', ['forum/account/header'], function(header) { } }); - socket.emit('user.saveSettings', {uid: ajaxify.variables.get('theirid'), settings: settings}, function(err, newSettings) { + socket.emit('user.saveSettings', {uid: ajaxify.data.theirid, settings: settings}, function(err, newSettings) { if (err) { return app.alertError(err.message); } @@ -46,7 +46,7 @@ define('forum/account/settings', ['forum/account/header'], function(header) { } } app.exposeConfigToTemplates(); - if (requireReload && parseInt(app.user.uid, 10) === parseInt(ajaxify.variables.get('theirid'), 10)) { + if (requireReload && parseInt(app.user.uid, 10) === parseInt(ajaxify.data.theirid, 10)) { app.alert({ id: 'setting-change', message: '[[user:settings-require-reload]]', diff --git a/public/src/client/account/topics.js b/public/src/client/account/topics.js index ed0f50bc88..33bf212633 100644 --- a/public/src/client/account/topics.js +++ b/public/src/client/account/topics.js @@ -9,7 +9,7 @@ define('forum/account/topics', ['forum/account/header', 'forum/infinitescroll'], AccountTopics.init = function() { header.init(); - AccountTopics.handleInfiniteScroll('account/topics', 'uid:' + ajaxify.variables.get('theirid') + ':topics'); + AccountTopics.handleInfiniteScroll('account/topics', 'uid:' + ajaxify.data.theirid + ':topics'); }; AccountTopics.handleInfiniteScroll = function(_template, _set) { diff --git a/public/src/client/account/watched.js b/public/src/client/account/watched.js index 56fd52d0d4..dae195df2d 100644 --- a/public/src/client/account/watched.js +++ b/public/src/client/account/watched.js @@ -7,7 +7,7 @@ define('forum/account/watched', ['forum/account/header', 'forum/account/topics'] AccountWatched.init = function() { header.init(); - topics.handleInfiniteScroll('account/watched', 'uid:' + ajaxify.variables.get('theirid') + ':followed_tids'); + topics.handleInfiniteScroll('account/watched', 'uid:' + ajaxify.data.theirid + ':followed_tids'); }; return AccountWatched; diff --git a/public/src/client/category.js b/public/src/client/category.js index 777d3f808a..0ba3bdcd6e 100644 --- a/public/src/client/category.js +++ b/public/src/client/category.js @@ -2,7 +2,6 @@ /* global define, config, templates, app, utils, ajaxify, socket */ define('forum/category', [ - 'composer', 'forum/pagination', 'forum/infinitescroll', 'share', @@ -11,7 +10,7 @@ define('forum/category', [ 'sort', 'components', 'translator' -], function(composer, pagination, infinitescroll, share, navigator, categoryTools, sort, components, translator) { +], function(pagination, infinitescroll, share, navigator, categoryTools, sort, components, translator) { var Category = {}; $(window).on('action:ajaxify.start', function(ev, data) { @@ -28,23 +27,23 @@ define('forum/category', [ } Category.init = function() { - var cid = ajaxify.variables.get('category_id'); + var cid = ajaxify.data.cid; app.enterRoom('category_' + cid); - share.addShareHandlers(ajaxify.variables.get('category_name')); + share.addShareHandlers(ajaxify.data.name); socket.removeListener('event:new_topic', Category.onNewTopic); socket.on('event:new_topic', Category.onNewTopic); categoryTools.init(cid); - sort.handleSort('categoryTopicSort', 'user.setCategorySort', 'category/' + ajaxify.variables.get('category_slug')); + sort.handleSort('categoryTopicSort', 'user.setCategorySort', 'category/' + ajaxify.data.slug); enableInfiniteLoadingOrPagination(); if (!config.usePagination) { - navigator.init('[component="category/topic"]', ajaxify.variables.get('topic_count'), Category.toTop, Category.toBottom, Category.navigatorCallback); + navigator.init('[component="category/topic"]', ajaxify.data.topic_count, Category.toTop, Category.toBottom, Category.navigatorCallback); } $('[component="category"]').on('click', '[component="topic/header"]', function() { @@ -84,7 +83,7 @@ define('forum/category', [ }; Category.toBottom = function() { - socket.emit('categories.getTopicCount', ajaxify.variables.get('category_id'), function(err, count) { + socket.emit('categories.getTopicCount', ajaxify.data.cid, function(err, count) { navigator.scrollBottom(count - 1); }); }; @@ -158,7 +157,7 @@ define('forum/category', [ } var scrollTo = components.get('category/topic', 'index', bookmarkIndex); - var cid = ajaxify.variables.get('category_id'); + var cid = ajaxify.data.cid; if (scrollTo.length && cid) { $('html, body').animate({ scrollTop: (scrollTo.offset().top - $('#header-menu').height() - offset) + 'px' @@ -174,12 +173,12 @@ define('forum/category', [ infinitescroll.init(Category.loadMoreTopics); } else { navigator.hide(); - pagination.init(ajaxify.variables.get('currentPage'), ajaxify.variables.get('pageCount')); + pagination.init(ajaxify.data.currentPage, ajaxify.data.pageCount); } } Category.onNewTopic = function(topic) { - var cid = ajaxify.variables.get('category_id'); + var cid = ajaxify.data.cid; if (!topic || parseInt(topic.cid, 10) !== parseInt(cid, 10)) { return; } @@ -236,7 +235,7 @@ define('forum/category', [ }; function updateTopicCount() { - socket.emit('categories.getTopicCount', ajaxify.variables.get('category_id'), function(err, topicCount) { + socket.emit('categories.getTopicCount', ajaxify.data.cid, function(err, topicCount) { if(err) { return app.alertError(err.message); } @@ -338,7 +337,7 @@ define('forum/category', [ $(window).trigger('action:categories.loading'); infinitescroll.loadMore('categories.loadMore', { - cid: ajaxify.variables.get('category_id'), + cid: ajaxify.data.cid, after: after, author: utils.params().author }, function (data, done) { diff --git a/public/src/client/groups/details.js b/public/src/client/groups/details.js index 1a2742fb6b..ae6c46e03c 100644 --- a/public/src/client/groups/details.js +++ b/public/src/client/groups/details.js @@ -1,20 +1,29 @@ "use strict"; /* globals define, socket, ajaxify, app, bootbox, RELATIVE_PATH, utils */ -define('forum/groups/details', ['iconSelect', 'components', 'vendor/colorpicker/colorpicker', 'vendor/jquery/draggable-background/backgroundDraggable'], function(iconSelect, components) { +define('forum/groups/details', ['iconSelect', 'components', 'forum/infinitescroll', 'vendor/colorpicker/colorpicker', 'vendor/jquery/draggable-background/backgroundDraggable'], function(iconSelect, components, infinitescroll) { var Details = { cover: {} }; + var searchInterval; + var groupName; + Details.init = function() { var detailsPage = components.get('groups/container'), settingsFormEl = detailsPage.find('form'); - if (ajaxify.variables.get('is_owner') === 'true') { + groupName = ajaxify.data.group.name; + + if (ajaxify.data.group.isOwner) { Details.prepareSettings(); Details.initialiseCover(); } + handleMemberSearch(); + handleMemberInfiniteScroll(); + handleMemberInvitations(); + components.get('groups/activity').find('.content img').addClass('img-responsive'); detailsPage.on('click', '[data-action]', function() { @@ -29,7 +38,7 @@ define('forum/groups/details', ['iconSelect', 'components', 'vendor/colorpicker/ case 'toggleOwnership': socket.emit('groups.' + (isOwner ? 'rescind' : 'grant'), { toUid: uid, - groupName: ajaxify.variables.get('group_name') + groupName: groupName }, function(err) { if (!err) { ownerFlagEl.toggleClass('invisible'); @@ -42,7 +51,7 @@ define('forum/groups/details', ['iconSelect', 'components', 'vendor/colorpicker/ case 'kick': socket.emit('groups.kick', { uid: uid, - groupName: ajaxify.variables.get('group_name') + groupName: groupName }, function(err) { if (!err) { userRow.slideUp().remove(); @@ -64,11 +73,15 @@ define('forum/groups/details', ['iconSelect', 'components', 'vendor/colorpicker/ case 'leave': case 'accept': case 'reject': + case 'issueInvite': + case 'rescindInvite': case 'acceptInvite': case 'rejectInvite': + case 'acceptAll': + case 'rejectAll': socket.emit('groups.' + action, { toUid: uid, - groupName: ajaxify.variables.get('group_name') + groupName: groupName }, function(err) { if (!err) { ajaxify.refresh(); @@ -149,7 +162,7 @@ define('forum/groups/details', ['iconSelect', 'components', 'vendor/colorpicker/ }); socket.emit('groups.update', { - groupName: ajaxify.variables.get('group_name'), + groupName: groupName, values: settings }, function(err) { if (err) { @@ -171,15 +184,15 @@ define('forum/groups/details', ['iconSelect', 'components', 'vendor/colorpicker/ }; Details.deleteGroup = function() { - bootbox.confirm('Are you sure you want to delete the group: ' + utils.escapeHTML(ajaxify.variables.get('group_name')), function(confirm) { + bootbox.confirm('Are you sure you want to delete the group: ' + utils.escapeHTML(groupName), function(confirm) { if (confirm) { bootbox.prompt('Please enter the name of this group in order to delete it:', function(response) { - if (response === ajaxify.variables.get('group_name')) { + if (response === groupName) { socket.emit('groups.delete', { - groupName: ajaxify.variables.get('group_name') + groupName: groupName }, function(err) { if (!err) { - app.alertSuccess('[[groups:event.deleted, ' + utils.escapeHTML(ajaxify.variables.get('group_name')) + ']]'); + app.alertSuccess('[[groups:event.deleted, ' + utils.escapeHTML(groupName) + ']]'); ajaxify.go('groups'); } else { app.alertError(err.message); @@ -211,7 +224,7 @@ define('forum/groups/details', ['iconSelect', 'components', 'vendor/colorpicker/ Details.cover.load = function() { socket.emit('groups.cover.get', { - groupName: ajaxify.variables.get('group_name') + groupName: groupName }, function(err, data) { if (!err) { var coverEl = components.get('groups/cover'); @@ -260,7 +273,7 @@ define('forum/groups/details', ['iconSelect', 'components', 'vendor/colorpicker/ coverEl.addClass('saving'); socket.emit('groups.cover.update', { - groupName: ajaxify.variables.get('group_name'), + groupName: groupName, imageData: Details.cover.newCover || undefined, position: components.get('groups/cover').css('background-position') }, function(err) { @@ -278,5 +291,106 @@ define('forum/groups/details', ['iconSelect', 'components', 'vendor/colorpicker/ }); }; + function handleMemberSearch() { + $('[component="groups/members/search"]').on('keyup', function() { + var query = $(this).val(); + if (searchInterval) { + clearInterval(searchInterval); + searchInterval = 0; + } + + searchInterval = setTimeout(function() { + socket.emit('groups.searchMembers', {groupName: groupName, query: query}, function(err, results) { + if (err) { + return app.alertError(err.message); + } + + infinitescroll.parseAndTranslate('groups/details', 'members', { + group: { + members: results.users, + isOwner: ajaxify.data.group.isOwner + } + }, function(html) { + $('[component="groups/members"] tbody').html(html); + $('[component="groups/members"]').attr('data-nextstart', 20); + }); + }); + }, 250); + }); + } + + function handleMemberInfiniteScroll() { + $('[component="groups/members"] tbody').on('scroll', function() { + var $this = $(this); + var bottom = ($this[0].scrollHeight - $this.height()) * 0.9; + if ($this.scrollTop() > bottom) { + loadMoreMembers(); + } + }); + } + + function handleMemberInvitations() { + if (ajaxify.data.group.isOwner) { + var searchInput = $('[component="groups/members/invite"]'); + require(['autocomplete'], function(autocomplete) { + autocomplete.user(searchInput, function(e, selected) { + socket.emit('groups.issueInvite', { + toUid: selected.item.user.uid, + groupName: ajaxify.data.group.name + }, function(err) { + if (!err) { + ajaxify.refresh(); + } else { + app.alertError(err.message); + } + }); + }); + }); + } + } + + function loadMoreMembers() { + + var members = $('[component="groups/members"]'); + if (members.attr('loading')) { + return; + } + + members.attr('loading', 1); + socket.emit('groups.loadMoreMembers', { + groupName: groupName, + after: members.attr('data-nextstart') + }, function(err, data) { + if (err) { + return app.alertError(err.message); + } + + if (data && data.users.length) { + onMembersLoaded(data.users, function() { + members.removeAttr('loading'); + members.attr('data-nextstart', data.nextStart); + }); + } else { + members.removeAttr('loading'); + } + }); + } + + function onMembersLoaded(users, callback) { + users = users.filter(function(user) { + return !$('[component="groups/members"] [data-uid="' + user.uid + '"]').length; + }); + + infinitescroll.parseAndTranslate('groups/details', 'members', { + group: { + members: users, + isOwner: ajaxify.data.group.isOwner + } + }, function(html) { + $('[component="groups/members"] tbody').append(html); + callback(); + }); + } + return Details; }); \ No newline at end of file diff --git a/public/src/client/groups/list.js b/public/src/client/groups/list.js index 42bdbf88ae..e70eed5789 100644 --- a/public/src/client/groups/list.js +++ b/public/src/client/groups/list.js @@ -1,7 +1,7 @@ "use strict"; /* globals app, define, ajaxify, socket, bootbox, utils, templates */ -define('forum/groups/list', function() { +define('forum/groups/list', ['forum/infinitescroll'], function(infinitescroll) { var Groups = {}; Groups.init = function() { @@ -13,6 +13,8 @@ define('forum/groups/list', function() { ajaxify.go('groups/' + groupSlug); }); + infinitescroll.init(Groups.loadMoreGroups); + // Group creation $('button[data-action="new"]').on('click', function() { bootbox.prompt('Group Name:', function(name) { @@ -29,13 +31,43 @@ define('forum/groups/list', function() { } }); }); + var params = utils.params(); + $('#search-sort').val(params.sort || 'alpha'); // Group searching $('#search-text').on('keyup', Groups.search); $('#search-button').on('click', Groups.search); - $('#search-sort').on('change', Groups.search); + $('#search-sort').on('change', function() { + ajaxify.go('groups?sort=' + $('#search-sort').val()); + }); }; + Groups.loadMoreGroups = function(direction) { + if (direction < 0) { + return; + } + + infinitescroll.loadMore('groups.loadMore', { + sort: $('#search-sort').val(), + after: $('[component="groups/container"]').attr('data-nextstart') + }, function(data, done) { + if (data && data.groups.length) { + templates.parse('partials/groups/list', { + groups: data.groups + }, function(html) { + $('#groups-list').append(html); + done(); + }); + } else { + done(); + } + + if (data && data.nextStart) { + $('[component="groups/container"]').attr('data-nextstart', data.nextStart); + } + }); + } + Groups.search = function() { var groupsEl = $('#groups-list'), queryEl = $('#search-text'), @@ -44,9 +76,8 @@ define('forum/groups/list', function() { socket.emit('groups.search', { query: queryEl.val(), options: { - expand: true, - truncateUserList: true, - sort: sortEl.val() + sort: sortEl.val(), + filterHidden: true } }, function(err, groups) { templates.parse('partials/groups/list', { diff --git a/public/src/client/notifications.js b/public/src/client/notifications.js index 3d710cafca..2848704541 100644 --- a/public/src/client/notifications.js +++ b/public/src/client/notifications.js @@ -2,7 +2,7 @@ /* globals define, socket, app */ -define('forum/notifications', ['components'], function(components) { +define('forum/notifications', ['components', 'notifications'], function(components, notifs) { var Notifications = {}; Notifications.init = function() { @@ -25,6 +25,7 @@ define('forum/notifications', ['components'], function(components) { } components.get('notifications/item').removeClass('unread'); + notifs.updateNotifCount(0); }); }); }; diff --git a/public/src/client/popular.js b/public/src/client/popular.js index 0d05186faf..6331df49a6 100644 --- a/public/src/client/popular.js +++ b/public/src/client/popular.js @@ -2,11 +2,11 @@ /* globals define, app, socket*/ -define('forum/popular', ['forum/recent'], function(recent) { +define('forum/popular', function() { var Popular = {}; Popular.init = function() { - app.enterRoom('recent_posts'); + app.enterRoom('popular_topics'); selectActivePill(); }; diff --git a/public/src/client/recent.js b/public/src/client/recent.js index fc55ee9b08..3468fba152 100644 --- a/public/src/client/recent.js +++ b/public/src/client/recent.js @@ -15,7 +15,7 @@ define('forum/recent', ['forum/infinitescroll', 'components'], function(infinite }); Recent.init = function() { - app.enterRoom('recent_posts'); + app.enterRoom('recent_topics'); Recent.watchForNewPosts(); diff --git a/public/src/client/register.js b/public/src/client/register.js index 6955fcb989..cca8617972 100644 --- a/public/src/client/register.js +++ b/public/src/client/register.js @@ -24,6 +24,12 @@ define('forum/register', ['csrf', 'translator'], function(csrf, translator) { } }); + var query = utils.params(); + if (query.email && query.token) { + email.val(query.email); + $('#token').val(query.token); + } + // Update the "others can mention you via" text username.on('keyup', function() { $('#yourUsername').text(this.value.length > 0 ? utils.slugify(this.value) : 'username'); @@ -69,7 +75,15 @@ define('forum/register', ['csrf', 'translator'], function(csrf, translator) { 'x-csrf-token': csrf.get() }, success: function(data, status) { - window.location.href = data; + registerBtn.removeClass('disabled'); + if (!data) { + return; + } + if (data.referrer) { + window.location.href = data.referrer; + } else if (data.message) { + app.alert({message: data.message, timeout: 20000}); + } }, error: function(data, status) { var errorEl = $('#register-error-notify'); @@ -84,7 +98,7 @@ define('forum/register', ['csrf', 'translator'], function(csrf, translator) { }); }); - if(agreeTerms.length) { + if (agreeTerms.length) { agreeTerms.on('click', function() { if ($(this).prop('checked')) { register.removeAttr('disabled'); diff --git a/public/src/client/reset_code.js b/public/src/client/reset_code.js index 846feffb42..abbc4582db 100644 --- a/public/src/client/reset_code.js +++ b/public/src/client/reset_code.js @@ -5,7 +5,7 @@ define('forum/reset_code', function() { var ResetCode = {}; ResetCode.init = function() { - var reset_code = ajaxify.variables.get('reset_code'); + var reset_code = ajaxify.data.code; var resetEl = $('#reset'), password = $('#password'), diff --git a/public/src/client/search.js b/public/src/client/search.js index 027f76ca18..5633884ff5 100644 --- a/public/src/client/search.js +++ b/public/src/client/search.js @@ -123,8 +123,15 @@ define('forum/search', ['search', 'autocomplete'], function(searchModule, autoco $('.search-result-text').each(function() { var result = $(this); + var text = result.html().replace(regex, '$1'); - result.html(text).find('img').addClass('img-responsive'); + result.html(text).find('img').addClass('img-responsive').each(function() { + $(this).attr('src', $(this).attr('src').replace(/([\s\S]*?)<\/strong>/gi, '$1')); + }); + + result.find('a').each(function() { + $(this).attr('href', $(this).attr('href').replace(/([\s\S]*?)<\/strong>/gi, '$1')); + }); }); } catch(e) { return; diff --git a/public/src/client/tag.js b/public/src/client/tag.js index d2e6dd30b7..1758539b61 100644 --- a/public/src/client/tag.js +++ b/public/src/client/tag.js @@ -24,7 +24,7 @@ define('forum/tag', ['forum/recent', 'forum/infinitescroll'], function(recent, i } infinitescroll.loadMore('topics.loadMoreFromSet', { - set: 'tag:' + ajaxify.variables.get('tag') + ':topics', + set: 'tag:' + ajaxify.data.tag + ':topics', after: $('[component="category"]').attr('data-nextstart') }, function(data, done) { if (data.topics && data.topics.length) { diff --git a/public/src/client/topic.js b/public/src/client/topic.js index 95b84204fe..1cdd1cabbc 100644 --- a/public/src/client/topic.js +++ b/public/src/client/topic.js @@ -27,22 +27,30 @@ define('forum/topic', [ events.removeListeners(); } + + if (!data.url.startsWith('topic/')) { + require(['search'], function(search) { + if (search.topicDOM.active) { + search.topicDOM.end(); + } + }); + } }); Topic.init = function() { - var tid = ajaxify.variables.get('topic_id'); + var tid = ajaxify.data.tid; $(window).trigger('action:topic.loading'); app.enterRoom('topic_' + tid); - posts.processPage($('.topic')); + posts.processPage(components.get('post')); postTools.init(tid); threadTools.init(tid); events.init(); - sort.handleSort('topicPostSort', 'user.setTopicSort', 'topic/' + ajaxify.variables.get('topic_slug')); + sort.handleSort('topicPostSort', 'user.setTopicSort', 'topic/' + ajaxify.data.slug); enableInfiniteLoadingOrPagination(); @@ -50,7 +58,7 @@ define('forum/topic', [ handleBookmark(tid); - navigator.init('[component="post"]', ajaxify.variables.get('postcount'), Topic.toTop, Topic.toBottom, Topic.navigatorCallback, Topic.calculateIndex); + navigator.init('[component="post"]', ajaxify.data.postcount, Topic.toTop, Topic.toBottom, Topic.navigatorCallback, Topic.calculateIndex); $(window).on('scroll', updateTopicTitle); @@ -64,14 +72,42 @@ define('forum/topic', [ browsing.onUpdateUsersInRoom(data); }); } + + handleTopicSearch(); }; + function handleTopicSearch() { + require(['search', 'mousetrap'], function(search, Mousetrap) { + $('.topic-search') + .on('click', '.prev', function() { + search.topicDOM.prev(); + }) + .on('click', '.next', function() { + search.topicDOM.next(); + }); + + Mousetrap.bind('ctrl+f', function(e) { + if (config.topicSearchEnabled) { + // If in topic, open search window and populate, otherwise regular behaviour + var match = ajaxify.currentPage.match(/^topic\/([\d]+)/), + tid; + if (match) { + e.preventDefault(); + tid = match[1]; + $('#search-fields input').val('in:topic-' + tid + ' '); + app.prepareSearch(); + } + } + }); + }); + } + Topic.toTop = function() { navigator.scrollTop(0); }; Topic.toBottom = function() { - socket.emit('topics.postcount', ajaxify.variables.get('topic_id'), function(err, postCount) { + socket.emit('topics.postcount', ajaxify.data.tid, function(err, postCount) { if (config.topicPostSort !== 'oldest_to_newest') { postCount = 2; } @@ -85,7 +121,7 @@ define('forum/topic', [ if (postIndex && window.location.search.indexOf('page=') === -1) { navigator.scrollToPost(postIndex - 1, true); - } else if (bookmark && (!config.usePagination || (config.usePagination && pagination.currentPage === 1)) && ajaxify.variables.get('postcount') > 1) { + } else if (bookmark && (!config.usePagination || (config.usePagination && pagination.currentPage === 1)) && ajaxify.data.postcount > 1) { app.alert({ alert_id: 'bookmark', message: '[[topic:bookmark_instructions]]', @@ -126,14 +162,14 @@ define('forum/topic', [ } else { navigator.hide(); - pagination.init(parseInt(ajaxify.variables.get('currentPage'), 10), parseInt(ajaxify.variables.get('pageCount'), 10)); + pagination.init(parseInt(ajaxify.data.currentPage, 10), parseInt(ajaxify.data.pageCount, 10)); } } function updateTopicTitle() { if($(window).scrollTop() > 50) { - components.get('navbar/title').find('span').text(ajaxify.variables.get('topic_name')).show(); + components.get('navbar/title').find('span').text(ajaxify.data.title).show(); } else { components.get('navbar/title').find('span').text('').hide(); } @@ -161,10 +197,10 @@ define('forum/topic', [ } } - var currentBookmark = localStorage.getItem('topic:' + ajaxify.variables.get('topic_id') + ':bookmark'); + var currentBookmark = localStorage.getItem('topic:' + ajaxify.data.tid + ':bookmark'); if (!currentBookmark || parseInt(postIndex, 10) >= parseInt(currentBookmark, 10)) { - localStorage.setItem('topic:' + ajaxify.variables.get('topic_id') + ':bookmark', postIndex); + localStorage.setItem('topic:' + ajaxify.data.tid + ':bookmark', postIndex); app.removeAlert('bookmark'); } diff --git a/public/src/client/topic/browsing.js b/public/src/client/topic/browsing.js index 2c125ba4ec..0bed58f952 100644 --- a/public/src/client/topic/browsing.js +++ b/public/src/client/topic/browsing.js @@ -9,13 +9,13 @@ define('forum/topic/browsing', ['translator'], function(translator) { var Browsing = {}; Browsing.onUpdateUsersInRoom = function(data) { - if (data && data.room.indexOf('topic_' + ajaxify.variables.get('topic_id')) !== -1) { + if (data && data.room.indexOf('topic_' + ajaxify.data.tid) !== -1) { $('[component="topic/browsing/list"]').parent().toggleClass('hidden', !data.users.length); for(var i=0; i 0) { - var snippet = selection.toString(); - if (snippet.length) { - selectionText = '> ' + snippet.replace(/\n/g, '\n> ') + '\n\n'; - } + if ($(selection.baseNode).parents('[component="post/content"]').length > 0) { + var snippet = selection.toString(); + if (snippet.length) { + selectionText = '> ' + snippet.replace(/\n/g, '\n> ') + '\n\n'; } + } - var username = getUserName(selectionText ? $(selection.baseNode) : button); - if (getData(button, 'data-uid') === '0') { - username = ''; - } - if (selectionText.length) { - composer.addQuote(tid, ajaxify.variables.get('topic_slug'), getData(button, 'data-index'), getData(button, 'data-pid'), topicName, username, selectionText, topicUUID); - } else { - composer.newReply(tid, getData(button, 'data-pid'), topicName, username ? username + ' ' : ''); - } - }); - + var username = getUserName(selectionText ? $(selection.baseNode) : button); + if (getData(button, 'data-uid') === '0') { + username = ''; + } + if (selectionText.length) { + $(window).trigger('action:composer.addQuote', { + tid: tid, + slug: ajaxify.data.slug, + index: getData(button, 'data-index'), + pid: getData(button, 'data-pid'), + topicName: topicName, + username: username, + text: selectionText + }); + } else { + $(window).trigger('action:composer.post.new', { + tid: tid, + pid: getData(button, 'data-pid'), + topicName: topicName, + text: username ? username + ' ' : '' + }); + } } function onQuoteClicked(button, tid, topicName) { - require(['composer'], function(composer) { - var username = getUserName(button), - pid = getData(button, 'data-pid'), - topicUUID = composer.findByTid(tid); + var username = getUserName(button), + pid = getData(button, 'data-pid'); - socket.emit('posts.getRawPost', pid, function(err, post) { - if(err) { - return app.alertError(err.message); - } - var quoted = ''; - if(post) { - quoted = '> ' + post.replace(/\n/g, '\n> ') + '\n\n'; - } + socket.emit('posts.getRawPost', pid, function(err, post) { + if(err) { + return app.alertError(err.message); + } + var quoted = ''; + if(post) { + quoted = '> ' + post.replace(/\n/g, '\n> ') + '\n\n'; + } - if(topicUUID) { - composer.addQuote(tid, ajaxify.variables.get('topic_slug'), getData(button, 'data-index'), pid, topicName, username, quoted, topicUUID); - } else { - composer.newReply(tid, pid, topicName, '[[modules:composer.user_said, ' + username + ']]\n' + quoted); - } + $(window).trigger('action:composer.addQuote', { + tid: tid, + slug: ajaxify.data.slug, + index: getData(button, 'data-index'), + pid: pid, + username: username, + topicName: topicName, + text: quoted }); }); } @@ -216,7 +226,7 @@ define('forum/topic/postTools', ['share', 'navigator', 'components', 'translator } function showVotes(pid) { - socket.emit('posts.getVoters', {pid: pid, cid: ajaxify.variables.get('category_id')}, function(err, data) { + socket.emit('posts.getVoters', {pid: pid, cid: ajaxify.data.cid}, function(err, data) { if (err) { return app.alertError(err.message); } diff --git a/public/src/client/topic/posts.js b/public/src/client/topic/posts.js index 2104bc04fa..6532ae648c 100644 --- a/public/src/client/topic/posts.js +++ b/public/src/client/topic/posts.js @@ -13,7 +13,7 @@ define('forum/topic/posts', [ var Posts = {}; Posts.onNewPost = function(data) { - var tid = ajaxify.variables.get('topic_id'); + var tid = ajaxify.data.tid; if (data && data.posts && data.posts.length && parseInt(data.posts[0].tid, 10) !== parseInt(tid, 10)) { return; } @@ -133,8 +133,11 @@ define('forum/topic/posts', [ findInsertionPoint(); - data.title = $('
    ').text(ajaxify.variables.get('topic_name')).html(); - data.viewcount = ajaxify.variables.get('viewcount'); + data.title = $('
    ').text(ajaxify.data.title).html(); + data.slug = ajaxify.data.slug; + data.viewcount = ajaxify.data.viewcount; + + $(window).trigger('action:posts.loading', {posts: data.posts, after: after, before: before}); infinitescroll.parseAndTranslate('topic', 'posts', data, function(html) { if (after) { @@ -218,7 +221,7 @@ define('forum/topic/posts', [ }; function loadPostsAfter(after) { - var tid = ajaxify.variables.get('topic_id'); + var tid = ajaxify.data.tid; if (!utils.isNumber(tid) || !utils.isNumber(after) || (after === 0 && components.get('post', 'index', 1).length)) { return; } @@ -247,21 +250,21 @@ define('forum/topic/posts', [ }); } - Posts.processPage = function(element) { + Posts.processPage = function(posts) { app.createUserTooltips(); - app.replaceSelfLinks(element.find('a')); - utils.addCommasToNumbers(element.find('.formatted-number')); - utils.makeNumbersHumanReadable(element.find('.human-readable-number')); - element.find('.timeago').timeago(); - element.find('[component="post/content"] img:not(.emoji)').each(function() { + app.replaceSelfLinks(posts.find('a')); + utils.addCommasToNumbers(posts.find('.formatted-number')); + utils.makeNumbersHumanReadable(posts.find('.human-readable-number')); + posts.find('.timeago').timeago(); + posts.find('[component="post/content"] img:not(.emoji)').each(function() { var $this = $(this); if (!$this.parent().is('a')) { $this.wrap(''); } }); postTools.updatePostCount(); - addBlockquoteEllipses(element.find('[component="post/content"] > blockquote')); - hidePostToolsForDeletedPosts(element); + addBlockquoteEllipses(posts.find('[component="post/content"] > blockquote')); + hidePostToolsForDeletedPosts(posts); showBottomPostBar(); }; @@ -271,9 +274,11 @@ define('forum/topic/posts', [ } } - function hidePostToolsForDeletedPosts(element) { - element.find('[data-pid].deleted').each(function() { - postTools.toggle($(this).attr('data-pid'), true); + function hidePostToolsForDeletedPosts(posts) { + posts.each(function() { + if ($(this).hasClass('deleted')) { + postTools.toggle($(this).attr('data-pid'), true); + } }); } diff --git a/public/src/client/topic/threadTools.js b/public/src/client/topic/threadTools.js index 3bf8d539b2..95d1e61adb 100644 --- a/public/src/client/topic/threadTools.js +++ b/public/src/client/topic/threadTools.js @@ -23,22 +23,22 @@ define('forum/topic/threadTools', ['forum/topic/fork', 'forum/topic/move', 'comp }); components.get('topic/lock').on('click', function() { - socket.emit('topics.lock', {tids: [tid], cid: ajaxify.variables.get('category_id')}); + socket.emit('topics.lock', {tids: [tid], cid: ajaxify.data.cid}); return false; }); components.get('topic/unlock').on('click', function() { - socket.emit('topics.unlock', {tids: [tid], cid: ajaxify.variables.get('category_id')}); + socket.emit('topics.unlock', {tids: [tid], cid: ajaxify.data.cid}); return false; }); components.get('topic/pin').on('click', function() { - socket.emit('topics.pin', {tids: [tid], cid: ajaxify.variables.get('category_id')}); + socket.emit('topics.pin', {tids: [tid], cid: ajaxify.data.cid}); return false; }); components.get('topic/unpin').on('click', function() { - socket.emit('topics.unpin', {tids: [tid], cid: ajaxify.variables.get('category_id')}); + socket.emit('topics.unpin', {tids: [tid], cid: ajaxify.data.cid}); return false; }); @@ -55,7 +55,7 @@ define('forum/topic/threadTools', ['forum/topic/fork', 'forum/topic/move', 'comp }); components.get('topic/move').on('click', function(e) { - move.init([tid], ajaxify.variables.get('category_id')); + move.init([tid], ajaxify.data.cid); return false; }); @@ -95,7 +95,7 @@ define('forum/topic/threadTools', ['forum/topic/fork', 'forum/topic/move', 'comp translator.translate('[[topic:thread_tools.' + command + '_confirm]]', function(msg) { bootbox.confirm(msg, function(confirm) { if (confirm) { - socket.emit('topics.' + command, {tids: [tid], cid: ajaxify.variables.get('category_id')}); + socket.emit('topics.' + command, {tids: [tid], cid: ajaxify.data.cid}); } }); }); diff --git a/public/src/client/unread.js b/public/src/client/unread.js index 406116e9f1..47b96e54ad 100644 --- a/public/src/client/unread.js +++ b/public/src/client/unread.js @@ -12,7 +12,7 @@ define('forum/unread', ['forum/recent', 'topicSelect', 'forum/infinitescroll', ' }); Unread.init = function() { - app.enterRoom('recent_posts'); + app.enterRoom('unread_topics'); $('#new-topics-alert').on('click', function() { $(this).addClass('hide'); @@ -68,8 +68,6 @@ define('forum/unread', ['forum/recent', 'topicSelect', 'forum/infinitescroll', ' }); }); - socket.emit('categories.get', onCategoriesLoaded); - topicSelect.init(); if ($("body").height() <= $(window).height() && $('[component="category"]').children().length >= 20) { @@ -86,9 +84,11 @@ define('forum/unread', ['forum/recent', 'topicSelect', 'forum/infinitescroll', ' if(direction < 0 || !$('[component="category"]').length) { return; } - + var params = utils.params(); + var cid = params.cid; infinitescroll.loadMore('topics.loadMoreUnreadTopics', { - after: $('[component="category"]').attr('data-nextstart') + after: $('[component="category"]').attr('data-nextstart'), + cid: cid }, function(data, done) { if (data.topics && data.topics.length) { recent.onTopicsLoaded('unread', data.topics, true, done); @@ -118,31 +118,6 @@ define('forum/unread', ['forum/recent', 'topicSelect', 'forum/infinitescroll', ' } } - function onCategoriesLoaded(err, categories) { - createCategoryLinks(categories); - } - - function createCategoryLinks(categories) { - for (var i=0; i'); - - if (category.icon) { - link.append(' ' + category.name); - } else { - link.append(category.name); - } - - $('') - .append(link) - .appendTo($('.markread .dropdown-menu')); - } return Unread; }); diff --git a/public/src/client/users.js b/public/src/client/users.js index 9f440470b6..e0d4166a49 100644 --- a/public/src/client/users.js +++ b/public/src/client/users.js @@ -8,7 +8,7 @@ define('forum/users', ['translator'], function(translator) { var loadingMoreUsers = false; Users.init = function() { - + app.enterRoom('user_list'); var active = getActiveSection(); $('.nav-pills li').removeClass('active'); @@ -22,6 +22,8 @@ define('forum/users', ['translator'], function(translator) { handleSearch(); + handleInvite(); + socket.removeListener('event:user_status_change', onUserStatusChange); socket.on('event:user_status_change', onUserStatusChange); @@ -200,5 +202,22 @@ define('forum/users', ['translator'], function(translator) { return parts[parts.length - 1]; } + function handleInvite() { + $('[component="user/invite"]').on('click', function() { + bootbox.prompt('Email: ', function(email) { + if (!email) { + return; + } + + socket.emit('user.invite', email, function(err) { + if (err) { + return app.alertError(err.message); + } + app.alertSuccess('[[users:invitation-email-sent, ' + email + ']]'); + }); + }); + }); + } + return Users; }); diff --git a/public/src/modules/autocomplete.js b/public/src/modules/autocomplete.js index 55c15b8686..a66fff7264 100644 --- a/public/src/modules/autocomplete.js +++ b/public/src/modules/autocomplete.js @@ -6,32 +6,68 @@ define('autocomplete', function() { var module = {}; - module.user = function (input) { - return input.autocomplete({ - delay: 100, - source: function(request, response) { - socket.emit('user.search', {query: request.term}, function(err, result) { - if (err) { - return app.alertError(err.message); - } + module.user = function (input, onselect) { + app.loadJQueryUI(function() { + input.autocomplete({ + delay: 100, + select: onselect, + source: function(request, response) { + socket.emit('user.search', {query: request.term}, function(err, result) { + if (err) { + return app.alertError(err.message); + } - if (result && result.users) { - var names = result.users.map(function(user) { - return user && { - label: user.username, - value: user.username, - user: { - uid: user.uid, - name: user.username, - slug: user.userslug - } - }; - }); - response(names); - } - $('.ui-autocomplete a').attr('data-ajaxify', 'false'); - }); - } + if (result && result.users) { + var names = result.users.map(function(user) { + return user && { + label: user.username, + value: user.username, + user: { + uid: user.uid, + name: user.username, + slug: user.userslug + } + }; + }); + response(names); + } + $('.ui-autocomplete a').attr('data-ajaxify', 'false'); + }); + } + }); + }); + }; + + module.group = function(input, onselect) { + app.loadJQueryUI(function() { + input.autocomplete({ + delay: 100, + select: onselect, + source: function(request, response) { + socket.emit('groups.search', { + query: request.term + }, function(err, results) { + if (err) { + return app.alertError(err.message); + } + + if (results && results.length) { + var names = results.map(function(group) { + return group && { + label: group.name, + value: group.name, + group: { + name: group.name, + slug: group.slug + } + }; + }); + response(names); + } + $('.ui-autocomplete a').attr('data-ajaxify', 'false'); + }); + } + }); }); }; diff --git a/public/src/modules/chat.js b/public/src/modules/chat.js index 9c1183d6a1..b27b932eec 100644 --- a/public/src/modules/chat.js +++ b/public/src/modules/chat.js @@ -179,28 +179,31 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra chatModal.css('zIndex', 100); chatModal.appendTo($('body')); module.center(chatModal); - chatModal.draggable({ - start:function() { - module.bringModalToTop(chatModal); - }, - stop:function() { - chatModal.find('#chat-message-input').focus(); - }, - distance: 10, - handle: '.modal-header' - }); - chatModal.find('.modal-content').resizable({ - minHeight: 250, - minWidth: 400 - }); + app.loadJQueryUI(function() { + chatModal.find('.modal-content').resizable({ + minHeight: 250, + minWidth: 400 + }); - chatModal.find('.modal-content').on('resize', function(event, ui) { - if (ui.originalSize.height === ui.size.height) { - return; - } + chatModal.find('.modal-content').on('resize', function(event, ui) { + if (ui.originalSize.height === ui.size.height) { + return; + } - chatModal.find('#chat-content').css('height', module.calculateChatListHeight(chatModal)); + chatModal.find('#chat-content').css('height', module.calculateChatListHeight(chatModal)); + }); + + chatModal.draggable({ + start:function() { + module.bringModalToTop(chatModal); + }, + stop:function() { + chatModal.find('#chat-message-input').focus(); + }, + distance: 10, + handle: '.modal-header' + }); }); chatModal.find('#chat-with-name').html(username); diff --git a/public/src/modules/composer.js b/public/src/modules/composer.js deleted file mode 100644 index 8ec6b7ca2d..0000000000 --- a/public/src/modules/composer.js +++ /dev/null @@ -1,593 +0,0 @@ -'use strict'; - -/* globals define, socket, app, config, ajaxify, utils, templates, bootbox */ - -define('composer', [ - 'taskbar', - 'translator', - 'composer/controls', - 'composer/uploads', - 'composer/formatting', - 'composer/drafts', - 'composer/tags', - 'composer/categoryList', - 'composer/preview', - 'composer/resize' -], function(taskbar, translator, controls, uploads, formatting, drafts, tags, categoryList, preview, resize) { - var composer = { - active: undefined, - posts: {}, - bsEnvironment: undefined, - formatting: [] - }; - - $(window).off('resize', onWindowResize).on('resize', onWindowResize); - - $(window).on('action:composer.topics.post', function(ev, data) { - localStorage.removeItem('category:' + data.data.cid + ':bookmark'); - localStorage.removeItem('category:' + data.data.cid + ':bookmark:clicked'); - }); - - $(window).on('popstate', function(ev, data) { - var env = utils.findBootstrapEnvironment(); - - if (composer.active && (env === 'xs' || env ==='sm')) { - if (!composer.posts[composer.active].modified) { - discard(composer.active); - return; - } - - translator.translate('[[modules:composer.discard]]', function(translated) { - bootbox.confirm(translated, function(confirm) { - if (confirm) { - discard(composer.active); - } - }); - }); - } - }); - - function removeComposerHistory() { - var env = utils.findBootstrapEnvironment(); - if (env === 'xs' || env ==='sm') { - history.back(); - } - } - - // Query server for formatting options - socket.emit('modules.composer.getFormattingOptions', function(err, options) { - composer.formatting = options; - }); - - function onWindowResize() { - if (composer.active !== undefined) { - resize.reposition($('#cmp-uuid-' + composer.active)); - } - } - - function alreadyOpen(post) { - // If a composer for the same cid/tid/pid is already open, return the uuid, else return bool false - var type, id; - - if (post.hasOwnProperty('cid')) { - type = 'cid'; - } else if (post.hasOwnProperty('tid')) { - type = 'tid'; - } else if (post.hasOwnProperty('pid')) { - type = 'pid'; - } - - id = post[type]; - - // Find a match - for(var uuid in composer.posts) { - if (composer.posts[uuid].hasOwnProperty(type) && id === composer.posts[uuid][type]) { - return uuid; - } - } - - // No matches... - return false; - } - - function push(post) { - var uuid = utils.generateUUID(), - existingUUID = alreadyOpen(post); - - if (existingUUID) { - taskbar.updateActive(existingUUID); - return composer.load(existingUUID); - } - - translator.translate('[[topic:composer.new_topic]]', function(newTopicStr) { - taskbar.push('composer', uuid, { - title: post.title ? post.title : newTopicStr - }); - }); - - // Construct a save_id - if (0 !== parseInt(app.user.uid, 10)) { - if (post.hasOwnProperty('cid')) { - post.save_id = ['composer', app.user.uid, 'cid', post.cid].join(':'); - } else if (post.hasOwnProperty('tid')) { - post.save_id = ['composer', app.user.uid, 'tid', post.tid].join(':'); - } else if (post.hasOwnProperty('pid')) { - post.save_id = ['composer', app.user.uid, 'pid', post.pid].join(':'); - } - } - - composer.posts[uuid] = post; - composer.load(uuid); - } - - function composerAlert(post_uuid, message) { - $('#cmp-uuid-' + post_uuid).find('.composer-submit').removeAttr('disabled'); - app.alert({ - type: 'danger', - timeout: 3000, - title: '', - message: message, - alert_id: 'post_error' - }); - } - - composer.findByTid = function(tid) { - // Iterates through the initialised composers and returns the uuid of the matching composer - for(var uuid in composer.posts) { - if (composer.posts.hasOwnProperty(uuid) && composer.posts[uuid].hasOwnProperty('tid') && parseInt(composer.posts[uuid].tid, 10) === parseInt(tid, 10)) { - return uuid; - } - } - - return null; - }; - - composer.addButton = function(iconClass, onClick) { - formatting.addButton(iconClass, onClick); - }; - - composer.newTopic = function(cid) { - socket.emit('categories.isModerator', cid, function(err, isMod) { - if (err) { - return app.alertError(err.message); - } - push({ - cid: cid, - title: '', - body: '', - modified: false, - isMain: true, - isMod: isMod - }); - }); - }; - - composer.addQuote = function(tid, topicSlug, postIndex, pid, title, username, text, uuid) { - uuid = uuid || composer.active; - - if (uuid === undefined) { - composer.newReply(tid, pid, title, '[[modules:composer.user_said, ' + username + ']]\n' + text); - return; - } else if (uuid !== composer.active) { - // If the composer is not currently active, activate it - composer.load(uuid); - } - - var postContainer = $('#cmp-uuid-' + uuid); - var bodyEl = postContainer.find('textarea'); - var prevText = bodyEl.val(); - if (parseInt(tid, 10) !== parseInt(composer.posts[uuid].tid, 10)) { - var link = '[' + title + '](/topic/' + topicSlug + '/' + (parseInt(postIndex, 10) + 1) + ')'; - translator.translate('[[modules:composer.user_said_in, ' + username + ', ' + link + ']]\n', config.defaultLang, onTranslated); - } else { - translator.translate('[[modules:composer.user_said, ' + username + ']]\n', config.defaultLang, onTranslated); - } - - function onTranslated(translated) { - composer.posts[uuid].body = (prevText.length ? prevText + '\n\n' : '') + translated + text; - bodyEl.val(composer.posts[uuid].body); - focusElements(postContainer); - preview.render(postContainer); - } - }; - - composer.newReply = function(tid, pid, title, text) { - socket.emit('topics.isModerator', tid, function(err, isMod) { - if (err) { - return app.alertError(err.message); - } - translator.translate(text, config.defaultLang, function(translated) { - push({ - tid: tid, - toPid: pid, - title: $('
    ').text(title).html(), - body: translated, - modified: false, - isMain: false, - isMod: isMod - }); - }); - }); - }; - - composer.editPost = function(pid) { - socket.emit('modules.composer.push', pid, function(err, threadData) { - if(err) { - return app.alertError(err.message); - } - - push({ - pid: pid, - uid: threadData.uid, - handle: threadData.handle, - title: threadData.title, - body: threadData.body, - modified: false, - isMain: threadData.isMain, - topic_thumb: threadData.topic_thumb, - tags: threadData.tags - }); - }); - }; - - composer.load = function(post_uuid) { - var postContainer = $('#cmp-uuid-' + post_uuid); - if (postContainer.length) { - activate(post_uuid); - resize.reposition(postContainer); - focusElements(postContainer); - } else { - createNewComposer(post_uuid); - } - - startNotifyTyping(composer.posts[post_uuid]); - }; - - function startNotifyTyping(postData) { - function emit() { - socket.emit('modules.composer.notifyTyping', { - tid: postData.tid, - uid: app.user.uid - }); - } - - if (!parseInt(postData.tid, 10)) { - return; - } - - stopNotifyInterval(postData); - - emit(); - postData.notifyTypingIntervalId = setInterval(emit, 5000); - } - - function stopNotifyTyping(postData) { - if (!parseInt(postData.tid, 10)) { - return; - } - socket.emit('modules.composer.stopNotifyTyping', { - tid: postData.tid, - uid: app.user.uid - }); - } - - function stopNotifyInterval(postData) { - if (postData.notifyTypingIntervalId) { - clearInterval(postData.notifyTypingIntervalId); - postData.notifyTypingIntervalId = 0; - } - } - - function createNewComposer(post_uuid) { - var postData = composer.posts[post_uuid]; - - var allowTopicsThumbnail = config.allowTopicsThumbnail && postData.isMain && (config.hasImageUploadPlugin || config.allowFileUploads), - isTopic = postData ? !!postData.cid : false, - isMain = postData ? !!postData.isMain : false, - isEditing = postData ? !!postData.pid : false, - isGuestPost = postData ? parseInt(postData.uid, 10) === 0 : false; - - composer.bsEnvironment = utils.findBootstrapEnvironment(); - - // see - // https://github.com/NodeBB/NodeBB/issues/2994 and - // https://github.com/NodeBB/NodeBB/issues/1951 - // remove when 1951 is resolved - - var title = postData.title.replace(/%/g, '%').replace(/,/g, ','); - - var data = { - title: title, - mobile: composer.bsEnvironment === 'xs' || composer.bsEnvironment === 'sm', - allowTopicsThumbnail: allowTopicsThumbnail, - isTopicOrMain: isTopic || isMain, - minimumTagLength: config.minimumTagLength, - maximumTagLength: config.maximumTagLength, - isTopic: isTopic, - isEditing: isEditing, - showHandleInput: config.allowGuestHandles && (app.user.uid === 0 || (isEditing && isGuestPost && app.user.isAdmin)), - handle: postData ? postData.handle || '' : undefined, - formatting: composer.formatting, - isAdminOrMod: app.user.isAdmin || postData.isMod - }; - - if (data.mobile) { - ajaxify.go('compose', function() { - renderComposer(); - }); - } else { - renderComposer(); - } - - function renderComposer() { - parseAndTranslate('composer', data, function(composerTemplate) { - if ($('#cmp-uuid-' + post_uuid).length) { - return; - } - composerTemplate = $(composerTemplate); - - composerTemplate.attr('id', 'cmp-uuid-' + post_uuid); - - $(document.body).append(composerTemplate); - - var postContainer = $(composerTemplate[0]), - bodyEl = postContainer.find('textarea'), - draft = drafts.getDraft(postData.save_id), - submitBtn = postContainer.find('.composer-submit'); - - preview.handleToggler(postContainer); - tags.init(postContainer, composer.posts[post_uuid]); - categoryList.init(postContainer, composer.posts[post_uuid]); - - activate(post_uuid); - resize.reposition(postContainer); - - if (config.allowFileUploads || config.hasImageUploadPlugin) { - uploads.initialize(post_uuid); - } - - formatting.addHandler(postContainer); - - if (allowTopicsThumbnail) { - uploads.toggleThumbEls(postContainer, composer.posts[post_uuid].topic_thumb || ''); - } - - postContainer.on('change', 'input, textarea', function() { - composer.posts[post_uuid].modified = true; - }); - - submitBtn.on('click', function() { - var action = $(this).attr('data-action'); - - switch(action) { - case 'post-lock': - $(this).attr('disabled', true); - post(post_uuid, {lock: true}); - break; - - case 'post': // intentional fall-through - default: - $(this).attr('disabled', true); - post(post_uuid); - break; - } - }); - - postContainer.on('click', 'a[data-switch-action]', function() { - var action = $(this).attr('data-switch-action'), - label = $(this).html(); - - submitBtn.attr('data-action', action).html(label); - }); - - postContainer.find('.composer-discard').on('click', function() { - if (!composer.posts[post_uuid].modified) { - removeComposerHistory(); - discard(post_uuid); - return; - } - var btn = $(this).prop('disabled', true); - translator.translate('[[modules:composer.discard]]', function(translated) { - bootbox.confirm(translated, function(confirm) { - if (confirm) { - removeComposerHistory(); - discard(post_uuid); - } - btn.prop('disabled', false); - }); - }); - }); - - postContainer.on('click', function() { - if (!taskbar.isActive(post_uuid)) { - taskbar.updateActive(post_uuid); - } - }); - - bodyEl.on('input propertychange', function() { - preview.render(postContainer); - }); - - bodyEl.on('scroll', function() { - preview.matchScroll(postContainer); - }); - - bodyEl.val(draft ? draft : postData.body); - - preview.render(postContainer, function() { - preview.matchScroll(postContainer); - }); - - drafts.init(postContainer, postData); - - resize.handleResize(postContainer); - - handleHelp(postContainer); - - $(window).trigger('action:composer.loaded', { - post_uuid: post_uuid - }); - - formatting.addComposerButtons(); - focusElements(postContainer); - }); - } - } - - function parseAndTranslate(template, data, callback) { - templates.parse(template, data, function(composerTemplate) { - translator.translate(composerTemplate, callback); - }); - } - - function handleHelp(postContainer) { - var helpBtn = postContainer.find('.help'); - socket.emit('modules.composer.renderHelp', function(err, html) { - if (!err && html && html.length > 0) { - helpBtn.removeClass('hidden'); - helpBtn.on('click', function() { - bootbox.alert(html); - }); - } - }); - } - - function activate(post_uuid) { - if(composer.active && composer.active !== post_uuid) { - composer.minimize(composer.active); - } - - composer.active = post_uuid; - } - - function focusElements(postContainer) { - var title = postContainer.find('input.title'); - if (title.length) { - title.focus(); - } else { - postContainer.find('textarea').focus().putCursorAtEnd(); - } - } - - function post(post_uuid, options) { - var postData = composer.posts[post_uuid], - postContainer = $('#cmp-uuid-' + post_uuid), - handleEl = postContainer.find('.handle'), - titleEl = postContainer.find('.title'), - bodyEl = postContainer.find('textarea'), - thumbEl = postContainer.find('input#topic-thumb-url'); - - options = options || {}; - - titleEl.val(titleEl.val().trim()); - bodyEl.val(bodyEl.val().trim()); - if (thumbEl.length) { - thumbEl.val(thumbEl.val().trim()); - } - - var checkTitle = (parseInt(postData.cid, 10) || parseInt(postData.pid, 10)) && postContainer.find('input.title').length; - - if (uploads.inProgress[post_uuid] && uploads.inProgress[post_uuid].length) { - return composerAlert(post_uuid, '[[error:still-uploading]]'); - } else if (checkTitle && titleEl.val().length < parseInt(config.minimumTitleLength, 10)) { - return composerAlert(post_uuid, '[[error:title-too-short, ' + config.minimumTitleLength + ']]'); - } else if (checkTitle && titleEl.val().length > parseInt(config.maximumTitleLength, 10)) { - return composerAlert(post_uuid, '[[error:title-too-long, ' + config.maximumTitleLength + ']]'); - } else if (checkTitle && !utils.slugify(titleEl.val()).length) { - return composerAlert(post_uuid, '[[error:invalid-title]]'); - } else if (bodyEl.val().length < parseInt(config.minimumPostLength, 10)) { - return composerAlert(post_uuid, '[[error:content-too-short, ' + config.minimumPostLength + ']]'); - } else if (bodyEl.val().length > parseInt(config.maximumPostLength, 10)) { - return composerAlert(post_uuid, '[[error:content-too-long, ' + config.maximumPostLength + ']]'); - } - - var composerData = {}, action; - - if (parseInt(postData.cid, 10) > 0) { - action = 'topics.post'; - composerData = { - handle: handleEl ? handleEl.val() : undefined, - title: titleEl.val(), - content: bodyEl.val(), - topic_thumb: thumbEl.val() || '', - category_id: postData.cid, - tags: tags.getTags(post_uuid), - lock: options.lock || false - }; - } else if (parseInt(postData.tid, 10) > 0) { - action = 'posts.reply'; - composerData = { - tid: postData.tid, - handle: handleEl ? handleEl.val() : undefined, - content: bodyEl.val(), - toPid: postData.toPid, - lock: options.lock || false - }; - } else if (parseInt(postData.pid, 10) > 0) { - action = 'posts.edit'; - composerData = { - pid: postData.pid, - handle: handleEl ? handleEl.val() : undefined, - content: bodyEl.val(), - title: titleEl.val(), - topic_thumb: thumbEl.val() || '', - tags: tags.getTags(post_uuid) - }; - } - - socket.emit(action, composerData, function (err, data) { - postContainer.find('.composer-submit').removeAttr('disabled'); - if (err) { - if (err.message === '[[error:email-not-confirmed]]') { - return app.showEmailConfirmWarning(err); - } - - return app.alertError(err.message); - } - - discard(post_uuid); - drafts.removeDraft(postData.save_id); - - if (action === 'topics.post') { - ajaxify.go('topic/' + data.slug); - } else { - removeComposerHistory(); - } - - $(window).trigger('action:composer.' + action, {composerData: composerData, data: data}); - }); - } - - function discard(post_uuid) { - if (composer.posts[post_uuid]) { - $('#cmp-uuid-' + post_uuid).remove(); - drafts.removeDraft(composer.posts[post_uuid].save_id); - stopNotifyInterval(composer.posts[post_uuid]); - stopNotifyTyping(composer.posts[post_uuid]); - - delete composer.posts[post_uuid]; - composer.active = undefined; - taskbar.discard('composer', post_uuid); - $('body').css({'margin-bottom': 0}); - $('[data-action="post"]').removeAttr('disabled'); - - - $('html').removeClass('composing mobile'); - - } - } - - composer.minimize = function(post_uuid) { - var postContainer = $('#cmp-uuid-' + post_uuid); - postContainer.css('visibility', 'hidden'); - composer.active = undefined; - taskbar.minimize('composer', post_uuid); - - stopNotifyInterval(composer.posts[post_uuid]); - stopNotifyTyping(composer.posts[post_uuid]); - - $('body').css({'margin-bottom': '0px'}); - }; - - return composer; -}); diff --git a/public/src/modules/composer/categoryList.js b/public/src/modules/composer/categoryList.js deleted file mode 100644 index 1eb86051bf..0000000000 --- a/public/src/modules/composer/categoryList.js +++ /dev/null @@ -1,44 +0,0 @@ - -'use strict'; - -/*globals define, config, socket, app*/ - -define('composer/categoryList', function() { - var categoryList = {}; - - categoryList.init = function(postContainer, postData) { - var listEl = postContainer.find('.category-list'); - if (!listEl.length) { - return; - } - - socket.emit('categories.getCategoriesByPrivilege', 'topics:create', function(err, categories) { - if (err) { - return app.alertError(err.message); - } - - // Remove categories that are just external links - categories = categories.filter(function(category) { - return !category.link; - }); - - categories.forEach(function(category) { - $('').appendTo(listEl); - }); - - if (postData.cid) { - listEl.find('option[value="' + postData.cid + '"]').prop('selected', true); - } - }); - - listEl.on('change', function() { - if (postData.cid) { - postData.cid = this.value; - } - - $('[tabindex=' + (parseInt($(this).attr('tabindex'), 10) + 1) + ']').trigger('focus'); - }); - }; - - return categoryList; -}); diff --git a/public/src/modules/composer/controls.js b/public/src/modules/composer/controls.js deleted file mode 100644 index 05b6bdcf3c..0000000000 --- a/public/src/modules/composer/controls.js +++ /dev/null @@ -1,46 +0,0 @@ -"use strict"; - -/*global define*/ - -define('composer/controls', function() { - var controls = {}; - - /*************************************************/ - /* Rich Textarea Controls */ - /*************************************************/ - controls.insertIntoTextarea = function(textarea, value) { - var $textarea = $(textarea); - var currentVal = $textarea.val(); - - $textarea.val( - currentVal.slice(0, textarea.selectionStart) + - value + - currentVal.slice(textarea.selectionStart) - ); - }; - - controls.wrapSelectionInTextareaWith = function(textarea, leading, trailing){ - if(trailing === undefined){ - trailing = leading; - } - - var $textarea = $(textarea); - var currentVal = $textarea.val(); - - $textarea.val( - currentVal.slice(0, textarea.selectionStart) + - leading + - currentVal.slice(textarea.selectionStart, textarea.selectionEnd) + - trailing + - currentVal.slice(textarea.selectionEnd) - ); - }; - - controls.updateTextareaSelection = function(textarea, start, end){ - textarea.setSelectionRange(start, end); - $(textarea).focus(); - }; - - - return controls; -}); \ No newline at end of file diff --git a/public/src/modules/composer/drafts.js b/public/src/modules/composer/drafts.js deleted file mode 100644 index 27370607ca..0000000000 --- a/public/src/modules/composer/drafts.js +++ /dev/null @@ -1,69 +0,0 @@ -'use strict'; - -/* globals define */ - -define('composer/drafts', function() { - - var drafts = {}; - var saveThrottleId; - var saving = false; - - drafts.init = function(postContainer, postData) { - - var bodyEl = postContainer.find('textarea'); - bodyEl.on('keyup', function() { - resetTimeout(); - - saveThrottleId = setTimeout(function() { - saveDraft(postContainer, postData); - }, 1000); - }); - }; - - function resetTimeout() { - if (saveThrottleId) { - clearTimeout(saveThrottleId); - saveThrottleId = 0; - } - } - - drafts.getDraft = function(save_id) { - return localStorage.getItem(save_id); - }; - - function saveDraft(postContainer, postData) { - var raw; - - if (canSave() && postData && postData.save_id && postContainer.length) { - raw = postContainer.find('textarea').val(); - if (raw.length) { - localStorage.setItem(postData.save_id, raw); - } else { - drafts.removeDraft(postData.save_id); - } - } - } - - drafts.removeDraft = function(save_id) { - resetTimeout(); - return localStorage.removeItem(save_id); - }; - - function canSave() { - if (saving) { - return saving; - } - - try { - localStorage.setItem('test', 'test'); - localStorage.removeItem('test'); - saving = true; - return true; - } catch(e) { - saving = false; - return false; - } - } - - return drafts; -}); \ No newline at end of file diff --git a/public/src/modules/composer/formatting.js b/public/src/modules/composer/formatting.js deleted file mode 100644 index 05315a7edf..0000000000 --- a/public/src/modules/composer/formatting.js +++ /dev/null @@ -1,58 +0,0 @@ -'use strict'; - -/* globals define */ - -define('composer/formatting', ['composer/controls', 'composer/preview'], function(controls, preview) { - - var formatting = {}; - - var formattingDispatchTable = { - 'picture': function(){ - $('#files').click(); - }, - - upload: function(){ - $('#files').click(); - }, - - tags: function() { - $('.tags-container').toggleClass('hidden'); - } - }; - - var buttons = []; - - formatting.addComposerButtons = function() { - for(var x=0,numButtons=buttons.length;x'); - } - }; - - formatting.addButton = function(iconClass, onClick) { - var name = iconClass.replace('fa fa-', ''); - - formattingDispatchTable[name] = onClick; - buttons.push({ - name: name, - iconClass: iconClass - }); - }; - - formatting.addButtonDispatch = function(name, onClick) { - formattingDispatchTable[name] = onClick; - }; - - formatting.addHandler = function(postContainer) { - postContainer.on('click', '.formatting-bar span', function () { - var format = $(this).attr('data-format'), - textarea = $(this).parents('.composer').find('textarea')[0]; - - if(formattingDispatchTable.hasOwnProperty(format)){ - formattingDispatchTable[format](textarea, textarea.selectionStart, textarea.selectionEnd); - preview.render(postContainer); - } - }); - }; - - return formatting; -}); diff --git a/public/src/modules/composer/preview.js b/public/src/modules/composer/preview.js deleted file mode 100644 index e6bab0cb4e..0000000000 --- a/public/src/modules/composer/preview.js +++ /dev/null @@ -1,83 +0,0 @@ -'use strict'; - -/* globals define, socket*/ - -define('composer/preview', function() { - var preview = {}; - - var timeoutId = 0; - - preview.render = function(postContainer, callback) { - callback = callback || function() {}; - if (timeoutId) { - clearTimeout(timeoutId); - timeoutId = 0; - } - var textarea = postContainer.find('textarea'); - - timeoutId = setTimeout(function() { - socket.emit('modules.composer.renderPreview', textarea.val(), function(err, preview) { - timeoutId = 0; - if (err) { - return; - } - preview = $(preview); - preview.find('img').addClass('img-responsive'); - postContainer.find('.preview').html(preview); - $(window).trigger('action:composer.preview'); - callback(); - }); - }, 250); - }; - - preview.matchScroll = function(postContainer) { - var textarea = postContainer.find('textarea'); - var preview = postContainer.find('.preview'); - var diff = textarea[0].scrollHeight - textarea.height(); - - if (diff === 0) { - return; - } - - var scrollPercent = textarea.scrollTop() / diff; - - preview.scrollTop(Math.max(preview[0].scrollHeight - preview.height(), 0) * scrollPercent); - }; - - preview.handleToggler = function(postContainer) { - function hidePreview() { - togglePreview(false); - localStorage.setItem('composer:previewToggled', true); - } - - function showPreview() { - togglePreview(true); - localStorage.removeItem('composer:previewToggled'); - } - - function togglePreview(show) { - previewContainer.toggleClass('hide', !show); - writeContainer.toggleClass('maximized', !show); - showBtn.toggleClass('hide', show); - - $('.write').focus(); - preview.matchScroll(postContainer); - } - - var showBtn = postContainer.find('.write-container .toggle-preview'), - hideBtn = postContainer.find('.preview-container .toggle-preview'), - previewContainer = $('.preview-container'), - writeContainer = $('.write-container'); - - hideBtn.on('click', hidePreview); - showBtn.on('click', showPreview); - - if (localStorage.getItem('composer:previewToggled')) { - hidePreview(); - } else { - showPreview(); - } - }; - - return preview; -}); \ No newline at end of file diff --git a/public/src/modules/composer/resize.js b/public/src/modules/composer/resize.js deleted file mode 100644 index 23b5f67adb..0000000000 --- a/public/src/modules/composer/resize.js +++ /dev/null @@ -1,195 +0,0 @@ - -'use strict'; - -/* globals app, define, config, utils*/ - -define('composer/resize', ['autosize'], function(autosize) { - var resize = {}, - oldPercentage = 0; - - resize.reposition = function(postContainer) { - var percentage = localStorage.getItem('composer:resizePercentage') || 0.5; - - doResize(postContainer, percentage); - }; - - function doResize(postContainer, percentage) { - var env = utils.findBootstrapEnvironment(); - - - // todo, lump in browsers that don't support transform (ie8) here - // at this point we should use modernizr - if (env === 'sm' || env === 'xs' || window.innerHeight < 480) { - $('html').addClass('composing mobile'); - autosize(postContainer.find('textarea')[0]); - percentage = 1; - } else { - $('html').removeClass('composing mobile'); - } - - if (percentage) { - var max = getMaximumPercentage(); - - if (percentage < 0.25) { - percentage = 0.25; - } else if (percentage > max) { - percentage = max; - } - - if (env === 'md' || env === 'lg') { - var transform = 'translate(0, ' + (Math.abs(1-percentage) * 100) + '%)'; - postContainer.css({ - '-webkit-transform': transform, - '-moz-transform': transform, - '-ms-transform': transform, - '-o-transform': transform, - 'transform': transform - }); - } else { - postContainer.removeAttr('style'); - } - } - - postContainer.percentage = percentage; - - if (config.hasImageUploadPlugin) { - postContainer.find('.img-upload-btn').removeClass('hide'); - postContainer.find('#files.lt-ie9').removeClass('hide'); - } - - if (config.allowFileUploads) { - postContainer.find('.file-upload-btn').removeClass('hide'); - postContainer.find('#files.lt-ie9').removeClass('hide'); - } - - postContainer.css('visibility', 'visible'); - - // Add some extra space at the bottom of the body so that the user can still scroll to the last post w/ composer open - $('body').css({'margin-bottom': postContainer.css('height')}); - - resizeWritePreview(postContainer); - } - - resize.handleResize = function(postContainer) { - function resizeStart(e) { - var resizeRect = resizeEl[0].getBoundingClientRect(), - resizeCenterY = resizeRect.top + (resizeRect.height / 2); - - resizeOffset = (resizeCenterY - e.clientY) / 2; - resizeActive = true; - resizeDown = e.clientY; - - $(window).on('mousemove', resizeAction); - $(window).on('mouseup', resizeStop); - $('body').on('touchmove', resizeTouchAction); - } - - function resizeStop(e) { - resizeActive = false; - - postContainer.find('textarea').focus(); - $(window).off('mousemove', resizeAction); - $(window).off('mouseup', resizeStop); - $('body').off('touchmove', resizeTouchAction); - - var position = (e.clientY - resizeOffset), - newHeight = $(window).height() - position, - windowHeight = $(window).height(); - - if (newHeight > windowHeight - $('#header-menu').height() - (windowHeight / 15)) { - snapToTop = true; - } else { - snapToTop = false; - } - - toggleMaximize(e); - } - - function toggleMaximize(e) { - if (e.clientY - resizeDown === 0 || snapToTop) { - var newPercentage = getMaximumPercentage(); - - if (!postContainer.hasClass('maximized') || !snapToTop) { - oldPercentage = postContainer.percentage; - doResize(postContainer, newPercentage); - postContainer.addClass('maximized'); - } else { - doResize(postContainer, oldPercentage); - postContainer.removeClass('maximized'); - } - } - } - - function resizeTouchAction(e) { - e.preventDefault(); - resizeAction(e.touches[0]); - } - - function resizeAction(e) { - if (resizeActive) { - var position = (e.clientY - resizeOffset), - newHeight = $(window).height() - position; - - doResize(postContainer, newHeight / $(window).height()); - - resizeWritePreview(postContainer); - resizeSavePosition(newHeight); - - if (Math.abs(e.clientY - resizeDown) > 0) { - postContainer.removeClass('maximized'); - } - } - - e.preventDefault(); - return false; - } - - function resizeSavePosition(px) { - var percentage = px / $(window).height(), - max = getMaximumPercentage(); - localStorage.setItem('composer:resizePercentage', percentage < max ? percentage : max); - } - - var resizeActive = false, - resizeOffset = 0, - resizeDown = 0, - snapToTop = false, - resizeEl = postContainer.find('.resizer'); - - resizeEl - .on('mousedown', resizeStart) - .on('touchstart', function(e) { - e.preventDefault(); - resizeStart(e.touches[0]); - }) - .on('touchend', function(e) { - e.preventDefault(); - resizeStop(); - }); - }; - - function getMaximumPercentage() { - return ($(window).height() - $('#header-menu').height() - 1) / $(window).height(); - } - - function resizeWritePreview(postContainer) { - var total = getFormattingHeight(postContainer); - postContainer - .find('.write-preview-container') - .css('height', postContainer.percentage * $(window).height() - $('#header-menu').height() - total); - } - - function getFormattingHeight(postContainer) { - return [ - postContainer.find('.title-container').outerHeight(true), - postContainer.find('.formatting-bar').outerHeight(true), - postContainer.find('.topic-thumb-container').outerHeight(true), - $('.taskbar').height() - ].reduce(function(a, b) { - return a + b; - }); - } - - - return resize; -}); diff --git a/public/src/modules/composer/tags.js b/public/src/modules/composer/tags.js deleted file mode 100644 index f074a5abc2..0000000000 --- a/public/src/modules/composer/tags.js +++ /dev/null @@ -1,85 +0,0 @@ - -'use strict'; - -/*globals define, config, socket, app*/ - -define('composer/tags', function() { - var tags = {}; - - tags.init = function(postContainer, postData) { - var tagEl = postContainer.find('.tags'); - if (!tagEl.length) { - return; - } - - tagEl.tagsinput({ - maxTags: config.tagsPerTopic, - confirmKeys: [13, 44], - trimValue: true - }); - - tagEl.on('beforeItemAdd', function(event) { - event.cancel = event.item.length < config.minimumTagLength || event.item.length > config.maximumTagLength; - if (event.item.length < config.minimumTagLength) { - app.alertError('[[error:tag-too-short, ' + config.minimumTagLength + ']]'); - } else if (event.item.length > config.maximumTagLength) { - app.alertError('[[error:tag-too-long, ' + config.maximumTagLength + ']]'); - } - }); - - tagEl.on('itemAdded', function(event) { - $(window).trigger('action:tag.added', {cid: postData.cid, tagEl: tagEl, tag: event.item}); - }); - - addTags(postData.tags, tagEl); - - var input = postContainer.find('.bootstrap-tagsinput input'); - input.autocomplete({ - delay: 100, - source: function(request, response) { - socket.emit('topics.searchTags', {query: request.term, cid: postData.cid}, function(err, tags) { - if (err) { - return app.alertError(err.message); - } - if (tags) { - response(tags); - } - $('.ui-autocomplete a').attr('data-ajaxify', 'false'); - }); - }, - select: function(event, ui) { - // when autocomplete is selected from the dropdown simulate a enter key down to turn it into a tag - triggerEnter(input); - } - }); - - input.attr('tabIndex', tagEl.attr('tabIndex')); - input.on('blur', function() { - triggerEnter(input); - }); - }; - - function triggerEnter(input) { - // http://stackoverflow.com/a/3276819/583363 - var e = jQuery.Event('keypress'); - e.which = 13; - e.keyCode = 13; - setTimeout(function() { - input.trigger(e); - }, 100); - } - - function addTags(tags, tagEl) { - if (tags && tags.length) { - for(var i=0; i').closest('form').get(0).reset(); - $el.unwrap(); - } - - function initializeDragAndDrop(post_uuid) { - - function onDragEnter() { - if(draggingDocument) { - return; - } - drop.css('top', postContainer.find('.write-preview-container').position().top + 'px'); - drop.css('height', textarea.height()); - drop.css('line-height', textarea.height() + 'px'); - drop.show(); - - drop.on('dragleave', function() { - drop.hide(); - drop.off('dragleave'); - }); - } - - function onDragDrop(e) { - e.preventDefault(); - var files = e.files || (e.dataTransfer || {}).files || (e.target.value ? [e.target.value] : []), - fd; - - if(files.length) { - if (window.FormData) { - fd = new FormData(); - for (var i = 0; i < files.length; ++i) { - fd.append('files[]', files[i], files[i].name); - } - } - - uploadContentFiles({ - files: files, - post_uuid: post_uuid, - route: '/api/post/upload', - formData: fd - }); - } - - drop.hide(); - return false; - } - - function cancel(e) { - e.preventDefault(); - return false; - } - - if($.event.props.indexOf('dataTransfer') === -1) { - $.event.props.push('dataTransfer'); - } - - var draggingDocument = false; - - var postContainer = $('#cmp-uuid-' + post_uuid), - drop = postContainer.find('.imagedrop'), - textarea = postContainer.find('textarea'); - - $(document).off('dragstart').on('dragstart', function() { - draggingDocument = true; - }).off('dragend').on('dragend', function() { - draggingDocument = false; - }); - - textarea.on('dragenter', onDragEnter); - - drop.on('dragover', cancel); - drop.on('dragenter', cancel); - drop.on('drop', onDragDrop); - } - - function initializePaste(post_uuid) { - $(window).off('paste').on('paste', function(event) { - - var items = (event.clipboardData || event.originalEvent.clipboardData || {}).items, - fd; - - if(items && items.length) { - - var blob = items[0].getAsFile(); - if(blob) { - blob.name = 'upload-' + utils.generateUUID(); - - if (window.FormData) { - fd = new FormData(); - fd.append('files[]', blob, blob.name); - } - - uploadContentFiles({ - files: [blob], - post_uuid: post_uuid, - route: '/api/post/upload', - formData: fd - }); - } - } - }); - } - - function escapeRegExp(text) { - return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); - } - - function maybeParse(response) { - if (typeof response === 'string') { - try { - return $.parseJSON(response); - } catch (e) { - return {status: 500, message: 'Something went wrong while parsing server response'}; - } - } - return response; - } - - function insertText(str, index, insert) { - return str.slice(0, index) + insert + str.slice(index); - } - - function uploadContentFiles(params) { - var files = params.files, - post_uuid = params.post_uuid, - formData = params.formData, - postContainer = $('#cmp-uuid-' + post_uuid), - textarea = postContainer.find('textarea'), - text = textarea.val(), - uploadForm = postContainer.find('#fileForm'); - - uploadForm.attr('action', config.relative_path + params.route); - - for(var i = 0; i < files.length; ++i) { - var isImage = files[i].type.match(/image./); - - text = insertText(text, textarea.getCursorPosition(), (isImage ? '!' : '') + '[' + files[i].name + '](uploading...) '); - - if(files[i].size > parseInt(config.maximumFileSize, 10) * 1024) { - uploadForm[0].reset(); - return app.alertError('[[error:file-too-big, ' + config.maximumFileSize + ']]'); - } - } - - textarea.val(text); - - uploadForm.off('submit').submit(function() { - function updateTextArea(filename, text) { - var current = textarea.val(); - var re = new RegExp(escapeRegExp(filename) + "]\\([^)]+\\)", 'g'); - textarea.val(current.replace(re, filename + '](' + text + ')')); - } - - uploads.inProgress[post_uuid] = uploads.inProgress[post_uuid] || []; - uploads.inProgress[post_uuid].push(1); - - $(this).ajaxSubmit({ - headers: { - 'x-csrf-token': csrf.get() - }, - resetForm: true, - clearForm: true, - formData: formData, - - error: onUploadError, - - uploadProgress: function(event, position, total, percent) { - for(var i=0; i < files.length; ++i) { - updateTextArea(files[i].name, 'uploading ' + percent + '%'); - } - }, - - success: function(uploads) { - uploads = maybeParse(uploads); - - if(uploads && uploads.length) { - for(var i=0; i Leave Group'; + return ''; } else { if (groupObj.isPending) { return ''; } else if (groupObj.isInvited) { - return ''; + return ''; } else { - return ''; + return ''; } } }; diff --git a/public/src/modules/notifications.js b/public/src/modules/notifications.js index c3a9b984de..bf4313b066 100644 --- a/public/src/modules/notifications.js +++ b/public/src/modules/notifications.js @@ -47,7 +47,7 @@ define('notifications', ['sounds', 'translator'], function(sound, translator) { if (err) { return app.alertError(err.message); } - increaseNotifCount(-1); + incrementNotifCount(-1); }); }); @@ -56,7 +56,7 @@ define('notifications', ['sounds', 'translator'], function(sound, translator) { if (err) { app.alertError(err.message); } - updateNotifCount(0); + Notifications.updateNotifCount(0); }); }); @@ -73,33 +73,20 @@ define('notifications', ['sounds', 'translator'], function(sound, translator) { } liEl.toggleClass('unread'); - increaseNotifCount(unread ? -1 : 1); + incrementNotifCount(unread ? -1 : 1); }); }); - function updateNotifCount(count) { - if (count > 0) { - notifIcon.removeClass('fa-bell-o').addClass('fa-bell'); - } else { - notifIcon.removeClass('fa-bell').addClass('fa-bell-o'); - } - - notifIcon.toggleClass('unread-count', count > 0); - notifIcon.attr('data-content', count > 20 ? '20+' : count); - - Tinycon.setBubble(count); - } - - function increaseNotifCount(delta) { + function incrementNotifCount(delta) { var count = parseInt(notifIcon.attr('data-content'), 10) + delta; - updateNotifCount(count); + Notifications.updateNotifCount(count); } socket.emit('notifications.getCount', function(err, count) { if (!err) { - updateNotifCount(count); + Notifications.updateNotifCount(count); } else { - updateNotifCount(0); + Notifications.updateNotifCount(0); } }); @@ -117,15 +104,30 @@ define('notifications', ['sounds', 'translator'], function(sound, translator) { ajaxify.refresh(); } - increaseNotifCount(1); + incrementNotifCount(1); sound.play('notification'); }); socket.on('event:notifications.updateCount', function(count) { - updateNotifCount(count); + Notifications.updateNotifCount(count); }); }; + Notifications.updateNotifCount = function(count) { + var notifIcon = $('.notification-icon'); + + if (count > 0) { + notifIcon.removeClass('fa-bell-o').addClass('fa-bell'); + } else { + notifIcon.removeClass('fa-bell').addClass('fa-bell-o'); + } + + notifIcon.toggleClass('unread-count', count > 0); + notifIcon.attr('data-content', count > 20 ? '20+' : count); + + Tinycon.setBubble(count); + }; + return Notifications; }); diff --git a/public/src/modules/search.js b/public/src/modules/search.js index f7ae2243f4..957032c47f 100644 --- a/public/src/modules/search.js +++ b/public/src/modules/search.js @@ -96,7 +96,7 @@ define('search', ['navigator', 'translator'], function(nav, translator) { }; Search.checkPagePresence = function(tid, callback) { - if (parseInt(ajaxify.variables.get('topic_id'), 10) !== parseInt(tid, 10)) { + if (parseInt(ajaxify.data.tid, 10) !== parseInt(tid, 10)) { ajaxify.go('topic/' + tid, callback); } else { callback(); diff --git a/public/src/modules/settings.js b/public/src/modules/settings.js index 17302ea1d1..d541b35e84 100644 --- a/public/src/modules/settings.js +++ b/public/src/modules/settings.js @@ -471,6 +471,7 @@ define('settings', function () { } $(formEl).deserialize(values); + $(window).trigger('action:admin.settingsLoaded'); callback(null, values); }); diff --git a/public/src/modules/translator.js b/public/src/modules/translator.js index 689f3c8d2e..4569fb0d5b 100644 --- a/public/src/modules/translator.js +++ b/public/src/modules/translator.js @@ -9,7 +9,7 @@ var languages = {}, regexes = { - match: /\[\[.*?\]\]/g, + match: /\[\[\w+:[\s\S]+?\]\]/g, split: /[,][\s]*/, replace: /\]+$/ }; @@ -296,11 +296,13 @@ if (typeof define === 'function' && define.amd) { define('translator', translator); + var _translator = translator; + // Expose a global `translator` object for backwards compatibility window.translator = { translate: function() { console.warn('[translator] Global invocation of the translator is now deprecated, please `require` the module instead.'); - translator.translate.apply(translator, arguments); + _translator.translate.apply(_translator, arguments); } } } diff --git a/public/src/overrides.js b/public/src/overrides.js index 8c0e492cce..dde1b55dd3 100644 --- a/public/src/overrides.js +++ b/public/src/overrides.js @@ -79,25 +79,27 @@ if ('undefined' !== typeof window) { })(jQuery || {fn:{}}); - - // FIX FOR #1245 - https://github.com/NodeBB/NodeBB/issues/1245 - // from http://stackoverflow.com/questions/15931962/bootstrap-dropdown-disappear-with-right-click-on-firefox - // obtain a reference to the original handler - var _clearMenus = $._data(document, "events").click.filter(function (el) { - return el.namespace === 'bs.data-api.dropdown' && el.selector === undefined; - }); - - if(_clearMenus.length) { - _clearMenus = _clearMenus[0].handler; - } - - // disable the old listener - $(document) - .off('click.data-api.dropdown', _clearMenus) - .on('click.data-api.dropdown', function (e) { - // call the handler only when not right-click - if (e.button !== 2) { - _clearMenus(); - } + (function(){ + // FIX FOR #1245 - https://github.com/NodeBB/NodeBB/issues/1245 + // from http://stackoverflow.com/questions/15931962/bootstrap-dropdown-disappear-with-right-click-on-firefox + // obtain a reference to the original handler + var _clearMenus = $._data(document, "events").click.filter(function (el) { + return el.namespace === 'bs.data-api.dropdown' && el.selector === undefined; }); + + if(_clearMenus.length) { + _clearMenus = _clearMenus[0].handler; + } + + // disable the old listener + $(document) + .off('click.data-api.dropdown', _clearMenus) + .on('click.data-api.dropdown', function (e) { + // call the handler only when not right-click + if (e.button !== 2) { + _clearMenus(); + } + }); + })(); + } diff --git a/public/src/variables.js b/public/src/variables.js index 0473499176..16ad223073 100644 --- a/public/src/variables.js +++ b/public/src/variables.js @@ -7,10 +7,16 @@ ajaxify.variables = {}; ajaxify.variables.set = function(key, value) { + if (console && console.warn) { + console.warn('[deprecated] variables.set is deprecated, please use ajaxify.data, key=' + key); + } parsedVariables[key] = value; }; ajaxify.variables.get = function(key) { + if (console && console.warn) { + console.warn('[deprecated] variables.get is deprecated, please use ajaxify.data, key=' + key); + } return parsedVariables[key]; }; @@ -37,5 +43,9 @@ ajaxify.variables.set($(element).attr('template-variable'), value); }); + var dataEl = $('#content [ajaxify-data]'); + if (dataEl.length) { + ajaxify.data = JSON.parse(decodeURIComponent(dataEl.attr('ajaxify-data'))); + } }; }(ajaxify || {})); diff --git a/public/src/widgets.js b/public/src/widgets.js index e83353ec1b..56a7927e47 100644 --- a/public/src/widgets.js +++ b/public/src/widgets.js @@ -15,6 +15,10 @@ }; ajaxify.widgets.render = function(template, url, callback) { + if (template.match(/^admin/)) { + return callback(); + } + var widgetLocations = ['sidebar', 'footer', 'header'], numLocations; $('#content [widget-area]').each(function() { diff --git a/public/vendor/fontawesome/.gitignore b/public/vendor/fontawesome/.gitignore index 51d8053d8c..aaad45f3c8 100644 --- a/public/vendor/fontawesome/.gitignore +++ b/public/vendor/fontawesome/.gitignore @@ -1,29 +1,29 @@ -*.pyc -*.egg-info -*.db -*.db.old -*.swp -*.db-journal - -.coverage -.DS_Store -.installed.cfg - -.idea/* -.svn/* -src/website/static/* -src/website/media/* - -bin -build -cfcache -develop-eggs -dist -downloads -eggs -parts +*.pyc +*.egg-info +*.db +*.db.old +*.swp +*.db-journal + +.coverage +.DS_Store +.installed.cfg + +.idea/* +.svn/* +src/website/static/* +src/website/media/* + +bin +build +cfcache +develop-eggs +dist +downloads +eggs +parts tmp -.sass-cache - -src/website/settingslocal.py +.sass-cache + +src/website/settingslocal.py stunnel.log \ No newline at end of file diff --git a/public/vendor/jquery/sortable/Sortable.js b/public/vendor/jquery/sortable/Sortable.js new file mode 100644 index 0000000000..cf0b5f047b --- /dev/null +++ b/public/vendor/jquery/sortable/Sortable.js @@ -0,0 +1,1145 @@ +/**! + * Sortable + * @author RubaXa + * @license MIT + */ + + +(function (factory) { + "use strict"; + + if (typeof define === "function" && define.amd) { + define(factory); + } + else if (typeof module != "undefined" && typeof module.exports != "undefined") { + module.exports = factory(); + } + else if (typeof Package !== "undefined") { + Sortable = factory(); // export for Meteor.js + } + else { + /* jshint sub:true */ + window["Sortable"] = factory(); + } +})(function () { + "use strict"; + + var dragEl, + ghostEl, + cloneEl, + rootEl, + nextEl, + + scrollEl, + scrollParentEl, + + lastEl, + lastCSS, + + oldIndex, + newIndex, + + activeGroup, + autoScroll = {}, + + tapEvt, + touchEvt, + + /** @const */ + RSPACE = /\s+/g, + + expando = 'Sortable' + (new Date).getTime(), + + win = window, + document = win.document, + parseInt = win.parseInt, + + supportDraggable = !!('draggable' in document.createElement('div')), + + _silent = false, + + abs = Math.abs, + slice = [].slice, + + touchDragOverListeners = [], + + _autoScroll = _throttle(function (/**Event*/evt, /**Object*/options, /**HTMLElement*/rootEl) { + // Bug: https://bugzilla.mozilla.org/show_bug.cgi?id=505521 + if (rootEl && options.scroll) { + var el, + rect, + sens = options.scrollSensitivity, + speed = options.scrollSpeed, + + x = evt.clientX, + y = evt.clientY, + + winWidth = window.innerWidth, + winHeight = window.innerHeight, + + vx, + vy + ; + + // Delect scrollEl + if (scrollParentEl !== rootEl) { + scrollEl = options.scroll; + scrollParentEl = rootEl; + + if (scrollEl === true) { + scrollEl = rootEl; + + do { + if ((scrollEl.offsetWidth < scrollEl.scrollWidth) || + (scrollEl.offsetHeight < scrollEl.scrollHeight) + ) { + break; + } + /* jshint boss:true */ + } while (scrollEl = scrollEl.parentNode); + } + } + + if (scrollEl) { + el = scrollEl; + rect = scrollEl.getBoundingClientRect(); + vx = (abs(rect.right - x) <= sens) - (abs(rect.left - x) <= sens); + vy = (abs(rect.bottom - y) <= sens) - (abs(rect.top - y) <= sens); + } + + + if (!(vx || vy)) { + vx = (winWidth - x <= sens) - (x <= sens); + vy = (winHeight - y <= sens) - (y <= sens); + + /* jshint expr:true */ + (vx || vy) && (el = win); + } + + + if (autoScroll.vx !== vx || autoScroll.vy !== vy || autoScroll.el !== el) { + autoScroll.el = el; + autoScroll.vx = vx; + autoScroll.vy = vy; + + clearInterval(autoScroll.pid); + + if (el) { + autoScroll.pid = setInterval(function () { + if (el === win) { + win.scrollTo(win.pageXOffset + vx * speed, win.pageYOffset + vy * speed); + } else { + vy && (el.scrollTop += vy * speed); + vx && (el.scrollLeft += vx * speed); + } + }, 24); + } + } + } + }, 30) + ; + + + + /** + * @class Sortable + * @param {HTMLElement} el + * @param {Object} [options] + */ + function Sortable(el, options) { + this.el = el; // root element + this.options = options = _extend({}, options); + + + // Export instance + el[expando] = this; + + + // Default options + var defaults = { + group: Math.random(), + sort: true, + disabled: false, + store: null, + handle: null, + scroll: true, + scrollSensitivity: 30, + scrollSpeed: 10, + draggable: /[uo]l/i.test(el.nodeName) ? 'li' : '>*', + ghostClass: 'sortable-ghost', + ignore: 'a, img', + filter: null, + animation: 0, + setData: function (dataTransfer, dragEl) { + dataTransfer.setData('Text', dragEl.textContent); + }, + dropBubble: false, + dragoverBubble: false, + dataIdAttr: 'data-id', + delay: 0 + }; + + + // Set default options + for (var name in defaults) { + !(name in options) && (options[name] = defaults[name]); + } + + + var group = options.group; + + if (!group || typeof group != 'object') { + group = options.group = { name: group }; + } + + + ['pull', 'put'].forEach(function (key) { + if (!(key in group)) { + group[key] = true; + } + }); + + + options.groups = ' ' + group.name + (group.put.join ? ' ' + group.put.join(' ') : '') + ' '; + + + // Bind all private methods + for (var fn in this) { + if (fn.charAt(0) === '_') { + this[fn] = _bind(this, this[fn]); + } + } + + + // Bind events + _on(el, 'mousedown', this._onTapStart); + _on(el, 'touchstart', this._onTapStart); + + _on(el, 'dragover', this); + _on(el, 'dragenter', this); + + touchDragOverListeners.push(this._onDragOver); + + // Restore sorting + options.store && this.sort(options.store.get(this)); + } + + + Sortable.prototype = /** @lends Sortable.prototype */ { + constructor: Sortable, + + _onTapStart: function (/** Event|TouchEvent */evt) { + var _this = this, + el = this.el, + options = this.options, + type = evt.type, + touch = evt.touches && evt.touches[0], + target = (touch || evt).target, + originalTarget = target, + filter = options.filter; + + + if (type === 'mousedown' && evt.button !== 0 || options.disabled) { + return; // only left button or enabled + } + + target = _closest(target, options.draggable, el); + + if (!target) { + return; + } + + // get the index of the dragged element within its parent + oldIndex = _index(target); + + // Check filter + if (typeof filter === 'function') { + if (filter.call(this, evt, target, this)) { + _dispatchEvent(_this, originalTarget, 'filter', target, el, oldIndex); + evt.preventDefault(); + return; // cancel dnd + } + } + else if (filter) { + filter = filter.split(',').some(function (criteria) { + criteria = _closest(originalTarget, criteria.trim(), el); + + if (criteria) { + _dispatchEvent(_this, criteria, 'filter', target, el, oldIndex); + return true; + } + }); + + if (filter) { + evt.preventDefault(); + return; // cancel dnd + } + } + + + if (options.handle && !_closest(originalTarget, options.handle, el)) { + return; + } + + + // Prepare `dragstart` + this._prepareDragStart(evt, touch, target); + }, + + _prepareDragStart: function (/** Event */evt, /** Touch */touch, /** HTMLElement */target) { + var _this = this, + el = _this.el, + options = _this.options, + ownerDocument = el.ownerDocument, + dragStartFn; + + if (target && !dragEl && (target.parentNode === el)) { + tapEvt = evt; + + rootEl = el; + dragEl = target; + nextEl = dragEl.nextSibling; + activeGroup = options.group; + + dragStartFn = function () { + // Delayed drag has been triggered + // we can re-enable the events: touchmove/mousemove + _this._disableDelayedDrag(); + + // Make the element draggable + dragEl.draggable = true; + + // Disable "draggable" + options.ignore.split(',').forEach(function (criteria) { + _find(dragEl, criteria.trim(), _disableDraggable); + }); + + // Bind the events: dragstart/dragend + _this._triggerDragStart(touch); + }; + + _on(ownerDocument, 'mouseup', _this._onDrop); + _on(ownerDocument, 'touchend', _this._onDrop); + _on(ownerDocument, 'touchcancel', _this._onDrop); + + if (options.delay) { + // If the user moves the pointer before the delay has been reached: + // disable the delayed drag + _on(ownerDocument, 'mousemove', _this._disableDelayedDrag); + _on(ownerDocument, 'touchmove', _this._disableDelayedDrag); + + _this._dragStartTimer = setTimeout(dragStartFn, options.delay); + } else { + dragStartFn(); + } + } + }, + + _disableDelayedDrag: function () { + var ownerDocument = this.el.ownerDocument; + + clearTimeout(this._dragStartTimer); + + _off(ownerDocument, 'mousemove', this._disableDelayedDrag); + _off(ownerDocument, 'touchmove', this._disableDelayedDrag); + }, + + _triggerDragStart: function (/** Touch */touch) { + if (touch) { + // Touch device support + tapEvt = { + target: dragEl, + clientX: touch.clientX, + clientY: touch.clientY + }; + + this._onDragStart(tapEvt, 'touch'); + } + else if (!supportDraggable) { + this._onDragStart(tapEvt, true); + } + else { + _on(dragEl, 'dragend', this); + _on(rootEl, 'dragstart', this._onDragStart); + } + + try { + if (document.selection) { + document.selection.empty(); + } else { + window.getSelection().removeAllRanges(); + } + } catch (err) { + } + }, + + _dragStarted: function () { + if (rootEl && dragEl) { + // Apply effect + _toggleClass(dragEl, this.options.ghostClass, true); + + Sortable.active = this; + + // Drag start event + _dispatchEvent(this, rootEl, 'start', dragEl, rootEl, oldIndex); + } + }, + + _emulateDragOver: function () { + if (touchEvt) { + _css(ghostEl, 'display', 'none'); + + var target = document.elementFromPoint(touchEvt.clientX, touchEvt.clientY), + parent = target, + groupName = ' ' + this.options.group.name + '', + i = touchDragOverListeners.length; + + if (parent) { + do { + if (parent[expando] && parent[expando].options.groups.indexOf(groupName) > -1) { + while (i--) { + touchDragOverListeners[i]({ + clientX: touchEvt.clientX, + clientY: touchEvt.clientY, + target: target, + rootEl: parent + }); + } + + break; + } + + target = parent; // store last element + } + /* jshint boss:true */ + while (parent = parent.parentNode); + } + + _css(ghostEl, 'display', ''); + } + }, + + + _onTouchMove: function (/**TouchEvent*/evt) { + if (tapEvt) { + var touch = evt.touches ? evt.touches[0] : evt, + dx = touch.clientX - tapEvt.clientX, + dy = touch.clientY - tapEvt.clientY, + translate3d = evt.touches ? 'translate3d(' + dx + 'px,' + dy + 'px,0)' : 'translate(' + dx + 'px,' + dy + 'px)'; + + touchEvt = touch; + + _css(ghostEl, 'webkitTransform', translate3d); + _css(ghostEl, 'mozTransform', translate3d); + _css(ghostEl, 'msTransform', translate3d); + _css(ghostEl, 'transform', translate3d); + + evt.preventDefault(); + } + }, + + + _onDragStart: function (/**Event*/evt, /**boolean*/useFallback) { + var dataTransfer = evt.dataTransfer, + options = this.options; + + this._offUpEvents(); + + if (activeGroup.pull == 'clone') { + cloneEl = dragEl.cloneNode(true); + _css(cloneEl, 'display', 'none'); + rootEl.insertBefore(cloneEl, dragEl); + } + + if (useFallback) { + var rect = dragEl.getBoundingClientRect(), + css = _css(dragEl), + ghostRect; + + ghostEl = dragEl.cloneNode(true); + + _css(ghostEl, 'top', rect.top - parseInt(css.marginTop, 10)); + _css(ghostEl, 'left', rect.left - parseInt(css.marginLeft, 10)); + _css(ghostEl, 'width', rect.width); + _css(ghostEl, 'height', rect.height); + _css(ghostEl, 'opacity', '0.8'); + _css(ghostEl, 'position', 'fixed'); + _css(ghostEl, 'zIndex', '100000'); + + rootEl.appendChild(ghostEl); + + // Fixing dimensions. + ghostRect = ghostEl.getBoundingClientRect(); + _css(ghostEl, 'width', rect.width * 2 - ghostRect.width); + _css(ghostEl, 'height', rect.height * 2 - ghostRect.height); + + if (useFallback === 'touch') { + // Bind touch events + _on(document, 'touchmove', this._onTouchMove); + _on(document, 'touchend', this._onDrop); + _on(document, 'touchcancel', this._onDrop); + } else { + // Old brwoser + _on(document, 'mousemove', this._onTouchMove); + _on(document, 'mouseup', this._onDrop); + } + + this._loopId = setInterval(this._emulateDragOver, 150); + } + else { + if (dataTransfer) { + dataTransfer.effectAllowed = 'move'; + options.setData && options.setData.call(this, dataTransfer, dragEl); + } + + _on(document, 'drop', this); + } + + setTimeout(this._dragStarted, 0); + }, + + _onDragOver: function (/**Event*/evt) { + var el = this.el, + target, + dragRect, + revert, + options = this.options, + group = options.group, + groupPut = group.put, + isOwner = (activeGroup === group), + canSort = options.sort; + + if (evt.preventDefault !== void 0) { + evt.preventDefault(); + !options.dragoverBubble && evt.stopPropagation(); + } + + if (activeGroup && !options.disabled && + (isOwner + ? canSort || (revert = !rootEl.contains(dragEl)) // Reverting item into the original list + : activeGroup.pull && groupPut && ( + (activeGroup.name === group.name) || // by Name + (groupPut.indexOf && ~groupPut.indexOf(activeGroup.name)) // by Array + ) + ) && + (evt.rootEl === void 0 || evt.rootEl === this.el) // touch fallback + ) { + // Smart auto-scrolling + _autoScroll(evt, options, this.el); + + if (_silent) { + return; + } + + target = _closest(evt.target, options.draggable, el); + dragRect = dragEl.getBoundingClientRect(); + + + if (revert) { + _cloneHide(true); + + if (cloneEl || nextEl) { + rootEl.insertBefore(dragEl, cloneEl || nextEl); + } + else if (!canSort) { + rootEl.appendChild(dragEl); + } + + return; + } + + + if ((el.children.length === 0) || (el.children[0] === ghostEl) || + (el === evt.target) && (target = _ghostInBottom(el, evt)) + ) { + if (target) { + if (target.animated) { + return; + } + targetRect = target.getBoundingClientRect(); + } + + _cloneHide(isOwner); + + if (_onMove(rootEl, el, dragEl, dragRect, target, targetRect) !== false) { + el.appendChild(dragEl); + this._animate(dragRect, dragEl); + target && this._animate(targetRect, target); + } + } + else if (target && !target.animated && target !== dragEl && (target.parentNode[expando] !== void 0)) { + if (lastEl !== target) { + lastEl = target; + lastCSS = _css(target); + } + + + var targetRect = target.getBoundingClientRect(), + width = targetRect.right - targetRect.left, + height = targetRect.bottom - targetRect.top, + floating = /left|right|inline/.test(lastCSS.cssFloat + lastCSS.display), + isWide = (target.offsetWidth > dragEl.offsetWidth), + isLong = (target.offsetHeight > dragEl.offsetHeight), + halfway = (floating ? (evt.clientX - targetRect.left) / width : (evt.clientY - targetRect.top) / height) > 0.5, + nextSibling = target.nextElementSibling, + moveVector = _onMove(rootEl, el, dragEl, dragRect, target, targetRect), + after + ; + + if (moveVector !== false) { + _silent = true; + setTimeout(_unsilent, 30); + + _cloneHide(isOwner); + + if (moveVector === 1 || moveVector === -1) { + after = (moveVector === 1); + } + else if (floating) { + after = (target.previousElementSibling === dragEl) && !isWide || halfway && isWide; + } else { + after = (nextSibling !== dragEl) && !isLong || halfway && isLong; + } + + if (after && !nextSibling) { + el.appendChild(dragEl); + } else { + target.parentNode.insertBefore(dragEl, after ? nextSibling : target); + } + + this._animate(dragRect, dragEl); + this._animate(targetRect, target); + } + } + } + }, + + _animate: function (prevRect, target) { + var ms = this.options.animation; + + if (ms) { + var currentRect = target.getBoundingClientRect(); + + _css(target, 'transition', 'none'); + _css(target, 'transform', 'translate3d(' + + (prevRect.left - currentRect.left) + 'px,' + + (prevRect.top - currentRect.top) + 'px,0)' + ); + + target.offsetWidth; // repaint + + _css(target, 'transition', 'all ' + ms + 'ms'); + _css(target, 'transform', 'translate3d(0,0,0)'); + + clearTimeout(target.animated); + target.animated = setTimeout(function () { + _css(target, 'transition', ''); + _css(target, 'transform', ''); + target.animated = false; + }, ms); + } + }, + + _offUpEvents: function () { + var ownerDocument = this.el.ownerDocument; + + _off(document, 'touchmove', this._onTouchMove); + _off(ownerDocument, 'mouseup', this._onDrop); + _off(ownerDocument, 'touchend', this._onDrop); + _off(ownerDocument, 'touchcancel', this._onDrop); + }, + + _onDrop: function (/**Event*/evt) { + var el = this.el, + options = this.options; + + clearInterval(this._loopId); + clearInterval(autoScroll.pid); + + clearTimeout(this.dragStartTimer); + + // Unbind events + _off(document, 'drop', this); + _off(document, 'mousemove', this._onTouchMove); + _off(el, 'dragstart', this._onDragStart); + + this._offUpEvents(); + + if (evt) { + evt.preventDefault(); + !options.dropBubble && evt.stopPropagation(); + + ghostEl && ghostEl.parentNode.removeChild(ghostEl); + + if (dragEl) { + _off(dragEl, 'dragend', this); + + _disableDraggable(dragEl); + _toggleClass(dragEl, this.options.ghostClass, false); + + if (rootEl !== dragEl.parentNode) { + newIndex = _index(dragEl); + + // drag from one list and drop into another + _dispatchEvent(null, dragEl.parentNode, 'sort', dragEl, rootEl, oldIndex, newIndex); + _dispatchEvent(this, rootEl, 'sort', dragEl, rootEl, oldIndex, newIndex); + + // Add event + _dispatchEvent(null, dragEl.parentNode, 'add', dragEl, rootEl, oldIndex, newIndex); + + // Remove event + _dispatchEvent(this, rootEl, 'remove', dragEl, rootEl, oldIndex, newIndex); + } + else { + // Remove clone + cloneEl && cloneEl.parentNode.removeChild(cloneEl); + + if (dragEl.nextSibling !== nextEl) { + // Get the index of the dragged element within its parent + newIndex = _index(dragEl); + + // drag & drop within the same list + _dispatchEvent(this, rootEl, 'update', dragEl, rootEl, oldIndex, newIndex); + _dispatchEvent(this, rootEl, 'sort', dragEl, rootEl, oldIndex, newIndex); + } + } + + if (Sortable.active) { + // Drag end event + _dispatchEvent(this, rootEl, 'end', dragEl, rootEl, oldIndex, newIndex); + + // Save sorting + this.save(); + } + } + + // Nulling + rootEl = + dragEl = + ghostEl = + nextEl = + cloneEl = + + scrollEl = + scrollParentEl = + + tapEvt = + touchEvt = + + lastEl = + lastCSS = + + activeGroup = + Sortable.active = null; + } + }, + + + handleEvent: function (/**Event*/evt) { + var type = evt.type; + + if (type === 'dragover' || type === 'dragenter') { + if (dragEl) { + this._onDragOver(evt); + _globalDragOver(evt); + } + } + else if (type === 'drop' || type === 'dragend') { + this._onDrop(evt); + } + }, + + + /** + * Serializes the item into an array of string. + * @returns {String[]} + */ + toArray: function () { + var order = [], + el, + children = this.el.children, + i = 0, + n = children.length, + options = this.options; + + for (; i < n; i++) { + el = children[i]; + if (_closest(el, options.draggable, this.el)) { + order.push(el.getAttribute(options.dataIdAttr) || _generateId(el)); + } + } + + return order; + }, + + + /** + * Sorts the elements according to the array. + * @param {String[]} order order of the items + */ + sort: function (order) { + var items = {}, rootEl = this.el; + + this.toArray().forEach(function (id, i) { + var el = rootEl.children[i]; + + if (_closest(el, this.options.draggable, rootEl)) { + items[id] = el; + } + }, this); + + order.forEach(function (id) { + if (items[id]) { + rootEl.removeChild(items[id]); + rootEl.appendChild(items[id]); + } + }); + }, + + + /** + * Save the current sorting + */ + save: function () { + var store = this.options.store; + store && store.set(this); + }, + + + /** + * For each element in the set, get the first element that matches the selector by testing the element itself and traversing up through its ancestors in the DOM tree. + * @param {HTMLElement} el + * @param {String} [selector] default: `options.draggable` + * @returns {HTMLElement|null} + */ + closest: function (el, selector) { + return _closest(el, selector || this.options.draggable, this.el); + }, + + + /** + * Set/get option + * @param {string} name + * @param {*} [value] + * @returns {*} + */ + option: function (name, value) { + var options = this.options; + + if (value === void 0) { + return options[name]; + } else { + options[name] = value; + } + }, + + + /** + * Destroy + */ + destroy: function () { + var el = this.el; + + el[expando] = null; + + _off(el, 'mousedown', this._onTapStart); + _off(el, 'touchstart', this._onTapStart); + + _off(el, 'dragover', this); + _off(el, 'dragenter', this); + + // Remove draggable attributes + Array.prototype.forEach.call(el.querySelectorAll('[draggable]'), function (el) { + el.removeAttribute('draggable'); + }); + + touchDragOverListeners.splice(touchDragOverListeners.indexOf(this._onDragOver), 1); + + this._onDrop(); + + this.el = el = null; + } + }; + + + function _cloneHide(state) { + if (cloneEl && (cloneEl.state !== state)) { + _css(cloneEl, 'display', state ? 'none' : ''); + !state && cloneEl.state && rootEl.insertBefore(cloneEl, dragEl); + cloneEl.state = state; + } + } + + + function _bind(ctx, fn) { + var args = slice.call(arguments, 2); + return fn.bind ? fn.bind.apply(fn, [ctx].concat(args)) : function () { + return fn.apply(ctx, args.concat(slice.call(arguments))); + }; + } + + + function _closest(/**HTMLElement*/el, /**String*/selector, /**HTMLElement*/ctx) { + if (el) { + ctx = ctx || document; + selector = selector.split('.'); + + var tag = selector.shift().toUpperCase(), + re = new RegExp('\\s(' + selector.join('|') + ')\\s', 'g'); + + do { + if ( + (tag === '>*' && el.parentNode === ctx) || ( + (tag === '' || el.nodeName.toUpperCase() == tag) && + (!selector.length || ((' ' + el.className + ' ').match(re) || []).length == selector.length) + ) + ) { + return el; + } + } + while (el !== ctx && (el = el.parentNode)); + } + + return null; + } + + + function _globalDragOver(/**Event*/evt) { + evt.dataTransfer.dropEffect = 'move'; + evt.preventDefault(); + } + + + function _on(el, event, fn) { + el.addEventListener(event, fn, false); + } + + + function _off(el, event, fn) { + el.removeEventListener(event, fn, false); + } + + + function _toggleClass(el, name, state) { + if (el) { + if (el.classList) { + el.classList[state ? 'add' : 'remove'](name); + } + else { + var className = (' ' + el.className + ' ').replace(RSPACE, ' ').replace(' ' + name + ' ', ' '); + el.className = (className + (state ? ' ' + name : '')).replace(RSPACE, ' '); + } + } + } + + + function _css(el, prop, val) { + var style = el && el.style; + + if (style) { + if (val === void 0) { + if (document.defaultView && document.defaultView.getComputedStyle) { + val = document.defaultView.getComputedStyle(el, ''); + } + else if (el.currentStyle) { + val = el.currentStyle; + } + + return prop === void 0 ? val : val[prop]; + } + else { + if (!(prop in style)) { + prop = '-webkit-' + prop; + } + + style[prop] = val + (typeof val === 'string' ? '' : 'px'); + } + } + } + + + function _find(ctx, tagName, iterator) { + if (ctx) { + var list = ctx.getElementsByTagName(tagName), i = 0, n = list.length; + + if (iterator) { + for (; i < n; i++) { + iterator(list[i], i); + } + } + + return list; + } + + return []; + } + + + + function _dispatchEvent(sortable, rootEl, name, targetEl, fromEl, startIndex, newIndex) { + var evt = document.createEvent('Event'), + options = (sortable || rootEl[expando]).options, + onName = 'on' + name.charAt(0).toUpperCase() + name.substr(1); + + evt.initEvent(name, true, true); + + evt.to = rootEl; + evt.from = fromEl || rootEl; + evt.item = targetEl || rootEl; + evt.clone = cloneEl; + + evt.oldIndex = startIndex; + evt.newIndex = newIndex; + + rootEl.dispatchEvent(evt); + + if (options[onName]) { + options[onName].call(sortable, evt); + } + } + + + function _onMove(fromEl, toEl, dragEl, dragRect, targetEl, targetRect) { + var evt, + sortable = fromEl[expando], + onMoveFn = sortable.options.onMove, + retVal; + + if (onMoveFn) { + evt = document.createEvent('Event'); + evt.initEvent('move', true, true); + + evt.to = toEl; + evt.from = fromEl; + evt.dragged = dragEl; + evt.draggedRect = dragRect; + evt.related = targetEl || toEl; + evt.relatedRect = targetRect || toEl.getBoundingClientRect(); + + retVal = onMoveFn.call(sortable, evt); + } + + return retVal; + } + + + function _disableDraggable(el) { + el.draggable = false; + } + + + function _unsilent() { + _silent = false; + } + + + /** @returns {HTMLElement|false} */ + function _ghostInBottom(el, evt) { + var lastEl = el.lastElementChild, + rect = lastEl.getBoundingClientRect(); + + return (evt.clientY - (rect.top + rect.height) > 5) && lastEl; // min delta + } + + + /** + * Generate id + * @param {HTMLElement} el + * @returns {String} + * @private + */ + function _generateId(el) { + var str = el.tagName + el.className + el.src + el.href + el.textContent, + i = str.length, + sum = 0; + + while (i--) { + sum += str.charCodeAt(i); + } + + return sum.toString(36); + } + + /** + * Returns the index of an element within its parent + * @param el + * @returns {number} + * @private + */ + function _index(/**HTMLElement*/el) { + var index = 0; + while (el && (el = el.previousElementSibling)) { + if (el.nodeName.toUpperCase() !== 'TEMPLATE') { + index++; + } + } + return index; + } + + function _throttle(callback, ms) { + var args, _this; + + return function () { + if (args === void 0) { + args = arguments; + _this = this; + + setTimeout(function () { + if (args.length === 1) { + callback.call(_this, args[0]); + } else { + callback.apply(_this, args); + } + + args = void 0; + }, ms); + } + }; + } + + function _extend(dst, src) { + if (dst && src) { + for (var key in src) { + if (src.hasOwnProperty(key)) { + dst[key] = src[key]; + } + } + } + + return dst; + } + + + // Export utils + Sortable.utils = { + on: _on, + off: _off, + css: _css, + find: _find, + bind: _bind, + is: function (el, selector) { + return !!_closest(el, selector, el); + }, + extend: _extend, + throttle: _throttle, + closest: _closest, + toggleClass: _toggleClass, + index: _index + }; + + + Sortable.version = '1.2.0'; + + + /** + * Create sortable instance + * @param {HTMLElement} el + * @param {Object} [options] + */ + Sortable.create = function (el, options) { + return new Sortable(el, options); + }; + + // Export + return Sortable; +}); diff --git a/public/vendor/jquery/timeago/locales/jquery.timeago.fa.js b/public/vendor/jquery/timeago/locales/jquery.timeago.fa.js index 0a70819d44..2bfbf53ccd 100644 --- a/public/vendor/jquery/timeago/locales/jquery.timeago.fa.js +++ b/public/vendor/jquery/timeago/locales/jquery.timeago.fa.js @@ -1,22 +1,22 @@ - -// Persian -// Use DIR attribute for RTL text in Persian Language for ABBR tag . -// By MB.seifollahi@gmail.com -jQuery.timeago.settings.strings = { - prefixAgo: null, - prefixFromNow: null, - suffixAgo: "پیش", - suffixFromNow: "از حال", - seconds: "کمتر از یک دقیقه", - minute: "حدود یک دقیقه", - minutes: "%d دقیقه", - hour: "حدود یک ساعت", - hours: "حدود %d ساعت", - day: "یک روز", - days: "%d روز", - month: "حدود یک ماه", - months: "%d ماه", - year: "حدود یک سال", - years: "%d سال", - wordSeparator: " " + +// Persian +// Use DIR attribute for RTL text in Persian Language for ABBR tag . +// By MB.seifollahi@gmail.com +jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "پیش", + suffixFromNow: "از حال", + seconds: "کمتر از یک دقیقه", + minute: "حدود یک دقیقه", + minutes: "%d دقیقه", + hour: "حدود یک ساعت", + hours: "حدود %d ساعت", + day: "یک روز", + days: "%d روز", + month: "حدود یک ماه", + months: "%d ماه", + year: "حدود یک سال", + years: "%d سال", + wordSeparator: " " }; \ No newline at end of file diff --git a/public/vendor/jquery/timeago/locales/jquery.timeago.uz.js b/public/vendor/jquery/timeago/locales/jquery.timeago.uz.js index 31c0c02af5..380f18dc04 100755 --- a/public/vendor/jquery/timeago/locales/jquery.timeago.uz.js +++ b/public/vendor/jquery/timeago/locales/jquery.timeago.uz.js @@ -1,19 +1,19 @@ -//Uzbek -jQuery.timeago.settings.strings = { - prefixAgo: null, - prefixFromNow: "keyin", - suffixAgo: "avval", - suffixFromNow: null, - seconds: "bir necha soniya", - minute: "1 daqiqa", - minutes: function(value) { return "%d daqiqa" }, - hour: "1 soat", - hours: function(value) { return "%d soat" }, - day: "1 kun", - days: function(value) { return "%d kun" }, - month: "1 oy", - months: function(value) { return "%d oy" }, - year: "1 yil", - years: function(value) { return "%d yil" }, - wordSeparator: " " -}; +//Uzbek +jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: "keyin", + suffixAgo: "avval", + suffixFromNow: null, + seconds: "bir necha soniya", + minute: "1 daqiqa", + minutes: function(value) { return "%d daqiqa" }, + hour: "1 soat", + hours: function(value) { return "%d soat" }, + day: "1 kun", + days: function(value) { return "%d kun" }, + month: "1 oy", + months: function(value) { return "%d oy" }, + year: "1 yil", + years: function(value) { return "%d yil" }, + wordSeparator: " " +}; diff --git a/src/analytics.js b/src/analytics.js index 38d6ec53b7..49254954bc 100644 --- a/src/analytics.js +++ b/src/analytics.js @@ -63,5 +63,7 @@ var cronJob = require('cron').CronJob, } }; - + Analytics.getUnwrittenPageviews = function() { + return pageViews; + }; }(exports)); \ No newline at end of file diff --git a/src/categories.js b/src/categories.js index 134a671744..ac89bb4ac1 100644 --- a/src/categories.js +++ b/src/categories.js @@ -198,6 +198,15 @@ var async = require('async'), }); }; + Categories.getAllCategoryFields = function(fields, callback) { + async.waterfall([ + async.apply(db.getSortedSetRange, 'categories:cid', 0, -1), + function(cids, next) { + Categories.getMultipleCategoryFields(cids, fields, next); + } + ], callback); + }; + Categories.getCategoryFields = function(cid, fields, callback) { db.getObjectFields('category:' + cid, fields, callback); }; @@ -244,7 +253,7 @@ var async = require('async'), if (categories[i]) { categories[i]['unread-class'] = (parseInt(categories[i].topic_count, 10) === 0 || (hasRead[i] && uid !== 0)) ? '' : 'unread'; categories[i].children = results.children[i]; - categories[i].parent = results.parents[i] && !results.parents[i].disabled ? results.parents[i] : null; + categories[i].parent = results.parents[i] || undefined; calculateTopicPostCount(categories[i]); } } @@ -310,4 +319,28 @@ var async = require('async'), ], callback); }; + /** + * Recursively build tree + * + * @param categories {array} flat list of categories + * @param parentCid {number} start from 0 to build full tree + */ + Categories.getTree = function(categories, parentCid) { + var tree = [], i = 0, len = categories.length, category; + + for(i; i < len; ++i) { + category = categories[i]; + if (!category.hasOwnProperty('parentCid')) { + category.parentCid = 0; + } + + if(category.parentCid == parentCid){ + tree.push(category); + category.children = Categories.getTree(categories, category.cid); + } + } + + return tree; + }; + }(exports)); diff --git a/src/categories/create.js b/src/categories/create.js index b18f2b17dc..71eef4e72e 100644 --- a/src/categories/create.js +++ b/src/categories/create.js @@ -3,6 +3,7 @@ var async = require('async'), db = require('../database'), privileges = require('../privileges'), + plugins = require('../plugins'), utils = require('../../public/src/utils'); module.exports = function(Categories) { @@ -37,17 +38,23 @@ module.exports = function(Categories) { imageClass: 'auto' }; + plugins.fireHook('filter:category.create', {category: category, data: data}, next); + }, + function(data, next) { + category = data.category; + var defaultPrivileges = ['find', 'read', 'topics:create', 'topics:reply']; async.series([ - async.apply(db.setObject, 'category:' + cid, category), - async.apply(db.sortedSetAdd, 'categories:cid', order, cid), - async.apply(privileges.categories.give, defaultPrivileges, cid, 'administrators'), - async.apply(privileges.categories.give, defaultPrivileges, cid, 'registered-users'), - async.apply(privileges.categories.give, ['find', 'read'], cid, 'guests') + async.apply(db.setObject, 'category:' + category.cid, category), + async.apply(db.sortedSetAdd, 'categories:cid', category.order, category.cid), + async.apply(privileges.categories.give, defaultPrivileges, category.cid, 'administrators'), + async.apply(privileges.categories.give, defaultPrivileges, category.cid, 'registered-users'), + async.apply(privileges.categories.give, ['find', 'read'], category.cid, 'guests') ], next); }, function(results, next) { + plugins.fireHook('action:category.create', category); next(null, category); } ], callback); diff --git a/src/categories/delete.js b/src/categories/delete.js index 0ebac949d7..1679ab6731 100644 --- a/src/categories/delete.js +++ b/src/categories/delete.js @@ -3,6 +3,7 @@ var async = require('async'), db = require('../database'), batch = require('../batch'), + plugins = require('../plugins'), threadTools = require('../threadTools'); @@ -17,7 +18,10 @@ module.exports = function(Categories) { if (err) { return callback(err); } - purgeCategory(cid, callback); + async.series([ + async.apply(purgeCategory, cid), + async.apply(plugins.fireHook, 'action:category.delete', cid) + ], callback); }); }; diff --git a/src/categories/recentreplies.js b/src/categories/recentreplies.js index bd6871ce99..f495ab1463 100644 --- a/src/categories/recentreplies.js +++ b/src/categories/recentreplies.js @@ -5,6 +5,7 @@ var async = require('async'), winston = require('winston'), _ = require('underscore'), + meta = require('../meta'), db = require('../database'), posts = require('../posts'), topics = require('../topics'), @@ -48,7 +49,11 @@ module.exports = function(Categories) { privileges.posts.filter('read', pids, uid, next); }, function(pids, next) { - posts.getPostSummaryByPids(pids, uid, {stripTags: true}, next); + if (meta.config.teaserPost === 'first') { + getMainPosts(pids, uid, next); + } else { + posts.getPostSummaryByPids(pids, uid, {stripTags: true}, next); + } }, function(posts, next) { categoryData.forEach(function(category) { @@ -59,10 +64,33 @@ module.exports = function(Categories) { ], callback); }; + function getMainPosts(pids, uid, callback) { + async.waterfall([ + function(next) { + var keys = pids.map(function(pid) { + return 'post:' + pid; + }); + db.getObjectsFields(keys, ['tid'], next); + }, + function(posts, next) { + var keys = posts.map(function(post) { + return 'topic:' + post.tid; + }); + db.getObjectsFields(keys, ['mainPid'], next); + }, + function(topics, next) { + var mainPids = topics.map(function(topic) { + return topic.mainPid; + }); + posts.getPostSummaryByPids(mainPids, uid, {stripTags: true}, next); + } + ], callback); + } + function assignPostsToCategory(category, posts) { category.posts = posts.filter(function(post) { - return post.category && (parseInt(post.category.cid, 10) === parseInt(category.cid, 10) - || parseInt(post.category.parentCid, 10) === parseInt(category.cid, 10)); + return post.category && (parseInt(post.category.cid, 10) === parseInt(category.cid, 10) || + parseInt(post.category.parentCid, 10) === parseInt(category.cid, 10)); }).sort(function(a, b) { return b.timestamp - a.timestamp; }).slice(0, parseInt(category.numRecentReplies, 10)); diff --git a/src/categories/update.js b/src/categories/update.js index 6fcf7d4b57..fb5cec5054 100644 --- a/src/categories/update.js +++ b/src/categories/update.js @@ -16,11 +16,28 @@ module.exports = function(Categories) { return next(err); } - plugins.fireHook('filter:category.update', modified[cid], function(err, category) { + var modifiedFields = modified[cid]; + + if (modifiedFields.hasOwnProperty('name')) { + modifiedFields.slug = cid + '/' + utils.slugify(modifiedFields.name); + } + + plugins.fireHook('filter:category.update', {category: modifiedFields}, function(err, categoryData) { + if (err) { + return next(err); + } + + var category = categoryData.category; var fields = Object.keys(category); async.each(fields, function(key, next) { updateCategoryField(cid, key, category[key], next); - }, next); + }, function(err) { + if (err) { + return next(err); + } + plugins.fireHook('action:category.update', {cid: cid, modified: category}); + next(); + }); }); }); } @@ -38,10 +55,7 @@ module.exports = function(Categories) { return callback(err); } - if (key === 'name') { - var slug = cid + '/' + utils.slugify(value); - db.setObjectField('category:' + cid, 'slug', slug, callback); - } else if (key === 'order') { + if (key === 'order') { db.sortedSetAdd('categories:cid', value, cid, callback); } else { callback(); diff --git a/src/controllers/accounts.js b/src/controllers/accounts.js index 1fca24aae1..64a6d26786 100644 --- a/src/controllers/accounts.js +++ b/src/controllers/accounts.js @@ -80,6 +80,7 @@ function getUserDataByUserSlug(userslug, callerUID, callback) { userData.uid = userData.uid; userData.yourid = callerUID; userData.theirid = userData.uid; + userData.isAdmin = isAdmin; userData.isSelf = self; userData.showHidden = self || isAdmin; userData.groups = Array.isArray(results.groups) && results.groups.length ? results.groups[0] : []; @@ -89,6 +90,7 @@ function getUserDataByUserSlug(userslug, callerUID, callback) { userData.status = require('../socket.io').isUserOnline(userData.uid) ? (userData.status || 'online') : 'offline'; userData.banned = parseInt(userData.banned, 10) === 1; userData.website = validator.escape(userData.website); + userData.websiteLink = !userData.website.startsWith('http') ? 'http://' + userData.website : userData.website; userData.websiteName = userData.website.replace(validator.escape('http://'), '').replace(validator.escape('https://'), ''); userData.followingCount = parseInt(userData.followingCount, 10) || 0; userData.followerCount = parseInt(userData.followerCount, 10) || 0; @@ -255,12 +257,13 @@ accountsController.getGroups = function(req, res, next) { return helpers.notFound(req, res); } - groups.getUserGroups([userData.uid], function(err, groups) { + groups.getUserGroups([userData.uid], function(err, groupsData) { if (err) { return next(err); } - userData.groups = groups[0]; + userData.groups = groupsData[0]; + userData.groups.forEach(groups.escapeGroupData); res.render('account/groups', userData); }); @@ -487,6 +490,13 @@ accountsController.getChats = function(req, res, next) { if (parseInt(meta.config.disableChat) === 1) { return helpers.notFound(req, res); } + + // In case a userNAME is passed in instead of a slug, the route should not 404 + var slugified = utils.slugify(req.params.userslug); + if (req.params.userslug && req.params.userslug !== slugified) { + return res.redirect(nconf.get('relative_path') + '/chats/' + slugified); + } + async.parallel({ contacts: async.apply(user.getFollowing, req.user.uid, 0, 19), recentChats: async.apply(messaging.getRecentChats, req.user.uid, 0, 19) diff --git a/src/controllers/admin.js b/src/controllers/admin.js index 65112ad229..934881a6bd 100644 --- a/src/controllers/admin.js +++ b/src/controllers/admin.js @@ -15,7 +15,6 @@ var async = require('async'), events = require('../events'), languages = require('../languages'), plugins = require('../plugins'), - groups = require('../groups'), validator = require('validator'); @@ -24,7 +23,7 @@ var adminController = { tags: {}, flags: {}, topics: {}, - groups: {}, + groups: require('./admin/groups'), appearance: {}, extend: { widgets: {} @@ -52,9 +51,25 @@ adminController.home = function(req, res, next) { }, notices: function(next) { var notices = [ - {done: !meta.reloadRequired, doneText: 'Reload not required', notDoneText:'Reload required'}, - {done: plugins.hasListeners('action:email.send'), doneText: 'Emailer Installed', notDoneText:'Emailer not installed'}, - {done: plugins.hasListeners('filter:search.query'), doneText: 'Search Plugin Installed', notDoneText:'Search Plugin not installed'} + { + done: !meta.reloadRequired, + doneText: 'Reload not required', + notDoneText:'Reload required' + }, + { + done: plugins.hasListeners('action:email.send'), + doneText: 'Emailer Installed', + notDoneText:'Emailer not installed', + tooltip:'Install an emailer plugin from the plugin page in order to activate registration emails and email digests', + link:'/admin/extend/plugins' + }, + { + done: plugins.hasListeners('filter:search.query'), + doneText: 'Search Plugin Installed', + notDoneText:'Search Plugin not installed', + tooltip: 'Install a search plugin from the plugin page in order to activate search functionality', + link:'/admin/extend/plugins' + } ]; plugins.fireHook('filter:admin.notices', notices, next); } @@ -150,28 +165,8 @@ adminController.categories.get = function(req, res, next) { }; adminController.categories.getAll = function(req, res, next) { - var active = [], - disabled = []; - - async.waterfall([ - async.apply(db.getSortedSetRangeByScore, 'categories:cid', 0, -1, 0, Date.now()), - async.apply(categories.getCategoriesData), - function(categories, next) { - plugins.fireHook('filter:admin.categories.get', {req: req, res: res, categories: categories}, next); - } - ], function(err, data) { - if (err) { - return next(err); - } - data.categories.filter(Boolean).forEach(function(category) { - (category.disabled ? disabled : active).push(category); - }); - - res.render('admin/manage/categories', { - active: active, - disabled: disabled - }); - }); + //Categories list will be rendered on client side with recursion, etc. + res.render('admin/manage/categories', {}); }; adminController.tags.get = function(req, res, next) { @@ -280,7 +275,12 @@ adminController.plugins.get = function(req, res, next) { } res.render('admin/extend/plugins' , { - plugins: plugins + installed: plugins.filter(function(plugin) { + return plugin.installed; + }), + download: plugins.filter(function(plugin) { + return !plugin.installed; + }) }); }); }; @@ -380,24 +380,6 @@ adminController.extend.rewards = function(req, res, next) { }); }; -adminController.groups.get = function(req, res, next) { - groups.list({ - expand: true, - truncateUserList: true, - isAdmin: true, - showSystemGroups: true - }, function(err, groups) { - if (err) { - return next(err); - } - - res.render('admin/manage/groups', { - groups: groups, - yourid: req.user.uid - }); - }); -}; - adminController.themes.get = function(req, res, next) { var themeDir = path.join(__dirname, '../../node_modules/' + req.params.theme); fs.exists(themeDir, function(exists) { diff --git a/src/controllers/admin/groups.js b/src/controllers/admin/groups.js new file mode 100644 index 0000000000..c52204f7a5 --- /dev/null +++ b/src/controllers/admin/groups.js @@ -0,0 +1,73 @@ +"use strict"; + +var async = require('async'), + + db = require('../../database'), + groups = require('../../groups'), + meta = require('../../meta'), + pagination = require('../../pagination'), + helpers = require('../helpers'); + + +var groupsController = {}; + + +groupsController.list = function(req, res, next) { + var page = parseInt(req.query.page, 10) || 1; + var groupsPerPage = 20; + var pageCount = 0; + + async.waterfall([ + function(next) { + db.getSortedSetRevRange('groups:createtime', 0, -1, next); + }, + function(groupNames, next) { + groupNames = groupNames.filter(function(name) { + return name.indexOf(':privileges:') === -1 && name !== 'registered-users'; + }); + pageCount = Math.ceil(groupNames.length / groupsPerPage); + + var start = (page - 1) * groupsPerPage; + var stop = start + groupsPerPage - 1; + + groupNames = groupNames.slice(start, stop + 1); + groups.getGroupsData(groupNames, next); + }, + function(groupData, next) { + groupData.forEach(groups.escapeGroupData); + next(null, {groups: groupData, pagination: pagination.create(page, pageCount)}); + } + ], function(err, data) { + if (err) { + return next(err); + } + + res.render('admin/manage/groups', { + groups: data.groups, + pagination: data.pagination, + yourid: req.user.uid + }); + }); +}; + +groupsController.get = function(req, res, next) { + var groupName = req.params.name; + async.waterfall([ + function(next){ + groups.exists(groupName, next); + }, + function(exists, next) { + if (!exists) { + return helpers.notFound(req, res); + } + groups.get(groupName, {uid: req.uid}, next); + } + ], function(err, group) { + if (err) { + return next(err); + } + res.render('admin/manage/group', {group: group}); + }); +}; + +module.exports = groupsController; diff --git a/src/controllers/admin/users.js b/src/controllers/admin/users.js index 7f884b79ce..6c10694ba6 100644 --- a/src/controllers/admin/users.js +++ b/src/controllers/admin/users.js @@ -30,6 +30,15 @@ usersController.banned = function(req, res, next) { getUsers('users:banned', req, res, next); }; +usersController.registrationQueue = function(req, res, next) { + user.getRegistrationQueue(0, -1, function(err, data) { + if (err) { + return next(err); + } + res.render('admin/manage/registration', {users: data}); + }) +}; + function getUsers(set, req, res, next) { user.getUsersFromSet(set, req.uid, 0, 49, function(err, users) { if (err) { diff --git a/src/controllers/api.js b/src/controllers/api.js index a5a5c14e68..5518addd45 100644 --- a/src/controllers/api.js +++ b/src/controllers/api.js @@ -42,6 +42,7 @@ apiController.getConfig = function(req, res, next) { config.maximumAboutMeLength = meta.config.maximumAboutMeLength || 1000; config.useOutgoingLinksPage = parseInt(meta.config.useOutgoingLinksPage, 10) === 1; config.allowGuestSearching = parseInt(meta.config.allowGuestSearching, 10) === 1; + config.allowGuestUserSearching = parseInt(meta.config.allowGuestUserSearching, 10) === 1; config.allowGuestHandles = parseInt(meta.config.allowGuestHandles, 10) === 1; config.allowFileUploads = parseInt(meta.config.allowFileUploads, 10) === 1; config.allowProfileImageUploads = parseInt(meta.config.allowProfileImageUploads) === 1; @@ -54,7 +55,8 @@ apiController.getConfig = function(req, res, next) { config.disableChat = parseInt(meta.config.disableChat, 10) === 1; config.maxReconnectionAttempts = meta.config.maxReconnectionAttempts || 5; config.reconnectionDelay = meta.config.reconnectionDelay || 1500; - config.tagsPerTopic = meta.config.tagsPerTopic || 5; + config.minimumTagsPerTopic = meta.config.minimumTagsPerTopic || 0; + config.maximumTagsPerTopic = meta.config.maximumTagsPerTopic || 5; config.minimumTagLength = meta.config.minimumTagLength || 3; config.maximumTagLength = meta.config.maximumTagLength || 15; config.topicsPerPage = meta.config.topicsPerPage || 20; diff --git a/src/controllers/authentication.js b/src/controllers/authentication.js new file mode 100644 index 0000000000..beb319b04c --- /dev/null +++ b/src/controllers/authentication.js @@ -0,0 +1,274 @@ +"use strict"; + +var async = require('async'), + winston = require('winston'), + passport = require('passport'), + nconf = require('nconf'), + validator = require('validator'), + + db = require('../database'), + meta = require('../meta'), + user = require('../user'), + plugins = require('../plugins'), + utils = require('../../public/src/utils'), + Password = require('../password'), + + authenticationController = {}; + +authenticationController.register = function(req, res, next) { + var registrationType = meta.config.registrationType || 'normal'; + + if (registrationType === 'disabled') { + return res.sendStatus(403); + } + + var userData = {}; + + for (var key in req.body) { + if (req.body.hasOwnProperty(key)) { + userData[key] = req.body[key]; + } + } + + var uid; + async.waterfall([ + function(next) { + if (registrationType === 'invite-only') { + user.verifyInvitation(userData, next); + } else { + next(); + } + }, + function(next) { + if (!userData.email) { + return next(new Error('[[error:invalid-email]]')); + } + + if (!userData.username || userData.username.length < meta.config.minimumUsernameLength) { + return next(new Error('[[error:username-too-short]]')); + } + + if (userData.username.length > meta.config.maximumUsernameLength) { + return next(new Error('[[error:username-too-long')); + } + + if (!userData.password || userData.password.length < meta.config.minimumPasswordLength) { + return next(new Error('[[user:change_password_error_length]]')); + } + + next(); + }, + function(next) { + plugins.fireHook('filter:register.check', {req: req, res: res, userData: userData}, next); + }, + function(data, next) { + if (registrationType === 'normal' || registrationType === 'invite-only') { + registerAndLoginUser(req, res, userData, next); + } else if (registrationType === 'admin-approval') { + addToApprovalQueue(req, res, userData, next); + } + } + ], function(err, data) { + if (err) { + return res.status(400).send(err.message); + } + + res.json(data); + }); +}; + +function registerAndLoginUser(req, res, userData, callback) { + var uid; + async.waterfall([ + function(next) { + user.create(userData, next); + }, + function(_uid, next) { + uid = _uid; + req.login({uid: uid}, next); + }, + function(next) { + user.logIP(uid, req.ip); + + user.deleteInvitation(userData.email); + + user.notifications.sendWelcomeNotification(uid); + + plugins.fireHook('filter:register.complete', {uid: uid, referrer: req.body.referrer || nconf.get('relative_path') + '/'}, next); + } + ], callback); +} + +function addToApprovalQueue(req, res, userData, callback) { + async.waterfall([ + function(next) { + userData.ip = req.ip; + user.addToApprovalQueue(userData, next); + }, + function(next) { + next(null, {message: '[[register:registration-added-to-queue]]'}); + } + ], callback); +} + +authenticationController.login = function(req, res, next) { + // Handle returnTo data + if (req.body.hasOwnProperty('returnTo') && !req.session.returnTo) { + req.session.returnTo = req.body.returnTo; + } + + if (plugins.hasListeners('action:auth.overrideLogin')) { + return continueLogin(req, res, next); + } + + var loginWith = meta.config.allowLoginWith || 'username-email'; + + if (req.body.username && utils.isEmailValid(req.body.username) && loginWith.indexOf('email') !== -1) { + user.getUsernameByEmail(req.body.username, function(err, username) { + if (err) { + return next(err); + } + req.body.username = username ? username : req.body.username; + continueLogin(req, res, next); + }); + } else if (loginWith.indexOf('username') !== -1 && !validator.isEmail(req.body.username)) { + continueLogin(req, res, next); + } else { + res.status(500).send('[[error:wrong-login-type-' + loginWith + ']]'); + } +}; + +function continueLogin(req, res, next) { + passport.authenticate('local', function(err, userData, info) { + if (err) { + return res.status(403).send(err.message); + } + + if (!userData) { + if (typeof info === 'object') { + info = '[[error:invalid-username-or-password]]'; + } + + return res.status(403).send(info); + } + + var passwordExpiry = userData.passwordExpiry !== undefined ? parseInt(userData.passwordExpiry, 10) : null; + + // Alter user cookie depending on passed-in option + if (req.body.remember === 'on') { + var duration = 1000*60*60*24*parseInt(meta.config.loginDays || 14, 10); + req.session.cookie.maxAge = duration; + req.session.cookie.expires = new Date(Date.now() + duration); + } else { + req.session.cookie.maxAge = false; + req.session.cookie.expires = false; + } + + if (passwordExpiry && passwordExpiry < Date.now()) { + winston.verbose('[auth] Triggering password reset for uid ' + userData.uid + ' due to password policy'); + req.session.passwordExpired = true; + user.reset.generate(userData.uid, function(err, code) { + res.status(200).send(nconf.get('relative_path') + '/reset/' + code); + }); + } else { + req.login({ + uid: userData.uid + }, function(err) { + if (err) { + return res.status(403).send(err.message); + } + if (userData.uid) { + user.logIP(userData.uid, req.ip); + + plugins.fireHook('action:user.loggedIn', userData.uid); + } + + if (!req.session.returnTo) { + res.status(200).send(nconf.get('relative_path') + '/'); + } else { + var next = req.session.returnTo; + delete req.session.returnTo; + + res.status(200).send(next); + } + }); + } + })(req, res, next); +} + +authenticationController.localLogin = function(req, username, password, next) { + if (!username || !password) { + return next(new Error('[[error:invalid-password]]')); + } + + var userslug = utils.slugify(username); + var uid, userData = {}; + + async.waterfall([ + function(next) { + user.getUidByUserslug(userslug, next); + }, + function(_uid, next) { + if (!_uid) { + return next(new Error('[[error:no-user]]')); + } + uid = _uid; + user.auth.logAttempt(uid, req.ip, next); + }, + function(next) { + async.parallel({ + userData: function(next) { + db.getObjectFields('user:' + uid, ['password', 'banned', 'passwordExpiry'], next); + }, + isAdmin: function(next) { + user.isAdministrator(uid, next); + } + }, next); + }, + function(result, next) { + userData = result.userData; + userData.uid = uid; + userData.isAdmin = result.isAdmin; + + if (!result.isAdmin && parseInt(meta.config.allowLocalLogin, 10) === 0) { + return next(new Error('[[error:local-login-disabled]]')); + } + + if (!userData || !userData.password) { + return next(new Error('[[error:invalid-user-data]]')); + } + if (userData.banned && parseInt(userData.banned, 10) === 1) { + return next(new Error('[[error:user-banned]]')); + } + Password.compare(password, userData.password, next); + }, + function(passwordMatch, next) { + if (!passwordMatch) { + return next(new Error('[[error:invalid-password]]')); + } + user.auth.clearLoginAttempts(uid); + next(null, userData, '[[success:authentication-successful]]'); + } + ], next); +}; + +authenticationController.logout = function(req, res, next) { + if (req.user && parseInt(req.user.uid, 10) > 0 && req.sessionID) { + var uid = parseInt(req.user.uid, 10); + require('../socket.io').logoutUser(req.user.uid); + db.sessionStore.destroy(req.sessionID, function(err) { + if (err) { + return next(err); + } + req.logout(); + + plugins.fireHook('action:user.loggedOut', {req: req, res: res, uid: uid}); + res.status(200).send(''); + }); + } else { + res.status(200).send(''); + } +}; + + +module.exports = authenticationController; diff --git a/src/controllers/categories.js b/src/controllers/categories.js index 53ad658966..fe44af90a8 100644 --- a/src/controllers/categories.js +++ b/src/controllers/categories.js @@ -68,28 +68,6 @@ categoriesController.popular = function(req, res, next) { }); }; -categoriesController.unread = function(req, res, next) { - var stop = (parseInt(meta.config.topicsPerList, 10) || 20) - 1; - topics.getUnreadTopics(req.uid, 0, stop, function (err, data) { - if (err) { - return next(err); - } - - data.breadcrumbs = helpers.buildBreadcrumbs([{text: '[[unread:title]]'}]); - res.render('unread', data); - }); -}; - -categoriesController.unreadTotal = function(req, res, next) { - topics.getTotalUnread(req.uid, function (err, data) { - if (err) { - return next(err); - } - - res.json(data); - }); -}; - categoriesController.list = function(req, res, next) { async.parallel({ header: function (next) { diff --git a/src/controllers/groups.js b/src/controllers/groups.js index e3bdd500a6..071ffcb975 100644 --- a/src/controllers/groups.js +++ b/src/controllers/groups.js @@ -2,6 +2,7 @@ var async = require('async'), nconf = require('nconf'), + db = require('../database'), meta = require('../meta'), groups = require('../groups'), user = require('../user'), @@ -9,17 +10,33 @@ var async = require('async'), groupsController = {}; groupsController.list = function(req, res, next) { - groups.list({ - truncateUserList: true, - expand: true, - uid: req.uid - }, function(err, groups) { + var sort = req.query.sort || 'alpha'; + + groupsController.getGroupsFromSet(req.uid, sort, 0, 14, function(err, data) { if (err) { return next(err); } - res.render('groups/list', { + res.render('groups/list', data); + }); +}; + +groupsController.getGroupsFromSet = function(uid, sort, start, stop, callback) { + var set = 'groups:visible:name'; + if (sort === 'count') { + set = 'groups:visible:memberCount'; + } else if (sort === 'date') { + set = 'groups:visible:createtime'; + } + + groups.getGroupsFromSet(set, uid, start, stop, function(err, groups) { + if (err) { + return callback(err); + } + + callback(null, { groups: groups, - allowGroupCreation: parseInt(meta.config.allowGroupCreation, 10) === 1 + allowGroupCreation: parseInt(meta.config.allowGroupCreation, 10) === 1, + nextStart: stop + 1 }); }); }; @@ -62,13 +79,15 @@ groupsController.details = function(req, res, next) { async.parallel({ group: function(next) { groups.get(res.locals.groupName, { - expand: true, - uid: req.uid + uid: req.uid, + truncateUserList: true, + userListCount: 20 }, next); }, posts: function(next) { groups.getLatestMemberPosts(res.locals.groupName, 10, req.uid, next); - } + }, + isAdmin: async.apply(user.isAdministrator, req.uid) }, function(err, results) { if (err) { return next(err); diff --git a/src/controllers/helpers.js b/src/controllers/helpers.js index 8d4ccb8895..6a35540088 100644 --- a/src/controllers/helpers.js +++ b/src/controllers/helpers.js @@ -57,15 +57,17 @@ helpers.buildCategoryBreadcrumbs = function(cid, callback) { async.whilst(function() { return parseInt(cid, 10); }, function(next) { - categories.getCategoryFields(cid, ['name', 'slug', 'parentCid'], function(err, data) { + categories.getCategoryFields(cid, ['name', 'slug', 'parentCid', 'disabled'], function(err, data) { if (err) { return next(err); } - breadcrumbs.unshift({ - text: validator.escape(data.name), - url: nconf.get('relative_path') + '/category/' + data.slug - }); + if (!parseInt(data.disabled, 10)) { + breadcrumbs.unshift({ + text: validator.escape(data.name), + url: nconf.get('relative_path') + '/category/' + data.slug + }); + } cid = data.parentCid; next(); diff --git a/src/controllers/index.js b/src/controllers/index.js index d1e2ae6c4a..25e0b7f1ee 100644 --- a/src/controllers/index.js +++ b/src/controllers/index.js @@ -5,7 +5,6 @@ var async = require('async'), validator = require('validator'), winston = require('winston'), - auth = require('../routes/authentication'), meta = require('../meta'), user = require('../user'), posts = require('../posts'), @@ -19,11 +18,13 @@ var Controllers = { posts: require('./posts'), topics: require('./topics'), categories: require('./categories'), + unread: require('./unread'), tags: require('./tags'), search: require('./search'), users: require('./users'), groups: require('./groups'), accounts: require('./accounts'), + authentication: require('./authentication'), api: require('./api'), admin: require('./admin') }; @@ -74,14 +75,16 @@ Controllers.reset = function(req, res, next) { Controllers.login = function(req, res, next) { var data = {}, - loginStrategies = auth.getLoginStrategies(), + loginStrategies = require('../routes/authentication').getLoginStrategies(), emailersPresent = plugins.hasListeners('action:email.send'); + var registrationType = meta.config.registrationType || 'normal'; + data.alternate_logins = loginStrategies.length > 0; data.authentication = loginStrategies; data.showResetLink = emailersPresent; data.allowLocalLogin = parseInt(meta.config.allowLocalLogin, 10) === 1 || parseInt(req.query.local, 10) === 1; - data.allowRegistration = parseInt(meta.config.allowRegistration, 10) === 1; + data.allowRegistration = registrationType === 'normal' || registrationType === 'admin-approval'; data.allowLoginWith = '[[login:' + (meta.config.allowLoginWith || 'username-email') + ']]'; data.breadcrumbs = helpers.buildBreadcrumbs([{text: '[[global:login]]'}]); data.error = req.flash('error')[0]; @@ -90,38 +93,41 @@ Controllers.login = function(req, res, next) { }; Controllers.register = function(req, res, next) { - if(meta.config.allowRegistration !== undefined && parseInt(meta.config.allowRegistration, 10) === 0) { - return res.redirect(nconf.get('relative_path') + '/403'); + var registrationType = meta.config.registrationType || 'normal'; + + if (registrationType === 'disabled') { + return helpers.notFound(req, res); } - var data = {}, - loginStrategies = auth.getLoginStrategies(); + async.waterfall([ + function(next) { + if (registrationType === 'invite-only') { + user.verifyInvitation(req.query, next); + } else { + next(); + } + }, + function(next) { + var loginStrategies = require('../routes/authentication').getLoginStrategies(); + var data = { + 'register_window:spansize': loginStrategies.length ? 'col-md-6' : 'col-md-12', + 'alternate_logins': !!loginStrategies.length + }; - if (loginStrategies.length === 0) { - data = { - 'register_window:spansize': 'col-md-12', - 'alternate_logins': false - }; - } else { - data = { - 'register_window:spansize': 'col-md-6', - 'alternate_logins': true - }; - } + data.authentication = loginStrategies; - data.authentication = loginStrategies; + data.minimumUsernameLength = meta.config.minimumUsernameLength; + data.maximumUsernameLength = meta.config.maximumUsernameLength; + data.minimumPasswordLength = meta.config.minimumPasswordLength; + data.termsOfUse = meta.config.termsOfUse; + data.breadcrumbs = helpers.buildBreadcrumbs([{text: '[[register:register]]'}]); + data.regFormEntry = []; + data.error = req.flash('error')[0]; - data.minimumUsernameLength = meta.config.minimumUsernameLength; - data.maximumUsernameLength = meta.config.maximumUsernameLength; - data.minimumPasswordLength = meta.config.minimumPasswordLength; - data.termsOfUse = meta.config.termsOfUse; - data.breadcrumbs = helpers.buildBreadcrumbs([{text: '[[register:register]]'}]); - data.regFormEntry = []; - data.error = req.flash('error')[0]; - - plugins.fireHook('filter:register.build', {req: req, res: res, templateData: data}, function(err, data) { - if (err && global.env === 'development') { - winston.warn(JSON.stringify(err)); + plugins.fireHook('filter:register.build', {req: req, res: res, templateData: data}, next); + } + ], function(err, data) { + if (err) { return next(err); } res.render('register', data.templateData); @@ -129,6 +135,13 @@ Controllers.register = function(req, res, next) { }; Controllers.compose = function(req, res, next) { + if (req.query.p && !res.locals.isAPI) { + if (req.query.p.startsWith(nconf.get('relative_path'))) { + req.query.p = req.query.p.replace(nconf.get('relative_path'), ''); + } + return helpers.redirect(res, req.query.p); + } + res.render('', {}); }; diff --git a/src/controllers/topics.js b/src/controllers/topics.js index b65b3b997a..2975c73cb3 100644 --- a/src/controllers/topics.js +++ b/src/controllers/topics.js @@ -89,9 +89,9 @@ topicsController.get = function(req, res, next) { } if (!settings.usePagination) { if (reverse) { - postIndex = Math.max(0, postCount - (req.params.post_index || postCount) - (settings.postsPerPage / 2)); + postIndex = Math.max(0, postCount - (req.params.post_index || postCount) - Math.ceil(settings.postsPerPage / 2)); } else { - postIndex = Math.max(0, (req.params.post_index || 1) - (settings.postsPerPage / 2)); + postIndex = Math.max(0, (req.params.post_index || 1) - Math.ceil(settings.postsPerPage / 2)); } } else if (!req.query.page) { var index = 0; diff --git a/src/controllers/unread.js b/src/controllers/unread.js new file mode 100644 index 0000000000..0354ea7310 --- /dev/null +++ b/src/controllers/unread.js @@ -0,0 +1,69 @@ + +'use strict'; + +var async = require('async'), + + meta = require('../meta'), + categories = require('../categories'), + user = require('../user'), + topics = require('../topics'), + helpers = require('./helpers'); + +var unreadController = {}; + + +unreadController.unread = function(req, res, next) { + var stop = (parseInt(meta.config.topicsPerList, 10) || 20) - 1; + var results; + var cid = req.query.cid; + + async.waterfall([ + function(next) { + async.parallel({ + watchedCategories: function(next) { + user.getWatchedCategories(req.uid, next); + }, + unreadTopics: function(next) { + topics.getUnreadTopics(cid, req.uid, 0, stop, next); + } + }, next); + }, + function(_results, next) { + results = _results; + categories.getMultipleCategoryFields(results.watchedCategories, ['cid', 'name', 'slug', 'icon', 'link'], next); + }, + function(categories, next) { + categories = categories.filter(function(category) { + return category && !category.link; + }); + categories.forEach(function(category) { + category.selected = parseInt(category.cid, 10) === parseInt(cid, 10); + if (category.selected) { + results.unreadTopics.selectedCategory = category; + } + }); + results.unreadTopics.categories = categories; + next(null, results.unreadTopics); + } + ], function(err, data) { + if (err) { + return next(err); + } + + data.breadcrumbs = helpers.buildBreadcrumbs([{text: '[[unread:title]]'}]); + res.render('unread', data); + }); +}; + + +unreadController.unreadTotal = function(req, res, next) { + topics.getTotalUnread(req.uid, function (err, data) { + if (err) { + return next(err); + } + + res.json(data); + }); +}; + +module.exports = unreadController; \ No newline at end of file diff --git a/src/controllers/users.js b/src/controllers/users.js index f82e486900..d416207e29 100644 --- a/src/controllers/users.js +++ b/src/controllers/users.js @@ -3,8 +3,12 @@ var usersController = {}; var async = require('async'), + validator = require('validator'), + user = require('../user'), meta = require('../meta'), + categories = require('../categories'), + topics = require('../topics'), pagination = require('../pagination'), plugins = require('../plugins'), db = require('../database'), @@ -36,7 +40,7 @@ usersController.getOnlineUsers = function(req, res, next) { } var userData = { - 'route_users:online': true, + 'users:online': true, search_display: 'hidden', loadmore_display: results.count > 50 ? 'block' : 'hide', users: results.users, @@ -71,7 +75,7 @@ usersController.getUsers = function(set, start, stop, req, res, next) { users: data.users, pagination: pagination.create(1, pageCount) }; - userData['route_' + set] = true; + userData[set] = true; render(req, res, userData, next); }); }; @@ -97,7 +101,7 @@ usersController.getUsersAndCount = function(set, uid, start, stop, callback) { }; usersController.getUsersForSearch = function(req, res, next) { - if (!req.uid) { + if (!req.uid && parseInt(meta.config.allowGuestUserSearching, 10) !== 1) { return helpers.notAllowed(req, res); } var resultsPerPage = parseInt(meta.config.userSearchResultsPerPage, 10) || 20; @@ -117,11 +121,79 @@ usersController.getUsersForSearch = function(req, res, next) { }); }; +usersController.getMap = function(req, res, next) { + var socketIO = require('../socket.io'); + var rooms = require('../socket.io/rooms'); + + var roomNames = ['user_list', 'categories', 'unread_topics', 'recent_topics', 'popular_topics', 'tags']; + var links = { + user_list: '/users', + categories: '/categories', + unread_topics: '/unread', + recent_topics: '/recent', + popular_topics: '/popular', + tags: '/tags' + }; + + var keys = Object.keys(rooms.roomClients()); + + keys = keys.filter(function(key) { + return key.startsWith('topic_') || key.startsWith('category_'); + }); + + roomNames = roomNames.concat(keys); + + async.map(roomNames, function(roomName, next) { + socketIO.getUsersInRoom(0, roomName, 0, 39, function(err, data) { + if (err) { + return next(err); + } + + if (roomName.startsWith('category_')) { + var cid = roomName.split('_')[1]; + categories.getCategoryFields(cid, ['slug', 'name'], function(err, categoryData) { + if (err) { + return next(err); + } + data.room = validator.escape(categoryData.name); + data.link = '/category/' + categoryData.slug; + next(null, data); + }); + } else if (roomName.startsWith('topic_')) { + var tid = roomName.split('_')[1]; + topics.getTopicFields(tid, ['slug', 'title'], function(err, topicData) { + if (err) { + return next(err); + } + data.room = validator.escape(topicData.title); + data.link = '/topic/' + topicData.slug; + next(null, data); + }); + } else { + next(null, data); + } + }); + }, function(err, data) { + data.sort(function(a, b) { + return b.total - a.total; + }); + + data.forEach(function(room) { + if (!room.link) { + room.link = links[room.room]; + } + }); + + res.render('usersMap', {rooms: data}); + }); +}; + function render(req, res, data, next) { plugins.fireHook('filter:users.build', {req: req, res: res, templateData: data}, function(err, data) { if (err) { return next(err); } + data.templateData.inviteOnly = meta.config.registrationType === 'invite-only'; res.render('users', data.templateData); }); } diff --git a/src/database.js b/src/database.js index 40399aa028..14a5a52892 100644 --- a/src/database.js +++ b/src/database.js @@ -30,11 +30,15 @@ function setupSecondaryDB() { primaryDBhelpers = primaryDB.helpers; primaryDB.init = function(callback) { - async.parallel([primaryDBinit, secondaryDB.init], callback); + async.parallel([primaryDBinit, secondaryDB.init], function(err, results) { + callback(err); + }); }; primaryDB.close = function(callback) { - async.parallel([primaryDBclose, secondaryDB.close], callback); + async.parallel([primaryDBclose, secondaryDB.close], function(err, results) { + callback(err); + }); }; primaryDB.helpers = {}; diff --git a/src/database/mongo.js b/src/database/mongo.js index ecae3402e7..389a85bf7d 100644 --- a/src/database/mongo.js +++ b/src/database/mongo.js @@ -7,9 +7,12 @@ async = require('async'), nconf = require('nconf'), session = require('express-session'), + _ = require('underscore'), semver = require('semver'), db, mongoClient; + _.mixin(require('underscore.deep')); + module.questions = [ { name: 'mongo:host', @@ -60,7 +63,7 @@ var usernamePassword = ''; if (nconf.get('mongo:username') && nconf.get('mongo:password')) { - usernamePassword = nconf.get('mongo:username') + ':' + nconf.get('mongo:password') + '@'; + usernamePassword = nconf.get('mongo:username') + ':' + encodeURIComponent(nconf.get('mongo:password')) + '@'; } // Sensible defaults for Mongo, if not set @@ -89,6 +92,9 @@ poolSize: parseInt(nconf.get('mongo:poolSize'), 10) || 10 } }; + + connOptions = _.deepExtend((nconf.get('mongo:options') || {}), connOptions); + mongoClient.connect(connString, connOptions, function(err, _db) { if (err) { winston.error("NodeBB could not connect to your Mongo database. Mongo returned the following error: " + err.message); @@ -161,7 +167,6 @@ }; module.checkCompatibility = function(callback) { - // For MongoDB, we need to ensure that the mongodb package is >= 2.0.0 var mongoPkg = require.main.require('./node_modules/mongodb/package.json'), err = semver.lt(mongoPkg.version, '2.0.0') ? new Error('The `mongodb` package is out-of-date, please run `./nodebb setup` again.') : null; diff --git a/src/database/mongo/hash.js b/src/database/mongo/hash.js index 65157d8cb9..8a833699f9 100644 --- a/src/database/mongo/hash.js +++ b/src/database/mongo/hash.js @@ -39,14 +39,14 @@ module.exports = function(db, module) { return callback(null, []); } db.collection('objects').find({_key: {$in: keys}}, {_id: 0}).toArray(function(err, data) { - if(err) { + if (err) { return callback(err); } var map = helpers.toMap(data); var returnData = []; - for(var i=0; i data.members.length; + }); + + callback(null, data.groups); }); - }); + } }; Groups.getGroups = function(start, stop, callback) { @@ -101,125 +110,48 @@ var async = require('async'), }; Groups.get = function(groupName, options, callback) { - var truncated = false, - numUsers; + if (!groupName) { + return callback(new Error('[[error:invalid-group]]')); + } + + options.escape = options.hasOwnProperty('escape') ? options.escape : true; + var stop = -1; async.parallel({ base: function (next) { - if (ephemeralGroups.indexOf(groupName) === -1) { - db.getObject('group:' + groupName, next); - } else { - next(null, internals.getEphemeralGroup(groupName)); - } + db.getObject('group:' + groupName, next); }, - users: function (next) { - db.getSortedSetRevRange('group:' + groupName + ':members', 0, -1, function (err, uids) { - if (err) { - return next(err); - } + members: function (next) { + if (options.truncateUserList) { + stop = (parseInt(options.userListCount, 10) || 4) - 1; + } - uids = uids.filter(function(uid) { - return uid && parseInt(uid, 10); - }); - - if (options.truncateUserList) { - var userListCount = parseInt(options.userListCount, 10) || 4; - if (uids.length > userListCount) { - numUsers = uids.length; - uids.length = userListCount; - truncated = true; - } - } - - if (options.expand) { - async.waterfall([ - async.apply(user.getUsers, uids, options.uid || 0), - function(users, next) { - // Filter out non-matches - users = users.filter(Boolean); - - async.mapLimit(users, 10, function(userObj, next) { - Groups.ownership.isOwner(userObj.uid, groupName, function(err, isOwner) { - if (err) { - winston.warn('[groups.get] Could not determine ownership in group `' + groupName + '` for uid `' + userObj.uid + '`: ' + err.message); - return next(null, userObj); - } - - userObj.isOwner = isOwner; - next(null, userObj); - }); - }, function(err, users) { - if (err) { - return next(); - } - - next(null, users.sort(function(a, b) { - if (a.isOwner === b.isOwner) { - return 0; - } else { - return a.isOwner && !b.isOwner ? -1 : 1; - } - })); - }); - } - ], next); - } else { - next(err, uids); - } - }); + Groups.getOwnersAndMembers(groupName, options.uid, 0, stop, next); }, pending: function (next) { - db.getSetMembers('group:' + groupName + ':pending', function (err, uids) { - if (err) { - return next(err); + async.waterfall([ + function(next) { + db.getSetMembers('group:' + groupName + ':pending', next); + }, + function(uids, next) { + user.getUsersData(uids, next); } - - if (options.expand && uids.length) { - async.map(uids, user.getUserData, next); - } else { - next(err, uids); + ], next); + }, + invited: function (next) { + async.waterfall([ + function(next) { + db.getSetMembers('group:' + groupName + ':invited', next); + }, + function(uids, next) { + user.getUsersData(uids, next); } - }); - }, - isMember: function(next) { - // Retrieve group membership state, if uid is passed in - if (!options.uid) { - return next(); - } - - Groups.isMember(options.uid, groupName, function(err, isMember) { - if (err) { - winston.warn('[groups.get] Could not determine membership in group `' + groupName + '` for uid `' + options.uid + '`: ' + err.message); - return next(); - } - - next(null, isMember); - }); - }, - isPending: function(next) { - // Retrieve group membership state, if uid is passed in - if (!options.uid) { - return next(); - } - - db.isSetMember('group:' + groupName + ':pending', options.uid, next); + ], next); }, + isMember: async.apply(Groups.isMember, options.uid, groupName), + isPending: async.apply(Groups.isPending, options.uid, groupName), isInvited: async.apply(Groups.isInvited, options.uid, groupName), - isOwner: function(next) { - // Retrieve group ownership state, if uid is passed in - if (!options.uid) { - return next(); - } - - Groups.ownership.isOwner(options.uid, groupName, function(err, isOwner) { - if (err) { - winston.warn('[groups.get] Could not determine ownership in group `' + groupName + '` for uid `' + options.uid + '`: ' + err.message); - return next(); - } - - next(null, isOwner); - }); - } + isOwner: async.apply(Groups.ownership.isOwner, options.uid, groupName) }, function (err, results) { if (err) { return callback(err); @@ -237,28 +169,27 @@ var async = require('async'), if (err) { return callback(err); } - results.base.name = !options.unescape ? validator.escape(results.base.name) : results.base.name; - results.base.description = !options.unescape ? validator.escape(results.base.description) : results.base.description; + + if (options.escape) { + Groups.escapeGroupData(results.base); + } + results.base.descriptionParsed = descriptionParsed; - results.base.userTitle = !options.unescape ? validator.escape(results.base.userTitle) : results.base.userTitle; results.base.userTitleEnabled = results.base.userTitleEnabled ? !!parseInt(results.base.userTitleEnabled, 10) : true; results.base.createtimeISO = utils.toISOString(results.base.createtime); - results.base.members = results.users.filter(Boolean); + results.base.members = results.members; + results.base.membersNextStart = stop + 1; results.base.pending = results.pending.filter(Boolean); - results.base.count = numUsers || results.base.members.length; - results.base.memberCount = numUsers || results.base.members.length; + results.base.invited = results.invited.filter(Boolean); results.base.deleted = !!parseInt(results.base.deleted, 10); results.base.hidden = !!parseInt(results.base.hidden, 10); results.base.system = !!parseInt(results.base.system, 10); results.base.private = results.base.private ? !!parseInt(results.base.private, 10) : true; - results.base.deletable = !results.base.system; - results.base.truncated = truncated; results.base.isMember = results.isMember; results.base.isPending = results.isPending; results.base.isInvited = results.isInvited; results.base.isOwner = results.isOwner; - plugins.fireHook('filter:group.get', {group: results.base}, function(err, data) { callback(err, data ? data.group : null); }); @@ -266,6 +197,52 @@ var async = require('async'), }); }; + Groups.getOwnersAndMembers = function(groupName, uid, start, stop, callback) { + async.parallel({ + owners: function (next) { + async.waterfall([ + function(next) { + db.getSetMembers('group:' + groupName + ':owners', next); + }, + function(uids, next) { + user.getUsers(uids, uid, next); + } + ], next); + }, + members: function (next) { + user.getUsersFromSet('group:' + groupName + ':members', uid, start, stop, next); + } + }, function(err, results) { + if (err) { + return callback(err); + } + + var ownerUids = []; + results.owners.forEach(function(user) { + if (user) { + user.isOwner = true; + ownerUids.push(user.uid.toString()); + } + }); + + results.members = results.members.filter(function(user, index, array) { + return user && user.uid && ownerUids.indexOf(user.uid.toString()) === -1; + }); + results.members = results.owners.concat(results.members); + + callback(null, results.members); + }); + }; + + Groups.escapeGroupData = function(group) { + if (group) { + group.nameEncoded = encodeURIComponent(group.name); + group.displayName = validator.escape(group.name); + group.description = validator.escape(group.description); + group.userTitle = validator.escape(group.userTitle) || group.displayName; + } + }; + Groups.getByGroupslug = function(slug, options, callback) { db.getObjectField('groupslug:groupname', slug, function(err, groupName) { if (err) { @@ -295,8 +272,13 @@ var async = require('async'), }; Groups.setGroupField = function(groupName, field, value, callback) { - plugins.fireHook('action:group.set', {field: field, value: value, type: 'set'}); - db.setObjectField('group:' + groupName, field, value, callback); + db.setObjectField('group:' + groupName, field, value, function(err) { + if (err) { + return callback(err); + } + plugins.fireHook('action:group.set', {field: field, value: value, type: 'set'}); + callback(); + }); }; Groups.isPrivate = function(groupName, callback) { @@ -322,139 +304,6 @@ var async = require('async'), }); }; - Groups.getMembers = function(groupName, start, stop, callback) { - db.getSortedSetRevRange('group:' + groupName + ':members', start, stop, callback); - }; - - Groups.getMembersOfGroups = function(groupNames, callback) { - db.getSortedSetsMembers(groupNames.map(function(name) { - return 'group:' + name + ':members'; - }), callback); - }; - - Groups.isMember = function(uid, groupName, callback) { - if (!uid || parseInt(uid, 10) <= 0) { - return callback(null, false); - } - db.isSortedSetMember('group:' + groupName + ':members', uid, callback); - }; - - Groups.isMembers = function(uids, groupName, callback) { - db.isSortedSetMembers('group:' + groupName + ':members', uids, callback); - }; - - Groups.isMemberOfGroups = function(uid, groups, callback) { - if (!uid || parseInt(uid, 10) <= 0) { - return callback(null, groups.map(function() {return false;})); - } - groups = groups.map(function(groupName) { - return 'group:' + groupName + ':members'; - }); - - db.isMemberOfSortedSets(groups, uid, callback); - }; - - Groups.getMemberCount = function(groupName, callback) { - db.getObjectField('group:' + groupName, 'memberCount', callback); - }; - - Groups.isMemberOfGroupList = function(uid, groupListKey, callback) { - db.getSortedSetRange('group:' + groupListKey + ':members', 0, -1, function(err, groupNames) { - if (err) { - return callback(err); - } - groupNames = internals.removeEphemeralGroups(groupNames); - if (groupNames.length === 0) { - return callback(null, false); - } - - Groups.isMemberOfGroups(uid, groupNames, function(err, isMembers) { - if (err) { - return callback(err); - } - - callback(null, isMembers.indexOf(true) !== -1); - }); - }); - }; - - Groups.isMemberOfGroupsList = function(uid, groupListKeys, callback) { - var sets = groupListKeys.map(function(groupName) { - return 'group:' + groupName + ':members'; - }); - - db.getSortedSetsMembers(sets, function(err, members) { - if (err) { - return callback(err); - } - - var uniqueGroups = _.unique(_.flatten(members)); - uniqueGroups = internals.removeEphemeralGroups(uniqueGroups); - - Groups.isMemberOfGroups(uid, uniqueGroups, function(err, isMembers) { - if (err) { - return callback(err); - } - - var map = {}; - - uniqueGroups.forEach(function(groupName, index) { - map[groupName] = isMembers[index]; - }); - - var result = members.map(function(groupNames) { - for (var i=0; i
    @@ -83,7 +75,13 @@
    - {notices.doneText} {notices.notDoneText} + + {notices.doneText} + + + {notices.notDoneText} + +
    @@ -93,6 +91,22 @@
    +
    +
    System Control
    +
    +

    + + +

    +

    + Maintenance Mode +

    + +
    + Realtime Chart Updates OFF +
    +
    +
    Anonymous vs Registered Users
    @@ -114,7 +128,8 @@
  • On categories list
  • Reading posts
  • Browsing topics
  • -
  • Recent / Unread
  • +
  • Recent
  • +
  • Unread
  • diff --git a/src/views/admin/general/navigation.tpl b/src/views/admin/general/navigation.tpl index 36c7ac2ce7..7023f97f12 100644 --- a/src/views/admin/general/navigation.tpl +++ b/src/views/admin/general/navigation.tpl @@ -7,30 +7,37 @@
  • - - - - - - +
    +
    + + + +
    +
    + + + +
    +

    + Properties:
    @@ -38,6 +45,11 @@ checked/> Only display to logged in users
    +
    + +

    diff --git a/src/views/admin/header.tpl b/src/views/admin/header.tpl index d0460bb058..9516b945d7 100644 --- a/src/views/admin/header.tpl +++ b/src/views/admin/header.tpl @@ -26,6 +26,7 @@ + + diff --git a/src/views/admin/manage/categories.tpl b/src/views/admin/manage/categories.tpl index 39d2a15c9b..c57996551a 100644 --- a/src/views/admin/manage/categories.tpl +++ b/src/views/admin/manage/categories.tpl @@ -1,120 +1,20 @@
    -
    -
    -
    Active Categories
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameDescriptionTopicsPosts
    - - - - {active.name}{active.description}{active.topic_count}{active.post_count} -
    - Edit - -
    -
    -
    - You have no active categories. -
    -
    -
    -
    +
    +
    +
    Categories
    +
    +
    +
    +
    +
    -
    -
    Disabled Categories
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameDescriptionTopicsPosts
    - - - - {disabled.name}{disabled.description}{disabled.topic_count}{disabled.post_count} -
    - Edit - -
    -
    -
    - You have no disabled categories. -
    -
    -
    -
    -
    - -
    -
    -
    Categories Control Panel
    -
    - -
    -
    -
    +
    +
    +
    Categories Control Panel
    +
    + +
    +
    +
    diff --git a/src/views/admin/manage/category.tpl b/src/views/admin/manage/category.tpl index 80b5dbe694..ee53173869 100644 --- a/src/views/admin/manage/category.tpl +++ b/src/views/admin/manage/category.tpl @@ -17,15 +17,12 @@
    - -
    -
    - +
    +
    +
    - - - +
    @@ -86,9 +83,9 @@ these settings.


    - -
    - +
    + +
  • diff --git a/src/views/admin/manage/group.tpl b/src/views/admin/manage/group.tpl new file mode 100644 index 0000000000..318a913de2 --- /dev/null +++ b/src/views/admin/manage/group.tpl @@ -0,0 +1,125 @@ +
    + +
    +
    +
    Group Settings
    +
    + + +
    +
    + + readonly/> +
    +
    + +
    +
    + + +
    +
    + +
    +
    + + +
    +
    + +
    +
    +
    + +
    +
    + +
    +
    + + {group.userTitle} + +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    + + +
      +
      +
      + +
      +
      + +

      Click on a user to remove them from the group

      +
        + +
      • + + {group.members.username} +
      • + +
      +
      +
      + + +
      +
      + +
      +
      +
      Groups Control Panel
      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      + + +
      + + + diff --git a/src/views/admin/manage/groups.tpl b/src/views/admin/manage/groups.tpl index 26fe157dac..e2079e74b4 100644 --- a/src/views/admin/manage/groups.tpl +++ b/src/views/admin/manage/groups.tpl @@ -2,37 +2,36 @@
      Groups List
      -
      -
        - -
      • -
        -
        -

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

        -

        {groups.description}

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

        {groups.description}

        +
        +
        @@ -48,8 +47,6 @@
      - - - + diff --git a/src/views/admin/manage/registration.tpl b/src/views/admin/manage/registration.tpl new file mode 100644 index 0000000000..b602f54141 --- /dev/null +++ b/src/views/admin/manage/registration.tpl @@ -0,0 +1,55 @@ +
      +
      +
      +
      Registration Queue
      +
      + + + + + + + + + + + + + + + + + +
      NameEmailIPTime
      + + + + + + {users.username} + + + + + + + {users.email} + + + + + + + {users.ip} + + + +
      + + +
      +
      +
      +
      +
      +
      \ No newline at end of file diff --git a/src/views/admin/manage/tags.tpl b/src/views/admin/manage/tags.tpl index 49072e43af..e503c0d418 100644 --- a/src/views/admin/manage/tags.tpl +++ b/src/views/admin/manage/tags.tpl @@ -39,15 +39,15 @@
      Modify Tag

      Select tags via clicking and/or dragging, use shift to select multiple.

      - - + +
      -
      Search for Tags
      -
      +
      + Click here to visit the tag settings page.
      diff --git a/src/views/admin/manage/users.tpl b/src/views/admin/manage/users.tpl index e8dda5de23..8b60cd2b0a 100644 --- a/src/views/admin/manage/users.tpl +++ b/src/views/admin/manage/users.tpl @@ -8,6 +8,7 @@
    • Top Posters
    • Most Reputation
    • Banned
    • +
    • Registration Queue
    • User Search
    • diff --git a/src/views/admin/partials/categories/category-rows.tpl b/src/views/admin/partials/categories/category-rows.tpl new file mode 100644 index 0000000000..2124797cb1 --- /dev/null +++ b/src/views/admin/partials/categories/category-rows.tpl @@ -0,0 +1,29 @@ +
        + +
      • class="disabled"> +
        +
        +
        +
        + +
        +
        +
        {categories.name}
        +

        {categories.description}

        +
        +
        +
        +
        +
        +
        + + Edit +
        +
        +
        +
        +
      • + +
      \ No newline at end of file diff --git a/src/views/admin/partials/categories/create.tpl b/src/views/admin/partials/categories/create.tpl new file mode 100644 index 0000000000..52e218fc0e --- /dev/null +++ b/src/views/admin/partials/categories/create.tpl @@ -0,0 +1,15 @@ +
      +
      + + +
      +
      + + +
      +
      \ No newline at end of file diff --git a/src/views/admin/partials/categories/groups.tpl b/src/views/admin/partials/categories/groups.tpl index 8121d2f1f7..aa2da68cfc 100644 --- a/src/views/admin/partials/categories/groups.tpl +++ b/src/views/admin/partials/categories/groups.tpl @@ -1,5 +1,5 @@ -
    • +
    • - {groups.name} + {groups.displayName}
    • \ No newline at end of file diff --git a/src/views/admin/partials/categories/privileges.tpl b/src/views/admin/partials/categories/privileges.tpl index 7e1f97b580..eb97e6bcac 100644 --- a/src/views/admin/partials/categories/privileges.tpl +++ b/src/views/admin/partials/categories/privileges.tpl @@ -1,51 +1,74 @@ -
      - - - - - - - - - - - - - {function.spawnPrivilegeStates, privileges.users.username, privileges} - - - - - - - -
      User{privileges.labels.users.name}
      {privileges.users.username}
      -
      No user-specific privileges in this category.
      -
      + + + + + + + + + + + + + {function.spawnPrivilegeStates, privileges.users.username, privileges} + + + + + + + + + + +
      User{privileges.labels.users.name}
      {privileges.users.username}
      + +
      +
      + + No user-specific privileges in this category. +
      +
      - - - - - - - - - - - {function.spawnPrivilegeStates, name, privileges} - - -
      Group{privileges.labels.groups.name}
      - - - - {privileges.groups.name} -
      -
      - If the registered-users group is granted a specific privilege, all other groups receive an - implicit privilege, even if they are not explicitly defined/checked. This implicit - privilege is shown to you because all users are part of the registered-users user group, - and so, privileges for additional groups need not be explicitly granted. -
      -
      \ No newline at end of file + + + + + + + + + + + + + {function.spawnPrivilegeStates, name, privileges} + + + + + + + + + + +
      Group{privileges.labels.groups.name}
      + + + + {privileges.groups.name} +
      + +
      +
      + + No group-specific privileges in this category. +
      +
      +
      + If the registered-users group is granted a specific privilege, all other groups receive an + implicit privilege, even if they are not explicitly defined/checked. This implicit + privilege is shown to you because all users are part of the registered-users user group, + and so, privileges for additional groups need not be explicitly granted. +
      diff --git a/src/views/admin/partials/download_plugin_item.tpl b/src/views/admin/partials/download_plugin_item.tpl new file mode 100644 index 0000000000..cd31aea8b4 --- /dev/null +++ b/src/views/admin/partials/download_plugin_item.tpl @@ -0,0 +1,18 @@ +
    • +
      + + +
      + +

      {download.name}

      + + +

      {download.description}

      + + + Latest {download.latest} + + +

      For more information: {download.url}

      + +
    • diff --git a/src/views/admin/partials/installed_plugin_item.tpl b/src/views/admin/partials/installed_plugin_item.tpl new file mode 100644 index 0000000000..b0399df5e2 --- /dev/null +++ b/src/views/admin/partials/installed_plugin_item.tpl @@ -0,0 +1,40 @@ + +
    • +
      + + Themes + + + + + +
      + +

      {installed.name}

      + + +

      {installed.description}

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

      For more information: {installed.url}

      + +
    • + + +
    • +
      + + + +
      + +

      {installed.id}

      +

      + The state of this plugin could not be determined, possibly due to a misconfiguration error. +

      +
    • + diff --git a/src/views/admin/partials/menu.tpl b/src/views/admin/partials/menu.tpl index 4ef3372288..ff59c181d9 100644 --- a/src/views/admin/partials/menu.tpl +++ b/src/views/admin/partials/menu.tpl @@ -26,7 +26,7 @@
    • Email
    • User
    • Group
    • -
    • Guest
    • +
    • Guests
    • Post
    • Pagination
    • Tags
    • @@ -79,7 +79,12 @@
    • - {plugins.name} + + + + + {plugins.name} +
    • diff --git a/src/views/admin/settings/general.tpl b/src/views/admin/settings/general.tpl index 1a97060292..ae63b28cbf 100644 --- a/src/views/admin/settings/general.tpl +++ b/src/views/admin/settings/general.tpl @@ -32,9 +32,12 @@
      Site Logo
      - +
      - + +

      + +
    • @@ -54,11 +57,6 @@
      Miscellaneous
      -
      - -
      + +
      + +
      + +
      + +
      + +
      diff --git a/src/views/admin/settings/post.tpl b/src/views/admin/settings/post.tpl index 4f4314228d..1649b42e8c 100644 --- a/src/views/admin/settings/post.tpl +++ b/src/views/admin/settings/post.tpl @@ -66,6 +66,22 @@ +
      +
      Teaser Settings
      +
      +
      +
      + + +
      +
      +
      +
      + +
      Signature Settings
      diff --git a/src/views/admin/settings/tags.tpl b/src/views/admin/settings/tags.tpl index 716cdbf56d..857080ddcf 100644 --- a/src/views/admin/settings/tags.tpl +++ b/src/views/admin/settings/tags.tpl @@ -10,8 +10,12 @@
      - - + + +
      +
      + +
      @@ -22,6 +26,7 @@
      + Click here to visit the tag management page.
      diff --git a/src/views/admin/settings/user.tpl b/src/views/admin/settings/user.tpl index 94c3b536fc..f03c84d838 100644 --- a/src/views/admin/settings/user.tpl +++ b/src/views/admin/settings/user.tpl @@ -4,11 +4,6 @@
      User List
      -
      - -
      + +
      + + +
      diff --git a/src/views/admin/settings/web-crawler.tpl b/src/views/admin/settings/web-crawler.tpl index 349b5f6664..672360d150 100644 --- a/src/views/admin/settings/web-crawler.tpl +++ b/src/views/admin/settings/web-crawler.tpl @@ -27,11 +27,12 @@
      - - + +
      + View Sitemap diff --git a/src/views/emails/digest.tpl b/src/views/emails/digest.tpl index aca42a05e0..e763b53755 100644 --- a/src/views/emails/digest.tpl +++ b/src/views/emails/digest.tpl @@ -40,5 +40,5 @@

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

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

      [[email:greeting_no_name]],

      + +

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

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

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

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

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

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

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

      + +

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

      + +

      [[email:welcome.text3]]

      + +

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

      diff --git a/src/views/emails/registration_accepted_plaintext.tpl b/src/views/emails/registration_accepted_plaintext.tpl new file mode 100644 index 0000000000..a1bacf0f9f --- /dev/null +++ b/src/views/emails/registration_accepted_plaintext.tpl @@ -0,0 +1,9 @@ +[[email:greeting_with_name, {username}]], + +[[email:welcome.text1, {site_title}]] + +[[email:welcome.text3]] + + +[[email:closing]] +{site_title} \ No newline at end of file diff --git a/src/views/partials/requirejs-config.tpl b/src/views/partials/requirejs-config.tpl index 932c571f4a..0666e6ffdf 100644 --- a/src/views/partials/requirejs-config.tpl +++ b/src/views/partials/requirejs-config.tpl @@ -4,7 +4,7 @@ waitSeconds: 3, urlArgs: "{cache-buster}", paths: { - 'forum': '../forum', + 'forum': '../client', 'vendor': '../../vendor', 'mousetrap': '../../bower/mousetrap/mousetrap' } diff --git a/src/views/partials/variables/groups/details.tpl b/src/views/partials/variables/groups/details.tpl index 0cefdde305..5379a4a8aa 100644 --- a/src/views/partials/variables/groups/details.tpl +++ b/src/views/partials/variables/groups/details.tpl @@ -1,2 +1,2 @@ - + \ No newline at end of file diff --git a/src/webserver.js b/src/webserver.js index cc41be029e..abeb367a90 100644 --- a/src/webserver.js +++ b/src/webserver.js @@ -1,230 +1,230 @@ - -'use strict'; - -var path = require('path'), - fs = require('fs'), - nconf = require('nconf'), - express = require('express'), - app = express(), - server, - winston = require('winston'), - async = require('async'), - - emailer = require('./emailer'), - meta = require('./meta'), - logger = require('./logger'), - plugins = require('./plugins'), - middleware = require('./middleware'), - routes = require('./routes'), - emitter = require('./emitter'), - - helpers = require('../public/src/modules/helpers'); - -if (nconf.get('ssl')) { - server = require('https').createServer({ - key: fs.readFileSync(nconf.get('ssl').key), - cert: fs.readFileSync(nconf.get('ssl').cert) - }, app); -} else { - server = require('http').createServer(app); -} - -module.exports.server = server; - -server.on('error', function(err) { - winston.error(err); - if (err.code === 'EADDRINUSE') { - winston.error('NodeBB address in use, exiting...'); - process.exit(0); - } else { - throw err; - } -}); - - -module.exports.listen = function() { - emailer.registerApp(app); - - middleware = middleware(app); - - helpers.register(); - - logger.init(app); - - emitter.all(['templates:compiled', 'meta:js.compiled', 'meta:css.compiled'], function() { - winston.info('NodeBB Ready'); - emitter.emit('nodebb:ready'); - listen(); - }); - - initializeNodeBB(function(err) { - if (err) { - winston.error(err); - process.exit(); - } - if (process.send) { - process.send({ - action: 'ready' - }); - } - }); -}; - -function initializeNodeBB(callback) { - var skipJS, skipLess, fromFile = nconf.get('from-file') || ''; - - if (fromFile.match('js')) { - winston.info('[minifier] Minifying client-side JS skipped'); - skipJS = true; - } - - if (fromFile.match('less')) { - winston.info('[minifier] Compiling LESS files skipped'); - skipLess = true; - } - - async.waterfall([ - async.apply(cacheStaticFiles), - async.apply(meta.themes.setupPaths), - function(next) { - plugins.init(app, middleware, next); - }, - function(next) { - async.parallel([ - async.apply(meta.templates.compile), - async.apply(!skipJS ? meta.js.minify : meta.js.getFromFile, app.enabled('minification')), - async.apply(!skipLess ? meta.css.minify : meta.css.getFromFile), - async.apply(meta.sounds.init) - ], next); - }, - function(results, next) { - plugins.fireHook('static:app.preload', { - app: app, - middleware: middleware - }, next); - }, - function(next) { - routes(app, middleware); - next(); - } - ], callback); -} - -function cacheStaticFiles(callback) { - if (global.env === 'development') { - return 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(); - }); - } - }); -} - -function listen(callback) { - var port = nconf.get('port'); - - if (Array.isArray(port)) { - if (!port.length) { - winston.error('[startup] empty ports array in config.json'); - process.exit(); - } - - winston.warn('[startup] If you want to start nodebb on multiple ports please use loader.js'); - winston.warn('[startup] Defaulting to first port in array, ' + port[0]); - port = port[0]; - if (!port) { - winston.error('[startup] Invalid port, exiting'); - process.exit(); - } - } - - if (port !== 80 && port !== 443 && nconf.get('use_port') === false) { - winston.info('Enabling \'trust proxy\''); - app.enable('trust proxy'); - } - - if ((port === 80 || port === 443) && process.env.NODE_ENV !== 'development') { - winston.info('Using ports 80 and 443 is not recommend; use a proxy instead. See README.md'); - } - - var isSocket = isNaN(port), - args = isSocket ? [port] : [port, nconf.get('bind_address')], - bind_address = ((nconf.get('bind_address') === "0.0.0.0" || !nconf.get('bind_address')) ? '0.0.0.0' : nconf.get('bind_address')) + ':' + port, - oldUmask; - - args.push(function(err) { - if (err) { - winston.info('[startup] NodeBB was unable to listen on: ' + bind_address); - process.exit(); - } - - winston.info('NodeBB is now listening on: ' + (isSocket ? port : bind_address)); - if (oldUmask) { - process.umask(oldUmask); - } - }); - - // Alter umask if necessary - if (isSocket) { - oldUmask = process.umask('0000'); - module.exports.testSocket(port, function(err) { - if (!err) { - server.listen.apply(server, args); - } else { - winston.error('[startup] NodeBB was unable to secure domain socket access (' + port + ')'); - winston.error('[startup] ' + err.message); - process.exit(); - } - }); - } else { - server.listen.apply(server, args); - } -} - -module.exports.testSocket = function(socketPath, callback) { - if (typeof socketPath !== 'string') { - return callback(new Error('invalid socket path : ' + socketPath)); - } - var net = require('net'); - async.series([ - function(next) { - fs.exists(socketPath, function(exists) { - if (exists) { - next(); - } else { - callback(); - } - }); - }, - function(next) { - var testSocket = new net.Socket(); - testSocket.on('error', function(err) { - next(err.code !== 'ECONNREFUSED' ? err : null); - }); - testSocket.connect({ path: socketPath }, function() { - // Something's listening here, abort - callback(new Error('port-in-use')); - }); - }, - async.apply(fs.unlink, socketPath), // The socket was stale, kick it out of the way - ], callback); -}; - - + +'use strict'; + +var path = require('path'), + fs = require('fs'), + nconf = require('nconf'), + express = require('express'), + app = express(), + server, + winston = require('winston'), + async = require('async'), + + emailer = require('./emailer'), + meta = require('./meta'), + logger = require('./logger'), + plugins = require('./plugins'), + middleware = require('./middleware'), + routes = require('./routes'), + emitter = require('./emitter'), + + helpers = require('../public/src/modules/helpers'); + +if (nconf.get('ssl')) { + server = require('https').createServer({ + key: fs.readFileSync(nconf.get('ssl').key), + cert: fs.readFileSync(nconf.get('ssl').cert) + }, app); +} else { + server = require('http').createServer(app); +} + +module.exports.server = server; + +server.on('error', function(err) { + winston.error(err); + if (err.code === 'EADDRINUSE') { + winston.error('NodeBB address in use, exiting...'); + process.exit(0); + } else { + throw err; + } +}); + + +module.exports.listen = function() { + emailer.registerApp(app); + + middleware = middleware(app); + + helpers.register(); + + logger.init(app); + + emitter.all(['templates:compiled', 'meta:js.compiled', 'meta:css.compiled'], function() { + winston.info('NodeBB Ready'); + emitter.emit('nodebb:ready'); + listen(); + }); + + initializeNodeBB(function(err) { + if (err) { + winston.error(err); + process.exit(); + } + if (process.send) { + process.send({ + action: 'ready' + }); + } + }); +}; + +function initializeNodeBB(callback) { + var skipJS, skipLess, fromFile = nconf.get('from-file') || ''; + + if (fromFile.match('js')) { + winston.info('[minifier] Minifying client-side JS skipped'); + skipJS = true; + } + + if (fromFile.match('less')) { + winston.info('[minifier] Compiling LESS files skipped'); + skipLess = true; + } + + async.waterfall([ + async.apply(cacheStaticFiles), + async.apply(meta.themes.setupPaths), + function(next) { + plugins.init(app, middleware, next); + }, + function(next) { + async.parallel([ + async.apply(meta.templates.compile), + async.apply(!skipJS ? meta.js.minify : meta.js.getFromFile, app.enabled('minification')), + async.apply(!skipLess ? meta.css.minify : meta.css.getFromFile), + async.apply(meta.sounds.init) + ], next); + }, + function(results, next) { + plugins.fireHook('static:app.preload', { + app: app, + middleware: middleware + }, next); + }, + function(next) { + routes(app, middleware); + next(); + } + ], callback); +} + +function cacheStaticFiles(callback) { + if (global.env === 'development') { + return 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(); + }); + } + }); +} + +function listen(callback) { + var port = nconf.get('port'); + + if (Array.isArray(port)) { + if (!port.length) { + winston.error('[startup] empty ports array in config.json'); + process.exit(); + } + + winston.warn('[startup] If you want to start nodebb on multiple ports please use loader.js'); + winston.warn('[startup] Defaulting to first port in array, ' + port[0]); + port = port[0]; + if (!port) { + winston.error('[startup] Invalid port, exiting'); + process.exit(); + } + } + + if (port !== 80 && port !== 443 && nconf.get('use_port') === false) { + winston.info('Enabling \'trust proxy\''); + app.enable('trust proxy'); + } + + if ((port === 80 || port === 443) && process.env.NODE_ENV !== 'development') { + winston.info('Using ports 80 and 443 is not recommend; use a proxy instead. See README.md'); + } + + var isSocket = isNaN(port), + args = isSocket ? [port] : [port, nconf.get('bind_address')], + bind_address = ((nconf.get('bind_address') === "0.0.0.0" || !nconf.get('bind_address')) ? '0.0.0.0' : nconf.get('bind_address')) + ':' + port, + oldUmask; + + args.push(function(err) { + if (err) { + winston.info('[startup] NodeBB was unable to listen on: ' + bind_address); + process.exit(); + } + + winston.info('NodeBB is now listening on: ' + (isSocket ? port : bind_address)); + if (oldUmask) { + process.umask(oldUmask); + } + }); + + // Alter umask if necessary + if (isSocket) { + oldUmask = process.umask('0000'); + module.exports.testSocket(port, function(err) { + if (!err) { + server.listen.apply(server, args); + } else { + winston.error('[startup] NodeBB was unable to secure domain socket access (' + port + ')'); + winston.error('[startup] ' + err.message); + process.exit(); + } + }); + } else { + server.listen.apply(server, args); + } +} + +module.exports.testSocket = function(socketPath, callback) { + if (typeof socketPath !== 'string') { + return callback(new Error('invalid socket path : ' + socketPath)); + } + var net = require('net'); + async.series([ + function(next) { + fs.exists(socketPath, function(exists) { + if (exists) { + next(); + } else { + callback(); + } + }); + }, + function(next) { + var testSocket = new net.Socket(); + testSocket.on('error', function(err) { + next(err.code !== 'ECONNREFUSED' ? err : null); + }); + testSocket.connect({ path: socketPath }, function() { + // Something's listening here, abort + callback(new Error('port-in-use')); + }); + }, + async.apply(fs.unlink, socketPath), // The socket was stale, kick it out of the way + ], callback); +}; + + diff --git a/tests/database/keys.js b/tests/database/keys.js index 43a44e5753..36e5fe5989 100644 --- a/tests/database/keys.js +++ b/tests/database/keys.js @@ -7,6 +7,9 @@ var async = require('async'), describe('Key methods', function() { + beforeEach(function(done) { + db.set('testKey', 'testValue', done); + }); it('should set a key without error', function(done) { db.set('testKey', 'testValue', function(err) { @@ -56,11 +59,15 @@ describe('Key methods', function() { }); }); - it('should return false if key does not exist or was deleted', function(done) { - db.exists('testKey', function(err, exists) { + it('should return false if key was deleted', function(done) { + db.delete('testKey', function(err) { assert.equal(err, null); - assert.strictEqual(exists, false); - done(); + assert.equal(arguments.length, 1); + db.exists('testKey', function(err, exists) { + assert.equal(err, null); + assert.strictEqual(exists, false); + done(); + }); }); }); diff --git a/tests/groups.js b/tests/groups.js index a4d5c9da30..095518ae3b 100644 --- a/tests/groups.js +++ b/tests/groups.js @@ -34,10 +34,10 @@ describe('Groups', function() { describe('.list()', function() { it('should list the groups present', function(done) { - Groups.list({}, function(err, groups) { + Groups.getGroupsFromSet('groups:createtime', 0, 0, -1, function(err, groups) { if (err) return done(err); - assert.equal(groups.length, 1); + assert.equal(groups.length, 3); done(); }); }); @@ -52,21 +52,6 @@ describe('Groups', function() { Groups.get('Test', {}, function(err, groupObj) { if (err) return done(err); - assert.equal(typeof groupObj, 'object'); - assert(Array.isArray(groupObj.members)); - assert.strictEqual(groupObj.name, 'Test'); - assert.strictEqual(groupObj.description, 'Foobar!'); - assert.strictEqual(groupObj.memberCount, 1); - assert.notEqual(typeof groupObj.members[0], 'object'); - - done(); - }); - }); - - it('with the "expand" option, should show both group information and user information', function(done) { - Groups.get('Test', { expand: true }, function(err, groupObj) { - if (err) return done(err); - assert.equal(typeof groupObj, 'object'); assert(Array.isArray(groupObj.members)); assert.strictEqual(groupObj.name, 'Test');