template: problem_detail

pull/1/head
masnn 5 years ago
parent 2e23d05a59
commit ffa193b5dd

@ -16,6 +16,11 @@ process.on('restart', async () => {
delete require.cache;
run();
});
const path = require('path');
const i18n = require('./lib/i18n');
i18n(path.resolve(__dirname, '..', 'locales', 'zh_CN.yaml'), 'zh_CN');
const EventEmitter = require('events');
global.bus = new EventEmitter();
async function run() {
@ -27,13 +32,14 @@ async function run() {
});
});
let server = require('./service/server');
require('./lib/i18n');
require('./handler/ui');
require('./handler/base');
require('./handler/home');
require('./handler/problem');
require('./handler/record');
require('./handler/judge');
require('./handler/user');
server.start();
}
run().catch(e => {

@ -15,13 +15,9 @@ MIDDLEWARE(async (ctx, next) => {
let sid = ctx.cookies.get('sid');
let save = ctx.cookies.get('save');
let tokenType, expireSeconds;
if (save) {
tokenType = token.TYPE_SAVED_SESSION;
expireSeconds = options.session.saved_expire_seconds;
} else {
tokenType = token.TYPE_UNSAVED_SESSION;
expireSeconds = options.session.unsaved_expire_seconds;
}
tokenType = token.TYPE_SESSION;
if (save) expireSeconds = options.session.saved_expire_seconds;
else expireSeconds = options.session.unsaved_expire_seconds;
ctx.session = sid ?
await token.update(sid, tokenType, expireSeconds, Object.assign({
update_ip: ctx.request.ip,
@ -48,6 +44,7 @@ MIDDLEWARE(async (ctx, next) => {
}
}
};
console.log(ctx.session.uid);
await next();
if (ctx.session.sid)
await token.update(ctx.session.sid, tokenType, expireSeconds, Object.assign({
@ -61,7 +58,8 @@ MIDDLEWARE(async (ctx, next) => {
update_ip: ctx.request.ip,
update_ua: ctx.request.headers['user-agent'] || ''
}, ctx.session));
let cookie = { domain: options.session.domain, secure: options.session.secure, httponly: true };
console.log(ctx.session.sid, ctx.session.uid);
let cookie = { secure: options.session.secure, httponly: true };
if (save) {
cookie.expires = ctx.session.expireAt, cookie.maxAge = expireSeconds;
ctx.cookies.set('save', 'true', cookie);

@ -1,5 +1,5 @@
const
{ CONTEXT } = require('../service/server'),
{ GET, POST } = require('../service/server'),
queue = require('../service/queue'),
record = require('../model/record'),
{ requirePerm } = require('./tools'),
@ -7,12 +7,10 @@ const
queue.assert('judge');
const { MIDDLEWARE, GET, POST } = CONTEXT();
MIDDLEWARE(requirePerm(PERM_JUDGE));
GET('/judge/noop', async ctx => {
GET('/judge/noop', requirePerm(PERM_JUDGE), async ctx => {
ctx.body = {};
});
GET('/judge/fetch', async ctx => {
GET('/judge/fetch', requirePerm(PERM_JUDGE), async ctx => {
let rid = await queue.get('judge', false);
if (rid) {
let rdoc = await record.get(rid);
@ -27,9 +25,9 @@ GET('/judge/fetch', async ctx => {
ctx.body = data;
}
});
POST('/judge/next', async ctx => {
POST('/judge/next', requirePerm(PERM_JUDGE), async ctx => {
console.log(ctx.request.body);
});
POST('/judge/end', async ctx => {
POST('/judge/end', requirePerm(PERM_JUDGE), async ctx => {
console.log(ctx.request.body);
});

@ -38,6 +38,7 @@ GET('/p/:pid', requirePerm(PERM_VIEW_PROBLEM), async ctx => {
let pdoc = await problem.get({ pid, uid });
if (pdoc.hidden) ctx.checkPerm(PERM_VIEW_PROBLEM_HIDDEN);
let udoc = await user.getById(pdoc.owner);
ctx.templateName = 'problem_detail.html';
ctx.body = { pdoc, udoc, title: pdoc.title };
});
POST('/p/:pid/submit', requirePerm(PERM_SUBMIT_PROBLEM), async ctx => {

@ -1,25 +1,58 @@
const
path = require('path'),
md5 = require('blueimp-md5'),
static = require('koa-static'),
nunjucks = require('nunjucks'),
hljs = require('hljs'),
MarkdownIt = require('markdown-it'),
katex = require('markdown-it-katex'),
options = require('../options'),
perm = require('../permission'),
builtin = require('../model/builtin'),
{ MIDDLEWARE } = require('../service/server'),
{ NotFoundError } = require('../error');
{ NotFoundError } = require('../error'),
MD5_REGEX = /^[0-9a-f]{32}$/;
function getHash(email) {
email = (typeof email === 'string') ? email.trim().toLowerCase() : 'unspecified';
return email.match(MD5_REGEX) ? email : md5(email);
}
class Markdown extends MarkdownIt {
constructor() {
super({
linkify: true,
highlight: function (str, lang) {
if (lang && hljs.getLanguage(lang))
try {
return hljs.highlight(lang, str).value;
} catch (__) { } // eslint-disable-line no-empty
return '';
}
});
this.linkify.tlds('.py', false);
this.use(katex);
}
}
const md = new Markdown();
class Nunjucks extends nunjucks.Environment {
constructor() {
super(
new nunjucks.FileSystemLoader(path.resolve(options.template.path)),
{ autoescape: true, trimBlocks: true }
);
this.addFilter('json', function (data) {
return JSON.stringify(data);
this.addFilter('json', function (self) {
return JSON.stringify(self);
}, false);
this.addFilter('assign', function (self, data) {
return Object.assign(self, data);
});
this.addFilter('markdown', function (self) {
return md.render(self);
});
this.addFilter('gravatar_url', function (email, size) {
return `//gravatar.loli.net/avatar/${getHash(email)}?d=mm&s=${size}`;
});
this.addGlobal('static_url', str => `/${str}`);
this.addGlobal('reverse_url', str => str);
this.addGlobal('perm', perm);
@ -30,8 +63,10 @@ const env = new Nunjucks();
MIDDLEWARE(static(path.resolve(process.cwd(), '.uibuild')));
MIDDLEWARE(async (ctx, next) => {
ctx.render_title = str => str;
ctx.ui_context = {};
ctx.user_context = {};
ctx.UIContext = {
cdn_prefix: '/',
url_prefix: '/'
};
ctx.render = async (name, context) => {
ctx.user = ctx.state.user;
ctx.translate = str => {
@ -53,12 +88,17 @@ MIDDLEWARE(async (ctx, next) => {
try {
await next();
if (!ctx.body) throw new NotFoundError();
if (ctx.query.template || ctx.templateName) {
Object.assign(ctx.body, JSON.parse(ctx.query.data || '{}'));
await ctx.render(ctx.query.template || ctx.templateName, ctx.body);
} else if (ctx.setRedirect) {
ctx.response.type = 'application/octet-stream';
ctx.redirect(ctx.setRedirect);
}
} catch (error) {
if (error.toString().startsWith('Template render error')) throw error;
await ctx.render('error.html', { error });
}
Object.assign(ctx.body, JSON.parse(ctx.query.data || '{}'));
console.log(ctx.body);
if (ctx.query.template || ctx.templateName) await ctx.render(ctx.query.template || ctx.templateName, ctx.body);
} catch (error) {
await ctx.render('bsod.html', { error });
}

@ -12,12 +12,16 @@ const
GET('/user', async ctx => {
let udoc = await user.getById(ctx.session.uid);
ctx.templateName = 'user_detail.html';
ctx.body = { udoc };
});
GET('/login', async ctx => {
ctx.templateName = 'user_login.html';
});
POST('/login', async ctx => {
let { username, password, rememberme } = ctx.request.body;
let udoc = await user.getByUname(username);
if (!udoc) throw new LoginError(username);
let { uname, password, rememberme = false } = ctx.request.body;
let udoc = await user.getByUname(uname);
if (!udoc) throw new LoginError(uname);
if (udoc) udoc.checkPassword(password);
await user.setById(udoc._id, { loginat: new Date(), loginip: ctx.request.ip });
udoc.salt = '';
@ -25,22 +29,20 @@ POST('/login', async ctx => {
console.log(udoc);
ctx.session.uid = udoc._id;
ctx.session.rememberme = rememberme;
ctx.body = { udoc };
ctx.body = {};
ctx.setRedirect = ctx.request.headers.referer || '/';
});
POST('/logout', requirePerm(PERM_LOGGEDIN), async ctx => {
ctx.session = { uid: 1 };
ctx.body = {};
});
let { GET: _GET, POST: _POST, MIDDLEWARE: _MIDDLEWARE } = CONTEXT();
_MIDDLEWARE(requirePerm(PERM_REGISTER_USER));
_GET('/register/:code', async ctx => {
GET('/register/:code', requirePerm(PERM_REGISTER_USER), async ctx => {
let code = ctx.request.body.code;
let { mail } = await token.get(code, token.TYPE_REGISTRATION);
if (!mail) throw new InvalidTokenError(token.TYPE_REGISTRATION, code);
ctx.body = { mail };
});
_GET('/register/:code', async ctx => {
GET('/register/:code', requirePerm(PERM_REGISTER_USER), async ctx => {
let { code, password, verify_password, uname } = ctx.request.body;
let { mail } = await token.get(code, token.TYPE_REGISTRATION);
if (!mail) throw new InvalidTokenError(token.TYPE_REGISTRATION, code);
@ -53,7 +55,7 @@ _GET('/register/:code', async ctx => {
});
if (options.smtp.user) {
_POST('/register', limitRate('send_mail', 3600, 30), async ctx => {
POST('/register', requirePerm(PERM_REGISTER_USER), limitRate('send_mail', 3600, 30), async ctx => {
let email = ctx.request.body.email;
validator.check_mail(email);
if (await user.get_by_mail(email)) throw new UserAlreadyExistError(email);
@ -95,7 +97,7 @@ if (options.smtp.user) {
ctx.redirect('/');
});
} else
_POST('/register', async ctx => {
POST('/register', requirePerm(PERM_REGISTER_USER), async ctx => {
let email = ctx.request.body.email;
validator.check_mail(email);
if (await user.get_by_mail(email)) throw new UserAlreadyExistError(email);

@ -25,8 +25,8 @@ String.prototype.rawformat = function (object) {
let res = this.split('{@}');
return [res[0], object, res[1]];
};
String.prototype.translate = function (language = 'zh-CN') {
if (locales[language]) return this;
String.prototype.translate = function (language = 'zh_CN') {
if (locales[language]) return locales[language][this] || this;
else return this;
};

@ -13,37 +13,7 @@ let server = http.createServer(app.callback());
app.keys = options.session.keys;
app.use(morgan(':method :url :status :res[content-length] - :response-time ms'));
app.use(Body());
let router = new Router();
let contexts = [];
function CONTEXT() {
this.router = new Router();
/**
* @param {import('koa').Middleware} middleware
*/
function MIDDLEWARE(middleware) {
router.use(middleware);
}
/**
* @param {string} route
* @param {...import('koa').Middleware} handler
*/
function GET(route, ...handler) {
router.get(route, ...handler);
}
/**
* @param {string} route
* @param {...import('koa').Middleware} handler
*/
function POST(route, ...handler) {
router.post(route, ...handler);
}
this.MIDDLEWARE = MIDDLEWARE;
this.GET = GET;
this.POST = POST;
contexts.push(this);
return this;
}
/**
* @param {import('koa').Middleware} middleware
@ -78,15 +48,12 @@ function SOCKET(prefix, handler) {
sock.on('connection', handler);
sock.installHandlers(server);
}
exports.CONTEXT = CONTEXT;
exports.MIDDLEWARE = MIDDLEWARE;
exports.GET = GET;
exports.POST = POST;
exports.SOCKET = SOCKET;
exports.start = function start() {
app.use(router.routes()).use(router.allowedMethods());
for (let c of contexts)
app.use(c.router.routes()).use(c.router.allowedMethods());
app.listen(options.listen.port);
console.log('Server listening at: %s', options.listen.port);
};

@ -7,7 +7,9 @@
"license": "MIT",
"dependencies": {
"axios": "^0.19.0",
"blueimp-md5": "^2.13.0",
"bson": "^4.0.2",
"hljs": "^6.2.3",
"js-yaml": "^3.13.1",
"koa": "^2.11.0",
"koa-body": "^4.1.1",
@ -15,6 +17,8 @@
"koa-router": "^7.4.0",
"koa-static": "^5.0.0",
"lodash": "^4.17.15",
"markdown-it": "^10.0.0",
"markdown-it-katex": "^2.0.3",
"mongodb": "^3.3.4",
"nodemailer": "^6.3.1",
"nunjucks": "^3.2.1",

@ -34,7 +34,7 @@
<div class="media__left top">
{% if not mode_create %}
<div class="vote vote--discussion">
{% if handler.has_perm(vj4.model.builtin.PERM_VOTE_PROBLEM_SOLUTION) %}
{% if handler.has_perm(perm.PERM_VOTE_PROBLEM_SOLUTION) %}
<div class="vote-number rotator--enabled">{{ doc['vote'] }}</div>
<div class="vote-op clearfix">
<form method="post">

@ -2,7 +2,7 @@
{%- if not invalid %}
<a
{%- if tdoc is none %}
href="{{ reverse_url('problem_detail', domain_id=pdoc['domain_id'], pid=pdoc['_id']) }}"
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 %}
@ -29,7 +29,7 @@
{%- if pdoc['tag']|length > 0 %}
<ul class="problem__tags">
{%- for tag in pdoc['tag'] %}
<li class="problem__tag"><a class="problem__tag-link" href="{{ reverse_url('problem_category', category=tag) }}">{{ tag }}</a></li>
<li class="problem__tag"><a class="problem__tag-link" href="/p?category={{ tag }}">{{ tag }}</a></li>
{%- endfor %}
</ul>
{%- else %}

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

@ -115,7 +115,7 @@
</div>
</div>
<div class="medium-3 columns">
{% if handler.has_perm(vj4.model.builtin.PERM_CREATE_CONTEST) %}
{% if handler.has_perm(perm.PERM_CREATE_CONTEST) %}
<div class="section side">
<div class="section__header">
<h1 class="section__title">

@ -29,7 +29,7 @@
</div>
</div>
<ul class="section__footer supplementary dot list">
{% if handler.own(ddoc, vj4.model.builtin.PERM_EDIT_DISCUSSION_SELF) or handler.has_perm(vj4.model.builtin.PERM_EDIT_DISCUSSION) %}
{% if handler.own(ddoc, perm.PERM_EDIT_DISCUSSION_SELF) or handler.has_perm(perm.PERM_EDIT_DISCUSSION) %}
<li>
<a href="{{ reverse_url('discussion_edit', did=ddoc['doc_id']) }}"><span class="icon icon-edit"></span> {{ _('Edit') }}</a>
</li>
@ -63,16 +63,16 @@
udict = udict,
comment_ref = 'drid',
reply_ref = 'drrid',
comment_post_perm = vj4.model.builtin.PERM_REPLY_DISCUSSION,
comment_edit_perm = vj4.model.builtin.PERM_NONE if handler.own(ddoc, vj4.model.builtin.PERM_EDIT_DISCUSSION_REPLY_SELF_DISCUSSION) else vj4.model.builtin.PERM_EDIT_DISCUSSION_REPLY,
comment_edit_self_perm = vj4.model.builtin.PERM_EDIT_DISCUSSION_REPLY_SELF,
comment_delete_perm = vj4.model.builtin.PERM_NONE if handler.own(ddoc, vj4.model.builtin.PERM_DELETE_DISCUSSION_REPLY_SELF_DISCUSSION) else vj4.model.builtin.PERM_DELETE_DISCUSSION_REPLY,
comment_delete_self_perm = vj4.model.builtin.PERM_DELETE_DISCUSSION_REPLY_SELF,
reply_post_perm = vj4.model.builtin.PERM_REPLY_DISCUSSION,
reply_edit_perm = vj4.model.builtin.PERM_NONE if handler.own(ddoc, vj4.model.builtin.PERM_EDIT_DISCUSSION_REPLY_SELF_DISCUSSION) else vj4.model.builtin.PERM_EDIT_DISCUSSION_REPLY,
reply_edit_self_perm = vj4.model.builtin.PERM_EDIT_DISCUSSION_REPLY_SELF,
reply_delete_perm = vj4.model.builtin.PERM_NONE if handler.own(ddoc, vj4.model.builtin.PERM_DELETE_DISCUSSION_REPLY_SELF_DISCUSSION) else vj4.model.builtin.PERM_DELETE_DISCUSSION_REPLY,
reply_delete_self_perm = vj4.model.builtin.PERM_DELETE_DISCUSSION_REPLY_SELF
comment_post_perm = perm.PERM_REPLY_DISCUSSION,
comment_edit_perm = perm.PERM_NONE if handler.own(ddoc, perm.PERM_EDIT_DISCUSSION_REPLY_SELF_DISCUSSION) else perm.PERM_EDIT_DISCUSSION_REPLY,
comment_edit_self_perm = perm.PERM_EDIT_DISCUSSION_REPLY_SELF,
comment_delete_perm = perm.PERM_NONE if handler.own(ddoc, perm.PERM_DELETE_DISCUSSION_REPLY_SELF_DISCUSSION) else perm.PERM_DELETE_DISCUSSION_REPLY,
comment_delete_self_perm = perm.PERM_DELETE_DISCUSSION_REPLY_SELF,
reply_post_perm = perm.PERM_REPLY_DISCUSSION,
reply_edit_perm = perm.PERM_NONE if handler.own(ddoc, perm.PERM_EDIT_DISCUSSION_REPLY_SELF_DISCUSSION) else perm.PERM_EDIT_DISCUSSION_REPLY,
reply_edit_self_perm = perm.PERM_EDIT_DISCUSSION_REPLY_SELF,
reply_delete_perm = perm.PERM_NONE if handler.own(ddoc, perm.PERM_DELETE_DISCUSSION_REPLY_SELF_DISCUSSION) else perm.PERM_DELETE_DISCUSSION_REPLY,
reply_delete_self_perm = perm.PERM_DELETE_DISCUSSION_REPLY_SELF
) }}
{{ paginator.render(page, pcount) }}
{% if drcount == 0 %}

@ -11,7 +11,7 @@
<button name="operation" value="update" type="submit" class="rounded primary button">
{{ _('Update') }} (Ctrl+Enter)
</button>
{% if handler.own(ddoc, vj4.model.builtin.PERM_DELETE_DISCUSSION_SELF) or handler.has_perm(vj4.model.builtin.PERM_DELETE_DISCUSSION) %}
{% if handler.own(ddoc, perm.PERM_DELETE_DISCUSSION_SELF) or handler.has_perm(perm.PERM_DELETE_DISCUSSION) %}
<button name="operation" value="delete" type="submit" class="rounded button">
{{ _('Delete') }}
</button>

@ -23,7 +23,7 @@
</div>
<div class="section__body">
{% if vnode %}
{% if handler.has_perm(vj4.model.builtin.PERM_CREATE_DISCUSSION) %}
{% if handler.has_perm(perm.PERM_CREATE_DISCUSSION) %}
<p><a href="{{ reverse_url('discussion_create', doc_id=vnode['doc_id']) }}" class="expanded primary button">{{ _('Create a Discussion') }}</a></p>
{% else %}
{% if not handler.has_perm(perm.PERM_LOGGEDIN) %}

@ -13,7 +13,7 @@
</div>
<ol class="menu collapsed">
{{ sidemenu.render_item(null, 'domain_manage_dashboard') }}
{% if handler.has_perm(vj4.model.builtin.PERM_EDIT_DESCRIPTION) %}
{% if handler.has_perm(perm.PERM_EDIT_DESCRIPTION) %}
{{ sidemenu.render_item(null, 'domain_manage_edit') }}
{{ sidemenu.render_item(null, 'domain_manage_join_applications') }}
{{ sidemenu.render_item(null, 'domain_manage_discussion') }}
@ -21,7 +21,7 @@
<!-- Ranking Settings -->
</ol>
</li>
{% if handler.has_perm(vj4.model.builtin.PERM_EDIT_PERM) %}
{% if handler.has_perm(perm.PERM_EDIT_PERM) %}
<li class="menu__item">
<div class="menu__link expandable">
<span class="icon icon-user"></span> {{ _('Access Control') }}

@ -164,7 +164,7 @@
{% endif %}
</div>
<div class="large-3 columns">
{% if handler.has_perm(vj4.model.builtin.PERM_VIEW_DISCUSSION) %}
{% if handler.has_perm(perm.PERM_VIEW_DISCUSSION) %}
{% include 'discussion_nodes_widget.html' %}
{% endif %}
{% if domain_id == vj4.model.builtin.DOMAIN_ID_SYSTEM %}

@ -21,7 +21,7 @@
{% endfor %}
</tr>
</thead>
{% for family, perms in vj4.model.builtin.PERMS_BY_FAMILY.items() %}
{% for family, perms in perm.PERMS_BY_FAMILY.items() %}
<tbody>
<tr>
<td class="col--family" colspan="{{ (roles|length)+1 }}">

@ -1,4 +1,5 @@
{% set no_path_section = true %}
{% set page_name = "error" %}
{% extends "layout/basic.html" %}
{% block content %}
<div class="error__container clearfix">

@ -18,7 +18,7 @@
<option value="list">{{ _('List View') }}</option>
</select>
</span>
{% if handler.has_perm(vj4.model.builtin.PERM_CREATE_HOMEWORK) %}
{% if handler.has_perm(perm.PERM_CREATE_HOMEWORK) %}
<a class="compact button" href="{{ reverse_url('homework_create') }}">{{ _('Create Homework') }}</a>
{% endif %}
</div>

@ -28,8 +28,8 @@
<script>
var _htmlNode = document.documentElement;
_htmlNode.className = _htmlNode.className.replace(' nojs', ' hasjs');
var UiContext = {{ handler.ui_context|json|safe }};
var UserContext = {{ handler.user_context|json|safe }};
var UiContext = {{ handler.UIContext|json|safe }};
var UserContext = {{ handler.user|json|safe }};
</script>
</head>
<body>

@ -18,7 +18,7 @@
<div>
<ol class="menu">
{% if page_name == 'discussion_node' %}
{% if handler.has_perm(vj4.model.builtin.PERM_CREATE_DISCUSSION) %}
{% if handler.has_perm(perm.PERM_CREATE_DISCUSSION) %}
<li class="menu__item"><a class="menu__link highlight" href="{{ reverse_url('discussion_create_document_as_node', doc_type=tdoc['doc_type'], doc_id=tdoc['doc_id']) }}">
<span class="icon icon-add"></span> {{ _('Create a Discussion') }}
</a></li>
@ -40,7 +40,7 @@
{% endif %}
{% if not attended and not handler.is_done(tdoc) %}
<li class="menu__item">
{% if handler.has_perm(vj4.model.builtin.PERM_ATTEND_CONTEST) and handler.has_perm(perm.PERM_LOGGEDIN) %}
{% if handler.has_perm(perm.PERM_ATTEND_CONTEST) and handler.has_perm(perm.PERM_LOGGEDIN) %}
<form action="{{ reverse_url('contest_detail', tid=tdoc['doc_id']) }}" method="POST">
<input type="hidden" name="operation" value="attend">
<input type="hidden" name="csrf_token" value="{{ handler.csrf_token }}">
@ -68,12 +68,12 @@
<span class="icon icon-statistics"></span> {{ _('Scoreboard (Hidden)') }}
</a></li>
{% endif %}
{% if handler.own(tdoc, vj4.model.builtin.PERM_EDIT_CONTEST_SELF) or handler.has_perm(vj4.model.builtin.PERM_EDIT_CONTEST) %}
{% if handler.own(tdoc, perm.PERM_EDIT_CONTEST_SELF) or handler.has_perm(perm.PERM_EDIT_CONTEST) %}
<li class="menu__item"><a class="menu__link" href="{{ reverse_url('contest_edit', tid=tdoc['doc_id']) }}">
<span class="icon icon-edit"></span> {{ _('Edit Contest') }}
</a></li>
{% endif %}
{% if handler.has_perm(vj4.model.builtin.PERM_READ_RECORD_CODE) %}
{% if handler.has_perm(perm.PERM_READ_RECORD_CODE) %}
<li class="menu__item"><a class="menu__link" href="{{ reverse_url('contest_code', tid=tdoc['doc_id']) }}">
<span class="icon icon-download"></span> {{ _('Export All Code') }}
</a></li>

@ -1,5 +1,5 @@
{% import "components/form.html" as form with context %}
{% if ddoc['highlight'] or handler.has_perm(vj4.model.builtin.PERM_HIGHLIGHT_DISCUSSION) %}
{% if ddoc['highlight'] or handler.has_perm(perm.PERM_HIGHLIGHT_DISCUSSION) %}
{{ form.form_checkbox(label='Highlight', name='highlight', value=ddoc['highlight']|default(false)) }}
{% endif %}
{{ form.form_text(label='Title', name='title', value=ddoc['title']|default(''), autofocus=true, required=true) }}

@ -24,7 +24,7 @@
{% endif %}
{% if not attended and not handler.is_done(tdoc) %}
<li class="menu__item">
{% if handler.has_perm(vj4.model.builtin.PERM_ATTEND_HOMEWORK) and handler.has_perm(perm.PERM_LOGGEDIN) %}
{% if handler.has_perm(perm.PERM_ATTEND_HOMEWORK) and handler.has_perm(perm.PERM_LOGGEDIN) %}
<form action="{{ reverse_url('homework_detail', tid=tdoc['doc_id']) }}" method="POST">
<input type="hidden" name="operation" value="attend">
<input type="hidden" name="csrf_token" value="{{ handler.csrf_token }}">
@ -52,12 +52,12 @@
<span class="icon icon-statistics"></span> {{ _('Scoreboard (Hidden)') }}
</a></li>
{% endif %}
{% if handler.own(tdoc, vj4.model.builtin.PERM_EDIT_HOMEWORK_SELF) or handler.has_perm(vj4.model.builtin.PERM_EDIT_HOMEWORK) %}
{% if handler.own(tdoc, perm.PERM_EDIT_HOMEWORK_SELF) or handler.has_perm(perm.PERM_EDIT_HOMEWORK) %}
<li class="menu__item"><a class="menu__link" href="{{ reverse_url('homework_edit', tid=tdoc['doc_id']) }}">
<span class="icon icon-edit"></span> {{ _('Edit Homework') }}
</a></li>
{% endif %}
{% if handler.has_perm(vj4.model.builtin.PERM_READ_RECORD_CODE) %}
{% if handler.has_perm(perm.PERM_READ_RECORD_CODE) %}
<li class="menu__item"><a class="menu__link" href="{{ reverse_url('homework_code', tid=tdoc['doc_id']) }}">
<span class="icon icon-download"></span> {{ _('Export All Code') }}
</a></li>

@ -2,13 +2,13 @@
<div class="dialog__content">
<div class="dialog--signin__bg">
<div class="dialog--signin__side">
<h1>{{ _('Don\'t have an account?') }}</h1>
<h1>{{ _("Don\'t have an account?") }}</h1>
<p>{{ _('By signing up a Vijos universal account, you can submit code and join discussions in all online judging services provided by us.') }}</p>
<div><a href="{{ reverse_url('user_register') }}" class="inverse rounded button">{{ _('Sign Up Now') }}</a></div>
<div><a href="/register" class="inverse rounded button">{{ _('Sign Up Now') }}</a></div>
</div>
</div>
<div class="dialog--signin__main">
<form action="{{ reverse_url('user_login') }}" method="post">
<form action="/login" method="post">
<div class="dialog--signin__close-container supplementary">
<a name="dialog--signin__close" href="javascript:;">{{ _('CLOSE') }}</a>
</div>
@ -36,7 +36,7 @@
</div></div>
<div class="row"><div class="columns">
<div class="supplementary text-center">
<a href="{{ reverse_url('user_lostpass') }}">{{ _('Forgot password or username?') }}</a>
<a href="/lostpass">{{ _('Forgot password or username?') }}</a>
</div>
</div></div>
</form>

@ -1,6 +1,6 @@
{% macro nav_item(target_page_name, target_page_prefix) %}
{% macro nav_item(target_page_url, target_page_name, target_page_prefix) %}
<li class="nav__list-item">
<a href="{{ reverse_url(target_page_name) }}" class="nav__item{% if page_name.startsWith(target_page_prefix) %} nav--active{% endif %}">
<a href="{{ target_page_url }}" class="nav__item{% if page_name.startsWith(target_page_prefix) %} nav--active{% endif %}">
{{ _(target_page_name) }}
</a>
</li>
@ -9,80 +9,64 @@
<nav class="nav slideout-menu" id="menu">
<div class="row"><div class="columns clearfix">
<ol class="nav__list nav__list--main clearfix">
<li class="nav__list-item"><a href="{{ reverse_url('domain_main', domain_id=vj4.model.builtin.DOMAIN_ID_SYSTEM) }}" class="nav__logo">&nbsp;</a></li>
{{ nav_item('domain_main', 'domain_main') }}
<li class="nav__list-item"><a href="/" class="nav__logo">&nbsp;</a></li>
{{ nav_item('/', 'domain_main', 'domain_main') }}
{% if handler.has_perm(perm.PERM_VIEW_PROBLEM) %}
{{ nav_item('problem_main', 'problem') }}
{{ nav_item('/p', 'problem_main', 'problem') }}
{% endif %}
{% if handler.has_perm(perm.PERM_VIEW_TRAINING) %}
{{ nav_item('training_main', 'training') }}
{{ nav_item('/t', 'training_main', 'training') }}
{% endif %}
{% if handler.has_perm(perm.PERM_VIEW_DISCUSSION) %}
{{ nav_item('discussion_main', 'discussion') }}
{{ nav_item('/d', 'discussion_main', 'discussion') }}
{% endif %}
{% if handler.has_perm(perm.PERM_VIEW_CONTEST) %}
{{ nav_item('contest_main', 'contest') }}
{{ nav_item('/c', 'contest_main', 'contest') }}
{% endif %}
{% if handler.has_perm(perm.PERM_VIEW_HOMEWORK) %}
{{ nav_item('homework_main', 'homework') }}
{{ nav_item('/h', 'homework_main', 'homework') }}
{% endif %}
{% if handler.has_perm(perm.PERM_EDIT_DESCRIPTION) or handler.has_perm(perm.PERM_EDIT_PERM) %}
{{ nav_item('domain_manage', 'domain_manage') }}
{{ nav_item('/d', 'domain_manage', 'domain_manage') }}
{% endif %}
</ol>
<ol class="nav__list nav__list--secondary clearfix">
{% if not handler.has_perm(perm.PERM_LOGGEDIN) %}
<li class="nav__list-item"><a href="{{ reverse_url('user_login') }}" class="nav__item" name="nav_login">{{ _('Login') }}</a></li>
<li class="nav__list-item"><a href="{{ reverse_url('user_register') }}" class="nav__item--round">{{ _('Sign Up') }}</a></li>
<li class="nav__list-item"><a href="/login" class="nav__item" name="nav_login">{{ _('Login') }}</a></li>
<li class="nav__list-item"><a href="/register" class="nav__item--round">{{ _('Sign Up') }}</a></li>
{% else %}
<li class="nav__list-item" data-dropdown-pos="bottom right" data-dropdown-target="#menu-nav-user" data-dropdown-trigger-desktop-only>
<a href="{{ reverse_url('user_detail', uid=handler.user['_id']) }}" class="nav__item">{{ handler.user.uname }} <span class="icon icon-expand_more nojs--hide"></span></a>
<a href="/user/{{ handler.user._id }}" class="nav__item">{{ handler.user.uname }} <span class="icon icon-expand_more nojs--hide"></span></a>
<ol class="dropdown-target menu" id="menu-nav-user">
<li class="menu__item">
<a href="{{ reverse_url('user_detail', uid=handler.user['_id']) }}" class="menu__link">
<a href="/user/{{ handler.user._id }}" class="menu__link">
<span class="icon icon-account--circle"></span> {{ _('My Profile') }}
</a>
</li>
<li class="menu__item">
<a href="{{ reverse_url('home_messages') }}" class="menu__link">
<a href="/home/messages" class="menu__link">
<span class="icon icon-comment--multiple"></span> {{ _('home_messages') }}
</a>
</li>
<li class="menu__seperator"></li>
<li class="menu__item">
<a href="{{ reverse_url('home_account') }}" class="menu__link">
<a href="/home/account" class="menu__link">
<span class="icon icon-wrench"></span> {{ _('home_account') }}
</a>
</li>
<li class="menu__item">
<a href="{{ reverse_url('home_domain_account') }}" class="menu__link">
<span class="icon icon-web"></span> @ {{ handler.domain['name'] }}
</a>
</li>
<li class="menu__item">
<a href="{{ reverse_url('home_preference') }}" class="menu__link">
<a href="/home/preference" class="menu__link">
<span class="icon icon-sliders"></span> {{ _('home_preference') }}
</a>
</li>
<li class="menu__item">
<a href="{{ reverse_url('home_security') }}" class="menu__link">
<a href="/home/security" class="menu__link">
<span class="icon icon-security"></span> {{ _('home_security') }}
</a>
</li>
<li class="menu__seperator"></li>
<li class="menu__item">
<a href="{{ reverse_url('home_file') }}" class="menu__link">
<span class="icon icon-file"></span> {{ _('My Files') }}
</a>
</li>
<li class="menu__item">
<a href="{{ reverse_url('home_domain') }}" class="menu__link">
<span class="icon icon-web"></span> {{ _('My Domains') }}
</a>
</li>
<li class="menu__seperator"></li>
<li class="menu__item">
<a href="{{ reverse_url('user_logout') }}" class="menu__link" name="nav_logout">
<a href="/logout" class="menu__link" name="nav_logout">
<span class="icon icon-logout"></span> {{ _('Logout') }}
</a>
</li>

@ -1,14 +1,14 @@
{% if tdoc %}
{% if tdoc['doc_type'] == vj4.model.document.TYPE_CONTEST %}
{% include "partials/problem_sidebar_contest.html" %}
{% with owner_udoc=udoc, owner_dudoc=dudoc %}
{% include "partials/contest_sidebar.html" %}
{% endwith %}
{% set owner_udoc=udoc %}
{% set owner_dudoc=dudoc %}
{% include "partials/contest_sidebar.html" %}
{% else %}
{% include "partials/problem_sidebar_homework.html" %}
{% with owner_udoc=udoc, owner_dudoc=dudoc %}
{% include "partials/homework_sidebar.html" %}
{% endwith %}
{% set owner_udoc=udoc %}
{% set owner_dudoc=dudoc %}
{% include "partials/homework_sidebar.html" %}
{% endif %}
{% else %}
{% include "partials/problem_sidebar_normal.html" %}

@ -24,11 +24,11 @@
<span class="icon icon-send"></span> {{ _('Submit') }}
</a></li>
{% elif handler.is_done(tdoc) %}
<li class="menu__item scratchpad--hide"><a class="menu__link" href="{{ reverse_url('problem_detail', pid=pdoc['_id']) }}">
<li class="menu__item scratchpad--hide"><a class="menu__link" href="/p/{{ pdoc.pid }}">
<span class="icon icon-send"></span> {{ _('Open in Problem Set') }}
</a></li>
{% endif %}
{% if handler.own(pdoc, vj4.model.builtin.PERM_EDIT_PROBLEM_SELF) or handler.has_perm(vj4.model.builtin.PERM_EDIT_PROBLEM) %}
{% if handler.own(pdoc, perm.PERM_EDIT_PROBLEM_SELF) or handler.has_perm(perm.PERM_EDIT_PROBLEM) %}
<li class="menu__seperator"></li>
<li class="menu__item"><a class="menu__link{% if page_name == 'problem_edit' %} active{% endif %}" href="{{ reverse_url('problem_edit', pid=pdoc['_id']) }}">
<span class="icon icon-edit"></span> {{ _('Edit') }}

@ -24,11 +24,11 @@
<span class="icon icon-send"></span> {{ _('Submit') }}
</a></li>
{% elif handler.is_done(tdoc) %}
<li class="menu__item scratchpad--hide"><a class="menu__link" href="{{ reverse_url('problem_detail', pid=pdoc['_id']) }}">
<li class="menu__item scratchpad--hide"><a class="menu__link" href="/p/{{ pdoc.pid }}">
<span class="icon icon-send"></span> {{ _('Open in Problem Set') }}
</a></li>
{% endif %}
{% if handler.own(pdoc, vj4.model.builtin.PERM_EDIT_PROBLEM_SELF) or handler.has_perm(vj4.model.builtin.PERM_EDIT_PROBLEM) %}
{% if handler.own(pdoc, perm.PERM_EDIT_PROBLEM_SELF) or handler.has_perm(perm.PERM_EDIT_PROBLEM) %}
<li class="menu__seperator"></li>
<li class="menu__item"><a class="menu__link{% if page_name == 'problem_edit' %} active{% endif %}" href="{{ reverse_url('problem_edit', pid=pdoc['_id']) }}">
<span class="icon icon-edit"></span> {{ _('Edit') }}

@ -9,7 +9,7 @@
<div>
<ol class="menu">
{% if page_name == 'discussion_node' or page_name == 'discussion_detail' %}
{% if handler.has_perm(vj4.model.builtin.PERM_CREATE_DISCUSSION) %}
{% if handler.has_perm(perm.PERM_CREATE_DISCUSSION) %}
<li class="menu__item"><a class="menu__link highlight" href="{{ reverse_url('discussion_create_document_as_node', doc_type=pdoc['doc_type'], doc_id=pdoc['_id']) }}">
<span class="icon icon-add"></span> {{ _('Create a Discussion') }}
</a></li>
@ -25,7 +25,7 @@
<li class="menu__seperator"></li>
{% endif %}
{% if page_name == 'problem_detail' %}
{% if handler.has_perm(vj4.model.builtin.PERM_SUBMIT_PROBLEM) %}
{% if handler.has_perm(perm.PERM_SUBMIT_PROBLEM) %}
<li class="menu__item scratchpad--hide"><a class="menu__link highlight" name="problem-sidebar__open-scratchpad" href="javascript:;" data-global-hotkey="alt+e">
<span class="icon icon-enlarge"></span> {{ _('Open Scratchpad') }} (Alt+E)
</a></li>
@ -34,11 +34,11 @@
</a></li>
{% endif %}
{% else %}
<li class="menu__item"><a class="menu__link" href="{{ reverse_url('problem_detail', pid=pdoc['_id']) }}">
<li class="menu__item"><a class="menu__link" href="/p/{{ pdoc.pid }}">
<span class="icon icon-flag"></span> {{ _('View Problem') }}
</a></li>
{% endif %}
{% if handler.has_perm(vj4.model.builtin.PERM_SUBMIT_PROBLEM) %}
{% if handler.has_perm(perm.PERM_SUBMIT_PROBLEM) %}
<li class="menu__item scratchpad--hide"><a class="menu__link{% if page_name == 'problem_submit' %} active{% endif %}" href="{{ reverse_url('problem_submit', pid=pdoc['_id']) }}">
<span class="icon icon-send"></span> {{ _('Submit') }}
</a></li>
@ -51,15 +51,15 @@
<span class="icon icon-send"></span> {{ _('No Permission to Submit') }}
</a></li>
{% endif %}
{% if handler.has_perm(vj4.model.builtin.PERM_VIEW_DISCUSSION) or handler.has_perm(vj4.model.builtin.PERM_VIEW_PROBLEM_SOLUTION) %}
{% if handler.has_perm(perm.PERM_VIEW_DISCUSSION) or handler.has_perm(perm.PERM_VIEW_PROBLEM_SOLUTION) %}
<li class="menu__seperator"></li>
{% endif %}
{% if handler.has_perm(vj4.model.builtin.PERM_VIEW_DISCUSSION) %}
{% if handler.has_perm(perm.PERM_VIEW_DISCUSSION) %}
<li class="menu__item"><a class="menu__link{% if page_name == 'discussion_node' or page_name == 'discussion_detail' %} active{% endif %}" href="{{ reverse_url('discussion_node_document_as_node', doc_type=pdoc['doc_type'], doc_id=pdoc['_id']) }}">
<span class="icon icon-comment--text"></span> {{ _('Discussions') }}
</a></li>
{% endif %}
{% if handler.has_perm(vj4.model.builtin.PERM_VIEW_PROBLEM_SOLUTION) %}
{% if handler.has_perm(perm.PERM_VIEW_PROBLEM_SOLUTION) %}
<li class="menu__item"><a class="menu__link{% if page_name == 'problem_solution' %} active{% endif %}" href="{{ reverse_url('problem_solution', pid=pdoc['_id']) }}">
<span class="icon icon-comment--text"></span> {{ _('Solutions') }}
</a></li>
@ -70,7 +70,7 @@
<span class="icon icon-copy"></span> {{ _('Copy Problem') }}
</a></li>
{% endif %}
{% if handler.own(pdoc, vj4.model.builtin.PERM_EDIT_PROBLEM_SELF) or handler.has_perm(vj4.model.builtin.PERM_EDIT_PROBLEM) %}
{% if handler.user._id == pdoc.owner or handler.has_perm(perm.PERM_EDIT_PROBLEM) %}
<li class="menu__seperator"></li>
<li class="menu__item"><a class="menu__link{% if page_name == 'problem_edit' %} active{% endif %}" href="{{ reverse_url('problem_edit', pid=pdoc['_id']) }}">
<span class="icon icon-edit"></span> {{ _('Edit') }}

@ -1,43 +1,15 @@
{% set page_name = "problem_detail" %}
{% extends "layout/basic.html" %}
{% block content %}
<div style="display: none" class="dialog__body--copy-to">
<div>
<div class="row"><div class="columns">
<h1>{{ _('Copy Problem To') }}</h1>
</div></div>
<div class="row">
<div class="medium-12 columns">
<label>
{{ _('Destination Domain') }}
<input name="domain_id" placeholder="{{ _('Domain ID') }}" type="text" class="textbox" autocomplete="off" data-autofocus>
</label>
</div>
</div>
<div class="row">
<div class="medium-12 columns">
<label>
<label class="checkbox">
<input type="checkbox" name="numeric_pid" checked>{{ _('Numeric PID') }}
</label>
<label class="checkbox">
<input type="checkbox" name="hidden">{{ _('Hidden') }}
</label>
</label>
</div>
</div>
</div>
</div>
<script>
var Context = {{ {
'problemId': pdoc['_id'],
'socketUrl': '{}-conn'.format(reverse_url('problem_pretest', pid=pdoc['_id'])),
'postPretestUrl': reverse_url('problem_pretest', pid=pdoc['_id']),
'postSubmitUrl': reverse_url('problem_submit', pid=pdoc['_id']) if not tdoc else reverse_url('contest_detail_problem_submit' if tdoc['doc_type'] == vj4.model.document.TYPE_CONTEST else 'homework_detail_problem_submit', tid=tdoc['doc_id'], pid=pdoc['_id']),
'getSubmissionsUrl': reverse_url('problem_submit', pid=pdoc['_id']) if not tdoc else reverse_url('contest_detail_problem_submit' if tdoc['doc_type'] == vj4.model.document.TYPE_CONTEST else 'homework_detail_problem_submit', tid=tdoc['doc_id'], pid=pdoc['_id']),
'getRecordDetailUrl': reverse_url('record_detail', rid='{rid}'),
'code_lang': handler.get_setting('code_lang'),
'code_template': handler.get_code_template(),
}|json|safe }};
var Context = {
'problemId': "{{ pdoc.pid }}",
'postSubmitUrl': "/p/{{ pdoc.pid }}",
'getSubmissionsUrl': "/p/{{ pdoc.pid }}",
'getRecordDetailUrl': "/r/{{ rid }}",
'code_lang': "{{ handler.user.codeLang }}",
'code_template': "{{ handler.user.codeTemplate }}",
};
</script>
<div class="row">
<div class="medium-9 columns">
@ -51,10 +23,9 @@
<blockquote class="warn">
<p>{{ _('No dataset at present.') }}</p>
</blockquote>
{% endif %}
{% if pdoc['data'] is mapping %}
{% elif pdoc.data.startsWith('from:') %}
<blockquote class="note">
<p>{{ _('Test data comes from') }} <a href="{{ reverse_url('problem_detail', domain_id=pdoc['data']['domain'], pid=pdoc['data']['pid']) }}" target="_blank">{{ pdoc['data']['domain'] }}/{{ pdoc['data']['pid'] }}</a></p>
<p>{{ _('Test data comes from') }} <a href="#" target="_blank">{{ pdoc.data.split(':')[1] }}</a></p>
</blockquote>
{% endif %}
{% if tdoc %}
@ -64,27 +35,17 @@
<p>{{ _('You cannot submit for this problem because the contest is ended. You can click "Open in Problem Set" to view this problem in normal mode.') }}</p>
</blockquote>
{% endif %}{# handler.is_done(tdoc) #}
{% else %}{# tdoc['doc_type'] == vj4.model.document.TYPE_CONTEST #}
{% if handler.is_homework_extended(tdoc) %}
<blockquote class="note">
<p>{{ _('The homework\'s deadline is due but in extension. You can still submit for this problem but your score will be penalized.') }}</p>
</blockquote>
{% elif handler.is_done(tdoc) %}{# handler.is_homework_extended(tdoc) #}
<blockquote class="note">
<p>{{ _('You cannot submit for this problem because the homework\'s deadline is due.') }}</p>
</blockquote>
{% endif %}{# handler.is_homework_extended(tdoc) #}
{% endif %}{# tdoc['doc_type'] == vj4.model.document.TYPE_CONTEST #}
{% endif %}{# tdoc #}
{{ pdoc['content']|markdown }}
{{ pdoc['content']|markdown|safe }}
</div>
</div></div>
</div>
</div>
<div class="medium-3 columns">
{% with owner_udoc=udoc, owner_dudoc=dudoc %}
{% set owner_udoc=udoc %}
{% set owner_dudoc=dudoc %}
{% include "partials/problem_sidebar.html" %}
{% endwith %}
</div>
</div>
<div class="scratchpad-container" style="display:none">

@ -28,13 +28,13 @@
{% for sub_categories in category %}
<li class="group-list__item">
<h2 class="section__title">
<a href="{{ reverse_url('problem_category', category=category|urlencode) }}">{{ category }}</a>
<a href="/p?category={{ category|urlencode }}">{{ category }}</a>
</h2>
{% if sub_categories | length > 0 %}
<ol class="chip-list">
{% for sub_category in sub_categories %}
<li class="chip-list__item">
<a class="typo-a" href="{{ reverse_url('problem_category', category=(category|urlencode + ',' + sub_category|urlencode)) }}">{{ sub_category }}</a>
<a class="typo-a" href="/p?category={{ category|urlencode + ',' + sub_category|urlencode }}">{{ sub_category }}</a>
</li>
{% endfor %}
</ol>
@ -52,9 +52,8 @@
<div class="section__header">
<h1 class="section__title">{{ _('Search') }}</h1>
</div>
<form method="get" action="{{ reverse_url('problem_search') }}">
<form method="get" action="/problem/search">
<div class="section__body">
<!-- TODO: replace with form_builder -->
<label>
<input name="q" type="text" class="textbox" value="" placeholder="1001">
</label>
@ -62,21 +61,21 @@
</div>
</form>
</div>
{% if handler.has_perm(vj4.model.builtin.PERM_CREATE_PROBLEM) %}
{% if handler.has_perm(perm.PERM_CREATE_PROBLEM) %}
<div class="section side">
<div class="section__header">
<h1 class="section__title">{{ _('Create Problem') }}</h1>
</div>
<ol class="menu">
<li class="menu__item">
<a href="{{ reverse_url('problem_create') }}" class="menu__link">
<a href="/problem/create" class="menu__link">
<span class="icon icon-add"></span>
{{ _('Create Problem') }}</a>
</li>
<li class="menu__item">
<a href="{{ reverse_url('problem_copy') }}" class="menu__link">
<a href="/problem/import" class="menu__link">
<span class="icon icon-copy"></span>
{{ _('Copy Problem') }}</a>
{{ _('Import Problem') }}</a>
</li>
</ol>
<div class="section__body">

@ -9,7 +9,7 @@
<div class="section__body">
<iframe src="{{ reverse_url('problem_upload', pid=pdoc['_id']) }}" frameborder="0" style="width: 100%; height: 100px;"></iframe>
<p class="help-text">{{ _('Hint') }}: <a href="{{ reverse_url('wiki_help') }}#upload">{{ _('Dataset Format') }}</a>, {{ _('An example of dataset') }}: <a href="https://github.com/vijos/jd4/blob/master/jd4/testdata/aplusb-legacy.zip?raw=true">{{ _('Download') }}</a></p>
{% if pdoc and (handler.own(pdoc, vj4.model.builtin.PERM_READ_PROBLEM_DATA_SELF) or handler.has_perm(vj4.model.builtin.PERM_READ_PROBLEM_DATA) or handler.has_priv(vj4.model.builtin.PRIV_READ_PROBLEM_DATA)) %}
{% if pdoc and (handler.own(pdoc, perm.PERM_READ_PROBLEM_DATA_SELF) or handler.has_perm(perm.PERM_READ_PROBLEM_DATA) or handler.has_priv(vj4.model.builtin.PRIV_READ_PROBLEM_DATA)) %}
<p><a href="{{ reverse_url('problem_data', pid=pdoc['_id']) }}" class="expanded button">{{ _('Download Dataset') }}</a></p>
{% endif %}
</div>

@ -22,16 +22,16 @@
reply_edit_op = 'edit_reply',
comment_delete_op = 'delete_solution',
reply_delete_op = 'delete_reply',
comment_post_perm = vj4.model.builtin.PERM_CREATE_PROBLEM_SOLUTION,
comment_edit_perm = vj4.model.builtin.PERM_EDIT_PROBLEM_SOLUTION,
comment_edit_self_perm = vj4.model.builtin.PERM_EDIT_PROBLEM_SOLUTION_SELF,
comment_delete_perm = vj4.model.builtin.PERM_DELETE_PROBLEM_SOLUTION,
comment_delete_self_perm = vj4.model.builtin.PERM_DELETE_PROBLEM_SOLUTION_SELF,
reply_post_perm = vj4.model.builtin.PERM_REPLY_PROBLEM_SOLUTION,
reply_edit_perm = vj4.model.builtin.PERM_EDIT_PROBLEM_SOLUTION_REPLY,
reply_edit_self_perm = vj4.model.builtin.PERM_EDIT_PROBLEM_SOLUTION_REPLY_SELF,
reply_delete_perm = vj4.model.builtin.PERM_DELETE_PROBLEM_SOLUTION_REPLY,
reply_delete_self_perm = vj4.model.builtin.PERM_DELETE_PROBLEM_SOLUTION_REPLY_SELF
comment_post_perm = perm.PERM_CREATE_PROBLEM_SOLUTION,
comment_edit_perm = perm.PERM_EDIT_PROBLEM_SOLUTION,
comment_edit_self_perm = perm.PERM_EDIT_PROBLEM_SOLUTION_SELF,
comment_delete_perm = perm.PERM_DELETE_PROBLEM_SOLUTION,
comment_delete_self_perm = perm.PERM_DELETE_PROBLEM_SOLUTION_SELF,
reply_post_perm = perm.PERM_REPLY_PROBLEM_SOLUTION,
reply_edit_perm = perm.PERM_EDIT_PROBLEM_SOLUTION_REPLY,
reply_edit_self_perm = perm.PERM_EDIT_PROBLEM_SOLUTION_REPLY_SELF,
reply_delete_perm = perm.PERM_DELETE_PROBLEM_SOLUTION_REPLY,
reply_delete_self_perm = perm.PERM_DELETE_PROBLEM_SOLUTION_REPLY_SELF
) }}
{{ paginator.render(page, pcount) }}
{% if drcount == 0 %}

@ -27,7 +27,7 @@
<div class="section__header">
<h1 class="section__title">{{ _('Information') }}</h1>
</div>
{% if handler.has_perm(vj4.model.builtin.PERM_REJUDGE) %}
{% if handler.has_perm(perm.PERM_REJUDGE) %}
<div class="section__body no-padding">
<ol class="menu">
<li class="menu__item">
@ -79,7 +79,7 @@
{% if rdoc['type'] == vj4.constant.record.TYPE_PRETEST and (handler.own(rdoc, priv=vj4.model.builtin.PRIV_READ_PRETEST_DATA_SELF, field='uid') or handler.has_priv(vj4.model.builtin.PRIV_READ_PRETEST_DATA)) %}
<dt>{{ _('Pretest Data') }}</dt>
<dd><a href="{{ reverse_url('record_pretest_data', rid=rdoc['_id']) }}"><span class="icon icon-download"></span> {{ _('Download') }}</a></dd>
{% elif rdoc['type'] == vj4.constant.record.TYPE_SUBMISSION and pdoc and (handler.own(pdoc, vj4.model.builtin.PERM_READ_PROBLEM_DATA_SELF) or handler.has_perm(vj4.model.builtin.PERM_READ_PROBLEM_DATA) or handler.has_priv(vj4.model.builtin.PRIV_READ_PROBLEM_DATA)) %}
{% elif rdoc['type'] == vj4.constant.record.TYPE_SUBMISSION and pdoc and (handler.own(pdoc, perm.PERM_READ_PROBLEM_DATA_SELF) or handler.has_perm(perm.PERM_READ_PROBLEM_DATA) or handler.has_priv(vj4.model.builtin.PRIV_READ_PROBLEM_DATA)) %}
<dt>{{ _('Problem Data') }}</dt>
<dd><a href="{{ reverse_url('problem_data', pid=pdoc['_id']) }}"><span class="icon icon-download"></span> {{ _('Download') }}</a></dd>
{% endif %}

@ -3,7 +3,7 @@
<tr data-rid="{{ rdoc['_id'] }}">
{{ record.render_status_td(rdoc) }}
<td class="col--problem col--problem-name">
{% if (rdoc['domain_id'] == handler.domain_id and handler.has_perm(vj4.model.builtin.PERM_REJUDGE)) or handler.has_priv(vj4.model.builtin.PRIV_REJUDGE) %}
{% if (rdoc['domain_id'] == handler.domain_id and handler.has_perm(perm.PERM_REJUDGE)) or handler.has_priv(vj4.model.builtin.PRIV_REJUDGE) %}
<form class="form--inline" method="post" action="{{ reverse_url('record_rejudge', rid=rdoc['_id']) }}">
<input type="hidden" name="csrf_token" value="{{ handler.csrf_token }}">
<button type="submit" class="link text-maroon lighter">
@ -15,7 +15,7 @@
{% if rdoc['type'] == vj4.constant.record.TYPE_PRETEST %}
({{ _('Pretest') }})
{% endif %}
{% if pdoc and (not pdoc['hidden'] or (pdoc['domain_id'] == handler.domain_id and handler.has_perm(vj4.model.builtin.PERM_VIEW_PROBLEM_HIDDEN))) %}
{% if pdoc and (not pdoc['hidden'] or (pdoc['domain_id'] == handler.domain_id and handler.has_perm(perm.PERM_VIEW_PROBLEM_HIDDEN))) %}
{{ problem.render_problem_title(pdoc, show_tags=false) }}
{% else %}
*

@ -175,7 +175,7 @@
</form>
</li>
{% endif %}
{% if handler.own(tdoc, vj4.model.builtin.PERM_EDIT_TRAINING_SELF) or handler.has_perm(vj4.model.builtin.PERM_EDIT_TRAINING) %}
{% if handler.own(tdoc, perm.PERM_EDIT_TRAINING_SELF) or handler.has_perm(perm.PERM_EDIT_TRAINING) %}
<li class="menu__item"><a class="menu__link" href="{{ reverse_url('training_edit', tid=tdoc['doc_id']) }}">
<span class="icon icon-edit"></span> {{ _('Edit') }}
</a></li>

@ -76,7 +76,7 @@
</div>
</div>
{% endif %}
{% if handler.has_perm(vj4.model.builtin.PERM_CREATE_TRAINING) %}
{% if handler.has_perm(perm.PERM_CREATE_TRAINING) %}
<div class="section side">
<div class="section__header">
<h1 class="section__title">{{ _('Create Training Plan') }}</h1>

@ -66,7 +66,7 @@
{{ vj4.constant.model.USER_GENDER_ICONS[handler.get_udoc_setting(udoc, 'gender')] }}
</span>
{% endif %}
{% if handler.dudoc_has_perm(udoc, dudoc, vj4.model.builtin.PERM_MOD_BADGE) %}
{% if handler.dudoc_has_perm(udoc, dudoc, perm.PERM_MOD_BADGE) %}
<span
class="profile-header__contact-item user-profile-badge badge--mod"
data-tooltip="{{ _('{} is a moderator of this domain.').format(_('She' if handler.get_udoc_setting(udoc, 'gender') == 1 else 'He')) }}"
@ -111,7 +111,7 @@
</div>
<div class="section__body">
{% for pdoc in pdocs %}
<p><a href="{{ reverse_url('problem_detail', pid=pdoc['_id']) }}">{{ pdoc['title'] }}</a></p>
<p><a href="/p/{{ pdoc.pid }}">{{ pdoc['title'] }}</a></p>
{% endfor %}
</div>
{% endif %}{# if pdocs #}

@ -28,7 +28,7 @@
</div></div>
<div class="row"><div class="columns">
<div class="text-center supplementary inverse">
<a href="{{ reverse_url('user_lostpass') }}">{{ _('Forgot password or username?') }}</a>
<a href="/lostpass">{{ _('Forgot password or username?') }}</a>
</div>
</div></div>
</form>

@ -192,7 +192,7 @@
<div class="section__body typo">
{{ _('no_translation_warn')|safe }}
<p>如果您无法登录,请仔细想想,是不是用户名记错了。比如,自己原本想要注册的用户名已经被注册,所以使用了一个带有前和/或后缀的用户名。</p>
<p>如果您确信您的账号被盗或者忘记了账号和/或密码,请及时<a href="{{ reverse_url('user_lostpass') }}">{{ _('Reset Password or Find Username') }}</a></p>
<p>如果您确信您的账号被盗或者忘记了账号和/或密码,请及时<a href="/lostpass">{{ _('Reset Password or Find Username') }}</a></p>
</div>
</div>
{% endblock %}

@ -153,6 +153,11 @@ binary-extensions@^2.0.0:
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c"
integrity sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==
blueimp-md5@^2.13.0:
version "2.13.0"
resolved "https://registry.yarnpkg.com/blueimp-md5/-/blueimp-md5-2.13.0.tgz#07314b0c64dda0bf1733f96ce40d5af94eb28965"
integrity sha512-lmp0m647R5e77ORduxLW5mISIDcvgJZa52vMBv5uVI3UmSWTQjkJsZVBfaFqQPw/QFogJwvY6e3Gl9nP+Loe+Q==
brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
@ -401,6 +406,11 @@ encodeurl@^1.0.2:
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
entities@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4"
integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==
error-inject@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/error-inject/-/error-inject-1.0.0.tgz#e2b3d91b54aed672f309d950d154850fa11d4f37"
@ -651,6 +661,11 @@ has-flag@^3.0.0:
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
hljs@^6.2.3:
version "6.2.3"
resolved "https://registry.yarnpkg.com/hljs/-/hljs-6.2.3.tgz#d4d6208fa2a84f294956bc50f2c812e9cbd49bcc"
integrity sha1-1NYgj6KoTylJVrxQ8sgS6cvUm8w=
http-assert@^1.3.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/http-assert/-/http-assert-1.4.1.tgz#c5f725d677aa7e873ef736199b89686cceb37878"
@ -839,6 +854,13 @@ json-stable-stringify-without-jsonify@^1.0.1:
resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=
katex@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/katex/-/katex-0.6.0.tgz#12418e09121c05c92041b6b3b9fb6bab213cb6f3"
integrity sha1-EkGOCRIcBckgQbazuftrqyE8tvM=
dependencies:
match-at "^0.1.0"
keygrip@~1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/keygrip/-/keygrip-1.1.0.tgz#871b1681d5e159c62a445b0c74b615e0917e7226"
@ -950,6 +972,13 @@ levn@^0.3.0, levn@~0.3.0:
prelude-ls "~1.1.2"
type-check "~0.3.2"
linkify-it@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-2.2.0.tgz#e3b54697e78bf915c70a38acd78fd09e0058b1cf"
integrity sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==
dependencies:
uc.micro "^1.0.1"
lodash@^4.17.14, lodash@^4.17.15:
version "4.17.15"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
@ -960,6 +989,34 @@ long@^4.0.0:
resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28"
integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==
markdown-it-katex@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/markdown-it-katex/-/markdown-it-katex-2.0.3.tgz#d7b86a1aea0b9d6496fab4e7919a18fdef589c39"
integrity sha1-17hqGuoLnWSW+rTnkZoY/e9YnDk=
dependencies:
katex "^0.6.0"
markdown-it@^10.0.0:
version "10.0.0"
resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-10.0.0.tgz#abfc64f141b1722d663402044e43927f1f50a8dc"
integrity sha512-YWOP1j7UbDNz+TumYP1kpwnP0aEa711cJjrAQrzd0UXlbJfc5aAq0F/PZHjiioqDC1NKgvIMX+o+9Bk7yuM2dg==
dependencies:
argparse "^1.0.7"
entities "~2.0.0"
linkify-it "^2.0.0"
mdurl "^1.0.1"
uc.micro "^1.0.5"
match-at@^0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/match-at/-/match-at-0.1.1.tgz#25d040d291777704d5e6556bbb79230ec2de0540"
integrity sha512-h4Yd392z9mST+dzc+yjuybOGFNOZjmXIPKWjxBd1Bb23r4SmDOsk2NYCU2BMUBGbSpZqwVsZYNq26QS3xfaT3Q==
mdurl@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e"
integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=
media-typer@0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
@ -1498,6 +1555,11 @@ type-is@^1.6.14, type-is@^1.6.16:
media-typer "0.3.0"
mime-types "~2.1.24"
uc.micro@^1.0.1, uc.micro@^1.0.5:
version "1.0.6"
resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac"
integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==
unpipe@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"

Loading…
Cancel
Save