diff --git a/.eslintrc b/.eslintrc index 52211a126d..bbba3aa94e 100644 --- a/.eslintrc +++ b/.eslintrc @@ -32,6 +32,7 @@ "no-prototype-builtins": "off", "new-cap": "off", "no-plusplus": ["error", { "allowForLoopAfterthoughts": true }], + "import/no-unresolved": "error", // ES6 "prefer-rest-params": "off", @@ -43,7 +44,6 @@ "vars-on-top": "off", // TODO - "import/no-unresolved": "off", "import/no-extraneous-dependencies": "off", "import/no-dynamic-require": "off", "import/newline-after-import": "off", diff --git a/app.js b/app.js index de9d417045..5a6a782c57 100644 --- a/app.js +++ b/app.js @@ -19,6 +19,12 @@ 'use strict'; +if (require.main !== module) { + require.main.require = function (path) { + return require(path); + }; +} + var nconf = require('nconf'); nconf.argv().env('__'); diff --git a/loader.js b/loader.js index 654d77fb23..214f785eb9 100644 --- a/loader.js +++ b/loader.js @@ -7,6 +7,7 @@ var path = require('path'); var fork = require('child_process').fork; var async = require('async'); var logrotate = require('logrotate-stream'); + var file = require('./src/file'); var pkg = require('./package.json'); @@ -23,6 +24,7 @@ var workers = []; var Loader = { timesStarted: 0, }; +var appPath = path.join(__dirname, 'app.js'); Loader.init = function (callback) { if (silent) { @@ -114,7 +116,7 @@ function forkWorker(index, isPrimary) { process.env.isCluster = ports.length > 1; process.env.port = ports[index]; - var worker = fork('app.js', args, { + var worker = fork(appPath, args, { silent: silent, env: process.env, }); diff --git a/nodebb b/nodebb index ee87b24288..757afabf40 100755 --- a/nodebb +++ b/nodebb @@ -2,16 +2,25 @@ 'use strict'; +var cproc; +var args; +var fs; +var path; +var request; +var semver; +var prompt; +var async; + try { require('colors'); - var cproc = require('child_process'); - var args = require('minimist')(process.argv.slice(2)); - var fs = require('fs'); - var path = require('path'); - var request = require('request'); - var semver = require('semver'); - var prompt = require('prompt'); - var async = require('async'); + cproc = require('child_process'); + args = require('minimist')(process.argv.slice(2)); + fs = require('fs'); + path = require('path'); + request = require('request'); + semver = require('semver'); + prompt = require('prompt'); + async = require('async'); } catch (e) { if (e.code === 'MODULE_NOT_FOUND') { process.stdout.write('NodeBB could not be started because it\'s dependencies have not been installed.\n'); @@ -23,13 +32,16 @@ try { } } +var loaderPath = path.join(__dirname, 'loader.js'); +var appPath = path.join(__dirname, 'app.js'); + if (args.dev) { process.env.NODE_ENV = 'development'; } function getRunningPid(callback) { - fs.readFile(__dirname + '/pidfile', { - encoding: 'utf-8' + fs.readFile(path.join(__dirname, 'pidfile'), { + encoding: 'utf-8', }, function (err, pid) { if (err) { return callback(err); @@ -38,7 +50,7 @@ function getRunningPid(callback) { try { process.kill(parseInt(pid, 10), 0); callback(null, parseInt(pid, 10)); - } catch(e) { + } catch (e) { callback(e); } }); @@ -52,28 +64,29 @@ function getCurrentVersion(callback) { try { pkg = JSON.parse(pkg); return callback(null, pkg.version); - } catch(err) { + } catch (err) { return callback(err); } }); } function fork(args) { - return cproc.fork('app.js', args, { + return cproc.fork(appPath, args, { cwd: __dirname, - silent: false + silent: false, }); } function getInstalledPlugins(callback) { async.parallel({ files: async.apply(fs.readdir, path.join(__dirname, 'node_modules')), - deps: async.apply(fs.readFile, path.join(__dirname, 'package.json'), { encoding: 'utf-8' }) + deps: async.apply(fs.readFile, path.join(__dirname, 'package.json'), { encoding: 'utf-8' }), }, function (err, payload) { if (err) { return callback(err); } - var isNbbModule = /^nodebb-(?:plugin|theme|widget|rewards)-[\w\-]+$/, - moduleName, isGitRepo; + var isNbbModule = /^nodebb-(?:plugin|theme|widget|rewards)-[\w-]+$/; + var moduleName; + var isGitRepo; payload.files = payload.files.filter(function (file) { return isNbbModule.test(file); @@ -98,7 +111,7 @@ function getInstalledPlugins(callback) { try { fs.accessSync(path.join(__dirname, 'node_modules/' + moduleName, '.git')); isGitRepo = true; - } catch(e) { + } catch (e) { isGitRepo = false; } @@ -144,7 +157,7 @@ function checkPlugins(standalone, callback) { async.waterfall([ async.apply(async.parallel, { plugins: async.apply(getInstalledPlugins), - version: async.apply(getCurrentVersion) + version: async.apply(getCurrentVersion), }), function (payload, next) { var toCheck = Object.keys(payload.plugins); @@ -157,7 +170,7 @@ function checkPlugins(standalone, callback) { request({ method: 'GET', url: 'https://packages.nodebb.org/api/v1/suggest?version=' + payload.version + '&package[]=' + toCheck.join('&package[]='), - json: true + json: true, }, function (err, res, body) { if (err) { process.stdout.write('error'.red + '\n'.reset); @@ -169,25 +182,25 @@ function checkPlugins(standalone, callback) { body = [body]; } - var current, suggested, - upgradable = body.map(function (suggestObj) { - current = payload.plugins[suggestObj.package]; - suggested = suggestObj.version; - - if (suggestObj.code === 'match-found' && semver.gt(suggested, current)) { - return { - name: suggestObj.package, - current: current, - suggested: suggested - }; - } else { - return null; - } - }).filter(Boolean); + var current; + var suggested; + var upgradable = body.map(function (suggestObj) { + current = payload.plugins[suggestObj.package]; + suggested = suggestObj.version; + + if (suggestObj.code === 'match-found' && semver.gt(suggested, current)) { + return { + name: suggestObj.package, + current: current, + suggested: suggested, + }; + } + return null; + }).filter(Boolean); next(null, upgradable); }); - } + }, ], callback); } function upgradePlugins(callback) { @@ -199,7 +212,7 @@ function upgradePlugins(callback) { checkPlugins(standalone, function (err, found) { if (err) { - process.stdout.write('\Warning'.yellow + ': An unexpected error occured when attempting to verify plugin upgradability\n'.reset); + process.stdout.write('Warning'.yellow + ': An unexpected error occured when attempting to verify plugin upgradability\n'.reset); return callback(err); } @@ -223,7 +236,7 @@ function upgradePlugins(callback) { prompt.get({ name: 'upgrade', description: 'Proceed with upgrade (y|n)?'.reset, - type: 'string' + type: 'string', }, function (err, result) { if (err) { return callback(err); @@ -279,8 +292,8 @@ var commands = { process.stdout.write(' "' + './nodebb restart'.yellow + '" to restart NodeBB\n\n'.reset); // Spawn a new NodeBB process - cproc.fork(__dirname + '/loader.js', { - env: process.env + cproc.fork(loaderPath, { + env: process.env, }); }, }, @@ -320,7 +333,7 @@ var commands = { process.stdout.write('\n\n'.reset); cproc.spawn('tail', ['-F', './logs/output.log'], { cwd: __dirname, - stdio: 'inherit' + stdio: 'inherit', }); }, }, @@ -333,12 +346,12 @@ var commands = { process.stdout.write('\n\n'.reset); // Spawn a new NodeBB process - cproc.fork(__dirname + '/loader.js', { - env: process.env + cproc.fork(loaderPath, { + env: process.env, }); cproc.spawn('tail', ['-F', './logs/output.log'], { cwd: __dirname, - stdio: 'inherit' + stdio: 'inherit', }); }, }, @@ -347,14 +360,14 @@ var commands = { usage: 'Usage: ' + './nodebb dev'.yellow, handler: function () { process.env.NODE_ENV = 'development'; - cproc.fork(__dirname + '/loader.js', ['--no-daemon', '--no-silent'], { - env: process.env + cproc.fork(loaderPath, ['--no-daemon', '--no-silent'], { + env: process.env, }); }, }, build: { description: 'Compile static assets (CSS, Javascript, etc)', - usage: 'Usage: ' + './nodebb build'.yellow + ' [js,clientCSS,acpCSS,tpl,lang]'.red + '\n' + + usage: 'Usage: ' + './nodebb build'.yellow + ' [js,clientCSS,acpCSS,tpl,lang]'.red + '\n' + ' e.g. ' + './nodebb build js,tpl'.yellow + '\tbuilds JS and templates\n' + ' ' + './nodebb build'.yellow + '\t\tbuilds all targets\n', handler: function () { @@ -422,7 +435,7 @@ var commands = { var upgradeProc = fork(arr); upgradeProc.on('close', next); - } + }, ], function (err) { if (err) { process.stdout.write('\nError'.red + ': ' + err.message + '\n'); @@ -430,7 +443,7 @@ var commands = { var message = 'NodeBB Upgrade Complete!'; // some consoles will return undefined/zero columns, so just use 2 spaces in upgrade script if we can't get our column count var columns = process.stdout.columns; - var spaces = columns ? new Array(Math.floor(columns / 2) - (message.length / 2) + 1).join(' ') : " "; + var spaces = columns ? new Array(Math.floor(columns / 2) - (message.length / 2) + 1).join(' ') : ' '; process.stdout.write('OK\n'.green); process.stdout.write('\n' + spaces + message.green.bold + '\n\n'.reset); diff --git a/package.json b/package.json index 828165182b..3c0bbb20d5 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "main": "app.js", "scripts": { "start": "node loader.js", - "lint": "eslint --cache .", + "lint": "eslint --cache ./nodebb .", "pretest": "npm run lint", "test": "istanbul cover node_modules/mocha/bin/_mocha -- -R dot", "coveralls": "istanbul cover _mocha --report lcovonly -- -R dot && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage" diff --git a/public/language/hu/global.json b/public/language/hu/global.json index ea60f7dd9b..c05367f938 100644 --- a/public/language/hu/global.json +++ b/public/language/hu/global.json @@ -61,7 +61,7 @@ "reputation": "Hírnév", "read_more": "tovább olvas", "more": "Több", - "posted_ago_by_guest": "Vendég hozzászólás %1", + "posted_ago_by_guest": "%1 vendég hozzászólás", "posted_ago_by": "%2 hozzászólás %1", "posted_ago": "%1 hozzászólás", "posted_in": "hozzászólt itt: %1", @@ -103,5 +103,5 @@ "cookies.message": "A weboldal sütiket használ, a legjobb weboldalas élmény érdekében.", "cookies.accept": "Értem!", "cookies.learn_more": "Tudnivalók", - "edited": "Edited" + "edited": "Szerkesztett" } \ No newline at end of file diff --git a/public/language/hu/recent.json b/public/language/hu/recent.json index bddd57893f..95004a40a6 100644 --- a/public/language/hu/recent.json +++ b/public/language/hu/recent.json @@ -4,7 +4,7 @@ "week": "Hét", "month": "Hónap", "year": "Év", - "alltime": "Minden idők", + "alltime": "Bármikor", "no_recent_topics": "Nincs friss témakör.", "no_popular_topics": "Nincs népszerű témakör.", "there-is-a-new-topic": "Van egy új témakör.", diff --git a/public/language/nl/global.json b/public/language/nl/global.json index 73eb607f24..f18dccae8d 100644 --- a/public/language/nl/global.json +++ b/public/language/nl/global.json @@ -103,5 +103,5 @@ "cookies.message": "Deze website gebruikt cookies om je ervan te verzekeren dat je de beste ervaring krijgt tijdens het gebruik van onze website.", "cookies.accept": "Begrepen", "cookies.learn_more": "Meer", - "edited": "Edited" + "edited": "Bewerkt" } \ No newline at end of file diff --git a/public/language/nl/groups.json b/public/language/nl/groups.json index 912765ec55..0cf86d03a7 100644 --- a/public/language/nl/groups.json +++ b/public/language/nl/groups.json @@ -27,7 +27,7 @@ "details.disableJoinRequests": "Groepsverzoeken uitschakelen", "details.grant": "Toekennen/herroepen van eigendom", "details.kick": "Kick", - "details.kick_confirm": "Are you sure you want to remove this member from the group?", + "details.kick_confirm": "Weet u zeker dat u de gebruiker wilt verwijderen uit de groep?", "details.owner_options": "Groepsadministratie", "details.group_name": "Groepsnaam", "details.member_count": "Ledentelling", @@ -54,5 +54,5 @@ "upload-group-cover": "Upload groepscover", "bulk-invite-instructions": "Vul een lijst is met gebruikersnamen gescheiden met komma's om deze uit te nodigen voor deze groep", "bulk-invite": "Massa uitnodiging", - "remove_group_cover_confirm": "Are you sure you want to remove the cover picture?" + "remove_group_cover_confirm": "Weet u zeker dat u de cover foto wilt verwijderen?" } \ No newline at end of file diff --git a/public/language/nl/modules.json b/public/language/nl/modules.json index 34c8925ae7..59d0396464 100644 --- a/public/language/nl/modules.json +++ b/public/language/nl/modules.json @@ -13,7 +13,7 @@ "chat.contacts": "Contacten", "chat.message-history": "Berichtengeschiedenis", "chat.pop-out": "Chatvenster opbrengen bij chat", - "chat.minimize": "Minimize", + "chat.minimize": "Verkleinen", "chat.maximize": "Maximaliseren", "chat.seven_days": "7 dagen", "chat.thirty_days": "30 dagen", diff --git a/public/language/nl/notifications.json b/public/language/nl/notifications.json index a15226841a..29b4234fb7 100644 --- a/public/language/nl/notifications.json +++ b/public/language/nl/notifications.json @@ -10,14 +10,14 @@ "return_to": "Terug naar %1", "new_notification": "Nieuwe notificatie", "you_have_unread_notifications": "Je hebt nieuwe notificaties.", - "all": "All", - "topics": "Topics", - "replies": "Replies", + "all": "Alles", + "topics": "Onderwerpen", + "replies": "Antwoorden", "chat": "Chats", - "follows": "Follows", + "follows": "Volgt", "upvote": "Upvotes", - "new-flags": "New Flags", - "my-flags": "Flags assigned to me", + "new-flags": "Nieuwe markeringen", + "my-flags": "Markeringen toegewezen aan mij", "bans": "Bans", "new_message_from": "Nieuw bericht van %1", "upvoted_your_post_in": "%1 heeft voor een bericht gestemd in %2.", @@ -28,9 +28,9 @@ "user_flagged_post_in": "%1 rapporteerde een bericht in %2", "user_flagged_post_in_dual": "%1 en %2 rapporteerde een bericht in %3", "user_flagged_post_in_multiple": "%1 en %2 andere rapporteede een bericht in %3", - "user_flagged_user": "%1 flagged a user profile (%2)", - "user_flagged_user_dual": "%1 and %2 flagged a user profile (%3)", - "user_flagged_user_multiple": "%1 and %2 others flagged a user profile (%3)", + "user_flagged_user": "%1 markeerde een gebruikersprofiel (%2)", + "user_flagged_user_dual": "%1 en %2 markeerden een gebruikersprofiel (%3)", + "user_flagged_user_multiple": "%1 en %2 anderen markeerde een gebruikersprofiel (%3)", "user_posted_to": "%1 heeft een reactie geplaatst in %2", "user_posted_to_dual": "%1 en %2 hebben een reactie geplaatst in: %3", "user_posted_to_multiple": "%1 en %2 hebben een reactie geplaatst in: %3", @@ -40,7 +40,7 @@ "user_started_following_you_multiple": "%1 en %2 andere volgen jou nu.", "new_register": "%1 heeft een registratie verzoek aangevraagd.", "new_register_multiple": "Er is/zijn %1 registratieverzoek(en) die wacht(en) op goedkeuring.", - "flag_assigned_to_you": "Flag %1 has been assigned to you", + "flag_assigned_to_you": "Flag %1 is aan u toegewezen", "email-confirmed": "E-mailadres bevestigd", "email-confirmed-message": "Bedankt voor het bevestigen van je e-mailadres. Je account is nu volledig geactiveerd.", "email-confirm-error-message": "Er was een probleem met het bevestigen van dit e-mailadres. Misschien is de code niet goed ingevoerd of was de beschikbare tijd inmiddels verstreken.", diff --git a/public/language/nl/pages.json b/public/language/nl/pages.json index 816f62be22..64441b8b54 100644 --- a/public/language/nl/pages.json +++ b/public/language/nl/pages.json @@ -6,7 +6,7 @@ "popular-month": "De populaire onderwerpen van deze maand", "popular-alltime": "De populaire onderwerpen", "recent": "Recente onderwerpen", - "flagged-content": "Flagged Content", + "flagged-content": "Gemarkeerde content", "ip-blacklist": "IP zwarte lijst", "users/online": "Online Gebruikers", "users/latest": "Meest recente gebruikers", @@ -27,7 +27,7 @@ "group": "%1's groep", "chats": "Chats", "chat": "Chatten met %1", - "flags": "Flags", + "flags": "Markeringen", "flag-details": "Flag %1 Details", "account/edit": "\"%1\" aanpassen", "account/edit/password": "Wachtwoord van \"%1\" aanpassen", diff --git a/public/language/zh-CN/admin/manage/categories.json b/public/language/zh-CN/admin/manage/categories.json index b535e4f0b8..77aa93ec68 100644 --- a/public/language/zh-CN/admin/manage/categories.json +++ b/public/language/zh-CN/admin/manage/categories.json @@ -12,55 +12,55 @@ "ext-link": "外部链接", "upload-image": "上传图片", "delete-image": "移除", - "category-image": "板块图片", - "parent-category": "父板块", - "optional-parent-category": "(可选)父板块", + "category-image": "版块图片", + "parent-category": "父版块", + "optional-parent-category": "(可选)父版块", "parent-category-none": "(无)", "copy-settings": "复制设置", - "optional-clone-settings": "(可选) 从板块复制设置", - "purge": "删除板块", + "optional-clone-settings": "(可选) 从版块复制设置", + "purge": "删除版块", "enable": "启用", "disable": "禁用", "edit": "编辑", - "select-category": "选择板块", - "set-parent-category": "设置父板块", + "select-category": "选择版块", + "set-parent-category": "设置父版块", - "privileges.description": "您可以在此部分中配置此板块的访问控制权限。 可以根据每个用户或每个组授予权限。 您可以通过在下面的表格中搜索,将新用户添加到此表中。", + "privileges.description": "您可以在此部分中配置此版块的访问控制权限。 可以根据每个用户或每个组授予权限。 您可以通过在下面的表格中搜索,将新用户添加到此表中。", "privileges.warning": "注意:权限设置会立即生效。 调整这些设置后,无需保存。", "privileges.section-viewing": "查看权限", "privileges.section-posting": "发帖权限", "privileges.section-moderation": "审核权限", "privileges.section-user": "用户", "privileges.search-user": "添加用户", - "privileges.no-users": "此板块中没有用户特定的权限。", + "privileges.no-users": "此版块中没有用户特定的权限。", "privileges.section-group": "群组", "privileges.group-private": "这个群组是私密的", "privileges.search-group": "添加群组", "privileges.copy-to-children": "复制到子版块", - "privileges.copy-from-category": "从板块复制", + "privileges.copy-from-category": "从版块复制", "privileges.inherit": "如果 registered-users 组被授予特定权限,所有其他组都会收到隐式权限,即使它们未被明确定义/检查。 将显示此隐式权限,因为所有用户都是 registered-users 群组的一部分,因此无需显式授予其他组的权限。", - "analytics.back": "返回板块列表", - "analytics.title": "“%1”板块的统计", - "analytics.pageviews-hourly": "图1 – 此板块的每小时页面浏览量", - "analytics.pageviews-daily": "图2 – 此板块的每日页面浏览量", - "analytics.topics-daily": "图3 – 每日在此板块中创建的主题", - "analytics.posts-daily": "图4 – 每日在此板块中每日发布的帖子", + "analytics.back": "返回版块列表", + "analytics.title": "“%1”版块的统计", + "analytics.pageviews-hourly": "图1 – 此版块的每小时页面浏览量", + "analytics.pageviews-daily": "图2 – 此版块的每日页面浏览量", + "analytics.topics-daily": "图3 – 每日在此版块中创建的主题", + "analytics.posts-daily": "图4 – 每日在此版块中每日发布的帖子", "alert.created": "创建", - "alert.create-success": "板块创建成功!", - "alert.none-active": "您没有有效的板块。", - "alert.create": "创建一个板块", + "alert.create-success": "版块创建成功!", + "alert.none-active": "您没有有效的版块。", + "alert.create": "创建一个版块", "alert.confirm-moderate": "您确定要将审核权限授予此群组吗?此群组是公开的,任何用户都可以随意加入。", - "alert.confirm-purge": "

