diff --git a/packages/hydrooj/package.json b/packages/hydrooj/package.json index 4d753ccd6..cf22342e6 100644 --- a/packages/hydrooj/package.json +++ b/packages/hydrooj/package.json @@ -1,6 +1,6 @@ { "name": "hydrooj", - "version": "2.29.23", + "version": "2.29.24", "bin": "bin/hydrooj.js", "main": "dist/loader.js", "typings": "dist/loader.d.ts", diff --git a/packages/hydrooj/src/handler/contest.ts b/packages/hydrooj/src/handler/contest.ts index 5dff3a915..8824d46d2 100644 --- a/packages/hydrooj/src/handler/contest.ts +++ b/packages/hydrooj/src/handler/contest.ts @@ -4,7 +4,7 @@ import AdmZip from 'adm-zip'; import { Time } from '@hydrooj/utils/lib/utils'; import { ContestNotLiveError, ValidationError, ProblemNotFoundError, - ContestNotAttendedError, PermissionError, BadRequestError, ContestNotFoundError, + ContestNotAttendedError, PermissionError, BadRequestError, ContestNotFoundError, InvalidTokenError, } from '../error'; import { ProblemDoc, Tdoc, User } from '../interface'; import paginate from '../lib/paginate'; @@ -86,9 +86,11 @@ export class ContestDetailHandler extends Handler { } @param('tid', Types.ObjectID) - async postAttend(domainId: string, tid: ObjectID) { + @param('code', Types.String, true) + async postAttend(domainId: string, tid: ObjectID, code = '') { const tdoc = await contest.get(domainId, tid); if (contest.isDone(tdoc)) throw new ContestNotLiveError(tid); + if (tdoc._code && code !== tdoc._code) throw new InvalidTokenError(code); await contest.attend(domainId, tid, this.user._id); this.back(); } @@ -215,9 +217,10 @@ export class ContestEditHandler extends Handler { @param('rule', Types.Range(contest.RULES)) @param('pids', Types.Content) @param('rated', Types.Boolean) + @param('code', Types.String, true) async post( domainId: string, tid: ObjectID, 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, _code = '', ) { const pids = _pids.replace(/,/g, ',').split(',').map((i) => { i = i.trim(); @@ -241,6 +244,7 @@ export class ContestEditHandler extends Handler { } else { tid = await contest.add(domainId, title, content, this.user._id, rule, beginAt, endAt, pids, rated); } + await contest.edit(domainId, tid, { _code }); this.response.body = { tid }; this.response.redirect = this.url('contest_detail', { tid }); } diff --git a/packages/hydrooj/src/interface.ts b/packages/hydrooj/src/interface.ts index 8a68d9d76..45f481770 100644 --- a/packages/hydrooj/src/interface.ts +++ b/packages/hydrooj/src/interface.ts @@ -321,6 +321,7 @@ export interface Tdoc { + $('[data-contest-code]').on('click', (ev) => { + ev.preventDefault(); + // eslint-disable-next-line no-alert + const code = prompt(i18n('Invitation code:')); + request.post('', { + operation: 'attend', + code, + }).then(() => { + Notification.success(i18n('Successfully attended')); + delay(1000).then(() => window.location.reload()); + }).catch((e) => { + Notification.error(e.message || e); + }); + }); +}); + +export default contestPage; diff --git a/packages/ui-default/package.json b/packages/ui-default/package.json index 089425479..bcbdc5d0a 100644 --- a/packages/ui-default/package.json +++ b/packages/ui-default/package.json @@ -1,6 +1,6 @@ { "name": "@hydrooj/ui-default", - "version": "4.12.19", + "version": "4.12.20", "author": "undefined ", "license": "AGPL-3.0", "main": "hydro.js", diff --git a/packages/ui-default/templates/contest_edit.html b/packages/ui-default/templates/contest_edit.html index 0e7f60f78..29edee899 100644 --- a/packages/ui-default/templates/contest_edit.html +++ b/packages/ui-default/templates/contest_edit.html @@ -7,7 +7,7 @@
{{ form.form_select({ - columns:4, + columns:3, label:'Rule', name:'rule', options:rules, @@ -23,8 +23,16 @@ autofocus:true, row:false }) }} + {{ form.form_text({ + columns:3, + label:'Invitation code', + name:'code', + placeholder:_('(leave blank if none)'), + value:tdoc.code|default(''), + row:false + }) }} {{ form.form_checkbox({ - columns:4, + columns:2, label:'Rated', name:'rated', placeholder:_('Is rated'), diff --git a/packages/ui-default/templates/partials/contest_sidebar.html b/packages/ui-default/templates/partials/contest_sidebar.html index d79533c0b..1c3ce9648 100644 --- a/packages/ui-default/templates/partials/contest_sidebar.html +++ b/packages/ui-default/templates/partials/contest_sidebar.html @@ -41,13 +41,13 @@ {% if not attended and not model.contest.isDone(tdoc) %} +
+ + + +
{% elif not handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %} {{ _('Login to Attend Contest') }}