core&ui: fix sidebar preview (#491)

Co-authored-by: panda <panda_dtdyy@outlook.com>
This commit is contained in:
undefinedandpanda authored and GitHub committed 2023-01-18 18:32:04 +08:00
1 parent feb3b5cecb
commit feb5180476
19 files changed
+152 -244

No files matched your search

+10 -6
View File
@@ -671,19 +671,23 @@ export class ProblemFilesHandler extends ProblemDetailHandler {
@param('testdata', Types.Boolean)
@param('additional_file', Types.Boolean)
@param('pjax', Types.Boolean)
async get(domainId: string, getTestdata = true, getAdditionalFile = true, pjax = false) {
@param('sidebar', Types.Boolean)
async get(domainId: string, getTestdata = true, getAdditionalFile = true, pjax = false, sidebar = false) {
this.response.body.testdata = getTestdata ? sortFiles(this.pdoc.data || []) : [];
this.response.body.reference = getTestdata ? this.pdoc.reference : '';
this.response.body.additional_file = getAdditionalFile ? sortFiles(this.pdoc.additional_file || []) : [];
if (pjax) {
const { testdata, additional_file } = this.response.body;
const owner = await user.getById(domainId, this.pdoc.owner);
const args = {
testdata, additional_file, pdoc: this.pdoc, owner_udoc: owner, sidebar,
};
const tasks = [];
if (getTestdata) tasks.push(this.renderHTML('partials/problem_files.html', { ...args, filetype: 'testdata' }));
if (getAdditionalFile) tasks.push(this.renderHTML('partials/problem_files.html', { ...args, filetype: 'additional_file' }));
if (!sidebar) tasks.push(this.renderHTML('partials/problem-sidebar-information.html', args));
this.response.body = {
fragments: (await Promise.all([
this.renderHTML('partials/problem_files-testdata.html', { testdata, pdoc: this.pdoc }),
this.renderHTML('partials/problem_files-additional_file.html', { additional_file, pdoc: this.pdoc }),
this.renderHTML('partials/problem-sidebar-information.html', { pdoc: this.pdoc, owner_udoc: owner }),
])).map((i) => ({ html: i })),
fragments: (await Promise.all(tasks)).map((i) => ({ html: i })),
};
this.response.template = '';
} else this.response.template = 'problem_files.html';
+1 -1
View File
@@ -64,7 +64,7 @@ export const Types: Types = {
Float: [(v) => +v, (v) => Number.isFinite(+v)],
// eslint-disable-next-line no-shadow
ObjectID: [(v) => new ObjectID(v), ObjectID.isValid],
Boolean: [(v) => !!v, null, true],
Boolean: [(v) => v && !['false', 'off'].includes(v), null, true],
Date: [
(v) => {
const d = v.split('-');
@@ -4,9 +4,8 @@ import { nanoid } from 'nanoid';
import { ActionDialog, InfoDialog } from 'vj/components/dialog/index';
import Notification from 'vj/components/notification';
import { AutoloadPage } from 'vj/misc/Page';
import {
i18n, pjax, request, tpl,
} from 'vj/utils';
import { i18n, request, tpl } from 'vj/utils';
import uploadFiles from '../upload';
async function startEdit(filename, value, fileCategory = 'file') {
const { default: Editor } = await import('vj/components/editor/index');
@@ -144,17 +143,17 @@ export async function previewFile(ev, type = '') {
} else Notification.info(i18n('Loading editor...'));
const val = await startEdit(filename, content, type || 'file');
if (typeof val !== 'string') return null;
Notification.info(i18n('Saving file...'));
const data = new FormData();
data.append('filename', filename);
data.append('file', new Blob([val], { type: 'text/plain' }));
if (type) data.append('type', type);
data.append('operation', 'upload_file');
const postUrl = !window.location.href.endsWith('/files')
? `${window.location.href.substring(0, window.location.href.lastIndexOf('/'))}/files` : '';
await request.postFile(postUrl, data);
Notification.success(i18n('File saved.'));
return pjax.request({ push: false });
const file = new File([val], filename, { type: 'text/plain' });
const endpoint = new URL(!window.location.href.endsWith('/files')
? `${window.location.href.substring(0, window.location.href.lastIndexOf('/'))}/files` : '', window.location.href);
const sidebarType = $(ev.currentTarget).closest('[data-fragment-id]').data('type');
const sidebar = $(ev.currentTarget).closest('[data-fragment-id]').data('sidebar') !== undefined;
await uploadFiles(endpoint.toString(), [file], {
type: sidebarType || type,
sidebar,
pjax: true,
});
return null;
}
const dataPreviewPage = new AutoloadPage('dataPreview', () => {
+7 -1
View File
@@ -11,6 +11,7 @@ function onBeforeUnload(e) {
interface UploadOptions {
type?: string;
pjax?: boolean;
sidebar?: boolean;
singleFileUploadCallback?: (file: File) => any;
}
export default async function uploadFiles(endpoint = '', files: File[] | FileList = [], options: UploadOptions = {}) {
@@ -65,7 +66,12 @@ export default async function uploadFiles(endpoint = '', files: File[] | FileLis
}
window.removeEventListener('beforeunload', onBeforeUnload);
Notification.success(i18n('File uploaded successfully.'));
if (options.pjax) await pjax.request({ push: false });
if (options.pjax) {
let params = '';
if (options.type) params += `?testdata=${options.type === 'testdata'}&additional_file=${options.type === 'additional_file'}`;
if (options.sidebar) params += `${params ? '&' : '?'}sidebar=true`;
await pjax.request({ push: false, url: `${endpoint}${params || ''}` });
}
} catch (e) {
console.error(e);
Notification.error(i18n('File upload failed: {0}', e.toString()));
+3 -2
View File
@@ -68,6 +68,7 @@ Add module: 添加模块
Add new data: 添加新数据
Add User: 添加用户
Add: 添加
additional_file: 附加文件
All {0} Contests: 所有 {0} 比赛
All Contests: 所有比赛
All Homeworks: 所有作业
@@ -529,8 +530,7 @@ New file: 新文件
New Password: 新密码
New Training Plan: 新训练计划
New: 创建
No additional file at current: 当前没有附加文件
No additional file at current.: 暂无附加文件。
No {0} at current.: 当前没有{0}。
No comments so far...: 目前还没有评论...
no discussion yet...: 目前没有讨论…
No Permission to Attend Contest: 您没有权限参加比赛
@@ -814,6 +814,7 @@ Temporary session: 临时会话
Terms of Service: 服务条款
Test data comes from: 测试数据来自
Testdata Config: 测试数据配置
testdata: 测试数据
Testdata: 测试数据
Text: 文本
The 'default' role applies to ALL REGISTERED USER.: default 角色作用于<b>所有已注册用户</b>
@@ -1,8 +1,7 @@
.page--problem_config
.section--problem-sidebar-testdata
.problem-sidebar-testdata__detail
max-height: 270px
overflow-y: auto
.problem-sidebar-testdata
max-height: 270px
overflow-y: auto
.col--size
width: 80px
@@ -1,4 +1,4 @@
import { normalizeSubtasks, readSubtasksFromFiles, size } from '@hydrooj/utils/lib/common';
import { normalizeSubtasks, readSubtasksFromFiles } from '@hydrooj/utils/lib/common';
import type { SubtaskType } from 'hydrooj/src/interface';
import $ from 'jquery';
import yaml from 'js-yaml';
@@ -11,7 +11,7 @@ import uploadFiles from 'vj/components/upload';
import download from 'vj/components/zipDownloader';
import { NamedPage } from 'vj/misc/Page';
import {
i18n, loadReactRedux, request, tpl,
i18n, loadReactRedux, pjax, request, tpl,
} from 'vj/utils';
const page = new NamedPage('problem_config', () => {
@@ -25,6 +25,8 @@ const page = new NamedPage('problem_config', () => {
await new Promise((resolve) => { input.onchange = resolve; });
await uploadFiles('./files', input.files, {
type: 'testdata',
sidebar: true,
pjax: true,
singleFileUploadCallback: (file) => {
reduxStore.dispatch({
type: 'CONFIG_ADD_TESTDATA',
@@ -34,13 +36,6 @@ const page = new NamedPage('problem_config', () => {
size: file.size,
},
});
$('.testdata-table tbody').append(
$(tpl`<tr data-filename="${file.name}" data-size="${file.size.toString()}">
<td class="col--name" title="${file.name}"><a href="./file/${file.name}?type=testdata">${file.name}</a></td>
<td class="col--size">${size(file.size)}</td>
<td class="col--operation"><a href="javascript:;" name="testdata__delete"><span class="icon icon-delete"></span></a></td>
</tr>`),
);
},
});
}
@@ -62,7 +57,7 @@ const page = new NamedPage('problem_config', () => {
type: 'CONFIG_DELETE_TESTDATA',
value: file,
});
$(ev.currentTarget).parent().parent().remove();
await pjax.request({ url: './files?testdata=false&sidebar=true', push: false });
} catch (error) {
Notification.error(error.message);
}
+4 -12
View File
@@ -1,4 +1,3 @@
import { size } from '@hydrooj/utils/lib/common';
import $ from 'jquery';
import _ from 'lodash';
import { ConfirmDialog } from 'vj/components/dialog';
@@ -9,7 +8,7 @@ import uploadFiles from 'vj/components/upload';
import download from 'vj/components/zipDownloader';
import { NamedPage } from 'vj/misc/Page';
import {
i18n, request, slideDown, slideUp, tpl,
i18n, pjax, request, slideDown, slideUp, tpl,
} from 'vj/utils';
const categories = {};
@@ -197,15 +196,8 @@ export default new NamedPage(['problem_create', 'problem_edit'], (pagename) => {
await new Promise((resolve) => { input.onchange = resolve; });
await uploadFiles('./files', input.files, {
type: 'additional_file',
singleFileUploadCallback: (file) => {
$('.additionalfile-table tbody').append(
$(tpl`<tr data-filename="${file.name}" data-size="${file.size.toString()}">
<td class="col--name" title="${file.name}"><a href="./file/${file.name}?type=testdata">${file.name}</a></td>
<td class="col--size">${size(file.size)}</td>
<td class="col--operation"><a href="javascript:;" name="testdata__delete"><span class="icon icon-delete"></span></a></td>
</tr>`),
);
},
sidebar: true,
pjax: true,
});
}
@@ -222,7 +214,7 @@ export default new NamedPage(['problem_create', 'problem_edit'], (pagename) => {
type: 'additional_file',
});
Notification.success(i18n('File have been deleted.'));
$(ev.currentTarget).parent().parent().remove();
await pjax.request({ url: './files?testdata=false&sidebar=true', push: false });
} catch (error) {
Notification.error(error.message);
}
@@ -12,10 +12,9 @@
&.tab--active
border-color: #ed5f82;
.section--problem-sidebar-additional_file
.problem-sidebar-additional_file__detail
height: 270px
overflow-y: auto
.problem-sidebar-additional_file
height: 270px
overflow-y: auto
.col--size
width: 80px
@@ -1,6 +1,6 @@
{% macro render(text, compact=false) %}
{% macro render(text, compact=false, args=[]) %}
<div class="nothing-placeholder{% if compact %} compact{% endif %}">
<div class="nothing-icon"></div>
{{ _(text) }}
{{ _(text).format(args) }}
</div>
{% endmacro %}
@@ -1,7 +1,7 @@
<div class="section__body typo" data-fragment-id="problem-description">
{% if not pdoc.data.length and not pdoc.reference %}
<blockquote class="warn">
<p>{{ _('No testdata at current.') }}</p>
<p>{{ _('No {0} at current.').format(_("testdata")) }}</p>
</blockquote>
{% endif %}
{% if typeof(pdoc.config) == 'string' %}
@@ -1,40 +0,0 @@
{% import "components/nothing.html" as nothing with context %}
<div class="section__body no-padding problem-files-additional_file" data-fragment-id="problem-files-additional_file">
{% if additional_file.length %}
<table class="data-table">
<colgroup>
<col class="col--checkbox">
<col class="col--name">
<col class="col--size">
</colgroup>
<thead>
<tr>
<th class="col--checkbox">
<label class="compact checkbox">
<input type="checkbox" name="select_all" data-checkbox-toggle="additional_file">
</label>
</th>
<th class="col--name">{{ _('Filename') }}</th>
<th class="col--size">{{ _('Size') }}</th>
</tr>
</thead>
<tbody>
{%- for file in additional_file -%}
<tr data-filename="{{ file.name }}" data-size="{{ file.size }}">
<td class="col--checkbox">
<label class="compact checkbox">
<input type="checkbox" data-checkbox-group="additional_file" data-checkbox-range>
</label>
</td>
<td class="col--name" data-preview="additional_file">
<a href="{{ url('problem_file_download', {pid: pdoc.docId, filename: file.name}) }}">{{ file.name }}</a>
</td>
<td class="col--size">{{ size(file.size) }}</td>
</tr>
{%- endfor -%}
</tbody>
</table>
{% else %}
{{ nothing.render('No additional file at current.') }}
{% endif %}
</div>
@@ -1,48 +0,0 @@
{% import "components/nothing.html" as nothing with context %}
<div class="section__body no-padding problem-files-testdata" data-fragment-id="problem-files-testdata">
{% if reference %}
<blockquote class="note">
<p>{{ _('This is a copy of another problem.') }}</p>
</blockquote>
{% elif testdata.length %}
<table class="data-table">
<colgroup>
<col class="col--checkbox">
<col class="col--name">
<col class="col--size">
</colgroup>
<thead>
<tr>
<th class="col--checkbox">
<label class="compact checkbox">
<input type="checkbox" name="select_all" data-checkbox-toggle="testdata">
</label>
</th>
<th class="col--name">{{ _('Filename') }}</th>
<th class="col--size">{{ _('Size') }}</th>
</tr>
</thead>
<tbody>
{%- for file in testdata -%}
<tr data-filename="{{ file.name }}" data-size="{{ file.size }}">
<td class="col--checkbox">
<label class="compact checkbox">
<input type="checkbox" data-checkbox-group="testdata" data-checkbox-range>
</label>
</td>
<td class="col--name" data-preview="testdata">
{% if handler.user.own(pdoc) or handler.user.hasPriv(PRIV.PRIV_READ_PROBLEM_DATA) or handler.user.hasPerm(perm.PERM_READ_PROBLEM_DATA) %}
<a href="{{ url('problem_file_download', {pid: pdoc.docId, filename: file.name, query:{type:'testdata'} }) }}">{{ file.name }}</a>
{% else %}
<p>{{ file.name }}</p>
{% endif %}
</td>
<td class="col--size">{{ size(file.size) }}</td>
</tr>
{%- endfor -%}
</tbody>
</table>
{% else %}
{{ nothing.render('No testdata at current.') }}
{% endif %}
</div>
@@ -0,0 +1,59 @@
{% import "components/nothing.html" as nothing with context %}
{% set _files = testdata if filetype == 'testdata' else additional_file %}
<div class="section__body no-padding problem-{{ 'sidebar' if sidebar else 'files' }}-{{ filetype }}" data-fragment-id="problem-files-{{ filetype }}"
{% if sidebar %}data-type="{{ filetype }}" data-sidebar{% endif %}>
{% if reference %}
<blockquote class="note">
<p>{{ _('This is a copy of another problem.') }}</p>
</blockquote>
{% elif _files.length %}
<table class="data-table {{ filetype }}-table">
<colgroup>
{% if not sidebar %}<col class="col--checkbox">{% endif %}
<col class="col--name">
<col class="col--size">
{% if sidebar %}<col class="col--operation">{% endif %}
</colgroup>
<thead>
<tr>
{% if not sidebar %}
<th class="col--checkbox">
<label class="compact checkbox">
<input type="checkbox" name="select_all" data-checkbox-toggle="{{ filetype }}">
</label>
</th>
{% endif %}
<th class="col--name">{{ _('Filename') }}</th>
<th class="col--size">{{ _('Size') }}</th>
{% if sidebar %}<th class="col--operation"><span class="icon icon-wrench"></span></th>{% endif %}
</tr>
</thead>
<tbody>
{%- for file in _files -%}
<tr data-filename="{{ file.name }}" data-size="{{ file.size }}">
{% if not sidebar %}
<td class="col--checkbox">
<label class="compact checkbox">
<input type="checkbox" data-checkbox-group="{{ filetype }}" data-checkbox-range>
</label>
</td>
{% endif %}
<td class="col--name" data-preview="{{ filetype }}">
{% if filetype != 'testdata' or handler.user.own(pdoc) or handler.user.hasPriv(PRIV.PRIV_READ_PROBLEM_DATA) or handler.user.hasPerm(perm.PERM_READ_PROBLEM_DATA) %}
<a href="{{ url('problem_file_download', {pid: pdoc.docId, filename: file.name, query:{type:filetype} }) }}">{{ file.name }}</a>
{% else %}
<p>{{ file.name }}</p>
{% endif %}
</td>
<td class="col--size">{{ size(file.size) }}</td>
{% if sidebar %}
<td class="col--operation"><a href="javascript:;" name="{{ filetype }}__delete"><span class="icon icon-delete"></span></a></td>
{% endif %}
</tr>
{%- endfor -%}
</tbody>
</table>
{% else %}
{{ nothing.render('No {0} at current.', compact=sidebar, args=[_(filetype)]) }}
{% endif %}
</div>
@@ -1,48 +0,0 @@
{% import "components/nothing.html" as nothing with context %}
{% import "components/problem.html" as problem with context %}
<div class="section side section--problem-sidebar-additional_file">
<div class="section__header">
<h1 class="section__title">{{ additional_file.length|default(0) }} {{_('Additional File')}}</h1>
</div>
<ol class="menu">
<li class="menu__item flex-row">
<a class="menu__link half" href="javascript:;" name="additional_file__upload">
<span class="icon icon-upload"></span> {{ _('Upload') }}
</a>
<a class="menu__link half" href="javascript:;" name="additional_file__download">
<span class="icon icon-download"></span> {{ _('Download All') }}
</a>
</li>
</ol>
<div class="section__body no-padding problem-sidebar-additional_file__detail" data-fragment-id="problem-sidebar-additional_file">
{% if additional_file.length %}
<table class="data-table additionalfile-table">
<colgroup>
<col class="col--name">
<col class="col--size">
<col class="col--operation">
</colgroup>
<thead>
<tr>
<th class="col--name">{{ _('Filename') }}</th>
<th class="col--size">{{ _('Size') }}</th>
<th class="col--operation"><span class="icon icon-wrench"></span></th>
</tr>
</thead>
<tbody>
{%- for file in additional_file -%}
<tr data-filename="{{ file.name }}" data-size="{{ file.size }}">
<td class="col--name" title="{{ file.name }}" data-preview="additional_file">
<a href="{{ url('problem_file_download', {pid: pdoc.docId, filename: file.name}) }}">{{ file.name }}</a>
</td>
<td class="col--size">{{ size(file.size) }}</td>
<td class="col--operation"><a href="javascript:;"name="additional_file__delete"><span class="icon icon-delete"></span></a></td>
</tr>
{%- endfor -%}
</tbody>
</table>
{% else %}
{{ nothing.render('No additional file at current.',compact = true) }}
{% endif %}
</div>
</div>
@@ -1,47 +0,0 @@
{% import "components/nothing.html" as nothing with context %}
{% import "components/problem.html" as problem with context %}
<div class="section side section--problem-sidebar-testdata">
<div class="section__header">
<h1 class="section__title">{{ testdata.length if testdata.length else 0 }} {{_('Testdata')}}</h1>
</div>
<ol class="menu">
<li class="menu__item flex-row">
<a class="menu__link half" href="javascript:;" name="testdata__upload">
<span class="icon icon-upload"></span> {{ _('Upload') }}
</a>
<a class="menu__link half" href="javascript:;" name="testdata__download__all">
<span class="icon icon-download"></span> {{ _('Download All') }}
</a>
</li>
<li class="menu__seperator"></li>
</ol>
<div class="section__body no-padding problem-sidebar-testdata__detail" data-fragment-id="problem-sidebar-testdata">
{% if testdata.length %}
<table class="data-table testdata-table">
<colgroup>
<col class="col--name">
<col class="col--size">
<col class="col--operation">
</colgroup>
<thead>
<tr>
<th class="col--name">{{ _('Filename') }}</th>
<th class="col--size">{{ _('Size') }}</th>
<th class="col--operation"><span class="icon icon-wrench"></span></th>
</tr>
</thead>
<tbody>
{%- for file in testdata -%}
<tr data-filename="{{ file.name }}" data-size="{{ file.size }}">
<td class="col--name" title="{{ file.name }}" data-preview="testdata"><a href="{{ url('problem_file_download', {pid: pdoc.docId, filename: file.name, query:{type:'testdata'} }) }}">{{ file.name }}</a></td>
<td class="col--size">{{ size(file.size) }}</td>
<td class="col--operation"><a href="javascript:;" name="testdata__delete"><span class="icon icon-delete"></span></a></td>
</tr>
{%- endfor -%}
</tbody>
</table>
{% else %}
{{ nothing.render('No testdata at current.', compact = true) }}
{% endif %}
</div>
</div>
@@ -13,7 +13,25 @@
</div>
</div>
<div class="medium-3 columns">
{% include "partials/problem_sidebar_testdata.html" %}
<div class="section side">
<div class="section__header">
<h1 class="section__title">{{ testdata.length|default(0) }} {{_('Testdata')}}</h1>
</div>
<ol class="menu">
<li class="menu__item flex-row">
<a class="menu__link half" href="javascript:;" name="testdata__upload">
<span class="icon icon-upload"></span> {{ _('Upload') }}
</a>
<a class="menu__link half" href="javascript:;" name="testdata__download__all">
<span class="icon icon-download"></span> {{ _('Download All') }}
</a>
</li>
<li class="menu__seperator"></li>
</ol>
{% set sidebar = true %}
{% set filetype = "testdata" %}
{% include "partials/problem_files.html" %}
</div>
{% set owner_udoc = udoc %}
{% include "partials/problem_sidebar.html" %}
</div>
@@ -92,7 +92,24 @@
</div>
</div>
{% if page_name == 'problem_edit' %}
{% include "partials/problem_sidebar_additional_file.html" %}
<div class="section side">
<div class="section__header">
<h1 class="section__title">{{ additional_file.length|default(0) }} {{_('Additional File')}}</h1>
</div>
<ol class="menu">
<li class="menu__item flex-row">
<a class="menu__link half" href="javascript:;" name="additional_file__upload">
<span class="icon icon-upload"></span> {{ _('Upload') }}
</a>
<a class="menu__link half" href="javascript:;" name="additional_file__download">
<span class="icon icon-download"></span> {{ _('Download All') }}
</a>
</li>
</ol>
{% set sidebar = true %}
{% set filetype = "additional_file" %}
{% include "partials/problem_files.html" %}
</div>
{% set owner_udoc = udoc %}
{% include "partials/problem_sidebar.html" %}
{% else %}
@@ -14,7 +14,8 @@
</div>
</div>
{{ noscript_note.render() }}
{% include "partials/problem_files-testdata.html" %}
{% set filetype = "testdata" %}
{% include "partials/problem_files.html" %}
{% if not reference and (handler.user.own(pdoc) or handler.user.hasPerm(perm.PERM_READ_PROBLEM_DATA) or handler.user.hasPriv(PRIV.PRIV_READ_PROBLEM_DATA)) %}
<div class="section__body">
<button class="rounded button" name="download_selected_testdata">{{ _('Download Selected') }}</button>
@@ -36,7 +37,8 @@
{% endif %}
</div>
</div>
{% include "partials/problem_files-additional_file.html" %}
{% set filetype = "additional_file" %}
{% include "partials/problem_files.html" %}
<div class="section__body">
<button class="rounded button" name="download_selected_file">{{ _('Download Selected') }}</button>
{% if handler.user.own(pdoc, perm.PERM_EDIT_PROBLEM_SELF) or handler.user.hasPerm(perm.PERM_EDIT_PROBLEM) %}