您确定要清除此板块“%1”吗?

警告! 板块将被清除!

清除板块将删除所有主题和帖子,并从数据库中删除板块。 如果您想暂时移除板块,请使用停用板块。

", - "alert.purge-success": "板块已删除!", + "alert.confirm-purge": "

您确定要清除此版块“%1”吗?

警告! 版块将被清除!

清除版块将删除所有主题和帖子,并从数据库中删除版块。 如果您想暂时移除版块,请使用停用版块。

", + "alert.purge-success": "版块已删除!", "alert.copy-success": "设置已复制!", - "alert.set-parent-category": "设置父板块", - "alert.updated": "板块已更新", - "alert.updated-success": "板块ID %1 成功更新。", - "alert.upload-image": "上传板块图片", + "alert.set-parent-category": "设置父版块", + "alert.updated": "版块已更新", + "alert.updated-success": "版块ID %1 成功更新。", + "alert.upload-image": "上传版块图片", "alert.find-user": "查找用户", "alert.user-search": "在这里查找用户…", "alert.find-group": "查找群组", diff --git a/public/language/zh-CN/admin/settings/pagination.json b/public/language/zh-CN/admin/settings/pagination.json index ec44dd3661..b4e16d4f7f 100644 --- a/public/language/zh-CN/admin/settings/pagination.json +++ b/public/language/zh-CN/admin/settings/pagination.json @@ -3,7 +3,7 @@ "enable": "在主题和帖子使用分页替代无限滚动浏览。", "topics": "话题分页", "posts-per-page": "每页帖子数", - "categories": "板块分页", + "categories": "版块分页", "topics-per-page": "每页主题数", "initial-num-load": "最初加载未读,最新,热门的话题" } \ No newline at end of file diff --git a/public/language/zh-CN/admin/settings/post.json b/public/language/zh-CN/admin/settings/post.json index d2f36c5a83..857f72b98e 100644 --- a/public/language/zh-CN/admin/settings/post.json +++ b/public/language/zh-CN/admin/settings/post.json @@ -6,21 +6,21 @@ "sorting.most-votes": "最多投票", "sorting.topic-default": "默认主题排序", "restrictions": "发帖限制", - "restrictions.seconds-between": "发帖间隔", - "restrictions.seconds-between-new": "对于新用户的发帖间隔", + "restrictions.seconds-between": "发帖间隔(单位:秒)", + "restrictions.seconds-between-new": "对于新用户的发帖间隔(单位:秒)", "restrictions.rep-threshold": "取消发帖限制所需的声望值", - "restrictions.seconds-defore-new": "见习时间", - "restrictions.seconds-edit-after": "用户在发布后允许编辑帖子的秒数。 (0为禁用) ", - "restrictions.seconds-delete-after": "允许在发布后删除帖子的秒数。 (0为禁用) ", + "restrictions.seconds-defore-new": "见习时间(单位:秒)", + "restrictions.seconds-edit-after": "用户在发布后允许编辑帖子的时间(0为禁用,单位:秒)", + "restrictions.seconds-delete-after": "用户在发布后允许删除帖子的时间(0为禁用,单位:秒)", "restrictions.replies-no-delete": "在用户被禁止删除自己的主题后的回复数。 (0为禁用) ", "restrictions.min-title-length": "最小标题长度", "restrictions.max-title-length": "最大标题长度", "restrictions.min-post-length": "最小帖子长度", "restrictions.max-post-length": "最大帖子长度", - "restrictions.days-until-stale": "主题过期时间", + "restrictions.days-until-stale": "主题过期时间(单位:天)", "restrictions.stale-help": "如果某个主题被视为“过时”,则会向尝试回复该主题的用户显示警告。", "timestamp": "时间戳", - "timestamp.cut-off": "日期截止日期 (天) ", + "timestamp.cut-off": "日期截止日期(单位:天)", "timestamp.cut-off-help": "日期&时间将以相对方式 (例如,“3小时前” / “5天前”) 显示,并且会依照访客语言时区转换。在某一时刻之后,可以切换该文本以显示本地化日期本身 (例如2016年11月5日15:30) 。
(默认值: 30 或一个月) 。 设置为0可始终显示日期,留空以始终显示相对时间。", "teaser": "预览帖子", "teaser.last-post": "最后– 显示最新的帖子,包括原帖,如果没有回复", diff --git a/public/language/zh-CN/modules.json b/public/language/zh-CN/modules.json index e38d6114ee..235ec2c80b 100644 --- a/public/language/zh-CN/modules.json +++ b/public/language/zh-CN/modules.json @@ -38,7 +38,7 @@ "composer.upload-picture": "上传图片", "composer.upload-file": "上传文件", "composer.zen_mode": "无干扰模式", - "composer.select_category": "选择一个板块", + "composer.select_category": "选择一个版块", "bootbox.ok": "确认", "bootbox.cancel": "取消", "bootbox.confirm": "确认", diff --git a/public/language/zh-CN/search.json b/public/language/zh-CN/search.json index 92ea7f452f..2d6b500297 100644 --- a/public/language/zh-CN/search.json +++ b/public/language/zh-CN/search.json @@ -6,8 +6,8 @@ "titles": "标题", "titles-posts": "标题和回帖", "posted-by": "发表", - "in-categories": "在版面", - "search-child-categories": "搜索子版面", + "in-categories": "在版块", + "search-child-categories": "搜索子版块", "has-tags": "有标签", "reply-count": "回复数", "at-least": "至少", diff --git a/public/language/zh-CN/topic.json b/public/language/zh-CN/topic.json index cb7880b641..cbd70048ef 100644 --- a/public/language/zh-CN/topic.json +++ b/public/language/zh-CN/topic.json @@ -48,7 +48,7 @@ "not-watching": "未关注", "ignoring": "忽略中", "watching.description": "有新回复时通知我。
在未读主题中显示。", - "not-watching.description": "不要在有新回复时通知我。
如果这个分类未被忽略则在未读主题中显示。", + "not-watching.description": "不要在有新回复时通知我。
如果这个版块未被忽略则在未读主题中显示。", "ignoring.description": "不要在有新回复时通知我。
不要在未读主题中显示该主题。", "thread_tools.title": "主题工具", "thread_tools.markAsUnreadForAll": "标记全部未读", @@ -70,8 +70,8 @@ "post_delete_confirm": "确定删除此帖吗?", "post_restore_confirm": "确定恢复此帖吗?", "post_purge_confirm": "确认清除此回帖吗?", - "load_categories": "正在载入板块", - "disabled_categories_note": "停用的板块为灰色", + "load_categories": "正在载入版块", + "disabled_categories_note": "停用的版块为灰色", "confirm_move": "移动", "confirm_fork": "分割", "bookmark": "书签", diff --git a/public/language/zh-CN/users.json b/public/language/zh-CN/users.json index a6a2267467..bdc9eda8de 100644 --- a/public/language/zh-CN/users.json +++ b/public/language/zh-CN/users.json @@ -15,7 +15,7 @@ "recent_topics": "最新主题", "popular_topics": "热门主题", "unread_topics": "未读主题", - "categories": "版面", + "categories": "版块", "tags": "话题", "no-users-found": "未找到匹配的用户!" } \ No newline at end of file diff --git a/public/src/modules/translator.js b/public/src/modules/translator.js index ce49f222f5..eea3f7495b 100644 --- a/public/src/modules/translator.js +++ b/public/src/modules/translator.js @@ -97,6 +97,9 @@ // and the strings of untranslated text in between var toTranslate = []; + // to store the state of if we're currently in a top-level token for later + var inToken = false; + // split a translator string into an array of tokens // but don't split by commas inside other translator strings function split(text) { @@ -141,6 +144,8 @@ // 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; @@ -176,6 +181,8 @@ invalidTextRegex.test(sliced[0])) { cursor += 1; lastBreak -= 2; + // no longer in a token + inToken = false; if (level > 0) { level -= 1; } else { @@ -191,18 +198,26 @@ // if we're at the base level, then this is the end if (level === 0) { // so grab the name and args - var result = split(str.slice(lastBreak, cursor)); + 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)); + 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 @@ -219,8 +234,16 @@ cursor += 1; } + // ending string of source + var last = str.slice(lastBreak); + + // if we were mid-token, treat it as invalid + if (inToken) { + last = this.translate('[[' + last); + } + // add the remaining text after the last translation string - toTranslate.push(str.slice(lastBreak, cursor + 2)); + toTranslate.push(last); // and return a promise for the concatenated translated string return Promise.all(toTranslate).then(function (translated) { @@ -232,9 +255,10 @@ * Translates a specific key and array of arguments * @param {string} name - Translation key (ex. 'global:home') * @param {string[]} args - Arguments for `%1`, `%2`, etc + * @param {string|Promise} backup - Text to use in case the key can't be found * @returns {Promise} */ - Translator.prototype.translateKey = function translateKey(name, args) { + Translator.prototype.translateKey = function translateKey(name, args, backup) { var self = this; var result = name.split(':', 2); @@ -251,29 +275,27 @@ } var translation = this.getTranslation(namespace, key); - var argsToTranslate = args.map(function (arg) { - return string(arg).collapseWhitespace().decodeHTMLEntities().escapeHTML().s; - }).map(function (arg) { - return self.translate(arg); - }); - - // so we can await all promises at once - argsToTranslate.unshift(translation); - - return Promise.all(argsToTranslate).then(function (result) { - var translated = result[0]; - var translatedArgs = result.slice(1); - + return translation.then(function (translated) { + // check if the translation is missing first if (!translated) { warn('Missing translation "' + name + '"'); - return key; + return backup || key; } - var out = translated; - translatedArgs.forEach(function (arg, i) { - var escaped = arg.replace(/%/g, '%').replace(/\\,/g, ','); - out = out.replace(new RegExp('%' + (i + 1), 'g'), escaped); + + var argsToTranslate = args.map(function (arg) { + return string(arg).collapseWhitespace().decodeHTMLEntities().escapeHTML().s; + }).map(function (arg) { + return self.translate(arg); + }); + + return Promise.all(argsToTranslate).then(function (translatedArgs) { + var out = translated; + translatedArgs.forEach(function (arg, i) { + var escaped = arg.replace(/%/g, '%').replace(/\\,/g, ','); + out = out.replace(new RegExp('%' + (i + 1), 'g'), escaped); + }); + return out; }); - return out; }); }; @@ -281,7 +303,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} + * @returns {Promise|Promise} */ Translator.prototype.getTranslation = function getTranslation(namespace, key) { var translation; diff --git a/public/src/utils.js b/public/src/utils.js index 171e3d86db..16a5bdbef7 100644 --- a/public/src/utils.js +++ b/public/src/utils.js @@ -1,13 +1,16 @@ 'use strict'; -(function (module) { - var utils; - var fs; - var XRegExp; - - if (typeof window === 'undefined') { - fs = require('fs'); - XRegExp = require('xregexp'); +(function (factory) { + if (typeof module === 'object' && module.exports) { + var winston = require('winston'); + + var file = require('../../src/file'); + module.exports = factory(require('xregexp')); + module.exports.walk = function (dir, done) { + // DEPRECATED + winston.warn('[deprecated] `utils.walk` is deprecated. Use `file.walk` instead.'); + file.walk(dir, done); + }; process.profile = function (operation, start) { console.log('%s took %d milliseconds', operation, process.elapsedTimeSince(start)); @@ -18,11 +21,10 @@ return (diff[0] * 1e3) + (diff[1] / 1e6); }; } else { - XRegExp = window.XRegExp; + window.utils = factory(window.XRegExp); } - - - utils = { +}(function (XRegExp) { + var utils = { generateUUID: function () { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { var r = Math.random() * 16 | 0; @@ -31,49 +33,6 @@ }); }, - // Adapted from http://stackoverflow.com/questions/5827612/node-js-fs-readdir-recursive-directory-search - walk: function (dir, done) { - var results = []; - - fs.readdir(dir, function (err, list) { - if (err) { - return done(err); - } - var pending = list.length; - if (!pending) { - return done(null, results); - } - list.forEach(function (file) { - file = dir + '/' + file; - fs.stat(file, function (err, stat) { - if (err) { - return done(err); - } - - if (stat && stat.isDirectory()) { - utils.walk(file, function (err, res) { - if (err) { - return done(err); - } - - results = results.concat(res); - pending -= 1; - if (!pending) { - done(null, results); - } - }); - } else { - results.push(file); - pending -= 1; - if (!pending) { - done(null, results); - } - } - }); - }); - }); - }, - invalidUnicodeChars: XRegExp('[^\\p{L}\\s\\d\\-_]', 'g'), invalidLatinChars: /[^\w\s\d\-_]/g, trimRegex: /^\s+|\s+$/g, @@ -447,11 +406,6 @@ }, }; - module.exports = utils; - if (typeof window !== 'undefined') { - window.utils = module.exports; - } - /* eslint "no-extend-native": "off" */ if (typeof String.prototype.startsWith !== 'function') { String.prototype.startsWith = function (prefix) { @@ -474,13 +428,5 @@ }; } - if (typeof String.prototype.rtrim !== 'function') { - String.prototype.rtrim = function () { - return this.replace(/\s+$/g, ''); - }; - } -}(typeof module === 'undefined' ? { - module: { - exports: {}, - }, -} : module)); + return utils; +})); diff --git a/src/admin/search.js b/src/admin/search.js index 3a6f52cfd0..6ffd9db768 100644 --- a/src/admin/search.js +++ b/src/admin/search.js @@ -6,8 +6,8 @@ var async = require('async'); var sanitizeHTML = require('sanitize-html'); var nconf = require('nconf'); -var utils = require('../../public/src/utils'); -var Translator = require('../../public/src/modules/translator').Translator; +var file = require('../file'); +var Translator = require('../translator').Translator; function filterDirectories(directories) { return directories.map(function (dir) { @@ -24,7 +24,7 @@ function filterDirectories(directories) { } function getAdminNamespaces(callback) { - utils.walk(path.resolve(nconf.get('views_dir'), 'admin'), function (err, directories) { + file.walk(path.resolve(nconf.get('views_dir'), 'admin'), function (err, directories) { if (err) { return callback(err); } diff --git a/src/batch.js b/src/batch.js index cb3fd298a1..475c4aa7ca 100644 --- a/src/batch.js +++ b/src/batch.js @@ -4,7 +4,7 @@ var async = require('async'); var db = require('./database'); -var utils = require('../public/src/utils'); +var utils = require('./utils'); var DEFAULT_BATCH_SIZE = 100; diff --git a/bcrypt.js b/src/bcrypt.js similarity index 100% rename from bcrypt.js rename to src/bcrypt.js diff --git a/src/categories/create.js b/src/categories/create.js index 7be163bd5b..90380c0de0 100644 --- a/src/categories/create.js +++ b/src/categories/create.js @@ -6,7 +6,7 @@ var db = require('../database'); var groups = require('../groups'); var plugins = require('../plugins'); var privileges = require('../privileges'); -var utils = require('../../public/src/utils'); +var utils = require('../utils'); module.exports = function (Categories) { Categories.create = function (data, callback) { diff --git a/src/categories/update.js b/src/categories/update.js index 04bf0e2235..866e6cd748 100644 --- a/src/categories/update.js +++ b/src/categories/update.js @@ -5,8 +5,8 @@ var async = require('async'); var db = require('../database'); var meta = require('../meta'); -var utils = require('../../public/src/utils'); -var translator = require('../../public/src/modules/translator'); +var utils = require('../utils'); +var translator = require('../translator'); var plugins = require('../plugins'); module.exports = function (Categories) { diff --git a/src/controllers/accounts/helpers.js b/src/controllers/accounts/helpers.js index 1ee9a08678..2a40e4044d 100644 --- a/src/controllers/accounts/helpers.js +++ b/src/controllers/accounts/helpers.js @@ -9,7 +9,7 @@ var user = require('../../user'); var groups = require('../../groups'); var plugins = require('../../plugins'); var meta = require('../../meta'); -var utils = require('../../../public/src/utils'); +var utils = require('../../utils'); var helpers = {}; diff --git a/src/controllers/admin/categories.js b/src/controllers/admin/categories.js index 4a60fc14e2..3a3b1a6fa4 100644 --- a/src/controllers/admin/categories.js +++ b/src/controllers/admin/categories.js @@ -6,7 +6,7 @@ var categories = require('../../categories'); var privileges = require('../../privileges'); var analytics = require('../../analytics'); var plugins = require('../../plugins'); -var translator = require('../../../public/src/modules/translator'); +var translator = require('../../translator'); var categoriesController = {}; diff --git a/src/controllers/admin/settings.js b/src/controllers/admin/settings.js index 55e5a1dfbf..7d1db3e73e 100644 --- a/src/controllers/admin/settings.js +++ b/src/controllers/admin/settings.js @@ -24,13 +24,13 @@ settingsController.get = function (req, res, next) { function renderEmail(req, res, next) { var fs = require('fs'); var path = require('path'); - var utils = require('../../../public/src/utils'); + var file = require('../../file'); var emailsPath = path.join(nconf.get('views_dir'), 'emails'); async.waterfall([ function (next) { - utils.walk(emailsPath, next); + file.walk(emailsPath, next); }, function (emails, next) { async.map(emails, function (email, next) { diff --git a/src/controllers/api.js b/src/controllers/api.js index d0975c71fa..36f9355488 100644 --- a/src/controllers/api.js +++ b/src/controllers/api.js @@ -12,7 +12,7 @@ var categories = require('../categories'); var privileges = require('../privileges'); var plugins = require('../plugins'); var widgets = require('../widgets'); -var translator = require('../../public/src/modules/translator'); +var translator = require('../translator'); var apiController = module.exports; diff --git a/src/controllers/authentication.js b/src/controllers/authentication.js index 6a2051d2c9..396aafd130 100644 --- a/src/controllers/authentication.js +++ b/src/controllers/authentication.js @@ -11,7 +11,7 @@ var db = require('../database'); var meta = require('../meta'); var user = require('../user'); var plugins = require('../plugins'); -var utils = require('../../public/src/utils'); +var utils = require('../utils'); var Password = require('../password'); var sockets = require('../socket.io'); diff --git a/src/controllers/category.js b/src/controllers/category.js index ce5f7b5e07..88b49c0b24 100644 --- a/src/controllers/category.js +++ b/src/controllers/category.js @@ -11,7 +11,7 @@ var categories = require('../categories'); var meta = require('../meta'); var pagination = require('../pagination'); var helpers = require('./helpers'); -var utils = require('../../public/src/utils'); +var utils = require('../utils'); var categoryController = {}; diff --git a/src/controllers/topics.js b/src/controllers/topics.js index adfad461e2..4fe1bf96d5 100644 --- a/src/controllers/topics.js +++ b/src/controllers/topics.js @@ -13,7 +13,7 @@ var privileges = require('../privileges'); var plugins = require('../plugins'); var helpers = require('./helpers'); var pagination = require('../pagination'); -var utils = require('../../public/src/utils'); +var utils = require('../utils'); var topicsController = {}; diff --git a/src/database/mongo/sorted.js b/src/database/mongo/sorted.js index defaed80fc..59bd05b9fd 100644 --- a/src/database/mongo/sorted.js +++ b/src/database/mongo/sorted.js @@ -1,7 +1,7 @@ 'use strict'; var async = require('async'); -var utils = require('../../../public/src/utils'); +var utils = require('../../utils'); module.exports = function (db, module) { var helpers = module.helpers.mongo; diff --git a/src/database/redis/sorted.js b/src/database/redis/sorted.js index a2c18296db..67f55bf343 100644 --- a/src/database/redis/sorted.js +++ b/src/database/redis/sorted.js @@ -1,7 +1,7 @@ 'use strict'; module.exports = function (redisClient, module) { - var utils = require('../../../public/src/utils'); + var utils = require('../../utils'); var helpers = module.helpers.redis; diff --git a/src/emailer.js b/src/emailer.js index 95ffbdceba..bad8f22650 100644 --- a/src/emailer.js +++ b/src/emailer.js @@ -13,7 +13,7 @@ var url = require('url'); var User = require('./user'); var Plugins = require('./plugins'); var meta = require('./meta'); -var translator = require('../public/src/modules/translator'); +var translator = require('./translator'); var transports = { sendmail: nodemailer.createTransport(sendmailTransport()), diff --git a/src/events.js b/src/events.js index ab2864bc23..c7a8b26638 100644 --- a/src/events.js +++ b/src/events.js @@ -7,7 +7,7 @@ var validator = require('validator'); var db = require('./database'); var batch = require('./batch'); var user = require('./user'); -var utils = require('../public/src/utils'); +var utils = require('./utils'); (function (events) { events.log = function (data, callback) { diff --git a/src/file.js b/src/file.js index 78d428c41e..5a1e65a4b2 100644 --- a/src/file.js +++ b/src/file.js @@ -8,9 +8,9 @@ var jimp = require('jimp'); var mkdirp = require('mkdirp'); var mime = require('mime'); -var utils = require('../public/src/utils'); +var utils = require('./utils'); -var file = {}; +var file = module.exports; file.saveFileToLocal = function (filename, folder, tempPath, callback) { /* @@ -129,4 +129,47 @@ file.typeToExtension = function (type) { return extension; }; +// Adapted from http://stackoverflow.com/questions/5827612/node-js-fs-readdir-recursive-directory-search +file.walk = function (dir, done) { + var results = []; + + fs.readdir(dir, function (err, list) { + if (err) { + return done(err); + } + var pending = list.length; + if (!pending) { + return done(null, results); + } + list.forEach(function (filename) { + filename = dir + '/' + filename; + fs.stat(filename, function (err, stat) { + if (err) { + return done(err); + } + + if (stat && stat.isDirectory()) { + file.walk(filename, function (err, res) { + if (err) { + return done(err); + } + + results = results.concat(res); + pending -= 1; + if (!pending) { + done(null, results); + } + }); + } else { + results.push(filename); + pending -= 1; + if (!pending) { + done(null, results); + } + } + }); + }); + }); +}; + module.exports = file; diff --git a/src/groups.js b/src/groups.js index fd8c4a958f..6462819e9e 100644 --- a/src/groups.js +++ b/src/groups.js @@ -6,7 +6,7 @@ var validator = require('validator'); var user = require('./user'); var db = require('./database'); var plugins = require('./plugins'); -var utils = require('../public/src/utils'); +var utils = require('./utils'); var Groups = module.exports; diff --git a/src/groups/create.js b/src/groups/create.js index 4b680b8992..a14441608f 100644 --- a/src/groups/create.js +++ b/src/groups/create.js @@ -3,7 +3,7 @@ var async = require('async'); var meta = require('../meta'); var plugins = require('../plugins'); -var utils = require('../../public/src/utils'); +var utils = require('../utils'); var db = require('../database'); module.exports = function (Groups) { diff --git a/src/groups/data.js b/src/groups/data.js index 24e7f99a02..919a257368 100644 --- a/src/groups/data.js +++ b/src/groups/data.js @@ -5,7 +5,7 @@ var validator = require('validator'); var db = require('../database'); var plugins = require('../plugins'); -var utils = require('../../public/src/utils'); +var utils = require('../utils'); module.exports = function (Groups) { Groups.getGroupsData = function (groupNames, callback) { diff --git a/src/groups/delete.js b/src/groups/delete.js index 6aed7173a2..e05eff6370 100644 --- a/src/groups/delete.js +++ b/src/groups/delete.js @@ -2,7 +2,7 @@ var async = require('async'); var plugins = require('../plugins'); -var utils = require('../../public/src/utils'); +var utils = require('../utils'); var db = require('./../database'); module.exports = function (Groups) { diff --git a/src/groups/membership.js b/src/groups/membership.js index 6bcafd2343..6337db1d8a 100644 --- a/src/groups/membership.js +++ b/src/groups/membership.js @@ -5,7 +5,7 @@ var winston = require('winston'); var _ = require('underscore'); var user = require('../user'); -var utils = require('../../public/src/utils'); +var utils = require('../utils'); var plugins = require('../plugins'); var notifications = require('../notifications'); var db = require('../database'); diff --git a/src/groups/update.js b/src/groups/update.js index 9bf53886e5..73104fd229 100644 --- a/src/groups/update.js +++ b/src/groups/update.js @@ -4,7 +4,7 @@ var async = require('async'); var winston = require('winston'); var plugins = require('../plugins'); -var utils = require('../../public/src/utils'); +var utils = require('../utils'); var db = require('../database'); diff --git a/src/install.js b/src/install.js index a06a138f26..50e8328682 100644 --- a/src/install.js +++ b/src/install.js @@ -6,7 +6,7 @@ var path = require('path'); var prompt = require('prompt'); var winston = require('winston'); var nconf = require('nconf'); -var utils = require('../public/src/utils.js'); +var utils = require('./utils.js'); var install = {}; var questions = {}; diff --git a/src/messaging.js b/src/messaging.js index e141adff4f..aa16cd0b36 100644 --- a/src/messaging.js +++ b/src/messaging.js @@ -8,7 +8,7 @@ var db = require('./database'); var user = require('./user'); var plugins = require('./plugins'); var meta = require('./meta'); -var utils = require('../public/src/utils'); +var utils = require('./utils'); var Messaging = module.exports; diff --git a/src/messaging/data.js b/src/messaging/data.js index e3cb67fbd8..3805b1c6f1 100644 --- a/src/messaging/data.js +++ b/src/messaging/data.js @@ -5,7 +5,7 @@ var S = require('string'); var db = require('../database'); var user = require('../user'); -var utils = require('../../public/src/utils'); +var utils = require('../utils'); module.exports = function (Messaging) { Messaging.getMessageField = function (mid, field, callback) { diff --git a/src/meta.js b/src/meta.js index 6cbccd70a1..c67369a8d9 100644 --- a/src/meta.js +++ b/src/meta.js @@ -6,7 +6,7 @@ var os = require('os'); var nconf = require('nconf'); var pubsub = require('./pubsub'); -var utils = require('../public/src/utils'); +var utils = require('./utils'); (function (Meta) { Meta.reloadRequired = false; diff --git a/src/meta/css.js b/src/meta/css.js index 33bd0877e0..88ad3348e8 100644 --- a/src/meta/css.js +++ b/src/meta/css.js @@ -13,7 +13,6 @@ var clean = require('postcss-clean'); var plugins = require('../plugins'); var db = require('../database'); var file = require('../file'); -var utils = require('../../public/src/utils'); module.exports = function (Meta) { Meta.css = {}; @@ -106,7 +105,7 @@ module.exports = function (Meta) { }); async.each(pluginDirectories, function (directory, next) { - utils.walk(directory, function (err, styleFiles) { + file.walk(directory, function (err, styleFiles) { if (err) { return next(err); } diff --git a/src/meta/js.js b/src/meta/js.js index 15c6945f37..08b959fb3c 100644 --- a/src/meta/js.js +++ b/src/meta/js.js @@ -11,7 +11,6 @@ var uglifyjs = require('uglify-js'); var file = require('../file'); var plugins = require('../plugins'); -var utils = require('../../public/src/utils'); var minifierPath = path.join(__dirname, 'minifier.js'); @@ -20,12 +19,12 @@ module.exports = function (Meta) { target: {}, scripts: { base: [ - './node_modules/jquery/dist/jquery.js', - './node_modules/socket.io-client/dist/socket.io.js', + 'node_modules/jquery/dist/jquery.js', + 'node_modules/socket.io-client/dist/socket.io.js', 'public/vendor/jquery/timeago/jquery.timeago.js', 'public/vendor/jquery/js/jquery.form.min.js', 'public/vendor/visibility/visibility.min.js', - './node_modules/bootstrap/dist/js/bootstrap.js', + 'node_modules/bootstrap/dist/js/bootstrap.js', 'public/vendor/jquery/bootstrap-tagsinput/bootstrap-tagsinput.min.js', 'public/vendor/jquery/textcomplete/jquery.textcomplete.js', 'public/vendor/requirejs/require.js', @@ -35,14 +34,14 @@ module.exports = function (Meta) { 'public/vendor/tinycon/tinycon.js', 'public/vendor/xregexp/xregexp.js', 'public/vendor/xregexp/unicode/unicode-base.js', - './node_modules/templates.js/lib/templates.js', + 'node_modules/templates.js/lib/templates.js', 'public/src/utils.js', 'public/src/sockets.js', 'public/src/app.js', 'public/src/ajaxify.js', 'public/src/overrides.js', 'public/src/widgets.js', - './node_modules/promise-polyfill/promise.js', + 'node_modules/promise-polyfill/promise.js', ], // files listed below are only available client-side, or are bundled in to reduce # of network requests on cold load @@ -84,12 +83,12 @@ module.exports = function (Meta) { // modules listed below are built (/src/modules) so they can be defined anonymously modules: { - 'Chart.js': './node_modules/chart.js/dist/Chart.min.js', - 'mousetrap.js': './node_modules/mousetrap/mousetrap.min.js', - 'cropper.js': './node_modules/cropperjs/dist/cropper.min.js', + 'Chart.js': 'node_modules/chart.js/dist/Chart.min.js', + 'mousetrap.js': 'node_modules/mousetrap/mousetrap.min.js', + 'cropper.js': 'node_modules/cropperjs/dist/cropper.min.js', 'jqueryui.js': 'public/vendor/jquery/js/jquery-ui.js', - 'zxcvbn.js': './node_modules/zxcvbn/dist/zxcvbn.js', - ace: './node_modules/ace-builds/src-min', + 'zxcvbn.js': 'node_modules/zxcvbn/dist/zxcvbn.js', + ace: 'node_modules/ace-builds/src-min', }, }, }; @@ -199,7 +198,7 @@ module.exports = function (Meta) { return next(); } - utils.walk(srcPath, function (err, files) { + file.walk(srcPath, function (err, files) { if (err) { return next(err); } @@ -225,10 +224,10 @@ module.exports = function (Meta) { function clearModules(callback) { var builtPaths = moduleDirs.map(function (p) { - return '../../build/public/src/' + p; + return path.join(__dirname, '../../build/public/src', p); }); async.each(builtPaths, function (builtPath, next) { - rimraf(path.join(__dirname, builtPath), next); + rimraf(builtPath, next); }, function (err) { callback(err); }); @@ -325,7 +324,7 @@ module.exports = function (Meta) { }); async.each(pluginDirectories, function (directory, next) { - utils.walk(directory, function (err, scripts) { + file.walk(directory, function (err, scripts) { pluginsScripts = pluginsScripts.concat(scripts); next(err); }); @@ -343,7 +342,7 @@ module.exports = function (Meta) { } Meta.js.target[target].scripts = Meta.js.target[target].scripts.map(function (script) { - return path.relative(basePath, script).replace(/\\/g, '/'); + return path.resolve(basePath, script).replace(/\\/g, '/'); }); callback(); @@ -357,7 +356,7 @@ module.exports = function (Meta) { }; Meta.js.commitToFile = function (target, callback) { - fs.writeFile(path.join(__dirname, '../../build/public/' + target), Meta.js.target[target].cache, function (err) { + fs.writeFile(path.join(__dirname, '../../build/public', target), Meta.js.target[target].cache, function (err) { callback(err); }); }; diff --git a/src/meta/languages.js b/src/meta/languages.js index 67bd9fabd5..b420445f16 100644 --- a/src/meta/languages.js +++ b/src/meta/languages.js @@ -8,7 +8,6 @@ var mkdirp = require('mkdirp'); var rimraf = require('rimraf'); var file = require('../file'); -var utils = require('../../public/src/utils'); var Plugins = require('../plugins'); var db = require('../database'); @@ -67,7 +66,7 @@ function getTranslationTree(callback) { async.parallel([ // get core languages and namespaces function (nxt) { - utils.walk(coreLanguagesPath, function (err, paths) { + file.walk(coreLanguagesPath, function (err, paths) { if (err) { return nxt(err); } @@ -80,7 +79,7 @@ function getTranslationTree(callback) { function (nxt) { async.each(plugins, function (pluginData, cb) { var pathToFolder = path.join(__dirname, '../../node_modules/', pluginData.id, pluginData.languages); - utils.walk(pathToFolder, function (err, paths) { + file.walk(pathToFolder, function (err, paths) { if (err) { return cb(err); } diff --git a/src/meta/templates.js b/src/meta/templates.js index 21b5889d26..9420959694 100644 --- a/src/meta/templates.js +++ b/src/meta/templates.js @@ -9,7 +9,7 @@ var fs = require('fs'); var nconf = require('nconf'); var plugins = require('../plugins'); -var utils = require('../../public/src/utils'); +var file = require('../file'); var Templates = {}; @@ -62,11 +62,11 @@ function preparePaths(baseTemplatesPaths, callback) { async.parallel({ coreTpls: function (next) { - utils.walk(coreTemplatesPath, next); + file.walk(coreTemplatesPath, next); }, baseThemes: function (next) { async.map(baseTemplatesPaths, function (baseTemplatePath, next) { - utils.walk(baseTemplatePath, function (err, paths) { + file.walk(baseTemplatePath, function (err, paths) { paths = paths.map(function (tpl) { return { base: baseTemplatePath, diff --git a/src/middleware/render.js b/src/middleware/render.js index 88e9879887..366f6f9f2a 100644 --- a/src/middleware/render.js +++ b/src/middleware/render.js @@ -6,7 +6,7 @@ var validator = require('validator'); var winston = require('winston'); var plugins = require('../plugins'); -var translator = require('../../public/src/modules/translator'); +var translator = require('../translator'); module.exports = function (middleware) { middleware.processRender = function (req, res, next) { diff --git a/src/navigation/admin.js b/src/navigation/admin.js index 0917e9e524..6b0984a466 100644 --- a/src/navigation/admin.js +++ b/src/navigation/admin.js @@ -3,7 +3,7 @@ var async = require('async'); var plugins = require('../plugins'); var db = require('../database'); -var translator = require('../../public/src/modules/translator'); +var translator = require('../translator'); var pubsub = require('../pubsub'); var admin = module.exports; diff --git a/src/navigation/index.js b/src/navigation/index.js index 2cb24f3a9f..76b0ceeceb 100644 --- a/src/navigation/index.js +++ b/src/navigation/index.js @@ -2,7 +2,7 @@ var nconf = require('nconf'); var admin = require('./admin'); -var translator = require('../../public/src/modules/translator'); +var translator = require('../translator'); var navigation = {}; diff --git a/src/notifications.js b/src/notifications.js index 31960dc2f1..147ae7fea4 100644 --- a/src/notifications.js +++ b/src/notifications.js @@ -13,7 +13,7 @@ var groups = require('./groups'); var meta = require('./meta'); var batch = require('./batch'); var plugins = require('./plugins'); -var utils = require('../public/src/utils'); +var utils = require('./utils'); (function (Notifications) { Notifications.init = function () { diff --git a/src/password.js b/src/password.js index d4fd1b0f8d..816e357d12 100644 --- a/src/password.js +++ b/src/password.js @@ -2,6 +2,7 @@ (function (module) { var fork = require('child_process').fork; + var path = require('path'); module.hash = function (rounds, password, callback) { forkChild({ type: 'hash', rounds: rounds, password: password }, callback); @@ -16,7 +17,7 @@ if (global.v8debug || parseInt(process.execArgv.indexOf('--debug'), 10) !== -1) { forkProcessParams = { execArgv: ['--debug=' + (5859), '--nolazy'] }; } - var child = fork('./bcrypt', [], forkProcessParams); + var child = fork(path.join(__dirname, 'bcrypt'), [], forkProcessParams); child.on('message', function (msg) { if (msg.err) { diff --git a/src/plugins.js b/src/plugins.js index 7c2ecaee25..e4f1fa8312 100644 --- a/src/plugins.js +++ b/src/plugins.js @@ -9,7 +9,6 @@ var express = require('express'); var nconf = require('nconf'); var db = require('./database'); -var utils = require('../public/src/utils'); var hotswap = require('./hotswap'); var file = require('./file'); var languages = require('./languages'); @@ -176,7 +175,7 @@ var middleware; if (plugin.templates || plugin.id.startsWith('nodebb-theme-')) { winston.verbose('[plugins] Loading templates (' + plugin.id + ')'); var templatesPath = path.join(__dirname, '../node_modules', plugin.id, plugin.templates || 'templates'); - utils.walk(templatesPath, function (err, pluginTemplates) { + file.walk(templatesPath, function (err, pluginTemplates) { if (pluginTemplates) { pluginTemplates.forEach(function (pluginTemplate) { if (pluginTemplate.endsWith('.tpl')) { diff --git a/src/posts.js b/src/posts.js index c7e7ec3e0a..4b9febae07 100644 --- a/src/posts.js +++ b/src/posts.js @@ -4,7 +4,7 @@ var async = require('async'); var _ = require('underscore'); var db = require('./database'); -var utils = require('../public/src/utils'); +var utils = require('./utils'); var user = require('./user'); var topics = require('./topics'); var privileges = require('./privileges'); diff --git a/src/posts/create.js b/src/posts/create.js index e8e24a5585..86faf0ffa9 100644 --- a/src/posts/create.js +++ b/src/posts/create.js @@ -9,7 +9,7 @@ var plugins = require('../plugins'); var user = require('../user'); var topics = require('../topics'); var categories = require('../categories'); -var utils = require('../../public/src/utils'); +var utils = require('../utils'); module.exports = function (Posts) { Posts.create = function (data, callback) { diff --git a/src/posts/edit.js b/src/posts/edit.js index 863888a6ce..3e9d295f19 100644 --- a/src/posts/edit.js +++ b/src/posts/edit.js @@ -11,7 +11,7 @@ var privileges = require('../privileges'); var plugins = require('../plugins'); var cache = require('./cache'); var pubsub = require('../pubsub'); -var utils = require('../../public/src/utils'); +var utils = require('../utils'); module.exports = function (Posts) { pubsub.on('post:edit', function (pid) { diff --git a/src/posts/parse.js b/src/posts/parse.js index 8ac2028bbd..e5ce10e1ac 100644 --- a/src/posts/parse.js +++ b/src/posts/parse.js @@ -9,7 +9,7 @@ var S = require('string'); var meta = require('../meta'); var cache = require('./cache'); var plugins = require('../plugins'); -var translator = require('../../public/src/modules/translator'); +var translator = require('../translator'); var urlRegex = /href="([^"]+)"/g; diff --git a/src/posts/summary.js b/src/posts/summary.js index 93e322229d..a05718b95c 100644 --- a/src/posts/summary.js +++ b/src/posts/summary.js @@ -9,7 +9,7 @@ var db = require('../database'); var user = require('../user'); var plugins = require('../plugins'); var categories = require('../categories'); -var utils = require('../../public/src/utils'); +var utils = require('../utils'); module.exports = function (Posts) { diff --git a/src/posts/topics.js b/src/posts/topics.js index eca6da493a..219be7b0d3 100644 --- a/src/posts/topics.js +++ b/src/posts/topics.js @@ -4,7 +4,7 @@ var async = require('async'); var topics = require('../topics'); -var utils = require('../../public/src/utils'); +var utils = require('../utils'); module.exports = function (Posts) { Posts.getPostsFromSet = function (set, start, stop, uid, reverse, callback) { diff --git a/src/search.js b/src/search.js index ffe33c8ef5..e223e3ea14 100644 --- a/src/search.js +++ b/src/search.js @@ -10,7 +10,7 @@ var categories = require('./categories'); var user = require('./user'); var plugins = require('./plugins'); var privileges = require('./privileges'); -var utils = require('../public/src/utils'); +var utils = require('./utils'); var search = {}; diff --git a/src/sitemap.js b/src/sitemap.js index d6f6fa39fe..d828f9d784 100644 --- a/src/sitemap.js +++ b/src/sitemap.js @@ -10,7 +10,7 @@ var topics = require('./topics'); var privileges = require('./privileges'); var meta = require('./meta'); var plugins = require('./plugins'); -var utils = require('../public/src/utils'); +var utils = require('./utils'); var sitemap = { maps: { diff --git a/src/socket.io/groups.js b/src/socket.io/groups.js index ed6cb00180..f9f6c1b263 100644 --- a/src/socket.io/groups.js +++ b/src/socket.io/groups.js @@ -5,7 +5,7 @@ var async = require('async'); var groups = require('../groups'); var meta = require('../meta'); var user = require('../user'); -var utils = require('../../public/src/utils'); +var utils = require('../utils'); var groupsController = require('../controllers/groups'); var SocketGroups = {}; diff --git a/src/socket.io/modules.js b/src/socket.io/modules.js index f0309e8d30..4480a9d0f7 100644 --- a/src/socket.io/modules.js +++ b/src/socket.io/modules.js @@ -7,7 +7,7 @@ var meta = require('../meta'); var notifications = require('../notifications'); var plugins = require('../plugins'); var Messaging = require('../messaging'); -var utils = require('../../public/src/utils'); +var utils = require('../utils'); var server = require('./'); var user = require('../user'); diff --git a/src/socket.io/notifications.js b/src/socket.io/notifications.js index 66e5135ed6..af47782d5d 100644 --- a/src/socket.io/notifications.js +++ b/src/socket.io/notifications.js @@ -3,7 +3,7 @@ var async = require('async'); var user = require('../user'); var notifications = require('../notifications'); -var utils = require('../../public/src/utils'); +var utils = require('../utils'); var SocketNotifs = {}; diff --git a/src/socket.io/posts.js b/src/socket.io/posts.js index b4b5ac40cd..e98052a2f9 100644 --- a/src/socket.io/posts.js +++ b/src/socket.io/posts.js @@ -9,7 +9,7 @@ var topics = require('../topics'); var user = require('../user'); var websockets = require('./index'); var socketHelpers = require('./helpers'); -var utils = require('../../public/src/utils'); +var utils = require('../utils'); var apiController = require('../controllers/api'); diff --git a/src/socket.io/posts/flag.js b/src/socket.io/posts/flag.js index dccf302c43..1464c6ac93 100644 --- a/src/socket.io/posts/flag.js +++ b/src/socket.io/posts/flag.js @@ -11,7 +11,7 @@ var privileges = require('../../privileges'); var notifications = require('../../notifications'); var plugins = require('../../plugins'); var meta = require('../../meta'); -var utils = require('../../../public/src/utils'); +var utils = require('../../utils'); module.exports = function (SocketPosts) { SocketPosts.flag = function (socket, data, callback) { diff --git a/src/socket.io/topics/infinitescroll.js b/src/socket.io/topics/infinitescroll.js index b0d7407a0a..87b27a6561 100644 --- a/src/socket.io/topics/infinitescroll.js +++ b/src/socket.io/topics/infinitescroll.js @@ -5,7 +5,7 @@ var async = require('async'); var topics = require('../../topics'); var privileges = require('../../privileges'); var meta = require('../../meta'); -var utils = require('../../../public/src/utils'); +var utils = require('../../utils'); var social = require('../../social'); module.exports = function (SocketTopics) { diff --git a/src/socket.io/topics/tags.js b/src/socket.io/topics/tags.js index 0b7076fa20..fd0c0d0987 100644 --- a/src/socket.io/topics/tags.js +++ b/src/socket.io/topics/tags.js @@ -3,7 +3,7 @@ var async = require('async'); var db = require('../../database'); var topics = require('../../topics'); -var utils = require('../../../public/src/utils'); +var utils = require('../../utils'); module.exports = function (SocketTopics) { SocketTopics.isTagAllowed = function (socket, data, callback) { diff --git a/src/topics.js b/src/topics.js index ed7deacc2e..12632193fb 100644 --- a/src/topics.js +++ b/src/topics.js @@ -5,7 +5,7 @@ var _ = require('underscore'); var db = require('./database'); var posts = require('./posts'); -var utils = require('../public/src/utils'); +var utils = require('./utils'); var plugins = require('./plugins'); var user = require('./user'); var categories = require('./categories'); diff --git a/src/topics/create.js b/src/topics/create.js index da16489f05..2c7f87741b 100644 --- a/src/topics/create.js +++ b/src/topics/create.js @@ -5,7 +5,7 @@ var async = require('async'); var validator = require('validator'); var S = require('string'); var db = require('../database'); -var utils = require('../../public/src/utils'); +var utils = require('../utils'); var plugins = require('../plugins'); var analytics = require('../analytics'); var user = require('../user'); @@ -87,6 +87,10 @@ module.exports = function (Topics) { ], callback); }; + function rtrim(str) { + return str.replace(/\s+$/g, ''); + } + Topics.post = function (data, callback) { var uid = data.uid; data.title = String(data.title).trim(); @@ -101,7 +105,7 @@ module.exports = function (Topics) { }, function (next) { if (data.content) { - data.content = data.content.rtrim(); + data.content = rtrim(data.content); } check(data.content, meta.config.minimumPostLength, meta.config.maximumPostLength, 'content-too-short', 'content-too-long', next); }, @@ -234,7 +238,7 @@ module.exports = function (Topics) { function (filteredData, next) { content = filteredData.content || data.content; if (content) { - content = content.rtrim(); + content = rtrim(content); } check(content, meta.config.minimumPostLength, meta.config.maximumPostLength, 'content-too-short', 'content-too-long', next); diff --git a/src/topics/data.js b/src/topics/data.js index 17e060f679..8df112dcbf 100644 --- a/src/topics/data.js +++ b/src/topics/data.js @@ -4,7 +4,7 @@ var validator = require('validator'); var db = require('../database'); var categories = require('../categories'); -var utils = require('../../public/src/utils'); +var utils = require('../utils'); module.exports = function (Topics) { Topics.getTopicField = function (tid, field, callback) { diff --git a/src/topics/tags.js b/src/topics/tags.js index d10c127ac0..d2f2b32694 100644 --- a/src/topics/tags.js +++ b/src/topics/tags.js @@ -7,7 +7,7 @@ var db = require('../database'); var meta = require('../meta'); var _ = require('underscore'); var plugins = require('../plugins'); -var utils = require('../../public/src/utils'); +var utils = require('../utils'); module.exports = function (Topics) { diff --git a/src/topics/teaser.js b/src/topics/teaser.js index b990b608d1..f864e3e23d 100644 --- a/src/topics/teaser.js +++ b/src/topics/teaser.js @@ -9,7 +9,7 @@ var meta = require('../meta'); var user = require('../user'); var posts = require('../posts'); var plugins = require('../plugins'); -var utils = require('../../public/src/utils'); +var utils = require('../utils'); module.exports = function (Topics) { Topics.getTeasers = function (topics, uid, callback) { diff --git a/src/topics/unread.js b/src/topics/unread.js index b2f5b199d1..e089fdaad9 100644 --- a/src/topics/unread.js +++ b/src/topics/unread.js @@ -9,7 +9,7 @@ var notifications = require('../notifications'); var categories = require('../categories'); var privileges = require('../privileges'); var meta = require('../meta'); -var utils = require('../../public/src/utils'); +var utils = require('../utils'); module.exports = function (Topics) { Topics.getTotalUnread = function (uid, filter, callback) { diff --git a/src/translator.js b/src/translator.js new file mode 100644 index 0000000000..21d4891332 --- /dev/null +++ b/src/translator.js @@ -0,0 +1,3 @@ +'use strict'; + +module.exports = require('../public/src/modules/translator'); diff --git a/src/user/approval.js b/src/user/approval.js index cbb1ba08b4..23d24a1e16 100644 --- a/src/user/approval.js +++ b/src/user/approval.js @@ -9,8 +9,8 @@ var meta = require('../meta'); var emailer = require('../emailer'); var notifications = require('../notifications'); var groups = require('../groups'); -var translator = require('../../public/src/modules/translator'); -var utils = require('../../public/src/utils'); +var translator = require('../translator'); +var utils = require('../utils'); var plugins = require('../plugins'); module.exports = function (User) { diff --git a/src/user/create.js b/src/user/create.js index 7d5e56f96f..e9cda9bce2 100644 --- a/src/user/create.js +++ b/src/user/create.js @@ -2,7 +2,7 @@ var async = require('async'); var db = require('../database'); -var utils = require('../../public/src/utils'); +var utils = require('../utils'); var validator = require('validator'); var plugins = require('../plugins'); var groups = require('../groups'); diff --git a/src/user/data.js b/src/user/data.js index 120da7e3a2..d0504ffce6 100644 --- a/src/user/data.js +++ b/src/user/data.js @@ -7,7 +7,7 @@ var winston = require('winston'); var db = require('../database'); var plugins = require('../plugins'); -var utils = require('../../public/src/utils'); +var utils = require('../utils'); module.exports = function (User) { var iconBackgrounds = ['#f44336', '#e91e63', '#9c27b0', '#673ab7', '#3f51b5', '#2196f3', diff --git a/src/user/digest.js b/src/user/digest.js index 73cd773713..bf11e23a65 100644 --- a/src/user/digest.js +++ b/src/user/digest.js @@ -10,7 +10,7 @@ var user = require('../user'); var topics = require('../topics'); var plugins = require('../plugins'); var emailer = require('../emailer'); -var utils = require('../../public/src/utils'); +var utils = require('../utils'); (function (Digest) { Digest.execute = function (interval, callback) { diff --git a/src/user/email.js b/src/user/email.js index 8b8919c73a..d0cdbbed3c 100644 --- a/src/user/email.js +++ b/src/user/email.js @@ -5,8 +5,8 @@ var async = require('async'); var nconf = require('nconf'); var user = require('../user'); -var utils = require('../../public/src/utils'); -var translator = require('../../public/src/modules/translator'); +var utils = require('../utils'); +var translator = require('../translator'); var plugins = require('../plugins'); var db = require('../database'); var meta = require('../meta'); diff --git a/src/user/invite.js b/src/user/invite.js index 4e41b824a5..b7d62bdc7b 100644 --- a/src/user/invite.js +++ b/src/user/invite.js @@ -7,8 +7,8 @@ var nconf = require('nconf'); var db = require('./../database'); var meta = require('../meta'); var emailer = require('../emailer'); -var translator = require('../../public/src/modules/translator'); -var utils = require('../../public/src/utils'); +var translator = require('../translator'); +var utils = require('../utils'); module.exports = function (User) { diff --git a/src/user/profile.js b/src/user/profile.js index b2b3b7eb5f..e6ac5adf0f 100644 --- a/src/user/profile.js +++ b/src/user/profile.js @@ -4,7 +4,7 @@ var async = require('async'); var S = require('string'); -var utils = require('../../public/src/utils'); +var utils = require('../utils'); var meta = require('../meta'); var db = require('../database'); var groups = require('../groups'); diff --git a/src/user/reset.js b/src/user/reset.js index ba0e18b513..25bd4118f9 100644 --- a/src/user/reset.js +++ b/src/user/reset.js @@ -5,8 +5,8 @@ var nconf = require('nconf'); var winston = require('winston'); var user = require('../user'); -var utils = require('../../public/src/utils'); -var translator = require('../../public/src/modules/translator'); +var utils = require('../utils'); +var translator = require('../translator'); var db = require('../database'); var meta = require('../meta'); diff --git a/src/utils.js b/src/utils.js new file mode 100644 index 0000000000..8913909584 --- /dev/null +++ b/src/utils.js @@ -0,0 +1,3 @@ +'use strict'; + +module.exports = require('../public/src/utils'); diff --git a/src/widgets/index.js b/src/widgets/index.js index 7ea6727a1e..2e439c52d8 100644 --- a/src/widgets/index.js +++ b/src/widgets/index.js @@ -5,7 +5,7 @@ var winston = require('winston'); var templates = require('templates.js'); var plugins = require('../plugins'); -var translator = require('../../public/src/modules/translator'); +var translator = require('../translator'); var db = require('../database'); var widgets = {}; diff --git a/test/translator.js b/test/translator.js index 196d5ce56f..e7f69ccdf0 100644 --- a/test/translator.js +++ b/test/translator.js @@ -145,6 +145,27 @@ describe('new Translator(language)', function () { assert.strictEqual(translated, 'Latest Users'); }); }); + + it('should use key for unknown keys without arguments', function () { + var translator = Translator.create('en-GB'); + return translator.translate('[[unknown:key.without.args]]').then(function (translated) { + assert.strictEqual(translated, 'key.without.args'); + }); + }); + + it('should use backup for unknown keys with arguments', function () { + var translator = Translator.create('en-GB'); + return translator.translate('[[unknown:key.with.args, arguments are here, derpity, derp]]').then(function (translated) { + assert.strictEqual(translated, '[[unknown:key.with.args, arguments are here, derpity, derp[['); + }); + }); + + it('should ignore unclosed tokens', function () { + var translator = Translator.create('en-GB'); + return translator.translate('here is some stuff and other things [[abc:xyz, other random stuff should be fine here [[global:home]] and more things [[pages:users/latest]]').then(function (translated) { + assert.strictEqual(translated, 'here is some stuff and other things [[abc:xyz, other random stuff should be fine here Home and more things Latest Users'); + }); + }); }); });