diff --git a/packages/hydrooj/locales/ko.yaml b/packages/hydrooj/locales/ko.yaml index 5c0549beb..07d2cf394 100644 --- a/packages/hydrooj/locales/ko.yaml +++ b/packages/hydrooj/locales/ko.yaml @@ -546,6 +546,7 @@ Rank: 순위 ranking: 순위 Read data of own problems: 자신의 문제 데이터 읽기 Read data of problem: 문제 데이터 읽기 +Read record codes: 점수 읽기 Ready (☆▽☆): 준비 완료! (☆▽☆) Ready: 준비됨 Recent {0} Records: 최근 {0} 점수 diff --git a/packages/hydrooj/locales/zh_TW.yaml b/packages/hydrooj/locales/zh_TW.yaml index 599a6eadd..8219f7490 100644 --- a/packages/hydrooj/locales/zh_TW.yaml +++ b/packages/hydrooj/locales/zh_TW.yaml @@ -351,9 +351,9 @@ QQ Group: QQ 群 QQ Visibility: QQ 可見性 Quit Scratchpad: 退出線上程式設計模式 Rank: 排名 -Read all records: 讀取記錄 Read data of own problems: 讀取自己題目的資料 Read data of problem: 讀取題目資料 +Read record codes: 讀取記錄的程式碼 Ready (☆▽☆): 馬上開始 (☆▽☆) Ready: 即將開始 Recent {0} Records: 最近 {0} 條記錄 diff --git a/packages/hydrooj/package.json b/packages/hydrooj/package.json index 97410d3eb..7f4b93ba5 100644 --- a/packages/hydrooj/package.json +++ b/packages/hydrooj/package.json @@ -1,6 +1,6 @@ { "name": "hydrooj", - "version": "2.32.0", + "version": "2.32.1", "bin": "bin/hydrooj.js", "main": "src/loader", "module": "src/loader", diff --git a/packages/hydrooj/src/handler/contest.ts b/packages/hydrooj/src/handler/contest.ts index 5f35e38f1..acb2de81c 100644 --- a/packages/hydrooj/src/handler/contest.ts +++ b/packages/hydrooj/src/handler/contest.ts @@ -388,8 +388,8 @@ export class ContestCodeHandler extends Handler { async get(domainId: string, tid: ObjectID) { await this.limitRate('contest_code', 3600, 60); const [tdoc, tsdocs] = await contest.getAndListStatus(domainId, tid); - if (!this.user.own(tdoc) && !this.user.hasPriv(PRIV.PRIV_READ_RECORD)) { - this.checkPerm(PERM.PERM_READ_RECORD); + if (!this.user.own(tdoc) && !this.user.hasPriv(PRIV.PRIV_READ_RECORD_CODE)) { + this.checkPerm(PERM.PERM_READ_RECORD_CODE); } if (!contest.canShowRecord.call(this, tdoc as any, true)) { throw new PermissionError(PERM.PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD); diff --git a/packages/hydrooj/src/handler/homework.ts b/packages/hydrooj/src/handler/homework.ts index 59b848a27..8a1d5e914 100644 --- a/packages/hydrooj/src/handler/homework.ts +++ b/packages/hydrooj/src/handler/homework.ts @@ -393,8 +393,8 @@ class HomeworkCodeHandler extends Handler { const [tdoc, tsdocs] = await contest.getAndListStatus( domainId, tid, document.TYPE_HOMEWORK, ); - if (!this.user.own(tdoc) && !this.user.hasPriv(PRIV.PRIV_READ_RECORD)) { - this.checkPerm(PERM.PERM_READ_RECORD); + if (!this.user.own(tdoc) && !this.user.hasPriv(PRIV.PRIV_READ_RECORD_CODE)) { + this.checkPerm(PERM.PERM_READ_RECORD_CODE); } const rnames = {}; for (const tsdoc of tsdocs) { diff --git a/packages/hydrooj/src/handler/record.ts b/packages/hydrooj/src/handler/record.ts index 593f0a733..cd7a24364 100644 --- a/packages/hydrooj/src/handler/record.ts +++ b/packages/hydrooj/src/handler/record.ts @@ -97,21 +97,23 @@ class RecordDetailHandler extends Handler { if (!rdoc) throw new RecordNotFoundError(rid); if (rdoc.contest) { const tdoc = await contest.get(domainId, rdoc.contest.tid, rdoc.contest.type); - if ( - (rdoc.uid !== this.user._id && !contest.canShowRecord.call(this, tdoc, true)) - || (rdoc.uid === this.user._id && !contest.canShowSelfRecord.call(this, tdoc, true)) - ) { - throw new PermissionError(rid); - } + let canView = this.user.own(tdoc); + canView ||= contest.canShowRecord.call(this, tdoc); + canView ||= contest.canShowSelfRecord.call(this, tdoc, true) && rdoc.uid === this.user._id; + if (!canView) throw new PermissionError(rid); } - if (rdoc.uid !== this.user._id && !this.user.hasPriv(PRIV.PRIV_READ_RECORD)) { - this.checkPerm(PERM.PERM_READ_RECORD); + if (rdoc.uid !== this.user._id && !this.user.hasPriv(PRIV.PRIV_READ_RECORD_CODE)) { + if (!this.user.hasPerm(PERM.PERM_READ_RECORD_CODE)) { + rdoc.code = null; + rdoc.compilerTexts = []; + } } // eslint-disable-next-line prefer-const let [pdoc, udoc] = await Promise.all([ problem.get(rdoc.pdomain, rdoc.pid), user.getById(domainId, rdoc.uid), ]); + if (!rdoc.contest && pdoc.hidden && this.user._id !== rdoc.uid) throw new PermissionError(PERM.PERM_READ_RECORD_CODE); if ( !pdoc || !this.user.hasPerm(PERM.PERM_VIEW_PROBLEM) @@ -119,15 +121,7 @@ class RecordDetailHandler extends Handler { ) { pdoc = { ...problem.default, docId: pdoc?.docId || 0, pid: '*' }; } - this.response.body = { - path: [ - ['Hydro', 'homepage'], - ['record_detail', null], - ], - udoc, - rdoc, - pdoc, - }; + this.response.body = { udoc, rdoc, pdoc }; if (rdoc.contest) { this.response.body.tdoc = await contest.get(domainId, rdoc.contest.tid, rdoc.contest.type); } @@ -159,7 +153,7 @@ class RecordDetailHandler extends Handler { }; const [latest] = await Promise.all([ record.update(domainId, rid, $set), - bus.emit('record/change', rdoc, $set), + TaskModel.deleteMany({ rid: rdoc._id }), ]); await postJudge(latest); } @@ -168,7 +162,7 @@ class RecordDetailHandler extends Handler { } class RecordMainConnectionHandler extends ConnectionHandler { - dispose: bus.Disposable; + cleanup: bus.Disposable; tid: string; uid: number; pdomain: string; @@ -210,19 +204,14 @@ class RecordMainConnectionHandler extends ConnectionHandler { } else throw new ProblemNotFoundError(domainId, pid); } if (status) this.status = status; - this.dispose = bus.on('record/change', this.onRecordChange.bind(this)); + this.cleanup = bus.on('record/change', this.onRecordChange.bind(this)); } - async message(msg) { - if (msg.rids instanceof Array) { - const rids = msg.rids.map((id: string) => new ObjectID(id)); - const rdocs = await record.getMulti(this.domainId, { _id: { $in: rids } }).toArray(); - for (const rdoc of rdocs) this.onRecordChange(rdoc); - } - } - - async cleanup() { - if (this.dispose) this.dispose(); + async message(msg: { rids: string[] }) { + if (!(msg.rids instanceof Array)) return; + const rids = msg.rids.map((id) => new ObjectID(id)); + const rdocs = await record.getMulti(this.domainId, { _id: { $in: rids } }).project(buildProjection(record.PROJECTION_LIST)).toArray(); + for (const rdoc of rdocs) this.onRecordChange(rdoc); } async onRecordChange(rdoc: RecordDoc) { @@ -253,7 +242,7 @@ class RecordMainConnectionHandler extends ConnectionHandler { } class RecordDetailConnectionHandler extends ConnectionHandler { - dispose: bus.Disposable; + cleanup: bus.Disposable; rid: string; @param('rid', Types.ObjectID) @@ -261,13 +250,21 @@ class RecordDetailConnectionHandler extends ConnectionHandler { const rdoc = await record.get(domainId, rid); if (rdoc.contest && rdoc.input === undefined) { const tdoc = await contest.get(domainId, rdoc.contest.tid, -1); - if (!contest.canShowRecord.call(this, tdoc)) throw new PermissionError(PERM.PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD); + let canView = this.user.own(tdoc); + canView ||= contest.canShowRecord.call(this, tdoc); + canView ||= this.user._id === rdoc.uid && contest.canShowSelfRecord.call(this, tdoc); + if (!canView) throw new PermissionError(PERM.PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD); } - if (rdoc.uid !== this.user._id && !this.user.hasPriv(PRIV.PRIV_READ_RECORD)) { - this.checkPerm(PERM.PERM_READ_RECORD); + if (rdoc.uid !== this.user._id && !this.user.hasPriv(PRIV.PRIV_READ_RECORD_CODE)) { + if (!this.user.hasPerm(PERM.PERM_READ_RECORD_CODE)) { + rdoc.code = null; + rdoc.compilerTexts = []; + } } + const pdoc = await problem.get(rdoc.pdomain, rdoc.pid); + if (!rdoc.contest && pdoc.hidden && this.user._id !== rdoc.uid) throw new PermissionError(PERM.PERM_READ_RECORD_CODE); this.rid = rid.toString(); - this.dispose = bus.on('record/change', this.onRecordChange.bind(this)); + this.cleanup = bus.on('record/change', this.onRecordChange.bind(this)); this.onRecordChange(rdoc); } @@ -281,10 +278,6 @@ class RecordDetailConnectionHandler extends ConnectionHandler { summary_html: await this.renderHTML('record_detail_summary.html', { rdoc }), }); } - - async cleanup() { - if (this.dispose) this.dispose(); - } } export async function apply() { diff --git a/packages/hydrooj/src/model/builtin.ts b/packages/hydrooj/src/model/builtin.ts index 365b6cf13..08e32ff88 100644 --- a/packages/hydrooj/src/model/builtin.ts +++ b/packages/hydrooj/src/model/builtin.ts @@ -30,7 +30,7 @@ export const PERM = { PERM_VIEW_PROBLEM_HIDDEN: 1n << 8n, PERM_SUBMIT_PROBLEM: 1n << 9n, PERM_READ_PROBLEM_DATA: 1n << 10n, - PERM_READ_RECORD: 1n << 12n, + PERM_READ_RECORD_CODE: 1n << 12n, PERM_REJUDGE_PROBLEM: 1n << 13n, PERM_REJUDGE: 1n << 14n, @@ -113,7 +113,7 @@ export const PERMS = [ 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, 'Read all records'), + 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'), @@ -232,7 +232,7 @@ export const PRIV = { PRIV_USER_PROFILE: 1 << 2, PRIV_REGISTER_USER: 1 << 3, PRIV_READ_PROBLEM_DATA: 1 << 4, - PRIV_READ_RECORD: 1 << 7, + PRIV_READ_RECORD_CODE: 1 << 7, PRIV_VIEW_HIDDEN_RECORD: 1 << 8, PRIV_JUDGE: 1 << 9, // (renamed) PRIV_CREATE_DOMAIN: 1 << 10, diff --git a/packages/hydrooj/src/model/task.ts b/packages/hydrooj/src/model/task.ts index 07a8be77c..bb874c4f4 100644 --- a/packages/hydrooj/src/model/task.ts +++ b/packages/hydrooj/src/model/task.ts @@ -114,6 +114,10 @@ class TaskModel { return coll.deleteOne({ _id }); } + static deleteMany(query: FilterQuery) { + return coll.deleteMany(query); + } + static getFirst = getFirst; static async getDelay(query?: FilterQuery): Promise<[number, Date]> { diff --git a/packages/ui-default/locales/ko.yaml b/packages/ui-default/locales/ko.yaml index 074d85792..9dc90b1d4 100644 --- a/packages/ui-default/locales/ko.yaml +++ b/packages/ui-default/locales/ko.yaml @@ -550,6 +550,7 @@ Rank: 순위 ranking: 순위 Read data of own problems: 자신의 문제 데이터 읽기 Read data of problem: 문제 데이터 읽기 +Read record codes: 점수 읽기 Ready (☆▽☆): 준비 완료! (☆▽☆) Ready: 준비됨 Recent {0} Records: 최근 {0} 점수 diff --git a/packages/ui-default/locales/zh.yaml b/packages/ui-default/locales/zh.yaml index 639ef7a5a..78384e6f5 100644 --- a/packages/ui-default/locales/zh.yaml +++ b/packages/ui-default/locales/zh.yaml @@ -584,9 +584,9 @@ Quit Scratchpad: 退出在线编程模式 Quote: 引用 Rank: 排名 ranking: 排名 -Read all records: 读取所有提交记录 Read data of own problems: 读取自己题目的数据 Read data of problem: 读取题目数据 +Read record codes: 读取记录的代码 Ready (☆▽☆): 马上开始 (☆▽☆) Ready: 即将开始 Recent {0} Records: 最近 {0} 条记录 diff --git a/packages/ui-default/locales/zh_TW.yaml b/packages/ui-default/locales/zh_TW.yaml index 4d139d9eb..b83bbd57c 100644 --- a/packages/ui-default/locales/zh_TW.yaml +++ b/packages/ui-default/locales/zh_TW.yaml @@ -353,9 +353,9 @@ QQ Group: QQ 群 QQ Visibility: QQ 可見性 Quit Scratchpad: 退出線上程式設計模式 Rank: 排名 -Read all records: 讀取記錄 Read data of own problems: 讀取自己題目的資料 Read data of problem: 讀取題目資料 +Read record codes: 讀取記錄的程式碼 Ready (☆▽☆): 馬上開始 (☆▽☆) Ready: 即將開始 Recent {0} Records: 最近 {0} 條記錄 diff --git a/packages/ui-default/package.json b/packages/ui-default/package.json index baeb42778..ddea002c2 100644 --- a/packages/ui-default/package.json +++ b/packages/ui-default/package.json @@ -1,6 +1,6 @@ { "name": "@hydrooj/ui-default", - "version": "4.15.0", + "version": "4.15.1", "author": "undefined ", "license": "AGPL-3.0", "main": "hydro.js", diff --git a/packages/ui-default/templates/partials/contest_sidebar.html b/packages/ui-default/templates/partials/contest_sidebar.html index 693cfd0fb..797fb03e2 100644 --- a/packages/ui-default/templates/partials/contest_sidebar.html +++ b/packages/ui-default/templates/partials/contest_sidebar.html @@ -73,7 +73,7 @@ {{ _('Edit Contest') }} {% endif %} - {% if handler.user.own(tdoc) or handler.user.hasPriv(PRIV.PRIV_READ_RECORD) or handler.user.hasPerm(perm.PERM_READ_RECORD) %} + {% if handler.user.own(tdoc) or handler.user.hasPriv(PRIV.PRIV_READ_RECORD_CODE) or handler.user.hasPerm(perm.PERM_READ_RECORD_CODE) %} {% if model.contest.canShowRecord.call(handler, tdoc, true) %} {% endif %} - {% if handler.user.own(tdoc) or handler.user.hasPriv(PRIV.PRIV_READ_RECORD) or handler.user.hasPerm(perm.PERM_READ_RECORD) %} + {% if handler.user.own(tdoc) or handler.user.hasPriv(PRIV.PRIV_READ_RECORD_CODE) or handler.user.hasPerm(perm.PERM_READ_RECORD_CODE) %}