修复一堆错误

This commit is contained in:
undefined committed 2020-06-20 13:32:53 +08:00
1 parent 2d3cee7753
commit b5e3b81c17
23 files changed
+115 -154

No files matched your search

+15 -14
View File
@@ -1,4 +1,5 @@
module.exports = { module.exports = {
root: true,
env: { env: {
commonjs: true, commonjs: true,
node: true, node: true,
@@ -14,19 +15,19 @@ module.exports = {
ecmaVersion: 2018, ecmaVersion: 2018,
}, },
rules: { rules: {
"indent": ["warn", 4], indent: ['warn', 4],
"no-plusplus": "off", 'no-plusplus': 'off',
"no-console": "off", 'no-console': 'off',
"no-extend-native": "off", 'no-extend-native': 'off',
"no-underscore-dangle": "off", 'no-underscore-dangle': 'off',
"no-restricted-syntax": "off", 'no-restricted-syntax': 'off',
"max-classes-per-file": "off", 'max-classes-per-file': 'off',
"radix": "off", radix: 'off',
"guard-for-in": "off", 'guard-for-in': 'off',
"no-param-reassign": "off", 'no-param-reassign': 'off',
"global-require": "off", 'global-require': 'off',
"no-nested-ternary": "off", 'no-nested-ternary': 'off',
"no-multi-assign": "off", 'no-multi-assign': 'off',
"no-return-await": "off", 'no-return-await': 'off',
}, },
}; };
+2
View File
@@ -7,6 +7,8 @@ Hydro是一个高效的信息学在线测评系统。
Hydro 的界面基于 Vijos 二次开发。 Hydro 的界面基于 Vijos 二次开发。
如果您认为本项目有价值,欢迎 star 。
## 鸣谢 ## 鸣谢
排名不分先后,按照链接字典序 排名不分先后,按照链接字典序
-41
View File
@@ -274,96 +274,55 @@ class FileTooLongError(ValidationError):
def message(self): def message(self):
return 'The uploaded file is too long.' return 'The uploaded file is too long.'
class FileTypeNotAllowedError(ValidationError): class FileTypeNotAllowedError(ValidationError):
@property @property
def message(self): def message(self):
return 'This type of files are not allowed to be uploaded.' return 'This type of files are not allowed to be uploaded.'
class UnknownFieldError(ForbiddenError):
@property
def message(self):
return 'Unknown field {0}.'
class CsrfTokenError(ForbiddenError): class CsrfTokenError(ForbiddenError):
pass pass
class InvalidOperationError(ForbiddenError): class InvalidOperationError(ForbiddenError):
pass pass
class InvalidTokenDigestError(ForbiddenError): class InvalidTokenDigestError(ForbiddenError):
pass pass
class CurrentPasswordError(ForbiddenError): class CurrentPasswordError(ForbiddenError):
@property @property
def message(self): def message(self):
return "Current password doesn't match." return "Current password doesn't match."
class DiscussionCategoryAlreadyExistError(ForbiddenError): class DiscussionCategoryAlreadyExistError(ForbiddenError):
@property @property
def message(self): def message(self):
return 'Discussion category {1} already exists.' return 'Discussion category {1} already exists.'
class DiscussionCategoryNotFoundError(NotFoundError): class DiscussionCategoryNotFoundError(NotFoundError):
@property @property
def message(self): def message(self):
return 'Discussion category {1} not found.' return 'Discussion category {1} not found.'
class DiscussionNodeAlreadyExistError(ForbiddenError): class DiscussionNodeAlreadyExistError(ForbiddenError):
@property @property
def message(self): def message(self):
return 'Discussion node {1} already exists.' return 'Discussion node {1} already exists.'
class TrainingRequirementNotSatisfiedError(ForbiddenError): class TrainingRequirementNotSatisfiedError(ForbiddenError):
@property @property
def message(self): def message(self):
return 'Training requirement is not satisfied.' return 'Training requirement is not satisfied.'
class UsageExceededError(ForbiddenError): class UsageExceededError(ForbiddenError):
@property @property
def message(self): def message(self):
return 'Usage exceeded.' return 'Usage exceeded.'
class InvalidArgumentError(BadRequestError): class InvalidArgumentError(BadRequestError):
@property @property
def message(self): def message(self):
return 'Argument {0} is invalid.' return 'Argument {0} is invalid.'
class BatchCopyLimitExceededError(ForbiddenError):
@property
def message(self):
return 'Only {0} problems can be copied in one request, got {1}.'
class UpgradeLockAcquireError(Error):
@property
def message(self):
return 'Failed to acquire the upgrade lock. There may be another ongoing upgrade process, or a previous process is exited unexpectedly.'
class UpgradeLockReleaseError(Error):
@property
def message(self):
return 'Failed to release the upgrade lock. The database is malformed during the upgrade.'
class DatabaseVersionMismatchError(Error):
@property
def message(self):
return 'Database version mismatch, got {0}, expect {1}. You need to invoke database upgrades.'
class SendMailError(UserFacingError): class SendMailError(UserFacingError):
@property @property
def message(self): def message(self):
+20 -19
View File
@@ -31,13 +31,15 @@ class DiscussionHandler extends Handler {
type = discussion.typeDisplay[this.ddoc.parentType]; type = discussion.typeDisplay[this.ddoc.parentType];
name = this.ddoc.parentId; name = this.ddoc.parentId;
if (drid) { if (drid) {
this.drdoc = await discussion.getReply(domainId, drid, did); this.drdoc = await discussion.getReply(domainId, drid);
if (!this.drdoc) throw new DiscussionNotFoundError(drid); if (!this.drdoc) throw new DiscussionNotFoundError(drid);
if (this.drdoc.parent !== this.ddoc._id) throw new DocumentNotFoundError(drid); if (!this.drdoc.parentId.equals(this.ddoc._id)) {
throw new DocumentNotFoundError(drid);
}
if (drrid) { if (drrid) {
[, this.drrdoc] = await discussion.getTailReply(domainId, drid, drrid); [, this.drrdoc] = await discussion.getTailReply(domainId, drid, drrid);
if (!this.drrdoc) throw new DiscussionNotFoundError(drrid); if (!this.drrdoc) throw new DiscussionNotFoundError(drrid);
if (this.drrdoc.parent !== this.drdoc._id) { if (!this.drrdoc.parentId.equals(this.drdoc._id)) {
throw new DocumentNotFoundError(drid); throw new DocumentNotFoundError(drid);
} }
} }
@@ -196,7 +198,7 @@ class DiscussionDetailHandler extends DiscussionHandler {
async postDeleteReply({ domainId, drid }) { async postDeleteReply({ domainId, drid }) {
if (this.drdoc.owner !== this.user._id) this.checkPerm(PERM_DELETE_DISCUSSION_REPLY); if (this.drdoc.owner !== this.user._id) this.checkPerm(PERM_DELETE_DISCUSSION_REPLY);
await discussion.deleteReply(domainId, drid); await discussion.delReply(domainId, drid);
this.back(); this.back();
} }
@@ -210,20 +212,18 @@ class DiscussionDetailHandler extends DiscussionHandler {
async postDeleteTailReply({ domainId, drid, drrid }) { async postDeleteTailReply({ domainId, drid, drrid }) {
if (this.drrdoc.owner !== this.user._id) this.checkPerm(PERM_DELETE_DISCUSSION_REPLY); if (this.drrdoc.owner !== this.user._id) this.checkPerm(PERM_DELETE_DISCUSSION_REPLY);
await discussion.deleteTailReply(domainId, drid, drrid); await discussion.delTailReply(domainId, drid, drrid);
this.back(); this.back();
} }
async postStar({ domainId, did, star }) { async postStar({ domainId, did }) {
await discussion.setStar(domainId, did, this.user._id, true); await discussion.setStar(domainId, did, this.user._id, true);
this.response.body = { star }; this.back({ star: true });
this.response.direct = this.request.path;
} }
async postUnstar({ domainId, did, star }) { async postUnstar({ domainId, did }) {
await discussion.setStar(domainId, did, this.user._id, false); await discussion.setStar(domainId, did, this.user._id, false);
this.response.body = { star }; this.back({ star: false });
this.response.direct = this.request.path;
} }
} }
@@ -241,7 +241,6 @@ class DiscussionReplyRawHandler extends DiscussionHandler {
} }
} }
class DiscussionTailReplyRawHandler extends DiscussionHandler { class DiscussionTailReplyRawHandler extends DiscussionHandler {
async get() { async get() {
this.response.type = 'text/markdown'; this.response.type = 'text/markdown';
@@ -263,22 +262,24 @@ class DiscussionEditHandler extends DiscussionHandler {
this.response.body = { ddoc: this.ddoc, path }; this.response.body = { ddoc: this.ddoc, path };
} }
async post({ async postUpdate({
domainId, did, title, content, highlight, operation, domainId, did, title, content, highlight,
}) { }) {
if (operation) return;
if (this.ddoc.owner !== this.user._id) this.checkPerm(PERM_EDIT_DISCUSSION); if (this.ddoc.owner !== this.user._id) this.checkPerm(PERM_EDIT_DISCUSSION);
if (highlight && !this.ddoc.highlight) this.checkPerm(PERM_HIGHLIGHT_DISCUSSION); if (highlight && !this.ddoc.highlight) this.checkPerm(PERM_HIGHLIGHT_DISCUSSION);
await discussion.edit(domainId, did, title, content, highlight); await discussion.edit(domainId, did, title, content, highlight);
this.response.body = { did }; this.response.body = { did };
this.response.redirect = `/discuss/${did}`; this.response.redirect = this.url('discussion_detail', { did });
} }
async postDelete({ domainId, did }) { async postDelete({ domainId, did }) {
if (this.ddoc.owner !== this.user._id) this.checkPerm(PERM_DELETE_DISCUSSION); if (this.ddoc.owner !== this.user._id) this.checkPerm(PERM_DELETE_DISCUSSION);
await discussion.delete(domainId, did); await discussion.del(domainId, did);
this.response.body = { type: this.ddoc.parentType, parent: this.ddoc.parentId }; this.response.body = { type: this.ddoc.parentType, parent: this.ddoc.parentId };
this.response.redirect = `/discuss/${this.ddoc.parentType}/${this.ddoc.parentId}`; this.response.redirect = this.url('discussion_node', {
type: discussion.typeDisplay[this.ddoc.parentType],
name: this.ddoc.parentId,
});
} }
} }
@@ -287,7 +288,7 @@ async function apply() {
Route('discussion_detail', '/discuss/:did', DiscussionDetailHandler); Route('discussion_detail', '/discuss/:did', DiscussionDetailHandler);
Route('discussion_edit', '/discuss/:did/edit', DiscussionEditHandler); Route('discussion_edit', '/discuss/:did/edit', DiscussionEditHandler);
Route('discussion_detail', '/discuss/:did/raw', DiscussionDetailRawHandler); Route('discussion_detail', '/discuss/:did/raw', DiscussionDetailRawHandler);
Route('discission_reply_raw', '/discuss/:did/:drid/raw', DiscussionReplyRawHandler); Route('discussion_reply_raw', '/discuss/:did/:drid/raw', DiscussionReplyRawHandler);
Route('discussion_tail_reply_raw', '/discuss/:did/:drid/:drrid/raw', DiscussionTailReplyRawHandler); Route('discussion_tail_reply_raw', '/discuss/:did/:drid/:drrid/raw', DiscussionTailReplyRawHandler);
Route('discussion_node', '/discuss/:type/:name', DiscussionNodeHandler); Route('discussion_node', '/discuss/:type/:name', DiscussionNodeHandler);
Route('discussion_create', '/discuss/:type/:name/create', DiscussionCreateHandler); Route('discussion_create', '/discuss/:type/:name/create', DiscussionCreateHandler);
+2 -1
View File
@@ -14,7 +14,6 @@ class SystemHandler extends Handler {
this.response.body = { this.response.body = {
path: [ path: [
['Hydro', 'homepage'], ['Hydro', 'homepage'],
['manage_main', null],
], ],
}; };
} }
@@ -118,6 +117,8 @@ class SystemSettingHandler extends SystemHandler {
this.response.body.current = {}; this.response.body.current = {};
this.response.body.settings = setting.SYSTEM_SETTINGS; this.response.body.settings = setting.SYSTEM_SETTINGS;
for (const s of this.response.body.settings) { for (const s of this.response.body.settings) {
// FIXME no-await-in-loop
// eslint-disable-next-line no-await-in-loop
this.response.body.current[s.key] = await system.get(s.key); this.response.body.current[s.key] = await system.get(s.key);
} }
} }
-1
View File
@@ -1,4 +1,3 @@
const { PERM_JUDGE, PERM_LOGGEDIN } = require('../permission'); const { PERM_JUDGE, PERM_LOGGEDIN } = require('../permission');
const file = require('../model/file'); const file = require('../model/file');
const user = require('../model/user'); const user = require('../model/user');
-1
View File
@@ -1,4 +1,3 @@
const fs = require('fs');
const paginate = require('../lib/paginate'); const paginate = require('../lib/paginate');
const validator = require('../lib/validator'); const validator = require('../lib/validator');
const file = require('../model/file'); const file = require('../model/file');
+2 -1
View File
@@ -61,7 +61,8 @@ class UserRegisterHandler extends Handler {
); );
if (await system.get('smtp.user')) { if (await system.get('smtp.user')) {
const m = await this.renderHTML('user_register_mail.html', { const m = await this.renderHTML('user_register_mail.html', {
url: `${await system.get('server.url')}/register/${t[0]}`, path: `register/${t[0]}`,
url_prefix: await system.get('server.url'),
}); });
await sendMail(mail, 'Sign Up', 'user_register_mail', m); await sendMail(mail, 'Sign Up', 'user_register_mail', m);
this.response.template = 'user_register_mail_sent.html'; this.response.template = 'user_register_mail_sent.html';
-8
View File
@@ -59,39 +59,31 @@ ID_RE = re.compile(r'[^\\/\s\u3000]([^\\/\n\r]*[^\\/\s\u3000])?')
def is_id(s): def is_id(s):
return bool(ID_RE.fullmatch(s)) return bool(ID_RE.fullmatch(s))
def check_category_name(s): def check_category_name(s):
if not is_id(s): if not is_id(s):
raise error.ValidationError('category_name') raise error.ValidationError('category_name')
def check_node_name(s): def check_node_name(s):
if not is_id(s): if not is_id(s):
raise error.ValidationError('node_name') raise error.ValidationError('node_name')
def is_intro(s): def is_intro(s):
return isinstance(s, str) and 0 < len(s.strip()) < 500 return isinstance(s, str) and 0 < len(s.strip()) < 500
def check_intro(s): def check_intro(s):
if not is_intro(s): if not is_intro(s):
raise error.ValidationError('intro') raise error.ValidationError('intro')
def is_description(s): def is_description(s):
return isinstance(s, str) and len(s) < 65536 return isinstance(s, str) and len(s) < 65536
def check_description(s): def check_description(s):
if not is_description(s): if not is_description(s):
raise error.ValidationError('description') raise error.ValidationError('description')
def is_lang(i): def is_lang(i):
return i in constant.language.LANG_TEXTS return i in constant.language.LANG_TEXTS
def check_lang(i): def check_lang(i):
if not is_lang(i): if not is_lang(i):
raise error.ValidationError('lang') raise error.ValidationError('lang')
+4 -1
View File
@@ -96,7 +96,10 @@ async function load() {
process.stdin.setEncoding('utf8'); process.stdin.setEncoding('utf8');
process.stdin.on('data', (input) => { process.stdin.on('data', (input) => {
if (input[0] === '@') { if (input[0] === '@') {
cluster.workers[1].send({ event: 'run', command: input.substr(1, input.length - 1) }); for (const i in cluster.workers) {
cluster.workers[i].send({ event: 'run', command: input.substr(1, input.length - 1) });
break;
}
} else { } else {
executeCommand(input); executeCommand(input);
} }
-1
View File
@@ -186,7 +186,6 @@ const oi = {
rank: (tdocs) => ranked(tdocs, (a, b) => a.score === b.score), rank: (tdocs) => ranked(tdocs, (a, b) => a.score === b.score),
}; };
const RULES = { const RULES = {
acm, oi, acm, oi,
}; };
+1 -1
View File
@@ -98,7 +98,7 @@ async function getByEmail(domainId, mail, ignoreMissing = false) {
function setPassword(uid, password) { function setPassword(uid, password) {
const salt = String.random(); const salt = String.random();
return coll.findOneAndUpdate({ _id: uid }, { return coll.findOneAndUpdate({ _id: uid }, {
$set: { salt, hash: pwhash.hash(password, salt), hashType: 'hydro' }, $set: { salt, hash: pwhash(password, salt), hashType: 'hydro' },
}); });
} }
+4 -4
View File
@@ -194,7 +194,7 @@ class Handler {
} }
back(body) { back(body) {
if (body) this.response.body = body; this.response.body = body || this.response.body || {};
this.response.redirect = this.request.headers.referer || '/'; this.response.redirect = this.request.headers.referer || '/';
} }
@@ -225,6 +225,7 @@ class Handler {
if (anchor) return `${res}#${anchor}`; if (anchor) return `${res}#${anchor}`;
} catch (e) { } catch (e) {
console.error(e.message); console.error(e.message);
console.log(name, kwargs);
} }
return res; return res;
} }
@@ -284,16 +285,15 @@ class Handler {
async putResponse() { async putResponse() {
if (this.response.disposition) this.ctx.set('Content-Disposition', this.response.disposition); if (this.response.disposition) this.ctx.set('Content-Disposition', this.response.disposition);
if (this.response.redirect) { if (this.response.redirect && !this.preferJson) {
if (!this.preferJson) {
this.ctx.response.type = 'application/octet-stream'; this.ctx.response.type = 'application/octet-stream';
this.ctx.response.status = 302; this.ctx.response.status = 302;
this.ctx.redirect(this.response.redirect); this.ctx.redirect(this.response.redirect);
} else { } else {
if (this.response.body.redirect) {
this.response.body = this.response.body || {}; this.response.body = this.response.body || {};
this.response.body.url = this.response.redirect; this.response.body.url = this.response.redirect;
} }
} else {
if (this.response.body != null) { if (this.response.body != null) {
this.ctx.response.body = this.response.body; this.ctx.response.body = this.response.body;
this.ctx.response.status = this.response.status || 200; this.ctx.response.status = this.response.status || 200;
+1 -3
View File
@@ -1,8 +1,6 @@
const { hasIn } = require("lodash");
const dict = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'; const dict = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890';
String.random = function random(digit) { String.random = function random(digit = 32) {
let str = ''; let str = '';
for (let i = 1; i <= digit; i++) str += dict[Math.floor(Math.random() * 62)]; for (let i = 1; i <= digit; i++) str += dict[Math.floor(Math.random() * 62)];
return str; return str;
+12 -8
View File
@@ -10,7 +10,14 @@ discussion_edit: Discussion Edit
discussion_main: Discussion discussion_main: Discussion
discussion_node: Discussion discussion_node: Discussion
display_name: Display Name display_name: Display Name
domain_dashboard: Dashboard
domain_discussion: Discussion Nodes
domain_edit: Edit Info
domain_join_applications: Join Applications
domain_main: Main domain_main: Main
domain_permission: System Permission
domain_role: System Role
domain_user: System User
fs_upload: File Upload fs_upload: File Upload
home_account: Account Settings home_account: Account Settings
home_domain_account: Profile @ Domain home_domain_account: Profile @ Domain
@@ -20,6 +27,7 @@ home_file: My Files
home_messages: Messages home_messages: Messages
home_preference: Preference home_preference: Preference
home_security: Security home_security: Security
homepage: Home
homework_create: Homework Create homework_create: Homework Create
homework_detail_problem_submit: Submit Homework Problem homework_detail_problem_submit: Submit Homework Problem
homework_edit: Homework Edit homework_edit: Homework Edit
@@ -27,15 +35,9 @@ homework_main: Homework
homework_scoreboard: Scoreboard homework_scoreboard: Scoreboard
judge_playground: Judge Playground judge_playground: Judge Playground
main: Home main: Home
manage_dashboard: Dashboard
manage_discussion: Discussion Nodes
manage_edit: Edit Info
manage_join_applications: Join Applications
manage_module: Module Management manage_module: Module Management
manage_permission: System Permission manage_script: Scripts
manage_role: System Role
manage_setting: System Settings manage_setting: System Settings
manage_user: System User
manage: System Manage manage: System Manage
no_translation_warn: <blockquote class="warn">This part of content is under translation.</blockquote> no_translation_warn: <blockquote class="warn">This part of content is under translation.</blockquote>
page.problem_detail.sidebar.show_category: Click to Show page.problem_detail.sidebar.show_category: Click to Show
@@ -53,6 +55,7 @@ perm_record: Records
perm_training: Trainings perm_training: Trainings
problem_create: Problem Create problem_create: Problem Create
problem_edit: Problem Edit problem_edit: Problem Edit
problem_import: Import Problem
problem_main: Problem Set problem_main: Problem Set
problem_settings: Problem Settings problem_settings: Problem Settings
problem_solution: Problem Solution problem_solution: Problem Solution
@@ -60,6 +63,7 @@ problem_statistics: Problem Statistics
problem_submit: Problem Submit problem_submit: Problem Submit
problem-category-delim: '|' problem-category-delim: '|'
problem-subcategory-delim: ', ' problem-subcategory-delim: ', '
ranking: Ranking
record_detail: Record Detail record_detail: Record Detail
record_main: Judging Queue record_main: Judging Queue
setting_customize: Customize setting_customize: Customize
@@ -70,7 +74,7 @@ setting_info: Personal Info
setting_preference: Preference setting_preference: Preference
setting_privacy: Privacy setting_privacy: Privacy
setting_usage: Usage Preference setting_usage: Usage Preference
timeago_locale: en timeago_locale: en_US
training_create: Training Create training_create: Training Create
training_edit: Training Edit training_edit: Training Edit
training_main: Training training_main: Training
+2
View File
@@ -506,6 +506,7 @@ Problem Solution: 题解列表
Problem Tags Visibility: 题目标签可见性 Problem Tags Visibility: 题目标签可见性
problem_create: 创建题目 problem_create: 创建题目
problem_edit: 编辑题目 problem_edit: 编辑题目
problem_import: 导入题目
problem_main: 题库 problem_main: 题库
problem_settings: 题目设置 problem_settings: 题目设置
problem_solution: 题解 problem_solution: 题解
@@ -762,6 +763,7 @@ WeChat Visibility: 微信可见性
WeChat: 微信 WeChat: 微信
Week: 周 Week: 周
What is domain?: 什么是域? What is domain?: 什么是域?
What is this?: 这是什么?
What's domain?: 什么是域? What's domain?: 什么是域?
What's script?: 什么是脚本? What's script?: 什么是脚本?
What's this?: 这是什么? What's this?: 这是什么?
+1 -1
View File
@@ -11,6 +11,7 @@
"axios": "^0.19.0", "axios": "^0.19.0",
"bson": "^4.0.2", "bson": "^4.0.2",
"detect-browser": "^5.1.1", "detect-browser": "^5.1.1",
"eslint": "7.2.0",
"js-yaml": "^3.13.1", "js-yaml": "^3.13.1",
"koa": "^2.12.1", "koa": "^2.12.1",
"koa-body": "^4.2.0", "koa-body": "^4.2.0",
@@ -36,7 +37,6 @@
"yargs": "^15.3.1" "yargs": "^15.3.1"
}, },
"devDependencies": { "devDependencies": {
"eslint": "^7.2.0",
"eslint-config-airbnb-base": "^14.2.0", "eslint-config-airbnb-base": "^14.2.0",
"eslint-plugin-import": "^2.20.2", "eslint-plugin-import": "^2.20.2",
"friendly-errors-webpack-plugin": "^1.7.0", "friendly-errors-webpack-plugin": "^1.7.0",
@@ -14,7 +14,7 @@
</div> </div>
{{ reply_operations(doc, rdoc) }} {{ reply_operations(doc, rdoc) }}
</div> </div>
<div class="typo" data-emoji-enabled data-raw-url="{{ url('discussion_tail_reply_raw', did=doc.parentId, drid=doc._id, drrid=rdoc._id }}"> <div class="typo" data-emoji-enabled data-raw-url="{{ url('discussion_tail_reply_raw', did=doc.parentId, drid=doc._id, drrid=rdoc._id) }}">
{{ rdoc['content']|markdown|safe }} {{ rdoc['content']|markdown|safe }}
</div> </div>
<div class="commentbox-edit-target"></div> <div class="commentbox-edit-target"></div>
@@ -33,12 +33,12 @@
{% endif %} {% endif %}
{% if doc.owner == handler.state.user._id or handler.hasPerm(reply_edit_perm) %} {% if doc.owner == handler.state.user._id or handler.hasPerm(reply_edit_perm) %}
<a href="javascript:;" data-tooltip="{{ _('Edit') }}" name="dczcomments__op-edit-reply" <a href="javascript:;" data-tooltip="{{ _('Edit') }}" name="dczcomments__op-edit-reply"
data-form="{&quot;operation&quot;:&quot;{{reply_edit_op}}&quot;,&quot;{{comment_ref}}&quot;:&quot;{{doc._id}}&quot;,&quot;{{reply_ref}}&quot;:&quot;{{rdoc._id}}&quot;}" data-form="{{ {operation: reply_edit_op, drid: doc._id, drrid: rdoc._id}|json }}"
><span class="icon icon-edit"></span></a> ><span class="icon icon-edit"></span></a>
{% endif %} {% endif %}
{% if doc.owner == handler.state.user._id or handler.hasPerm(reply_delete_perm) %} {% if doc.owner == handler.state.user._id or handler.hasPerm(reply_delete_perm) %}
<a href="javascript:;" data-tooltip="{{ _('Delete') }}" name="dczcomments__op-delete-reply" <a href="javascript:;" data-tooltip="{{ _('Delete') }}" name="dczcomments__op-delete-reply"
data-form="{&quot;operation&quot;:&quot;{{reply_delete_op}}&quot;,&quot;{{comment_ref}}&quot;:&quot;{{doc._id}}&quot;,&quot;{{reply_ref}}&quot;:&quot;{{rdoc._id}}&quot;}" data-form="{{ {operation: reply_delete_op, drid: doc._id, drrid: rdoc._id}|json }}"
><span class="icon icon-delete"></span></a> ><span class="icon icon-delete"></span></a>
{% endif %} {% endif %}
</div> </div>
@@ -92,7 +92,7 @@
</form> </form>
</div> </div>
<div class="nojs--hide"> <div class="nojs--hide">
<textarea class="textbox" name="dczcomments__dummy-box" readonly data-form="{&quot;operation&quot;:&quot;{{comment_post_op}}&quot;}" placeholder="{{ _(comment_placeholder) }}"></textarea> <textarea class="textbox" name="dczcomments__dummy-box" readonly data-form="{{ {operation: comment_post_op}|json }}" placeholder="{{ _(comment_placeholder) }}"></textarea>
</div> </div>
<div class="commentbox-placeholder"></div> <div class="commentbox-placeholder"></div>
<ul style="display:none"> <ul style="display:none">
@@ -118,28 +118,28 @@
<div class="dczcomments__operations nojs--hide"> <div class="dczcomments__operations nojs--hide">
{% if handler.hasPerm(reply_post_perm) %} {% if handler.hasPerm(reply_post_perm) %}
<a href="javascript:;" data-tooltip="{{ _('Reply') }}" name="dczcomments__op-reply-comment" <a href="javascript:;" data-tooltip="{{ _('Reply') }}" name="dczcomments__op-reply-comment"
data-form="{&quot;operation&quot;:&quot;{{reply_post_op}}&quot;, &quot;{{comment_ref}}&quot;:&quot;{{doc._id}}&quot;}" data-form="{{ {operation: reply_post_op, drid: doc._id}|json }}"
><span class="icon icon-reply"></span></a> ><span class="icon icon-reply"></span></a>
{% endif %} {% endif %}
{% if doc.owner == handler.state.user._id or handler.hasPerm(comment_edit_perm) %} {% if doc.owner == handler.state.user._id or handler.hasPerm(comment_edit_perm) %}
<a href="javascript:;" data-tooltip="{{ _('Edit') }}" name="dczcomments__op-edit-comment" <a href="javascript:;" data-tooltip="{{ _('Edit') }}" name="dczcomments__op-edit-comment"
data-form="{&quot;operation&quot;:&quot;{{comment_edit_op}}&quot;,&quot;{{comment_ref}}&quot;:&quot;{{doc._id}}&quot;}" data-form="{{ {operation: comment_edit_op, drid: doc._id}|json }}"
><span class="icon icon-edit"></span></a> ><span class="icon icon-edit"></span></a>
{% endif %} {% endif %}
{% if doc.owner == handler.state.user._id or handler.hasPerm(comment_delete_perm) %} {% if doc.owner == handler.state.user._id or handler.hasPerm(comment_delete_perm) %}
<a href="javascript:;" data-tooltip="{{ _('Delete') }}" name="dczcomments__op-delete-comment" <a href="javascript:;" data-tooltip="{{ _('Delete') }}" name="dczcomments__op-delete-comment"
data-form="{&quot;operation&quot;:&quot;{{comment_delete_op}}&quot;,&quot;{{comment_ref}}&quot;:&quot;{{doc._id}}&quot;}" data-form="{{ {operation: comment_delete_op, drid: doc._id}|json }}"
><span class="icon icon-delete"></span></a> ><span class="icon icon-delete"></span></a>
{% endif %} {% endif %}
</div> </div>
</div> </div>
<div class="typo" data-emoji-enabled data-raw-url="{{ url('discuss_reply_raw', did=doc.parentId, drid=doc._id) }}"> <div class="typo" data-emoji-enabled data-raw-url="{{ url('discussion_reply_raw', did=doc.parentId, drid=doc._id) }}">
{{ doc['content']|markdown|safe }} {{ doc['content']|markdown|safe }}
</div> </div>
<div class="commentbox-edit-target"></div> <div class="commentbox-edit-target"></div>
<ul class="dczcomments__replies commentbox-reply-target"> <ul class="dczcomments__replies commentbox-reply-target">
{% for rdoc in doc['reply'] %} {% for rdoc in doc['reply'] %}
{{ reply(mode_create, udict[rdoc['owner']], doc, rdoc) }} {{ reply(mode_create, udict[rdoc.owner], doc, rdoc) }}
{% endfor %} {% endfor %}
</ul> </ul>
</div> </div>
+6 -6
View File
@@ -58,7 +58,7 @@
</form> </form>
</div> </div>
<div class="nojs--hide"> <div class="nojs--hide">
<textarea class="textbox" name="dczcomments__dummy-box" readonly data-form="{&quot;operation&quot;:&quot;{{comment_post_op}}&quot;}" placeholder="{{ _(comment_placeholder) }}"></textarea> <textarea class="textbox" name="dczcomments__dummy-box" readonly data-form="{{ {operation: comment_post_op}|json }}" placeholder="{{ _(comment_placeholder) }}"></textarea>
</div> </div>
<div class="commentbox-placeholder"></div> <div class="commentbox-placeholder"></div>
<ul style="display:none"> <ul style="display:none">
@@ -115,17 +115,17 @@
<div class="dczcomments__operations nojs--hide"> <div class="dczcomments__operations nojs--hide">
{% if handler.hasPerm(reply_post_perm) %} {% if handler.hasPerm(reply_post_perm) %}
<a href="javascript:;" data-tooltip="{{ _('Reply') }}" name="dczcomments__op-reply-comment" <a href="javascript:;" data-tooltip="{{ _('Reply') }}" name="dczcomments__op-reply-comment"
data-form="{&quot;operation&quot;:&quot;{{reply_post_op}}&quot;, &quot;{{comment_ref}}&quot;:&quot;{{doc._id}}&quot;}" data-form="{{ {operation: reply_post_op, psrid: doc._id}|json }}"
><span class="icon icon-reply"></span></a> ><span class="icon icon-reply"></span></a>
{% endif %} {% endif %}
{% if doc.owner == handler.state.user._id or handler.hasPerm(comment_edit_perm) %} {% if doc.owner == handler.state.user._id or handler.hasPerm(comment_edit_perm) %}
<a href="javascript:;" data-tooltip="{{ _('Edit') }}" name="dczcomments__op-edit-comment" <a href="javascript:;" data-tooltip="{{ _('Edit') }}" name="dczcomments__op-edit-comment"
data-form="{&quot;operation&quot;:&quot;{{comment_edit_op}}&quot;,&quot;{{comment_ref}}&quot;:&quot;{{doc._id}}&quot;}" data-form="{{ {operation: comment_edit_op, psrid: doc._id}|json }}"
><span class="icon icon-edit"></span></a> ><span class="icon icon-edit"></span></a>
{% endif %} {% endif %}
{% if doc.owner == handler.state.user._id or handler.hasPerm(comment_delete_perm) %} {% if doc.owner == handler.state.user._id or handler.hasPerm(comment_delete_perm) %}
<a href="javascript:;" data-tooltip="{{ _('Delete') }}" name="dczcomments__op-delete-comment" <a href="javascript:;" data-tooltip="{{ _('Delete') }}" name="dczcomments__op-delete-comment"
data-form="{&quot;operation&quot;:&quot;{{comment_delete_op}}&quot;,&quot;{{comment_ref}}&quot;:&quot;{{doc._id}}&quot;}" data-form="{{ {operation: comment_delete_op, psrid: doc._id}|json }}"
><span class="icon icon-delete"></span></a> ><span class="icon icon-delete"></span></a>
{% endif %} {% endif %}
</div> </div>
@@ -150,12 +150,12 @@
{% endif %} {% endif %}
{% if doc.owner == handler.state.user._id or handler.hasPerm(reply_edit_perm) %} {% if doc.owner == handler.state.user._id or handler.hasPerm(reply_edit_perm) %}
<a href="javascript:;" data-tooltip="{{ _('Edit') }}" name="dczcomments__op-edit-reply" <a href="javascript:;" data-tooltip="{{ _('Edit') }}" name="dczcomments__op-edit-reply"
data-form="{&quot;operation&quot;:&quot;{{reply_edit_op}}&quot;,&quot;{{comment_ref}}&quot;:&quot;{{doc._id}}&quot;,&quot;{{reply_ref}}&quot;:&quot;{{rdoc._id}}&quot;}" data-form="{{ {operation: reply_edit_op, psid: doc._id, psrid: rdoc._id}|json }}"
><span class="icon icon-edit"></span></a> ><span class="icon icon-edit"></span></a>
{% endif %} {% endif %}
{% if doc.owner == handler.state.user._id or handler.hasPerm(reply_delete_perm) %} {% if doc.owner == handler.state.user._id or handler.hasPerm(reply_delete_perm) %}
<a href="javascript:;" data-tooltip="{{ _('Delete') }}" name="dczcomments__op-delete-reply" <a href="javascript:;" data-tooltip="{{ _('Delete') }}" name="dczcomments__op-delete-reply"
data-form="{&quot;operation&quot;:&quot;{{reply_delete_op}}&quot;,&quot;{{comment_ref}}&quot;:&quot;{{doc._id}}&quot;,&quot;{{reply_ref}}&quot;:&quot;{{rdoc._id}}&quot;}" data-form="{{ {operation: reply_delete_op, psid: doc._id, psrid: rdoc._id}|json }}"
><span class="icon icon-delete"></span></a> ><span class="icon icon-delete"></span></a>
{% endif %} {% endif %}
</div> </div>
+5 -5
View File
@@ -7,7 +7,7 @@
<div class="section__body"> <div class="section__body">
<ul class="supplementary list"> <ul class="supplementary list">
<li> <li>
<a class="discussion-node-tag" href="{{ url('discussion_node', type=model.discussion.typeDisplay[vnode.parentType], name=vnode.id) }}"> <a class="discussion-node-tag" href="{{ url('discussion_node', type=model.discussion.typeDisplay[vnode.type], name=vnode.id) }}">
{% if ddoc.parentType == model.document.TYPE_DISCUSSION_NODE %} {% if ddoc.parentType == model.document.TYPE_DISCUSSION_NODE %}
<span class="v-center icon icon-tag"></span> <span class="v-center icon icon-tag"></span>
{% elif ddoc.parentType == model.document.TYPE_CONTEST %} {% elif ddoc.parentType == model.document.TYPE_CONTEST %}
@@ -97,26 +97,26 @@
<div class="balancer sidebar-user-stat"> <div class="balancer sidebar-user-stat">
<div class="balancer__body"> <div class="balancer__body">
<div class="numbox"> <div class="numbox">
<div class="numbox__num medium">{{ udict[ddoc['owner']].nAccept or 0 }}</div> <div class="numbox__num medium">{{ udict[ddoc.owner].nAccept or 0 }}</div>
<div class="numbox__text">{{ _('Accepted') }}</div> <div class="numbox__text">{{ _('Accepted') }}</div>
</div> </div>
</div> </div>
<div class="balancer__body"> <div class="balancer__body">
<div class="numbox"> <div class="numbox">
<div class="numbox__num medium">{{ udict[ddoc['owner']].nLike or 0 }}</div> <div class="numbox__num medium">{{ udict[ddoc.owner].nLike or 0 }}</div>
<div class="numbox__text">{{ _('Solutions Liked') }}</div> <div class="numbox__text">{{ _('Solutions Liked') }}</div>
</div> </div>
</div> </div>
<div class="balancer__body"> <div class="balancer__body">
<div class="numbox"> <div class="numbox">
<div class="numbox__num medium">{{ udict[ddoc['owner']].nProblems or 0 }}</div> <div class="numbox__num medium">{{ udict[ddoc.owner].nProblems or 0 }}</div>
<div class="numbox__text">{{ _('Upload Problem') }}</div> <div class="numbox__text">{{ _('Upload Problem') }}</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{% if vnode.type == 'problem' %} {% if vnode.type == model.document.TYPE_PROBLEM %}
{% set pdoc = vnode %} {% set pdoc = vnode %}
{% set owner_udoc = udict[vnode['owner']] %} {% set owner_udoc = udict[vnode['owner']] %}
{% include "partials/problem_sidebar.html" %} {% include "partials/problem_sidebar.html" %}
+4 -4
View File
@@ -7,15 +7,15 @@
</div> </div>
</div> </div>
<div class="medium-3 columns"> <div class="medium-3 columns">
{% if vnode.parentType == model.document.TYPE_PROBLEM %} {% if vnode.type == model.document.TYPE_PROBLEM %}
{% set pdoc = vnode %} {% set pdoc = vnode %}
{% set owner_udoc = udict[vnode.owner] %} {% set owner_udoc = udict[vnode.owner] %}
{% include "partials/problem_sidebar.html" %} {% include "partials/problem_sidebar.html" %}
{% elif vnode.parentType == model.document.TYPE_CONTEST %} {% elif vnode.type == model.document.TYPE_CONTEST %}
{% set tdoc = vnode %} {% set tdoc = vnode %}
{% set owner_udoc = udict[vnode.owner] %} {% set owner_udoc = udict[vnode.owner] %}
{% include "partials/contest_sidebar.html" %} {% include "partials/contest_sidebar.html" %}
{% elif vnode.parentType == model.document.TYPE_DISCUSSION_NODE %} {% elif vnode.type == model.document.TYPE_DISCUSSION_NODE %}
<div class="section side"> <div class="section side">
{% if vnode.pic %}<div class="discussion-node__bg {{ vnode.pic }}"></div>{% endif %} {% if vnode.pic %}<div class="discussion-node__bg {{ vnode.pic }}"></div>{% endif %}
<div class="section__header"> <div class="section__header">
@@ -24,7 +24,7 @@
<div class="section__body"> <div class="section__body">
{% if vnode %} {% if vnode %}
{% if handler.hasPerm(perm.PERM_CREATE_DISCUSSION) %} {% if handler.hasPerm(perm.PERM_CREATE_DISCUSSION) %}
<p><a href="{{ url('discussion_create', type=model.discussion.typeDisplay[vnode.parentType], name=vnode.parentId) }}" class="expanded primary button">{{ _('Create a Discussion') }}</a></p> <p><a href="{{ url('discussion_create', type=model.discussion.typeDisplay[vnode.type], name=vnode.id) }}" class="expanded primary button">{{ _('Create a Discussion') }}</a></p>
{% else %} {% else %}
{% if not handler.hasPerm(perm.PERM_LOGGEDIN) %} {% if not handler.hasPerm(perm.PERM_LOGGEDIN) %}
<p><a href="javascript:showSignInDialog();" class="expanded primary button">{{ _('Login to Create a Discussion') }}</a></p> <p><a href="javascript:showSignInDialog();" class="expanded primary button">{{ _('Login to Create a Discussion') }}</a></p>
+2 -2
View File
@@ -1,6 +1,6 @@
{% extends "layout/mail.html" %} {% extends "layout/mail.html" %}
{% block title %}{{ _('Sign Up') }}{% endblock %} {% block title %}{{ _('Sign Up') }}{% endblock %}
{% block content %} {% block content %}
<p>{{ _('Hello! You can click following link to sign up your Vijos account:') }}</p> <p>{{ _('Hello! You can click following link to sign up your Hydro account:') }}</p>
<p><a href="{{ url_prefix }}{{ url }}">{{ url_prefix }}{{ url }}</a></p> <p><a href="{{ url_prefix|safe }}{{ path|safe }}">{{ url_prefix|safe }}{{ path|safe }}</a></p>
{% endblock %} {% endblock %}
+23 -23
View File
@@ -3,23 +3,23 @@
"@babel/code-frame@^7.0.0": "@babel/code-frame@^7.0.0":
version "7.10.1" version "7.10.3"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.1.tgz#d5481c5095daa1c57e16e54c6f9198443afb49ff" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.3.tgz#324bcfd8d35cd3d47dae18cde63d752086435e9a"
integrity sha512-IGhtTmpjGbYzcEDOw7DcQtbQSXcG9ftmAXtWTu9V936vDye4xjjekktFAtgZsWpzTj/X01jocB46mTywm/4SZw== integrity sha512-fDx9eNW0qz0WkUeqL6tXEXzVlPh6Y5aCDEZesl0xBGA8ndRukX91Uk44ZqnkECp01NAZUdCAl+aiQNGi0k88Eg==
dependencies: dependencies:
"@babel/highlight" "^7.10.1" "@babel/highlight" "^7.10.3"
"@babel/helper-validator-identifier@^7.10.1": "@babel/helper-validator-identifier@^7.10.3":
version "7.10.1" version "7.10.3"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.1.tgz#5770b0c1a826c4f53f5ede5e153163e0318e94b5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.3.tgz#60d9847f98c4cea1b279e005fdb7c28be5412d15"
integrity sha512-5vW/JXLALhczRCWP0PnFDMCJAchlBvM7f4uk/jXritBnIa6E1KmqmtrS3yn1LAnxFBypQ3eneLuXjsnfQsgILw== integrity sha512-bU8JvtlYpJSBPuj1VUmKpFGaDZuLxASky3LhaKj3bmpSTY6VWooSM8msk+Z0CZoErFye2tlABF6yDkT3FOPAXw==
"@babel/highlight@^7.10.1": "@babel/highlight@^7.10.3":
version "7.10.1" version "7.10.3"
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.1.tgz#841d098ba613ba1a427a2b383d79e35552c38ae0" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.3.tgz#c633bb34adf07c5c13156692f5922c81ec53f28d"
integrity sha512-8rMof+gVP8mxYZApLF/JgNDAkdKa+aJt3ZYxF8z6+j/hpeXL7iMsKCPHa2jNMHu/qqBwzQF4OHNoYi8dMA/rYg== integrity sha512-Ih9B/u7AtgEnySE2L2F0Xm0GaM729XqqLfHkalTsbjXGyqmf/6M0Cu0WpvqueUlW+xk88BHw9Nkpj49naU+vWw==
dependencies: dependencies:
"@babel/helper-validator-identifier" "^7.10.1" "@babel/helper-validator-identifier" "^7.10.3"
chalk "^2.0.0" chalk "^2.0.0"
js-tokens "^4.0.0" js-tokens "^4.0.0"
@@ -245,9 +245,9 @@ ajv-errors@^1.0.0:
integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ== integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==
ajv-keywords@^3.1.0, ajv-keywords@^3.4.1: ajv-keywords@^3.1.0, ajv-keywords@^3.4.1:
version "3.4.1" version "3.5.0"
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.4.1.tgz#ef916e271c64ac12171fd8384eaae6b2345854da" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.0.tgz#5c894537098785926d71e696114a53ce768ed773"
integrity sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ== integrity sha512-eyoaac3btgU8eJlvh01En8OCKzRqlLe2G5jDsCr3RiE2uLGMEEB1aaGwVVpwR8M95956tGH6R+9edC++OvzaVw==
ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2: ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2:
version "6.12.2" version "6.12.2"
@@ -1397,11 +1397,11 @@ eslint-utils@^2.0.0:
eslint-visitor-keys "^1.1.0" eslint-visitor-keys "^1.1.0"
eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.2.0: eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.2.0:
version "1.2.0" version "1.3.0"
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.2.0.tgz#74415ac884874495f78ec2a97349525344c981fa" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e"
integrity sha512-WFb4ihckKil6hu3Dp798xdzSfddwKKU3+nGniKF6HfeW6OLd2OUDEPP7TcHtB5+QXOKg2s6B2DaMPE1Nn/kxKQ== integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==
eslint@^7.2.0: eslint@7.2.0:
version "7.2.0" version "7.2.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.2.0.tgz#d41b2e47804b30dbabb093a967fb283d560082e6" resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.2.0.tgz#d41b2e47804b30dbabb093a967fb283d560082e6"
integrity sha512-B3BtEyaDKC5MlfDa2Ha8/D6DsS4fju95zs0hjS3HdGazw+LNayai38A25qMppK37wWGWNYSPOR6oYzlz5MHsRQ== integrity sha512-B3BtEyaDKC5MlfDa2Ha8/D6DsS4fju95zs0hjS3HdGazw+LNayai38A25qMppK37wWGWNYSPOR6oYzlz5MHsRQ==
@@ -2865,9 +2865,9 @@ object-copy@^0.1.0:
kind-of "^3.0.3" kind-of "^3.0.3"
object-inspect@^1.7.0: object-inspect@^1.7.0:
version "1.7.0" version "1.8.0"
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.8.0.tgz#df807e5ecf53a609cc6bfe93eac3cc7be5b3a9d0"
integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw== integrity sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==
object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1:
version "1.1.1" version "1.1.1"