diff --git a/db/app_uoj233.sql b/db/app_uoj233.sql index 13fad39..00460ec 100644 --- a/db/app_uoj233.sql +++ b/db/app_uoj233.sql @@ -591,6 +591,49 @@ LOCK TABLES `problems_tags` WRITE; /*!40000 ALTER TABLE `problems_tags` ENABLE KEYS */; UNLOCK TABLES; +-- +-- Table structure for table `lists` +-- + +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `lists` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `title` text NOT NULL, + `is_hidden` tinyint(1) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `lists_problems` +-- + +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `lists_problems` ( + `list_id` int(11) NOT NULL, + `problem_id` int(11) NOT NULL, + PRIMARY KEY (`list_id`, `problem_id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `lists_tags` +-- + +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `lists_tags` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `list_id` int(11) NOT NULL, + `tag` varchar(30) NOT NULL, + PRIMARY KEY (`id`), + KEY `list_id` (`list_id`), + KEY `tag` (`tag`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; +/*!40101 SET character_set_client = @saved_cs_client */; + -- -- Table structure for table `search_requests` -- diff --git a/web/app/controllers/problem_list.php b/web/app/controllers/problem_list.php new file mode 100644 index 0000000..6935b48 --- /dev/null +++ b/web/app/controllers/problem_list.php @@ -0,0 +1,279 @@ +name = 'list'; + $list_editor->blog_url = null; + $list_editor->cur_data = array( + 'title' => $list['title'], + 'tags' => $list_tags, + 'is_hidden' => $list['is_hidden'] + ); + $list_editor->label_text = array_merge($list_editor->label_text, array( + 'view blog' => '保存题单信息', + 'blog visibility' => '题单可见性' + )); + $list_editor->show_editor = false; + + $list_editor->save = function($data) { + global $list_id, $list; + DB::update("update lists set title = '" . DB::escape($data['title']) . "' where id = {$list_id}"); + + if ($data['tags'] !== $list_tags) { + DB::delete("delete from lists_tags where list_id = {$list_id}"); + foreach ($data['tags'] as $tag) { + DB::insert("insert into lists_tags (list_id, tag) values ({$list_id}, '" . DB::escape($tag) . "')"); + } + } + + if ($data['is_hidden'] != $list['is_hidden'] ) { + DB::update("update lists set is_hidden = {$data['is_hidden']} where id = {$list_id}"); + } + }; + + $list_editor->runAtServer(); + } + + function removeFromProblemListForm($problem_id) { + $res_form = new UOJForm("remove_problem_{$problem_id}"); + $input_name = "problem_id_delete_{$problem_id}"; + $res_form->addHidden($input_name, $problem_id, function($problem_id) { + global $myUser; + if (!isSuperUser($myUser)) { + return '只有超级用户可以编辑题单'; + } + }, null); + $res_form->handle = function() use ($input_name) { + global $list_id; + $problem_id = $_POST[$input_name]; + DB::query("delete from lists_problems where problem_id={$problem_id} and list_id={$list_id}"); + }; + $res_form->submit_button_config['class_str'] = 'btn btn-danger'; + $res_form->submit_button_config['text'] = '删除'; + $res_form->submit_button_config['align'] = 'inline'; + return $res_form; + } + + $removeProblemForms = array(); + if (isSuperUser($myUser)) { + $problem_ids = DB::query("select problem_id from lists_problems where list_id = {$list_id}"); + while ($row = DB::fetch($problem_ids)) { + $problem_id = $row['problem_id']; + $removeForm = removeFromProblemListForm($problem_id); + $removeForm->runAtServer(); + $removeProblemForms[$problem_id] = $removeForm; + } + } + + if (isSuperUser($myUser)) { + $add_new_problem_form = new UOJForm('add_new_problem'); + $add_new_problem_form->addInput('problem_id', 'text', '题目 ID', '', + function ($x) { + global $myUser, $list_id; + + if (!isSuperUser($myUser)) { + return '只有超级用户可以编辑题单'; + } + + if (!validateUInt($x)) { + return 'ID 不合法'; + } + $problem = queryProblemBrief($x); + if (!$problem) { + return '题目不存在'; + } + + if (queryProblemInList($list_id, $x)) { + return '该题目已经在题单中'; + } + + return ''; + }, + null + ); + $add_new_problem_form->submit_button_config['align'] = 'compressed'; + $add_new_problem_form->submit_button_config['text'] = '添加到题单'; + $add_new_problem_form->handle = function() { + global $list_id, $myUser; + $problem_id = $_POST['problem_id']; + + DB::insert("insert into lists_problems (list_id, problem_id) values ({$list_id}, {$problem_id})"); + }; + $add_new_problem_form->runAtServer(); + } + + function echoProblem($problem) { + global $myUser, $removeProblemForms; + + if (isProblemVisibleToUser($problem, $myUser)) { + echo ''; + if ($problem['submission_id']) { + echo ''; + } else { + echo ''; + } + echo '#', $problem['id'], ''; + echo ''; + if (isSuperUser($myUser)) { + $form = $removeProblemForms[$problem['id']]; + $form->printHTML(); + } + if ($problem['is_hidden']) { + echo ' [隐藏] '; + } + if ($problem['uploader'] == $myUser['username']) { + echo ' [我的题目] '; + } + echo '', $problem['title'], ''; + + if (isset($_COOKIE['show_tags_mode'])) { + echo ' ' . $problem["uploader"] . ' '; + + foreach (queryProblemTags($problem['id']) as $tag) { + echo '', '', HTML::escape($tag), '', ''; + } + } + echo ''; + if (isset($_COOKIE['show_submit_mode'])) { + $perc = $problem['submit_num'] > 0 ? round(100 * $problem['ac_num'] / $problem['submit_num']) : 0; + echo <<×{$problem['ac_num']} + ×{$problem['submit_num']} + +
+
{$perc}%
+
+ +EOD; + } + if (isset($_COOKIE['show_difficulty'])) { + $extra_config = getProblemExtraConfig($problem); + if ($extra_config['difficulty'] == 0) { + echo ""; + } else { + echo "{$extra_config['difficulty']}"; + } + } + echo '', getClickZanBlock('P', $problem['id'], $problem['zan']), ''; + echo ''; + } + } + + $header = ''; + $header .= 'ID'; + $header .= ''.UOJLocale::get('problems::problem').''; + if (isset($_COOKIE['show_submit_mode'])) { + $header .= ''.UOJLocale::get('problems::ac').''; + $header .= ''.UOJLocale::get('problems::submit').''; + $header .= ''.UOJLocale::get('problems::ac ratio').''; + } + if (isset($_COOKIE['show_difficulty'])) { + $header .= ''.UOJLocale::get('problems::difficulty').''; + } + $header .= ''.UOJLocale::get('appraisal').''; + $header .= ''; + + $pag_config = array('page_len' => 40); + $pag_config['col_names'] = array('best_ac_submissions.submission_id as submission_id', 'problems.id as id', 'problems.is_hidden as is_hidden', 'problems.title as title', 'problems.submit_num as submit_num', 'problems.ac_num as ac_num', 'problems.zan as zan', 'problems.extra_config as extra_config', 'problems.uploader as uploader'); + + $pag_config['table_name'] = "problems left join best_ac_submissions on best_ac_submissions.submitter = '{$myUser['username']}' and problems.id = best_ac_submissions.problem_id inner join lists_problems lp on lp.list_id = {$list_id} and lp.problem_id = problems.id"; + + $pag_config['cond'] = '1'; + $pag_config['tail'] = "order by id asc"; + $pag = new Paginator($pag_config); + + $div_classes = array('table-responsive'); + $table_classes = array('table', 'table-bordered', 'table-hover', 'table-striped'); + ?> + +编辑题单信息'; + echo '
'; + $list_editor->printHTML(); + echo '
'; + + echo '
添加题目到题单
'; + $add_new_problem_form->printHTML(); + } + ?> +
+
+
"" 中的题目:
+

(题单 ID: #)

+
+
+ + +
+
+ pagination(); ?> +
+
+
+ + +'; + echo ''; + echo ''; + echo $header; + echo ''; + echo ''; + + foreach ($pag->get() as $idx => $row) { + echoProblem($row); + echo "\n"; + } + if ($pag->isEmpty()) { + echo ''; + } + + echo ''; + echo '
'.UOJLocale::get('none').'
'; + echo ''; + + echo $pag->pagination(); + ?> + + diff --git a/web/app/controllers/problem_lists.php b/web/app/controllers/problem_lists.php new file mode 100644 index 0000000..e5140d2 --- /dev/null +++ b/web/app/controllers/problem_lists.php @@ -0,0 +1,107 @@ +handle = function() { + DB::query("insert into lists (title, is_hidden) values ('未命名题单', 1)"); + }; + $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->runAtServer(); + } + + function echoList($list) { + global $myUser; + + echo ''; + if ($list['problem_count'] == $list['accepted'] && $list['problem_count'] > 0) { + echo ''; + } else { + echo ''; + } + echo '#', $list['list_id'], ''; + + echo ''; + if ($list['is_hidden']) { + echo ' [隐藏] '; + } + echo '', $list['title'], ''; + foreach (queryProblemListTags($list['list_id']) as $tag) { + echo '', '', HTML::escape($tag), '', ''; + } + echo ''; + + echo "{$list['accepted']}"; + echo "{$list['problem_count']}"; + + echo ''; + } + ?> + + + +printHTML(); + } + + $problem_list_caption = UOJLocale::get('problems::problem list'); + $ac_caption = UOJLocale::get('problems::ac'); + $total_caption = UOJLocale::get('problems::total'); + $header = << + ID + {$problem_list_caption} + {$ac_caption} + {$total_caption} + +EOD; + + $cond = array(); + + $search_tag = null; + if (isset($_GET['tag'])) { + $search_tag = $_GET['tag']; + } + if ($search_tag) { + $cond[] = "'" . DB::escape($search_tag) . "' in (select tag from lists_tags where lists_tags.list_id = a.id)"; + } + if (!isSuperUser($myUser)) { + $cond[] = "is_hidden = 0"; + } + + if ($cond) { + $cond = join($cond, ' and '); + } else { + $cond = '1'; + } + + $from = "lists a left join lists_problems b on a.id = b.list_id left join best_ac_submissions c on (b.problem_id = c.problem_id and c.submitter = '{$myUser['username']}')"; + + echoLongTable( + array('a.id as list_id', 'a.title as title', 'a.is_hidden as is_hidden', 'count(b.problem_id) as problem_count', 'count(c.submitter) as accepted'), + $from, $cond, 'group by a.id order by a.id desc', + $header, + 'echoList', + array('page_len' => 40, + 'table_classes' => array('table', 'table-bordered', 'table-hover', 'table-striped'), + 'print_after_table' => function() { + global $myUser; + }, + 'head_pagination' => true + ) + ); + ?> + + diff --git a/web/app/libs/uoj-query-lib.php b/web/app/libs/uoj-query-lib.php index 3364df8..5d357a1 100644 --- a/web/app/libs/uoj-query-lib.php +++ b/web/app/libs/uoj-query-lib.php @@ -63,6 +63,25 @@ function queryProblemTags($id) { } return $tags; } + +function queryProblemList($id) { + return DB::selectFirst("select * from lists where id = $id", MYSQLI_ASSOC); +} +function queryProblemListTags($id) { + $tags = array(); + $result = DB::query("select tag from lists_tags where list_id = $id order by id"); + if (!$result) { + return $tags; + } + while ($row = DB::fetch($result, MYSQLI_NUM)) { + $tags[] = $row[0]; + } + return $tags; +} +function queryProblemInList($list_id, $problem_id) { + return DB::selectFirst("select * from lists_problems where list_id='$blog_id' and problem_id='$problem_id'", MYSQLI_ASSOC); +} + function queryContestProblemRank($contest, $problem) { if (!DB::selectFirst("select * from contests_problems where contest_id = {$contest['id']} and problem_id = {$problem['id']}")) { return null; diff --git a/web/app/locale/basic/en.php b/web/app/locale/basic/en.php index e9d1479..7c17bd5 100644 --- a/web/app/locale/basic/en.php +++ b/web/app/locale/basic/en.php @@ -11,6 +11,7 @@ return [ 'system manage' => 'System Manage', 'contests' => 'Contests', 'problems' => 'Problems', + 'problems lists' => 'Problems Lists', 'groups' => 'Groups', 'add new group' => 'Add new group', 'users count' => 'Users', diff --git a/web/app/locale/basic/zh-cn.php b/web/app/locale/basic/zh-cn.php index 032d5fc..abc9a40 100644 --- a/web/app/locale/basic/zh-cn.php +++ b/web/app/locale/basic/zh-cn.php @@ -11,6 +11,7 @@ return [ 'system manage' => '系统管理', 'contests' => '比赛', 'problems' => '题库', + 'problems lists' => '题单', 'groups' => '小组', 'add new group' => '添加新小组', 'users count' => '用户数量', diff --git a/web/app/locale/problems/en.php b/web/app/locale/problems/en.php index 9df005f..0e1edcc 100644 --- a/web/app/locale/problems/en.php +++ b/web/app/locale/problems/en.php @@ -1,15 +1,19 @@ 'Problem', + 'problem list' => 'Problem List', 'all problems' => 'All Problems', 'template problems' => 'Template Problems', 'add new' => 'Add new problem', + 'add new list' => 'Add new problem list', 'title' => 'Title', + 'total' => 'Total', 'ac' => 'AC', 'submit' => 'Submit', 'ac ratio' => 'AC Ratio', 'show tags' => 'Show tags', 'show statistics' => 'Show statistics', + 'submissions statistics' => 'Submissions statistics', 'statement' => 'Statement', 'custom test' => 'Custom Test', 'manage' => 'Manage', diff --git a/web/app/locale/problems/zh-cn.php b/web/app/locale/problems/zh-cn.php index 0523cb5..7e3f5e0 100644 --- a/web/app/locale/problems/zh-cn.php +++ b/web/app/locale/problems/zh-cn.php @@ -1,9 +1,13 @@ '题目', + 'problem list' => '题单', 'all problems' => '总题库', 'template problems' => '模板题库', 'add new' => '添加新题', + 'add new list' => '添加新题单', + 'title' => '标题', + 'total' => '总题数', 'ac' => 'AC', 'submit' => '提交', 'ac ratio' => 'AC 率', diff --git a/web/app/route.php b/web/app/route.php index d045857..e12bfc4 100644 --- a/web/app/route.php +++ b/web/app/route.php @@ -18,6 +18,9 @@ Route::group([ 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('/problem_lists', '/problem_lists.php'); + Route::any('/problem_list/{id}', '/problem_list.php'); Route::any('/contests', '/contests.php'); Route::any('/contest/new', '/add_contest.php'); diff --git a/web/app/views/main-nav.php b/web/app/views/main-nav.php index 871545f..c51f7fa 100644 --- a/web/app/views/main-nav.php +++ b/web/app/views/main-nav.php @@ -6,8 +6,9 @@