mirror of
https://github.com/NodeBB/NodeBB.git
synced 2026-05-06 10:17:15 +02:00
Merge branch master into develop
This commit is contained in:
8
.github/CONTRIBUTING.md
vendored
8
.github/CONTRIBUTING.md
vendored
@@ -38,11 +38,9 @@ There is a chance that the issue you are experiencing may have already been fixe
|
||||
You can find the NodeBB version number in the Admin Control Panel (ACP), as well as the first line output to the shell when running NodeBB
|
||||
|
||||
``` plaintext
|
||||
info: NodeBB v0.5.2-dev Copyright (C) 2013-2014 NodeBB Inc.
|
||||
info: This program comes with ABSOLUTELY NO WARRANTY.
|
||||
info: This is free software, and you are welcome to redistribute it under certain conditions.
|
||||
info:
|
||||
info: Time: Tue Oct 07 2014 20:25:20 GMT-0400 (EDT)
|
||||
3/4 12:38:57 [10752] - info: NodeBB v1.4.5 Copyright (C) 2013-2017 NodeBB Inc.
|
||||
3/4 12:38:57 [10752] - info: This program comes with ABSOLUTELY NO WARRANTY.
|
||||
3/4 12:38:57 [10752] - info: This is free software, and you are welcome to redistribute it under certain conditions.
|
||||
```
|
||||
|
||||
If you are running NodeBB via git, it is also helpful to let the maintainers know what commit hash you are on. To find the commit hash, execute the following command:
|
||||
|
||||
6
app.js
6
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('__');
|
||||
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
70
nodebb
70
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,12 +32,15 @@ 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', {
|
||||
fs.readFile(path.join(__dirname, 'pidfile'), {
|
||||
encoding: 'utf-8',
|
||||
}, function (err, pid) {
|
||||
if (err) {
|
||||
@@ -58,7 +70,7 @@ function getCurrentVersion(callback) {
|
||||
});
|
||||
}
|
||||
function fork(args) {
|
||||
return cproc.fork('app.js', args, {
|
||||
return cproc.fork(appPath, args, {
|
||||
cwd: __dirname,
|
||||
silent: false,
|
||||
});
|
||||
@@ -72,7 +84,7 @@ function getInstalledPlugins(callback) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
var isNbbModule = /^nodebb-(?:plugin|theme|widget|rewards)-[\w\-]+$/;
|
||||
var isNbbModule = /^nodebb-(?:plugin|theme|widget|rewards)-[\w-]+$/;
|
||||
var moduleName;
|
||||
var isGitRepo;
|
||||
|
||||
@@ -170,21 +182,21 @@ function checkPlugins(standalone, callback) {
|
||||
body = [body];
|
||||
}
|
||||
|
||||
var current,
|
||||
suggested,
|
||||
upgradable = body.map(function (suggestObj) {
|
||||
current = payload.plugins[suggestObj.package];
|
||||
suggested = suggestObj.version;
|
||||
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);
|
||||
if (suggestObj.code === 'match-found' && semver.gt(suggested, current)) {
|
||||
return {
|
||||
name: suggestObj.package,
|
||||
current: current,
|
||||
suggested: suggested,
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}).filter(Boolean);
|
||||
|
||||
next(null, upgradable);
|
||||
});
|
||||
@@ -200,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);
|
||||
}
|
||||
|
||||
@@ -280,7 +292,7 @@ var commands = {
|
||||
process.stdout.write(' "' + './nodebb restart'.yellow + '" to restart NodeBB\n\n'.reset);
|
||||
|
||||
// Spawn a new NodeBB process
|
||||
cproc.fork(__dirname + '/loader.js', {
|
||||
cproc.fork(loaderPath, {
|
||||
env: process.env,
|
||||
});
|
||||
},
|
||||
@@ -334,7 +346,7 @@ var commands = {
|
||||
process.stdout.write('\n\n'.reset);
|
||||
|
||||
// Spawn a new NodeBB process
|
||||
cproc.fork(__dirname + '/loader.js', {
|
||||
cproc.fork(loaderPath, {
|
||||
env: process.env,
|
||||
});
|
||||
cproc.spawn('tail', ['-F', './logs/output.log'], {
|
||||
@@ -348,7 +360,7 @@ var commands = {
|
||||
usage: 'Usage: ' + './nodebb dev'.yellow,
|
||||
handler: function () {
|
||||
process.env.NODE_ENV = 'development';
|
||||
cproc.fork(__dirname + '/loader.js', ['--no-daemon', '--no-silent'], {
|
||||
cproc.fork(loaderPath, ['--no-daemon', '--no-silent'], {
|
||||
env: process.env,
|
||||
});
|
||||
},
|
||||
|
||||
@@ -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"
|
||||
@@ -95,7 +95,8 @@
|
||||
"validator": "^6.1.0",
|
||||
"winston": "^2.1.0",
|
||||
"xml": "^1.0.1",
|
||||
"xregexp": "~3.1.0"
|
||||
"xregexp": "~3.1.0",
|
||||
"zxcvbn": "^4.4.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"coveralls": "^2.11.14",
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
"min-username-length": "Minimum Username Length",
|
||||
"max-username-length": "Maximum Username Length",
|
||||
"min-password-length": "Minimum Password Length",
|
||||
"min-password-strength": "Minimum Password Strength",
|
||||
"max-about-me-length": "Maximum About Me Length",
|
||||
"terms-of-use": "Forum Terms of Use <small>(Leave blank to disable)</small>",
|
||||
"user-search": "User Search",
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
"username_taken_workaround": "The username you requested was already taken, so we have altered it slightly. You are now known as <strong>%1</strong>",
|
||||
"password_same_as_username": "Your password is the same as your username, please select another password.",
|
||||
"password_same_as_email": "Your password is the same as your email, please select another password.",
|
||||
"weak_password": "Weak password.",
|
||||
|
||||
"upload_picture": "Upload picture",
|
||||
"upload_a_picture": "Upload a picture",
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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.",
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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?"
|
||||
}
|
||||
@@ -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",
|
||||
|
||||
@@ -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 <strong>%1</strong>",
|
||||
"upvoted_your_post_in": "<strong>%1</strong> heeft voor een bericht gestemd in <strong>%2</strong>.",
|
||||
@@ -28,9 +28,9 @@
|
||||
"user_flagged_post_in": "<strong>%1</strong> rapporteerde een bericht in <strong>%2</strong>",
|
||||
"user_flagged_post_in_dual": "<strong>%1</strong> en <strong>%2</strong> rapporteerde een bericht in <strong>%3</strong>",
|
||||
"user_flagged_post_in_multiple": "<strong>%1</strong> en %2 andere rapporteede een bericht in <strong>%3</strong>",
|
||||
"user_flagged_user": "<strong>%1</strong> flagged a user profile (%2)",
|
||||
"user_flagged_user_dual": "<strong>%1</strong> and <strong>%2</strong> flagged a user profile (%3)",
|
||||
"user_flagged_user_multiple": "<strong>%1</strong> and %2 others flagged a user profile (%3)",
|
||||
"user_flagged_user": "<strong>%1</strong> markeerde een gebruikersprofiel (%2)",
|
||||
"user_flagged_user_dual": "<strong>%1</strong> en <strong>%2</strong> markeerden een gebruikersprofiel (%3)",
|
||||
"user_flagged_user_multiple": "<strong>%1</strong> en %2 anderen markeerde een gebruikersprofiel (%3)",
|
||||
"user_posted_to": "<strong>%1</strong> heeft een reactie geplaatst in <strong>%2</strong>",
|
||||
"user_posted_to_dual": "<strong>%1</strong> en <strong>%2</strong> hebben een reactie geplaatst in: <strong>%3</strong>",
|
||||
"user_posted_to_multiple": "<strong>%1</strong> en %2 hebben een reactie geplaatst in: <strong>%3</strong>",
|
||||
@@ -40,7 +40,7 @@
|
||||
"user_started_following_you_multiple": "<strong%1>%1</strong> en %2 andere volgen jou nu.",
|
||||
"new_register": "<strong>%1</strong> heeft een registratie verzoek aangevraagd.",
|
||||
"new_register_multiple": "Er is/zijn <strong>%1</strong> registratieverzoek(en) die wacht(en) op goedkeuring.",
|
||||
"flag_assigned_to_you": "<strong>Flag %1</strong> has been assigned to you",
|
||||
"flag_assigned_to_you": "<strong>Flag %1</strong> 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.",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -10,17 +10,17 @@
|
||||
"return_to": "Geri dön.",
|
||||
"new_notification": "Yeni bildirim",
|
||||
"you_have_unread_notifications": "Okunmamış bildirimleriniz var.",
|
||||
"all": "All",
|
||||
"topics": "Topics",
|
||||
"replies": "Replies",
|
||||
"chat": "Chats",
|
||||
"follows": "Follows",
|
||||
"all": "Hepsi",
|
||||
"topics": "Başlıklar",
|
||||
"replies": "Yanıtlar",
|
||||
"chat": "Sohbetler",
|
||||
"follows": "Takip ediyor",
|
||||
"upvote": "Upvotes",
|
||||
"new-flags": "New Flags",
|
||||
"new-flags": "Yeni Bayrak",
|
||||
"my-flags": "Flags assigned to me",
|
||||
"bans": "Bans",
|
||||
"bans": "Yasaklamalar",
|
||||
"new_message_from": "<strong>%1</strong> size bir mesaj gönderdi",
|
||||
"upvoted_your_post_in": "<strong>%1</strong> iletinizi beğendi. <strong>%2</strong>",
|
||||
"upvoted_your_post_in": "<strong>%1</strong> iletinizi beğendi. <strong>%2</strong>.",
|
||||
"upvoted_your_post_in_dual": "<strong>%1</strong> ve <strong>%2</strong> <strong>%3</strong> içindeki gönderini beğendi.",
|
||||
"upvoted_your_post_in_multiple": "<strong>%1</strong> ve %2 iki kişi daha <strong>%3</strong> içindeki gönderini beğendi.",
|
||||
"moved_your_post": "<strong>%1</strong> senin iletin <strong>%2</strong> taşındı",
|
||||
|
||||
@@ -131,5 +131,5 @@
|
||||
"info.email-history": "Email Geçmişi",
|
||||
"info.moderation-note": "Moderasyon Notu",
|
||||
"info.moderation-note.success": "Moderasyon notu kaydedildi",
|
||||
"info.moderation-note.add": "Add note"
|
||||
"info.moderation-note.add": "Not ekle"
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"daily": "Щоденні скарги",
|
||||
"by-user": "Скарги за користувачем",
|
||||
"by-user-search": "Пошук оскаржених постів за іменем користувача",
|
||||
"category": "Категорія",
|
||||
"sort-by": "Сортувати за",
|
||||
"sort-by.most-flags": "Найбільше скарг",
|
||||
"sort-by.most-recent": "Найсвіжіші",
|
||||
"search": "Шукати",
|
||||
"dismiss-all": "Відхилити всі",
|
||||
"none-flagged": "Скарг немає!",
|
||||
"posted-in": "Запощено в %1",
|
||||
"read-more": "Читати далі",
|
||||
"flagged-x-times": "Цей пост було оскаржено %1 раз(ів):",
|
||||
"dismiss": "Відхилити цю скаргу",
|
||||
"delete-post": "Видалити цей пост",
|
||||
|
||||
"alerts.confirm-delete-post": "Ви точно бажаєте видалити цей пост?"
|
||||
}
|
||||
@@ -131,5 +131,5 @@
|
||||
"info.email-history": "Історія електронної пошти",
|
||||
"info.moderation-note": "Коментар модератора",
|
||||
"info.moderation-note.success": "Коментар модератора збережено",
|
||||
"info.moderation-note.add": "Add note"
|
||||
"info.moderation-note.add": "Додати коментар"
|
||||
}
|
||||
@@ -26,7 +26,7 @@
|
||||
"redis.blocked-clients": "阻止的客户端",
|
||||
"redis.used-memory": "已使用内存",
|
||||
"redis.memory-frag-ratio": "内存碎片比率",
|
||||
"redis.total-connections-recieved": "已接收连接总数",
|
||||
"redis.total-connections-recieved": "已接收的连接总数",
|
||||
"redis.total-commands-processed": "已执行命令总数",
|
||||
"redis.iops": "每秒实时操作数",
|
||||
"redis.keyspace-hits": "Keyspace 命中",
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
"events": "事件",
|
||||
"no-events": "暂无事件。",
|
||||
"control-panel": "事件控制面板",
|
||||
"delete-events": "删除事件"
|
||||
"delete-events": "清除事件"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"installed": "已安装",
|
||||
"active": "生效中",
|
||||
"active": "激活",
|
||||
"inactive": "未生效",
|
||||
"out-of-date": "已过期",
|
||||
"none-found": "无插件。",
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
"container.alert": "警报",
|
||||
|
||||
"alert.confirm-delete": "确认删除此窗口部件?",
|
||||
"alert.updated": "窗口部件升级",
|
||||
"alert.update-success": "已成功升级窗口部件"
|
||||
"alert.updated": "窗口部件更新",
|
||||
"alert.update-success": "已成功更新窗口部件"
|
||||
|
||||
}
|
||||
@@ -33,7 +33,7 @@
|
||||
"control-panel": "系统控制",
|
||||
"reload": "重载",
|
||||
"restart": "重启",
|
||||
"restart-warning": "重新载入或重启 NodeBB 会丢弃数秒内所有的连接。",
|
||||
"restart-warning": "重载或重启 NodeBB 会丢失数秒内所有的连接。",
|
||||
"maintenance-mode": "维护模式",
|
||||
"maintenance-mode-title": "点击此处设置 NodeBB 的维护模式",
|
||||
"realtime-chart-updates": "实时图表更新",
|
||||
|
||||
@@ -12,57 +12,57 @@
|
||||
"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.warning": "<strong>注意</ strong>:权限设置会立即生效。 调整这些设置后,无需保存。",
|
||||
"privileges.description": "您可以在此部分中配置此版块的访问控制权限。 可以根据每个用户或每个组授予权限。 您可以通过在下面的表格中搜索,将新用户添加到此表中。",
|
||||
"privileges.warning": "<strong>注意</strong>:权限设置会立即生效。 调整这些设置后,无需保存。",
|
||||
"privileges.section-viewing": "查看权限",
|
||||
"privileges.section-posting": "发帖权限",
|
||||
"privileges.section-moderation": "审核权限",
|
||||
"privileges.section-user": "用户",
|
||||
"privileges.search-user": "添加用户",
|
||||
"privileges.no-users": "此类别中没有用户特定的权限。",
|
||||
"privileges.section-group": "用户组",
|
||||
"privileges.group-private": "这个用户组是私密的",
|
||||
"privileges.search-group": "添加用户组",
|
||||
"privileges.no-users": "此版块中没有用户特定的权限。",
|
||||
"privileges.section-group": "群组",
|
||||
"privileges.group-private": "这个群组是私密的",
|
||||
"privileges.search-group": "添加群组",
|
||||
"privileges.copy-to-children": "复制到子版块",
|
||||
"privileges.copy-from-category": "从板块复制",
|
||||
"privileges.inherit": "如果 <code>registered-users</ code> 组被授予特定权限,所有其他组都会收到<strong>隐式权限</ strong>,即使它们未被明确定义/检查。 将显示此隐式权限,因为所有用户都是 <code>registered-users</ code> 用户组的一部分,因此无需显式授予其他组的权限。",
|
||||
"privileges.copy-from-category": "从版块复制",
|
||||
"privileges.inherit": "如果 <code>registered-users</code> 组被授予特定权限,所有其他组都会收到<strong>隐式权限</strong>,即使它们未被明确定义/检查。 将显示此隐式权限,因为所有用户都是 <code>registered-users</code> 群组的一部分,因此无需显式授予其他组的权限。",
|
||||
|
||||
"analytics.back": "返回板块列表",
|
||||
"analytics.title": "“%1”板块的统计",
|
||||
"analytics.pageviews-hourly": "<strong>图1 </ strong> – 此板块的每小时页面浏览量</ small>",
|
||||
"analytics.pageviews-daily": "<strong>图2 </ strong> – 此板块的每日页面浏览量</small>",
|
||||
"analytics.topics-daily": "<strong>图3 </ strong> – 每日在此板块中创建的主题</ small>",
|
||||
"analytics.posts-daily": "<strong>图4 </ strong> – 每日在此板块中每日发布的帖子</ small>",
|
||||
"analytics.back": "返回版块列表",
|
||||
"analytics.title": "“%1”版块的统计",
|
||||
"analytics.pageviews-hourly": "<strong>图1</strong> – 此版块的每小时页面浏览量</small>",
|
||||
"analytics.pageviews-daily": "<strong>图2</strong> – 此版块的每日页面浏览量</small>",
|
||||
"analytics.topics-daily": "<strong>图3</strong> – 每日在此版块中创建的主题</small>",
|
||||
"analytics.posts-daily": "<strong>图4</strong> – 每日在此版块中每日发布的帖子</small>",
|
||||
|
||||
"alert.created": "创建",
|
||||
"alert.create-success": "板块创建成功!",
|
||||
"alert.none-active": "您没有有效的板块。",
|
||||
"alert.create": "创建一个板块",
|
||||
"alert.confirm-moderate": "<strong>您确定要将审核权限授予此用户组吗?</ strong>此群组是公开的,任何用户都可以随意加入。",
|
||||
"alert.confirm-purge": "<p class =“lead”>您确定要清除此板块“%1”吗?</ p> <h5> <strong class =“text-danger”>警告!</ strong> 板块将被清除!</ h5> <p class =“help-block”>清除板块将删除所有主题和帖子,并从数据库中删除板块。 如果您想<em>暂时</ em>移除板块,请使用停用板块。</ p>",
|
||||
"alert.purge-success": "板块已删除!",
|
||||
"alert.create-success": "版块创建成功!",
|
||||
"alert.none-active": "您没有有效的版块。",
|
||||
"alert.create": "创建一个版块",
|
||||
"alert.confirm-moderate": "<strong>您确定要将审核权限授予此群组吗?</strong>此群组是公开的,任何用户都可以随意加入。",
|
||||
"alert.confirm-purge": "<p class =“lead”>您确定要清除此版块“%1”吗?</p> <h5> <strong class =“text-danger”>警告!</strong> 版块将被清除!</h5> <p class =“help-block”>清除版块将删除所有主题和帖子,并从数据库中删除版块。 如果您想<em>暂时</em>移除版块,请使用停用版块。</p>",
|
||||
"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": "查找用户组",
|
||||
"alert.group-search": "在此处搜索用户组..."
|
||||
"alert.find-group": "查找群组",
|
||||
"alert.group-search": "在此处搜索群组..."
|
||||
}
|
||||
@@ -1,31 +1,31 @@
|
||||
{
|
||||
"name": "用户组名",
|
||||
"description": "用户组描述",
|
||||
"system": "系统用户组",
|
||||
"name": "群组名",
|
||||
"description": "群组描述",
|
||||
"system": "系统群组",
|
||||
"edit": "编辑",
|
||||
"search-placeholder": "索索",
|
||||
"create": "创建用户组",
|
||||
"description-placeholder": "一个关于你的用户组的简短描述",
|
||||
"search-placeholder": "搜索",
|
||||
"create": "创建群组",
|
||||
"description-placeholder": "一个关于你的群组的简短描述",
|
||||
"create-button": "创建",
|
||||
|
||||
"alerts.create-failure": "<strong>哦不!</strong><p>创建您的用户组时出现问题。 请稍后再试!</p>",
|
||||
"alerts.confirm-delete": "确认要删除这个用户组么?",
|
||||
"alerts.create-failure": "<strong>哦不!</strong><p>创建您的群组时出现问题。 请稍后再试!</p>",
|
||||
"alerts.confirm-delete": "确认要删除这个群组么?",
|
||||
|
||||
"edit.name": "名字",
|
||||
"edit.description": "描述",
|
||||
"edit.user-title": "成员标题",
|
||||
"edit.icon": "用户组标志",
|
||||
"edit.icon": "群组标志",
|
||||
"edit.label-color": "群组标签颜色",
|
||||
"edit.show-badge": "显示徽章",
|
||||
"edit.private-details": "启用此选项后,加入用户组的请求将需要组长审批。",
|
||||
"edit.private-override": "警告:系统禁用了私有用户组,优先于该选项。",
|
||||
"edit.private-details": "启用此选项后,加入群组的请求将需要群组所有者审批。",
|
||||
"edit.private-override": "警告:系统已禁用了私有群组,优先级高于该选项。",
|
||||
"edit.disable-requests": "禁止加入请求",
|
||||
"edit.hidden": "隐藏",
|
||||
"edit.hidden-details": "启用此选项后,此用户组将不在用户组列表展现,并且用户只能被手动邀请加入",
|
||||
"edit.add-user": "向此小组添加成员",
|
||||
"edit.hidden-details": "启用此选项后,此群组将不在群组列表展现,并且用户只能被手动邀请加入",
|
||||
"edit.add-user": "向此群组添加成员",
|
||||
"edit.add-user-search": "搜索用户",
|
||||
"edit.members": "成员列表",
|
||||
"control-panel": "小组控制面板",
|
||||
"control-panel": "群组控制面板",
|
||||
"revert": "重置",
|
||||
|
||||
"edit.no-users-found": "没有找到用户",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"queue": "队列",
|
||||
"description": "注册队列里面没有用户。<br>要开启这项功能,请去<a href=\"%1\">设置 → 用户 → 用户注册</a> 并设置<strong>注册类型</strong>为“管理员批准”。",
|
||||
"queue": "申请",
|
||||
"description": "注册申请队列里面还没有用户申请。<br>要开启这项功能,请去<a href=\"%1\">设置 → 用户 → 用户注册</a> 并设置<strong>注册类型</strong>为“管理员批准”。",
|
||||
|
||||
"list.name": "姓名",
|
||||
"list.email": "邮件",
|
||||
|
||||
@@ -30,9 +30,9 @@
|
||||
"search.username": "通过用户名",
|
||||
"search.username-placeholder": "输入你想找的用户名",
|
||||
"search.email": "通过邮箱",
|
||||
"search.email-placeholder": "输入你想找的邮箱地址",
|
||||
"search.email-placeholder": "输入你想查询的邮箱地址",
|
||||
"search.ip": "通过IP地址",
|
||||
"search.ip-placeholder": "输入你想找的IP",
|
||||
"search.ip-placeholder": "输入你想查询的IP",
|
||||
"search.not-found": "未找到用户!",
|
||||
|
||||
"inactive.3-months": "3个月",
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
"manage/categories": "版块",
|
||||
"manage/tags": "话题",
|
||||
"manage/users": "用户",
|
||||
"manage/registration": "注册队列",
|
||||
"manage/groups": "用户组",
|
||||
"manage/registration": "注册申请",
|
||||
"manage/groups": "群组",
|
||||
"manage/ip-blacklist": "IP 黑名单",
|
||||
|
||||
"section-settings": "设置",
|
||||
@@ -20,7 +20,7 @@
|
||||
"settings/reputation": "声望",
|
||||
"settings/email": "邮件",
|
||||
"settings/user": "用户",
|
||||
"settings/group": "用户组",
|
||||
"settings/group": "群组",
|
||||
"settings/guest": "游客",
|
||||
"settings/uploads": "上传",
|
||||
"settings/post": "发帖",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"headers.acam": "Access-Control-Allow-Methods",
|
||||
"headers.acah": "Access-Control-Allow-Headers",
|
||||
"traffic-management": "流量管理",
|
||||
"traffic.help": "NodeBB 拥有在高流量情况下自动拒绝请求的模块。 您可以在这里调整这些设置,虽然默认值就很棒。",
|
||||
"traffic.help": "NodeBB 拥有在高流量情况下自动拒绝请求的模块。尽管默认值就很棒,但您可以在这里调整这些设置。",
|
||||
"traffic.enable": "启用流量管理",
|
||||
"traffic.event-lag": "事件循环滞后阈值(毫秒)",
|
||||
"traffic.event-lag-help": "降低此值会减少页面加载的等待时间,但也会向更多用户显示“过载”消息。(需要重新启动)",
|
||||
|
||||
@@ -6,6 +6,6 @@
|
||||
"consent.link-text": "政策链接文本",
|
||||
"consent.blank-localised-default": "留空以便使用 NodeBB 本地默认值",
|
||||
"settings": "设置",
|
||||
"cookie-domain": "会话 cookie 域名",
|
||||
"cookie-domain": "Session cookie 域名",
|
||||
"blank-default": "留空以保持默认"
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"general": "通用",
|
||||
"private-groups": "私有用户组",
|
||||
"private-groups.help": "启用此选项后,加入用户组需要组长审批<em>(默认启用)</em>。",
|
||||
"private-groups.warning": "<strong>注意!</strong>如果这个选项未启用并且你有私有用户组,那么你的用户组将变为公共的。",
|
||||
"allow-creation": "允许创建用户组",
|
||||
"allow-creation-help": "如果启用,用户就可以创建用户组<em>(默认:不启用)</em>",
|
||||
"max-name-length": "用户组名字的最大长度",
|
||||
"cover-image": "用户组封面图片",
|
||||
"private-groups": "私有群组",
|
||||
"private-groups.help": "启用此选项后,加入用户组需要群组所有者审批<em>(默认启用)</em>。",
|
||||
"private-groups.warning": "<strong>注意!</strong>如果这个选项未启用并且你有私有群组,那么你的群组将变为公共的。",
|
||||
"allow-creation": "允许创建群组",
|
||||
"allow-creation-help": "如果启用,用户就可以创建群组<em>(默认:不启用)</em>",
|
||||
"max-name-length": "群组名字的最大长度",
|
||||
"cover-image": "群组封面图片",
|
||||
"default-cover": "默认封面图片",
|
||||
"default-cover-help": "为没有上传封面图片的群组添加以逗号分隔的默认封面图片"
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
"enable": "在主题和帖子使用分页替代无限滚动浏览。",
|
||||
"topics": "话题分页",
|
||||
"posts-per-page": "每页帖子数",
|
||||
"categories": "板块分页",
|
||||
"categories": "版块分页",
|
||||
"topics-per-page": "每页主题数",
|
||||
"initial-num-load": "最初加载未读,最新,热门的话题"
|
||||
}
|
||||
@@ -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) 。<br /> <em> (默认值:<code> 30 </code>或一个月) 。 设置为0可始终显示日期,留空以始终显示相对时间。</em>",
|
||||
"teaser": "预览帖子",
|
||||
"teaser.last-post": "最后– 显示最新的帖子,包括原帖,如果没有回复",
|
||||
@@ -35,7 +35,7 @@
|
||||
"signature.no-images": "禁用签名中的图片",
|
||||
"signature.max-length": "签名最大长度",
|
||||
"composer": "编辑器设置",
|
||||
"composer-help": "以下设置控制所示后期编辑器的功能和/或外观\n\\t\\t\\t\\t当用户创建新主题或回复现有主题时。",
|
||||
"composer-help": "以下设置控制所示后期编辑器的功能和/或外观\n\t\t\t\t当用户创建新主题或回复现有主题时。",
|
||||
"composer.show-help": "显示“帮助”选项卡",
|
||||
"composer.enable-plugin-help": "允许插件将内容添加到帮助选项卡",
|
||||
"composer.custom-help": "自定义帮助文本",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"disable": "禁用声望系统",
|
||||
"disable-down-voting": "禁用 踩",
|
||||
"votes-are-public": "所有投票是公开的",
|
||||
"thresholds": "活动阈值",
|
||||
"thresholds": "活动闸值",
|
||||
"min-rep-downvote": "踩帖子所需要声望的最小值",
|
||||
"min-rep-flag": "举报帖子需要的最小声望"
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
"allow-files": "允许用户上传普通文件",
|
||||
"private": "使上传的文件私有化",
|
||||
"max-image-width": "缩小图片到指定宽度(单位像素)",
|
||||
"max-image-width-help": "(像素单位,默认760像素,设置为0以禁用)",
|
||||
"max-image-width-help": "(像素单位,默认 760 px,设置为0以禁用)",
|
||||
"max-file-size": "最大文件尺寸(单位 KiB)",
|
||||
"max-file-size-help": "(单位 KiB,默认2048KiB)",
|
||||
"allow-topic-thumbnails": "允许用户上传主题缩略图",
|
||||
@@ -16,11 +16,11 @@
|
||||
"default-avatar": "访客默认头像",
|
||||
"upload": "上传",
|
||||
"profile-image-dimension": "个人资料相片尺寸",
|
||||
"profile-image-dimension-help": "(使用像素作为单位,默认:128px)",
|
||||
"profile-image-dimension-help": "(使用 px 作为单位,默认:128px)",
|
||||
"max-profile-image-size": "个人资料相片最大大小",
|
||||
"max-profile-image-size-help": "(单位KiB,默认256KiB)",
|
||||
"max-profile-image-size-help": "(单位 KiB ,默认256KiB)",
|
||||
"max-cover-image-size": "最大封面图片文件大小",
|
||||
"max-cover-image-size-help": "(单位kb,默认:2048KiB)",
|
||||
"max-cover-image-size-help": "(单位 KiB ,默认:2048KiB)",
|
||||
"keep-all-user-images": "在服务器上保留旧头像和旧的资料封面",
|
||||
"profile-covers": "资料封面",
|
||||
"default-covers": "默认封面图片",
|
||||
|
||||
@@ -33,11 +33,11 @@
|
||||
"registration-type.help": "通常 - 用户可以通过/register页面注册<br/>\n管理员批准 - 用户注册请求会被放入 <a href=\"%1/admin/manage/registration\">请求队列</a> 待管理员批准。<br/>\n管理员批准 IP地址 - 新用户不受影响,已存在帐户的IP地址注册需要管理员批准。<br/>\n邀请制 - 用户可以通过 <a href=\"%1/users\" target=\"_blank\">用户</a> 页面邀请其它用户。<br/>\n管理员邀请制 - 只有管理员可以通过 <a href=\"%1/users\" target=\"_blank\">用户</a> 和 <a href=\"%1/admin/manage/users\">admin/manage/users</a> 页面邀请其它用户。<br/>\n无注册 - 不开放用户注册。<br/>",
|
||||
"registration.max-invites": "每个用户最大邀请数",
|
||||
"max-invites": "每个用户最大邀请数",
|
||||
"max-invites-help": "无限制填0。管理员没有邀请限制<br>仅在邀请制时可用",
|
||||
"max-invites-help": "无限制填 0 。管理员没有邀请限制<br>仅在邀请制时可用",
|
||||
"min-username-length": "最小用户名长度",
|
||||
"max-username-length": "最大用户名长度",
|
||||
"min-password-length": "最小密码长度",
|
||||
"max-about-me-length": "最大自我介绍长度",
|
||||
"max-about-me-length": "自我介绍的最大长度",
|
||||
"terms-of-use": "论坛使用条款 <small>(留空即可禁用)</small>",
|
||||
"user-search": "用户搜索",
|
||||
"user-search-results-per-page": "展示的结果数量",
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"invalid-pid": "无效帖子 ID",
|
||||
"invalid-uid": "无效用户 ID",
|
||||
"invalid-username": "无效用户名",
|
||||
"invalid-email": "无效电子邮箱",
|
||||
"invalid-email": "无效的电子邮箱",
|
||||
"invalid-title": "无效标题!",
|
||||
"invalid-user-data": "无效用户数据",
|
||||
"invalid-password": "无效密码",
|
||||
@@ -29,14 +29,14 @@
|
||||
"username-too-long": "用户名太长",
|
||||
"password-too-long": "密码太长",
|
||||
"user-banned": "用户已禁止",
|
||||
"user-banned-reason": "抱歉,此帐号已经被封号 (原因:%1)",
|
||||
"user-banned-reason": "抱歉,此帐号已经被封禁 (原因:%1)",
|
||||
"user-too-new": "抱歉,您需要等待 %1 秒后,才可以发帖!",
|
||||
"blacklisted-ip": "对不起,您的 IP 地址已被社区禁用。如果您认为这是一个错误,请与管理员联系。",
|
||||
"ban-expiry-missing": "请提供此次禁言结束日期",
|
||||
"no-category": "版块不存在",
|
||||
"no-topic": "主题不存在",
|
||||
"no-post": "帖子不存在",
|
||||
"no-group": "用户组不存在",
|
||||
"no-group": "群组不存在",
|
||||
"no-user": "用户不存在",
|
||||
"no-teaser": "主题预览不存在",
|
||||
"no-privileges": "您没有权限执行此操作。",
|
||||
@@ -80,13 +80,13 @@
|
||||
"invalid-image-type": "无效的图像类型。允许的类型有:%1",
|
||||
"invalid-image-extension": "无效的图像扩展",
|
||||
"invalid-file-type": "无效文件格式,允许的格式有:%1",
|
||||
"group-name-too-short": "用户组名太短",
|
||||
"group-name-too-long": "用户组名太长",
|
||||
"group-already-exists": "用户组已存在",
|
||||
"group-name-change-not-allowed": "不允许更改用户组名称",
|
||||
"group-already-member": "已经是此用户组的成员",
|
||||
"group-not-member": "不是此用户组的成员",
|
||||
"group-needs-owner": "用户组需要指定至少一名组长",
|
||||
"group-name-too-short": "群组名太短",
|
||||
"group-name-too-long": "群组名太长",
|
||||
"group-already-exists": "群组已存在",
|
||||
"group-name-change-not-allowed": "不允许更改群组名称",
|
||||
"group-already-member": "已经是此群组的成员",
|
||||
"group-not-member": "不是此群组的成员",
|
||||
"group-needs-owner": "群组需要指定至少一名群组所有者",
|
||||
"group-already-invited": "您已邀请该用户",
|
||||
"group-already-requested": "已提交您的请求",
|
||||
"post-already-deleted": "此帖已被删除",
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
"header.tags": "话题",
|
||||
"header.popular": "热门",
|
||||
"header.users": "会员",
|
||||
"header.groups": "用户组",
|
||||
"header.groups": "群组",
|
||||
"header.chats": "聊天",
|
||||
"header.notifications": "通知",
|
||||
"header.search": "搜索",
|
||||
@@ -45,7 +45,7 @@
|
||||
"alert.success": "成功",
|
||||
"alert.error": "错误",
|
||||
"alert.banned": "封禁",
|
||||
"alert.banned.message": "您刚刚被封禁,现在您将退出登录。",
|
||||
"alert.banned.message": "您刚刚被封禁了,现在您将登出站点。",
|
||||
"alert.unfollow": "您已取消关注 %1!",
|
||||
"alert.follow": "您已关注 %1!",
|
||||
"online": "在线",
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
{
|
||||
"groups": "用户组",
|
||||
"view_group": "查看用户组",
|
||||
"owner": "组长",
|
||||
"new_group": "创建用户组",
|
||||
"no_groups_found": "尚无用户组信息",
|
||||
"groups": "群组",
|
||||
"view_group": "查看群组",
|
||||
"owner": "群组所有者",
|
||||
"new_group": "创建群组",
|
||||
"no_groups_found": "尚无群组信息",
|
||||
"pending.accept": "接受",
|
||||
"pending.reject": "拒绝",
|
||||
"pending.accept_all": "接受全部",
|
||||
"pending.reject_all": "拒绝全部",
|
||||
"pending.accept_all": "全部同意",
|
||||
"pending.reject_all": "全部拒绝",
|
||||
"pending.none": "暂时没有待加入的成员",
|
||||
"invited.none": "暂时没有接受邀请的成员",
|
||||
"invited.uninvite": "取消邀请",
|
||||
"invited.search": "选择用户加入用户组",
|
||||
"invited.search": "选择用户加入群组",
|
||||
"invited.notification_title": "您已被邀请加入 <strong>%1</strong>",
|
||||
"request.notification_title": "来自 <strong>%1</strong> 的用户组成员请求",
|
||||
"request.notification_title": "来自 <strong>%1</strong> 的群组成员请求",
|
||||
"request.notification_text": "<strong>%1</strong> 已被邀请加入 <strong>%2</strong>",
|
||||
"cover-save": "保存",
|
||||
"cover-saving": "正在保存",
|
||||
"details.title": "用户组信息",
|
||||
"details.title": "群组信息",
|
||||
"details.members": "成员列表",
|
||||
"details.pending": "待加入成员",
|
||||
"details.invited": "已邀请成员",
|
||||
"details.has_no_posts": "此用户组的会员尚未发表任何帖子。",
|
||||
"details.has_no_posts": "此群组的会员尚未发表任何帖子。",
|
||||
"details.latest_posts": "最新帖子",
|
||||
"details.private": "私有",
|
||||
"details.disableJoinRequests": "禁止申请加入用户组",
|
||||
"details.grant": "授予/取消管理权",
|
||||
"details.kick": "踢出用户组",
|
||||
"details.kick_confirm": "您确定要将此成员从用户组中移除吗?",
|
||||
"details.owner_options": "用户组管理",
|
||||
"details.group_name": "用户组名",
|
||||
"details.kick_confirm": "您确定要将此成员从群组中移除吗?",
|
||||
"details.owner_options": "群组管理",
|
||||
"details.group_name": "群组名",
|
||||
"details.member_count": "用户组成员数",
|
||||
"details.creation_date": "创建时间",
|
||||
"details.description": "描述",
|
||||
@@ -38,21 +38,21 @@
|
||||
"details.change_colour": "更改颜色",
|
||||
"details.badge_text": "徽章文本",
|
||||
"details.userTitleEnabled": "显示组内称号",
|
||||
"details.private_help": "启用此选项后,加入用户组需要组长审批。",
|
||||
"details.private_help": "启用此选项后,加入群组需要组长审批。",
|
||||
"details.hidden": "隐藏",
|
||||
"details.hidden_help": "启用此选项后,用户组将不在用户组列表中展现,成员只能通过邀请加入。",
|
||||
"details.delete_group": "删除用户组",
|
||||
"details.private_system_help": "系统禁用了私有用户组,这个选项不起任何作用",
|
||||
"event.updated": "用户组信息已更新",
|
||||
"event.deleted": "用户组 \"%1\" 已被删除",
|
||||
"details.hidden_help": "启用此选项后,群组将不在群组列表中展现,成员只能通过邀请加入。",
|
||||
"details.delete_group": "删除群组",
|
||||
"details.private_system_help": "系统禁用了私有群组,这个选项不起任何作用",
|
||||
"event.updated": "群组信息已更新",
|
||||
"event.deleted": "群组 \"%1\" 已被删除",
|
||||
"membership.accept-invitation": "接受邀请",
|
||||
"membership.invitation-pending": "邀请中",
|
||||
"membership.join-group": "加入用户组",
|
||||
"membership.leave-group": "退出用户组",
|
||||
"membership.join-group": "加入群组",
|
||||
"membership.leave-group": "退出群组",
|
||||
"membership.reject": "拒绝",
|
||||
"new-group.group_name": "组名: ",
|
||||
"upload-group-cover": "上传组封面",
|
||||
"bulk-invite-instructions": "输入您要邀请加入此用户组的用户名,多个用户以逗号分隔",
|
||||
"new-group.group_name": "群组名: ",
|
||||
"upload-group-cover": "上传群组封面",
|
||||
"bulk-invite-instructions": "输入您要邀请加入此群组的用户名,多个用户以逗号分隔",
|
||||
"bulk-invite": "批量邀请",
|
||||
"remove_group_cover_confirm": "确定要移除封面图片吗?"
|
||||
}
|
||||
@@ -20,7 +20,7 @@
|
||||
"chat.three_months": "3个月",
|
||||
"chat.delete_message_confirm": "确认删除此消息吗?",
|
||||
"chat.add-users-to-room": "向此聊天室中添加成员",
|
||||
"composer.compose": "编写",
|
||||
"composer.compose": "编写帮助",
|
||||
"composer.show_preview": "显示预览",
|
||||
"composer.hide_preview": "隐藏预览",
|
||||
"composer.user_said_in": "%1 在 %2 中说:",
|
||||
@@ -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": "确认",
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
"upvoted_your_post_in_dual": "<strong>%1</strong> 和 <strong>%2</strong> 在 <strong>%3</strong> 赞了您的帖子。",
|
||||
"upvoted_your_post_in_multiple": "<strong>%1</strong> 和 %2 个其他人在 <strong>%3</strong> 赞了您的帖子。",
|
||||
"moved_your_post": "您的帖子已被 <strong>%1</strong> 移动到了 <strong>%2</strong>",
|
||||
"moved_your_topic": "<strong>%1</strong> 移动到了 <strong>%2</strong>",
|
||||
"moved_your_topic": "<strong>%1</strong> 移动了 <strong>%2</strong>",
|
||||
"user_flagged_post_in": "<strong>%1</strong> 在 <strong>%2</strong> 标记了一个帖子",
|
||||
"user_flagged_post_in_dual": "<strong>%1</strong> 和 <strong>%2</strong> 在 <strong>%3</strong> 举报了一个帖子",
|
||||
"user_flagged_post_in_multiple": "<strong>%1</strong> 和 %2 个其他人在 <strong>%3</strong> 举报了一个帖子",
|
||||
@@ -42,7 +42,7 @@
|
||||
"new_register_multiple": "有 <strong>%1</strong> 条注册申请等待批准。",
|
||||
"flag_assigned_to_you": "<strong>举报 %1</strong> 已经被指派给你",
|
||||
"email-confirmed": "电子邮箱已确认",
|
||||
"email-confirmed-message": "感谢您验证您的电子邮箱。您的帐户现已全面激活。",
|
||||
"email-confirm-error-message": "验证您电子邮箱地址时出现了问题。可能是因为验证码无效或已过期。",
|
||||
"email-confirmed-message": "感谢您验证您的电子邮箱。您的帐户现已完全激活。",
|
||||
"email-confirm-error-message": "验证的您电子邮箱地址时出现了问题。可能是因为验证码无效或已过期。",
|
||||
"email-confirm-sent": "确认邮件已发送。"
|
||||
}
|
||||
@@ -22,9 +22,9 @@
|
||||
"registration-complete": "注册完成",
|
||||
"login": "登录帐号",
|
||||
"reset": "重置帐户密码",
|
||||
"categories": "板块",
|
||||
"groups": "用户组",
|
||||
"group": "%1 的用户组",
|
||||
"categories": "版块",
|
||||
"groups": "群组",
|
||||
"group": "%1 的群组",
|
||||
"chats": "聊天",
|
||||
"chat": "与 %1 聊天",
|
||||
"flags": "举报",
|
||||
@@ -38,7 +38,7 @@
|
||||
"account/followers": "关注 %1 的人",
|
||||
"account/posts": "%1 发布的帖子",
|
||||
"account/topics": "%1 创建的主题",
|
||||
"account/groups": "%1 的用户组",
|
||||
"account/groups": "%1 的群组",
|
||||
"account/bookmarks": "%1 收藏的帖子",
|
||||
"account/settings": "用户设置",
|
||||
"account/watched": "主题已被 %1 关注",
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"password_placeholder": "输入密码",
|
||||
"confirm_password": "确认密码",
|
||||
"confirm_password_placeholder": "再次输入密码",
|
||||
"register_now_button": "马上注册",
|
||||
"register_now_button": "立即注册",
|
||||
"alternative_registration": "其他方式注册",
|
||||
"terms_of_use": "使用条款",
|
||||
"agree_to_terms_of_use": "我同意使用条款",
|
||||
|
||||
@@ -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": "至少",
|
||||
@@ -31,7 +31,7 @@
|
||||
"number-of-views": "查看数",
|
||||
"topic-start-date": "主题开始日期",
|
||||
"username": "用户名",
|
||||
"category": "板块",
|
||||
"category": "版块",
|
||||
"descending": "倒序",
|
||||
"ascending": "顺序",
|
||||
"save-preferences": "保存设置",
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
"login_to_subscribe": "请注册或登录后,再订阅此主题。",
|
||||
"markAsUnreadForAll.success": "将全部主题标为未读。",
|
||||
"mark_unread": "标记为未读",
|
||||
"mark_unread.success": "未读话题",
|
||||
"mark_unread.success": "主题已被标记为未读。",
|
||||
"watch": "关注",
|
||||
"unwatch": "取消关注",
|
||||
"watch.title": "当此主题有新回复时,通知我",
|
||||
@@ -48,7 +48,7 @@
|
||||
"not-watching": "未关注",
|
||||
"ignoring": "忽略中",
|
||||
"watching.description": "有新回复时通知我。<br/>在未读主题中显示。",
|
||||
"not-watching.description": "不要在有新回复时通知我。<br/>如果这个分类未被忽略则在未读主题中显示。",
|
||||
"not-watching.description": "不要在有新回复时通知我。<br/>如果这个版块未被忽略则在未读主题中显示。",
|
||||
"ignoring.description": "不要在有新回复时通知我。<br/>不要在未读主题中显示该主题。",
|
||||
"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": "书签",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"banned": "封禁",
|
||||
"banned": "已封禁",
|
||||
"offline": "离线",
|
||||
"username": "用户名",
|
||||
"joindate": "注册日期",
|
||||
@@ -8,7 +8,7 @@
|
||||
"confirm_email": "确认电子邮箱",
|
||||
"account_info": "账户信息",
|
||||
"ban_account": "封禁账户",
|
||||
"ban_account_confirm": "您确定封禁这位用户吗?",
|
||||
"ban_account_confirm": "您确定要封禁这位用户吗?",
|
||||
"unban_account": "解禁账户",
|
||||
"delete_account": "删除帐号",
|
||||
"delete_account_confirm": "确认要删除您的帐户吗?<br /><strong>此操作是不可逆转的,您将无法恢复您的任何数据</strong><br /><br />请输入您的用户名,确认您想要删除此帐户。",
|
||||
@@ -108,8 +108,8 @@
|
||||
"scroll_to_my_post": "在提交回复之后显示新回复",
|
||||
"follow_topics_you_reply_to": "关注你回复过的主题",
|
||||
"follow_topics_you_create": "关注你创建的主题",
|
||||
"grouptitle": "用户组标题",
|
||||
"no-group-title": "不展示用户组称号",
|
||||
"grouptitle": "群组标题",
|
||||
"no-group-title": "不展示群组称号",
|
||||
"select-skin": "选择皮肤",
|
||||
"select-homepage": "选择首页",
|
||||
"homepage": "首页",
|
||||
@@ -117,8 +117,8 @@
|
||||
"custom_route": "自定义首页路由",
|
||||
"custom_route_help": "输入路由名称,前面不需要斜杠 ( 例如, \"recent\" 或 \"popular\" )",
|
||||
"sso.title": "单点登录服务",
|
||||
"sso.associated": "关联到",
|
||||
"sso.not-associated": "点击这里关联",
|
||||
"sso.associated": "已关联到",
|
||||
"sso.not-associated": "点击这里来关联",
|
||||
"info.latest-flags": "最新举报",
|
||||
"info.no-flags": "没有找到被举报的帖子",
|
||||
"info.ban-history": "最近封禁历史",
|
||||
@@ -129,7 +129,7 @@
|
||||
"info.banned-no-reason": "没有原因",
|
||||
"info.username-history": "历史用户名",
|
||||
"info.email-history": "历史邮箱",
|
||||
"info.moderation-note": "版主留言",
|
||||
"info.moderation-note.success": "修改未保存",
|
||||
"info.moderation-note.add": "添加注解"
|
||||
"info.moderation-note": "版主备注",
|
||||
"info.moderation-note.success": "版主备注已保存",
|
||||
"info.moderation-note.add": "添加备注"
|
||||
}
|
||||
@@ -15,7 +15,7 @@
|
||||
"recent_topics": "最新主题",
|
||||
"popular_topics": "热门主题",
|
||||
"unread_topics": "未读主题",
|
||||
"categories": "版面",
|
||||
"categories": "版块",
|
||||
"tags": "话题",
|
||||
"no-users-found": "未找到匹配的用户!"
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
|
||||
define('admin/modules/search', ['mousetrap'], function (mousetrap) {
|
||||
var search = {};
|
||||
|
||||
@@ -11,16 +10,17 @@ define('admin/modules/search', ['mousetrap'], function (mousetrap) {
|
||||
var namespace = params.namespace;
|
||||
var translations = params.translations;
|
||||
var title = params.title;
|
||||
var escaped = utils.escapeRegexChars(term);
|
||||
|
||||
var results = translations
|
||||
// remove all lines without a match
|
||||
.replace(new RegExp('^(?:(?!' + term + ').)*$', 'gmi'), '')
|
||||
.replace(new RegExp('^(?:(?!' + escaped + ').)*$', 'gmi'), '')
|
||||
// remove lines that only match the title
|
||||
.replace(new RegExp('(^|\\n).*?' + title + '.*?(\\n|$)', 'g'), '')
|
||||
// get up to 25 characters of context on both sides of the match
|
||||
// and wrap the match in a `.search-match` element
|
||||
.replace(
|
||||
new RegExp('^[\\s\\S]*?(.{0,25})(' + term + ')(.{0,25})[\\s\\S]*?$', 'gmi'),
|
||||
new RegExp('^[\\s\\S]*?(.{0,25})(' + escaped + ')(.{0,25})[\\s\\S]*?$', 'gmi'),
|
||||
'...$1<span class="search-match">$2</span>$3...<br>'
|
||||
)
|
||||
// collapse whitespace
|
||||
@@ -28,7 +28,7 @@ define('admin/modules/search', ['mousetrap'], function (mousetrap) {
|
||||
.trim();
|
||||
|
||||
title = title.replace(
|
||||
new RegExp('(^.*?)(' + term + ')(.*?$)', 'gi'),
|
||||
new RegExp('(^.*?)(' + escaped + ')(.*?$)', 'gi'),
|
||||
'$1<span class="search-match">$2</span>$3'
|
||||
);
|
||||
|
||||
@@ -123,7 +123,7 @@ define('admin/modules/search', ['mousetrap'], function (mousetrap) {
|
||||
|
||||
menu.children('.result').remove();
|
||||
|
||||
var len = value.length;
|
||||
var len = /\W/.test(value) ? 3 : value.length;
|
||||
var results;
|
||||
|
||||
menu.toggleClass('state-start-typing', len === 0);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
|
||||
define('forum/account/edit/password', ['forum/account/header', 'translator'], function (header, translator) {
|
||||
define('forum/account/edit/password', ['forum/account/header', 'translator', 'zxcvbn'], function (header, translator, zxcvbn) {
|
||||
var AccountEditPassword = {};
|
||||
|
||||
AccountEditPassword.init = function () {
|
||||
@@ -20,6 +20,7 @@ define('forum/account/edit/password', ['forum/account/header', 'translator'], fu
|
||||
var passwordsmatch = false;
|
||||
|
||||
function onPasswordChanged() {
|
||||
var passwordStrength = zxcvbn(password.val());
|
||||
passwordvalid = false;
|
||||
if (password.val().length < ajaxify.data.minimumPasswordLength) {
|
||||
showError(password_notify, '[[user:change_password_error_length]]');
|
||||
@@ -29,6 +30,8 @@ define('forum/account/edit/password', ['forum/account/header', 'translator'], fu
|
||||
showError(password_notify, '[[user:password_same_as_username]]');
|
||||
} else if (password.val() === ajaxify.data.email) {
|
||||
showError(password_notify, '[[user:password_same_as_email]]');
|
||||
} else if (passwordStrength.score < ajaxify.data.minimumPasswordStrength) {
|
||||
showError(password_notify, '[[user:weak_password]]');
|
||||
} else {
|
||||
showSuccess(password_notify);
|
||||
passwordvalid = true;
|
||||
|
||||
@@ -98,7 +98,11 @@ define('forum/chats', [
|
||||
}
|
||||
loading = true;
|
||||
var start = parseInt($('.chat-content').children('[data-index]').first().attr('data-index'), 10) + 1;
|
||||
socket.emit('modules.chats.getMessages', { roomId: roomId, uid: uid, start: start }, function (err, data) {
|
||||
socket.emit('modules.chats.getMessages', {
|
||||
roomId: roomId,
|
||||
uid: uid,
|
||||
start: start,
|
||||
}, function (err, data) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
@@ -122,7 +126,7 @@ define('forum/chats', [
|
||||
Chats.addEditDeleteHandler = function (element, roomId) {
|
||||
element.on('click', '[data-action="edit"]', function () {
|
||||
var messageId = $(this).parents('[data-mid]').attr('data-mid');
|
||||
var inputEl = components.get('chat/input');
|
||||
var inputEl = $('[data-roomid="' + roomId + '"] [component="chat/input"]');
|
||||
messages.prepEdit(inputEl, messageId, roomId);
|
||||
}).on('click', '[data-action="delete"]', function () {
|
||||
var messageId = $(this).parents('[data-mid]').attr('data-mid');
|
||||
@@ -170,7 +174,10 @@ define('forum/chats', [
|
||||
if (oldName === newName) {
|
||||
return;
|
||||
}
|
||||
socket.emit('modules.chats.renameRoom', { roomId: roomId, newName: newName }, function (err) {
|
||||
socket.emit('modules.chats.renameRoom', {
|
||||
roomId: roomId,
|
||||
newName: newName,
|
||||
}, function (err) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
@@ -235,10 +242,15 @@ define('forum/chats', [
|
||||
if (event.item === app.user.username) {
|
||||
return;
|
||||
}
|
||||
socket.emit('modules.chats.addUserToRoom', { roomId: data.roomId, username: event.item }, function (err) {
|
||||
socket.emit('modules.chats.addUserToRoom', {
|
||||
roomId: data.roomId,
|
||||
username: event.item,
|
||||
}, function (err) {
|
||||
if (err) {
|
||||
app.alertError(err.message);
|
||||
tagEl.tagsinput('remove', event.item, { nouser: true });
|
||||
tagEl.tagsinput('remove', event.item, {
|
||||
nouser: true,
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -262,7 +274,10 @@ define('forum/chats', [
|
||||
if (event.options && event.options.nouser) {
|
||||
return;
|
||||
}
|
||||
socket.emit('modules.chats.removeUserFromRoom', { roomId: data.roomId, username: event.item }, function (err) {
|
||||
socket.emit('modules.chats.removeUserFromRoom', {
|
||||
roomId: data.roomId,
|
||||
username: event.item,
|
||||
}, function (err) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
@@ -325,7 +340,12 @@ define('forum/chats', [
|
||||
} else {
|
||||
var recentEl = components.get('chat/recent');
|
||||
templates.parse('partials/chats/recent_room', {
|
||||
rooms: { roomId: data.roomId, lastUser: data.message.fromUser, usernames: data.message.fromUser.username, unread: true },
|
||||
rooms: {
|
||||
roomId: data.roomId,
|
||||
lastUser: data.message.fromUser,
|
||||
usernames: data.message.fromUser.username,
|
||||
unread: true,
|
||||
},
|
||||
}, function (html) {
|
||||
translator.translate(html, function (translated) {
|
||||
recentEl.prepend(translated);
|
||||
@@ -347,7 +367,7 @@ define('forum/chats', [
|
||||
};
|
||||
|
||||
Chats.resizeMainWindow = function () {
|
||||
var messagesList = $('.expanded-chat .chat-content');
|
||||
var messagesList = $('.expanded-chat .chat-content');
|
||||
|
||||
if (messagesList.length) {
|
||||
var margin = $('.expanded-chat ul').outerHeight(true) - $('.expanded-chat ul').height();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
|
||||
define('forum/register', ['translator'], function (translator) {
|
||||
define('forum/register', ['translator', 'zxcvbn'], function (translator, zxcvbn) {
|
||||
var Register = {};
|
||||
var validationError = false;
|
||||
var successIcon = '';
|
||||
@@ -170,6 +170,7 @@ define('forum/register', ['translator'], function (translator) {
|
||||
function validatePassword(password, password_confirm) {
|
||||
var password_notify = $('#password-notify');
|
||||
var password_confirm_notify = $('#password-confirm-notify');
|
||||
var passwordStrength = zxcvbn(password);
|
||||
|
||||
if (password.length < ajaxify.data.minimumPasswordLength) {
|
||||
showError(password_notify, '[[user:change_password_error_length]]');
|
||||
@@ -181,6 +182,8 @@ define('forum/register', ['translator'], function (translator) {
|
||||
showError(password_notify, '[[user:password_same_as_username]]');
|
||||
} else if (password === $('#email').val()) {
|
||||
showError(password_notify, '[[user:password_same_as_email]]');
|
||||
} else if (passwordStrength.score < ajaxify.data.minimumPasswordStrength) {
|
||||
showError(password_notify, '[[user:weak_password]]');
|
||||
} else {
|
||||
showSuccess(password_notify, successIcon);
|
||||
}
|
||||
|
||||
@@ -75,7 +75,9 @@ define('chat', [
|
||||
});
|
||||
}
|
||||
} else {
|
||||
socket.emit('modules.chats.loadRoom', { roomId: data.roomId }, function (err, roomData) {
|
||||
socket.emit('modules.chats.loadRoom', {
|
||||
roomId: data.roomId,
|
||||
}, function (err, roomData) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
@@ -111,7 +113,10 @@ define('chat', [
|
||||
};
|
||||
|
||||
module.loadChatsDropdown = function (chatsListEl) {
|
||||
socket.emit('modules.chats.getRecentChats', { uid: app.user.uid, after: 0 }, function (err, data) {
|
||||
socket.emit('modules.chats.getRecentChats', {
|
||||
uid: app.user.uid,
|
||||
after: 0,
|
||||
}, function (err, data) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
@@ -163,7 +168,7 @@ define('chat', [
|
||||
var dragged = false;
|
||||
|
||||
chatModal.attr('id', 'chat-modal-' + data.roomId);
|
||||
chatModal.attr('roomId', data.roomId);
|
||||
chatModal.attr('data-roomid', data.roomId);
|
||||
chatModal.attr('intervalId', 0);
|
||||
chatModal.attr('UUID', uuid);
|
||||
chatModal.css('position', 'fixed');
|
||||
@@ -211,7 +216,7 @@ define('chat', [
|
||||
components.get('chat/input').val(text);
|
||||
});
|
||||
|
||||
ajaxify.go('user/' + app.user.userslug + '/chats/' + chatModal.attr('roomId'));
|
||||
ajaxify.go('user/' + app.user.userslug + '/chats/' + chatModal.attr('data-roomid'));
|
||||
module.close(chatModal);
|
||||
}
|
||||
|
||||
@@ -252,14 +257,14 @@ define('chat', [
|
||||
messagesEl.css('height', module.calculateChatListHeight(chatModal));
|
||||
});
|
||||
|
||||
Chats.addRenameHandler(chatModal.attr('roomId'), chatModal.find('[component="chat/room/name"]'));
|
||||
Chats.addRenameHandler(chatModal.attr('data-roomid'), chatModal.find('[component="chat/room/name"]'));
|
||||
|
||||
Chats.addSendHandlers(chatModal.attr('roomId'), chatModal.find('#chat-message-input'), chatModal.find('#chat-message-send-btn'));
|
||||
Chats.addSendHandlers(chatModal.attr('data-roomid'), chatModal.find('#chat-message-input'), chatModal.find('#chat-message-send-btn'));
|
||||
|
||||
Chats.createTagsInput(chatModal.find('.users-tag-input'), data);
|
||||
Chats.createAutoComplete(chatModal.find('[component="chat/input"]'));
|
||||
|
||||
Chats.addScrollHandler(chatModal.attr('roomId'), data.uid, chatModal.find('.chat-content'));
|
||||
Chats.addScrollHandler(chatModal.attr('data-roomid'), data.uid, chatModal.find('.chat-content'));
|
||||
|
||||
taskbar.push('chat', chatModal.attr('UUID'), {
|
||||
title: data.roomName || (data.users.length ? data.users[0].username : ''),
|
||||
@@ -314,7 +319,7 @@ define('chat', [
|
||||
ChatsMessages.scrollToBottom(chatModal.find('.chat-content'));
|
||||
module.bringModalToTop(chatModal);
|
||||
module.focusInput(chatModal);
|
||||
socket.emit('modules.chats.markRead', chatModal.attr('roomId'));
|
||||
socket.emit('modules.chats.markRead', chatModal.attr('data-roomid'));
|
||||
|
||||
var env = utils.findBootstrapEnvironment();
|
||||
if (env === 'xs' || env === 'sm') {
|
||||
|
||||
@@ -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<string>} backup - Text to use in case the key can't be found
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
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(/%(?=\d)/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(/%(?=\d)/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<Object|string>}
|
||||
* @returns {Promise<Object>|Promise<string>}
|
||||
*/
|
||||
Translator.prototype.getTranslation = function getTranslation(namespace, key) {
|
||||
var translation;
|
||||
|
||||
@@ -86,7 +86,7 @@ module.exports = function (Categories) {
|
||||
privileges.topics.filterTids('read', tids, uid, next);
|
||||
},
|
||||
function (tids, next) {
|
||||
getTopics(tids, next);
|
||||
getTopics(tids, uid, next);
|
||||
},
|
||||
function (topics, next) {
|
||||
assignTopicsToCategories(categoryData, topics);
|
||||
@@ -98,7 +98,7 @@ module.exports = function (Categories) {
|
||||
], callback);
|
||||
};
|
||||
|
||||
function getTopics(tids, callback) {
|
||||
function getTopics(tids, uid, callback) {
|
||||
var topicData;
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
@@ -119,7 +119,7 @@ module.exports = function (Categories) {
|
||||
|
||||
async.parallel({
|
||||
categoryData: async.apply(Categories.getCategoriesFields, cids, ['cid', 'parentCid']),
|
||||
teasers: async.apply(topics.getTeasers, _topicData),
|
||||
teasers: async.apply(topics.getTeasers, _topicData, uid),
|
||||
}, next);
|
||||
},
|
||||
function (results, next) {
|
||||
|
||||
@@ -80,6 +80,7 @@ function renderRoute(name, req, res, next) {
|
||||
|
||||
if (name === 'password') {
|
||||
userData.minimumPasswordLength = parseInt(meta.config.minimumPasswordLength, 10);
|
||||
userData.minimumPasswordStrength = parseInt(meta.config.minimumPasswordStrength || 0, 10);
|
||||
}
|
||||
|
||||
userData.title = '[[pages:account/edit/' + name + ', ' + userData.username + ']]';
|
||||
|
||||
@@ -43,13 +43,14 @@ exports.handleErrors = function (err, req, res, next) { // eslint-disable-line n
|
||||
return res.status(403).type('text/plain').send(err.message);
|
||||
}
|
||||
|
||||
if (parseInt(err.status, 10) === 302 && err.path) {
|
||||
return res.locals.isAPI ? res.status(302).json(err.path) : res.redirect(err.path);
|
||||
var status = parseInt(err.status, 10);
|
||||
if ((status === 302 || status === 308) && err.path) {
|
||||
return res.locals.isAPI ? res.status(status).json(err.path) : res.redirect(err.path);
|
||||
}
|
||||
|
||||
winston.error(req.path + '\n', err.stack);
|
||||
|
||||
res.status(err.status || 500);
|
||||
res.status(status || 500);
|
||||
|
||||
var path = String(req.path || '');
|
||||
if (res.locals.isAPI) {
|
||||
|
||||
@@ -48,7 +48,11 @@ Controllers.home = function (req, res, next) {
|
||||
var hook = 'action:homepage.get:' + route;
|
||||
|
||||
if (plugins.hasListeners(hook)) {
|
||||
return plugins.fireHook(hook, { req: req, res: res, next: next });
|
||||
return plugins.fireHook(hook, {
|
||||
req: req,
|
||||
res: res,
|
||||
next: next,
|
||||
});
|
||||
}
|
||||
|
||||
if (route === 'categories' || route === '/') {
|
||||
@@ -85,7 +89,15 @@ Controllers.reset = function (req, res, next) {
|
||||
displayExpiryNotice: req.session.passwordExpired,
|
||||
code: req.params.code,
|
||||
minimumPasswordLength: parseInt(meta.config.minimumPasswordLength, 10),
|
||||
breadcrumbs: helpers.buildBreadcrumbs([{ text: '[[reset_password:reset_password]]', url: '/reset' }, { text: '[[reset_password:update_password]]' }]),
|
||||
breadcrumbs: helpers.buildBreadcrumbs([
|
||||
{
|
||||
text: '[[reset_password:reset_password]]',
|
||||
url: '/reset',
|
||||
},
|
||||
{
|
||||
text: '[[reset_password:update_password]]',
|
||||
},
|
||||
]),
|
||||
title: '[[pages:reset]]',
|
||||
});
|
||||
|
||||
@@ -94,7 +106,9 @@ Controllers.reset = function (req, res, next) {
|
||||
} else {
|
||||
res.render('reset', {
|
||||
code: null,
|
||||
breadcrumbs: helpers.buildBreadcrumbs([{ text: '[[reset_password:reset_password]]' }]),
|
||||
breadcrumbs: helpers.buildBreadcrumbs([{
|
||||
text: '[[reset_password:reset_password]]',
|
||||
}]),
|
||||
title: '[[pages:reset]]',
|
||||
});
|
||||
}
|
||||
@@ -124,7 +138,9 @@ Controllers.login = function (req, res, next) {
|
||||
data.allowLocalLogin = parseInt(meta.config.allowLocalLogin, 10) === 1 || parseInt(req.query.local, 10) === 1;
|
||||
data.allowRegistration = registrationType === 'normal' || registrationType === 'admin-approval' || registrationType === 'admin-approval-ip';
|
||||
data.allowLoginWith = '[[login:' + allowLoginWith + ']]';
|
||||
data.breadcrumbs = helpers.buildBreadcrumbs([{ text: '[[global:login]]' }]);
|
||||
data.breadcrumbs = helpers.buildBreadcrumbs([{
|
||||
text: '[[global:login]]',
|
||||
}]);
|
||||
data.error = req.flash('error')[0] || errorText;
|
||||
data.title = '[[pages:login]]';
|
||||
|
||||
@@ -171,7 +187,11 @@ Controllers.register = function (req, res, next) {
|
||||
}
|
||||
},
|
||||
function (next) {
|
||||
plugins.fireHook('filter:parse.post', { postData: { content: meta.config.termsOfUse || '' } }, next);
|
||||
plugins.fireHook('filter:parse.post', {
|
||||
postData: {
|
||||
content: meta.config.termsOfUse || '',
|
||||
},
|
||||
}, next);
|
||||
},
|
||||
], function (err, termsOfUse) {
|
||||
if (err) {
|
||||
@@ -188,8 +208,11 @@ Controllers.register = function (req, res, next) {
|
||||
data.minimumUsernameLength = parseInt(meta.config.minimumUsernameLength, 10);
|
||||
data.maximumUsernameLength = parseInt(meta.config.maximumUsernameLength, 10);
|
||||
data.minimumPasswordLength = parseInt(meta.config.minimumPasswordLength, 10);
|
||||
data.minimumPasswordStrength = parseInt(meta.config.minimumPasswordStrength || 0, 10);
|
||||
data.termsOfUse = termsOfUse.postData.content;
|
||||
data.breadcrumbs = helpers.buildBreadcrumbs([{ text: '[[register:register]]' }]);
|
||||
data.breadcrumbs = helpers.buildBreadcrumbs([{
|
||||
text: '[[register:register]]',
|
||||
}]);
|
||||
data.regFormEntry = [];
|
||||
data.error = req.flash('error')[0] || errorText;
|
||||
data.title = '[[pages:register]]';
|
||||
@@ -333,7 +356,9 @@ Controllers.outgoing = function (req, res, next) {
|
||||
res.render('outgoing', {
|
||||
outgoing: validator.escape(String(url)),
|
||||
title: meta.config.title,
|
||||
breadcrumbs: helpers.buildBreadcrumbs([{ text: '[[notifications:outgoing_link]]' }]),
|
||||
breadcrumbs: helpers.buildBreadcrumbs([{
|
||||
text: '[[notifications:outgoing_link]]',
|
||||
}]),
|
||||
});
|
||||
};
|
||||
|
||||
@@ -341,7 +366,9 @@ Controllers.termsOfUse = function (req, res, next) {
|
||||
if (!meta.config.termsOfUse) {
|
||||
return next();
|
||||
}
|
||||
res.render('tos', { termsOfUse: meta.config.termsOfUse });
|
||||
res.render('tos', {
|
||||
termsOfUse: meta.config.termsOfUse,
|
||||
});
|
||||
};
|
||||
|
||||
Controllers.ping = function (req, res) {
|
||||
|
||||
@@ -67,6 +67,7 @@ topicsController.get = function (req, res, callback) {
|
||||
settings = results.settings;
|
||||
var postCount = parseInt(results.topic.postcount, 10);
|
||||
pageCount = Math.max(1, Math.ceil(postCount / settings.postsPerPage));
|
||||
results.topic.postcount = postCount;
|
||||
|
||||
if (utils.isNumber(req.params.post_index) && (req.params.post_index < 1 || req.params.post_index > postCount)) {
|
||||
return helpers.redirect(res, '/topic/' + req.params.topic_id + '/' + req.params.slug + (req.params.post_index > postCount ? '/' + postCount : ''));
|
||||
|
||||
@@ -166,7 +166,7 @@
|
||||
};
|
||||
|
||||
module.checkCompatibility = function (callback) {
|
||||
var mongoPkg = require.main.require('./node_modules/mongodb/package.json');
|
||||
var mongoPkg = require('mongodb/package.json');
|
||||
|
||||
if (semver.lt(mongoPkg.version, '2.0.0')) {
|
||||
return callback(new Error('The `mongodb` package is out-of-date, please run `./nodebb setup` again.'));
|
||||
|
||||
@@ -101,16 +101,21 @@ module.exports = function (Meta) {
|
||||
}
|
||||
|
||||
function updateConfig(config) {
|
||||
updateLocalConfig(config);
|
||||
pubsub.publish('config:update', config);
|
||||
}
|
||||
|
||||
function updateLocalConfig(config) {
|
||||
for (var field in config) {
|
||||
if (config.hasOwnProperty(field)) {
|
||||
Meta.config[field] = config[field];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pubsub.on('config:update', function onConfigReceived(config) {
|
||||
if (typeof config === 'object' && Meta.config) {
|
||||
for (var field in config) {
|
||||
if (config.hasOwnProperty(field)) {
|
||||
Meta.config[field] = config[field];
|
||||
}
|
||||
}
|
||||
updateLocalConfig(config);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -20,8 +20,8 @@ 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',
|
||||
@@ -35,14 +35,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,11 +84,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',
|
||||
'Chart.js': 'node_modules/chart.js/dist/Chart.min.js',
|
||||
'mousetrap.js': 'node_modules/mousetrap/mousetrap.min.js',
|
||||
'jqueryui.js': 'public/vendor/jquery/js/jquery-ui.js',
|
||||
'buzz.js': 'public/vendor/buzz/buzz.js',
|
||||
'cropper.js': './node_modules/cropperjs/dist/cropper.min.js',
|
||||
'cropper.js': 'node_modules/cropperjs/dist/cropper.min.js',
|
||||
'zxcvbn.js': 'node_modules/zxcvbn/dist/zxcvbn.js',
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -110,7 +111,9 @@ module.exports = function (Meta) {
|
||||
}
|
||||
|
||||
if (filePath.endsWith('.min.js')) {
|
||||
minified = { code: buffer.toString() };
|
||||
minified = {
|
||||
code: buffer.toString(),
|
||||
};
|
||||
return cb();
|
||||
}
|
||||
|
||||
@@ -193,10 +196,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);
|
||||
});
|
||||
@@ -311,7 +314,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();
|
||||
@@ -325,7 +328,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);
|
||||
});
|
||||
};
|
||||
@@ -345,7 +348,9 @@ module.exports = function (Meta) {
|
||||
/**
|
||||
* otherwise, just clean up --debug/--debug-brk options which are set up by default from the parent one
|
||||
*/
|
||||
forkProcessParams = { execArgv: [] };
|
||||
forkProcessParams = {
|
||||
execArgv: [],
|
||||
};
|
||||
}
|
||||
|
||||
return forkProcessParams;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
var path = require('path');
|
||||
var winston = require('winston');
|
||||
var nconf = require('nconf');
|
||||
var async = require('async');
|
||||
@@ -83,7 +84,7 @@ function resetTheme(themeId, callback) {
|
||||
var meta = require('./meta');
|
||||
var fs = require('fs');
|
||||
|
||||
fs.access('node_modules/' + themeId + '/package.json', function (err) {
|
||||
fs.access(path.join(__dirname, '../node_modules', themeId, 'package.json'), function (err) {
|
||||
if (err) {
|
||||
winston.warn('[reset] Theme `%s` is not installed on this forum', themeId);
|
||||
callback(new Error('theme-not-found'));
|
||||
|
||||
@@ -104,7 +104,6 @@ function setupConfigs() {
|
||||
|
||||
function printStartupInfo() {
|
||||
if (nconf.get('isPrimary') === 'true') {
|
||||
winston.info('Time: %s', (new Date()).toString());
|
||||
winston.info('Initializing NodeBB v%s', nconf.get('version'));
|
||||
|
||||
var host = nconf.get(nconf.get('database') + ':host');
|
||||
|
||||
@@ -126,7 +126,7 @@ var social = require('./social');
|
||||
Topics.getUserBookmarks(tids, uid, next);
|
||||
},
|
||||
teasers: function (next) {
|
||||
Topics.getTeasers(topics, next);
|
||||
Topics.getTeasers(topics, uid, next);
|
||||
},
|
||||
tags: function (next) {
|
||||
Topics.getTopicsTagsObjects(tids, next);
|
||||
|
||||
@@ -63,7 +63,7 @@ module.exports = function (Topics) {
|
||||
var uids = [];
|
||||
|
||||
postData.forEach(function (postData) {
|
||||
if (postData && postData[field] && uids.indexOf(postData[field]) === -1) {
|
||||
if (postData && parseInt(postData[field], 10) >= 0 && uids.indexOf(postData[field]) === -1) {
|
||||
uids.push(postData[field]);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -12,7 +12,11 @@ var plugins = require('../plugins');
|
||||
var utils = require('../../public/src/utils');
|
||||
|
||||
module.exports = function (Topics) {
|
||||
Topics.getTeasers = function (topics, callback) {
|
||||
Topics.getTeasers = function (topics, uid, callback) {
|
||||
if (typeof uid === 'function') {
|
||||
callback = uid;
|
||||
uid = 0;
|
||||
}
|
||||
if (!Array.isArray(topics) || !topics.length) {
|
||||
return callback(null, []);
|
||||
}
|
||||
@@ -94,7 +98,7 @@ module.exports = function (Topics) {
|
||||
return tidToPost[topic.tid];
|
||||
});
|
||||
|
||||
plugins.fireHook('filter:teasers.get', { teasers: teasers }, next);
|
||||
plugins.fireHook('filter:teasers.get', { teasers: teasers, uid: uid }, next);
|
||||
},
|
||||
function (data, next) {
|
||||
next(null, data.teasers);
|
||||
@@ -102,7 +106,11 @@ module.exports = function (Topics) {
|
||||
], callback);
|
||||
};
|
||||
|
||||
Topics.getTeasersByTids = function (tids, callback) {
|
||||
Topics.getTeasersByTids = function (tids, uid, callback) {
|
||||
if (typeof uid === 'function') {
|
||||
callback = uid;
|
||||
uid = 0;
|
||||
}
|
||||
if (!Array.isArray(tids) || !tids.length) {
|
||||
return callback(null, []);
|
||||
}
|
||||
@@ -111,13 +119,17 @@ module.exports = function (Topics) {
|
||||
Topics.getTopicsFields(tids, ['tid', 'postcount', 'teaserPid'], next);
|
||||
},
|
||||
function (topics, next) {
|
||||
Topics.getTeasers(topics, next);
|
||||
Topics.getTeasers(topics, uid, next);
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
Topics.getTeaser = function (tid, callback) {
|
||||
Topics.getTeasersByTids([tid], function (err, teasers) {
|
||||
Topics.getTeaser = function (tid, uid, callback) {
|
||||
if (typeof uid === 'function') {
|
||||
callback = uid;
|
||||
uid = 0;
|
||||
}
|
||||
Topics.getTeasersByTids([tid], uid, function (err, teasers) {
|
||||
callback(err, Array.isArray(teasers) && teasers.length ? teasers[0] : null);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -199,20 +199,21 @@ var privileges = require('../privileges');
|
||||
|
||||
db.getObjectsFields(keys, ['mergeId'], next);
|
||||
},
|
||||
], function (err, mergeIds) {
|
||||
// A missing (null) mergeId means that notification is counted separately.
|
||||
mergeIds = mergeIds.map(function (set) {
|
||||
return set.mergeId;
|
||||
});
|
||||
function (mergeIds, next) {
|
||||
mergeIds = mergeIds.map(function (set) {
|
||||
return set.mergeId;
|
||||
});
|
||||
|
||||
callback(err, mergeIds.reduce(function (count, cur, idx, arr) {
|
||||
if (cur === null || idx === arr.indexOf(cur)) {
|
||||
count += 1;
|
||||
}
|
||||
next(null, mergeIds.reduce(function (count, mergeId, idx, arr) {
|
||||
// A missing (null) mergeId means that notification is counted separately.
|
||||
if (mergeId === null || idx === arr.indexOf(mergeId)) {
|
||||
count += 1;
|
||||
}
|
||||
|
||||
return count;
|
||||
}, 0));
|
||||
});
|
||||
return count;
|
||||
}, 0));
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
UserNotifications.getUnreadByField = function (uid, field, values, callback) {
|
||||
|
||||
@@ -167,6 +167,16 @@
|
||||
<label>[[admin/settings/user:min-password-length]]</label>
|
||||
<input type="text" class="form-control" value="6" data-field="minimumPasswordLength">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>[[admin/settings/user:min-password-strength]]</label>
|
||||
<select class="form-control" data-field="minimumPasswordStrength">
|
||||
<option value="0">0 - too guessable: risky password</option>
|
||||
<option value="1">1 - very guessable</option>
|
||||
<option value="2">2 - somewhat guessable</option>
|
||||
<option value="3">3 - safely unguessable</option>
|
||||
<option value="4">4 - very unguessable</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>[[admin/settings/user:max-about-me-length]]</label>
|
||||
<input type="text" class="form-control" value="500" data-field="maximumAboutMeLength">
|
||||
|
||||
@@ -156,6 +156,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');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user