From 5e6d8ea566b1c356d1252550727edae8f0996237 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Sat, 14 Oct 2017 08:34:04 -0400 Subject: [PATCH 01/11] handle 404 first --- src/controllers/posts.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/controllers/posts.js b/src/controllers/posts.js index 4f906fb0ba..5f41d6a9d9 100644 --- a/src/controllers/posts.js +++ b/src/controllers/posts.js @@ -26,12 +26,12 @@ postsController.redirectToPost = function (req, res, next) { }, next); }, function (results, next) { - if (!results.canRead) { - return helpers.notAllowed(req, res); - } if (!results.path) { return next(); } + if (!results.canRead) { + return helpers.notAllowed(req, res); + } helpers.redirect(res, results.path); }, ], next); From 73b3e08682c9172772d7231b7336673718caf636 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Sun, 15 Oct 2017 17:54:40 -0400 Subject: [PATCH 02/11] up mentions --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 18512b8104..f228fa064b 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "nodebb-plugin-emoji-extended": "1.1.1", "nodebb-plugin-emoji-one": "1.2.1", "nodebb-plugin-markdown": "8.1.0", - "nodebb-plugin-mentions": "2.1.6", + "nodebb-plugin-mentions": "2.1.7", "nodebb-plugin-soundpack-default": "1.0.0", "nodebb-plugin-spam-be-gone": "0.5.1", "nodebb-rewards-essentials": "0.0.9", From 684a66a760ef507df608dea56b60fa527c79ed80 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 16 Oct 2017 10:51:53 -0400 Subject: [PATCH 03/11] fixes #5989 --- public/src/client/account/header.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/src/client/account/header.js b/public/src/client/account/header.js index 5c16a45a95..985cd7c82c 100644 --- a/public/src/client/account/header.js +++ b/public/src/client/account/header.js @@ -93,7 +93,7 @@ define('forum/account/header', [ paramValue: ajaxify.data.theirid, accept: '.png,.jpg,.bmp', }, function (imageUrlOnServer) { - components.get('account/cover').css('background-image', 'url(' + imageUrlOnServer + '?' + config['cache-buster'] + ')'); + components.get('account/cover').css('background-image', 'url(' + config.relative_path + imageUrlOnServer + '?' + config['cache-buster'] + ')'); }); }, removeCover From f4355efb305644335c619fd8e10f94354927e392 Mon Sep 17 00:00:00 2001 From: Peter Jaszkowiak Date: Sat, 14 Oct 2017 13:47:05 -0600 Subject: [PATCH 04/11] Optimize translator - Use `slice` less - Skip iterations with `indexOf` --- public/src/modules/translator.js | 202 ++++++++++++++++--------------- 1 file changed, 102 insertions(+), 100 deletions(-) diff --git a/public/src/modules/translator.js b/public/src/modules/translator.js index 465a240e4e..c71158c2e2 100644 --- a/public/src/modules/translator.js +++ b/public/src/modules/translator.js @@ -111,10 +111,10 @@ var level = 0; while (i + 2 <= len) { - if (text.slice(i, i + 2) === '[[') { + if (text[i] === '[' && text[i + 1] === '[') { level += 1; i += 1; - } else if (text.slice(i, i + 2) === ']]') { + } else if (text[i] === ']' && text[i + 1] === ']') { level -= 1; i += 1; } else if (level === 0 && text[i] === ',' && text[i - 1] !== '\\') { @@ -128,111 +128,113 @@ return arr; } + // move to the first [[ + cursor = str.indexOf('[[', cursor); + // the loooop, we'll go to where the cursor // is equal to the length of the string since // slice doesn't include the ending index - while (cursor + 2 <= len) { - // if the current position in the string looks - // like the beginning of a translation string - if (str.slice(cursor, cursor + 2) === '[[') { - // split the string from the last break - // to the character before the cursor - // add that to the result array - toTranslate.push(str.slice(lastBreak, cursor)); - // set the cursor position past the beginning - // brackets of the translation string - cursor += 2; - // set the last break to our current - // spot since we just broke the string - lastBreak = cursor; - // we're in a token now - inToken = true; - - // the current level of nesting of the translation strings - var level = 0; - var sliced; - // validating the current string is actually a translation - var textBeforeColonFound = false; - var colonFound = false; - var textAfterColonFound = false; - var commaAfterNameFound = false; - - while (cursor + 2 <= len) { - sliced = str.slice(cursor, cursor + 2); - // found some text after the double bracket, - // so this is probably a translation string - if (!textBeforeColonFound && validTextRegex.test(sliced[0])) { - textBeforeColonFound = true; - cursor += 1; - // found a colon, so this is probably a translation string - } else if (textBeforeColonFound && !colonFound && sliced[0] === ':') { - colonFound = true; - cursor += 1; - // found some text after the colon, - // so this is probably a translation string - } else if (colonFound && !textAfterColonFound && validTextRegex.test(sliced[0])) { - textAfterColonFound = true; - cursor += 1; - } else if (textAfterColonFound && !commaAfterNameFound && sliced[0] === ',') { - commaAfterNameFound = true; - cursor += 1; - // a space or comma was found before the name - // this isn't a translation string, so back out - } else if (!(textBeforeColonFound && colonFound && textAfterColonFound && commaAfterNameFound) && - invalidTextRegex.test(sliced[0])) { - cursor += 1; - lastBreak -= 2; - // no longer in a token - inToken = false; - if (level > 0) { - level -= 1; - } else { - break; - } - // if we're at the beginning of another translation string, - // we're nested, so add to our level - } else if (sliced === '[[') { - level += 1; - cursor += 2; - // if we're at the end of a translation string - } else if (sliced === ']]') { - // if we're at the base level, then this is the end - if (level === 0) { - // so grab the name and args - var currentSlice = str.slice(lastBreak, cursor); - var result = split(currentSlice); - var name = result[0]; - var args = result.slice(1); - - // make a backup based on the raw string of the token - // if there are arguments to the token - var backup = ''; - if (args && args.length) { - backup = this.translate(currentSlice); - } - // add the translation promise to the array - toTranslate.push(this.translateKey(name, args, backup)); - // skip past the ending brackets - cursor += 2; - // set this as our last break - lastBreak = cursor; - // and we're no longer in a translation string, - // so continue with the main loop - inToken = false; - break; - } - // otherwise we lower the level + while (cursor + 2 <= len && cursor !== -1) { + // split the string from the last break + // to the character before the cursor + // add that to the result array + toTranslate.push(str.slice(lastBreak, cursor)); + // set the cursor position past the beginning + // brackets of the translation string + cursor += 2; + // set the last break to our current + // spot since we just broke the string + lastBreak = cursor; + // we're in a token now + inToken = true; + + // the current level of nesting of the translation strings + var level = 0; + var char0; + var char1; + // validating the current string is actually a translation + var textBeforeColonFound = false; + var colonFound = false; + var textAfterColonFound = false; + var commaAfterNameFound = false; + + while (cursor + 2 <= len) { + char0 = str[cursor]; + char1 = str[cursor + 1]; + // found some text after the double bracket, + // so this is probably a translation string + if (!textBeforeColonFound && validTextRegex.test(char0)) { + textBeforeColonFound = true; + cursor += 1; + // found a colon, so this is probably a translation string + } else if (textBeforeColonFound && !colonFound && char0 === ':') { + colonFound = true; + cursor += 1; + // found some text after the colon, + // so this is probably a translation string + } else if (colonFound && !textAfterColonFound && validTextRegex.test(char0)) { + textAfterColonFound = true; + cursor += 1; + } else if (textAfterColonFound && !commaAfterNameFound && char0 === ',') { + commaAfterNameFound = true; + cursor += 1; + // a space or comma was found before the name + // this isn't a translation string, so back out + } else if (!(textBeforeColonFound && colonFound && textAfterColonFound && commaAfterNameFound) && + invalidTextRegex.test(char0)) { + cursor += 1; + lastBreak -= 2; + // no longer in a token + inToken = false; + if (level > 0) { level -= 1; - // and skip past the ending brackets - cursor += 2; } else { - // otherwise just move to the next character - cursor += 1; + break; } + // if we're at the beginning of another translation string, + // we're nested, so add to our level + } else if (char0 === '[' && char1 === '[') { + level += 1; + cursor += 2; + // if we're at the end of a translation string + } else if (char0 === ']' && char1 === ']') { + // if we're at the base level, then this is the end + if (level === 0) { + // so grab the name and args + var currentSlice = str.slice(lastBreak, cursor); + var result = split(currentSlice); + var name = result[0]; + var args = result.slice(1); + + // make a backup based on the raw string of the token + // if there are arguments to the token + var backup = ''; + if (args && args.length) { + backup = this.translate(currentSlice); + } + // add the translation promise to the array + toTranslate.push(this.translateKey(name, args, backup)); + // skip past the ending brackets + cursor += 2; + // set this as our last break + lastBreak = cursor; + // and we're no longer in a translation string, + // so continue with the main loop + inToken = false; + break; + } + // otherwise we lower the level + level -= 1; + // and skip past the ending brackets + cursor += 2; + } else { + // otherwise just move to the next character + cursor += 1; } } - // move to the next character - cursor += 1; + + // skip to the next [[ + cursor = str.indexOf('[[', cursor); } // ending string of source @@ -304,7 +306,7 @@ * 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} [key] - The key of the specific translation to getJSON - * @returns {Promise<{ [key: string]: string }>|Promise} + * @returns {Promise<{ [key: string]: string } | string>} */ Translator.prototype.getTranslation = function getTranslation(namespace, key) { var translation; From 8a499e09ed5e70dda6cc630413a00ca080296df7 Mon Sep 17 00:00:00 2001 From: Baris Usakli Date: Mon, 16 Oct 2017 13:51:47 -0400 Subject: [PATCH 05/11] fix nconf lowercasing --- src/install.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/install.js b/src/install.js index 40065adbc6..aa862f5338 100644 --- a/src/install.js +++ b/src/install.js @@ -442,7 +442,7 @@ function enableDefaultPlugins(next) { 'nodebb-plugin-emoji-extended', 'nodebb-plugin-emoji-one', ]; - var customDefaults = nconf.get('defaultPlugins'); + var customDefaults = nconf.get('defaultplugins') || nconf.get('defaultPlugins'); winston.info('[install/defaultPlugins] customDefaults', customDefaults); From d8dc79e3c71ddcf0332125a45ca45ef9c48f2a6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Wed, 18 Oct 2017 14:49:42 -0400 Subject: [PATCH 06/11] closes #5992 --- public/src/admin/general/dashboard.js | 27 ++++++++++++--------------- src/socket.io/admin/rooms.js | 12 +++++------- 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/public/src/admin/general/dashboard.js b/public/src/admin/general/dashboard.js index 3b448a4de8..e4023745f8 100644 --- a/public/src/admin/general/dashboard.js +++ b/public/src/admin/general/dashboard.js @@ -117,7 +117,7 @@ define('admin/general/dashboard', ['semver', 'Chart', 'translator', 'benchpress' updateRegisteredGraph(data.onlineRegisteredCount, data.onlineGuestCount); updatePresenceGraph(data.users); - updateTopicsGraph(data.topics); + updateTopicsGraph(data.topTenTopics); $('#active-users').translateHtml(html); }; @@ -444,39 +444,36 @@ define('admin/general/dashboard', ['semver', 'Chart', 'translator', 'benchpress' } function updateTopicsGraph(topics) { - if (!Object.keys(topics).length) { - topics = { 0: { + if (!topics.length) { + topics = [{ title: 'No users browsing', - value: 1, - } }; + count: 1, + }]; } - var tids = Object.keys(topics); - graphs.topics.data.labels = []; graphs.topics.data.datasets[0].data = []; graphs.topics.data.datasets[0].backgroundColor = []; graphs.topics.data.datasets[0].hoverBackgroundColor = []; - for (var i = 0, ii = tids.length; i < ii; i += 1) { - graphs.topics.data.labels.push(topics[tids[i]].title); - graphs.topics.data.datasets[0].data.push(topics[tids[i]].value); + topics.forEach(function (topic, i) { + graphs.topics.data.labels.push(topic.title); + graphs.topics.data.datasets[0].data.push(topic.count); graphs.topics.data.datasets[0].backgroundColor.push(topicColors[i]); graphs.topics.data.datasets[0].hoverBackgroundColor.push(lighten(topicColors[i], 10)); - } + }); function buildTopicsLegend() { var legend = $('#topics-legend').html(''); - for (var i = 0, ii = tids.length; i < ii; i += 1) { - var topic = topics[tids[i]]; - var label = topic.value === '0' ? topic.title : ' ' + topic.title + ''; + topics.forEach(function (topic, i) { + var label = topic.count === '0' ? topic.title : ' ' + topic.title + ''; legend.append('
  • ' + '
    ' + '' + label + '' + '
  • '); - } + }); } buildTopicsLegend(); diff --git a/src/socket.io/admin/rooms.js b/src/socket.io/admin/rooms.js index 85aec8e52e..8f3d36eb0a 100644 --- a/src/socket.io/admin/rooms.js +++ b/src/socket.io/admin/rooms.js @@ -89,7 +89,7 @@ SocketRooms.getAll = function (socket, data, callback) { var topTenTopics = []; Object.keys(totals.topics).forEach(function (tid) { - topTenTopics.push({ tid: tid, count: totals.topics[tid].count }); + topTenTopics.push({ tid: tid, count: totals.topics[tid].count || 0 }); }); topTenTopics = topTenTopics.sort(function (a, b) { @@ -105,13 +105,11 @@ SocketRooms.getAll = function (socket, data, callback) { topics.getTopicsFields(topTenTids, ['title'], next); }, function (titles, next) { - totals.topics = {}; - topTenTopics.forEach(function (topic, index) { - totals.topics[topic.tid] = { - value: topic.count || 0, - title: String(titles[index].title), - }; + totals.topTenTopics = topTenTopics.map(function (topic, index) { + topic.title = titles[index].title; + return topic; }); + next(null, totals); }, ], callback); From 797857e1d2fe73fa54fa4cc95efa8bd6ccfbbbc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Wed, 18 Oct 2017 15:06:22 -0400 Subject: [PATCH 07/11] remove old test --- test/socket.io.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/socket.io.js b/test/socket.io.js index eb7489ef8a..cb50b51a13 100644 --- a/test/socket.io.js +++ b/test/socket.io.js @@ -379,7 +379,6 @@ describe('socket.io', function () { assert(data.hasOwnProperty('socketCount')); assert(data.hasOwnProperty('topics')); assert(data.hasOwnProperty('users')); - assert.equal(data.topics['1'].title, 'test topic title'); done(); }); }, 1000); From 05c1a6cfed07d80b679e2db57e5f0619c1f8cc5e Mon Sep 17 00:00:00 2001 From: "Misty (Bot)" Date: Thu, 19 Oct 2017 09:26:06 +0000 Subject: [PATCH 08/11] Latest translations and fallbacks --- public/language/de/admin/settings/email.json | 6 ++-- public/language/de/email.json | 2 +- public/language/th/admin/extend/plugins.json | 36 +++++++++---------- .../language/th/admin/general/homepage.json | 2 +- public/language/th/admin/settings/chat.json | 10 +++--- public/language/th/error.json | 6 ++-- public/language/th/global.json | 4 +-- public/language/th/notifications.json | 2 +- public/language/th/pages.json | 4 +-- public/language/th/success.json | 2 +- public/language/th/topic.json | 4 +-- 11 files changed, 39 insertions(+), 39 deletions(-) diff --git a/public/language/de/admin/settings/email.json b/public/language/de/admin/settings/email.json index 3fd5c2e343..d9b0779e24 100644 --- a/public/language/de/admin/settings/email.json +++ b/public/language/de/admin/settings/email.json @@ -15,10 +15,10 @@ "smtp-transport.gmail-warning2": "Um mehr zu diesem Workaround zu erfahren, ließ dir bitten diesen NodeMailer Artikel zu diesem Problem durch. Eine Alternative wäre, Emailer-Plugins von dritten wie etwa SendGrid, Mailgun usw. zu verwenden. Verfügbare Plugins durchsuchen.", "smtp-transport.host": "SMTP Host", "smtp-transport.port": "SMTP Port", - "smtp-transport.security": "Connection security", - "smtp-transport.security-encrypted": "Encrypted", + "smtp-transport.security": "Verbindungssicherheit", + "smtp-transport.security-encrypted": "Verschlüsselt", "smtp-transport.security-starttls": "StartTLS", - "smtp-transport.security-none": "None", + "smtp-transport.security-none": "Keine", "smtp-transport.username": "Benutzername", "smtp-transport.username-help": "Bitte füge die komplette Email-Adresse für Gmail hier ein, insbesondere wenn du eine von Google Apps gemanagete domain verwendest.", "smtp-transport.password": "Passwort", diff --git a/public/language/de/email.json b/public/language/de/email.json index dcd3eb4fa3..b897966610 100644 --- a/public/language/de/email.json +++ b/public/language/de/email.json @@ -31,7 +31,7 @@ "notif.post.cta": "Hier klicken, um das gesamte Thema zu lesen", "notif.post.unsub.info": "Diese Mitteilung wurde dir aufgrund deiner Abonnement-Einstellungen gesendet.", "test.text1": "Dies ist eine Test-E-Mail, um zu überprüfen, ob der E-Mailer deines NodeBB korrekt eingestellt wurde.", - "unsub.cta": "Klicke hier, um diese Einstellungen zu ändern.", + "unsub.cta": "Klicke hier, um diese Einstellungen zu ändern", "banned.subject": "Du wurdest von %1 gebannt.", "banned.text1": "Der Benutzer %1 wurde von %2 gebannt.", "banned.text2": "Dieser Bann wird bis %1 dauern.", diff --git a/public/language/th/admin/extend/plugins.json b/public/language/th/admin/extend/plugins.json index 4b40bbe720..f5b80b31b4 100644 --- a/public/language/th/admin/extend/plugins.json +++ b/public/language/th/admin/extend/plugins.json @@ -17,27 +17,27 @@ "order.description": "Certain plugins work ideally when they are initialised before/after other plugins.", "order.explanation": "Plugins load in the order specified here, from top to bottom", - "plugin-item.themes": "Themes", - "plugin-item.deactivate": "Deactivate", - "plugin-item.activate": "Activate", - "plugin-item.install": "Install", - "plugin-item.uninstall": "Uninstall", - "plugin-item.settings": "Settings", - "plugin-item.installed": "Installed", - "plugin-item.latest": "Latest", - "plugin-item.upgrade": "Upgrade", - "plugin-item.more-info": "For more information:", - "plugin-item.unknown": "Unknown", + "plugin-item.themes": "ธีม", + "plugin-item.deactivate": "ปิดการใช้งาน", + "plugin-item.activate": "เปิดการใช้งาน", + "plugin-item.install": "ติดตั้ง", + "plugin-item.uninstall": "ถอนการติดตั้ง", + "plugin-item.settings": "ตั้งค่า", + "plugin-item.installed": "ติดตั้งแล้ว", + "plugin-item.latest": "ล่าสุด", + "plugin-item.upgrade": "อัพเกรด", + "plugin-item.more-info": "ข้อมูลเพิ่มเติม:", + "plugin-item.unknown": "ไม่ทราบ", "plugin-item.unknown-explanation": "The state of this plugin could not be determined, possibly due to a misconfiguration error.", "alert.enabled": "Plugin Enabled", "alert.disabled": "Plugin Disabled", - "alert.upgraded": "Plugin Upgraded", - "alert.installed": "Plugin Installed", - "alert.uninstalled": "Plugin Uninstalled", - "alert.activate-success": "Please restart your NodeBB to fully activate this plugin", - "alert.deactivate-success": "Plugin successfully deactivated", - "alert.upgrade-success": "Please reload your NodeBB to fully upgrade this plugin", + "alert.upgraded": "อัพเกรดปลั๊กอินแล้ว", + "alert.installed": "ติดตั้งปลั๊กอินแล้ว", + "alert.uninstalled": "ถอนการติดตั้งปลั๊กอินแล้ว", + "alert.activate-success": "โปรดรีสตาร์ท NodeBB ของคุณเพื่อเปิดการทำงานของธีมนี้", + "alert.deactivate-success": "ปิดการใช้งานปลั๊กอินนี้แล้ว", + "alert.upgrade-success": "โปรดรีสตาร์ท NodeBB ของคุณเพื่ออัพเกรดปลั๊กอินนี้", "alert.install-success": "Plugin successfully installed, please activate the plugin.", "alert.uninstall-success": "The plugin has been successfully deactivated and uninstalled.", "alert.suggest-error": "

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

    Server returned (%1): %2
    ", @@ -45,7 +45,7 @@ "alert.incompatible": "

    Your version of NodeBB (v%1) is only cleared to upgrade to v%2 of this plugin. Please update your NodeBB if you wish to install a newer version of this plugin.

    ", "alert.possibly-incompatible": "

    No Compatibility Information Found

    This plugin did not specify a specific version for installation given your NodeBB version. Full compatibility cannot be guaranteed, and may cause your NodeBB to no longer start properly.

    In the event that NodeBB cannot boot properly:

    $ ./nodebb reset plugin=\"%1\"

    Continue installation of latest version of this plugin?

    ", - "license.title": "Plugin License Information", + "license.title": "ข้อมูลลิขสิทธิ์ปลั๊กอิน", "license.intro": "The plugin %1 is licensed under the %2. Please read and understand the license terms prior to activating this plugin.", "license.cta": "Do you wish to continue with activating this plugin?" } diff --git a/public/language/th/admin/general/homepage.json b/public/language/th/admin/general/homepage.json index 4866b8baf6..f195d37850 100644 --- a/public/language/th/admin/general/homepage.json +++ b/public/language/th/admin/general/homepage.json @@ -1,5 +1,5 @@ { - "home-page": "Home Page", + "home-page": "หน้าแรก", "description": "Choose what page is shown when users navigate to the root URL of your forum.", "home-page-route": "Home Page Route", "custom-route": "Custom Route", diff --git a/public/language/th/admin/settings/chat.json b/public/language/th/admin/settings/chat.json index 0b22127341..420d6e5ebe 100644 --- a/public/language/th/admin/settings/chat.json +++ b/public/language/th/admin/settings/chat.json @@ -1,9 +1,9 @@ { - "chat-settings": "Chat Settings", - "disable": "Disable chat", - "disable-editing": "Disable chat message editing/deletion", + "chat-settings": "ตั้งค่าแชท", + "disable": "ปิดการใช้งานแชท", + "disable-editing": "ปิดการแก้ไข และการลบแชท", "disable-editing-help": "Administrators and global moderators are exempt from this restriction", - "max-length": "Maximum length of chat messages", - "max-room-size": "Maximum number of users in chat rooms", + "max-length": "จำนวนอักขระมากที่มากที่สุดต่อแชท", + "max-room-size": "จำนวนผู้ใช้ในห้องแชทมากที่สุด", "delay": "Time between chat messages in milliseconds" } \ No newline at end of file diff --git a/public/language/th/error.json b/public/language/th/error.json index 08176c6b7e..0960ffd360 100644 --- a/public/language/th/error.json +++ b/public/language/th/error.json @@ -1,10 +1,10 @@ { "invalid-data": "ข้อมูลไม่ถูกต้อง", - "invalid-json": "Invalid JSON", + "invalid-json": "รูปแบบ JSON ไม่ถูกต้อง", "not-logged-in": "คุณยังไม่ได้ลงชื่อเข้าระบบ", "account-locked": "บัญชีของคุณถูกระงับการใช้งานชั่วคราว", "search-requires-login": "\"ฟังก์ชั่นการค้นหา\" ต้องการบัญชีผู้ใช้ กรุณาเข้าสู่ระบบหรือสมัครสมาชิก", - "goback": "Press back to return to the previous page", + "goback": "กดย้อนกลับเพื่อกลับไปยังหน้าที่แล้ว", "invalid-cid": "Category ID ไม่ถูกต้อง", "invalid-tid": "Topic ID ไม่ถูกต้อง", "invalid-pid": "Post ID ไม่ถูกต้อง", @@ -14,7 +14,7 @@ "invalid-title": "คำนำหน้าชื่อไม่ถูกต้อง", "invalid-user-data": "User Data ไม่ถูกต้อง", "invalid-password": "รหัสผ่านไม่ถูกต้อง", - "invalid-login-credentials": "Invalid login credentials", + "invalid-login-credentials": "session login หมดอายุ", "invalid-username-or-password": "กรุณาระบุชื่อผู้ใช้และรหัสผ่าน", "invalid-search-term": "ข้อความค้นหาไม่ถูกต้อง", "csrf-invalid": "เราไม่สามารถนำท่านเข้าสู่ระบบได้ เหมือนกับว่าเซสชั่นหมดอายุแล้ว กรุณาลองใหม่อีกครั้ง", diff --git a/public/language/th/global.json b/public/language/th/global.json index ac6b834e34..d5faf08079 100644 --- a/public/language/th/global.json +++ b/public/language/th/global.json @@ -104,6 +104,6 @@ "cookies.accept": "เข้าใจแล้ว!!! ", "cookies.learn_more": "เรียนรู้เพิ่มเติม", "edited": "ถูกแก้ไขแล้ว", - "disabled": "Disabled", - "select": "Select" + "disabled": "ปิด", + "select": "เลือก" } \ No newline at end of file diff --git a/public/language/th/notifications.json b/public/language/th/notifications.json index df09ed07f0..d40f9af8ef 100644 --- a/public/language/th/notifications.json +++ b/public/language/th/notifications.json @@ -41,7 +41,7 @@ "new_register": "%1ได้ส่งคำขอสมัครสมาชิก", "new_register_multiple": "มี%1คำขอสมัครสมาชิกที่รอการรีวิว", "flag_assigned_to_you": "ปักธง %1ได้ถูกปักธงให้คุณ", - "post_awaiting_review": "Post awaiting review", + "post_awaiting_review": "โพสกำลังรอการพิจารณา", "email-confirmed": "Email ได้รับการยืนยันแล้ว", "email-confirmed-message": "ขอบคุณที่ยืนยัน Email ของคุณ บัญชีของคุณสามารถใช้งานได้แล้ว", "email-confirm-error-message": "มีปัญหาในการยืนยัน Email ของคุณ บางทีรหัสไม่ถูกต้องหรือหมดอายุแล้ว", diff --git a/public/language/th/pages.json b/public/language/th/pages.json index dcdb8194b3..9706c2b715 100644 --- a/public/language/th/pages.json +++ b/public/language/th/pages.json @@ -6,10 +6,10 @@ "popular-month": "กระทู้ฮิตเดือนนี้", "popular-alltime": "กระทู้ฮิตตลาดกาล", "recent": "กระทู้ล่าสุด", - "moderator-tools": "Moderator Tools", + "moderator-tools": "เครื่องมือผู้ดูแลระบบ", "flagged-content": "เนื้อหาที่ถูกปักธง", "ip-blacklist": "ไอดีที่ถูกขึ้นบัญชีดำ", - "post-queue": "Post Queue", + "post-queue": "คิวโพส", "users/online": "ผู้ใช้ออนไลน์", "users/latest": "ผู้ใช้ล่าสุด", "users/sort-posts": "ผู้ใช้ที่โพสต์เยอะที่สุด", diff --git a/public/language/th/success.json b/public/language/th/success.json index b79ab75c71..3173db70a8 100644 --- a/public/language/th/success.json +++ b/public/language/th/success.json @@ -1,7 +1,7 @@ { "success": "สำเร็จ", "topic-post": "คุณลงข้อความสำเร็จแล้ว", - "post-queued": "Your post is queued for approval.", + "post-queued": "โพสของคุณกำลังรอการอนุมัติ", "authentication-successful": "การระบุตัวตนสำเร็จแล้ว", "settings-saved": "การตั้งค่าได้ถูกบันทึกแล้ว" } \ No newline at end of file diff --git a/public/language/th/topic.json b/public/language/th/topic.json index 82108b7a0f..69e1b0b54f 100644 --- a/public/language/th/topic.json +++ b/public/language/th/topic.json @@ -14,7 +14,7 @@ "quote": "คำอ้างอิง", "reply": "ตอบ", "replies_to_this_post": " %1 คำตอบ", - "one_reply_to_this_post": "1 Reply", + "one_reply_to_this_post": "1 การตอบกลับ", "last_reply_time": "คำตอบล่าสุด", "reply-as-topic": "ตอบโดยตั้งกระทู้ใหม่", "guest-login-reply": "เข้าสู่ระบบเพื่อตอบกลับ", @@ -59,7 +59,7 @@ "thread_tools.unlock": "ปลดล็อคกระทู้", "thread_tools.move": "ย้ายกระทู้", "thread_tools.move_all": "ย้ายทั้งหมด", - "thread_tools.select_category": "Select Category", + "thread_tools.select_category": "เลือกประเภท", "thread_tools.fork": "แยกกระทู้", "thread_tools.delete": "ลบกระทู้", "thread_tools.delete-posts": "ลบโพสต์", From 79598c32d3015bedc2d99d81ff6591ed01dcd8c5 Mon Sep 17 00:00:00 2001 From: Baris Usakli Date: Thu, 19 Oct 2017 12:56:51 -0400 Subject: [PATCH 09/11] remove unused function --- src/languages.js | 4 ---- src/webserver.js | 1 - 2 files changed, 5 deletions(-) diff --git a/src/languages.js b/src/languages.js index 5d49e60f53..77945bacc6 100644 --- a/src/languages.js +++ b/src/languages.js @@ -7,10 +7,6 @@ var async = require('async'); var Languages = module.exports; var languagesPath = path.join(__dirname, '../build/public/language'); -Languages.init = function (next) { - next(); -}; - Languages.get = function (language, namespace, callback) { fs.readFile(path.join(languagesPath, language, namespace + '.json'), { encoding: 'utf-8' }, function (err, data) { if (err) { diff --git a/src/webserver.js b/src/webserver.js index 6f89ea8570..2564642ade 100644 --- a/src/webserver.js +++ b/src/webserver.js @@ -105,7 +105,6 @@ function initializeNodeBB(callback) { function (next) { async.series([ meta.sounds.addUploads, - languages.init, meta.blacklist.load, flags.init, ], next); From 8733f51f86204a455c23c5bd22fcb5cb73e1356d Mon Sep 17 00:00:00 2001 From: Baris Usakli Date: Thu, 19 Oct 2017 13:13:40 -0400 Subject: [PATCH 10/11] remove test for unused code --- test/translator.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/translator.js b/test/translator.js index 4f5ce04a41..62aa2dcea1 100644 --- a/test/translator.js +++ b/test/translator.js @@ -6,8 +6,6 @@ var shim = require('../public/src/modules/translator.js'); var Translator = shim.Translator; var db = require('./mocks/databasemock'); -require('../src/languages').init(function () {}); - describe('Translator shim', function () { describe('.translate()', function () { it('should translate correctly', function (done) { From 19dc7da42f51e50e3c71dcd246b59caea3a4a10e Mon Sep 17 00:00:00 2001 From: Baris Usakli Date: Thu, 19 Oct 2017 13:53:05 -0400 Subject: [PATCH 11/11] closes #5994 --- public/language/en-GB/unread.json | 3 +- src/controllers/helpers.js | 26 ++++++++++++++ src/controllers/popular.js | 60 +++++++++++++++---------------- src/controllers/recent.js | 22 ++---------- src/controllers/unread.js | 25 +++---------- src/topics/recent.js | 2 ++ src/topics/unread.js | 16 +++++++++ 7 files changed, 83 insertions(+), 71 deletions(-) diff --git a/public/language/en-GB/unread.json b/public/language/en-GB/unread.json index 4a449a53f5..74634f9080 100644 --- a/public/language/en-GB/unread.json +++ b/public/language/en-GB/unread.json @@ -9,5 +9,6 @@ "topics_marked_as_read.success": "Topics marked as read!", "all-topics": "All Topics", "new-topics": "New Topics", - "watched-topics": "Watched Topics" + "watched-topics": "Watched Topics", + "unreplied-topics": "Unreplied Topics" } \ No newline at end of file diff --git a/src/controllers/helpers.js b/src/controllers/helpers.js index ee0ba75482..70de194323 100644 --- a/src/controllers/helpers.js +++ b/src/controllers/helpers.js @@ -32,6 +32,32 @@ helpers.noScriptErrors = function (req, res, error, httpStatus) { }); }; +helpers.validFilters = { '': true, new: true, watched: true, unreplied: true }; + +helpers.buildFilters = function (url, filter) { + return [{ + name: '[[unread:all-topics]]', + url: url, + selected: filter === '', + filter: '', + }, { + name: '[[unread:new-topics]]', + url: url + '/new', + selected: filter === 'new', + filter: 'new', + }, { + name: '[[unread:watched-topics]]', + url: url + '/watched', + selected: filter === 'watched', + filter: 'watched', + }, { + name: '[[unread:unreplied-topics]]', + url: url + '/unreplied', + selected: filter === 'unreplied', + filter: 'unreplied', + }]; +}; + helpers.notAllowed = function (req, res, error) { plugins.fireHook('filter:helpers.notAllowed', { req: req, diff --git a/src/controllers/popular.js b/src/controllers/popular.js index 21c07224c1..99af5baf19 100644 --- a/src/controllers/popular.js +++ b/src/controllers/popular.js @@ -1,12 +1,13 @@ 'use strict'; +var async = require('async'); var nconf = require('nconf'); var topics = require('../topics'); var meta = require('../meta'); var helpers = require('./helpers'); -var popularController = {}; +var popularController = module.exports; var anonCache = {}; var lastUpdateTime = 0; @@ -38,36 +39,35 @@ popularController.get = function (req, res, next) { } } - topics.getPopular(term, req.uid, meta.config.topicsPerList, function (err, topics) { - if (err) { - return next(err); - } - - var data = { - topics: topics, - 'feeds:disableRSS': parseInt(meta.config['feeds:disableRSS'], 10) === 1, - rssFeedUrl: nconf.get('relative_path') + '/popular/' + (req.params.term || 'daily') + '.rss', - title: '[[pages:popular-' + term + ']]', - term: term, - }; - - if (req.path.startsWith('/api/popular') || req.path.startsWith('/popular')) { - var breadcrumbs = [{ text: termToBreadcrumb[term] }]; - - if (req.params.term) { - breadcrumbs.unshift({ text: '[[global:header.popular]]', url: '/popular' }); + async.waterfall([ + function (next) { + topics.getPopular(term, req.uid, meta.config.topicsPerList, next); + }, + function (topics) { + var data = { + topics: topics, + 'feeds:disableRSS': parseInt(meta.config['feeds:disableRSS'], 10) === 1, + rssFeedUrl: nconf.get('relative_path') + '/popular/' + (req.params.term || 'daily') + '.rss', + title: '[[pages:popular-' + term + ']]', + term: term, + }; + + if (req.path.startsWith('/api/popular') || req.path.startsWith('/popular')) { + var breadcrumbs = [{ text: termToBreadcrumb[term] }]; + + if (req.params.term) { + breadcrumbs.unshift({ text: '[[global:header.popular]]', url: '/popular' }); + } + + data.breadcrumbs = helpers.buildBreadcrumbs(breadcrumbs); } - data.breadcrumbs = helpers.buildBreadcrumbs(breadcrumbs); - } - - if (!req.uid) { - anonCache[term] = data; - lastUpdateTime = Date.now(); - } + if (!req.uid) { + anonCache[term] = data; + lastUpdateTime = Date.now(); + } - res.render('popular', data); - }); + res.render('popular', data); + }, + ], next); }; - -module.exports = popularController; diff --git a/src/controllers/recent.js b/src/controllers/recent.js index 7d95c2db2b..3bb337d2f0 100644 --- a/src/controllers/recent.js +++ b/src/controllers/recent.js @@ -13,8 +13,6 @@ var pagination = require('../pagination'); var recentController = module.exports; -var validFilter = { '': true, new: true, watched: true }; - recentController.get = function (req, res, next) { var page = parseInt(req.query.page, 10) || 1; var stop = 0; @@ -23,7 +21,8 @@ recentController.get = function (req, res, next) { var filter = req.params.filter || ''; var categoryData; var rssToken; - if (!validFilter[filter]) { + + if (!helpers.validFilters[filter]) { return next(); } @@ -62,22 +61,7 @@ recentController.get = function (req, res, next) { data.rssFeedUrl += '?uid=' + req.uid + '&token=' + rssToken; } data.title = '[[pages:recent]]'; - data.filters = [{ - name: '[[unread:all-topics]]', - url: 'recent', - selected: filter === '', - filter: '', - }, { - name: '[[unread:new-topics]]', - url: 'recent/new', - selected: filter === 'new', - filter: 'new', - }, { - name: '[[unread:watched-topics]]', - url: 'recent/watched', - selected: filter === 'watched', - filter: 'watched', - }]; + data.filters = helpers.buildFilters('recent', filter); data.selectedFilter = data.filters.find(function (filter) { return filter && filter.selected; diff --git a/src/controllers/unread.js b/src/controllers/unread.js index 9308e87251..a3ee68192d 100644 --- a/src/controllers/unread.js +++ b/src/controllers/unread.js @@ -13,8 +13,6 @@ var helpers = require('./helpers'); var unreadController = module.exports; -var validFilter = { '': true, new: true, watched: true }; - unreadController.get = function (req, res, next) { var page = parseInt(req.query.page, 10) || 1; var results; @@ -24,7 +22,7 @@ unreadController.get = function (req, res, next) { async.waterfall([ function (next) { - plugins.fireHook('filter:unread.getValidFilters', { filters: validFilter }, next); + plugins.fireHook('filter:unread.getValidFilters', { filters: Object.assign({}, helpers.validFilters) }, next); }, function (data, _next) { if (!data.filters[filter]) { @@ -72,22 +70,7 @@ unreadController.get = function (req, res, next) { } data.title = '[[pages:unread]]'; - data.filters = [{ - name: '[[unread:all-topics]]', - url: 'unread', - selected: filter === '', - filter: '', - }, { - name: '[[unread:new-topics]]', - url: 'unread/new', - selected: filter === 'new', - filter: 'new', - }, { - name: '[[unread:watched-topics]]', - url: 'unread/watched', - selected: filter === 'watched', - filter: 'watched', - }]; + data.filters = helpers.buildFilters('unread', filter); data.selectedFilter = data.filters.find(function (filter) { return filter && filter.selected; @@ -105,10 +88,10 @@ unreadController.unreadTotal = function (req, res, next) { async.waterfall([ function (next) { - plugins.fireHook('filter:unread.getValidFilters', { filters: validFilter }, next); + plugins.fireHook('filter:unread.getValidFilters', { filters: Object.assign({}, helpers.validFilters) }, next); }, function (data, _next) { - if (!validFilter[filter]) { + if (!data.filters[filter]) { return next(); } topics.getTotalUnread(req.uid, filter, _next); diff --git a/src/topics/recent.js b/src/topics/recent.js index 7242f6bb06..c5dce9209f 100644 --- a/src/topics/recent.js +++ b/src/topics/recent.js @@ -61,6 +61,8 @@ module.exports = function (Topics) { Topics.filterWatchedTids(tids, uid, next); } else if (filter === 'new') { Topics.filterNewTids(tids, uid, next); + } else if (filter === 'unreplied') { + Topics.filterUnrepliedTids(tids, next); } else { Topics.filterNotIgnoredTids(tids, uid, next); } diff --git a/src/topics/unread.js b/src/topics/unread.js index 4a46e4c0c9..6abad117ef 100644 --- a/src/topics/unread.js +++ b/src/topics/unread.js @@ -125,6 +125,8 @@ module.exports = function (Topics) { if (params.filter === 'watched') { Topics.filterWatchedTids(tids, uid, next); + } else if (params.filter === 'unreplied') { + Topics.filterUnrepliedTids(tids, next); } else { next(null, tids); } @@ -390,4 +392,18 @@ module.exports = function (Topics) { }, ], callback); }; + + Topics.filterUnrepliedTids = function (tids, callback) { + async.waterfall([ + function (next) { + db.sortedSetScores('topics:posts', tids, next); + }, + function (scores, next) { + tids = tids.filter(function (tid, index) { + return tid && scores[index] <= 1; + }); + next(null, tids); + }, + ], callback); + }; };