From 187a43335fb6233cf8bf4b0d600462819b3adace Mon Sep 17 00:00:00 2001 From: liuhengxi2007 <2019891597@qq.com> Date: Mon, 7 Jul 2025 18:54:25 +0800 Subject: [PATCH 01/20] change click zan permission; change upload hint; add cut off time filter; visualize tl and ml; add copyable code in md; add collection; add warning when content contains oj address; hide unsuccessful hack in submission history; support iframe document; change color (mostly affected around 50pts); --- .gitignore | 3 + app/.default-userinfo.php | 2 +- app/controllers/click_zan.php | 3 + app/controllers/collection.php | 152 +++++++++++++++++ app/controllers/collection_manage.php | 179 +++++++++++++++++++ app/controllers/collection_propose.php | 16 ++ app/controllers/file_upload.php | 2 +- app/controllers/hack_list.php | 13 +- app/controllers/problem.php | 62 ++++++- app/controllers/problem_set.php | 7 +- app/controllers/problem_statement_md.php | 68 ++++++++ app/controllers/submissions_list.php | 4 - app/locale/basic/en.php | 4 + app/locale/basic/zh-cn.php | 4 + app/models/UOJBlogEditor.php | 17 +- app/route.php | 5 + app/uoj-form-lib.php | 4 + app/uoj-html-lib.php | 2 +- app/uoj-query-lib.php | 20 ++- app/views/blog-preview.php | 10 ++ css/soj-theme.css | 4 + js/blog-editor/blog-editor.js | 3 + js/marked.js | 209 ++++++++++++++++++----- js/soj.js | 32 +++- 24 files changed, 761 insertions(+), 64 deletions(-) create mode 100644 app/controllers/collection.php create mode 100644 app/controllers/collection_manage.php create mode 100644 app/controllers/collection_propose.php create mode 100644 app/controllers/problem_statement_md.php diff --git a/.gitignore b/.gitignore index bf6ea2d..431a4d7 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,6 @@ uploads/* utility/ pictures/ js/mathjax/ +app/locale/basic/zh-en.php +app/locale/contests/zh-en.php +app/locale/problems/zh-en.php diff --git a/app/.default-userinfo.php b/app/.default-userinfo.php index 0b2b413..fadd576 100644 --- a/app/.default-userinfo.php +++ b/app/.default-userinfo.php @@ -132,4 +132,4 @@ function (str) { return $res; } ) -); \ No newline at end of file +); diff --git a/app/controllers/click_zan.php b/app/controllers/click_zan.php index 9f7282b..ac1363c 100644 --- a/app/controllers/click_zan.php +++ b/app/controllers/click_zan.php @@ -16,6 +16,9 @@ function validateZan() { if (!Auth::check()) { die('
please login
'); } + if (!isBlogAllowedUser(Auth::user())) { + die('
permission denied
'); + } $id = $_POST['id']; $delta = $_POST['delta']; diff --git a/app/controllers/collection.php b/app/controllers/collection.php new file mode 100644 index 0000000..1659273 --- /dev/null +++ b/app/controllers/collection.php @@ -0,0 +1,152 @@ += 0 && $state <= 2)) die('invalid state'); + $username = Auth::id(); + DB::insert("insert into collection_state (id, username, state) values ('$id', '$username', $state) on duplicate key update state = $state"); + die('ok'); + } + + function echoProblem($problem) { + $username = Auth::id(); + if (isBlogAllowedUser(Auth::user())) { + $state = 0; + $stateResult = DB::selectFirst("select state from collection_state where id = '{$problem['id']}' and username = '$username'"); + if(isset($stateResult)) $state = $stateResult['state']; + $color = ['fff','feb','dfb'][$state]; + echo ''; + echo ''; + echo $problem['id'], ''; + echo ''; + echo '
'; + echo ''; + echo '', $problem['title'], ''; + echo ''; + echo ''; + echo ' '; + echo ' '; + echo ''; + echo ''; + echo '
'; + echo ''; + echo ''; + echo $problem['note']; + echo ''; + echo ''; + } + } + + $cur_tab = isset($_GET['type']) ? $_GET['type'] : 'null'; + + $cond = "exists (select 1 from collection_tags where collection_tags.collection_id = collection.id and collection_tags.tag = '{$cur_tab}')"; + + $header = ''; + $header .= 'ID'; + $header .= '' . UOJLocale::get('problems::problem') . ''; + $header .= '' . UOJLocale::get('note') . ''; + $header .= ''; + + $tabs_info = array( + 'counting' => array( + 'name' => 'Counting', + 'url' => '/collection?type=counting' + ), + 'constructive' => array( + 'name' => 'Constructive', + 'url' => '/collection?type=constructive' + ), + 'interactive' => array( + 'name' => 'Interactive', + 'url' => '/collection?type=interactive' + ), + ); + + $pag_config = array('page_len' => 1000); + $pag_config['col_names'] = array('*'); + $username = Auth::id(); + $pag_config['table_name'] = "collection"; + $pag_config['cond'] = $cond; + $pag_config['tail'] = 'order by id asc'; + $pag_config['max_extend'] = 5; + $pag_config['timeout'] = 1000; + $pag = new Paginator($pag_config); + + $div_classes = array('table-responsive'); + $table_classes = array('table', 'table-bordered', 'table-hover', 'table-striped'); +?> + + +
+
+ +
+ +
+ +
+ + +
+ +
+ +
+
+
+
+
+ pagination(); ?> +
+
+
+'; + echo ''; + echo ''; + echo $header; + echo ''; + echo ''; + + foreach ($pag->get() as $idx => $row) { + echoProblem($row); + } + + echo ''; + echo '
'; + echo ''; + + echo $pag->pagination(); +?> + diff --git a/app/controllers/collection_manage.php b/app/controllers/collection_manage.php new file mode 100644 index 0000000..7dc0ebf --- /dev/null +++ b/app/controllers/collection_manage.php @@ -0,0 +1,179 @@ +'; + echo ''; + echo $problem['id'], ''; + echo ''; + echo '', $problem['title'], ''; + foreach (queryCollectionTags($problem['id']) as $tag) { + echo '', '', HTML::escape($tag), '', ''; + } + echo ''; + echo ''; + echo $problem['note']; + echo ''; + echo ''; + } + } + + $cur_tab = isset($_GET['type']) ? $_GET['type'] : 'null'; + + $cond = "1"; + + $header = ''; + $header .= 'ID'; + $header .= '' . UOJLocale::get('problems::problem') . ''; + $header .= '' . UOJLocale::get('note') . ''; + $header .= ''; + + $pag_config = array('page_len' => 10000); + $pag_config['col_names'] = array('*'); + $username = Auth::id(); + $pag_config['table_name'] = "collection"; + $pag_config['cond'] = $cond; + $pag_config['tail'] = 'order by id asc'; + $pag_config['max_extend'] = 5; + $pag_config['timeout'] = 1000; + $pag = new Paginator($pag_config); + + $div_classes = array('table-responsive'); + $table_classes = array('table', 'table-bordered', 'table-hover', 'table-striped'); + + $append_form = new UOJForm('append'); + + $append_form->addInput('id', 'text', 'id', '', + function($result) { + if(!preg_match('/^-?[a-z0-9]{1,12}$/', $result)) return '题号长度不能超过 12,且只能包含小写字母和数字'; + return ''; + }, + null + ); + $append_form->addInput('title', 'text', 'title', '', + function($result) { + if(strlen($result) > 100) return '长度不能超过 100 byte'; + return ''; + }, + null + ); + $append_form->addInput('url', 'text', 'url', '', + function($result) { + if(strlen($result) > 100) return '长度不能超过 100 byte'; + return ''; + }, + null + ); + $append_form->addInput('tag', 'text', 'tag', '', + function($result) { + if(strlen($result) > 100) return '长度不能超过 100 byte'; + return ''; + }, + null + ); + $append_form->addInput('note', 'text', 'note', '', + function($result) { + if(strlen($result) > 100) return '长度不能超过 100 byte'; + return ''; + }, + null + ); + + $append_form->handle = function() { + $delete_permission = isProblemCreator(Auth::user()); + + $id = $_POST['id']; + if ($id[0] == '-') { + $id = substr($id, 1); + if($delete_permission) DB::delete("delete from collection where id='$id'"); + if($delete_permission) DB::delete("delete from collection_tags where collection_id='$id'"); + return; + } + $title = DB::escape($_POST['title']); + $url = DB::escape($_POST['url']); + $tag = DB::escape(strtolower($_POST['tag'])); + $note = DB::escape($_POST['note']); + + $upd_cmd = ''; + if($title != '') $upd_cmd .= ", title='$title'"; + if($url != '') $upd_cmd .= ", url='$url'"; + if($note != '') { + if($note == '-') $note = ''; + $upd_cmd .= ", note='$note'"; + } + if($upd_cmd != '') { + DB::insert("insert into collection (id, title, url, note) values ('$id', '$title', '$url', '$note') on duplicate key update" . substr($upd_cmd, 1)); + } + if($tag == '-') { + if($delete_permission) DB::delete("delete from collection_tags where collection_id='$id'"); + } + else if($tag != '' && DB::selectCount("select count(*) from collection where id = '$id'")) { + DB::insert("insert ignore into collection_tags (collection_id, tag) values ('$id', '$tag')"); + } + }; + $append_form->runAtServer(); +?> + +printHTML(); ?> +

约定

+选择一个题目所属的 OJ 时,按照以下顺序选择: +
    +
  1. 原出处,且有中文题面或英文题面
  2. +
  3. 有中文题面
  4. +
  5. 有英文题面
  6. +
  7. 原出处,但无中文题面或英文题面
  8. +
  9. 其他
  10. +
+有多个同一类的 OJ,按照以下优先级选择。 +

各 OJ 题号格式(按照优先级排序)

+ + + + + + + + + + +
OJ题号格式
AtCoderagc001a
Codeforces Problemsetcf1a
UOJuoj1
QOJqoj1
LOJloj1
SOJsoj1
Luogulg1001
Codeforces Gymcf111111a
+ +

列表

+'; + echo ''; + echo ''; + echo $header; + echo ''; + echo ''; + + foreach ($pag->get() as $idx => $row) { + echoProblem($row); + } + + echo ''; + echo '
'; + echo ''; + + echo $pag->pagination(); +?> + diff --git a/app/controllers/collection_propose.php b/app/controllers/collection_propose.php new file mode 100644 index 0000000..ef34a81 --- /dev/null +++ b/app/controllers/collection_propose.php @@ -0,0 +1,16 @@ + + + diff --git a/app/controllers/file_upload.php b/app/controllers/file_upload.php index 2a46889..8197c5c 100644 --- a/app/controllers/file_upload.php +++ b/app/controllers/file_upload.php @@ -203,7 +203,7 @@ function Ls($path) { } if (move_uploaded_file($_FILES['upload']['tmp_name'], UOJContext::documentRoot() . '/' . $path)) { - becomeMsgPage("
上传成功!最终路径:{$url}/{$path}
" . '返回'); + becomeMsgPage("
上传成功!最终路径:/{$path}
" . '返回'); } else { becomeMsgPage('上传失败!'); } diff --git a/app/controllers/hack_list.php b/app/controllers/hack_list.php index 0031ff9..77797d7 100644 --- a/app/controllers/hack_list.php +++ b/app/controllers/hack_list.php @@ -9,9 +9,10 @@ $common = 'class="form-control input-sm" '; $q_submission_id = $search_form->addText('submission_id', UOJLocale::get('problems::submission id').':', $common.'maxlength="6" style="width:5em"', 'validateUInt'); $q_problem_id = $search_form->addText('problem_id', UOJLocale::get('problems::problem id').':', $common.'maxlength="4" style="width:4em"', 'validateUInt'); - $q_hacker = $search_form->addText('hacker', UOJLocale::get('problems::hacker').':', $common.'maxlength="20" style="width:10em"', 'validateUsername'); - $q_owner = $search_form->addText('owner', UOJLocale::get('problems::owner').':', $common.'maxlength="20" style="width:10em"', 'validateUsername'); - $q_status = $search_form->addSelect('status', UOJLocale::get('problems::result').':', $common, [''=>'All', 1=>'Success!', 2=>'Failed.']); + $q_hacker = $search_form->addText('hacker', UOJLocale::get('problems::hacker').':', $common.'maxlength="20" style="width:8em"', 'validateUsername'); + $q_owner = $search_form->addText('owner', UOJLocale::get('problems::owner').':', $common.'maxlength="20" style="width:8em"', 'validateUsername'); + $q_status = $search_form->addSelect('status', UOJLocale::get('problems::result').':', $common.'style="width:7em"', [''=>'All', 1=>'Success!', 2=>'Failed.']); + $q_cut_off_time = $search_form->addText('cut_off_time', UOJLocale::get('cut off time').':', $common . 'maxlength="11" style="width:8em; font-family: monospace" placeholder="YYYY-MM-DD"', validateTime); $search_form->addSubmit(UOJLocale::get('search')); $conds = array(); @@ -33,6 +34,12 @@ if($q_status == 2) { $conds[] = 'success = 0'; } + if ($q_cut_off_time != null) { + if (strlen($q_cut_off_time) == 10) { + $q_cut_off_time = $q_cut_off_time . ' 23:59:59'; + } + $conds[] = 'submit_time <=\'' . DB::escape($q_cut_off_time) . '\''; + } if ($conds) { $cond = join($conds, ' and '); diff --git a/app/controllers/problem.php b/app/controllers/problem.php index 5417e81..6bf34a8 100644 --- a/app/controllers/problem.php +++ b/app/controllers/problem.php @@ -86,8 +86,24 @@ $memory_suf = 'MiB*'; } } - $tags['time limit'] = getUOJConfVal($problem_conf, 'time_limit', 1) . $time_suf; - $tags['memory limit'] = getUOJConfVal($problem_conf, 'memory_limit', 256) . $memory_suf; + $time_limit = getUOJConfVal($problem_conf, 'time_limit', 1); + $memory_limit = getUOJConfVal($problem_conf, 'memory_limit', 256); + $time_style = ""; + if($time_limit < 0.5) { + $time_style = "color:red"; + } + if($time_limit > 5) { + $time_style = "color:blue"; + } + $memory_style = ""; + if($memory_limit < 128) { + $memory_style = "color:red"; + } + if($memory_limit > 1024) { + $memory_style = "color:blue"; + } + $tags['time limit'] = "" . $time_limit . $time_suf . ""; + $tags['memory limit'] = "" . $memory_limit . $memory_suf . ""; } $tags['checker'] = getUOJConfVal($problem_conf, 'use_builtin_checker', UOJLocale::get('problems::custom checker')); if (getUOJConfVal($problem_conf, 'use_builtin_judger', null) !== 'on') { @@ -332,7 +348,47 @@ function(response_text) {custom_test_onsubmit(response_text, $('#div-custom_test
-
+
+ + +
diff --git a/app/controllers/problem_set.php b/app/controllers/problem_set.php index e947ffb..927e300 100644 --- a/app/controllers/problem_set.php +++ b/app/controllers/problem_set.php @@ -179,10 +179,13 @@ function echoProblem($problem) { ?>
-
+
-
+
+ +
+
diff --git a/app/controllers/problem_statement_md.php b/app/controllers/problem_statement_md.php new file mode 100644 index 0000000..843a99b --- /dev/null +++ b/app/controllers/problem_statement_md.php @@ -0,0 +1,68 @@ + diff --git a/app/controllers/submissions_list.php b/app/controllers/submissions_list.php index cc4f565..224d8c6 100644 --- a/app/controllers/submissions_list.php +++ b/app/controllers/submissions_list.php @@ -5,10 +5,6 @@ requirePHPLib('form'); - function validateTime($time) { - return is_string($time) && (preg_match('/^(19|20)\d{2}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])( (0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]))?$/', $time)); - } - $search_form = new SOJForm(); $common = 'class="form-control input-sm" '; $q_problem_id = $search_form->addText('problem_id', UOJLocale::get('problems::problem id').':', $common . 'maxlength="4" style="width:4em"', 'validateUInt'); diff --git a/app/locale/basic/en.php b/app/locale/basic/en.php index 0ef6edf..4ee35b0 100644 --- a/app/locale/basic/en.php +++ b/app/locale/basic/en.php @@ -173,4 +173,8 @@ 'manageable' => 'Manageable', 'log out all' => 'Log out on all devices', 'cut off time' => 'Cut-off time', + 'problem collection' => 'Collection', + 'manage' => 'Manage', + 'propose' => 'Propose', + 'note' => 'Note', ]; diff --git a/app/locale/basic/zh-cn.php b/app/locale/basic/zh-cn.php index 32137b6..32a2829 100644 --- a/app/locale/basic/zh-cn.php +++ b/app/locale/basic/zh-cn.php @@ -173,4 +173,8 @@ 'manageable' => '有权限', 'log out all' => '在所有设备登出', 'cut off time' => '截止时间', + 'problem collection' => '题单', + 'manage' => '管理', + 'propose' => '提议', + 'note' => '备注', ]; diff --git a/app/models/UOJBlogEditor.php b/app/models/UOJBlogEditor.php index c032032..bb3d8c5 100644 --- a/app/models/UOJBlogEditor.php +++ b/app/models/UOJBlogEditor.php @@ -41,6 +41,11 @@ function __construct() { if (strlen($content_md) > 1000000) { return '内容过长'; } + foreach (UOJConfig::$data['profile']['oj-address'] as $address) { + if (strpos($content_md, $address) !== false) { + return 'Warning: 使用相对链接'; + } + } return ''; }, 'tags' => function(&$tags) { @@ -78,10 +83,15 @@ public function validate($name) { } private function receivePostData() { $errors = array(); + $warnings = array(); foreach (array('title', 'content_md', 'tags') as $name) { $cur_err = $this->validate($name); if ($cur_err) { - $errors[$name] = $cur_err; + if (substr($cur_err, 0, 7) == 'Warning') { + $warnings[$name] = $cur_err; + } else { + $errors[$name] = $cur_err; + } } } if ($errors) { @@ -188,11 +198,12 @@ private function receivePostData() { } $this->post_data['content'] = json_encode($config) . "\n" . $this->post_data['content']; } + return $warnings; } public function handleSave() { $save = $this->save; - $this->receivePostData(); + $warnings = $this->receivePostData(); $ret = $save($this->post_data); if (!$ret) { $ret = array(); @@ -218,6 +229,8 @@ public function handleSave() { $ret['html'] = ob_get_contents(); ob_end_clean(); } + + $ret['warnings'] = $warnings; die(json_encode($ret)); } diff --git a/app/route.php b/app/route.php index 9d0d949..102508a 100644 --- a/app/route.php +++ b/app/route.php @@ -17,11 +17,16 @@ Route::any('/problems/template', '/problem_set.php?tab=template'); Route::any('/problems/contested', '/problem_set.php?tab=contested'); Route::any('/problem/{id}', '/problem.php'); + Route::any('/problem/{id}/statement.md', '/problem_statement_md.php'); Route::any('/problem/{id}/statistics', '/problem_statistics.php'); Route::any('/problem/{id}/manage/statement', '/problem_statement_manage.php'); Route::any('/problem/{id}/manage/managers', '/problem_managers_manage.php'); Route::any('/problem/{id}/manage/data', '/problem_data_manage.php'); + Route::any('/collection', '/collection.php'); + Route::any('/collection/propose', '/collection_propose.php'); + Route::any('/collection/manage', '/collection_manage.php'); + Route::any('/contests', '/contests.php'); Route::any('/contest/new', '/add_contest.php'); Route::any('/contest/{id}', '/contest_inside.php'); diff --git a/app/uoj-form-lib.php b/app/uoj-form-lib.php index 7aaa827..0a43404 100644 --- a/app/uoj-form-lib.php +++ b/app/uoj-form-lib.php @@ -998,3 +998,7 @@ public function printHTML($url) { function validateLength($len) { return function($x) use ($len) {return mb_strlen($x) <= $len;}; } + + function validateTime($time) { + return is_string($time) && (preg_match('/^(19|20)\d{2}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])( (0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]))?$/', $time)); + } diff --git a/app/uoj-html-lib.php b/app/uoj-html-lib.php index c1c28c4..67236df 100644 --- a/app/uoj-html-lib.php +++ b/app/uoj-html-lib.php @@ -321,7 +321,7 @@ function echoSubmission($submission, $config, $user) { $limitLevel = querySubmissionDetailPermission($user, $submission); $problem = queryProblemBrief($submission['problem_id']); $status = explode(', ', $submission['status'])[0]; - $show_status_details = isOurSubmission($user, $submission) && $status !== 'Judged'; + $show_status_details = (isOurSubmission($user, $submission) || isSuperUser(Auth::user())) && $status !== 'Judged'; $submission_uri = getSubmissionUri($submission['id']); $submission_id_str = getSubmissionLink($submission['id']); diff --git a/app/uoj-query-lib.php b/app/uoj-query-lib.php index 47eb374..fd982de 100644 --- a/app/uoj-query-lib.php +++ b/app/uoj-query-lib.php @@ -94,6 +94,12 @@ function queryProblemTags($id) { return $tags; } +function queryCollectionTags($id) { + $result = DB::select("select tag from collection_tags where collection_id = '$id' order by id"); + for ($tags = array(); $row = DB::fetch($result, MYSQLI_NUM); $tags[] = $row[0]); + return $tags; +} + function queryContestProblemRank($contest, $problem) { if (!DB::selectFirst("select * from contests_problems where contest_id = {$contest['id']} and problem_id = {$problem['id']}")) { return null; @@ -405,12 +411,14 @@ function getHacksAuditLog($config = array()) { 'actor' => $his['hacker'], 'details' => $his ); - $audit_log[] = array( - 'time' => $his['judge_time'], - 'type' => 'hack judgement, auto', - 'hack_id' => $his['hack_id'], - 'details' => $his - ); + if($his['success'] != null) { + $audit_log[] = array( + 'time' => $his['judge_time'], + 'type' => 'hack judgement, auto', + 'hack_id' => $his['hack_id'], + 'details' => $his + ); + } } sortAuditLogByTime($audit_log); return $audit_log; diff --git a/app/views/blog-preview.php b/app/views/blog-preview.php index fc426e7..bc15b0c 100644 --- a/app/views/blog-preview.php +++ b/app/views/blog-preview.php @@ -23,6 +23,16 @@
+
diff --git a/css/soj-theme.css b/css/soj-theme.css index 0ff2a2a..6c9ad3f 100644 --- a/css/soj-theme.css +++ b/css/soj-theme.css @@ -401,6 +401,10 @@ pre { margin-left: 5px; margin-right: 5px; } +.uoj-collection-tag { + margin-left: 5px; + margin-right: 5px; +} .uoj-blog-tag { margin-left: 5px; margin-right: 5px; diff --git a/js/blog-editor/blog-editor.js b/js/blog-editor/blog-editor.js index 747fa52..310d0ed 100644 --- a/js/blog-editor/blog-editor.js +++ b/js/blog-editor/blog-editor.js @@ -184,6 +184,9 @@ function blog_editor_init(name, editor_config) { $(['title', 'content_md', 'tags']).each(function() { ok &= showErrorHelp(name + '_' + this, data[this]); }); + $(['title', 'content_md', 'tags']).each(function() { + showWarningHelp(name + '_' + this, data['warnings'][this]); + }); if (data.extra !== undefined) { alert(data.extra); ok = false; diff --git a/js/marked.js b/js/marked.js index f2c231e..a5a0595 100644 --- a/js/marked.js +++ b/js/marked.js @@ -1,5 +1,5 @@ /** - * marked - a markdown parser + * Modified from marked - a markdown parser * Copyright (c) 2011-2013, Christopher Jeffrey. (MIT Licensed) * https://github.com/chjj/marked */ @@ -363,7 +363,8 @@ Lexer.prototype.token = function(src, top) { src = src.substring(cap[0].length); this.tokens.links[cap[1].toLowerCase()] = { href: cap[2], - title: cap[3] + title: cap[3], + rest: '' }; continue; } @@ -452,11 +453,14 @@ var inline = { math: /^\$((?:[^\\]|\\\\|\\[^\\]+?)+?)\$|^\$\$((?:[^\\]|\\\\|\\[^\\]+?)+?)\$\$|^\\begin{[^}]+}((?:[^\\]|\\\\|\\[^\\]+?)+?)\\end{[^}]+}/, br: /^ {2,}\n(?!\s*$)/, del: noop, - text: /^[\s\S]+?(?=[\\?(?:\s+['"]([\s\S]*?)['"])?\s*/; +inline._href = /\s*?(?:\s+['"]([^)]*?)['"])?(\s*(?:\s\.[\w=]+\s*)*)/; inline.link = replace(inline.link) ('inside', inline._inside) @@ -618,7 +622,8 @@ InlineLexer.prototype.output = function(src) { src = src.substring(cap[0].length); out += this.outputLink(cap, { href: cap[2], - title: cap[3] + title: cap[3], + rest: cap[4] }); continue; } @@ -703,6 +708,31 @@ InlineLexer.prototype.output = function(src) { InlineLexer.prototype.outputLink = function(cap, link) { if (cap[0][0] !== '!') { + + if (link.href[0] == '%') { + link.href = link.href.substring(1); + return '' + + this.output(cap[1]) + + ''; + } + + if (link.href[0] == ':') { + var ret = this.extractProperties(link.rest); + if (link.href == ':user' && ret[1].rating !== undefined) { + return '' + escape(cap[1]) + ''; + } else { + return '{errors occur in processing ' + escape(link.href) + '}'; + } + } + return ''; } else { + var ret = this.extractProperties(link.rest) + , style = '' + , cls; + + if (ret[1].h !== undefined) { + style += "height:" + ret[1].h + "px;"; + } + if (ret[1].w !== undefined) { + style += "width:" + ret[1].w + "px;"; + } + if (ret[1].left) { + cls = 'img-responsive'; + } else if (ret[1].right) { + cls = 'img-responsive'; + style += "margin-left: auto;"; + } else if (ret[1].inline) { + cls = ''; + } else { + cls = 'img-responsive center-block'; + } + return ''
       + escape(cap[1])
       + ''; } }; +/** + * Extract Properties + */ + +InlineLexer.prototype.extractProperties = function(src) { + var props = {} + , cap; + + if (!src) { + return ['', props]; + } + while (src) { + // width, height, rating + if (cap = this.rules.property_num.exec(src)) { + src = src.substring(0, src.length - cap[0].length); + if (!(cap[1] in props)) { + props[cap[1]] = cap[2]; + } + continue; + } + + // keep, left, right, center + if (cap = this.rules.property_bool.exec(src)) { + src = src.substring(0, src.length - cap[0].length); + props[cap[1]] = true; + continue; + } + + break; + } + + return [src, props]; +}; + /** * Mangle Links */ @@ -820,8 +903,8 @@ Parser.prototype.parseText = function() { */ Parser.prototype.tok = function() { - var tok_class = this.options.getElementClass == null ? null : this.options.getElementClass(this.token); - tok_class = tok_class == null ? '' : ' class="' + tok_class + '"'; + var inner_tok_class = this.options.getElementClass == null ? null : this.options.getElementClass(this.token); + tok_class = inner_tok_class == null ? '' : ' class="' + inner_tok_class + '"'; switch (this.token.type) { case 'space': { return ''; @@ -851,16 +934,18 @@ Parser.prototype.tok = function() { if (!this.token.escaped) { this.token.text = escape(this.token.text, true); } - - return '
' + this.token.text + '
'; + } else if (this.options.getLangClass && this.token.lang != undefined) { + return '
'
+        + '">'
         + this.token.text
         + '
\n'; + } else { + return '
' + this.token.text + '
'; + } } case 'table': { var body = '' @@ -868,15 +953,32 @@ Parser.prototype.tok = function() { , i , row , cell + , attr , j; + var rowspan = false; + // header body += '\n\n'; for (i = 0; i < this.token.header.length; i++) { - heading = this.inline.output(this.token.header[i]); - body += this.token.align[i] - ? '' + heading + '\n' - : '' + heading + '\n'; + if (!this.token.header[i]) { + continue; + } + + heading = this.inline.extractProperties(this.token.header[i]); + heading[0] = this.inline.output(heading[0]); + attr = ''; + if (this.token.align[i]) { + attr += ' style="text-align:' + this.token.align[i] + '"'; + } + if (heading[1].w !== undefined) { + attr += ' colspan="' + cell[1].w + '"'; + } + if (heading[1].h !== undefined) { + attr += ' rowspan="' + cell[1].h + '"'; + rowspan = true; + } + body += '' + heading[0] + '\n'; } body += '\n\n'; @@ -886,18 +988,39 @@ Parser.prototype.tok = function() { row = this.token.cells[i]; body += '\n'; for (j = 0; j < row.length; j++) { - cell = this.inline.output(row[j]); - body += this.token.align[j] - ? '' + cell + '\n' - : '' + cell + '\n'; + if (!row[j]) { + continue; + } + + cell = this.inline.extractProperties(row[j]); + cell[0] = this.inline.output(cell[0]); + attr = ''; + if (this.token.align[j]) { + attr += ' style="text-align:' + this.token.align[j] + '"'; + } + if (cell[1].w !== undefined) { + attr += ' colspan="' + cell[1].w + '"'; + } + if (cell[1].h !== undefined) { + attr += ' rowspan="' + cell[1].h + '"'; + rowspan = true; + } + body += '' + cell[0] + '\n'; } body += '\n'; } body += '\n'; - return '\n' + if (!rowspan) { + if (inner_tok_class != null) { + inner_tok_class += ' table-striped'; + } + tok_class = inner_tok_class == null ? '' : ' class="' + inner_tok_class + '"'; + } + + return '
\n' + '\n' + body - + '\n'; + + '\n
\n'; } case 'blockquote_start': { var body = ''; @@ -953,6 +1076,9 @@ Parser.prototype.tok = function() { } case 'html': { return this.token.text; + /*return !this.token.pre && !this.options.pedantic + ? this.inline.output(this.token.text) + : this.token.text;*/ } case 'paragraph': { return '' @@ -1051,18 +1177,21 @@ marked.defaults = { silent: false, highlight: null, getLangClass: function(lang) { - lang = lang.toLowerCase(); - switch (lang) { - case 'c': return 'sh_c'; - case 'c++': return 'sh_cpp'; - case 'pascal': return 'sh_pascal'; - default: return 'sh_' + lang; - } + lang = lang.toLowerCase(); + switch (lang) { + case 'c': return 'sh_c'; + case 'c++': return 'sh_cpp'; + case 'pascal': return 'sh_pascal'; + default: return 'sh_' + lang; + } }, - getElementClass: function (token) { - if (token.type === "table") - return "table table-bordered table-hover table-striped table-text-center"; - return null; + getElementClass: function(token) { + switch (token.type) { + case 'table': + return 'table table-bordered table-text-center table-vertical-middle'; + default: + return null; + } } }; diff --git a/js/soj.js b/js/soj.js index a0af683..0e2bd66 100644 --- a/js/soj.js +++ b/js/soj.js @@ -183,7 +183,7 @@ function getColOfScore(score) { } else if (score == 100) { return ColorConverter.toStr(ColorConverter.toRGB(new HSV(120, 100, 80))); } else { - return ColorConverter.toStr(ColorConverter.toRGB(new HSV(30 + score * 0.6, 100, 90))); + return ColorConverter.toStr(ColorConverter.toRGB(new HSV(30 + score * 0.6, 100, 90 - Math.max(Math.min(score - 30, 90 - score), 0) * 0.5))); } } @@ -284,6 +284,16 @@ function showErrorHelp(name, err) { } } +function showWarningHelp(name, err) { + if (err) { + $('#div-' + name).addClass('has-warning'); + $('#help-' + name).text(err); + } else { + $('#div-' + name).removeClass('has-warning'); + $('#help-' + name).text(''); + } +} + function getFormErrorAndShowHelp(name, val) { var err = val($('#input-' + name).val()); return showErrorHelp(name, err); @@ -385,6 +395,12 @@ $.fn.uoj_blog_tag = function() { }); } +$.fn.uoj_collection_tag = function() { + return this.each(function() { + $(this).attr('href', '/collection?type=' + encodeURIComponent($(this).text())); + }); +} + // click zan function click_zan(zan_id, zan_type, zan_delta, node) { var loading_node = $('
loading...
'); @@ -451,8 +467,21 @@ function getCountdownStr(t) { var mm = toFilledStr(x % 60, '0', 2); x = Math.floor(x / 60); var hh = x.toString(); + if (x >= 24) { + hh = toFilledStr(x % 24, '0', 2); + } + x = Math.floor(x / 24); + var dd = x.toString(); var res = ''; + if (x > 0) { + res += '' + dd + ''; + res += ' day'; + if (dd > 1) { + res += 's'; + } + res += ' + '; + } res += '' + hh + ''; res += ':'; res += '' + mm + ''; @@ -539,6 +568,7 @@ $.fn.uoj_highlight = function() { }); $(this).find(".uoj-problem-tag").uoj_problem_tag(); $(this).find(".uoj-blog-tag").uoj_blog_tag(); + $(this).find(".uoj-collection-tag").uoj_collection_tag(); $(this).find(".uoj-click-zan-block").click_zan_block(); $(this).find(".countdown").countdown(); $(this).find(".uoj-readmore").readmore({ From 1e63827f97a6587a47f25b297cdf49cc04905391 Mon Sep 17 00:00:00 2001 From: liuhengxi2007 <2019891597@qq.com> Date: Mon, 7 Jul 2025 19:09:16 +0800 Subject: [PATCH 02/20] =?UTF-8?q?=E6=97=A0=E5=8F=AF=E5=A5=89=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 --- 1 file changed, 3 deletions(-) diff --git a/.gitignore b/.gitignore index 431a4d7..bf6ea2d 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,3 @@ uploads/* utility/ pictures/ js/mathjax/ -app/locale/basic/zh-en.php -app/locale/contests/zh-en.php -app/locale/problems/zh-en.php From 1554e9504b1242762858ce3afc4c3a76437d64cb Mon Sep 17 00:00:00 2001 From: liuhengxi2007 <2019891597@qq.com> Date: Sun, 7 Sep 2025 19:51:25 +0800 Subject: [PATCH 03/20] collection propose --- app/controllers/collection.php | 2 +- app/controllers/collection_manage.php | 26 +++---- app/controllers/collection_propose.php | 90 +++++++++++++++++++++++++ app/controllers/hack_list.php | 2 +- app/controllers/problem_data_manage.php | 4 +- app/controllers/problem_statistics.php | 14 ++-- app/controllers/submissions_list.php | 6 +- app/uoj-html-lib.php | 10 +++ app/uoj-validate-lib.php | 8 +++ js/soj.js | 2 +- 10 files changed, 137 insertions(+), 27 deletions(-) diff --git a/app/controllers/collection.php b/app/controllers/collection.php index 1659273..774f327 100644 --- a/app/controllers/collection.php +++ b/app/controllers/collection.php @@ -117,7 +117,7 @@ function changeProblemState(state, id) {
- + diff --git a/app/controllers/collection_manage.php b/app/controllers/collection_manage.php index 7dc0ebf..2a2de33 100644 --- a/app/controllers/collection_manage.php +++ b/app/controllers/collection_manage.php @@ -8,7 +8,7 @@ redirectToLogin(); } - if(!isProblemCreator(Auth::user()) && !isBlogAllowedUser(Auth::user())) { + if(!isProblemCreator(Auth::user())) { become403Page(); } @@ -53,37 +53,39 @@ function echoProblem($problem) { $div_classes = array('table-responsive'); $table_classes = array('table', 'table-bordered', 'table-hover', 'table-striped'); + $proposal = DB::selectFirst("select * from collection_proposal"); + $append_form = new UOJForm('append'); - $append_form->addInput('id', 'text', 'id', '', + $append_form->addInput('id', 'text', 'id', $proposal['id'], function($result) { if(!preg_match('/^-?[a-z0-9]{1,12}$/', $result)) return '题号长度不能超过 12,且只能包含小写字母和数字'; return ''; }, null ); - $append_form->addInput('title', 'text', 'title', '', + $append_form->addInput('title', 'text', 'title', $proposal['title'], function($result) { if(strlen($result) > 100) return '长度不能超过 100 byte'; return ''; }, null ); - $append_form->addInput('url', 'text', 'url', '', + $append_form->addInput('url', 'text', 'url', $proposal['url'], function($result) { if(strlen($result) > 100) return '长度不能超过 100 byte'; return ''; }, null ); - $append_form->addInput('tag', 'text', 'tag', '', + $append_form->addInput('tag', 'text', 'tag', $proposal['tag'], function($result) { if(strlen($result) > 100) return '长度不能超过 100 byte'; return ''; }, null ); - $append_form->addInput('note', 'text', 'note', '', + $append_form->addInput('note', 'text', 'note', $proposal['note'], function($result) { if(strlen($result) > 100) return '长度不能超过 100 byte'; return ''; @@ -92,15 +94,14 @@ function($result) { ); $append_form->handle = function() { - $delete_permission = isProblemCreator(Auth::user()); - $id = $_POST['id']; if ($id[0] == '-') { $id = substr($id, 1); - if($delete_permission) DB::delete("delete from collection where id='$id'"); - if($delete_permission) DB::delete("delete from collection_tags where collection_id='$id'"); + DB::delete("delete from collection where id='$id'"); + DB::delete("delete from collection_tags where collection_id='$id'"); return; } + DB::delete("delete from collection_proposal where id='$id'"); $title = DB::escape($_POST['title']); $url = DB::escape($_POST['url']); $tag = DB::escape(strtolower($_POST['tag'])); @@ -117,7 +118,7 @@ function($result) { DB::insert("insert into collection (id, title, url, note) values ('$id', '$title', '$url', '$note') on duplicate key update" . substr($upd_cmd, 1)); } if($tag == '-') { - if($delete_permission) DB::delete("delete from collection_tags where collection_id='$id'"); + DB::delete("delete from collection_tags where collection_id='$id'"); } else if($tag != '' && DB::selectCount("select count(*) from collection where id = '$id'")) { DB::insert("insert ignore into collection_tags (collection_id, tag) values ('$id', '$tag')"); @@ -126,6 +127,7 @@ function($result) { $append_form->runAtServer(); ?> + printHTML(); ?>

约定

选择一个题目所属的 OJ 时,按照以下顺序选择: @@ -147,7 +149,7 @@ function($result) { LOJloj1 SOJsoj1 Luogulg1001 - Codeforces Gymcf111111a + Codeforces Gymcf123456a
  • diff --git a/app/controllers/collection_propose.php b/app/controllers/collection_propose.php index ef34a81..ab08cf0 100644 --- a/app/controllers/collection_propose.php +++ b/app/controllers/collection_propose.php @@ -11,6 +11,96 @@ if(!isBlogAllowedUser(Auth::user())) { become403Page(); } + + function echoProblem($problem) { + if (isBlogAllowedUser(Auth::user())) { + echo ''; + echo ''; + echo $problem['id'], ''; + echo ''; + echo '', $problem['title'], ''; + foreach (queryCollectionTags($problem['id']) as $tag) { + echo '', '', HTML::escape($tag), '', ''; + } + echo ''; + echo ''; + echo $problem['note']; + echo ''; + echo ''; + } + } + + $cur_tab = isset($_GET['type']) ? $_GET['type'] : 'null'; + + $cond = "1"; + + $header = ''; + $header .= 'ID'; + $header .= '' . UOJLocale::get('problems::problem') . ''; + $header .= '' . UOJLocale::get('note') . ''; + $header .= ''; + + $pag_config = array('page_len' => 10000); + $pag_config['col_names'] = array('*'); + $username = Auth::id(); + $pag_config['table_name'] = "collection"; + $pag_config['cond'] = $cond; + $pag_config['tail'] = 'order by id asc'; + $pag_config['max_extend'] = 5; + $pag_config['timeout'] = 1000; + $pag = new Paginator($pag_config); + + $div_classes = array('table-responsive'); + $table_classes = array('table', 'table-bordered', 'table-hover', 'table-striped'); + + $append_form = new UOJForm('append'); + + $append_form->addInput('id', 'text', 'id', '', + function($result) { + if(!preg_match('/^[a-z0-9]{1,12}$/', $result)) return '题号长度不能超过 12,且只能包含小写字母和数字'; + return ''; + }, + null + ); + $append_form->addInput('title', 'text', 'title', '', + function($result) { + if(strlen($result) > 100) return '长度不能超过 100 byte'; + return ''; + }, + null + ); + $append_form->addInput('url', 'text', 'url', '', + function($result) { + if(strlen($result) > 100) return '长度不能超过 100 byte'; + return ''; + }, + null + ); + $append_form->addInput('tag', 'text', 'tag', '', + function($result) { + if(strlen($result) > 100) return '长度不能超过 100 byte'; + return ''; + }, + null + ); + $append_form->addInput('note', 'text', 'note', '', + function($result) { + if(strlen($result) > 100) return '长度不能超过 100 byte'; + return ''; + }, + null + ); + + $append_form->handle = function() { + $id = $_POST['id']; + $title = DB::escape($_POST['title']); + $url = DB::escape($_POST['url']); + $tag = DB::escape(strtolower($_POST['tag'])); + $note = DB::escape($_POST['note']); + DB::insert("insert into collection_proposal (id, title, url, tag, note) values ('$id', '$title', '$url', '$tag', '$note')"); + }; + $append_form->runAtServer(); ?> +printHTML(); ?> diff --git a/app/controllers/hack_list.php b/app/controllers/hack_list.php index 77797d7..3832fc9 100644 --- a/app/controllers/hack_list.php +++ b/app/controllers/hack_list.php @@ -12,7 +12,7 @@ $q_hacker = $search_form->addText('hacker', UOJLocale::get('problems::hacker').':', $common.'maxlength="20" style="width:8em"', 'validateUsername'); $q_owner = $search_form->addText('owner', UOJLocale::get('problems::owner').':', $common.'maxlength="20" style="width:8em"', 'validateUsername'); $q_status = $search_form->addSelect('status', UOJLocale::get('problems::result').':', $common.'style="width:7em"', [''=>'All', 1=>'Success!', 2=>'Failed.']); - $q_cut_off_time = $search_form->addText('cut_off_time', UOJLocale::get('cut off time').':', $common . 'maxlength="11" style="width:8em; font-family: monospace" placeholder="YYYY-MM-DD"', validateTime); + $q_cut_off_time = $search_form->addText('cut_off_time', UOJLocale::get('cut off time').':', $common . 'maxlength="11" style="width:8em; font-family: monospace" placeholder="YYYY-MM-DD"', "validateTime"); $search_form->addSubmit(UOJLocale::get('search')); $conds = array(); diff --git a/app/controllers/problem_data_manage.php b/app/controllers/problem_data_manage.php index 682fbd4..52483f0 100644 --- a/app/controllers/problem_data_manage.php +++ b/app/controllers/problem_data_manage.php @@ -313,12 +313,12 @@ function getDataDisplayer() { return function() use($name, $allow_files) { $src_name = $name . '.cpp'; if (isset($allow_files[$src_name])) { - echoFilePre($src_name); + echoFilePre($src_name, 100 * 1024); } else { echoFileNotFound($src_name); } if (isset($allow_files[$name])) { - echoFilePre($name); + echoFilePre($name, 100 * 1024); } else { echoFileNotFound($name); } diff --git a/app/controllers/problem_statistics.php b/app/controllers/problem_statistics.php index 0d078b7..ad88c05 100644 --- a/app/controllers/problem_statistics.php +++ b/app/controllers/problem_statistics.php @@ -31,24 +31,24 @@ function scoreDistributionData() { } else if ($row[0] == 100) { $has_score_100 = true; } - $score = $row[0] * 100; + $score = round($row[0] * 1000000); $data[] = array('score' => $score, 'count' => $row[1]); } if (!$has_score_0) { array_unshift($data, array('score' => 0, 'count' => 0)); } if (!$has_score_100) { - $data[] = array('score' => 10000, 'count' => 0); + $data[] = array('score' => 100 * 1000000, 'count' => 0); } return $data; } $data = scoreDistributionData(); - $pre_data = $data; - $suf_data = $data; for ($i = 0; $i < count($data); $i++) { - $data[$i]['score'] /= 100; + $data[$i]['score'] /= 1000000; } + $pre_data = $data; + $suf_data = $data; for ($i = 1; $i < count($data); $i++) { $pre_data[$i]['count'] += $pre_data[$i - 1]['count']; } @@ -134,7 +134,7 @@ function scoreDistributionData() { return (x.getTime() / 100).toString(); }, hoverCallback: function(index, options, content, row) { - var scr = row.score / 100; + var scr = row.score; return '
    ' + 'score: ≤' + scr + '
    ' + ''; }, @@ -161,7 +161,7 @@ function scoreDistributionData() { return (x.getTime() / 100).toString(); }, hoverCallback: function(index, options, content, row) { - var scr = row.score / 100; + var scr = row.score; return '
    ' + 'score: ≥' + scr + '
    ' + ''; }, diff --git a/app/controllers/submissions_list.php b/app/controllers/submissions_list.php index 224d8c6..900480d 100644 --- a/app/controllers/submissions_list.php +++ b/app/controllers/submissions_list.php @@ -9,10 +9,10 @@ $common = 'class="form-control input-sm" '; $q_problem_id = $search_form->addText('problem_id', UOJLocale::get('problems::problem id').':', $common . 'maxlength="4" style="width:4em"', 'validateUInt'); $q_submitter = $search_form->addText('submitter', UOJLocale::get('username').':', $common . 'maxlength="20" style="width:10em"', 'validateUsername'); - $q_min_score = $search_form->addText('min_score', UOJLocale::get('score range').':', $common . 'maxlength="5" style="width:4em" placeholder="-∞"', 'validateInt'); - $q_max_score = $search_form->addText('max_score', '~', $common . 'maxlength="5" style="width:4em" placeholder="+∞"', 'validateInt'); + $q_min_score = $search_form->addText('min_score', UOJLocale::get('score range').':', $common . 'maxlength="5" style="width:4em" placeholder="-∞"', 'validateReal'); + $q_max_score = $search_form->addText('max_score', '~', $common . 'maxlength="5" style="width:4em" placeholder="+∞"', 'validateReal'); $q_language = $search_form->addText('language', UOJLocale::get('problems::language').':', $common . 'maxlength="10" style="width:8em"', validateLength(10)); - $q_cut_off_time = $search_form->addText('cut_off_time', UOJLocale::get('cut off time').':', $common . 'maxlength="20" style="width:14em; font-family: monospace" placeholder="YYYY-MM-DD[ hh:mm:ss]"', validateTime); + $q_cut_off_time = $search_form->addText('cut_off_time', UOJLocale::get('cut off time').':', $common . 'maxlength="20" style="width:14em; font-family: monospace" placeholder="YYYY-MM-DD[ hh:mm:ss]"', "validateTime"); $search_form->addSubmit(UOJLocale::get('search')); $conds = array(); diff --git a/app/uoj-html-lib.php b/app/uoj-html-lib.php index 67236df..e4043f6 100644 --- a/app/uoj-html-lib.php +++ b/app/uoj-html-lib.php @@ -168,6 +168,8 @@ function getSubmissionJudgedStatusStr($result_error, $score, $uri = null) { $html_type = 'span'; $head = $html_type; } + $score = rtrim($score, '0'); + $score = rtrim($score, '.'); if (isset($result_error)) return '<' . $head . " class=\"small\">$result_error'; else @@ -1266,6 +1268,14 @@ private function _print($node) { $this->_print_c($node); echo ' '; + } elseif ($node->nodeName == 'err') { + if(count($node->childNodes)) { + echo '

    error (stderr):

    +';
    +				$this->_print_c($node);
    +				echo '
    +
    '; + } } else { echo '<', $node->nodeName; foreach ($node->attributes as $attr) { diff --git a/app/uoj-validate-lib.php b/app/uoj-validate-lib.php index 17eadb5..4cbff04 100644 --- a/app/uoj-validate-lib.php +++ b/app/uoj-validate-lib.php @@ -32,6 +32,14 @@ function validateInt($x) { return is_string($x) and validateUInt($x[0] === '-' ? substr($x, 1) : $x); } +function validateUReal($x) { + return is_string($x) and preg_match('/^(0|[1-9][0-9]{0,8})(\.[0-9]{0,6})?$/', $x); +} + +function validateReal($x) { + return is_string($x) and validateUReal($x[0] === '-' ? substr($x, 1) : $x); +} + function validateUploadedFile($name) { return isset($_FILES[$name]) && is_uploaded_file($_FILES[$name]['tmp_name']); } diff --git a/js/soj.js b/js/soj.js index 0e2bd66..925a7ef 100644 --- a/js/soj.js +++ b/js/soj.js @@ -548,7 +548,7 @@ $.fn.uoj_highlight = function() { $(this).find("span.uoj-groupname").each(replaceWithHighlightUsername); $(this).find(".uoj-honor").uoj_honor(); $(this).find(".uoj-score").each(function() { - var score_str = $(this).text(), score = parseInt(score_str), maxscore = parseInt($(this).data('max')); + var score_str = $(this).text(), score = parseFloat(score_str), maxscore = parseFloat($(this).data('max')); if (score_str === '?') $(this).css("color", "rgb(0, 204, 204)"); else if (score_str.startsWith('+')) $(this).css("color", getColOfScore(100)); else if (score_str.startsWith('-')) $(this).css("color", getColOfScore(0)); From 201c297d9500dcd5d062df43d52bcbb5ed6a8fc3 Mon Sep 17 00:00:00 2001 From: liuhengxi <2019891597@qq.com> Date: Mon, 8 Sep 2025 15:17:43 +0800 Subject: [PATCH 04/20] user collection --- app/controllers/collection_user.php | 135 +++++++++++++++ app/controllers/collection_user_manage.php | 185 +++++++++++++++++++++ app/route.php | 3 + 3 files changed, 323 insertions(+) create mode 100644 app/controllers/collection_user.php create mode 100644 app/controllers/collection_user_manage.php diff --git a/app/controllers/collection_user.php b/app/controllers/collection_user.php new file mode 100644 index 0000000..d37a86e --- /dev/null +++ b/app/controllers/collection_user.php @@ -0,0 +1,135 @@ += 0 && $state <= 2)) die('invalid state'); + $username = Auth::id(); + DB::insert("insert into collection_state (id, username, state) values ('$id', '$username', $state) on duplicate key update state = $state"); + die('ok'); + } + + function echoProblem($problem) { + $username = Auth::id(); + if (isBlogAllowedUser(Auth::user())) { + $state = 0; + $stateResult = DB::selectFirst("select state from collection_state where id = '{$problem['id']}' and username = '$username'"); + if(isset($stateResult)) $state = $stateResult['state']; + $color = ['fff','feb','dfb'][$state]; + echo ''; + echo ''; + echo $problem['id'], ''; + echo ''; + echo '
    '; + echo ''; + echo '', $problem['title'], ''; + echo ''; + echo ''; + echo ' '; + echo ' '; + echo ''; + echo ''; + echo '
    '; + echo ''; + echo ''; + echo $problem['note']; + echo ''; + echo ''; + } + } + + $cur_tab = isset($_GET['type']) ? $_GET['type'] : 'null'; + + $cond = "exists (select 1 from collection_user_tags where collection_user_tags.collection_id = collection_user.id and collection_user_tags.tag = '{$cur_tab}') and owner='{$owner}'"; + + $header = ''; + $header .= 'ID'; + $header .= '' . UOJLocale::get('problems::problem') . ''; + $header .= '' . UOJLocale::get('note') . ''; + $header .= ''; + + $tabs_info = array(); + + $pag_config = array('page_len' => 1000); + $pag_config['col_names'] = array('*'); + $username = Auth::id(); + $pag_config['table_name'] = "collection_user"; + $pag_config['cond'] = $cond; + $pag_config['tail'] = 'order by id asc'; + $pag_config['max_extend'] = 5; + $pag_config['timeout'] = 1000; + $pag = new Paginator($pag_config); + + $div_classes = array('table-responsive'); + $table_classes = array('table', 'table-bordered', 'table-hover', 'table-striped'); +?> + + +
    +
    + +
    + +
    + +
    + +
    +
    +
    +
    +
    + pagination(); ?> +
    +
    +
    +'; + echo ''; + echo ''; + echo $header; + echo ''; + echo ''; + + foreach ($pag->get() as $idx => $row) { + echoProblem($row); + } + + echo ''; + echo '
    '; + echo '
'; + + echo $pag->pagination(); +?> + diff --git a/app/controllers/collection_user_manage.php b/app/controllers/collection_user_manage.php new file mode 100644 index 0000000..3a36d2b --- /dev/null +++ b/app/controllers/collection_user_manage.php @@ -0,0 +1,185 @@ +'; + echo ''; + echo $problem['id'], ''; + echo ''; + echo '', $problem['title'], ''; + foreach (queryCollectionTags($problem['id']) as $tag) { + echo '', '', HTML::escape($tag), '', ''; + } + echo ''; + echo ''; + echo $problem['note']; + echo ''; + echo ''; + } + } + + $cur_tab = isset($_GET['type']) ? $_GET['type'] : 'null'; + + $cond = "1"; + + $header = ''; + $header .= 'ID'; + $header .= '' . UOJLocale::get('problems::problem') . ''; + $header .= '' . UOJLocale::get('note') . ''; + $header .= ''; + + $pag_config = array('page_len' => 10000); + $pag_config['col_names'] = array('*'); + $username = Auth::id(); + $pag_config['table_name'] = "collection"; + $pag_config['cond'] = $cond; + $pag_config['tail'] = 'order by id asc'; + $pag_config['max_extend'] = 5; + $pag_config['timeout'] = 1000; + $pag = new Paginator($pag_config); + + $div_classes = array('table-responsive'); + $table_classes = array('table', 'table-bordered', 'table-hover', 'table-striped'); + + if($owner == Auth::id()) { + $append_form = new UOJForm('append'); + + $append_form->addInput('id', 'text', 'id', '', + function($result) { + if(!preg_match('/^-?[a-z0-9]{1,12}$/', $result)) return '题号长度不能超过 12,且只能包含小写字母和数字'; + return ''; + }, + null + ); + $append_form->addInput('title', 'text', 'title', '', + function($result) { + if(strlen($result) > 100) return '长度不能超过 100 byte'; + return ''; + }, + null + ); + $append_form->addInput('url', 'text', 'url', '', + function($result) { + if(strlen($result) > 100) return '长度不能超过 100 byte'; + return ''; + }, + null + ); + $append_form->addInput('tag', 'text', 'tag', '', + function($result) { + if(strlen($result) > 100) return '长度不能超过 100 byte'; + return ''; + }, + null + ); + $append_form->addInput('note', 'text', 'note', '', + function($result) { + if(strlen($result) > 100) return '长度不能超过 100 byte'; + return ''; + }, + null + ); + + $append_form->handle = function() { + $id = $_POST['id']; + if ($id[0] == '-') { + $id = substr($id, 1); + DB::delete("delete from collection_user where id='$id' and owner = '$owner'"); + DB::delete("delete from collection_user_tags where collection_id='$id' and owner = '$owner'"); + return; + } + $title = DB::escape($_POST['title']); + $url = DB::escape($_POST['url']); + $tag = DB::escape(strtolower($_POST['tag'])); + $note = DB::escape($_POST['note']); + + $upd_cmd = ''; + if($title != '') $upd_cmd .= ", title='$title'"; + if($url != '') $upd_cmd .= ", url='$url'"; + if($note != '') { + if($note == '-') $note = ''; + $upd_cmd .= ", note='$note'"; + } + if($upd_cmd != '') { + DB::insert("insert into collection_user (owner, id, title, url, note) values ('$owner', '$id', '$title', '$url', '$note') on duplicate key update" . substr($upd_cmd, 1)); + } + if($tag == '-') { + DB::delete("delete from collection_user_tags where collection_id='$id' and owner = '$owner'"); + } + else if($tag != '' && DB::selectCount("select count(*) from collection_user_tags where id = '$id' and owner = '$owner'")) { + DB::insert("insert ignore into collection_user_tags (owner, collection_id, tag) values ('$owner', '$id', '$tag')"); + } + }; + $append_form->runAtServer(); + } +?> + +printHTML(); ?> +

约定

+选择一个题目所属的 OJ 时,按照以下顺序选择: +
    +
  1. 原出处,且有中文题面或英文题面
  2. +
  3. 有中文题面
  4. +
  5. 有英文题面
  6. +
  7. 原出处,但无中文题面或英文题面
  8. +
  9. 其他
  10. +
+有多个同一类的 OJ,按照以下优先级选择。 +

各 OJ 题号格式(按照优先级排序)

+ + + + + + + + + + +
OJ题号格式
AtCoderagc001a
Codeforces Problemsetcf1a
UOJuoj1
QOJqoj1
LOJloj1
SOJsoj1
Luogulg1001
Codeforces Gymcf123456a
+
    +
  • + 对于 Codeforces 上的题目,使用题库风格 url。(https://codeforces.com/problemset/problem/1/A) +
  • +
  • + 尽可能去除 url 中的比赛编号。(https://qoj.ac/contest/259/problem/2) +
  • +
+

列表

+'; + echo ''; + echo ''; + echo $header; + echo ''; + echo ''; + + foreach ($pag->get() as $idx => $row) { + echoProblem($row); + } + + echo ''; + echo '
'; + echo '
'; + + echo $pag->pagination(); +?> + diff --git a/app/route.php b/app/route.php index 102508a..39699f3 100644 --- a/app/route.php +++ b/app/route.php @@ -27,6 +27,9 @@ Route::any('/collection/propose', '/collection_propose.php'); Route::any('/collection/manage', '/collection_manage.php'); + Route::any('/collection/{username}', '/collection_user.php'); + Route::any('/collection/{username}/manage', '/collection_user_manage.php'); + Route::any('/contests', '/contests.php'); Route::any('/contest/new', '/add_contest.php'); Route::any('/contest/{id}', '/contest_inside.php'); From 9bf399ff5a5eba1dfed05829b9a31e9cf14d5735 Mon Sep 17 00:00:00 2001 From: liuhengxi <2019891597@qq.com> Date: Mon, 8 Sep 2025 19:38:45 +0800 Subject: [PATCH 05/20] 2 --- app/controllers/collection_user_manage.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/collection_user_manage.php b/app/controllers/collection_user_manage.php index 3a36d2b..b43d286 100644 --- a/app/controllers/collection_user_manage.php +++ b/app/controllers/collection_user_manage.php @@ -38,7 +38,7 @@ function echoProblem($problem) { $cur_tab = isset($_GET['type']) ? $_GET['type'] : 'null'; - $cond = "1"; + $cond = "owner = '$owner'"; $header = ''; $header .= 'ID'; @@ -49,7 +49,7 @@ function echoProblem($problem) { $pag_config = array('page_len' => 10000); $pag_config['col_names'] = array('*'); $username = Auth::id(); - $pag_config['table_name'] = "collection"; + $pag_config['table_name'] = "collection_user"; $pag_config['cond'] = $cond; $pag_config['tail'] = 'order by id asc'; $pag_config['max_extend'] = 5; From 8cc77c932475a1908e18fd2a5f354feac5f45952 Mon Sep 17 00:00:00 2001 From: liuhengxi <2019891597@qq.com> Date: Mon, 8 Sep 2025 20:12:00 +0800 Subject: [PATCH 06/20] 3 --- app/controllers/collection_user_manage.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/controllers/collection_user_manage.php b/app/controllers/collection_user_manage.php index b43d286..7743a06 100644 --- a/app/controllers/collection_user_manage.php +++ b/app/controllers/collection_user_manage.php @@ -99,6 +99,7 @@ function($result) { ); $append_form->handle = function() { + global $owner; $id = $_POST['id']; if ($id[0] == '-') { $id = substr($id, 1); From 3b0fd7e2ac8df146d417a9804983eeaf3776a5c0 Mon Sep 17 00:00:00 2001 From: liuhengxi <2019891597@qq.com> Date: Mon, 8 Sep 2025 20:21:15 +0800 Subject: [PATCH 07/20] 4 --- app/controllers/collection_user_manage.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/collection_user_manage.php b/app/controllers/collection_user_manage.php index 7743a06..3caff1f 100644 --- a/app/controllers/collection_user_manage.php +++ b/app/controllers/collection_user_manage.php @@ -125,7 +125,7 @@ function($result) { if($tag == '-') { DB::delete("delete from collection_user_tags where collection_id='$id' and owner = '$owner'"); } - else if($tag != '' && DB::selectCount("select count(*) from collection_user_tags where id = '$id' and owner = '$owner'")) { + else if($tag != '' && DB::selectCount("select count(*) from collection_user where id = '$id' and owner = '$owner'")) { DB::insert("insert ignore into collection_user_tags (owner, collection_id, tag) values ('$owner', '$id', '$tag')"); } }; From 1d044ea045d777347ccddb92cd895d025aba55ce Mon Sep 17 00:00:00 2001 From: liuhengxi <2019891597@qq.com> Date: Mon, 8 Sep 2025 20:32:20 +0800 Subject: [PATCH 08/20] 5 --- app/controllers/collection_user_manage.php | 3 ++- app/uoj-query-lib.php | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/app/controllers/collection_user_manage.php b/app/controllers/collection_user_manage.php index 3caff1f..d1a6148 100644 --- a/app/controllers/collection_user_manage.php +++ b/app/controllers/collection_user_manage.php @@ -19,13 +19,14 @@ } function echoProblem($problem) { + global $owner; if (isBlogAllowedUser(Auth::user())) { echo ''; echo ''; echo $problem['id'], ''; echo ''; echo '', $problem['title'], ''; - foreach (queryCollectionTags($problem['id']) as $tag) { + foreach (queryUserCollectionTags($owner, $problem['id']) as $tag) { echo '', '', HTML::escape($tag), '', ''; } echo ''; diff --git a/app/uoj-query-lib.php b/app/uoj-query-lib.php index fd982de..56293b3 100644 --- a/app/uoj-query-lib.php +++ b/app/uoj-query-lib.php @@ -100,6 +100,12 @@ function queryCollectionTags($id) { return $tags; } +function queryUserCollectionTags($owner, $id) { + $result = DB::select("select tag from collection_tags where collection_id = '$id' and owner='$owner' order by id"); + for ($tags = array(); $row = DB::fetch($result, MYSQLI_NUM); $tags[] = $row[0]); + return $tags; +} + function queryContestProblemRank($contest, $problem) { if (!DB::selectFirst("select * from contests_problems where contest_id = {$contest['id']} and problem_id = {$problem['id']}")) { return null; From fffcc5626d7cfbee42b96e5ae598fd9de15336f6 Mon Sep 17 00:00:00 2001 From: liuhengxi <2019891597@qq.com> Date: Tue, 9 Sep 2025 15:37:19 +0800 Subject: [PATCH 09/20] 6 --- app/controllers/collection_user.php | 2 +- app/route.php | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/app/controllers/collection_user.php b/app/controllers/collection_user.php index d37a86e..be3ea41 100644 --- a/app/controllers/collection_user.php +++ b/app/controllers/collection_user.php @@ -84,7 +84,7 @@ function echoProblem($problem) { function changeProblemState(state, id) { title = document.getElementById('problem-' + id).children[1].children[0].children[0].textContent; if(!confirm(id + ' - ' + title + '\n' + ['Unsolved', 'Uncertain', 'Solved'][state] + '?')) return; - $.post('/collection_user', { + $.post('/collection_user/', { 'state': state, 'id': id, }, function(msg) { diff --git a/app/route.php b/app/route.php index 39699f3..da74051 100644 --- a/app/route.php +++ b/app/route.php @@ -26,9 +26,8 @@ Route::any('/collection', '/collection.php'); Route::any('/collection/propose', '/collection_propose.php'); Route::any('/collection/manage', '/collection_manage.php'); - - Route::any('/collection/{username}', '/collection_user.php'); - Route::any('/collection/{username}/manage', '/collection_user_manage.php'); +// Route::any('/collection/{username}', '/collection_user.php'); +// Route::any('/collection/{username}/manage', '/collection_user_manage.php'); Route::any('/contests', '/contests.php'); Route::any('/contest/new', '/add_contest.php'); From 7ed358f2cc9e48d83e2d818789a2bf8e355ab2c0 Mon Sep 17 00:00:00 2001 From: liuhengxi <2019891597@qq.com> Date: Sun, 14 Sep 2025 18:00:20 +0800 Subject: [PATCH 10/20] 7 --- app/route.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/route.php b/app/route.php index da74051..f631a39 100644 --- a/app/route.php +++ b/app/route.php @@ -26,8 +26,8 @@ Route::any('/collection', '/collection.php'); Route::any('/collection/propose', '/collection_propose.php'); Route::any('/collection/manage', '/collection_manage.php'); -// Route::any('/collection/{username}', '/collection_user.php'); -// Route::any('/collection/{username}/manage', '/collection_user_manage.php'); + Route::any('/collection/{username}', '/collection_user.php'); + Route::any('/collection/{username}/manage', '/collection_user_manage.php'); Route::any('/contests', '/contests.php'); Route::any('/contest/new', '/add_contest.php'); From 8cdd6a1d740c74259c37b9bb8fbdcf4357ad5339 Mon Sep 17 00:00:00 2001 From: liuhengxi <2019891597@qq.com> Date: Sun, 14 Sep 2025 18:03:19 +0800 Subject: [PATCH 11/20] 8 --- app/controllers/collection_user.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/collection_user.php b/app/controllers/collection_user.php index be3ea41..5031770 100644 --- a/app/controllers/collection_user.php +++ b/app/controllers/collection_user.php @@ -84,7 +84,7 @@ function echoProblem($problem) { function changeProblemState(state, id) { title = document.getElementById('problem-' + id).children[1].children[0].children[0].textContent; if(!confirm(id + ' - ' + title + '\n' + ['Unsolved', 'Uncertain', 'Solved'][state] + '?')) return; - $.post('/collection_user/', { + $.post('/collection/', { 'state': state, 'id': id, }, function(msg) { From ab17f7f3ade64780518192ec25b32a23349b4ce1 Mon Sep 17 00:00:00 2001 From: liuhengxi <2019891597@qq.com> Date: Sun, 14 Sep 2025 18:06:03 +0800 Subject: [PATCH 12/20] 9 --- app/controllers/.collection_user.php.swp | Bin 0 -> 20480 bytes app/controllers/collection_user.php | 1 + 2 files changed, 1 insertion(+) create mode 100644 app/controllers/.collection_user.php.swp diff --git a/app/controllers/.collection_user.php.swp b/app/controllers/.collection_user.php.swp new file mode 100644 index 0000000000000000000000000000000000000000..52441ca2b30f6098612bb11e27579d25c9093ede GIT binary patch literal 20480 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 C#M=1) literal 0 HcmV?d00001 diff --git a/app/controllers/collection_user.php b/app/controllers/collection_user.php index 5031770..a4e58e4 100644 --- a/app/controllers/collection_user.php +++ b/app/controllers/collection_user.php @@ -21,6 +21,7 @@ function isValidProblemId($id) { if(!isValidProblemId($id)) die('invalid id'); if(!($state >= 0 && $state <= 2)) die('invalid state'); $username = Auth::id(); + die("insert into collection_state (id, username, state) values ('$id', '$username', $state) on duplicate key update state = $state"); DB::insert("insert into collection_state (id, username, state) values ('$id', '$username', $state) on duplicate key update state = $state"); die('ok'); } From 0c4808841c4b6b9e266fa9e3e1144342875e3110 Mon Sep 17 00:00:00 2001 From: liuhengxi <2019891597@qq.com> Date: Sun, 14 Sep 2025 18:08:25 +0800 Subject: [PATCH 13/20] 10 --- app/controllers/.collection_user.php.swp | Bin 20480 -> 0 bytes app/controllers/collection.php | 1 + 2 files changed, 1 insertion(+) delete mode 100644 app/controllers/.collection_user.php.swp diff --git a/app/controllers/.collection_user.php.swp b/app/controllers/.collection_user.php.swp deleted file mode 100644 index 52441ca2b30f6098612bb11e27579d25c9093ede..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20480 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 C#M=1) diff --git a/app/controllers/collection.php b/app/controllers/collection.php index 774f327..7836b56 100644 --- a/app/controllers/collection.php +++ b/app/controllers/collection.php @@ -20,6 +20,7 @@ function isValidProblemId($id) { if(!DB::selectCount("select count(*) from collection where id = '$id'")) die('no such problem'); if(!($state >= 0 && $state <= 2)) die('invalid state'); $username = Auth::id(); + die("insert into collection_state (id, username, state) values ('$id', '$username', $state) on duplicate key update state = $state"); DB::insert("insert into collection_state (id, username, state) values ('$id', '$username', $state) on duplicate key update state = $state"); die('ok'); } From 08853a1aa34defdf3570aa2cea62720ac989622e Mon Sep 17 00:00:00 2001 From: liuhengxi <2019891597@qq.com> Date: Sun, 14 Sep 2025 18:11:15 +0800 Subject: [PATCH 14/20] 11 --- app/controllers/collection.php | 1 - app/controllers/collection_user.php | 1 - 2 files changed, 2 deletions(-) diff --git a/app/controllers/collection.php b/app/controllers/collection.php index 7836b56..774f327 100644 --- a/app/controllers/collection.php +++ b/app/controllers/collection.php @@ -20,7 +20,6 @@ function isValidProblemId($id) { if(!DB::selectCount("select count(*) from collection where id = '$id'")) die('no such problem'); if(!($state >= 0 && $state <= 2)) die('invalid state'); $username = Auth::id(); - die("insert into collection_state (id, username, state) values ('$id', '$username', $state) on duplicate key update state = $state"); DB::insert("insert into collection_state (id, username, state) values ('$id', '$username', $state) on duplicate key update state = $state"); die('ok'); } diff --git a/app/controllers/collection_user.php b/app/controllers/collection_user.php index a4e58e4..5031770 100644 --- a/app/controllers/collection_user.php +++ b/app/controllers/collection_user.php @@ -21,7 +21,6 @@ function isValidProblemId($id) { if(!isValidProblemId($id)) die('invalid id'); if(!($state >= 0 && $state <= 2)) die('invalid state'); $username = Auth::id(); - die("insert into collection_state (id, username, state) values ('$id', '$username', $state) on duplicate key update state = $state"); DB::insert("insert into collection_state (id, username, state) values ('$id', '$username', $state) on duplicate key update state = $state"); die('ok'); } From 31955565cc38e4179402ff7bf63c22d3cef8e3dd Mon Sep 17 00:00:00 2001 From: liuhengxi <2019891597@qq.com> Date: Sun, 14 Sep 2025 18:16:55 +0800 Subject: [PATCH 15/20] 12 --- app/uoj-query-lib.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/uoj-query-lib.php b/app/uoj-query-lib.php index 56293b3..c5b9753 100644 --- a/app/uoj-query-lib.php +++ b/app/uoj-query-lib.php @@ -101,7 +101,7 @@ function queryCollectionTags($id) { } function queryUserCollectionTags($owner, $id) { - $result = DB::select("select tag from collection_tags where collection_id = '$id' and owner='$owner' order by id"); + $result = DB::select("select tag from collection_user_tags where collection_id = '$id' and owner='$owner' order by id"); for ($tags = array(); $row = DB::fetch($result, MYSQLI_NUM); $tags[] = $row[0]); return $tags; } From af28e2664f5276102ca44828d61c36f4573f4a54 Mon Sep 17 00:00:00 2001 From: liuhengxi <2019891597@qq.com> Date: Sun, 14 Sep 2025 18:46:49 +0800 Subject: [PATCH 16/20] 13 --- app/controllers/collection_manage.php | 1 + app/controllers/collection_user_manage.php | 1 + js/soj_collection_id2url.js | 90 ++++++++++++++++++++++ 3 files changed, 92 insertions(+) create mode 100644 js/soj_collection_id2url.js diff --git a/app/controllers/collection_manage.php b/app/controllers/collection_manage.php index 2a2de33..339b64e 100644 --- a/app/controllers/collection_manage.php +++ b/app/controllers/collection_manage.php @@ -178,4 +178,5 @@ function($result) { echo $pag->pagination(); ?> + diff --git a/app/controllers/collection_user_manage.php b/app/controllers/collection_user_manage.php index d1a6148..26044b3 100644 --- a/app/controllers/collection_user_manage.php +++ b/app/controllers/collection_user_manage.php @@ -184,4 +184,5 @@ function($result) { echo $pag->pagination(); ?> + diff --git a/js/soj_collection_id2url.js b/js/soj_collection_id2url.js new file mode 100644 index 0000000..7d07e5b --- /dev/null +++ b/js/soj_collection_id2url.js @@ -0,0 +1,90 @@ +document.getElementById('input-id').addEventListener('input', function(e) { + const input = e.target.value.trim(); + const outputElement = document.getElementById('input-url'); + + if (!input) { + outputElement.value = ''; + return; + } + + // 1. Codeforces 常规题目 (cf1a 或 1a) + if (input.startsWith('cf') || /^\d{1,5}[a-zA-Z]$/.test(input)) { + let problemId = input.startsWith('cf') ? input.substring(2) : input; + + const cfMatch = problemId.match(/^(\d{1,5})([a-zA-Z])$/); + if (cfMatch) { + const contestId = cfMatch[1]; + const problemLetter = cfMatch[2].toUpperCase(); // 注意:大写字母 + outputElement.value = `https://codeforces.com/problemset/problem/${contestId}/${problemLetter}`; + return; + } + } + + // 2. AtCoder 格式 (abc/arc/agc + 数字 + 字母) + const atcoderMatch = input.match(/^(a[bgc]r?c)(\d+)([a-zA-Z])$/i); + if (atcoderMatch) { + const contestType = atcoderMatch[1].toLowerCase(); + const contestNumber = atcoderMatch[2]; + const problemLetter = atcoderMatch[3].toLowerCase(); + const contestId = contestType + contestNumber; + + outputElement.value = `https://atcoder.jp/contests/${contestId}/tasks/${contestId}_${problemLetter}`; + return; + } + + // 3. UOJ 格式 (uoj78) + const uojMatch = input.match(/^uoj(\d+)$/i); + if (uojMatch) { + const problemId = uojMatch[1]; + outputElement.value = `https://uoj.ac/problem/${problemId}`; + return; + } + + // 4. QOJ 格式 (qoj789) + const qojMatch = input.match(/^qoj(\d+)$/i); + if (qojMatch) { + const problemId = qojMatch[1]; + outputElement.value = `https://qoj.ac/problem/${problemId}`; + return; + } + + // 5. LOJ 格式 (loj456) + const lojMatch = input.match(/^loj(\d+)$/i); + if (lojMatch) { + const problemId = lojMatch[1]; + outputElement.value = `https://loj.ac/p/${problemId}`; + return; + } + + // 6. SOJ 格式 (soj567) - 相对路径 + const sojMatch = input.match(/^soj(\d+)$/i); + if (sojMatch) { + const problemId = sojMatch[1]; + outputElement.value = `/problem/${problemId}`; + return; + } + + // 7. 洛谷格式 (lg1001 或 p1001) + const lgMatch = input.match(/^(?:lg|p)(\d+)$/i); + if (lgMatch) { + const problemId = lgMatch[1]; + outputElement.value = `https://www.luogu.com.cn/problem/P${problemId}`; + return; + } + + // 8. Codeforces Gym 题目 (cf123456a 或 123456a,6位以上数字) + if (input.startsWith('cf') || /^\d{6,}[a-zA-Z]$/.test(input)) { + let problemId = input.startsWith('cf') ? input.substring(2) : input; + + const cfGymMatch = problemId.match(/^(\d{6,})([a-zA-Z])$/); + if (cfGymMatch) { + const contestId = cfGymMatch[1]; + const problemLetter = cfGymMatch[2].toUpperCase(); // 注意:大写字母 + outputElement.value = `https://codeforces.com/problemset/gymProblem/${contestId}/${problemLetter}`; + return; + } + } + + // 如果不是已知格式,清空输出 + outputElement.value = ''; +}); From 3c7e8fc0113d5afbe5887dd85b6f972585d443e9 Mon Sep 17 00:00:00 2001 From: liuhengxi <2019891597@qq.com> Date: Sun, 14 Sep 2025 19:12:05 +0800 Subject: [PATCH 17/20] 14 --- js/soj_collection_id2url.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/js/soj_collection_id2url.js b/js/soj_collection_id2url.js index 7d07e5b..14f4916 100644 --- a/js/soj_collection_id2url.js +++ b/js/soj_collection_id2url.js @@ -8,10 +8,10 @@ document.getElementById('input-id').addEventListener('input', function(e) { } // 1. Codeforces 常规题目 (cf1a 或 1a) - if (input.startsWith('cf') || /^\d{1,5}[a-zA-Z]$/.test(input)) { + if (input.startsWith('cf') || /^\d{1,5}[a-zA-Z]\d{0,1}$/.test(input)) { let problemId = input.startsWith('cf') ? input.substring(2) : input; - const cfMatch = problemId.match(/^(\d{1,5})([a-zA-Z])$/); + const cfMatch = problemId.match(/^(\d{1,5})([a-zA-Z]\d{0,1})$/); if (cfMatch) { const contestId = cfMatch[1]; const problemLetter = cfMatch[2].toUpperCase(); // 注意:大写字母 @@ -85,6 +85,6 @@ document.getElementById('input-id').addEventListener('input', function(e) { } } - // 如果不是已知格式,清空输出 - outputElement.value = ''; + // 如果不是已知格式,不改变输出 + // outputElement.value = ''; }); From f4db09cd3023819e6d4e35ad04d41046ddbdcf94 Mon Sep 17 00:00:00 2001 From: liuhengxi <2019891597@qq.com> Date: Mon, 15 Sep 2025 18:16:44 +0800 Subject: [PATCH 18/20] 15 --- js/soj_collection_id2url.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/js/soj_collection_id2url.js b/js/soj_collection_id2url.js index 14f4916..d4e503b 100644 --- a/js/soj_collection_id2url.js +++ b/js/soj_collection_id2url.js @@ -8,10 +8,10 @@ document.getElementById('input-id').addEventListener('input', function(e) { } // 1. Codeforces 常规题目 (cf1a 或 1a) - if (input.startsWith('cf') || /^\d{1,5}[a-zA-Z]\d{0,1}$/.test(input)) { + if (input.startsWith('cf') || /^\d{1,5}[a-z]\d{0,1}$/.test(input)) { let problemId = input.startsWith('cf') ? input.substring(2) : input; - const cfMatch = problemId.match(/^(\d{1,5})([a-zA-Z]\d{0,1})$/); + const cfMatch = problemId.match(/^(\d{1,5})([a-z]\d{0,1})$/); if (cfMatch) { const contestId = cfMatch[1]; const problemLetter = cfMatch[2].toUpperCase(); // 注意:大写字母 @@ -21,7 +21,7 @@ document.getElementById('input-id').addEventListener('input', function(e) { } // 2. AtCoder 格式 (abc/arc/agc + 数字 + 字母) - const atcoderMatch = input.match(/^(a[bgc]r?c)(\d+)([a-zA-Z])$/i); + const atcoderMatch = input.match(/^(a[brg]c)(\d+)([a-z])$/i); if (atcoderMatch) { const contestType = atcoderMatch[1].toLowerCase(); const contestNumber = atcoderMatch[2]; @@ -73,10 +73,10 @@ document.getElementById('input-id').addEventListener('input', function(e) { } // 8. Codeforces Gym 题目 (cf123456a 或 123456a,6位以上数字) - if (input.startsWith('cf') || /^\d{6,}[a-zA-Z]$/.test(input)) { + if (input.startsWith('cf') || /^\d{6,}[a-z]$/.test(input)) { let problemId = input.startsWith('cf') ? input.substring(2) : input; - const cfGymMatch = problemId.match(/^(\d{6,})([a-zA-Z])$/); + const cfGymMatch = problemId.match(/^(\d{6,})([a-z])$/); if (cfGymMatch) { const contestId = cfGymMatch[1]; const problemLetter = cfGymMatch[2].toUpperCase(); // 注意:大写字母 From 0f74f72d601860db8e89785f22fd84c8a3cd37c4 Mon Sep 17 00:00:00 2001 From: liuhengxi <2019891597@qq.com> Date: Fri, 19 Sep 2025 09:00:52 +0800 Subject: [PATCH 19/20] 16 --- app/controllers/collection_user_manage.php | 2 +- js/soj.js | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/controllers/collection_user_manage.php b/app/controllers/collection_user_manage.php index 26044b3..3404a62 100644 --- a/app/controllers/collection_user_manage.php +++ b/app/controllers/collection_user_manage.php @@ -27,7 +27,7 @@ function echoProblem($problem) { echo ''; echo '', $problem['title'], ''; foreach (queryUserCollectionTags($owner, $problem['id']) as $tag) { - echo '', '', HTML::escape($tag), '', ''; + echo '', '', HTML::escape($tag), '', ''; } echo ''; echo ''; diff --git a/js/soj.js b/js/soj.js index 925a7ef..ac58982 100644 --- a/js/soj.js +++ b/js/soj.js @@ -397,7 +397,10 @@ $.fn.uoj_blog_tag = function() { $.fn.uoj_collection_tag = function() { return this.each(function() { - $(this).attr('href', '/collection?type=' + encodeURIComponent($(this).text())); + var owner = $(this).data('owner'); + var path_owner = ''; + if(owner) path_owner = '/' + owner; + $(this).attr('href', '/collection' + path_owner + '?type=' + encodeURIComponent($(this).text())); }); } From c2fd0380d3aa641c849a3ca648c716a973e884b0 Mon Sep 17 00:00:00 2001 From: liuhengxi <2019891597@qq.com> Date: Sat, 20 Sep 2025 14:45:39 +0800 Subject: [PATCH 20/20] allow autoplay --- app/views/blog-preview.php | 1 + js/marked.js | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/app/views/blog-preview.php b/app/views/blog-preview.php index bc15b0c..6e36cf1 100644 --- a/app/views/blog-preview.php +++ b/app/views/blog-preview.php @@ -30,6 +30,7 @@ iframe.title = anchor.textContent; iframe.width = "100%"; iframe.height = "700px"; + iframe.allow = "autoplay"; anchor.parentNode.replaceChild(iframe, anchor); }); diff --git a/js/marked.js b/js/marked.js index a5a0595..0c29643 100644 --- a/js/marked.js +++ b/js/marked.js @@ -710,19 +710,19 @@ InlineLexer.prototype.outputLink = function(cap, link) { if (cap[0][0] !== '!') { if (link.href[0] == '%') { - link.href = link.href.substring(1); - return '' - + this.output(cap[1]) - + ''; - } + link.href = link.href.substring(1); + return '' + + this.output(cap[1]) + + ''; + } if (link.href[0] == ':') { var ret = this.extractProperties(link.rest); @@ -937,7 +937,7 @@ Parser.prototype.tok = function() { if (this.options.getLangClass && this.token.lang == 'copyable') { return '
' + this.token.text + '
'; - } else if (this.options.getLangClass && this.token.lang != undefined) { + } else if (this.options.getLangClass && this.token.lang != undefined) { return '
'