mirror of
https://github.com/NodeBB/NodeBB.git
synced 2026-01-05 23:30:36 +01:00
Merge remote-tracking branch 'origin/master'
# Conflicts: # package.json
This commit is contained in:
@@ -32,6 +32,9 @@
|
||||
"no-prototype-builtins": "off",
|
||||
"new-cap": "off",
|
||||
"no-plusplus": ["error", { "allowForLoopAfterthoughts": true }],
|
||||
"object-curly-newline": "off",
|
||||
"no-restricted-globals": "off",
|
||||
"function-paren-newline": "off",
|
||||
"import/no-unresolved": "error",
|
||||
|
||||
// ES6
|
||||
@@ -42,6 +45,7 @@
|
||||
"no-var": "off",
|
||||
"object-shorthand": "off",
|
||||
"vars-on-top": "off",
|
||||
"prefer-destructuring": "off",
|
||||
|
||||
// TODO
|
||||
"import/no-extraneous-dependencies": "off",
|
||||
|
||||
70
package.json
70
package.json
@@ -19,27 +19,27 @@
|
||||
"dependencies": {
|
||||
"ace-builds": "^1.2.8",
|
||||
"async": "2.5.0",
|
||||
"autoprefixer": "7.1.3",
|
||||
"autoprefixer": "7.1.4",
|
||||
"bcryptjs": "2.4.3",
|
||||
"benchpressjs": "^1.0.3",
|
||||
"body-parser": "^1.17.2",
|
||||
"benchpressjs": "^1.1.0",
|
||||
"body-parser": "^1.18.2",
|
||||
"bootstrap": "^3.3.7",
|
||||
"chart.js": "^2.6.0",
|
||||
"colors": "^1.1.2",
|
||||
"compression": "^1.7.0",
|
||||
"compression": "^1.7.1",
|
||||
"connect-ensure-login": "^0.1.1",
|
||||
"connect-flash": "^0.1.1",
|
||||
"connect-mongo": "1.3.2",
|
||||
"connect-multiparty": "^2.0.0",
|
||||
"connect-redis": "3.3.0",
|
||||
"connect-redis": "3.3.2",
|
||||
"cookie-parser": "^1.4.3",
|
||||
"cron": "^1.2.1",
|
||||
"cropperjs": "^0.8.1",
|
||||
"cropperjs": "^1.0.0",
|
||||
"csurf": "^1.9.0",
|
||||
"daemon": "^1.1.0",
|
||||
"express": "^4.15.4",
|
||||
"express-session": "^1.15.5",
|
||||
"express-useragent": "1.0.7",
|
||||
"express": "^4.16.1",
|
||||
"express-session": "^1.15.6",
|
||||
"express-useragent": "1.0.8",
|
||||
"html-to-text": "3.3.0",
|
||||
"ipaddr.js": "^1.5.2",
|
||||
"jimp": "0.2.28",
|
||||
@@ -49,14 +49,14 @@
|
||||
"lodash": "^4.17.4",
|
||||
"logrotate-stream": "^0.2.5",
|
||||
"lru-cache": "4.1.1",
|
||||
"mime": "^1.4.0",
|
||||
"mime": "^2.0.3",
|
||||
"minimist": "^1.2.0",
|
||||
"mkdirp": "^0.5.1",
|
||||
"mongodb": "2.2.31",
|
||||
"morgan": "^1.8.2",
|
||||
"morgan": "^1.9.0",
|
||||
"mousetrap": "^1.6.1",
|
||||
"nconf": "^0.8.4",
|
||||
"nodebb-plugin-composer-default": "6.0.0",
|
||||
"nconf": "^0.8.5",
|
||||
"nodebb-plugin-composer-default": "6.0.1",
|
||||
"nodebb-plugin-dbsearch": "2.0.6",
|
||||
"nodebb-plugin-emoji-extended": "1.1.1",
|
||||
"nodebb-plugin-emoji-one": "1.2.1",
|
||||
@@ -65,25 +65,25 @@
|
||||
"nodebb-plugin-soundpack-default": "1.0.0",
|
||||
"nodebb-plugin-spam-be-gone": "0.5.1",
|
||||
"nodebb-rewards-essentials": "0.0.9",
|
||||
"nodebb-theme-lavender": "4.0.5",
|
||||
"nodebb-theme-persona": "6.0.1",
|
||||
"nodebb-theme-lavender": "4.1.0",
|
||||
"nodebb-theme-persona": "6.1.0",
|
||||
"nodebb-theme-slick": "1.1.1",
|
||||
"nodebb-theme-vanilla": "7.0.1",
|
||||
"nodebb-theme-vanilla": "7.1.0",
|
||||
"nodebb-widget-essentials": "3.0.6",
|
||||
"nodemailer": "4.1.0",
|
||||
"passport": "^0.3.0",
|
||||
"nodemailer": "4.1.1",
|
||||
"passport": "^0.4.0",
|
||||
"passport-local": "1.0.0",
|
||||
"postcss": "6.0.10",
|
||||
"postcss-clean": "1.0.3",
|
||||
"postcss": "6.0.12",
|
||||
"postcss-clean": "1.1.0",
|
||||
"promise-polyfill": "^6.0.2",
|
||||
"prompt": "^1.0.0",
|
||||
"redis": "2.8.0",
|
||||
"request": "2.81.0",
|
||||
"rimraf": "2.6.1",
|
||||
"request": "2.83.0",
|
||||
"rimraf": "2.6.2",
|
||||
"rss": "^1.2.2",
|
||||
"sanitize-html": "^1.14.0",
|
||||
"sanitize-html": "^1.14.1",
|
||||
"semver": "^5.4.1",
|
||||
"serve-favicon": "^2.4.3",
|
||||
"serve-favicon": "^2.4.5",
|
||||
"sitemap": "^1.13.0",
|
||||
"socket.io": "2.0.3",
|
||||
"socket.io-client": "2.0.3",
|
||||
@@ -92,24 +92,24 @@
|
||||
"spdx-license-list": "^3.0.1",
|
||||
"string": "^3.3.3",
|
||||
"toobusy-js": "^0.5.1",
|
||||
"uglify-js": "^3.0.28",
|
||||
"validator": "8.1.0",
|
||||
"uglify-js": "^3.1.3",
|
||||
"validator": "9.0.0",
|
||||
"winston": "^2.3.1",
|
||||
"xml": "^1.0.1",
|
||||
"xregexp": "3.2.0",
|
||||
"zxcvbn": "^4.4.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"coveralls": "^2.11.14",
|
||||
"eslint": "^3.12.0",
|
||||
"eslint-config-airbnb-base": "^11.1.0",
|
||||
"eslint-plugin-import": "^2.2.0",
|
||||
"coveralls": "^3.0.0",
|
||||
"eslint": "^4.8.0",
|
||||
"eslint-config-airbnb-base": "^12.0.1",
|
||||
"eslint-plugin-import": "^2.7.0",
|
||||
"grunt": "^1.0.1",
|
||||
"grunt-contrib-watch": "^1.0.0",
|
||||
"istanbul": "^0.4.2",
|
||||
"jsdom": "^11.2.0",
|
||||
"mocha": "^3.5.0",
|
||||
"mocha-lcov-reporter": "^1.2.0",
|
||||
"istanbul": "^0.4.5",
|
||||
"jsdom": "^11.3.0",
|
||||
"mocha": "^3.5.3",
|
||||
"mocha-lcov-reporter": "^1.3.0",
|
||||
"xmlhttprequest": "1.8.0",
|
||||
"xmlhttprequest-ssl": "1.5.3"
|
||||
},
|
||||
@@ -136,4 +136,4 @@
|
||||
"url": "https://github.com/barisusakli"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,10 @@
|
||||
"smtp-transport.gmail-warning2": "For more information about this workaround, <a href=\"https://nodemailer.com/usage/using-gmail/\">please consult this NodeMailer article on the issue.</a> An alternative would be to utilise a third-party emailer plugin such as SendGrid, Mailgun, etc. <a href=\"{config.relative_path}/admin/extend/plugins\">Browse available plugins here</a>.",
|
||||
"smtp-transport.host": "SMTP Host",
|
||||
"smtp-transport.port": "SMTP Port",
|
||||
"smtp-transport.security": "Connection security",
|
||||
"smtp-transport.security-encrypted": "Encrypted",
|
||||
"smtp-transport.security-starttls": "StartTLS",
|
||||
"smtp-transport.security-none": "None",
|
||||
"smtp-transport.username": "Username",
|
||||
"smtp-transport.username-help": "<b>For the Gmail service,</b> enter the full email address here, especially if you are using a Google Apps managed domain.",
|
||||
"smtp-transport.password": "Password",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"post-sharing": "Post Sharing",
|
||||
"info-plugins-additional": "Plugins can add additional networks for sharing posts.",
|
||||
"save-success": "Successfully saved Post Sharing Networks!"
|
||||
"post-sharing": "การแชร์กระทู้",
|
||||
"info-plugins-additional": "ส่วนเสริมสามารถเพิ่มการเชือมต่อโซเชียลมิเดียเพื่อแชร์กระทู้",
|
||||
"save-success": "การบันทึกการโพสแชร์เนื้อหาเสร็จสมบูรณ์!"
|
||||
}
|
||||
@@ -1,91 +1,91 @@
|
||||
{
|
||||
"users": "Users",
|
||||
"edit": "Edit",
|
||||
"make-admin": "Make Admin",
|
||||
"remove-admin": "Remove Admin",
|
||||
"validate-email": "Validate Email",
|
||||
"send-validation-email": "Send Validation Email",
|
||||
"password-reset-email": "Send Password Reset Email",
|
||||
"ban": "Ban User(s)",
|
||||
"temp-ban": "Ban User(s) Temporarily",
|
||||
"unban": "Unban User(s)",
|
||||
"reset-lockout": "Reset Lockout",
|
||||
"reset-flags": "Reset Flags",
|
||||
"delete": "Delete User(s)",
|
||||
"purge": "Delete User(s) and Content",
|
||||
"download-csv": "Download CSV",
|
||||
"invite": "Invite",
|
||||
"new": "New User",
|
||||
"users": "ผู้ใช้",
|
||||
"edit": "แก้ไข",
|
||||
"make-admin": "ทำให้เป็นแอดมิน",
|
||||
"remove-admin": "ยกเลิกการเป็นแอดมิน",
|
||||
"validate-email": "ยืนยันอีเมล",
|
||||
"send-validation-email": "ส่งอีเมลยืนยัน",
|
||||
"password-reset-email": "ส่งการล้างค่ารหัสผ่านทางอีเมล",
|
||||
"ban": "ผู้ใช้งานที่โดนแบน",
|
||||
"temp-ban": "ผู้ใช้งานที่โดนแบนชั่วคราว",
|
||||
"unban": "ยกเลิกการแบนผู้ใช้งาน",
|
||||
"reset-lockout": "ยกเลิกการกักกัน",
|
||||
"reset-flags": "ยกเลิกการการเฝ้าระวัง",
|
||||
"delete": "ลบผู้ใช้งาน",
|
||||
"purge": "ลบผู้ใช้งานและเนื้อหา",
|
||||
"download-csv": "ดาวน์โหลด CSV",
|
||||
"invite": "เชิญ",
|
||||
"new": "ผู้ใช้งานใหม่",
|
||||
|
||||
"pills.latest": "Latest Users",
|
||||
"pills.unvalidated": "Not Validated",
|
||||
"pills.no-posts": "No Posts",
|
||||
"pills.top-posters": "Top Posters",
|
||||
"pills.top-rep": "Most Reputation",
|
||||
"pills.inactive": "Inactive",
|
||||
"pills.flagged": "Most Flagged",
|
||||
"pills.banned": "Banned",
|
||||
"pills.search": "User Search",
|
||||
"pills.latest": "ผู้ใช้งานคนล่าสุด",
|
||||
"pills.unvalidated": "ยังไม่ได้ยืนยัน",
|
||||
"pills.no-posts": "ไม่มีกระทู้",
|
||||
"pills.top-posters": "ผู้ที่โพสกระทู้มากที่สุด",
|
||||
"pills.top-rep": "ชื่อเสียงมากที่สุด",
|
||||
"pills.inactive": "ไม่มีการใช้งาน",
|
||||
"pills.flagged": "ได้รับการติดตาม",
|
||||
"pills.banned": "แบน",
|
||||
"pills.search": "ค้นหาผู้ใช้งาน",
|
||||
|
||||
"search.username": "By User Name",
|
||||
"search.username-placeholder": "Enter a username to search",
|
||||
"search.email": "By Email",
|
||||
"search.email-placeholder": "Enter a email to search",
|
||||
"search.ip": "By IP Address",
|
||||
"search.ip-placeholder": "Enter an IP Address to search",
|
||||
"search.not-found": "User not found!",
|
||||
"search.username": "โดยชื่อผู้ใช้งาน",
|
||||
"search.username-placeholder": "ใส่ชื่อผู้ใช้งานเพื่อทำการค้นหา",
|
||||
"search.email": "โดยอีเมล",
|
||||
"search.email-placeholder": "ใส่อีเมลเพื่อทำการค้นหา",
|
||||
"search.ip": "โดย IP แอดเดรส",
|
||||
"search.ip-placeholder": "ใส่ IP แอดเดรสเพื่อทำการค้นหา",
|
||||
"search.not-found": "ไม่พบผู้ใช้งาน!",
|
||||
|
||||
"inactive.3-months": "3 months",
|
||||
"inactive.6-months": "6 months",
|
||||
"inactive.12-months": "12 months",
|
||||
"inactive.3-months": "สามเดือน",
|
||||
"inactive.6-months": "หกเดือน",
|
||||
"inactive.12-months": "สิบสองเดือน",
|
||||
|
||||
"users.uid": "uid",
|
||||
"users.username": "username",
|
||||
"users.email": "email",
|
||||
"users.postcount": "postcount",
|
||||
"users.reputation": "reputation",
|
||||
"users.flags": "flags",
|
||||
"users.joined": "joined",
|
||||
"users.last-online": "last online",
|
||||
"users.banned": "banned",
|
||||
"users.username": "ชื่อผู้ใช้",
|
||||
"users.email": "อีเมล",
|
||||
"users.postcount": "จำนวนกระทู้",
|
||||
"users.reputation": "ชื่อเสียง",
|
||||
"users.flags": "ติดตาม",
|
||||
"users.joined": "เข้าร่วม",
|
||||
"users.last-online": "ออนไลน์ครั้งสุดท้าย",
|
||||
"users.banned": "แบน",
|
||||
|
||||
"create.username": "User Name",
|
||||
"create.email": "Email",
|
||||
"create.email-placeholder": "Email of this user",
|
||||
"create.password": "Password",
|
||||
"create.password-confirm": "Confirm Password",
|
||||
"create.username": "ชื่อผู้ใช้งาน",
|
||||
"create.email": "อีเมล",
|
||||
"create.email-placeholder": "อีเมลของผู้ใช้",
|
||||
"create.password": "รหัสผ่าน",
|
||||
"create.password-confirm": "ยืนยันรหัสผ่าน",
|
||||
|
||||
"temp-ban.length": "Ban Length",
|
||||
"temp-ban.reason": "Reason <span class=\"text-muted\">(Optional)</span>",
|
||||
"temp-ban.hours": "Hours",
|
||||
"temp-ban.days": "Days",
|
||||
"temp-ban.explanation": "Enter the length of time for the ban. Note that a time of 0 will be a considered a permanent ban.",
|
||||
"temp-ban.length": "ระยะเวลาการแบน",
|
||||
"temp-ban.reason": "เหตุผล <span class=\"text-muted\">(ตัวเลือก)</span>",
|
||||
"temp-ban.hours": "ชั่วโมง",
|
||||
"temp-ban.days": "วัน",
|
||||
"temp-ban.explanation": "ระบุระยะเวลาของการแบน ถ้าระยะเวลาเป็น \"0\" คือการแบนถาวร",
|
||||
|
||||
"alerts.confirm-ban": "Do you really want to ban this user <strong>permanently</strong>?",
|
||||
"alerts.confirm-ban-multi": "Do you really want to ban these users <strong>permanently</strong>?",
|
||||
"alerts.ban-success": "User(s) banned!",
|
||||
"alerts.button-ban-x": "Ban %1 user(s)",
|
||||
"alerts.unban-success": "User(s) unbanned!",
|
||||
"alerts.lockout-reset-success": "Lockout(s) reset!",
|
||||
"alerts.flag-reset-success": "Flags(s) reset!",
|
||||
"alerts.no-remove-yourself-admin": "You can't remove yourself as Administrator!",
|
||||
"alerts.make-admin-success": "User(s) are now administrators.",
|
||||
"alerts.confirm-remove-admin": "Do you really want to remove admins?",
|
||||
"alerts.remove-admin-success": "User(s) are no longer administrators.",
|
||||
"alerts.confirm-validate-email": "Do you want to validate email(s) of these user(s)?",
|
||||
"alerts.validate-email-success": "Emails validated",
|
||||
"alerts.password-reset-confirm": "Do you want to send password reset email(s) to these user(s)?",
|
||||
"alerts.confirm-delete": "<b>Warning!</b><br/>Do you really want to delete user(s)?<br/> This action is not reversable! Only the user account will be deleted, their posts and topics will remain.",
|
||||
"alerts.delete-success": "User(s) Deleted!",
|
||||
"alerts.confirm-purge": "<b>Warning!</b><br/>Do you really want to delete user(s) and their content?<br/> This action is not reversable! All user data and content will be erased!",
|
||||
"alerts.create": "Create User",
|
||||
"alerts.button-create": "Create",
|
||||
"alerts.button-cancel": "Cancel",
|
||||
"alerts.error-passwords-different": "Passwords must match!",
|
||||
"alerts.error-x": "<strong>Error</strong><p>%1</p>",
|
||||
"alerts.create-success": "User created!",
|
||||
"alerts.confirm-ban": "คุณต้องการที่จะแบนผู้ใช้คนนี้ <strong>ถาวร </strong>?",
|
||||
"alerts.confirm-ban-multi": "คุณต้องการที่จะแบนผู้ใช้กลุ่มนี้ <strong>ถาวร </strong>?",
|
||||
"alerts.ban-success": "ผู้ใช้งานที่โดนแบน",
|
||||
"alerts.button-ban-x": "แบน %1 ผู้ใช้งาน",
|
||||
"alerts.unban-success": "ยกเลิกการแบนผู้ใช้งาน",
|
||||
"alerts.lockout-reset-success": "ยกเลิกการกักกัน",
|
||||
"alerts.flag-reset-success": "ยกเลิกการติดตาม",
|
||||
"alerts.no-remove-yourself-admin": "คุณไม่สามารถที่จะยกเลิกตัวเองจากการเป็นผู้ดูแลระบบ",
|
||||
"alerts.make-admin-success": "ผู้ใช้งานได้เป็นแอดมิน",
|
||||
"alerts.confirm-remove-admin": "คุณต้องการที่จะยกเลิกแอดมินเหล่านี้?",
|
||||
"alerts.remove-admin-success": "ผู้ใช้งานที่ไม่ได้เป็นแอดมิน",
|
||||
"alerts.confirm-validate-email": "คุณต้องการที่ยืนยันอีเมลของผู้ใช้เหล่านี้หรือไม่?",
|
||||
"alerts.validate-email-success": "อีเมลที่ได้รับการยืนยัน",
|
||||
"alerts.password-reset-confirm": "คุณต้องการที่จะส่งอีเมลการล้างค่ารหัสผ่านให้กับผู้ใช้เหล่านี้หรือไม่?",
|
||||
"alerts.confirm-delete": "<b>ระวัง!</b><br/>คุณต้องการที่จะลบผู้ใช้งานเหล่านี้หรือไม่?<br/> การกระทำนี้ไม่สามารถย้อนกลับได้! มีเพียงแค่บัญชีที่จะโดนลบ แต่ว่ากระทู้และคอมเมนท์จะยังคงอยู่",
|
||||
"alerts.delete-success": "ผู้ใช้งานโดนลบ!",
|
||||
"alerts.confirm-purge": "<b>ระวัง!</b><br/>คุณต้องการที่จะลบผู้ใช้งานเหล่านี้และเนื้อหาของเขาหรือไม่?<br/> การกระทำนี้ไม่สามรถย้อนกลับได้! บัญชีและเนื้อหาของผู้ใช้เหล่านี้จะโดนลบ!",
|
||||
"alerts.create": "สร้างผู้ใช้งาน",
|
||||
"alerts.button-create": "สร้าง",
|
||||
"alerts.button-cancel": "ยกเลิก",
|
||||
"alerts.error-passwords-different": "รหัสผ่านจะต้องเหมือนกัน! ",
|
||||
"alerts.error-x": "<strong>ผิดพลาด</strong><p>%1</p>",
|
||||
"alerts.create-success": "ผู้ใช้งานถูกสร้าง!",
|
||||
|
||||
"alerts.prompt-email": "Email: ",
|
||||
"alerts.email-sent-to": "An invitation email has been sent to %1",
|
||||
"alerts.x-users-found": "%1 user(s) found! Search took %2 ms."
|
||||
"alerts.prompt-email": "อีเมล: ",
|
||||
"alerts.email-sent-to": "อีเมลคำเชิญถูกส่งไปที่ %1",
|
||||
"alerts.x-users-found": "%1 ผู้ใช้งานถูกพบ! การค้นหาใช้เวลา %2 ms."
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"notifications": "Notifications",
|
||||
"welcome-notification": "Welcome Notification",
|
||||
"welcome-notification-link": "Welcome Notification Link"
|
||||
"notifications": "การแจ้งเตือน",
|
||||
"welcome-notification": "การยินดีต้อนรับแจ้งเตือน",
|
||||
"welcome-notification-link": "ลิงค์การยินดีต้อนรับแจ้งเตือน"
|
||||
}
|
||||
@@ -72,8 +72,8 @@ define('admin/appearance/skins', ['translator', 'benchpress'], function (transla
|
||||
|
||||
if (config['theme:src']) {
|
||||
var skin = config['theme:src']
|
||||
.match(/latest\/(\S+)\/bootstrap.min.css/)[1]
|
||||
.replace(/(^|\s)([a-z])/g, function (m, p1, p2) { return p1 + p2.toUpperCase(); });
|
||||
.match(/latest\/(\S+)\/bootstrap.min.css/)[1]
|
||||
.replace(/(^|\s)([a-z])/g, function (m, p1, p2) { return p1 + p2.toUpperCase(); });
|
||||
|
||||
highlightSelectedTheme(skin);
|
||||
}
|
||||
@@ -105,9 +105,9 @@ define('admin/appearance/skins', ['translator', 'benchpress'], function (transla
|
||||
$('[data-theme="' + themeId + '"]')
|
||||
.addClass('selected')
|
||||
.find('[data-action="use"]')
|
||||
.html(current)
|
||||
.removeClass('btn-primary')
|
||||
.addClass('btn-success');
|
||||
.html(current)
|
||||
.removeClass('btn-primary')
|
||||
.addClass('btn-success');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -94,16 +94,16 @@ define('admin/appearance/themes', ['translator', 'benchpress'], function (transl
|
||||
$('[data-theme]')
|
||||
.removeClass('selected')
|
||||
.find('[data-action="use"]')
|
||||
.html(select)
|
||||
.removeClass('btn-success')
|
||||
.addClass('btn-primary');
|
||||
.html(select)
|
||||
.removeClass('btn-success')
|
||||
.addClass('btn-primary');
|
||||
|
||||
$('[data-theme="' + themeId + '"]')
|
||||
.addClass('selected')
|
||||
.find('[data-action="use"]')
|
||||
.html(current)
|
||||
.removeClass('btn-primary')
|
||||
.addClass('btn-success');
|
||||
.html(current)
|
||||
.removeClass('btn-primary')
|
||||
.addClass('btn-success');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -148,8 +148,7 @@ define('admin/extend/widgets', ['jqueryui'], function () {
|
||||
.addClass(btn.attr('data-class'));
|
||||
|
||||
container.attr('data-container-html', container.attr('data-container-html')
|
||||
.replace(/class="[a-zA-Z0-9-\s]+"/, 'class="' + container[0].className.replace(' pointer ui-draggable', '') + '"')
|
||||
);
|
||||
.replace(/class="[a-zA-Z0-9-\s]+"/, 'class="' + container[0].className.replace(' pointer ui-draggable', '') + '"'));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -472,8 +472,7 @@ define('admin/general/dashboard', ['semver', 'Chart', 'translator', 'benchpress'
|
||||
var topic = topics[tids[i]];
|
||||
var label = topic.value === '0' ? topic.title : '<a title="' + topic.title + '"href="' + RELATIVE_PATH + '/topic/' + tids[i] + '" target="_blank"> ' + topic.title + '</a>';
|
||||
|
||||
legend.append(
|
||||
'<li>' +
|
||||
legend.append('<li>' +
|
||||
'<div style="background-color: ' + topicColors[i] + ';"></div>' +
|
||||
'<span>' + label + '</span>' +
|
||||
'</li>');
|
||||
|
||||
@@ -36,7 +36,7 @@ define('admin/modules/search', ['mousetrap'], function (mousetrap) {
|
||||
'<a role= "menuitem" href= "' + config.relative_path + '/' + namespace + '" >' +
|
||||
title +
|
||||
'<br>' + (!results ? '' :
|
||||
('<small><code>' +
|
||||
('<small><code>' +
|
||||
results +
|
||||
'</small></code>')) +
|
||||
'</a>' +
|
||||
|
||||
@@ -439,6 +439,9 @@ app.cacheBuster = null;
|
||||
.replace('{pageTitle}', function () { return title; })
|
||||
.replace('{browserTitle}', function () { return config.browserTitle; });
|
||||
|
||||
// Allow translation strings in title on ajaxify (#5927)
|
||||
title = translator.unescape(title);
|
||||
|
||||
translator.translate(title, function (translated) {
|
||||
titleObj.titles[0] = translated;
|
||||
app.alternatingTitle('');
|
||||
|
||||
@@ -73,7 +73,8 @@ define('forum/account/header', [
|
||||
}
|
||||
|
||||
function setupCoverPhoto() {
|
||||
coverPhoto.init(components.get('account/cover'),
|
||||
coverPhoto.init(
|
||||
components.get('account/cover'),
|
||||
function (imageData, position, callback) {
|
||||
socket.emit('user.updateCover', {
|
||||
uid: ajaxify.data.uid,
|
||||
|
||||
@@ -96,9 +96,7 @@ define('forum/chats/messages', ['components', 'sounds', 'translator', 'benchpres
|
||||
|
||||
messages.scrollToBottom = function (containerEl) {
|
||||
if (containerEl.length) {
|
||||
containerEl.scrollTop(
|
||||
containerEl[0].scrollHeight - containerEl.height()
|
||||
);
|
||||
containerEl.scrollTop(containerEl[0].scrollHeight - containerEl.height());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -63,8 +63,8 @@ define('forum/chats/search', ['components'], function (components) {
|
||||
}
|
||||
|
||||
var chatEl = $('<li component="chat/search/user" />')
|
||||
.attr('data-uid', userObj.uid)
|
||||
.appendTo(chatsListEl);
|
||||
.attr('data-uid', userObj.uid)
|
||||
.appendTo(chatsListEl);
|
||||
|
||||
chatEl.append(createUserImage());
|
||||
return chatEl;
|
||||
|
||||
@@ -21,7 +21,8 @@ define('forum/groups/details', [
|
||||
if (ajaxify.data.group.isOwner) {
|
||||
Details.prepareSettings();
|
||||
|
||||
coverPhoto.init(components.get('groups/cover'),
|
||||
coverPhoto.init(
|
||||
components.get('groups/cover'),
|
||||
function (imageData, position, callback) {
|
||||
socket.emit('groups.cover.update', {
|
||||
groupName: groupName,
|
||||
|
||||
@@ -77,8 +77,9 @@
|
||||
var type = tag.type ? 'type="' + tag.type + '" ' : '';
|
||||
var href = tag.href ? 'href="' + tag.href + '" ' : '';
|
||||
var sizes = tag.sizes ? 'sizes="' + tag.sizes + '" ' : '';
|
||||
var title = tag.title ? 'title="' + tag.title + '" ' : '';
|
||||
|
||||
return '<link ' + link + rel + type + sizes + href + '/>\n\t';
|
||||
return '<link ' + link + rel + type + sizes + title + href + '/>\n\t';
|
||||
}
|
||||
|
||||
function stringify(obj) {
|
||||
|
||||
@@ -4,7 +4,7 @@ define('navigator', ['forum/pagination', 'components'], function (pagination, co
|
||||
var navigator = {};
|
||||
var index = 1;
|
||||
var count = 0;
|
||||
var navigatorUpdateTimeoutId = 0;
|
||||
var navigatorUpdateTimeoutId;
|
||||
|
||||
navigator.scrollActive = false;
|
||||
|
||||
@@ -91,11 +91,12 @@ define('navigator', ['forum/pagination', 'components'], function (pagination, co
|
||||
}
|
||||
|
||||
navigator.delayedUpdate = function () {
|
||||
if (navigatorUpdateTimeoutId) {
|
||||
clearTimeout(navigatorUpdateTimeoutId);
|
||||
navigatorUpdateTimeoutId = 0;
|
||||
if (!navigatorUpdateTimeoutId) {
|
||||
navigatorUpdateTimeoutId = setTimeout(function () {
|
||||
navigator.update();
|
||||
navigatorUpdateTimeoutId = undefined;
|
||||
}, 100);
|
||||
}
|
||||
navigatorUpdateTimeoutId = setTimeout(navigator.update, 100);
|
||||
};
|
||||
|
||||
navigator.update = function (threshold) {
|
||||
@@ -165,7 +166,9 @@ define('navigator', ['forum/pagination', 'components'], function (pagination, co
|
||||
index = index > count ? count : index;
|
||||
|
||||
$('.pagination-block .pagination-text').translateHtml('[[global:pagination.out_of, ' + index + ', ' + count + ']]');
|
||||
$('.pagination-block .progress-bar').width((index / count * 100) + '%');
|
||||
var fraction = $(window).scrollTop() / ($(document).height() - $(window).height());
|
||||
$('.pagination-block meter').val(fraction);
|
||||
$('.pagination-block .progress-bar').width((fraction * 100) + '%');
|
||||
};
|
||||
|
||||
navigator.scrollUp = function () {
|
||||
|
||||
@@ -23,22 +23,22 @@
|
||||
|
||||
widgetsAtLocation.forEach(function (widget) {
|
||||
html += widget.html;
|
||||
|
||||
if (location === 'footer' && !$('#content [widget-area="footer"]').length) {
|
||||
$('#content').append($('<div class="row"><div widget-area="footer" class="col-xs-12"></div></div>'));
|
||||
} else if (location === 'sidebar' && !$('#content [widget-area="sidebar"]').length) {
|
||||
if ($('[component="account/cover"]').length) {
|
||||
$('[component="account/cover"]').nextAll().wrapAll($('<div class="row"><div class="col-lg-9 col-xs-12"></div><div widget-area="sidebar" class="col-lg-3 col-xs-12"></div></div></div>'));
|
||||
} else if ($('[component="groups/cover"]').length) {
|
||||
$('[component="groups/cover"]').nextAll().wrapAll($('<div class="row"><div class="col-lg-9 col-xs-12"></div><div widget-area="sidebar" class="col-lg-3 col-xs-12"></div></div></div>'));
|
||||
} else {
|
||||
$('#content > *').wrapAll($('<div class="row"><div class="col-lg-9 col-xs-12"></div><div widget-area="sidebar" class="col-lg-3 col-xs-12"></div></div></div>'));
|
||||
}
|
||||
} else if (location === 'header' && !$('#content [widget-area="header"]').length) {
|
||||
$('#content').prepend($('<div class="row"><div widget-area="header" class="col-xs-12"></div></div>'));
|
||||
}
|
||||
});
|
||||
|
||||
if (location === 'footer' && !$('#content [widget-area="footer"]').length) {
|
||||
$('#content').append($('<div class="row"><div widget-area="footer" class="col-xs-12"></div></div>'));
|
||||
} else if (location === 'sidebar' && !$('#content [widget-area="sidebar"]').length) {
|
||||
if ($('[component="account/cover"]').length) {
|
||||
$('[component="account/cover"]').nextAll().wrapAll($('<div class="row"><div class="col-lg-9 col-xs-12"></div><div widget-area="sidebar" class="col-lg-3 col-xs-12"></div></div></div>'));
|
||||
} else if ($('[component="groups/cover"]').length) {
|
||||
$('[component="groups/cover"]').nextAll().wrapAll($('<div class="row"><div class="col-lg-9 col-xs-12"></div><div widget-area="sidebar" class="col-lg-3 col-xs-12"></div></div></div>'));
|
||||
} else {
|
||||
$('#content > *').wrapAll($('<div class="row"><div class="col-lg-9 col-xs-12"></div><div widget-area="sidebar" class="col-lg-3 col-xs-12"></div></div></div>'));
|
||||
}
|
||||
} else if (location === 'header' && !$('#content [widget-area="header"]').length) {
|
||||
$('#content').prepend($('<div class="row"><div widget-area="header" class="col-xs-12"></div></div>'));
|
||||
}
|
||||
|
||||
area = $('#content [widget-area="' + location + '"]');
|
||||
if (html && area.length) {
|
||||
area.html(html);
|
||||
|
||||
@@ -89,7 +89,7 @@ uploadsController.uploadLogo = function (req, res, next) {
|
||||
uploadsController.uploadSound = function (req, res, next) {
|
||||
var uploadedFile = req.files.files[0];
|
||||
|
||||
var mimeType = mime.lookup(uploadedFile.name);
|
||||
var mimeType = mime.getType(uploadedFile.name);
|
||||
if (!/^audio\//.test(mimeType)) {
|
||||
return next(Error('[[error:invalid-data]]'));
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
var nconf = require('nconf');
|
||||
var winston = require('winston');
|
||||
var validator = require('validator');
|
||||
var plugins = require('../plugins');
|
||||
|
||||
exports.handleURIErrors = function (err, req, res, next) {
|
||||
// Handle cases where malformed URIs are passed in
|
||||
@@ -35,30 +36,50 @@ exports.handleURIErrors = function (err, req, res, next) {
|
||||
// this needs to have four arguments or express treats it as `(req, res, next)`
|
||||
// don't remove `next`!
|
||||
exports.handleErrors = function (err, req, res, next) { // eslint-disable-line no-unused-vars
|
||||
switch (err.code) {
|
||||
case 'EBADCSRFTOKEN':
|
||||
winston.error(req.path + '\n', err.message);
|
||||
return res.sendStatus(403);
|
||||
case 'blacklisted-ip':
|
||||
return res.status(403).type('text/plain').send(err.message);
|
||||
}
|
||||
var cases = {
|
||||
EBADCSRFTOKEN: function () {
|
||||
winston.error(req.path + '\n', err.message);
|
||||
res.sendStatus(403);
|
||||
},
|
||||
'blacklisted-ip': function () {
|
||||
res.status(403).type('text/plain').send(err.message);
|
||||
},
|
||||
};
|
||||
var defaultHandler = function () {
|
||||
// Display NodeBB error page
|
||||
var status = parseInt(err.status, 10);
|
||||
if ((status === 302 || status === 308) && err.path) {
|
||||
return res.locals.isAPI ? res.set('X-Redirect', err.path).status(200).json(err.path) : res.redirect(err.path);
|
||||
}
|
||||
|
||||
var status = parseInt(err.status, 10);
|
||||
if ((status === 302 || status === 308) && err.path) {
|
||||
return res.locals.isAPI ? res.set('X-Redirect', err.path).status(200).json(err.path) : res.redirect(err.path);
|
||||
}
|
||||
winston.error(req.path + '\n', err.stack);
|
||||
|
||||
winston.error(req.path + '\n', err.stack);
|
||||
res.status(status || 500);
|
||||
|
||||
res.status(status || 500);
|
||||
var path = String(req.path || '');
|
||||
if (res.locals.isAPI) {
|
||||
res.json({ path: validator.escape(path), error: err.message });
|
||||
} else {
|
||||
var middleware = require('../middleware');
|
||||
middleware.buildHeader(req, res, function () {
|
||||
res.render('500', { path: validator.escape(path), error: validator.escape(String(err.message)) });
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var path = String(req.path || '');
|
||||
if (res.locals.isAPI) {
|
||||
res.json({ path: validator.escape(path), error: err.message });
|
||||
} else {
|
||||
var middleware = require('../middleware');
|
||||
middleware.buildHeader(req, res, function () {
|
||||
res.render('500', { path: validator.escape(path), error: validator.escape(String(err.message)) });
|
||||
});
|
||||
}
|
||||
plugins.fireHook('filter:error.handle', {
|
||||
cases: cases,
|
||||
}, function (_err, data) {
|
||||
if (_err) {
|
||||
// Assume defaults
|
||||
winston.warn('[errors/handle] Unable to retrieve plugin handlers for errors: ' + _err.message);
|
||||
data.cases = cases;
|
||||
}
|
||||
|
||||
if (data.cases.hasOwnProperty(err.code)) {
|
||||
data.cases[err.code](err, req, res, defaultHandler);
|
||||
} else {
|
||||
defaultHandler();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -8,7 +8,7 @@ var meta = require('../meta');
|
||||
|
||||
module.exports.handle = function (req, res, next) {
|
||||
if (plugins.hasListeners('filter:search.query')) {
|
||||
res.type('application/xml').send(generateXML());
|
||||
res.type('application/opensearchdescription+xml').send(generateXML());
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
@@ -17,9 +17,21 @@ module.exports.handle = function (req, res, next) {
|
||||
function generateXML() {
|
||||
return xml([{
|
||||
OpenSearchDescription: [
|
||||
{ _attr: { xmlns: 'http://a9.com/-/spec/opensearch/1.1/' } },
|
||||
{ ShortName: String(meta.config.title || meta.config.browserTitle || 'NodeBB') },
|
||||
{ Description: String(meta.config.description || '') },
|
||||
{ _attr: {
|
||||
xmlns: 'http://a9.com/-/spec/opensearch/1.1/',
|
||||
'xmlns:moz': 'http://www.mozilla.org/2006/browser/search/',
|
||||
} },
|
||||
{ ShortName: trimToLength(String(meta.config.title || meta.config.browserTitle || 'NodeBB'), 16) },
|
||||
{ Description: trimToLength(String(meta.config.description || ''), 1024) },
|
||||
{ InputEncoding: 'UTF-8' },
|
||||
{ Image: [
|
||||
{ _attr: {
|
||||
width: '16',
|
||||
height: '16',
|
||||
type: 'image/x-icon',
|
||||
} },
|
||||
nconf.get('url') + '/favicon.ico',
|
||||
] },
|
||||
{ Url: {
|
||||
_attr: {
|
||||
type: 'text/html',
|
||||
@@ -27,6 +39,11 @@ function generateXML() {
|
||||
template: nconf.get('url') + '/search?term={searchTerms}&in=titlesposts',
|
||||
},
|
||||
} },
|
||||
{ 'moz:SearchForm': nconf.get('url') + '/search' },
|
||||
],
|
||||
}], { declaration: true, indent: '\t' });
|
||||
}
|
||||
|
||||
function trimToLength(string, length) {
|
||||
return string.trim().substring(0, length).trim();
|
||||
}
|
||||
|
||||
@@ -56,17 +56,33 @@ Emailer.registerApp = function (expressApp) {
|
||||
|
||||
// Enable Gmail transport if enabled in ACP
|
||||
if (parseInt(meta.config['email:smtpTransport:enabled'], 10) === 1) {
|
||||
var smtpOptions = {
|
||||
auth: {
|
||||
var smtpOptions = {};
|
||||
|
||||
if (meta.config['email:smtpTransport:user'] || meta.config['email:smtpTransport:pass']) {
|
||||
smtpOptions.auth = {
|
||||
user: meta.config['email:smtpTransport:user'],
|
||||
pass: meta.config['email:smtpTransport:pass'],
|
||||
},
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
if (meta.config['email:smtpTransport:serice'] === 'nodebb-custom-smtp') {
|
||||
if (meta.config['email:smtpTransport:service'] === 'nodebb-custom-smtp') {
|
||||
smtpOptions.port = meta.config['email:smtpTransport:port'];
|
||||
smtpOptions.host = meta.config['email:smtpTransport:host'];
|
||||
smtpOptions.secure = true;
|
||||
|
||||
if (meta.config['email:smtpTransport:security'] === 'NONE') {
|
||||
smtpOptions.secure = false;
|
||||
smtpOptions.requireTLS = false;
|
||||
smtpOptions.ignoreTLS = true;
|
||||
} else if (meta.config['email:smtpTransport:security'] === 'STARTTLS') {
|
||||
smtpOptions.secure = false;
|
||||
smtpOptions.requireTLS = true;
|
||||
smtpOptions.ignoreTLS = false;
|
||||
} else {
|
||||
// meta.config['email:smtpTransport:security'] === 'ENCRYPTED' or undefined
|
||||
smtpOptions.secure = true;
|
||||
smtpOptions.requireTLS = true;
|
||||
smtpOptions.ignoreTLS = false;
|
||||
}
|
||||
} else {
|
||||
smtpOptions.service = meta.config['email:smtpTransport:service'];
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ file.saveFileToLocal = function (filename, folder, tempPath, callback) {
|
||||
};
|
||||
|
||||
file.base64ToLocal = function (imageData, uploadPath, callback) {
|
||||
var buffer = new Buffer(imageData.slice(imageData.indexOf('base64') + 7), 'base64');
|
||||
var buffer = Buffer.from(imageData.slice(imageData.indexOf('base64') + 7), 'base64');
|
||||
uploadPath = path.join(nconf.get('upload_path'), uploadPath);
|
||||
|
||||
fs.writeFile(uploadPath, buffer, {
|
||||
@@ -141,7 +141,7 @@ file.linkDirs = function linkDirs(sourceDir, destDir, callback) {
|
||||
file.typeToExtension = function (type) {
|
||||
var extension;
|
||||
if (type) {
|
||||
extension = '.' + mime.extension(type);
|
||||
extension = '.' + mime.getExtension(type);
|
||||
}
|
||||
return extension;
|
||||
};
|
||||
|
||||
@@ -240,7 +240,7 @@ Flags.validate = function (payload, callback) {
|
||||
}
|
||||
|
||||
var minimumReputation = utils.isNumber(meta.config['privileges:flag']) ? parseInt(meta.config['privileges:flag'], 10) : 1;
|
||||
// Check if reporter meets rep threshold (or can edit the target post, in which case threshold does not apply)
|
||||
// Check if reporter meets rep threshold (or can edit the target post, in which case threshold does not apply)
|
||||
if (!editable.flag && parseInt(data.reporter.reputation, 10) < minimumReputation) {
|
||||
return callback(new Error('[[error:not-enough-reputation-to-flag]]'));
|
||||
}
|
||||
@@ -256,7 +256,7 @@ Flags.validate = function (payload, callback) {
|
||||
}
|
||||
|
||||
var minimumReputation = utils.isNumber(meta.config['privileges:flag']) ? parseInt(meta.config['privileges:flag'], 10) : 1;
|
||||
// Check if reporter meets rep threshold (or can edit the target user, in which case threshold does not apply)
|
||||
// Check if reporter meets rep threshold (or can edit the target user, in which case threshold does not apply)
|
||||
if (!editable && parseInt(data.reporter.reputation, 10) < minimumReputation) {
|
||||
return callback(new Error('[[error:not-enough-reputation-to-flag]]'));
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ module.exports = function (Groups) {
|
||||
|
||||
var tempPath = data.file ? data.file : '';
|
||||
var url;
|
||||
var type = data.file ? mime.lookup(data.file) : 'image/png';
|
||||
var type = data.file ? mime.getType(data.file) : 'image/png';
|
||||
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
|
||||
@@ -141,7 +141,7 @@ image.writeImageDataToTempFile = function (imageData, callback) {
|
||||
|
||||
var filepath = path.join(os.tmpdir(), filename + extension);
|
||||
|
||||
var buffer = new Buffer(imageData.slice(imageData.indexOf('base64') + 7), 'base64');
|
||||
var buffer = Buffer.from(imageData.slice(imageData.indexOf('base64') + 7), 'base64');
|
||||
|
||||
fs.writeFile(filepath, buffer, {
|
||||
encoding: 'base64',
|
||||
|
||||
@@ -20,7 +20,7 @@ Blacklist.load = function (callback) {
|
||||
Blacklist.get,
|
||||
Blacklist.validate,
|
||||
function (rules, next) {
|
||||
winston.verbose('[meta/blacklist] Loading ' + rules.valid.length + ' blacklist rules');
|
||||
winston.verbose('[meta/blacklist] Loading ' + rules.valid.length + ' blacklist rule(s)' + (rules.duplicateCount > 0 ? ', ignored ' + rules.duplicateCount + ' duplicate(s)' : ''));
|
||||
if (rules.invalid.length) {
|
||||
winston.warn('[meta/blacklist] ' + rules.invalid.length + ' invalid blacklist rule(s) were ignored.');
|
||||
}
|
||||
@@ -44,8 +44,8 @@ Blacklist.save = function (rules, callback) {
|
||||
db.setObject('ip-blacklist-rules', { rules: rules }, next);
|
||||
},
|
||||
function (next) {
|
||||
Blacklist.load(next);
|
||||
pubsub.publish('blacklist:reload');
|
||||
setImmediate(next);
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
@@ -71,8 +71,11 @@ Blacklist.test = function (clientIp, callback) {
|
||||
Blacklist._rules.ipv4.indexOf(clientIp) === -1 && // not explicitly specified in ipv4 list
|
||||
Blacklist._rules.ipv6.indexOf(clientIp) === -1 && // not explicitly specified in ipv6 list
|
||||
!Blacklist._rules.cidr.some(function (subnet) {
|
||||
return addr.match(ipaddr.parseCIDR(subnet));
|
||||
// return ip.cidrSubnet(subnet).contains(clientIp);
|
||||
var cidr = ipaddr.parseCIDR(subnet);
|
||||
if (addr.kind() !== cidr[0].kind()) {
|
||||
return false;
|
||||
}
|
||||
return addr.match(cidr);
|
||||
}) // not in a blacklisted IPv4 or IPv6 cidr range
|
||||
) {
|
||||
plugins.fireHook('filter:blacklist.test', { // To return test failure, pass back an error in callback
|
||||
@@ -108,6 +111,7 @@ Blacklist.validate = function (rules, callback) {
|
||||
var ipv6 = [];
|
||||
var cidr = [];
|
||||
var invalid = [];
|
||||
var duplicateCount = 0;
|
||||
|
||||
var inlineCommentMatch = /#.*$/;
|
||||
var whitelist = ['127.0.0.1', '::1', '::ffff:0:127.0.0.1'];
|
||||
@@ -119,6 +123,16 @@ Blacklist.validate = function (rules, callback) {
|
||||
return rule.length && !rule.startsWith('#') ? rule : null;
|
||||
}).filter(Boolean);
|
||||
|
||||
// Filter out duplicates
|
||||
rules = rules.filter(function (rule, index) {
|
||||
const pass = rules.indexOf(rule) === index;
|
||||
if (!pass) {
|
||||
duplicateCount += 1;
|
||||
}
|
||||
|
||||
return pass;
|
||||
});
|
||||
|
||||
// Filter out invalid rules
|
||||
rules = rules.filter(function (rule) {
|
||||
var addr;
|
||||
@@ -164,6 +178,7 @@ Blacklist.validate = function (rules, callback) {
|
||||
cidr: cidr,
|
||||
valid: rules,
|
||||
invalid: invalid,
|
||||
duplicateCount: duplicateCount,
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -282,9 +282,9 @@ function buildCSS(data, callback) {
|
||||
processImportFrom: ['local'],
|
||||
}),
|
||||
] : [autoprefixer]).process(lessOutput.css).then(function (result) {
|
||||
callback(null, { code: result.css });
|
||||
process.nextTick(callback, null, { code: result.css });
|
||||
}, function (err) {
|
||||
callback(err);
|
||||
process.nextTick(callback, err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ Tags.parse = function (req, data, meta, link, callback) {
|
||||
defaultLinks.push({
|
||||
rel: 'search',
|
||||
type: 'application/opensearchdescription+xml',
|
||||
title: validator.escape(String(Meta.config.title || Meta.config.browserTitle || 'NodeBB')),
|
||||
href: nconf.get('relative_path') + '/osd.xml',
|
||||
});
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ var nconf = require('nconf');
|
||||
var ensureLoggedIn = require('connect-ensure-login');
|
||||
var toobusy = require('toobusy-js');
|
||||
var Benchpress = require('benchpressjs');
|
||||
var LRU = require('lru-cache');
|
||||
|
||||
var plugins = require('../plugins');
|
||||
var meta = require('../meta');
|
||||
@@ -23,6 +24,10 @@ var controllers = {
|
||||
helpers: require('../controllers/helpers'),
|
||||
};
|
||||
|
||||
var delayCache = LRU({
|
||||
maxAge: 1000 * 60,
|
||||
});
|
||||
|
||||
var middleware = module.exports;
|
||||
|
||||
middleware.applyCSRF = csrf();
|
||||
@@ -186,6 +191,14 @@ middleware.processTimeagoLocales = function (req, res, next) {
|
||||
|
||||
middleware.delayLoading = function (req, res, next) {
|
||||
// Introduces an artificial delay during load so that brute force attacks are effectively mitigated
|
||||
|
||||
// Add IP to cache so if too many requests are made, subsequent requests are blocked for a minute
|
||||
var timesSeen = delayCache.get(req.ip) || 0;
|
||||
if (timesSeen > 10) {
|
||||
return res.sendStatus(429);
|
||||
}
|
||||
delayCache.set(req.ip, timesSeen += 1);
|
||||
|
||||
setTimeout(next, 1000);
|
||||
};
|
||||
|
||||
|
||||
@@ -39,8 +39,7 @@ module.exports = function (Plugins) {
|
||||
(Plugins.deprecatedHooks[data.hook] ?
|
||||
'please use `' + Plugins.deprecatedHooks[data.hook] + '` instead.' :
|
||||
'there is no alternative.'
|
||||
)
|
||||
);
|
||||
));
|
||||
} else {
|
||||
// handle hook's startsWith, i.e. action:homepage.get
|
||||
var parts = data.hook.split(':');
|
||||
@@ -61,7 +60,7 @@ module.exports = function (Plugins) {
|
||||
if (memo && memo[prop]) {
|
||||
return memo[prop];
|
||||
}
|
||||
// Couldn't find method by path, aborting
|
||||
// Couldn't find method by path, aborting
|
||||
return null;
|
||||
}, Plugins.libraries[data.id]);
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ module.exports = function (Topics) {
|
||||
|
||||
var extension = path.extname(data.thumb);
|
||||
if (!extension) {
|
||||
extension = '.' + mime.extension(type);
|
||||
extension = '.' + mime.getExtension(type);
|
||||
}
|
||||
filename = Date.now() + '-topic-thumb' + extension;
|
||||
pathToUpload = path.join(nconf.get('upload_path'), 'files', filename);
|
||||
|
||||
@@ -26,7 +26,7 @@ module.exports = {
|
||||
}
|
||||
|
||||
fs.access(sourcePath, function (err) {
|
||||
if (err) {
|
||||
if (err || path.extname(sourcePath) === '.svg') {
|
||||
skip = true;
|
||||
return setImmediate(next);
|
||||
}
|
||||
@@ -46,6 +46,7 @@ module.exports = {
|
||||
|
||||
meta.configs.setMultiple({
|
||||
'brand:logo': path.join(nconf.get('upload_path'), 'system', path.basename(meta.config['brand:logo'])),
|
||||
'brand:emailLogo': '/assets/uploads/system/site-logo-x50.png',
|
||||
}, next);
|
||||
},
|
||||
], callback);
|
||||
|
||||
@@ -28,7 +28,7 @@ module.exports = function (User) {
|
||||
var uploadSize = parseInt(meta.config.maximumProfileImageSize, 10) || 256;
|
||||
var size = res.headers['content-length'];
|
||||
var type = res.headers['content-type'];
|
||||
var extension = mime.extension(type);
|
||||
var extension = mime.getExtension(type);
|
||||
|
||||
if (['png', 'jpeg', 'jpg', 'gif'].indexOf(extension) === -1) {
|
||||
return callback(new Error('[[error:invalid-image-extension]]'));
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
<script>
|
||||
define('/assets/templates/500.js', function () {
|
||||
function compiled(helpers, context, get, iter, helper) {
|
||||
return '<div class="alert alert-danger">\n\t<strong>[[global:500.title]]</strong>\n\t<p>[[global:500.message]]</p>\n\t<p>' +
|
||||
helpers.__escape(get(context && context['path'])) + '</p>\n\t' +
|
||||
(get(context && context['error']) ? '<p>' + helpers.__escape(get(context && context['error'])) + '</p>' : '') + '\n\n\t' +
|
||||
(get(context && context['returnLink']) ? '\n\t<p>[[error:goback]]</p>\n\t' : '') + '\n</div>\n';
|
||||
}
|
||||
window.addEventListener('load', function () {
|
||||
define('/assets/templates/500.js', function () {
|
||||
function compiled(helpers, context, get, iter, helper) {
|
||||
return '<div class="alert alert-danger">\n\t<strong>[[global:500.title]]</strong>\n\t<p>[[global:500.message]]</p>\n\t<p>' +
|
||||
helpers.__escape(get(context && context['path'])) + '</p>\n\t' +
|
||||
(get(context && context['error']) ? '<p>' + helpers.__escape(get(context && context['error'])) + '</p>' : '') + '\n\n\t' +
|
||||
(get(context && context['returnLink']) ? '\n\t<p>[[error:goback]]</p>\n\t' : '') + '\n</div>\n';
|
||||
}
|
||||
|
||||
return compiled;
|
||||
});
|
||||
return compiled;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@@ -40,12 +40,12 @@
|
||||
<div class="form-group">
|
||||
<label for="email:smtpTransport:service"><strong>[[admin/settings/email:smtp-transport.service]]</strong></label>
|
||||
<select class="form-control input-lg" id="email:smtpTransport:service" data-field="email:smtpTransport:service">
|
||||
<option value="nodebb-custom-smtp" style="font-weight: bold">[[admin/settings/email:smtp-transport.service-custom]]</option>
|
||||
<option style="font-size: 10px" disabled> </option>
|
||||
|
||||
<!-- BEGIN services -->
|
||||
<option value="@value">@value</option>
|
||||
<!-- END services -->
|
||||
|
||||
<option style="font-size: 10px" disabled> </option>
|
||||
<option value="nodebb-custom-smtp" style="font-weight: bold">[[admin/settings/email:smtp-transport.service-custom]]</option>
|
||||
</select>
|
||||
<p class="help-block">
|
||||
[[admin/settings/email:smtp-transport.service-help]]
|
||||
@@ -63,6 +63,13 @@
|
||||
|
||||
<label for="email:smtpTransport:port">[[admin/settings/email:smtp-transport.port]]</label>
|
||||
<input type="text" class="form-control input-md" id="email:smtpTransport:port" data-field="email:smtpTransport:port" placeholder="5555">
|
||||
|
||||
<label for="email:smtpTransport:security">[[admin/settings/email:smtp-transport.security]]</label>
|
||||
<select class="form-control" id="email:smtpTransport:security" data-field="email:smtpTransport:security">
|
||||
<option value="ENCRYPTED">[[admin/settings/email:smtp-transport.security-encrypted]]</option>
|
||||
<option value="STARTTLS">[[admin/settings/email:smtp-transport.security-starttls]]</option>
|
||||
<option value="NONE">[[admin/settings/email:smtp-transport.security-none]]</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="email:smtpTransport:user"><strong>[[admin/settings/email:smtp-transport.username]]</strong></label>
|
||||
@@ -136,4 +143,4 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- IMPORT admin/partials/settings/footer.tpl -->
|
||||
<!-- IMPORT admin/partials/settings/footer.tpl -->
|
||||
|
||||
@@ -180,8 +180,15 @@ function setupExpressApp(app, callback) {
|
||||
setupAutoLocale(app, callback);
|
||||
}
|
||||
|
||||
function ping(req, res) {
|
||||
res.status(200).send(req.path === '/sping' ? 'healthy' : '200');
|
||||
function ping(req, res, next) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
db.getObject('config', next);
|
||||
},
|
||||
function () {
|
||||
res.status(200).send(req.path === '/sping' ? 'healthy' : '200');
|
||||
},
|
||||
], next);
|
||||
}
|
||||
|
||||
function setupFavicon(app) {
|
||||
|
||||
@@ -72,4 +72,9 @@ describe('blacklist', function () {
|
||||
assert(blacklist.test('1.1.1.1'));
|
||||
done();
|
||||
});
|
||||
|
||||
it('should pass ip test and not crash with ipv6 address', function (done) {
|
||||
assert(!blacklist.test('2001:db8:85a3:0:0:8a2e:370:7334'));
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -114,7 +114,7 @@ describe('minifier', function () {
|
||||
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}');
|
||||
assert.strictEqual(bundle.code, '.help{margin:10px}.yellow{background:#ff0}.help{display:block}.help .blue{background:#00f}');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -46,20 +46,20 @@ describe('admin search', function () {
|
||||
describe('sanitize', function () {
|
||||
it('should strip out scripts', function (done) {
|
||||
assert.equal(
|
||||
search.sanitize('Pellentesque tristique senectus' +
|
||||
'<script>alert("nope");</script> habitant morbi'),
|
||||
'Pellentesque tristique senectus' +
|
||||
' habitant morbi'
|
||||
);
|
||||
search.sanitize('Pellentesque tristique senectus' +
|
||||
'<script>alert("nope");</script> habitant morbi'),
|
||||
'Pellentesque tristique senectus' +
|
||||
' habitant morbi'
|
||||
);
|
||||
done();
|
||||
});
|
||||
it('should remove all tags', function (done) {
|
||||
assert.equal(
|
||||
search.sanitize('<p>Pellentesque <b>habitant morbi</b> tristique senectus' +
|
||||
'Aenean <i>vitae</i> est.Mauris <a href="placerat">eleifend</a> leo.</p>'),
|
||||
'Pellentesque habitant morbi tristique senectus' +
|
||||
'Aenean vitae est.Mauris eleifend leo.'
|
||||
);
|
||||
search.sanitize('<p>Pellentesque <b>habitant morbi</b> tristique senectus' +
|
||||
'Aenean <i>vitae</i> est.Mauris <a href="placerat">eleifend</a> leo.</p>'),
|
||||
'Pellentesque habitant morbi tristique senectus' +
|
||||
'Aenean vitae est.Mauris eleifend leo.'
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -67,24 +67,20 @@ describe('admin search', function () {
|
||||
describe('simplify', function () {
|
||||
it('should remove all mustaches', function (done) {
|
||||
assert.equal(
|
||||
search.simplify(
|
||||
'Pellentesque tristique {{senectus}}habitant morbi' +
|
||||
'liquam tincidunt {mauris.eu}risus'
|
||||
),
|
||||
'Pellentesque tristique habitant morbi' +
|
||||
'liquam tincidunt risus'
|
||||
);
|
||||
search.simplify('Pellentesque tristique {{senectus}}habitant morbi' +
|
||||
'liquam tincidunt {mauris.eu}risus'),
|
||||
'Pellentesque tristique habitant morbi' +
|
||||
'liquam tincidunt risus'
|
||||
);
|
||||
done();
|
||||
});
|
||||
it('should collapse all whitespace', function (done) {
|
||||
assert.equal(
|
||||
search.simplify(
|
||||
'Pellentesque tristique habitant morbi' +
|
||||
' \n\n liquam tincidunt mauris eu risus.'
|
||||
),
|
||||
'Pellentesque tristique habitant morbi' +
|
||||
'\nliquam tincidunt mauris eu risus.'
|
||||
);
|
||||
search.simplify('Pellentesque tristique habitant morbi' +
|
||||
' \n\n liquam tincidunt mauris eu risus.'),
|
||||
'Pellentesque tristique habitant morbi' +
|
||||
'\nliquam tincidunt mauris eu risus.'
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -72,7 +72,8 @@ describe('socket.io', function () {
|
||||
helpers.connectSocketIO(res, function (err, _io) {
|
||||
io = _io;
|
||||
assert.ifError(err);
|
||||
done(err);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -654,8 +654,8 @@ describe('Topic\'s', function () {
|
||||
function (next) {
|
||||
topicPids = replies.map(function (reply) { return reply.pid; });
|
||||
socketTopics.bookmark({ uid: topic.userId }, { tid: newTopic.tid, index: originalBookmark }, next);
|
||||
}],
|
||||
done);
|
||||
},
|
||||
], done);
|
||||
});
|
||||
|
||||
it('should fail with invalid data', function (done) {
|
||||
@@ -711,7 +711,8 @@ describe('Topic\'s', function () {
|
||||
'Fork test, no bookmark update',
|
||||
topicPids.slice(1, 3),
|
||||
newTopic.tid,
|
||||
next);
|
||||
next
|
||||
);
|
||||
},
|
||||
function (forkedTopicData, next) {
|
||||
topics.getUserBookmark(newTopic.tid, topic.userId, next);
|
||||
|
||||
Reference in New Issue
Block a user