diff --git a/packages/hydrooj/src/handler/problem.ts b/packages/hydrooj/src/handler/problem.ts index 6d0fe92c..e014a7ce 100644 --- a/packages/hydrooj/src/handler/problem.ts +++ b/packages/hydrooj/src/handler/problem.ts @@ -389,10 +389,11 @@ export class ProblemFilesHandler extends ProblemDetailHandler { this.response.body.links = links; } - @post('filename', Types.Name) - @post('type', Types.Content, true) + @post('filename', Types.Name, true) + @post('type', Types.Range(['testdata', 'additional_file']), true) async postUploadFile(domainId: string, filename: string, type = 'testdata') { if (!this.request.files.file) throw new ValidationError('file'); + if (!filename) filename = this.request.files.file.name || String.random(16); if (this.pdoc.owner !== this.user._id) this.checkPerm(PERM.PERM_EDIT_PROBLEM); if (filename.endsWith('.zip')) { const zip = new AdmZip(this.request.files.file.path); diff --git a/packages/hydrooj/src/lib/testdataConfig.ts b/packages/hydrooj/src/lib/testdataConfig.ts index 4c2f6655..d00aad1f 100644 --- a/packages/hydrooj/src/lib/testdataConfig.ts +++ b/packages/hydrooj/src/lib/testdataConfig.ts @@ -21,8 +21,8 @@ export async function parseConfig(config: string | ProblemConfig = {}) { let cfg: ProblemConfig = {}; if (typeof config === 'string' && config.length) { // TODO should validate here? - cfg = await readYamlCases(load(config) as Record) as ProblemConfig; - } else if (typeof config === 'object') cfg = config; + cfg = await readYamlCases(load(config) as Record); + } else if (typeof config === 'object') cfg = await readYamlCases(config); if (cfg.subtasks.length) { for (const subtask of cfg.subtasks) { result.memoryMax = Math.max(result.memoryMax, subtask.memory); diff --git a/packages/ui-default/components/cmeditor/index.js b/packages/ui-default/components/cmeditor/index.js index 7f935684..f72990ac 100644 --- a/packages/ui-default/components/cmeditor/index.js +++ b/packages/ui-default/components/cmeditor/index.js @@ -5,8 +5,8 @@ export const config = { toolbar: [ 'emoji', 'headings', 'bold', 'italic', 'strike', 'link', '|', 'list', 'ordered-list', 'check', 'outdent', 'indent', '|', - 'quote', 'line', 'code', 'inline-code', '|', - 'upload', 'record', 'table', '|', + 'quote', 'line', 'code', 'inline-code', 'table', '|', + 'upload', 'record', '|', 'edit-mode', 'content-theme', 'code-theme', 'export', 'preview', ], mode: 'ir', @@ -38,20 +38,11 @@ export default class CmEditor extends DOMAttachedObject { await new Promise((resolve) => { this.editor = new Vditor(ele, { ...config, + ...this.options, after: resolve, input(v) { $dom.text(v); }, value, cache: { id: Math.random().toString() }, - upload: { - accept: 'image/*,.mp3, .wav, .zip', - url: '/api/upload/editor', - linkToImgUrl: '/api/upload/fetch', - filename(name) { - return name.replace(/[^(a-zA-Z0-9\u4e00-\u9fa5.)]/g, '') - .replace(/[?\\/:|<>*[\]()$%{}@~]/g, '') - .replace('/\\s/g', ''); - }, - }, }); }); $(ele).addClass('textbox'); @@ -71,10 +62,6 @@ export default class CmEditor extends DOMAttachedObject { return ret; } - destory() { - this.editor.destory(); - } - focus() { this.ensureValid(); this.editor.focus(); diff --git a/packages/ui-default/pages/problem_edit.page.js b/packages/ui-default/pages/problem_edit.page.js index 04ad9511..fce4280a 100644 --- a/packages/ui-default/pages/problem_edit.page.js +++ b/packages/ui-default/pages/problem_edit.page.js @@ -5,6 +5,7 @@ import tpl from 'vj/utils/tpl'; import i18n from 'vj/utils/i18n'; import { ConfirmDialog } from 'vj/components/dialog'; import Dropdown from 'vj/components/dropdown/Dropdown'; +import CmEditor from 'vj/components/cmeditor/index'; const categories = {}; const dirtyCategories = []; @@ -141,7 +142,7 @@ function buildCategoryFilter() { }); } -export default new NamedPage(['problem_create', 'problem_edit'], () => { +export default new NamedPage(['problem_create', 'problem_edit'], (pagename) => { $(document).on('click', '[name="problem-sidebar__show-category"]', (ev) => { $(ev.currentTarget).hide(); $('[name="problem-sidebar__categories"]').show(); @@ -169,4 +170,57 @@ export default new NamedPage(['problem_create', 'problem_edit'], () => { $(document).on('change', '[name="tag"]', parseCategorySelection); buildCategoryFilter(); parseCategorySelection(); + + setInterval(() => { + $('img').each(function () { + if (this.src.startsWith('file://')) this.setAttribute('src', this.src.replace('file://', './file/')); + }); + }, 50); + + CmEditor.getOrConstruct($('textarea[data-markdown-upload]'), { + upload: { + accept: 'image/*,.mp3, .wav, .zip', + url: './files', + extraData: { + type: 'additional_file', + operation: 'upload_file', + }, + multiple: false, + fieldName: 'file', + setHeaders() { + return { accept: 'application/json' }; + }, + format(files, resp) { + const res = JSON.parse(resp); + if (res.error) { + return JSON.stringify({ + msg: res.error.message, + code: -1, + data: { + errFiles: [files[0].name], + succMap: {}, + }, + }); + } + return JSON.stringify({ + msg: '', + code: 0, + data: { + errFiles: [], + succMap: { + [files[0].name]: `file://${files[0].name.replace(/[^(a-zA-Z0-9\u4e00-\u9fa5.)]/g, '') + .replace(/[?\\/:|<>*[\]()$%{}@~]/g, '') + .replace('/\\s/g', '')}`, + }, + }, + }); + }, + filename(name) { + return name.replace(/[^(a-zA-Z0-9\u4e00-\u9fa5.)]/g, '') + .replace(/[?\\/:|<>*[\]()$%{}@~]/g, '') + .replace('/\\s/g', ''); + }, + validate: () => (pagename === 'problem_create' ? i18n('Cannot upload file before problem is created.') : true), + }, + }); }); diff --git a/packages/ui-default/templates/problem_edit.html b/packages/ui-default/templates/problem_edit.html index 3e94d976..f9c91d03 100644 --- a/packages/ui-default/templates/problem_edit.html +++ b/packages/ui-default/templates/problem_edit.html @@ -40,7 +40,7 @@