diff --git a/.config.php b/.config.php
new file mode 100644
index 0000000..74a55c9
--- /dev/null
+++ b/.config.php
@@ -0,0 +1,45 @@
+ [
+ 'oj-name' => '石家庄二中信息学在线评测系统',
+ 'oj-name-short' => 'S2OJ',
+ 'administrator' => 'root',
+ 'admin-email' => 'admin@sjzezoj.com',
+ 'QQ-group' => '',
+ 'ICP-license' => '冀ICP备2020028886号',
+ ],
+ 'database' => [
+ 'database' => 'app_uoj233',
+ 'username' => 'root',
+ 'password' => 'root',
+ 'host' => 'uoj-db',
+ 'port' => '3306',
+ ],
+ 'security' => [
+ 'user' => [
+ 'client_salt' => 'salt_0',
+ ],
+ 'cookie' => [
+ 'checksum_salt' => ['salt_1', 'salt_2', 'salt_3'],
+ ],
+ ],
+ 'mail' => [
+ 'noreply' => [
+ 'username' => 'noreply@local_uoj.ac',
+ 'password' => '_mail_noreply_password_',
+ 'host' => 'smtp.local_uoj.ac',
+ 'secure' => 'tls',
+ 'port' => 587,
+ ]
+ ],
+ 'judger' => [
+ 'socket' => [
+ 'port' => '2333',
+ 'password' => '_judger_socket_password_'
+ ],
+ ],
+ 'switch' => [
+ 'blog-domain-mode' => 3,
+ 'open-register' => false,
+ ],
+];
diff --git a/.gitignore b/.gitignore
index 8264c45..e4d53e4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,6 @@ uoj_data_1/
uoj_data_2/
.php-cs-fixer.cache
docker-compose.local.yml
+.config.php
+.config.development.php
+.config.local.php
diff --git a/db/app_uoj233.sql b/db/app_uoj233.sql
index 9fb0e3a..12b362a 100644
--- a/db/app_uoj233.sql
+++ b/db/app_uoj233.sql
@@ -979,6 +979,17 @@ CREATE TABLE `upgrades` (
LOCK TABLES `upgrades` WRITE;
/*!40000 ALTER TABLE `upgrades` DISABLE KEYS */;
+INSERT INTO `upgrades` (`name`, `status`, `updated_at`) VALUES
+ ('3_parsedown', 'up', now()),
+ ('4_image_hosting', 'up', now()),
+ ('6_user_info_v2', 'up', now()),
+ ('8_group_v2', 'up', now()),
+ ('9_list_v2', 'up', now()),
+ ('14_sync_from_uoj.ac', 'up', now()),
+ ('16_list_v3', 'up', now()),
+ ('18_user_permissions', 'up', now()),
+ ('20_problem_difficulty', 'up', now()),
+ ('21_problem_difficulty', 'up', now());
/*!40000 ALTER TABLE `upgrades` ENABLE KEYS */;
UNLOCK TABLES;
diff --git a/docker-compose.development.yml b/docker-compose.development.yml
index 90fb54b..31daa8d 100644
--- a/docker-compose.development.yml
+++ b/docker-compose.development.yml
@@ -60,15 +60,6 @@ services:
volumes:
- ./uoj_data/web/data:/var/uoj_data
- ./uoj_data/web/storage:/opt/uoj/web/app/storage
+ - ./.config.development.php:/opt/uoj/web/app/.config.php
ports:
- "80:80"
- environment:
- - UOJ_PROTOCOL=http
- - DATABASE_HOST=uoj-db
- - DATABASE_PASSWORD=root
- - JUDGER_SOCKET_PORT=2333
- - JUDGER_SOCKET_PASSWORD=_judger_socket_password_
- - SALT_0=salt_0
- - SALT_1=salt_1
- - SALT_2=salt_2
- - SALT_3=salt_3
diff --git a/docker-compose.yml b/docker-compose.yml
index f96fde1..1da0339 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -43,15 +43,6 @@ services:
volumes:
- ./uoj_data/web/data:/var/uoj_data
- ./uoj_data/web/storage:/opt/uoj/web/app/storage
+ - ./.config.php:/opt/uoj/web/app/.config.php
ports:
- "80:80"
- environment:
- - UOJ_PROTOCOL=https
- - DATABASE_HOST=uoj-db
- - DATABASE_PASSWORD=root
- - JUDGER_SOCKET_PORT=2333
- - JUDGER_SOCKET_PASSWORD=_judger_socket_password_
- - SALT_0=salt_0
- - SALT_1=salt_1
- - SALT_2=salt_2
- - SALT_3=salt_3
diff --git a/web/app/.default-config.php b/web/app/.default-config.php
index dc84dbf..fab8e0e 100644
--- a/web/app/.default-config.php
+++ b/web/app/.default-config.php
@@ -20,21 +20,21 @@ return [
'domain' => null,
'main' => [
'protocol' => 'http',
- 'host' => '_httpHost_',
- 'port' => '80/443'
+ 'host' => UOJContext::requestDomain(),
+ 'port' => '80/443',
],
'blog' => [
'protocol' => 'http',
- 'host' => '_httpHost_',
- 'port' => '80/443'
+ 'host' => UOJContext::requestDomain(),
+ 'port' => '80/443',
]
],
'security' => [
'user' => [
- 'client_salt' => 'salt0'
+ 'client_salt' => 'salt0',
],
'cookie' => [
- 'checksum_salt' => ['salt1', 'salt2', 'salt3']
+ 'checksum_salt' => ['salt1', 'salt2', 'salt3'],
],
],
'mail' => [
@@ -43,17 +43,17 @@ return [
'password' => '_mail_noreply_password_',
'host' => 'smtp.local_uoj.ac',
'secure' => 'tls',
- 'port' => 587
+ 'port' => 587,
]
],
'judger' => [
'socket' => [
'port' => '233',
- 'password' => '_judger_socket_password_'
+ 'password' => '_judger_socket_password_',
]
],
'switch' => [
'blog-domain-mode' => 3,
- 'open-register' => false
- ]
+ 'open-register' => false,
+ ],
];
diff --git a/web/app/controllers/add_contest.php b/web/app/controllers/add_contest.php
index ae5b6a7..64a2a2f 100644
--- a/web/app/controllers/add_contest.php
+++ b/web/app/controllers/add_contest.php
@@ -79,19 +79,20 @@ $time_form->succ_href = "/contests";
$time_form->runAtServer();
?>
-
+
- = HTML::purifier_inline()->purify(HTML::parsedown()->line(UOJGroup::info('announcement'))) ?>
+ = HTML::purifier_inline()->purify(HTML::parsedown(['username_with_color' => true])->line(UOJGroup::info('announcement'))) ?>
diff --git a/web/app/controllers/html2markdown.php b/web/app/controllers/html2markdown.php
deleted file mode 100644
index 4206260..0000000
--- a/web/app/controllers/html2markdown.php
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-
-
-
- = UOJLocale::get('html to markdown') ?>
-
-
-
-
-
-
-= HTML::js_src('/js/h2m.js') ?>
-
-
-
-
diff --git a/web/app/controllers/list_manage.php b/web/app/controllers/list_manage.php
index 757c722..c158934 100644
--- a/web/app/controllers/list_manage.php
+++ b/web/app/controllers/list_manage.php
@@ -318,7 +318,7 @@ EOD);
echo HTML::tag_begin('tr');
echo HTML::tag('td', ['class' => 'text-center'], $problem->info['id']);
echo HTML::tag_begin('td');
- echo $problem->getLink();
+ echo $problem->getLink(['with' => 'none']);
if ($problem->info['is_hidden']) {
echo '
', UOJLocale::get('hidden'), ' ';
}
diff --git a/web/app/controllers/lists.php b/web/app/controllers/lists.php
index 2e81ef3..a24c626 100644
--- a/web/app/controllers/lists.php
+++ b/web/app/controllers/lists.php
@@ -9,16 +9,28 @@ Auth::check() || redirectToLogin();
UOJUser::checkPermission(Auth::user(), 'lists.view') || UOJResponse::page403();
if (UOJList::userCanCreateList(Auth::user())) {
- $new_list_form = new UOJBs4Form('new_list');
+ $new_list_form = new UOJForm('new_list');
$new_list_form->handle = function () {
- DB::insert("insert into lists (title, is_hidden) values ('未命名题单', 1)");
- $id = DB::insert_id();
- DB::insert("insert into lists_contents (id, content, content_md) values ($id, '', '')");
+ DB::insert([
+ "insert into lists",
+ DB::bracketed_fields(['title', 'is_hidden']),
+ "values",
+ DB::tuple(['未命名题单', 1]),
+ ]);
+ $list_id = DB::insert_id();
+ DB::insert([
+ "insert into lists_contents",
+ DB::bracketed_fields(['id', 'content', 'content_md']),
+ "values",
+ DB::tuple([$list_id, '', '']),
+ ]);
+ redirectTo("/list/{$list_id}");
+ die();
};
- $new_list_form->submit_button_config['align'] = 'right';
- $new_list_form->submit_button_config['class_str'] = 'btn btn-primary';
- $new_list_form->submit_button_config['text'] = UOJLocale::get('problems::add new list');
- $new_list_form->submit_button_config['smart_confirm'] = '';
+ $new_list_form->config['submit_container']['class'] = 'text-end';
+ $new_list_form->config['submit_button']['class'] = 'btn btn-primary';
+ $new_list_form->config['submit_button']['text'] = UOJLocale::get('problems::add new list');
+ $new_list_form->config['confirm']['smart'] = true;
$new_list_form->runAtServer();
}
diff --git a/web/app/controllers/problem.php b/web/app/controllers/problem.php
index 8ce857b..40757b3 100644
--- a/web/app/controllers/problem.php
+++ b/web/app/controllers/problem.php
@@ -100,7 +100,7 @@ if (UOJContest::cur()) {
$submission_requirement = UOJProblem::cur()->getSubmissionRequirement();
$custom_test_requirement = UOJProblem::cur()->getCustomTestRequirement();
-$custom_test_enabled = false; // $custom_test_requirement && $pre_submit_check_ret === true;
+$custom_test_enabled = $custom_test_requirement && $pre_submit_check_ret === true;
function handleUpload($zip_file_name, $content, $tot_size) {
global $is_participating;
@@ -193,7 +193,7 @@ if ($pre_submit_check_ret === true && !$no_more_submission) {
'FS::randomAvailableSubmissionFileName',
'handleUpload'
);
- $zip_answer_form->extra_validators[] = $submission_extra_validator;
+ $zip_answer_form->extra_validator = $submission_extra_validator;
$zip_answer_form->succ_href = $is_participating ? '/contest/' . UOJContest::info('id') . '/submissions' : '/submissions';
$zip_answer_form->runAtServer();
}
@@ -385,46 +385,64 @@ if (UOJContest::cur()) {
-
- 上传者
- = UOJProblem::cur()->getUploaderLink() ?>
+
+ = UOJLocale::get('problems::uploader') ?>
+
+
+ = UOJProblem::cur()->getUploaderLink() ?>
+
- -
- 难度
- = UOJProblem::cur()->getDifficultyHTML() ?>
-
-
+ progress() >= CONTEST_FINISHED) : ?>
-
- 历史分数
- UOJProblem::info('id'), "submitter" => Auth::id()]]) ?>
+
+ = UOJLocale::get('problems::difficulty') ?>
+
+
+ = UOJProblem::cur()->getDifficultyHTML() ?>
+
+
+
+ -
+
+ = UOJLocale::get('problems::historical score') ?>
+
+ UOJProblem::info('id'), "submitter" => Auth::id()]]) ?>
-
- = is_null($his_score) ? '无' : $his_score ?>
-
+
+ = is_null($his_score) ? '无' : $his_score ?>
+
+
+
+ -
+
+ = UOJLocale::get('problems::tags') ?>
+
+
+
+
+
+
+ = UOJLocale::get('hidden') ?>
+
+
+
+ queryTags() as $tag) : ?>
+ = HTML::tag(
+ 'a',
+ ['class' => 'uoj-problem-tag'],
+ HTML::tag('span', ['class' => 'badge bg-secondary'], HTML::escape($tag))
+ ) ?>
+
+
-
- 标签
-
-
-
-
-
- = UOJLocale::get('hidden') ?>
-
-
-
- queryTags() as $tag) : ?>
- = HTML::tag(
- 'a',
- ['class' => 'uoj-problem-tag'],
- HTML::tag('span', ['class' => 'badge bg-secondary'], HTML::escape($tag))
- ) ?>
-
+
+ = UOJLocale::get('appraisal') ?>
+
+
+ = UOJProblem::cur()->getZanBlock() ?>
-
- -
- 评价
- = UOJProblem::cur()->getZanBlock() ?>
diff --git a/web/app/controllers/problem_set.php b/web/app/controllers/problem_set.php
index 0edda88..47623eb 100644
--- a/web/app/controllers/problem_set.php
+++ b/web/app/controllers/problem_set.php
@@ -73,6 +73,9 @@ EOD;
])
]);
dataNewProblem($id);
+
+ redirectTo("/problem/{$id}/manage/statement");
+ die();
};
$new_problem_form->submit_button_config['align'] = 'right';
$new_problem_form->submit_button_config['class_str'] = 'btn btn-primary';
diff --git a/web/app/controllers/problem_solutions.php b/web/app/controllers/problem_solutions.php
index 8889288..143c6f0 100644
--- a/web/app/controllers/problem_solutions.php
+++ b/web/app/controllers/problem_solutions.php
@@ -101,6 +101,29 @@ if (UOJProblem::cur()->userCanManage(Auth::user()) || UOJProblem::cur()->userPer
]);
};
$add_new_solution_form->runAtServer();
+
+ if (UOJUser::checkPermission(Auth::user(), 'blogs.create')) {
+ $quick_add_new_solution_form = new UOJForm('quick_add_new_solution');
+ $quick_add_new_solution_form->config['submit_container']['class'] = '';
+ $quick_add_new_solution_form->config['submit_button']['class'] = 'btn btn-link text-decoration-none p-0';
+ $quick_add_new_solution_form->config['submit_button']['text'] = '快速新建文章';
+ $quick_add_new_solution_form->handle = function () {
+ DB::insert([
+ "insert into blogs",
+ "(title, content, content_md, poster, is_hidden, post_time, active_time)",
+ "values", DB::tuple([
+ '【题解】' . UOJProblem::cur()->getTitle(), '', '',
+ Auth::id(), false, DB::now(), DB::now()
+ ])
+ ]);
+
+ $blog_id = DB::insert_id();
+
+ redirectTo(HTML::blog_url(Auth::id(), "/post/{$blog_id}/write"));
+ die();
+ };
+ $quick_add_new_solution_form->runAtServer();
+ }
}
$pag_config = [
@@ -262,18 +285,16 @@ $pag = new Paginator($pag_config);
您当前无法为本题新增题解。
-
+
+
+
-
-
+
-
+
diff --git a/web/app/controllers/problem_statement_manage.php b/web/app/controllers/problem_statement_manage.php
index 7726722..3aa2e63 100644
--- a/web/app/controllers/problem_statement_manage.php
+++ b/web/app/controllers/problem_statement_manage.php
@@ -174,6 +174,85 @@ $difficulty_form->runAtServer();
+
-
- printHTML() ?>
-
+
+ printHTML() ?>
+
-
- printHTML() ?>
-
+
+ printHTML() ?>
+
-
- printHTML() ?>
-
+
+ printHTML() ?>
+
diff --git a/web/app/controllers/super_manage.php b/web/app/controllers/super_manage.php
index 113fb1c..c5a61e7 100644
--- a/web/app/controllers/super_manage.php
+++ b/web/app/controllers/super_manage.php
@@ -516,7 +516,7 @@ EOD);
'new_tmp_expiration_time',
'text',
'过期时间',
- (new DateTime())->add(new DateInterval('P7D'))->format('Y-m-d H:i:s'),
+ UOJTime::time2str((new DateTime())->add(new DateInterval('P7D'))->setTime(0, 0, 0)),
function ($str, &$vdata) {
try {
$vdata['expiration_time'] = new DateTime($str);
@@ -657,6 +657,7 @@ EOD);
);
$change_usergroup_form->addVSelect('op_type', [
'banneduser' => '设为封禁用户',
+ 'tempuser' => '设为临时用户',
'normaluser' => '设为普通用户',
'superuser' => '设为超级用户',
], '操作类型', '');
@@ -666,15 +667,59 @@ EOD);
switch ($_POST['op_type']) {
case 'banneduser':
- DB::update("update user_info set usergroup = 'B', usertype = 'banned' where username = '{$username}'");
- $usergroup = '被封禁的用户';
+ DB::update([
+ "update user_info",
+ "set", [
+ "usergroup" => "B",
+ "usertype" => "banned",
+ "expiration_time" => null,
+ ],
+ "where", [
+ "username" => $username,
+ ],
+ ]);
+ $usergroup = '被封禁的用户,该用户将无法再次登录系统';
+ break;
+ case 'tempuser':
+ DB::update([
+ "update user_info",
+ "set", [
+ "usergroup" => "T",
+ "usertype" => "student",
+ "expiration_time" => DB::now(),
+ ],
+ "where", [
+ "username" => $username,
+ ],
+ ]);
+ $usergroup = '临时用户,请前往个人信息编辑页面修改过期时间';
break;
case 'normaluser':
- DB::update("update user_info set usergroup = 'U', usertype = 'student' where username = '{$username}'");
+ DB::update([
+ "update user_info",
+ "set", [
+ "usergroup" => "U",
+ "usertype" => "student",
+ "expiration_time" => null,
+ ],
+ "where", [
+ "username" => $username,
+ ],
+ ]);
$usergroup = '普通用户';
break;
case 'superuser':
- DB::update("update user_info set usergroup = 'S', usertype = 'student' where username = '{$username}'");
+ DB::update([
+ "update user_info",
+ "set", [
+ "usergroup" => "S",
+ "usertype" => "student",
+ "expiration_time" => null,
+ ],
+ "where", [
+ "username" => $username,
+ ],
+ ]);
$usergroup = '超级用户';
break;
}
@@ -1336,7 +1381,7 @@ EOD);
EOD,
function ($row) {
echo '';
- echo '', '', $row['username'], '', ' | ';
+ echo '', UOJUser::getLink($row), ' | ';
echo '', HTML::escape($row['school']), ' | ';
echo '';
switch ($row['usergroup']) {
@@ -1429,7 +1474,8 @@ EOD);
注意事项
- 用户被封禁后将不能再次登录系统。
- - 将当前用户移除权限后将无法再次访问本页面。
+ - 将用户修改为临时用户后,请前往个人信息编辑页面修改过期时间。
+ - 将当前用户移除管理权限后将无法再次访问本页面。
- 在修改用户类别前请仔细核对用户名以免产生不必要的麻烦。
- 如需为用户设置题目上传者、题目管理员等权限,请前往对应用户的个人资料编辑页面,点击「特权」选项卡修改。
diff --git a/web/app/controllers/user_info_edit.php b/web/app/controllers/user_info_edit.php
index 6301c87..189862a 100644
--- a/web/app/controllers/user_info_edit.php
+++ b/web/app/controllers/user_info_edit.php
@@ -266,6 +266,43 @@ EOD);
},
]
);
+ if ($user['usergroup'] == 'B') {
+ $update_profile_form->appendHTML(<<
+
+
+ 被封禁的用户无法修改用户名颜色。
+
+ EOD);
+ } else if ($user['usergroup'] == 'T') {
+ $update_profile_form->appendHTML(<<
+
+
+ 临时用户无法修改用户名颜色。
+
+ EOD);
+ } else {
+ $additional_colors = [];
+
+ if (isSuperUser($user)) {
+ $additional_colors['#9d3dcf'] = '紫色 - #9d3dcf';
+ }
+
+ $update_profile_form->addSelect('username_color', [
+ 'div_class' => 'mb-3',
+ 'label' => '用户名颜色',
+ 'default_value' => $extra['username_color'],
+ 'options' => $additional_colors + [
+ '#0d6efd' => '蓝色 - #0d6efd',
+ '#2da44e' => '绿色 - #2da44e',
+ '#e85aad' => '粉色 - #e85aad',
+ '#f32a38' => '红色 - #f32a38',
+ '#f57c00' => '橙色 - #f57c00',
+ '#00acc1' => '青色 - #00acc1',
+ ],
+ ]);
+ }
$update_profile_form->handle = function (&$vdata) use ($user) {
$data = [
'email' => $vdata['email'],
@@ -301,7 +338,9 @@ EOD);
'$.social.codeforces',
$vdata['codeforces'],
'$.social.website',
- $vdata['website']
+ $vdata['website'],
+ '$.username_color',
+ $_POST['username_color']
),
],
"where", ["username" => $user['username']]
@@ -376,6 +415,17 @@ EOD);
}
$update_user_permissions_form->appendHTML(HTML::tag('span', [], UOJLocale::get('user::user group')));
$update_user_permissions_form->appendHTML(HTML::tag('span', ['class' => 'd-inline-block ms-3'], $type_text));
+ $update_user_permissions_form->addSelect('user_type', [
+ 'label' => '账号类型',
+ 'options' => [
+ 'student' => '学生',
+ 'teacher' => '老师',
+ 'system' => '系统',
+ ],
+ 'div_class' => 'my-3 row gy-2 gx-3 align-items-center',
+ 'label_class' => 'form-label col-auto',
+ 'select_class' => 'form-select w-auto col-auto',
+ ]);
$update_user_permissions_form->appendHTML(HTML::tag('h3', ['class' => 'h5 mt-3'], '题目'));
$update_user_permissions_form->addCheckbox('problems__view', [
'checked' => $extra['permissions']['problems']['view'],
@@ -707,6 +757,7 @@ EOD);
DB::update([
"update user_info",
"set", [
+ "usertype" => $_POST['user_type'],
"extra" => json_encode($extra),
],
"where", [
diff --git a/web/app/libs/uoj-contest-lib.php b/web/app/libs/uoj-contest-lib.php
index 1b53904..f360bf0 100644
--- a/web/app/libs/uoj-contest-lib.php
+++ b/web/app/libs/uoj-contest-lib.php
@@ -87,14 +87,23 @@ function queryContestData($contest, $config = []) {
$people = [];
if ($contest['extra_config']['individual_or_team'] == 'individual') {
- $people = DB::selectAll([
- "select contests_registrants.username, user_info.realname from contests_registrants",
+ $res = DB::selectAll([
+ "select contests_registrants.username, user_info.realname, user_info.extra, user_info.usergroup from contests_registrants",
"inner join user_info on contests_registrants.username = user_info.username",
"where", [
"contest_id" => $contest['id'],
"has_participated" => 1
]
], DB::NUM);
+ foreach ($res as $row) {
+ $extra = json_decode($row[2], true);
+ $people[] = [
+ $row[0],
+ trim(HTML::escape($row[1])),
+ null,
+ UOJUser::getUserColor2($row[3], $extra['username_color']),
+ ];
+ }
} elseif ($contest['extra_config']['individual_or_team'] == 'team') {
$res = DB::selectAll([
"select user_info.username, null, user_info.extra from contests_registrants, user_info",
@@ -106,11 +115,15 @@ function queryContestData($contest, $config = []) {
], DB::NUM);
foreach ($res as $row) {
$extra = json_decode($row[2], true);
- $row[2] = [
- 'team_name' => $extra['acm']['team_name'],
- 'members' => $extra['acm']['members']
+ $people[] = [
+ $row[0],
+ null,
+ [
+ 'team_name' => $extra['acm']['team_name'],
+ 'members' => $extra['acm']['members'],
+ ],
+ null,
];
- $people[] = $row;
}
}
@@ -373,7 +386,7 @@ function calcStandings($contest, $contest_data, &$score, &$standings, $cfg = [])
}
}
- // standings: rank => score, penalty, [username, realname], virtual_rank, ?review
+ // standings: rank => score, penalty, [username, realname, null|array, null|color], virtual_rank, ?review
$standings = [];
foreach ($contest_data['people'] as $person) {
$cur = array(0, 0, $person);
diff --git a/web/app/libs/uoj-form-lib.php b/web/app/libs/uoj-form-lib.php
index 1e03485..620da94 100644
--- a/web/app/libs/uoj-form-lib.php
+++ b/web/app/libs/uoj-form-lib.php
@@ -735,7 +735,7 @@ function newSubmissionForm($form_name, $requirement, $zip_file_name_gen, $handle
$stat = $zip_file->statName($req['file_name']);
if ($req['type'] == 'source code') {
- $max_size = isset($req['size']) ? (int)$req['size'] : 50;
+ $max_size = isset($req['size']) ? (int)$req['size'] : 100;
if ($stat['size'] > $max_size * 1024) {
$zip_file->close();
unlink(UOJContext::storagePath() . $zip_file_name);
diff --git a/web/app/libs/uoj-html-lib.php b/web/app/libs/uoj-html-lib.php
index 1eb63f3..02242c0 100644
--- a/web/app/libs/uoj-html-lib.php
+++ b/web/app/libs/uoj-html-lib.php
@@ -1009,62 +1009,6 @@ function echoUOJPageFooter($config = array()) {
uojIncludeView('page-footer', $config);
}
-function echoRanklist($config = []) {
- $header_row = '';
- $header_row .= '';
- $header_row .= '# | ';
- $header_row .= '' . UOJLocale::get('username') . ' | ';
- $header_row .= '' . UOJLocale::get('motto') . ' | ';
- $header_row .= '' . UOJLocale::get('solved') . ' | ';
- $header_row .= ' ';
-
- $parsedown = HTML::parsedown();
- $purifier = HTML::purifier_inline();
- $users = [];
- $print_row = function ($user, $now_cnt) use (&$users, $config, $purifier, $parsedown) {
- if (!$users) {
- if ($now_cnt == 1) {
- $rank = 1;
- } else {
- $rank = DB::selectCount("select count(*) from (select b.username as username, count(*) as accepted from best_ac_submissions a inner join user_info b on a.submitter = b.username group by username) as derived where accepted > {$user['ac_num']}") + 1;
- }
- } else {
- $rank = $now_cnt;
- }
-
- $user['rank'] = $rank;
-
- echo '';
- echo '' . $user['rank'] . ' | ';
- echo '' . UOJUser::getLink($user['username']) . ' | ';
- echo "";
- echo $purifier->purify($parsedown->line($user['motto']));
- echo " | ";
- echo '' . $user['ac_num'] . ' | ';
- echo ' ';
-
- $users[] = $user;
- };
-
- $from = 'user_info';
- $col_names = ['user_info.username as username', 'ac_num', 'motto'];
- $cond = '1';
- $tail = 'group by user_info.username order by ac_num desc, user_info.username asc';
-
- if (isset($config['group_id'])) {
- $group_id = $config['group_id'];
- $from = "user_info inner join groups_users on (user_info.username = groups_users.username and groups_users.group_id = {$group_id})";
- $config['pagination_cond'] = "group_id = {$group_id}";
- }
-
- if (isset($config['top10'])) {
- $tail .= ' limit 10';
- }
-
- $config['get_row_index'] = '';
- echoLongTable($col_names, $from, $cond, $tail, $header_row, $print_row, $config);
-}
-
// ===== uoj.ac =====
function echoJudgmentDetails($raw_details, $styler, $name) {
diff --git a/web/app/locale/contests/en.php b/web/app/locale/contests/en.php
index 8421782..da44fcb 100644
--- a/web/app/locale/contests/en.php
+++ b/web/app/locale/contests/en.php
@@ -1,5 +1,6 @@
'New Contest',
'current or upcoming contests' => 'Current or upcoming contests',
'ended contests' => 'Ended contests',
'back to the contest' => 'Back to the contest',
diff --git a/web/app/locale/contests/zh-cn.php b/web/app/locale/contests/zh-cn.php
index e09aad0..6adefb6 100644
--- a/web/app/locale/contests/zh-cn.php
+++ b/web/app/locale/contests/zh-cn.php
@@ -1,5 +1,6 @@
'新建比赛',
'current or upcoming contests' => '正在进行或即将到来的比赛',
'ended contests' => '已结束的比赛',
'back to the contest' => '返回比赛',
diff --git a/web/app/locale/problems/en.php b/web/app/locale/problems/en.php
index 9522bc4..a7c1884 100644
--- a/web/app/locale/problems/en.php
+++ b/web/app/locale/problems/en.php
@@ -52,4 +52,7 @@ return [
'hacks to me' => 'Hacks to me',
'difficulty' => 'Difficulty',
'show difficulty' => 'Show difficulty',
+ 'tags' => 'Tags',
+ 'historical score' => 'Historical Score',
+ 'uploader' => 'Uploader',
];
diff --git a/web/app/locale/problems/zh-cn.php b/web/app/locale/problems/zh-cn.php
index 797ed51..580882f 100644
--- a/web/app/locale/problems/zh-cn.php
+++ b/web/app/locale/problems/zh-cn.php
@@ -52,4 +52,7 @@ return [
'hacks to me' => '我的被Hack记录',
'difficulty' => '难度',
'show difficulty' => '显示难度',
+ 'tags' => '标签',
+ 'historical score' => '历史分数',
+ 'uploader' => '上传者',
];
diff --git a/web/app/models/HTML.php b/web/app/models/HTML.php
index 537a8e0..df75d89 100644
--- a/web/app/models/HTML.php
+++ b/web/app/models/HTML.php
@@ -436,7 +436,11 @@ class HTML {
$def->addElement('footer', 'Block', 'Flow', 'Common');
$extra_allowed_html = [
- 'span' => ['data-realname' => 'Text', 'data-uoj-username' => 'Number'],
+ 'span' => [
+ 'class' => 'Enum#uoj-username',
+ 'data-realname' => 'Text',
+ 'data-color' => 'Color',
+ ],
'img' => ['width' => 'Text'],
];
@@ -463,7 +467,11 @@ class HTML {
'small' => [],
'del' => [],
'br' => [],
- 'span' => ['data-realname' => 'Text', 'data-uoj-username' => 'Number'],
+ 'span' => [
+ 'class' => 'Enum#uoj-username',
+ 'data-realname' => 'Text',
+ 'data-color' => 'Color',
+ ],
];
$allowed_elements = [];
@@ -490,8 +498,8 @@ class HTML {
return new HTMLPurifier($config);
}
- public static function parsedown() {
- return new UOJMarkdown([
+ public static function parsedown($config = []) {
+ return new UOJMarkdown($config + [
'math' => [
'enabled' => true,
'matchSingleDollar' => true
diff --git a/web/app/models/UOJForm.php b/web/app/models/UOJForm.php
index 893ecf3..b22e743 100644
--- a/web/app/models/UOJForm.php
+++ b/web/app/models/UOJForm.php
@@ -258,7 +258,7 @@ class UOJForm {
'options' => [],
'default_value' => '',
'label' => '',
- 'label_class' => 'form-check-label',
+ 'label_class' => 'form-label',
'help' => '',
'help_class' => 'form-text',
'disabled' => false,
diff --git a/web/app/models/UOJMarkdown.php b/web/app/models/UOJMarkdown.php
index e4f32d7..6528f81 100644
--- a/web/app/models/UOJMarkdown.php
+++ b/web/app/models/UOJMarkdown.php
@@ -1,10 +1,12 @@
options['username_with_color'] = $options['username_with_color'] ?: false;
+
// https://gist.github.com/ShNURoK42/b5ce8baa570975db487c
$this->InlineTypes['@'][] = 'UserMention';
$this->inlineMarkerList .= '@';
@@ -19,18 +21,18 @@ class UOJMarkdown extends ParsedownMath {
}
// https://github.com/taufik-nurrohman/parsedown-extra-plugin/blob/1653418c5a9cf5277cd28b0b23ba2d95d18e9bc4/ParsedownExtraPlugin.php#L347-L358
- protected function doGetContent($Element) {
- if (isset($Element['text'])) {
- return $Element['text'];
- }
- if (isset($Element['rawHtml'])) {
- return $Element['rawHtml'];
- }
- if (isset($Element['handler']['argument'])) {
- return implode("\n", (array) $Element['handler']['argument']);
- }
- return null;
- }
+ protected function doGetContent($Element) {
+ if (isset($Element['text'])) {
+ return $Element['text'];
+ }
+ if (isset($Element['rawHtml'])) {
+ return $Element['rawHtml'];
+ }
+ if (isset($Element['handler']['argument'])) {
+ return implode("\n", (array) $Element['handler']['argument']);
+ }
+ return null;
+ }
// https://github.com/taufik-nurrohman/parsedown-extra-plugin/blob/1653418c5a9cf5277cd28b0b23ba2d95d18e9bc4/ParsedownExtraPlugin.php#L369-L378
protected function doSetAttributes(&$Element, $From, $Args = array()) {
@@ -52,27 +54,35 @@ class UOJMarkdown extends ParsedownMath {
}
// https://gist.github.com/ShNURoK42/b5ce8baa570975db487c
- protected function inlineUserMention($Excerpt) {
- if (preg_match('/^@([^\s]+)/', $Excerpt['text'], $matches)) {
- if (($user = UOJUser::query($matches[1])) && $user['usergroup'] != 'B') {
- return [
- 'extent' => strlen($matches[0]),
- 'element' => [
- 'name' => 'span',
- 'text' => '@' . $user['username'],
- 'attributes' => [
- 'class' => 'uoj-username',
- 'data-realname' => $user['realname'],
- 'data-uoj-username' => 1,
- ],
- ],
- ];
- }
+ protected function inlineUserMention($Excerpt) {
+ if (preg_match('/^@([^\s]+)/', $Excerpt['text'], $matches)) {
+ $mentioned_user = UOJUser::query($matches[1]);
- return [
- 'extent' => strlen($matches[0]),
- 'markup' => $matches[0],
- ];
- }
- }
+ if ($mentioned_user) {
+ $color = '#0d6efd';
+
+ if ($this->options['username_with_color']) {
+ $color = UOJUser::getUserColor($mentioned_user);
+ }
+
+ return [
+ 'extent' => strlen($matches[0]),
+ 'element' => [
+ 'name' => 'span',
+ 'text' => '@' . $mentioned_user['username'],
+ 'attributes' => [
+ 'class' => 'uoj-username',
+ 'data-realname' => UOJUser::getRealname($mentioned_user),
+ 'data-color' => $color,
+ ],
+ ],
+ ];
+ }
+
+ return [
+ 'extent' => strlen($matches[0]),
+ 'markup' => $matches[0],
+ ];
+ }
+ }
}
diff --git a/web/app/models/UOJProblem.php b/web/app/models/UOJProblem.php
index e200a14..c0bd929 100644
--- a/web/app/models/UOJProblem.php
+++ b/web/app/models/UOJProblem.php
@@ -22,6 +22,7 @@ class UOJProblem {
2300,
2400,
2500,
+ 2600,
2700,
2900,
3100,
@@ -43,13 +44,254 @@ class UOJProblem {
2300 => '#ff8000',
2400 => '#ff8000',
2500 => '#ff8000',
+ 2600 => '#ff0000',
2700 => '#ff0000',
2900 => '#ff0000',
- 3100 => '#ff0000',
+ 3100 => '#aa0000',
3300 => '#aa0000',
3500 => '#aa0000',
];
+ public static array $categories = [
+ '算法基础' => [
+ '暴力',
+ '枚举',
+ '模拟',
+ '递归与分治',
+ '贪心',
+ '排序',
+ '前缀和与差分',
+ '二分',
+ '倍增',
+ '构造',
+ '打表',
+ ],
+ '搜索' => [
+ '深度优先搜索',
+ '广度优先搜索',
+ '双向搜索',
+ '启发式搜索',
+ 'A*',
+ 'IDA*',
+ '迭代加深',
+ '回溯法',
+ 'Dancing Links',
+ ],
+ '动态规划' => [
+ '记忆化搜索',
+ '线性 DP',
+ '背包 DP',
+ '区间 DP',
+ '树形 DP',
+ '状压 DP',
+ '数位 DP',
+ 'DAG 上 DP',
+ '插头 DP',
+ '概率 DP',
+ '单调队列优化 DP',
+ '斜率优化 DP',
+ '四边形不等式优化 DP',
+ ],
+ '计算几何' => [
+ 'Pick 定理',
+ '三角剖分',
+ '凸包',
+ '扫描线',
+ '旋转卡壳',
+ '半平面交',
+ '平面最近点对',
+ '随机增量法',
+ '反演变换',
+ ],
+ '数学' => [
+ '位运算',
+ '快速幂',
+ '高精度',
+ '生成函数',
+ '指数生成函数',
+ '向量',
+ '矩阵',
+ '高斯消元',
+ '线性基',
+ '线性规划',
+ '容斥',
+ '组合计数',
+ '离散对数',
+ '单纯形算法',
+ '概率',
+ '置换群',
+ '斐波那契数列',
+ '牛顿迭代法',
+ '数值积分',
+ '分块打表',
+ ],
+ '数论' => [
+ '最大公约数',
+ '分解质因数',
+ '欧拉函数',
+ '筛法',
+ '欧拉定理',
+ '费马小定理',
+ '类欧几里得算法',
+ '翡蜀定理',
+ '乘法逆元',
+ '线性同余方程',
+ 'Meissel-Lehmer 算法',
+ '二次剩余',
+ 'BSGS',
+ '原根',
+ '卢卡斯定理',
+ '莫比乌斯反演',
+ '拉格朗日反演',
+ '杜教筛',
+ 'Powerful Number 筛',
+ 'Min_25 筛',
+ '洲阁筛',
+ '连分数',
+ 'Stern-Brocot 数与 Farey 序列',
+ 'Pell 方程',
+ ],
+ '字符串' => [
+ '字符串哈希',
+ '字典树',
+ 'KMP',
+ 'Boyer-Moore',
+ 'Z 函数(扩展 KMP)',
+ 'AC 自动机',
+ '后缀数组',
+ '后缀自动机',
+ '后缀平衡树',
+ '广义后缀自动机',
+ 'Manacher',
+ '回文树',
+ '序列自动机',
+ '最小表示法',
+ 'Lyndon 分解',
+ ],
+ '图论' => [
+ '拓扑排序',
+ '最短路',
+ 'K 短路',
+ '同余最短路',
+ '虚树',
+ '树分治',
+ '动态树分治',
+ '树哈希',
+ '树上启发式合并',
+ 'AHU 算法',
+ '矩阵树定理',
+ '最小生成树',
+ '最小树形图',
+ '最小直径生成树',
+ '斯坦纳树',
+ '拆点',
+ '差分约束',
+ '强连通分量',
+ '双连通分量',
+ '割点与桥',
+ '圆方树',
+ '2-SAT',
+ '欧拉图',
+ '哈密顿图',
+ '最小环',
+ '平面图',
+ '网络流',
+ '最大流',
+ '最小割',
+ '费用流',
+ '上下界网络流',
+ 'Stoer-Wagner 算法',
+ '二分图',
+ '二分图最大匹配',
+ '二分图最大权匹配',
+ '一般图最大匹配',
+ '一般图最大权匹配',
+ 'Prufer 序列',
+ 'LGV 引理',
+ '弦图',
+ ],
+ '组合数学' => [
+ '排列组合',
+ '卡特兰数',
+ '斯特林数',
+ '贝尔数',
+ '伯努利数',
+ '康托展开',
+ '容斥原理',
+ '抽屉原理',
+ '欧拉数',
+ ],
+ '数据结构' => [
+ '栈',
+ '队列',
+ '链表',
+ '哈希表',
+ '并查集',
+ '二叉堆',
+ '配对堆',
+ '树状数组',
+ '线段树',
+ '平衡树',
+ '左偏树',
+ '块状数组',
+ '块状链表',
+ '树分块',
+ 'Sqrt Tree',
+ '可持久化数据结构',
+ '单调栈',
+ '单调队列',
+ 'ST 表',
+ '树套树',
+ '李超线段树',
+ '区间最值操作与区间历史最值',
+ '划分树',
+ '跳表',
+ 'K-D Tree',
+ '珂朵莉树',
+ '动态树',
+ '析合树',
+ ],
+ '多项式' => [
+ '拉格朗日插值',
+ '快速傅里叶变换',
+ '快速数论变换',
+ '快速沃尔什变换',
+ '多项式求逆',
+ '多项式开方',
+ '多项式除法与取模',
+ '多项式对数函数与指数函数',
+ '多项式牛顿迭代',
+ '多项式多点求值与快速插值',
+ '多项式三角函数',
+ '多项式反三角函数',
+ '常系数齐次线性递推',
+ ],
+ '博弈论' => [
+ '不平等博弈',
+ 'SG 函数',
+ 'Nim 游戏',
+ 'Anti-Nim',
+ '纳什均衡',
+ ],
+ '杂项' => [
+ '构造',
+ '离散化',
+ 'CDQ 分治',
+ '整体二分',
+ '分块',
+ '莫队',
+ '分数规划',
+ '随机化',
+ '模拟退火',
+ '爬山法',
+ '悬线法',
+ '编译原理',
+ '复杂度分析',
+ '语义分析',
+ '底层优化',
+ ],
+ ];
+
public static function query($id) {
if (!isset($id) || !validateUInt($id)) {
return null;
diff --git a/web/app/models/UOJRanklist.php b/web/app/models/UOJRanklist.php
index 2aadd7a..b423fc7 100644
--- a/web/app/models/UOJRanklist.php
+++ b/web/app/models/UOJRanklist.php
@@ -22,7 +22,7 @@ class UOJRanklist {
}
$last_user = null;
- $parsedown = HTML::parsedown();
+ $parsedown = HTML::parsedown(['username_with_color' => true]);
$purifier = HTML::purifier_inline();
$print_row = function ($user, $now_cnt) use (&$last_user, &$conds, &$parsedown, &$purifier) {
if ($last_user === null) {
@@ -138,7 +138,7 @@ class UOJRanklist {
$header_row .= ' |
';
$last_user = null;
- $parsedown = HTML::parsedown();
+ $parsedown = HTML::parsedown(['username_with_color' => true]);
$purifier = HTML::purifier_inline();
$print_row = function ($user, $now_cnt) use (&$last_user, &$conds, &$parsedown, &$purifier) {
if ($last_user === null) {
diff --git a/web/app/models/UOJUser.php b/web/app/models/UOJUser.php
index 46e955e..29ba52f 100644
--- a/web/app/models/UOJUser.php
+++ b/web/app/models/UOJUser.php
@@ -210,6 +210,43 @@ class UOJUser {
}
}
+ public static function getRealname($user) {
+ $realname = $user['realname'];
+
+ if ($user['usertype'] == 'teacher') {
+ $realname .= '老师';
+ }
+
+ return $realname;
+ }
+
+ public static function getUserColor($user) {
+ $extra = UOJUser::getExtra($user);
+
+ return UOJUser::getUserColor2($user['usergroup'], $extra['username_color']);
+ }
+
+ public static function getUserColor2($usergroup, $custom_color = null) {
+ if ($usergroup == 'B') {
+ return '#996600';
+ }
+
+ if ($usergroup == 'T') {
+ return '#707070';
+ }
+
+ if ($usergroup == 'S') {
+ return $custom_color ?: '#9d3dcf';
+ }
+
+ // 前管理员设置颜色为紫色的,颜色改为蓝色
+ if ($custom_color == '#9d3dcf') {
+ return '#0d6efd';
+ }
+
+ return $custom_color ?: '#0d6efd';
+ }
+
public static function getLink($user) {
if (is_string($user)) {
$info = UOJUser::query($user);
@@ -221,14 +258,18 @@ class UOJUser {
}
}
- if ($user['usergroup'] == 'B') {
- return HTML::tag('a', ['class' => 'text-danger fw-bold', 'href' => "/user/{$user['username']}"], $user['username']);
- }
+ $realname = UOJUser::getRealname($user);
// 未登录不可查看真实姓名
- $realname = Auth::check() ? $user['realname'] : '';
+ if (!Auth::check()) {
+ $realname = '';
+ }
- return HTML::tag('span', ['class' => 'uoj-username', 'data-realname' => trim(HTML::escape($realname))], $user['username']);
+ return HTML::tag('span', [
+ 'class' => 'uoj-username',
+ 'data-color' => UOJUser::getUserColor($user),
+ 'data-realname' => trim(HTML::escape($realname)),
+ ], $user['username']);
}
public static function getUpdatedExtraVisitHistory($history, $cur) {
@@ -290,6 +331,7 @@ class UOJUser {
'show_email' => 'all',
'show_qq' => 'all',
'avatar_source' => 'gravatar',
+ 'username_color' => isSuperUser($user) ? '#9d3dcf' : '#0d6efd',
]);
return $extra;
}
@@ -340,6 +382,7 @@ class UOJUser {
$extra = UOJUser::getExtra($user);
$cur = [
'addr' => $info['remote_addr'],
+ 'forwarded_addr' => $info['http_x_forwarded_for'],
'ua' => substr($info['http_user_agent'], 0, UOJUser::MAX_UA_LEN),
'last' => UOJTime::$time_now_str
];
diff --git a/web/app/route.php b/web/app/route.php
index b90f26c..1f11278a 100644
--- a/web/app/route.php
+++ b/web/app/route.php
@@ -92,9 +92,9 @@ Route::group(
Route::any('/click-zan', '/click_zan.php');
// Apps
- Route::any('/image_hosting', '/image_hosting/index.php');
- Route::get('/image_hosting/{image_name}.png', '/image_hosting/get_image.php');
- Route::any('/html2markdown', '/html2markdown.php');
+ Route::any('/image_hosting', '/app/image_hosting/index.php');
+ Route::get('/image_hosting/{image_name}.png', '/app/image_hosting/get_image.php');
+ Route::any('/html2markdown', '/app/html2markdown.php');
}
);
diff --git a/web/app/vendor/erusev/parsedown/Parsedown.php b/web/app/vendor/erusev/parsedown/Parsedown.php
index 1b9d6d5..9f7eb5f 100644
--- a/web/app/vendor/erusev/parsedown/Parsedown.php
+++ b/web/app/vendor/erusev/parsedown/Parsedown.php
@@ -19,6 +19,8 @@ class Parsedown
const version = '1.7.4';
+ protected $options = [];
+
# ~
function text($text)
diff --git a/web/app/views/contest-reviews.php b/web/app/views/contest-reviews.php
index 7b73701..d0478e0 100644
--- a/web/app/views/contest-reviews.php
+++ b/web/app/views/contest-reviews.php
@@ -1,7 +1,7 @@
true]);
$purifier = HTML::purifier_inline();
foreach ($contest_data['people'] as $person) {
diff --git a/web/app/views/contest-standings.php b/web/app/views/contest-standings.php
index 35ee895..b5240f8 100644
--- a/web/app/views/contest-standings.php
+++ b/web/app/views/contest-standings.php
@@ -35,7 +35,7 @@
function(row) {
var col_tr = '