mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-11-16 10:46:14 +01:00
Resolve merge conflicts
This commit is contained in:
@@ -17,3 +17,4 @@ logs/
|
|||||||
/coverage
|
/coverage
|
||||||
/build
|
/build
|
||||||
.eslintrc
|
.eslintrc
|
||||||
|
test/files
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
|||||||
|
yarn.lock
|
||||||
npm-debug.log
|
npm-debug.log
|
||||||
node_modules/
|
node_modules/
|
||||||
sftp-config.json
|
sftp-config.json
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ var initWorker;
|
|||||||
var incomplete = [];
|
var incomplete = [];
|
||||||
var running = 0;
|
var running = 0;
|
||||||
|
|
||||||
|
env.NODE_ENV = env.NODE_ENV || 'development';
|
||||||
|
|
||||||
module.exports = function (grunt) {
|
module.exports = function (grunt) {
|
||||||
var args = [];
|
var args = [];
|
||||||
var initArgs = ['--build'];
|
var initArgs = ['--build'];
|
||||||
|
|||||||
@@ -58,14 +58,14 @@
|
|||||||
"nodebb-plugin-composer-default": "4.4.8",
|
"nodebb-plugin-composer-default": "4.4.8",
|
||||||
"nodebb-plugin-dbsearch": "2.0.2",
|
"nodebb-plugin-dbsearch": "2.0.2",
|
||||||
"nodebb-plugin-emoji-extended": "1.1.1",
|
"nodebb-plugin-emoji-extended": "1.1.1",
|
||||||
"nodebb-plugin-emoji-one": "1.1.5",
|
"nodebb-plugin-emoji-one": "1.2.1",
|
||||||
"nodebb-plugin-markdown": "7.1.1",
|
"nodebb-plugin-markdown": "7.1.1",
|
||||||
"nodebb-plugin-mentions": "2.0.3",
|
"nodebb-plugin-mentions": "2.0.3",
|
||||||
"nodebb-plugin-soundpack-default": "1.0.0",
|
"nodebb-plugin-soundpack-default": "1.0.0",
|
||||||
"nodebb-plugin-spam-be-gone": "0.5.0",
|
"nodebb-plugin-spam-be-gone": "0.5.0",
|
||||||
"nodebb-rewards-essentials": "0.0.9",
|
"nodebb-rewards-essentials": "0.0.9",
|
||||||
"nodebb-theme-lavender": "4.0.0",
|
"nodebb-theme-lavender": "4.0.0",
|
||||||
"nodebb-theme-persona": "5.0.0",
|
"nodebb-theme-persona": "5.0.1",
|
||||||
"nodebb-theme-vanilla": "6.0.2",
|
"nodebb-theme-vanilla": "6.0.2",
|
||||||
"nodebb-widget-essentials": "3.0.0",
|
"nodebb-widget-essentials": "3.0.0",
|
||||||
"nodemailer": "2.6.4",
|
"nodemailer": "2.6.4",
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
"quote": "Quote",
|
"quote": "Quote",
|
||||||
"reply": "Reply",
|
"reply": "Reply",
|
||||||
"replies_to_this_post": "%1 Replies",
|
"replies_to_this_post": "%1 Replies",
|
||||||
|
"one_reply_to_this_post": "1 Reply",
|
||||||
"last_reply_time": "Last reply",
|
"last_reply_time": "Last reply",
|
||||||
"reply-as-topic": "Reply as topic",
|
"reply-as-topic": "Reply as topic",
|
||||||
"guest-login-reply": "Log in to reply",
|
"guest-login-reply": "Log in to reply",
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
"none-active": "No hay Plug-ins activos",
|
"none-active": "No hay Plug-ins activos",
|
||||||
"find-plugins": "Buscar Plug-in",
|
"find-plugins": "Buscar Plug-in",
|
||||||
|
|
||||||
"plugin-search": "Plug-in de Búsqueda",
|
"plugin-search": "Buscar",
|
||||||
"plugin-search-placeholder": "Búscando Plug-in",
|
"plugin-search-placeholder": "Búscando Plug-in",
|
||||||
"reorder-plugins": "Re-ordenar Plug-ins",
|
"reorder-plugins": "Re-ordenar Plug-ins",
|
||||||
"order-active": "Ordenar Plug-ins Activos",
|
"order-active": "Ordenar Plug-ins Activos",
|
||||||
@@ -18,14 +18,14 @@
|
|||||||
"order.explanation": "Los plug-in son cargados en el orden especificado, de arriba a abajo.",
|
"order.explanation": "Los plug-in son cargados en el orden especificado, de arriba a abajo.",
|
||||||
|
|
||||||
"plugin-item.themes": "Temas",
|
"plugin-item.themes": "Temas",
|
||||||
"plugin-item.deactivate": "Desactivado",
|
"plugin-item.deactivate": "Desactivar",
|
||||||
"plugin-item.activate": "Activado",
|
"plugin-item.activate": "Activar",
|
||||||
"plugin-item.install": "Instalar",
|
"plugin-item.install": "Instalar",
|
||||||
"plugin-item.uninstall": "Desinstalar",
|
"plugin-item.uninstall": "Desinstalar",
|
||||||
"plugin-item.settings": "Configuraciones",
|
"plugin-item.settings": "Configuraciones",
|
||||||
"plugin-item.installed": "Instalados",
|
"plugin-item.installed": "Instalados",
|
||||||
"plugin-item.latest": "Ultimos",
|
"plugin-item.latest": "Ultimos",
|
||||||
"plugin-item.upgrade": "Actualizado",
|
"plugin-item.upgrade": "Actualizar",
|
||||||
"plugin-item.more-info": "Para mas información:",
|
"plugin-item.more-info": "Para mas información:",
|
||||||
"plugin-item.unknown": "Desconocido",
|
"plugin-item.unknown": "Desconocido",
|
||||||
"plugin-item.unknown-explanation": "El estado de este plug-in no puede determinsarse, posiblemente es debido a un error de configuración.",
|
"plugin-item.unknown-explanation": "El estado de este plug-in no puede determinsarse, posiblemente es debido a un error de configuración.",
|
||||||
@@ -35,9 +35,9 @@
|
|||||||
"alert.upgraded": "Plug-in Actualizado",
|
"alert.upgraded": "Plug-in Actualizado",
|
||||||
"alert.installed": "Plug-in Instalado",
|
"alert.installed": "Plug-in Instalado",
|
||||||
"alert.uninstalled": "Plug-in Desinstalado",
|
"alert.uninstalled": "Plug-in Desinstalado",
|
||||||
"alert.activate-success": "Por favor reinicia NodeBB para activar el plug-in por completo",
|
"alert.activate-success": "Por favor reiniciá NodeBB para activar el plug-in por completo",
|
||||||
"alert.deactivate-success": "Plugin successfully deactivated",
|
"alert.deactivate-success": "Plugin successfully deactivated",
|
||||||
"alert.upgrade-success": "Please reload your NodeBB to fully upgrade this plugin",
|
"alert.upgrade-success": "Por favor recargá NodeBB para actualizar el plug-in por completo",
|
||||||
"alert.install-success": "Plugin successfully installed, please activate the plugin.",
|
"alert.install-success": "Plugin successfully installed, please activate the plugin.",
|
||||||
"alert.uninstall-success": "The plugin has been successfully deactivated and uninstalled.",
|
"alert.uninstall-success": "The plugin has been successfully deactivated and uninstalled.",
|
||||||
"alert.suggest-error": "<p>NodeBB could not reach the package manager, proceed with installation of latest version?</p><div class=\"alert alert-danger\"><strong>Server returned (%1)</strong>: %2</div>",
|
"alert.suggest-error": "<p>NodeBB could not reach the package manager, proceed with installation of latest version?</p><div class=\"alert alert-danger\"><strong>Server returned (%1)</strong>: %2</div>",
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
{
|
{
|
||||||
"category": "Category",
|
"category": "หมวดหมู่",
|
||||||
"subcategories": "Subcategories",
|
"subcategories": "หมวดหมู่ย่อย",
|
||||||
"new_topic_button": "กระทู้",
|
"new_topic_button": "ตั้งกระทู้",
|
||||||
"guest-login-post": "เข้าสู่ระบบเพื่อโพส",
|
"guest-login-post": "เข้าสู่ระบบเพื่อโพสต์",
|
||||||
"no_topics": "<strong>ยังไม่มีกระทู้ในหมวดนี้</strong><br />โพสต์กระทู้แรก?",
|
"no_topics": "<strong>ยังไม่มีกระทู้ในหมวดนี้</strong><br />โพสต์กระทู้แรก?",
|
||||||
"browsing": "เรียกดู",
|
"browsing": "เรียกดู",
|
||||||
"no_replies": "ยังไม่มีใครตอบ",
|
"no_replies": "ยังไม่มีใครตอบ",
|
||||||
"no_new_posts": "No new posts.",
|
"no_new_posts": "ไม่มีกระทู้ใหม่",
|
||||||
"share_this_category": "แชร์ Category นี้",
|
"share_this_category": "แชร์หมวดนี้",
|
||||||
"watch": "Watch",
|
"watch": "ตามดู",
|
||||||
"ignore": "ไม่ต้องสนใจอีก",
|
"ignore": "ไม่ต้องสนใจอีก",
|
||||||
"watching": "Watching",
|
"watching": "กำลังตามดู",
|
||||||
"ignoring": "Ignoring",
|
"ignoring": "เมินเฉย",
|
||||||
"watching.description": "Show topics in unread",
|
"watching.description": "แสดงกระทู้ที่ยังไม่ได้อ่าน",
|
||||||
"ignoring.description": "Do not show topics in unread",
|
"ignoring.description": "ไม่แสดงกระทู้ที่ยังไม่ได้อ่าน",
|
||||||
"watch.message": "You are now watching updates from this category and all subcategories",
|
"watch.message": "ตอนนี้คุณกำลังตามดูอัพเดทจากกระทู้หมวดนี้และหมวดหมู่ย่อยทั้งหมดในนี้",
|
||||||
"ignore.message": "You are now ignoring updates from this category and all subcategories",
|
"ignore.message": "ตอนนี้คุณกำลังเมินเฉยต่ออัพเดทจากกระทู้หมวดนี้และหมวดหมู่ย่อยทั้งหมดในนี้",
|
||||||
"watched-categories": "Watched categories"
|
"watched-categories": "หมวดหมู่ที่ดูแล้ว"
|
||||||
}
|
}
|
||||||
@@ -1,40 +1,40 @@
|
|||||||
{
|
{
|
||||||
"password-reset-requested": "Password Reset Requested - %1!",
|
"password-reset-requested": "ส่งคำขอตั้งค่ารหัสผ่านใหม่แล้ว - %1!",
|
||||||
"welcome-to": "ยินดีต้อนรับ %1",
|
"welcome-to": "ยินดีต้อนรับ %1",
|
||||||
"invite": "Invitation from %1",
|
"invite": "คำเชิญจาก %1",
|
||||||
"greeting_no_name": "สวัสดี",
|
"greeting_no_name": "สวัสดี",
|
||||||
"greeting_with_name": "สวัสดี %1",
|
"greeting_with_name": "สวัสดี %1",
|
||||||
"welcome.text1": "ขอบคุณที่ลงทะเบียนกับ %1",
|
"welcome.text1": "ขอบคุณที่ลงทะเบียนกับ %1",
|
||||||
"welcome.text2": "To fully activate your account, we need to verify that you own the email address you registered with.",
|
"welcome.text2": "เพื่อให้การบัญชีของคุณใช้งานได้อย่างเสร็จสมบูรณ์ เราจำเป็นต้องยืนยันว่าคุณเป็นเจ้าของที่แท้จริงอีเมล์ที่ใช้สมัครสมาชิก",
|
||||||
"welcome.text3": "An administrator has accepted your registration application. You can login with your username/password now.",
|
"welcome.text3": "ผู้ดูแลระบบได้ทำการยอมรับการสมัครสมาชิกของคุณแล้ว คุณสามารถเข้าสู่ระบบด้วย ชื่อผู้ใช้/รหัสผ่าน ได้แล้วตอนนี้",
|
||||||
"welcome.cta": "กดตรงนี้เพื่อยืนยันอีเมลของคุณ",
|
"welcome.cta": "กดตรงนี้เพื่อยืนยันอีเมลของคุณ",
|
||||||
"invitation.text1": "%1 has invited you to join %2",
|
"invitation.text1": "%1 ได้เชิญคุณให้เข้าร่วม %2",
|
||||||
"invitation.ctr": "Click here to create your account.",
|
"invitation.ctr": "คลิกที่นี่เพื่อสร้างบัญชีของคุณ",
|
||||||
"reset.text1": "We received a request to reset your password, possibly because you have forgotten it. If this is not the case, please ignore this email.",
|
"reset.text1": "เราได้รับคำร้องให้ตั้งค่ารหัสผ่านใหม่ของคุณ อาจจะเป็นเพราะว่าคุณลืมรหัสผ่านและได้ทำการส่งคำขอเข้ามา หากไม่ใช่ กรุณาเพิกเฉยต่ออีเมล์นี้และไม่ต้องดำเนินการใดๆทั้งสิ้น",
|
||||||
"reset.text2": "เพื่อดำเนินการตั้งรหัสผ่านใหม่ต่อไป, โปรดกดที่ลิ้งค์นี้:",
|
"reset.text2": "เพื่อดำเนินการตั้งรหัสผ่านใหม่ต่อไป, โปรดกดที่ลิ้งค์นี้:",
|
||||||
"reset.cta": "กดตรงนี้เพื่อตั้งรหัสผ่านใหม่",
|
"reset.cta": "กดตรงนี้เพื่อตั้งรหัสผ่านใหม่",
|
||||||
"reset.notify.subject": "Password successfully changed",
|
"reset.notify.subject": "ตั้งค่ารหัสผ่านใหม่เรียบร้อยแล้ว",
|
||||||
"reset.notify.text1": "We are notifying you that on %1, your password was changed successfully.",
|
"reset.notify.text1": "เรากำลังแจ้งคุณว่าตอน %1 รหัสผ่านของคุณถูกเปลี่ยนเรียบร้อยแล้ว",
|
||||||
"reset.notify.text2": "If you did not authorise this, please notify an administrator immediately.",
|
"reset.notify.text2": "หากคุณไม่ได้เป็นคนอนุญาตสิ่งนี้ กรุณาแจ้งไปยังผู้ดูแลระบบโดยทันที",
|
||||||
"digest.notifications": "คุณมีข้อความแจ้งเตือนที่ยังไม่ได้อ่านจาก %1:",
|
"digest.notifications": "คุณมีข้อความแจ้งเตือนที่ยังไม่ได้อ่านจาก %1:",
|
||||||
"digest.latest_topics": "หัวข้อสนทนาล่าสุดจาก %1",
|
"digest.latest_topics": "หัวข้อสนทนาล่าสุดจาก %1",
|
||||||
"digest.cta": "กดตรงนี้เพื่อเข้าดู %1",
|
"digest.cta": "กดตรงนี้เพื่อเข้าดู %1",
|
||||||
"digest.unsub.info": "This digest was sent to you due to your subscription settings.",
|
"digest.unsub.info": "คำชี้แจงถูกส่งไปให้คุณแล้ว เนื่องมาจากการตั้งค่าสมาชิกของคุณ",
|
||||||
"digest.no_topics": "There have been no active topics in the past %1",
|
"digest.no_topics": "ไม่มีกระทู้ใดๆเลยใน %1 ที่ผ่านมา",
|
||||||
"digest.day": "day",
|
"digest.day": "วัน",
|
||||||
"digest.week": "week",
|
"digest.week": "สัปดาห์",
|
||||||
"digest.month": "month",
|
"digest.month": "เดือน",
|
||||||
"digest.subject": "Digest for %1",
|
"digest.subject": "คำชี้แจงสำหรับ %1",
|
||||||
"notif.chat.subject": "New chat message received from %1",
|
"notif.chat.subject": "ได้รับข้อความแชทใหม่จาก %1",
|
||||||
"notif.chat.cta": "กดตรงนี้เพื่อกลับไปยังบทสนทนา",
|
"notif.chat.cta": "กดตรงนี้เพื่อกลับไปยังบทสนทนา",
|
||||||
"notif.chat.unsub.info": "This chat notification was sent to you due to your subscription settings.",
|
"notif.chat.unsub.info": "การแจ้งเตือนแชทนี้ถูกส่งไปหาคุณเนื่องจากการตั้งค่าสมาชิกของคุณ",
|
||||||
"notif.post.cta": "Click here to read the full topic",
|
"notif.post.cta": "คลิกที่นี่เพื่ออ่านกระทู้ฉบับเต็ม",
|
||||||
"notif.post.unsub.info": "This post notification was sent to you due to your subscription settings.",
|
"notif.post.unsub.info": "การแจ้งเตือนกระทู้นี้ถูกส่งไปยังคุณเนื่องการตั้งค่าสมาชิกของคุณ",
|
||||||
"test.text1": "นี่คืออีเมลทดสอบเพื่อยืนยันว่าระบบอีเมลมีการตั้งค่าที่ถูกต้องสำหรับ NodeBB ของคุณ",
|
"test.text1": "นี่คืออีเมลทดสอบเพื่อยืนยันว่าระบบอีเมลมีการตั้งค่าที่ถูกต้องสำหรับ NodeBB ของคุณ",
|
||||||
"unsub.cta": "กดตรงนี้เพื่อเปลี่ยนแปลงการตั้งค่า",
|
"unsub.cta": "กดตรงนี้เพื่อเปลี่ยนแปลงการตั้งค่า",
|
||||||
"banned.subject": "You have been banned from %1",
|
"banned.subject": "คุณถูกแบนจาก %1 แล้ว",
|
||||||
"banned.text1": "The user %1 has been banned from %2.",
|
"banned.text1": "ผู้ใช้ %1 ได้ถูกแบนจาก %2",
|
||||||
"banned.text2": "This ban will last until %1.",
|
"banned.text2": "การแบนนี้จะใช้เวลาจนถึง %1",
|
||||||
"banned.text3": "This is the reason why you have been banned:",
|
"banned.text3": "นี่คือเหตุผลที่ทำไมคุณถึงถูกแบน",
|
||||||
"closing": "ขอบคุณ!"
|
"closing": "ขอบคุณ!"
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
"invalid-data": "ข้อมูลไม่ถูกต้อง",
|
"invalid-data": "ข้อมูลไม่ถูกต้อง",
|
||||||
"not-logged-in": "คุณยังไม่ได้ลงชื่อเข้าระบบ",
|
"not-logged-in": "คุณยังไม่ได้ลงชื่อเข้าระบบ",
|
||||||
"account-locked": "บัญชีของคุณถูกระงับการใช้งานชั่วคราว",
|
"account-locked": "บัญชีของคุณถูกระงับการใช้งานชั่วคราว",
|
||||||
"search-requires-login": "Searching requires an account - please login or register.",
|
"search-requires-login": "\"ฟังก์ชั่นการค้นหา\" ต้องการบัญชีผู้ใช้ กรุณาเข้าสู่ระบบหรือสมัครสมาชิก",
|
||||||
"invalid-cid": "Category ID ไม่ถูกต้อง",
|
"invalid-cid": "Category ID ไม่ถูกต้อง",
|
||||||
"invalid-tid": "Topic ID ไม่ถูกต้อง",
|
"invalid-tid": "Topic ID ไม่ถูกต้อง",
|
||||||
"invalid-pid": "Post ID ไม่ถูกต้อง",
|
"invalid-pid": "Post ID ไม่ถูกต้อง",
|
||||||
@@ -14,35 +14,35 @@
|
|||||||
"invalid-password": "รหัสผ่านไม่ถูกต้อง",
|
"invalid-password": "รหัสผ่านไม่ถูกต้อง",
|
||||||
"invalid-username-or-password": "กรุณาระบุชื่อผู้ใช้และรหัสผ่าน",
|
"invalid-username-or-password": "กรุณาระบุชื่อผู้ใช้และรหัสผ่าน",
|
||||||
"invalid-search-term": "ข้อความค้นหาไม่ถูกต้อง",
|
"invalid-search-term": "ข้อความค้นหาไม่ถูกต้อง",
|
||||||
"csrf-invalid": "We were unable to log you in, likely due to an expired session. Please try again",
|
"csrf-invalid": "เราไม่สามารถนำท่านเข้าสู่ระบบได้ เหมือนกับว่าเซสชั่นหมดอายุแล้ว กรุณาลองใหม่อีกครั้ง",
|
||||||
"invalid-pagination-value": "Invalid pagination value, must be at least %1 and at most %2",
|
"invalid-pagination-value": "หมายเลขหน้าไม่ถูกต้อง จำเป็นต้องเป็นตัวเลขอย่างน้อย %1 และอย่างมาก %2",
|
||||||
"username-taken": "ชื่อผู้ใช้นี้มีการใช้แล้ว",
|
"username-taken": "ชื่อผู้ใช้นี้มีการใช้แล้ว",
|
||||||
"email-taken": "อีเมลนี้มีการใช้แล้ว",
|
"email-taken": "อีเมลนี้มีการใช้แล้ว",
|
||||||
"email-not-confirmed": "ยังไม่มีการยืนยันอีเมลของคุณ, โปรดกดยืนยันอีเมลของคุณตรงนี้",
|
"email-not-confirmed": "ยังไม่มีการยืนยันอีเมลของคุณ, โปรดกดยืนยันอีเมลของคุณตรงนี้",
|
||||||
"email-not-confirmed-chat": "You are unable to chat until your email is confirmed, please click here to confirm your email.",
|
"email-not-confirmed-chat": "คุณไม่สามารถแชทได้จนกว่าอีเมล์ของคุณจะได้รับการยืนยัน กรุณาคลิกที่นี่เพื่อยืนยันอีกมเมล์ของคุณ",
|
||||||
"email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email.",
|
"email-not-confirmed-email-sent": "อีเมล์ของคุณยังไม่ได้รับการยืนยัน กรุณาเช็คกล่องข้อความในอีเมล์เพื่อกดยืนยัน",
|
||||||
"no-email-to-confirm": "Forum นี้ต้องการการยืนยันอีเมล กรุณากดที่นี่เพื่อระบุอีเมล",
|
"no-email-to-confirm": "Forum นี้ต้องการการยืนยันอีเมล กรุณากดที่นี่เพื่อระบุอีเมล",
|
||||||
"email-confirm-failed": "เราไม่สามารถยืนยันอีเมลของคุณ ณ ขณะนี้ กรุณาลองใหม่อีกครั้งภายหลัง",
|
"email-confirm-failed": "เราไม่สามารถยืนยันอีเมลของคุณ ณ ขณะนี้ กรุณาลองใหม่อีกครั้งภายหลัง",
|
||||||
"confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.",
|
"confirm-email-already-sent": "อีเมล์ยืนยันตัวตนถูกส่งไปยังคุณเรียบร้อยแล้ว กรุณารอ %1 นาที(s) ก่อนการตัดสินใจส่งอีกครั้ง",
|
||||||
"sendmail-not-found": "The sendmail executable could not be found, please ensure it is installed and executable by the user running NodeBB.",
|
"sendmail-not-found": "ไม่พบการประมวลผลสำหรับการส่งอีเมล์ กรุณาตรวจสอบให้แน่ใจว่าได้มีการติดตั้งโปรแกรมการประมวลผลแล้วโดยผู้ใช้ที่กำลังใช้ NodeBB",
|
||||||
"username-too-short": "ชื่อบัญชีผู้ใช้ สั้นเกินไป",
|
"username-too-short": "ชื่อบัญชีผู้ใช้ สั้นเกินไป",
|
||||||
"username-too-long": "ชื่อบัญชีผู้ใช้ ยาวเกินไป",
|
"username-too-long": "ชื่อบัญชีผู้ใช้ ยาวเกินไป",
|
||||||
"password-too-long": "Password too long",
|
"password-too-long": "รหัสผ่านยาวเกินไป",
|
||||||
"user-banned": "User banned",
|
"user-banned": "ผู้ใช้ได้รับการแบน",
|
||||||
"user-banned-reason": "Sorry, this account has been banned (Reason: %1)",
|
"user-banned-reason": "ขออภัย บัญชีผู้ใช้นี้ได้รับการแบน (เหตุผล : %1)",
|
||||||
"user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)",
|
"user-banned-reason-until": "ขออภัย บัญชีผู้ใช้นี้ได้รับการแบนจนถึง %1 (เหตุผล : %2)",
|
||||||
"user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post",
|
"user-too-new": "ขออภัย คุณจำเป็นต้องรอ %1 วินาที(s) ก่อนการสร้างกระทู้แรกของคุณ",
|
||||||
"blacklisted-ip": "Sorry, your IP address has been banned from this community. If you feel this is in error, please contact an administrator.",
|
"blacklisted-ip": "ขออภัย IP Address ของคุณถูกแบนจากชุมชนนี้ หากคุณคิดว่านี่เป็นเออเร่อของระบบ กรุณาติดต่อผู้ดูแลระบบ",
|
||||||
"ban-expiry-missing": "Please provide an end date for this ban",
|
"ban-expiry-missing": "กรุณาระบุวันสิ้นสุดสำหรับการแบนในครั้งนี้",
|
||||||
"no-category": "ยังไม่มี Category นี้",
|
"no-category": "ยังไม่มี Category นี้",
|
||||||
"no-topic": "ยังไม่มี Topic นี้",
|
"no-topic": "ยังไม่มี Topic นี้",
|
||||||
"no-post": "ยังไม่มี Post นี้",
|
"no-post": "ยังไม่มี Post นี้",
|
||||||
"no-group": "ยังไม่มี Group นี้",
|
"no-group": "ยังไม่มี Group นี้",
|
||||||
"no-user": "ยังไม่มีผู้ใช้งานนี้",
|
"no-user": "ยังไม่มีผู้ใช้งานนี้",
|
||||||
"no-teaser": "Teaser does not exist",
|
"no-teaser": "ยังไม่มีทีเซอร์นี้",
|
||||||
"no-privileges": "คุณมีสิทธิ์ไม่เพียงพอที่จะทำรายการนี้",
|
"no-privileges": "คุณมีสิทธิ์ไม่เพียงพอที่จะทำรายการนี้",
|
||||||
"category-disabled": "Category นี้ถูกปิดการใช้งานแล้ว",
|
"category-disabled": "Category นี้ถูกปิดการใช้งานแล้ว",
|
||||||
"topic-locked": "Topic Locked",
|
"topic-locked": "กระทู้ถูกล็อก",
|
||||||
"post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting",
|
"post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting",
|
||||||
"post-edit-duration-expired-minutes": "You are only allowed to edit posts for %1 minute(s) after posting",
|
"post-edit-duration-expired-minutes": "You are only allowed to edit posts for %1 minute(s) after posting",
|
||||||
"post-edit-duration-expired-minutes-seconds": "You are only allowed to edit posts for %1 minute(s) %2 second(s) after posting",
|
"post-edit-duration-expired-minutes-seconds": "You are only allowed to edit posts for %1 minute(s) %2 second(s) after posting",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"uploading-file": "Uploading the file...",
|
"uploading-file": "กำลังอัพโหลดไฟล์ ...",
|
||||||
"select-file-to-upload": "Select a file to upload!",
|
"select-file-to-upload": "กรุณาเลือกไฟล์ที่จะอัพโหลด",
|
||||||
"upload-success": "File uploaded successfully!",
|
"upload-success": "อัพโหลดไฟล์เรียบร้อยแล้ว",
|
||||||
"maximum-file-size": "Maximum %1 kb"
|
"maximum-file-size": "มากที่สุดได้ %1 kb"
|
||||||
}
|
}
|
||||||
@@ -104,7 +104,7 @@
|
|||||||
"open_links_in_new_tab": "เปิดลิงค์ในแท็บใหม่",
|
"open_links_in_new_tab": "เปิดลิงค์ในแท็บใหม่",
|
||||||
"enable_topic_searching": "เปิดใช้การค้นหาแบบ In-Topic",
|
"enable_topic_searching": "เปิดใช้การค้นหาแบบ In-Topic",
|
||||||
"topic_search_help": "หากเปิดใช้งาน, \"การค้นหาภายในกระทู้\" จะแทนที่ระบบ \"การค้นหาจากค่าเริ่มต้นของเบราเซอร์\" และจะทำให้คุณค้นหาข้อมูลต่างๆภายในกระทู้ได้ แทนที่จะเป็นการหาแค่สิ่งที่แสดงบนหน้าจอเท่านั้น",
|
"topic_search_help": "หากเปิดใช้งาน, \"การค้นหาภายในกระทู้\" จะแทนที่ระบบ \"การค้นหาจากค่าเริ่มต้นของเบราเซอร์\" และจะทำให้คุณค้นหาข้อมูลต่างๆภายในกระทู้ได้ แทนที่จะเป็นการหาแค่สิ่งที่แสดงบนหน้าจอเท่านั้น",
|
||||||
"delay_image_loading": "Delay Image Loading",
|
"delay_image_loading": "การโหลดรูปภาพช้าลง",
|
||||||
"image_load_delay_help": "หากเปิดใช้งาน, รูปภาพในกระทู้จะไม่โหลดจนกว่าจะมีการเลื่อนไปดู",
|
"image_load_delay_help": "หากเปิดใช้งาน, รูปภาพในกระทู้จะไม่โหลดจนกว่าจะมีการเลื่อนไปดู",
|
||||||
"scroll_to_my_post": "After posting a reply, show the new post",
|
"scroll_to_my_post": "After posting a reply, show the new post",
|
||||||
"follow_topics_you_reply_to": "ดูกระทู้ที่คุณตอบ",
|
"follow_topics_you_reply_to": "ดูกระทู้ที่คุณตอบ",
|
||||||
|
|||||||
@@ -8,13 +8,13 @@
|
|||||||
"mongo": "Mongo",
|
"mongo": "Mongo",
|
||||||
"mongo.version": "MongoDB Sürümü",
|
"mongo.version": "MongoDB Sürümü",
|
||||||
"mongo.storage-engine": "Storage Engine",
|
"mongo.storage-engine": "Storage Engine",
|
||||||
"mongo.collections": "Collections",
|
"mongo.collections": "Koleksiyonlar",
|
||||||
"mongo.objects": "Objects",
|
"mongo.objects": "Objeler",
|
||||||
"mongo.avg-object-size": "Avg. Object Size",
|
"mongo.avg-object-size": "Avg. Object Size",
|
||||||
"mongo.data-size": "Veri Boyutu",
|
"mongo.data-size": "Veri Boyutu",
|
||||||
"mongo.storage-size": "Storage Size",
|
"mongo.storage-size": "Storage Size",
|
||||||
"mongo.index-size": "Index Size",
|
"mongo.index-size": "İndex Boyutu",
|
||||||
"mongo.file-size": "File Size",
|
"mongo.file-size": "Dosya Boyutu",
|
||||||
"mongo.resident-memory": "Resident Memory",
|
"mongo.resident-memory": "Resident Memory",
|
||||||
"mongo.virtual-memory": "Virtual Memory",
|
"mongo.virtual-memory": "Virtual Memory",
|
||||||
"mongo.mapped-memory": "Mapped Memory",
|
"mongo.mapped-memory": "Mapped Memory",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"events": "Olaylar",
|
"events": "Olaylar",
|
||||||
"no-events": "Olay yok",
|
"no-events": "Olay yok",
|
||||||
"control-panel": "Events Control Panel",
|
"control-panel": "Etkinlik Kontrol Paneli",
|
||||||
"delete-events": "Olayları Sil"
|
"delete-events": "Olayları Sil"
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"custom-css": "Özel CSS",
|
"custom-css": "Özel CSS",
|
||||||
"custom-css.description": "Enter your own CSS declarations here, which will be applied after all other styles.",
|
"custom-css.description": "Özel CSS kodlarınızı bu alana girin.",
|
||||||
"custom-css.enable": "Özel CSS Aktif",
|
"custom-css.enable": "Özel CSS Etkinleştir",
|
||||||
|
|
||||||
"custom-header": "Özel Header",
|
"custom-header": "Özel Header",
|
||||||
"custom-header.description": "Enter custom HTML here (ex. JavaScript, Meta Tags, etc.), which will be appended to the <code><head></code> section of your forum's markup.",
|
"custom-header.description": "Forumunuzun biçimlendirmesini sağlayacak <code><head></code> bölümüne eklenecek özel HTML'yi (ör. JavaScript, Meta Etiketler vb.) Girin.",
|
||||||
"custom-header.enable": "Enable Custom Header"
|
"custom-header.enable": "Özel Header'ı Etkinleştir"
|
||||||
}
|
}
|
||||||
@@ -2,8 +2,8 @@
|
|||||||
"loading": "Deriler yükleniyor...",
|
"loading": "Deriler yükleniyor...",
|
||||||
"homepage": "Anasayfa",
|
"homepage": "Anasayfa",
|
||||||
"select-skin": "Deri Seç",
|
"select-skin": "Deri Seç",
|
||||||
"current-skin": "Current Skin",
|
"current-skin": "Mevcut Deri",
|
||||||
"skin-updated": "Skin Updated",
|
"skin-updated": "Deri Güncellendi",
|
||||||
"applied-success": "%1 skin was succesfully applied",
|
"applied-success": "%1 deri başarıyla uygulandı",
|
||||||
"revert-success": "Skin reverted to base colours"
|
"revert-success": "Deri taban renkleri geri döndürüldü"
|
||||||
}
|
}
|
||||||
@@ -2,10 +2,10 @@
|
|||||||
"checking-for-installed": "Yüklü temalar kontrol ediliyor...",
|
"checking-for-installed": "Yüklü temalar kontrol ediliyor...",
|
||||||
"homepage": "Anasayfa",
|
"homepage": "Anasayfa",
|
||||||
"select-theme": "Tema Seç",
|
"select-theme": "Tema Seç",
|
||||||
"current-theme": "Current Theme",
|
"current-theme": "Geçerli Tema",
|
||||||
"no-themes": "No installed themes found",
|
"no-themes": "Yüklü tema bulunamadı",
|
||||||
"revert-confirm": "Are you sure you wish to restore the default NodeBB theme?",
|
"revert-confirm": "Varsayılan NodeBB temasını geri yüklemek istediğinizden emin misiniz?",
|
||||||
"theme-changed": "Theme Changed",
|
"theme-changed": "Tema Değiştirildi",
|
||||||
"revert-success": "You have successfully reverted your NodeBB back to it's default theme.",
|
"revert-success": "NodeBB'nin varsayılan temasına başarıyla geri dönüş yaptınız.",
|
||||||
"restart-to-activate": "Please restart your NodeBB to fully activate this theme"
|
"restart-to-activate": "Temayı tamamen aktif hale getirebilmek için NodeBB'yi yeniden başlat"
|
||||||
}
|
}
|
||||||
@@ -5,14 +5,14 @@
|
|||||||
"users": "Kullanıcılar",
|
"users": "Kullanıcılar",
|
||||||
"posts": "İletiler",
|
"posts": "İletiler",
|
||||||
"topics": "Başlıklar",
|
"topics": "Başlıklar",
|
||||||
"page-views-seven": "Last 7 Days",
|
"page-views-seven": "Son 7 Gün",
|
||||||
"page-views-thirty": "Last 30 Days",
|
"page-views-thirty": "Son 30 Gün",
|
||||||
"page-views-last-day": "Last 24 hours",
|
"page-views-last-day": "Son 24 saat",
|
||||||
"page-views-custom": "Custom Date Range",
|
"page-views-custom": "Özel Tarih Aralığı",
|
||||||
"page-views-custom-start": "Range Start",
|
"page-views-custom-start": "Başlangıç",
|
||||||
"page-views-custom-end": "Range End",
|
"page-views-custom-end": "Son",
|
||||||
"page-views-custom-help": "Enter a date range of page views you would like to view. If no date picker is available, the accepted format is <code>YYYY-MM-DD</code>",
|
"page-views-custom-help": "Enter a date range of page views you would like to view. If no date picker is available, the accepted format is <code>YYYY-MM-DD</code>",
|
||||||
"page-views-custom-error": "Please enter a valid date range in the format <code>YYYY-MM-DD</code>",
|
"page-views-custom-error": "Lütfen tarih aralığını geçerli formatta girin <code>YYYY-MM-DD</code>",
|
||||||
|
|
||||||
"stats.day": "Gün",
|
"stats.day": "Gün",
|
||||||
"stats.week": "Hafta",
|
"stats.week": "Hafta",
|
||||||
@@ -36,9 +36,9 @@
|
|||||||
"search-plugin-tooltip": "Install a search plugin from the plugin page in order to activate search functionality",
|
"search-plugin-tooltip": "Install a search plugin from the plugin page in order to activate search functionality",
|
||||||
|
|
||||||
"control-panel": "Sistem Kontrol",
|
"control-panel": "Sistem Kontrol",
|
||||||
"reload": "Tekrar Yükle",
|
"reload": "Reload",
|
||||||
"restart": "Yeniden Başlat",
|
"restart": "Restart",
|
||||||
"restart-warning": "Reloading or Restarting your NodeBB will drop all existing connections for a few seconds.",
|
"restart-warning": "NodeBB yeniden yüklemek veya yeniden başlatmak için mevcut tüm bağlantıları birkaç saniye düşürür.",
|
||||||
"maintenance-mode": "Bakım Modu",
|
"maintenance-mode": "Bakım Modu",
|
||||||
"maintenance-mode-title": "NodeBB için bakım modunu ayarlamak için buraya tıklayın",
|
"maintenance-mode-title": "NodeBB için bakım modunu ayarlamak için buraya tıklayın",
|
||||||
"realtime-chart-updates": "Gerçek Zamanlı Grafik Güncellemeleri",
|
"realtime-chart-updates": "Gerçek Zamanlı Grafik Güncellemeleri",
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
"recent": "Yeni",
|
"recent": "Yeni",
|
||||||
"unread": "Okunmamış",
|
"unread": "Okunmamış",
|
||||||
|
|
||||||
"high-presence-topics": "High Presence Topics",
|
"high-presence-topics": "Öne Çıkan Başlıklar",
|
||||||
|
|
||||||
"graphs.page-views": "Sayfa Gösterimi",
|
"graphs.page-views": "Sayfa Gösterimi",
|
||||||
"graphs.unique-visitors": "Benzersiz Ziyaretçiler",
|
"graphs.unique-visitors": "Benzersiz Ziyaretçiler",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"home-page": "Ana Sayfa",
|
"home-page": "Ana Sayfa",
|
||||||
"description": "Choose what page is shown when users navigate to the root URL of your forum.",
|
"description": "Choose what page is shown when users navigate to the root URL of your forum.",
|
||||||
"home-page-route": "Home Page Route",
|
"home-page-route": "Anasayfa Yolu",
|
||||||
"custom-route": "Custom Route",
|
"custom-route": "Özel Yol",
|
||||||
"allow-user-home-pages": "Allow User Home Pages"
|
"allow-user-home-pages": "Kullanıcılara anasayfayı özelleştirmeye izin ver"
|
||||||
}
|
}
|
||||||
@@ -2,8 +2,8 @@
|
|||||||
"notifications": "Bildiriler",
|
"notifications": "Bildiriler",
|
||||||
"chat-messages": "Sohbet Mesajları",
|
"chat-messages": "Sohbet Mesajları",
|
||||||
"play-sound": "Oynat",
|
"play-sound": "Oynat",
|
||||||
"incoming-message": "Incoming Message",
|
"incoming-message": "Gelen İleti",
|
||||||
"outgoing-message": "Outgoing Message",
|
"outgoing-message": "Giden İleti",
|
||||||
"upload-new-sound": "Yeni Ses Yükle",
|
"upload-new-sound": "Yeni Ses Yükle",
|
||||||
"saved": "Settings Saved"
|
"saved": "Ayarlar Kaydedildi"
|
||||||
}
|
}
|
||||||
@@ -1,30 +1,30 @@
|
|||||||
{
|
{
|
||||||
"settings": "Kategori Ayarları",
|
"settings": "Kategori Ayarları",
|
||||||
"privileges": "Privileges",
|
"privileges": "İzinler",
|
||||||
|
|
||||||
"name": "Kategori Adı",
|
"name": "Kategori Adı",
|
||||||
"description": "Category Description",
|
"description": "Kategori Açıklama",
|
||||||
"bg-color": "Background Colour",
|
"bg-color": "Arkaplan Rengi",
|
||||||
"text-color": "Yazı Rengi",
|
"text-color": "Yazı Rengi",
|
||||||
"bg-image-size": "Arkaplan Görseli Boyutu",
|
"bg-image-size": "Arkaplan Görseli Boyutu",
|
||||||
"custom-class": "Özel Sınıf",
|
"custom-class": "Özel Sınıf",
|
||||||
"num-recent-replies": "# of Recent Replies",
|
"num-recent-replies": "# of Recent Replies",
|
||||||
"ext-link": "External Link",
|
"ext-link": "Harici Bağlantı",
|
||||||
"upload-image": "Görsel Yükle",
|
"upload-image": "Görsel Yükle",
|
||||||
"delete-image": "Remove",
|
"delete-image": "Sil",
|
||||||
"category-image": "Category Image",
|
"category-image": "Kategori Görseli",
|
||||||
"parent-category": "Parent Category",
|
"parent-category": "Parent Category",
|
||||||
"optional-parent-category": "(Optional) Parent Category",
|
"optional-parent-category": "(Optional) Parent Category",
|
||||||
"parent-category-none": "(None)",
|
"parent-category-none": "(Hiçbiri)",
|
||||||
"copy-settings": "Copy Settings From",
|
"copy-settings": "Copy Settings From",
|
||||||
"optional-clone-settings": "(Optional) Clone Settings From Category",
|
"optional-clone-settings": "(Optional) Clone Settings From Category",
|
||||||
"purge": "Purge Category",
|
"purge": "Purge Category",
|
||||||
|
|
||||||
"enable": "Enable",
|
"enable": "Etkinleştir",
|
||||||
"disable": "Disable",
|
"disable": "Devredışı",
|
||||||
"edit": "Edit",
|
"edit": "Düzenle",
|
||||||
|
|
||||||
"select-category": "Select Category",
|
"select-category": "Kategori Seç",
|
||||||
"set-parent-category": "Set Parent Category",
|
"set-parent-category": "Set Parent Category",
|
||||||
|
|
||||||
"privileges.description": "You can configure the access control privileges for this category in this section. Privileges can be granted on a per-user or a per-group basis. You can add a new user to this table by searching for them in the form below.",
|
"privileges.description": "You can configure the access control privileges for this category in this section. Privileges can be granted on a per-user or a per-group basis. You can add a new user to this table by searching for them in the form below.",
|
||||||
@@ -32,27 +32,27 @@
|
|||||||
"privileges.section-viewing": "Viewing Privileges",
|
"privileges.section-viewing": "Viewing Privileges",
|
||||||
"privileges.section-posting": "Posting Privileges",
|
"privileges.section-posting": "Posting Privileges",
|
||||||
"privileges.section-moderation": "Moderation Privileges",
|
"privileges.section-moderation": "Moderation Privileges",
|
||||||
"privileges.section-user": "User",
|
"privileges.section-user": "Kullanıcı",
|
||||||
"privileges.search-user": "Add User",
|
"privileges.search-user": "Kullanıcı Ekle",
|
||||||
"privileges.no-users": "No user-specific privileges in this category.",
|
"privileges.no-users": "No user-specific privileges in this category.",
|
||||||
"privileges.section-group": "Group",
|
"privileges.section-group": "Grup",
|
||||||
"privileges.group-private": "This group is private",
|
"privileges.group-private": "Bu grup gizlidir",
|
||||||
"privileges.search-group": "Add Group",
|
"privileges.search-group": "Grup Ekle",
|
||||||
"privileges.copy-to-children": "Copy to Children",
|
"privileges.copy-to-children": "Copy to Children",
|
||||||
"privileges.copy-from-category": "Copy from Category",
|
"privileges.copy-from-category": "Copy from Category",
|
||||||
"privileges.inherit": "If the <code>registered-users</code> group is granted a specific privilege, all other groups receive an <strong>implicit privilege</strong>, even if they are not explicitly defined/checked. This implicit privilege is shown to you because all users are part of the <code>registered-users</code> user group, and so, privileges for additional groups need not be explicitly granted.",
|
"privileges.inherit": "If the <code>registered-users</code> group is granted a specific privilege, all other groups receive an <strong>implicit privilege</strong>, even if they are not explicitly defined/checked. This implicit privilege is shown to you because all users are part of the <code>registered-users</code> user group, and so, privileges for additional groups need not be explicitly granted.",
|
||||||
|
|
||||||
"analytics.back": "Back to Categories List",
|
"analytics.back": "Kategori listesine geri dön",
|
||||||
"analytics.title": "Analytics for \"%1\" category",
|
"analytics.title": "Analytics for \"%1\" category",
|
||||||
"analytics.pageviews-hourly": "<strong>Figure 1</strong> – Hourly page views for this category</small>",
|
"analytics.pageviews-hourly": "<strong>Figure 1</strong> – Hourly page views for this category</small>",
|
||||||
"analytics.pageviews-daily": "<strong>Figure 2</strong> – Daily page views for this category</small>",
|
"analytics.pageviews-daily": "<strong>Figure 2</strong> – Daily page views for this category</small>",
|
||||||
"analytics.topics-daily": "<strong>Figure 3</strong> – Daily topics created in this category</small>",
|
"analytics.topics-daily": "<strong>Figure 3</strong> – Daily topics created in this category</small>",
|
||||||
"analytics.posts-daily": "<strong>Figure 4</strong> – Daily posts made in this category</small>",
|
"analytics.posts-daily": "<strong>Figure 4</strong> – Daily posts made in this category</small>",
|
||||||
|
|
||||||
"alert.created": "Created",
|
"alert.created": "Yaratıldı",
|
||||||
"alert.create-success": "Category successfully created!",
|
"alert.create-success": "Category successfully created!",
|
||||||
"alert.none-active": "You have no active categories.",
|
"alert.none-active": "You have no active categories.",
|
||||||
"alert.create": "Create a Category",
|
"alert.create": "Bir Kategori Yarat",
|
||||||
"alert.confirm-moderate": "<strong>Are you sure you wish to grant the moderation privilege to this user group?</strong> This group is public, and any users can join at will.",
|
"alert.confirm-moderate": "<strong>Are you sure you wish to grant the moderation privilege to this user group?</strong> This group is public, and any users can join at will.",
|
||||||
"alert.confirm-purge": "<p class=\"lead\">Do you really want to purge this category \"%1\"?</p><h5><strong class=\"text-danger\">Warning!</strong> All topics and posts in this category will be purged!</h5> <p class=\"help-block\">Purging a category will remove all topics and posts, and delete the category from the database. If you want to remove a category <em>temporarily</em>, you'll want to \"disable\" the category instead.</p>",
|
"alert.confirm-purge": "<p class=\"lead\">Do you really want to purge this category \"%1\"?</p><h5><strong class=\"text-danger\">Warning!</strong> All topics and posts in this category will be purged!</h5> <p class=\"help-block\">Purging a category will remove all topics and posts, and delete the category from the database. If you want to remove a category <em>temporarily</em>, you'll want to \"disable\" the category instead.</p>",
|
||||||
"alert.purge-success": "Category purged!",
|
"alert.purge-success": "Category purged!",
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
"alert.updated": "Updated Categories",
|
"alert.updated": "Updated Categories",
|
||||||
"alert.updated-success": "Category IDs %1 successfully updated.",
|
"alert.updated-success": "Category IDs %1 successfully updated.",
|
||||||
"alert.upload-image": "Kategori görseli yükle",
|
"alert.upload-image": "Kategori görseli yükle",
|
||||||
"alert.find-user": "Find a User",
|
"alert.find-user": "Bir Kullanıcı Ara",
|
||||||
"alert.user-search": "Search for a user here...",
|
"alert.user-search": "Search for a user here...",
|
||||||
"alert.find-group": "Find a Group",
|
"alert.find-group": "Find a Group",
|
||||||
"alert.group-search": "Search for a group here..."
|
"alert.group-search": "Search for a group here..."
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"users": "Kullanıcılar",
|
"users": "Kullanıcılar",
|
||||||
"edit": "Düzenle",
|
"edit": "Düzenle",
|
||||||
"make-admin": "Yönetici Yap",
|
"make-admin": "Yönetici Yap",
|
||||||
"remove-admin": "Remove Admin",
|
"remove-admin": "Yöneticiliği Sil",
|
||||||
"validate-email": "Validate Email",
|
"validate-email": "Validate Email",
|
||||||
"send-validation-email": "Send Validation Email",
|
"send-validation-email": "Send Validation Email",
|
||||||
"password-reset-email": "Send Password Reset Email",
|
"password-reset-email": "Send Password Reset Email",
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
"temp-ban": "Ban User(s) Temporarily",
|
"temp-ban": "Ban User(s) Temporarily",
|
||||||
"unban": "Unban User(s)",
|
"unban": "Unban User(s)",
|
||||||
"reset-lockout": "Reset Lockout",
|
"reset-lockout": "Reset Lockout",
|
||||||
"reset-flags": "Reset Flags",
|
"reset-flags": "Bayrakları Sıfırla",
|
||||||
"delete": "Delete User(s)",
|
"delete": "Delete User(s)",
|
||||||
"purge": "Delete User(s) and Content",
|
"purge": "Delete User(s) and Content",
|
||||||
"download-csv": "Download CSV",
|
"download-csv": "Download CSV",
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
"pills.top-rep": "Most Reputation",
|
"pills.top-rep": "Most Reputation",
|
||||||
"pills.inactive": "Inactive",
|
"pills.inactive": "Inactive",
|
||||||
"pills.flagged": "Most Flagged",
|
"pills.flagged": "Most Flagged",
|
||||||
"pills.banned": "Banned",
|
"pills.banned": "Yasaklandı",
|
||||||
"pills.search": "User Search",
|
"pills.search": "User Search",
|
||||||
|
|
||||||
"search.username": "By User Name",
|
"search.username": "By User Name",
|
||||||
|
|||||||
@@ -8,19 +8,19 @@
|
|||||||
"max-file-size-help": "(in kilobytes, default: 2048 KiB)",
|
"max-file-size-help": "(in kilobytes, default: 2048 KiB)",
|
||||||
"allow-topic-thumbnails": "Allow users to upload topic thumbnails",
|
"allow-topic-thumbnails": "Allow users to upload topic thumbnails",
|
||||||
"topic-thumb-size": "Topic Thumb Size",
|
"topic-thumb-size": "Topic Thumb Size",
|
||||||
"allowed-file-extensions": "Allowed File Extensions",
|
"allowed-file-extensions": "İzin Verilen Dosya Uzantıları",
|
||||||
"allowed-file-extensions-help": "Enter comma-separated list of file extensions here (e.g. <code>pdf,xls,doc</code>). An empty list means all extensions are allowed.",
|
"allowed-file-extensions-help": "Enter comma-separated list of file extensions here (e.g. <code>pdf,xls,doc</code>). An empty list means all extensions are allowed.",
|
||||||
"profile-avatars": "Profile Avatars",
|
"profile-avatars": "Profil Avatarları",
|
||||||
"allow-profile-image-uploads": "Allow users to upload profile images",
|
"allow-profile-image-uploads": "Allow users to upload profile images",
|
||||||
"convert-profile-image-png": "Convert profile image uploads to PNG",
|
"convert-profile-image-png": "Convert profile image uploads to PNG",
|
||||||
"default-avatar": "Custom Default Avatar",
|
"default-avatar": "Custom Default Avatar",
|
||||||
"upload": "Yükle",
|
"upload": "Yükle",
|
||||||
"profile-image-dimension": "Profile Image Dimension",
|
"profile-image-dimension": "Profil Resmi Boyutu",
|
||||||
"profile-image-dimension-help": "(in pixels, default: 128 pixels)",
|
"profile-image-dimension-help": "(Piksel cinsinden, varsayılan: 128 piksel)",
|
||||||
"max-profile-image-size": "Maximum Profile Image File Size",
|
"max-profile-image-size": "Maximum Profile Image File Size",
|
||||||
"max-profile-image-size-help": "(in kilobytes, default: 256 KiB)",
|
"max-profile-image-size-help": "(Kilobayt, varsayılan: 256 KiB)",
|
||||||
"max-cover-image-size": "Maksimum Kapak Görseli Dosya Boyutu",
|
"max-cover-image-size": "Maksimum Kapak Görseli Dosya Boyutu",
|
||||||
"max-cover-image-size-help": "(in kilobytes, default: 2,048 KiB)",
|
"max-cover-image-size-help": "(Kilobayt, varsayılan: 2,048 KiB)",
|
||||||
"keep-all-user-images": "Keep old versions of avatars and profile covers on the server",
|
"keep-all-user-images": "Keep old versions of avatars and profile covers on the server",
|
||||||
"profile-covers": "Profil Kapakları",
|
"profile-covers": "Profil Kapakları",
|
||||||
"default-covers": "Varsayılan Kapak Görseli",
|
"default-covers": "Varsayılan Kapak Görseli",
|
||||||
|
|||||||
@@ -32,9 +32,9 @@
|
|||||||
"notif.post.unsub.info": "Bu yazı bildirimi size abonelik ayarlarınız nedeni ile gönderilmiştir.",
|
"notif.post.unsub.info": "Bu yazı bildirimi size abonelik ayarlarınız nedeni ile gönderilmiştir.",
|
||||||
"test.text1": "Bu ileti NodeBB e-posta ayarlarınızın doğru çalışıp çalışmadığını kontrol etmek için gönderildi.",
|
"test.text1": "Bu ileti NodeBB e-posta ayarlarınızın doğru çalışıp çalışmadığını kontrol etmek için gönderildi.",
|
||||||
"unsub.cta": "Buraya tıklayarak ayarlarınızı değiştirebilirsiniz.",
|
"unsub.cta": "Buraya tıklayarak ayarlarınızı değiştirebilirsiniz.",
|
||||||
"banned.subject": "You have been banned from %1",
|
"banned.subject": "%1 'den yasaklandınız",
|
||||||
"banned.text1": "The user %1 has been banned from %2.",
|
"banned.text1": "%1 kullanıcısı %2 'den yasaklandı.",
|
||||||
"banned.text2": "This ban will last until %1.",
|
"banned.text2": "Bu yasak %1 'e kadar sürecek.",
|
||||||
"banned.text3": "This is the reason why you have been banned:",
|
"banned.text3": "Yasaklanmanın nedeni:",
|
||||||
"closing": "Teşekkürler!"
|
"closing": "Teşekkürler!"
|
||||||
}
|
}
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
"password-too-long": "Parola çok uzun",
|
"password-too-long": "Parola çok uzun",
|
||||||
"user-banned": "Kullanıcı Yasaklı",
|
"user-banned": "Kullanıcı Yasaklı",
|
||||||
"user-banned-reason": "Maalesef, bu hesap yasaklandı (Sebep:% 1)",
|
"user-banned-reason": "Maalesef, bu hesap yasaklandı (Sebep:% 1)",
|
||||||
"user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)",
|
"user-banned-reason-until": "Maalesef, bu hesap %1 kadar yasaklandı (Sebep: %2)",
|
||||||
"user-too-new": "Özür dileriz, ilk iletinizi yapmadan önce %1 saniye beklemeniz gerekiyor",
|
"user-too-new": "Özür dileriz, ilk iletinizi yapmadan önce %1 saniye beklemeniz gerekiyor",
|
||||||
"blacklisted-ip": "Üzgünüz, IP adresiniz, bu toplulukta yasaklandı. Bunun bir hata olduğunu düşünüyorsanız, bir yönetici ile irtibata geçiniz.",
|
"blacklisted-ip": "Üzgünüz, IP adresiniz, bu toplulukta yasaklandı. Bunun bir hata olduğunu düşünüyorsanız, bir yönetici ile irtibata geçiniz.",
|
||||||
"ban-expiry-missing": "Bu yasak için bir bitiş tarihi girin",
|
"ban-expiry-missing": "Bu yasak için bir bitiş tarihi girin",
|
||||||
@@ -105,7 +105,7 @@
|
|||||||
"chat-disabled": "Sohbet özelliği kapalı",
|
"chat-disabled": "Sohbet özelliği kapalı",
|
||||||
"too-many-messages": "Ardı ardına çok fazla mesaj yolladınız, lütfen biraz bekleyiniz.",
|
"too-many-messages": "Ardı ardına çok fazla mesaj yolladınız, lütfen biraz bekleyiniz.",
|
||||||
"invalid-chat-message": "Geçersiz sohbet mesajı",
|
"invalid-chat-message": "Geçersiz sohbet mesajı",
|
||||||
"chat-message-too-long": "Chat messages can not be longer than %1 characters.",
|
"chat-message-too-long": "Sohbet mesajı %1 karakterden daha uzun olamaz.",
|
||||||
"cant-edit-chat-message": "Bu mesajı düzenlemek için izin verilmez",
|
"cant-edit-chat-message": "Bu mesajı düzenlemek için izin verilmez",
|
||||||
"cant-remove-last-user": "Son kullanıcıyı silemezsiniz",
|
"cant-remove-last-user": "Son kullanıcıyı silemezsiniz",
|
||||||
"cant-delete-chat-message": "Bu mesajı silmek için izin verilmez",
|
"cant-delete-chat-message": "Bu mesajı silmek için izin verilmez",
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
"chat.three_months": "3 Ay",
|
"chat.three_months": "3 Ay",
|
||||||
"chat.delete_message_confirm": "Bu mesajı silmek istediğinden emin misin?",
|
"chat.delete_message_confirm": "Bu mesajı silmek istediğinden emin misin?",
|
||||||
"chat.add-users-to-room": "Odaya Kullanıcı Ekle",
|
"chat.add-users-to-room": "Odaya Kullanıcı Ekle",
|
||||||
"chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?",
|
"chat.confirm-chat-with-dnd-user": "Bu kullanıcı durumunu rahatsız etmeyin olarak ayarladı. Hala onunla sohbet etmek istiyor musun?",
|
||||||
"composer.compose": "Yaz",
|
"composer.compose": "Yaz",
|
||||||
"composer.show_preview": "Önizleme Göster",
|
"composer.show_preview": "Önizleme Göster",
|
||||||
"composer.hide_preview": "Önizleme Sakla",
|
"composer.hide_preview": "Önizleme Sakla",
|
||||||
|
|||||||
@@ -40,7 +40,7 @@
|
|||||||
"user_started_following_you_multiple": "<strong>%1</strong> ve %2 kişi daha seni takip etmeye başladı.",
|
"user_started_following_you_multiple": "<strong>%1</strong> ve %2 kişi daha seni takip etmeye başladı.",
|
||||||
"new_register": "<strong>%1</strong> kayıt olma isteği gönderdi.",
|
"new_register": "<strong>%1</strong> kayıt olma isteği gönderdi.",
|
||||||
"new_register_multiple": "Beklemede <strong>%1</strong> kayıt olma isteği bulunmaktadır.",
|
"new_register_multiple": "Beklemede <strong>%1</strong> kayıt olma isteği bulunmaktadır.",
|
||||||
"flag_assigned_to_you": "<strong>Flag %1</strong> has been assigned to you",
|
"flag_assigned_to_you": "<Strong>Bayrak %1</ strong> size devredildi",
|
||||||
"email-confirmed": "E-posta onaylandı",
|
"email-confirmed": "E-posta onaylandı",
|
||||||
"email-confirmed-message": "E-postanızı onaylandığınız için teşekkürler. Hesabınız tamamen aktive edildi.",
|
"email-confirmed-message": "E-postanızı onaylandığınız için teşekkürler. Hesabınız tamamen aktive edildi.",
|
||||||
"email-confirm-error-message": "E-posta adresinizi onaylarken bir hata oluştu. Kodunuz geçersiz ya da eski olabilir.",
|
"email-confirm-error-message": "E-posta adresinizi onaylarken bir hata oluştu. Kodunuz geçersiz ya da eski olabilir.",
|
||||||
|
|||||||
@@ -89,8 +89,19 @@ define('forum/topic/replies', ['navigator', 'components', 'forum/topic/posts'],
|
|||||||
var timestamp = replyCount.find('.timeago').attr('title', post.timestampISO);
|
var timestamp = replyCount.find('.timeago').attr('title', post.timestampISO);
|
||||||
|
|
||||||
countEl.attr('data-replies', count);
|
countEl.attr('data-replies', count);
|
||||||
replyCount.toggleClass('hidden', !count);
|
replyCount.toggleClass('hidden', count <= 0);
|
||||||
countEl.translateText('[[topic:replies_to_this_post, ' + count + ']]');
|
if (count > 1) {
|
||||||
|
countEl.translateText('[[topic:replies_to_this_post, ' + count + ']]');
|
||||||
|
} else {
|
||||||
|
countEl.translateText('[[topic:one_reply_to_this_post]]');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!avatars.find('[data-uid="' + post.uid + '"]').length && count < 7) {
|
||||||
|
app.parseAndTranslate('topic', 'posts', { posts: [{ replies: { users: [post.user] } }] }, function (html) {
|
||||||
|
avatars.prepend(html.find('[component="post/reply-count/avatars"] [component="user/picture"]'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
avatars.addClass('hasMore');
|
avatars.addClass('hasMore');
|
||||||
|
|
||||||
timestamp.data('timeago', null).timeago();
|
timestamp.data('timeago', null).timeago();
|
||||||
|
|||||||
@@ -304,7 +304,7 @@
|
|||||||
* Load translation file (or use a cached version), and optionally return the translation of a certain key
|
* Load translation file (or use a cached version), and optionally return the translation of a certain key
|
||||||
* @param {string} namespace - The file name of the translation namespace
|
* @param {string} namespace - The file name of the translation namespace
|
||||||
* @param {string} [key] - The key of the specific translation to getJSON
|
* @param {string} [key] - The key of the specific translation to getJSON
|
||||||
* @returns {Promise<Object>|Promise<string>}
|
* @returns {Promise<{ [key: string]: string }>|Promise<string>}
|
||||||
*/
|
*/
|
||||||
Translator.prototype.getTranslation = function getTranslation(namespace, key) {
|
Translator.prototype.getTranslation = function getTranslation(namespace, key) {
|
||||||
var translation;
|
var translation;
|
||||||
@@ -324,6 +324,70 @@
|
|||||||
return translation;
|
return translation;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Node} node
|
||||||
|
* @returns {Node[]}
|
||||||
|
*/
|
||||||
|
function descendantTextNodes(node) {
|
||||||
|
var textNodes = [];
|
||||||
|
|
||||||
|
function helper(node) {
|
||||||
|
if (node.nodeType === 3) {
|
||||||
|
textNodes.push(node);
|
||||||
|
} else {
|
||||||
|
for (var i = 0, c = node.childNodes, l = c.length; i < l; i += 1) {
|
||||||
|
helper(c[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
helper(node);
|
||||||
|
return textNodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively translate a DOM element in place
|
||||||
|
* @param {Element} element - Root element to translate
|
||||||
|
* @param {string[]} [attributes] - Array of node attributes to translate
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
Translator.prototype.translateInPlace = function translateInPlace(element, attributes) {
|
||||||
|
attributes = attributes || ['placeholder', 'title'];
|
||||||
|
|
||||||
|
var nodes = descendantTextNodes(element);
|
||||||
|
var text = nodes.map(function (node) {
|
||||||
|
return node.nodeValue;
|
||||||
|
}).join(' || ');
|
||||||
|
|
||||||
|
var attrNodes = attributes.reduce(function (prev, attr) {
|
||||||
|
var tuples = Array.prototype.map.call(element.querySelectorAll('[' + attr + '*="[["]'), function (el) {
|
||||||
|
return [attr, el];
|
||||||
|
});
|
||||||
|
return prev.concat(tuples);
|
||||||
|
}, []);
|
||||||
|
var attrText = attrNodes.map(function (node) {
|
||||||
|
return node[1].getAttribute(node[0]);
|
||||||
|
}).join(' || ');
|
||||||
|
|
||||||
|
return Promise.all([
|
||||||
|
this.translate(text),
|
||||||
|
this.translate(attrText),
|
||||||
|
]).then(function (ref) {
|
||||||
|
var translated = ref[0];
|
||||||
|
var translatedAttrs = ref[1];
|
||||||
|
if (translated) {
|
||||||
|
translated.split(' || ').forEach(function (html, i) {
|
||||||
|
$(nodes[i]).replaceWith(html);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (translatedAttrs) {
|
||||||
|
translatedAttrs.split(' || ').forEach(function (text, i) {
|
||||||
|
attrNodes[i][1].setAttribute(attrNodes[i][0], text);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the language of the current environment, falling back to defaults
|
* Get the language of the current environment, falling back to defaults
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
|
|||||||
59
public/vendor/bootbox/wrapper.js
vendored
59
public/vendor/bootbox/wrapper.js
vendored
@@ -1,67 +1,20 @@
|
|||||||
/* global bootbox */
|
/* global bootbox */
|
||||||
|
|
||||||
require(['translator'], function (shim) {
|
require(['translator'], function (shim) {
|
||||||
"use strict";
|
'use strict';
|
||||||
|
|
||||||
function descendantTextNodes(node) {
|
|
||||||
var textNodes = [];
|
|
||||||
|
|
||||||
function helper(node) {
|
|
||||||
if (node.nodeType === 3) {
|
|
||||||
textNodes.push(node);
|
|
||||||
} else {
|
|
||||||
for (var i = 0, c = node.childNodes, l = c.length; i < l; i += 1) {
|
|
||||||
helper(c[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
helper(node);
|
|
||||||
return textNodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
var translator = shim.Translator.create();
|
var translator = shim.Translator.create();
|
||||||
var dialog = bootbox.dialog;
|
var dialog = bootbox.dialog;
|
||||||
var attrsToTranslate = ['placeholder', 'title', 'value'];
|
var attrsToTranslate = ['placeholder', 'title', 'value'];
|
||||||
bootbox.dialog = function (options) {
|
bootbox.dialog = function (options) {
|
||||||
var show, $elem, nodes, text, attrNodes, attrText;
|
var show = options.show !== false;
|
||||||
|
|
||||||
show = options.show !== false;
|
|
||||||
options.show = false;
|
options.show = false;
|
||||||
|
|
||||||
$elem = dialog.call(bootbox, options);
|
var $elem = dialog.call(bootbox, options);
|
||||||
|
var element = $elem[0];
|
||||||
|
|
||||||
if (/\[\[.+\]\]/.test($elem[0].outerHTML)) {
|
if (/\[\[.+\]\]/.test(element.outerHTML)) {
|
||||||
nodes = descendantTextNodes($elem[0]);
|
translator.translateInPlace(element, attrsToTranslate).then(function () {
|
||||||
text = nodes.map(function (node) {
|
|
||||||
return node.nodeValue;
|
|
||||||
}).join(' || ');
|
|
||||||
|
|
||||||
attrNodes = attrsToTranslate.reduce(function (prev, attr) {
|
|
||||||
return prev.concat(nodes.map.call($elem.find('[' + attr + '*="[["]'), function (el) {
|
|
||||||
return [attr, el];
|
|
||||||
}));
|
|
||||||
}, []);
|
|
||||||
attrText = attrNodes.map(function (node) {
|
|
||||||
return node[1].getAttribute(node[0]);
|
|
||||||
}).join(' || ');
|
|
||||||
|
|
||||||
Promise.all([
|
|
||||||
translator.translate(text),
|
|
||||||
translator.translate(attrText),
|
|
||||||
]).then(function (ref) {
|
|
||||||
var translated = ref[0];
|
|
||||||
var translatedAttrs = ref[1];
|
|
||||||
if (translated) {
|
|
||||||
translated.split(' || ').forEach(function (html, i) {
|
|
||||||
$(nodes[i]).replaceWith(html);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (translatedAttrs) {
|
|
||||||
translatedAttrs.split(' || ').forEach(function (text, i) {
|
|
||||||
attrNodes[i][1].setAttribute(attrNodes[i][0], text);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (show) {
|
if (show) {
|
||||||
$elem.modal('show');
|
$elem.modal('show');
|
||||||
}
|
}
|
||||||
|
|||||||
9921
public/vendor/jquery/js/jquery.js
vendored
9921
public/vendor/jquery/js/jquery.js
vendored
File diff suppressed because it is too large
Load Diff
@@ -30,18 +30,20 @@ exports.handle404 = function (req, res) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
meta.errors.log404(req.path.replace(/^\/api/, '') || '');
|
meta.errors.log404(req.path.replace(/^\/api/, '') || '');
|
||||||
res.status(404);
|
exports.send404(req, res);
|
||||||
|
|
||||||
var path = String(req.path || '');
|
|
||||||
|
|
||||||
if (res.locals.isAPI) {
|
|
||||||
return res.json({ path: validator.escape(path.replace(/^\/api/, '')), title: '[[global:404.title]]' });
|
|
||||||
}
|
|
||||||
var middleware = require('../middleware');
|
|
||||||
middleware.buildHeader(req, res, function () {
|
|
||||||
res.render('404', { path: validator.escape(path), title: '[[global:404.title]]' });
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
res.status(404).type('txt').send('Not found');
|
res.status(404).type('txt').send('Not found');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
exports.send404 = function (req, res) {
|
||||||
|
res.status(404);
|
||||||
|
var path = String(req.path || '');
|
||||||
|
if (res.locals.isAPI) {
|
||||||
|
return res.json({ path: validator.escape(path.replace(/^\/api/, '')), title: '[[global:404.title]]' });
|
||||||
|
}
|
||||||
|
var middleware = require('../middleware');
|
||||||
|
middleware.buildHeader(req, res, function () {
|
||||||
|
res.render('404', { path: validator.escape(path), title: '[[global:404.title]]' });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -26,13 +26,7 @@ authenticationController.register = function (req, res) {
|
|||||||
return res.sendStatus(403);
|
return res.sendStatus(403);
|
||||||
}
|
}
|
||||||
|
|
||||||
var userData = {};
|
var userData = req.body;
|
||||||
|
|
||||||
for (var key in req.body) {
|
|
||||||
if (req.body.hasOwnProperty(key)) {
|
|
||||||
userData[key] = req.body[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
function (next) {
|
function (next) {
|
||||||
@@ -88,7 +82,7 @@ authenticationController.register = function (req, res) {
|
|||||||
return res.status(400).send(err.message);
|
return res.status(400).send(err.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req.body.userLang) {
|
if (data.uid && req.body.userLang) {
|
||||||
user.setSetting(data.uid, 'userLang', req.body.userLang);
|
user.setSetting(data.uid, 'userLang', req.body.userLang);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,21 +97,18 @@ function registerAndLoginUser(req, res, userData, callback) {
|
|||||||
plugins.fireHook('filter:register.interstitial', {
|
plugins.fireHook('filter:register.interstitial', {
|
||||||
userData: userData,
|
userData: userData,
|
||||||
interstitials: [],
|
interstitials: [],
|
||||||
}, function (err, data) {
|
}, next);
|
||||||
if (err) {
|
},
|
||||||
return next(err);
|
function (data, next) {
|
||||||
}
|
// If interstitials are found, save registration attempt into session and abort
|
||||||
|
var deferRegistration = data.interstitials.length;
|
||||||
|
|
||||||
// If interstitials are found, save registration attempt into session and abort
|
if (!deferRegistration) {
|
||||||
var deferRegistration = data.interstitials.length;
|
return next();
|
||||||
|
}
|
||||||
if (!deferRegistration) {
|
userData.register = true;
|
||||||
return next();
|
req.session.registration = userData;
|
||||||
}
|
return res.json({ referrer: nconf.get('relative_path') + '/register/complete' });
|
||||||
userData.register = true;
|
|
||||||
req.session.registration = userData;
|
|
||||||
return res.json({ referrer: nconf.get('relative_path') + '/register/complete' });
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
function (next) {
|
function (next) {
|
||||||
user.create(userData, next);
|
user.create(userData, next);
|
||||||
@@ -282,14 +273,14 @@ authenticationController.doLogin = function (req, uid, callback) {
|
|||||||
if (!uid) {
|
if (!uid) {
|
||||||
return callback();
|
return callback();
|
||||||
}
|
}
|
||||||
|
async.waterfall([
|
||||||
req.login({ uid: uid }, function (err) {
|
function (next) {
|
||||||
if (err) {
|
req.login({ uid: uid }, next);
|
||||||
return callback(err);
|
},
|
||||||
}
|
function (next) {
|
||||||
|
authenticationController.onSuccessfulLogin(req, uid, next);
|
||||||
authenticationController.onSuccessfulLogin(req, uid, callback);
|
},
|
||||||
});
|
], callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
authenticationController.onSuccessfulLogin = function (req, uid, callback) {
|
authenticationController.onSuccessfulLogin = function (req, uid, callback) {
|
||||||
@@ -312,28 +303,30 @@ authenticationController.onSuccessfulLogin = function (req, uid, callback) {
|
|||||||
version: req.useragent.version,
|
version: req.useragent.version,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Associate login session with user
|
async.waterfall([
|
||||||
async.parallel([
|
|
||||||
function (next) {
|
function (next) {
|
||||||
user.auth.addSession(uid, req.sessionID, next);
|
async.parallel([
|
||||||
|
function (next) {
|
||||||
|
user.auth.addSession(uid, req.sessionID, next);
|
||||||
|
},
|
||||||
|
function (next) {
|
||||||
|
db.setObjectField('uid:' + uid + ':sessionUUID:sessionId', uuid, req.sessionID, next);
|
||||||
|
},
|
||||||
|
function (next) {
|
||||||
|
user.updateLastOnlineTime(uid, next);
|
||||||
|
},
|
||||||
|
], function (err) {
|
||||||
|
next(err);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
function (next) {
|
function (next) {
|
||||||
db.setObjectField('uid:' + uid + ':sessionUUID:sessionId', uuid, req.sessionID, next);
|
// Force session check for all connected socket.io clients with the same session id
|
||||||
},
|
sockets.in('sess_' + req.sessionID).emit('checkSession', uid);
|
||||||
function (next) {
|
|
||||||
user.updateLastOnlineTime(uid, next);
|
|
||||||
},
|
|
||||||
], function (err) {
|
|
||||||
if (err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Force session check for all connected socket.io clients with the same session id
|
plugins.fireHook('action:user.loggedIn', { uid: uid, req: req });
|
||||||
sockets.in('sess_' + req.sessionID).emit('checkSession', uid);
|
next();
|
||||||
|
},
|
||||||
plugins.fireHook('action:user.loggedIn', { uid: uid, req: req });
|
], callback);
|
||||||
callback();
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
authenticationController.localLogin = function (req, username, password, next) {
|
authenticationController.localLogin = function (req, username, password, next) {
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ topicsController.get = function (req, res, callback) {
|
|||||||
|
|
||||||
userPrivileges = results.privileges;
|
userPrivileges = results.privileges;
|
||||||
|
|
||||||
if (!userPrivileges.read || !userPrivileges['topics:read'] || (parseInt(results.topic.deleted, 10) && !userPrivileges.view_deleted)) {
|
if (!userPrivileges['topics:read'] || (parseInt(results.topic.deleted, 10) && !userPrivileges.view_deleted)) {
|
||||||
return helpers.notAllowed(req, res);
|
return helpers.notAllowed(req, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -161,8 +161,11 @@ mongoModule.createIndices = function (callback) {
|
|||||||
|
|
||||||
mongoModule.checkCompatibility = function (callback) {
|
mongoModule.checkCompatibility = function (callback) {
|
||||||
var mongoPkg = require('mongodb/package.json');
|
var mongoPkg = require('mongodb/package.json');
|
||||||
|
mongoModule.checkCompatibilityVersion(mongoPkg.version, callback);
|
||||||
|
};
|
||||||
|
|
||||||
if (semver.lt(mongoPkg.version, '2.0.0')) {
|
mongoModule.checkCompatibilityVersion = function (version, callback) {
|
||||||
|
if (semver.lt(version, '2.0.0')) {
|
||||||
return callback(new Error('The `mongodb` package is out-of-date, please run `./nodebb setup` again.'));
|
return callback(new Error('The `mongodb` package is out-of-date, please run `./nodebb setup` again.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,66 +176,75 @@ mongoModule.info = function (db, callback) {
|
|||||||
if (!db) {
|
if (!db) {
|
||||||
return callback();
|
return callback();
|
||||||
}
|
}
|
||||||
async.parallel({
|
async.waterfall([
|
||||||
serverStatus: function (next) {
|
function (next) {
|
||||||
db.command({ serverStatus: 1 }, next);
|
async.parallel({
|
||||||
|
serverStatus: function (next) {
|
||||||
|
db.command({ serverStatus: 1 }, next);
|
||||||
|
},
|
||||||
|
stats: function (next) {
|
||||||
|
db.command({ dbStats: 1 }, next);
|
||||||
|
},
|
||||||
|
listCollections: function (next) {
|
||||||
|
getCollectionStats(db, next);
|
||||||
|
},
|
||||||
|
}, next);
|
||||||
},
|
},
|
||||||
stats: function (next) {
|
function (results, next) {
|
||||||
db.command({ dbStats: 1 }, next);
|
var stats = results.stats;
|
||||||
},
|
var scale = 1024 * 1024 * 1024;
|
||||||
listCollections: function (next) {
|
|
||||||
db.listCollections().toArray(function (err, items) {
|
results.listCollections = results.listCollections.map(function (collectionInfo) {
|
||||||
if (err) {
|
return {
|
||||||
return next(err);
|
name: collectionInfo.ns,
|
||||||
}
|
count: collectionInfo.count,
|
||||||
async.map(items, function (collection, next) {
|
size: collectionInfo.size,
|
||||||
db.collection(collection.name).stats(next);
|
avgObjSize: collectionInfo.avgObjSize,
|
||||||
}, next);
|
storageSize: collectionInfo.storageSize,
|
||||||
|
totalIndexSize: collectionInfo.totalIndexSize,
|
||||||
|
indexSizes: collectionInfo.indexSizes,
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
stats.mem = results.serverStatus.mem;
|
||||||
|
stats.mem = results.serverStatus.mem;
|
||||||
|
stats.mem.resident = (stats.mem.resident / 1024).toFixed(2);
|
||||||
|
stats.mem.virtual = (stats.mem.virtual / 1024).toFixed(2);
|
||||||
|
stats.mem.mapped = (stats.mem.mapped / 1024).toFixed(2);
|
||||||
|
stats.collectionData = results.listCollections;
|
||||||
|
stats.network = results.serverStatus.network;
|
||||||
|
stats.raw = JSON.stringify(stats, null, 4);
|
||||||
|
|
||||||
|
stats.avgObjSize = stats.avgObjSize.toFixed(2);
|
||||||
|
stats.dataSize = (stats.dataSize / scale).toFixed(2);
|
||||||
|
stats.storageSize = (stats.storageSize / scale).toFixed(2);
|
||||||
|
stats.fileSize = stats.fileSize ? (stats.fileSize / scale).toFixed(2) : 0;
|
||||||
|
stats.indexSize = (stats.indexSize / scale).toFixed(2);
|
||||||
|
stats.storageEngine = results.serverStatus.storageEngine ? results.serverStatus.storageEngine.name : 'mmapv1';
|
||||||
|
stats.host = results.serverStatus.host;
|
||||||
|
stats.version = results.serverStatus.version;
|
||||||
|
stats.uptime = results.serverStatus.uptime;
|
||||||
|
stats.mongo = true;
|
||||||
|
|
||||||
|
next(null, stats);
|
||||||
},
|
},
|
||||||
}, function (err, results) {
|
], callback);
|
||||||
if (err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
var stats = results.stats;
|
|
||||||
var scale = 1024 * 1024 * 1024;
|
|
||||||
|
|
||||||
results.listCollections = results.listCollections.map(function (collectionInfo) {
|
|
||||||
return {
|
|
||||||
name: collectionInfo.ns,
|
|
||||||
count: collectionInfo.count,
|
|
||||||
size: collectionInfo.size,
|
|
||||||
avgObjSize: collectionInfo.avgObjSize,
|
|
||||||
storageSize: collectionInfo.storageSize,
|
|
||||||
totalIndexSize: collectionInfo.totalIndexSize,
|
|
||||||
indexSizes: collectionInfo.indexSizes,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
stats.mem = results.serverStatus.mem;
|
|
||||||
stats.mem = results.serverStatus.mem;
|
|
||||||
stats.mem.resident = (stats.mem.resident / 1024).toFixed(2);
|
|
||||||
stats.mem.virtual = (stats.mem.virtual / 1024).toFixed(2);
|
|
||||||
stats.mem.mapped = (stats.mem.mapped / 1024).toFixed(2);
|
|
||||||
stats.collectionData = results.listCollections;
|
|
||||||
stats.network = results.serverStatus.network;
|
|
||||||
stats.raw = JSON.stringify(stats, null, 4);
|
|
||||||
|
|
||||||
stats.avgObjSize = stats.avgObjSize.toFixed(2);
|
|
||||||
stats.dataSize = (stats.dataSize / scale).toFixed(2);
|
|
||||||
stats.storageSize = (stats.storageSize / scale).toFixed(2);
|
|
||||||
stats.fileSize = stats.fileSize ? (stats.fileSize / scale).toFixed(2) : 0;
|
|
||||||
stats.indexSize = (stats.indexSize / scale).toFixed(2);
|
|
||||||
stats.storageEngine = results.serverStatus.storageEngine ? results.serverStatus.storageEngine.name : 'mmapv1';
|
|
||||||
stats.host = results.serverStatus.host;
|
|
||||||
stats.version = results.serverStatus.version;
|
|
||||||
stats.uptime = results.serverStatus.uptime;
|
|
||||||
stats.mongo = true;
|
|
||||||
|
|
||||||
callback(null, stats);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
mongoModule.close = function () {
|
function getCollectionStats(db, callback) {
|
||||||
db.close();
|
async.waterfall([
|
||||||
|
function (next) {
|
||||||
|
db.listCollections().toArray(next);
|
||||||
|
},
|
||||||
|
function (items, next) {
|
||||||
|
async.map(items, function (collection, next) {
|
||||||
|
db.collection(collection.name).stats(next);
|
||||||
|
}, next);
|
||||||
|
},
|
||||||
|
], callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
mongoModule.close = function (callback) {
|
||||||
|
callback = callback || function () {};
|
||||||
|
db.close(callback);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var _ = require('underscore');
|
var _ = require('underscore');
|
||||||
|
var async = require('async');
|
||||||
var winston = require('winston');
|
var winston = require('winston');
|
||||||
var nconf = require('nconf');
|
var nconf = require('nconf');
|
||||||
var semver = require('semver');
|
var semver = require('semver');
|
||||||
@@ -71,10 +72,6 @@ redisModule.connect = function (options) {
|
|||||||
var redis_socket_or_host = nconf.get('redis:host');
|
var redis_socket_or_host = nconf.get('redis:host');
|
||||||
var cxn;
|
var cxn;
|
||||||
|
|
||||||
if (!redis) {
|
|
||||||
redis = require('redis');
|
|
||||||
}
|
|
||||||
|
|
||||||
options = options || {};
|
options = options || {};
|
||||||
|
|
||||||
if (nconf.get('redis:password')) {
|
if (nconf.get('redis:password')) {
|
||||||
@@ -101,10 +98,10 @@ redisModule.connect = function (options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var dbIdx = parseInt(nconf.get('redis:database'), 10);
|
var dbIdx = parseInt(nconf.get('redis:database'), 10);
|
||||||
if (dbIdx) {
|
if (dbIdx >= 0) {
|
||||||
cxn.select(dbIdx, function (error) {
|
cxn.select(dbIdx, function (err) {
|
||||||
if (error) {
|
if (err) {
|
||||||
winston.error('NodeBB could not connect to your Redis database. Redis returned the following error: ' + error.message);
|
winston.error('NodeBB could not connect to your Redis database. Redis returned the following error: ' + err.message);
|
||||||
process.exit();
|
process.exit();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -118,46 +115,52 @@ redisModule.createIndices = function (callback) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
redisModule.checkCompatibility = function (callback) {
|
redisModule.checkCompatibility = function (callback) {
|
||||||
redisModule.info(redisModule.client, function (err, info) {
|
async.waterfall([
|
||||||
if (err) {
|
function (next) {
|
||||||
return callback(err);
|
redisModule.info(redisModule.client, next);
|
||||||
}
|
},
|
||||||
|
function (info, next) {
|
||||||
if (semver.lt(info.redis_version, '2.8.9')) {
|
redisModule.checkCompatibilityVersion(info.redis_version, next);
|
||||||
return callback(new Error('Your Redis version is not new enough to support NodeBB, please upgrade Redis to v2.8.9 or higher.'));
|
},
|
||||||
}
|
], callback);
|
||||||
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
redisModule.close = function () {
|
redisModule.checkCompatibilityVersion = function (version, callback) {
|
||||||
redisClient.quit();
|
if (semver.lt(version, '2.8.9')) {
|
||||||
|
return callback(new Error('Your Redis version is not new enough to support NodeBB, please upgrade Redis to v2.8.9 or higher.'));
|
||||||
|
}
|
||||||
|
callback();
|
||||||
|
};
|
||||||
|
|
||||||
|
redisModule.close = function (callback) {
|
||||||
|
callback = callback || function () {};
|
||||||
|
redisClient.quit(callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
redisModule.info = function (cxn, callback) {
|
redisModule.info = function (cxn, callback) {
|
||||||
if (!cxn) {
|
if (!cxn) {
|
||||||
return callback();
|
return callback();
|
||||||
}
|
}
|
||||||
cxn.info(function (err, data) {
|
async.waterfall([
|
||||||
if (err) {
|
function (next) {
|
||||||
return callback(err);
|
cxn.info(next);
|
||||||
}
|
},
|
||||||
|
function (data, next) {
|
||||||
|
var lines = data.toString().split('\r\n').sort();
|
||||||
|
var redisData = {};
|
||||||
|
lines.forEach(function (line) {
|
||||||
|
var parts = line.split(':');
|
||||||
|
if (parts[1]) {
|
||||||
|
redisData[parts[0]] = parts[1];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
redisData.used_memory_human = (redisData.used_memory / (1024 * 1024 * 1024)).toFixed(2);
|
||||||
|
redisData.raw = JSON.stringify(redisData, null, 4);
|
||||||
|
redisData.redis = true;
|
||||||
|
|
||||||
var lines = data.toString().split('\r\n').sort();
|
next(null, redisData);
|
||||||
var redisData = {};
|
},
|
||||||
lines.forEach(function (line) {
|
], callback);
|
||||||
var parts = line.split(':');
|
|
||||||
if (parts[1]) {
|
|
||||||
redisData[parts[0]] = parts[1];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
redisData.used_memory_human = (redisData.used_memory / (1024 * 1024 * 1024)).toFixed(2);
|
|
||||||
redisData.raw = JSON.stringify(redisData, null, 4);
|
|
||||||
redisData.redis = true;
|
|
||||||
|
|
||||||
callback(null, redisData);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
redisModule.helpers = redisModule.helpers || {};
|
redisModule.helpers = redisModule.helpers || {};
|
||||||
|
|||||||
@@ -3,6 +3,9 @@
|
|||||||
module.exports = function (redisClient, module) {
|
module.exports = function (redisClient, module) {
|
||||||
module.listPrepend = function (key, value, callback) {
|
module.listPrepend = function (key, value, callback) {
|
||||||
callback = callback || function () {};
|
callback = callback || function () {};
|
||||||
|
if (!key) {
|
||||||
|
return callback();
|
||||||
|
}
|
||||||
redisClient.lpush(key, value, function (err) {
|
redisClient.lpush(key, value, function (err) {
|
||||||
callback(err);
|
callback(err);
|
||||||
});
|
});
|
||||||
@@ -10,6 +13,9 @@ module.exports = function (redisClient, module) {
|
|||||||
|
|
||||||
module.listAppend = function (key, value, callback) {
|
module.listAppend = function (key, value, callback) {
|
||||||
callback = callback || function () {};
|
callback = callback || function () {};
|
||||||
|
if (!key) {
|
||||||
|
return callback();
|
||||||
|
}
|
||||||
redisClient.rpush(key, value, function (err) {
|
redisClient.rpush(key, value, function (err) {
|
||||||
callback(err);
|
callback(err);
|
||||||
});
|
});
|
||||||
@@ -17,11 +23,17 @@ module.exports = function (redisClient, module) {
|
|||||||
|
|
||||||
module.listRemoveLast = function (key, callback) {
|
module.listRemoveLast = function (key, callback) {
|
||||||
callback = callback || function () {};
|
callback = callback || function () {};
|
||||||
|
if (!key) {
|
||||||
|
return callback();
|
||||||
|
}
|
||||||
redisClient.rpop(key, callback);
|
redisClient.rpop(key, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.listRemoveAll = function (key, value, callback) {
|
module.listRemoveAll = function (key, value, callback) {
|
||||||
callback = callback || function () {};
|
callback = callback || function () {};
|
||||||
|
if (!key) {
|
||||||
|
return callback();
|
||||||
|
}
|
||||||
redisClient.lrem(key, 0, value, function (err) {
|
redisClient.lrem(key, 0, value, function (err) {
|
||||||
callback(err);
|
callback(err);
|
||||||
});
|
});
|
||||||
@@ -29,6 +41,9 @@ module.exports = function (redisClient, module) {
|
|||||||
|
|
||||||
module.listTrim = function (key, start, stop, callback) {
|
module.listTrim = function (key, start, stop, callback) {
|
||||||
callback = callback || function () {};
|
callback = callback || function () {};
|
||||||
|
if (!key) {
|
||||||
|
return callback();
|
||||||
|
}
|
||||||
redisClient.ltrim(key, start, stop, function (err) {
|
redisClient.ltrim(key, start, stop, function (err) {
|
||||||
callback(err);
|
callback(err);
|
||||||
});
|
});
|
||||||
@@ -36,6 +51,9 @@ module.exports = function (redisClient, module) {
|
|||||||
|
|
||||||
module.getListRange = function (key, start, stop, callback) {
|
module.getListRange = function (key, start, stop, callback) {
|
||||||
callback = callback || function () {};
|
callback = callback || function () {};
|
||||||
|
if (!key) {
|
||||||
|
return callback();
|
||||||
|
}
|
||||||
redisClient.lrange(key, start, stop, callback);
|
redisClient.lrange(key, start, stop, callback);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -129,7 +129,13 @@ module.exports = function (redisClient, module) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
redisClient.zscore(key, value, function (err, score) {
|
redisClient.zscore(key, value, function (err, score) {
|
||||||
callback(err, !err ? parseFloat(score) : null);
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
if (score === null) {
|
||||||
|
return callback(null, score);
|
||||||
|
}
|
||||||
|
callback(null, parseFloat(score));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -9,26 +9,33 @@ var plugins = require('../plugins');
|
|||||||
|
|
||||||
module.exports = function (Messaging) {
|
module.exports = function (Messaging) {
|
||||||
Messaging.getRoomData = function (roomId, callback) {
|
Messaging.getRoomData = function (roomId, callback) {
|
||||||
db.getObject('chat:room:' + roomId, function (err, data) {
|
async.waterfall([
|
||||||
if (err || !data) {
|
function (next) {
|
||||||
return callback(err || new Error('[[error:no-chat-room]]'));
|
db.getObject('chat:room:' + roomId, next);
|
||||||
}
|
},
|
||||||
modifyRoomData([data]);
|
function (data, next) {
|
||||||
callback(null, data);
|
if (!data) {
|
||||||
});
|
return callback(new Error('[[error:no-chat-room]]'));
|
||||||
|
}
|
||||||
|
modifyRoomData([data]);
|
||||||
|
next(null, data);
|
||||||
|
},
|
||||||
|
], callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
Messaging.getRoomsData = function (roomIds, callback) {
|
Messaging.getRoomsData = function (roomIds, callback) {
|
||||||
var keys = roomIds.map(function (roomId) {
|
var keys = roomIds.map(function (roomId) {
|
||||||
return 'chat:room:' + roomId;
|
return 'chat:room:' + roomId;
|
||||||
});
|
});
|
||||||
db.getObjects(keys, function (err, roomData) {
|
async.waterfall([
|
||||||
if (err) {
|
function (next) {
|
||||||
return callback(err);
|
db.getObjects(keys, next);
|
||||||
}
|
},
|
||||||
modifyRoomData(roomData);
|
function (roomData, next) {
|
||||||
callback(null, roomData);
|
modifyRoomData(roomData);
|
||||||
});
|
next(null, roomData);
|
||||||
|
},
|
||||||
|
], callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
function modifyRoomData(rooms) {
|
function modifyRoomData(rooms) {
|
||||||
@@ -96,13 +103,14 @@ module.exports = function (Messaging) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Messaging.isRoomOwner = function (uid, roomId, callback) {
|
Messaging.isRoomOwner = function (uid, roomId, callback) {
|
||||||
db.getObjectField('chat:room:' + roomId, 'owner', function (err, owner) {
|
async.waterfall([
|
||||||
if (err) {
|
function (next) {
|
||||||
return callback(err);
|
db.getObjectField('chat:room:' + roomId, 'owner', next);
|
||||||
}
|
},
|
||||||
|
function (owner, next) {
|
||||||
callback(null, parseInt(uid, 10) === parseInt(owner, 10));
|
next(null, parseInt(uid, 10) === parseInt(owner, 10));
|
||||||
});
|
},
|
||||||
|
], callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
Messaging.addUsersToRoom = function (uid, uids, roomId, callback) {
|
Messaging.addUsersToRoom = function (uid, uids, roomId, callback) {
|
||||||
|
|||||||
118
src/meta.js
118
src/meta.js
@@ -8,71 +8,71 @@ var nconf = require('nconf');
|
|||||||
var pubsub = require('./pubsub');
|
var pubsub = require('./pubsub');
|
||||||
var utils = require('./utils');
|
var utils = require('./utils');
|
||||||
|
|
||||||
(function (Meta) {
|
var Meta = module.exports;
|
||||||
Meta.reloadRequired = false;
|
|
||||||
|
|
||||||
require('./meta/configs')(Meta);
|
Meta.reloadRequired = false;
|
||||||
require('./meta/themes')(Meta);
|
|
||||||
require('./meta/js')(Meta);
|
|
||||||
require('./meta/css')(Meta);
|
|
||||||
require('./meta/sounds')(Meta);
|
|
||||||
require('./meta/settings')(Meta);
|
|
||||||
require('./meta/logs')(Meta);
|
|
||||||
require('./meta/errors')(Meta);
|
|
||||||
require('./meta/tags')(Meta);
|
|
||||||
require('./meta/dependencies')(Meta);
|
|
||||||
Meta.templates = require('./meta/templates');
|
|
||||||
Meta.blacklist = require('./meta/blacklist');
|
|
||||||
Meta.languages = require('./meta/languages');
|
|
||||||
|
|
||||||
/* Assorted */
|
require('./meta/configs')(Meta);
|
||||||
Meta.userOrGroupExists = function (slug, callback) {
|
require('./meta/themes')(Meta);
|
||||||
var user = require('./user');
|
require('./meta/js')(Meta);
|
||||||
var groups = require('./groups');
|
require('./meta/css')(Meta);
|
||||||
slug = utils.slugify(slug);
|
require('./meta/sounds')(Meta);
|
||||||
async.parallel([
|
require('./meta/settings')(Meta);
|
||||||
async.apply(user.existsBySlug, slug),
|
require('./meta/logs')(Meta);
|
||||||
async.apply(groups.existsBySlug, slug),
|
require('./meta/errors')(Meta);
|
||||||
], function (err, results) {
|
require('./meta/tags')(Meta);
|
||||||
callback(err, results ? results.some(function (result) { return result; }) : false);
|
require('./meta/dependencies')(Meta);
|
||||||
});
|
Meta.templates = require('./meta/templates');
|
||||||
};
|
Meta.blacklist = require('./meta/blacklist');
|
||||||
|
Meta.languages = require('./meta/languages');
|
||||||
|
|
||||||
/**
|
/* Assorted */
|
||||||
* Reload deprecated as of v1.1.2+, remove in v2.x
|
Meta.userOrGroupExists = function (slug, callback) {
|
||||||
*/
|
var user = require('./user');
|
||||||
Meta.reload = function (callback) {
|
var groups = require('./groups');
|
||||||
restart();
|
slug = utils.slugify(slug);
|
||||||
callback();
|
async.parallel([
|
||||||
};
|
async.apply(user.existsBySlug, slug),
|
||||||
|
async.apply(groups.existsBySlug, slug),
|
||||||
|
], function (err, results) {
|
||||||
|
callback(err, results ? results.some(function (result) { return result; }) : false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
Meta.restart = function () {
|
/**
|
||||||
pubsub.publish('meta:restart', { hostname: os.hostname() });
|
* Reload deprecated as of v1.1.2+, remove in v2.x
|
||||||
restart();
|
*/
|
||||||
};
|
Meta.reload = function (callback) {
|
||||||
|
restart();
|
||||||
|
callback();
|
||||||
|
};
|
||||||
|
|
||||||
Meta.getSessionTTLSeconds = function () {
|
Meta.restart = function () {
|
||||||
var ttlDays = 60 * 60 * 24 * (parseInt(Meta.config.loginDays, 10) || 0);
|
pubsub.publish('meta:restart', { hostname: os.hostname() });
|
||||||
var ttlSeconds = (parseInt(Meta.config.loginSeconds, 10) || 0);
|
restart();
|
||||||
var ttl = ttlSeconds || ttlDays || 1209600; // Default to 14 days
|
};
|
||||||
return ttl;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (nconf.get('isPrimary') === 'true') {
|
Meta.getSessionTTLSeconds = function () {
|
||||||
pubsub.on('meta:restart', function (data) {
|
var ttlDays = 60 * 60 * 24 * (parseInt(Meta.config.loginDays, 10) || 0);
|
||||||
if (data.hostname !== os.hostname()) {
|
var ttlSeconds = (parseInt(Meta.config.loginSeconds, 10) || 0);
|
||||||
restart();
|
var ttl = ttlSeconds || ttlDays || 1209600; // Default to 14 days
|
||||||
}
|
return ttl;
|
||||||
});
|
};
|
||||||
}
|
|
||||||
|
|
||||||
function restart() {
|
if (nconf.get('isPrimary') === 'true') {
|
||||||
if (process.send) {
|
pubsub.on('meta:restart', function (data) {
|
||||||
process.send({
|
if (data.hostname !== os.hostname()) {
|
||||||
action: 'restart',
|
restart();
|
||||||
});
|
|
||||||
} else {
|
|
||||||
winston.error('[meta.restart] Could not restart, are you sure NodeBB was started with `./nodebb start`?');
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function restart() {
|
||||||
|
if (process.send) {
|
||||||
|
process.send({
|
||||||
|
action: 'restart',
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
winston.error('[meta.restart] Could not restart, are you sure NodeBB was started with `./nodebb start`?');
|
||||||
}
|
}
|
||||||
}(exports));
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
var winston = require('winston');
|
var winston = require('winston');
|
||||||
var os = require('os');
|
|
||||||
var nconf = require('nconf');
|
var nconf = require('nconf');
|
||||||
var padstart = require('lodash.padstart');
|
var padstart = require('lodash.padstart');
|
||||||
|
|
||||||
@@ -183,11 +182,14 @@ function build(targets, callback) {
|
|||||||
var startTime;
|
var startTime;
|
||||||
var totalTime;
|
var totalTime;
|
||||||
async.series([
|
async.series([
|
||||||
|
async.apply(beforeBuild, targets),
|
||||||
function (next) {
|
function (next) {
|
||||||
beforeBuild(targets, next);
|
var threads = parseInt(nconf.get('threads'), 10);
|
||||||
},
|
if (threads) {
|
||||||
function (next) {
|
require('./minifier').maxThreads = threads - 1;
|
||||||
var parallel = os.cpus().length > 1 && !nconf.get('series');
|
}
|
||||||
|
|
||||||
|
var parallel = !nconf.get('series');
|
||||||
if (parallel) {
|
if (parallel) {
|
||||||
winston.info('[build] Building in parallel mode');
|
winston.info('[build] Building in parallel mode');
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -89,43 +89,40 @@ module.exports = function (Meta) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function minifyModules(modules, fork, callback) {
|
function minifyModules(modules, fork, callback) {
|
||||||
// for it to never fork
|
var moduleDirs = modules.reduce(function (prev, mod) {
|
||||||
// otherwise it spawns way too many processes
|
var dir = path.resolve(path.dirname(mod.destPath));
|
||||||
// maybe eventually we can pool modules
|
if (prev.indexOf(dir) === -1) {
|
||||||
// and pass the pools to the minifer
|
prev.push(dir);
|
||||||
// to reduce the total number of threads
|
}
|
||||||
fork = false;
|
return prev;
|
||||||
|
}, []);
|
||||||
|
|
||||||
async.eachLimit(modules, 500, function (mod, next) {
|
async.eachLimit(moduleDirs, 1000, mkdirp, function (err) {
|
||||||
var srcPath = mod.srcPath;
|
if (err) {
|
||||||
var destPath = mod.destPath;
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
async.parallel({
|
var filtered = modules.reduce(function (prev, mod) {
|
||||||
dirped: function (cb) {
|
if (mod.srcPath.endsWith('.min.js') || path.dirname(mod.srcPath).endsWith('min')) {
|
||||||
mkdirp(path.dirname(destPath), cb);
|
prev.skip.push(mod);
|
||||||
},
|
} else {
|
||||||
minified: function (cb) {
|
prev.minify.push(mod);
|
||||||
fs.readFile(srcPath, function (err, buffer) {
|
|
||||||
if (err) {
|
|
||||||
return cb(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (srcPath.endsWith('.min.js') || path.dirname(srcPath).endsWith('min')) {
|
|
||||||
return cb(null, { code: buffer.toString() });
|
|
||||||
}
|
|
||||||
|
|
||||||
minifier.js.minify(buffer.toString(), fork, cb);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
}, function (err, results) {
|
|
||||||
if (err) {
|
|
||||||
return next(err);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var minified = results.minified;
|
return prev;
|
||||||
fs.writeFile(destPath, minified.code, next);
|
}, { minify: [], skip: [] });
|
||||||
});
|
|
||||||
}, callback);
|
async.parallel([
|
||||||
|
function (cb) {
|
||||||
|
minifier.js.minifyBatch(filtered.minify, fork, cb);
|
||||||
|
},
|
||||||
|
function (cb) {
|
||||||
|
async.eachLimit(filtered.skip, 500, function (mod, next) {
|
||||||
|
file.link(mod.srcPath, mod.destPath, next);
|
||||||
|
}, cb);
|
||||||
|
},
|
||||||
|
], callback);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function linkModules(callback) {
|
function linkModules(callback) {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ var async = require('async');
|
|||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var childProcess = require('child_process');
|
var childProcess = require('child_process');
|
||||||
var os = require('os');
|
var os = require('os');
|
||||||
|
var winston = require('winston');
|
||||||
var less = require('less');
|
var less = require('less');
|
||||||
var postcss = require('postcss');
|
var postcss = require('postcss');
|
||||||
var autoprefixer = require('autoprefixer');
|
var autoprefixer = require('autoprefixer');
|
||||||
@@ -37,23 +38,38 @@ function setupDebugging() {
|
|||||||
return forkProcessParams;
|
return forkProcessParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
var children = [];
|
var pool = [];
|
||||||
|
var free = [];
|
||||||
|
|
||||||
|
var maxThreads = 0;
|
||||||
|
|
||||||
|
Object.defineProperty(Minifier, 'maxThreads', {
|
||||||
|
get: function () {
|
||||||
|
return maxThreads;
|
||||||
|
},
|
||||||
|
set: function (val) {
|
||||||
|
maxThreads = val;
|
||||||
|
winston.verbose('[minifier] utilizing a maximum of ' + maxThreads + ' additional threads');
|
||||||
|
},
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
Minifier.maxThreads = os.cpus().length - 1;
|
||||||
|
|
||||||
Minifier.killAll = function () {
|
Minifier.killAll = function () {
|
||||||
children.forEach(function (child) {
|
pool.forEach(function (child) {
|
||||||
child.kill('SIGTERM');
|
child.kill('SIGTERM');
|
||||||
});
|
});
|
||||||
|
|
||||||
children = [];
|
pool.length = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
function removeChild(proc) {
|
function getChild() {
|
||||||
children = children.filter(function (child) {
|
if (free.length) {
|
||||||
return child !== proc;
|
return free.shift();
|
||||||
});
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function forkAction(action, callback) {
|
|
||||||
var forkProcessParams = setupDebugging();
|
var forkProcessParams = setupDebugging();
|
||||||
var proc = childProcess.fork(__filename, [], Object.assign({}, forkProcessParams, {
|
var proc = childProcess.fork(__filename, [], Object.assign({}, forkProcessParams, {
|
||||||
cwd: __dirname,
|
cwd: __dirname,
|
||||||
@@ -61,17 +77,32 @@ function forkAction(action, callback) {
|
|||||||
minifier_child: true,
|
minifier_child: true,
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
pool.push(proc);
|
||||||
|
|
||||||
children.push(proc);
|
return proc;
|
||||||
|
}
|
||||||
|
|
||||||
|
function freeChild(proc) {
|
||||||
|
proc.removeAllListeners();
|
||||||
|
free.push(proc);
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeChild(proc) {
|
||||||
|
var i = pool.indexOf(proc);
|
||||||
|
pool.splice(i, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function forkAction(action, callback) {
|
||||||
|
var proc = getChild();
|
||||||
|
|
||||||
proc.on('message', function (message) {
|
proc.on('message', function (message) {
|
||||||
|
freeChild(proc);
|
||||||
|
|
||||||
if (message.type === 'error') {
|
if (message.type === 'error') {
|
||||||
proc.kill();
|
return callback(message.err);
|
||||||
return callback(new Error(message.message));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.type === 'end') {
|
if (message.type === 'end') {
|
||||||
proc.kill();
|
|
||||||
callback(null, message.result);
|
callback(null, message.result);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -85,10 +116,6 @@ function forkAction(action, callback) {
|
|||||||
type: 'action',
|
type: 'action',
|
||||||
action: action,
|
action: action,
|
||||||
});
|
});
|
||||||
|
|
||||||
proc.on('close', function () {
|
|
||||||
removeChild(proc);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var actions = {};
|
var actions = {};
|
||||||
@@ -100,7 +127,7 @@ if (process.env.minifier_child) {
|
|||||||
if (typeof actions[action.act] !== 'function') {
|
if (typeof actions[action.act] !== 'function') {
|
||||||
process.send({
|
process.send({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
message: 'Unknown action',
|
err: Error('Unknown action'),
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -109,7 +136,7 @@ if (process.env.minifier_child) {
|
|||||||
if (err) {
|
if (err) {
|
||||||
process.send({
|
process.send({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
message: err.message,
|
err: err,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -124,7 +151,7 @@ if (process.env.minifier_child) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function executeAction(action, fork, callback) {
|
function executeAction(action, fork, callback) {
|
||||||
if (fork) {
|
if (fork && (pool.length - free.length) < Minifier.maxThreads) {
|
||||||
forkAction(action, callback);
|
forkAction(action, callback);
|
||||||
} else {
|
} else {
|
||||||
if (typeof actions[action.act] !== 'function') {
|
if (typeof actions[action.act] !== 'function') {
|
||||||
@@ -141,7 +168,7 @@ function concat(data, callback) {
|
|||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
var output = files.join(os.EOL + ';');
|
var output = files.join('\n;');
|
||||||
callback(null, { code: output });
|
callback(null, { code: output });
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -153,32 +180,38 @@ function concat(data, callback) {
|
|||||||
actions.concat = concat;
|
actions.concat = concat;
|
||||||
|
|
||||||
function minifyJS(data, callback) {
|
function minifyJS(data, callback) {
|
||||||
var minified;
|
if (data.batch) {
|
||||||
|
async.eachLimit(data.files, 1000, function (ref, next) {
|
||||||
|
var srcPath = ref.srcPath;
|
||||||
|
var destPath = ref.destPath;
|
||||||
|
|
||||||
if (data.fromSource) {
|
fs.readFile(srcPath, function (err, buffer) {
|
||||||
var sources = data.source;
|
if (err && err.code === 'ENOENT') {
|
||||||
var multiple = Array.isArray(sources);
|
return next(null, null);
|
||||||
if (!multiple) {
|
}
|
||||||
sources = [sources];
|
if (err) {
|
||||||
}
|
return next(err);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
minified = sources.map(function (source) {
|
var minified = uglifyjs.minify(buffer.toString(), {
|
||||||
return uglifyjs.minify(source, {
|
// outSourceMap: data.filename + '.map',
|
||||||
// outSourceMap: data.filename + '.map',
|
compress: data.compress,
|
||||||
compress: data.compress,
|
fromString: true,
|
||||||
fromString: true,
|
output: {
|
||||||
output: {
|
// suppress uglify line length warnings
|
||||||
// suppress uglify line length warnings
|
max_line_len: 400000,
|
||||||
max_line_len: 400000,
|
},
|
||||||
},
|
});
|
||||||
});
|
|
||||||
|
fs.writeFile(destPath, minified.code, next);
|
||||||
|
} catch (e) {
|
||||||
|
next(e);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} catch (e) {
|
}, callback);
|
||||||
return callback(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return callback(null, multiple ? minified : minified[0]);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.files && data.files.length) {
|
if (data.files && data.files.length) {
|
||||||
@@ -188,16 +221,16 @@ function minifyJS(data, callback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
minified = uglifyjs.minify(scripts, {
|
var minified = uglifyjs.minify(scripts, {
|
||||||
// outSourceMap: data.filename + '.map',
|
// outSourceMap: data.filename + '.map',
|
||||||
compress: data.compress,
|
compress: data.compress,
|
||||||
fromString: false,
|
fromString: false,
|
||||||
});
|
});
|
||||||
} catch (e) {
|
|
||||||
return callback(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(null, minified);
|
callback(null, minified);
|
||||||
|
} catch (e) {
|
||||||
|
callback(e);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@@ -216,11 +249,11 @@ Minifier.js.bundle = function (scripts, minify, fork, callback) {
|
|||||||
}, fork, callback);
|
}, fork, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
Minifier.js.minify = function (source, fork, callback) {
|
Minifier.js.minifyBatch = function (scripts, fork, callback) {
|
||||||
executeAction({
|
executeAction({
|
||||||
act: 'minifyJS',
|
act: 'minifyJS',
|
||||||
fromSource: true,
|
files: scripts,
|
||||||
source: source,
|
batch: true,
|
||||||
}, fork, callback);
|
}, fork, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ Notifications.get = function (nid, callback) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Notifications.getMultiple = function (nids, callback) {
|
Notifications.getMultiple = function (nids, callback) {
|
||||||
if (!nids.length) {
|
if (!Array.isArray(nids) || !nids.length) {
|
||||||
return setImmediate(callback, null, []);
|
return setImmediate(callback, null, []);
|
||||||
}
|
}
|
||||||
var keys = nids.map(function (nid) {
|
var keys = nids.map(function (nid) {
|
||||||
@@ -106,50 +106,47 @@ Notifications.findRelated = function (mergeIds, set, callback) {
|
|||||||
|
|
||||||
db.getObjectsFields(keys, ['mergeId'], next);
|
db.getObjectsFields(keys, ['mergeId'], next);
|
||||||
},
|
},
|
||||||
], function (err, sets) {
|
function (sets, next) {
|
||||||
if (err) {
|
sets = sets.map(function (set) {
|
||||||
return callback(err);
|
return set.mergeId;
|
||||||
}
|
});
|
||||||
|
|
||||||
sets = sets.map(function (set) {
|
next(null, _nids.filter(function (nid, idx) {
|
||||||
return set.mergeId;
|
return mergeIds.indexOf(sets[idx]) !== -1;
|
||||||
});
|
}));
|
||||||
|
},
|
||||||
callback(null, _nids.filter(function (nid, idx) {
|
], callback);
|
||||||
return mergeIds.indexOf(sets[idx]) !== -1;
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Notifications.create = function (data, callback) {
|
Notifications.create = function (data, callback) {
|
||||||
if (!data.nid) {
|
if (!data.nid) {
|
||||||
return callback(new Error('no-notification-id'));
|
return callback(new Error('[[error:no-notification-id]]'));
|
||||||
}
|
}
|
||||||
data.importance = data.importance || 5;
|
data.importance = data.importance || 5;
|
||||||
db.getObject('notifications:' + data.nid, function (err, oldNotification) {
|
async.waterfall([
|
||||||
if (err) {
|
function (next) {
|
||||||
return callback(err);
|
db.getObject('notifications:' + data.nid, next);
|
||||||
}
|
},
|
||||||
|
function (oldNotification, next) {
|
||||||
if (oldNotification) {
|
if (oldNotification) {
|
||||||
if (parseInt(oldNotification.pid, 10) === parseInt(data.pid, 10) && parseInt(oldNotification.importance, 10) > parseInt(data.importance, 10)) {
|
if (parseInt(oldNotification.pid, 10) === parseInt(data.pid, 10) && parseInt(oldNotification.importance, 10) > parseInt(data.importance, 10)) {
|
||||||
return callback(null, null);
|
return callback(null, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
var now = Date.now();
|
||||||
|
data.datetime = now;
|
||||||
var now = Date.now();
|
async.parallel([
|
||||||
data.datetime = now;
|
function (next) {
|
||||||
async.parallel([
|
db.sortedSetAdd('notifications', now, data.nid, next);
|
||||||
function (next) {
|
},
|
||||||
db.sortedSetAdd('notifications', now, data.nid, next);
|
function (next) {
|
||||||
},
|
db.setObject('notifications:' + data.nid, data, next);
|
||||||
function (next) {
|
},
|
||||||
db.setObject('notifications:' + data.nid, data, next);
|
], function (err) {
|
||||||
},
|
next(err, data);
|
||||||
], function (err) {
|
});
|
||||||
callback(err, data);
|
},
|
||||||
});
|
], callback);
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Notifications.push = function (notification, uids, callback) {
|
Notifications.push = function (notification, uids, callback) {
|
||||||
@@ -233,25 +230,31 @@ function pushToUids(uids, notification, callback) {
|
|||||||
|
|
||||||
Notifications.pushGroup = function (notification, groupName, callback) {
|
Notifications.pushGroup = function (notification, groupName, callback) {
|
||||||
callback = callback || function () {};
|
callback = callback || function () {};
|
||||||
groups.getMembers(groupName, 0, -1, function (err, members) {
|
async.waterfall([
|
||||||
if (err || !Array.isArray(members) || !members.length) {
|
function (next) {
|
||||||
return callback(err);
|
groups.getMembers(groupName, 0, -1, next);
|
||||||
}
|
},
|
||||||
|
function (members, next) {
|
||||||
|
if (!Array.isArray(members) || !members.length) {
|
||||||
|
return callback();
|
||||||
|
}
|
||||||
|
|
||||||
Notifications.push(notification, members, callback);
|
Notifications.push(notification, members, next);
|
||||||
});
|
},
|
||||||
|
], callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
Notifications.pushGroups = function (notification, groupNames, callback) {
|
Notifications.pushGroups = function (notification, groupNames, callback) {
|
||||||
callback = callback || function () {};
|
callback = callback || function () {};
|
||||||
groups.getMembersOfGroups(groupNames, function (err, groupMembers) {
|
async.waterfall([
|
||||||
if (err) {
|
function (next) {
|
||||||
return callback(err);
|
groups.getMembersOfGroups(groupNames, next);
|
||||||
}
|
},
|
||||||
|
function (groupMembers, next) {
|
||||||
var members = _.unique(_.flatten(groupMembers));
|
var members = _.unique(_.flatten(groupMembers));
|
||||||
Notifications.push(notification, members, callback);
|
Notifications.push(notification, members, next);
|
||||||
});
|
},
|
||||||
|
], callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
Notifications.rescind = function (nid, callback) {
|
Notifications.rescind = function (nid, callback) {
|
||||||
@@ -261,13 +264,7 @@ Notifications.rescind = function (nid, callback) {
|
|||||||
async.apply(db.sortedSetRemove, 'notifications', nid),
|
async.apply(db.sortedSetRemove, 'notifications', nid),
|
||||||
async.apply(db.delete, 'notifications:' + nid),
|
async.apply(db.delete, 'notifications:' + nid),
|
||||||
], function (err) {
|
], function (err) {
|
||||||
if (err) {
|
callback(err);
|
||||||
winston.error('Encountered error rescinding notification (' + nid + '): ' + err.message);
|
|
||||||
} else {
|
|
||||||
winston.verbose('[notifications/rescind] Rescinded notification "' + nid + '"');
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(err, nid);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -284,18 +281,22 @@ Notifications.markUnread = function (nid, uid, callback) {
|
|||||||
if (!parseInt(uid, 10) || !nid) {
|
if (!parseInt(uid, 10) || !nid) {
|
||||||
return callback();
|
return callback();
|
||||||
}
|
}
|
||||||
|
async.waterfall([
|
||||||
|
function (next) {
|
||||||
|
db.getObject('notifications:' + nid, next);
|
||||||
|
},
|
||||||
|
function (notification, next) {
|
||||||
|
if (!notification) {
|
||||||
|
return callback(new Error('[[error:no-notification]]'));
|
||||||
|
}
|
||||||
|
notification.datetime = notification.datetime || Date.now();
|
||||||
|
|
||||||
db.getObject('notifications:' + nid, function (err, notification) {
|
async.parallel([
|
||||||
if (err || !notification) {
|
async.apply(db.sortedSetRemove, 'uid:' + uid + ':notifications:read', nid),
|
||||||
return callback(err || new Error('[[error:no-notification]]'));
|
async.apply(db.sortedSetAdd, 'uid:' + uid + ':notifications:unread', notification.datetime, nid),
|
||||||
}
|
], next);
|
||||||
notification.datetime = notification.datetime || Date.now();
|
},
|
||||||
|
], callback);
|
||||||
async.parallel([
|
|
||||||
async.apply(db.sortedSetRemove, 'uid:' + uid + ':notifications:read', nid),
|
|
||||||
async.apply(db.sortedSetAdd, 'uid:' + uid + ':notifications:unread', notification.datetime, nid),
|
|
||||||
], callback);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Notifications.markReadMultiple = function (nids, uid, callback) {
|
Notifications.markReadMultiple = function (nids, uid, callback) {
|
||||||
@@ -377,9 +378,9 @@ Notifications.markAllRead = function (uid, callback) {
|
|||||||
|
|
||||||
Notifications.prune = function (callback) {
|
Notifications.prune = function (callback) {
|
||||||
callback = callback || function () {};
|
callback = callback || function () {};
|
||||||
var week = 604800000;
|
var week = 604800000;
|
||||||
|
|
||||||
var cutoffTime = Date.now() - week;
|
var cutoffTime = Date.now() - week;
|
||||||
|
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
function (next) {
|
function (next) {
|
||||||
@@ -390,7 +391,7 @@ Notifications.prune = function (callback) {
|
|||||||
return callback();
|
return callback();
|
||||||
}
|
}
|
||||||
|
|
||||||
var keys = nids.map(function (nid) {
|
var keys = nids.map(function (nid) {
|
||||||
return 'notifications:' + nid;
|
return 'notifications:' + nid;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ var categories = require('../categories');
|
|||||||
var meta = require('../meta');
|
var meta = require('../meta');
|
||||||
var helpers = require('../controllers/helpers');
|
var helpers = require('../controllers/helpers');
|
||||||
var privileges = require('../privileges');
|
var privileges = require('../privileges');
|
||||||
|
var controllers404 = require('../controllers/404.js');
|
||||||
|
|
||||||
module.exports = function (app, middleware) {
|
module.exports = function (app, middleware) {
|
||||||
app.get('/topic/:topic_id.rss', middleware.maintenanceMode, generateForTopic);
|
app.get('/topic/:topic_id.rss', middleware.maintenanceMode, generateForTopic);
|
||||||
@@ -25,10 +26,9 @@ module.exports = function (app, middleware) {
|
|||||||
app.get('/tags/:tag.rss', middleware.maintenanceMode, generateForTag);
|
app.get('/tags/:tag.rss', middleware.maintenanceMode, generateForTag);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
function generateForTopic(req, res, callback) {
|
function generateForTopic(req, res, callback) {
|
||||||
if (parseInt(meta.config['feeds:disableRSS'], 10) === 1) {
|
if (parseInt(meta.config['feeds:disableRSS'], 10) === 1) {
|
||||||
return callback();
|
return controllers404.send404(req, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
var tid = req.params.topic_id;
|
var tid = req.params.topic_id;
|
||||||
@@ -45,65 +45,59 @@ function generateForTopic(req, res, callback) {
|
|||||||
}, next);
|
}, next);
|
||||||
},
|
},
|
||||||
function (results, next) {
|
function (results, next) {
|
||||||
if (!results.topic) {
|
if (!results.topic || (parseInt(results.topic.deleted, 10) && !results.privileges.view_deleted)) {
|
||||||
return callback();
|
return controllers404.send404(req, res);
|
||||||
}
|
}
|
||||||
if (parseInt(results.topic.deleted, 10) && !results.privileges.view_deleted) {
|
if (!results.privileges['topics:read']) {
|
||||||
return callback();
|
|
||||||
}
|
|
||||||
if (!results.privileges.read || !results.privileges['topics:read']) {
|
|
||||||
return helpers.notAllowed(req, res);
|
return helpers.notAllowed(req, res);
|
||||||
}
|
}
|
||||||
userPrivileges = results.privileges;
|
userPrivileges = results.privileges;
|
||||||
topics.getTopicWithPosts(results.topic, 'tid:' + tid + ':posts', req.uid, 0, 25, false, next);
|
topics.getTopicWithPosts(results.topic, 'tid:' + tid + ':posts', req.uid, 0, 25, false, next);
|
||||||
},
|
},
|
||||||
], function (err, topicData) {
|
function (topicData) {
|
||||||
if (err) {
|
topics.modifyPostsByPrivilege(topicData, userPrivileges);
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
topics.modifyPostsByPrivilege(topicData, userPrivileges);
|
var description = topicData.posts.length ? topicData.posts[0].content : '';
|
||||||
|
var image_url = topicData.posts.length ? topicData.posts[0].picture : '';
|
||||||
|
var author = topicData.posts.length ? topicData.posts[0].username : '';
|
||||||
|
|
||||||
var description = topicData.posts.length ? topicData.posts[0].content : '';
|
var feed = new rss({
|
||||||
var image_url = topicData.posts.length ? topicData.posts[0].picture : '';
|
title: topicData.title,
|
||||||
var author = topicData.posts.length ? topicData.posts[0].username : '';
|
description: description,
|
||||||
|
feed_url: nconf.get('url') + '/topic/' + tid + '.rss',
|
||||||
|
site_url: nconf.get('url') + '/topic/' + topicData.slug,
|
||||||
|
image_url: image_url,
|
||||||
|
author: author,
|
||||||
|
ttl: 60,
|
||||||
|
});
|
||||||
|
var dateStamp;
|
||||||
|
|
||||||
var feed = new rss({
|
if (topicData.posts.length > 0) {
|
||||||
title: topicData.title,
|
feed.pubDate = new Date(parseInt(topicData.posts[0].timestamp, 10)).toUTCString();
|
||||||
description: description,
|
|
||||||
feed_url: nconf.get('url') + '/topic/' + tid + '.rss',
|
|
||||||
site_url: nconf.get('url') + '/topic/' + topicData.slug,
|
|
||||||
image_url: image_url,
|
|
||||||
author: author,
|
|
||||||
ttl: 60,
|
|
||||||
});
|
|
||||||
var dateStamp;
|
|
||||||
|
|
||||||
if (topicData.posts.length > 0) {
|
|
||||||
feed.pubDate = new Date(parseInt(topicData.posts[0].timestamp, 10)).toUTCString();
|
|
||||||
}
|
|
||||||
|
|
||||||
topicData.posts.forEach(function (postData) {
|
|
||||||
if (!postData.deleted) {
|
|
||||||
dateStamp = new Date(parseInt(parseInt(postData.edited, 10) === 0 ? postData.timestamp : postData.edited, 10)).toUTCString();
|
|
||||||
|
|
||||||
feed.item({
|
|
||||||
title: 'Reply to ' + topicData.title + ' on ' + dateStamp,
|
|
||||||
description: postData.content,
|
|
||||||
url: nconf.get('url') + '/post/' + postData.pid,
|
|
||||||
author: postData.user ? postData.user.username : '',
|
|
||||||
date: dateStamp,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
sendFeed(feed, res);
|
topicData.posts.forEach(function (postData) {
|
||||||
});
|
if (!postData.deleted) {
|
||||||
|
dateStamp = new Date(parseInt(parseInt(postData.edited, 10) === 0 ? postData.timestamp : postData.edited, 10)).toUTCString();
|
||||||
|
|
||||||
|
feed.item({
|
||||||
|
title: 'Reply to ' + topicData.title + ' on ' + dateStamp,
|
||||||
|
description: postData.content,
|
||||||
|
url: nconf.get('url') + '/post/' + postData.pid,
|
||||||
|
author: postData.user ? postData.user.username : '',
|
||||||
|
date: dateStamp,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
sendFeed(feed, res);
|
||||||
|
},
|
||||||
|
], callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateForUserTopics(req, res, callback) {
|
function generateForUserTopics(req, res, callback) {
|
||||||
if (parseInt(meta.config['feeds:disableRSS'], 10) === 1) {
|
if (parseInt(meta.config['feeds:disableRSS'], 10) === 1) {
|
||||||
return callback();
|
return controllers404.send404(req, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
var userslug = req.params.userslug;
|
var userslug = req.params.userslug;
|
||||||
@@ -118,24 +112,21 @@ function generateForUserTopics(req, res, callback) {
|
|||||||
}
|
}
|
||||||
user.getUserFields(uid, ['uid', 'username'], next);
|
user.getUserFields(uid, ['uid', 'username'], next);
|
||||||
},
|
},
|
||||||
], function (err, userData) {
|
function (userData, next) {
|
||||||
if (err) {
|
generateForTopics({
|
||||||
return callback(err);
|
uid: req.uid,
|
||||||
}
|
title: 'Topics by ' + userData.username,
|
||||||
|
description: 'A list of topics that are posted by ' + userData.username,
|
||||||
generateForTopics({
|
feed_url: '/user/' + userslug + '/topics.rss',
|
||||||
uid: req.uid,
|
site_url: '/user/' + userslug + '/topics',
|
||||||
title: 'Topics by ' + userData.username,
|
}, 'uid:' + userData.uid + ':topics', req, res, next);
|
||||||
description: 'A list of topics that are posted by ' + userData.username,
|
},
|
||||||
feed_url: '/user/' + userslug + '/topics.rss',
|
], callback);
|
||||||
site_url: '/user/' + userslug + '/topics',
|
|
||||||
}, 'uid:' + userData.uid + ':topics', req, res, callback);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateForCategory(req, res, next) {
|
function generateForCategory(req, res, next) {
|
||||||
if (parseInt(meta.config['feeds:disableRSS'], 10) === 1) {
|
if (parseInt(meta.config['feeds:disableRSS'], 10) === 1) {
|
||||||
return next();
|
return controllers404.send404(req, res);
|
||||||
}
|
}
|
||||||
var cid = req.params.category_id;
|
var cid = req.params.category_id;
|
||||||
|
|
||||||
@@ -169,17 +160,15 @@ function generateForCategory(req, res, next) {
|
|||||||
site_url: '/category/' + results.category.cid,
|
site_url: '/category/' + results.category.cid,
|
||||||
}, results.category.topics, next);
|
}, results.category.topics, next);
|
||||||
},
|
},
|
||||||
], function (err, feed) {
|
function (feed) {
|
||||||
if (err) {
|
sendFeed(feed, res);
|
||||||
return next(err);
|
},
|
||||||
}
|
], next);
|
||||||
sendFeed(feed, res);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateForRecent(req, res, next) {
|
function generateForRecent(req, res, next) {
|
||||||
if (parseInt(meta.config['feeds:disableRSS'], 10) === 1) {
|
if (parseInt(meta.config['feeds:disableRSS'], 10) === 1) {
|
||||||
return next();
|
return controllers404.send404(req, res);
|
||||||
}
|
}
|
||||||
generateForTopics({
|
generateForTopics({
|
||||||
uid: req.uid,
|
uid: req.uid,
|
||||||
@@ -192,7 +181,7 @@ function generateForRecent(req, res, next) {
|
|||||||
|
|
||||||
function generateForPopular(req, res, next) {
|
function generateForPopular(req, res, next) {
|
||||||
if (parseInt(meta.config['feeds:disableRSS'], 10) === 1) {
|
if (parseInt(meta.config['feeds:disableRSS'], 10) === 1) {
|
||||||
return next();
|
return controllers404.send404(req, res);
|
||||||
}
|
}
|
||||||
var terms = {
|
var terms = {
|
||||||
daily: 'day',
|
daily: 'day',
|
||||||
@@ -215,12 +204,10 @@ function generateForPopular(req, res, next) {
|
|||||||
site_url: '/popular/' + (req.params.term || 'daily'),
|
site_url: '/popular/' + (req.params.term || 'daily'),
|
||||||
}, topics, next);
|
}, topics, next);
|
||||||
},
|
},
|
||||||
], function (err, feed) {
|
function (feed) {
|
||||||
if (err) {
|
sendFeed(feed, res);
|
||||||
return next(err);
|
},
|
||||||
}
|
], next);
|
||||||
sendFeed(feed, res);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateForTopics(options, set, req, res, next) {
|
function generateForTopics(options, set, req, res, next) {
|
||||||
@@ -233,12 +220,10 @@ function generateForTopics(options, set, req, res, next) {
|
|||||||
function (data, next) {
|
function (data, next) {
|
||||||
generateTopicsFeed(options, data.topics, next);
|
generateTopicsFeed(options, data.topics, next);
|
||||||
},
|
},
|
||||||
], function (err, feed) {
|
function (feed) {
|
||||||
if (err) {
|
sendFeed(feed, res);
|
||||||
return next(err);
|
},
|
||||||
}
|
], next);
|
||||||
sendFeed(feed, res);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateTopicsFeed(feedOptions, feedTopics, callback) {
|
function generateTopicsFeed(feedOptions, feedTopics, callback) {
|
||||||
@@ -254,7 +239,7 @@ function generateTopicsFeed(feedOptions, feedTopics, callback) {
|
|||||||
feed.pubDate = new Date(parseInt(feedTopics[0].lastposttime, 10)).toUTCString();
|
feed.pubDate = new Date(parseInt(feedTopics[0].lastposttime, 10)).toUTCString();
|
||||||
}
|
}
|
||||||
|
|
||||||
async.map(feedTopics, function (topicData, next) {
|
async.each(feedTopics, function (topicData, next) {
|
||||||
var feedItem = {
|
var feedItem = {
|
||||||
title: topicData.title,
|
title: topicData.title,
|
||||||
url: nconf.get('url') + '/topic/' + topicData.slug,
|
url: nconf.get('url') + '/topic/' + topicData.slug,
|
||||||
@@ -272,83 +257,80 @@ function generateTopicsFeed(feedOptions, feedTopics, callback) {
|
|||||||
return next(err);
|
return next(err);
|
||||||
}
|
}
|
||||||
if (!mainPost) {
|
if (!mainPost) {
|
||||||
return next(null, feedItem);
|
feed.item(feedItem);
|
||||||
|
return next();
|
||||||
}
|
}
|
||||||
feedItem.description = mainPost.content;
|
feedItem.description = mainPost.content;
|
||||||
feedItem.author = mainPost.user.username;
|
feedItem.author = mainPost.user.username;
|
||||||
next(null, feedItem);
|
feed.item(feedItem);
|
||||||
|
next();
|
||||||
});
|
});
|
||||||
}, function (err, feedItems) {
|
}, function (err) {
|
||||||
if (err) {
|
callback(err, feed);
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
feedItems.forEach(function (feedItem) {
|
|
||||||
if (feedItem) {
|
|
||||||
feed.item(feedItem);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
callback(null, feed);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateForRecentPosts(req, res, next) {
|
function generateForRecentPosts(req, res, next) {
|
||||||
if (parseInt(meta.config['feeds:disableRSS'], 10) === 1) {
|
if (parseInt(meta.config['feeds:disableRSS'], 10) === 1) {
|
||||||
return next();
|
return controllers404.send404(req, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
posts.getRecentPosts(req.uid, 0, 19, 'month', function (err, posts) {
|
async.waterfall([
|
||||||
if (err) {
|
function (next) {
|
||||||
return next(err);
|
posts.getRecentPosts(req.uid, 0, 19, 'month', next);
|
||||||
}
|
},
|
||||||
|
function (posts) {
|
||||||
|
var feed = generateForPostsFeed({
|
||||||
|
title: 'Recent Posts',
|
||||||
|
description: 'A list of recent posts',
|
||||||
|
feed_url: '/recentposts.rss',
|
||||||
|
site_url: '/recentposts',
|
||||||
|
}, posts);
|
||||||
|
|
||||||
var feed = generateForPostsFeed({
|
sendFeed(feed, res);
|
||||||
title: 'Recent Posts',
|
},
|
||||||
description: 'A list of recent posts',
|
], next);
|
||||||
feed_url: '/recentposts.rss',
|
|
||||||
site_url: '/recentposts',
|
|
||||||
}, posts);
|
|
||||||
|
|
||||||
sendFeed(feed, res);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateForCategoryRecentPosts(req, res, next) {
|
function generateForCategoryRecentPosts(req, res, next) {
|
||||||
if (parseInt(meta.config['feeds:disableRSS'], 10) === 1) {
|
if (parseInt(meta.config['feeds:disableRSS'], 10) === 1) {
|
||||||
return next();
|
return controllers404.send404(req, res);
|
||||||
}
|
}
|
||||||
var cid = req.params.category_id;
|
var cid = req.params.category_id;
|
||||||
|
|
||||||
async.parallel({
|
async.waterfall([
|
||||||
privileges: function (next) {
|
function (next) {
|
||||||
privileges.categories.get(cid, req.uid, next);
|
async.parallel({
|
||||||
|
privileges: function (next) {
|
||||||
|
privileges.categories.get(cid, req.uid, next);
|
||||||
|
},
|
||||||
|
category: function (next) {
|
||||||
|
categories.getCategoryData(cid, next);
|
||||||
|
},
|
||||||
|
posts: function (next) {
|
||||||
|
categories.getRecentReplies(cid, req.uid, 20, next);
|
||||||
|
},
|
||||||
|
}, next);
|
||||||
},
|
},
|
||||||
category: function (next) {
|
function (results, next) {
|
||||||
categories.getCategoryData(cid, next);
|
if (!results.category) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!results.privileges.read) {
|
||||||
|
return helpers.notAllowed(req, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
var feed = generateForPostsFeed({
|
||||||
|
title: results.category.name + ' Recent Posts',
|
||||||
|
description: 'A list of recent posts from ' + results.category.name,
|
||||||
|
feed_url: '/category/' + cid + '/recentposts.rss',
|
||||||
|
site_url: '/category/' + cid + '/recentposts',
|
||||||
|
}, results.posts);
|
||||||
|
|
||||||
|
sendFeed(feed, res);
|
||||||
},
|
},
|
||||||
posts: function (next) {
|
], next);
|
||||||
categories.getRecentReplies(cid, req.uid, 20, next);
|
|
||||||
},
|
|
||||||
}, function (err, results) {
|
|
||||||
if (err) {
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
if (!results.category) {
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!results.privileges.read) {
|
|
||||||
return helpers.notAllowed(req, res);
|
|
||||||
}
|
|
||||||
|
|
||||||
var feed = generateForPostsFeed({
|
|
||||||
title: results.category.name + ' Recent Posts',
|
|
||||||
description: 'A list of recent posts from ' + results.category.name,
|
|
||||||
feed_url: '/category/' + cid + '/recentposts.rss',
|
|
||||||
site_url: '/category/' + cid + '/recentposts',
|
|
||||||
}, results.posts);
|
|
||||||
|
|
||||||
sendFeed(feed, res);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateForPostsFeed(feedOptions, posts) {
|
function generateForPostsFeed(feedOptions, posts) {
|
||||||
@@ -377,7 +359,7 @@ function generateForPostsFeed(feedOptions, posts) {
|
|||||||
|
|
||||||
function generateForTag(req, res, next) {
|
function generateForTag(req, res, next) {
|
||||||
if (parseInt(meta.config['feeds:disableRSS'], 10) === 1) {
|
if (parseInt(meta.config['feeds:disableRSS'], 10) === 1) {
|
||||||
return next();
|
return controllers404.send404(req, res);
|
||||||
}
|
}
|
||||||
var tag = validator.escape(String(req.params.tag));
|
var tag = validator.escape(String(req.params.tag));
|
||||||
var page = parseInt(req.query.page, 10) || 1;
|
var page = parseInt(req.query.page, 10) || 1;
|
||||||
|
|||||||
@@ -169,21 +169,26 @@ SocketHelpers.sendNotificationToTopicOwner = function (tid, fromuid, command, no
|
|||||||
};
|
};
|
||||||
|
|
||||||
SocketHelpers.rescindUpvoteNotification = function (pid, fromuid) {
|
SocketHelpers.rescindUpvoteNotification = function (pid, fromuid) {
|
||||||
var nid = 'upvote:post:' + pid + ':uid:' + fromuid;
|
var uid;
|
||||||
notifications.rescind(nid);
|
async.waterfall([
|
||||||
|
function (next) {
|
||||||
posts.getPostField(pid, 'uid', function (err, uid) {
|
notifications.rescind('upvote:post:' + pid + ':uid:' + fromuid, next);
|
||||||
if (err) {
|
},
|
||||||
return winston.error(err);
|
function (next) {
|
||||||
}
|
posts.getPostField(pid, 'uid', next);
|
||||||
|
},
|
||||||
user.notifications.getUnreadCount(uid, function (err, count) {
|
function (_uid, next) {
|
||||||
if (err) {
|
uid = _uid;
|
||||||
return winston.error(err);
|
user.notifications.getUnreadCount(uid, next);
|
||||||
}
|
},
|
||||||
|
function (count, next) {
|
||||||
websockets.in('uid_' + uid).emit('event:notifications.updateCount', count);
|
websockets.in('uid_' + uid).emit('event:notifications.updateCount', count);
|
||||||
});
|
next();
|
||||||
|
},
|
||||||
|
], function (err) {
|
||||||
|
if (err) {
|
||||||
|
winston.error(err);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -12,16 +12,16 @@ var utils = require('../utils');
|
|||||||
var server = require('./');
|
var server = require('./');
|
||||||
var user = require('../user');
|
var user = require('../user');
|
||||||
|
|
||||||
var SocketModules = {
|
var SocketModules = module.exports;
|
||||||
chats: {},
|
|
||||||
sounds: {},
|
SocketModules.chats = {};
|
||||||
settings: {},
|
SocketModules.sounds = {};
|
||||||
};
|
SocketModules.settings = {};
|
||||||
|
|
||||||
/* Chat */
|
/* Chat */
|
||||||
|
|
||||||
SocketModules.chats.getRaw = function (socket, data, callback) {
|
SocketModules.chats.getRaw = function (socket, data, callback) {
|
||||||
if (!data || !data.hasOwnProperty('mid')) {
|
if (!data || !data.hasOwnProperty('mid') || !data.hasOwnProperty('roomId')) {
|
||||||
return callback(new Error('[[error:invalid-data]]'));
|
return callback(new Error('[[error:invalid-data]]'));
|
||||||
}
|
}
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
@@ -57,13 +57,14 @@ SocketModules.chats.newRoom = function (socket, data, callback) {
|
|||||||
return callback(new Error('[[error:too-many-messages]]'));
|
return callback(new Error('[[error:too-many-messages]]'));
|
||||||
}
|
}
|
||||||
|
|
||||||
Messaging.canMessageUser(socket.uid, data.touid, function (err) {
|
async.waterfall([
|
||||||
if (err) {
|
function (next) {
|
||||||
return callback(err);
|
Messaging.canMessageUser(socket.uid, data.touid, next);
|
||||||
}
|
},
|
||||||
|
function (next) {
|
||||||
Messaging.newRoom(socket.uid, [data.touid], callback);
|
Messaging.newRoom(socket.uid, [data.touid], next);
|
||||||
});
|
},
|
||||||
|
], callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
SocketModules.chats.send = function (socket, data, callback) {
|
SocketModules.chats.send = function (socket, data, callback) {
|
||||||
@@ -223,17 +224,21 @@ SocketModules.chats.leave = function (socket, roomid, callback) {
|
|||||||
|
|
||||||
|
|
||||||
SocketModules.chats.edit = function (socket, data, callback) {
|
SocketModules.chats.edit = function (socket, data, callback) {
|
||||||
if (!data || !data.roomId) {
|
if (!data || !data.roomId || !data.message) {
|
||||||
return callback(new Error('[[error:invalid-data]]'));
|
return callback(new Error('[[error:invalid-data]]'));
|
||||||
}
|
}
|
||||||
|
|
||||||
Messaging.canEdit(data.mid, socket.uid, function (err, allowed) {
|
async.waterfall([
|
||||||
if (err || !allowed) {
|
function (next) {
|
||||||
return callback(err || new Error('[[error:cant-edit-chat-message]]'));
|
Messaging.canEdit(data.mid, socket.uid, next);
|
||||||
}
|
},
|
||||||
|
function (allowed, next) {
|
||||||
Messaging.editMessage(socket.uid, data.mid, data.roomId, data.message, callback);
|
if (!allowed) {
|
||||||
});
|
return next(new Error('[[error:cant-edit-chat-message]]'));
|
||||||
|
}
|
||||||
|
Messaging.editMessage(socket.uid, data.mid, data.roomId, data.message, next);
|
||||||
|
},
|
||||||
|
], callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
SocketModules.chats.delete = function (socket, data, callback) {
|
SocketModules.chats.delete = function (socket, data, callback) {
|
||||||
@@ -241,13 +246,18 @@ SocketModules.chats.delete = function (socket, data, callback) {
|
|||||||
return callback(new Error('[[error:invalid-data]]'));
|
return callback(new Error('[[error:invalid-data]]'));
|
||||||
}
|
}
|
||||||
|
|
||||||
Messaging.canEdit(data.messageId, socket.uid, function (err, allowed) {
|
async.waterfall([
|
||||||
if (err || !allowed) {
|
function (next) {
|
||||||
return callback(err || new Error('[[error:cant-delete-chat-message]]'));
|
Messaging.canEdit(data.messageId, socket.uid, next);
|
||||||
}
|
},
|
||||||
|
function (allowed, next) {
|
||||||
|
if (!allowed) {
|
||||||
|
return next(new Error('[[error:cant-delete-chat-message]]'));
|
||||||
|
}
|
||||||
|
|
||||||
Messaging.deleteMessage(data.messageId, data.roomId, callback);
|
Messaging.deleteMessage(data.messageId, data.roomId, next);
|
||||||
});
|
},
|
||||||
|
], callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
SocketModules.chats.canMessage = function (socket, roomId, callback) {
|
SocketModules.chats.canMessage = function (socket, roomId, callback) {
|
||||||
@@ -255,37 +265,38 @@ SocketModules.chats.canMessage = function (socket, roomId, callback) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
SocketModules.chats.markRead = function (socket, roomId, callback) {
|
SocketModules.chats.markRead = function (socket, roomId, callback) {
|
||||||
if (!socket.uid) {
|
if (!socket.uid || !roomId) {
|
||||||
return callback(new Error('[[error:invalid-data]]'));
|
return callback(new Error('[[error:invalid-data]]'));
|
||||||
}
|
}
|
||||||
async.parallel({
|
async.waterfall([
|
||||||
uidsInRoom: async.apply(Messaging.getUidsInRoom, roomId, 0, -1),
|
function (next) {
|
||||||
markRead: async.apply(Messaging.markRead, socket.uid, roomId),
|
async.parallel({
|
||||||
}, function (err, results) {
|
uidsInRoom: async.apply(Messaging.getUidsInRoom, roomId, 0, -1),
|
||||||
if (err) {
|
markRead: async.apply(Messaging.markRead, socket.uid, roomId),
|
||||||
return callback(err);
|
}, next);
|
||||||
}
|
},
|
||||||
|
function (results, next) {
|
||||||
|
Messaging.pushUnreadCount(socket.uid);
|
||||||
|
server.in('uid_' + socket.uid).emit('event:chats.markedAsRead', { roomId: roomId });
|
||||||
|
|
||||||
Messaging.pushUnreadCount(socket.uid);
|
if (results.uidsInRoom.indexOf(socket.uid.toString()) === -1) {
|
||||||
server.in('uid_' + socket.uid).emit('event:chats.markedAsRead', { roomId: roomId });
|
return callback();
|
||||||
|
}
|
||||||
|
|
||||||
if (results.uidsInRoom.indexOf(socket.uid.toString()) === -1) {
|
// Mark notification read
|
||||||
return callback();
|
var nids = results.uidsInRoom.filter(function (uid) {
|
||||||
}
|
return parseInt(uid, 10) !== socket.uid;
|
||||||
|
}).map(function (uid) {
|
||||||
|
return 'chat_' + uid + '_' + roomId;
|
||||||
|
});
|
||||||
|
|
||||||
// Mark notification read
|
notifications.markReadMultiple(nids, socket.uid, function () {
|
||||||
var nids = results.uidsInRoom.filter(function (uid) {
|
user.notifications.pushCount(socket.uid);
|
||||||
return parseInt(uid, 10) !== socket.uid;
|
});
|
||||||
}).map(function (uid) {
|
|
||||||
return 'chat_' + uid + '_' + roomId;
|
|
||||||
});
|
|
||||||
|
|
||||||
notifications.markReadMultiple(nids, socket.uid, function () {
|
next();
|
||||||
user.notifications.pushCount(socket.uid);
|
},
|
||||||
});
|
], callback);
|
||||||
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
SocketModules.chats.markAllRead = function (socket, data, callback) {
|
SocketModules.chats.markAllRead = function (socket, data, callback) {
|
||||||
@@ -301,8 +312,8 @@ SocketModules.chats.markAllRead = function (socket, data, callback) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
SocketModules.chats.renameRoom = function (socket, data, callback) {
|
SocketModules.chats.renameRoom = function (socket, data, callback) {
|
||||||
if (!data) {
|
if (!data || !data.roomId || !data.newName) {
|
||||||
return callback(new Error('[[error:invalid-name]]'));
|
return callback(new Error('[[error:invalid-data]]'));
|
||||||
}
|
}
|
||||||
|
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
@@ -333,13 +344,13 @@ SocketModules.chats.getRecentChats = function (socket, data, callback) {
|
|||||||
|
|
||||||
SocketModules.chats.hasPrivateChat = function (socket, uid, callback) {
|
SocketModules.chats.hasPrivateChat = function (socket, uid, callback) {
|
||||||
if (!socket.uid || !uid) {
|
if (!socket.uid || !uid) {
|
||||||
return callback(null, new Error('[[error:invalid-data]]'));
|
return callback(new Error('[[error:invalid-data]]'));
|
||||||
}
|
}
|
||||||
Messaging.hasPrivateChat(socket.uid, uid, callback);
|
Messaging.hasPrivateChat(socket.uid, uid, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
SocketModules.chats.getMessages = function (socket, data, callback) {
|
SocketModules.chats.getMessages = function (socket, data, callback) {
|
||||||
if (!socket.uid || !data.uid || !data.roomId) {
|
if (!socket.uid || !data || !data.uid || !data.roomId) {
|
||||||
return callback(new Error('[[error:invalid-data]]'));
|
return callback(new Error('[[error:invalid-data]]'));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -358,5 +369,3 @@ SocketModules.chats.getMessages = function (socket, data, callback) {
|
|||||||
SocketModules.sounds.getUserSoundMap = function getUserSoundMap(socket, data, callback) {
|
SocketModules.sounds.getUserSoundMap = function getUserSoundMap(socket, data, callback) {
|
||||||
meta.sounds.getUserSoundMap(socket.uid, callback);
|
meta.sounds.getUserSoundMap(socket.uid, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = SocketModules;
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ module.exports = function (SocketTopics) {
|
|||||||
}, next);
|
}, next);
|
||||||
},
|
},
|
||||||
function (results, next) {
|
function (results, next) {
|
||||||
if (!results.privileges.read || (parseInt(results.topic.deleted, 10) && !results.privileges.view_deleted)) {
|
if (!results.privileges['topics:read'] || (parseInt(results.topic.deleted, 10) && !results.privileges.view_deleted)) {
|
||||||
return callback(new Error('[[error:no-privileges]]'));
|
return callback(new Error('[[error:no-privileges]]'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -392,30 +392,36 @@ module.exports = function (Topics) {
|
|||||||
|
|
||||||
function getPostReplies(pids, callerUid, callback) {
|
function getPostReplies(pids, callerUid, callback) {
|
||||||
async.map(pids, function (pid, next) {
|
async.map(pids, function (pid, next) {
|
||||||
db.getSortedSetRange('pid:' + pid + ':replies', 0, -1, function (err, replyPids) {
|
var replyPids;
|
||||||
if (err) {
|
var uids = [];
|
||||||
return next(err);
|
async.waterfall([
|
||||||
}
|
function (next) {
|
||||||
|
db.getSortedSetRange('pid:' + pid + ':replies', 0, -1, next);
|
||||||
|
},
|
||||||
|
function (_replyPids, next) {
|
||||||
|
replyPids = _replyPids;
|
||||||
|
|
||||||
var uids = [];
|
var count = 0;
|
||||||
var count = 0;
|
|
||||||
|
|
||||||
async.until(function () {
|
|
||||||
return count === replyPids.length || uids.length === 6;
|
|
||||||
}, function (next) {
|
|
||||||
posts.getPostField(replyPids[count], 'uid', function (err, uid) {
|
|
||||||
uid = parseInt(uid, 10);
|
|
||||||
if (uids.indexOf(uid) === -1) {
|
|
||||||
uids.push(uid);
|
|
||||||
}
|
|
||||||
count += 1;
|
|
||||||
next(err);
|
|
||||||
});
|
|
||||||
}, function (err) {
|
|
||||||
if (err) {
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
async.until(function () {
|
||||||
|
return count === replyPids.length || uids.length === 6;
|
||||||
|
}, function (next) {
|
||||||
|
async.waterfall([
|
||||||
|
function (next) {
|
||||||
|
posts.getPostField(replyPids[count], 'uid', next);
|
||||||
|
},
|
||||||
|
function (uid, next) {
|
||||||
|
uid = parseInt(uid, 10);
|
||||||
|
if (uids.indexOf(uid) === -1) {
|
||||||
|
uids.push(uid);
|
||||||
|
}
|
||||||
|
count += 1;
|
||||||
|
next();
|
||||||
|
},
|
||||||
|
], next);
|
||||||
|
}, next);
|
||||||
|
},
|
||||||
|
function (next) {
|
||||||
async.parallel({
|
async.parallel({
|
||||||
users: function (next) {
|
users: function (next) {
|
||||||
user.getUsersWithFields(uids, ['uid', 'username', 'userslug', 'picture'], callerUid, next);
|
user.getUsersWithFields(uids, ['uid', 'username', 'userslug', 'picture'], callerUid, next);
|
||||||
@@ -425,17 +431,19 @@ module.exports = function (Topics) {
|
|||||||
next(err, utils.toISOString(timestamp));
|
next(err, utils.toISOString(timestamp));
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
}, function (err, replies) {
|
}, next);
|
||||||
if (replies.users.length > 5) {
|
},
|
||||||
replies.users.shift();
|
function (replies, next) {
|
||||||
replies.hasMore = true;
|
if (replies.users.length > 5) {
|
||||||
}
|
replies.users.shift();
|
||||||
|
replies.hasMore = true;
|
||||||
|
}
|
||||||
|
|
||||||
replies.count = replyPids.length;
|
replies.count = replyPids.length;
|
||||||
next(err, replies);
|
replies.text = replies.count > 1 ? '[[topic:replies_to_this_post, ' + replies.count + ']]' : '[[topic:one_reply_to_this_post]]';
|
||||||
});
|
next(null, replies);
|
||||||
});
|
},
|
||||||
});
|
], next);
|
||||||
}, callback);
|
}, callback);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -267,6 +267,7 @@ module.exports = function (Topics) {
|
|||||||
categories.markAsRead(cids, uid, next);
|
categories.markAsRead(cids, uid, next);
|
||||||
},
|
},
|
||||||
function (next) {
|
function (next) {
|
||||||
|
plugins.fireHook('action:topics.markAsRead', { uid: uid, tids: tids });
|
||||||
next(null, true);
|
next(null, true);
|
||||||
},
|
},
|
||||||
], callback);
|
], callback);
|
||||||
|
|||||||
@@ -13,9 +13,8 @@ var file = require('../src/file');
|
|||||||
*
|
*
|
||||||
* 1. Copy TEMPLATE to a file name of your choice. Try to be succinct.
|
* 1. Copy TEMPLATE to a file name of your choice. Try to be succinct.
|
||||||
* 2. Open up that file and change the user-friendly name (can be longer/more descriptive than the file name)
|
* 2. Open up that file and change the user-friendly name (can be longer/more descriptive than the file name)
|
||||||
* and timestamp
|
* and timestamp (don't forget the timestamp!)
|
||||||
* 3. Add your script under the "method" property
|
* 3. Add your script under the "method" property
|
||||||
* 4. Append your filename to the array below for the next NodeBB version.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var Upgrade = {};
|
var Upgrade = {};
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ module.exports = function (User) {
|
|||||||
async.waterfall([
|
async.waterfall([
|
||||||
async.apply(User.getUserFields, uid, ['banned', 'banned:expire']),
|
async.apply(User.getUserFields, uid, ['banned', 'banned:expire']),
|
||||||
function (userData, next) {
|
function (userData, next) {
|
||||||
var banned = parseInt(userData.banned, 10) === 1;
|
var banned = userData && parseInt(userData.banned, 10) === 1;
|
||||||
if (!banned) {
|
if (!banned) {
|
||||||
return next(null, banned);
|
return next(null, banned);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,9 @@ module.exports = function (User) {
|
|||||||
], next);
|
], next);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
], callback);
|
], function (err) {
|
||||||
|
callback(err);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
User.getFollowing = function (uid, start, stop, callback) {
|
User.getFollowing = function (uid, start, stop, callback) {
|
||||||
|
|||||||
@@ -10,16 +10,17 @@ var meta = require('../meta');
|
|||||||
var notifications = require('../notifications');
|
var notifications = require('../notifications');
|
||||||
var privileges = require('../privileges');
|
var privileges = require('../privileges');
|
||||||
|
|
||||||
(function (UserNotifications) {
|
var UserNotifications = module.exports;
|
||||||
UserNotifications.get = function (uid, callback) {
|
|
||||||
if (!parseInt(uid, 10)) {
|
|
||||||
return callback(null, { read: [], unread: [] });
|
|
||||||
}
|
|
||||||
getNotifications(uid, 0, 9, function (err, notifications) {
|
|
||||||
if (err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
UserNotifications.get = function (uid, callback) {
|
||||||
|
if (!parseInt(uid, 10)) {
|
||||||
|
return callback(null, { read: [], unread: [] });
|
||||||
|
}
|
||||||
|
async.waterfall([
|
||||||
|
function (next) {
|
||||||
|
getNotifications(uid, 0, 9, next);
|
||||||
|
},
|
||||||
|
function (notifications, next) {
|
||||||
notifications.read = notifications.read.filter(Boolean);
|
notifications.read = notifications.read.filter(Boolean);
|
||||||
notifications.unread = notifications.unread.filter(Boolean);
|
notifications.unread = notifications.unread.filter(Boolean);
|
||||||
|
|
||||||
@@ -28,200 +29,209 @@ var privileges = require('../privileges');
|
|||||||
notifications.read.length = maxNotifs - notifications.unread.length;
|
notifications.read.length = maxNotifs - notifications.unread.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(null, notifications);
|
next(null, notifications);
|
||||||
});
|
},
|
||||||
};
|
], callback);
|
||||||
|
};
|
||||||
|
|
||||||
function filterNotifications(nids, filter, callback) {
|
function filterNotifications(nids, filter, callback) {
|
||||||
if (!filter) {
|
if (!filter) {
|
||||||
return setImmediate(callback, null, nids);
|
return setImmediate(callback, null, nids);
|
||||||
}
|
|
||||||
async.waterfall([
|
|
||||||
function (next) {
|
|
||||||
var keys = nids.map(function (nid) {
|
|
||||||
return 'notifications:' + nid;
|
|
||||||
});
|
|
||||||
db.getObjectsFields(keys, ['nid', 'type'], next);
|
|
||||||
},
|
|
||||||
function (notifications, next) {
|
|
||||||
nids = notifications.filter(function (notification) {
|
|
||||||
return notification && notification.nid && notification.type === filter;
|
|
||||||
}).map(function (notification) {
|
|
||||||
return notification.nid;
|
|
||||||
});
|
|
||||||
next(null, nids);
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
}
|
}
|
||||||
|
async.waterfall([
|
||||||
|
function (next) {
|
||||||
|
var keys = nids.map(function (nid) {
|
||||||
|
return 'notifications:' + nid;
|
||||||
|
});
|
||||||
|
db.getObjectsFields(keys, ['nid', 'type'], next);
|
||||||
|
},
|
||||||
|
function (notifications, next) {
|
||||||
|
nids = notifications.filter(function (notification) {
|
||||||
|
return notification && notification.nid && notification.type === filter;
|
||||||
|
}).map(function (notification) {
|
||||||
|
return notification.nid;
|
||||||
|
});
|
||||||
|
next(null, nids);
|
||||||
|
},
|
||||||
|
], callback);
|
||||||
|
}
|
||||||
|
|
||||||
UserNotifications.getAll = function (uid, filter, callback) {
|
UserNotifications.getAll = function (uid, filter, callback) {
|
||||||
var nids;
|
var nids;
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
function (next) {
|
function (next) {
|
||||||
async.parallel({
|
async.parallel({
|
||||||
unread: function (next) {
|
unread: function (next) {
|
||||||
db.getSortedSetRevRange('uid:' + uid + ':notifications:unread', 0, -1, next);
|
db.getSortedSetRevRange('uid:' + uid + ':notifications:unread', 0, -1, next);
|
||||||
},
|
},
|
||||||
read: function (next) {
|
read: function (next) {
|
||||||
db.getSortedSetRevRange('uid:' + uid + ':notifications:read', 0, -1, next);
|
db.getSortedSetRevRange('uid:' + uid + ':notifications:read', 0, -1, next);
|
||||||
},
|
},
|
||||||
}, next);
|
}, next);
|
||||||
},
|
},
|
||||||
function (results, next) {
|
function (results, next) {
|
||||||
nids = results.unread.concat(results.read);
|
nids = results.unread.concat(results.read);
|
||||||
db.isSortedSetMembers('notifications', nids, next);
|
db.isSortedSetMembers('notifications', nids, next);
|
||||||
},
|
},
|
||||||
function (exists, next) {
|
function (exists, next) {
|
||||||
var deleteNids = [];
|
var deleteNids = [];
|
||||||
|
|
||||||
nids = nids.filter(function (nid, index) {
|
nids = nids.filter(function (nid, index) {
|
||||||
if (!nid || !exists[index]) {
|
if (!nid || !exists[index]) {
|
||||||
deleteNids.push(nid);
|
deleteNids.push(nid);
|
||||||
}
|
|
||||||
return nid && exists[index];
|
|
||||||
});
|
|
||||||
|
|
||||||
deleteUserNids(deleteNids, uid, next);
|
|
||||||
},
|
|
||||||
function (next) {
|
|
||||||
filterNotifications(nids, filter, next);
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
function deleteUserNids(nids, uid, callback) {
|
|
||||||
callback = callback || function () {};
|
|
||||||
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);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getNotifications(uid, start, stop, callback) {
|
|
||||||
async.parallel({
|
|
||||||
unread: function (next) {
|
|
||||||
getNotificationsFromSet('uid:' + uid + ':notifications:unread', false, uid, start, stop, next);
|
|
||||||
},
|
|
||||||
read: function (next) {
|
|
||||||
getNotificationsFromSet('uid:' + uid + ':notifications:read', true, uid, start, stop, next);
|
|
||||||
},
|
|
||||||
}, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getNotificationsFromSet(set, read, uid, start, stop, callback) {
|
|
||||||
async.waterfall([
|
|
||||||
function (next) {
|
|
||||||
db.getSortedSetRevRange(set, start, stop, next);
|
|
||||||
},
|
|
||||||
function (nids, next) {
|
|
||||||
if (!Array.isArray(nids) || !nids.length) {
|
|
||||||
return callback(null, []);
|
|
||||||
}
|
}
|
||||||
|
return nid && exists[index];
|
||||||
|
});
|
||||||
|
|
||||||
UserNotifications.getNotifications(nids, uid, next);
|
deleteUserNids(deleteNids, uid, next);
|
||||||
},
|
},
|
||||||
], callback);
|
function (next) {
|
||||||
|
filterNotifications(nids, filter, next);
|
||||||
|
},
|
||||||
|
], callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
function deleteUserNids(nids, uid, callback) {
|
||||||
|
callback = callback || function () {};
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
UserNotifications.getNotifications = function (nids, uid, callback) {
|
function getNotifications(uid, start, stop, callback) {
|
||||||
var notificationData = [];
|
async.parallel({
|
||||||
async.waterfall([
|
unread: function (next) {
|
||||||
function (next) {
|
getNotificationsFromSet('uid:' + uid + ':notifications:unread', false, uid, start, stop, next);
|
||||||
async.parallel({
|
},
|
||||||
notifications: function (next) {
|
read: function (next) {
|
||||||
notifications.getMultiple(nids, next);
|
getNotificationsFromSet('uid:' + uid + ':notifications:read', true, uid, start, stop, next);
|
||||||
},
|
},
|
||||||
hasRead: function (next) {
|
}, callback);
|
||||||
db.isSortedSetMembers('uid:' + uid + ':notifications:read', nids, next);
|
}
|
||||||
},
|
|
||||||
}, next);
|
|
||||||
},
|
|
||||||
function (results, next) {
|
|
||||||
var deletedNids = [];
|
|
||||||
notificationData = results.notifications.filter(function (notification, index) {
|
|
||||||
if (!notification || !notification.nid) {
|
|
||||||
deletedNids.push(nids[index]);
|
|
||||||
}
|
|
||||||
if (notification) {
|
|
||||||
notification.read = results.hasRead[index];
|
|
||||||
notification.readClass = !notification.read ? 'unread' : '';
|
|
||||||
}
|
|
||||||
|
|
||||||
return notification && notification.path;
|
|
||||||
});
|
|
||||||
|
|
||||||
deleteUserNids(deletedNids, uid, next);
|
|
||||||
},
|
|
||||||
function (next) {
|
|
||||||
notifications.merge(notificationData, next);
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
UserNotifications.getDailyUnread = function (uid, callback) {
|
|
||||||
var yesterday = Date.now() - (1000 * 60 * 60 * 24); // Approximate, can be more or less depending on time changes, makes no difference really.
|
|
||||||
|
|
||||||
db.getSortedSetRevRangeByScore('uid:' + uid + ':notifications:unread', 0, 20, '+inf', yesterday, function (err, nids) {
|
|
||||||
if (err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
function getNotificationsFromSet(set, read, uid, start, stop, callback) {
|
||||||
|
async.waterfall([
|
||||||
|
function (next) {
|
||||||
|
db.getSortedSetRevRange(set, start, stop, next);
|
||||||
|
},
|
||||||
|
function (nids, next) {
|
||||||
if (!Array.isArray(nids) || !nids.length) {
|
if (!Array.isArray(nids) || !nids.length) {
|
||||||
return callback(null, []);
|
return callback(null, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
UserNotifications.getNotifications(nids, uid, callback);
|
UserNotifications.getNotifications(nids, uid, next);
|
||||||
});
|
},
|
||||||
};
|
], callback);
|
||||||
|
}
|
||||||
|
|
||||||
UserNotifications.getUnreadCount = function (uid, callback) {
|
UserNotifications.getNotifications = function (nids, uid, callback) {
|
||||||
if (!parseInt(uid, 10)) {
|
var notificationData = [];
|
||||||
return callback(null, 0);
|
async.waterfall([
|
||||||
}
|
function (next) {
|
||||||
|
async.parallel({
|
||||||
|
notifications: function (next) {
|
||||||
|
notifications.getMultiple(nids, next);
|
||||||
|
},
|
||||||
|
hasRead: function (next) {
|
||||||
|
db.isSortedSetMembers('uid:' + uid + ':notifications:read', nids, next);
|
||||||
|
},
|
||||||
|
}, next);
|
||||||
|
},
|
||||||
|
function (results, next) {
|
||||||
|
var deletedNids = [];
|
||||||
|
notificationData = results.notifications.filter(function (notification, index) {
|
||||||
|
if (!notification || !notification.nid) {
|
||||||
|
deletedNids.push(nids[index]);
|
||||||
|
}
|
||||||
|
if (notification) {
|
||||||
|
notification.read = results.hasRead[index];
|
||||||
|
notification.readClass = !notification.read ? 'unread' : '';
|
||||||
|
}
|
||||||
|
|
||||||
// Collapse any notifications with identical mergeIds
|
return notification && notification.path;
|
||||||
async.waterfall([
|
});
|
||||||
async.apply(db.getSortedSetRevRange, 'uid:' + uid + ':notifications:unread', 0, 99),
|
|
||||||
async.apply(notifications.filterExists),
|
|
||||||
function (nids, next) {
|
|
||||||
var keys = nids.map(function (nid) {
|
|
||||||
return 'notifications:' + nid;
|
|
||||||
});
|
|
||||||
|
|
||||||
db.getObjectsFields(keys, ['mergeId'], next);
|
deleteUserNids(deletedNids, uid, next);
|
||||||
},
|
},
|
||||||
function (mergeIds, next) {
|
function (next) {
|
||||||
mergeIds = mergeIds.map(function (set) {
|
notifications.merge(notificationData, next);
|
||||||
return set.mergeId;
|
},
|
||||||
});
|
], callback);
|
||||||
|
};
|
||||||
|
|
||||||
next(null, mergeIds.reduce(function (count, mergeId, idx, arr) {
|
UserNotifications.getDailyUnread = function (uid, callback) {
|
||||||
// A missing (null) mergeId means that notification is counted separately.
|
var yesterday = Date.now() - (1000 * 60 * 60 * 24); // Approximate, can be more or less depending on time changes, makes no difference really.
|
||||||
if (mergeId === null || idx === arr.indexOf(mergeId)) {
|
|
||||||
count += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return count;
|
async.waterfall([
|
||||||
}, 0));
|
function (next) {
|
||||||
},
|
db.getSortedSetRevRangeByScore('uid:' + uid + ':notifications:unread', 0, 20, '+inf', yesterday, next);
|
||||||
], callback);
|
},
|
||||||
};
|
function (nids, next) {
|
||||||
|
if (!Array.isArray(nids) || !nids.length) {
|
||||||
UserNotifications.getUnreadByField = function (uid, field, values, callback) {
|
return callback(null, []);
|
||||||
db.getSortedSetRevRange('uid:' + uid + ':notifications:unread', 0, 99, function (err, nids) {
|
|
||||||
if (err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UserNotifications.getNotifications(nids, uid, next);
|
||||||
|
},
|
||||||
|
], callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
UserNotifications.getUnreadCount = function (uid, callback) {
|
||||||
|
if (!parseInt(uid, 10)) {
|
||||||
|
return callback(null, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async.waterfall([
|
||||||
|
function (next) {
|
||||||
|
db.getSortedSetRevRange('uid:' + uid + ':notifications:unread', 0, 99, next);
|
||||||
|
},
|
||||||
|
function (nids, next) {
|
||||||
|
notifications.filterExists(nids, next);
|
||||||
|
},
|
||||||
|
function (nids, next) {
|
||||||
|
var keys = nids.map(function (nid) {
|
||||||
|
return 'notifications:' + nid;
|
||||||
|
});
|
||||||
|
|
||||||
|
db.getObjectsFields(keys, ['mergeId'], next);
|
||||||
|
},
|
||||||
|
function (mergeIds, next) {
|
||||||
|
// Collapse any notifications with identical mergeIds
|
||||||
|
mergeIds = mergeIds.map(function (set) {
|
||||||
|
return set.mergeId;
|
||||||
|
});
|
||||||
|
|
||||||
|
next(null, mergeIds.reduce(function (count, mergeId, idx, arr) {
|
||||||
|
// A missing (null) mergeId means that notification is counted separately.
|
||||||
|
if (mergeId === null || idx === arr.indexOf(mergeId)) {
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}, 0));
|
||||||
|
},
|
||||||
|
], callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
UserNotifications.getUnreadByField = function (uid, field, values, callback) {
|
||||||
|
var nids;
|
||||||
|
async.waterfall([
|
||||||
|
function (next) {
|
||||||
|
db.getSortedSetRevRange('uid:' + uid + ':notifications:unread', 0, 99, next);
|
||||||
|
},
|
||||||
|
function (_nids, next) {
|
||||||
|
nids = _nids;
|
||||||
if (!Array.isArray(nids) || !nids.length) {
|
if (!Array.isArray(nids) || !nids.length) {
|
||||||
return callback(null, []);
|
return callback(null, []);
|
||||||
}
|
}
|
||||||
@@ -230,124 +240,125 @@ var privileges = require('../privileges');
|
|||||||
return 'notifications:' + nid;
|
return 'notifications:' + nid;
|
||||||
});
|
});
|
||||||
|
|
||||||
db.getObjectsFields(keys, ['nid', field], function (err, notifications) {
|
db.getObjectsFields(keys, ['nid', field], next);
|
||||||
if (err) {
|
},
|
||||||
return callback(err);
|
function (notifications, next) {
|
||||||
}
|
values = values.map(function () { return values.toString(); });
|
||||||
|
nids = notifications.filter(function (notification) {
|
||||||
values = values.map(function () { return values.toString(); });
|
return notification && notification[field] && values.indexOf(notification[field].toString()) !== -1;
|
||||||
nids = notifications.filter(function (notification) {
|
}).map(function (notification) {
|
||||||
return notification && notification[field] && values.indexOf(notification[field].toString()) !== -1;
|
return notification.nid;
|
||||||
}).map(function (notification) {
|
|
||||||
return notification.nid;
|
|
||||||
});
|
|
||||||
|
|
||||||
callback(null, nids);
|
|
||||||
});
|
});
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
UserNotifications.deleteAll = function (uid, callback) {
|
next(null, nids);
|
||||||
if (!parseInt(uid, 10)) {
|
},
|
||||||
return callback();
|
], callback);
|
||||||
}
|
};
|
||||||
async.parallel([
|
|
||||||
function (next) {
|
|
||||||
db.delete('uid:' + uid + ':notifications:unread', next);
|
|
||||||
},
|
|
||||||
function (next) {
|
|
||||||
db.delete('uid:' + uid + ':notifications:read', next);
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
UserNotifications.sendTopicNotificationToFollowers = function (uid, topicData, postData) {
|
UserNotifications.deleteAll = function (uid, callback) {
|
||||||
var followers;
|
if (!parseInt(uid, 10)) {
|
||||||
async.waterfall([
|
return callback();
|
||||||
function (next) {
|
}
|
||||||
db.getSortedSetRange('followers:' + uid, 0, -1, next);
|
async.parallel([
|
||||||
},
|
function (next) {
|
||||||
function (followers, next) {
|
db.delete('uid:' + uid + ':notifications:unread', next);
|
||||||
if (!Array.isArray(followers) || !followers.length) {
|
},
|
||||||
return;
|
function (next) {
|
||||||
}
|
db.delete('uid:' + uid + ':notifications:read', next);
|
||||||
privileges.categories.filterUids('read', topicData.cid, followers, next);
|
},
|
||||||
},
|
], callback);
|
||||||
function (_followers, next) {
|
};
|
||||||
followers = _followers;
|
|
||||||
if (!followers.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var title = topicData.title;
|
UserNotifications.sendTopicNotificationToFollowers = function (uid, topicData, postData) {
|
||||||
if (title) {
|
var followers;
|
||||||
title = S(title).decodeHTMLEntities().s;
|
async.waterfall([
|
||||||
}
|
function (next) {
|
||||||
|
db.getSortedSetRange('followers:' + uid, 0, -1, next);
|
||||||
notifications.create({
|
},
|
||||||
type: 'new-topic',
|
function (followers, next) {
|
||||||
bodyShort: '[[notifications:user_posted_topic, ' + postData.user.username + ', ' + title + ']]',
|
if (!Array.isArray(followers) || !followers.length) {
|
||||||
bodyLong: postData.content,
|
return;
|
||||||
pid: postData.pid,
|
}
|
||||||
path: '/post/' + postData.pid,
|
privileges.categories.filterUids('read', topicData.cid, followers, next);
|
||||||
nid: 'tid:' + postData.tid + ':uid:' + uid,
|
},
|
||||||
tid: postData.tid,
|
function (_followers, next) {
|
||||||
from: uid,
|
followers = _followers;
|
||||||
}, next);
|
if (!followers.length) {
|
||||||
},
|
return;
|
||||||
], function (err, notification) {
|
|
||||||
if (err) {
|
|
||||||
return winston.error(err);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (notification) {
|
var title = topicData.title;
|
||||||
notifications.push(notification, followers);
|
if (title) {
|
||||||
|
title = S(title).decodeHTMLEntities().s;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
UserNotifications.sendWelcomeNotification = function (uid, callback) {
|
notifications.create({
|
||||||
callback = callback || function () {};
|
type: 'new-topic',
|
||||||
if (!meta.config.welcomeNotification) {
|
bodyShort: '[[notifications:user_posted_topic, ' + postData.user.username + ', ' + title + ']]',
|
||||||
return callback();
|
bodyLong: postData.content,
|
||||||
|
pid: postData.pid,
|
||||||
|
path: '/post/' + postData.pid,
|
||||||
|
nid: 'tid:' + postData.tid + ':uid:' + uid,
|
||||||
|
tid: postData.tid,
|
||||||
|
from: uid,
|
||||||
|
}, next);
|
||||||
|
},
|
||||||
|
], function (err, notification) {
|
||||||
|
if (err) {
|
||||||
|
return winston.error(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
var path = meta.config.welcomeLink ? meta.config.welcomeLink : '#';
|
if (notification) {
|
||||||
|
notifications.push(notification, followers);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
notifications.create({
|
UserNotifications.sendWelcomeNotification = function (uid, callback) {
|
||||||
bodyShort: meta.config.welcomeNotification,
|
callback = callback || function () {};
|
||||||
path: path,
|
if (!meta.config.welcomeNotification) {
|
||||||
nid: 'welcome_' + uid,
|
return callback();
|
||||||
}, function (err, notification) {
|
}
|
||||||
if (err || !notification) {
|
|
||||||
return callback(err);
|
var path = meta.config.welcomeLink ? meta.config.welcomeLink : '#';
|
||||||
|
|
||||||
|
async.waterfall([
|
||||||
|
function (next) {
|
||||||
|
notifications.create({
|
||||||
|
bodyShort: meta.config.welcomeNotification,
|
||||||
|
path: path,
|
||||||
|
nid: 'welcome_' + uid,
|
||||||
|
}, next);
|
||||||
|
},
|
||||||
|
function (notification, next) {
|
||||||
|
if (!notification) {
|
||||||
|
return next();
|
||||||
}
|
}
|
||||||
|
notifications.push(notification, [uid], next);
|
||||||
|
},
|
||||||
|
], callback);
|
||||||
|
};
|
||||||
|
|
||||||
notifications.push(notification, [uid], callback);
|
UserNotifications.sendNameChangeNotification = function (uid, username) {
|
||||||
});
|
notifications.create({
|
||||||
};
|
bodyShort: '[[user:username_taken_workaround, ' + username + ']]',
|
||||||
|
image: 'brand:logo',
|
||||||
|
nid: 'username_taken:' + uid,
|
||||||
|
datetime: Date.now(),
|
||||||
|
}, function (err, notification) {
|
||||||
|
if (!err && notification) {
|
||||||
|
notifications.push(notification, uid);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
UserNotifications.sendNameChangeNotification = function (uid, username) {
|
UserNotifications.pushCount = function (uid) {
|
||||||
notifications.create({
|
var websockets = require('./../socket.io');
|
||||||
bodyShort: '[[user:username_taken_workaround, ' + username + ']]',
|
UserNotifications.getUnreadCount(uid, function (err, count) {
|
||||||
image: 'brand:logo',
|
if (err) {
|
||||||
nid: 'username_taken:' + uid,
|
return winston.error(err.stack);
|
||||||
datetime: Date.now(),
|
}
|
||||||
}, function (err, notification) {
|
|
||||||
if (!err && notification) {
|
|
||||||
notifications.push(notification, uid);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
UserNotifications.pushCount = function (uid) {
|
websockets.in('uid_' + uid).emit('event:notifications.updateCount', count);
|
||||||
var websockets = require('./../socket.io');
|
});
|
||||||
UserNotifications.getUnreadCount(uid, function (err, count) {
|
};
|
||||||
if (err) {
|
|
||||||
return winston.error(err.stack);
|
|
||||||
}
|
|
||||||
|
|
||||||
websockets.in('uid_' + uid).emit('event:notifications.updateCount', count);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}(exports));
|
|
||||||
|
|||||||
@@ -68,16 +68,17 @@ module.exports = function (User) {
|
|||||||
var resultsPerPage = parseInt(meta.config.userSearchResultsPerPage, 10) || 20;
|
var resultsPerPage = parseInt(meta.config.userSearchResultsPerPage, 10) || 20;
|
||||||
var hardCap = resultsPerPage * 10;
|
var hardCap = resultsPerPage * 10;
|
||||||
|
|
||||||
db.getSortedSetRangeByLex(searchBy + ':sorted', min, max, 0, hardCap, function (err, data) {
|
async.waterfall([
|
||||||
if (err) {
|
function (next) {
|
||||||
return callback(err);
|
db.getSortedSetRangeByLex(searchBy + ':sorted', min, max, 0, hardCap, next);
|
||||||
}
|
},
|
||||||
|
function (data, next) {
|
||||||
var uids = data.map(function (data) {
|
var uids = data.map(function (data) {
|
||||||
return data.split(':')[1];
|
return data.split(':')[1];
|
||||||
});
|
});
|
||||||
callback(null, uids);
|
next(null, uids);
|
||||||
});
|
},
|
||||||
|
], callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
function filterAndSortUids(uids, data, callback) {
|
function filterAndSortUids(uids, data, callback) {
|
||||||
@@ -94,37 +95,38 @@ module.exports = function (User) {
|
|||||||
fields.push('flags');
|
fields.push('flags');
|
||||||
}
|
}
|
||||||
|
|
||||||
User.getUsersFields(uids, fields, function (err, userData) {
|
async.waterfall([
|
||||||
if (err) {
|
function (next) {
|
||||||
return callback(err);
|
User.getUsersFields(uids, fields, next);
|
||||||
}
|
},
|
||||||
|
function (userData, next) {
|
||||||
|
if (data.onlineOnly) {
|
||||||
|
userData = userData.filter(function (user) {
|
||||||
|
return user && user.status !== 'offline' && (Date.now() - parseInt(user.lastonline, 10) < 300000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (data.onlineOnly) {
|
if (data.bannedOnly) {
|
||||||
userData = userData.filter(function (user) {
|
userData = userData.filter(function (user) {
|
||||||
return user && user.status !== 'offline' && (Date.now() - parseInt(user.lastonline, 10) < 300000);
|
return user && parseInt(user.banned, 10) === 1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.flaggedOnly) {
|
||||||
|
userData = userData.filter(function (user) {
|
||||||
|
return user && parseInt(user.flags, 10) > 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
sortUsers(userData, sortBy);
|
||||||
|
|
||||||
|
uids = userData.map(function (user) {
|
||||||
|
return user && user.uid;
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
if (data.bannedOnly) {
|
next(null, uids);
|
||||||
userData = userData.filter(function (user) {
|
},
|
||||||
return user && user.banned;
|
], callback);
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.flaggedOnly) {
|
|
||||||
userData = userData.filter(function (user) {
|
|
||||||
return user && parseInt(user.flags, 10) > 0;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
sortUsers(userData, sortBy);
|
|
||||||
|
|
||||||
uids = userData.map(function (user) {
|
|
||||||
return user && user.uid;
|
|
||||||
});
|
|
||||||
|
|
||||||
callback(null, uids);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function sortUsers(userData, sortBy) {
|
function sortUsers(userData, sortBy) {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ var meta = require('../meta');
|
|||||||
var db = require('../database');
|
var db = require('../database');
|
||||||
var plugins = require('../plugins');
|
var plugins = require('../plugins');
|
||||||
|
|
||||||
|
var pubsub = require('../pubsub');
|
||||||
var LRU = require('lru-cache');
|
var LRU = require('lru-cache');
|
||||||
|
|
||||||
var cache = LRU({
|
var cache = LRU({
|
||||||
@@ -19,6 +20,10 @@ var cache = LRU({
|
|||||||
module.exports = function (User) {
|
module.exports = function (User) {
|
||||||
User.settingsCache = cache;
|
User.settingsCache = cache;
|
||||||
|
|
||||||
|
pubsub.on('user:settings:cache:del', function (uid) {
|
||||||
|
cache.del('user:' + uid + ':settings');
|
||||||
|
});
|
||||||
|
|
||||||
User.getSettings = function (uid, callback) {
|
User.getSettings = function (uid, callback) {
|
||||||
if (!parseInt(uid, 10)) {
|
if (!parseInt(uid, 10)) {
|
||||||
return onSettingsLoaded(0, {}, callback);
|
return onSettingsLoaded(0, {}, callback);
|
||||||
@@ -178,6 +183,7 @@ module.exports = function (User) {
|
|||||||
},
|
},
|
||||||
function (next) {
|
function (next) {
|
||||||
cache.del('user:' + uid + ':settings');
|
cache.del('user:' + uid + ':settings');
|
||||||
|
pubsub.publish('user:settings:cache:del', uid);
|
||||||
User.getSettings(uid, next);
|
User.getSettings(uid, next);
|
||||||
},
|
},
|
||||||
], callback);
|
], callback);
|
||||||
|
|||||||
@@ -7,8 +7,64 @@ var request = require('request');
|
|||||||
|
|
||||||
var db = require('./mocks/databasemock');
|
var db = require('./mocks/databasemock');
|
||||||
var user = require('../src/user');
|
var user = require('../src/user');
|
||||||
|
var meta = require('../src/meta');
|
||||||
|
|
||||||
describe('authentication', function () {
|
describe('authentication', function () {
|
||||||
|
function loginUser(username, password, callback) {
|
||||||
|
var jar = request.jar();
|
||||||
|
request({
|
||||||
|
url: nconf.get('url') + '/api/config',
|
||||||
|
json: true,
|
||||||
|
jar: jar,
|
||||||
|
}, function (err, response, body) {
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
request.post(nconf.get('url') + '/login', {
|
||||||
|
form: {
|
||||||
|
username: username,
|
||||||
|
password: password,
|
||||||
|
},
|
||||||
|
json: true,
|
||||||
|
jar: jar,
|
||||||
|
headers: {
|
||||||
|
'x-csrf-token': body.csrf_token,
|
||||||
|
},
|
||||||
|
}, function (err, response, body) {
|
||||||
|
callback(err, response, body, jar);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function registerUser(email, username, password, callback) {
|
||||||
|
var jar = request.jar();
|
||||||
|
request({
|
||||||
|
url: nconf.get('url') + '/api/config',
|
||||||
|
json: true,
|
||||||
|
jar: jar,
|
||||||
|
}, function (err, response, body) {
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
request.post(nconf.get('url') + '/register', {
|
||||||
|
form: {
|
||||||
|
email: email,
|
||||||
|
username: username,
|
||||||
|
password: password,
|
||||||
|
},
|
||||||
|
json: true,
|
||||||
|
jar: jar,
|
||||||
|
headers: {
|
||||||
|
'x-csrf-token': body.csrf_token,
|
||||||
|
},
|
||||||
|
}, function (err, response, body) {
|
||||||
|
callback(err, response, body, jar);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
var jar = request.jar();
|
var jar = request.jar();
|
||||||
var regularUid;
|
var regularUid;
|
||||||
before(function (done) {
|
before(function (done) {
|
||||||
@@ -89,43 +145,24 @@ describe('authentication', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should login a user', function (done) {
|
it('should login a user', function (done) {
|
||||||
var jar = request.jar();
|
loginUser('regular', 'regularpwd', function (err, response, body, jar) {
|
||||||
request({
|
|
||||||
url: nconf.get('url') + '/api/config',
|
|
||||||
json: true,
|
|
||||||
jar: jar,
|
|
||||||
}, function (err, response, body) {
|
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
|
assert(body);
|
||||||
|
|
||||||
request.post(nconf.get('url') + '/login', {
|
request({
|
||||||
form: {
|
url: nconf.get('url') + '/api/me',
|
||||||
username: 'regular',
|
|
||||||
password: 'regularpwd',
|
|
||||||
},
|
|
||||||
json: true,
|
json: true,
|
||||||
jar: jar,
|
jar: jar,
|
||||||
headers: {
|
|
||||||
'x-csrf-token': body.csrf_token,
|
|
||||||
},
|
|
||||||
}, function (err, response, body) {
|
}, function (err, response, body) {
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
assert(body);
|
assert(body);
|
||||||
|
assert.equal(body.username, 'regular');
|
||||||
request({
|
assert.equal(body.email, 'regular@nodebb.org');
|
||||||
url: nconf.get('url') + '/api/me',
|
db.getObject('uid:' + regularUid + ':sessionUUID:sessionId', function (err, sessions) {
|
||||||
json: true,
|
|
||||||
jar: jar,
|
|
||||||
}, function (err, response, body) {
|
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
assert(body);
|
assert(sessions);
|
||||||
assert.equal(body.username, 'regular');
|
assert(Object.keys(sessions).length > 0);
|
||||||
assert.equal(body.email, 'regular@nodebb.org');
|
done();
|
||||||
db.getObject('uid:' + regularUid + ':sessionUUID:sessionId', function (err, sessions) {
|
|
||||||
assert.ifError(err);
|
|
||||||
assert(sessions);
|
|
||||||
assert(Object.keys(sessions).length > 0);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -147,6 +184,119 @@ describe('authentication', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should fail to login if user does not exist', function (done) {
|
||||||
|
loginUser('doesnotexist', 'nopassword', function (err, response, body) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.equal(response.statusCode, 403);
|
||||||
|
assert.equal(body, '[[error:invalid-login-credentials]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail to login if username is empty', function (done) {
|
||||||
|
loginUser('', 'some password', function (err, response, body) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.equal(response.statusCode, 403);
|
||||||
|
assert.equal(body, '[[error:invalid-username-or-password]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail to login if password is empty', function (done) {
|
||||||
|
loginUser('someuser', '', function (err, response, body) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.equal(response.statusCode, 403);
|
||||||
|
assert.equal(body, '[[error:invalid-username-or-password]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail to login if username and password are empty', function (done) {
|
||||||
|
loginUser('', '', function (err, response, body) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.equal(response.statusCode, 403);
|
||||||
|
assert.equal(body, '[[error:invalid-username-or-password]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail to login if password is longer than 4096', function (done) {
|
||||||
|
var longPassword;
|
||||||
|
for (var i = 0; i < 5000; i++) {
|
||||||
|
longPassword += 'a';
|
||||||
|
}
|
||||||
|
loginUser('someuser', longPassword, function (err, response, body) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.equal(response.statusCode, 403);
|
||||||
|
assert.equal(body, '[[error:password-too-long]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should fail to login if local login is disabled', function (done) {
|
||||||
|
meta.config.allowLocalLogin = 0;
|
||||||
|
loginUser('someuser', 'somepass', function (err, response, body) {
|
||||||
|
meta.config.allowLocalLogin = 1;
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.equal(response.statusCode, 403);
|
||||||
|
assert.equal(body, '[[error:local-login-disabled]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail to register if registraton is disabled', function (done) {
|
||||||
|
meta.config.registrationType = 'disabled';
|
||||||
|
registerUser('some@user.com', 'someuser', 'somepassword', function (err, response, body) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.equal(response.statusCode, 403);
|
||||||
|
assert.equal(body, 'Forbidden');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return error if invitation is not valid', function (done) {
|
||||||
|
meta.config.registrationType = 'invite-only';
|
||||||
|
registerUser('some@user.com', 'someuser', 'somepassword', function (err, response, body) {
|
||||||
|
meta.config.registrationType = 'normal';
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.equal(response.statusCode, 400);
|
||||||
|
assert.equal(body, '[[error:invalid-data]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail to register if email is falsy', function (done) {
|
||||||
|
registerUser('', 'someuser', 'somepassword', function (err, response, body) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.equal(response.statusCode, 400);
|
||||||
|
assert.equal(body, '[[error:invalid-email]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail to register if username is falsy or too short', function (done) {
|
||||||
|
registerUser('some@user.com', '', 'somepassword', function (err, response, body) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.equal(response.statusCode, 400);
|
||||||
|
assert.equal(body, '[[error:username-too-short]]');
|
||||||
|
registerUser('some@user.com', 'a', 'somepassword', function (err, response, body) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.equal(response.statusCode, 400);
|
||||||
|
assert.equal(body, '[[error:username-too-short]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail to register if username is too long', function (done) {
|
||||||
|
registerUser('some@user.com', 'thisisareallylongusername', '123456', function (err, response, body) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.equal(response.statusCode, 400);
|
||||||
|
assert.equal(body, '[[error:username-too-long]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
after(function (done) {
|
after(function (done) {
|
||||||
db.emptydb(done);
|
db.emptydb(done);
|
||||||
|
|||||||
192
test/build.js
192
test/build.js
@@ -1,19 +1,199 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
var string = require('string');
|
||||||
|
var path = require('path');
|
||||||
|
var fs = require('fs');
|
||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
|
var mkdirp = require('mkdirp');
|
||||||
|
var rimraf = require('rimraf');
|
||||||
|
var async = require('async');
|
||||||
|
|
||||||
var db = require('./mocks/databasemock');
|
var db = require('./mocks/databasemock');
|
||||||
|
var file = require('../src/file');
|
||||||
|
|
||||||
describe('Build', function () {
|
describe('minifier', function () {
|
||||||
before(function (done) {
|
before(function (done) {
|
||||||
db.setupMockDefaults(done);
|
mkdirp(path.join(__dirname, '../build/test'), done);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should build all assets', function (done) {
|
var minifier = require('../src/meta/minifier');
|
||||||
this.timeout(50000);
|
var scripts = [
|
||||||
var build = require('../src/meta/build');
|
path.resolve(__dirname, './files/1.js'),
|
||||||
build.buildAll(function (err) {
|
path.resolve(__dirname, './files/2.js'),
|
||||||
|
];
|
||||||
|
it('.js.bundle() should concat scripts', function (done) {
|
||||||
|
minifier.js.bundle(scripts, false, false, function (err, bundle) {
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
|
assert.strictEqual(
|
||||||
|
bundle.code,
|
||||||
|
'(function (window, document) {' +
|
||||||
|
'\n\twindow.doStuff = function () {' +
|
||||||
|
'\n\t\tdocument.body.innerHTML = \'Stuff has been done\';' +
|
||||||
|
'\n\t};' +
|
||||||
|
'\n})(window, document);' +
|
||||||
|
'\n' +
|
||||||
|
'\n;function foo(name, age) {' +
|
||||||
|
'\n\treturn \'The person known as "\' + name + \'" is \' + age + \' years old\';' +
|
||||||
|
'\n}' +
|
||||||
|
'\n'
|
||||||
|
);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('.js.bundle() should minify scripts', function (done) {
|
||||||
|
minifier.js.bundle(scripts, true, false, function (err, bundle) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.strictEqual(
|
||||||
|
bundle.code,
|
||||||
|
'(function(n,o){n.doStuff=function(){o.body.innerHTML="Stuff has been done"}})(window,document);function foo(n,o){return\'The person known as "\'+n+\'" is \'+o+" years old"}'
|
||||||
|
);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('.js.minifyBatch() should minify each script', function (done) {
|
||||||
|
var s = scripts.map(function (script) {
|
||||||
|
return {
|
||||||
|
srcPath: script,
|
||||||
|
destPath: path.resolve(__dirname, '../build/test', path.basename(script)),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
minifier.js.minifyBatch(s, false, function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
|
||||||
|
assert(file.existsSync(s[0].destPath));
|
||||||
|
assert(file.existsSync(s[1].destPath));
|
||||||
|
|
||||||
|
fs.readFile(s[0].destPath, function (err, buffer) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.strictEqual(
|
||||||
|
buffer.toString(),
|
||||||
|
'(function(n,o){n.doStuff=function(){o.body.innerHTML="Stuff has been done"}})(window,document);'
|
||||||
|
);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
var styles = [
|
||||||
|
'@import (inline) "./1.css";',
|
||||||
|
'@import "./2.less";',
|
||||||
|
].join('\n');
|
||||||
|
var paths = [
|
||||||
|
path.resolve(__dirname, './files'),
|
||||||
|
];
|
||||||
|
it('.css.bundle() should concat styles', function (done) {
|
||||||
|
minifier.css.bundle(styles, paths, false, false, function (err, bundle) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.strictEqual(bundle.code, '.help { margin: 10px; } .yellow { background: yellow; }\n.help {\n display: block;\n}\n.help .blue {\n background: blue;\n}\n');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('.css.bundle() should minify styles', function (done) {
|
||||||
|
minifier.css.bundle(styles, paths, true, false, function (err, bundle) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.strictEqual(bundle.code, '.help{margin:10px;display:block}.yellow{background:#ff0}.help .blue{background:#00f}');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Build', function (done) {
|
||||||
|
var build = require('../src/meta/build');
|
||||||
|
|
||||||
|
before(function (done) {
|
||||||
|
async.parallel([
|
||||||
|
async.apply(rimraf, path.join(__dirname, '../build/public')),
|
||||||
|
db.setupMockDefaults,
|
||||||
|
], done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should build plugin static dirs', function (done) {
|
||||||
|
build.build(['plugin static dirs'], function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert(file.existsSync(path.join(__dirname, '../build/public/plugins/nodebb-plugin-dbsearch/dbsearch')));
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should build requirejs modules', function (done) {
|
||||||
|
build.build(['requirejs modules'], function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
var filename = path.join(__dirname, '../build/public/src/modules/Chart.js');
|
||||||
|
assert(file.existsSync(filename));
|
||||||
|
assert(fs.readFileSync(filename).toString().startsWith('/*!\n * Chart.js'));
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should build client js bundle', function (done) {
|
||||||
|
build.build(['client js bundle'], function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
var filename = path.join(__dirname, '../build/public/nodebb.min.js');
|
||||||
|
assert(file.existsSync(filename));
|
||||||
|
assert(fs.readFileSync(filename).length > 1000);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should build admin js bundle', function (done) {
|
||||||
|
build.build(['admin js bundle'], function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
var filename = path.join(__dirname, '../build/public/acp.min.js');
|
||||||
|
assert(file.existsSync(filename));
|
||||||
|
assert(fs.readFileSync(filename).length > 1000);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should build client side styles', function (done) {
|
||||||
|
build.build(['client side styles'], function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
var filename = path.join(__dirname, '../build/public/stylesheet.css');
|
||||||
|
assert(file.existsSync(filename));
|
||||||
|
assert(fs.readFileSync(filename).toString().startsWith('/*! normalize.css'));
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should build admin control panel styles', function (done) {
|
||||||
|
build.build(['admin control panel styles'], function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
var filename = path.join(__dirname, '../build/public/admin.css');
|
||||||
|
assert(file.existsSync(filename));
|
||||||
|
assert(fs.readFileSync(filename).toString().startsWith('@charset "UTF-8";'));
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should build templates', function (done) {
|
||||||
|
build.build(['templates'], function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
var filename = path.join(__dirname, '../build/public/templates/admin/header.tpl');
|
||||||
|
assert(file.existsSync(filename));
|
||||||
|
assert(fs.readFileSync(filename).toString().startsWith('<!DOCTYPE html>'));
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should build languages', function (done) {
|
||||||
|
build.build(['languages'], function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
var filename = path.join(__dirname, '../build/public/language/en-GB/global.json');
|
||||||
|
assert(file.existsSync(filename));
|
||||||
|
var global = fs.readFileSync(filename).toString();
|
||||||
|
assert.strictEqual(JSON.parse(global).home, 'Home');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should build sounds', function (done) {
|
||||||
|
build.build(['sounds'], function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
var filename = path.join(__dirname, '../build/public/sounds/fileMap.json');
|
||||||
|
assert(file.existsSync(filename));
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
|
|
||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
|
var nconf = require('nconf');
|
||||||
var db = require('./mocks/databasemock');
|
var db = require('./mocks/databasemock');
|
||||||
|
|
||||||
|
|
||||||
@@ -12,14 +13,46 @@ describe('Test database', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return info about database', function (done) {
|
describe('info', function () {
|
||||||
db.info(db.client, function (err, info) {
|
it('should return info about database', function (done) {
|
||||||
assert.ifError(err);
|
db.info(db.client, function (err, info) {
|
||||||
assert(info);
|
assert.ifError(err);
|
||||||
done();
|
assert(info);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not error and return null if client is falsy', function (done) {
|
||||||
|
db.info(null, function (err, info) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.equal(info, null);
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('checkCompatibility', function () {
|
||||||
|
it('should not throw', function (done) {
|
||||||
|
db.checkCompatibility(done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return error with a too low version', function (done) {
|
||||||
|
var dbName = nconf.get('database');
|
||||||
|
if (dbName === 'redis') {
|
||||||
|
db.checkCompatibilityVersion('2.4.0', function (err) {
|
||||||
|
assert.equal(err.message, 'Your Redis version is not new enough to support NodeBB, please upgrade Redis to v2.8.9 or higher.');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
} else if (dbName === 'mongo') {
|
||||||
|
db.checkCompatibilityVersion('1.8.0', function (err) {
|
||||||
|
assert.equal(err.message, 'The `mongodb` package is out-of-date, please run `./nodebb setup` again.');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
require('./database/keys');
|
require('./database/keys');
|
||||||
require('./database/list');
|
require('./database/list');
|
||||||
require('./database/sets');
|
require('./database/sets');
|
||||||
|
|||||||
@@ -190,6 +190,15 @@ describe('Hash methods', function () {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should return undefined for all fields if object does not exist', function (done) {
|
||||||
|
db.getObjectsFields(['doesnotexist1', 'doesnotexist2'], ['name', 'age'], function (err, data) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.equal(data.name, null);
|
||||||
|
assert.equal(data.age, null);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getObjectKeys()', function () {
|
describe('getObjectKeys()', function () {
|
||||||
|
|||||||
@@ -102,6 +102,37 @@ describe('Key methods', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should delete all sorted set elements', function (done) {
|
||||||
|
async.parallel([
|
||||||
|
function (next) {
|
||||||
|
db.sortedSetAdd('deletezset', 1, 'value1', next);
|
||||||
|
},
|
||||||
|
function (next) {
|
||||||
|
db.sortedSetAdd('deletezset', 2, 'value2', next);
|
||||||
|
},
|
||||||
|
], function (err) {
|
||||||
|
if (err) {
|
||||||
|
return done(err);
|
||||||
|
}
|
||||||
|
db.delete('deletezset', function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
async.parallel({
|
||||||
|
key1exists: function (next) {
|
||||||
|
db.isSortedSetMember('deletezset', 'value1', next);
|
||||||
|
},
|
||||||
|
key2exists: function (next) {
|
||||||
|
db.isSortedSetMember('deletezset', 'value2', next);
|
||||||
|
},
|
||||||
|
}, function (err, results) {
|
||||||
|
assert.equal(err, null);
|
||||||
|
assert.equal(results.key1exists, false);
|
||||||
|
assert.equal(results.key2exists, false);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('increment', function () {
|
describe('increment', function () {
|
||||||
it('should initialize key to 1', function (done) {
|
it('should initialize key to 1', function (done) {
|
||||||
db.increment('keyToIncrement', function (err, value) {
|
db.increment('keyToIncrement', function (err, value) {
|
||||||
|
|||||||
@@ -9,11 +9,18 @@ describe('List methods', function () {
|
|||||||
describe('listAppend()', function () {
|
describe('listAppend()', function () {
|
||||||
it('should append to a list', function (done) {
|
it('should append to a list', function (done) {
|
||||||
db.listAppend('testList1', 5, function (err) {
|
db.listAppend('testList1', 5, function (err) {
|
||||||
assert.equal(err, null);
|
assert.ifError(err);
|
||||||
assert.equal(arguments.length, 1);
|
assert.equal(arguments.length, 1);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not add anyhing if key is falsy', function (done) {
|
||||||
|
db.listAppend(null, 3, function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('listPrepend()', function () {
|
describe('listPrepend()', function () {
|
||||||
@@ -38,6 +45,13 @@ describe('List methods', function () {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not add anyhing if key is falsy', function (done) {
|
||||||
|
db.listPrepend(null, 3, function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getListRange()', function () {
|
describe('getListRange()', function () {
|
||||||
@@ -83,6 +97,14 @@ describe('List methods', function () {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not get anything if key is falsy', function (done) {
|
||||||
|
db.getListRange(null, 0, -1, function (err, data) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.equal(data, undefined);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('listRemoveLast()', function () {
|
describe('listRemoveLast()', function () {
|
||||||
@@ -105,6 +127,13 @@ describe('List methods', function () {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not remove anyhing if key is falsy', function (done) {
|
||||||
|
db.listRemoveLast(null, function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('listRemoveAll()', function () {
|
describe('listRemoveAll()', function () {
|
||||||
@@ -132,6 +161,13 @@ describe('List methods', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not remove anyhing if key is falsy', function (done) {
|
||||||
|
db.listRemoveAll(null, 3, function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('listTrim()', function () {
|
describe('listTrim()', function () {
|
||||||
@@ -156,6 +192,13 @@ describe('List methods', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not add anyhing if key is falsy', function (done) {
|
||||||
|
db.listTrim(null, 0, 3, function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -303,6 +303,7 @@ describe('Sorted Set methods', function () {
|
|||||||
assert.equal(err, null);
|
assert.equal(err, null);
|
||||||
assert.equal(arguments.length, 2);
|
assert.equal(arguments.length, 2);
|
||||||
assert.equal(!!score, false);
|
assert.equal(!!score, false);
|
||||||
|
assert.strictEqual(score, null);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -312,6 +313,7 @@ describe('Sorted Set methods', function () {
|
|||||||
assert.equal(err, null);
|
assert.equal(err, null);
|
||||||
assert.equal(arguments.length, 2);
|
assert.equal(arguments.length, 2);
|
||||||
assert.equal(!!score, false);
|
assert.equal(!!score, false);
|
||||||
|
assert.strictEqual(score, null);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
120
test/feeds.js
Normal file
120
test/feeds.js
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var assert = require('assert');
|
||||||
|
var async = require('async');
|
||||||
|
var request = require('request');
|
||||||
|
var nconf = require('nconf');
|
||||||
|
|
||||||
|
var db = require('./mocks/databasemock');
|
||||||
|
var topics = require('../src/topics');
|
||||||
|
var categories = require('../src/categories');
|
||||||
|
var groups = require('../src/groups');
|
||||||
|
var user = require('../src/user');
|
||||||
|
var meta = require('../src/meta');
|
||||||
|
var privileges = require('../src/privileges');
|
||||||
|
|
||||||
|
describe('feeds', function () {
|
||||||
|
var tid;
|
||||||
|
var pid;
|
||||||
|
var fooUid;
|
||||||
|
var cid;
|
||||||
|
before(function (done) {
|
||||||
|
groups.resetCache();
|
||||||
|
meta.config['feeds:disableRSS'] = 1;
|
||||||
|
async.series({
|
||||||
|
category: function (next) {
|
||||||
|
categories.create({
|
||||||
|
name: 'Test Category',
|
||||||
|
description: 'Test category created by testing script',
|
||||||
|
}, next);
|
||||||
|
},
|
||||||
|
user: function (next) {
|
||||||
|
user.create({ username: 'foo', password: 'barbar', email: 'foo@test.com' }, next);
|
||||||
|
},
|
||||||
|
}, function (err, results) {
|
||||||
|
if (err) {
|
||||||
|
return done(err);
|
||||||
|
}
|
||||||
|
cid = results.category.cid;
|
||||||
|
fooUid = results.user;
|
||||||
|
|
||||||
|
topics.post({ uid: results.user, title: 'test topic title', content: 'test topic content', cid: results.category.cid }, function (err, result) {
|
||||||
|
tid = result.topicData.tid;
|
||||||
|
pid = result.postData.pid;
|
||||||
|
done(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should 404', function (done) {
|
||||||
|
var feedUrls = [
|
||||||
|
nconf.get('url') + '/topic/' + tid + '.rss',
|
||||||
|
nconf.get('url') + '/category/' + cid + '.rss',
|
||||||
|
nconf.get('url') + '/recent.rss',
|
||||||
|
nconf.get('url') + '/popular.rss',
|
||||||
|
nconf.get('url') + '/popular/day.rss',
|
||||||
|
nconf.get('url') + '/recentposts.rss',
|
||||||
|
nconf.get('url') + '/category/' + cid + '/recentposts.rss',
|
||||||
|
nconf.get('url') + '/user/foo/topics.rss',
|
||||||
|
nconf.get('url') + '/tags/nodebb.rss',
|
||||||
|
];
|
||||||
|
async.eachSeries(feedUrls, function (url, next) {
|
||||||
|
request(url, function (err, res) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.equal(res.statusCode, 404);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
}, function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
meta.config['feeds:disableRSS'] = 0;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should 404 if topic does not exist', function (done) {
|
||||||
|
request(nconf.get('url') + '/topic/' + 1000 + '.rss', function (err, res) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.equal(res.statusCode, 404);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should redirect if we do not have read privilege', function (done) {
|
||||||
|
privileges.categories.rescind(['topics:read'], cid, 'guests', function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
request(nconf.get('url') + '/topic/' + tid + '.rss', function (err, res, body) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.equal(res.statusCode, 200);
|
||||||
|
assert(body);
|
||||||
|
assert(body.indexOf('Login to your account') !== -1);
|
||||||
|
privileges.categories.give(['topics:read'], cid, 'guests', done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should 404 if user is not found', function (done) {
|
||||||
|
request(nconf.get('url') + '/user/doesnotexist/topics.rss', function (err, res) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.equal(res.statusCode, 404);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should redirect if we do not have read privilege', function (done) {
|
||||||
|
privileges.categories.rescind(['read'], cid, 'guests', function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
request(nconf.get('url') + '/category/' + cid + '.rss', function (err, res, body) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.equal(res.statusCode, 200);
|
||||||
|
assert(body);
|
||||||
|
assert(body.indexOf('Login to your account') !== -1);
|
||||||
|
privileges.categories.give(['read'], cid, 'guests', done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
after(function (done) {
|
||||||
|
db.emptydb(done);
|
||||||
|
});
|
||||||
|
});
|
||||||
1
test/files/1.css
Normal file
1
test/files/1.css
Normal file
@@ -0,0 +1 @@
|
|||||||
|
.help { margin: 10px; } .yellow { background: yellow; }
|
||||||
5
test/files/1.js
Normal file
5
test/files/1.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
(function (window, document) {
|
||||||
|
window.doStuff = function () {
|
||||||
|
document.body.innerHTML = 'Stuff has been done';
|
||||||
|
};
|
||||||
|
})(window, document);
|
||||||
3
test/files/2.js
Normal file
3
test/files/2.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
function foo(name, age) {
|
||||||
|
return 'The person known as "' + name + '" is ' + age + ' years old';
|
||||||
|
}
|
||||||
1
test/files/2.less
Normal file
1
test/files/2.less
Normal file
@@ -0,0 +1 @@
|
|||||||
|
.help { display: block; .blue { background: blue; } }
|
||||||
@@ -11,7 +11,7 @@ var User = require('../src/user');
|
|||||||
var Groups = require('../src/groups');
|
var Groups = require('../src/groups');
|
||||||
var Messaging = require('../src/messaging');
|
var Messaging = require('../src/messaging');
|
||||||
var helpers = require('./helpers');
|
var helpers = require('./helpers');
|
||||||
|
var socketModules = require('../src/socket.io/modules');
|
||||||
|
|
||||||
describe('Messaging Library', function () {
|
describe('Messaging Library', function () {
|
||||||
var fooUid;
|
var fooUid;
|
||||||
@@ -55,7 +55,10 @@ describe('Messaging Library', function () {
|
|||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
Messaging.canMessageUser(herpUid, bazUid, function (err) {
|
Messaging.canMessageUser(herpUid, bazUid, function (err) {
|
||||||
assert.strictEqual(err.message, '[[error:chat-restricted]]');
|
assert.strictEqual(err.message, '[[error:chat-restricted]]');
|
||||||
done();
|
socketModules.chats.addUserToRoom({ uid: herpUid }, { roomId: 1, username: 'baz' }, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:chat-restricted]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -78,23 +81,93 @@ describe('Messaging Library', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('rooms', function () {
|
describe('rooms', function () {
|
||||||
var socketModules = require('../src/socket.io/modules');
|
it('should fail to create a new chat room with invalid data', function (done) {
|
||||||
|
socketModules.chats.newRoom({ uid: fooUid }, null, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:invalid-data]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return rate limit error on second try', function (done) {
|
||||||
|
var socketMock = { uid: fooUid };
|
||||||
|
socketModules.chats.newRoom(socketMock, { touid: bazUid }, function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
socketModules.chats.newRoom(socketMock, { touid: bazUid }, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:too-many-messages]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should create a new chat room', function (done) {
|
it('should create a new chat room', function (done) {
|
||||||
socketModules.chats.newRoom({ uid: fooUid }, { touid: bazUid }, function (err, _roomId) {
|
socketModules.chats.newRoom({ uid: fooUid }, { touid: bazUid }, function (err, _roomId) {
|
||||||
roomId = _roomId;
|
roomId = _roomId;
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
assert(roomId);
|
assert(roomId);
|
||||||
done();
|
socketModules.chats.canMessage({ uid: fooUid }, _roomId, function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail to add user to room with invalid data', function (done) {
|
||||||
|
socketModules.chats.addUserToRoom({ uid: fooUid }, null, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:invalid-data]]');
|
||||||
|
socketModules.chats.addUserToRoom({ uid: fooUid }, { roomId: null }, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:invalid-data]]');
|
||||||
|
socketModules.chats.addUserToRoom({ uid: fooUid }, { roomId: roomId, username: null }, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:invalid-data]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should add a user to room', function (done) {
|
it('should add a user to room', function (done) {
|
||||||
socketModules.chats.addUserToRoom({ uid: fooUid }, { roomId: roomId, username: 'herp' }, function (err) {
|
socketModules.chats.addUserToRoom({ uid: fooUid }, { roomId: roomId, username: 'herp' }, function (err) {
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
|
Messaging.isUserInRoom(herpUid, roomId, function (err, isInRoom) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert(isInRoom);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail to add users to room if max is reached', function (done) {
|
||||||
|
meta.config.maximumUsersInChatRoom = 2;
|
||||||
|
socketModules.chats.addUserToRoom({ uid: fooUid }, { roomId: roomId, username: 'test' }, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:cant-add-more-users-to-chat-room]]');
|
||||||
|
meta.config.maximumUsersInChatRoom = 0;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should fail to add users to room if user does not exist', function (done) {
|
||||||
|
socketModules.chats.addUserToRoom({ uid: fooUid }, { roomId: roomId, username: 'doesnotexist' }, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:no-user]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail to add self to room', function (done) {
|
||||||
|
socketModules.chats.addUserToRoom({ uid: fooUid }, { roomId: roomId, username: 'foo' }, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:cant-add-self-to-chat-room]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail to leave room with invalid data', function (done) {
|
||||||
|
socketModules.chats.leave({ uid: null }, roomId, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:invalid-data]]');
|
||||||
|
socketModules.chats.leave({ uid: fooUid }, null, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:invalid-data]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should leave the chat room', function (done) {
|
it('should leave the chat room', function (done) {
|
||||||
socketModules.chats.leave({ uid: bazUid }, roomId, function (err) {
|
socketModules.chats.leave({ uid: bazUid }, roomId, function (err) {
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
@@ -106,6 +179,60 @@ describe('Messaging Library', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should fail to remove user from room', function (done) {
|
||||||
|
socketModules.chats.removeUserFromRoom({ uid: fooUid }, null, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:invalid-data]]');
|
||||||
|
socketModules.chats.removeUserFromRoom({ uid: fooUid }, {}, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:invalid-data]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail to remove user from room if user does not exist', function (done) {
|
||||||
|
socketModules.chats.removeUserFromRoom({ uid: fooUid }, { roomId: roomId, username: 'doesnotexist' }, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:no-user]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove user from room', function (done) {
|
||||||
|
socketModules.chats.newRoom({ uid: fooUid }, { touid: herpUid }, function (err, roomId) {
|
||||||
|
assert.ifError(err);
|
||||||
|
Messaging.isUserInRoom(herpUid, roomId, function (err, isInRoom) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert(isInRoom);
|
||||||
|
socketModules.chats.removeUserFromRoom({ uid: fooUid }, { roomId: roomId, username: 'herp' }, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:cant-remove-last-user]]');
|
||||||
|
socketModules.chats.addUserToRoom({ uid: fooUid }, { roomId: roomId, username: 'baz' }, function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
socketModules.chats.removeUserFromRoom({ uid: fooUid }, { roomId: roomId, username: 'herp' }, function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
Messaging.isUserInRoom(herpUid, roomId, function (err, isInRoom) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert(!isInRoom);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail to send a message to room with invalid data', function (done) {
|
||||||
|
socketModules.chats.send({ uid: fooUid }, null, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:invalid-data]]');
|
||||||
|
socketModules.chats.send({ uid: fooUid }, { roomId: null }, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:invalid-data]]');
|
||||||
|
socketModules.chats.send({ uid: null }, { roomId: 1 }, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:invalid-data]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should send a message to a room', function (done) {
|
it('should send a message to a room', function (done) {
|
||||||
socketModules.chats.send({ uid: fooUid }, { roomId: roomId, message: 'first chat message' }, function (err, messageData) {
|
socketModules.chats.send({ uid: fooUid }, { roomId: roomId, message: 'first chat message' }, function (err, messageData) {
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
@@ -116,11 +243,39 @@ describe('Messaging Library', function () {
|
|||||||
socketModules.chats.getRaw({ uid: fooUid }, { roomId: roomId, mid: messageData.mid }, function (err, raw) {
|
socketModules.chats.getRaw({ uid: fooUid }, { roomId: roomId, mid: messageData.mid }, function (err, raw) {
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
assert.equal(raw, 'first chat message');
|
assert.equal(raw, 'first chat message');
|
||||||
|
setTimeout(done, 300);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail to send second message due to rate limit', function (done) {
|
||||||
|
var socketMock = { uid: fooUid };
|
||||||
|
socketModules.chats.send(socketMock, { roomId: roomId, message: 'first chat message' }, function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
socketModules.chats.send(socketMock, { roomId: roomId, message: 'first chat message' }, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:too-many-messages]]');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should return invalid-data error', function (done) {
|
||||||
|
socketModules.chats.getRaw({ uid: fooUid }, null, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:invalid-data]]');
|
||||||
|
socketModules.chats.getRaw({ uid: fooUid }, { }, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:invalid-data]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return not in room error', function (done) {
|
||||||
|
socketModules.chats.getRaw({ uid: 0 }, { roomId: roomId, mid: 1 }, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:not-allowed]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should notify offline users of message', function (done) {
|
it('should notify offline users of message', function (done) {
|
||||||
Messaging.notificationSendDelay = 100;
|
Messaging.notificationSendDelay = 100;
|
||||||
|
|
||||||
@@ -143,6 +298,22 @@ describe('Messaging Library', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should fail to get messages from room with invalid data', function (done) {
|
||||||
|
socketModules.chats.getMessages({ uid: null }, null, function (err, messages) {
|
||||||
|
assert.equal(err.message, '[[error:invalid-data]]');
|
||||||
|
socketModules.chats.getMessages({ uid: fooUid }, null, function (err, messages) {
|
||||||
|
assert.equal(err.message, '[[error:invalid-data]]');
|
||||||
|
socketModules.chats.getMessages({ uid: fooUid }, { uid: null }, function (err, messages) {
|
||||||
|
assert.equal(err.message, '[[error:invalid-data]]');
|
||||||
|
socketModules.chats.getMessages({ uid: fooUid }, { uid: 1, roomId: null }, function (err, messages) {
|
||||||
|
assert.equal(err.message, '[[error:invalid-data]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should get messages from room', function (done) {
|
it('should get messages from room', function (done) {
|
||||||
socketModules.chats.getMessages({ uid: fooUid }, {
|
socketModules.chats.getMessages({ uid: fooUid }, {
|
||||||
uid: fooUid,
|
uid: fooUid,
|
||||||
@@ -157,6 +328,23 @@ describe('Messaging Library', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should fail to mark read with invalid data', function (done) {
|
||||||
|
socketModules.chats.markRead({ uid: null }, roomId, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:invalid-data]]');
|
||||||
|
socketModules.chats.markRead({ uid: fooUid }, null, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:invalid-data]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not error if user is not in room', function (done) {
|
||||||
|
socketModules.chats.markRead({ uid: herpUid }, 10, function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should mark room read', function (done) {
|
it('should mark room read', function (done) {
|
||||||
socketModules.chats.markRead({ uid: fooUid }, roomId, function (err) {
|
socketModules.chats.markRead({ uid: fooUid }, roomId, function (err) {
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
@@ -171,10 +359,39 @@ describe('Messaging Library', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should fail to rename room with invalid data', function (done) {
|
||||||
|
socketModules.chats.renameRoom({ uid: fooUid }, null, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:invalid-data]]');
|
||||||
|
socketModules.chats.renameRoom({ uid: fooUid }, { roomId: null }, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:invalid-data]]');
|
||||||
|
socketModules.chats.renameRoom({ uid: fooUid }, { roomId: roomId, newName: null }, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:invalid-data]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should rename room', function (done) {
|
it('should rename room', function (done) {
|
||||||
socketModules.chats.renameRoom({ uid: fooUid }, { roomId: roomId, newName: 'new room name' }, function (err) {
|
socketModules.chats.renameRoom({ uid: fooUid }, { roomId: roomId, newName: 'new room name' }, function (err) {
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail to load room with invalid-data', function (done) {
|
||||||
|
socketModules.chats.loadRoom({ uid: fooUid }, null, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:invalid-data]]');
|
||||||
|
socketModules.chats.loadRoom({ uid: fooUid }, { roomId: null }, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:invalid-data]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail to load room if user is not in', function (done) {
|
||||||
|
socketModules.chats.loadRoom({ uid: 0 }, { roomId: roomId }, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:not-allowed]]');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -198,6 +415,45 @@ describe('Messaging Library', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should fail to load recent chats with invalid data', function (done) {
|
||||||
|
socketModules.chats.getRecentChats({ uid: fooUid }, null, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:invalid-data]]');
|
||||||
|
socketModules.chats.getRecentChats({ uid: fooUid }, { after: null }, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:invalid-data]]');
|
||||||
|
socketModules.chats.getRecentChats({ uid: fooUid }, { after: 0, uid: null }, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:invalid-data]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should load recent chats of user', function (done) {
|
||||||
|
socketModules.chats.getRecentChats({ uid: fooUid }, { after: 0, uid: fooUid }, function (err, data) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert(Array.isArray(data.rooms));
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail to check if user has private chat with invalid data', function (done) {
|
||||||
|
socketModules.chats.hasPrivateChat({ uid: null }, null, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:invalid-data]]');
|
||||||
|
socketModules.chats.hasPrivateChat({ uid: fooUid }, null, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:invalid-data]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check if user has private chat with another uid', function (done) {
|
||||||
|
socketModules.chats.hasPrivateChat({ uid: fooUid }, herpUid, function (err, roomId) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert(roomId);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('edit/delete', function () {
|
describe('edit/delete', function () {
|
||||||
@@ -211,6 +467,26 @@ describe('Messaging Library', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should fail to edit message with invalid data', function (done) {
|
||||||
|
socketModules.chats.edit({ uid: fooUid }, null, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:invalid-data]]');
|
||||||
|
socketModules.chats.edit({ uid: fooUid }, { roomId: null }, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:invalid-data]]');
|
||||||
|
socketModules.chats.edit({ uid: fooUid }, { roomId: 1, message: null }, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:invalid-data]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail to edit message if not own message', function (done) {
|
||||||
|
socketModules.chats.edit({ uid: herpUid }, { mid: 5, roomId: roomId, message: 'message edited' }, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:cant-edit-chat-message]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should edit message', function (done) {
|
it('should edit message', function (done) {
|
||||||
socketModules.chats.edit({ uid: fooUid }, { mid: mid, roomId: roomId, message: 'message edited' }, function (err) {
|
socketModules.chats.edit({ uid: fooUid }, { mid: mid, roomId: roomId, message: 'message edited' }, function (err) {
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
@@ -222,6 +498,27 @@ describe('Messaging Library', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should fail to delete message with invalid data', function (done) {
|
||||||
|
socketModules.chats.delete({ uid: fooUid }, null, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:invalid-data]]');
|
||||||
|
socketModules.chats.delete({ uid: fooUid }, { roomId: null }, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:invalid-data]]');
|
||||||
|
socketModules.chats.delete({ uid: fooUid }, { roomId: 1, messageId: null }, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:invalid-data]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail to delete message if not owner', function (done) {
|
||||||
|
socketModules.chats.delete({ uid: herpUid }, { messageId: mid, roomId: roomId }, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:cant-delete-chat-message]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should delete message', function (done) {
|
it('should delete message', function (done) {
|
||||||
socketModules.chats.delete({ uid: fooUid }, { messageId: mid, roomId: roomId }, function (err) {
|
socketModules.chats.delete({ uid: fooUid }, { messageId: mid, roomId: roomId }, function (err) {
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
|
|||||||
15
test/meta.js
15
test/meta.js
@@ -262,6 +262,21 @@ describe('meta', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('sounds', function () {
|
||||||
|
var socketModules = require('../src/socket.io/modules');
|
||||||
|
|
||||||
|
it('should getUserMap', function (done) {
|
||||||
|
socketModules.sounds.getUserSoundMap({ uid: 1 }, null, function (err, data) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert(data.hasOwnProperty('chat-incoming'));
|
||||||
|
assert(data.hasOwnProperty('chat-outgoing'));
|
||||||
|
assert(data.hasOwnProperty('notification'));
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
after(function (done) {
|
after(function (done) {
|
||||||
db.emptydb(done);
|
db.emptydb(done);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,185 +5,182 @@
|
|||||||
* ATTENTION: testing db is flushed before every use!
|
* ATTENTION: testing db is flushed before every use!
|
||||||
*/
|
*/
|
||||||
|
|
||||||
(function (module) {
|
var async = require('async');
|
||||||
var async = require('async');
|
var winston = require('winston');
|
||||||
var winston = require('winston');
|
var path = require('path');
|
||||||
var path = require('path');
|
var nconf = require('nconf');
|
||||||
var nconf = require('nconf');
|
var url = require('url');
|
||||||
var url = require('url');
|
var errorText;
|
||||||
var errorText;
|
|
||||||
|
|
||||||
|
|
||||||
nconf.file({ file: path.join(__dirname, '../../config.json') });
|
nconf.file({ file: path.join(__dirname, '../../config.json') });
|
||||||
nconf.defaults({
|
nconf.defaults({
|
||||||
base_dir: path.join(__dirname, '../..'),
|
base_dir: path.join(__dirname, '../..'),
|
||||||
themes_path: path.join(__dirname, '../../node_modules'),
|
themes_path: path.join(__dirname, '../../node_modules'),
|
||||||
upload_path: 'public/uploads',
|
upload_path: 'public/uploads',
|
||||||
views_dir: path.join(__dirname, '../../build/public/templates'),
|
views_dir: path.join(__dirname, '../../build/public/templates'),
|
||||||
relative_path: '',
|
relative_path: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!nconf.get('isCluster')) {
|
if (!nconf.get('isCluster')) {
|
||||||
nconf.set('isPrimary', 'true');
|
nconf.set('isPrimary', 'true');
|
||||||
nconf.set('isCluster', 'false');
|
nconf.set('isCluster', 'false');
|
||||||
}
|
}
|
||||||
|
|
||||||
var dbType = nconf.get('database');
|
var dbType = nconf.get('database');
|
||||||
var testDbConfig = nconf.get('test_database');
|
var testDbConfig = nconf.get('test_database');
|
||||||
var productionDbConfig = nconf.get(dbType);
|
var productionDbConfig = nconf.get(dbType);
|
||||||
|
|
||||||
if (!testDbConfig) {
|
if (!testDbConfig) {
|
||||||
errorText = 'test_database is not defined';
|
errorText = 'test_database is not defined';
|
||||||
winston.info(
|
winston.info(
|
||||||
'\n===========================================================\n' +
|
'\n===========================================================\n' +
|
||||||
'Please, add parameters for test database in config.json\n' +
|
'Please, add parameters for test database in config.json\n' +
|
||||||
'For example (redis):\n' +
|
'For example (redis):\n' +
|
||||||
'"test_database": {\n' +
|
'"test_database": {\n' +
|
||||||
' "host": "127.0.0.1",\n' +
|
' "host": "127.0.0.1",\n' +
|
||||||
' "port": "6379",\n' +
|
' "port": "6379",\n' +
|
||||||
' "password": "",\n' +
|
' "password": "",\n' +
|
||||||
' "database": "1"\n' +
|
' "database": "1"\n' +
|
||||||
'}\n' +
|
'}\n' +
|
||||||
' or (mongo):\n' +
|
' or (mongo):\n' +
|
||||||
'"test_database": {\n' +
|
'"test_database": {\n' +
|
||||||
' "host": "127.0.0.1",\n' +
|
' "host": "127.0.0.1",\n' +
|
||||||
' "port": "27017",\n' +
|
' "port": "27017",\n' +
|
||||||
' "password": "",\n' +
|
' "password": "",\n' +
|
||||||
' "database": "1\n' +
|
' "database": "1\n' +
|
||||||
'}\n' +
|
'}\n' +
|
||||||
' or (mongo) in a replicaset\n' +
|
' or (mongo) in a replicaset\n' +
|
||||||
'"test_database": {\n' +
|
'"test_database": {\n' +
|
||||||
' "host": "127.0.0.1,127.0.0.1,127.0.0.1",\n' +
|
' "host": "127.0.0.1,127.0.0.1,127.0.0.1",\n' +
|
||||||
' "port": "27017,27018,27019",\n' +
|
' "port": "27017,27018,27019",\n' +
|
||||||
' "username": "",\n' +
|
' "username": "",\n' +
|
||||||
' "password": "",\n' +
|
' "password": "",\n' +
|
||||||
' "database": "nodebb_test"\n' +
|
' "database": "nodebb_test"\n' +
|
||||||
'}\n' +
|
'}\n' +
|
||||||
'==========================================================='
|
'==========================================================='
|
||||||
);
|
);
|
||||||
winston.error(errorText);
|
winston.error(errorText);
|
||||||
throw new Error(errorText);
|
throw new Error(errorText);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (testDbConfig.database === productionDbConfig.database &&
|
if (testDbConfig.database === productionDbConfig.database &&
|
||||||
testDbConfig.host === productionDbConfig.host &&
|
testDbConfig.host === productionDbConfig.host &&
|
||||||
testDbConfig.port === productionDbConfig.port) {
|
testDbConfig.port === productionDbConfig.port) {
|
||||||
errorText = 'test_database has the same config as production db';
|
errorText = 'test_database has the same config as production db';
|
||||||
winston.error(errorText);
|
winston.error(errorText);
|
||||||
throw new Error(errorText);
|
throw new Error(errorText);
|
||||||
}
|
}
|
||||||
|
|
||||||
nconf.set(dbType, testDbConfig);
|
nconf.set(dbType, testDbConfig);
|
||||||
|
|
||||||
winston.info('database config');
|
winston.info('database config');
|
||||||
winston.info(dbType);
|
winston.info(dbType);
|
||||||
winston.info(testDbConfig);
|
winston.info(testDbConfig);
|
||||||
|
|
||||||
var db = require('../../src/database');
|
var db = require('../../src/database');
|
||||||
|
module.exports = db;
|
||||||
|
|
||||||
before(function (done) {
|
before(function (done) {
|
||||||
this.timeout(30000);
|
this.timeout(30000);
|
||||||
async.series([
|
async.series([
|
||||||
function (next) {
|
function (next) {
|
||||||
db.init(next);
|
db.init(next);
|
||||||
},
|
},
|
||||||
function (next) {
|
function (next) {
|
||||||
setupMockDefaults(next);
|
setupMockDefaults(next);
|
||||||
},
|
},
|
||||||
function (next) {
|
function (next) {
|
||||||
db.initSessionStore(next);
|
db.initSessionStore(next);
|
||||||
},
|
},
|
||||||
function (next) {
|
function (next) {
|
||||||
var meta = require('../../src/meta');
|
var meta = require('../../src/meta');
|
||||||
|
|
||||||
// nconf defaults, if not set in config
|
// nconf defaults, if not set in config
|
||||||
if (!nconf.get('sessionKey')) {
|
if (!nconf.get('sessionKey')) {
|
||||||
nconf.set('sessionKey', 'express.sid');
|
nconf.set('sessionKey', 'express.sid');
|
||||||
}
|
}
|
||||||
// Parse out the relative_url and other goodies from the configured URL
|
// Parse out the relative_url and other goodies from the configured URL
|
||||||
var urlObject = url.parse(nconf.get('url'));
|
var urlObject = url.parse(nconf.get('url'));
|
||||||
var relativePath = urlObject.pathname !== '/' ? urlObject.pathname : '';
|
var relativePath = urlObject.pathname !== '/' ? urlObject.pathname : '';
|
||||||
nconf.set('base_url', urlObject.protocol + '//' + urlObject.host);
|
nconf.set('base_url', urlObject.protocol + '//' + urlObject.host);
|
||||||
nconf.set('secure', urlObject.protocol === 'https:');
|
nconf.set('secure', urlObject.protocol === 'https:');
|
||||||
nconf.set('use_port', !!urlObject.port);
|
nconf.set('use_port', !!urlObject.port);
|
||||||
nconf.set('relative_path', relativePath);
|
nconf.set('relative_path', relativePath);
|
||||||
nconf.set('port', urlObject.port || nconf.get('port') || nconf.get('PORT') || (nconf.get('PORT_ENV_VAR') ? nconf.get(nconf.get('PORT_ENV_VAR')) : false) || 4567);
|
nconf.set('port', urlObject.port || nconf.get('port') || nconf.get('PORT') || (nconf.get('PORT_ENV_VAR') ? nconf.get(nconf.get('PORT_ENV_VAR')) : false) || 4567);
|
||||||
nconf.set('upload_path', path.join(nconf.get('base_dir'), nconf.get('upload_path')));
|
nconf.set('upload_path', path.join(nconf.get('base_dir'), nconf.get('upload_path')));
|
||||||
|
|
||||||
nconf.set('core_templates_path', path.join(__dirname, '../../src/views'));
|
nconf.set('core_templates_path', path.join(__dirname, '../../src/views'));
|
||||||
nconf.set('base_templates_path', path.join(nconf.get('themes_path'), 'nodebb-theme-persona/templates'));
|
nconf.set('base_templates_path', path.join(nconf.get('themes_path'), 'nodebb-theme-persona/templates'));
|
||||||
nconf.set('theme_templates_path', meta.config['theme:templates'] ? path.join(nconf.get('themes_path'), meta.config['theme:id'], meta.config['theme:templates']) : nconf.get('base_templates_path'));
|
nconf.set('theme_templates_path', meta.config['theme:templates'] ? path.join(nconf.get('themes_path'), meta.config['theme:id'], meta.config['theme:templates']) : nconf.get('base_templates_path'));
|
||||||
nconf.set('theme_config', path.join(nconf.get('themes_path'), 'nodebb-theme-persona', 'theme.json'));
|
nconf.set('theme_config', path.join(nconf.get('themes_path'), 'nodebb-theme-persona', 'theme.json'));
|
||||||
nconf.set('bcrypt_rounds', 1);
|
nconf.set('bcrypt_rounds', 1);
|
||||||
|
|
||||||
next();
|
next();
|
||||||
},
|
},
|
||||||
function (next) {
|
function (next) {
|
||||||
var webserver = require('../../src/webserver');
|
var webserver = require('../../src/webserver');
|
||||||
var sockets = require('../../src/socket.io');
|
var sockets = require('../../src/socket.io');
|
||||||
sockets.init(webserver.server);
|
sockets.init(webserver.server);
|
||||||
|
|
||||||
require('../../src/notifications').startJobs();
|
require('../../src/notifications').startJobs();
|
||||||
require('../../src/user').startJobs();
|
require('../../src/user').startJobs();
|
||||||
|
|
||||||
webserver.listen(next);
|
webserver.listen(next);
|
||||||
},
|
},
|
||||||
], done);
|
], done);
|
||||||
});
|
});
|
||||||
|
|
||||||
function setupMockDefaults(callback) {
|
function setupMockDefaults(callback) {
|
||||||
var meta = require('../../src/meta');
|
var meta = require('../../src/meta');
|
||||||
|
|
||||||
async.series([
|
async.series([
|
||||||
function (next) {
|
function (next) {
|
||||||
db.emptydb(next);
|
db.emptydb(next);
|
||||||
},
|
},
|
||||||
function (next) {
|
function (next) {
|
||||||
winston.info('test_database flushed');
|
winston.info('test_database flushed');
|
||||||
setupDefaultConfigs(meta, next);
|
setupDefaultConfigs(meta, next);
|
||||||
},
|
},
|
||||||
function (next) {
|
function (next) {
|
||||||
meta.configs.init(next);
|
meta.configs.init(next);
|
||||||
},
|
},
|
||||||
function (next) {
|
function (next) {
|
||||||
meta.dependencies.check(next);
|
meta.dependencies.check(next);
|
||||||
},
|
},
|
||||||
function (next) {
|
function (next) {
|
||||||
meta.config.postDelay = 0;
|
meta.config.postDelay = 0;
|
||||||
meta.config.initialPostDelay = 0;
|
meta.config.initialPostDelay = 0;
|
||||||
meta.config.newbiePostDelay = 0;
|
meta.config.newbiePostDelay = 0;
|
||||||
|
|
||||||
enableDefaultPlugins(next);
|
enableDefaultPlugins(next);
|
||||||
},
|
},
|
||||||
function (next) {
|
function (next) {
|
||||||
meta.themes.set({
|
meta.themes.set({
|
||||||
type: 'local',
|
type: 'local',
|
||||||
id: 'nodebb-theme-persona',
|
id: 'nodebb-theme-persona',
|
||||||
}, next);
|
}, next);
|
||||||
},
|
},
|
||||||
], callback);
|
], callback);
|
||||||
}
|
}
|
||||||
db.setupMockDefaults = setupMockDefaults;
|
db.setupMockDefaults = setupMockDefaults;
|
||||||
|
|
||||||
function setupDefaultConfigs(meta, next) {
|
function setupDefaultConfigs(meta, next) {
|
||||||
winston.info('Populating database with default configs, if not already set...\n');
|
winston.info('Populating database with default configs, if not already set...\n');
|
||||||
|
|
||||||
var defaults = require(path.join(nconf.get('base_dir'), 'install/data/defaults.json'));
|
var defaults = require(path.join(nconf.get('base_dir'), 'install/data/defaults.json'));
|
||||||
|
|
||||||
meta.configs.setOnEmpty(defaults, next);
|
meta.configs.setOnEmpty(defaults, next);
|
||||||
}
|
}
|
||||||
|
|
||||||
function enableDefaultPlugins(callback) {
|
function enableDefaultPlugins(callback) {
|
||||||
winston.info('Enabling default plugins\n');
|
winston.info('Enabling default plugins\n');
|
||||||
|
|
||||||
var defaultEnabled = [
|
var defaultEnabled = [
|
||||||
'nodebb-plugin-dbsearch',
|
'nodebb-plugin-dbsearch',
|
||||||
];
|
];
|
||||||
|
|
||||||
winston.info('[install/enableDefaultPlugins] activating default plugins', defaultEnabled);
|
winston.info('[install/enableDefaultPlugins] activating default plugins', defaultEnabled);
|
||||||
|
|
||||||
db.sortedSetAdd('plugins:active', [0], defaultEnabled, callback);
|
db.sortedSetAdd('plugins:active', [0], defaultEnabled, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = db;
|
|
||||||
}(module));
|
|
||||||
|
|||||||
@@ -5,7 +5,11 @@ var assert = require('assert');
|
|||||||
var async = require('async');
|
var async = require('async');
|
||||||
|
|
||||||
var db = require('./mocks/databasemock');
|
var db = require('./mocks/databasemock');
|
||||||
|
var meta = require('../src/meta');
|
||||||
var user = require('../src/user');
|
var user = require('../src/user');
|
||||||
|
var topics = require('../src/topics');
|
||||||
|
var categories = require('../src/categories');
|
||||||
|
var groups = require('../src/groups');
|
||||||
var notifications = require('../src/notifications');
|
var notifications = require('../src/notifications');
|
||||||
var socketNotifications = require('../src/socket.io/notifications');
|
var socketNotifications = require('../src/socket.io/notifications');
|
||||||
|
|
||||||
@@ -14,6 +18,7 @@ describe('Notifications', function () {
|
|||||||
var notification;
|
var notification;
|
||||||
|
|
||||||
before(function (done) {
|
before(function (done) {
|
||||||
|
groups.resetCache();
|
||||||
user.create({ username: 'poster' }, function (err, _uid) {
|
user.create({ username: 'poster' }, function (err, _uid) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return done(err);
|
return done(err);
|
||||||
@@ -24,11 +29,19 @@ describe('Notifications', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should fail to create notification without a nid', function (done) {
|
||||||
|
notifications.create({}, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:no-notification-id]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should create a notification', function (done) {
|
it('should create a notification', function (done) {
|
||||||
notifications.create({
|
notifications.create({
|
||||||
bodyShort: 'bodyShort',
|
bodyShort: 'bodyShort',
|
||||||
nid: 'notification_id',
|
nid: 'notification_id',
|
||||||
path: '/notification/path',
|
path: '/notification/path',
|
||||||
|
pid: 1,
|
||||||
}, function (err, _notification) {
|
}, function (err, _notification) {
|
||||||
notification = _notification;
|
notification = _notification;
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
@@ -45,6 +58,29 @@ describe('Notifications', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should return null if pid is same and importance is lower', function (done) {
|
||||||
|
notifications.create({
|
||||||
|
bodyShort: 'bodyShort',
|
||||||
|
nid: 'notification_id',
|
||||||
|
path: '/notification/path',
|
||||||
|
pid: 1,
|
||||||
|
importance: 1,
|
||||||
|
}, function (err, notification) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.strictEqual(notification, null);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get empty array', function (done) {
|
||||||
|
notifications.getMultiple(null, function (err, data) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert(Array.isArray(data));
|
||||||
|
assert.equal(data.length, 0);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should get notifications', function (done) {
|
it('should get notifications', function (done) {
|
||||||
notifications.getMultiple([notification.nid], function (err, notificationsData) {
|
notifications.getMultiple([notification.nid], function (err, notificationsData) {
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
@@ -55,6 +91,19 @@ describe('Notifications', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should do nothing', function (done) {
|
||||||
|
notifications.push(null, [], function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
notifications.push({ nid: null }, [], function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
notifications.push(notification, [], function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should push a notification to uid', function (done) {
|
it('should push a notification to uid', function (done) {
|
||||||
notifications.push(notification, [uid], function (err) {
|
notifications.push(notification, [uid], function (err) {
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
@@ -94,6 +143,16 @@ describe('Notifications', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not mark anything with invalid uid or nid', function (done) {
|
||||||
|
socketNotifications.markRead({ uid: null }, null, function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
socketNotifications.markRead({ uid: uid }, null, function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should mark a notification read', function (done) {
|
it('should mark a notification read', function (done) {
|
||||||
socketNotifications.markRead({ uid: uid }, notification.nid, function (err) {
|
socketNotifications.markRead({ uid: uid }, notification.nid, function (err) {
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
@@ -109,6 +168,23 @@ describe('Notifications', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not mark anything with invalid uid or nid', function (done) {
|
||||||
|
socketNotifications.markUnread({ uid: null }, null, function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
socketNotifications.markUnread({ uid: uid }, null, function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should error if notification does not exist', function (done) {
|
||||||
|
socketNotifications.markUnread({ uid: uid }, 123123, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:no-notification]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should mark a notification unread', function (done) {
|
it('should mark a notification unread', function (done) {
|
||||||
socketNotifications.markUnread({ uid: uid }, notification.nid, function (err) {
|
socketNotifications.markUnread({ uid: uid }, notification.nid, function (err) {
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
@@ -143,6 +219,13 @@ describe('Notifications', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not do anything', function (done) {
|
||||||
|
socketNotifications.markAllRead({ uid: 1000 }, null, function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should link to the first unread post in a watched topic', function (done) {
|
it('should link to the first unread post in a watched topic', function (done) {
|
||||||
var categories = require('../src/categories');
|
var categories = require('../src/categories');
|
||||||
var topics = require('../src/topics');
|
var topics = require('../src/topics');
|
||||||
@@ -251,14 +334,145 @@ describe('Notifications', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should return empty with falsy uid', function (done) {
|
||||||
|
user.notifications.get(0, function (err, data) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.equal(data.read.length, 0);
|
||||||
|
assert.equal(data.unread.length, 0);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get all notifications and filter', function (done) {
|
||||||
|
var nid = 'willbefiltered';
|
||||||
|
notifications.create({
|
||||||
|
bodyShort: 'bodyShort',
|
||||||
|
nid: nid,
|
||||||
|
path: '/notification/path',
|
||||||
|
type: 'post',
|
||||||
|
}, function (err, notification) {
|
||||||
|
assert.ifError(err);
|
||||||
|
notifications.push(notification, [uid], function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
setTimeout(function () {
|
||||||
|
user.notifications.getAll(uid, 'post', function (err, nids) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.notEqual(nids.indexOf(nid), -1);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
}, 1500);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not get anything if notifications does not exist', function (done) {
|
||||||
|
user.notifications.getNotifications(['doesnotexistnid1', 'doesnotexistnid2'], uid, function (err, data) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.deepEqual(data, []);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get daily notifications', function (done) {
|
||||||
|
user.notifications.getDailyUnread(uid, function (err, data) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.equal(data[0].nid, 'willbefiltered');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return 0 for falsy uid', function (done) {
|
||||||
|
user.notifications.getUnreadCount(0, function (err, count) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.equal(count, 0);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not do anything if uid is falsy', function (done) {
|
||||||
|
user.notifications.deleteAll(0, function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should send notification to followers of user when he posts', function (done) {
|
||||||
|
var followerUid;
|
||||||
|
async.waterfall([
|
||||||
|
function (next) {
|
||||||
|
user.create({ username: 'follower' }, next);
|
||||||
|
},
|
||||||
|
function (_followerUid, next) {
|
||||||
|
followerUid = _followerUid;
|
||||||
|
user.follow(followerUid, uid, next);
|
||||||
|
},
|
||||||
|
function (next) {
|
||||||
|
categories.create({
|
||||||
|
name: 'Test Category',
|
||||||
|
description: 'Test category created by testing script',
|
||||||
|
}, next);
|
||||||
|
},
|
||||||
|
function (category, next) {
|
||||||
|
topics.post({
|
||||||
|
uid: uid,
|
||||||
|
cid: category.cid,
|
||||||
|
title: 'Test Topic Title',
|
||||||
|
content: 'The content of test topic',
|
||||||
|
}, next);
|
||||||
|
},
|
||||||
|
function (data, next) {
|
||||||
|
setTimeout(next, 1100);
|
||||||
|
},
|
||||||
|
function (next) {
|
||||||
|
user.notifications.getAll(followerUid, '', next);
|
||||||
|
},
|
||||||
|
], function (err, data) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert(data);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should send welcome notification', function (done) {
|
||||||
|
meta.config.welcomeNotification = 'welcome to the forums';
|
||||||
|
user.notifications.sendWelcomeNotification(uid, function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
user.notifications.sendWelcomeNotification(uid, function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
setTimeout(function () {
|
||||||
|
user.notifications.getAll(uid, '', function (err, data) {
|
||||||
|
meta.config.welcomeNotification = '';
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.notEqual(data.indexOf('welcome_' + uid), -1);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
}, 1100);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should prune notifications', function (done) {
|
it('should prune notifications', function (done) {
|
||||||
notifications.create({
|
notifications.create({
|
||||||
bodyShort: 'bodyShort',
|
bodyShort: 'bodyShort',
|
||||||
nid: 'tobedeleted',
|
nid: 'tobedeleted',
|
||||||
path: '/notification/path',
|
path: '/notification/path',
|
||||||
}, function (err) {
|
}, function (err, notification) {
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
notifications.prune(done);
|
notifications.prune(function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
var week = 604800000;
|
||||||
|
db.sortedSetAdd('notifications', Date.now() - (2 * week), notification.nid, function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
notifications.prune(function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
notifications.get(notification.nid, function (err, data) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert(!data);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -760,13 +760,13 @@ describe('Topic\'s', function () {
|
|||||||
|
|
||||||
it('should 401 if not allowed to read as guest', function (done) {
|
it('should 401 if not allowed to read as guest', function (done) {
|
||||||
var privileges = require('../src/privileges');
|
var privileges = require('../src/privileges');
|
||||||
privileges.categories.rescind(['read'], topicData.cid, 'guests', function (err) {
|
privileges.categories.rescind(['topics:read'], topicData.cid, 'guests', function (err) {
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
request(nconf.get('url') + '/api/topic/' + topicData.slug, function (err, response, body) {
|
request(nconf.get('url') + '/api/topic/' + topicData.slug, function (err, response, body) {
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
assert.equal(response.statusCode, 401);
|
assert.equal(response.statusCode, 401);
|
||||||
assert(body);
|
assert(body);
|
||||||
privileges.categories.give(['read'], topicData.cid, 'guests', done);
|
privileges.categories.give(['topics:read'], topicData.cid, 'guests', done);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -1551,7 +1551,7 @@ describe('Topic\'s', function () {
|
|||||||
|
|
||||||
|
|
||||||
it('should return empty array if first param is empty', function (done) {
|
it('should return empty array if first param is empty', function (done) {
|
||||||
topics.getTeasers([], function (err, teasers) {
|
topics.getTeasers([], 1, function (err, teasers) {
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
assert.equal(0, teasers.length);
|
assert.equal(0, teasers.length);
|
||||||
done();
|
done();
|
||||||
@@ -1559,7 +1559,7 @@ describe('Topic\'s', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should get teasers with 2 params', function (done) {
|
it('should get teasers with 2 params', function (done) {
|
||||||
topics.getTeasers([topic1.topicData, topic2.topicData], function (err, teasers) {
|
topics.getTeasers([topic1.topicData, topic2.topicData], 1, function (err, teasers) {
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
assert.deepEqual([undefined, undefined], teasers);
|
assert.deepEqual([undefined, undefined], teasers);
|
||||||
done();
|
done();
|
||||||
@@ -1568,7 +1568,7 @@ describe('Topic\'s', function () {
|
|||||||
|
|
||||||
it('should get teasers with first posts', function (done) {
|
it('should get teasers with first posts', function (done) {
|
||||||
meta.config.teaserPost = 'first';
|
meta.config.teaserPost = 'first';
|
||||||
topics.getTeasers([topic1.topicData, topic2.topicData], function (err, teasers) {
|
topics.getTeasers([topic1.topicData, topic2.topicData], 1, function (err, teasers) {
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
assert.equal(2, teasers.length);
|
assert.equal(2, teasers.length);
|
||||||
assert(teasers[0]);
|
assert(teasers[0]);
|
||||||
@@ -1581,7 +1581,7 @@ describe('Topic\'s', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should get teasers even if one topic is falsy', function (done) {
|
it('should get teasers even if one topic is falsy', function (done) {
|
||||||
topics.getTeasers([null, topic2.topicData], function (err, teasers) {
|
topics.getTeasers([null, topic2.topicData], 1, function (err, teasers) {
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
assert.equal(2, teasers.length);
|
assert.equal(2, teasers.length);
|
||||||
assert.equal(undefined, teasers[0]);
|
assert.equal(undefined, teasers[0]);
|
||||||
@@ -1598,7 +1598,7 @@ describe('Topic\'s', function () {
|
|||||||
topics.reply({ uid: adminUid, content: 'reply 1 content', tid: topic1.topicData.tid }, function (err, result) {
|
topics.reply({ uid: adminUid, content: 'reply 1 content', tid: topic1.topicData.tid }, function (err, result) {
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
topic1.topicData.teaserPid = result.pid;
|
topic1.topicData.teaserPid = result.pid;
|
||||||
topics.getTeasers([topic1.topicData, topic2.topicData], function (err, teasers) {
|
topics.getTeasers([topic1.topicData, topic2.topicData], 1, function (err, teasers) {
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
assert(teasers[0]);
|
assert(teasers[0]);
|
||||||
assert(teasers[1]);
|
assert(teasers[1]);
|
||||||
@@ -1610,7 +1610,7 @@ describe('Topic\'s', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should get teasers by tids', function (done) {
|
it('should get teasers by tids', function (done) {
|
||||||
topics.getTeasersByTids([topic2.topicData.tid, topic1.topicData.tid], function (err, teasers) {
|
topics.getTeasersByTids([topic2.topicData.tid, topic1.topicData.tid], 1, function (err, teasers) {
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
assert(2, teasers.length);
|
assert(2, teasers.length);
|
||||||
assert.equal(teasers[1].content, 'reply 1 content');
|
assert.equal(teasers[1].content, 'reply 1 content');
|
||||||
@@ -1619,7 +1619,7 @@ describe('Topic\'s', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return empty array ', function (done) {
|
it('should return empty array ', function (done) {
|
||||||
topics.getTeasersByTids([], function (err, teasers) {
|
topics.getTeasersByTids([], 1, function (err, teasers) {
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
assert.equal(0, teasers.length);
|
assert.equal(0, teasers.length);
|
||||||
done();
|
done();
|
||||||
@@ -1627,7 +1627,7 @@ describe('Topic\'s', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should get teaser by tid', function (done) {
|
it('should get teaser by tid', function (done) {
|
||||||
topics.getTeaser(topic2.topicData.tid, function (err, teaser) {
|
topics.getTeaser(topic2.topicData.tid, 1, function (err, teaser) {
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
assert(teaser);
|
assert(teaser);
|
||||||
assert.equal(teaser.content, 'content 2');
|
assert.equal(teaser.content, 'content 2');
|
||||||
|
|||||||
70
test/user.js
70
test/user.js
@@ -237,6 +237,76 @@ describe('User', function () {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should search users by ip', function (done) {
|
||||||
|
User.create({ username: 'ipsearch' }, function (err, uid) {
|
||||||
|
assert.ifError(err);
|
||||||
|
db.sortedSetAdd('ip:1.1.1.1:uid', [1, 1], [testUid, uid], function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
socketUser.search({ uid: testUid }, { query: '1.1.1.1', searchBy: 'ip' }, function (err, data) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert(Array.isArray(data.users));
|
||||||
|
assert.equal(data.users.length, 2);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return empty array if query is empty', function (done) {
|
||||||
|
socketUser.search({ uid: testUid }, { query: '' }, function (err, data) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.equal(data.users.length, 0);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should filter users', function (done) {
|
||||||
|
User.create({ username: 'ipsearch_filter' }, function (err, uid) {
|
||||||
|
assert.ifError(err);
|
||||||
|
User.setUserFields(uid, { banned: 1, flags: 10 }, function (err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
socketUser.search({ uid: testUid }, {
|
||||||
|
query: 'ipsearch',
|
||||||
|
onlineOnly: true,
|
||||||
|
bannedOnly: true,
|
||||||
|
flaggedOnly: true,
|
||||||
|
}, function (err, data) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.equal(data.users[0].username, 'ipsearch_filter');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should sort results by username', function (done) {
|
||||||
|
async.waterfall([
|
||||||
|
function (next) {
|
||||||
|
User.create({ username: 'brian' }, next);
|
||||||
|
},
|
||||||
|
function (uid, next) {
|
||||||
|
User.create({ username: 'baris' }, next);
|
||||||
|
},
|
||||||
|
function (uid, next) {
|
||||||
|
User.create({ username: 'bzari' }, next);
|
||||||
|
},
|
||||||
|
function (uid, next) {
|
||||||
|
User.search({
|
||||||
|
uid: testUid,
|
||||||
|
query: 'b',
|
||||||
|
sortBy: 'username',
|
||||||
|
paginate: false,
|
||||||
|
}, next);
|
||||||
|
},
|
||||||
|
], function (err, data) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.equal(data.users[0].username, 'baris');
|
||||||
|
assert.equal(data.users[1].username, 'brian');
|
||||||
|
assert.equal(data.users[2].username, 'bzari');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.delete()', function () {
|
describe('.delete()', function () {
|
||||||
|
|||||||
Reference in New Issue
Block a user