diff --git a/install/package.json b/install/package.json
index 3adb44f064..c6dc672f71 100644
--- a/install/package.json
+++ b/install/package.json
@@ -64,18 +64,18 @@
"mubsub": "^1.4.0",
"nconf": "^0.9.1",
"nodebb-plugin-composer-default": "6.0.17",
- "nodebb-plugin-dbsearch": "2.0.9",
+ "nodebb-plugin-dbsearch": "2.0.10",
"nodebb-plugin-emoji": "^2.1.0",
"nodebb-plugin-emoji-android": "2.0.0",
"nodebb-plugin-markdown": "8.4.2",
- "nodebb-plugin-mentions": "2.2.3",
+ "nodebb-plugin-mentions": "2.2.4",
"nodebb-plugin-soundpack-default": "1.0.0",
"nodebb-plugin-spam-be-gone": "0.5.3",
"nodebb-rewards-essentials": "0.0.11",
"nodebb-theme-lavender": "5.0.4",
- "nodebb-theme-persona": "8.0.3",
+ "nodebb-theme-persona": "8.0.4",
"nodebb-theme-slick": "1.1.5",
- "nodebb-theme-vanilla": "9.0.1",
+ "nodebb-theme-vanilla": "9.0.3",
"nodebb-widget-essentials": "4.0.2",
"nodemailer": "4.4.1",
"passport": "^0.4.0",
@@ -143,4 +143,4 @@
"url": "https://github.com/barisusakli"
}
]
-}
+}
\ No newline at end of file
diff --git a/public/language/ar/admin/settings/post.json b/public/language/ar/admin/settings/post.json
index 6a1ab9a2a5..6594fe384b 100644
--- a/public/language/ar/admin/settings/post.json
+++ b/public/language/ar/admin/settings/post.json
@@ -49,5 +49,6 @@
"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
"composer.custom-help": "Custom Help Text",
"ip-tracking": "IP Tracking",
- "ip-tracking.each-post": "Track IP Address for each post"
+ "ip-tracking.each-post": "Track IP Address for each post",
+ "enable-post-history": "Enable Post History"
}
\ No newline at end of file
diff --git a/public/language/bg/admin/settings/post.json b/public/language/bg/admin/settings/post.json
index 432076b60e..d13225c433 100644
--- a/public/language/bg/admin/settings/post.json
+++ b/public/language/bg/admin/settings/post.json
@@ -49,5 +49,6 @@
"composer.enable-plugin-help": "Позволяване на добавките да добавят съдържание в раздела за помощ",
"composer.custom-help": "Персонализиран текст за помощ",
"ip-tracking": "Записване на IP адреса",
- "ip-tracking.each-post": "Записване на IP адреса за всяка публикация"
+ "ip-tracking.each-post": "Записване на IP адреса за всяка публикация",
+ "enable-post-history": "Включване на историята на публикациите"
}
\ No newline at end of file
diff --git a/public/language/bn/admin/settings/post.json b/public/language/bn/admin/settings/post.json
index 6a1ab9a2a5..6594fe384b 100644
--- a/public/language/bn/admin/settings/post.json
+++ b/public/language/bn/admin/settings/post.json
@@ -49,5 +49,6 @@
"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
"composer.custom-help": "Custom Help Text",
"ip-tracking": "IP Tracking",
- "ip-tracking.each-post": "Track IP Address for each post"
+ "ip-tracking.each-post": "Track IP Address for each post",
+ "enable-post-history": "Enable Post History"
}
\ No newline at end of file
diff --git a/public/language/cs/admin/settings/post.json b/public/language/cs/admin/settings/post.json
index b8c9350e8e..05f82cded2 100644
--- a/public/language/cs/admin/settings/post.json
+++ b/public/language/cs/admin/settings/post.json
@@ -49,5 +49,6 @@
"composer.enable-plugin-help": "Povolit rozšíření přidat obsah do záložky nápovědy",
"composer.custom-help": "Uživatelský text nápovědy",
"ip-tracking": "Sledování IP",
- "ip-tracking.each-post": "Sledovat adresu IP u každého příspěvku"
+ "ip-tracking.each-post": "Sledovat adresu IP u každého příspěvku",
+ "enable-post-history": "Enable Post History"
}
\ No newline at end of file
diff --git a/public/language/da/admin/settings/post.json b/public/language/da/admin/settings/post.json
index 6a1ab9a2a5..6594fe384b 100644
--- a/public/language/da/admin/settings/post.json
+++ b/public/language/da/admin/settings/post.json
@@ -49,5 +49,6 @@
"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
"composer.custom-help": "Custom Help Text",
"ip-tracking": "IP Tracking",
- "ip-tracking.each-post": "Track IP Address for each post"
+ "ip-tracking.each-post": "Track IP Address for each post",
+ "enable-post-history": "Enable Post History"
}
\ No newline at end of file
diff --git a/public/language/de/admin/settings/post.json b/public/language/de/admin/settings/post.json
index d43fe27f77..706cea1b92 100644
--- a/public/language/de/admin/settings/post.json
+++ b/public/language/de/admin/settings/post.json
@@ -49,5 +49,6 @@
"composer.enable-plugin-help": "Plugins erlauben Inhalte dem \"Help\"-Tab hinzuzufügen",
"composer.custom-help": "Benutzerdefinierter Hilfe-Text",
"ip-tracking": "IP-Verfolgung",
- "ip-tracking.each-post": "IP-Adresse für jeden Beitrag speichern"
+ "ip-tracking.each-post": "IP-Adresse für jeden Beitrag speichern",
+ "enable-post-history": "Enable Post History"
}
\ No newline at end of file
diff --git a/public/language/el/admin/settings/post.json b/public/language/el/admin/settings/post.json
index 6a1ab9a2a5..6594fe384b 100644
--- a/public/language/el/admin/settings/post.json
+++ b/public/language/el/admin/settings/post.json
@@ -49,5 +49,6 @@
"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
"composer.custom-help": "Custom Help Text",
"ip-tracking": "IP Tracking",
- "ip-tracking.each-post": "Track IP Address for each post"
+ "ip-tracking.each-post": "Track IP Address for each post",
+ "enable-post-history": "Enable Post History"
}
\ No newline at end of file
diff --git a/public/language/en-GB/admin/admin.json b/public/language/en-GB/admin/admin.json
index 9c01f56006..cca6420575 100644
--- a/public/language/en-GB/admin/admin.json
+++ b/public/language/en-GB/admin/admin.json
@@ -1,5 +1,5 @@
{
- "alert.confirm-reload": "Are you sure you wish to reload NodeBB?",
+ "alert.confirm-reload": "Are you sure you wish to rebuild and restart NodeBB?",
"alert.confirm-restart": "Are you sure you wish to restart NodeBB?",
"acp-title": "%1 | NodeBB Admin Control Panel",
diff --git a/public/language/en-GB/admin/general/dashboard.json b/public/language/en-GB/admin/general/dashboard.json
index 3b4ed54444..4d287c409d 100644
--- a/public/language/en-GB/admin/general/dashboard.json
+++ b/public/language/en-GB/admin/general/dashboard.json
@@ -23,10 +23,11 @@
"running-version": "You are running NodeBB v%1.",
"keep-updated": "Always make sure that your NodeBB is up to date for the latest security patches and bug fixes.",
"up-to-date": "
This is an outdated pre-release version of NodeBB. A new version (v%1) has been released. Consider upgrading your NodeBB.
",
"prerelease-warning": "
This is a pre-release version of NodeBB. Unintended bugs may occur.
",
"running-in-development": "Forum is running in development mode. The forum may be open to potential vulnerabilities; please contact your system administrator.",
+ "latest-lookup-failed": "
Failed to look up latest available version of NodeBB
",
"notices": "Notices",
"restart-not-required": "Restart not required",
diff --git a/public/language/en-GB/admin/menu.json b/public/language/en-GB/admin/menu.json
index 51099e9af4..bebff6aa37 100644
--- a/public/language/en-GB/admin/menu.json
+++ b/public/language/en-GB/admin/menu.json
@@ -63,7 +63,7 @@
"development/logger": "Logger",
"development/info": "Info",
- "reload-forum": "Reload Forum",
+ "reload-forum": "Rebuild & Restart Forum",
"restart-forum": "Restart Forum",
"logout": "Log out",
"view-forum": "View Forum",
@@ -74,5 +74,8 @@
"search.keep-typing": "Type more to see results...",
"search.start-typing": "Start typing to see results...",
- "connection-lost": "Connection to %1 has been lost, attempting to reconnect..."
+ "connection-lost": "Connection to %1 has been lost, attempting to reconnect...",
+
+ "alerts.version": "Running NodeBB v%1",
+ "alerts.upgrade": "Upgrade to v%1"
}
\ No newline at end of file
diff --git a/public/language/en-GB/admin/settings/advanced.json b/public/language/en-GB/admin/settings/advanced.json
index 05a1929cf0..8da7b1c46a 100644
--- a/public/language/en-GB/admin/settings/advanced.json
+++ b/public/language/en-GB/admin/settings/advanced.json
@@ -7,6 +7,7 @@
"headers.powered-by": "Customise the \"Powered By\" header sent by NodeBB",
"headers.acao": "Access-Control-Allow-Origin",
"headers.acao-help": "To deny access to all sites, leave empty",
+ "headers.acac": "Access-Control-Allow-Credentials",
"headers.acam": "Access-Control-Allow-Methods",
"headers.acah": "Access-Control-Allow-Headers",
"traffic-management": "Traffic Management",
diff --git a/public/language/en-GB/admin/settings/uploads.json b/public/language/en-GB/admin/settings/uploads.json
index a458870354..f08b6efedf 100644
--- a/public/language/en-GB/admin/settings/uploads.json
+++ b/public/language/en-GB/admin/settings/uploads.json
@@ -4,6 +4,8 @@
"private": "Make uploaded files private",
"max-image-width": "Resize images down to specified width (in pixels)",
"max-image-width-help": "(in pixels, default: 760 pixels, set to 0 to disable)",
+ "resize-image-quality": "Quality to use when resizing images",
+ "resize-image-quality-help": "Use a lower quality setting to reduce the file size of resized images.",
"max-file-size": "Maximum File Size (in KiB)",
"max-file-size-help": "(in kibibytes, default: 2048 KiB)",
"allow-topic-thumbnails": "Allow users to upload topic thumbnails",
diff --git a/public/language/en-US/admin/settings/post.json b/public/language/en-US/admin/settings/post.json
index 71587955de..94fb374293 100644
--- a/public/language/en-US/admin/settings/post.json
+++ b/public/language/en-US/admin/settings/post.json
@@ -49,5 +49,6 @@
"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
"composer.custom-help": "Custom Help Text",
"ip-tracking": "IP Tracking",
- "ip-tracking.each-post": "Track IP Address for each post"
+ "ip-tracking.each-post": "Track IP Address for each post",
+ "enable-post-history": "Enable Post History"
}
\ No newline at end of file
diff --git a/public/language/en-x-pirate/admin/settings/post.json b/public/language/en-x-pirate/admin/settings/post.json
index 6a1ab9a2a5..6594fe384b 100644
--- a/public/language/en-x-pirate/admin/settings/post.json
+++ b/public/language/en-x-pirate/admin/settings/post.json
@@ -49,5 +49,6 @@
"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
"composer.custom-help": "Custom Help Text",
"ip-tracking": "IP Tracking",
- "ip-tracking.each-post": "Track IP Address for each post"
+ "ip-tracking.each-post": "Track IP Address for each post",
+ "enable-post-history": "Enable Post History"
}
\ No newline at end of file
diff --git a/public/language/es/admin/settings/post.json b/public/language/es/admin/settings/post.json
index 6a1ab9a2a5..6594fe384b 100644
--- a/public/language/es/admin/settings/post.json
+++ b/public/language/es/admin/settings/post.json
@@ -49,5 +49,6 @@
"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
"composer.custom-help": "Custom Help Text",
"ip-tracking": "IP Tracking",
- "ip-tracking.each-post": "Track IP Address for each post"
+ "ip-tracking.each-post": "Track IP Address for each post",
+ "enable-post-history": "Enable Post History"
}
\ No newline at end of file
diff --git a/public/language/es/topic.json b/public/language/es/topic.json
index e7b783b689..203226c9cf 100644
--- a/public/language/es/topic.json
+++ b/public/language/es/topic.json
@@ -30,12 +30,12 @@
"locked": "Cerrado",
"pinned": "Fijo",
"moved": "Movido",
- "copy-ip": "Copy IP",
- "ban-ip": "Ban IP",
- "view-history": "Edit History",
+ "copy-ip": "Copiar IP",
+ "ban-ip": "Banear IP",
+ "view-history": "Editar Historial",
"bookmark_instructions": "Haz click aquí para volver a tu último mensaje leído en este tema",
"flag_title": "Reportar este mensaje",
- "merged_message": "This topic has been merged into %2",
+ "merged_message": "Este tema ha sido fusionado en %2",
"deleted_message": "Este tema ha sido borrado. Solo los usuarios que tengan privilegios de administración de temas pueden verlo.",
"following_topic.message": "Ahora recibiras notificaciones cuando alguien publique en este tema.",
"not_following_topic.message": "Podras ver este tema en la lista de no leidos, pero no recibirás notificaciones cuando alguien escriba en él.",
@@ -56,7 +56,7 @@
"not-watching.description": "No notificarme de nuevas respuestas. Mostrar tema en no leídos si sigo esa categoría. ",
"ignoring.description": "No notificarme de nuevas respuestas. No mostrar tema en no leídos. ",
"thread_tools.title": "Herramientas",
- "thread_tools.markAsUnreadForAll": "Mark Unread For All",
+ "thread_tools.markAsUnreadForAll": "Marcar todo como no leído",
"thread_tools.pin": "Adherir tema",
"thread_tools.unpin": "Despegar tema",
"thread_tools.lock": "Cerrar tema",
@@ -72,8 +72,8 @@
"thread_tools.restore_confirm": "¿Estás seguro que deseas restaurar este tema?",
"thread_tools.purge": "Purgar tema",
"thread_tools.purge_confirm": "¿Está seguro que desea eliminar definitivamente (purgar) este tema?",
- "thread_tools.merge_topics": "Merge Topics",
- "thread_tools.merge": "Merge",
+ "thread_tools.merge_topics": "Fusionar temas",
+ "thread_tools.merge": "Fusionar",
"topic_move_success": "El tema ha sido movido correctamente a %1",
"post_delete_confirm": "¿Estás seguro de que quieres eliminar esta respuesta?",
"post_restore_confirm": "¿Estás seguro de que quieres restaurar esta respuesta?",
@@ -95,7 +95,7 @@
"fork_pid_count": "%1 mensaje(s) seleccionados",
"fork_success": "¡Se ha creado un nuevo tema a partir del original! Haz click aquí para ir al nuevo tema.",
"delete_posts_instruction": "Haz click en los mensajes que quieres eliminar/limpiar",
- "merge_topics_instruction": "Click the topics you want to merge",
+ "merge_topics_instruction": "Selecciona los temas que quieres fusionar",
"composer.title_placeholder": "Ingresa el título de tu tema...",
"composer.handle_placeholder": "Nombre",
"composer.discard": "Descartar",
diff --git a/public/language/es/user.json b/public/language/es/user.json
index b5e18d08d7..124a63e9e3 100644
--- a/public/language/es/user.json
+++ b/public/language/es/user.json
@@ -1,7 +1,7 @@
{
"banned": "Baneado",
"offline": "Desconectado",
- "deleted": "Deleted",
+ "deleted": "Borrado",
"username": "Nombre de usuario",
"joindate": "Fecha de registro",
"postcount": "Número De Publicaciones",
@@ -26,7 +26,7 @@
"reputation": "Reputación",
"bookmarks": "Marcadores",
"watched": "Suscritos",
- "ignored": "Ignored",
+ "ignored": "Ignorado",
"followers": "Seguidores",
"following": "Siguiendo",
"aboutme": "Sobre mí",
@@ -86,7 +86,7 @@
"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.",
- "has_no_ignored_topics": "This user hasn't ignored any topics yet.",
+ "has_no_ignored_topics": "Este usuario no ha ignorado ningún tema aun.",
"has_no_upvoted_posts": "Este usuario todavía no ha votado ninguna publicación positivamente.",
"has_no_downvoted_posts": "Este usuario todavía no ha votado ninguna publicación negativamente.",
"has_no_voted_posts": "Este usuario no ha votado ninguna publicación",
diff --git a/public/language/et/admin/settings/post.json b/public/language/et/admin/settings/post.json
index 6a1ab9a2a5..6594fe384b 100644
--- a/public/language/et/admin/settings/post.json
+++ b/public/language/et/admin/settings/post.json
@@ -49,5 +49,6 @@
"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
"composer.custom-help": "Custom Help Text",
"ip-tracking": "IP Tracking",
- "ip-tracking.each-post": "Track IP Address for each post"
+ "ip-tracking.each-post": "Track IP Address for each post",
+ "enable-post-history": "Enable Post History"
}
\ No newline at end of file
diff --git a/public/language/fa-IR/admin/settings/post.json b/public/language/fa-IR/admin/settings/post.json
index 6a1ab9a2a5..6594fe384b 100644
--- a/public/language/fa-IR/admin/settings/post.json
+++ b/public/language/fa-IR/admin/settings/post.json
@@ -49,5 +49,6 @@
"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
"composer.custom-help": "Custom Help Text",
"ip-tracking": "IP Tracking",
- "ip-tracking.each-post": "Track IP Address for each post"
+ "ip-tracking.each-post": "Track IP Address for each post",
+ "enable-post-history": "Enable Post History"
}
\ No newline at end of file
diff --git a/public/language/fi/admin/settings/post.json b/public/language/fi/admin/settings/post.json
index 6a1ab9a2a5..6594fe384b 100644
--- a/public/language/fi/admin/settings/post.json
+++ b/public/language/fi/admin/settings/post.json
@@ -49,5 +49,6 @@
"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
"composer.custom-help": "Custom Help Text",
"ip-tracking": "IP Tracking",
- "ip-tracking.each-post": "Track IP Address for each post"
+ "ip-tracking.each-post": "Track IP Address for each post",
+ "enable-post-history": "Enable Post History"
}
\ No newline at end of file
diff --git a/public/language/fr/admin/extend/widgets.json b/public/language/fr/admin/extend/widgets.json
index 287e44f1d7..20729c29d7 100644
--- a/public/language/fr/admin/extend/widgets.json
+++ b/public/language/fr/admin/extend/widgets.json
@@ -2,7 +2,7 @@
"available": "Widgets disponibles",
"explanation": "Sélectionnez un widget depuis le menu puis glissez-déposez le dans une zone template du widget à gauche.",
"none-installed": "Aucun widget trouvé ! Activez le plugin widgets essentiels dans le panneau de contrôle plugins.",
- "clone-from": "Clone widgets from",
+ "clone-from": "Cloner le widget",
"containers.available": "Conteneurs disponibles",
"containers.explanation": "Glissez-déposez sur n'importe quel widget actif",
"containers.none": "Aucun",
@@ -16,7 +16,7 @@
"alert.confirm-delete": "Êtes-vous sûr de vouloir supprimer ce widget ?",
"alert.updated": "Widgets mis à jour",
"alert.update-success": "Widgets mis à jour avec succès",
- "alert.clone-success": "Successfully cloned widgets",
+ "alert.clone-success": "Widget cloné avec succès",
- "error.select-clone": "Please select a page to clone from"
+ "error.select-clone": "Veuillez sélectionner une page à cloner"
}
\ No newline at end of file
diff --git a/public/language/fr/admin/general/dashboard.json b/public/language/fr/admin/general/dashboard.json
index ec011f5d59..709868bf51 100644
--- a/public/language/fr/admin/general/dashboard.json
+++ b/public/language/fr/admin/general/dashboard.json
@@ -36,10 +36,10 @@
"search-plugin-tooltip": "Installer un plugin de recherche depuis la page des plugins pour activer la fonctionnalité de recherche",
"control-panel": "Contrôle du système",
- "reload": "Restart",
- "restart": "Rebuild & Restart",
- "restart-warning": "Rebuilding or Restarting your NodeBB will drop all existing connections for a few seconds.",
- "restart-disabled": "Rebuilding and Restarting your NodeBB has been disabled as you do not seem to be running it via the appropriate daemon.",
+ "reload": "Recharger",
+ "restart": "Redémarrer",
+ "restart-warning": "Recharger ou redémarrer NodeBB coupera toutes les connexions existantes pendant quelques secondes. ",
+ "restart-disabled": "La recharge et le redémarrage de votre forum ont été désactivés car vous ne semblez pas les exécuter à l'aide du serveur approprié.",
"maintenance-mode": "Mode maintenance",
"maintenance-mode-title": "Cliquez ici pour passer NodeBB en mode maintenance",
"realtime-chart-updates": "Mises à jour des graphiques en temps réel",
diff --git a/public/language/fr/admin/manage/categories.json b/public/language/fr/admin/manage/categories.json
index fbf7585b60..6fcb864f86 100644
--- a/public/language/fr/admin/manage/categories.json
+++ b/public/language/fr/admin/manage/categories.json
@@ -66,6 +66,6 @@
"alert.user-search": "Chercher un utilisateur ici...",
"alert.find-group": "Trouver un groupe",
"alert.group-search": "Chercher un groupe ici...",
- "collapse-all": "Collapse All",
- "expand-all": "Expand All"
+ "collapse-all": "Tout réduire",
+ "expand-all": "Tout développer"
}
\ No newline at end of file
diff --git a/public/language/fr/admin/manage/ip-blacklist.json b/public/language/fr/admin/manage/ip-blacklist.json
index 1c057100ec..36c55cfefd 100644
--- a/public/language/fr/admin/manage/ip-blacklist.json
+++ b/public/language/fr/admin/manage/ip-blacklist.json
@@ -15,5 +15,5 @@
"analytics.blacklist-hourly": "Figure 1 – Nombre de visites de la liste noire par heure",
"analytics.blacklist-daily": "Figure 2 – Nombre de visites de la liste noire par jour",
- "ip-banned": "IP banned"
+ "ip-banned": "IP bannies"
}
\ No newline at end of file
diff --git a/public/language/fr/admin/menu.json b/public/language/fr/admin/menu.json
index 641a7f9f20..1fc36102ba 100644
--- a/public/language/fr/admin/menu.json
+++ b/public/language/fr/admin/menu.json
@@ -17,7 +17,7 @@
"manage/post-queue": "File d’attente des messages",
"manage/groups": "Groupes",
"manage/ip-blacklist": "Liste noire d'IPs",
- "manage/uploads": "Uploads",
+ "manage/uploads": "Téléversements",
"section-settings": "Réglages",
"settings/general": "Général",
diff --git a/public/language/fr/admin/settings/post.json b/public/language/fr/admin/settings/post.json
index 33bf906260..e5ad519e29 100644
--- a/public/language/fr/admin/settings/post.json
+++ b/public/language/fr/admin/settings/post.json
@@ -49,5 +49,6 @@
"composer.enable-plugin-help": "Autoriser les plugins à modifier l'onglet d'aide",
"composer.custom-help": "Message d'aide personnalisé",
"ip-tracking": "Suivi d'IP",
- "ip-tracking.each-post": "Suivre l'adresse IP pour chaque message"
+ "ip-tracking.each-post": "Suivre l'adresse IP pour chaque message",
+ "enable-post-history": "Activer l'historique des publications"
}
\ No newline at end of file
diff --git a/public/language/fr/admin/settings/user.json b/public/language/fr/admin/settings/user.json
index 04cd6e7296..c4d2ccb6ce 100644
--- a/public/language/fr/admin/settings/user.json
+++ b/public/language/fr/admin/settings/user.json
@@ -63,5 +63,5 @@
"email-post-notif": "Envoyer un email lors de réponses envoyées aux sujets auxquels je suis abonné",
"follow-created-topics": "S'abonner aux sujets que vous créez",
"follow-replied-topics": "S'abonner aux sujets auxquels vous répondez",
- "default-notification-settings": "Default notification settings"
+ "default-notification-settings": "Paramètres des notifications par défaut"
}
\ No newline at end of file
diff --git a/public/language/fr/error.json b/public/language/fr/error.json
index 0aae11d65c..639a0248da 100644
--- a/public/language/fr/error.json
+++ b/public/language/fr/error.json
@@ -116,8 +116,8 @@
"cant-delete-chat-message": "Vous n'avez pas l'autorisation de supprimer ce message",
"chat-edit-duration-expired": "Vous n'êtes autorisé à modifier des messages que pendant %1 seconde(s) après les avoir postés",
"chat-delete-duration-expired": "Vous n'êtes autorisé à supprimer des messages que pendant %1 seconde(s) après les avoir postés",
- "chat-deleted-already": "This chat message has already been deleted.",
- "chat-restored'already": "This chat message has already been restored.",
+ "chat-deleted-already": "Ce message a déjà été supprimé.",
+ "chat-restored'already": "Ce message a déjà été restauré.",
"already-voting-for-this-post": "Vous avez déjà voté pour ce message.",
"reputation-system-disabled": "Le système de réputation est désactivé",
"downvoting-disabled": "Les votes négatifs ne sont pas autorisés",
diff --git a/public/language/fr/global.json b/public/language/fr/global.json
index ded409848e..a10dac68b0 100644
--- a/public/language/fr/global.json
+++ b/public/language/fr/global.json
@@ -54,8 +54,8 @@
"posts": "Messages",
"best": "Meilleurs",
"votes": "Votes",
- "upvoters": "Votes pour",
- "upvoted": "Votes pour",
+ "upvoters": "Votes positifs",
+ "upvoted": "Votes positifs",
"downvoters": "Votes contre",
"downvoted": "Vote négatif",
"views": "Vues",
@@ -107,5 +107,5 @@
"edited": "Modifié",
"disabled": "Désactivé",
"select": "Sélectionner",
- "user-search-prompt": "Type something here to find users..."
+ "user-search-prompt": "Écrivez ici pour rechercher des utilisateurs ..."
}
\ No newline at end of file
diff --git a/public/language/fr/modules.json b/public/language/fr/modules.json
index 60f8759c99..cde5f5bc82 100644
--- a/public/language/fr/modules.json
+++ b/public/language/fr/modules.json
@@ -1,5 +1,5 @@
{
- "chat.chatting_with": "Chat with",
+ "chat.chatting_with": "Discuter avec",
"chat.placeholder": "Tapez votre message ici, appuyez sur Entrée pour envoyer",
"chat.send": "Envoyer",
"chat.no_active": "Vous n'avez aucune discussion en cours.",
@@ -12,7 +12,7 @@
"chat.recent-chats": "Discussions récentes",
"chat.contacts": "Contacts",
"chat.message-history": "Historique des messages",
- "chat.options": "Chat options",
+ "chat.options": "Options",
"chat.pop-out": "Afficher la discussion",
"chat.minimize": "Réduire",
"chat.maximize": "Agrandir",
@@ -21,17 +21,17 @@
"chat.three_months": "3 Mois",
"chat.delete_message_confirm": "Êtes-vous sûr de vouloir supprimer ce message ?",
"chat.add-users-to-room": "Ajouter des participants",
- "chat.retrieving-users": "Retrieving users...",
- "chat.manage-room": "Manage Chat Room",
- "chat.add-user-help": "Search for users here. When selected, the user will be added to the chat. The new user will not be able to see chat messages written before they were added to the conversation.",
+ "chat.retrieving-users": "Ajouter des utilisateurs ...",
+ "chat.manage-room": "Gérer l'espace de discussion",
+ "chat.add-user-help": "Rechercher des utilisateurs ici. Lorsque cette option est sélectionnée, l'utilisateur sera ajouté au chat. Le nouvel utilisateur ne pourra pas voir les messages de écrits avant d'avoir été ajouté à la dicussion.",
"chat.confirm-chat-with-dnd-user": "Cet utilisateur a son statut en mode \"Ne pas déranger\". Voulez-vous quand même discuter avec lui ?",
- "chat.rename-room": "Rename room",
- "chat.rename-placeholder": "Enter your room name here",
- "chat.rename-help": "The room name set here will be viewable by all participants in the room.",
- "chat.leave": "Leave Chat",
- "chat.leave-prompt": "Are you sure you wish to leave this chat?",
- "chat.leave-help": "Leaving this chat will remove you from future correspondence in this chat. If you are re-added in the future, you will not see any chat history from prior to your re-joining.",
- "chat.in-room": "In this room",
+ "chat.rename-room": "Renommer l'espace de discussion",
+ "chat.rename-placeholder": "Entrer le nom ici ",
+ "chat.rename-help": "Le nom de l'espace de discussion défini ici sera visible par tous les participants à la discussion.",
+ "chat.leave": "Quitter la discussion",
+ "chat.leave-prompt": "Êtes-vous sûr de vouloir quitter la discussion ?",
+ "chat.leave-help": "Si vous quittez vous ne pourrez plus suivre la discussion. Si vous êtes de nouveau ajouté, vous ne verrez aucun historique de la discussion avant votre réintégration.",
+ "chat.in-room": "Dans cet espace de discussion",
"composer.compose": "Écrire",
"composer.show_preview": "Afficher l'aperçu",
"composer.hide_preview": "Masquer l'aperçu",
diff --git a/public/language/fr/notifications.json b/public/language/fr/notifications.json
index 6b61063749..ba7d321da6 100644
--- a/public/language/fr/notifications.json
+++ b/public/language/fr/notifications.json
@@ -16,7 +16,7 @@
"replies": "Réponses",
"chat": "Discussions",
"follows": "Suivis",
- "upvote": "Votes pour",
+ "upvote": "Vote positif",
"new-flags": "Nouveaux drapeaux",
"my-flags": "Drapeaux assignés à moi",
"bans": "Bannissements",
diff --git a/public/language/fr/topic.json b/public/language/fr/topic.json
index 23db1d8ed7..45bc76c53c 100644
--- a/public/language/fr/topic.json
+++ b/public/language/fr/topic.json
@@ -30,12 +30,12 @@
"locked": "Verrouillé",
"pinned": "Épinglé",
"moved": "Déplacé",
- "copy-ip": "Copy IP",
- "ban-ip": "Ban IP",
- "view-history": "Edit History",
+ "copy-ip": "Copier l'IP",
+ "ban-ip": "Bannir l'IP",
+ "view-history": "Éditer l'historique",
"bookmark_instructions": "Cliquez ici pour retourner au dernier message lu de ce fil.",
"flag_title": "Signaler ce message à la modération",
- "merged_message": "This topic has been merged into %2",
+ "merged_message": "Ce sujet a été fusionné dans %2",
"deleted_message": "Ce sujet a été supprimé. Seuls les utilisateurs avec les droits d'administration peuvent le voir.",
"following_topic.message": "Vous recevrez désormais des notifications lorsque quelqu'un postera dans ce sujet.",
"not_following_topic.message": "Vous verrez ce sujet dans la liste des sujets non-lus, mais vous ne recevrez pas de notification lorsque quelqu'un postera dans ce sujet.",
@@ -123,7 +123,7 @@
"stale.create": "Créer un nouveau sujet",
"stale.reply_anyway": "Répondre à ce sujet quand même",
"link_back": "Re : [%1](%2)",
- "diffs.title": "Post Edit History",
- "diffs.description": "This post has %1 revisions. Click one of the revisions below to see the post content at that point in time.",
- "diffs.no-revisions-description": "This post has %1 revisions."
+ "diffs.title": "Historique",
+ "diffs.description": "Cet article a % 1 révisions. Cliquez sur l'une des révisions ci-dessous pour voir le contenu du message.",
+ "diffs.no-revisions-description": "Cet article a % 1 révisions."
}
\ No newline at end of file
diff --git a/public/language/fr/user.json b/public/language/fr/user.json
index ab9e50f1f9..8277ab98d6 100644
--- a/public/language/fr/user.json
+++ b/public/language/fr/user.json
@@ -1,7 +1,7 @@
{
"banned": "Banni",
"offline": "Hors-ligne",
- "deleted": "Deleted",
+ "deleted": "Effacé",
"username": "Nom d'utilisateur",
"joindate": "Date d'inscription",
"postcount": "Nombre de messages",
@@ -96,7 +96,7 @@
"topics_per_page": "Sujets par page",
"posts_per_page": "Messages par page",
"max_items_per_page": "Maximum %1",
- "acp_language": "Admin Page Language",
+ "acp_language": "Page de gestion des langues",
"notification_sounds": "Émettre un son lors de la réception de notifications",
"notifications_and_sounds": "Notifications & Sons",
"incoming-message-sound": "Son pour les messages entrants",
diff --git a/public/language/gl/admin/settings/post.json b/public/language/gl/admin/settings/post.json
index 6a1ab9a2a5..6594fe384b 100644
--- a/public/language/gl/admin/settings/post.json
+++ b/public/language/gl/admin/settings/post.json
@@ -49,5 +49,6 @@
"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
"composer.custom-help": "Custom Help Text",
"ip-tracking": "IP Tracking",
- "ip-tracking.each-post": "Track IP Address for each post"
+ "ip-tracking.each-post": "Track IP Address for each post",
+ "enable-post-history": "Enable Post History"
}
\ No newline at end of file
diff --git a/public/language/he/admin/settings/post.json b/public/language/he/admin/settings/post.json
index 6a1ab9a2a5..6594fe384b 100644
--- a/public/language/he/admin/settings/post.json
+++ b/public/language/he/admin/settings/post.json
@@ -49,5 +49,6 @@
"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
"composer.custom-help": "Custom Help Text",
"ip-tracking": "IP Tracking",
- "ip-tracking.each-post": "Track IP Address for each post"
+ "ip-tracking.each-post": "Track IP Address for each post",
+ "enable-post-history": "Enable Post History"
}
\ No newline at end of file
diff --git a/public/language/hr/admin/settings/post.json b/public/language/hr/admin/settings/post.json
index 0fdff61367..6962bccf7a 100644
--- a/public/language/hr/admin/settings/post.json
+++ b/public/language/hr/admin/settings/post.json
@@ -49,5 +49,6 @@
"composer.enable-plugin-help": "Dozvoli dodatcima da dodaju sadržaj u \"Pomoć\"",
"composer.custom-help": "Tekst \"Pomoć\"",
"ip-tracking": "IP praćenje",
- "ip-tracking.each-post": "Prati IP adresu za svaku objavu"
+ "ip-tracking.each-post": "Prati IP adresu za svaku objavu",
+ "enable-post-history": "Enable Post History"
}
\ No newline at end of file
diff --git a/public/language/hu/admin/settings/post.json b/public/language/hu/admin/settings/post.json
index 6a1ab9a2a5..6594fe384b 100644
--- a/public/language/hu/admin/settings/post.json
+++ b/public/language/hu/admin/settings/post.json
@@ -49,5 +49,6 @@
"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
"composer.custom-help": "Custom Help Text",
"ip-tracking": "IP Tracking",
- "ip-tracking.each-post": "Track IP Address for each post"
+ "ip-tracking.each-post": "Track IP Address for each post",
+ "enable-post-history": "Enable Post History"
}
\ No newline at end of file
diff --git a/public/language/id/admin/settings/post.json b/public/language/id/admin/settings/post.json
index 6a1ab9a2a5..6594fe384b 100644
--- a/public/language/id/admin/settings/post.json
+++ b/public/language/id/admin/settings/post.json
@@ -49,5 +49,6 @@
"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
"composer.custom-help": "Custom Help Text",
"ip-tracking": "IP Tracking",
- "ip-tracking.each-post": "Track IP Address for each post"
+ "ip-tracking.each-post": "Track IP Address for each post",
+ "enable-post-history": "Enable Post History"
}
\ No newline at end of file
diff --git a/public/language/it/admin/settings/post.json b/public/language/it/admin/settings/post.json
index 6a1ab9a2a5..6594fe384b 100644
--- a/public/language/it/admin/settings/post.json
+++ b/public/language/it/admin/settings/post.json
@@ -49,5 +49,6 @@
"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
"composer.custom-help": "Custom Help Text",
"ip-tracking": "IP Tracking",
- "ip-tracking.each-post": "Track IP Address for each post"
+ "ip-tracking.each-post": "Track IP Address for each post",
+ "enable-post-history": "Enable Post History"
}
\ No newline at end of file
diff --git a/public/language/ja/admin/settings/post.json b/public/language/ja/admin/settings/post.json
index 82025acaa8..21d504c242 100644
--- a/public/language/ja/admin/settings/post.json
+++ b/public/language/ja/admin/settings/post.json
@@ -49,5 +49,6 @@
"composer.enable-plugin-help": "プラグインがヘルプタブにコンテンツを追加できるようにする",
"composer.custom-help": "カスタムヘルプテキスト",
"ip-tracking": "IPトラッキング",
- "ip-tracking.each-post": "各投稿のトラックIPアドレス"
+ "ip-tracking.each-post": "各投稿のトラックIPアドレス",
+ "enable-post-history": "Enable Post History"
}
\ No newline at end of file
diff --git a/public/language/ko/admin/appearance/customise.json b/public/language/ko/admin/appearance/customise.json
index 7fc83ddd50..72daf9fc13 100644
--- a/public/language/ko/admin/appearance/customise.json
+++ b/public/language/ko/admin/appearance/customise.json
@@ -1,9 +1,9 @@
{
- "custom-css": "Custom CSS/LESS",
- "custom-css.description": "Enter your own CSS/LESS declarations here, which will be applied after all other styles.",
- "custom-css.enable": "Enable Custom CSS/LESS",
+ "custom-css": "사용자 정의 CSS/LESS ",
+ "custom-css.description": "사용자 정의 CSS/LESS 코드를 입력하세요. 모든 스타일 다음에 적용됩니다.",
+ "custom-css.enable": "사용자 정의 CSS/LESS 활성화",
- "custom-js": "Custom Javascript",
+ "custom-js": "사용자 정의 javascript",
"custom-js.description": "Enter your own javascript here. It will be executed after the page is loaded completely.",
"custom-js.enable": "Enable Custom Javascript",
diff --git a/public/language/ko/admin/manage/users.json b/public/language/ko/admin/manage/users.json
index bac6329a8a..f958abc2a6 100644
--- a/public/language/ko/admin/manage/users.json
+++ b/public/language/ko/admin/manage/users.json
@@ -1,5 +1,5 @@
{
- "users": "유저",
+ "users": "사용자",
"edit": "편집",
"make-admin": "관리자로 만들기",
"remove-admin": "관리자 제거",
diff --git a/public/language/ko/admin/menu.json b/public/language/ko/admin/menu.json
index b3a30e673b..8da9883159 100644
--- a/public/language/ko/admin/menu.json
+++ b/public/language/ko/admin/menu.json
@@ -11,7 +11,7 @@
"manage/categories": "게시판",
"manage/privileges": "Privileges",
"manage/tags": "태그",
- "manage/users": "용자",
+ "manage/users": "사용자",
"manage/admins-mods": "Admins & Mods",
"manage/registration": "회원 가입 승인 대기자",
"manage/post-queue": "게시 대기열",
diff --git a/public/language/ko/admin/settings/guest.json b/public/language/ko/admin/settings/guest.json
index bc0d920595..48bae6788e 100644
--- a/public/language/ko/admin/settings/guest.json
+++ b/public/language/ko/admin/settings/guest.json
@@ -4,5 +4,5 @@
"handles.enabled-help": "이 옵션은 게스트들이 포스트를 작성할 때 이름을 적는 공간을 제공합니다. 이 옵션이 비활성화 돼있다면 \"Guest\" 라고 표시될 것입니다.",
"privileges": "게스트 권한",
"privileges.can-search": "로그인 없이 검색 허용",
- "privileges.can-search-users": "로그인 없이 유저 검색 허용"
+ "privileges.can-search-users": "로그인 없이 사용자 검색 허용"
}
\ No newline at end of file
diff --git a/public/language/ko/admin/settings/post.json b/public/language/ko/admin/settings/post.json
index e2552f4ec7..7d3cea3e49 100644
--- a/public/language/ko/admin/settings/post.json
+++ b/public/language/ko/admin/settings/post.json
@@ -49,5 +49,6 @@
"composer.enable-plugin-help": "플러그인이 도움말 탭에 내용 추가하는 것을 허용",
"composer.custom-help": "사용자 설정 \"도움말\" 내용",
"ip-tracking": "IP 추적",
- "ip-tracking.each-post": "모든 글의 IP 주소 추적"
+ "ip-tracking.each-post": "모든 글의 IP 주소 추적",
+ "enable-post-history": "Enable Post History"
}
\ No newline at end of file
diff --git a/public/language/ko/admin/settings/uploads.json b/public/language/ko/admin/settings/uploads.json
index 047eae9345..02080d62b4 100644
--- a/public/language/ko/admin/settings/uploads.json
+++ b/public/language/ko/admin/settings/uploads.json
@@ -1,6 +1,6 @@
{
"posts": "게시물",
- "allow-files": "유저들이 보통 파일들을 업로드하는것을 허용",
+ "allow-files": "사용자가 (이미지가 아닌) 일반 파일을 업로드하는것을 허용",
"private": "업로드된 파일들을 개인만 볼 수 있게 바꿉니다.",
"max-image-width": "이미지를 특정 가로 길이까지 축소 (단위: 픽셀)",
"max-image-width-help": "(단위: 픽셀, 기본값: 760 픽셀, 비활성화를 원하시면 0으로 지정하세요)",
diff --git a/public/language/ko/admin/settings/user.json b/public/language/ko/admin/settings/user.json
index de9ca6f34f..b219d71a22 100644
--- a/public/language/ko/admin/settings/user.json
+++ b/public/language/ko/admin/settings/user.json
@@ -2,7 +2,7 @@
"authentication": "로그인",
"allow-local-login": "로컬 로그인 허용",
"require-email-confirmation": "이메일 인증 필수화",
- "email-confirm-interval": "유저는 인증 이메일을 전송할 수 없습니다:",
+ "email-confirm-interval": "다음의 경우 사용자는 인증 이메일을 재전송할 수 없습니다:",
"email-confirm-email2": "분 후",
"allow-login-with": "로그인 허용 수단",
"allow-login-with.username-email": "사용자명 또는 이메일",
diff --git a/public/language/ko/user.json b/public/language/ko/user.json
index 5656a23506..20d6fd9768 100644
--- a/public/language/ko/user.json
+++ b/public/language/ko/user.json
@@ -1,7 +1,7 @@
{
"banned": "차단된 계정",
"offline": "오프라인",
- "deleted": "Deleted",
+ "deleted": "삭제된 계정",
"username": "사용자 이름",
"joindate": "가입일",
"postcount": "포스트 수",
@@ -26,7 +26,7 @@
"reputation": "등급",
"bookmarks": "즐겨찾기",
"watched": "관심있는 게시물",
- "ignored": "Ignored",
+ "ignored": "무시됨",
"followers": "팔로워",
"following": "팔로잉",
"aboutme": "자기소개",
@@ -86,7 +86,7 @@
"has_no_posts": "이 사용자가 작성한 포스트가 없습니다.",
"has_no_topics": "이 사용자가 작성한 게시물이 없습니다.",
"has_no_watched_topics": "이 사용자가 관심 목록에 추가한 게시물이 없습니다.",
- "has_no_ignored_topics": "This user hasn't ignored any topics yet.",
+ "has_no_ignored_topics": "이 사용자는 아직 무시 중인 게시물이 없습니다.",
"has_no_upvoted_posts": "이 사용자가 추천한 포스트가 없습니다.",
"has_no_downvoted_posts": "이 사용자가 비추천한 포스트가 없습니다.",
"has_no_voted_posts": "평가받은 게시물이 없습니다.",
@@ -96,18 +96,18 @@
"topics_per_page": "페이지 당 게시물 수",
"posts_per_page": "페이지 당 포스트 수",
"max_items_per_page": "최대 %1 ",
- "acp_language": "Admin Page Language",
+ "acp_language": "관리 페이지 언어",
"notification_sounds": "알림 수신시 소리로 알려주기",
"notifications_and_sounds": "알림 / 알림음 설정",
"incoming-message-sound": "수신 메시지 알림음",
"outgoing-message-sound": "발신 메시지 알림음",
"notification-sound": "알림음",
"no-sound": "음소거",
- "upvote-notif-freq": "Upvote Notification Frequency",
- "upvote-notif-freq.all": "All Upvotes",
- "upvote-notif-freq.everyTen": "Every Ten Upvotes",
- "upvote-notif-freq.logarithmic": "On 10, 100, 1000...",
- "upvote-notif-freq.disabled": "Disabled",
+ "upvote-notif-freq": "추천 알림 빈도",
+ "upvote-notif-freq.all": "모든 추천에 알림 사용",
+ "upvote-notif-freq.everyTen": "열 개의 추천마다 알림",
+ "upvote-notif-freq.logarithmic": "10, 100, 1000개 마다...",
+ "upvote-notif-freq.disabled": "비활성화",
"browsing": "브라우징 설정",
"open_links_in_new_tab": "외부 링크를 새로운 탭에서 열람",
"enable_topic_searching": "게시물 내 검색 허용",
@@ -128,9 +128,9 @@
"sso.title": "통합 인증 서비스",
"sso.associated": "와/과 연동된",
"sso.not-associated": "이 곳을 클릭하여 연동시키세요.",
- "sso.dissociate": "Dissociate",
- "sso.dissociate-confirm-title": "Confirm Dissociation",
- "sso.dissociate-confirm": "Are you sure you wish to dissociate your account from %1?",
+ "sso.dissociate": "계정 분리",
+ "sso.dissociate-confirm-title": "계정 분리를 확정하기",
+ "sso.dissociate-confirm": "%1 로부터 계정을 분리하시겠습니까?",
"info.latest-flags": "최근에 들어온 신고",
"info.no-flags": "신고된 포스트가 없습니다.",
"info.ban-history": "최근 차단 히스토리",
diff --git a/public/language/lt/admin/settings/post.json b/public/language/lt/admin/settings/post.json
index 6a1ab9a2a5..6594fe384b 100644
--- a/public/language/lt/admin/settings/post.json
+++ b/public/language/lt/admin/settings/post.json
@@ -49,5 +49,6 @@
"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
"composer.custom-help": "Custom Help Text",
"ip-tracking": "IP Tracking",
- "ip-tracking.each-post": "Track IP Address for each post"
+ "ip-tracking.each-post": "Track IP Address for each post",
+ "enable-post-history": "Enable Post History"
}
\ No newline at end of file
diff --git a/public/language/ms/admin/settings/post.json b/public/language/ms/admin/settings/post.json
index 6a1ab9a2a5..6594fe384b 100644
--- a/public/language/ms/admin/settings/post.json
+++ b/public/language/ms/admin/settings/post.json
@@ -49,5 +49,6 @@
"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
"composer.custom-help": "Custom Help Text",
"ip-tracking": "IP Tracking",
- "ip-tracking.each-post": "Track IP Address for each post"
+ "ip-tracking.each-post": "Track IP Address for each post",
+ "enable-post-history": "Enable Post History"
}
\ No newline at end of file
diff --git a/public/language/nb/admin/settings/post.json b/public/language/nb/admin/settings/post.json
index 6a1ab9a2a5..6594fe384b 100644
--- a/public/language/nb/admin/settings/post.json
+++ b/public/language/nb/admin/settings/post.json
@@ -49,5 +49,6 @@
"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
"composer.custom-help": "Custom Help Text",
"ip-tracking": "IP Tracking",
- "ip-tracking.each-post": "Track IP Address for each post"
+ "ip-tracking.each-post": "Track IP Address for each post",
+ "enable-post-history": "Enable Post History"
}
\ No newline at end of file
diff --git a/public/language/nl/admin/settings/post.json b/public/language/nl/admin/settings/post.json
index 6a1ab9a2a5..6594fe384b 100644
--- a/public/language/nl/admin/settings/post.json
+++ b/public/language/nl/admin/settings/post.json
@@ -49,5 +49,6 @@
"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
"composer.custom-help": "Custom Help Text",
"ip-tracking": "IP Tracking",
- "ip-tracking.each-post": "Track IP Address for each post"
+ "ip-tracking.each-post": "Track IP Address for each post",
+ "enable-post-history": "Enable Post History"
}
\ No newline at end of file
diff --git a/public/language/pl/admin/settings/post.json b/public/language/pl/admin/settings/post.json
index 1cbb47e88c..70509cc83e 100644
--- a/public/language/pl/admin/settings/post.json
+++ b/public/language/pl/admin/settings/post.json
@@ -49,5 +49,6 @@
"composer.enable-plugin-help": "Zezwalaj wtyczkom na dodawanie zawartości do zakładki pomocy",
"composer.custom-help": "Własny tekst pomocy",
"ip-tracking": "Śledzenie IP",
- "ip-tracking.each-post": "Śledź adres IP każdego z postów"
+ "ip-tracking.each-post": "Śledź adres IP każdego z postów",
+ "enable-post-history": "Enable Post History"
}
\ No newline at end of file
diff --git a/public/language/pt-BR/admin/settings/post.json b/public/language/pt-BR/admin/settings/post.json
index fa653ce7cd..3941fcc260 100644
--- a/public/language/pt-BR/admin/settings/post.json
+++ b/public/language/pt-BR/admin/settings/post.json
@@ -49,5 +49,6 @@
"composer.enable-plugin-help": "Permitir plugins de adicionar conteúdo à aba ajuda",
"composer.custom-help": "Texto de Ajuda Personalizado",
"ip-tracking": "Rastreamento de IP",
- "ip-tracking.each-post": "Rastrear Endereço IP para cada post"
+ "ip-tracking.each-post": "Rastrear Endereço IP para cada post",
+ "enable-post-history": "Enable Post History"
}
\ No newline at end of file
diff --git a/public/language/pt-PT/admin/settings/post.json b/public/language/pt-PT/admin/settings/post.json
index 6a1ab9a2a5..6594fe384b 100644
--- a/public/language/pt-PT/admin/settings/post.json
+++ b/public/language/pt-PT/admin/settings/post.json
@@ -49,5 +49,6 @@
"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
"composer.custom-help": "Custom Help Text",
"ip-tracking": "IP Tracking",
- "ip-tracking.each-post": "Track IP Address for each post"
+ "ip-tracking.each-post": "Track IP Address for each post",
+ "enable-post-history": "Enable Post History"
}
\ No newline at end of file
diff --git a/public/language/ro/admin/settings/post.json b/public/language/ro/admin/settings/post.json
index 6a1ab9a2a5..6594fe384b 100644
--- a/public/language/ro/admin/settings/post.json
+++ b/public/language/ro/admin/settings/post.json
@@ -49,5 +49,6 @@
"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
"composer.custom-help": "Custom Help Text",
"ip-tracking": "IP Tracking",
- "ip-tracking.each-post": "Track IP Address for each post"
+ "ip-tracking.each-post": "Track IP Address for each post",
+ "enable-post-history": "Enable Post History"
}
\ No newline at end of file
diff --git a/public/language/ru/admin/settings/post.json b/public/language/ru/admin/settings/post.json
index 6a1ab9a2a5..6594fe384b 100644
--- a/public/language/ru/admin/settings/post.json
+++ b/public/language/ru/admin/settings/post.json
@@ -49,5 +49,6 @@
"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
"composer.custom-help": "Custom Help Text",
"ip-tracking": "IP Tracking",
- "ip-tracking.each-post": "Track IP Address for each post"
+ "ip-tracking.each-post": "Track IP Address for each post",
+ "enable-post-history": "Enable Post History"
}
\ No newline at end of file
diff --git a/public/language/rw/admin/settings/post.json b/public/language/rw/admin/settings/post.json
index 6a1ab9a2a5..6594fe384b 100644
--- a/public/language/rw/admin/settings/post.json
+++ b/public/language/rw/admin/settings/post.json
@@ -49,5 +49,6 @@
"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
"composer.custom-help": "Custom Help Text",
"ip-tracking": "IP Tracking",
- "ip-tracking.each-post": "Track IP Address for each post"
+ "ip-tracking.each-post": "Track IP Address for each post",
+ "enable-post-history": "Enable Post History"
}
\ No newline at end of file
diff --git a/public/language/sc/admin/settings/post.json b/public/language/sc/admin/settings/post.json
index 6a1ab9a2a5..6594fe384b 100644
--- a/public/language/sc/admin/settings/post.json
+++ b/public/language/sc/admin/settings/post.json
@@ -49,5 +49,6 @@
"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
"composer.custom-help": "Custom Help Text",
"ip-tracking": "IP Tracking",
- "ip-tracking.each-post": "Track IP Address for each post"
+ "ip-tracking.each-post": "Track IP Address for each post",
+ "enable-post-history": "Enable Post History"
}
\ No newline at end of file
diff --git a/public/language/sk/admin/settings/post.json b/public/language/sk/admin/settings/post.json
index 8ae9b903e2..91c542dbf6 100644
--- a/public/language/sk/admin/settings/post.json
+++ b/public/language/sk/admin/settings/post.json
@@ -49,5 +49,6 @@
"composer.enable-plugin-help": "Povoliť zásuvné moduly pre pridanie obsahu do záložky nápovedy",
"composer.custom-help": "Používateľský text nápovedy",
"ip-tracking": "IP Tracking",
- "ip-tracking.each-post": "Track IP Address for each post"
+ "ip-tracking.each-post": "Track IP Address for each post",
+ "enable-post-history": "Enable Post History"
}
\ No newline at end of file
diff --git a/public/language/sl/admin/settings/post.json b/public/language/sl/admin/settings/post.json
index 6a1ab9a2a5..6594fe384b 100644
--- a/public/language/sl/admin/settings/post.json
+++ b/public/language/sl/admin/settings/post.json
@@ -49,5 +49,6 @@
"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
"composer.custom-help": "Custom Help Text",
"ip-tracking": "IP Tracking",
- "ip-tracking.each-post": "Track IP Address for each post"
+ "ip-tracking.each-post": "Track IP Address for each post",
+ "enable-post-history": "Enable Post History"
}
\ No newline at end of file
diff --git a/public/language/sr/admin/settings/post.json b/public/language/sr/admin/settings/post.json
index 5deedb8d42..28be4cea86 100644
--- a/public/language/sr/admin/settings/post.json
+++ b/public/language/sr/admin/settings/post.json
@@ -49,5 +49,6 @@
"composer.enable-plugin-help": "Dozvoli plugin-ovima da dodaju sadržaj na tab-u \"pomoć\"",
"composer.custom-help": "Prilagođen tekst za pomoć",
"ip-tracking": "Praćenje IP adrese",
- "ip-tracking.each-post": "Prati IP Adresu za svaki post"
+ "ip-tracking.each-post": "Prati IP Adresu za svaki post",
+ "enable-post-history": "Enable Post History"
}
\ No newline at end of file
diff --git a/public/language/sr/modules.json b/public/language/sr/modules.json
index 82a571fdd3..9788089afc 100644
--- a/public/language/sr/modules.json
+++ b/public/language/sr/modules.json
@@ -21,16 +21,16 @@
"chat.three_months": "3 месеца",
"chat.delete_message_confirm": "Да ли сте сигурни да желите да избришете ову поруку?",
"chat.add-users-to-room": "Додајте кориснике у собу",
- "chat.retrieving-users": "Retrieving users...",
+ "chat.retrieving-users": "Преузимање корисника...",
"chat.manage-room": "Управљај собама за ћаскање",
- "chat.add-user-help": "Search for users here. When selected, the user will be added to the chat. The new user will not be able to see chat messages written before they were added to the conversation.",
+ "chat.add-user-help": "Потражите кориснике овде. Када буде изабран, корисник ће бити додан у ћаскање. Нови корисник неће бити у могућности да види поруке написане пре него што је додан у преписку.",
"chat.confirm-chat-with-dnd-user": "Овај корисник је поставио свој статус на \"Не узнемиравај\". Да ли и даље желите да ћаскате са њим?",
"chat.rename-room": "Преименуј собу",
"chat.rename-placeholder": "Унесите назив собе овде",
- "chat.rename-help": "The room name set here will be viewable by all participants in the room.",
+ "chat.rename-help": "Име собе постављено овде биће видљиво свим учесницима у соби.",
"chat.leave": "Напусти ћаскање",
"chat.leave-prompt": "Да ли сте сигурни да желите да напустите ово ћаскање?",
- "chat.leave-help": "Leaving this chat will remove you from future correspondence in this chat. If you are re-added in the future, you will not see any chat history from prior to your re-joining.",
+ "chat.leave-help": "Напуштање овог ћаскања ће вас уклонити из будућих преписки у овом ћаскању. Ако будете поново додани у будућности, нећете видети историју ћаскања од пре вашег поновног придруживања.",
"chat.in-room": "У овој соби",
"composer.compose": "Писање поруке",
"composer.show_preview": "Прикажи преглед",
diff --git a/public/language/sv/admin/settings/post.json b/public/language/sv/admin/settings/post.json
index 6a1ab9a2a5..6594fe384b 100644
--- a/public/language/sv/admin/settings/post.json
+++ b/public/language/sv/admin/settings/post.json
@@ -49,5 +49,6 @@
"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
"composer.custom-help": "Custom Help Text",
"ip-tracking": "IP Tracking",
- "ip-tracking.each-post": "Track IP Address for each post"
+ "ip-tracking.each-post": "Track IP Address for each post",
+ "enable-post-history": "Enable Post History"
}
\ No newline at end of file
diff --git a/public/language/th/admin/settings/post.json b/public/language/th/admin/settings/post.json
index 6a1ab9a2a5..6594fe384b 100644
--- a/public/language/th/admin/settings/post.json
+++ b/public/language/th/admin/settings/post.json
@@ -49,5 +49,6 @@
"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
"composer.custom-help": "Custom Help Text",
"ip-tracking": "IP Tracking",
- "ip-tracking.each-post": "Track IP Address for each post"
+ "ip-tracking.each-post": "Track IP Address for each post",
+ "enable-post-history": "Enable Post History"
}
\ No newline at end of file
diff --git a/public/language/tr/admin/settings/post.json b/public/language/tr/admin/settings/post.json
index 92b8e7dfaa..abf2eab638 100644
--- a/public/language/tr/admin/settings/post.json
+++ b/public/language/tr/admin/settings/post.json
@@ -49,5 +49,6 @@
"composer.enable-plugin-help": "Eklentilerin yardım sekmesine içerik eklemesine izin ver",
"composer.custom-help": "Özel Yardım Metni",
"ip-tracking": "IP İzleme",
- "ip-tracking.each-post": "Her ileti için IP Adresini takip et"
+ "ip-tracking.each-post": "Her ileti için IP Adresini takip et",
+ "enable-post-history": "Enable Post History"
}
\ No newline at end of file
diff --git a/public/language/tr/error.json b/public/language/tr/error.json
index 079fe755d8..67d5c52c0e 100644
--- a/public/language/tr/error.json
+++ b/public/language/tr/error.json
@@ -116,8 +116,8 @@
"cant-delete-chat-message": "Bu mesajı silmek için izin verilmez",
"chat-edit-duration-expired": "Gönderildikten sonra yalnızca %1 saniye mesajı(ları) düzenlemene izin verilir",
"chat-delete-duration-expired": "Gönderildikten sonra yalnızca %1 saniye mesajı(ları) silmene izin verilir",
- "chat-deleted-already": "This chat message has already been deleted.",
- "chat-restored'already": "This chat message has already been restored.",
+ "chat-deleted-already": "Bu sohbet mesajı zaten silinmiş.",
+ "chat-restored'already": "Bu sohbet mesajı zaten geri yüklendi.",
"already-voting-for-this-post": "Bu gönderi için zaten oy verdin.",
"reputation-system-disabled": "İtibar sistemi devre dışı.",
"downvoting-disabled": "Aşagı oylama kapatılmış",
@@ -134,7 +134,7 @@
"wrong-login-type-email": "Lütfen giriş için e-posta adresinizi kullanın",
"wrong-login-type-username": "Lütfen giriş için kullanıcı adınızı kullanın",
"sso-registration-disabled": "%1 hesap için kayıt işlemi devre dışı bırakıldı, lütfen öncelikle bir eposta adresi ile kayıt olun",
- "sso-multiple-association": "You cannot associate multiple accounts from this service to your NodeBB account. Please dissociate your existing account and try again.",
+ "sso-multiple-association": "Bu hizmetten birden fazla hesabı, NodeBB hesabınızla ilişkilendiremezsiniz. Lütfen mevcut hesabınızı ayırın ve tekrar deneyin.",
"invite-maximum-met": "Sen maksimum miktarda insanı davet ettin (%2 üzerinden %1).",
"no-session-found": "Giriş yapılmış bir oturum bulunamadı!",
"not-in-room": "Odada kullanıcı yok",
diff --git a/public/language/tr/global.json b/public/language/tr/global.json
index ba16c0f1e9..53434b2403 100644
--- a/public/language/tr/global.json
+++ b/public/language/tr/global.json
@@ -107,5 +107,5 @@
"edited": "Düzenlendi",
"disabled": "Devre dışı",
"select": "Seç",
- "user-search-prompt": "Type something here to find users..."
+ "user-search-prompt": "Kullanıcı bulmak için buraya bir şeyler yazın ..."
}
\ No newline at end of file
diff --git a/public/language/tr/modules.json b/public/language/tr/modules.json
index 1a19b5edde..b13c7ea4ca 100644
--- a/public/language/tr/modules.json
+++ b/public/language/tr/modules.json
@@ -1,5 +1,5 @@
{
- "chat.chatting_with": "Chat with",
+ "chat.chatting_with": "Sohbet",
"chat.placeholder": "Buraya bir mesaj girin, göndermek için girişe basın",
"chat.send": "Gönder",
"chat.no_active": "Aktif sohbet mevcut değil",
@@ -12,7 +12,7 @@
"chat.recent-chats": "Güncel Sohbetler",
"chat.contacts": "Kontaklar",
"chat.message-history": "Mesaj Geçmişi",
- "chat.options": "Chat options",
+ "chat.options": "Sohbet Ayarları",
"chat.pop-out": "Sohbeti Pencereye Çevir",
"chat.minimize": "Küçült",
"chat.maximize": "Büyüt",
@@ -21,17 +21,17 @@
"chat.three_months": "3 Ay",
"chat.delete_message_confirm": "Bu mesajı silmek istediğinden emin misin?",
"chat.add-users-to-room": "Odaya Kullanıcı Ekle",
- "chat.retrieving-users": "Retrieving users...",
- "chat.manage-room": "Manage Chat Room",
- "chat.add-user-help": "Search for users here. When selected, the user will be added to the chat. The new user will not be able to see chat messages written before they were added to the conversation.",
+ "chat.retrieving-users": "Kullanıcılar alınıyor ...",
+ "chat.manage-room": "Sohbet Odasını Yönet",
+ "chat.add-user-help": "Kullanıcılar için arama yapın. Seçildiğinde, kullanıcı sohbete eklenecektir. Yeni kullanıcı, sohbete eklenmeden önce yazılı sohbet mesajlarını göremeyecektir.",
"chat.confirm-chat-with-dnd-user": "Bu kullanıcı durumunu rahatsız etmeyin olarak ayarladı. Hala onunla sohbet etmek istiyor musun?",
- "chat.rename-room": "Rename room",
- "chat.rename-placeholder": "Enter your room name here",
- "chat.rename-help": "The room name set here will be viewable by all participants in the room.",
- "chat.leave": "Leave Chat",
- "chat.leave-prompt": "Are you sure you wish to leave this chat?",
- "chat.leave-help": "Leaving this chat will remove you from future correspondence in this chat. If you are re-added in the future, you will not see any chat history from prior to your re-joining.",
- "chat.in-room": "In this room",
+ "chat.rename-room": "Odanın adını değiştir",
+ "chat.rename-placeholder": "Oda isminizi buraya girin",
+ "chat.rename-help": "Buradaki oda adı odadaki tüm katılımcılar tarafından görülebilir.",
+ "chat.leave": "Sohbetten Ayrıl",
+ "chat.leave-prompt": "Sohbetten ayrılmak istediğinden emin misin?",
+ "chat.leave-help": "Bu sohbetten ayrılmak, bu sohbetteki gelecekteki yazışmalardan sizi silecektir. Gelecekte tekrar eklendiyseniz, yeniden katılmadan önce herhangi bir sohbet geçmişi görmezsiniz.",
+ "chat.in-room": "Bu odada",
"composer.compose": "Yaz",
"composer.show_preview": "Önizleme Göster",
"composer.hide_preview": "Önizleme Sakla",
diff --git a/public/language/tr/topic.json b/public/language/tr/topic.json
index a058a99b1e..d538842a6b 100644
--- a/public/language/tr/topic.json
+++ b/public/language/tr/topic.json
@@ -30,12 +30,12 @@
"locked": "Kilitli",
"pinned": "İğnelendi",
"moved": "Taşındı",
- "copy-ip": "Copy IP",
- "ban-ip": "Ban IP",
- "view-history": "Edit History",
+ "copy-ip": "IP Kopyala",
+ "ban-ip": "IP Engelle",
+ "view-history": "Geçmişi Düzenle",
"bookmark_instructions": "Bu başlıkta en son kaldığın yere dönmek için tıklayın.",
"flag_title": "Bu iletiyi moderatöre haber et",
- "merged_message": "This topic has been merged into %2",
+ "merged_message": "Bu konu %2 olarak birleştirildi",
"deleted_message": "Bu başlık silindi. Sadece başlık düzenleme yetkisi olan kullanıcılar görebilir.",
"following_topic.message": "Artık bir kullanıcı bu başlığa ileti gönderdiğinde bildirim alacaksınız.",
"not_following_topic.message": "Bu başlığı okunmamışlarda göreceksiniz ama biri bir şey yazdığında bildirim gelmeyecek.",
@@ -123,7 +123,7 @@
"stale.create": "Yeni bir başlık yarat",
"stale.reply_anyway": "Bu konuyu cevapla",
"link_back": "Re: [%1](%2)",
- "diffs.title": "Post Edit History",
- "diffs.description": "This post has %1 revisions. Click one of the revisions below to see the post content at that point in time.",
- "diffs.no-revisions-description": "This post has %1 revisions."
+ "diffs.title": "İleti Geçmiş Düzenle",
+ "diffs.description": "Bu iletinin %1revizyonu var. Zaman içerisinde ileti içeriğinin tamamını görmek için aşağıdaki düzeltmelerden birine tıklayın.",
+ "diffs.no-revisions-description": "Bu iletinin %1 revizyonu var."
}
\ No newline at end of file
diff --git a/public/language/tr/user.json b/public/language/tr/user.json
index 3b6f6f54c7..a2f8caae9d 100644
--- a/public/language/tr/user.json
+++ b/public/language/tr/user.json
@@ -1,7 +1,7 @@
{
"banned": "Yasaklı",
"offline": "Çevrimdışı",
- "deleted": "Deleted",
+ "deleted": "Silindi",
"username": "Kullanıcı Adı",
"joindate": "Katılım Tarihi",
"postcount": "İleti Sayısı",
@@ -96,7 +96,7 @@
"topics_per_page": "Sayfa başına Konular",
"posts_per_page": "Sayfa başına İletiler",
"max_items_per_page": "Maksimum %1",
- "acp_language": "Admin Page Language",
+ "acp_language": "Yönetici Sayfası Dili",
"notification_sounds": "Uyarı alındığında ses çal",
"notifications_and_sounds": "Bildirimler & Sesler",
"incoming-message-sound": "Gelen mesaj sesi",
diff --git a/public/language/uk/admin/extend/widgets.json b/public/language/uk/admin/extend/widgets.json
index 84480a82c9..ad473c5682 100644
--- a/public/language/uk/admin/extend/widgets.json
+++ b/public/language/uk/admin/extend/widgets.json
@@ -2,7 +2,7 @@
"available": "Доступні віджети",
"explanation": "Оберіть віджет із випадаючого меню і перетягніть його в область зліва.",
"none-installed": "Віджетів не знайдено! Активуйте необхідний плагін віджетів в панелі керування плагінами.",
- "clone-from": "Clone widgets from",
+ "clone-from": "Клонувати віджет з",
"containers.available": "Доступні контейнери",
"containers.explanation": "Перетягніть поверх будь-якого активного віджету",
"containers.none": "Ніякий",
@@ -16,7 +16,7 @@
"alert.confirm-delete": "Ви впевнені, що бажаєте видалити цей віджет?",
"alert.updated": "Віджети оновлено",
"alert.update-success": "Віджети успішно оновлено",
- "alert.clone-success": "Successfully cloned widgets",
+ "alert.clone-success": "Віджети успішно клоновано",
- "error.select-clone": "Please select a page to clone from"
+ "error.select-clone": "Будь ласка, виберіть сторінку для клонування з"
}
\ No newline at end of file
diff --git a/public/language/uk/admin/general/dashboard.json b/public/language/uk/admin/general/dashboard.json
index e223a90143..46b4020cf8 100644
--- a/public/language/uk/admin/general/dashboard.json
+++ b/public/language/uk/admin/general/dashboard.json
@@ -36,10 +36,10 @@
"search-plugin-tooltip": "Встановіть пошуковий плагін зі сторінки плагінів, що активувати пошуковий функціонал",
"control-panel": "Керування системою",
- "reload": "Restart",
- "restart": "Rebuild & Restart",
- "restart-warning": "Rebuilding or Restarting your NodeBB will drop all existing connections for a few seconds.",
- "restart-disabled": "Rebuilding and Restarting your NodeBB has been disabled as you do not seem to be running it via the appropriate daemon.",
+ "reload": "Перезавантажити",
+ "restart": "Перебудувати та перезавантажити",
+ "restart-warning": "Перебудування або перезапуск вашого NodeBB призведе до втрати всіх існуючих з'єднань протягом декількох секунд.",
+ "restart-disabled": "Перебудування та перезапуск вашого NodeBB вимкнено, оскільки ви, здається, не запускаєте його через відповідний демон.",
"maintenance-mode": "Режим обслуговування",
"maintenance-mode-title": "Натисніть тут, щоб налаштувати режим обслуговування NodeBB",
"realtime-chart-updates": "Оновлення графіків в реальному часі",
diff --git a/public/language/uk/admin/manage/categories.json b/public/language/uk/admin/manage/categories.json
index 1676c20579..ca21065d91 100644
--- a/public/language/uk/admin/manage/categories.json
+++ b/public/language/uk/admin/manage/categories.json
@@ -66,6 +66,6 @@
"alert.user-search": "Шукайте користувача тут...",
"alert.find-group": "Знайти групу",
"alert.group-search": "Шукайте групу тут...",
- "collapse-all": "Collapse All",
- "expand-all": "Expand All"
+ "collapse-all": "Згорнути всі",
+ "expand-all": "Розгорнути всі"
}
\ No newline at end of file
diff --git a/public/language/uk/admin/manage/ip-blacklist.json b/public/language/uk/admin/manage/ip-blacklist.json
index da30c5283b..796d4b9847 100644
--- a/public/language/uk/admin/manage/ip-blacklist.json
+++ b/public/language/uk/admin/manage/ip-blacklist.json
@@ -15,5 +15,5 @@
"analytics.blacklist-hourly": "Графік 1 – Внесення до чорного списку за годину",
"analytics.blacklist-daily": "Графік 2 – Внесення до чорного списку за день",
- "ip-banned": "IP banned"
+ "ip-banned": "IP заблоковано"
}
\ No newline at end of file
diff --git a/public/language/uk/admin/manage/post-queue.json b/public/language/uk/admin/manage/post-queue.json
index 8d3eebeccf..47da91c9d0 100644
--- a/public/language/uk/admin/manage/post-queue.json
+++ b/public/language/uk/admin/manage/post-queue.json
@@ -7,5 +7,5 @@
"content": "Зміст",
"posted": "Опубліковано",
"reply-to": "Відповідь для \"%1\"",
- "content-editable": "You can click on individual content to edit before posting."
+ "content-editable": "Ви можете натиснути на окремий вміст, щоб редагувати його перед публікацією."
}
\ No newline at end of file
diff --git a/public/language/uk/admin/manage/tags.json b/public/language/uk/admin/manage/tags.json
index b47e84a1cf..2252ce563b 100644
--- a/public/language/uk/admin/manage/tags.json
+++ b/public/language/uk/admin/manage/tags.json
@@ -6,7 +6,7 @@
"description": "Виберіть теги тиснучи або перетягуючи. Використовуйте Shift, щоб обрати декілька.",
"create": "Створити тег",
"modify": "Змінити тег",
- "rename": "Rename Tags",
+ "rename": "Перейменувати теги",
"delete": "Видалити вибрані теги",
"search": "Пошук тегів...",
"settings": "Натисніть тут щоб перейти на сторінку налаштування тегів.",
diff --git a/public/language/uk/admin/manage/users.json b/public/language/uk/admin/manage/users.json
index d061626f0e..2cf4ef393d 100644
--- a/public/language/uk/admin/manage/users.json
+++ b/public/language/uk/admin/manage/users.json
@@ -27,8 +27,8 @@
"pills.banned": "Забанені",
"pills.search": "Пошук користувачів",
- "search.uid": "By User ID",
- "search.uid-placeholder": "Enter a user ID to search",
+ "search.uid": "За ID користувача",
+ "search.uid-placeholder": "Введіть ID користувача для пошуку",
"search.username": "За іменем",
"search.username-placeholder": "Введіть ім'я для пошуку",
"search.email": "За поштою",
@@ -71,15 +71,15 @@
"alerts.lockout-reset-success": "Блокування скинуто!",
"alerts.flag-reset-success": "Скарги скинуто!",
"alerts.no-remove-yourself-admin": "Ви не можете видалити себе як адміна!",
- "alerts.make-admin-success": "User is now administrator.",
- "alerts.confirm-remove-admin": "Do you really want to remove this administrator?",
- "alerts.remove-admin-success": "User is no longer administrator.",
- "alerts.make-global-mod-success": "User is now global moderator.",
- "alerts.confirm-remove-global-mod": "Do you really want to remove this global moderator?",
- "alerts.remove-global-mod-success": "User is no longer global moderator.",
- "alerts.make-moderator-success": "User is now moderator.",
- "alerts.confirm-remove-moderator": "Do you really want to remove this moderator?",
- "alerts.remove-moderator-success": "User is no longer moderator.",
+ "alerts.make-admin-success": "Користувач зараз є адміністратором.",
+ "alerts.confirm-remove-admin": "Ви дійсно хочете видалити цього адміністратора?",
+ "alerts.remove-admin-success": "Користувач більше не є адміністратором.",
+ "alerts.make-global-mod-success": "Користувач зараз є глобальним модератором.",
+ "alerts.confirm-remove-global-mod": "Ви дійсно хочете вилучити цього глобального модератора?",
+ "alerts.remove-global-mod-success": "Користувач більше не є глобальним модератором.",
+ "alerts.make-moderator-success": "Користувач зараз є модератором.",
+ "alerts.confirm-remove-moderator": "Ви дійсно хочете видалити цього модератора?",
+ "alerts.remove-moderator-success": "Користувач більше не є модератором.",
"alerts.confirm-validate-email": "Ви точно бажаєте підтвердити електронні пошти цих користувачів?",
"alerts.validate-email-success": "Електронні пошти підтверджено",
"alerts.password-reset-confirm": "Ви точно бажаєте скинути паролі цим користувачам електронною поштою?",
diff --git a/public/language/uk/admin/menu.json b/public/language/uk/admin/menu.json
index 266c961572..39d9c46b22 100644
--- a/public/language/uk/admin/menu.json
+++ b/public/language/uk/admin/menu.json
@@ -9,15 +9,15 @@
"section-manage": "Керування",
"manage/categories": "Категорії",
- "manage/privileges": "Privileges",
+ "manage/privileges": "Права",
"manage/tags": "Теги",
"manage/users": "Користувачі",
- "manage/admins-mods": "Admins & Mods",
+ "manage/admins-mods": "Адміністратори та моди",
"manage/registration": "Черга реєстрації",
"manage/post-queue": "Черга Постів",
"manage/groups": "Групи",
"manage/ip-blacklist": "Чорний список IP-адрес",
- "manage/uploads": "Uploads",
+ "manage/uploads": "Завантаження",
"section-settings": "Налаштування",
"settings/general": "Загальні",
@@ -42,7 +42,7 @@
"section-appearance": "Зовнішній вигляд",
"appearance/themes": "Теми",
"appearance/skins": "Стилі",
- "appearance/customise": "Custom Content (HTML/JS/CSS)",
+ "appearance/customise": "Користувацький вміст (HTML/JS/CSS)",
"section-extend": "Розширити",
"extend/plugins": "Плагіни",
@@ -68,7 +68,7 @@
"logout": "Вийти",
"view-forum": "Переглянути форум",
- "search.placeholder": "Search for settings",
+ "search.placeholder": "Пошук налаштувань",
"search.no-results": "Без результатів...",
"search.search-forum": "Шукати на форумі ",
"search.keep-typing": "Для результатів, надрукуйте ще...",
diff --git a/public/language/uk/admin/settings/chat.json b/public/language/uk/admin/settings/chat.json
index d73c11e7ef..10e4dbd855 100644
--- a/public/language/uk/admin/settings/chat.json
+++ b/public/language/uk/admin/settings/chat.json
@@ -6,6 +6,6 @@
"max-length": "Максимальна довжина повідомлення",
"max-room-size": "Максимальна кількість людей у кімнаті",
"delay": "Час між повідомленнями в мілісекундах",
- "restrictions.seconds-edit-after": "Number of seconds before users are allowed to edit chat messages after posting. (0 disabled)",
- "restrictions.seconds-delete-after": "Number of seconds before users are allowed to delete chat messages after posting. (0 disabled)"
+ "restrictions.seconds-edit-after": "Кількість секунд, перш ніж користувачі зможуть редагувати повідомлення чату після публікації. (0 вимкнено)",
+ "restrictions.seconds-delete-after": "Кількість секунд, перш ніж користувачі зможуть видаляти повідомлення чату після публікації. (0 вимкнено)"
}
\ No newline at end of file
diff --git a/public/language/uk/admin/settings/notifications.json b/public/language/uk/admin/settings/notifications.json
index 3a5f9976d1..55f71dae63 100644
--- a/public/language/uk/admin/settings/notifications.json
+++ b/public/language/uk/admin/settings/notifications.json
@@ -2,5 +2,5 @@
"notifications": "Сповіщення",
"welcome-notification": "Сповіщення \"Ласкаво просимо\"",
"welcome-notification-link": "Посилання для сповіщення \"Ласкаво просимо\"",
- "welcome-notification-uid": "Welcome Notification User (UID)"
+ "welcome-notification-uid": "Сповіщення \"Ласкаво просимо\" для користувача (UID)"
}
\ No newline at end of file
diff --git a/public/language/uk/admin/settings/pagination.json b/public/language/uk/admin/settings/pagination.json
index 4be8e3452a..e874e8d249 100644
--- a/public/language/uk/admin/settings/pagination.json
+++ b/public/language/uk/admin/settings/pagination.json
@@ -3,9 +3,9 @@
"enable": "Розбивати список тем та постів на сторінки замість нескінченної прокрутки",
"topics": "Пагінація тем",
"posts-per-page": "Постів на сторінку",
- "max-posts-per-page": "Maximum posts per page",
+ "max-posts-per-page": "Максимум постів на сторінку",
"categories": "Пагінація категорій",
"topics-per-page": "Тем на сторінку",
- "max-topics-per-page": "Maximum topics per page",
+ "max-topics-per-page": "Максимум тем на сторінку",
"initial-num-load": "Початкова кількість тем для завантаження в Непрочитаних, Недавніх та Популярних"
}
\ No newline at end of file
diff --git a/public/language/uk/admin/settings/post.json b/public/language/uk/admin/settings/post.json
index df8593c187..4ead41ee2c 100644
--- a/public/language/uk/admin/settings/post.json
+++ b/public/language/uk/admin/settings/post.json
@@ -4,27 +4,27 @@
"sorting.oldest-to-newest": "Старі > Нові",
"sorting.newest-to-oldest": "Нові > Старі",
"sorting.most-votes": "Кількість голосів",
- "sorting.most-posts": "Most Posts",
+ "sorting.most-posts": "Кількість постів",
"sorting.topic-default": "Типове сортування тем",
- "length": "Post Length",
+ "length": "Довжина посту",
"restrictions": "Обмеження постингу",
- "restrictions-new": "New User Restrictions",
+ "restrictions-new": "Нові обмеження користувачів",
"restrictions.post-queue": "Увімкнути чергу постів",
- "restrictions-new.post-queue": "Enable new user restrictions",
+ "restrictions-new.post-queue": "Увімкнути нові обмеження користувачів",
"restrictions.post-queue-help": "Після увімкнення черги постів, пости нових користувачів будуть ставитись в чергу на схвалення.",
- "restrictions-new.post-queue-help": "Enabling new user restrictions will set restrictions on posts created by new users.",
- "restrictions.seconds-between": "Seconds between posts",
- "restrictions.seconds-between-new": "Seconds between posts for new users",
- "restrictions.rep-threshold": "Reputation threshold before these restrictions are lifted",
+ "restrictions-new.post-queue-help": "Увімкнення нових обмежень користувача встановить обмеження на публікації, створені новими користувачами.",
+ "restrictions.seconds-between": "Секунд між постами",
+ "restrictions.seconds-between-new": "Секунд між постами для нових користувачів",
+ "restrictions.rep-threshold": "Рівень репутації до того, як ці обмеження скасовуються",
"restrictions.seconds-defore-new": "Кількість секунд до першого посту для нового користувача",
- "restrictions.seconds-edit-after": "Number of seconds before users are allowed to edit posts after posting. (0 disabled)",
- "restrictions.seconds-delete-after": "Number of seconds before users are allowed to delete posts after posting. (0 disabled)",
+ "restrictions.seconds-edit-after": "Кількість секунд, перш ніж користувачі зможуть редагувати повідомлення після публікації. (0 вимкнено)",
+ "restrictions.seconds-delete-after": "Кількість секунд, перш ніж користувачі зможуть видаляти повідомлення після публікації. (0 вимкнено)",
"restrictions.replies-no-delete": "Кількість відповідей після котрих користувач не зможе видалити власну тему. (0 — вимкнено)",
"restrictions.min-title-length": "Мінімальна довжина заголовку",
"restrictions.max-title-length": "Максимальна довжина заголовку",
"restrictions.min-post-length": "Мінімальна довжина посту",
"restrictions.max-post-length": "Максимальна довжина посту",
- "restrictions.days-until-stale": "Days until topic is considered stale",
+ "restrictions.days-until-stale": "Днів, доки тема не вважатиметься застарілою",
"restrictions.stale-help": "Якщо тема є \"застарілою\", то для користувачів, що бажають відповісти на неї буде показано попередження.",
"timestamp": "Часова мітка",
"timestamp.cut-off": "Обрізка дат (в днях)",
@@ -49,5 +49,6 @@
"composer.enable-plugin-help": "Дозволити плагінам додавати зміст довідки",
"composer.custom-help": "Користувацький текст довідки",
"ip-tracking": "Відстеження IP",
- "ip-tracking.each-post": "Відстежувати IP адреси для кожного посту"
+ "ip-tracking.each-post": "Відстежувати IP адреси для кожного посту",
+ "enable-post-history": "Увімкнути історію постів"
}
\ No newline at end of file
diff --git a/public/language/uk/admin/settings/reputation.json b/public/language/uk/admin/settings/reputation.json
index 40efe9e9cd..039ba2c10f 100644
--- a/public/language/uk/admin/settings/reputation.json
+++ b/public/language/uk/admin/settings/reputation.json
@@ -6,7 +6,7 @@
"thresholds": "Допуски активності",
"min-rep-downvote": "Мінімальна репутація для голосування проти постів",
"min-rep-flag": "Мінімальна репутація для подання скарг на пости",
- "min-rep-website": "Minimum reputation to add \"Website\" to user profile",
- "min-rep-aboutme": "Minimum reputation to add \"About me\" to user profile",
- "min-rep-signature": "Minimum reputation to add \"Signature\" to user profile"
+ "min-rep-website": "Мінімальна репутація для додавання \"Веб-сайту\" до профілю користувача",
+ "min-rep-aboutme": "Мінімальна репутація для додавання \"Про мене\" до профілю користувача",
+ "min-rep-signature": "Мінімальна репутація для додавання \"Підпис\" до профілю користувача"
}
\ No newline at end of file
diff --git a/public/language/uk/admin/settings/user.json b/public/language/uk/admin/settings/user.json
index 80b8e54787..25be496e4b 100644
--- a/public/language/uk/admin/settings/user.json
+++ b/public/language/uk/admin/settings/user.json
@@ -19,8 +19,8 @@
"themes": "Теми",
"disable-user-skins": "Заборонити користувачам обирати стиль сайту",
"account-protection": "Захист акаунту",
- "admin-relogin-duration": "Admin relogin duration (minutes)",
- "admin-relogin-duration-help": "After a set amount of time accessing the admin section will require re-login, set to 0 to disable",
+ "admin-relogin-duration": "Тривалість повторного входу адміністратора (хвилин)",
+ "admin-relogin-duration-help": "Після встановленої кількості часу для доступу до розділу адміністрування потрібно буде знову ввійти, встановити значення 0 для вимкнення",
"login-attempts": "Кількість спроб входу за годину",
"login-attempts-help": "Якщо кількість спроб входу в акаунт користувача перевищить цей ліміт, акаунт буде заблоковано на задану кількість часу",
"lockout-duration": "Тривалість блокування акаунту (хвилин)",
@@ -63,5 +63,5 @@
"email-post-notif": "Надсилати листа, коли в темах на які я підписаний з'являються відповіді",
"follow-created-topics": "Стежити за темами які ви створюєте",
"follow-replied-topics": "Стежити за темами в котрих ви відповідаєте",
- "default-notification-settings": "Default notification settings"
+ "default-notification-settings": "Стандартні налаштування сповіщень"
}
\ No newline at end of file
diff --git a/public/language/uk/email.json b/public/language/uk/email.json
index 9d44cf9f7f..eeba076ce9 100644
--- a/public/language/uk/email.json
+++ b/public/language/uk/email.json
@@ -30,7 +30,7 @@
"notif.chat.unsub.info": "Це повідомлення чату було вислано вам, згідно ваших налаштувань підписки",
"notif.post.cta": "Натисніть тут, щоб повністю прочитати статтю",
"notif.post.unsub.info": "Це поштове повідомлення було вислано вам, згідно ваших налаштувань підписки",
- "notif.cta": "Click here to go to forum",
+ "notif.cta": "Натисніть тут, щоб перейти на форум",
"test.text1": "Це пробний лист для верифікації поштової служби. Всі налаштування вірні для NodeBB.",
"unsub.cta": "Натисніть тут, щоб змінити ці налаштування",
"banned.subject": "Ви були забанені на %1",
diff --git a/public/language/uk/error.json b/public/language/uk/error.json
index 865a05426a..266c3a9140 100644
--- a/public/language/uk/error.json
+++ b/public/language/uk/error.json
@@ -11,13 +11,13 @@
"invalid-uid": "Невірний ID користувача",
"invalid-username": "Невірне ім'я користувача",
"invalid-email": "Невірна електронна адреса",
- "invalid-title": "Invalid title",
+ "invalid-title": "Невірний заголовок",
"invalid-user-data": "Невірні користувацькі дані",
"invalid-password": "Невірний пароль",
"invalid-login-credentials": "Невірне ім'я користувача або пароль",
"invalid-username-or-password": "Вкажіть, будь ласка, ім'я користувача та пароль",
"invalid-search-term": "Невірний пошуковий запит",
- "invalid-url": "Invalid URL",
+ "invalid-url": "Недійсна URL-адреса",
"csrf-invalid": "Нам не вдалося вас пустити, ймовірно, через прострочену сесію. Будь ласка, спробуйте ще раз",
"invalid-pagination-value": "Невірне значення сторінки, має бути щонайменше %1 та щонайбільше %2",
"username-taken": "Це ім'я зайняте",
@@ -82,7 +82,7 @@
"cant-ban-other-admins": "Ви не можете банити інших адмінів!",
"cant-remove-last-admin": "Ви єдиний адміністратор. Додайте іншого користувача в якості адміністратора перш ніж знімати з себе ці обов'язки.",
"cant-delete-admin": "Зніміть обов'язки адміністратора з цього акаунту перш ніж видаляти його.",
- "invalid-image": "Invalid image",
+ "invalid-image": "Невірне зображення",
"invalid-image-type": "Невірний тип зображення. Дозволені типи: %1",
"invalid-image-extension": "Невірне розширення зображення",
"invalid-file-type": "Невірний тип файлу. Дозволені типи: %1",
@@ -114,27 +114,27 @@
"cant-edit-chat-message": "Ви не можете редагувати повідомлення",
"cant-remove-last-user": "Ви не можете видалити останнього користувача",
"cant-delete-chat-message": "Ви не можете видалити це повідомлення",
- "chat-edit-duration-expired": "You are only allowed to edit chat messages for %1 second(s) after posting",
- "chat-delete-duration-expired": "You are only allowed to delete chat messages for %1 second(s) after posting",
- "chat-deleted-already": "This chat message has already been deleted.",
- "chat-restored'already": "This chat message has already been restored.",
+ "chat-edit-duration-expired": "Ви можете редагувати повідомлення чату лише через %1 секунд після публікації",
+ "chat-delete-duration-expired": "Ви можете видаляти повідомлення чату лише через %1 секунд після публікації",
+ "chat-deleted-already": "Це повідомлення чату вже було видалено.",
+ "chat-restored'already": "Це повідомлення чату вже відновлено.",
"already-voting-for-this-post": "Ви вже проголосували за цей пост.",
"reputation-system-disabled": "Система репутацій вимкнена.",
"downvoting-disabled": "Голосування проти вимкнено",
"not-enough-reputation-to-downvote": "У вас недостатньо репутації, щоб голосувати проти цього посту",
"not-enough-reputation-to-flag": "У вас недостатньо репутації, щоб помітити цей пост",
- "not-enough-reputation-min-rep-website": "You do not have enough reputation to add a website",
- "not-enough-reputation-min-rep-aboutme": "You do not have enough reputation to add an about me",
- "not-enough-reputation-min-rep-signature": "You do not have enough reputation to add a signature",
+ "not-enough-reputation-min-rep-website": "У вас недостатньо репутації для додавання веб-сайту",
+ "not-enough-reputation-min-rep-aboutme": "Ви не маєте достатньої репутації, щоб додати Про мене",
+ "not-enough-reputation-min-rep-signature": "У вас недостатньо репутації, щоб додати підпис",
"already-flagged": "Ви вже помітили цей пост",
- "self-vote": "You cannot vote on your own post",
+ "self-vote": "Ви не можете проголосувати за власний пост",
"reload-failed": "У NodeBB виникла проблема при перевантаженні: \"%1\". NodeBB продовжить надавати існуючі клієнтські ресурси, проте радимо вам скасувати те, що було зроблено до перевантаження.",
"registration-error": "Помилка реєстрації",
"parse-error": "Щось пішло не так при розборі відповіді сервера",
"wrong-login-type-email": "Будь ласка, використайте вашу електронну пошту для входу",
"wrong-login-type-username": "Будь ласка, використайте ваше ім'я для входу",
- "sso-registration-disabled": "Registration has been disabled for %1 accounts, please register with an email address first",
- "sso-multiple-association": "You cannot associate multiple accounts from this service to your NodeBB account. Please dissociate your existing account and try again.",
+ "sso-registration-disabled": "Реєстрація була відключена для %1 акаунтів, будь ласка, зареєструйтесь спочатку з адресою електронної пошти",
+ "sso-multiple-association": "Ви не можете пов'язати кілька облікових записів з цього сервісу з обліковим записом NodeBB. Будь ласка, від'єднайте існуючий обліковий запис і повторіть спробу.",
"invite-maximum-met": "Ви запросили максимальну кілкість людей (%1 з %2).",
"no-session-found": "Жодної сесії не знайдено!",
"not-in-room": "Користувача немає в кімнаті",
@@ -144,5 +144,5 @@
"invalid-home-page-route": "Невірний шлях на головну",
"invalid-session": "Сесія не існує",
"invalid-session-text": "Здається, що ваша сесія більше неактивна або розійшлася з серверною. Оновіть, будь ласка, цю сторінку.",
- "no-topics-selected": "No topics selected!"
+ "no-topics-selected": "Не вибрано жодної теми!"
}
\ No newline at end of file
diff --git a/public/language/uk/global.json b/public/language/uk/global.json
index a4c6209896..0b495a261a 100644
--- a/public/language/uk/global.json
+++ b/public/language/uk/global.json
@@ -53,7 +53,7 @@
"topics": "Теми",
"posts": "Пости",
"best": "Найкращі",
- "votes": "Votes",
+ "votes": "Голоси",
"upvoters": "За",
"upvoted": "За",
"downvoters": "Проти",
@@ -107,5 +107,5 @@
"edited": "Відредаговано",
"disabled": "Вимкнено",
"select": "Обрати",
- "user-search-prompt": "Type something here to find users..."
+ "user-search-prompt": "Введіть щось тут, щоб знайти користувачів..."
}
\ No newline at end of file
diff --git a/public/language/uk/modules.json b/public/language/uk/modules.json
index 469e353eae..80abccb68e 100644
--- a/public/language/uk/modules.json
+++ b/public/language/uk/modules.json
@@ -1,5 +1,5 @@
{
- "chat.chatting_with": "Chat with",
+ "chat.chatting_with": "Чат з",
"chat.placeholder": "Надрукуйте повідомлення і натисніть Enter для відправки",
"chat.send": "Надіслати",
"chat.no_active": "У вас немає активних чатів.",
@@ -12,7 +12,7 @@
"chat.recent-chats": "Нещодавні чати",
"chat.contacts": "Контакти",
"chat.message-history": "Історія повідомлень",
- "chat.options": "Chat options",
+ "chat.options": "Параметри чату",
"chat.pop-out": "Залишити розмову",
"chat.minimize": "Згорнути",
"chat.maximize": "Розгорнути",
@@ -21,17 +21,17 @@
"chat.three_months": "3 місяці",
"chat.delete_message_confirm": "Ви впевнені, що хочете видалити це повідомлення?",
"chat.add-users-to-room": "Додати користувачів до кімнати",
- "chat.retrieving-users": "Retrieving users...",
- "chat.manage-room": "Manage Chat Room",
- "chat.add-user-help": "Search for users here. When selected, the user will be added to the chat. The new user will not be able to see chat messages written before they were added to the conversation.",
+ "chat.retrieving-users": "Отримання користувачів...",
+ "chat.manage-room": "Управління чат кімнатами",
+ "chat.add-user-help": "Шукайте користувачів тут. Коли вибрано, користувач буде доданий до чату. Новий користувач не зможе переглядати повідомлення чату, перш ніж вони будуть додані до бесіди.",
"chat.confirm-chat-with-dnd-user": "Користувач змінив свій статус на DnD (Не турбувати). Ви дійсно бажаєте надіслати йому повідомлення в чат?",
- "chat.rename-room": "Rename room",
- "chat.rename-placeholder": "Enter your room name here",
- "chat.rename-help": "The room name set here will be viewable by all participants in the room.",
- "chat.leave": "Leave Chat",
- "chat.leave-prompt": "Are you sure you wish to leave this chat?",
- "chat.leave-help": "Leaving this chat will remove you from future correspondence in this chat. If you are re-added in the future, you will not see any chat history from prior to your re-joining.",
- "chat.in-room": "In this room",
+ "chat.rename-room": "Перейменувати кімнату",
+ "chat.rename-placeholder": "Введіть назву своєї кімнати тут",
+ "chat.rename-help": "Назва кімнати, яку буде встановлено тут, буде доступна для перегляду всіма учасниками в кімнаті.",
+ "chat.leave": "Залишити чат",
+ "chat.leave-prompt": "Ви впевнені, що хочете залишити цей чат?",
+ "chat.leave-help": "Залишивши цей чат, ви видалите вас із майбутньої кореспонденції у цьому чаті. Якщо ви знову будете додані в майбутньому, ви не побачите жодної історії чату перед тим, як знову приєднатися.",
+ "chat.in-room": "У цій кімнаті",
"composer.compose": "Редактор повідомлень",
"composer.show_preview": "Показати попередній перегляд",
"composer.hide_preview": "Сховати попередній перегляд",
diff --git a/public/language/uk/notifications.json b/public/language/uk/notifications.json
index e7b2f9099b..89b6e576c3 100644
--- a/public/language/uk/notifications.json
+++ b/public/language/uk/notifications.json
@@ -9,7 +9,7 @@
"continue_to": "Перейти до %1",
"return_to": "Повернутись до %1",
"new_notification": "Нове сповіщення",
- "new_notification_from": "You have a new Notification from %1",
+ "new_notification_from": "Ви отримали нове сповіщення від %1",
"you_have_unread_notifications": "У вас немає непрочитаних сповіщень",
"all": "Всі",
"topics": "Теми",
@@ -47,18 +47,18 @@
"email-confirmed-message": "Дякуємо за підтвердження електронної пошти. Ваш акаунт тепер повністю активовано.",
"email-confirm-error-message": "При перевірці вашої електронної пошти сталася проблема. Можливо код був недійсним або простроченим.",
"email-confirm-sent": "Підтвердження по електронній пошті було надіслано.",
- "none": "None",
- "notification_only": "Notification Only",
- "email_only": "Email Only",
- "notification_and_email": "Notification & Email",
- "notificationType_upvote": "When someone upvotes your post",
- "notificationType_new-topic": "When someone you follow posts a topic",
- "notificationType_new-reply": "When a new reply is posted in a topic you are watching",
- "notificationType_follow": "When someone starts following you",
- "notificationType_new-chat": "When you receive a chat message",
- "notificationType_group-invite": "When you receive a group invite",
- "notificationType_new-register": "When someone gets added to registration queue",
- "notificationType_post-queue": "When a new post is queued",
- "notificationType_new-post-flag": "When a post is flagged",
- "notificationType_new-user-flag": "When a user is flagged"
+ "none": "Немає",
+ "notification_only": "Тільки сповіщення",
+ "email_only": "Тільки електронну пошту ",
+ "notification_and_email": "Сповіщення та пошта",
+ "notificationType_upvote": "Коли хтось голосує за ваш пост",
+ "notificationType_new-topic": "Коли хтось, кого ви читаєте, публікує тему",
+ "notificationType_new-reply": "Коли з'являється нова відповідь у темі, за якою ви слідкуєте",
+ "notificationType_follow": "Коли хтось починає слідкувати за вами",
+ "notificationType_new-chat": "Коли ви отримуєте повідомлення чату",
+ "notificationType_group-invite": "Коли ви отримуєте запрошення до групи",
+ "notificationType_new-register": "Коли когось додано до черги на реєстрацію",
+ "notificationType_post-queue": "Коли новий пост знаходиться в черзі",
+ "notificationType_new-post-flag": "Коли повідомлення позначено",
+ "notificationType_new-user-flag": "Коли користувача позначено"
}
\ No newline at end of file
diff --git a/public/language/uk/pages.json b/public/language/uk/pages.json
index 656200c87e..c91612f352 100644
--- a/public/language/uk/pages.json
+++ b/public/language/uk/pages.json
@@ -6,7 +6,7 @@
"popular-month": "Популярні теми цього місяця",
"popular-alltime": "Популярні теми за весь час",
"recent": "Свіжі теми",
- "top": "Top Voted Topics",
+ "top": "Найпопулярніші теми",
"moderator-tools": "Інструменти Модератора",
"flagged-content": "Оскаржений вміст",
"ip-blacklist": "Чорний список IP адрес",
@@ -20,7 +20,7 @@
"users/search": "Пошук користувача",
"notifications": "Сповіщення",
"tags": "Теги",
- "tag": "Topics tagged under "%1"",
+ "tag": "Теми, позначені нижче "%1"",
"register": "Зареєструвати акаунт",
"registration-complete": "Реєстрацію завершено",
"login": "Увійдіть в свій акаунт",
@@ -45,7 +45,7 @@
"account/bookmarks": "Закладки %1",
"account/settings": "Налаштування користувача",
"account/watched": "Теми за якими стежить %1",
- "account/ignored": "Topics ignored by %1",
+ "account/ignored": "Теми, які ігноруються",
"account/upvoted": "Пости за які проголосував %1",
"account/downvoted": "Пости проти яких проголосував %1",
"account/best": "Найкращі пости %1",
diff --git a/public/language/uk/topic.json b/public/language/uk/topic.json
index eb0d466a6f..2a5c67279e 100644
--- a/public/language/uk/topic.json
+++ b/public/language/uk/topic.json
@@ -30,12 +30,12 @@
"locked": "Заблокована",
"pinned": "Закріплена",
"moved": "Переміщена",
- "copy-ip": "Copy IP",
- "ban-ip": "Ban IP",
- "view-history": "Edit History",
+ "copy-ip": "Копіювати IP",
+ "ban-ip": "Заблокувати IP",
+ "view-history": "Редагувати історію",
"bookmark_instructions": "Натисніть тут, щоб повернутися до останнього прочитаного посту у цій темі.",
"flag_title": "Поскаржитись на цей пост модератору",
- "merged_message": "This topic has been merged into %2",
+ "merged_message": "Ця тема була об'єднана в %2",
"deleted_message": "Цю тему було видалено. Лише користувачі з правом керування темами можуть її бачити.",
"following_topic.message": "Відтепер ви будете отримувати сповіщення коли хтось постить в цю тему.",
"not_following_topic.message": "Ви будете бачити цю тему в списку непрочитаних, але ви не будете отримувати сповіщень, коли хтось постить до неї.",
@@ -56,7 +56,7 @@
"not-watching.description": "Не сповіщати мене про нові відповіді. Показувати тему в непрочитаних якщо категорія не ігнорується.",
"ignoring.description": "Не сповіщати мене про нові відповіді. Не показувати тему в непрочитаних.",
"thread_tools.title": "Інструменти теми",
- "thread_tools.markAsUnreadForAll": "Mark Unread For All",
+ "thread_tools.markAsUnreadForAll": "Відмітити для всіх як непрочитана.",
"thread_tools.pin": "Прикріпити тему",
"thread_tools.unpin": "Відкріпити тему",
"thread_tools.lock": "Заблокувати тему",
@@ -72,8 +72,8 @@
"thread_tools.restore_confirm": "Ви точно бажаєте відновити цю тему?",
"thread_tools.purge": "Стерти тему",
"thread_tools.purge_confirm": "Ви точно бажаєте стерти цю тему?",
- "thread_tools.merge_topics": "Merge Topics",
- "thread_tools.merge": "Merge",
+ "thread_tools.merge_topics": "Об'єднати теми",
+ "thread_tools.merge": "Об'єднати",
"topic_move_success": "Тема була успішно переміщена до %1",
"post_delete_confirm": "Ви точно бажаєте видалити цей пост?",
"post_restore_confirm": "Ви точно бажаєте відновити цей пост?",
@@ -95,7 +95,7 @@
"fork_pid_count": "вибрано %1 пост(ів) ",
"fork_success": "Тему успішно відгалужено. Тисніть тут, щоб перейти до відгалуженої теми.",
"delete_posts_instruction": "Тисніть пости які ви бажаєте видалити/стерти",
- "merge_topics_instruction": "Click the topics you want to merge",
+ "merge_topics_instruction": "Натисніть на теми, які потрібно об'єднати",
"composer.title_placeholder": "Уведіть заголовок теми...",
"composer.handle_placeholder": "Ім'я",
"composer.discard": "Скасувати",
@@ -123,7 +123,7 @@
"stale.create": "Так, створити нову тему",
"stale.reply_anyway": "Ні, відповісти все ж на існуючу",
"link_back": "Re: [%1](%2)",
- "diffs.title": "Post Edit History",
- "diffs.description": "This post has %1 revisions. Click one of the revisions below to see the post content at that point in time.",
- "diffs.no-revisions-description": "This post has %1 revisions."
+ "diffs.title": "Історія редагування посту",
+ "diffs.description": "Цей пост має %1 версій. Натисніть одну з наведених нижче змін, щоб переглянути вміст публікації в той момент часу.",
+ "diffs.no-revisions-description": "Цей пост має %1 версій."
}
\ No newline at end of file
diff --git a/public/language/uk/unread.json b/public/language/uk/unread.json
index 8cd06c70a5..bd4079565b 100644
--- a/public/language/uk/unread.json
+++ b/public/language/uk/unread.json
@@ -10,6 +10,6 @@
"all-topics": "Всі теми",
"new-topics": "Нові теми",
"watched-topics": "Переглянуті теми",
- "unreplied-topics": "Unreplied Topics",
- "multiple-categories-selected": "Multiple Selected"
+ "unreplied-topics": "Теми без відповіді",
+ "multiple-categories-selected": "Мультивибір"
}
\ No newline at end of file
diff --git a/public/language/uk/user.json b/public/language/uk/user.json
index cda930773e..e4f2590039 100644
--- a/public/language/uk/user.json
+++ b/public/language/uk/user.json
@@ -1,7 +1,7 @@
{
- "banned": "Заборонений",
+ "banned": "Заблокований",
"offline": "Не в мережі",
- "deleted": "Deleted",
+ "deleted": "Видалено",
"username": "Ім'я користувача",
"joindate": "Дата вступу",
"postcount": "Кількість постів",
@@ -95,18 +95,18 @@
"paginate_description": "Розбивати список тем та постів на сторінки замість нескінченної прокрутки",
"topics_per_page": "Тем на сторінку",
"posts_per_page": "Постів на сторінку",
- "max_items_per_page": "Maximum %1",
- "acp_language": "Admin Page Language",
+ "max_items_per_page": "Максимум %1",
+ "acp_language": "Мова сторінки адміністратора",
"notification_sounds": "Програвати звук, коли ви отримуєте сповіщення",
"notifications_and_sounds": "Сповіщення та звуки",
"incoming-message-sound": "Звук вхідного повідомлення",
"outgoing-message-sound": "Звук вихідного повідомлення",
"notification-sound": "Звук сповіщення",
"no-sound": "Без звуку",
- "upvote-notif-freq": "Upvote Notification Frequency",
- "upvote-notif-freq.all": "All Upvotes",
- "upvote-notif-freq.everyTen": "Every Ten Upvotes",
- "upvote-notif-freq.logarithmic": "On 10, 100, 1000...",
+ "upvote-notif-freq": "Частота сповіщень позитивних відгуків",
+ "upvote-notif-freq.all": "Всі позитивні відгуки",
+ "upvote-notif-freq.everyTen": "Кожні 10 позитивних відгуків",
+ "upvote-notif-freq.logarithmic": "На 10, 100, 1000...",
"upvote-notif-freq.disabled": "Вимкнено",
"browsing": "Налаштування перегляду",
"open_links_in_new_tab": "Відкривати зовнішні посилання у новій вкладці",
@@ -130,7 +130,7 @@
"sso.not-associated": "Натисніть тут, щоб зв'язати з",
"sso.dissociate": "Від'єднати",
"sso.dissociate-confirm-title": "Підтвердьте від'єднання",
- "sso.dissociate-confirm": "Ви впевнені, що хочете відв'єднати свій обліковий запис від %1?",
+ "sso.dissociate-confirm": "Ви впевнені, що хочете від'єднати свій акаунт від %1?",
"info.latest-flags": "Останні скарги",
"info.no-flags": "Не знайдено постів зі скаргами",
"info.ban-history": "Історія банів",
diff --git a/public/language/vi/admin/settings/post.json b/public/language/vi/admin/settings/post.json
index 6a1ab9a2a5..6594fe384b 100644
--- a/public/language/vi/admin/settings/post.json
+++ b/public/language/vi/admin/settings/post.json
@@ -49,5 +49,6 @@
"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
"composer.custom-help": "Custom Help Text",
"ip-tracking": "IP Tracking",
- "ip-tracking.each-post": "Track IP Address for each post"
+ "ip-tracking.each-post": "Track IP Address for each post",
+ "enable-post-history": "Enable Post History"
}
\ No newline at end of file
diff --git a/public/language/zh-CN/admin/settings/post.json b/public/language/zh-CN/admin/settings/post.json
index 02761f9ad7..290fb1d077 100644
--- a/public/language/zh-CN/admin/settings/post.json
+++ b/public/language/zh-CN/admin/settings/post.json
@@ -49,5 +49,6 @@
"composer.enable-plugin-help": "允许插件将内容添加到帮助选项卡",
"composer.custom-help": "自定义帮助文本",
"ip-tracking": "IP 跟踪",
- "ip-tracking.each-post": "跟踪每个帖子的 IP 地址"
+ "ip-tracking.each-post": "跟踪每个帖子的 IP 地址",
+ "enable-post-history": "Enable Post History"
}
\ No newline at end of file
diff --git a/public/language/zh-TW/admin/settings/post.json b/public/language/zh-TW/admin/settings/post.json
index 6a1ab9a2a5..6594fe384b 100644
--- a/public/language/zh-TW/admin/settings/post.json
+++ b/public/language/zh-TW/admin/settings/post.json
@@ -49,5 +49,6 @@
"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
"composer.custom-help": "Custom Help Text",
"ip-tracking": "IP Tracking",
- "ip-tracking.each-post": "Track IP Address for each post"
+ "ip-tracking.each-post": "Track IP Address for each post",
+ "enable-post-history": "Enable Post History"
}
\ No newline at end of file
diff --git a/public/less/admin/header.less b/public/less/admin/header.less
index 7dd9540c38..76c49602c8 100644
--- a/public/less/admin/header.less
+++ b/public/less/admin/header.less
@@ -16,10 +16,31 @@
font-weight: 300;
}
- #user_label {
- position: absolute;
- right: 30px;
- bottom: 125px;
+ .quick-actions {
+ position: static;
+ padding: 15px;
+ display: flex;
+ flex-direction: row-reverse;
+ margin: 0;
+
+ > * {
+ margin-right: 20px;
+ }
+
+ > .menu-button {
+ margin-right: 0;
+ padding: 0 5px;
+ }
+
+ .alert {
+ font-size: 14px;
+ margin-bottom: 0;
+
+ &.alert-info {
+ background-color: #eee;
+ color: #333;
+ }
+ }
.dropdown {
margin-right: 0px;
@@ -29,7 +50,7 @@
}
}
- .fa-home {
+ .fa {
margin-top: 12px;
font-size: 25px;
}
@@ -46,9 +67,6 @@
}
#acp-search {
- margin-top: 2px;
- margin-right: 20px;
-
input {
padding: 10px 20px;
width: 250px;
@@ -96,9 +114,6 @@
}
.reconnect-spinner {
- left: auto;
- right: 380px;
- bottom: initial;
- top: 14px;
+ line-height: 44px;
}
}
diff --git a/public/less/admin/mobile.less b/public/less/admin/mobile.less
index c192351885..021145b1ae 100644
--- a/public/less/admin/mobile.less
+++ b/public/less/admin/mobile.less
@@ -45,7 +45,6 @@
width: 22px;
background: none;
border: none;
- vertical-align: 10%;
margin-right: 10px;
margin-left: -5px;
outline: none !important;
@@ -86,6 +85,23 @@
.menu-section {
margin: 25px 0;
+
+ &.quick-actions {
+ margin: 0;
+
+ .button-group {
+ display: flex;
+ justify-content: center;
+ }
+
+ .alert {
+ border-radius: 0;
+
+ .span {
+ display: block;
+ }
+ }
+ }
}
.menu-section-title {
@@ -99,9 +115,9 @@
}
.menu-section-list {
- padding:0;
+ padding: 0;
margin: 10px 0;
- list-style:none;
+ list-style: none;
a {
display: block;
diff --git a/public/src/admin/general/dashboard.js b/public/src/admin/general/dashboard.js
index 6d144d5338..b071e6412f 100644
--- a/public/src/admin/general/dashboard.js
+++ b/public/src/admin/general/dashboard.js
@@ -8,7 +8,6 @@ define('admin/general/dashboard', ['semver', 'Chart', 'translator', 'benchpress'
graphs: false,
};
var isMobile = false;
- var isPrerelease = /^v?\d+\.\d+\.\d+-.+$/;
var graphData = {
rooms: {},
traffic: {},
@@ -42,42 +41,6 @@ define('admin/general/dashboard', ['semver', 'Chart', 'translator', 'benchpress'
isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
- $.get('https://api.github.com/repos/NodeBB/NodeBB/tags', function (releases) {
- // Re-sort the releases, as they do not follow Semver (wrt pre-releases)
- releases = releases.sort(function (a, b) {
- a = a.name.replace(/^v/, '');
- b = b.name.replace(/^v/, '');
- return semver.lt(a, b) ? 1 : -1;
- }).filter(function (version) {
- return !isPrerelease.test(version.name); // filter out automated prerelease versions
- });
-
- var version = $('#version').html();
- var latestVersion = releases[0].name.slice(1);
- var checkEl = $('.version-check');
- var text;
-
- // Alter box colour accordingly
- if (semver.eq(latestVersion, version)) {
- checkEl.removeClass('alert-info').addClass('alert-success');
- text = '[[admin/general/dashboard:up-to-date]]';
- } else if (semver.gt(latestVersion, version)) {
- checkEl.removeClass('alert-info').addClass('alert-warning');
- if (!isPrerelease.test(version)) {
- text = '[[admin/general/dashboard:upgrade-available, ' + latestVersion + ']]';
- } else {
- text = '[[admin/general/dashboard:prerelease-upgrade-available, ' + latestVersion + ']]';
- }
- } else if (isPrerelease.test(version)) {
- checkEl.removeClass('alert-info').addClass('alert-info');
- text = '[[admin/general/dashboard:prerelease-warning]]';
- }
-
- translator.translate(text, function (text) {
- checkEl.append(text);
- });
- });
-
$('[data-toggle="tooltip"]').tooltip();
setupRealtimeButton();
diff --git a/public/src/client/topic/diffs.js b/public/src/client/topic/diffs.js
index 63dd2b7b28..3d57131624 100644
--- a/public/src/client/topic/diffs.js
+++ b/public/src/client/topic/diffs.js
@@ -1,6 +1,6 @@
'use strict';
-define('forum/topic/diffs', ['benchpress', 'translator'], function (Benchpress, translator) {
+define('forum/topic/diffs', ['forum/topic/images', 'benchpress', 'translator'], function (Images, Benchpress, translator) {
var Diffs = {};
Diffs.open = function (pid) {
@@ -13,6 +13,8 @@ define('forum/topic/diffs', ['benchpress', 'translator'], function (Benchpress,
Benchpress.parse('partials/modals/post_history', {
diffs: timestamps.map(function (timestamp) {
+ timestamp = parseInt(timestamp, 10);
+
return {
timestamp: timestamp,
pretty: new Date(timestamp).toLocaleString(config.userLang.replace('_', '-'), localeStringOpts),
@@ -24,6 +26,7 @@ define('forum/topic/diffs', ['benchpress', 'translator'], function (Benchpress,
var modal = bootbox.dialog({
title: '[[topic:diffs.title]]',
message: html,
+ size: 'large',
});
if (!timestamps.length) {
@@ -57,6 +60,8 @@ define('forum/topic/diffs', ['benchpress', 'translator'], function (Benchpress,
posts: [data],
}, function (html) {
postContainer.empty().append(html);
+ Images.unloadImages(html);
+ Images.loadImages();
});
});
};
diff --git a/public/src/client/topic/posts.js b/public/src/client/topic/posts.js
index b44d571919..9c4d698b01 100644
--- a/public/src/client/topic/posts.js
+++ b/public/src/client/topic/posts.js
@@ -219,7 +219,7 @@ define('forum/topic/posts', [
Posts._infiniteScrollTimeout = setTimeout(function () {
delete Posts._infiniteScrollTimeout;
}, 1000);
- var replies = components.get('post').not('[data-index=0]').not('.new');
+ var replies = components.get('topic').find(components.get('post').not('[data-index=0]').not('.new'));
var afterEl = direction > 0 ? replies.last() : replies.first();
var after = parseInt(afterEl.attr('data-index'), 10) || 0;
diff --git a/public/src/modules/chat.js b/public/src/modules/chat.js
index 5791a7828f..9e5dc016ba 100644
--- a/public/src/modules/chat.js
+++ b/public/src/modules/chat.js
@@ -253,6 +253,7 @@ define('chat', [
Chats.addRenameHandler(chatModal.attr('data-roomid'), chatModal.find('[data-action="rename"]'), chatModal.attr('data-name'));
Chats.addLeaveHandler(chatModal.attr('data-roomid'), chatModal.find('[data-action="leave"]'));
Chats.addSendHandlers(chatModal.attr('data-roomid'), chatModal.find('.chat-input'), chatModal.find('[data-action="send"]'));
+ Chats.addMemberHandler(chatModal.attr('data-roomid'), chatModal.find('[data-action="members"]'));
Chats.createAutoComplete(chatModal.find('[component="chat/input"]'));
diff --git a/src/admin/versions.js b/src/admin/versions.js
new file mode 100644
index 0000000000..bc29002f94
--- /dev/null
+++ b/src/admin/versions.js
@@ -0,0 +1,55 @@
+'use strict';
+
+var semver = require('semver');
+var request = require('request');
+
+var meta = require('../meta');
+
+var versionCache = '';
+var versionCacheLastModified = '';
+
+var isPrerelease = /^v?\d+\.\d+\.\d+-.+$/;
+
+function getLatestVersion(callback) {
+ var headers = {
+ Accept: 'application/vnd.github.v3+json',
+ 'User-Agent': 'NodeBB Admin Control Panel/' + meta.config.title,
+ };
+
+ if (versionCacheLastModified) {
+ headers['If-Modified-Since'] = versionCacheLastModified;
+ }
+
+ request('https://api.github.com/repos/NodeBB/NodeBB/tags', {
+ json: true,
+ headers: headers,
+ }, function (err, res, releases) {
+ if (err) {
+ return callback(err);
+ }
+
+ if (res.statusCode === 304) {
+ return callback(null, versionCache);
+ }
+
+ if (res.statusCode !== 200) {
+ return callback(Error(res.statusMessage));
+ }
+
+ releases = releases.filter(function (version) {
+ return !isPrerelease.test(version.name); // filter out automated prerelease versions
+ }).map(function (version) {
+ return version.name.replace(/^v/, '');
+ }).sort(function (a, b) {
+ return semver.lt(a, b) ? 1 : -1;
+ });
+
+ versionCache = releases[0];
+ versionCacheLastModified = res.headers['last-modified'];
+
+ callback(null, versionCache);
+ });
+}
+
+exports.getLatestVersion = getLatestVersion;
+exports.isPrerelease = isPrerelease;
diff --git a/src/categories/delete.js b/src/categories/delete.js
index 87ae9813d9..4e3a7060ce 100644
--- a/src/categories/delete.js
+++ b/src/categories/delete.js
@@ -58,9 +58,9 @@ module.exports = function (Categories) {
], next);
},
function (next) {
- async.eachSeries(privileges.privilegeList, function (privilege, next) {
- groups.destroy('cid:' + cid + ':privileges:' + privilege, next);
- }, next);
+ groups.destroy(privileges.privilegeList.map(function (privilege) {
+ return 'cid:' + cid + ':privileges:' + privilege;
+ }), next);
},
], function (err) {
callback(err);
diff --git a/src/controllers/admin/dashboard.js b/src/controllers/admin/dashboard.js
index b5ae760727..6d804faf47 100644
--- a/src/controllers/admin/dashboard.js
+++ b/src/controllers/admin/dashboard.js
@@ -2,7 +2,10 @@
var async = require('async');
var nconf = require('nconf');
+var semver = require('semver');
+var winston = require('winston');
+var versions = require('../../admin/versions');
var db = require('../../database');
var meta = require('../../meta');
var plugins = require('../../plugins');
@@ -13,9 +16,7 @@ dashboardController.get = function (req, res, next) {
async.waterfall([
function (next) {
async.parallel({
- stats: function (next) {
- getStats(next);
- },
+ stats: getStats,
notices: function (next) {
var notices = [
{
@@ -41,11 +42,26 @@ dashboardController.get = function (req, res, next) {
plugins.fireHook('filter:admin.notices', notices, next);
},
+ latestVersion: function (next) {
+ versions.getLatestVersion(function (err, result) {
+ if (err) {
+ winston.error('[acp] Failed to fetch latest version', err);
+ }
+
+ next(null, err ? null : result);
+ });
+ },
}, next);
},
function (results) {
+ var version = nconf.get('version');
+
res.render('admin/general/dashboard', {
- version: nconf.get('version'),
+ version: version,
+ lookupFailed: results.latestVersion === null,
+ latestVersion: results.latestVersion,
+ upgradeAvailable: results.latestVersion && semver.gt(results.latestVersion, version),
+ currentPrerelease: versions.isPrerelease.test(version),
notices: results.notices,
stats: results.stats,
canRestart: !!process.send,
diff --git a/src/controllers/uploads.js b/src/controllers/uploads.js
index 4417513612..3d225c22fe 100644
--- a/src/controllers/uploads.js
+++ b/src/controllers/uploads.js
@@ -118,6 +118,7 @@ function resizeImage(fileObj, callback) {
target: path.join(dirname, basename + '-resized' + extname),
extension: extname,
width: parseInt(meta.config.maximumImageWidth, 10) || 760,
+ quality: parseInt(meta.config.resizeImageQuality, 10) || 60,
}, next);
},
function (next) {
diff --git a/src/database/mongo/hash.js b/src/database/mongo/hash.js
index 5eca32fd4e..55ae9c7ea8 100644
--- a/src/database/mongo/hash.js
+++ b/src/database/mongo/hash.js
@@ -1,5 +1,6 @@
'use strict';
+var async = require('async');
var pubsub = require('../../pubsub');
module.exports = function (db, module) {
@@ -286,6 +287,36 @@ module.exports = function (db, module) {
field = helpers.fieldToString(field);
data[field] = value;
+ if (Array.isArray(key)) {
+ var bulk = db.collection('objects').initializeUnorderedBulkOp();
+ key.forEach(function (key) {
+ bulk.find({ _key: key }).upsert().update({ $inc: data });
+ });
+
+ async.waterfall([
+ function (next) {
+ bulk.execute(function (err) {
+ next(err);
+ });
+ },
+ function (next) {
+ key.forEach(function (key) {
+ module.delObjectCache(key);
+ });
+
+ module.getObjectsFields(key, [field], next);
+ },
+ function (data, next) {
+ data = data.map(function (data) {
+ return data && data[field];
+ });
+ next(null, data);
+ },
+ ], callback);
+ return;
+ }
+
+
db.collection('objects').findAndModify({ _key: key }, {}, { $inc: data }, { new: true, upsert: true }, function (err, result) {
if (err) {
return callback(err);
diff --git a/src/database/mongo/list.js b/src/database/mongo/list.js
index 0c5e2955e5..06be2808a0 100644
--- a/src/database/mongo/list.js
+++ b/src/database/mongo/list.js
@@ -100,4 +100,13 @@ module.exports = function (db, module) {
callback(null, data.array);
});
};
+
+ module.listLength = function (key, callback) {
+ db.collection('objects').aggregate([
+ { $match: { _key: key } },
+ { $project: { count: { $size: '$array' } } },
+ ], function (err, result) {
+ callback(err, Array.isArray(result) && result.length && result[0].count);
+ });
+ };
};
diff --git a/src/database/mongo/sets.js b/src/database/mongo/sets.js
index 6d6abbfbab..3315922b4a 100644
--- a/src/database/mongo/sets.js
+++ b/src/database/mongo/sets.js
@@ -47,7 +47,7 @@ module.exports = function (db, module) {
var bulk = db.collection('objects').initializeUnorderedBulkOp();
for (var i = 0; i < keys.length; i += 1) {
- bulk.find({ _key: keys[i] }).upsert().updateOne({ $addToSet: {
+ bulk.find({ _key: keys[i] }).upsert().updateOne({ $addToSet: {
members: {
$each: value,
},
@@ -69,9 +69,15 @@ module.exports = function (db, module) {
array[index] = helpers.valueToString(element);
});
- db.collection('objects').update({ _key: key }, { $pullAll: { members: value } }, function (err) {
- callback(err);
- });
+ if (Array.isArray(key)) {
+ db.collection('objects').updateMany({ _key: { $in: key } }, { $pullAll: { members: value } }, function (err) {
+ callback(err);
+ });
+ } else {
+ db.collection('objects').update({ _key: key }, { $pullAll: { members: value } }, function (err) {
+ callback(err);
+ });
+ }
};
module.setsRemove = function (keys, value, callback) {
@@ -81,15 +87,7 @@ module.exports = function (db, module) {
}
value = helpers.valueToString(value);
- var bulk = db.collection('objects').initializeUnorderedBulkOp();
-
- for (var i = 0; i < keys.length; i += 1) {
- bulk.find({ _key: keys[i] }).updateOne({ $pull: {
- members: value,
- } });
- }
-
- bulk.execute(function (err) {
+ db.collection('objects').update({ _key: { $in: keys } }, { $pull: { members: value } }, { multi: true }, function (err) {
callback(err);
});
};
diff --git a/src/database/mongo/sorted.js b/src/database/mongo/sorted.js
index ca1d53f63b..58eddb940e 100644
--- a/src/database/mongo/sorted.js
+++ b/src/database/mongo/sorted.js
@@ -384,7 +384,7 @@ module.exports = function (db, module) {
if (!Array.isArray(keys) || !keys.length) {
return callback(null, []);
}
- db.collection('objects').find({ _key: { $in: keys } }, { _id: 0, _key: 1, value: 1 }).toArray(function (err, data) {
+ db.collection('objects').find({ _key: { $in: keys } }, { _id: 0, _key: 1, value: 1 }).sort({ score: 1 }).toArray(function (err, data) {
if (err) {
return callback(err);
}
diff --git a/src/database/mongo/sorted/remove.js b/src/database/mongo/sorted/remove.js
index 0a3fd87b6e..c9bf121e10 100644
--- a/src/database/mongo/sorted/remove.js
+++ b/src/database/mongo/sorted/remove.js
@@ -11,10 +11,14 @@ module.exports = function (db, module) {
if (!key) {
return callback();
}
-
- if (Array.isArray(value)) {
+ if (Array.isArray(key) && Array.isArray(value)) {
+ db.collection('objects').remove({ _key: { $in: key }, value: { $in: value } }, done);
+ } else if (Array.isArray(value)) {
value = value.map(helpers.valueToString);
db.collection('objects').remove({ _key: key, value: { $in: value } }, done);
+ } else if (Array.isArray(key)) {
+ value = helpers.valueToString(value);
+ db.collection('objects').remove({ _key: { $in: key }, value: value }, done);
} else {
value = helpers.valueToString(value);
db.collection('objects').remove({ _key: key, value: value }, done);
diff --git a/src/database/redis.js b/src/database/redis.js
index e88663cf70..d5b03ec967 100644
--- a/src/database/redis.js
+++ b/src/database/redis.js
@@ -73,7 +73,7 @@ redisModule.connect = function (options, callback) {
callback = callback || function () {};
var redis_socket_or_host = nconf.get('redis:host');
var cxn;
-
+ var callbackCalled = false;
options = options || {};
if (nconf.get('redis:password')) {
@@ -92,11 +92,17 @@ redisModule.connect = function (options, callback) {
cxn.on('error', function (err) {
winston.error(err.stack);
- callback(err);
+ if (!callbackCalled) {
+ callbackCalled = true;
+ callback(err);
+ }
});
cxn.on('ready', function () {
- callback();
+ if (!callbackCalled) {
+ callbackCalled = true;
+ callback();
+ }
});
if (nconf.get('redis:password')) {
diff --git a/src/database/redis/hash.js b/src/database/redis/hash.js
index 6f3c799027..9dd6276f88 100644
--- a/src/database/redis/hash.js
+++ b/src/database/redis/hash.js
@@ -129,6 +129,14 @@ module.exports = function (redisClient, module) {
if (!key || isNaN(value)) {
return callback(null, null);
}
- redisClient.hincrby(key, field, value, callback);
+ if (Array.isArray(key)) {
+ var multi = redisClient.multi();
+ key.forEach(function (key) {
+ multi.hincrby(key, field, value);
+ });
+ multi.exec(callback);
+ } else {
+ redisClient.hincrby(key, field, value, callback);
+ }
};
};
diff --git a/src/database/redis/list.js b/src/database/redis/list.js
index f8108a194d..ba127d3100 100644
--- a/src/database/redis/list.js
+++ b/src/database/redis/list.js
@@ -56,4 +56,8 @@ module.exports = function (redisClient, module) {
}
redisClient.lrange(key, start, stop, callback);
};
+
+ module.listLength = function (key, callback) {
+ redisClient.llen(key, callback);
+ };
};
diff --git a/src/database/redis/sets.js b/src/database/redis/sets.js
index a5716438e4..1da6f4ff22 100644
--- a/src/database/redis/sets.js
+++ b/src/database/redis/sets.js
@@ -25,7 +25,17 @@ module.exports = function (redisClient, module) {
module.setRemove = function (key, value, callback) {
callback = callback || function () {};
- redisClient.srem(key, value, function (err) {
+ if (!Array.isArray(value)) {
+ value = [value];
+ }
+ if (!Array.isArray(key)) {
+ key = [key];
+ }
+ var multi = redisClient.multi();
+ key.forEach(function (key) {
+ multi.srem(key, value);
+ });
+ multi.exec(function (err) {
callback(err);
});
};
diff --git a/src/database/redis/sorted/remove.js b/src/database/redis/sorted/remove.js
index eacb6ca861..886bd7cdb9 100644
--- a/src/database/redis/sorted/remove.js
+++ b/src/database/redis/sorted/remove.js
@@ -13,9 +13,19 @@ module.exports = function (redisClient, module) {
value = [value];
}
- helpers.multiKeyValues(redisClient, 'zrem', key, value, function (err) {
- callback(err);
- });
+ if (Array.isArray(key)) {
+ var multi = redisClient.multi();
+ key.forEach(function (key) {
+ multi.zrem(key, value);
+ });
+ multi.exec(function (err) {
+ callback(err);
+ });
+ } else {
+ helpers.multiKeyValues(redisClient, 'zrem', key, value, function (err) {
+ callback(err);
+ });
+ }
};
module.sortedSetsRemove = function (keys, value, callback) {
diff --git a/src/emailer.js b/src/emailer.js
index a841eb1f91..db49bdf0f8 100644
--- a/src/emailer.js
+++ b/src/emailer.js
@@ -33,9 +33,9 @@ Emailer.transports = {
var app;
var viewsDir = nconf.get('views_dir');
-var emailsPath = path.join(viewsDir, 'emails');
Emailer.getTemplates = function (config, cb) {
+ var emailsPath = path.join(viewsDir, 'emails');
async.waterfall([
function (next) {
file.walk(emailsPath, next);
@@ -229,7 +229,7 @@ Emailer.sendToEmail = function (template, email, language, params, callback) {
to: email,
from: meta.config['email:from'] || 'no-reply@' + getHostname(),
from_name: meta.config['email:from_name'] || 'NodeBB',
- subject: results.subject,
+ subject: '[' + meta.config.title + '] ' + results.subject,
html: results.html,
plaintext: htmlToText.fromString(results.html, {
ignoreImage: true,
diff --git a/src/flags.js b/src/flags.js
index dbc301d776..3636f42e06 100644
--- a/src/flags.js
+++ b/src/flags.js
@@ -93,7 +93,7 @@ Flags.get = function (flagId, callback) {
// Final object return construction
next(err, Object.assign(data.base, {
description: validator.escape(data.base.description),
- datetimeISO: new Date(parseInt(data.base.datetime, 10)).toISOString(),
+ datetimeISO: utils.toISOString(data.base.datetime),
target_readable: data.base.type.charAt(0).toUpperCase() + data.base.type.slice(1) + ' ' + data.base.targetId,
target: payload.targetObj,
history: data.history,
@@ -203,7 +203,7 @@ Flags.list = function (filters, uid, callback) {
next(null, Object.assign(flagObj, {
description: validator.escape(String(flagObj.description)),
target_readable: flagObj.type.charAt(0).toUpperCase() + flagObj.type.slice(1) + ' ' + flagObj.targetId,
- datetimeISO: new Date(parseInt(flagObj.datetime, 10)).toISOString(),
+ datetimeISO: utils.toISOString(flagObj.datetime),
}));
});
}, next);
@@ -288,7 +288,7 @@ Flags.getNotes = function (flagId, callback) {
uid: noteObj[0],
content: noteObj[1],
datetime: note.score,
- datetimeISO: new Date(parseInt(note.score, 10)).toISOString(),
+ datetimeISO: utils.toISOString(note.score),
};
} catch (e) {
return next(e);
@@ -572,7 +572,7 @@ Flags.getHistory = function (flagId, callback) {
uid: entry.value[0],
fields: changeset,
datetime: entry.score,
- datetimeISO: new Date(parseInt(entry.score, 10)).toISOString(),
+ datetimeISO: utils.toISOString(entry.score),
};
});
diff --git a/src/groups/data.js b/src/groups/data.js
index 919a257368..f791b1f5f1 100644
--- a/src/groups/data.js
+++ b/src/groups/data.js
@@ -2,6 +2,7 @@
var async = require('async');
var validator = require('validator');
+var winston = require('winston');
var db = require('../database');
var plugins = require('../plugins');
@@ -68,17 +69,22 @@ module.exports = function (Groups) {
};
Groups.getGroupFields = function (groupName, fields, callback) {
- Groups.getMultipleGroupFields([groupName], fields, function (err, groups) {
+ Groups.getGroupsFields([groupName], fields, function (err, groups) {
callback(err, groups ? groups[0] : null);
});
};
- Groups.getMultipleGroupFields = function (groups, fields, callback) {
- db.getObjectsFields(groups.map(function (group) {
+ Groups.getGroupsFields = function (groupNames, fields, callback) {
+ db.getObjectsFields(groupNames.map(function (group) {
return 'group:' + group;
}), fields, callback);
};
+ Groups.getMultipleGroupFields = function (groups, fields, callback) {
+ winston.warn('[deprecated] Groups.getMultipleGroupFields is deprecated please use Groups.getGroupsFields');
+ Groups.getGroupsFields(groups, fields, callback);
+ };
+
Groups.setGroupField = function (groupName, field, value, callback) {
async.waterfall([
function (next) {
diff --git a/src/groups/delete.js b/src/groups/delete.js
index b0203aa60e..ed55a1f383 100644
--- a/src/groups/delete.js
+++ b/src/groups/delete.js
@@ -7,44 +7,61 @@ var db = require('./../database');
var batch = require('../batch');
module.exports = function (Groups) {
- Groups.destroy = function (groupName, callback) {
+ Groups.destroy = function (groupNames, callback) {
+ if (!Array.isArray(groupNames)) {
+ groupNames = [groupNames];
+ }
+
var groupObj;
+ var groupsData;
async.waterfall([
function (next) {
- Groups.getGroupsData([groupName], next);
+ Groups.getGroupsData(groupNames, next);
},
- function (groupsData, next) {
- if (!groupsData[0]) {
+ function (_groupsData, next) {
+ groupsData = _groupsData.filter(Boolean);
+ if (!groupsData.length) {
return callback();
}
+ // backwards compatibility
groupObj = groupsData[0];
async.parallel([
function (next) {
- db.deleteAll([
- 'group:' + groupName,
- 'group:' + groupName + ':members',
- 'group:' + groupName + ':pending',
- 'group:' + groupName + ':invited',
- 'group:' + groupName + ':owners',
- 'group:' + groupName + ':member:pids',
- ], next);
+ var keys = [];
+ groupNames.forEach(function (groupName) {
+ keys.push('group:' + groupName,
+ 'group:' + groupName + ':members',
+ 'group:' + groupName + ':pending',
+ 'group:' + groupName + ':invited',
+ 'group:' + groupName + ':owners',
+ 'group:' + groupName + ':member:pids'
+ );
+ });
+
+ db.deleteAll(keys, next);
},
function (next) {
- db.sortedSetsRemove([
+ db.sortedSetRemove([
'groups:createtime',
'groups:visible:createtime',
'groups:visible:memberCount',
- ], groupName, next);
+ ], groupNames, next);
},
function (next) {
- db.sortedSetRemove('groups:visible:name', groupName.toLowerCase() + ':' + groupName, next);
+ var keys = groupNames.map(function (groupName) {
+ return groupName.toLowerCase() + ':' + groupName;
+ });
+ db.sortedSetRemove('groups:visible:name', keys, next);
},
function (next) {
- db.deleteObjectField('groupslug:groupname', utils.slugify(groupName), next);
+ var fields = groupNames.map(function (groupName) {
+ return utils.slugify(groupName);
+ });
+ db.deleteObjectFields('groupslug:groupname', fields, next);
},
function (next) {
- removeGroupFromOtherGroups(groupName, next);
+ removeGroupsFromOtherGroups(groupNames, next);
},
], function (err) {
next(err);
@@ -53,17 +70,18 @@ module.exports = function (Groups) {
function (next) {
Groups.resetCache();
plugins.fireHook('action:group.destroy', { group: groupObj });
+ plugins.fireHook('action:groups.destroy', { groups: groupsData });
next();
},
], callback);
};
- function removeGroupFromOtherGroups(groupName, callback) {
- batch.processSortedSet('groups:createtime', function (groupNames, next) {
- var keys = groupNames.map(function (group) {
+ function removeGroupsFromOtherGroups(groupNames, callback) {
+ batch.processSortedSet('groups:createtime', function (otherGroups, next) {
+ var keys = otherGroups.map(function (group) {
return 'group:' + group + ':members';
});
- db.sortedSetsRemove(keys, groupName, next);
+ db.sortedSetRemove(keys, groupNames, next);
}, {
batch: 500,
}, callback);
diff --git a/src/groups/membership.js b/src/groups/membership.js
index 864fdea81f..09e86e12f6 100644
--- a/src/groups/membership.js
+++ b/src/groups/membership.js
@@ -142,19 +142,21 @@ module.exports = function (Groups) {
Groups.acceptMembership = function (groupName, uid, callback) {
async.waterfall([
- async.apply(db.setRemove, 'group:' + groupName + ':pending', uid),
- async.apply(db.setRemove, 'group:' + groupName + ':invited', uid),
+ async.apply(db.setsRemove, ['group:' + groupName + ':pending', 'group:' + groupName + ':invited'], uid),
async.apply(Groups.join, groupName, uid),
], callback);
};
- Groups.rejectMembership = function (groupName, uid, callback) {
- async.parallel([
- async.apply(db.setRemove, 'group:' + groupName + ':pending', uid),
- async.apply(db.setRemove, 'group:' + groupName + ':invited', uid),
- ], function (err) {
- callback(err);
+ Groups.rejectMembership = function (groupNames, uid, callback) {
+ if (!Array.isArray(groupNames)) {
+ groupNames = [groupNames];
+ }
+ var sets = [];
+ groupNames.forEach(function (groupName) {
+ sets.push('group:' + groupName + ':pending', 'group:' + groupName + ':invited');
});
+
+ db.setsRemove(sets, uid, callback);
};
Groups.invite = function (groupName, uid, callback) {
@@ -212,52 +214,70 @@ module.exports = function (Groups) {
], callback);
}
- Groups.leave = function (groupName, uid, callback) {
+ Groups.leave = function (groupNames, uid, callback) {
callback = callback || function () {};
+ if (!Array.isArray(groupNames)) {
+ groupNames = [groupNames];
+ }
+
async.waterfall([
function (next) {
- Groups.isMember(uid, groupName, next);
+ async.parallel({
+ isMembers: async.apply(Groups.isMemberOfGroups, uid, groupNames),
+ exists: async.apply(Groups.exists, groupNames),
+ }, next);
},
- function (isMember, next) {
- if (!isMember) {
+ function (result, next) {
+ groupNames = groupNames.filter(function (groupName, index) {
+ return result.isMembers[index] && result.exists[index];
+ });
+
+ if (!groupNames.length) {
return callback();
}
- Groups.exists(groupName, next);
- },
- function (exists, next) {
- if (!exists) {
- return callback();
- }
async.parallel([
- async.apply(db.sortedSetRemove, 'group:' + groupName + ':members', uid),
- async.apply(db.setRemove, 'group:' + groupName + ':owners', uid),
- async.apply(db.decrObjectField, 'group:' + groupName, 'memberCount'),
+ async.apply(db.sortedSetRemove, groupNames.map(groupName => 'group:' + groupName + ':members'), uid),
+ async.apply(db.setRemove, groupNames.map(groupName => 'group:' + groupName + ':owners'), uid),
+ async.apply(db.decrObjectField, groupNames.map(groupName => 'group:' + groupName), 'memberCount'),
], next);
},
function (results, next) {
- clearCache(uid, groupName);
- Groups.getGroupFields(groupName, ['hidden', 'memberCount'], next);
+ clearCache(uid, groupNames);
+ Groups.getGroupsFields(groupNames, ['name', 'hidden', 'memberCount'], next);
},
function (groupData, next) {
if (!groupData) {
return callback();
}
- if (Groups.isPrivilegeGroup(groupName) && parseInt(groupData.memberCount, 10) === 0) {
- Groups.destroy(groupName, next);
- } else if (parseInt(groupData.hidden, 10) !== 1) {
- db.sortedSetAdd('groups:visible:memberCount', groupData.memberCount, groupName, next);
- } else {
- next();
+ var tasks = [];
+
+ var emptyPrivilegeGroups = groupData.filter(function (groupData) {
+ return groupData && Groups.isPrivilegeGroup(groupData.name) && parseInt(groupData.memberCount, 10) === 0;
+ });
+ if (emptyPrivilegeGroups.length) {
+ tasks.push(async.apply(Groups.destroy, emptyPrivilegeGroups));
}
+
+ var visibleGroups = groupData.filter(function (groupData) {
+ return groupData && parseInt(groupData.hidden, 10) !== 1;
+ });
+ if (visibleGroups.length) {
+ tasks.push(async.apply(db.sortedSetAdd, 'groups:visible:memberCount', visibleGroups.map(groupData => groupData.memberCount), visibleGroups.map(groupData => groupData.name)));
+ }
+
+ async.parallel(tasks, function (err) {
+ next(err);
+ });
},
function (next) {
- clearGroupTitleIfSet(groupName, uid, next);
+ clearGroupTitleIfSet(groupNames, uid, next);
},
function (next) {
plugins.fireHook('action:group.leave', {
- groupName: groupName,
+ groupName: groupNames[0],
+ groupNames: groupNames,
uid: uid,
});
next();
@@ -265,8 +285,11 @@ module.exports = function (Groups) {
], callback);
};
- function clearGroupTitleIfSet(groupName, uid, callback) {
- if (groupName === 'registered-users' || Groups.isPrivilegeGroup(groupName)) {
+ function clearGroupTitleIfSet(groupNames, uid, callback) {
+ groupNames = groupNames.filter(function (groupName) {
+ return groupName !== 'registered-users' && !Groups.isPrivilegeGroup(groupName);
+ });
+ if (!groupNames.length) {
return callback();
}
async.waterfall([
@@ -274,7 +297,7 @@ module.exports = function (Groups) {
db.getObjectField('user:' + uid, 'groupTitle', next);
},
function (groupTitle, next) {
- if (groupTitle === groupName) {
+ if (groupNames.includes(groupTitle)) {
db.deleteObjectField('user:' + uid, 'groupTitle', next);
} else {
next();
@@ -289,16 +312,14 @@ module.exports = function (Groups) {
db.getSortedSetRange('groups:createtime', 0, -1, next);
},
function (groups, next) {
- async.each(groups, function (groupName, next) {
- async.parallel([
- function (next) {
- Groups.leave(groupName, uid, next);
- },
- function (next) {
- Groups.rejectMembership(groupName, uid, next);
- },
- ], next);
- }, next);
+ async.parallel([
+ function (next) {
+ Groups.leave(groups, uid, next);
+ },
+ function (next) {
+ Groups.rejectMembership(groups, uid, next);
+ },
+ ], next);
},
], callback);
};
@@ -335,13 +356,22 @@ module.exports = function (Groups) {
cache.reset();
});
- function clearCache(uid, groupName) {
- pubsub.publish('group:cache:del', { uid: uid, groupName: groupName });
- cache.del(uid + ':' + groupName);
+ function clearCache(uid, groupNames) {
+ if (!Array.isArray(groupNames)) {
+ groupNames = [groupNames];
+ }
+ pubsub.publish('group:cache:del', { uid: uid, groupNames: groupNames });
+ groupNames.forEach(function (groupName) {
+ cache.del(uid + ':' + groupName);
+ });
}
pubsub.on('group:cache:del', function (data) {
- cache.del(data.uid + ':' + data.groupName);
+ if (data && data.groupNames) {
+ data.groupNames.forEach(function (groupName) {
+ cache.del(data.uid + ':' + groupName);
+ });
+ }
});
Groups.isMember = function (uid, groupName, callback) {
diff --git a/src/image.js b/src/image.js
index f2afb21003..f99a73e3bc 100644
--- a/src/image.js
+++ b/src/image.js
@@ -20,6 +20,7 @@ image.resizeImage = function (data, callback) {
extension: data.extension,
width: data.width,
height: data.height,
+ quality: data.quality,
}, function (err) {
callback(err);
});
@@ -74,6 +75,9 @@ image.resizeImage = function (data, callback) {
}
},
function (image, next) {
+ if (data.quality) {
+ image.quality(data.quality);
+ }
image.write(data.target || data.path, next);
},
], function (err) {
diff --git a/src/middleware/admin.js b/src/middleware/admin.js
index 3086f045cc..1c72a31906 100644
--- a/src/middleware/admin.js
+++ b/src/middleware/admin.js
@@ -2,10 +2,14 @@
var async = require('async');
var winston = require('winston');
+var jsesc = require('jsesc');
+var nconf = require('nconf');
+var semver = require('semver');
+
var user = require('../user');
var meta = require('../meta');
var plugins = require('../plugins');
-var jsesc = require('jsesc');
+var versions = require('../admin/versions');
var controllers = {
api: require('../controllers/api'),
@@ -54,6 +58,15 @@ module.exports = function (middleware) {
configs: function (next) {
meta.configs.list(next);
},
+ latestVersion: function (next) {
+ versions.getLatestVersion(function (err, result) {
+ if (err) {
+ winston.error('[acp] Failed to fetch latest version', err);
+ }
+
+ next(null, err ? null : result);
+ });
+ },
}, next);
},
function (results, next) {
@@ -67,6 +80,8 @@ module.exports = function (middleware) {
});
acpPath = acpPath.join(' > ');
+ var version = nconf.get('version');
+
var templateValues = {
config: res.locals.config,
configJSON: jsesc(JSON.stringify(res.locals.config), { isScriptContext: true }),
@@ -81,6 +96,9 @@ module.exports = function (middleware) {
env: !!process.env.NODE_ENV,
title: (acpPath || 'Dashboard') + ' | NodeBB Admin Control Panel',
bodyClass: data.bodyClass,
+ version: version,
+ latestVersion: results.latestVersion,
+ upgradeAvailable: results.latestVersion && semver.gt(results.latestVersion, version),
};
templateValues.template = { name: res.locals.template };
diff --git a/src/middleware/headers.js b/src/middleware/headers.js
index 035608eab6..60af68a894 100644
--- a/src/middleware/headers.js
+++ b/src/middleware/headers.js
@@ -14,7 +14,18 @@ module.exports = function (middleware) {
};
if (meta.config['access-control-allow-origin']) {
- headers['Access-Control-Allow-Origin'] = encodeURI(meta.config['access-control-allow-origin']);
+ var origins = meta.config['access-control-allow-origin'].split(',');
+ origins = origins.map(function (origin) {
+ return origin && origin.trim();
+ });
+
+ if (origins.includes(req.get('origin'))) {
+ headers['Access-Control-Allow-Origin'] = encodeURI(req.get('origin'));
+ }
+ }
+
+ if (meta.config['access-control-allow-credentials']) {
+ headers['Access-Control-Allow-Credentials'] = meta.config['access-control-allow-credentials'];
}
if (process.env.NODE_ENV === 'development') {
diff --git a/src/posts/diffs.js b/src/posts/diffs.js
index 1472e913e4..2ff5296f0c 100644
--- a/src/posts/diffs.js
+++ b/src/posts/diffs.js
@@ -8,70 +8,105 @@ var db = require('../database');
var plugins = require('../plugins');
var translator = require('../translator');
+var Diffs = {};
+
+Diffs.exists = function (pid, callback) {
+ db.listLength('post:' + pid + ':diffs', function (err, numDiffs) {
+ return callback(err, !!numDiffs);
+ });
+};
+
+Diffs.get = function (pid, since, callback) {
+ async.waterfall([
+ async.apply(db.getListRange.bind(db), 'post:' + pid + ':diffs', 0, -1),
+ function (timestamps, next) {
+ // Pass those made after `since`, and create keys
+ const keys = timestamps.filter(function (timestamp) {
+ return (parseInt(timestamp, 10) || 0) > since;
+ }).map(function (timestamp) {
+ return 'diff:' + pid + '.' + timestamp;
+ });
+
+ db.getObjects(keys, next);
+ },
+ ], callback);
+};
+
+Diffs.list = function (pid, callback) {
+ db.getListRange('post:' + pid + ':diffs', 0, -1, callback);
+};
+
+Diffs.save = function (pid, oldContent, newContent, callback) {
+ const now = Date.now();
+ const patch = diff.createPatch('', newContent, oldContent);
+ async.parallel([
+ async.apply(db.listPrepend.bind(db), 'post:' + pid + ':diffs', now),
+ async.apply(db.setObject.bind(db), 'diff:' + pid + '.' + now, {
+ pid: pid,
+ patch: patch,
+ }),
+ ], function (err) {
+ // No return arguments passed back
+ callback(err);
+ });
+};
+
+Diffs.load = function (pid, since, uid, callback) {
+ var Posts = require('../posts');
+
+ // Retrieves all diffs made since `since` and replays them to reconstruct what the post looked like at `since`
+ since = parseInt(since, 10);
+
+ if (isNaN(since) || since > Date.now()) {
+ return callback(new Error('[[error:invalid-data]]'));
+ }
+
+ async.parallel({
+ post: async.apply(Posts.getPostSummaryByPids, [pid], uid, {
+ parse: false,
+ }),
+ diffs: async.apply(Posts.diffs.get, pid, since),
+ }, function (err, data) {
+ if (err) {
+ return callback(err);
+ }
+
+ postDiffLoad(data);
+
+ async.waterfall([
+ function (next) {
+ plugins.fireHook('filter:parse.post', { postData: data.post }, next);
+ },
+ function (data, next) {
+ data.postData.content = translator.escape(data.postData.content);
+ next(null, data.postData);
+ },
+ ], callback);
+ });
+};
+
+function postDiffLoad(data) {
+ data.post = data.post[0];
+ data.post.content = validator.unescape(data.post.content);
+
+ // Replace content with re-constructed content from that point in time
+ data.post.content = data.diffs.reduce(function (content, currentDiff) {
+ return diff.applyPatch(content, currentDiff.patch, {
+ fuzzFactor: 1,
+ });
+ }, data.post.content);
+
+ // Clear editor data (as it is outdated for this content)
+ delete data.post.edited;
+ data.post.editor = null;
+
+ data.post.content = String(data.post.content || '');
+}
+
module.exports = function (Posts) {
Posts.diffs = {};
- Posts.diffs.exists = function (pid, callback) {
- db.sortedSetCard('post:' + pid + ':diffs', function (err, numDiffs) {
- return callback(err, numDiffs > 0);
- });
- };
-
- Posts.diffs.list = function (pid, callback) {
- db.getSortedSetRangeWithScores('post:' + pid + ':diffs', 0, -1, function (err, diffs) {
- callback(err, diffs ? diffs.map(function (diffObj) {
- return diffObj.score;
- }).reverse() : null);
- });
- };
-
- Posts.diffs.save = function (pid, oldContent, newContent, callback) {
- db.sortedSetAdd('post:' + pid + ':diffs', Date.now(), diff.createPatch('', newContent, oldContent), callback);
- };
-
- Posts.diffs.load = function (pid, since, uid, callback) {
- // Retrieves all diffs made since `since` and replays them to reconstruct what the post looked like at `since`
- since = parseInt(since, 10);
-
- if (isNaN(since) || since > Date.now()) {
- return callback(new Error('[[error:invalid-data]]'));
- }
-
- async.parallel({
- post: async.apply(Posts.getPostSummaryByPids, [pid], uid, {
- parse: false,
- }),
- diffs: async.apply(db.getSortedSetRangeByScore.bind(db), 'post:' + pid + ':diffs', 0, -1, since, Date.now()),
- }, function (err, data) {
- if (err) {
- return callback(err);
- }
-
- data.post = data.post[0];
- data.post.content = validator.unescape(data.post.content);
-
- // Replace content with re-constructed content from that point in time
- data.post.content = data.diffs.reverse().reduce(function (content, diffString) {
- return diff.applyPatch(content, diffString, {
- fuzzFactor: 1,
- });
- }, data.post.content);
-
- // Clear editor data (as it is outdated for this content)
- delete data.post.edited;
- data.post.editor = null;
-
- data.post.content = String(data.post.content || '');
-
- async.waterfall([
- function (next) {
- plugins.fireHook('filter:parse.post', { postData: data.post }, next);
- },
- function (data, next) {
- data.postData.content = translator.escape(data.postData.content);
- next(null, data.postData);
- },
- ], callback);
- });
- };
+ Object.keys(Diffs).forEach(function (property) {
+ Posts.diffs[property] = Diffs[property];
+ });
};
diff --git a/src/socket.io/admin.js b/src/socket.io/admin.js
index 668dea0dbc..95bb430069 100644
--- a/src/socket.io/admin.js
+++ b/src/socket.io/admin.js
@@ -228,9 +228,8 @@ SocketAdmin.settings.clearSitemapCache = function (socket, data, callback) {
};
SocketAdmin.email.test = function (socket, data, callback) {
- var site_title = meta.config.title || 'NodeBB';
var payload = {
- subject: '[' + site_title + '] Test Email',
+ subject: 'Test Email',
};
switch (data.template) {
diff --git a/src/socket.io/admin/categories.js b/src/socket.io/admin/categories.js
index 7bd491c8cd..6ccc20c873 100644
--- a/src/socket.io/admin/categories.js
+++ b/src/socket.io/admin/categories.js
@@ -76,9 +76,19 @@ Categories.setPrivilege = function (socket, data, callback) {
if (Array.isArray(data.privilege)) {
async.each(data.privilege, function (privilege, next) {
groups[data.set ? 'join' : 'leave']('cid:' + data.cid + ':privileges:' + privilege, data.member, next);
- }, callback);
+ }, onSetComplete);
} else {
- groups[data.set ? 'join' : 'leave']('cid:' + data.cid + ':privileges:' + data.privilege, data.member, callback);
+ groups[data.set ? 'join' : 'leave']('cid:' + data.cid + ':privileges:' + data.privilege, data.member, onSetComplete);
+ }
+
+ function onSetComplete() {
+ events.log({
+ uid: socket.uid,
+ ip: socket.ip,
+ privilege: data.privilege,
+ action: data.set ? 'grant' : 'rescind',
+ target: data.member,
+ }, callback);
}
};
diff --git a/src/upgrades/1.8.1/diffs_zset_to_listhash.js b/src/upgrades/1.8.1/diffs_zset_to_listhash.js
new file mode 100644
index 0000000000..b7a2bba296
--- /dev/null
+++ b/src/upgrades/1.8.1/diffs_zset_to_listhash.js
@@ -0,0 +1,57 @@
+'use strict';
+
+var db = require('../../database');
+const batch = require('../../batch');
+
+var async = require('async');
+
+module.exports = {
+ name: 'Reformatting post diffs to be stored in lists and hash instead of single zset',
+ timestamp: Date.UTC(2017, 2, 15),
+ method: function (callback) {
+ var progress = this.progress;
+
+ batch.processSortedSet('posts:pid', function (pids, next) {
+ async.each(pids, function (pid, next) {
+ db.getSortedSetRangeWithScores('post:' + pid + ':diffs', 0, -1, function (err, diffs) {
+ if (err) {
+ return next(err);
+ }
+
+ if (!diffs || !diffs.length) {
+ progress.incr();
+ return next();
+ }
+
+ // For each diff, push to list
+ async.each(diffs, function (diff, next) {
+ async.series([
+ async.apply(db.delete.bind(db), 'post:' + pid + ':diffs'),
+ async.apply(db.listPrepend.bind(db), 'post:' + pid + ':diffs', diff.score),
+ async.apply(db.setObject.bind(db), 'diff:' + pid + '.' + diff.score, {
+ pid: pid,
+ patch: diff.value,
+ }),
+ ], next);
+ }, function (err) {
+ if (err) {
+ return next(err);
+ }
+
+ progress.incr();
+ return next();
+ });
+ });
+ }, function (err) {
+ if (err) {
+ // Probably type error, ok to incr and continue
+ progress.incr();
+ }
+
+ return next();
+ });
+ }, {
+ progress: progress,
+ }, callback);
+ },
+};
diff --git a/src/user/delete.js b/src/user/delete.js
index 0f1ad61047..ace9dd969c 100644
--- a/src/user/delete.js
+++ b/src/user/delete.js
@@ -50,15 +50,12 @@ module.exports = function (User) {
var userData;
async.waterfall([
function (next) {
- User.exists(uid, next);
- },
- function (exists, next) {
- if (!exists) {
- return callback();
- }
- User.getUserFields(uid, ['username', 'userslug', 'fullname', 'email'], next);
+ db.getObject('user:' + uid, next);
},
function (_userData, next) {
+ if (!_userData || !_userData.username) {
+ return callback();
+ }
userData = _userData;
plugins.fireHook('static:user.delete', { uid: uid }, next);
},
diff --git a/src/user/digest.js b/src/user/digest.js
index 876e1b7a07..3c74fcdfaa 100644
--- a/src/user/digest.js
+++ b/src/user/digest.js
@@ -134,7 +134,7 @@ Digest.send = function (data, callback) {
});
emailsSent += 1;
emailer.send('digest', userObj.uid, {
- subject: '[' + meta.config.title + '] [[email:digest.subject, ' + (now.getFullYear() + '/' + (now.getMonth() + 1) + '/' + now.getDate()) + ']]',
+ subject: '[[email:digest.subject, ' + (now.getFullYear() + '/' + (now.getMonth() + 1) + '/' + now.getDate()) + ']]',
username: userObj.username,
userslug: userObj.userslug,
notifications: notifications,
diff --git a/src/user/notifications.js b/src/user/notifications.js
index 04bc0fae9f..851a2d2e94 100644
--- a/src/user/notifications.js
+++ b/src/user/notifications.js
@@ -96,16 +96,10 @@ function deleteUserNids(nids, uid, callback) {
if (!nids.length) {
return setImmediate(callback);
}
- async.parallel([
- function (next) {
- db.sortedSetRemove('uid:' + uid + ':notifications:read', nids, next);
- },
- function (next) {
- db.sortedSetRemove('uid:' + uid + ':notifications:unread', nids, next);
- },
- ], function (err) {
- callback(err);
- });
+ db.sortedSetRemove([
+ 'uid:' + uid + ':notifications:read',
+ 'uid:' + uid + ':notifications:unread',
+ ], nids, callback);
}
function getNotifications(uid, start, stop, callback) {
diff --git a/src/user/settings.js b/src/user/settings.js
index 4268db7515..c654d2dd99 100644
--- a/src/user/settings.js
+++ b/src/user/settings.js
@@ -137,14 +137,22 @@ module.exports = function (User) {
incomingChatSound: data.incomingChatSound,
outgoingChatSound: data.outgoingChatSound,
upvoteNotifFreq: data.upvoteNotifFreq,
- notificationType_upvote: data.notificationType_upvote,
- 'notificationType_new-topic': data['notificationType_new-topic'],
- 'notificationType_new-reply': data['notificationType_new-reply'],
- notificationType_follow: data.notificationType_follow,
- 'notificationType_new-chat': data['notificationType_new-chat'],
- 'notificationType_group-invite': data['notificationType_group-invite'],
};
+ var notificationTypes = [
+ 'notificationType_upvote', 'notificationType_new-topic', 'notificationType_new-reply',
+ 'notificationType_follow', 'notificationType_new-chat', 'notificationType_group-invite',
+ 'notificationType_new-register', 'notificationType_post-queue', 'notificationType_new-post-flag',
+ 'notificationType_new-user-flag',
+ ];
+
+ notificationTypes.forEach(function (notificationType) {
+ if (data[notificationType]) {
+ settings[notificationType] = data[notificationType];
+ }
+ });
+
+
if (data.bootswatchSkin) {
settings.bootswatchSkin = data.bootswatchSkin;
}
diff --git a/src/views/admin/general/dashboard.tpl b/src/views/admin/general/dashboard.tpl
index 3090e2dc8b..c58bd6bef4 100644
--- a/src/views/admin/general/dashboard.tpl
+++ b/src/views/admin/general/dashboard.tpl
@@ -65,8 +65,27 @@