From aa2e35ab42d6b4a61f3e28735a60f4ae5ded2d07 Mon Sep 17 00:00:00 2001 From: undefined Date: Thu, 17 Jul 2025 17:40:58 +0800 Subject: [PATCH] core&ui: allow copy/import as hidden --- packages/hydrooj/src/handler/import.ts | 4 +- packages/hydrooj/src/handler/problem.ts | 5 ++- packages/hydrooj/src/model/problem.ts | 8 ++-- .../ui-default/pages/problem_main.page.tsx | 43 +++++++++++-------- .../ui-default/pages/problem_sidebar.page.ts | 2 +- .../ui-default/templates/problem_import.html | 9 +++- 6 files changed, 46 insertions(+), 25 deletions(-) diff --git a/packages/hydrooj/src/handler/import.ts b/packages/hydrooj/src/handler/import.ts index 5f1220d42..89678efc6 100644 --- a/packages/hydrooj/src/handler/import.ts +++ b/packages/hydrooj/src/handler/import.ts @@ -13,7 +13,8 @@ class ProblemImportHydroHandler extends Handler { @param('keepUser', Types.Boolean) @param('preferredPrefix', Types.String, true) - async post(domainId: string, keepUser: boolean, preferredPrefix?: string) { + @param('hidden', Types.Boolean) + async post(domainId: string, keepUser: boolean, preferredPrefix?: string, hidden?: boolean) { if (keepUser) this.checkPriv(PRIV.PRIV_EDIT_SYSTEM); if (!this.request.files.file) throw new ValidationError('file'); if (preferredPrefix && !/^[a-zA-Z]+$/.test(preferredPrefix)) throw new ValidationError('preferredPrefix'); @@ -24,6 +25,7 @@ class ProblemImportHydroHandler extends Handler { progress: this.progress.bind(this), operator: keepUser ? null : this.user._id, delSource: true, + hidden, }, ).catch((e) => MessageModel.send(1, this.user._id, `Import failed: ${e.message}\n${e.stack}`)); let resolved = false; diff --git a/packages/hydrooj/src/handler/problem.ts b/packages/hydrooj/src/handler/problem.ts index 463cf2606..beca5b820 100644 --- a/packages/hydrooj/src/handler/problem.ts +++ b/packages/hydrooj/src/handler/problem.ts @@ -190,7 +190,8 @@ export class ProblemMainHandler extends Handler { @param('pids', Types.NumericArray) @param('target', Types.String) - async postCopy(domainId: string, pids: number[], target: string) { + @param('hidden', Types.Boolean) + async postCopy(domainId: string, pids: number[], target: string, hidden?: boolean) { const t = `,${this.domain.share || ''},`; if (t !== ',*,' && !t.includes(`,${target},`)) throw new PermissionError(target); const ddoc = await domain.get(target); @@ -205,7 +206,7 @@ export class ProblemMainHandler extends Handler { const ids = []; for (const pid of pids) { // eslint-disable-next-line no-await-in-loop - ids.push(await problem.copy(domainId, pid, target)); + ids.push(await problem.copy(domainId, pid, target, undefined, hidden)); } this.response.body = ids; } diff --git a/packages/hydrooj/src/model/problem.ts b/packages/hydrooj/src/model/problem.ts index e90c1aabd..8f89687a5 100644 --- a/packages/hydrooj/src/model/problem.ts +++ b/packages/hydrooj/src/model/problem.ts @@ -66,6 +66,7 @@ interface ProblemImportOptions { override?: boolean; operator?: number; delSource?: boolean; + hidden?: boolean; } interface ProblemCreateOptions { @@ -244,7 +245,7 @@ export class ProblemModel { return result; } - static async copy(domainId: string, _id: number, target: string, pid?: string) { + static async copy(domainId: string, _id: number, target: string, pid?: string, hidden?: boolean) { const original = await ProblemModel.get(domainId, _id); if (!original) throw new ProblemNotFoundError(domainId, _id); if (original.reference) throw new ValidationError('reference'); @@ -252,7 +253,7 @@ export class ProblemModel { if (!pid && original.pid && !await ProblemModel.get(target, original.pid)) pid = original.pid; return await ProblemModel.add( target, pid, original.title, original.content, - original.owner, original.tag, { hidden: original.hidden, reference: { domainId, pid: _id } }, + original.owner, original.tag, { hidden: hidden || original.hidden, reference: { domainId, pid: _id } }, ); } @@ -560,10 +561,11 @@ export class ProblemModel { content: overrideContent || pdoc.content?.toString() || 'No content', tag, difficulty: pdoc.difficulty, + ...(options.hidden ? { hidden: true } : {}), })).docId : await ProblemModel.add( domainId, pid, title.trim(), overrideContent || pdoc.content?.toString() || 'No content', - operator || pdoc.owner, tag, { hidden: pdoc.hidden, difficulty: pdoc.difficulty }, + operator || pdoc.owner, tag, { hidden: options.hidden || pdoc.hidden, difficulty: pdoc.difficulty }, ); // TODO delete unused file when updating pdoc for (const [f, loc] of await getFiles('testdata', 'attachments', 'generators', 'include')) { diff --git a/packages/ui-default/pages/problem_main.page.tsx b/packages/ui-default/pages/problem_main.page.tsx index fe30acfce..b03da78bb 100644 --- a/packages/ui-default/pages/problem_main.page.tsx +++ b/packages/ui-default/pages/problem_main.page.tsx @@ -177,7 +177,9 @@ function ensureAndGetSelectedPids() { return selectedPids; } -async function handleOperation(operation) { +let refDiv: any; + +async function handleOperation(operation: string) { const pids = ensureAndGetSelectedPids(); if (pids === null) return; const payload: any = {}; @@ -187,9 +189,10 @@ async function handleOperation(operation) { }).open(); if (action !== 'yes') return; } else if (operation === 'copy') { - const domainSelector: any = DomainSelectAutoComplete.getOrConstruct($('.dialog__body--problem-copy [name="target"]')); + const $target = $(refDiv).find('[name="target"]'); + const domainSelector: any = DomainSelectAutoComplete.getOrConstruct($target); const copyDialog = await new ActionDialog({ - $body: $('.dialog__body--problem-copy > div'), + $body: refDiv, onDispatch(action) { if (action === 'ok' && domainSelector.value() === null) { domainSelector.focus(); @@ -199,9 +202,8 @@ async function handleOperation(operation) { }, }).open(); if (copyDialog !== 'ok') return; - const target = $('[name="target"]').val(); - if (!target) return; - payload.target = target; + payload.target = domainSelector.value()._id; + payload.hidden = $(refDiv).find('[name="hidden"]').prop('checked'); } try { await request.post('', { operation, pids, ...payload }); @@ -412,23 +414,30 @@ const page = new NamedPage(['problem_main'], () => { const $body = $('body'); $body.addClass('display-mode'); $('.section.display-mode').removeClass('display-mode'); - $(tpl` -