mirror of
https://github.com/renbaoshuo/S2OJ.git
synced 2024-12-23 19:11:53 +00:00
332 lines
8.1 KiB
PHP
332 lines
8.1 KiB
PHP
|
<?php
|
||
|
|
||
|
// this class depends on getUOJConf from uoj-judger-lib.php sometimes
|
||
|
// be sure to include the lib
|
||
|
// TODO: move getUOJConf into a static class independent of uoj-judger-lib.php
|
||
|
|
||
|
class UOJProblem {
|
||
|
use UOJDataTrait;
|
||
|
use UOJArticleTrait;
|
||
|
|
||
|
public static function query($id) {
|
||
|
if (!isset($id) || !validateUInt($id)) {
|
||
|
return null;
|
||
|
}
|
||
|
$info = DB::selectFirst([
|
||
|
"select * from problems",
|
||
|
"where", ['id' => $id]
|
||
|
]);
|
||
|
if (!$info) {
|
||
|
return null;
|
||
|
}
|
||
|
return new UOJProblem($info);
|
||
|
}
|
||
|
|
||
|
public static function upgradeToContestProblem() {
|
||
|
return (new UOJContestProblem(self::cur()->info, UOJContest::cur()))->setAsCur()->valid();
|
||
|
}
|
||
|
|
||
|
public static function userCanManageSomeProblem(array $user = null) {
|
||
|
if (!$user) {
|
||
|
return false;
|
||
|
}
|
||
|
return DB::selectFirst([
|
||
|
DB::lc(), "select 1 from problems_permissions",
|
||
|
"where", [
|
||
|
'username' => $user['username']
|
||
|
], DB::limit(1)
|
||
|
]) != null;
|
||
|
}
|
||
|
|
||
|
public function __construct($info) {
|
||
|
$this->info = $info;
|
||
|
}
|
||
|
|
||
|
public function getTitle(array $cfg = []) {
|
||
|
$cfg += [
|
||
|
'with' => 'id',
|
||
|
'simplify' => false
|
||
|
];
|
||
|
$title = $this->info['title'];
|
||
|
if ($cfg['simplify']) {
|
||
|
$title = trim($title);
|
||
|
$title = mb_ereg_replace('^(\[[^\]]*\]|【[^】]*】)', '', $title);
|
||
|
$title = trim($title);
|
||
|
}
|
||
|
if ($cfg['with'] == 'id') {
|
||
|
return "#{$this->info['id']}. {$title}";
|
||
|
} else {
|
||
|
return $title;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public function getUri($where = '') {
|
||
|
return "/problem/{$this->info['id']}{$where}";
|
||
|
}
|
||
|
|
||
|
public function getLink(array $cfg = []) {
|
||
|
return HTML::link($this->getUri(), $this->getTitle($cfg));
|
||
|
}
|
||
|
|
||
|
public function getAttachmentUri() {
|
||
|
return '/download/problem/' . $this->info['id'] . '/attachment.zip';
|
||
|
}
|
||
|
|
||
|
public function getMainDataUri() {
|
||
|
return '/download/problem/' . $this->info['id'] . '/data.zip';
|
||
|
}
|
||
|
|
||
|
public function getUploaderLink() {
|
||
|
return getUserLink($this->info['uploader'] ?: "root");
|
||
|
}
|
||
|
|
||
|
public function findInContests() {
|
||
|
$res = DB::selectAll([
|
||
|
"select contest_id from contests_problems",
|
||
|
"where", ['problem_id' => $this->info['id']]
|
||
|
]);
|
||
|
$cps = [];
|
||
|
foreach ($res as $row) {
|
||
|
$cp = new UOJContestProblem($this->info, UOJContest::query($row['contest_id']));
|
||
|
if ($cp->valid()) {
|
||
|
$cps[] = $cp;
|
||
|
}
|
||
|
}
|
||
|
return $cps;
|
||
|
}
|
||
|
|
||
|
public function userCanClickZan(array $user = null) {
|
||
|
if ($this->userCanView($user)) {
|
||
|
return true;
|
||
|
}
|
||
|
foreach ($this->findInContests() as $cp) {
|
||
|
if ($cp->userCanClickZan($user)) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
public function getZanBlock($show_text = true) {
|
||
|
return ClickZans::getBlock('P', $this->info['id'], $this->info['zan'], null, $show_text);
|
||
|
}
|
||
|
|
||
|
public function getSubmissionRequirement() {
|
||
|
return json_decode($this->info['submission_requirement'], true);
|
||
|
}
|
||
|
public function getExtraConfig($key = null) {
|
||
|
$extra_config = json_decode($this->info['extra_config'], true);
|
||
|
|
||
|
$extra_config += [
|
||
|
'view_content_type' => 'ALL',
|
||
|
'view_all_details_type' => 'ALL',
|
||
|
'view_details_type' => 'ALL',
|
||
|
'view_solution_type' => 'ALL',
|
||
|
'submit_solution_type' => 'ALL_AFTER_AC',
|
||
|
'need_to_review_hack' => false,
|
||
|
'add_hack_as' => 'ex_test',
|
||
|
];
|
||
|
|
||
|
return $key === null ? $extra_config : $extra_config[$key];
|
||
|
}
|
||
|
public function getCustomTestRequirement() {
|
||
|
$extra_config = json_decode($this->info['extra_config'], true);
|
||
|
if (isset($extra_config['custom_test_requirement'])) {
|
||
|
return $extra_config['custom_test_requirement'];
|
||
|
} else {
|
||
|
$answer = [
|
||
|
'name' => 'answer',
|
||
|
'type' => 'source code',
|
||
|
'file_name' => 'answer.code'
|
||
|
];
|
||
|
foreach ($this->getSubmissionRequirement() as $req) {
|
||
|
if ($req['name'] == 'answer' && $req['type'] == 'source code' && isset($req['languages'])) {
|
||
|
$answer['languages'] = $req['languages'];
|
||
|
}
|
||
|
}
|
||
|
return [
|
||
|
$answer, [
|
||
|
'name' => 'input',
|
||
|
'type' => 'text',
|
||
|
'file_name' => 'input.txt'
|
||
|
]
|
||
|
];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public function userCanView(array $user = null, array $cfg = []) {
|
||
|
$cfg += ['ensure' => false];
|
||
|
if ($this->info['is_hidden'] && !$this->userCanManage($user)) {
|
||
|
$cfg['ensure'] && UOJResponse::page404();
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get a SQL cause to determine whether a user can view a problem
|
||
|
* Need to be consistent with the member function userCanView
|
||
|
*/
|
||
|
public static function sqlForUserCanView(array $user = null) {
|
||
|
if (isSuperUser($user)) {
|
||
|
return "(1)";
|
||
|
} elseif (UOJProblem::userCanManageSomeProblem($user)) {
|
||
|
return DB::lor([
|
||
|
"problems.is_hidden" => false,
|
||
|
DB::land([
|
||
|
"problems.is_hidden" => true,
|
||
|
DB::lor([
|
||
|
[
|
||
|
"problems.id", "in", DB::rawbracket([
|
||
|
"select problem_id from problems_permissions",
|
||
|
"where", ["username" => $user['username']]
|
||
|
])
|
||
|
],
|
||
|
[
|
||
|
"problems.id", "in", DB::rawbracket([
|
||
|
"select problem_id from problems",
|
||
|
"where", ["uploader" => $user['username']]
|
||
|
])
|
||
|
],
|
||
|
])
|
||
|
])
|
||
|
]);
|
||
|
} else {
|
||
|
return "(problems.is_hidden = false)";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public function isUserOwnProblem(array $user = null) {
|
||
|
if (!$user) {
|
||
|
return false;
|
||
|
}
|
||
|
return $user['username'] === $this->info['uploader'];
|
||
|
}
|
||
|
|
||
|
public function userPermissionCodeCheck(array $user = null, $perm_code) {
|
||
|
switch ($perm_code) {
|
||
|
case 'ALL':
|
||
|
return true;
|
||
|
case 'ALL_AFTER_AC':
|
||
|
return $this->userHasAC($user);
|
||
|
case 'NONE':
|
||
|
return false;
|
||
|
default:
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public function userCanUploadSubmissionViaZip(array $user = null) {
|
||
|
foreach ($this->getSubmissionRequirement() as $req) {
|
||
|
if ($req['type'] == 'source code') {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
public function userCanDownloadAttachments(array $user = null) {
|
||
|
if ($this->userCanView($user)) {
|
||
|
return true;
|
||
|
}
|
||
|
foreach ($this->findInContests() as $cp) {
|
||
|
if ($cp->userCanDownloadAttachments($user)) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
public function userCanManage(array $user = null) {
|
||
|
if (!$user) {
|
||
|
return false;
|
||
|
}
|
||
|
if (isSuperUser($user) || $user['username'] == $this->info['poster'] || isProblemManager($user)) {
|
||
|
return true;
|
||
|
}
|
||
|
return DB::selectFirst([
|
||
|
DB::lc(), "select 1 from problems_permissions",
|
||
|
"where", [
|
||
|
'username' => $user['username'],
|
||
|
'problem_id' => $this->info['id']
|
||
|
]
|
||
|
]) != null;
|
||
|
}
|
||
|
|
||
|
public function userCanDownloadTestData(array $user = null) {
|
||
|
return $this->userCanManage($user);
|
||
|
}
|
||
|
|
||
|
public function preHackCheck(array $user = null) {
|
||
|
return $this->info['hackable'] && (!$user || $this->userCanView($user));
|
||
|
}
|
||
|
|
||
|
public function needToReviewHack() {
|
||
|
return $this->getExtraConfig('need_to_review_hack');
|
||
|
}
|
||
|
|
||
|
public function userHasAC(array $user = null) {
|
||
|
if (!$user) {
|
||
|
return false;
|
||
|
}
|
||
|
return DB::selectFirst([
|
||
|
DB::lc(), "select 1 from best_ac_submissions",
|
||
|
"where", [
|
||
|
'submitter' => $user['username'],
|
||
|
'problem_id' => $this->info['id']
|
||
|
]
|
||
|
]) != null;
|
||
|
}
|
||
|
|
||
|
public function preSubmitCheck() {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
public function additionalSubmissionComponentsCannotBeSeenByUser(array $user = null, UOJSubmission $submission) {
|
||
|
return [];
|
||
|
}
|
||
|
|
||
|
public function getDataFolderPath() {
|
||
|
return "/var/uoj_data/{$this->info['id']}";
|
||
|
}
|
||
|
|
||
|
public function getDataZipPath() {
|
||
|
return "/var/uoj_data/{$this->info['id']}.zip";
|
||
|
}
|
||
|
|
||
|
public function getDataFilePath($name = '') {
|
||
|
// return "zip://{$this->getDataZipPath()}#{$this->info['id']}/$name";
|
||
|
return "{$this->getDataFolderPath()}/$name";
|
||
|
}
|
||
|
|
||
|
public function getProblemConfArray(string $where = 'data') {
|
||
|
if ($where === 'data') {
|
||
|
return getUOJConf($this->getDataFilePath('problem.conf'));
|
||
|
} else {
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public function getProblemConf(string $where = 'data') {
|
||
|
if ($where === 'data') {
|
||
|
return UOJProblemConf::getFromFile($this->getDataFilePath('problem.conf'));
|
||
|
} else {
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public function getNonTraditionalJudgeType() {
|
||
|
$conf = $this->getProblemConf();
|
||
|
if (!($conf instanceof UOJProblemConf)) {
|
||
|
return false;
|
||
|
}
|
||
|
return $conf->getNonTraditionalJudgeType();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
UOJProblem::$table_for_content = 'problems_contents';
|
||
|
UOJProblem::$key_for_content = 'id';
|
||
|
UOJProblem::$fields_for_content = ['*'];
|
||
|
UOJProblem::$table_for_tags = 'problems_tags';
|
||
|
UOJProblem::$key_for_tags = 'problem_id';
|