添加部分注释,修复一些功能

pull/1/head
masnn 5 years ago
parent f40e4e2c74
commit d3e36fcb6c

@ -250,15 +250,12 @@ class ContestCreateHandler extends ContestHandler {
}
async post({ title, content, rule, beginAtDate, beginAtTime, duration, pids }) {
let beginAt, endAt;
console.log(beginAtDate, beginAtTime);
try {
beginAt = new Date(Date.parse(`${beginAtDate} ${beginAtTime.replace('-', ':')}`));
console.log(beginAt, duration);
} catch (e) {
throw new ValidationError('beginAtDate', 'beginAtTime');
}
endAt = new Date(beginAt.getTime() + duration * 3600 * 1000);
console.log(endAt);
if (beginAt >= endAt) throw new ValidationError('duration');
await this.verifyProblems(pids);
let tid = await contest.add(title, content, this.user._id, rule, beginAt, endAt, pids);

@ -56,10 +56,12 @@ async function end(body) {
}
class JudgeHandler extends Handler {
async prepare(){
async prepare() {
this.checkPerm(PERM_JUDGE);
}
async get() {
async get({ check = false }) {
this.response.body = {};
if (check) return;
let rid = await queue.get('judge', false);
if (rid) {
let rdoc = await record.get(rid);
@ -74,7 +76,6 @@ class JudgeHandler extends Handler {
};
this.response.body = { task };
}
else this.response.body = {};
}
async post_next() {
await next(this.request.body);

@ -56,7 +56,7 @@ class ProblemDetailHandler extends ProblemHandler {
this.response.template = 'problem_detail.html';
this.uid = this.user._id;
this.pid = pid;
if (pid) this.pdoc = await problem.get(this);
if (pid) this.pdoc = await problem.get(this.pid, this.uid);
if (this.pdoc.hidden && this.pdoc.owner != this.uid) this.checkPerm(PERM_VIEW_PROBLEM_HIDDEN);
if (this.pdoc) this.udoc = await user.getById(this.pdoc.owner);
this.response.body = {
@ -115,7 +115,7 @@ class ProblemManageHandler extends ProblemDetailHandler {
class ProblemSettingsHandler extends ProblemManageHandler {
async get() {
this.response.template = 'problem_settings.html';
this.ctx.body.path = [
this.response.body.path = [
['Hydro', '/'],
['problem_main', '/p'],
[this.pdoc.title, `/p/${this.pid}`, true],
@ -141,15 +141,14 @@ class ProblemEditHandler extends ProblemManageHandler {
}
async post({ title, content }) {
let pid = validator.checkPid(this.request.body.pid);
let pdoc = await problem.get({ pid: this.params.pid });
let pdoc = await problem.get(this.params.pid);
await problem.edit(pdoc._id, { title, content, pid });
this.response.redirect = `/p/${pid}`;
}
}
class ProblemDataUploadHandler extends ProblemManageHandler {
constructor(ctx) {
super(ctx);
async prepare(){
this.response.template = 'problem_upload.html';
}
async get() {

@ -17,7 +17,7 @@ class RecordListHandler extends Handler {
let pdict = {}, udict = {};
for (let rdoc of rdocs) {
udict[rdoc.uid] = await user.getById(rdoc.uid);
pdict[rdoc.pid] = await problem.get({ pid: rdoc.pid, uid: this.user._id });
pdict[rdoc.pid] = await problem.get(rdoc.pid, this.user._id);
}
this.response.body = {
path: [
@ -51,7 +51,7 @@ class RecordRejudgeHandler extends Handler {
await record.reset(rid);
await queue.push('judge', rid);
}
this.response.back();
this.back();
}
}
class RecordConnectionHandler extends ConnectionHandler {
@ -70,7 +70,7 @@ class RecordConnectionHandler extends ConnectionHandler {
async onRecordChange(data) {
let rdoc = data.value;
if (rdoc.tid && rdoc.tid.toString() != this.tid) return;
let [udoc, pdoc] = await Promise.all([user.getById(rdoc.uid), problem.get({ pid: rdoc.pid })]);
let [udoc, pdoc] = await Promise.all([user.getById(rdoc.uid), problem.getById(rdoc.pid)]);
if (pdoc.hidden && !this.user.hasPerm(PERM_VIEW_PROBLEM_HIDDEN)) pdoc = null;
this.send({ html: await this.renderHTML('record_main_tr.html', { rdoc, udoc, pdoc }) });
}

@ -1,8 +1,11 @@
require('./utils');
const
Mongo = require('mongodb'),
{ defaults } = require('lodash'),
builtin = require('./model/builtin'),
pwhash = require('./lib/pwhash'),
options = require('./options');
options = require('./options'),
{ udoc } = require('../interfaces');
async function run() {
let mongourl = 'mongodb://';
@ -13,30 +16,47 @@ async function run() {
let coll_user = db.collection('user');
let coll_role = db.collection('role');
let coll_blacklist = db.collection('blacklist');
async function createRootUser() {
let coll_token = db.collection('token');
async function createUser() {
let salt = pwhash.salt();
await coll_user.insertOne({
_id: -1,
email: 'root@hydro',
emailLower: 'root@hydro',
uname: 'Root',
unameLower: 'root',
password: pwhash.hash('root', salt),
salt,
regat: new Date(),
regip: '127.0.0.1',
loginat: new Date(),
loginip: '127.0.0.1',
gravatar: 'root@hydro',
role: 'admin'
});
await coll_user.insertMany([
defaults({
_id: 0,
uname: 'Hydro',
unameLower: 'hydro',
mail: 'hydro@hydro',
mailLower: 'hydro@hydro',
role: 'guest'
}, udoc),
defaults({
_id: 1,
mail: 'guest@hydro',
mailLower: 'guest@hydro',
uname: 'Guest',
unameLower: 'guest',
role: 'guest'
}, udoc),
defaults({
_id: -1,
mail: 'root@hydro',
mailLower: 'root@hydro',
uname: 'Root',
unameLower: 'root',
hash: pwhash.hash('rootroot', salt),
salt,
gravatar: 'root@hydro',
role: 'admin'
}, udoc)
]);
}
await coll_user.createIndex('unameLower', { unique: true });
await coll_user.createIndex('emailLower', { sparse: true });
await coll_user.createIndex('mailLower', { sparse: true });
await coll_role.insertMany(builtin.BUILTIN_ROLES);
await coll_user.insertMany(builtin.BUILTIN_USERS);
await coll_blacklist.createIndex('expireAt', { expireAfterSeconds: 0 });
await createRootUser();
await coll_token.createIndex([{ uid: 1 }, { tokenType: 1 }, { updateAt: -1 }], { sparse: true });
await coll_token.createIndex('expireAt', { expireAfterSeconds: 0 });
await createUser();
console.log('Installed');
process.exit(0);
}

@ -0,0 +1,72 @@
const { ObjectID } = require('bson');
exports.udoc = {
_id: 0,
mail: '',
mailLower: '',
uname: '',
unameLower: '',
salt: '',
hash: '',
hashType: 'hydro',
nAccept: 0,
nSubmit: 0,
nLike: 0,
bio: '',
gender: 0,
regat: new Date(),
regip: '0.0.0.0',
gravatar: '',
loginat: new Date(),
loginip: '0.0.0.0'
};
/*
export interface Pdoc {
_id: ObjectID
pid: string
owner: number
title: string
content: string
nSubmit: number
nAccept: number
tag: string[]
category: string[],
data: ObjectID | null
hidden: boolean
}
export interface TestCase {
time: number,
memory: number,
status: number,
message: string
}
export interface Rdoc {
_id: ObjectID,
pid: ObjectID,
owner: number,
lang: string,
code: string,
score: number,
memory: number,
time: number,
judgeTexts: string[],
compilerTexts: string[],
testCases: TestCase[],
rejudged: boolean,
judger: string,
judgeAt: Date,
status: number
}
export interface Bdoc {
_id: string,
expireAt: Date
}
export interface Tdoc {
_id: ObjectID
beginAt: Date
endAt: Date
attend: number
title: string
content: string
pids: ObjectID[]
}
*/

@ -1,18 +1,33 @@
export class User {
import { ObjectID } from 'bson';
export interface Udoc {
_id: number
email: string
emailLower: string
mail: string
mailLower: string
uname: string
unameLower: string
salt: string
hash: string
displayName: string = ''
nAccept: number = 0
nSubmit: number = 0
perm: string = '0'
constructor(user) { }
hasPerm(perm) { }
checkPassword(password) { }
hashType: string
nAccept: number
nSubmit: number
nLike: number
perm: string
hasPerm: Function
checkPassword: Function
}
export interface Pdoc {
_id: ObjectID
pid: string
owner: number
title: string
content: string
nSubmit: number
nAccept: number
tag: string[]
category: string[],
data: ObjectID | null
hidden: boolean
}
export interface TestCase {
time: number,
@ -20,10 +35,10 @@ export interface TestCase {
status: number,
message: string
}
export interface Record {
_id: import('bson').ObjectID,
pid: string | boolean,
creator: number,
export interface Rdoc {
_id: ObjectID,
pid: ObjectID,
owner: number,
lang: string,
code: string,
score: number,
@ -32,18 +47,21 @@ export interface Record {
judgeTexts: string[],
compilerTexts: string[],
testCases: TestCase[],
rejudged: boolean,
judger: string,
judgeAt: Date,
status: number
}
export interface Problem {
_id: number | string,
title: string,
content: string,
timeLimit: number,
memoryLimit: number,
nSubmit: number,
nAccept: number,
tags: string[],
categories: string[]
export interface Bdoc {
_id: string,
expireAt: Date
}
export interface Tdoc {
_id: ObjectID
beginAt: Date
endAt: Date
attend: number
title: string
content: string
pids: ObjectID[]
}

@ -2,11 +2,15 @@ const
db = require('../service/db.js'),
coll = db.collection('blacklist');
module.exports = {
add(ip) {
let expireAt = new Date(new Date().getTime() + 365 * 24 * 60 * 60 * 1000);
return coll.findOneAndUpdate({ _id: ip }, { $set: { expireAt } }, { upsert: true });
},
get: ip => coll.findOne({ _id: ip }),
delete: ip => coll.deleteOne({ _id: ip })
};
async function add(ip) {
let expireAt = new Date(new Date().getTime() + 365 * 24 * 60 * 60 * 1000);
return coll.findOneAndUpdate({ _id: ip }, { $set: { expireAt } }, { upsert: true });
}
function get(ip) {
return coll.findOne({ _id: ip });
}
function del(ip) {
return coll.deleteOne({ _id: ip });
}
module.exports = { add, get, del };

@ -1,37 +1,4 @@
const perm = require('../permission');
const BUILTIN_USERS = [
{
_id: 0,
uname: 'Hydro',
unameLower: 'hydro',
email: '',
emailLower: '',
salt: '',
hash: '',
gender: 'other',
regat: new Date(),
regip: '127.0.0.1',
gravatar: '',
loginat: new Date(),
loginip: '127.0.0.1',
role: 'guest'
},
{
_id: 1,
email: 'guest@hydro',
emailLower: 'guest@hydro',
uname: 'Guest',
unameLower: 'guest',
hash: '',
salt: '',
regat: new Date(),
regip: '127.0.0.1',
loginat: new Date(),
loginip: '127.0.0.1',
gravatar: 'guest@hydro',
role: 'guest'
}
];
const BUILTIN_ROLES = [
{ _id: 'guest', perm: perm.PERM_BASIC },
{ _id: 'default', perm: perm.PERM_DEFAULT },
@ -262,6 +229,6 @@ const STATUS_CODES = {
30: 'ignored'
};
module.exports = {
BUILTIN_USERS, BUILTIN_ROLES, CATEGORIES, VIEW_LANGS, FOOTER_EXTRA_HTMLS, LANGS,
BUILTIN_ROLES, CATEGORIES, VIEW_LANGS, FOOTER_EXTRA_HTMLS, LANGS,
LANG_TEXTS, LANG_HIGHLIGHT_ID, STATUS, STATUS_TEXTS, STATUS_CODES
};

@ -5,13 +5,28 @@ const
coll = db.collection('contest'),
coll_status = db.collection('contest.status');
const RULES = {
homework: require('../module/contest/homework'),
oi: require('../module/contest/oi'),
acm: require('../module/contest/acm')
};
/**
* @typedef {import('bson').ObjectID} ObjectID
* @typedef {import('../interface').Tdoc} Tdoc
*/
/**
* @param {string} title
* @param {string} content
* @param {number} owner
* @param {string} rule
* @param {Date} beginAt
* @param {Date} endAt
* @param {ObjectID[]} pids
* @param {object} data
* @returns {ObjectID} tid
*/
async function add(title, content, owner, rule,
beginAt = new Date(), endAt = new Date(), pids = [], data = {}) {
validator.checkTitle(title);
@ -23,6 +38,11 @@ async function add(title, content, owner, rule,
let res = await coll.insertOne(data);
return res.insertedId;
}
/**
* @param {ObjectID} tid
* @param {object} $set
* @returns {Tdoc} tdoc after modification
*/
async function edit(tid, $set) {
if ($set.title) validator.checkTitle($set.title);
if ($set.content) validator.checkIntro($set.content);
@ -36,6 +56,10 @@ async function edit(tid, $set) {
await coll.findOneAndUpdate({ tid }, { $set });
return tdoc;
}
/**
* @param {ObjectID} tid
* @returns {Tdoc}
*/
async function get(tid) {
let tdoc = await coll.findOne({ _id: tid });
if (!tdoc) throw new ContestNotFoundError(tid);

@ -6,6 +6,12 @@ const
coll = db.collection('problem'),
coll_status = db.collection('problem.status');
/**
* @typedef {import('../interface').Pdoc} Pdoc
* @typedef {import('bson').ObjectID} ObjectID
* @typedef {import('mongodb').Cursor} Cursor
*/
/**
* @param {string} title
* @param {string} content
@ -42,7 +48,12 @@ async function add({
});
return pid;
}
async function get({ pid, uid }) {
/**
* @param {string|ObjectID} pid
* @param {number} uid
* @returns {Pdoc}
*/
async function get(pid, uid = null) {
let query = {};
if (pid.generationTime || pid.length == 24) query = { _id: new ObjectID(pid) };
else query = { pid: parseInt(pid) || pid };
@ -54,18 +65,36 @@ async function get({ pid, uid }) {
}
return pdoc;
}
/**
* @param {ObjectID} pid
* @returns {Pdoc}
*/
async function getById(_id) {
_id = new ObjectID(_id);
let pdoc = await coll.findOne({ _id });
if (!pdoc) throw new ProblemNotFoundError(_id);
return pdoc;
}
async function getMany(query, sort, page, limit) {
return await coll.find(query).sort(sort).skip((page - 1) * limit).limit(limit).toArray();
/**
* @param {string|ObjectID} pid
* @param {number} uid
* @returns {Pdoc[]}
*/
function getMany(query, sort, page, limit) {
return coll.find(query).sort(sort).skip((page - 1) * limit).limit(limit).toArray();
}
/**
* @param {object} query
* @returns {Cursor}
*/
function getMulti(query) {
return coll.find(query);
}
/**
* @param {ObjectID} _id
* @param {object} query
* @returns {Pdoc}
*/
async function edit(_id, $set) {
if ($set.title) validator.checkTitle($set.title);
if ($set.content) validator.checkContent($set.content);
@ -74,6 +103,10 @@ async function edit(_id, $set) {
if (!pdoc) throw new ProblemNotFoundError(_id);
return pdoc;
}
/**
* @param {object} query
* @returns {number}
*/
async function count(query) {
return await coll.find(query).count();
}
@ -87,7 +120,7 @@ async function random(query) {
}
async function getList(pids) {
let r = {};
for (let pid of pids) r[pid] = await get({pid});
for (let pid of pids) r[pid] = await get({ pid });
return r;
}

@ -55,7 +55,8 @@ async function reset(rid) {
judgeTexts: [],
compilerTexts: [],
judgeAt: null,
judger: null
judger: null,
rejudged: true
});
}
async function count(query) {
@ -66,6 +67,9 @@ async function getList(rids) {
for (let rid of rids) r[rid] = await get(rid);
return r;
}
function getUserInProblemMulti(uid, pid) {
return coll.find({ owner: uid, pid });
}
module.exports = {
add,
@ -74,5 +78,6 @@ module.exports = {
update,
count,
reset,
getList
getList,
getUserInProblemMulti
};

@ -67,9 +67,5 @@ module.exports = {
},
async getMostRecentSessionByUid(uid) {
return await coll.findOne({ uid, token_type: this.TYPE_SESSION }, { sort: { updateAt: -1 } });
},
init: () => Promise.all([
coll.createIndex([{ uid: 1 }, { tokenType: 1 }, { updateAt: -1 }], { sparse: true }),
coll.createIndex('expireAt', { expireAfterSeconds: 0 })
])
};
}
};

@ -10,7 +10,7 @@ const
class USER {
constructor(user) {
this._id = user._id;
this.email = user.email;
this.mail = user.mail;
this.uname = user.uname;
this.salt = user.salt;
this.hash = user.hash;
@ -52,12 +52,12 @@ async function getByUname(uname) {
udoc.perm = role.perm;
return new USER(udoc);
}
async function getByEmail(email, ignoreMissing = false) {
let emailLower = email.trim().toLowerCase();
let udoc = await coll.findOne({ emailLower });
async function getByEmail(mail, ignoreMissing = false) {
let mailLower = mail.trim().toLowerCase();
let udoc = await coll.findOne({ mailLower });
if (!udoc) {
if (ignoreMissing) return null;
else throw new UserNotFoundError(email);
else throw new UserNotFoundError(mail);
}
let role = await coll_role.findOne({ _id: udoc.role || 'default' });
udoc.perm = role.perm;
@ -70,9 +70,9 @@ async function setPassword(uid, password) {
$set: { salt, hash: pwhash.hash(password, salt) }
});
}
async function setEmail(uid, email) {
validator.checkEmail(email);
return await setById(uid, { email, emailLower: email.trim().toLowerCase() });
async function setEmail(uid, mail) {
validator.checkEmail(mail);
return await setById(uid, { mail, mailLower: mail.trim().toLowerCase() });
}
function setById(uid, args) {
coll.findOneAndUpdate({ _id: uid }, { $set: args });
@ -88,17 +88,17 @@ async function changePassword(uid, currentPassword, newPassword) {
$set: { salt, hash: pwhash.hash(newPassword, salt) }
});
}
async function create({ uid, email, uname, password, regip = '127.0.0.1', role = 'default' }) {
async function create({ uid, mail, uname, password, regip = '127.0.0.1', role = 'default' }) {
validator.checkUname(uname);
validator.checkPassword(password);
validator.checkEmail(email);
validator.checkEmail(mail);
let salt = pwhash.salt();
if (!uid) uid = system.incUserCounter();
try {
await coll.insertOne({
_id: uid,
email,
emailLower: email.trim().toLowerCase(),
mail,
mailLower: mail.trim().toLowerCase(),
uname,
unameLower: uname.trim().toLowerCase(),
password: pwhash.hash(password, salt),
@ -108,7 +108,7 @@ async function create({ uid, email, uname, password, regip = '127.0.0.1', role =
loginat: new Date(),
loginip: regip,
role,
gravatar: email
gravatar: mail
});
} catch (e) {
throw new UserAlreadyExistError([uid, uname, email]);

@ -1,6 +1,6 @@
const
yaml = require('js-yaml'),
{ defaults } = require('lodash'),
{ defaultsDeep } = require('lodash'),
fs = require('fs'),
path = require('path');
@ -47,7 +47,7 @@ let options = {
try {
let t = yaml.safeLoad(fs.readFileSync(path.resolve(process.cwd(), 'config.yaml')));
options = defaults(t, options);
options = defaultsDeep(t, options);
} catch (e) {
console.error('Cannot load config');
}

@ -12,4 +12,4 @@ module.exports = {
publish(event, data) {
bus.emit(event, { value: data, event });
}
};
};

@ -13,7 +13,8 @@ const
blacklist = require('../model/blacklist'),
token = require('../model/token'),
opcount = require('../model/opcount'),
{ UserNotFoundError, BlacklistedError, PermissionError } = require('../error');
{ UserNotFoundError, BlacklistedError, PermissionError,
NotFoundError } = require('../error');
const options = require('../options');
let http = options.listen.https ? require('https') : require('http');
@ -49,7 +50,7 @@ class Handler {
this.response = {
body: '',
type: '',
status: 404,
status: null,
template: null,
redirect: null,
attachment: name => ctx.attachment(name)
@ -102,9 +103,10 @@ class Handler {
await opcount.inc(op, this.request.ip, period_secs, max_operations);
}
back() {
this.ctx.redirect(this.request.headers.referer || '/');
this.response.redirect = this.request.headers.referer || '/';
}
async ___prepare() {
this.now = new Date();
this._handler.sid = this.request.cookies.get('sid');
this._handler.save = this.request.cookies.get('save');
this._handler.tokenType = token.TYPE_SESSION;
@ -119,9 +121,8 @@ class Handler {
let bdoc = await blacklist.get(this.request.ip);
if (bdoc) throw new BlacklistedError(this.request.ip);
this.user = await user.getById(this.session.uid);
console.log(this.user, this.session.uid);
if (!this.user) throw new UserNotFoundError(this.session.uid);
this.csrf_token = (await token.add(token.TYPE_CSRF_TOKEN, 600, this.request.path))[0];
this.csrf_token = (await token.add(token.TYPE_CSRF_TOKEN, 600, { path: this.request.path }))[0];
this.preferJson = (this.request.headers['accept'] || '').includes('application/json');
}
async ___cleanup() {
@ -135,6 +136,10 @@ class Handler {
await this.saveCookie();
}
async renderBody() {
if (!(this.response.body || this.response.template)) {
this.response.body = { error: new NotFoundError() };
this.response.template = 'error.html';
}
if (!this.preferJson)
if (this.response.body || this.response.template) {
if (this.request.query.noTemplate || this.preferJson) return;
@ -150,9 +155,11 @@ class Handler {
this.ctx.response.status = 302;
this.ctx.redirect(this.response.redirect);
} else {
if (this.response.body) this.ctx.body = this.response.body;
if (this.response.body != null) {
this.ctx.response.body = this.response.body;
this.ctx.response.status = this.response.status || 200;
}
if (this.response.type) this.ctx.response.type = this.response.type;
if (this.response.status) this.ctx.response.status = this.response.status;
}
}
async saveCookie() {
@ -210,7 +217,6 @@ function Route(route, handler) {
if (h[`_${method}`]) await h[`_${method}`](args);
if (h[method]) await h[method](args);
console.log(ctx.request.body);
if (method == 'post' && ctx.request.body.operation) {
if (h[`${method}_${ctx.request.body.operation}`])
await h[`${method}_${ctx.request.body.operation}`](args);
@ -231,20 +237,22 @@ class ConnectionHandler {
* @param {import('sockjs').Connection} conn
*/
constructor(conn) {
let that = this;
this.conn = conn;
this.request = {
cookies: {
get(name) {
return conn.cookies[name];
return that.request.cookies[name];
},
set() { }
},
params: {},
headers: conn.headers
};
this._handler = {};
let p = (conn.url.split('?')[1] || '').split('&');
for (let i in p) p[i] = p[i].split('=');
for (let i in p) conn.params[p[i][0]] = decodeURIComponent(p[i][1]);
for (let i in p) this.request.params[p[i][0]] = decodeURIComponent(p[i][1]);
}
renderHTML(name, context) {
console.time(name);
@ -292,7 +300,7 @@ function Connection(prefix, handler) {
sock.on('connection', async conn => {
let h = new handler(conn);
try {
let args = Object.assign({}, h.conn.params);
let args = Object.assign({}, h.request.params);
if (args.uid) args.uid = parseInt(validator.checkUid(args.uid));
if (args.page) args.page = parseInt(args.page);
@ -313,6 +321,7 @@ function Connection(prefix, handler) {
if (h.___cleanup) await h.___cleanup(args);
});
} catch (e) {
console.log(e);
if (h.onerror) await h.onerror(e);
}
});

@ -4,9 +4,7 @@
{%- if tdoc is none %}
href="/p/{{ pdoc.pid }}"
{%- elif tdoc['doc_type'] == vj4.model.document.TYPE_CONTEST %}
href="{{ reverse_url('contest_detail_problem', domain_id=pdoc['domain_id'], pid=pdoc['_id'], tid=tdoc['doc_id']) }}"
{%- elif tdoc['doc_type'] == vj4.model.document.TYPE_HOMEWORK %}
href="{{ reverse_url('homework_detail_problem', domain_id=pdoc['domain_id'], pid=pdoc['_id'], tid=tdoc['doc_id']) }}"
href="/t/{{ tdoc._id }}/{{ pdoc._id }}"
{%- endif %}
>
{%- endif %}

@ -1,9 +1,9 @@
{% macro render_status_td(rdoc, rid_key='_id') %}
<td class="col--status record-status--border {{ builtin.STATUS_CODES[rdoc['status']] }}">
<td class="col--status record-status--border {{ model.builtin.STATUS_CODES[rdoc['status']] }}">
<div class="col--status__text">
<span class="icon record-status--icon {{ builtin.STATUS_CODES[rdoc['status']] }}"></span>
<a href="/r/{{ rdoc._id }}" class="record-status--text {{ builtin.STATUS_CODES[rdoc['status']] }}">
{{ builtin.STATUS_TEXTS[rdoc['status']] }}
<span class="icon record-status--icon {{ model.builtin.STATUS_CODES[rdoc['status']] }}"></span>
<a href="/r/{{ rdoc._id }}" class="record-status--text {{ model.builtin.STATUS_CODES[rdoc['status']] }}">
{{ model.builtin.STATUS_TEXTS[rdoc['status']] }}
</a>
</div>
{% if rdoc['status'] == status.STATUS_JUDGING %}

@ -12,7 +12,7 @@
{{ udoc['uname'] }}
</a>
{% if badge %}
<a class="user-profile-badge v-center badge--lv{{ udoc['level']|default(0) }}" href="/u/{{ udoc['_id']|default(0) }}" title="LV {{ udoc['level']|default(0) }}: Top {{ builtin.LEVELS[udoc['level']|default(0)]|default('N/A') }}%">LV {{ udoc['level']|default(0) }}</a>
<a class="user-profile-badge v-center badge--lv{{ udoc['level']|default(0) }}" href="/u/{{ udoc['_id']|default(0) }}" title="LV {{ udoc['level']|default(0) }}: Top {{ model.builtin.LEVELS[udoc['level']|default(0)]|default('N/A') }}%">LV {{ udoc['level']|default(0) }}</a>
{% if modbadge and handler.hasPerm(perm.PERM_MOD_BADGE) %}
<span class="user-profile-badge v-center badge--mod" title="Moderator">MOD</span>
{% endif %}

@ -23,7 +23,7 @@
{%- for column in rows[0] -%}
<th class="col--{{ column['type'] }}">
{% if column.type == 'problem_detail' %}
<a href="{{ reverse_url('contest_detail_problem' , tid=tdoc._id, pid=column.raw._id) }}" data-tooltip="{{ column['raw']['title'] }}">{{ column['value'] }}</a>
<a href="/c/{{ tdoc._id }}/p/{{ column.raw._id }}" data-tooltip="{{ column['raw']['title'] }}">{{ column['value'] }}</a>
{% else %}
{{ column.value }}
{% endif %}

@ -52,7 +52,7 @@
<li class="footer__extra-link-item" data-dropdown-target="#menu-footer-lang">
<span><span class="icon icon-global"></span> Language <span class="icon icon-expand_less"></span></span>
<ol class="dropdown-target menu" id="menu-footer-lang">
{% for item in builtin.VIEW_LANGS %}
{% for item in model.builtin.VIEW_LANGS %}
<li class="menu__item"><a class="menu__link" href="/lang/{{ item.code }}">{{ item.name }}</a></li>
{% endfor %}
</ol>
@ -61,7 +61,7 @@
</div>
<div class="footer__extra-right">
<ol class="clearfix">
{% for html in builtin.FOOTER_EXTRA_HTMLS %}
{% for html in model.builtin.FOOTER_EXTRA_HTMLS %}
<li class="footer__extra-link-item">{{ html|safe }}</li>
{% endfor %}
</ol>

@ -20,7 +20,7 @@
<span class="icon icon-flag"></span> {{ _('View Problem') }}
</a></li>
{% endif %}
<li class="menu__item scratchpad--hide"><a class="menu__link{% if page_name == 'contest_detail_problem_submit' %} active{% endif %}" href="{{ reverse_url('contest_detail_problem_submit', tid=tdoc['doc_id'], pid=pdoc['_id']) }}">
<li class="menu__item scratchpad--hide"><a class="menu__link{% if page_name == 'contest_detail_problem_submit' %} active{% endif %}" href="/t/{{ tdoc._id }}/p/{{ pdoc._id }}">
<span class="icon icon-send"></span> {{ _('Submit') }}
</a></li>
{% elif handler.is_done(tdoc) %}

@ -98,9 +98,9 @@
<dt>{{ _('Submission') }}</dt>
<dd>
{% if pdoc['psdoc']['status'] %}
<span class="icon record-status--icon {{ builtin.STATUS_CODES[pdoc['psdoc']['status']] }}"></span>
<a href="/r/{{ pdoc['psdoc']['rid'] }}" class="record-status--text {{ builtin.STATUS_CODES[pdoc['psdoc']['status']] }}">
{{ builtin.STATUS_TEXTS[pdoc['psdoc']['status']] }}
<span class="icon record-status--icon {{ model.builtin.STATUS_CODES[pdoc['psdoc']['status']] }}"></span>
<a href="/r/{{ pdoc['psdoc']['rid'] }}" class="record-status--text {{ model.builtin.STATUS_CODES[pdoc['psdoc']['status']] }}">
{{ model.builtin.STATUS_TEXTS[pdoc['psdoc']['status']] }}
</a>
{% else %}
{{ _('(None)') }}

@ -25,7 +25,7 @@
</div>
<div class="section__body">
<ul class="group-list" data-widget-cf-container>
{% for category, sub_categories in builtin.CATEGORIES %}
{% for category, sub_categories in model.builtin.CATEGORIES %}
<li class="group-list__item">
<h2 class="section__title">
<a href="/p?category={{ category|urlencode }}">{{ category }}</a>

@ -55,7 +55,7 @@
<h1 class="section__title">{{ _('Categories') }} ({{ _('click to add') }})</h1>
</div>
<div class="section__body">
{% for category, sub_categories in builtin.CATEGORIES %}
{% for category, sub_categories in model.builtin.CATEGORIES %}
<div>
<h2 class="section__title"><a class="category-a" href="javascript:;" data-category="{{ category }}">{{ category }}</a></h2>
<ol>

@ -3,13 +3,13 @@
{% block content %}
<div class="row">
<div class="medium-9 columns">
{% if not tdoc or rdocs %}
{% if not tdoc or rdocs.length %}
<div class="section">
<div class="section__header">
<h1 class="section__title">{{ _('Recent 10 Records') }}</h1>
</div>
<div class="section__body no-padding">
{% if not rdocs %}
{% if not rdocs.length %}
{{ nothing.render('You have not submitted any solutions for this problem') }}
{% else %}
<table class="data-table">
@ -44,7 +44,12 @@
</div>
<div class="section__body">
<form method="post">
{{ form.form_select({label:'Code language', options:builtin.LANG_TEXTS, name:'lang', value:handler.user.codeLang}) }}
{{ form.form_select({
label:'Code language',
options:model.builtin.LANG_TEXTS,
name:'lang',
value:handler.user.codeLang
}) }}
{{ form.form_textarea({label:'Code', columns:12, name:'code', extra_class:'monospace', autofocus:true}) }}
<div class="row"><div class="columns">
<input type="hidden" name="csrf_token" value="{{ handler.csrf_token }}">

@ -18,7 +18,7 @@
<h1 class="section__title">{{ _('Code') }}</h1>
</div>
<div class="section__body" data-syntax-hl-show-line-number>
<pre><code class="language-{{ builtin.LANG_HIGHLIGHT_ID[rdoc['lang']] }}">{{ rdoc['code'] }}</code></pre>
<pre><code class="language-{{ model.builtin.LANG_HIGHLIGHT_ID[rdoc['lang']] }}">{{ rdoc['code'] }}</code></pre>
</div>
</div>
{% endif %}
@ -82,7 +82,7 @@
{% endif %}
<dt>{{ _('Language') }}</dt>
<dd>
{{ builtin.LANG_TEXTS[rdoc['lang']] }}
{{ model.builtin.LANG_TEXTS[rdoc['lang']] }}
</dd>
<dt>
{{ _('Submit At') }}

@ -1,9 +1,9 @@
<div class="section visible" id="status">
<div class="section__header">
<h1 class="section__title">
<span class="icon record-status--icon {{ builtin.STATUS_CODES[rdoc['status']] }}"></span>
<span class="record-status--text {{ builtin.STATUS_CODES[rdoc['status']] }}">
{{ builtin.STATUS_TEXTS[rdoc['status']] }}
<span class="icon record-status--icon {{ model.builtin.STATUS_CODES[rdoc['status']] }}"></span>
<span class="record-status--text {{ model.builtin.STATUS_CODES[rdoc['status']] }}">
{{ model.builtin.STATUS_TEXTS[rdoc['status']] }}
</span>
</h1>
</div>
@ -37,13 +37,13 @@
<tbody>
{% for rcdoc in rdoc.cases %}
<tr>
<td class="col--case record-status--border {{ builtin.STATUS_CODES[rcdoc['status']] }}">
<td class="col--case record-status--border {{ model.builtin.STATUS_CODES[rcdoc['status']] }}">
#{{ loop.index }}
</td>
<td class="col--status">
<span class="icon record-status--icon {{ builtin.STATUS_CODES[rcdoc['status']] }}"></span>
<span class="record-status--text {{ builtin.STATUS_CODES[rcdoc['status']] }}">
{{ builtin.STATUS_TEXTS[rcdoc['status']] }}
<span class="icon record-status--icon {{ model.builtin.STATUS_CODES[rcdoc['status']] }}"></span>
<span class="record-status--text {{ model.builtin.STATUS_CODES[rcdoc['status']] }}">
{{ model.builtin.STATUS_TEXTS[rcdoc['status']] }}
</span>
<span>{{ rcdoc['judgeText'] }}</span>
<a class="tool-button" href="/wiki/help#status"><span class="icon icon-help"></span></a>

@ -25,6 +25,6 @@
</td>
<td class="col--time">{% if rdoc['status'] == status.STATUS_TIME_LIMIT_EXCEEDED or rdoc['status'] == status.STATUS_MEMORY_LIMIT_EXCEEDED or rdoc['status'] == status.STATUS_OUTPUT_LIMIT_EXCEEDED %}&ge;{% endif %}{{ rdoc.time }}ms</td>
<td class="col--memory">{% if rdoc['status'] == status.STATUS_TIME_LIMIT_EXCEEDED or rdoc['status'] == status.STATUS_MEMORY_LIMIT_EXCEEDED or rdoc['status'] == status.STATUS_OUTPUT_LIMIT_EXCEEDED %}&ge;{% endif %}{{ rdoc.memory|format_size(1024) }}</td>
<td class="col--lang">{{ builtin.LANG_TEXTS[rdoc['lang']] }}</td>
<td class="col--lang">{{ model.builtin.LANG_TEXTS[rdoc['lang']] }}</td>
<td class="col--submit-at">{{ datetime_span(rdoc['_id'])|safe }}</td>
</tr>

@ -50,12 +50,12 @@
<span class="icon icon-wechat"></span>
</a>
{% endif %}
{% if udoc.gender and udoc.gender != builtin.USER_GENDER_OTHER %}
{% if udoc.gender and udoc.gender != model.builtin.USER_GENDER_OTHER %}
<span
class="profile-header__contact-item"
data-tooltip="{{ builtin.USER_GENDER_RANGE[udoc.gender] }}"
data-tooltip="{{ model.builtin.USER_GENDER_RANGE[udoc.gender] }}"
>
{{ builtin.USER_GENDER_ICONS[udoc.gender] }}
{{ model.builtin.USER_GENDER_ICONS[udoc.gender] }}
</span>
{% endif %}
{% if handler.hasPerm(perm.PERM_MOD_BADGE) %}

@ -3,8 +3,6 @@ import { NamedPage } from 'vj/misc/PageLoader';
async function handleCategoryClick(ev) {
const $target = $(ev.currentTarget);
const $txt = $('[name="category"]');
console.log($target);
console.log($txt);
$txt.val(`${$txt.val()}, ${$target.data('category')}`);
}

Loading…
Cancel
Save