mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-18 17:38:19 +08:00
priv (unstable)
This commit is contained in:
1 parent
554698189c
commit
e092efca20
63 files changed
+706
-397
No files matched your search
@@ -27,7 +27,6 @@ async function load(call) {
|
||||
require('../lib/i18n');
|
||||
require('../utils');
|
||||
require('../error');
|
||||
require('../permission');
|
||||
try {
|
||||
require('../options');
|
||||
} catch (e) {
|
||||
|
||||
@@ -12,7 +12,6 @@ async function load() {
|
||||
require('../lib/i18n');
|
||||
require('../utils');
|
||||
require('../error');
|
||||
require('../permission');
|
||||
require('../options');
|
||||
await Promise.all([locale(pending, fail), template(pending, fail)]);
|
||||
const bus = require('../service/bus');
|
||||
|
||||
@@ -112,6 +112,17 @@ class PermissionError extends ForbiddenError {
|
||||
}
|
||||
}
|
||||
|
||||
class PrivilegeError extends ForbiddenError {
|
||||
constructor(priv) {
|
||||
if (priv === global.Hydro.model.builtin.PRIV.PRIV_USER_PROFILE) {
|
||||
super("You're not logged in.");
|
||||
} else {
|
||||
super("You don't have the required privilege.");
|
||||
}
|
||||
this.params = [priv];
|
||||
}
|
||||
}
|
||||
|
||||
class ValidationError extends ForbiddenError {
|
||||
constructor(field0, field1) {
|
||||
super('ValidationError');
|
||||
@@ -245,6 +256,7 @@ global.Hydro.error = module.exports = {
|
||||
ProblemDataNotFoundError,
|
||||
OpcountExceededError,
|
||||
PermissionError,
|
||||
PrivilegeError,
|
||||
NoProblemError,
|
||||
ValidationError,
|
||||
ProblemNotFoundError,
|
||||
|
||||
@@ -5,11 +5,16 @@ const {
|
||||
ContestNotLiveError, ValidationError, ProblemNotFoundError,
|
||||
ContestNotAttendedError,
|
||||
} = require('../error');
|
||||
const {
|
||||
PERM_CREATE_CONTEST, PERM_EDIT_CONTEST, PERM_READ_RECORD_CODE,
|
||||
PERM_VIEW_CONTEST,
|
||||
} = require('../permission');
|
||||
const paginate = require('../lib/paginate');
|
||||
const {
|
||||
PERM: {
|
||||
PERM_CREATE_CONTEST, PERM_EDIT_CONTEST, PERM_READ_RECORD_CODE,
|
||||
PERM_VIEW_CONTEST, PERM_EDIT_CONTEST_SELF,
|
||||
},
|
||||
PRIV: {
|
||||
PRIV_READ_RECORD_CODE
|
||||
}
|
||||
} = require('../model/builtin');
|
||||
const contest = require('../model/contest');
|
||||
const document = require('../model/document');
|
||||
const problem = require('../model/problem');
|
||||
@@ -58,7 +63,7 @@ class ContestDetailHandler extends ContestHandler {
|
||||
this.response.template = 'contest_detail.html';
|
||||
const [tsdoc, pdict] = await Promise.all([
|
||||
contest.getStatus(domainId, this.tdoc.docId, this.user._id),
|
||||
problem.getList(domainId, this.tdoc.pids),
|
||||
problem.getList(domainId, this.tdoc.pids, true),
|
||||
]);
|
||||
const psdict = {};
|
||||
let rdict = {};
|
||||
@@ -123,6 +128,7 @@ class ContestScoreboardDownloadHandler extends ContestDetailHandler {
|
||||
class ContestEditHandler extends ContestDetailHandler {
|
||||
async prepare() {
|
||||
if (this.tdoc.owner !== this.user._id) this.checkPerm(PERM_EDIT_CONTEST);
|
||||
else this.checkPerm(PERM_EDIT_CONTEST_SELF);
|
||||
}
|
||||
|
||||
async get() {
|
||||
@@ -266,7 +272,7 @@ class ContestProblemSubmitHandler extends ContestProblemHandler {
|
||||
|
||||
class ContestCodeHandler extends ContestDetailHandler {
|
||||
async get({ domainId, tid }) {
|
||||
this.checkPerm(PERM_READ_RECORD_CODE);
|
||||
if (!this.user.hasPriv(PRIV_READ_RECORD_CODE)) this.checkPerm(PERM_READ_RECORD_CODE);
|
||||
this.limitRate('homework_code', 3600, 60);
|
||||
const [tdoc, tsdocs] = await contest.getAndListStatus(domainId, tid);
|
||||
const rnames = {};
|
||||
|
||||
+16
-10
@@ -4,13 +4,18 @@ const system = require('../model/system');
|
||||
const user = require('../model/user');
|
||||
const discussion = require('../model/discussion');
|
||||
const document = require('../model/document');
|
||||
const {
|
||||
PERM: {
|
||||
PERM_VIEW_DISCUSSION, PERM_EDIT_DISCUSSION, PERM_EDIT_DISCUSSION_REPLY,
|
||||
PERM_DELETE_DISCUSSION, PERM_DELETE_DISCUSSION_REPLY, PERM_HIGHLIGHT_DISCUSSION,
|
||||
PERM_CREATE_DISCUSSION, PERM_REPLY_DISCUSSION,
|
||||
},
|
||||
PRIV: {
|
||||
PRIV_USER_PROFILE,
|
||||
},
|
||||
} = require('../model/builtin');
|
||||
const { Route, Handler } = require('../service/server');
|
||||
const { DiscussionNotFoundError, DocumentNotFoundError } = require('../error');
|
||||
const {
|
||||
PERM_VIEW_DISCUSSION, PERM_EDIT_DISCUSSION, PERM_EDIT_DISCUSSION_REPLY,
|
||||
PERM_DELETE_DISCUSSION, PERM_DELETE_DISCUSSION_REPLY, PERM_HIGHLIGHT_DISCUSSION,
|
||||
PERM_LOGGEDIN, PERM_CREATE_DISCUSSION, PERM_REPLY_DISCUSSION,
|
||||
} = require('../permission');
|
||||
|
||||
const typeMapper = {
|
||||
problem: document.TYPE_PROBLEM,
|
||||
@@ -119,7 +124,7 @@ class DiscussionNodeHandler extends DiscussionHandler {
|
||||
|
||||
class DiscussionCreateHandler extends DiscussionHandler {
|
||||
async prepare() {
|
||||
this.checkPerm(PERM_LOGGEDIN);
|
||||
this.checkPriv(PRIV_USER_PROFILE);
|
||||
this.checkPerm(PERM_CREATE_DISCUSSION);
|
||||
}
|
||||
|
||||
@@ -151,7 +156,7 @@ class DiscussionCreateHandler extends DiscussionHandler {
|
||||
|
||||
class DiscussionDetailHandler extends DiscussionHandler {
|
||||
async get({ domainId, did, page = 1 }) {
|
||||
const dsdoc = this.user.hasPerm(PERM_LOGGEDIN)
|
||||
const dsdoc = this.user.hasPriv(PRIV_USER_PROFILE)
|
||||
? await discussion.getStatus(domainId, did, this.user._id)
|
||||
: null;
|
||||
const [drdocs, pcount, drcount] = await paginate(
|
||||
@@ -181,8 +186,11 @@ class DiscussionDetailHandler extends DiscussionHandler {
|
||||
};
|
||||
}
|
||||
|
||||
async post() {
|
||||
this.checkPriv(PRIV_USER_PROFILE);
|
||||
}
|
||||
|
||||
async postReply({ domainId, did, content }) {
|
||||
this.checkPerm(PERM_LOGGEDIN);
|
||||
this.checkPerm(PERM_REPLY_DISCUSSION);
|
||||
this.limitRate('add_discussion', 3600, 30);
|
||||
await discussion.addReply(domainId, did, this.user._id, content, this.request.ip);
|
||||
@@ -190,7 +198,6 @@ class DiscussionDetailHandler extends DiscussionHandler {
|
||||
}
|
||||
|
||||
async postTailReply({ domainId, drid, content }) {
|
||||
this.checkPerm(PERM_LOGGEDIN);
|
||||
this.checkPerm(PERM_REPLY_DISCUSSION);
|
||||
this.limitRate('add_discussion', 3600, 30);
|
||||
await discussion.addTailReply(domainId, drid, this.user._id, content, this.request.ip);
|
||||
@@ -257,7 +264,6 @@ class DiscussionTailReplyRawHandler extends DiscussionHandler {
|
||||
|
||||
class DiscussionEditHandler extends DiscussionHandler {
|
||||
async get() {
|
||||
if (this.ddoc.owner !== this.user._id) this.checkPerm(PERM_EDIT_DISCUSSION);
|
||||
const path = [
|
||||
['Hydro', 'homepage'],
|
||||
['discussion_main', 'discussion_main'],
|
||||
|
||||
@@ -4,7 +4,7 @@ const system = require('../model/system');
|
||||
const { DOMAIN_SETTINGS, DOMAIN_SETTINGS_BY_KEY } = require('../model/setting');
|
||||
const paginate = require('../lib/paginate');
|
||||
const { Route, Handler } = require('../service/server');
|
||||
const { PERM_MANAGE } = require('../permission');
|
||||
const { PERM_MANAGE } = require('../model/builtin').PERM;
|
||||
const { RoleAlreadyExistError, ValidationError, PermissionError } = require('../error');
|
||||
|
||||
class DomainRankHandler extends Handler {
|
||||
|
||||
+15
-17
@@ -21,9 +21,14 @@ const discussion = require('../model/discussion');
|
||||
const token = require('../model/token');
|
||||
const training = require('../model/training');
|
||||
const {
|
||||
PERM_VIEW_TRAINING, PERM_VIEW_CONTEST, PERM_VIEW_DISCUSSION,
|
||||
PERM_LOGGEDIN, PERM_VIEW_HOMEWORK,
|
||||
} = require('../permission');
|
||||
PERM: {
|
||||
PERM_VIEW_TRAINING, PERM_VIEW_CONTEST, PERM_VIEW_DISCUSSION,
|
||||
PERM_VIEW_HOMEWORK,
|
||||
},
|
||||
PRIV: {
|
||||
PRIV_USER_PROFILE, PRIV_DELETE_FILE, PRIV_DELETE_FILE_SELF,
|
||||
},
|
||||
} = require('../model/builtin');
|
||||
|
||||
const { geoip, useragent } = global.Hydro.lib;
|
||||
|
||||
@@ -113,10 +118,6 @@ class HomeHandler extends Handler {
|
||||
}
|
||||
|
||||
class HomeSecurityHandler extends Handler {
|
||||
async prepare() {
|
||||
this.checkPerm(PERM_LOGGEDIN);
|
||||
}
|
||||
|
||||
async get() {
|
||||
// TODO(iceboy): pagination? or limit session count for uid?
|
||||
const sessions = await token.getSessionListByUid(this.user._id);
|
||||
@@ -172,10 +173,6 @@ class HomeSecurityHandler extends Handler {
|
||||
}
|
||||
|
||||
class HomeSettingsHandler extends Handler {
|
||||
async prepare() {
|
||||
this.checkPerm(PERM_LOGGEDIN);
|
||||
}
|
||||
|
||||
async get({ category }) {
|
||||
// eslint-disable-next-line prefer-destructuring
|
||||
category = category[0]; // Category would be splitted into array
|
||||
@@ -284,7 +281,6 @@ class HomeMessagesConnectionHandler extends ConnectionHandler {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO draft
|
||||
class HomeFileHandler extends Handler {
|
||||
async get() {
|
||||
const ufdocs = await file.getMulti({ owner: this.user._id }).toArray();
|
||||
@@ -296,6 +292,7 @@ class HomeFileHandler extends Handler {
|
||||
async postDelete(ufid) {
|
||||
const ufdoc = await file.get(ufid);
|
||||
if (ufdoc.owner !== this.user._id) this.checkPriv(PRIV_DELETE_FILE);
|
||||
else this.checkPriv(PRIV_DELETE_FILE_SELF);
|
||||
const result = await file.del(ufdoc._id);
|
||||
if (result) await file.decUsage(this.user._id, ufdoc.length);
|
||||
this.back();
|
||||
@@ -304,11 +301,12 @@ class HomeFileHandler extends Handler {
|
||||
|
||||
async function apply() {
|
||||
Route('homepage', '/', HomeHandler);
|
||||
Route('home_security', '/home/security', HomeSecurityHandler, PERM_LOGGEDIN);
|
||||
Route('user_changemail_with_code', '/home/changeMail/:code', UserChangemailWithCodeHandler, PERM_LOGGEDIN);
|
||||
Route('home_settings', '/home/settings/:category', HomeSettingsHandler, PERM_LOGGEDIN);
|
||||
Route('home_messages', '/home/messages', HomeMessagesHandler, PERM_LOGGEDIN);
|
||||
Connection('home_messages_conn', '/home/messages-conn', HomeMessagesConnectionHandler);
|
||||
Route('home_security', '/home/security', HomeSecurityHandler, PRIV_USER_PROFILE);
|
||||
Route('user_changemail_with_code', '/home/changeMail/:code', UserChangemailWithCodeHandler, PRIV_USER_PROFILE);
|
||||
Route('home_settings', '/home/settings/:category', HomeSettingsHandler, PRIV_USER_PROFILE);
|
||||
Route('home_messages', '/home/messages', HomeMessagesHandler, PRIV_USER_PROFILE);
|
||||
Route('home_file', '/home/file', HomeFileHandler, PRIV_USER_PROFILE);
|
||||
Connection('home_messages_conn', '/home/messages-conn', HomeMessagesConnectionHandler, PRIV_USER_PROFILE);
|
||||
}
|
||||
|
||||
global.Hydro.handler.home = module.exports = apply;
|
||||
@@ -6,12 +6,12 @@ const {
|
||||
ValidationError, HomeworkNotLiveError, ProblemNotFoundError,
|
||||
HomeworkNotAttendedError,
|
||||
} = require('../error');
|
||||
const { Route, Handler } = require('../service/server');
|
||||
const {
|
||||
PERM_VIEW_HOMEWORK, PERM_ATTEND_HOMEWORK, PERM_VIEW_PROBLEM,
|
||||
PERM_SUBMIT_PROBLEM, PERM_CREATE_HOMEWORK, PERM_EDIT_HOMEWORK,
|
||||
PERM_VIEW_HOMEWORK_SCOREBOARD, PERM_READ_RECORD_CODE,
|
||||
} = require('../permission');
|
||||
const { Route, Handler } = require('../service/server');
|
||||
} = require('../model/builtin').PERM;
|
||||
const system = require('../model/system');
|
||||
const user = require('../model/user');
|
||||
const contest = require('../model/contest');
|
||||
@@ -53,7 +53,7 @@ class HomeworkDetailHandler extends HomeworkHandler {
|
||||
const tdoc = await contest.get(domainId, tid, document.TYPE_HOMEWORK);
|
||||
const [tsdoc, pdict] = await Promise.all([
|
||||
contest.getStatus(domainId, tdoc.docId, this.user._id, document.TYPE_HOMEWORK),
|
||||
problem.getList(domainId, tdoc.pids),
|
||||
problem.getList(domainId, tdoc.pids, true),
|
||||
]);
|
||||
const psdict = {};
|
||||
let rdict = {};
|
||||
|
||||
@@ -2,8 +2,8 @@ const fs = require('fs');
|
||||
const path = require('path');
|
||||
const os = require('os');
|
||||
const problem = require('../model/problem');
|
||||
const { PERM_CREATE_PROBLEM } = require('../model/builtin').PERM;
|
||||
const { Route, Handler } = require('../service/server');
|
||||
const { PERM_CREATE_PROBLEM } = require('../permission');
|
||||
const { ValidationError } = require('../error');
|
||||
|
||||
class ProblemImportHandler extends Handler {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
const yaml = require('js-yaml');
|
||||
const { PERM_JUDGE } = require('../permission');
|
||||
const { parseTimeMS, parseMemoryMB } = require('../utils');
|
||||
const record = require('../model/record');
|
||||
const problem = require('../model/problem');
|
||||
@@ -164,8 +163,8 @@ class JudgeConnectionHandler extends ConnectionHandler {
|
||||
|
||||
async function apply() {
|
||||
Route('pretest', '/pretest', PretestHandler);
|
||||
Route('judge', '/judge', JudgeHandler, PERM_JUDGE);
|
||||
Connection('judge_conn', '/judge/conn', JudgeConnectionHandler, PERM_JUDGE);
|
||||
Route('judge', '/judge', JudgeHandler, builtin.PERM.PERM_JUDGE);
|
||||
Connection('judge_conn', '/judge/conn', JudgeConnectionHandler, builtin.PERM.PERM_JUDGE);
|
||||
}
|
||||
|
||||
apply.next = next;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const { PERM_JUDGE, PERM_LOGGEDIN } = require('../permission');
|
||||
const { PRIV_JUDGE, PRIV_USER_PROFILE } = require('../model/builtin').PRIV;
|
||||
const file = require('../model/file');
|
||||
const user = require('../model/user');
|
||||
const markdown = require('../lib/markdown');
|
||||
@@ -37,7 +37,7 @@ class StatusHandler extends Handler {
|
||||
|
||||
class StatusUpdateHandler extends Handler {
|
||||
async post(args) {
|
||||
this.checkPerm(PERM_JUDGE);
|
||||
this.checkPerm(PRIV_JUDGE);
|
||||
args.type = 'judger';
|
||||
return coll.updateOne(
|
||||
{ mid: args.mid, type: 'judger' },
|
||||
@@ -49,7 +49,7 @@ class StatusUpdateHandler extends Handler {
|
||||
|
||||
class SwitchLanguageHandler extends Handler {
|
||||
async get({ lang }) {
|
||||
if (this.user.hasPerm(PERM_LOGGEDIN)) await user.setById(this.user._id, { viewLang: lang });
|
||||
if (this.user.hasPriv(PRIV_USER_PROFILE)) await user.setById(this.user._id, { viewLang: lang });
|
||||
else this.session.viewLang = lang;
|
||||
this.back();
|
||||
}
|
||||
|
||||
+18
-11
@@ -6,6 +6,18 @@ const record = require('../model/record');
|
||||
const user = require('../model/user');
|
||||
const solution = require('../model/solution');
|
||||
const system = require('../model/system');
|
||||
const {
|
||||
PERM: {
|
||||
PERM_VIEW_PROBLEM, PERM_VIEW_PROBLEM_HIDDEN, PERM_SUBMIT_PROBLEM,
|
||||
PERM_CREATE_PROBLEM, PERM_READ_PROBLEM_DATA, PERM_EDIT_PROBLEM,
|
||||
PERM_VIEW_PROBLEM_SOLUTION, PERM_CREATE_PROBLEM_SOLUTION,
|
||||
PERM_EDIT_PROBLEM_SOLUTION, PERM_DELETE_PROBLEM_SOLUTION, PERM_EDIT_PROBLEM_SOLUTION_REPLY,
|
||||
PERM_REPLY_PROBLEM_SOLUTION,
|
||||
},
|
||||
PRIV: {
|
||||
PRIV_USER_PROFILE, PRIV_JUDGE,
|
||||
},
|
||||
} = require('../model/builtin');
|
||||
const bus = require('../service/bus');
|
||||
const {
|
||||
Route, Connection, Handler, ConnectionHandler,
|
||||
@@ -14,13 +26,6 @@ const {
|
||||
NoProblemError, ProblemDataNotFoundError, BadRequestError,
|
||||
SolutionNotFoundError,
|
||||
} = require('../error');
|
||||
const {
|
||||
PERM_VIEW_PROBLEM, PERM_VIEW_PROBLEM_HIDDEN, PERM_SUBMIT_PROBLEM,
|
||||
PERM_CREATE_PROBLEM, PERM_READ_PROBLEM_DATA, PERM_EDIT_PROBLEM,
|
||||
PERM_JUDGE, PERM_VIEW_PROBLEM_SOLUTION, PERM_CREATE_PROBLEM_SOLUTION,
|
||||
PERM_EDIT_PROBLEM_SOLUTION, PERM_DELETE_PROBLEM_SOLUTION, PERM_EDIT_PROBLEM_SOLUTION_REPLY,
|
||||
PERM_REPLY_PROBLEM_SOLUTION, PERM_LOGGEDIN,
|
||||
} = require('../permission');
|
||||
|
||||
class ProblemHandler extends Handler {
|
||||
async _prepare() {
|
||||
@@ -47,7 +52,7 @@ class ProblemHandler extends Handler {
|
||||
page,
|
||||
await system.get('PROBLEM_PER_PAGE'),
|
||||
);
|
||||
if (this.user.hasPerm(PERM_LOGGEDIN)) {
|
||||
if (this.user.hasPriv(PRIV_USER_PROFILE)) {
|
||||
psdict = await problem.getListStatus(
|
||||
domainId, this.user._id, pdocs.map((pdoc) => pdoc.docId),
|
||||
);
|
||||
@@ -109,7 +114,7 @@ class ProblemCategoryHandler extends ProblemHandler {
|
||||
page,
|
||||
await system.get('PROBLEM_PER_PAGE'),
|
||||
);
|
||||
if (this.user.hasPerm(PERM_LOGGEDIN)) {
|
||||
if (this.user.hasPriv(PRIV_USER_PROFILE)) {
|
||||
psdict = await problem.getListStatus(
|
||||
domainId, this.user._id, pdocs.map((pdoc) => pdoc.docId),
|
||||
);
|
||||
@@ -302,7 +307,7 @@ class ProblemEditHandler extends ProblemManageHandler {
|
||||
const pid = validator.checkPid(this.request.body.pid);
|
||||
const pdoc = await problem.get(domainId, this.request.params.pid);
|
||||
await problem.edit(domainId, pdoc.docId, { title, content, pid });
|
||||
this.response.redirect = `/p/${pid}`;
|
||||
this.back();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -332,7 +337,9 @@ class ProblemDataUploadHandler extends ProblemManageHandler {
|
||||
|
||||
class ProblemDataDownloadHandler extends ProblemDetailHandler {
|
||||
async get({ pid }) {
|
||||
if (this.user._id !== this.pdoc.owner) this.checkPerm([PERM_READ_PROBLEM_DATA, PERM_JUDGE]);
|
||||
if (this.user._id !== this.pdoc.owner) {
|
||||
if (!this.user.hasPerm(PERM_READ_PROBLEM_DATA)) this.checkPriv(PRIV_JUDGE);
|
||||
}
|
||||
if (!this.pdoc.data) throw new ProblemDataNotFoundError(pid);
|
||||
else if (typeof this.pdoc.data === 'string') [, this.response.redirect] = this.pdoc.data.split('from:');
|
||||
this.response.redirect = await file.url(this.pdoc.data, `${this.pdoc.title}.zip`);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const { ObjectID } = require('bson');
|
||||
const { PERM_READ_RECORD_CODE, PERM_REJUDGE, PERM_VIEW_PROBLEM_HIDDEN } = require('../permission');
|
||||
const { PermissionError } = require('../error');
|
||||
const { PERM_READ_RECORD_CODE, PERM_REJUDGE, PERM_VIEW_PROBLEM_HIDDEN } = require('../model/builtin').PERM;
|
||||
const problem = require('../model/problem');
|
||||
const record = require('../model/record');
|
||||
const contest = require('../model/contest');
|
||||
@@ -34,7 +34,7 @@ class RecordListHandler extends RecordHandler {
|
||||
);
|
||||
const [udict, pdict] = await Promise.all([
|
||||
user.getList(domainId, rdocs.map((rdoc) => rdoc.uid)),
|
||||
problem.getList(domainId, rdocs.map((rdoc) => rdoc.pid), false),
|
||||
problem.getList(domainId, rdocs.map((rdoc) => rdoc.pid), false, false),
|
||||
]);
|
||||
const path = [
|
||||
['Hydro', 'homepage'],
|
||||
|
||||
@@ -2,7 +2,7 @@ const domain = require('../model/domain');
|
||||
const system = require('../model/system');
|
||||
const setting = require('../model/setting');
|
||||
const { Route, Handler } = require('../service/server');
|
||||
const { PERM_MANAGE } = require('../permission');
|
||||
const { PERM_MANAGE } = require('../model/builtin').PERM;
|
||||
const hpm = require('../lib/hpm');
|
||||
const loader = require('../loader');
|
||||
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
const assert = require('assert');
|
||||
const { ValidationError, ProblemNotFoundError } = require('../error');
|
||||
const {
|
||||
PERM_LOGGEDIN, PERM_VIEW_TRAINING, PERM_VIEW_PROBLEM_HIDDEN,
|
||||
PERM_CREATE_TRAINING, PERM_EDIT_TRAINING,
|
||||
} = require('../permission');
|
||||
const paginate = require('../lib/paginate');
|
||||
const {
|
||||
PERM: {
|
||||
PERM_VIEW_TRAINING, PERM_VIEW_PROBLEM_HIDDEN, PERM_CREATE_TRAINING,
|
||||
PERM_EDIT_TRAINING,
|
||||
},
|
||||
PRIV: {
|
||||
PRIV_USER_PROFILE,
|
||||
},
|
||||
} = require('../model/builtin');
|
||||
const problem = require('../model/problem');
|
||||
const builtin = require('../model/builtin');
|
||||
const training = require('../model/training');
|
||||
@@ -70,7 +75,7 @@ class TrainingMainHandler extends TrainingHandler {
|
||||
for (const tdoc of tdocs) tids.add(tdoc.docId);
|
||||
const tsdict = {};
|
||||
let tdict = {};
|
||||
if (this.user.hasPerm(PERM_LOGGEDIN)) {
|
||||
if (this.user.hasPriv(PRIV_USER_PROFILE)) {
|
||||
const enrolledTids = new Set();
|
||||
const tsdocs = await training.getMultiStatus(domainId, {
|
||||
uid: this.user._id,
|
||||
@@ -101,10 +106,9 @@ class TrainingDetailHandler extends TrainingHandler {
|
||||
async get({ domainId, tid }) {
|
||||
const tdoc = await training.get(domainId, tid);
|
||||
const pids = training.getPids(tdoc);
|
||||
const f = this.user.hasPerm(PERM_VIEW_PROBLEM_HIDDEN) ? {} : { hidden: false };
|
||||
const [owner, pdict] = await Promise.all([
|
||||
user.getById(domainId, tdoc.owner),
|
||||
problem.getList(domainId, pids, f),
|
||||
problem.getList(domainId, pids, this.user.hasPerm(PERM_VIEW_PROBLEM_HIDDEN)),
|
||||
]);
|
||||
const psdict = await problem.getListStatus(domainId, this.user._id, pids);
|
||||
const donePids = new Set();
|
||||
@@ -151,7 +155,7 @@ class TrainingDetailHandler extends TrainingHandler {
|
||||
}
|
||||
|
||||
async postEnroll({ domainId, tid }) {
|
||||
this.checkPerm(PERM_LOGGEDIN);
|
||||
this.checkPriv(PRIV_USER_PROFILE);
|
||||
const tdoc = await training.get(domainId, tid);
|
||||
await training.enroll(domainId, tdoc.docId, this.user._id);
|
||||
this.back();
|
||||
|
||||
+23
-18
@@ -12,9 +12,16 @@ const misc = require('../lib/misc');
|
||||
const {
|
||||
UserAlreadyExistError, InvalidTokenError, VerifyPasswordError,
|
||||
UserNotFoundError, LoginError, SystemError,
|
||||
PermissionError, BlacklistedError, UserFacingError,
|
||||
BlacklistedError, UserFacingError,
|
||||
} = require('../error');
|
||||
const { PERM_LOGGEDIN } = require('../permission.js');
|
||||
const {
|
||||
PERM: {
|
||||
PERM_VIEW_PROBLEM_HIDDEN,
|
||||
},
|
||||
PRIV: {
|
||||
PRIV_USER_PROFILE, PRIV_REGISTER_USER,
|
||||
},
|
||||
} = require('../model/builtin');
|
||||
|
||||
class UserLoginHandler extends Handler {
|
||||
async get() {
|
||||
@@ -47,17 +54,11 @@ class UserLogoutHandler extends Handler {
|
||||
}
|
||||
|
||||
async post() {
|
||||
this.session.uid = 1;
|
||||
this.session.uid = 0;
|
||||
}
|
||||
}
|
||||
|
||||
class UserRegisterHandler extends Handler {
|
||||
async prepare() { // eslint-disable-line class-methods-use-this
|
||||
if (!await system.get('user.register')) {
|
||||
throw new PermissionError('Register');
|
||||
}
|
||||
}
|
||||
|
||||
async get() {
|
||||
this.response.template = 'user_register.html';
|
||||
}
|
||||
@@ -78,7 +79,7 @@ class UserRegisterHandler extends Handler {
|
||||
await sendMail(mail, 'Sign Up', 'user_register_mail', m);
|
||||
this.response.template = 'user_register_mail_sent.html';
|
||||
} else {
|
||||
this.response.redirect = `/register/${t[0]}`;
|
||||
this.response.redirect = this.url('user_register_with_code', { code: t[0] });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -103,7 +104,7 @@ class UserRegisterWithCodeHandler extends Handler {
|
||||
});
|
||||
await token.del(code, token.TYPE_REGISTRATION);
|
||||
this.session.uid = uid;
|
||||
this.response.redirect = '/';
|
||||
this.response.redirect = this.url('homepage');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,7 +143,7 @@ class UserLostPassWithCodeHandler extends Handler {
|
||||
if (password !== verifyPassword) throw new VerifyPasswordError();
|
||||
await user.setPassword(tdoc.uid, password);
|
||||
await token.del(code, token.TYPE_LOSTPASS);
|
||||
this.response.redirect = '/';
|
||||
this.response.redirect = this.url('homepage');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,9 +153,12 @@ class UserDetailHandler extends Handler {
|
||||
const udoc = await user.getById(domainId, uid, true);
|
||||
const sdoc = await token.getMostRecentSessionByUid(uid);
|
||||
const rdocs = await record.getByUid(domainId, uid);
|
||||
const pdict = await problem.getList(domainId, rdocs.map((rdoc) => rdoc.pid), false);
|
||||
const pdict = await problem.getList(
|
||||
domainId, rdocs.map((rdoc) => rdoc.pid),
|
||||
this.user.hasPerm(PERM_VIEW_PROBLEM_HIDDEN), false,
|
||||
);
|
||||
// Remove sensitive data
|
||||
if (!isSelfProfile) sdoc.createIp = sdoc.updateIp = sdoc._id = '';
|
||||
if (!isSelfProfile && sdoc) sdoc.createIp = sdoc.updateIp = sdoc._id = '';
|
||||
this.response.template = 'user_detail.html';
|
||||
this.response.body = {
|
||||
isSelfProfile, udoc, sdoc, rdocs, pdict,
|
||||
@@ -296,6 +300,7 @@ class OauthCallbackHandler extends Handler {
|
||||
if (udoc) {
|
||||
this.session.uid = udoc._id;
|
||||
} else {
|
||||
this.checkPriv(PRIV_REGISTER_USER);
|
||||
let username = '';
|
||||
r.uname = r.uname || [];
|
||||
r.uname.push(String.random(16));
|
||||
@@ -325,13 +330,13 @@ async function apply() {
|
||||
Route('user_login', '/login', UserLoginHandler);
|
||||
Route('user_oauth', '/oauth/:type', OauthHandler);
|
||||
Route('user_oauth_callback', '/oauth/:type/callback', OauthCallbackHandler);
|
||||
Route('user_register', '/register', UserRegisterHandler);
|
||||
Route('user_register_with_code', '/register/:code', UserRegisterWithCodeHandler);
|
||||
Route('user_logout', '/logout', UserLogoutHandler);
|
||||
Route('user_register', '/register', UserRegisterHandler, PRIV_REGISTER_USER);
|
||||
Route('user_register_with_code', '/register/:code', UserRegisterWithCodeHandler, PRIV_REGISTER_USER);
|
||||
Route('user_logout', '/logout', UserLogoutHandler, PRIV_USER_PROFILE);
|
||||
Route('user_lostpass', '/lostpass', UserLostPassHandler);
|
||||
Route('user_lostpass_with_code', '/lostpass/:code', UserLostPassWithCodeHandler);
|
||||
Route('user_search', '/user/search', UserSearchHandler);
|
||||
Route('user_delete', '/user/delete', UserDeleteHandler, PERM_LOGGEDIN);
|
||||
Route('user_delete', '/user/delete', UserDeleteHandler, PRIV_USER_PROFILE);
|
||||
Route('user_detail', '/user/:uid', UserDetailHandler);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ exports.udoc = {
|
||||
viewLang: 'zh_CN',
|
||||
loginat: new Date(),
|
||||
loginip: '0.0.0.0',
|
||||
priv: 0,
|
||||
priv: '',
|
||||
};
|
||||
|
||||
exports.pdoc = {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// From https://github.com/pvorb/node-md5/blob/master/md5.js
|
||||
// BSD 3-Clause "New" or "Revised" License
|
||||
|
||||
/* eslint-disable no-bitwise */ /* eslint-disable max-len */
|
||||
function safeAdd(x, y) {
|
||||
const lsw = (x & 0xffff) + (y & 0xffff);
|
||||
|
||||
+28
-6
@@ -1,13 +1,35 @@
|
||||
const permission = require('../permission');
|
||||
const { isNull } = require('lodash');
|
||||
|
||||
const permission = require('../model/builtin').PERM;
|
||||
|
||||
global.Hydro.ui.nav = [];
|
||||
|
||||
const trueChecker = () => true;
|
||||
const Item = (name, args, prefix, perm, checker) => {
|
||||
if (perm && checker) {
|
||||
checker = ((_chk) => (handler) => _chk(handler) && handler.user.hasPerm(perm))(checker);
|
||||
} else if (perm) {
|
||||
checker = (handler) => handler.user.hasPerm(perm);
|
||||
const Item = (name, args, prefix, ...permPrivChecker) => {
|
||||
let _priv;
|
||||
let _perm;
|
||||
let checker;
|
||||
for (const item of permPrivChecker) {
|
||||
if (typeof item === 'object' && !isNull(item)) {
|
||||
if (typeof item.call !== 'undefined') {
|
||||
checker = item;
|
||||
} if (typeof item[0] === 'number') {
|
||||
_priv = item;
|
||||
} else if (typeof item[0] === 'string') {
|
||||
_perm = item;
|
||||
}
|
||||
} else if (typeof item === 'number') {
|
||||
_priv = item;
|
||||
} else if (typeof item === 'string') {
|
||||
_perm = item;
|
||||
}
|
||||
}
|
||||
if ((_perm || _priv) && checker) {
|
||||
checker = ((_chk) => (handler) => _chk(handler)
|
||||
&& (_perm ? handler.user.hasPerm(_perm) : true)
|
||||
&& (_priv ? handler.user.hasPriv(_priv) : true))(checker);
|
||||
} else if (_perm) {
|
||||
checker = (handler) => handler.user.hasPerm(_perm);
|
||||
} else if (!checker) checker = trueChecker;
|
||||
global.Hydro.ui.nav.push({
|
||||
name, args: args || {}, prefix, checker,
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
const nunjucks = require('nunjucks');
|
||||
const markdown = require('./markdown');
|
||||
const perm = require('../permission');
|
||||
const builtin = require('../model/builtin');
|
||||
const model = require('../model');
|
||||
const { STATUS, PERM, PRIV } = require('../model/builtin');
|
||||
const misc = require('./misc');
|
||||
|
||||
function Loader() { }
|
||||
@@ -38,11 +36,12 @@ function render(name, state) {
|
||||
static_url: (str) => `/${str}`,
|
||||
datetimeSpan: misc.datetimeSpan,
|
||||
paginate: misc.paginate,
|
||||
perm,
|
||||
status: builtin.status,
|
||||
perm: PERM,
|
||||
PRIV,
|
||||
STATUS,
|
||||
size: misc.size,
|
||||
gravatar: misc.gravatar,
|
||||
model,
|
||||
model: global.Hydro.model,
|
||||
Context: global.Hydro.ui,
|
||||
isIE: (str) => str.includes('MSIE') || str.includes('rv:11.0'),
|
||||
}, (err, res) => {
|
||||
|
||||
+302
-5
@@ -1,9 +1,293 @@
|
||||
const perm = require('../permission');
|
||||
/* eslint-disable no-bitwise */
|
||||
const PERM = {
|
||||
PERM_NONE: '',
|
||||
|
||||
// Domain Settings
|
||||
PERM_VIEW: '0',
|
||||
PERM_EDIT_DOMAIN: '1',
|
||||
PERM_MOD_BADGE: '2',
|
||||
|
||||
// Problem and Record
|
||||
PERM_CREATE_PROBLEM: '3',
|
||||
PERM_EDIT_PROBLEM: '4',
|
||||
PERM_EDIT_PROBLEM_SELF: '5',
|
||||
PERM_VIEW_PROBLEM: '6',
|
||||
PERM_VIEW_PROBLEM_HIDDEN: '7',
|
||||
PERM_SUBMIT_PROBLEM: '8',
|
||||
PERM_READ_PROBLEM_DATA: '9',
|
||||
PERM_READ_PROBLEM_DATA_SELF: 'A',
|
||||
PERM_READ_RECORD_CODE: 'B',
|
||||
PERM_REJUDGE_PROBLEM: 'C',
|
||||
PERM_REJUDGE: 'D',
|
||||
|
||||
// Problem Solution
|
||||
PERM_VIEW_PROBLEM_SOLUTION: 'E',
|
||||
PERM_CREATE_PROBLEM_SOLUTION: 'F',
|
||||
PERM_VOTE_PROBLEM_SOLUTION: 'G',
|
||||
PERM_EDIT_PROBLEM_SOLUTION: 'H',
|
||||
PERM_EDIT_PROBLEM_SOLUTION_SELF: 'I',
|
||||
PERM_DELETE_PROBLEM_SOLUTION: 'J',
|
||||
PERM_DELETE_PROBLEM_SOLUTION_SELF: 'K',
|
||||
PERM_REPLY_PROBLEM_SOLUTION: 'L',
|
||||
PERM_EDIT_PROBLEM_SOLUTION_REPLY: 'M',
|
||||
PERM_EDIT_PROBLEM_SOLUTION_REPLY_SELF: 'N',
|
||||
PERM_DELETE_PROBLEM_SOLUTION_REPLY: 'O',
|
||||
PERM_DELETE_PROBLEM_SOLUTION_REPLY_SELF: 'P',
|
||||
|
||||
// Discussion
|
||||
PERM_VIEW_DISCUSSION: 'Q',
|
||||
PERM_CREATE_DISCUSSION: 'R',
|
||||
PERM_HIGHLIGHT_DISCUSSION: 'S',
|
||||
PERM_EDIT_DISCUSSION: 'T',
|
||||
PERM_EDIT_DISCUSSION_SELF: 'U',
|
||||
PERM_DELETE_DISCUSSION: 'V',
|
||||
PERM_DELETE_DISCUSSION_SELF: 'W',
|
||||
PERM_REPLY_DISCUSSION: 'X',
|
||||
PERM_EDIT_DISCUSSION_REPLY: 'Y',
|
||||
PERM_EDIT_DISCUSSION_REPLY_SELF: 'Z',
|
||||
PERM_EDIT_DISCUSSION_REPLY_SELF_DISCUSSION: 'a',
|
||||
PERM_DELETE_DISCUSSION_REPLY: 'b',
|
||||
PERM_DELETE_DISCUSSION_REPLY_SELF: 'c',
|
||||
PERM_DELETE_DISCUSSION_REPLY_SELF_DISCUSSION: 'd',
|
||||
|
||||
// Contest
|
||||
PERM_VIEW_CONTEST: 'e',
|
||||
PERM_VIEW_CONTEST_SCOREBOARD: 'f',
|
||||
PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD: 'g',
|
||||
PERM_CREATE_CONTEST: 'h',
|
||||
PERM_ATTEND_CONTEST: 'i',
|
||||
PERM_EDIT_CONTEST: 'j',
|
||||
PERM_EDIT_CONTEST_SELF: 'k',
|
||||
|
||||
// Homework
|
||||
PERM_VIEW_HOMEWORK: 'l',
|
||||
PERM_VIEW_HOMEWORK_SCOREBOARD: 'm',
|
||||
PERM_VIEW_HOMEWORK_HIDDEN_SCOREBOARD: 'n',
|
||||
PERM_CREATE_HOMEWORK: 'o',
|
||||
PERM_ATTEND_HOMEWORK: 'p',
|
||||
PERM_EDIT_HOMEWORK: 'q',
|
||||
PERM_EDIT_HOMEWORK_SELF: 'r',
|
||||
|
||||
// Training
|
||||
PERM_VIEW_TRAINING: 's',
|
||||
PERM_CREATE_TRAINING: 't',
|
||||
PERM_EDIT_TRAINING: 'u',
|
||||
PERM_EDIT_TRAINING_SELF: 'v',
|
||||
};
|
||||
|
||||
const Permission = (family, key, desc) => ({ family, key, desc });
|
||||
|
||||
const PERMS = [
|
||||
Permission('perm_general', PERM.PERM_VIEW, 'View this domain'),
|
||||
Permission('perm_general', PERM.PERM_EDIT_DOMAIN, 'Edit domain settings'),
|
||||
Permission('perm_general', PERM.PERM_MOD_BADGE, 'Show MOD badge'),
|
||||
Permission('perm_problem', PERM.PERM_CREATE_PROBLEM, 'Create problems'),
|
||||
Permission('perm_problem', PERM.PERM_EDIT_PROBLEM, 'Edit problems'),
|
||||
Permission('perm_problem', PERM.PERM_EDIT_PROBLEM_SELF, 'Edit own problems'),
|
||||
Permission('perm_problem', PERM.PERM_VIEW_PROBLEM, 'View problems'),
|
||||
Permission('perm_problem', PERM.PERM_VIEW_PROBLEM_HIDDEN, 'View hidden problems'),
|
||||
Permission('perm_problem', PERM.PERM_SUBMIT_PROBLEM, 'Submit problem'),
|
||||
Permission('perm_problem', PERM.PERM_READ_PROBLEM_DATA, 'Read data of problem'),
|
||||
Permission('perm_problem', PERM.PERM_READ_PROBLEM_DATA_SELF, 'Read data of own problems'),
|
||||
Permission('perm_record', PERM.PERM_READ_RECORD_CODE, 'Read record codes'),
|
||||
Permission('perm_record', PERM.PERM_REJUDGE_PROBLEM, 'Rejudge problems'),
|
||||
Permission('perm_record', PERM.PERM_REJUDGE, 'Rejudge records'),
|
||||
Permission('perm_problem_solution', PERM.PERM_VIEW_PROBLEM_SOLUTION, 'View problem solutions'),
|
||||
Permission('perm_problem_solution', PERM.PERM_CREATE_PROBLEM_SOLUTION, 'Create problem solutions'),
|
||||
Permission('perm_problem_solution', PERM.PERM_VOTE_PROBLEM_SOLUTION, 'Vote problem solutions'),
|
||||
Permission('perm_problem_solution', PERM.PERM_EDIT_PROBLEM_SOLUTION, 'Edit problem solutions'),
|
||||
Permission('perm_problem_solution', PERM.PERM_EDIT_PROBLEM_SOLUTION_SELF, 'Edit own problem solutions'),
|
||||
Permission('perm_problem_solution', PERM.PERM_DELETE_PROBLEM_SOLUTION, 'Delete problem solutions'),
|
||||
Permission('perm_problem_solution', PERM.PERM_DELETE_PROBLEM_SOLUTION_SELF, 'Delete own problem solutions'),
|
||||
Permission('perm_problem_solution', PERM.PERM_REPLY_PROBLEM_SOLUTION, 'Reply problem solutions'),
|
||||
Permission('perm_problem_solution', PERM.PERM_EDIT_PROBLEM_SOLUTION_REPLY, 'Edit problem solution replies'),
|
||||
Permission('perm_problem_solution', PERM.PERM_EDIT_PROBLEM_SOLUTION_REPLY_SELF, 'Edit own problem solution replies'),
|
||||
Permission('perm_problem_solution', PERM.PERM_DELETE_PROBLEM_SOLUTION_REPLY, 'Delete problem solution replies'),
|
||||
Permission('perm_problem_solution', PERM.PERM_DELETE_PROBLEM_SOLUTION_REPLY_SELF, 'Delete own problem solution replies'),
|
||||
Permission('perm_discussion', PERM.PERM_VIEW_DISCUSSION, 'View discussions'),
|
||||
Permission('perm_discussion', PERM.PERM_CREATE_DISCUSSION, 'Create discussions'),
|
||||
Permission('perm_discussion', PERM.PERM_HIGHLIGHT_DISCUSSION, 'Highlight discussions'),
|
||||
Permission('perm_discussion', PERM.PERM_EDIT_DISCUSSION, 'Edit discussions'),
|
||||
Permission('perm_discussion', PERM.PERM_EDIT_DISCUSSION_SELF, 'Edit own discussions'),
|
||||
Permission('perm_discussion', PERM.PERM_DELETE_DISCUSSION, 'Delete discussions'),
|
||||
Permission('perm_discussion', PERM.PERM_DELETE_DISCUSSION_SELF, 'Delete own discussions'),
|
||||
Permission('perm_discussion', PERM.PERM_REPLY_DISCUSSION, 'Reply discussions'),
|
||||
Permission('perm_discussion', PERM.PERM_EDIT_DISCUSSION_REPLY, 'Edit discussion replies'),
|
||||
Permission('perm_discussion', PERM.PERM_EDIT_DISCUSSION_REPLY_SELF, 'Edit own discussion replies'),
|
||||
Permission('perm_discussion', PERM.PERM_EDIT_DISCUSSION_REPLY_SELF_DISCUSSION, 'Edit discussion replies of own discussion'),
|
||||
Permission('perm_discussion', PERM.PERM_DELETE_DISCUSSION_REPLY, 'Delete discussion replies'),
|
||||
Permission('perm_discussion', PERM.PERM_DELETE_DISCUSSION_REPLY_SELF, 'Delete own discussion replies'),
|
||||
Permission('perm_discussion', PERM.PERM_DELETE_DISCUSSION_REPLY_SELF_DISCUSSION, 'Delete discussion replies of own discussion'),
|
||||
Permission('perm_contest', PERM.PERM_VIEW_CONTEST, 'View contests'),
|
||||
Permission('perm_contest', PERM.PERM_VIEW_CONTEST_SCOREBOARD, 'View contest scoreboard'),
|
||||
Permission('perm_contest', PERM.PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD, 'View hidden contest submission status and scoreboard'),
|
||||
Permission('perm_contest', PERM.PERM_CREATE_CONTEST, 'Create contests'),
|
||||
Permission('perm_contest', PERM.PERM_ATTEND_CONTEST, 'Attend contests'),
|
||||
Permission('perm_contest', PERM.PERM_EDIT_CONTEST, 'Edit any contests'),
|
||||
Permission('perm_contest', PERM.PERM_EDIT_CONTEST_SELF, 'Edit own contests'),
|
||||
Permission('perm_homework', PERM.PERM_VIEW_HOMEWORK, 'View homework'),
|
||||
Permission('perm_homework', PERM.PERM_VIEW_HOMEWORK_SCOREBOARD, 'View homework scoreboard'),
|
||||
Permission('perm_homework', PERM.PERM_VIEW_HOMEWORK_HIDDEN_SCOREBOARD, 'View hidden homework submission status and scoreboard'),
|
||||
Permission('perm_homework', PERM.PERM_CREATE_HOMEWORK, 'Create homework'),
|
||||
Permission('perm_homework', PERM.PERM_ATTEND_HOMEWORK, 'Claim homework'),
|
||||
Permission('perm_homework', PERM.PERM_EDIT_HOMEWORK, 'Edit any homework'),
|
||||
Permission('perm_homework', PERM.PERM_EDIT_HOMEWORK_SELF, 'Edit own homework'),
|
||||
Permission('perm_training', PERM.PERM_VIEW_TRAINING, 'View training plans'),
|
||||
Permission('perm_training', PERM.PERM_CREATE_TRAINING, 'Create training plans'),
|
||||
Permission('perm_training', PERM.PERM_EDIT_TRAINING, 'Edit training plans'),
|
||||
Permission('perm_training', PERM.PERM_EDIT_TRAINING_SELF, 'Edit own training plans'),
|
||||
Permission('perm_ranking', PERM.PERM_VIEW_RANKING, 'View ranking'),
|
||||
];
|
||||
|
||||
let PERM_ALL = '';
|
||||
for (const p in PERM) PERM_ALL += PERM[p];
|
||||
PERM.PERM_ALL = PERM_ALL;
|
||||
|
||||
const PERMS_BY_FAMILY = {};
|
||||
for (const p of PERMS) {
|
||||
if (!PERMS_BY_FAMILY[p.family]) PERMS_BY_FAMILY[p.family] = [p];
|
||||
else PERMS_BY_FAMILY[p.family].push(p);
|
||||
}
|
||||
|
||||
PERM.PERM_BASIC = PERM.PERM_VIEW
|
||||
+ PERM.PERM_VIEW_PROBLEM
|
||||
+ PERM.PERM_VIEW_PROBLEM_SOLUTION
|
||||
+ PERM.PERM_VIEW_DISCUSSION
|
||||
+ PERM.PERM_VIEW_CONTEST
|
||||
+ PERM.PERM_VIEW_CONTEST_SCOREBOARD
|
||||
+ PERM.PERM_VIEW_HOMEWORK
|
||||
+ PERM.PERM_VIEW_HOMEWORK_SCOREBOARD
|
||||
+ PERM.PERM_VIEW_TRAINING;
|
||||
|
||||
PERM.PERM_DEFAULT = PERM.PERM_VIEW
|
||||
+ PERM.PERM_VIEW_PROBLEM
|
||||
+ PERM.PERM_EDIT_PROBLEM_SELF
|
||||
+ PERM.PERM_SUBMIT_PROBLEM
|
||||
+ PERM.PERM_READ_PROBLEM_DATA_SELF
|
||||
+ PERM.PERM_VIEW_PROBLEM_SOLUTION
|
||||
+ PERM.PERM_CREATE_PROBLEM_SOLUTION
|
||||
+ PERM.PERM_VOTE_PROBLEM_SOLUTION
|
||||
+ PERM.PERM_EDIT_PROBLEM_SOLUTION_SELF
|
||||
+ PERM.PERM_DELETE_PROBLEM_SOLUTION_SELF
|
||||
+ PERM.PERM_REPLY_PROBLEM_SOLUTION
|
||||
+ PERM.PERM_EDIT_PROBLEM_SOLUTION_REPLY_SELF
|
||||
+ PERM.PERM_DELETE_PROBLEM_SOLUTION_REPLY_SELF
|
||||
+ PERM.PERM_VIEW_DISCUSSION
|
||||
+ PERM.PERM_CREATE_DISCUSSION
|
||||
+ PERM.PERM_EDIT_DISCUSSION_SELF
|
||||
+ PERM.PERM_REPLY_DISCUSSION
|
||||
+ PERM.PERM_EDIT_DISCUSSION_REPLY_SELF
|
||||
// PERM_EDIT_DISCUSSION_REPLY_SELF_DISCUSSION
|
||||
+ PERM.PERM_DELETE_DISCUSSION_REPLY_SELF
|
||||
+ PERM.PERM_DELETE_DISCUSSION_REPLY_SELF_DISCUSSION
|
||||
+ PERM.PERM_VIEW_CONTEST
|
||||
+ PERM.PERM_VIEW_CONTEST_SCOREBOARD
|
||||
+ PERM.PERM_ATTEND_CONTEST
|
||||
+ PERM.PERM_EDIT_CONTEST_SELF
|
||||
+ PERM.PERM_VIEW_HOMEWORK
|
||||
+ PERM.PERM_VIEW_HOMEWORK_SCOREBOARD
|
||||
+ PERM.PERM_ATTEND_HOMEWORK
|
||||
+ PERM.PERM_EDIT_HOMEWORK_SELF
|
||||
+ PERM.PERM_VIEW_TRAINING
|
||||
+ PERM.PERM_CREATE_TRAINING
|
||||
+ PERM.PERM_EDIT_TRAINING_SELF
|
||||
+ PERM.PERM_VIEW_RANKING
|
||||
+ PERM.PERM_SUBMIT_PROBLEM
|
||||
+ PERM.PERM_CREATE_PROBLEM_SOLUTION
|
||||
+ PERM.PERM_VOTE_PROBLEM_SOLUTION
|
||||
+ PERM.PERM_REPLY_PROBLEM_SOLUTION
|
||||
+ PERM.PERM_CREATE_DISCUSSION
|
||||
+ PERM.PERM_REPLY_DISCUSSION
|
||||
+ PERM.PERM_ATTEND_CONTEST
|
||||
+ PERM.PERM_CREATE_TRAINING
|
||||
+ PERM.PERM_ATTEND_HOMEWORK;
|
||||
|
||||
PERM.PERM_ADMIN = PERM.PERM_ALL;
|
||||
|
||||
const PRIV = {
|
||||
PRIV_NONE: 0,
|
||||
PRIV_SET_PRIV: 1 << 0,
|
||||
PRIV_SET_PERM: 1 << 1,
|
||||
PRIV_USER_PROFILE: 1 << 2,
|
||||
PRIV_REGISTER_USER: 1 << 3,
|
||||
PRIV_READ_PROBLEM_DATA: 1 << 4,
|
||||
PRIV_READ_PRETEST_DATA: 1 << 5, // deprecated
|
||||
PRIV_READ_PRETEST_DATA_SELF: 1 << 6, // deprecated
|
||||
PRIV_READ_RECORD_CODE: 1 << 7,
|
||||
PRIV_VIEW_HIDDEN_RECORD: 1 << 8,
|
||||
PRIV_JUDGE: 1 << 9, // (renamed)
|
||||
PRIV_CREATE_DOMAIN: 1 << 10,
|
||||
PRIV_VIEW_ALL_DOMAIN: 1 << 11,
|
||||
PRIV_MANAGE_ALL_DOMAIN: 1 << 12,
|
||||
PRIV_REJUDGE: 1 << 13,
|
||||
PRIV_VIEW_USER_SECRET: 1 << 14,
|
||||
PRIV_VIEW_JUDGE_STATISTICS: 1 << 15,
|
||||
PRIV_CREATE_FILE: 1 << 16,
|
||||
PRIV_UNLIMITED_QUOTA: 1 << 17,
|
||||
PRIV_DELETE_FILE: 1 << 18,
|
||||
PRIV_DELETE_FILE_SELF: 1 << 19,
|
||||
PRIV_ALL: -1,
|
||||
};
|
||||
|
||||
PRIV.PRIV_DETAULT = PRIV.PRIV_USER_PROFILE
|
||||
+ PRIV.PRIV_REGISTER_USER
|
||||
+ PRIV.PRIV_READ_PRETEST_DATA_SELF
|
||||
+ PRIV.PRIV_CREATE_DOMAIN
|
||||
+ PRIV.PRIV_CREATE_FILE
|
||||
+ PRIV.PRIV_DELETE_FILE_SELF
|
||||
+ PRIV.PRIV_USERFILE;
|
||||
|
||||
// [10, 1] means that people whose rank is less than 1% will get Level 10
|
||||
const LEVELS = [
|
||||
[10, 1],
|
||||
[9, 2],
|
||||
[8, 10],
|
||||
[7, 20],
|
||||
[6, 30],
|
||||
[5, 40],
|
||||
[4, 70],
|
||||
[3, 90],
|
||||
[2, 95],
|
||||
[1, 100],
|
||||
];
|
||||
|
||||
const BUILTIN_USERS = [
|
||||
{
|
||||
_id: 0,
|
||||
mail: 'Guest@hydro.local',
|
||||
mailLower: 'guest@hydro.local',
|
||||
uname: 'Guest',
|
||||
unameLower: 'guest',
|
||||
salt: '',
|
||||
hash: '',
|
||||
hashType: 'hydro',
|
||||
regat: new Date(0),
|
||||
regip: '127.0.0.1',
|
||||
loginat: new Date(0),
|
||||
loginip: '127.0.0.1',
|
||||
priv: PRIV.PRIV_NONE,
|
||||
},
|
||||
{
|
||||
_id: 1,
|
||||
mail: 'Hydro@hydro.local',
|
||||
mailLower: 'hydro@hydro.local',
|
||||
uname: 'Hydro',
|
||||
unameLower: 'hydro',
|
||||
salt: '',
|
||||
hash: '',
|
||||
hashType: 'hydro',
|
||||
regat: new Date(0),
|
||||
regip: '127.0.0.1',
|
||||
loginat: new Date(0),
|
||||
loginip: '127.0.0.1',
|
||||
priv: PRIV.PRIV_USER_PROFILE,
|
||||
},
|
||||
];
|
||||
|
||||
const BUILTIN_ROLES = {
|
||||
guest: { perm: perm.PERM_BASIC },
|
||||
default: { perm: perm.PERM_DEFAULT },
|
||||
admin: { perm: perm.PERM_ADMIN },
|
||||
guest: { perm: PERM.PERM_BASIC },
|
||||
default: { perm: PERM.PERM_DEFAULT },
|
||||
admin: { perm: PERM.PERM_ADMIN },
|
||||
};
|
||||
|
||||
const DEFAULT_NODES = {
|
||||
@@ -13,7 +297,7 @@ const DEFAULT_NODES = {
|
||||
{ pic: 'solution', name: '题解' },
|
||||
],
|
||||
Vijos: [
|
||||
{ pic: 'vijos', name: 'Vijos' },
|
||||
{ pic: 'hydro', name: 'Hydro' },
|
||||
{ pic: null, name: '团队' },
|
||||
{ pic: null, name: '月赛' },
|
||||
{ pic: 'advice', name: '建议' },
|
||||
@@ -201,12 +485,15 @@ const CATEGORIES = {
|
||||
'构造',
|
||||
],
|
||||
};
|
||||
|
||||
const FOOTER_EXTRA_HTMLS = [];
|
||||
|
||||
const VIEW_LANGS = [
|
||||
{ code: 'zh_CN', name: '简体中文' },
|
||||
{ code: 'zh_TW', name: '正體中文' },
|
||||
{ code: 'en', name: 'English' },
|
||||
];
|
||||
|
||||
const LANG_TEXTS = {
|
||||
c: 'C',
|
||||
cc: 'C++',
|
||||
@@ -222,6 +509,7 @@ const LANG_TEXTS = {
|
||||
go: 'Go',
|
||||
rb: 'Ruby',
|
||||
};
|
||||
|
||||
const LANG_HIGHLIGHT_ID = {
|
||||
c: 'c',
|
||||
cc: 'cpp',
|
||||
@@ -237,6 +525,7 @@ const LANG_HIGHLIGHT_ID = {
|
||||
go: 'go',
|
||||
rb: 'ruby',
|
||||
};
|
||||
|
||||
const STATUS = {
|
||||
STATUS_WAITING: 0,
|
||||
STATUS_ACCEPTED: 1,
|
||||
@@ -254,6 +543,7 @@ const STATUS = {
|
||||
STATUS_FETCHED: 22,
|
||||
STATUS_IGNORED: 30,
|
||||
};
|
||||
|
||||
const STATUS_TEXTS = {
|
||||
[STATUS.STATUS_WAITING]: 'Waiting',
|
||||
[STATUS.STATUS_ACCEPTED]: 'Accepted',
|
||||
@@ -271,6 +561,7 @@ const STATUS_TEXTS = {
|
||||
[STATUS.STATUS_FETCHED]: 'Fetched',
|
||||
[STATUS.STATUS_IGNORED]: 'Ignored',
|
||||
};
|
||||
|
||||
const STATUS_CODES = {
|
||||
0: 'pending',
|
||||
1: 'pass',
|
||||
@@ -290,6 +581,12 @@ const STATUS_CODES = {
|
||||
};
|
||||
|
||||
global.Hydro.model.builtin = module.exports = {
|
||||
PERM,
|
||||
PERMS,
|
||||
PERMS_BY_FAMILY,
|
||||
PRIV,
|
||||
LEVELS,
|
||||
BUILTIN_USERS,
|
||||
BUILTIN_ROLES,
|
||||
DEFAULT_NODES,
|
||||
CATEGORIES,
|
||||
|
||||
@@ -4,7 +4,7 @@ const {
|
||||
ValidationError, ContestNotFoundError, ContestAlreadyAttendedError,
|
||||
ContestNotAttendedError, ContestScoreboardHiddenError,
|
||||
} = require('../error');
|
||||
const { PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD } = require('../permission');
|
||||
const { PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD } = require('./builtin').PERM;
|
||||
const validator = require('../lib/validator');
|
||||
const ranked = require('../lib/rank');
|
||||
const document = require('./document');
|
||||
@@ -339,7 +339,7 @@ const ContestHandlerMixin = (c) => class extends c {
|
||||
for (const tsdoc of tsdocs) uids.push(tsdoc.uid);
|
||||
const [udict, pdict] = await Promise.all([
|
||||
user.getList(domainId, uids),
|
||||
problem.getList(domainId, tdoc.pids),
|
||||
problem.getList(domainId, tdoc.pids, true),
|
||||
]);
|
||||
const rankedTsdocs = RULES[tdoc.rule].rank(tsdocs);
|
||||
const rows = RULES[tdoc.rule].scoreboard(isExport, (str) => (str ? str.toString().translate(this.user.language) : ''), tdoc, rankedTsdocs, udict, pdict);
|
||||
|
||||
@@ -3,7 +3,7 @@ const problem = require('./problem');
|
||||
const document = require('./document');
|
||||
const contest = require('./contest');
|
||||
const training = require('./training');
|
||||
const { PERM_VIEW_PROBLEM_HIDDEN } = require('../permission');
|
||||
const { PERM_VIEW_PROBLEM_HIDDEN } = require('./builtin').PERM;
|
||||
const { DocumentNotFoundError } = require('../error');
|
||||
|
||||
const typeDisplay = {
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
module.exports = {
|
||||
blacklist: require('./blacklist'),
|
||||
builtin: require('./builtin'),
|
||||
contest: require('./contest'),
|
||||
document: require('./document'),
|
||||
opcount: require('./opcount'),
|
||||
problem: require('./problem'),
|
||||
record: require('./record'),
|
||||
solution: require('./solution'),
|
||||
system: require('./system'),
|
||||
token: require('./token'),
|
||||
training: require('./training'),
|
||||
user: require('./user'),
|
||||
task: require('./task'),
|
||||
message: require('./message'),
|
||||
setting: require('./setting'),
|
||||
discussion: require('./discussion'),
|
||||
file: require('./file'),
|
||||
};
|
||||
+65
-21
@@ -4,6 +4,7 @@ const document = require('./document');
|
||||
const domain = require('./domain');
|
||||
const { ProblemNotFoundError } = require('../error');
|
||||
const readConfig = require('../lib/readConfig');
|
||||
const { ObjectId } = require('bson');
|
||||
|
||||
/**
|
||||
* @typedef {import('../interface').Pdoc} Pdoc
|
||||
@@ -11,16 +12,50 @@ const readConfig = require('../lib/readConfig');
|
||||
* @typedef {import('mongodb').Cursor} Cursor
|
||||
*/
|
||||
|
||||
class Problem {
|
||||
constructor(pdoc) {
|
||||
this.docType = document.TYPE_PROBLEM;
|
||||
if (pdoc) {
|
||||
this._id = pdoc._id;
|
||||
this.docId = pdoc.docId;
|
||||
this.pid = pdoc.pid;
|
||||
this.title = pdoc.title;
|
||||
this.content = pdoc.content;
|
||||
this.owner = pdoc.owner;
|
||||
this.config = pdoc.config;
|
||||
this.data = pdoc.data;
|
||||
this.nSubmit = pdoc.nSubmit;
|
||||
this.nAccept = pdoc.nAccept;
|
||||
this.difficulty = pdoc.difficulty || 0;
|
||||
this.tag = pdoc.tag || [];
|
||||
this.category = pdoc.category || [];
|
||||
this.hidden = pdoc.hidden || false;
|
||||
} else {
|
||||
this._id = new ObjectId();
|
||||
this.docId = this._id;
|
||||
this.pid = String.random(8);
|
||||
this.title = '*';
|
||||
this.content = '';
|
||||
this.owner = 1;
|
||||
this.config = '';
|
||||
this.data = null;
|
||||
this.nSubmit = 0;
|
||||
this.nAccept = 0;
|
||||
this.difficulty = 0;
|
||||
this.tag = [];
|
||||
this.category = [];
|
||||
this.hidden = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} domainId
|
||||
* @param {string} title
|
||||
* @param {string} content
|
||||
* @param {number} owner
|
||||
* @param {number} pid
|
||||
* @param {import('bson').ObjectID} data
|
||||
* @param {string[]} category
|
||||
* @param {string[]} tag
|
||||
* @param {boolean} hidden
|
||||
* @param {object} args
|
||||
* @returns {Promise<ObjectID>} docId
|
||||
*/
|
||||
async function add(domainId, title, content, owner, {
|
||||
pid = null,
|
||||
@@ -43,7 +78,7 @@ async function add(domainId, title, content, owner, {
|
||||
* @param {string} domainId
|
||||
* @param {string|number} pid
|
||||
* @param {number} uid
|
||||
* @returns {Promise<Pdoc>}
|
||||
* @returns {Promise<Problem>}
|
||||
*/
|
||||
async function get(domainId, pid, uid = null, doThrow = true) {
|
||||
if (!Number.isNaN(parseInt(pid, 10))) pid = parseInt(pid, 10);
|
||||
@@ -57,7 +92,7 @@ async function get(domainId, pid, uid = null, doThrow = true) {
|
||||
if (uid) {
|
||||
pdoc.psdoc = await document.getStatus(domainId, document.TYPE_PROBLEM, pdoc.docId, uid);
|
||||
}
|
||||
return pdoc;
|
||||
return new Problem(pdoc);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,11 +101,16 @@ async function get(domainId, pid, uid = null, doThrow = true) {
|
||||
* @param {object} sort
|
||||
* @param {number} page
|
||||
* @param {number} limit
|
||||
* @returns {Promise<Pdoc[]>}
|
||||
* @returns {Promise<Problem[]>}
|
||||
*/
|
||||
function getMany(domainId, query, sort, page, limit) {
|
||||
return document.getMulti(domainId, query).sort(sort).skip((page - 1) * limit).limit(limit)
|
||||
async function getMany(domainId, query, sort, page, limit) {
|
||||
const pdocs = await document.getMulti(domainId, query)
|
||||
.sort(sort).skip((page - 1) * limit).limit(limit)
|
||||
.toArray();
|
||||
for (let i = 0; i < pdocs.length; i++) {
|
||||
pdocs[i] = new Problem(pdocs[i]);
|
||||
}
|
||||
return pdocs;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,10 +135,11 @@ function getMultiStatus(domainId, query) {
|
||||
* @param {string} domainId
|
||||
* @param {ObjectID} _id
|
||||
* @param {object} query
|
||||
* @returns {Promise<Pdoc>}
|
||||
* @returns {Promise<Problem>}
|
||||
*/
|
||||
function edit(domainId, _id, $set) {
|
||||
return document.set(domainId, document.TYPE_PROBLEM, _id, $set);
|
||||
async function edit(domainId, _id, $set) {
|
||||
const pdoc = await document.set(domainId, document.TYPE_PROBLEM, _id, $set);
|
||||
return new Problem(pdoc);
|
||||
}
|
||||
|
||||
function inc(domainId, _id, field, n) {
|
||||
@@ -118,20 +159,23 @@ async function random(domainId, query) {
|
||||
} return null;
|
||||
}
|
||||
|
||||
async function getList(domainId, pids, doThrow = true) {
|
||||
async function getList(domainId, pids, getHidden = false, doThrow = true) {
|
||||
pids = Array.from(new Set(pids));
|
||||
const r = {};
|
||||
const pdocs = await document.getMulti(
|
||||
domainId, document.TYPE_PROBLEM,
|
||||
{ $or: [{ docId: { $in: pids } }, { pid: { $in: pids } }] },
|
||||
).toArray();
|
||||
const q = { $or: [{ docId: { $in: pids } }, { pid: { $in: pids } }] };
|
||||
if (!getHidden) q.hidden = false;
|
||||
const pdocs = await document.getMulti(domainId, document.TYPE_PROBLEM, q).toArray();
|
||||
for (const pdoc of pdocs) {
|
||||
r[pdoc.docId] = r[pdoc.pid] = pdoc;
|
||||
}
|
||||
if (pdocs.length !== pids.length) {
|
||||
if (doThrow) {
|
||||
for (const pid of pids) {
|
||||
if (!r[pid]) throw new ProblemNotFoundError(domainId, pid);
|
||||
for (const pid of pids) {
|
||||
if (!r[pid]) {
|
||||
if (doThrow) {
|
||||
throw new ProblemNotFoundError(domainId, pid);
|
||||
} else {
|
||||
r[pid] = new Problem();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,8 +88,6 @@ DomainSetting(
|
||||
);
|
||||
|
||||
SystemSetting(
|
||||
Setting('setting_basic', 'user.register', null, false, 'checkbox', 'Allow register'),
|
||||
Setting('setting_basic', 'user.create_domain', null, true, 'checkbox', 'Allow domain creation'),
|
||||
Setting('setting_smtp', 'smtp.user', null, null, 'text', 'SMTP Username'),
|
||||
Setting('setting_smtp', 'smtp.pass', null, null, 'password', 'SMTP Password'),
|
||||
Setting('setting_smtp', 'smtp.host', null, null, 'text', 'SMTP Server Host'),
|
||||
|
||||
+28
-7
@@ -4,9 +4,9 @@ const system = require('./system');
|
||||
const token = require('./token');
|
||||
const setting = require('./setting');
|
||||
const { UserNotFoundError, UserAlreadyExistError, LoginError } = require('../error');
|
||||
const perm = require('../permission');
|
||||
const pwhash = require('../lib/hash.hydro');
|
||||
const db = require('../service/db');
|
||||
const { PRIV } = require('./builtin');
|
||||
|
||||
const coll = db.collection('user');
|
||||
|
||||
@@ -42,6 +42,11 @@ class USER {
|
||||
return this.perm.includes(p);
|
||||
}
|
||||
|
||||
hasPriv(p) {
|
||||
// eslint-disable-next-line no-bitwise
|
||||
return this.priv & p;
|
||||
}
|
||||
|
||||
checkPassword(password) {
|
||||
const h = global.Hydro.lib[`hash.${this.hashType || 'hydro'}`];
|
||||
if (!h) throw new Error('Unknown hash method');
|
||||
@@ -55,14 +60,17 @@ async function getInDomain(domainId, udoc) {
|
||||
let dudoc = await document.getStatus(domainId, document.TYPE_DOMAIN_USER, 0, udoc._id);
|
||||
dudoc = dudoc || {};
|
||||
if (udoc._id === 1) dudoc.role = 'guest';
|
||||
if (udoc.priv === 1) dudoc.role = 'admin';
|
||||
// eslint-disable-next-line no-bitwise
|
||||
if (udoc.priv & PRIV.PRIV_MANAGE_ALL_DOMAIN) dudoc.role = 'admin';
|
||||
const p = await document.get(domainId, document.TYPE_DOMAIN_USER, dudoc.role || 'default');
|
||||
dudoc.perm = p ? p.content : builtin.BUILTIN_ROLES[dudoc.role || 'default'].perm;
|
||||
return dudoc;
|
||||
}
|
||||
|
||||
async function getById(domainId, _id, throwError = false) {
|
||||
const udoc = await coll.findOne({ _id });
|
||||
const udoc = _id === 0 || _id === 1
|
||||
? builtin.BUILTIN_USERS[_id]
|
||||
: await coll.findOne({ _id });
|
||||
if (!udoc) {
|
||||
if (throwError) throw new UserNotFoundError(_id);
|
||||
else return null;
|
||||
@@ -81,7 +89,11 @@ async function getList(domainId, uids) {
|
||||
|
||||
async function getByUname(domainId, uname, ignoreMissing = false) {
|
||||
const unameLower = uname.trim().toLowerCase();
|
||||
const udoc = await coll.findOne({ unameLower });
|
||||
const udoc = (unameLower === 'guest')
|
||||
? builtin.BUILTIN_USERS[0]
|
||||
: unameLower === 'hydro'
|
||||
? builtin.BUILTIN_USERS[1]
|
||||
: await coll.findOne({ unameLower });
|
||||
if (!udoc) {
|
||||
if (ignoreMissing) return null;
|
||||
throw new UserNotFoundError(uname);
|
||||
@@ -92,7 +104,11 @@ async function getByUname(domainId, uname, ignoreMissing = false) {
|
||||
|
||||
async function getByEmail(domainId, mail, ignoreMissing = false) {
|
||||
const mailLower = mail.trim().toLowerCase();
|
||||
const udoc = await coll.findOne({ mailLower });
|
||||
const udoc = (mailLower === 'guest@hydro.local')
|
||||
? builtin.BUILTIN_USERS[0]
|
||||
: mailLower === 'hydro@hydro.local'
|
||||
? builtin.BUILTIN_USERS[1]
|
||||
: await coll.findOne({ mailLower });
|
||||
if (!udoc) {
|
||||
if (ignoreMissing) return null;
|
||||
throw new UserNotFoundError(mail);
|
||||
@@ -121,7 +137,7 @@ function setEmail(uid, mail) {
|
||||
}
|
||||
|
||||
async function changePassword(uid, currentPassword, newPassword) {
|
||||
const udoc = await getById(uid);
|
||||
const udoc = await getById('system', uid);
|
||||
udoc.checkPassword(currentPassword);
|
||||
const salt = String.random();
|
||||
return await coll.findOneAndUpdate(
|
||||
@@ -153,7 +169,7 @@ async function incDomain(domainId, uid, field, n = 1) {
|
||||
* @returns {Promise<number>} uid
|
||||
*/
|
||||
async function create({
|
||||
uid, mail, uname, password, regip = '127.0.0.1', priv = perm.PRIV_NONE,
|
||||
uid, mail, uname, password, regip = '127.0.0.1', priv = PRIV.PRIV_DEFAULT,
|
||||
}) {
|
||||
const salt = String.random();
|
||||
if (!uid) uid = await system.inc('user');
|
||||
@@ -195,6 +211,10 @@ async function getPrefixList(prefix, limit = 50) {
|
||||
return udocs;
|
||||
}
|
||||
|
||||
function setPriv(uid, priv) {
|
||||
return coll.findOneAndUpdate({ _id: uid }, { $set: priv }, { returnOriginal: false });
|
||||
}
|
||||
|
||||
function setRole(domainId, uid, role) {
|
||||
return document.setStatus(domainId, document.TYPE_DOMAIN_USER, 0, uid, { role });
|
||||
}
|
||||
@@ -271,6 +291,7 @@ global.Hydro.model.user = module.exports = {
|
||||
setMultiInDomain,
|
||||
getMultiInDomain,
|
||||
getPrefixList,
|
||||
setPriv,
|
||||
setRole,
|
||||
setRoles,
|
||||
getRole,
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
const perm = {
|
||||
PERM_MANAGE: '#',
|
||||
PERM_LOGGEDIN: '0',
|
||||
PERM_MOD_BADGE: '3',
|
||||
PERM_CREATE_PROBLEM: '5',
|
||||
PERM_EDIT_PROBLEM: '6',
|
||||
PERM_VIEW_PROBLEM: '7',
|
||||
PERM_VIEW_PROBLEM_HIDDEN: '8',
|
||||
PERM_SUBMIT_PROBLEM: '9',
|
||||
PERM_READ_PROBLEM_DATA: 'A',
|
||||
PERM_READ_RECORD_CODE: 'B',
|
||||
PERM_REJUDGE_PROBLEM: 'C',
|
||||
PERM_REJUDGE: 'D',
|
||||
PERM_VIEW_PROBLEM_SOLUTION: 'E',
|
||||
PERM_CREATE_PROBLEM_SOLUTION: 'F',
|
||||
PERM_VOTE_PROBLEM_SOLUTION: 'G',
|
||||
PERM_EDIT_PROBLEM_SOLUTION: 'H',
|
||||
PERM_DELETE_PROBLEM_SOLUTION: 'I',
|
||||
PERM_REPLY_PROBLEM_SOLUTION: 'J',
|
||||
PERM_EDIT_PROBLEM_SOLUTION_REPLY: 'K',
|
||||
PERM_DELETE_PROBLEM_SOLUTION_REPLY: 'L',
|
||||
PERM_VIEW_DISCUSSION: 'M',
|
||||
PERM_CREATE_DISCUSSION: 'N',
|
||||
PERM_HIGHLIGHT_DISCUSSION: 'O',
|
||||
PERM_EDIT_DISCUSSION: 'P',
|
||||
PERM_DELETE_DISCUSSION: 'Q',
|
||||
PERM_REPLY_DISCUSSION: 'R',
|
||||
PERM_EDIT_DISCUSSION_REPLY: 'S',
|
||||
PERM_DELETE_DISCUSSION_REPLY: 'T',
|
||||
PERM_VIEW_CONTEST: 'U',
|
||||
PERM_VIEW_CONTEST_SCOREBOARD: 'V',
|
||||
PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD: 'W',
|
||||
PERM_CREATE_CONTEST: 'X',
|
||||
PERM_ATTEND_CONTEST: 'Y',
|
||||
PERM_EDIT_CONTEST: 'Z',
|
||||
PERM_VIEW_TRAINING: 'a',
|
||||
PERM_CREATE_TRAINING: 'b',
|
||||
PERM_EDIT_TRAINING: 'c',
|
||||
PERM_VIEW_HOMEWORK: 'e',
|
||||
PERM_CREATE_HOMEWORK: 'f',
|
||||
PERM_EDIT_HOMEWORK: 'g',
|
||||
PERM_ATTEND_HOMEWORK: 'h',
|
||||
PERM_VIEW_HOMEWORK_SCOREBOARD: 'i',
|
||||
PERM_VIEW_HOMEWORK_HIDDEN_SCOREBOARD: 'j',
|
||||
PERM_JUDGE: 'd',
|
||||
};
|
||||
|
||||
let PERM_ALL = '';
|
||||
for (const p in perm) PERM_ALL += perm[p];
|
||||
perm.PERM_ALL = PERM_ALL;
|
||||
|
||||
const Permission = (family, key, desc) => ({ family, key, desc });
|
||||
|
||||
const PERMS = [
|
||||
Permission('perm_general', perm.PERM_MANAGE, 'Manage domain'),
|
||||
Permission('perm_general', perm.PERM_MOD_BADGE, 'Show MOD badge'),
|
||||
Permission('perm_problem', perm.PERM_CREATE_PROBLEM, 'Create problems'),
|
||||
Permission('perm_problem', perm.PERM_EDIT_PROBLEM, 'Edit problems'),
|
||||
Permission('perm_problem', perm.PERM_VIEW_PROBLEM, 'View problems'),
|
||||
Permission('perm_problem', perm.PERM_VIEW_PROBLEM_HIDDEN, 'View hidden problems'),
|
||||
Permission('perm_problem', perm.PERM_SUBMIT_PROBLEM, 'Submit problem'),
|
||||
Permission('perm_problem', perm.PERM_READ_PROBLEM_DATA, 'Read data of problem'),
|
||||
Permission('perm_record', perm.PERM_READ_RECORD_CODE, 'Read record codes'),
|
||||
Permission('perm_record', perm.PERM_REJUDGE_PROBLEM, 'Rejudge problems'),
|
||||
Permission('perm_record', perm.PERM_REJUDGE, 'Rejudge records'),
|
||||
Permission('perm_problem_solution', perm.PERM_VIEW_PROBLEM_SOLUTION, 'View problem solutions'),
|
||||
Permission('perm_problem_solution', perm.PERM_CREATE_PROBLEM_SOLUTION, 'Create problem solutions'),
|
||||
Permission('perm_problem_solution', perm.PERM_VOTE_PROBLEM_SOLUTION, 'Vote problem solutions'),
|
||||
Permission('perm_problem_solution', perm.PERM_EDIT_PROBLEM_SOLUTION, 'Edit problem solutions'),
|
||||
Permission('perm_problem_solution', perm.PERM_DELETE_PROBLEM_SOLUTION, 'Delete problem solutions'),
|
||||
Permission('perm_problem_solution', perm.PERM_REPLY_PROBLEM_SOLUTION, 'Reply problem solutions'),
|
||||
Permission('perm_problem_solution', perm.PERM_EDIT_PROBLEM_SOLUTION_REPLY, 'Edit problem solution replies'),
|
||||
Permission('perm_problem_solution', perm.PERM_DELETE_PROBLEM_SOLUTION_REPLY, 'Delete problem solution replies'),
|
||||
Permission('perm_discussion', perm.PERM_VIEW_DISCUSSION, 'View discussions'),
|
||||
Permission('perm_discussion', perm.PERM_CREATE_DISCUSSION, 'Create discussions'),
|
||||
Permission('perm_discussion', perm.PERM_HIGHLIGHT_DISCUSSION, 'Highlight discussions'),
|
||||
Permission('perm_discussion', perm.PERM_EDIT_DISCUSSION, 'Edit discussions'),
|
||||
Permission('perm_discussion', perm.PERM_DELETE_DISCUSSION, 'Delete discussions'),
|
||||
Permission('perm_discussion', perm.PERM_REPLY_DISCUSSION, 'Reply discussions'),
|
||||
Permission('perm_discussion', perm.PERM_EDIT_DISCUSSION_REPLY, 'Edit discussion replies'),
|
||||
Permission('perm_discussion', perm.PERM_DELETE_DISCUSSION_REPLY, 'Delete discussion replies'),
|
||||
Permission('perm_contest', perm.PERM_VIEW_CONTEST, 'View contests'),
|
||||
Permission('perm_contest', perm.PERM_VIEW_CONTEST_SCOREBOARD, 'View contest scoreboard'),
|
||||
Permission('perm_contest', perm.PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD, 'View hidden contest submission status and scoreboard'),
|
||||
Permission('perm_contest', perm.PERM_CREATE_CONTEST, 'Create contests'),
|
||||
Permission('perm_contest', perm.PERM_ATTEND_CONTEST, 'Attend contests'),
|
||||
Permission('perm_contest', perm.PERM_EDIT_CONTEST, 'Edit any contests'),
|
||||
Permission('perm_homework', perm.PERM_VIEW_HOMEWORK, 'View homework'),
|
||||
Permission('perm_homework', perm.PERM_VIEW_HOMEWORK_SCOREBOARD, 'View homework scoreboard'),
|
||||
Permission('perm_homework', perm.PERM_VIEW_HOMEWORK_HIDDEN_SCOREBOARD, 'View hidden homework submission status and scoreboard'),
|
||||
Permission('perm_homework', perm.PERM_CREATE_HOMEWORK, 'Create homework'),
|
||||
Permission('perm_homework', perm.PERM_ATTEND_HOMEWORK, 'Claim homework'),
|
||||
Permission('perm_homework', perm.PERM_EDIT_HOMEWORK, 'Edit any homework'),
|
||||
];
|
||||
|
||||
const PERMS_BY_FAMILY = {};
|
||||
for (const p of PERMS) {
|
||||
if (!PERMS_BY_FAMILY[p.family]) PERMS_BY_FAMILY[p.family] = [p];
|
||||
else PERMS_BY_FAMILY[p.family].push(p);
|
||||
}
|
||||
perm.PERMS_BY_FAMILY = PERMS_BY_FAMILY;
|
||||
|
||||
perm.PERM_BASIC = perm.PERM_VIEW_PROBLEM
|
||||
+ perm.PERM_VIEW_PROBLEM_SOLUTION
|
||||
+ perm.PERM_VIEW_DISCUSSION
|
||||
+ perm.PERM_VIEW_TRAINING
|
||||
+ perm.PERM_VIEW_CONTEST
|
||||
+ perm.PERM_VIEW_CONTEST_SCOREBOARD
|
||||
+ perm.PERM_VIEW_HOMEWORK
|
||||
+ perm.PERM_VIEW_HOMEWORK_SCOREBOARD;
|
||||
|
||||
perm.PERM_DEFAULT = perm.PERM_BASIC
|
||||
+ perm.PERM_SUBMIT_PROBLEM
|
||||
+ perm.PERM_CREATE_PROBLEM_SOLUTION
|
||||
+ perm.PERM_VOTE_PROBLEM_SOLUTION
|
||||
+ perm.PERM_REPLY_PROBLEM_SOLUTION
|
||||
+ perm.PERM_CREATE_DISCUSSION
|
||||
+ perm.PERM_REPLY_DISCUSSION
|
||||
+ perm.PERM_ATTEND_CONTEST
|
||||
+ perm.PERM_CREATE_TRAINING
|
||||
+ perm.PERM_LOGGEDIN
|
||||
+ perm.PERM_ATTEND_HOMEWORK;
|
||||
|
||||
perm.PERM_ADMIN = perm.PERM_ALL;
|
||||
|
||||
perm.PRIV_NONE = 0;
|
||||
perm.PRIV_ADMIN = 1;
|
||||
|
||||
global.Hydro.permission = module.exports = perm;
|
||||
+53
-8
@@ -21,7 +21,7 @@ const token = require('../model/token');
|
||||
const opcount = require('../model/opcount');
|
||||
const {
|
||||
UserNotFoundError, BlacklistedError, PermissionError,
|
||||
UserFacingError, ValidationError,
|
||||
UserFacingError, ValidationError, PrivilegeError,
|
||||
} = require('../error');
|
||||
|
||||
let enableLog = true;
|
||||
@@ -196,6 +196,23 @@ class Handler {
|
||||
}
|
||||
}
|
||||
|
||||
checkPriv(...args) {
|
||||
for (const i in args) {
|
||||
if (args[i] instanceof Array) {
|
||||
let p = false;
|
||||
for (const j in args) {
|
||||
if (this.user.hasPriv(args[i][j])) {
|
||||
p = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!p) throw new PrivilegeError([args[i]]);
|
||||
} else if (!this.user.hasPriv(args[i])) {
|
||||
throw new PrivilegeError([[args[i]]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async limitRate(op, periodSecs, maxOperations) {
|
||||
await opcount.inc(op, this.request.ip, periodSecs, maxOperations);
|
||||
}
|
||||
@@ -252,7 +269,7 @@ class Handler {
|
||||
updateUa: this.request.headers['user-agent'] || '',
|
||||
},
|
||||
) : { uid: 1 };
|
||||
if (!this.session) this.session = { uid: 1 };
|
||||
if (!this.session) this.session = { uid: 0 };
|
||||
const bdoc = await blacklist.get(this.request.ip);
|
||||
if (bdoc) throw new BlacklistedError(this.request.ip);
|
||||
this.user = await user.getById(domainId, this.session.uid);
|
||||
@@ -355,7 +372,7 @@ class Handler {
|
||||
}
|
||||
}
|
||||
|
||||
async function handle(ctx, HandlerClass, permission) {
|
||||
async function handle(ctx, HandlerClass, checker) {
|
||||
global.Hydro.stat.reqCount++;
|
||||
const h = new HandlerClass(ctx);
|
||||
try {
|
||||
@@ -366,7 +383,7 @@ async function handle(ctx, HandlerClass, permission) {
|
||||
h.args = args;
|
||||
|
||||
if (h.___prepare) await h.___prepare(args);
|
||||
if (permission) h.checkPerm(permission);
|
||||
if (checker) checker.call(h);
|
||||
|
||||
let checking = '';
|
||||
try {
|
||||
@@ -404,9 +421,38 @@ async function handle(ctx, HandlerClass, permission) {
|
||||
}
|
||||
}
|
||||
|
||||
function Route(name, route, RouteHandler, permission = null) {
|
||||
router.all(name, route, (ctx) => handle(ctx, RouteHandler, permission));
|
||||
router.all(`${name}_with_domainId`, `/d/:domainId${route}`, (ctx) => handle(ctx, RouteHandler, permission));
|
||||
function Route(name, route, RouteHandler, ...permPrivChecker) {
|
||||
let _priv;
|
||||
let _perm;
|
||||
let checker;
|
||||
for (const item of permPrivChecker) {
|
||||
if (typeof item === 'object') {
|
||||
if (typeof item.call !== 'undefined') {
|
||||
checker = item;
|
||||
} if (typeof item[0] === 'number') {
|
||||
_priv = item;
|
||||
} else if (typeof item[0] === 'string') {
|
||||
_perm = item;
|
||||
}
|
||||
} else if (typeof item === 'number') {
|
||||
_priv = item;
|
||||
} else if (typeof item === 'string') {
|
||||
_perm = item;
|
||||
}
|
||||
}
|
||||
if ((_perm || _priv) && checker) {
|
||||
checker = ((_chk) => function __() {
|
||||
_chk();
|
||||
if (_perm) this.checkPerm(_perm);
|
||||
if (_priv) this.checkPriv(_priv);
|
||||
})(checker);
|
||||
} else if (_perm) {
|
||||
checker = function __() {
|
||||
this.checkPerm(_perm);
|
||||
};
|
||||
}
|
||||
router.all(name, route, (ctx) => handle(ctx, RouteHandler, checker));
|
||||
router.all(`${name}_with_domainId`, `/d/:domainId${route}`, (ctx) => handle(ctx, RouteHandler, checker));
|
||||
}
|
||||
|
||||
class ConnectionHandler {
|
||||
@@ -444,7 +490,6 @@ class ConnectionHandler {
|
||||
}
|
||||
|
||||
async renderHTML(name, context) {
|
||||
this.hasPerm = (perm) => this.user.hasPerm(perm);
|
||||
this._user = { ...this.user, gravatar: misc.gravatar(this.user.gravatar, 128) };
|
||||
const res = await template.render(name, Object.assign(context, {
|
||||
handler: this,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"id": "migrate-vijos",
|
||||
"version": "1.0.2",
|
||||
"version": "1.0.3",
|
||||
"description": "Migrate From Vijos"
|
||||
}
|
||||
@@ -24,7 +24,7 @@ const tasks = {
|
||||
salt: doc.salt,
|
||||
hash: doc.hash.split('|')[1],
|
||||
hashType: doc.hash.split('|')[0] === 'vj4' ? 'hydro' : doc.hash.split('|')[0],
|
||||
priv: doc.priv === -1 ? 1 : 0,
|
||||
priv: doc.priv,
|
||||
gravatar: doc.gravatar,
|
||||
mail: doc.mail,
|
||||
mailLower: doc.mail_lower,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const { Route, Handler } = global.Hydro.service.server;
|
||||
const { PERM_LOGGEDIN } = global.Hydro.permission;
|
||||
const { PRIV: { PRIV_USER_PROFILE } } = global.Hydro.model.builtin;
|
||||
const { pastebin } = global.Hydro.model;
|
||||
const { nav } = global.Hydro.lib;
|
||||
|
||||
@@ -23,10 +23,6 @@ class PasteShowHandler extends Handler {
|
||||
}
|
||||
|
||||
class PasteCreateHandler extends Handler {
|
||||
async prepare() {
|
||||
this.checkPerm(PERM_LOGGEDIN);
|
||||
}
|
||||
|
||||
async get() {
|
||||
const path = [
|
||||
['Hydro', 'homepage'],
|
||||
@@ -49,9 +45,9 @@ class PasteCreateHandler extends Handler {
|
||||
|
||||
async function apply() {
|
||||
Route('pastebin', '/paste', PasteMainHandler);
|
||||
Route('paste_create', '/paste/create', PasteCreateHandler);
|
||||
Route('paste_create', '/paste/create', PasteCreateHandler, PRIV_USER_PROFILE);
|
||||
Route('paste_show', '/paste/:docId', PasteShowHandler);
|
||||
nav('pastebin', null, 'pastebin', PERM_LOGGEDIN);
|
||||
nav('pastebin', null, 'pastebin', PRIV_USER_PROFILE);
|
||||
}
|
||||
|
||||
global.Hydro.handler.pastebin = module.exports = apply;
|
||||
@@ -28,15 +28,15 @@
|
||||
|
||||
{% macro reply_operations(doc, rdoc) %}
|
||||
<div class="dczcomments__operations nojs--hide">
|
||||
{% if handler.hasPerm(reply_post_perm) %}
|
||||
{% if handler.user.hasPerm(reply_post_perm) %}
|
||||
<a href="javascript:;" data-tooltip="{{ _('Reply') }}" name="dczcomments__op-reply-reply"><span class="icon icon-reply"></span></a>
|
||||
{% endif %}
|
||||
{% if doc.owner == handler.state.user._id or handler.hasPerm(reply_edit_perm) %}
|
||||
{% if doc.owner == handler.state.user._id or handler.user.hasPerm(reply_edit_perm) %}
|
||||
<a href="javascript:;" data-tooltip="{{ _('Edit') }}" name="dczcomments__op-edit-reply"
|
||||
data-form="{{ {operation: reply_edit_op, drid: doc._id, drrid: rdoc._id}|json }}"
|
||||
><span class="icon icon-edit"></span></a>
|
||||
{% endif %}
|
||||
{% if doc.owner == handler.state.user._id or handler.hasPerm(reply_delete_perm) %}
|
||||
{% if doc.owner == handler.state.user._id or handler.user.hasPerm(reply_delete_perm) %}
|
||||
<a href="javascript:;" data-tooltip="{{ _('Delete') }}" name="dczcomments__op-delete-reply"
|
||||
data-form="{{ {operation: reply_delete_op, drid: doc._id, drrid: rdoc._id}|json }}"
|
||||
><span class="icon icon-delete"></span></a>
|
||||
@@ -71,7 +71,7 @@
|
||||
reply_delete_op = 'delete_tail_reply'
|
||||
) %}
|
||||
<ul class="dczcomments__list view--discussion">
|
||||
{% if handler.hasPerm(comment_post_perm) %}
|
||||
{% if handler.user.hasPerm(comment_post_perm) %}
|
||||
<li class="dczcomments__item">
|
||||
<div class="media">
|
||||
<div class="media__left top">
|
||||
@@ -116,17 +116,17 @@
|
||||
@ {{ datetimeSpan(doc['_id'])|safe }}
|
||||
</div>
|
||||
<div class="dczcomments__operations nojs--hide">
|
||||
{% if handler.hasPerm(reply_post_perm) %}
|
||||
{% if handler.user.hasPerm(reply_post_perm) %}
|
||||
<a href="javascript:;" data-tooltip="{{ _('Reply') }}" name="dczcomments__op-reply-comment"
|
||||
data-form="{{ {operation: reply_post_op, drid: doc._id}|json }}"
|
||||
><span class="icon icon-reply"></span></a>
|
||||
{% endif %}
|
||||
{% if doc.owner == handler.state.user._id or handler.hasPerm(comment_edit_perm) %}
|
||||
{% if doc.owner == handler.state.user._id or handler.user.hasPerm(comment_edit_perm) %}
|
||||
<a href="javascript:;" data-tooltip="{{ _('Edit') }}" name="dczcomments__op-edit-comment"
|
||||
data-form="{{ {operation: comment_edit_op, drid: doc._id}|json }}"
|
||||
><span class="icon icon-edit"></span></a>
|
||||
{% endif %}
|
||||
{% if doc.owner == handler.state.user._id or handler.hasPerm(comment_delete_perm) %}
|
||||
{% if doc.owner == handler.state.user._id or handler.user.hasPerm(comment_delete_perm) %}
|
||||
<a href="javascript:;" data-tooltip="{{ _('Delete') }}" name="dczcomments__op-delete-comment"
|
||||
data-form="{{ {operation: comment_delete_op, drid: doc._id}|json }}"
|
||||
><span class="icon icon-delete"></span></a>
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
reply_delete_op = 'delete_tail_reply'
|
||||
) %}
|
||||
<ul class="dczcomments__list view--solution">
|
||||
{% if handler.hasPerm(comment_post_perm) %}
|
||||
{% if handler.user.hasPerm(comment_post_perm) %}
|
||||
<li class="dczcomments__item">
|
||||
<div class="media">
|
||||
<div class="media__left top">
|
||||
@@ -85,7 +85,7 @@
|
||||
<div class="media">
|
||||
<div class="media__left top">
|
||||
<div class="vote vote--discussion">
|
||||
{% if handler.hasPerm(perm.PERM_VOTE_PROBLEM_SOLUTION) %}
|
||||
{% if handler.user.hasPerm(perm.PERM_VOTE_PROBLEM_SOLUTION) %}
|
||||
<div class="vote-number rotator--enabled">{{ doc.vote }}</div>
|
||||
<div class="vote-op clearfix">
|
||||
<form method="post">
|
||||
@@ -113,17 +113,17 @@
|
||||
@ {{ datetimeSpan(doc._id)|safe }}
|
||||
</div>
|
||||
<div class="dczcomments__operations nojs--hide">
|
||||
{% if handler.hasPerm(reply_post_perm) %}
|
||||
{% if handler.user.hasPerm(reply_post_perm) %}
|
||||
<a href="javascript:;" data-tooltip="{{ _('Reply') }}" name="dczcomments__op-reply-comment"
|
||||
data-form="{{ {operation: reply_post_op, psrid: doc._id}|json }}"
|
||||
><span class="icon icon-reply"></span></a>
|
||||
{% endif %}
|
||||
{% if doc.owner == handler.state.user._id or handler.hasPerm(comment_edit_perm) %}
|
||||
{% if doc.owner == handler.state.user._id or handler.user.hasPerm(comment_edit_perm) %}
|
||||
<a href="javascript:;" data-tooltip="{{ _('Edit') }}" name="dczcomments__op-edit-comment"
|
||||
data-form="{{ {operation: comment_edit_op, psrid: doc._id}|json }}"
|
||||
><span class="icon icon-edit"></span></a>
|
||||
{% endif %}
|
||||
{% if doc.owner == handler.state.user._id or handler.hasPerm(comment_delete_perm) %}
|
||||
{% if doc.owner == handler.state.user._id or handler.user.hasPerm(comment_delete_perm) %}
|
||||
<a href="javascript:;" data-tooltip="{{ _('Delete') }}" name="dczcomments__op-delete-comment"
|
||||
data-form="{{ {operation: comment_delete_op, psrid: doc._id}|json }}"
|
||||
><span class="icon icon-delete"></span></a>
|
||||
@@ -145,15 +145,15 @@
|
||||
{{ user.render_inline(udoc, badge=false) }} @ {{ datetimeSpan(rdoc['_id'])|safe }}
|
||||
</div>
|
||||
<div class="dczcomments__operations nojs--hide">
|
||||
{% if handler.hasPerm(reply_post_perm) %}
|
||||
{% if handler.user.hasPerm(reply_post_perm) %}
|
||||
<a href="javascript:;" data-tooltip="{{ _('Reply') }}" name="dczcomments__op-reply-reply"><span class="icon icon-reply"></span></a>
|
||||
{% endif %}
|
||||
{% if doc.owner == handler.state.user._id or handler.hasPerm(reply_edit_perm) %}
|
||||
{% if doc.owner == handler.state.user._id or handler.user.hasPerm(reply_edit_perm) %}
|
||||
<a href="javascript:;" data-tooltip="{{ _('Edit') }}" name="dczcomments__op-edit-reply"
|
||||
data-form="{{ {operation: reply_edit_op, psid: doc._id, psrid: rdoc._id}|json }}"
|
||||
><span class="icon icon-edit"></span></a>
|
||||
{% endif %}
|
||||
{% if doc.owner == handler.state.user._id or handler.hasPerm(reply_delete_perm) %}
|
||||
{% if doc.owner == handler.state.user._id or handler.user.hasPerm(reply_delete_perm) %}
|
||||
<a href="javascript:;" data-tooltip="{{ _('Delete') }}" name="dczcomments__op-delete-reply"
|
||||
data-form="{{ {operation: reply_delete_op, psid: doc._id, psrid: rdoc._id}|json }}"
|
||||
><span class="icon icon-delete"></span></a>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% macro render_problem_title(pdoc, tdoc=none, show_tags=true, show_invisible_flag=true, rp=none, invalid=false) %}
|
||||
{% macro render_problem_title(pdoc, tdoc=none, show_tags=true, show_invisible_flag=true, invalid=false) %}
|
||||
{%- if not invalid %}
|
||||
<a
|
||||
{%- if tdoc and tdoc.docType == model.document.TYPE_CONTEST %}
|
||||
@@ -17,9 +17,6 @@
|
||||
{%- if pdoc['hidden'] and show_invisible_flag %}
|
||||
<span class="text-orange">({{ _('Hidden') }})</span>
|
||||
{%- endif %}
|
||||
{%- if not rp is none %}
|
||||
<span class="problem__rp-tag">RP+{{ rp|round|int }}</span>
|
||||
{%- endif %}
|
||||
{%- if show_tags %}
|
||||
{{ render_problem_tags(pdoc) }}
|
||||
{%- endif %}
|
||||
@@ -29,7 +26,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="/p/category/{{ tag }}">{{ tag }}</a></li>
|
||||
<li class="problem__tag"><a class="problem__tag-link" href="{{ url('problem_category', category=tag) }}">{{ tag }}</a></li>
|
||||
{%- endfor %}
|
||||
</ul>
|
||||
{%- else %}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
{{ model.builtin.STATUS_TEXTS[rdoc['status']] }}
|
||||
</a>
|
||||
</div>
|
||||
{% if rdoc['status'] == status.STATUS_JUDGING %}
|
||||
{% if rdoc['status'] == STATUS.STATUS_JUDGING %}
|
||||
<div class="col--status__progress-container">
|
||||
<div class="col--status__progress" style="width: {{ rdoc['progress']|default(50)|int }}%"></div>
|
||||
</div>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<div class="section__body no-padding">
|
||||
<table class="data-table">
|
||||
<colgroup>
|
||||
{% if handler.hasPerm(perm.PERM_LOGGEDIN) %}
|
||||
{% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %}
|
||||
<col class="col--status">
|
||||
<col class="col--submit-at">
|
||||
{% endif %}
|
||||
@@ -32,7 +32,7 @@
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
{% if handler.hasPerm(perm.PERM_LOGGEDIN) %}
|
||||
{% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %}
|
||||
<th class="col--status record-status--border">{{ _('Status') }}</th>
|
||||
<th class="col--submit-at">{{ _('Last Submit At') }}</th>
|
||||
{% endif %}
|
||||
@@ -42,7 +42,7 @@
|
||||
<tbody>
|
||||
{% for pid in tdoc.pids %}
|
||||
<tr>
|
||||
{% if handler.hasPerm(perm.PERM_LOGGEDIN) %}
|
||||
{% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %}
|
||||
{% if psdict[pid] and psdict[pid].rid %}
|
||||
{% set rdoc = rdict[psdict[pid].rid] %}
|
||||
{% if handler.canShowRecord(tdoc) %}
|
||||
|
||||
@@ -115,7 +115,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="medium-3 columns">
|
||||
{% if handler.hasPerm(perm.PERM_CREATE_CONTEST) %}
|
||||
{% if handler.user.hasPerm(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 ddoc.owner == handler.user._id or handler.hasPerm(perm.PERM_EDIT_DISCUSSION) %}
|
||||
{% if ddoc.owner == handler.user._id or handler.user.hasPerm(perm.PERM_EDIT_DISCUSSION) %}
|
||||
<li>
|
||||
<a href="{{ url('discussion_edit', did=ddoc.docId) }}"><span class="icon icon-edit"></span> {{ _('Edit') }}</a>
|
||||
</li>
|
||||
@@ -37,7 +37,7 @@
|
||||
<li>
|
||||
{{ _('{0} views').format(ddoc['views']) }}
|
||||
</li>
|
||||
{% if handler.hasPerm(perm.PERM_LOGGEDIN) %}
|
||||
{% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %}
|
||||
<li><form class="form--inline" method="post">
|
||||
<input type="hidden" name="operation" value="{% if not dsdoc['star'] %}star{% else %}unstar{% endif %}">
|
||||
<input type="hidden" name="csrfToken" value="{{ handler.csrfToken }}">
|
||||
@@ -64,11 +64,11 @@
|
||||
comment_ref = 'drid',
|
||||
reply_ref = 'drrid',
|
||||
comment_post_perm = perm.PERM_REPLY_DISCUSSION,
|
||||
comment_edit_perm = perm.PERM_LOGGEDIN if ddoc.owner == handler.user._id else perm.PERM_EDIT_DISCUSSION_REPLY,
|
||||
comment_delete_perm = perm.PERM_LOGGEDIN if ddoc.owner == handler.user._id else perm.PERM_DELETE_DISCUSSION_REPLY,
|
||||
comment_edit_perm = perm.PERM_NONE if ddoc.owner == handler.user._id else perm.PERM_EDIT_DISCUSSION_REPLY,
|
||||
comment_delete_perm = perm.PERM_NONE if ddoc.owner == handler.user._id else perm.PERM_DELETE_DISCUSSION_REPLY,
|
||||
reply_post_perm = perm.PERM_REPLY_DISCUSSION,
|
||||
reply_edit_perm = perm.PERM_LOGGEDIN if ddoc.owner == handler.user._id else perm.PERM_EDIT_DISCUSSION_REPLY,
|
||||
reply_delete_perm = perm.PERM_LOGGEDIN if ddoc.owner == handler.user._id else perm.PERM_DELETE_DISCUSSION_REPLY
|
||||
reply_edit_perm = perm.PERM_NONE if ddoc.owner == handler.user._id else perm.PERM_EDIT_DISCUSSION_REPLY,
|
||||
reply_delete_perm = perm.PERM_NONE if ddoc.owner == handler.user._id else perm.PERM_DELETE_DISCUSSION_REPLY
|
||||
) }}
|
||||
{{ 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 ddoc.owner == handler.user._id or handler.hasPerm(perm.PERM_DELETE_DISCUSSION) %}
|
||||
{% if ddoc.owner == handler.user._id or handler.user.hasPerm(perm.PERM_DELETE_DISCUSSION) %}
|
||||
<button name="operation" value="delete" type="submit" class="rounded button">
|
||||
{{ _('Delete') }}
|
||||
</button>
|
||||
|
||||
@@ -23,10 +23,10 @@
|
||||
</div>
|
||||
<div class="section__body">
|
||||
{% if vnode %}
|
||||
{% if handler.hasPerm(perm.PERM_CREATE_DISCUSSION) %}
|
||||
{% if handler.user.hasPerm(perm.PERM_CREATE_DISCUSSION) %}
|
||||
<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 %}
|
||||
{% if not handler.hasPerm(perm.PERM_LOGGEDIN) %}
|
||||
{% if not handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %}
|
||||
<p><a href="javascript:showSignInDialog();" class="expanded primary button">{{ _('Login to Create a Discussion') }}</a></p>
|
||||
{% else %}
|
||||
<p><a href="javascript:;" class="disabled expanded primary button">{{ _('No Permission to Create a Discussion') }}</a></p>
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<div class="section__body no-padding">
|
||||
<table class="data-table">
|
||||
<colgroup>
|
||||
{% if handler.hasPerm(perm.PERM_LOGGEDIN) %}
|
||||
{% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %}
|
||||
<col class="col--status">
|
||||
<col class="col--submit-at">
|
||||
{% endif %}
|
||||
@@ -31,7 +31,7 @@
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
{% if handler.hasPerm(perm.PERM_LOGGEDIN) %}
|
||||
{% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %}
|
||||
<th class="col--status record-status--border">{{ _('Status') }}</th>
|
||||
<th class="col--submit-at">{{ _('Last Submit At') }}</th>
|
||||
{% endif %}
|
||||
@@ -41,7 +41,7 @@
|
||||
<tbody>
|
||||
{% for pid in tdoc['pids'] %}
|
||||
<tr>
|
||||
{% if handler.hasPerm(perm.PERM_LOGGEDIN) %}
|
||||
{% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %}
|
||||
{% if psdict[pid] and psdict[pid]['rid'] %}
|
||||
{% set rdoc = rdict[psdict[pid]['rid']] %}
|
||||
{% if handler.canShowRecord(tdoc) %}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<option value="list">{{ _('List View') }}</option>
|
||||
</select>
|
||||
</span>
|
||||
{% if handler.hasPerm(perm.PERM_CREATE_HOMEWORK) %}
|
||||
{% if handler.user.hasPerm(perm.PERM_CREATE_HOMEWORK) %}
|
||||
<a class="compact button" href="{{ url('homework_create') }}">{{ _('Create Homework') }}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
+2
-2
@@ -135,7 +135,7 @@
|
||||
<span class="icon training-status--icon done"></span>
|
||||
<span class="training-status--text done">{{ _('Completed') }} 100%</span>
|
||||
{% endif %}
|
||||
{% elif handler.hasPerm(perm.PERM_LOGGEDIN) %}
|
||||
{% elif handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %}
|
||||
<span class="icon training-status--icon outside"></span> <span class="training-status--text outside">{{ _('Not Enrolled') }}</span>
|
||||
{% endif %}
|
||||
</li>
|
||||
@@ -157,7 +157,7 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="large-3 columns">
|
||||
{% if handler.hasPerm(perm.PERM_VIEW_DISCUSSION) %}
|
||||
{% if handler.user.hasPerm(perm.PERM_VIEW_DISCUSSION) %}
|
||||
{% include 'partials/discussion_nodes_widget.html' %}
|
||||
{% endif %}
|
||||
<div class="section side">
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
{% endif %}
|
||||
{% if not attended and not model.contest.isDone(tdoc) %}
|
||||
<li class="menu__item">
|
||||
{% if handler.hasPerm(perm.PERM_ATTEND_CONTEST) and handler.hasPerm(perm.PERM_LOGGEDIN) %}
|
||||
{% if handler.user.hasPerm(perm.PERM_ATTEND_CONTEST) and handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %}
|
||||
<form action="{{ url('contest_detail', tid=tdoc.docId) }}" method="POST">
|
||||
<input type="hidden" name="operation" value="attend">
|
||||
<input type="hidden" name="csrfToken" value="{{ handler.csrfToken }}">
|
||||
@@ -32,7 +32,7 @@
|
||||
<span class="icon icon-add"></span> {{ _('Attend Contest') }}
|
||||
</button>
|
||||
</form>
|
||||
{% elif not handler.hasPerm(perm.PERM_LOGGEDIN) %}
|
||||
{% elif not handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %}
|
||||
<a class="menu__link" href="javascript:showSignInDialog()">
|
||||
<span class="icon icon-add"></span> {{ _('Login to Attend Contest') }}
|
||||
</a>
|
||||
@@ -52,12 +52,12 @@
|
||||
<span class="icon icon-statistics"></span> {{ _('Scoreboard (Hidden)') }}
|
||||
</a></li>
|
||||
{% endif %}
|
||||
{% if user._id == tdoc.owner or handler.hasPerm(perm.PERM_EDIT_CONTEST) %}
|
||||
{% if user._id == tdoc.owner or handler.user.hasPerm(perm.PERM_EDIT_CONTEST) %}
|
||||
<li class="menu__item"><a class="menu__link" href="{{ url('contest_edit', tid=tdoc.docId) }}">
|
||||
<span class="icon icon-edit"></span> {{ _('Edit Contest') }}
|
||||
</a></li>
|
||||
{% endif %}
|
||||
{% if handler.hasPerm(perm.PERM_READ_RECORD_CODE) %}
|
||||
{% if handler.user.hasPerm(perm.PERM_READ_RECORD_CODE) %}
|
||||
<li class="menu__item"><a class="menu__link" href="{{ url('contest_code', tid=tdoc.docId) }}">
|
||||
<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.hasPerm(perm.PERM_HIGHLIGHT_DISCUSSION) %}
|
||||
{% if ddoc.highlight or handler.user.hasPerm(perm.PERM_HIGHLIGHT_DISCUSSION) %}
|
||||
{{ form.form_checkbox({
|
||||
label:'Highlight',
|
||||
name:'highlight',
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
{% endif %}
|
||||
{% if not attended and not model.contest.isDone(tdoc) %}
|
||||
<li class="menu__item">
|
||||
{% if handler.hasPerm(perm.PERM_ATTEND_HOMEWORK) and handler.hasPerm(perm.PERM_LOGGEDIN) %}
|
||||
{% if handler.user.hasPerm(perm.PERM_ATTEND_HOMEWORK) and handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %}
|
||||
<form action="{{ url('homework_detail', tid=tdoc.docId) }}" method="POST">
|
||||
<input type="hidden" name="operation" value="attend">
|
||||
<input type="hidden" name="csrfToken" value="{{ handler.csrfToken }}">
|
||||
@@ -32,7 +32,7 @@
|
||||
<span class="icon icon-add"></span> {{ _('Claim Homework') }}
|
||||
</button>
|
||||
</form>
|
||||
{% elif not handler.hasPerm(perm.PERM_LOGGEDIN) %}
|
||||
{% elif not handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %}
|
||||
<a class="menu__link" href="javascript:showSignInDialog()">
|
||||
<span class="icon icon-add"></span> {{ _('Login to Claim Homework') }}
|
||||
</a>
|
||||
@@ -52,12 +52,12 @@
|
||||
<span class="icon icon-statistics"></span> {{ _('Scoreboard (Hidden)') }}
|
||||
</a></li>
|
||||
{% endif %}
|
||||
{% if tdoc.owner == handler.user._id or handler.hasPerm(perm.PERM_EDIT_HOMEWORK) %}
|
||||
{% if tdoc.owner == handler.user._id or handler.user.hasPerm(perm.PERM_EDIT_HOMEWORK) %}
|
||||
<li class="menu__item"><a class="menu__link" href="{{ url('homework_edit', tid=tdoc.docId) }}">
|
||||
<span class="icon icon-edit"></span> {{ _('Edit Homework') }}
|
||||
</a></li>
|
||||
{% endif %}
|
||||
{% if handler.hasPerm(perm.PERM_READ_RECORD_CODE) %}
|
||||
{% if handler.user.hasPerm(perm.PERM_READ_RECORD_CODE) %}
|
||||
<li class="menu__item"><a class="menu__link" href="{{ url('homework_code', tid=tdoc.docId) }}">
|
||||
<span class="icon icon-download"></span> {{ _('Export All Code') }}
|
||||
</a></li>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
{% endfor %}
|
||||
</ol>
|
||||
<ol class="nav__list nav__list--secondary clearfix">
|
||||
{% if not handler.hasPerm(perm.PERM_LOGGEDIN) %}
|
||||
{% if not handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %}
|
||||
<li class="nav__list-item"><a href="{{ url('user_login') }}" class="nav__item" name="nav_login">{{ _('Login') }}</a></li>
|
||||
<li class="nav__list-item"><a href="{{ url('user_register') }}" class="nav__item--round">{{ _('Sign Up') }}</a></li>
|
||||
{% else %}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
{% else %}
|
||||
<table class="data-table">
|
||||
<colgroup>
|
||||
{% if handler.hasPerm(perm.PERM_LOGGEDIN) %}
|
||||
{% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %}
|
||||
<col class="col--status">
|
||||
{% endif %}
|
||||
<col class="col--name">
|
||||
@@ -18,7 +18,7 @@
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
{% if handler.hasPerm(perm.PERM_LOGGEDIN) %}
|
||||
{% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %}
|
||||
<th class="col--status record-status--border">{{ _('Status') }}</th>
|
||||
{% endif %}
|
||||
<th class="col--name">{{ _('Problem') }}</th>
|
||||
@@ -31,7 +31,7 @@
|
||||
{% for pdoc in pdocs %}
|
||||
{% set psdoc = psdict[pdoc['docId']] %}
|
||||
<tr>
|
||||
{% if handler.hasPerm(perm.PERM_LOGGEDIN) %}
|
||||
{% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %}
|
||||
{% if psdoc['rid'] %}
|
||||
{{ record.render_status_td(psdoc, rid_key='rid') }}
|
||||
{% else %}
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<span class="icon icon-send"></span> {{ _('Open in Problem Set') }}
|
||||
</a></li>
|
||||
{% endif %}
|
||||
{% if pdoc.owner == handler.user._id or handler.hasPerm(perm.PERM_EDIT_PROBLEM) %}
|
||||
{% if pdoc.owner == handler.user._id or handler.user.hasPerm(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="{{ url('problem_edit', pid=pdoc.pid) }}">
|
||||
<span class="icon icon-edit"></span> {{ _('Edit') }}
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<span class="icon icon-send"></span> {{ _('Open in Problem Set') }}
|
||||
</a></li>
|
||||
{% endif %}
|
||||
{% if pdoc.owner == handler.user._id or handler.hasPerm(perm.PERM_EDIT_PROBLEM) %}
|
||||
{% if pdoc.owner == handler.user._id or handler.user.hasPerm(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="{{ url('problem_edit', pid=pdoc.pid) }}">
|
||||
<span class="icon icon-edit"></span> {{ _('Edit') }}
|
||||
|
||||
@@ -9,12 +9,12 @@
|
||||
<div>
|
||||
<ol class="menu">
|
||||
{% if page_name == 'discussion_node' or page_name == 'discussion_detail' %}
|
||||
{% if handler.hasPerm(perm.PERM_CREATE_DISCUSSION) %}
|
||||
{% if handler.user.hasPerm(perm.PERM_CREATE_DISCUSSION) %}
|
||||
<li class="menu__item"><a class="menu__link highlight" href="{{ url('discussion_create', type='problem', name=pdoc.docId) }}">
|
||||
<span class="icon icon-add"></span> {{ _('Create a Discussion') }}
|
||||
</a></li>
|
||||
{% else %}
|
||||
{% if not handler.hasPerm(perm.PERM_LOGGEDIN) %}
|
||||
{% if not handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %}
|
||||
<li class="menu__item"><a class="menu__link" href="javascript:showSignInDialog();">
|
||||
<span class="icon icon-add"></span> {{ _('Login to Create a Discussion') }}</a></li>
|
||||
{% else %}
|
||||
@@ -25,7 +25,7 @@
|
||||
<li class="menu__seperator"></li>
|
||||
{% endif %}
|
||||
{% if page_name == 'problem_detail' %}
|
||||
{% if handler.hasPerm(perm.PERM_SUBMIT_PROBLEM) %}
|
||||
{% if handler.user.hasPerm(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>
|
||||
@@ -38,11 +38,11 @@
|
||||
<span class="icon icon-flag"></span> {{ _('View Problem') }}
|
||||
</a></li>
|
||||
{% endif %}
|
||||
{% if handler.hasPerm(perm.PERM_SUBMIT_PROBLEM) %}
|
||||
{% if handler.user.hasPerm(perm.PERM_SUBMIT_PROBLEM) %}
|
||||
<li class="menu__item scratchpad--hide"><a class="menu__link{% if page_name == 'problem_submit' %} active{% endif %}" href="{{ url('problem_submit', pid=pdoc.pid) }}">
|
||||
<span class="icon icon-send"></span> {{ _('Submit') }}
|
||||
</a></li>
|
||||
{% elif not handler.hasPerm(perm.PERM_LOGGEDIN) %}
|
||||
{% elif not handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %}
|
||||
<li class="menu__item scratchpad--hide"><a class="menu__link{% if page_name == 'problem_submit' %} active{% endif %}" href="javascript:showSignInDialog();">
|
||||
<span class="icon icon-send"></span> {{ _('Login to Submit') }}
|
||||
</a></li>
|
||||
@@ -51,20 +51,20 @@
|
||||
<span class="icon icon-send"></span> {{ _('No Permission to Submit') }}
|
||||
</a></li>
|
||||
{% endif %}
|
||||
{% if handler.hasPerm(perm.PERM_VIEW_DISCUSSION) or handler.hasPerm(perm.PERM_VIEW_PROBLEM_SOLUTION) %}
|
||||
{% if handler.user.hasPerm(perm.PERM_VIEW_DISCUSSION) or handler.user.hasPerm(perm.PERM_VIEW_PROBLEM_SOLUTION) %}
|
||||
<li class="menu__seperator"></li>
|
||||
{% endif %}
|
||||
{% if handler.hasPerm(perm.PERM_VIEW_DISCUSSION) %}
|
||||
{% if handler.user.hasPerm(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="{{ url('discussion_node', type='problem', name=pdoc.docId) }}">
|
||||
<span class="icon icon-comment--text"></span> {{ _('Discussions') }}
|
||||
</a></li>
|
||||
{% endif %}
|
||||
{% if handler.hasPerm(perm.PERM_VIEW_PROBLEM_SOLUTION) %}
|
||||
{% if handler.user.hasPerm(perm.PERM_VIEW_PROBLEM_SOLUTION) %}
|
||||
<li class="menu__item"><a class="menu__link{% if page_name == 'problem_solution' %} active{% endif %}" href="{{ url('problem_solution', pid=pdoc.pid) }}">
|
||||
<span class="icon icon-comment--text"></span> {{ _('Solutions') }}
|
||||
</a></li>
|
||||
{% endif %}
|
||||
{% if user._id == pdoc.owner or handler.hasPerm(perm.PERM_EDIT_PROBLEM) %}
|
||||
{% if user._id == pdoc.owner or handler.user.hasPerm(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="{{ url('problem_edit', pid=pdoc.pid) }}">
|
||||
<span class="icon icon-edit"></span> {{ _('Edit') }}
|
||||
@@ -86,7 +86,7 @@
|
||||
<dt>{{ _('ID') }}</dt>
|
||||
<dd>{{ pdoc.docId }}</dd>
|
||||
{% if page_name != 'discussion_node' and page_name != 'discussion_detail' %}
|
||||
{% if handler.hasPerm(perm.PERM_LOGGEDIN) %}
|
||||
{% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %}
|
||||
<dt>{{ _('Submission') }}</dt>
|
||||
<dd>
|
||||
{% if pdoc.psdoc.status %}
|
||||
@@ -127,7 +127,7 @@
|
||||
<dt>{{ _('# Submissions') }}</dt>
|
||||
<dd><a href="{{ url('record_main', query={pid:pdoc.docId}) }}">{{ pdoc.nSubmit }}</a></dd>
|
||||
{% if page_name != 'discussion_node' and page_name != 'discussion_detail' %}
|
||||
{% if handler.hasPerm(perm.PERM_LOGGEDIN) %}
|
||||
{% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %}
|
||||
<dt>{{ _('# My Submissions') }}</dt>
|
||||
<dd><a href="{{ url('record_main', query={pid:pdoc.docId, uidOrName:handler.user._id}) }}">{{ pdoc['psdoc'].nSubmit|default(0) }}</a></dd>
|
||||
{% endif %}
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% if handler.hasPerm(perm.PERM_CREATE_PROBLEM) %}
|
||||
{% if handler.user.hasPerm(perm.PERM_CREATE_PROBLEM) %}
|
||||
<div class="section side">
|
||||
<div class="section__header">
|
||||
<h1 class="section__title">{{ _('Create Problem') }}</h1>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<div class="section__body">
|
||||
<iframe src="{{ url('problem_upload', pid=pdoc.pid) }}" frameborder="0" style="width: 100%; height: 100px;"></iframe>
|
||||
<p class="help-text">{{ _('Hint') }}: <a href="{{ url('wiki', page='help', anchor='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 (pdoc.owner == handler.user._id or handler.hasPerm(perm.PERM_READ_PROBLEM_DATA)) %}
|
||||
{% if pdoc and (pdoc.owner == handler.user._id or handler.user.hasPerm(perm.PERM_READ_PROBLEM_DATA)) %}
|
||||
<p><a href="{{ url('problem_data', pid=pdoc.pid) }}" class="expanded button">{{ _('Download Dataset') }}</a></p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<div class="row">
|
||||
<div class="medium-9 columns">
|
||||
{% if showStatus %}
|
||||
{% include 'record_detail_status.html' %}
|
||||
{% include 'record_detail_STATUS.html' %}
|
||||
{% endif %}
|
||||
{% if rdoc['code'] %}
|
||||
<div class="section">
|
||||
@@ -44,7 +44,7 @@
|
||||
<div class="section__header">
|
||||
<h1 class="section__title">{{ _('Information') }}</h1>
|
||||
</div>
|
||||
{% if handler.hasPerm(perm.PERM_REJUDGE) %}
|
||||
{% if handler.user.hasPerm(perm.PERM_REJUDGE) %}
|
||||
<div class="section__body no-padding">
|
||||
<ol class="menu">
|
||||
<li class="menu__item">
|
||||
@@ -86,7 +86,7 @@
|
||||
<a href="{{ url('contest_detail', tid=tdoc.docId) }}">{{ tdoc.title }}</a>
|
||||
</dd>
|
||||
{% endif %}
|
||||
{% if pdoc and (handler.user._id == pdoc.owner or handler.hasPerm(perm.PERM_READ_PROBLEM_DATA)) %}
|
||||
{% if pdoc and (handler.user._id == pdoc.owner or handler.user.hasPerm(perm.PERM_READ_PROBLEM_DATA)) %}
|
||||
<dt>{{ _('Problem Data') }}</dt>
|
||||
<dd><a href="{{ url('problem_data', pid=pdoc.pid) }}"><span class="icon icon-download"></span> {{ _('Download') }}</a></dd>
|
||||
{% endif %}
|
||||
|
||||
@@ -48,8 +48,8 @@
|
||||
<span>{{ rcdoc['judgeText'] }}</span>
|
||||
<a class="tool-button" href="{{ url('wiki', page='help', anchor='status') }}"><span class="icon icon-help"></span></a>
|
||||
</td>
|
||||
<td class="col--time">{% if rcdoc['status'] == status.STATUS_TIME_LIMIT_EXCEEDED or rcdoc['status'] == status.STATUS_MEMORY_LIMIT_EXCEEDED or rcdoc['status'] == status.STATUS_OUTPUT_LIMIT_EXCEEDED %}≥{% endif %}{{ rcdoc.time }}ms</td>
|
||||
<td class="col--memory">{% if rcdoc['status'] == status.STATUS_TIME_LIMIT_EXCEEDED or rcdoc['status'] == status.STATUS_MEMORY_LIMIT_EXCEEDED or rcdoc['status'] == status.STATUS_OUTPUT_LIMIT_EXCEEDED %}≥{% endif %}{{ size(rcdoc.memory, 1024) }}</td>
|
||||
<td class="col--time">{% if rcdoc['status'] == STATUS.STATUS_TIME_LIMIT_EXCEEDED or rcdoc['status'] == STATUS.STATUS_MEMORY_LIMIT_EXCEEDED or rcdoc['status'] == STATUS.STATUS_OUTPUT_LIMIT_EXCEEDED %}≥{% endif %}{{ rcdoc.time }}ms</td>
|
||||
<td class="col--memory">{% if rcdoc['status'] == STATUS.STATUS_TIME_LIMIT_EXCEEDED or rcdoc['status'] == STATUS.STATUS_MEMORY_LIMIT_EXCEEDED or rcdoc['status'] == STATUS.STATUS_OUTPUT_LIMIT_EXCEEDED %}≥{% endif %}{{ size(rcdoc.memory, 1024) }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<dd>{{ rdoc['score'] }}</dd>
|
||||
{% endif %}
|
||||
<dt>{{ _('Total Time') }}</dt>
|
||||
<dd>{% if rdoc['status'] == status.STATUS_TIME_LIMIT_EXCEEDED or rdoc['status'] == status.STATUS_MEMORY_LIMIT_EXCEEDED or rdoc['status'] == status.STATUS_OUTPUT_LIMIT_EXCEEDED %}≥{% endif %}{{ rdoc.time }}ms</dd>
|
||||
<dd>{% if rdoc['status'] == STATUS.STATUS_TIME_LIMIT_EXCEEDED or rdoc['status'] == STATUS.STATUS_MEMORY_LIMIT_EXCEEDED or rdoc['status'] == STATUS.STATUS_OUTPUT_LIMIT_EXCEEDED %}≥{% endif %}{{ rdoc.time }}ms</dd>
|
||||
<dt>{{ _('Peak Memory') }}</dt>
|
||||
<dd>{% if rdoc['status'] == status.STATUS_TIME_LIMIT_EXCEEDED or rdoc['status'] == status.STATUS_MEMORY_LIMIT_EXCEEDED or rdoc['status'] == status.STATUS_OUTPUT_LIMIT_EXCEEDED %}≥{% endif %}{{ size(rdoc.memory, 1024) }}</dd>
|
||||
<dd>{% if rdoc['status'] == STATUS.STATUS_TIME_LIMIT_EXCEEDED or rdoc['status'] == STATUS.STATUS_MEMORY_LIMIT_EXCEEDED or rdoc['status'] == STATUS.STATUS_OUTPUT_LIMIT_EXCEEDED %}≥{% endif %}{{ size(rdoc.memory, 1024) }}</dd>
|
||||
</dl>
|
||||
@@ -82,7 +82,7 @@
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% if handler.hasPerm(perm.PERM_VIEW_JUDGE_STATISTICS) %}
|
||||
{% if handler.user.hasPerm(perm.PERM_VIEW_JUDGE_STATISTICS) %}
|
||||
<ul class="pager">
|
||||
<li>
|
||||
<a class="pager__item next link" href="?start={{ rdocs[-1]['_id'] }}&{{ query_string }}">{{ _('pager_next') }}</a>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<tr data-rid="{{ rdoc['_id'] }}">
|
||||
{{ record.render_status_td(rdoc) }}
|
||||
<td class="col--problem col--problem-name">
|
||||
{% if handler.hasPerm(perm.PERM_REJUDGE) %}
|
||||
{% if handler.user.hasPerm(perm.PERM_REJUDGE) %}
|
||||
<form class="form--inline" method="post" action="{{ url('record_rejudge', rid=rdoc._id) }}">
|
||||
<input type="hidden" name="csrfToken" value="{{ handler.csrfToken }}">
|
||||
<button type="submit" class="link text-maroon lighter">
|
||||
@@ -12,7 +12,7 @@
|
||||
</button> |
|
||||
</form>
|
||||
{% endif %}
|
||||
{% if pdoc and (not pdoc.hidden or handler.hasPerm(perm.PERM_VIEW_PROBLEM_HIDDEN)) %}
|
||||
{% if pdoc and (not pdoc.hidden or handler.user.hasPerm(perm.PERM_VIEW_PROBLEM_HIDDEN)) %}
|
||||
{{ problem.render_problem_title(pdoc, show_tags=false) }}
|
||||
{% else %}
|
||||
*
|
||||
@@ -23,8 +23,8 @@
|
||||
{{ udoc['uname'] }}
|
||||
</a>
|
||||
</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 %}≥{% 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 %}≥{% endif %}{{ size(rdoc.memory, 1024) }}</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 %}≥{% 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 %}≥{% endif %}{{ size(rdoc.memory, 1024) }}</td>
|
||||
<td class="col--lang">{{ model.builtin.LANG_TEXTS[rdoc['lang']] }}</td>
|
||||
<td class="col--submit-at">{{ datetimeSpan(rdoc['_id'])|safe }}</td>
|
||||
</tr>
|
||||
@@ -79,7 +79,7 @@
|
||||
<div class="section__body no-padding training__problems">
|
||||
<table class="data-table">
|
||||
<colgroup>
|
||||
{% if handler.hasPerm(perm.PERM_LOGGEDIN) %}
|
||||
{% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %}
|
||||
<col class="col--status">
|
||||
{% endif %}
|
||||
<col class="col--name">
|
||||
@@ -89,7 +89,7 @@
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
{% if handler.hasPerm(perm.PERM_LOGGEDIN) %}
|
||||
{% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %}
|
||||
<th class="col--status record-status--border">{{ _('Status') }}</th>
|
||||
{% endif %}
|
||||
<th class="col--name">{{ _('Problem') }}</th>
|
||||
@@ -104,7 +104,7 @@
|
||||
{% set pdoc=pdict[pid] %}
|
||||
{% set psdoc=psdict[pid] %}
|
||||
<tr>
|
||||
{% if handler.hasPerm(perm.PERM_LOGGEDIN) %}
|
||||
{% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %}
|
||||
{% if psdoc['rid'] %}
|
||||
{{ record.render_status_td(psdoc, rid_key='rid') }}
|
||||
{% else %}
|
||||
@@ -112,7 +112,7 @@
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<td class="col--name col--problem-name">
|
||||
{% if handler.hasPerm(perm.PERM_LOGGEDIN) %}
|
||||
{% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %}
|
||||
<form class="form--inline" action="{{ url('problem_main') }}" method="post">
|
||||
<input type="hidden" name="pid" value="{{ pdoc.docId }}">
|
||||
<input type="hidden" name="operation" value="{% if not psdoc['star'] %}star{% else %}unstar{% endif %}">
|
||||
@@ -160,7 +160,7 @@
|
||||
<div class="section side">
|
||||
<div>
|
||||
<ol class="menu">
|
||||
{% if not tsdoc['enroll'] and handler.hasPerm(perm.PERM_LOGGEDIN) %}
|
||||
{% if not tsdoc['enroll'] and handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %}
|
||||
<li class="menu__item">
|
||||
<form action="" method="POST">
|
||||
<input type="hidden" name="operation" value="enroll">
|
||||
@@ -171,7 +171,7 @@
|
||||
</form>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if tdoc.owner == user._id or handler.hasPerm(perm.PERM_EDIT_TRAINING) %}
|
||||
{% if tdoc.owner == user._id or handler.user.hasPerm(perm.PERM_EDIT_TRAINING) %}
|
||||
<li class="menu__item"><a class="menu__link" href="{{ url('training_edit', tid=tdoc.docId) }}">
|
||||
<span class="icon icon-edit"></span> {{ _('Edit') }}
|
||||
</a></li>
|
||||
@@ -184,7 +184,7 @@
|
||||
</div>
|
||||
<div class="section__body typo">
|
||||
<dl class="large horizontal">
|
||||
{% if handler.hasPerm(perm.PERM_LOGGEDIN) %}
|
||||
{% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %}
|
||||
<dt>{{ _('Status') }}</dt><dd>{% if tsdoc['enroll'] %}{{ _('Completed' if tsdoc['done'] else 'In Progress') }}{% else %}{{ _('Not Enrolled') }}{% endif %}</dd>
|
||||
{% endif %}
|
||||
{% if tsdoc['enroll'] %}
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
<span class="icon training-status--icon done"></span>
|
||||
<span class="training-status--text done">{{ _('Completed') }} 100%</span>
|
||||
{% endif %}
|
||||
{% elif handler.hasPerm(perm.PERM_LOGGEDIN) %}
|
||||
{% elif handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %}
|
||||
<span class="icon training-status--icon outside"></span> <span class="training-status--text outside">{{ _('Not Enrolled') }}</span>
|
||||
{% endif %}
|
||||
</li>
|
||||
@@ -52,7 +52,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="large-3 columns">
|
||||
{% if handler.hasPerm(perm.PERM_LOGGEDIN) %}
|
||||
{% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %}
|
||||
<div class="section side">
|
||||
<div class="section__header">
|
||||
<h1 class="section__title">{{ _('Enrolled') }}</h1>
|
||||
@@ -76,7 +76,7 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if handler.hasPerm(perm.PERM_CREATE_TRAINING) %}
|
||||
{% if handler.user.hasPerm(perm.PERM_CREATE_TRAINING) %}
|
||||
<div class="section side">
|
||||
<div class="section__header">
|
||||
<h1 class="section__title">{{ _('Create Training Plan') }}</h1>
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
{{ model.builtin.USER_GENDER_ICONS[udoc.gender] }}
|
||||
</span>
|
||||
{% endif %}
|
||||
{% if handler.hasPerm(perm.PERM_MOD_BADGE) %}
|
||||
{% if handler.user.hasPerm(perm.PERM_MOD_BADGE) %}
|
||||
<span
|
||||
class="profile-header__contact-item user-profile-badge badge--mod"
|
||||
data-tooltip="{{ _('{0} is a moderator of this domain.').format(_('She' if udoc.gender == 1 else 'He')) }}"
|
||||
|
||||
@@ -55,12 +55,12 @@ Hydro.json 是模块的声明文件,格式如下。
|
||||
|
||||
```js
|
||||
const { Handler, Route } = global.Hydro.service.server;
|
||||
const { user } = global.Hydro.model;
|
||||
const { PERM_LOGGEDIN } = global.Hydro.permission;
|
||||
const { user, builtin } = global.Hydro.model;
|
||||
|
||||
class CustomHandler extends Handler {
|
||||
async prepare() {
|
||||
this.checkPassword(PERM_LOGGEDIN);
|
||||
this.checkPriv(builtin.PRIV.PRIV_USER_PROFILE);
|
||||
// this.checkPerm(), this.user.hasPerm(), this.user.hasPriv(), etc.
|
||||
}
|
||||
|
||||
async get({ username, password }) {
|
||||
@@ -95,7 +95,7 @@ _prepare, prepare, get, post, post[Operation], cleanup, _cleanup
|
||||
args 为传入的参数集合(包括 QueryString, Body, Path)中的全部参数,并对以下字段进行了校验:
|
||||
|
||||
| 名称 | 类型 |
|
||||
| :------: | :-----------: |
|
||||
|:--------:|:-------------:|
|
||||
| content | string |
|
||||
| title | string |
|
||||
| uid | number(int) |
|
||||
|
||||
Reference in new issue
Block a user