mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 09:46:28 +08:00
core: 删除题目前检查是否已被比赛使用
This commit is contained in:
1 parent
e4c6cf258d
commit
26ded9d5df
9 files changed
+78
-62
No files matched your search
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "hydrooj",
|
"name": "hydrooj",
|
||||||
"version": "2.26.18",
|
"version": "2.26.19",
|
||||||
"bin": "bin/hydrooj.js",
|
"bin": "bin/hydrooj.js",
|
||||||
"main": "dist/loader.js",
|
"main": "dist/loader.js",
|
||||||
"typings": "dist/loader.d.ts",
|
"typings": "dist/loader.d.ts",
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import moment from 'moment-timezone';
|
import moment from 'moment-timezone';
|
||||||
import { ObjectID } from 'mongodb';
|
import { ObjectID } from 'mongodb';
|
||||||
import AdmZip from 'adm-zip';
|
import AdmZip from 'adm-zip';
|
||||||
import { isSafeInteger } from 'lodash';
|
|
||||||
import {
|
import {
|
||||||
ContestNotLiveError, ValidationError, ProblemNotFoundError,
|
ContestNotLiveError, ValidationError, ProblemNotFoundError,
|
||||||
ContestNotAttendedError, PermissionError, BadRequestError,
|
ContestNotAttendedError, PermissionError, BadRequestError,
|
||||||
@@ -215,11 +214,8 @@ class ContestEditHandler extends Handler {
|
|||||||
domainId: string, beginAtDate: string, beginAtTime: string, duration: number,
|
domainId: string, beginAtDate: string, beginAtTime: string, duration: number,
|
||||||
title: string, content: string, rule: string, _pids: string, rated = false,
|
title: string, content: string, rule: string, _pids: string, rated = false,
|
||||||
) {
|
) {
|
||||||
const pids = _pids.replace(/,/g, ',').split(',').map((i) => {
|
const pids = _pids.replace(/,/g, ',').split(',').map((i) => +i);
|
||||||
if (isSafeInteger(parseInt(i, 10))) return parseInt(i, 10);
|
await problem.getList(domainId, pids, this.user.hasPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN), true);
|
||||||
return i;
|
|
||||||
});
|
|
||||||
await problem.getList(domainId, pids, this.user.hasPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN), false);
|
|
||||||
const beginAtMoment = moment.tz(`${beginAtDate} ${beginAtTime}`, this.user.timeZone);
|
const beginAtMoment = moment.tz(`${beginAtDate} ${beginAtTime}`, this.user.timeZone);
|
||||||
if (!beginAtMoment.isValid()) {
|
if (!beginAtMoment.isValid()) {
|
||||||
throw new ValidationError('beginAtDate', 'beginAtTime');
|
throw new ValidationError('beginAtDate', 'beginAtTime');
|
||||||
@@ -389,7 +385,7 @@ class ContestCodeHandler extends Handler {
|
|||||||
for (const rdoc of rdocs) {
|
for (const rdoc of rdocs) {
|
||||||
zip.addFile(`${rnames[rdoc._id.toHexString()]}.${rdoc.lang}`, Buffer.from(rdoc.code));
|
zip.addFile(`${rnames[rdoc._id.toHexString()]}.${rdoc.lang}`, Buffer.from(rdoc.code));
|
||||||
}
|
}
|
||||||
await this.binary(zip.toBuffer(), `${tdoc.title}.zip`);
|
this.binary(zip.toBuffer(), `${tdoc.title}.zip`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -429,19 +425,14 @@ class ContestCreateHandler extends Handler {
|
|||||||
domainId: string, beginAtDate: string, beginAtTime: string, duration: number,
|
domainId: string, beginAtDate: string, beginAtTime: string, duration: number,
|
||||||
title: string, content: string, rule: string, _pids: string, rated = false,
|
title: string, content: string, rule: string, _pids: string, rated = false,
|
||||||
) {
|
) {
|
||||||
const pids = _pids.replace(/,/g, ',').split(',').map((i) => {
|
const pids = _pids.replace(/,/g, ',').split(',').map((i) => +i);
|
||||||
if (isSafeInteger(parseInt(i, 10))) return parseInt(i, 10);
|
|
||||||
return i;
|
|
||||||
});
|
|
||||||
const beginAt = moment.tz(`${beginAtDate} ${beginAtTime}`, this.user.timeZone);
|
const beginAt = moment.tz(`${beginAtDate} ${beginAtTime}`, this.user.timeZone);
|
||||||
if (!beginAt.isValid()) {
|
if (!beginAt.isValid()) throw new ValidationError('beginAtDate', 'beginAtTime');
|
||||||
throw new ValidationError('beginAtDate', 'beginAtTime');
|
const endAt = beginAt.clone().add(duration, 'hours').toDate();
|
||||||
}
|
await problem.getList(domainId, pids, this.user.hasPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN), true);
|
||||||
const endAt = beginAt.clone().add(duration, 'hours');
|
|
||||||
await problem.getList(domainId, pids, this.user.hasPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN), false);
|
|
||||||
const tid = await contest.add(
|
const tid = await contest.add(
|
||||||
domainId, title, content,
|
domainId, title, content,
|
||||||
this.user._id, rule, beginAt.toDate(), endAt.toDate(), pids, rated,
|
this.user._id, rule, beginAt.toDate(), endAt, pids, rated,
|
||||||
);
|
);
|
||||||
this.response.body = { tid };
|
this.response.body = { tid };
|
||||||
this.response.redirect = this.url('contest_detail', { tid });
|
this.response.redirect = this.url('contest_detail', { tid });
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import paginate from '../lib/paginate';
|
|||||||
import * as system from '../model/system';
|
import * as system from '../model/system';
|
||||||
import user from '../model/user';
|
import user from '../model/user';
|
||||||
import * as oplog from '../model/oplog';
|
import * as oplog from '../model/oplog';
|
||||||
import message from '../model/message';
|
|
||||||
import * as discussion from '../model/discussion';
|
import * as discussion from '../model/discussion';
|
||||||
import * as document from '../model/document';
|
import * as document from '../model/document';
|
||||||
import { PERM, PRIV } from '../model/builtin';
|
import { PERM, PRIV } from '../model/builtin';
|
||||||
|
|||||||
@@ -2,12 +2,14 @@ import yaml from 'js-yaml';
|
|||||||
import moment from 'moment-timezone';
|
import moment from 'moment-timezone';
|
||||||
import { ObjectID } from 'mongodb';
|
import { ObjectID } from 'mongodb';
|
||||||
import AdmZip from 'adm-zip';
|
import AdmZip from 'adm-zip';
|
||||||
import { isSafeInteger } from 'lodash';
|
|
||||||
import {
|
import {
|
||||||
ValidationError, HomeworkNotLiveError, ProblemNotFoundError,
|
ValidationError, HomeworkNotLiveError, ProblemNotFoundError,
|
||||||
HomeworkNotAttendedError,
|
HomeworkNotAttendedError,
|
||||||
|
BadRequestError,
|
||||||
} from '../error';
|
} from '../error';
|
||||||
import { PenaltyRules, Tdoc, ProblemDoc } from '../interface';
|
import {
|
||||||
|
PenaltyRules, Tdoc, ProblemDoc, User,
|
||||||
|
} from '../interface';
|
||||||
import {
|
import {
|
||||||
Route, Handler, Types, param,
|
Route, Handler, Types, param,
|
||||||
} from '../service/server';
|
} from '../service/server';
|
||||||
@@ -22,6 +24,7 @@ import problem from '../model/problem';
|
|||||||
import record from '../model/record';
|
import record from '../model/record';
|
||||||
import * as document from '../model/document';
|
import * as document from '../model/document';
|
||||||
import paginate from '../lib/paginate';
|
import paginate from '../lib/paginate';
|
||||||
|
import { parseConfig } from '../lib/testdataConfig';
|
||||||
|
|
||||||
const validatePenaltyRules = (input: string) => {
|
const validatePenaltyRules = (input: string) => {
|
||||||
const s = yaml.load(input);
|
const s = yaml.load(input);
|
||||||
@@ -120,6 +123,8 @@ class HomeworkDetailProblemHandler extends Handler {
|
|||||||
tdoc: Tdoc<60>;
|
tdoc: Tdoc<60>;
|
||||||
pdoc: ProblemDoc;
|
pdoc: ProblemDoc;
|
||||||
tsdoc: any;
|
tsdoc: any;
|
||||||
|
udoc: User;
|
||||||
|
attended: boolean;
|
||||||
|
|
||||||
@param('tid', Types.ObjectID)
|
@param('tid', Types.ObjectID)
|
||||||
@param('pid', Types.UnsignedInt)
|
@param('pid', Types.UnsignedInt)
|
||||||
@@ -130,28 +135,38 @@ class HomeworkDetailProblemHandler extends Handler {
|
|||||||
problem.get(domainId, pid, this.user._id),
|
problem.get(domainId, pid, this.user._id),
|
||||||
contest.getStatus(domainId, tid, this.user._id, document.TYPE_HOMEWORK),
|
contest.getStatus(domainId, tid, this.user._id, document.TYPE_HOMEWORK),
|
||||||
]);
|
]);
|
||||||
|
this.udoc = await user.getById(domainId, this.tdoc.owner);
|
||||||
|
this.attended = this.tsdoc && this.tsdoc.attend === 1;
|
||||||
|
this.response.body = {
|
||||||
|
tdoc: this.tdoc,
|
||||||
|
pdoc: this.pdoc,
|
||||||
|
tsdoc: this.tsdoc,
|
||||||
|
udoc: this.udoc,
|
||||||
|
attended: this.attended,
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
this.response.body.pdoc.config = await parseConfig(this.pdoc.config);
|
||||||
|
} catch (e) {
|
||||||
|
this.response.body.pdoc.config = `Cannot parse: ${e.message}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@param('tid', Types.ObjectID)
|
@param('tid', Types.ObjectID)
|
||||||
@param('pid', Types.UnsignedInt)
|
@param('pid', Types.UnsignedInt)
|
||||||
async get(domainId: string, tid: ObjectID, pid: number) {
|
async get(domainId: string, tid: ObjectID, pid: number) {
|
||||||
const udoc = await user.getById(domainId, this.tdoc.owner);
|
|
||||||
const attended = this.tsdoc && this.tsdoc.attend === 1;
|
|
||||||
if (!contest.isDone(this.tdoc)) {
|
if (!contest.isDone(this.tdoc)) {
|
||||||
if (!attended) throw new HomeworkNotAttendedError(tid);
|
if (!this.attended) throw new HomeworkNotAttendedError(tid);
|
||||||
if (contest.isNotStarted(this.tdoc)) throw new HomeworkNotLiveError(tid);
|
if (contest.isNotStarted(this.tdoc)) throw new HomeworkNotLiveError(tid);
|
||||||
}
|
}
|
||||||
if (!this.tdoc.pids.includes(pid)) throw new ProblemNotFoundError(domainId, pid, tid);
|
if (!this.tdoc.pids.includes(pid)) throw new ProblemNotFoundError(domainId, pid, tid);
|
||||||
const path = [
|
this.response.template = 'problem_detail.html';
|
||||||
|
this.response.body.page_name = 'homework_detail_problem';
|
||||||
|
this.response.body.path = [
|
||||||
['Hydro', 'homepage'],
|
['Hydro', 'homepage'],
|
||||||
['homework_main', 'homework_main'],
|
['homework_main', 'homework_main'],
|
||||||
[this.tdoc.title, 'homework_detail', { tid }, true],
|
[this.tdoc.title, 'homework_detail', { tid }, true],
|
||||||
[this.pdoc.title, null, null, true],
|
[this.pdoc.title, null, null, true],
|
||||||
];
|
];
|
||||||
this.response.template = 'problem_detail.html';
|
|
||||||
this.response.body = {
|
|
||||||
tdoc: this.tdoc, pdoc: this.pdoc, tsdoc: this.tsdoc, udoc, attended, path, page_name: 'homework_detail_problem',
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,16 +174,14 @@ class HomeworkDetailProblemSubmitHandler extends HomeworkDetailProblemHandler {
|
|||||||
@param('tid', Types.ObjectID)
|
@param('tid', Types.ObjectID)
|
||||||
@param('pid', Types.UnsignedInt)
|
@param('pid', Types.UnsignedInt)
|
||||||
async get(domainId: string, tid: ObjectID, pid: number) {
|
async get(domainId: string, tid: ObjectID, pid: number) {
|
||||||
const udoc = await user.getById(domainId, this.tdoc.owner);
|
if (!this.attended) throw new HomeworkNotAttendedError(tid);
|
||||||
const attended = this.tsdoc && this.tsdoc.attend === 1;
|
|
||||||
if (!attended) throw new HomeworkNotAttendedError(tid);
|
|
||||||
if (!contest.isOngoing(this.tdoc)) throw new HomeworkNotLiveError(tid);
|
if (!contest.isOngoing(this.tdoc)) throw new HomeworkNotLiveError(tid);
|
||||||
if (!this.tdoc.pids.includes(pid)) throw new ProblemNotFoundError(domainId, pid, tid);
|
if (!this.tdoc.pids.includes(pid)) throw new ProblemNotFoundError(domainId, pid, tid);
|
||||||
const rdocs = contest.canShowRecord.call(this, this.tdoc)
|
this.response.body.rdocs = contest.canShowRecord.call(this, this.tdoc)
|
||||||
? await record.getUserInProblemMulti(domainId, this.user._id, pid, true)
|
? await record.getUserInProblemMulti(domainId, this.user._id, pid, true)
|
||||||
.sort('_id', -1).limit(10).toArray()
|
.sort('_id', -1).limit(10).toArray()
|
||||||
: [];
|
: [];
|
||||||
const path = [
|
this.response.body.path = [
|
||||||
['Hydro', 'homepage'],
|
['Hydro', 'homepage'],
|
||||||
['homework_main', 'homework_main'],
|
['homework_main', 'homework_main'],
|
||||||
[this.tdoc.title, 'homework_detail', { tid }, true],
|
[this.tdoc.title, 'homework_detail', { tid }, true],
|
||||||
@@ -176,9 +189,6 @@ class HomeworkDetailProblemSubmitHandler extends HomeworkDetailProblemHandler {
|
|||||||
['homework_detail_problem_submit', null],
|
['homework_detail_problem_submit', null],
|
||||||
];
|
];
|
||||||
this.response.template = 'problem_submit.html';
|
this.response.template = 'problem_submit.html';
|
||||||
this.response.body = {
|
|
||||||
tdoc: this.tdoc, pdoc: this.pdoc, tsdoc: this.tsdoc, udoc, attended, path, rdocs,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@param('tid', Types.ObjectID)
|
@param('tid', Types.ObjectID)
|
||||||
@@ -186,9 +196,11 @@ class HomeworkDetailProblemSubmitHandler extends HomeworkDetailProblemHandler {
|
|||||||
@param('code', Types.Content)
|
@param('code', Types.Content)
|
||||||
@param('lang', Types.Name)
|
@param('lang', Types.Name)
|
||||||
async post(domainId: string, tid: ObjectID, pid: number, code: string, lang: string) {
|
async post(domainId: string, tid: ObjectID, pid: number, code: string, lang: string) {
|
||||||
|
if (this.response.body.pdoc.config?.langs && !this.response.body.pdoc.config.langs.includes('lang')) {
|
||||||
|
throw new BadRequestError('Language not allowed.');
|
||||||
|
}
|
||||||
await this.limitRate('add_record', 60, 5);
|
await this.limitRate('add_record', 60, 5);
|
||||||
const tsdoc = await contest.getStatus(domainId, tid, this.user._id, document.TYPE_HOMEWORK);
|
if (!this.attended) throw new HomeworkNotAttendedError(tid);
|
||||||
if (!tsdoc.attend) throw new HomeworkNotAttendedError(tid);
|
|
||||||
if (!contest.isOngoing(this.tdoc)) throw new HomeworkNotLiveError(tid);
|
if (!contest.isOngoing(this.tdoc)) throw new HomeworkNotLiveError(tid);
|
||||||
if (!this.tdoc.pids.includes(pid)) throw new ProblemNotFoundError(domainId, pid);
|
if (!this.tdoc.pids.includes(pid)) throw new ProblemNotFoundError(domainId, pid);
|
||||||
const rid = await record.add(domainId, pid, this.user._id, lang, code, true, {
|
const rid = await record.add(domainId, pid, this.user._id, lang, code, true, {
|
||||||
@@ -203,7 +215,8 @@ class HomeworkDetailProblemSubmitHandler extends HomeworkDetailProblemHandler {
|
|||||||
rid, pid, false, 0, document.TYPE_HOMEWORK),
|
rid, pid, false, 0, document.TYPE_HOMEWORK),
|
||||||
]);
|
]);
|
||||||
bus.boardcast('record/change', rdoc);
|
bus.boardcast('record/change', rdoc);
|
||||||
this.response.body = { tid, rid };
|
this.response.body.tid = tid;
|
||||||
|
this.response.body.rid = rid;
|
||||||
if (contest.canShowRecord.call(this, this.tdoc)) this.response.redirect = this.url('record_detail', { rid });
|
if (contest.canShowRecord.call(this, this.tdoc)) this.response.redirect = this.url('record_detail', { rid });
|
||||||
else this.response.redirect = this.url('homework_detail', { tid });
|
else this.response.redirect = this.url('homework_detail', { tid });
|
||||||
}
|
}
|
||||||
@@ -246,10 +259,7 @@ class HomeworkCreateHandler extends Handler {
|
|||||||
penaltySinceDate: string, penaltySinceTime: string, extensionDays: number,
|
penaltySinceDate: string, penaltySinceTime: string, extensionDays: number,
|
||||||
penaltyRules: PenaltyRules, title: string, content: string, _pids: string, rated = false,
|
penaltyRules: PenaltyRules, title: string, content: string, _pids: string, rated = false,
|
||||||
) {
|
) {
|
||||||
const pids = _pids.replace(/,/g, ',').split(',').map((i) => {
|
const pids = _pids.replace(/,/g, ',').split(',').map((i) => +i);
|
||||||
if (isSafeInteger(parseInt(i, 10))) return parseInt(i, 10);
|
|
||||||
return i;
|
|
||||||
});
|
|
||||||
const beginAt = moment.tz(`${beginAtDate} ${beginAtTime}`, this.user.timeZone);
|
const beginAt = moment.tz(`${beginAtDate} ${beginAtTime}`, this.user.timeZone);
|
||||||
if (!beginAt.isValid()) throw new ValidationError('beginAtDate', 'beginAtTime');
|
if (!beginAt.isValid()) throw new ValidationError('beginAtDate', 'beginAtTime');
|
||||||
const penaltySince = moment.tz(`${penaltySinceDate} ${penaltySinceTime}`, this.user.timeZone);
|
const penaltySince = moment.tz(`${penaltySinceDate} ${penaltySinceTime}`, this.user.timeZone);
|
||||||
@@ -257,7 +267,7 @@ class HomeworkCreateHandler extends Handler {
|
|||||||
const endAt = penaltySince.clone().add(extensionDays, 'days');
|
const endAt = penaltySince.clone().add(extensionDays, 'days');
|
||||||
if (beginAt.isSameOrAfter(penaltySince)) throw new ValidationError('endAtDate', 'endAtTime');
|
if (beginAt.isSameOrAfter(penaltySince)) throw new ValidationError('endAtDate', 'endAtTime');
|
||||||
if (penaltySince.isAfter(endAt)) throw new ValidationError('extensionDays');
|
if (penaltySince.isAfter(endAt)) throw new ValidationError('extensionDays');
|
||||||
await problem.getList(domainId, pids, this.user.hasPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN), false);
|
await problem.getList(domainId, pids, this.user.hasPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN), true);
|
||||||
const tid = await contest.add(domainId, title, content, this.user._id,
|
const tid = await contest.add(domainId, title, content, this.user._id,
|
||||||
'homework', beginAt.toDate(), endAt.toDate(), pids, rated,
|
'homework', beginAt.toDate(), endAt.toDate(), pids, rated,
|
||||||
{ penaltySince: penaltySince.toDate(), penaltyRules }, document.TYPE_HOMEWORK);
|
{ penaltySince: penaltySince.toDate(), penaltyRules }, document.TYPE_HOMEWORK);
|
||||||
@@ -313,10 +323,7 @@ class HomeworkEditHandler extends Handler {
|
|||||||
penaltySinceDate: string, penaltySinceTime: string, extensionDays: number,
|
penaltySinceDate: string, penaltySinceTime: string, extensionDays: number,
|
||||||
penaltyRules: string, title: string, content: string, _pids: string, rated = false,
|
penaltyRules: string, title: string, content: string, _pids: string, rated = false,
|
||||||
) {
|
) {
|
||||||
const pids = _pids.replace(/,/g, ',').split(',').map((i) => {
|
const pids = _pids.replace(/,/g, ',').split(',').map((i) => +i);
|
||||||
if (isSafeInteger(parseInt(i, 10))) return parseInt(i, 10);
|
|
||||||
return i;
|
|
||||||
});
|
|
||||||
const tdoc = await contest.get(domainId, tid, document.TYPE_HOMEWORK);
|
const tdoc = await contest.get(domainId, tid, document.TYPE_HOMEWORK);
|
||||||
if (!this.user.own(tdoc)) this.checkPerm(PERM.PERM_EDIT_HOMEWORK);
|
if (!this.user.own(tdoc)) this.checkPerm(PERM.PERM_EDIT_HOMEWORK);
|
||||||
else this.checkPerm(PERM.PERM_EDIT_HOMEWORK_SELF);
|
else this.checkPerm(PERM.PERM_EDIT_HOMEWORK_SELF);
|
||||||
@@ -334,7 +341,7 @@ class HomeworkEditHandler extends Handler {
|
|||||||
}
|
}
|
||||||
let endAt = penaltySince.clone().add(extensionDays, 'days');
|
let endAt = penaltySince.clone().add(extensionDays, 'days');
|
||||||
if (beginAt.isSameOrAfter(penaltySince)) throw new ValidationError('endAtDate', 'endAtTime');
|
if (beginAt.isSameOrAfter(penaltySince)) throw new ValidationError('endAtDate', 'endAtTime');
|
||||||
await problem.getList(domainId, pids, this.user.hasPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN), false);
|
await problem.getList(domainId, pids, this.user.hasPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN), true);
|
||||||
beginAt = beginAt.toDate();
|
beginAt = beginAt.toDate();
|
||||||
endAt = endAt.toDate();
|
endAt = endAt.toDate();
|
||||||
penaltySince = penaltySince.toDate();
|
penaltySince = penaltySince.toDate();
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ import problem from '../model/problem';
|
|||||||
import record from '../model/record';
|
import record from '../model/record';
|
||||||
import domain from '../model/domain';
|
import domain from '../model/domain';
|
||||||
import user from '../model/user';
|
import user from '../model/user';
|
||||||
|
import * as document from '../model/document';
|
||||||
|
import * as contest from '../model/contest';
|
||||||
import solution from '../model/solution';
|
import solution from '../model/solution';
|
||||||
import { PERM, PRIV } from '../model/builtin';
|
import { PERM, PRIV } from '../model/builtin';
|
||||||
import storage from '../service/storage';
|
import storage from '../service/storage';
|
||||||
@@ -213,11 +215,14 @@ export class ProblemDetailHandler extends ProblemHandler {
|
|||||||
async get(..._args: any[]) {
|
async get(..._args: any[]) {
|
||||||
// Navigate to current additional file download
|
// Navigate to current additional file download
|
||||||
// e.g.  will navigate to 
|
// e.g.  will navigate to 
|
||||||
this.response.body.pdoc.content = this.response.body.pdoc.content
|
if (!this.request.json) {
|
||||||
.replace(/\(file:\/\//g, `(./${this.pdoc.docId}/file/`);
|
this.response.body.pdoc.content = this.response.body.pdoc.content
|
||||||
|
.replace(/\(file:\/\//g, `(./${this.pdoc.docId}/file/`);
|
||||||
|
}
|
||||||
if (this.pdoc.psdoc) {
|
if (this.pdoc.psdoc) {
|
||||||
this.response.body.rdoc = await record.get(this.domainId, this.pdoc.psdoc.rid);
|
this.response.body.rdoc = await record.get(this.domainId, this.pdoc.psdoc.rid);
|
||||||
}
|
}
|
||||||
|
this.response.body.ctdocs = await contest.getRelated(this.domainId, this.pdoc.docId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@param('pid', Types.UnsignedInt)
|
@param('pid', Types.UnsignedInt)
|
||||||
@@ -233,6 +238,12 @@ export class ProblemDetailHandler extends ProblemHandler {
|
|||||||
|
|
||||||
async postDelete() {
|
async postDelete() {
|
||||||
if (!this.user.own(this.pdoc, PERM.PERM_EDIT_PROBLEM_SELF)) this.checkPerm(PERM.PERM_EDIT_PROBLEM);
|
if (!this.user.own(this.pdoc, PERM.PERM_EDIT_PROBLEM_SELF)) this.checkPerm(PERM.PERM_EDIT_PROBLEM);
|
||||||
|
const [ctdocs, htdocs] = await Promise.all([
|
||||||
|
contest.getRelated(this.domainId, this.pdoc.docId, document.TYPE_CONTEST),
|
||||||
|
contest.getRelated(this.domainId, this.pdoc.docId, document.TYPE_HOMEWORK),
|
||||||
|
]);
|
||||||
|
if (ctdocs.length) throw new BadRequestError('Problem already used by contest {0}', ctdocs[0]._id);
|
||||||
|
if (htdocs.length) throw new BadRequestError('Problem already used by homwrork {0}', htdocs[0]._id);
|
||||||
await problem.del(this.pdoc.domainId, this.pdoc.docId);
|
await problem.del(this.pdoc.domainId, this.pdoc.docId);
|
||||||
this.response.redirect = this.url('problem_main');
|
this.response.redirect = this.url('problem_main');
|
||||||
}
|
}
|
||||||
@@ -264,7 +275,7 @@ export class ProblemSubmitHandler extends ProblemDetailHandler {
|
|||||||
@param('lang', Types.Name)
|
@param('lang', Types.Name)
|
||||||
@param('code', Types.Content)
|
@param('code', Types.Content)
|
||||||
async post(domainId: string, lang: string, code: string) {
|
async post(domainId: string, lang: string, code: string) {
|
||||||
if (this.response.body.pdoc.config.langs && !this.response.body.pdoc.config.langs.includes('lang')) {
|
if (this.response.body.pdoc.config?.langs && !this.response.body.pdoc.config.langs.includes('lang')) {
|
||||||
throw new BadRequestError('Language not allowed.');
|
throw new BadRequestError('Language not allowed.');
|
||||||
}
|
}
|
||||||
await this.limitRate('add_record', 60, 5);
|
await this.limitRate('add_record', 60, 5);
|
||||||
|
|||||||
@@ -444,6 +444,10 @@ export async function get(domainId: string, tid: ObjectID, type: Type | -1 = doc
|
|||||||
return tdoc;
|
return tdoc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getRelated(domainId: string, pid: number, type: Type = document.TYPE_CONTEST) {
|
||||||
|
return await document.getMulti(domainId, type, { pids: pid }).toArray();
|
||||||
|
}
|
||||||
|
|
||||||
export async function updateStatus(
|
export async function updateStatus(
|
||||||
domainId: string, tid: ObjectID, uid: number, rid: ObjectID, pid: number,
|
domainId: string, tid: ObjectID, uid: number, rid: ObjectID, pid: number,
|
||||||
accept = false, score = 0, type: 30 | 60 = document.TYPE_CONTEST,
|
accept = false, score = 0, type: 30 | 60 = document.TYPE_CONTEST,
|
||||||
@@ -624,6 +628,7 @@ global.Hydro.model.contest = {
|
|||||||
edit,
|
edit,
|
||||||
del,
|
del,
|
||||||
get,
|
get,
|
||||||
|
getRelated,
|
||||||
updateStatus,
|
updateStatus,
|
||||||
getStatus,
|
getStatus,
|
||||||
count,
|
count,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@hydrooj/ui-default",
|
"name": "@hydrooj/ui-default",
|
||||||
"version": "4.8.32",
|
"version": "4.8.33",
|
||||||
"author": "undefined <i@undefined.moe>",
|
"author": "undefined <i@undefined.moe>",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"main": "hydro.js",
|
"main": "hydro.js",
|
||||||
|
|||||||
@@ -251,7 +251,10 @@ const page = new NamedPage(['problem_detail', 'contest_detail_problem', 'homewor
|
|||||||
loadSubjective();
|
loadSubjective();
|
||||||
|
|
||||||
if (pagename === 'contest_detail_problem') return;
|
if (pagename === 'contest_detail_problem') return;
|
||||||
UiContext.pdoc.stats = UiContext.pdoc.stats || {};
|
if (!Object.keys(UiContext.pdoc.stats || {}).length) {
|
||||||
|
$('#submission-status-placeholder').parent().hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
const $status = document.getElementById('submission-status-placeholder');
|
const $status = document.getElementById('submission-status-placeholder');
|
||||||
const statusChart = echarts.init($status);
|
const statusChart = echarts.init($status);
|
||||||
statusChart.setOption({
|
statusChart.setOption({
|
||||||
|
|||||||
@@ -105,14 +105,14 @@
|
|||||||
</ol>
|
</ol>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% if page_name != 'problem_detail' and page_name != 'homework_detail_problem' %}
|
{% if page_name != 'problem_detail' and page_name != 'homework_detail_problem' and page_name != 'contest_detail_problem' %}
|
||||||
<div class="section side">
|
<div class="section side">
|
||||||
<div class="section__header">
|
<div class="section__header">
|
||||||
<h1 class="section__title">{{ _('Information') }}</h1>
|
<h1 class="section__title">{{ _('Information') }}</h1>
|
||||||
</div>
|
</div>
|
||||||
{% include "partials/problem-sidebar-information.html" %}
|
{% include "partials/problem-sidebar-information.html" %}
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% elif page_name != 'contest_detail_problem' %}
|
||||||
<div class="section side nojs-hide">
|
<div class="section side nojs-hide">
|
||||||
<div class="section__header">
|
<div class="section__header">
|
||||||
<h1 class="section__title">{{ _('Statistics') }}</h1>
|
<h1 class="section__title">{{ _('Statistics') }}</h1>
|
||||||
@@ -121,22 +121,22 @@
|
|||||||
<div id="submission-score-placeholder" style="width: 100%; height:230px"></div>
|
<div id="submission-score-placeholder" style="width: 100%; height:230px"></div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if page_name == 'problem_detail' and (tdocs or ctdocs) %}
|
{% if page_name == 'problem_detail' and (tdocs.length or ctdocs.length) %}
|
||||||
<div class="section side">
|
<div class="section side">
|
||||||
<div class="section__header">
|
<div class="section__header">
|
||||||
<h1 class="section__title">{{ _('Related') }}</h1>
|
<h1 class="section__title">{{ _('Related') }}</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="section__body typo">
|
<div class="section__body typo">
|
||||||
{% if tdocs %}
|
{% if tdocs.length %}
|
||||||
<p>{{ _('In following training plans') }}: </p>
|
<p>{{ _('In following training plans') }}: </p>
|
||||||
{%- for tdoc in tdocs -%}
|
{%- for tdoc in tdocs -%}
|
||||||
<p><a href="{{ url('training_detail', tid=tdoc.docId) }}">{{ tdoc.title }}</a></p>
|
<p><a href="{{ url('training_detail', tid=tdoc.docId) }}">{{ tdoc.title }}</a></p>
|
||||||
{%- endfor -%}
|
{%- endfor -%}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if ctdocs %}
|
{% if ctdocs.length %}
|
||||||
<p>{{ _('In following contests') }}: </p>
|
<p>{{ _('In following contests') }}: </p>
|
||||||
{%- for tdoc in ctdocs -%}
|
{%- for tdoc in ctdocs -%}
|
||||||
<p><a href="{{ url('contest_detail', tid=tdoc.docId) }}">{{ tdoc.title }}</a></p>
|
<p><a href="{{ url('contest_detail', tid=tdoc.docId) }}">{{ tdoc.title }}</a></p>
|
||||||
{%- endfor -%}
|
{%- endfor -%}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in new issue
Block a user