From d244f36a258bf51225ee5bd1ca18936382497571 Mon Sep 17 00:00:00 2001 From: Baoshuo Date: Sun, 12 Feb 2023 21:40:22 +0800 Subject: [PATCH 1/2] feat(contest): contest_links --- db/app_uoj233.sql | 2 +- web/app/controllers/add_contest.php | 5 +- web/app/controllers/contest_inside.php | 239 ++++++++++----------- web/app/controllers/contest_manage.php | 174 +++++++++++++-- web/app/controllers/contest_resources.php | 32 +++ web/app/controllers/problem.php | 2 +- web/app/controllers/problem_resources.php | 4 - web/app/locale/contests/en.php | 6 +- web/app/locale/contests/zh-cn.php | 4 +- web/app/models/UOJContest.php | 20 ++ web/app/route.php | 2 +- web/app/storage/contest_resources/.gitkeep | 0 web/install.sh | 1 + 13 files changed, 341 insertions(+), 150 deletions(-) create mode 100644 web/app/controllers/contest_resources.php create mode 100644 web/app/storage/contest_resources/.gitkeep diff --git a/db/app_uoj233.sql b/db/app_uoj233.sql index 870225a..806f345 100644 --- a/db/app_uoj233.sql +++ b/db/app_uoj233.sql @@ -181,7 +181,7 @@ CREATE TABLE `contests` ( `last_min` int NOT NULL, `player_num` int NOT NULL DEFAULT '0', `status` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL, - `extra_config` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '{}', + `extra_config` json NOT NULL, `zan` int NOT NULL DEFAULT '0', PRIMARY KEY (`id`), KEY `status` (`status`,`id`) USING BTREE diff --git a/web/app/controllers/add_contest.php b/web/app/controllers/add_contest.php index abccb5d..35c924f 100644 --- a/web/app/controllers/add_contest.php +++ b/web/app/controllers/add_contest.php @@ -71,8 +71,9 @@ $time_form->handle = function (&$vdata) { DB::insert([ "insert into contests", - "(name, start_time, last_min, status)", "values", - DB::tuple([$vdata['name'], $start_time_str, $vdata['last_min'], 'unfinished']) + DB::bracketed_fields(["name", "start_time", "last_min", "status", "extra_config"]), + "values", + DB::tuple([$vdata['name'], $start_time_str, $vdata['last_min'], 'unfinished', "{}"]) ]); }; $time_form->succ_href = "/contests"; diff --git a/web/app/controllers/contest_inside.php b/web/app/controllers/contest_inside.php index e65f3b6..2dfee24 100644 --- a/web/app/controllers/contest_inside.php +++ b/web/app/controllers/contest_inside.php @@ -64,7 +64,7 @@ if (UOJContest::cur()->userCanStartFinalTest(Auth::user())) { $start_test_form->config['confirm']['smart'] = true; $start_test_form->runAtServer(); } - if ($contest['cur_progress'] >= CONTEST_TESTING && UOJContest::cur()->queryJudgeProgress()['fully_judged']) { + if ($contest['cur_progress'] == CONTEST_TESTING && UOJContest::cur()->queryJudgeProgress()['fully_judged']) { $publish_result_form = new UOJForm('publish_result'); $publish_result_form->handle = function () { UOJContest::announceOfficialResults(); @@ -492,133 +492,132 @@ function echoSelfReviews() {
+
class="col-12" class="col-md-9" > + + +
+

+
+ + + + + + + + + + + +
+
+ +
+ +
+
+ -
- -
- - - -
-

-
- - - - - - + +
+ +
+
+
+

+ + + +

+
+ + + - + queryJudgeProgress() ?> + (%) - +
- -
- -
-
- - - -
-
+
+
  • + + + + + getZanBlock() ?> + +
  • -
    -
    -
    -

    - - - -

    -
    - - - - - - - - - - (%) - - - - -
    -
    - -
    +
    - basicRule() === 'OI') : ?> -

    此次比赛为 OI 赛制。

    -

    注意:比赛时只显示测样例的结果。

    - basicRule() === 'ACM') : ?> -

    此次比赛为 ACM 赛制。

    -

    封榜时间:format('Y-m-d H:i:s') ?>

    - basicRule() === 'IOI') : ?> -

    此次比赛为 IOI 赛制。

    -

    比赛时显示的得分即最终得分。

    - + basicRule() === 'OI') : ?> +

    此次比赛为 OI 赛制。

    +

    注意:比赛时只显示测样例的结果。

    + basicRule() === 'ACM') : ?> +

    此次比赛为 ACM 赛制。

    +

    封榜时间:format(UOJTime::FORMAT) ?>

    + basicRule() === 'IOI') : ?> +

    此次比赛为 IOI 赛制。

    +

    比赛时显示的得分即最终得分。

    + - - + + + + + + 管理 + + + +
    + printHTML(); ?> +
    + + +
    + printHTML(); ?> +
    + + + +
    +
    + +
    +
    + + - - - 管理 - - - -
    - printHTML(); ?> -
    - - -
    - printHTML(); ?> -
    - - -
    -
    -

    比赛资料

    -
    -
    - - - -
    -
    - -
    - -
    - + getAdditionalLinks() as $link) : ?> + + + + +
    +
    +
    + +
    + + diff --git a/web/app/controllers/contest_manage.php b/web/app/controllers/contest_manage.php index 2af6cbc..aa54f28 100644 --- a/web/app/controllers/contest_manage.php +++ b/web/app/controllers/contest_manage.php @@ -359,25 +359,156 @@ EOD); dieWithJsonData(['status' => 'success', 'message' => '修改成功']); }; $rule_form->setAjaxSubmit(<<runAtServer(); + + $links = UOJContest::cur()->getAdditionalLinks(); + $links_str = json_encode($links, JSON_FORCE_OBJECT); + $links_form = new UOJForm('links'); + $links_form->add('contest_links', '', function ($str, &$vdata) { + $data = json_decode($str, true); + $new_data = []; + + if ($data === null) return '不合法的 JSON'; + + foreach ($data as $idx => $link) { + $link_name = trim($link['name']); + $link_url = trim($link['url']); + + if ($link_name && $link_url) { + $new_data[] = [ + 'name' => $link_name, + 'url' => $link_url, + ]; + } + } + + $vdata['links'] = $new_data; + + return ''; + }, null); + $links_form->appendHTML(<<
    + + + EOD); + $links_form->setAjaxSubmit(<<handle = function (&$vdata) { + $extra_config = UOJContest::info('extra_config'); + $extra_config['links'] = $vdata['links']; + $esc_extra_config = json_encode($extra_config); + + DB::update([ + "update contests", + "set", [ + "extra_config" => $esc_extra_config, + ], + "where", [ + "id" => UOJContest::info('id'), + ], + ]); + + dieWithJsonData(['status' => 'success', 'message' => '修改成功']); + }; + $links_form->runAtServer(); } ?> @@ -527,6 +658,9 @@ EOD); +
    @@ -545,11 +679,15 @@ EOD);
    常见问题
      -
    • 暂无
    • +
    • 团体赛推荐使用团体账号报名参赛以解锁全部功能。
    + diff --git a/web/app/controllers/contest_resources.php b/web/app/controllers/contest_resources.php new file mode 100644 index 0000000..710fbf8 --- /dev/null +++ b/web/app/controllers/contest_resources.php @@ -0,0 +1,32 @@ +userCanView(Auth::user(), ['ensure' => true, 'check-register' => true]); + +// Create directory if not exists +if (!is_dir(UOJContest::cur()->getResourcesPath())) { + mkdir(UOJContest::cur()->getResourcesPath(), 0755, true); +} + +define('APP_TITLE', '比赛资源 - ' . UOJContest::info('title')); +define('FM_EMBED', true); +define('FM_DISABLE_COLS', true); +define('FM_DATETIME_FORMAT', UOJTime::FORMAT); +define('FM_ROOT_PATH', UOJContest::cur()->getResourcesFolderPath()); +define('FM_ROOT_URL', UOJContest::cur()->getResourcesBaseUri()); + +$sub_path = UOJRequest::get('sub_path', 'is_string', ''); + +if ($sub_path) { + $filepath = realpath(UOJContest::cur()->getResourcesPath(rawurldecode($sub_path))); + $realbasepath = realpath(UOJContest::cur()->getResourcesPath()); + + if (!strStartWith($filepath, $realbasepath)) { + UOJResponse::page406(); + } + + UOJResponse::xsendfile($filepath); +} + +$global_readonly = !UOJContest::cur()->userCanManage(Auth::user()); + +include(__DIR__ . '/tinyfilemanager/tinyfilemanager.php'); diff --git a/web/app/controllers/problem.php b/web/app/controllers/problem.php index 2b168df..4153930 100644 --- a/web/app/controllers/problem.php +++ b/web/app/controllers/problem.php @@ -358,7 +358,7 @@ if (UOJContest::cur()) {

    - +

    diff --git a/web/app/controllers/problem_resources.php b/web/app/controllers/problem_resources.php index 00de3a8..66eb9fa 100644 --- a/web/app/controllers/problem_resources.php +++ b/web/app/controllers/problem_resources.php @@ -1,11 +1,7 @@ info; -$problem_content = UOJProblem::cur()->queryContent(); $user_can_view = UOJProblem::cur()->userCanView(Auth::user()); if (!$user_can_view) { diff --git a/web/app/locale/contests/en.php b/web/app/locale/contests/en.php index f682c27..c87d217 100644 --- a/web/app/locale/contests/en.php +++ b/web/app/locale/contests/en.php @@ -36,7 +36,9 @@ return [ 'problem self review' => 'Problem self review', 'contest self review' => 'Contest self review', 'contest self reviews' => 'Contest self reviews', - 'will start in x days' => function($x) { - return "will start in $x ".($x > 1 ? "days" : "day"); + 'will start in x days' => function ($x) { + return "will start in $x " . ($x > 1 ? "days" : "day"); }, + 'links' => 'Links', + 'resources' => 'Resources', ]; diff --git a/web/app/locale/contests/zh-cn.php b/web/app/locale/contests/zh-cn.php index 3e2e951..884b872 100644 --- a/web/app/locale/contests/zh-cn.php +++ b/web/app/locale/contests/zh-cn.php @@ -36,7 +36,9 @@ return [ 'problem self review' => '题目总结', 'contest self review' => '比赛总结', 'contest self reviews' => '赛后总结', - 'will start in x days' => function($x) { + 'will start in x days' => function ($x) { return "将在 $x 天后开始"; }, + 'links' => '链接', + 'resources' => '相关资源', ]; diff --git a/web/app/models/UOJContest.php b/web/app/models/UOJContest.php index f50562d..60e3905 100644 --- a/web/app/models/UOJContest.php +++ b/web/app/models/UOJContest.php @@ -593,4 +593,24 @@ class UOJContest { updateContestPlayerNum($this->info); return true; } + + public function getResourcesFolderPath() { + return UOJContext::storagePath() . "/contest_resources/" . $this->info['id']; + } + + public function getResourcesPath($name = '') { + return "{$this->getResourcesFolderPath()}/$name"; + } + + public function getResourcesBaseUri() { + return "/contest/{$this->info['id']}/resources"; + } + + public function getResourcesUri($name = '') { + return "{$this->getResourcesBaseUri()}/{$name}"; + } + + public function getAdditionalLinks() { + return $this->info['extra_config']['links'] ?: []; + } } diff --git a/web/app/route.php b/web/app/route.php index ab0d726..421cc33 100644 --- a/web/app/route.php +++ b/web/app/route.php @@ -42,13 +42,13 @@ Route::group( Route::any('/contest/{id}/registrants', '/contest_members.php'); Route::any('/contest/{id}/register', '/contest_registration.php'); Route::any('/contest/{id}/confirm', '/contest_confirmation.php'); + Route::any('/contest/{id}/resources(?:/{sub_path})?', '/contest_resources.php'); Route::any('/contest/{id}/manage(?:/{tab})?', '/contest_manage.php'); Route::any('/contest/{id}/submissions', '/contest_inside.php?tab=submissions'); Route::any('/contest/{id}/standings', '/contest_inside.php?tab=standings'); Route::any('/contest/{id}/after_contest_standings', '/contest_inside.php?tab=after_contest_standings'); Route::any('/contest/{id}/self_reviews', '/contest_inside.php?tab=self_reviews'); Route::any('/contest/{id}/backstage', '/contest_inside.php?tab=backstage'); - Route::any('/contest/{id}/standings_unfrozen', '/contest_inside.php?tab=standings_unfrozen'); Route::any('/contest/{contest_id}/problem/{id}', '/problem.php'); Route::any('/contest/{contest_id}/problem/{id}/statistics', '/problem_statistics.php'); diff --git a/web/app/storage/contest_resources/.gitkeep b/web/app/storage/contest_resources/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/web/install.sh b/web/install.sh index d604b88..a87aff5 100644 --- a/web/install.sh +++ b/web/install.sh @@ -83,6 +83,7 @@ initProgress(){ mkdir -p /opt/uoj/web/app/storage/tmp mkdir -p /opt/uoj/web/app/storage/image_hosting mkdir -p /opt/uoj/web/app/storage/problem_resources + mkdir -p /opt/uoj/web/app/storage/contest_resources chmod -R 777 /opt/uoj/web/app/storage #Using cli upgrade to latest sleep 15 # Wait for uoj-db From ad0837689ae2d55f046704cf42c937f0d9b7fd2c Mon Sep 17 00:00:00 2001 From: Baoshuo Date: Sun, 12 Feb 2023 21:55:03 +0800 Subject: [PATCH 2/2] chore: add upgrader for #40 --- db/app_uoj233.sql | 3 +- web/app/upgrade/40_contest_resources/up.sql | 1 + .../upgrade/40_contest_resources/upgrade.php | 37 +++++++++++++++++++ 3 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 web/app/upgrade/40_contest_resources/up.sql create mode 100644 web/app/upgrade/40_contest_resources/upgrade.php diff --git a/db/app_uoj233.sql b/db/app_uoj233.sql index 806f345..1d2c42f 100644 --- a/db/app_uoj233.sql +++ b/db/app_uoj233.sql @@ -995,7 +995,8 @@ INSERT INTO `upgrades` (`name`, `status`, `updated_at`) VALUES ('21_problem_difficulty', 'up', now()), ('28_remote_judge', 'up', now()), ('31_problem_resources', 'up', now()), - ('36_decimal_score_range', 'up', now()); + ('36_decimal_score_range', 'up', now()), + ('40_contest_resources', 'up', now()); /*!40000 ALTER TABLE `upgrades` ENABLE KEYS */; UNLOCK TABLES; diff --git a/web/app/upgrade/40_contest_resources/up.sql b/web/app/upgrade/40_contest_resources/up.sql new file mode 100644 index 0000000..760bfc3 --- /dev/null +++ b/web/app/upgrade/40_contest_resources/up.sql @@ -0,0 +1 @@ +ALTER TABLE `contests` MODIFY `extra_config` json NOT NULL; diff --git a/web/app/upgrade/40_contest_resources/upgrade.php b/web/app/upgrade/40_contest_resources/upgrade.php new file mode 100644 index 0000000..6d51321 --- /dev/null +++ b/web/app/upgrade/40_contest_resources/upgrade.php @@ -0,0 +1,37 @@ + $link[0], + 'url' => '/blogs/' . $link[1], + ]; + } + + $extra_config['links'] = $new_links; + } + + DB::update([ + "update contests", + "set", [ + "extra_config" => json_encode($extra_config, JSON_FORCE_OBJECT), + ], + "where", [ + "id" => $contest['id'], + ], + ]); + } + } +};