S2OJ/web/app/controllers/problem_data_manage.php

944 lines
33 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
if (!Auth::check()) {
redirectToLogin();
}
requireLib('bootstrap5');
requirePHPLib('form');
requirePHPLib('judger');
requirePHPLib('data');
UOJProblem::init(UOJRequest::get('id')) || UOJResponse::page404();
UOJProblem::cur()->userCanManage(Auth::user()) || UOJResponse::page403();
$problem = UOJProblem::info();
$problem_extra_config = UOJProblem::cur()->getExtraConfig();
$data_dir = "/var/uoj_data/${problem['id']}";
function echoFileNotFound($file_name) {
echo '<h5>', htmlspecialchars($file_name), '</h5>';
echo '<div class="small text-danger"> ', '文件未找到', '</div>';
}
function echoFilePre($file_name) {
global $data_dir;
$file_full_name = $data_dir . '/' . $file_name;
$finfo = finfo_open(FILEINFO_MIME);
$mimetype = finfo_file($finfo, $file_full_name);
if ($mimetype === false) {
echoFileNotFound($file_name);
return;
}
finfo_close($finfo);
echo '<h5 class="mb-1">', htmlspecialchars($file_name), '</h5>';
echo '<div class="text-muted small mb-1 font-monospace">', $mimetype, '</div>';
echo '<pre class="bg-light rounded uoj-pre">', "\n";
$output_limit = 1000;
if (strStartWith($mimetype, 'text/')) {
echo htmlspecialchars(uojFilePreview($file_full_name, $output_limit));
} else {
echo htmlspecialchars(uojFilePreview($file_full_name, $output_limit, 'binary'));
}
echo "\n</pre>";
}
// 上传数据
if ($_POST['problem_data_file_submit'] == 'submit') {
if ($_FILES["problem_data_file"]["error"] > 0) {
$errmsg = "Error: " . $_FILES["problem_data_file"]["error"];
becomeMsgPage('<div>' . $errmsg . '</div><a href="/problem/' . $problem['id'] . '/manage/data">返回</a>');
} else {
$zip_mime_types = array('application/zip', 'application/x-zip', 'application/x-zip-compressed');
if (in_array($_FILES["problem_data_file"]["type"], $zip_mime_types) || $_FILES["problem_data_file"]["type"] == 'application/octet-stream' && substr($_FILES["problem_data_file"]["name"], -4) == '.zip') {
$up_filename = "/tmp/" . rand(0, 100000000) . "data.zip";
move_uploaded_file($_FILES["problem_data_file"]["tmp_name"], $up_filename);
$zip = new ZipArchive;
if ($zip->open($up_filename) === TRUE) {
$zip->extractTo("/var/uoj_data/upload/{$problem['id']}");
$zip->close();
exec("cd /var/uoj_data/upload/{$problem['id']}; if [ `find . -maxdepth 1 -type f`File = File ]; then for sub_dir in `find -maxdepth 1 -type d ! -name .`; do mv -f \$sub_dir/* . && rm -rf \$sub_dir; done; fi");
echo "<script>alert('上传成功!')</script>";
} else {
$errmsg = "解压失败!";
becomeMsgPage('<div>' . $errmsg . '</div><a href="/problem/' . $problem['id'] . '/manage/data">返回</a>');
}
unlink($up_filename);
} else {
$errmsg = "请上传zip格式";
becomeMsgPage('<div>' . $errmsg . '</div><a href="/problem/' . $problem['id'] . '/manage/data">返回</a>');
}
}
}
// 添加配置文件
if ($_POST['problem_settings_file_submit'] == 'submit') {
if ($_POST['use_builtin_checker'] and $_POST['n_tests']) {
$set_filename = "/var/uoj_data/upload/{$problem['id']}/problem.conf";
$has_legacy = false;
if (file_exists($set_filename)) {
$has_legacy = true;
unlink($set_filename);
}
$setfile = fopen($set_filename, "w");
fwrite($setfile, "use_builtin_judger on\n");
if ($_POST['use_builtin_checker'] != 'ownchk') {
fwrite($setfile, "use_builtin_checker " . $_POST['use_builtin_checker'] . "\n");
}
fwrite($setfile, "n_tests " . $_POST['n_tests'] . "\n");
if ($_POST['n_ex_tests']) {
fwrite($setfile, "n_ex_tests " . $_POST['n_ex_tests'] . "\n");
} else {
fwrite($setfile, "n_ex_tests 0\n");
}
if ($_POST['n_sample_tests']) {
fwrite($setfile, "n_sample_tests " . $_POST['n_sample_tests'] . "\n");
} else {
fwrite($setfile, "n_sample_tests 0\n");
}
if (isset($_POST['input_pre'])) {
fwrite($setfile, "input_pre " . $_POST['input_pre'] . "\n");
}
if (isset($_POST['input_suf'])) {
fwrite($setfile, "input_suf " . $_POST['input_suf'] . "\n");
}
if (isset($_POST['output_pre'])) {
fwrite($setfile, "output_pre " . $_POST['output_pre'] . "\n");
}
if (isset($_POST['output_suf'])) {
fwrite($setfile, "output_suf " . $_POST['output_suf'] . "\n");
}
fwrite($setfile, "time_limit " . ($_POST['time_limit'] ?: 1) . "\n");
fwrite($setfile, "memory_limit " . ($_POST['memory_limit'] ?: 256) . "\n");
fclose($setfile);
if (!$has_legacy) {
echo "<script>alert('添加成功!请点击「检验配置并同步数据」按钮以应用新配置文件。')</script>";
} else {
echo "<script>alert('替换成功!请点击「检验配置并同步数据」按钮以应用新配置文件。')</script>";
}
} else {
$errmsg = "添加配置文件失败,请检查是否所有必填输入框都已填写!";
becomeMsgPage('<div>' . $errmsg . '</div><a href="/problem/' . $problem['id'] . '/manage/data">返回</a>');
}
}
$info_form = new UOJBs4Form('info');
$http_host = HTML::escape(UOJContext::httpHost());
$attachment_url = HTML::url("/download.php?type=attachment&id={$problem['id']}");
$info_form->appendHTML(
<<<EOD
<div class="form-group row">
<label class="col-sm-3 control-label">problem_{$problem['id']}_attachment.zip</label>
<div class="col-sm-9">
<div class="form-control-static">
<a class="text-decoration-none" href="$attachment_url">$attachment_url</a>
</div>
</div>
</div>
EOD
);
$download_url = HTML::url("/download.php?type=problem&id={$problem['id']}");
$info_form->appendHTML(
<<<EOD
<div class="form-group row">
<label class="col-sm-3 control-label">problem_{$problem['id']}.zip</label>
<div class="col-sm-9">
<div class="form-control-static">
<a class="text-decoration-none" href="$download_url">$download_url</a>
</div>
</div>
</div>
EOD
);
$info_form->appendHTML(
<<<EOD
<div class="form-group row">
<label class="col-sm-3 control-label">testlib.h</label>
<div class="col-sm-9">
<div class="form-control-static">
<a class="text-decoration-none" href="/download.php?type=testlib.h">下载</a>
</div>
</div>
</div>
EOD
);
$esc_submission_requirement = HTML::escape(json_encode(json_decode($problem['submission_requirement']), JSON_PRETTY_PRINT));
$info_form->appendHTML(
<<<EOD
<div class="form-group row">
<label class="col-sm-3 control-label">提交文件配置</label>
<div class="col-sm-9">
<div class="form-control-static"><pre class="uoj-pre bg-light rounded">
$esc_submission_requirement
</pre>
</div>
</div>
</div>
EOD
);
$esc_extra_config = HTML::escape(json_encode(json_decode($problem['extra_config']), JSON_PRETTY_PRINT));
$info_form->appendHTML(
<<<EOD
<div class="form-group row">
<label class="col-sm-3 control-label">其它配置</label>
<div class="col-sm-9">
<div class="form-control-static"><pre class="uoj-pre bg-light rounded">
$esc_extra_config
</pre>
</div>
</div>
</div>
EOD
);
if (isSuperUser($myUser)) {
$info_form->addVInput(
'submission_requirement',
'text',
'提交文件配置',
$problem['submission_requirement'],
function ($submission_requirement, &$vdata) {
$submission_requirement = json_decode($submission_requirement, true);
if ($submission_requirement === null) {
return '不是合法的JSON';
}
$vdata['submission_requirement'] = json_encode($submission_requirement);
},
null
);
$info_form->addVInput(
'extra_config',
'text',
'其它配置',
$problem['extra_config'],
function ($extra_config, &$vdata) {
$extra_config = json_decode($extra_config, true);
if ($extra_config === null) {
return '不是合法的JSON';
}
$vdata['extra_config'] = json_encode($extra_config);
},
null
);
$info_form->handle = function (&$vdata) use ($problem) {
DB::update([
"update problems",
"set", [
"submission_requirement" => $vdata['submission_requirement'],
"extra_config" => $vdata['extra_config']
], "where", ["id" => $problem['id']]
]);
};
} else {
$info_form->no_submit = true;
}
$problem_conf = getUOJConf("$data_dir/problem.conf");
function displayProblemConf(UOJProblemDataDisplayer $self) {
global $info_form;
$info_form->printHTML();
echo '<hr class="my-3">';
$self->echoProblemConfTable();
$self->echoFilePre('problem.conf');
}
function addTestsTab(UOJProblemDataDisplayer $disp, array $problem_conf) {
$n_tests = getUOJConfVal($problem_conf, 'n_tests', 10);
if (!validateUInt($n_tests)) {
$disp->setProblemConfRowStatus('n_tests', 'danger');
return false;
}
$inputs = [];
$outputs = [];
for ($num = 1; $num <= $n_tests; $num++) {
$inputs[$num] = getUOJProblemInputFileName($problem_conf, $num);
$outputs[$num] = getUOJProblemOutputFileName($problem_conf, $num);
unset($disp->rest_data_files[$inputs[$num]]);
unset($disp->rest_data_files[$outputs[$num]]);
}
$disp->addTab('tests', function ($self) use ($inputs, $outputs, $n_tests) {
for ($num = 1; $num <= $n_tests; $num++) {
echo '<div class="row">';
echo '<div class="col-md-6">';
$self->echoFilePre($inputs[$num]);
echo '</div>';
echo '<div class="col-md-6">';
$self->echoFilePre($outputs[$num]);
echo '</div>';
echo '</div>';
}
});
return true;
}
function addExTestsTab(UOJProblemDataDisplayer $disp, array $problem_conf) {
$has_extra_tests = !(isset($problem_conf['submit_answer']) && $problem_conf['submit_answer'] == 'on');
if (!$has_extra_tests) {
return false;
}
$n_ex_tests = getUOJConfVal($problem_conf, 'n_ex_tests', 0);
if (!validateUInt($n_ex_tests)) {
$disp->setProblemConfRowStatus('n_ex_tests', 'danger');
return false;
}
if ($n_ex_tests == 0) {
return false;
}
$inputs = [];
$outputs = [];
for ($num = 1; $num <= $n_ex_tests; $num++) {
$inputs[$num] = getUOJProblemExtraInputFileName($problem_conf, $num);
$outputs[$num] = getUOJProblemExtraOutputFileName($problem_conf, $num);
unset($disp->rest_data_files[$inputs[$num]]);
unset($disp->rest_data_files[$outputs[$num]]);
}
$disp->addTab('extra tests', function ($self) use ($inputs, $outputs, $n_ex_tests) {
for ($num = 1; $num <= $n_ex_tests; $num++) {
echo '<div class="row">';
echo '<div class="col-md-6">';
$self->echoFilePre($inputs[$num]);
echo '</div>';
echo '<div class="col-md-6">';
$self->echoFilePre($outputs[$num]);
echo '</div>';
echo '</div>';
}
});
return true;
}
function addSrcTab(UOJProblemDataDisplayer $disp, $tab_name, string $name) {
$src = UOJLang::findSourceCode($name, '', [$disp, 'isFile']);
if ($src !== false) {
unset($disp->rest_data_files[$src['path']]);
}
unset($disp->rest_data_files[$name]);
$disp->addTab($tab_name, function ($self) use ($name, $src) {
if ($src !== false) {
$self->echoFilePre($src['path']);
}
$self->echoFilePre($name);
});
return true;
}
function getDataDisplayer() {
$disp = new UOJProblemDataDisplayer(UOJProblem::cur());
$problem_conf = UOJProblem::cur()->getProblemConfArray();
if ($problem_conf === -1) {
return $disp->addTab('problem.conf', function ($self) {
global $info_form;
$info_form->printHTML();
echo '<hr class="my-3">';
$self->echoFileNotFound('problem.conf');
});
} elseif ($problem_conf === -2) {
return $disp->addTab('problem.conf', function ($self) {
global $info_form;
$info_form->printHTML();
echo '<hr class="my-3">';
echo '<div class="fw-bold text-danger">problem.conf 文件格式有误</div>';
$self->echoFilePre('problem.conf');
});
}
$disp->setProblemConf($problem_conf);
unset($disp->rest_data_files['problem.conf']);
unset($disp->rest_data_files['download.zip']);
$disp->addTab('problem.conf', 'displayProblemConf');
addTestsTab($disp, $problem_conf);
addExTestsTab($disp, $problem_conf);
$judger_name = getUOJConfVal($problem_conf, 'use_builtin_judger', null);
if ($judger_name === null) {
return $disp;
} elseif ($judger_name === 'on') {
if (!isset($problem_conf['interaction_mode'])) {
if (isset($problem_conf['use_builtin_checker'])) {
$disp->addTab('checker', function ($self) {
echo '<h4>use builtin checker : ', $self->problem_conf['use_builtin_checker']['val'], '</h4>';
});
} else {
addSrcTab($disp, 'checker', 'chk');
}
}
if (UOJProblem::info('hackable')) {
addSrcTab($disp, 'standard', 'std');
addSrcTab($disp, 'validator', 'val');
}
if (isset($problem_conf['interaction_mode'])) {
addSrcTab($disp, 'interactor', 'interactor');
}
return $disp;
} else {
return $disp->setProblemConfRowStatus('use_builtin_judger', 'danger');
}
}
$data_disp = getDataDisplayer();
if (isset($_GET['display_file'])) {
if (!isset($_GET['file_name'])) {
echoFileNotFound('');
} else {
$data_disp->displayFile($_GET['file_name']);
}
die();
}
$hackable_form = new UOJBs4Form('hackable');
$hackable_form->handle = function () use ($problem) {
$problem['hackable'] = !$problem['hackable'];
$ret = dataSyncProblemData($problem);
if ($ret) {
becomeMsgPage('<div>' . $ret . '</div><a href="/problem/' . $problem['id'] . '/manage/data">返回</a>');
}
$hackable = $problem['hackable'] ? 1 : 0;
DB::update([
"update problems",
"set", ["hackable" => $hackable],
"where", ["id" => $problem['id']]
]);
};
$hackable_form->submit_button_config['class_str'] = 'btn btn-warning d-block w-100';
$hackable_form->submit_button_config['text'] = $problem['hackable'] ? '禁用 Hack 功能' : '启用 Hack 功能';
$hackable_form->submit_button_config['smart_confirm'] = '';
$data_form = new UOJBs4Form('data');
$data_form->handle = function () use ($problem) {
set_time_limit(60 * 5);
$ret = dataSyncProblemData($problem, Auth::user());
if ($ret) {
becomeMsgPage('<div>' . $ret . '</div><a href="/problem/' . $problem['id'] . '/manage/data">返回</a>');
}
};
$data_form->submit_button_config['class_str'] = 'btn btn-danger d-block w-100';
$data_form->submit_button_config['text'] = '检验配置并同步数据';
$data_form->submit_button_config['smart_confirm'] = '';
$clear_data_form = new UOJBs4Form('clear_data');
$clear_data_form->handle = function () {
global $problem;
dataClearProblemData($problem);
};
$clear_data_form->submit_button_config['class_str'] = 'btn btn-danger d-block w-100';
$clear_data_form->submit_button_config['text'] = '清空题目数据';
$clear_data_form->submit_button_config['smart_confirm'] = '';
$rejudge_form = new UOJBs4Form('rejudge');
$rejudge_form->handle = function () {
UOJSubmission::rejudgeProblem(UOJProblem::cur());
};
$rejudge_form->succ_href = "/submissions?problem_id={$problem['id']}";
$rejudge_form->submit_button_config['class_str'] = 'btn btn-danger d-block w-100';
$rejudge_form->submit_button_config['text'] = '重测该题';
$rejudge_form->submit_button_config['smart_confirm'] = '';
$rejudgege97_form = new UOJBs4Form('rejudgege97');
$rejudgege97_form->handle = function () {
UOJSubmission::rejudgeProblemGe97(UOJProblem::cur());
};
$rejudgege97_form->succ_href = "/submissions?problem_id={$problem['id']}";
$rejudgege97_form->submit_button_config['class_str'] = 'btn btn-danger d-block w-100';
$rejudgege97_form->submit_button_config['text'] = '重测 >=97 的程序';
$rejudgege97_form->submit_button_config['smart_confirm'] = '';
$view_type_form = new UOJBs4Form('view_type');
$view_type_form->addVSelect(
'view_content_type',
array(
'NONE' => '禁止',
'ALL_AFTER_AC' => 'AC后',
'ALL' => '所有人'
),
'查看提交文件:',
$problem_extra_config['view_content_type']
);
$view_type_form->addVSelect(
'view_all_details_type',
array(
'NONE' => '禁止',
'SELF' => '仅自己',
'ALL_AFTER_AC' => 'AC后',
'ALL' => '所有人'
),
'查看全部详细信息:',
$problem_extra_config['view_all_details_type']
);
$view_type_form->addVSelect(
'view_details_type',
array(
'NONE' => '禁止',
'SELF' => '仅自己',
'ALL_AFTER_AC' => 'AC后',
'ALL' => '所有人'
),
'查看测试点详细信息:',
$problem_extra_config['view_details_type']
);
$view_type_form->handle = function () {
global $problem, $problem_extra_config;
$config = $problem_extra_config;
$config['view_content_type'] = $_POST['view_content_type'];
$config['view_all_details_type'] = $_POST['view_all_details_type'];
$config['view_details_type'] = $_POST['view_details_type'];
$esc_config = json_encode($config);
DB::update([
"update problems",
"set", ["extra_config" => $esc_config],
"where", ["id" => $problem['id']]
]);
};
$view_type_form->submit_button_config['class_str'] = 'btn btn-warning d-block w-100 mt-2';
$solution_view_type_form = new UOJBs4Form('solution_view_type');
$solution_view_type_form->addVSelect(
'view_solution_type',
array(
'NONE' => '禁止',
'ALL_AFTER_AC' => 'AC后',
'ALL' => '所有人'
),
'查看题解:',
$problem_extra_config['view_solution_type']
);
$solution_view_type_form->addVSelect(
'submit_solution_type',
array(
'NONE' => '禁止',
'ALL_AFTER_AC' => 'AC后',
'ALL' => '所有人'
),
'提交题解:',
$problem_extra_config['submit_solution_type']
);
$solution_view_type_form->handle = function () {
global $problem, $problem_extra_config;
$config = $problem_extra_config;
$config['view_solution_type'] = $_POST['view_solution_type'];
$config['submit_solution_type'] = $_POST['submit_solution_type'];
$esc_config = DB::escape(json_encode($config));
DB::update([
"update problems",
"set", ["extra_config" => $esc_config],
"where", ["id" => $problem['id']]
]);
};
$solution_view_type_form->submit_button_config['class_str'] = 'btn btn-warning d-block w-100 mt-2';
$difficulty_form = new UOJBs4Form('difficulty');
$difficulty_form->addVInput(
'difficulty',
'text',
'难度系数',
$problem_extra_config['difficulty'],
function ($str) {
if (!is_numeric($str)) {
return '难度系数必须是一个数字';
}
return '';
},
null
);
$difficulty_form->handle = function () {
global $problem, $problem_extra_config;
$config = $problem_extra_config;
$config['difficulty'] = $_POST['difficulty'] + 0;
$esc_config = DB::escape(json_encode($config));
DB::query("update problems set extra_config = '$esc_config' where id = '{$problem['id']}'");
};
$difficulty_form->submit_button_config['class_str'] = 'btn btn-warning d-block w-100 mt-2';
if ($problem['hackable']) {
$test_std_form = new UOJBs4Form('test_std');
$test_std_form->handle = function () use ($problem, $data_disp) {
$user_std = UOJUser::query('std');
if (!$user_std) {
UOJResponse::message('Please create an user named "std"');
}
$requirement = json_decode($problem['submission_requirement'], true);
$src_std = UOJLang::findSourceCode('std', '', [$data_disp, 'isFile']);
if ($src_std === false) {
UOJResponse::message('未找到std');
}
$zip_file_name = FS::randomAvailableSubmissionFileName();
$zip_file = new ZipArchive();
if ($zip_file->open(UOJContext::storagePath() . $zip_file_name, ZipArchive::CREATE) !== true) {
UOJResponse::message('提交失败');
}
$content = [];
$content['file_name'] = $zip_file_name;
$content['config'] = [];
$tot_size = 0;
foreach ($requirement as $req) {
if ($req['type'] == "source code") {
$content['config'][] = ["{$req['name']}_language", $src_std['lang']];
if ($zip_file->addFromString($req['file_name'], $data_disp->getFile($src_std['path'])) === false) {
$zip_file->close();
unlink(UOJContext::storagePath() . $zip_file_name);
UOJResponse::message('提交失败');
}
$tot_size += $zip_file->statName($req['file_name'])['size'];
}
}
$zip_file->close();
$content['config'][] = ['validate_input_before_test', 'on'];
$content['config'][] = ['problem_id', $problem['id']];
$esc_content = json_encode($content);
$result = [];
$result['status'] = "Waiting";
$result_json = json_encode($result);
$is_hidden = $problem['is_hidden'] ? 1 : 0;
DB::insert([
"insert into submissions",
"(problem_id, submit_time, submitter, content, language, tot_size, status, result, is_hidden)",
"values", DB::tuple([
$problem['id'], DB::now(), $user_std['username'], $esc_content,
$src_std['lang'], $tot_size, $result['status'], $result_json, $is_hidden
])
]);
};
$test_std_form->succ_href = "/submissions?problem_id={$problem['id']}";
$test_std_form->submit_button_config['class_str'] = 'btn btn-danger d-block w-100';
$test_std_form->submit_button_config['text'] = '检验数据正确性';
$test_std_form->runAtServer();
}
$hackable_form->runAtServer();
$view_type_form->runAtServer();
$solution_view_type_form->runAtServer();
$difficulty_form->runAtServer();
$data_form->runAtServer();
$clear_data_form->runAtServer();
$rejudge_form->runAtServer();
$rejudgege97_form->runAtServer();
$info_form->runAtServer();
?>
<?php echoUOJPageHeader(HTML::stripTags($problem['title']) . ' - 数据 - 题目管理') ?>
<div class="row">
<!-- left col -->
<div class="col-12 col-lg-9">
<h1>
#<?= $problem['id'] ?>. <?= $problem['title'] ?> 管理
</h1>
<ul class="nav nav-pills my-3" role="tablist">
<li class="nav-item">
<a class="nav-link" href="/problem/<?= $problem['id'] ?>/manage/statement" role="tab">
题面
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/problem/<?= $problem['id'] ?>/manage/managers" role="tab">
管理者
</a>
</li>
<li class="nav-item">
<a class="nav-link active" href="/problem/<?= $problem['id'] ?>/manage/data" role="tab">
数据
</a>
</li>
</ul>
<div class="card">
<div class="card-header" id="div-file_list">
<ul class="nav nav-tabs card-header-tabs">
<?php $data_disp->echoAllTabs('problem.conf'); ?>
</ul>
</div>
<div class="card-body" id="div-file_content">
<?php $data_disp->displayFile('problem.conf'); ?>
</div>
<script type="text/javascript">
curFileName = '';
$('#div-file_list a').click(function(e) {
$('#div-file_content').html('<h3>Loading...</h3>');
$(this).tab('show');
var fileName = $(this).text();
curFileName = fileName;
$.get('/problem/<?= $problem['id'] ?>/manage/data', {
display_file: '',
file_name: fileName
},
function(data) {
if (curFileName != fileName) {
return;
}
$('#div-file_content').html(data);
},
'html'
);
return false;
});
</script>
</div>
</div>
<!-- right col -->
<aside class="col-12 col-lg-3 mt-3 mt-lg-0 d-flex flex-column">
<div class="card card-default mt-3 mt-lg-0 mb-2 order-2 order-lg-1">
<ul class="nav nav-pills nav-fill flex-column" role="tablist">
<li class="nav-item text-start">
<a href="/problem/<?= $problem['id'] ?>" class="nav-link" role="tab">
<i class="bi bi-journal-text"></i>
<?= UOJLocale::get('problems::statement') ?>
</a>
</li>
<li class="nav-item text-start">
<a href="/problem/<?= $problem['id'] ?>/solutions" class="nav-link" role="tab">
<i class="bi bi-journal-bookmark"></i>
<?= UOJLocale::get('problems::solutions') ?>
</a>
</li>
<li class="nav-item text-start">
<a class="nav-link" href="/problem/<?= $problem['id'] ?>/statistics">
<i class="bi bi-graph-up"></i>
<?= UOJLocale::get('problems::statistics') ?>
</a>
</li>
<li class="nav-item text-start">
<a class="nav-link active" href="#" role="tab">
<i class="bi bi-sliders"></i>
<?= UOJLocale::get('problems::manage') ?>
</a>
</li>
</ul>
<div class="card-footer bg-transparent">
评价:<?= ClickZans::getBlock('P', $problem['id'], $problem['zan']) ?>
</div>
</div>
<div class="order-1 order-lg-2">
<div>
<?php if ($problem['hackable']) : ?>
<i class="bi bi-check-lg text-success"></i> Hack 功能已启用
<?php else : ?>
<i class="bi bi-x-lg text-danger"></i> Hack 功能已禁用
<?php endif ?>
<?php $hackable_form->printHTML() ?>
</div>
<?php if ($problem['hackable']) : ?>
<div class="mt-2">
<?php $test_std_form->printHTML() ?>
</div>
<?php endif ?>
<div class="mt-2">
<button id="button-display_view_type" type="button" class="btn btn-primary d-block w-100" onclick="$('#div-view_type').toggle('fast');">提交记录可视权限</button>
<div class="mt-2" id="div-view_type" style="display:none; padding-left:5px; padding-right:5px;">
<?php $view_type_form->printHTML(); ?>
</div>
</div>
<div class="mt-2">
<button id="button-solution_view_type" type="button" class="btn btn-primary d-block w-100" onclick="$('#div-solution_view_type').toggle('fast');">题解可视权限</button>
<div class="mt-2" id="div-solution_view_type" style="display:none; padding-left:5px; padding-right:5px;">
<?php $solution_view_type_form->printHTML(); ?>
</div>
</div>
<div class="mt-2">
<?php $data_form->printHTML(); ?>
</div>
<div class="mt-2">
<?php $clear_data_form->printHTML(); ?>
</div>
<div class="mt-2">
<?php $rejudge_form->printHTML(); ?>
</div>
<div class="mt-2">
<?php $rejudgege97_form->printHTML(); ?>
</div>
<div class="mt-2">
<button type="button" class="btn d-block w-100 btn-primary" data-bs-toggle="modal" data-bs-target="#UploadDataModal">上传数据</button>
</div>
<div class="mt-2">
<button type="button" class="btn d-block w-100 btn-primary" data-bs-toggle="modal" data-bs-target="#ProblemSettingsFileModal">试题配置</button>
</div>
<div class="mt-2">
<button id="button-difficulty" type="button" class="btn d-block w-100 btn-primary" onclick="$('#div-difficulty').toggle('fast');">难度系数</button>
<div class="mt-2" id="div-difficulty" style="display:none; padding-left:5px; padding-right:5px;">
<?php $difficulty_form->printHTML(); ?>
</div>
</div>
</div>
</aside>
</div>
<div class="modal fade" id="UploadDataModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" id="myModalLabel">上传数据</h4>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form action="" method="post" enctype="multipart/form-data" role="form">
<div class="modal-body">
<label class="form-label" for="problem_data_file">上传 zip 文件</label>
<input class="form-control" type="file" name="problem_data_file" id="problem_data_file" accept=".zip">
<p class="form-text">
说明:请将所有数据放置于压缩包根目录内。若压缩包内仅存在文件夹而不存在文件,则会将这些一级子文件夹下的内容移动到根目录下,然后这些一级子文件夹删除;若这些子文件夹内存在同名文件,则会发生随机替换,仅保留一个副本。
</p>
<!-- hidden input for server-side check -->
<input type="hidden" name="problem_data_file_submit" value="submit">
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-success">上传</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button>
</div>
</form>
</div>
</div>
</div>
<div class="modal fade" id="ProblemSettingsFileModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" id="myModalLabel">试题配置</h4>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form class="form-horizontal" action="" method="post" role="form">
<div class="modal-body">
<div class="form-group row">
<label for="use_builtin_checker" class="col-sm-5 control-label">比对函数</label>
<div class="col-sm-7">
<?php $checker_value = is_array($problem_conf) ? getUOJConfVal($problem_conf, 'use_builtin_checker', 'ownchk') : ""; ?>
<select class="form-select" id="use_builtin_checker" name="use_builtin_checker">
<option value="ncmp" <?= $checker_value == "ncmp" ? 'selected' : '' ?>>ncmp: 整数序列</option>
<option value="wcmp" <?= $checker_value == "wcmp" ? 'selected' : '' ?>>wcmp: 字符串序列</option>
<option value="lcmp" <?= $checker_value == "lcmp" ? 'selected' : '' ?>>lcmp: 多行数据(忽略行内与行末的多余空格,同时忽略文末回车)</option>
<option value="fcmp" <?= $checker_value == "fcmp" ? 'selected' : '' ?>>fcmp: 多行数据(不忽略行末空格,但忽略文末回车)</option>
<option value="rcmp4" <?= $checker_value == "rcmp4" ? 'selected' : '' ?>>rcmp4: 浮点数序列(误差不超过 1e-4</option>
<option value="rcmp6" <?= $checker_value == "rcmp6" ? 'selected' : '' ?>>rcmp6: 浮点数序列(误差不超过 1e-6</option>
<option value="rcmp9" <?= $checker_value == "rcmp9" ? 'selected' : '' ?>>rcmp9: 浮点数序列(误差不超过 1e-9</option>
<option value="yesno" <?= $checker_value == "yesno" ? 'selected' : '' ?>>yesno: Yes、No不区分大小写</option>
<option value="uncmp" <?= $checker_value == "uncmp" ? 'selected' : '' ?>>uncmp: 整数集合</option>
<option value="bcmp" <?= $checker_value == "bcmp" ? 'selected' : '' ?>>bcmp: 二进制文件</option>
<option value="ownchk" <?= $checker_value == "ownchk" ? 'selected' : '' ?>>自定义校验器(需上传 chk.cpp</option>
</select>
</div>
</div>
<div class="form-group row">
<label for="n_tests" class="col-sm-5 control-label">n_tests</label>
<div class="col-sm-7">
<?php $n_tests_value = is_array($problem_conf) ? getUOJConfVal($problem_conf, 'n_tests', '') : ""; ?>
<input type="number" class="form-control" id="n_tests" name="n_tests" placeholder="数据点个数(必填)" value="<?= $n_tests_value ?>">
</div>
</div>
<div class="form-group row">
<label for="n_ex_tests" class="col-sm-5 control-label">n_ex_tests</label>
<div class="col-sm-7">
<?php $n_ex_tests_value = is_array($problem_conf) ? getUOJConfVal($problem_conf, 'n_ex_tests', 0) : ""; ?>
<input type="number" class="form-control" id="n_ex_tests" name="n_ex_tests" placeholder="额外数据点个数(默认为 0" value="<?= $n_ex_tests_value ?>">
</div>
</div>
<div class="form-group row">
<label for="n_sample_tests" class="col-sm-5 control-label">n_sample_tests</label>
<div class="col-sm-7">
<?php $n_sample_tests_value = is_array($problem_conf) ? getUOJConfVal($problem_conf, 'n_sample_tests', 0) : ""; ?>
<input type="number" class="form-control" id="n_sample_tests" name="n_sample_tests" placeholder="样例测试点个数(默认为 0" value="<?= $n_sample_tests_value ?>">
</div>
</div>
<div class="form-group row">
<label for="input_pre" class="col-sm-5 control-label">input_pre</label>
<div class="col-sm-7">
<?php $input_pre_value = is_array($problem_conf) ? getUOJConfVal($problem_conf, 'input_pre', 'input') : ""; ?>
<input type="text" class="form-control" id="input_pre" name="input_pre" placeholder="输入文件名称(默认为 input" value="<?= $input_pre_value ?>">
</div>
</div>
<div class="form-group row">
<label for="input_suf" class="col-sm-5 control-label">input_suf</label>
<div class="col-sm-7">
<?php $input_suf_value = is_array($problem_conf) ? getUOJConfVal($problem_conf, 'input_suf', 'txt') : ""; ?>
<input type="text" class="form-control" id="input_suf" name="input_suf" placeholder="输入文件后缀(默认为 txt" value="<?= $input_suf_value ?>">
</div>
</div>
<div class="form-group row">
<label for="output_pre" class="col-sm-5 control-label">output_pre</label>
<div class="col-sm-7">
<?php $output_pre_value = is_array($problem_conf) ? getUOJConfVal($problem_conf, 'output_pre', 'output') : ""; ?>
<input type="text" class="form-control" id="output_pre" name="output_pre" placeholder="输出文件名称(默认为 output" value="<?= $output_pre_value ?>">
</div>
</div>
<div class="form-group row">
<label for="output_suf" class="col-sm-5 control-label">output_suf</label>
<div class="col-sm-7">
<?php $output_suf_value = is_array($problem_conf) ? getUOJConfVal($problem_conf, 'output_suf', 'txt') : ""; ?>
<input type="text" class="form-control" id="output_suf" name="output_suf" placeholder="输出文件后缀(默认为 txt" value="<?= $output_suf_value ?>">
</div>
</div>
<div class="form-group row">
<label for="time_limit" class="col-sm-5 control-label">time_limit</label>
<div class="col-sm-7">
<?php $time_limit_value = is_array($problem_conf) ? getUOJConfVal($problem_conf, 'time_limit', 1) : ""; ?>
<input type="number" class="form-control" id="time_limit" name="time_limit" placeholder="时间限制(不能填写小数,默认为 1s" value="<?= $time_limit_value ?>">
</div>
</div>
<div class="form-group row">
<label for="memory_limit" class="col-sm-5 control-label">memory_limit</label>
<div class="col-sm-7">
<?php $memory_limit_value = is_array($problem_conf) ? getUOJConfVal($problem_conf, 'memory_limit', 256) : ""; ?>
<input type="number" class="form-control" id="memory_limit" name="memory_limit" placeholder="内存限制(默认为 256 MB" value="<?= $memory_limit_value ?>">
</div>
</div>
<input type="hidden" name="problem_settings_file_submit" value="submit">
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-success">确定</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button>
</div>
</form>
</div>
</div>
</div>
<?php echoUOJPageFooter() ?>