diff --git a/web/app/controllers/contest_members.php b/web/app/controllers/contest_members.php index 7741cee..b813fb9 100644 --- a/web/app/controllers/contest_members.php +++ b/web/app/controllers/contest_members.php @@ -48,15 +48,12 @@ if ($is_manager) { '小组 ID', '', function ($group_id, &$vdata) { - if (!validateUInt($group_id)) { - return '小组 ID 不合法'; - } - $group = queryGroup($group_id); + $group = UOJGroup::query($group_id); if (!$group) { return '小组不存在'; } - $vdata['group_id'] = $group_id; + $vdata['group'] = $group; return ''; }, @@ -65,9 +62,11 @@ if ($is_manager) { $add_group_to_contest_form->submit_button_config['align'] = 'compressed'; $add_group_to_contest_form->submit_button_config['text'] = '注册该小组中的用户'; $add_group_to_contest_form->handle = function (&$vdata) { - $users = queryGroupUsers($vdata['group_id']); + $usernames = $vdata['group']->getUsernames(); + + foreach ($usernames as $username) { + $user = UOJUser::query($username); - foreach ($users as $user) { UOJContest::cur()->userRegister($user); } }; @@ -177,7 +176,8 @@ if ($contest['cur_progress'] == CONTEST_NOT_STARTED) { #' . UOJLocale::get('username') . ''; +$header_row = ''; +$header_row .= '#' . UOJLocale::get('username') . ''; if ($show_ip) { $header_row .= 'remote_addrhttp_x_forwarded_for'; } diff --git a/web/app/controllers/group.php b/web/app/controllers/group.php index fa26f7e..1d0896a 100644 --- a/web/app/controllers/group.php +++ b/web/app/controllers/group.php @@ -1,160 +1,157 @@ userCanView(Auth::user(), ['ensure' => true]); +?> - $group_id = $_GET['id']; - if (!validateUInt($group_id) || !($group = queryGroup($group_id))) { - become404Page(); - } - - if (!isSuperUser($myUser) && $group['is_hidden']) { - become403Page(); - } - ?> - - +
- -
+ +
+ +
+

+ + (ID: #) + + + + + + +

- -
-

- - [隐藏] - - - (ID: #) -

- - - - -
- - - -
-
-

- -

- -
- purify(HTML::parsedown()->line($group['announcement'])) ?> + userCanManage(Auth::user())) : ?> +
+ getLink([ + 'where' => '/manage', + 'class' => 'btn btn-primary', + 'text' => UOJLocale::get('problems::manage'), + ]); + ?> +
+
- -
- + + + +
+
+

+ +

+ +
+ purify(HTML::parsedown()->line(UOJGroup::info('announcement'))) ?> +
+ +
+ +
+ +
- + +
+
+

+ +

+
    + getLatestGroupmatesAcceptedSubmissionIds(Auth::user()) as $id) : ?> + setProblem(); + $user = UOJUser::query($submission->info['submitter']); + ?> +
  • + + 解决了问题 + problem->getLink(['with' => 'id']) ?> + () +
  • + +
+
+
+ +
+
+

+ +

+ DB::raw("groups_assignments.list_id"), + ] + ], + [ + "groups_assignments.group_id" => UOJGroup::info('id'), + ["groups_assignments.end_time", ">", DB::raw("addtime(now(), '-168:00:00')")] + ], + 'order by groups_assignments.end_time desc, groups_assignments.list_id desc', + << + ID + 标题 + 状态 + 结束时间 + + EOD, + function ($info) { + $assignment = new UOJGroupAssignment($info, UOJGroup::cur()); + + echo HTML::tag_begin('tr'); + echo HTML::tag('td', ['class' => 'text-center'], $assignment->info['id']); + echo HTML::tag('td', [], $assignment->getLink()); + if ($assignment->info['end_time'] < UOJTime::$time_now) { + echo HTML::tag('td', ['class' => 'text-danger'], '已结束'); + } else { + echo HTML::tag('td', ['class' => 'text-success'], '进行中'); + } + echo HTML::tag('td', [], $assignment->info['end_time_str']); + echo HTML::tag_end('tr'); + }, + [ + 'echo_full' => true, + 'div_classes' => ['table-responsive'], + 'table_classes' => ['table', 'align-middle', 'mb-0'], + ], + ); + ?> +
+
+ +
+
+

+ +

+ 25, + 'group_id' => UOJGroup::info('id'), + ]) ?> +
+
+
-
-
-
-

- -

-
    - '; - echo getUserLink($ac['submitter']); - echo ' 解决了问题 '; - echo '', $ac['problem_title'], ' '; - echo ''; - echo ''; - } - if (count($current_ac) == 0) { - echo '暂无最新动态'; - } - ?> -
-
-
- -
-
-

- -

- addtime(now(), '-168:00:00')", - 'order by end_time desc, list_id desc', - << - ID - 标题 - 状态 - 结束时间 - -EOD, - function($row) use ($group) { - $end_time = DateTime::createFromFormat('Y-m-d H:i:s', $row['end_time']); - - echo ''; - echo '', $row['list_id'], ''; - echo '', '', HTML::escape($row['title']), '', ''; - if ($end_time < UOJTime::$time_now) { - echo '已结束'; - } else { - echo '进行中'; - } - echo '', $end_time->format('Y-m-d H:i:s'), ''; - echo ''; - }, - [ - 'echo_full' => true, - 'div_classes' => ['table-responsive'], - 'table_classes' => ['table', 'align-middle', 'mb-0'], - ] - ); - ?> -
-
- -
-
-

- -

- 50, - 'group_id' => $group_id, - 'by_accepted' => true, - 'div_classes' => ['table-responsive', 'mb-3'], - 'table_classes' => ['table', 'text-center', 'mb-0'], - ]) ?> -
-
- - -
- - - + +
diff --git a/web/app/controllers/group_assignment.php b/web/app/controllers/group_assignment.php index ab6a622..ded4ebb 100644 --- a/web/app/controllers/group_assignment.php +++ b/web/app/controllers/group_assignment.php @@ -11,23 +11,13 @@ if (!validateUInt($group_id)) { become404Page(); } -UOJList::init(UOJRequest::get('list_id')) || UOJResponse::page404(); - -$assignment = queryAssignmentByGroupListID($group_id, UOJList::info('id')); - -if (!$assignment) { - become404Page(); -} - -$group = queryGroup($assignment['group_id']); -$list = UOJList::info(); - -if (($group['is_hidden'] || $list['is_hidden']) && !isSuperUser($myUser)) { - become403Page(); -} +UOJGroup::init(UOJRequest::get('id')) || UOJResponse::page404(); +UOJGroupAssignment::init(UOJRequest::get('list_id')) || UOJResponse::page404(); +UOJGroupAssignment::cur()->valid() || UOJResponse::page404(); +UOJGroupAssignment::cur()->userCanView(['ensure' => true]); ?> - +
@@ -36,48 +26,48 @@ if (($group['is_hidden'] || $list['is_hidden']) && !isSuperUser($myUser)) { 作业:
    -
  • 对应题单:#
  • -
  • 所属小组:
  • -
  • 结束时间:
  • +
  • 对应题单:#
  • +
  • 所属小组:getLink() ?> +
  • 结束时间:
getProblemIDs(); - $users = queryGroupUsers($group['id']); - $usernames = []; + $problems = UOJGroupAssignment::cur()->getProblemIDs(); + $usernames = UOJGroup::cur()->getUsernames(); $n_users = count($users); - $submission_end_time = min(new DateTime(), DateTime::createFromFormat('Y-m-d H:i:s', $assignment['end_time'])); + $submission_end_time = min(new DateTime(), UOJGroupAssignment::info('end_time')); - foreach ($users as $user) { - $usernames[] = $user['username']; - } - - // standings: rank => [total_score, user => [username, realname], scores[]] + // standings: rank => [total_score, [username, realname], scores[]] $standings = []; foreach ($usernames as $username) { $user = UOJUser::query($username); - $row = ['total_score' => 0]; - $scores = []; + $row = [0, [$user['username'], $user['realname']], []]; - $row['user'] = [ - 'username' => $user['username'], - 'realname' => $user['realname'], - ]; - - $cond = "submitter = '{$user['username']}' AND unix_timestamp(submit_time) <= " . $submission_end_time->getTimestamp(); + $conds = DB::land([ + "submitter" => $user['username'], + ["unix_timestamp(submit_time)", "<=", $submission_end_time->getTimestamp()], + ]); foreach ($problems as $problem_id) { - $submission = DB::selectFirst("SELECT id, score FROM submissions WHERE problem_id = $problem_id AND $cond ORDER BY score DESC, id DESC"); + $submission = DB::selectFirst([ + "select", DB::fields(["id", "score"]), + "from submissions", + "where", [ + "problem_id" => $problem_id, + $conds, + ], + "order by score desc, id desc", + ]); if ($submission) { - $row['scores'][] = [ - 'submission_id' => (int)$submission['id'], - 'score' => (int)$submission['score'], + $row[2][] = [ + (int)$submission['id'], + (int)$submission['score'], ]; - $row['total_score'] += $submission['score']; + $row[0] += $submission['score']; } else { - $row['scores'][] = null; + $row[2][] = null; } } @@ -85,11 +75,11 @@ if (($group['is_hidden'] || $list['is_hidden']) && !isSuperUser($myUser)) { } usort($standings, function ($lhs, $rhs) { - if ($lhs['total_score'] != $rhs['total_score']) { - return $rhs['total_score'] - $lhs['total_score']; + if ($lhs[0] != $rhs[0]) { + return $rhs[0] - $lhs[0]; } - return strcmp($lhs['user']['username'], $rhs['user']['username']); + return strcmp($lhs[1][0], $rhs[1][0]); }); ?> @@ -112,26 +102,26 @@ if (($group['is_hidden'] || $list['is_hidden']) && !isSuperUser($myUser)) { function(row) { var col_tr = ''; - if (row['total_score'] == problems.length * 100) { + if (row[0] == problems.length * 100) { col_tr += ''; } else { col_tr += ''; } - col_tr += '' + getUserLink(row['user']['username'], row['user']['realname']) + ''; + col_tr += '' + getUserLink(row[1][0], row[1][1]) + ''; col_tr += '' + - '' + row['total_score'] + '' + + '' + row[0] + '' + ''; - for (var i = 0; i < row['scores'].length; i++) { - var col = row['scores'][i]; + for (var i = 0; i < row[2].length; i++) { + var col = row[2][i]; if (col) { - if (col['score'] == 100) { + if (col[1] == 100) { col_tr += ''; } else { col_tr += ''; } - col_tr += '' + col['score'] + ''; + col_tr += '' + col[1] + ''; col_tr += ''; } else { col_tr += ''; diff --git a/web/app/controllers/group_manage.php b/web/app/controllers/group_manage.php index e41c0d6..c64ee60 100644 --- a/web/app/controllers/group_manage.php +++ b/web/app/controllers/group_manage.php @@ -1,45 +1,33 @@ userCanManage(Auth::user()) || UOJResponse::page403(); -if (!isSuperUser($myUser)) { - become403Page(); -} - -if (isset($_GET['tab'])) { - $cur_tab = $_GET['tab']; -} else { - $cur_tab = 'profile'; -} +$cur_tab = UOJRequest::get('tab', 'is_string', 'profile'); $tabs_info = [ 'profile' => [ 'name' => '基本信息', - 'url' => "/group/{$group['id']}/manage/profile", + 'url' => '/group/' . UOJGroup::info('id') . '/manage/profile', ], 'assignments' => [ 'name' => '作业管理', - 'url' => "/group/{$group['id']}/manage/assignments", + 'url' => '/group/' . UOJGroup::info('id') . '/manage/assignments', ], 'users' => [ 'name' => '用户管理', - 'url' => "/group/{$group['id']}/manage/users", + 'url' => '/group/' . UOJGroup::info('id') . '/manage/users', ] ]; if (!isset($tabs_info[$cur_tab])) { - become404Page(); + UOJResponse::page404(); } if ($cur_tab == 'profile') { @@ -48,7 +36,7 @@ if ($cur_tab == 'profile') { 'name', 'text', '名称', - $group['title'], + UOJGroup::info('title'), function ($title, &$vdata) { if ($title == '') { return '名称不能为空'; @@ -72,11 +60,11 @@ if ($cur_tab == 'profile') { $update_profile_form->addVCheckboxes('is_hidden', [ '0' => '公开', '1' => '隐藏', - ], '可见性', $group['is_hidden']); + ], '可见性', UOJGroup::info('is_hidden')); $update_profile_form->addVTextArea( 'announcement', '公告', - $group['announcement'], + UOJGroup::info('announcement'), function ($announcement, &$vdata) { if (strlen($announcement) > 3000) { return '公告过长'; @@ -88,12 +76,18 @@ if ($cur_tab == 'profile') { }, null ); - $update_profile_form->handle = function ($vdata) use ($group) { - $esc_title = DB::escape($vdata['title']); - $is_hidden = $_POST['is_hidden']; - $esc_announcement = DB::escape($vdata['announcement']); - - DB::update("UPDATE `groups` SET title = '$esc_title', is_hidden = '$is_hidden', announcement = '$esc_announcement' WHERE id = {$group['id']}"); + $update_profile_form->handle = function ($vdata) { + DB::update([ + "update `groups`", + "set", [ + "title" => $vdata['title'], + "is_hidden" => $_POST['is_hidden'], + "announcement" => $vdata['announcement'], + ], + "where", [ + "id" => UOJGroup::info('id'), + ], + ]); dieWithJsonData(['status' => 'success', 'message' => '修改成功']); }; @@ -121,17 +115,20 @@ EOD); $update_profile_form->runAtServer(); } elseif ($cur_tab == 'assignments') { if (isset($_POST['submit-remove_assignment']) && $_POST['submit-remove_assignment'] == 'remove_assignment') { - $list_id = $_POST['list_id']; + $list_id = UOJRequest::post('list_id'); - if (!validateUInt($list_id)) { - dieWithAlert('题单 ID 不合法。'); + $list = UOJGroupAssignment::query($list_id); + if (!$list || !$list->valid()) { + dieWithAlert('题单不合法。'); } - if (!queryAssignmentByGroupListID($group['id'], $list_id)) { - dieWithAlert('该题单不在作业中。'); - } - - DB::delete("DELETE FROM `groups_assignments` WHERE `list_id` = $list_id AND `group_id` = {$group['id']}"); + DB::delete([ + "delete from groups_assignments", + "where", [ + "list_id" => $list->info['id'], + "group_id" => UOJGroup::info('id'), + ], + ]); dieWithAlert('移除成功!'); } @@ -142,7 +139,7 @@ EOD); 'text', '题单 ID', '', - function ($list_id, &$vdata) use ($group) { + function ($list_id, &$vdata) { if (!validateUInt($list_id)) { return '题单 ID 不合法'; } @@ -157,7 +154,7 @@ EOD); return '题单是隐藏的'; } - if (queryAssignmentByGroupListID($group['id'], $list->info['id'])) { + if (UOJGroup::cur()->hasAssignment($list)) { return '该题单已经在作业中'; } @@ -186,10 +183,16 @@ EOD); }, null ); - $add_new_assignment_form->handle = function (&$vdata) use ($group) { - $esc_end_time = DB::escape($vdata['end_time']->format('Y-m-d H:i:s')); - - DB::insert("insert into groups_assignments (group_id, list_id, end_time) values ({$group['id']}, '{$vdata['list_id']}', '{$esc_end_time}')"); + $add_new_assignment_form->handle = function (&$vdata) { + DB::insert([ + "insert into groups_assignments", + DB::bracketed_fields(["group_id", "list_id", "end_time"]), + "values", DB::tuple([ + UOJGroup::info('id'), + $vdata['list_id'], + $vdata['end_time']->format('Y-m-d H:i:s'), + ]), + ]); dieWithJsonData([ 'status' => 'success', @@ -220,24 +223,26 @@ EOD); $add_new_assignment_form->runAtServer(); $hidden_time = new DateTime(); - $hidden_time->sub(new DateInterval('P7D')); + $hidden_time->sub(new DateInterval('P3D')); } elseif ($cur_tab == 'users') { if (isset($_POST['submit-remove_user']) && $_POST['submit-remove_user'] == 'remove_user') { - $username = $_POST['remove_username']; + $user = UOJUser::query(UOJRequest::post('remove_username')); - if (!validateUsername($username)) { - dieWithAlert('用户名不合法。'); - } - - if (!queryUser($username)) { + if (!$user) { dieWithAlert('用户不存在。'); } - if (!queryUserInGroup($group['id'], $username)) { + if (!UOJGroup::cur()->hasUser($user)) { dieWithAlert('该用户不在小组中。'); } - DB::delete("DELETE FROM `groups_users` WHERE `username` = '$username' AND `group_id` = {$group['id']}"); + DB::delete([ + "delete from groups_users", + "where", [ + "username" => $user['username'], + "group_id" => UOJGroup::info('id'), + ], + ]); dieWithAlert('移除成功!'); } @@ -249,21 +254,17 @@ EOD); '用户名', '', function ($username, &$vdata) { - global $group_id; + $user = UOJUser::query($username); - if (!validateUsername($username)) { - return '用户名不合法'; + if (!$user) { + return '用户不存在。'; } - if (!queryUser($username)) { - return '用户不存在'; - } - - if (queryUserInGroup($group_id, $username)) { + if (UOJGroup::cur()->hasUser($user)) { return '该用户已经在小组中'; } - $vdata['username'] = $username; + $vdata['username'] = $user['username']; return ''; }, @@ -271,8 +272,16 @@ EOD); ); $add_new_user_form->submit_button_config['class_str'] = 'btn btn-secondary mt-3'; $add_new_user_form->submit_button_config['text'] = '添加'; - $add_new_user_form->handle = function (&$vdata) use ($group) { - DB::insert("insert into groups_users (group_id, username) values ({$group['id']}, '{$vdata['username']}')"); + $add_new_user_form->handle = function (&$vdata) { + DB::insert([ + "insert into groups_users", + DB::bracketed_fields(["group_id", "username"]), + "values", + DB::tuple([ + UOJGroup::info('id'), + $vdata['username'] + ]), + ]); dieWithJsonData(['status' => 'success', 'message' => '已将用户名为 ' . $vdata['username'] . ' 的用户添加到本小组。']); }; @@ -298,24 +307,25 @@ EOD); $add_new_user_form->runAtServer(); } ?> - +

- - (ID: #) + + (ID: #) 管理

- - "> - 返回 - - + getLink([ + 'class' => 'btn btn-light d-block mt-2 w-100 text-start text-primary uoj-back-btn', + 'text' => ' 返回', + ]); + ?>
@@ -358,42 +368,41 @@ EOD); echoLongTable( ['*'], 'groups_assignments', - "group_id = {$group['id']}", + ["group_id" => UOJGroup::info('id')], 'order by end_time desc, list_id desc', << - 题单 ID - 标题 - 状态 - 结束时间 - 操作 - -EOD, - function ($row) use ($group, $hidden_time) { - $list = UOJList::query($row['list_id']); - $end_time = DateTime::createFromFormat('Y-m-d H:i:s', $row['end_time']); + + 题单 ID + 标题 + 状态 + 结束时间 + 操作 + + EOD, + function ($row) use ($hidden_time) { + $assignment = UOJGroupAssignment::query($row['list_id']); - echo ''; - echo '', $list->info['id'], ''; - echo ''; - echo '', $list->info['title'], ''; - if ($list->info['is_hidden']) { + echo HTML::tag_begin('tr'); + echo HTML::tag('td', ['class' => 'text-center'], $assignment->info['id']); + echo HTML::tag_begin('td'); + echo $assignment->getLink(); + if ($assignment->info['is_hidden']) { echo ' ', UOJLocale::get('hidden'), ' '; } - echo ''; - if ($end_time < $hidden_time) { - echo '已隐藏'; - } elseif ($end_time < UOJTime::$time_now) { - echo '已结束'; + echo HTML::tag_end('td'); + if ($assignment->info['end_time'] < $hidden_time) { + echo HTML::tag('td', ['class' => 'text-secondary'], '已隐藏'); + } elseif ($assignment->info['end_time'] < UOJTime::$time_now) { + echo HTML::tag('td', ['class' => 'text-danger'], '已结束'); } else { - echo '进行中'; + echo HTML::tag('td', ['class' => 'text-success'], '进行中'); } - echo '', $end_time->format('Y-m-d H:i:s'), ''; + echo HTML::tag('td', [], $assignment->info['end_time_str']); echo ''; - echo ' 编辑 '; - echo '
info['id'], ')吗?移除作业不会删除题单。")\'>' - . '' - . '' + echo ' 编辑 '; + echo ' info['id'], ')吗?移除作业不会删除题单。")\'>' + . HTML::hiddenToken() + . '' . '' . '
'; echo ''; @@ -447,17 +456,17 @@ EOD, echoLongTable( ['*'], 'groups_users', - "group_id = {$group['id']}", + ["group_id" => UOJGroup::info('id')], 'order by username asc', << - 用户名 - 操作 - - EOD, - function ($row) use ($group) { - echo ''; - echo '', getUserLink($row['username']), ''; + + 用户名 + 操作 + + EOD, + function ($row) { + echo HTML::tag_begin('tr'); + echo HTML::tag('td', [], UOJUser::getLink($row['username'])); echo ''; echo '
' . '' @@ -465,7 +474,7 @@ EOD, . '' . '
'; echo ''; - echo ''; + echo HTML::tag_end('tr'); }, [ 'page_len' => 20, diff --git a/web/app/controllers/lists.php b/web/app/controllers/lists.php index 4c9691d..6694cf4 100644 --- a/web/app/controllers/lists.php +++ b/web/app/controllers/lists.php @@ -23,26 +23,30 @@ if (isSuperUser($myUser)) { function getListTR($info) { $list = new UOJList($info); $problems = $list->getProblemIDs(); - $accepted = DB::selectCount([ - "select count(*)", - "from best_ac_submissions", - "where", [ - "submitter" => Auth::id(), - ["problem_id", "in", DB::tuple($problems)], - ], - ]); + if (Auth::check() && !empty($problems)) { + $accepted = DB::selectCount([ + "select count(*)", + "from best_ac_submissions", + "where", [ + "submitter" => Auth::id(), + ["problem_id", "in", DB::rawtuple($problems)], + ], + ]); + } else { + $accepted = -1; + } $html = HTML::tag_begin('tr', ['class' => 'text-center']); $html .= HTML::tag('td', ['class' => $accepted == count($problems) ? 'table-success' : ''], "#{$list->info['id']}"); $html .= HTML::tag_begin('td', ['class' => 'text-start']); - $html .= HTML::tag('a', ['href' => "/list/{$list->info['id']}"], $list->info['title']); + $html .= $list->getLink(); if ($list->info['is_hidden']) { $html .= ' ' . UOJLocale::get('hidden') . ' '; } foreach ($list->queryTags() as $tag) { $html .= ' ' . $tag['tag'] . ' '; } - $html .= HTML::tag('td', [], $accepted); + $html .= HTML::tag('td', [], max(0, $accepted)); $html .= HTML::tag('td', [], count($problems)); $html .= HTML::tag_end('td'); diff --git a/web/app/libs/uoj-query-lib.php b/web/app/libs/uoj-query-lib.php index 3741f47..bee9094 100644 --- a/web/app/libs/uoj-query-lib.php +++ b/web/app/libs/uoj-query-lib.php @@ -52,46 +52,6 @@ function queryContest($id) { return DB::selectFirst("select * from contests where id = $id", MYSQLI_ASSOC); } -function queryGroup($id) { - return DB::selectFirst("select * from `groups` where id = $id", MYSQLI_ASSOC); -} -function queryGroupUsers($id) { - return DB::selectAll("SELECT * FROM groups_users WHERE group_id = $id"); -} -function queryUserInGroup($group_id, $username) { - return DB::selectFirst("select * from groups_users where username='$username' and group_id='$group_id'", MYSQLI_ASSOC); -} -function queryGroupsOfUser($username) { - return DB::selectAll([ - "select", DB::fields([ - "title" => "groups.title", - "id" => "groups.id", - ]), - "from groups_users", - "inner join `groups`", "on", [ - "groups_users.group_id" => DB::raw("groups.id"), - ], - "where", [ - "groups_users.username" => $username, - "groups.is_hidden" => false, - ], - ]); -} -function queryGroupmateCurrentAC($username) { - return DB::selectAll("select a.problem_id as problem_id, a.submitter as submitter, a.submission_id as submission_id, b.submit_time as submit_time, c.group_id as group_id, c.group_name as group_name, d.title as problem_title, b.submit_time as submit_time, e.realname as realname from best_ac_submissions a inner join submissions b on (a.submission_id = b.id) inner join (select a.username as username, any_value(a.group_id) as group_id, any_value(c.title) as group_name from groups_users a inner join (select a.group_id as group_id from groups_users a inner join `groups` b on a.group_id = b.id where a.username = '$username' and b.is_hidden = 0) b on a.group_id = b.group_id inner join `groups` c on a.group_id = c.id group by a.username) c on a.submitter = c.username inner join problems d on (a.problem_id = d.id and d.is_hidden = 0) inner join user_info e on a.submitter = e.username where b.submit_time > addtime(now(), '-360:00:00') order by b.submit_time desc limit 10", MYSQLI_ASSOC); -} -function queryGroupCurrentAC($group_id) { - return DB::selectAll("select a.problem_id as problem_id, a.submitter as submitter, a.submission_id as submission_id, b.submit_time as submit_time, d.title as problem_title, b.submit_time as submit_time, e.realname as realname from best_ac_submissions a inner join submissions b on (a.submission_id = b.id) inner join groups_users c on (a.submitter = c.username and c.group_id = $group_id) inner join problems d on (a.problem_id = d.id and d.is_hidden = 0) inner join user_info e on (a.submitter = e.username) where b.submit_time > addtime(now(), '-360:00:00') order by b.submit_time desc limit 10", MYSQLI_ASSOC); -} -function queryGroupAssignments($group_id) { - return DB::selectAll("select a.list_id as list_id, a.end_time as end_time, b.title from groups_assignments a left join lists b on a.list_id = b.id where a.group_id = $group_id order by a.end_time asc", MYSQLI_ASSOC); -} -function queryGroupActiveAssignments($group_id) { - return DB::selectAll("select a.group_id as group_id, a.list_id as list_id, a.end_time as end_time, b.title from groups_assignments a left join lists b on a.list_id = b.id where a.group_id = $group_id and a.end_time >= addtime(now(), '-168:00:00') order by a.end_time asc", MYSQLI_ASSOC); -} -function queryAssignmentByGroupListID($group_id, $list_id) { - return DB::selectFirst("select * from groups_assignments where list_id='$list_id' and group_id='$group_id'", MYSQLI_ASSOC); -} function queryBlog($id) { return DB::selectFirst("select * from blogs where id='$id'", MYSQLI_ASSOC); } diff --git a/web/app/models/UOJContest.php b/web/app/models/UOJContest.php index 3ef5a3d..05609c9 100644 --- a/web/app/models/UOJContest.php +++ b/web/app/models/UOJContest.php @@ -17,6 +17,34 @@ class UOJContest { return new UOJContest($info); } + public static function queryUpcomingContestIds(array $user = null, $limit = -1) { + return array_map(fn ($x) => $x['id'], DB::selectAll([ + "select id from contests", + "where", [ + "status" => "unfinished", + ], + "order by start_time asc, id asc", + $limit == -1 ? "" : DB::limit($limit), + ])); + } + + public static function userCanManageSomeContest(array $user = null) { + if (!$user) { + return false; + } + + if (isSuperUser($user)) { + return true; + } + + return DB::selectFirst([ + DB::lc(), "select 1 from contests_permissions", + "where", [ + 'username' => $user['username'] + ], DB::limit(1) + ]) != null; + } + public static function finalTest() { $contest = self::info(); @@ -436,6 +464,15 @@ class UOJContest { return "/contest/{$this->info['id']}{$where}"; } + public function getLink($cfg = []) { + $cfg += [ + 'where' => '', + 'class' => '', + ]; + + return HTML::tag('a', ['class' => $cfg['class'], 'href' => $this->getUri($cfg['where'])], $this->info['name']); + } + public function redirectToAnnouncementBlog() { $url = getContestBlogLink($this->info, '公告'); if ($url !== null) { diff --git a/web/app/models/UOJGroup.php b/web/app/models/UOJGroup.php new file mode 100644 index 0000000..12764e3 --- /dev/null +++ b/web/app/models/UOJGroup.php @@ -0,0 +1,131 @@ + $id] + ]); + if (!$info) { + return null; + } + return new UOJGroup($info); + } + + public static function queryGroupsOfUser(array $user = null) { + if ($user == null) { + return []; + } + + return array_map(fn ($x) => UOJGroup::query($x['group_id']), DB::selectAll([ + DB::lc(), "select group_id from groups_users", + "where", ['username' => $user['username']], + "order by group_id" + ])); + } + + public function __construct($info) { + $this->info = $info; + } + + public function userCanManage(array $user = null) { + return isSuperUser($user); + } + + public function userCanView(array $user = null, array $cfg = []) { + $cfg += ['ensure' => false]; + if ($this->info['is_hidden'] && !$this->userCanManage($user)) { + $cfg['ensure'] && UOJResponse::page404(); + return false; + } + return true; + } + + public function getUri($where = '') { + return "/group/{$this->info['id']}{$where}"; + } + + public function getLink($cfg = []) { + $cfg += [ + 'where' => '', + 'class' => '', + 'text' => $this->info['title'], + ]; + + return HTML::tag('a', [ + 'href' => $this->getUri($cfg['where']), + 'class' => $cfg['class'], + ], $cfg['text']); + } + + public function getUsernames() { + return array_map(fn ($x) => $x['username'], DB::selectAll([ + DB::lc(), "select username from groups_users", + "where", ['group_id' => $this->info['id']], + "order by username", + ])); + } + + public function getLatestGroupmatesAcceptedSubmissionIds(array $user = null, int $limit = 10) { + return array_map(fn ($x) => $x['id'], DB::selectAll([ + "select", DB::fields(["id" => "max(id)"]), + "from submissions", + "where", [ + "score" => 100, + ["submitter", "in", DB::rawtuple($this->getUsernames())], + UOJSubmission::sqlForUserCanView($user), + ], + "group by problem_id", + "order by id desc", + ])); + } + + public function getAssignmentIds($limit = -1) { + return array_map(fn ($x) => $x['list_id'], DB::selectAll([ + DB::lc(), "select list_id from groups_assignments", + "where", ['group_id' => $this->info['id']], + "order by end_time desc, list_id asc", + $limit == -1 ? "" : DB::limit($limit), + ])); + } + + public function getActiveAssignmentIds($limit = -1) { + return array_map(fn ($x) => $x['list_id'], DB::selectAll([ + DB::lc(), "select list_id from groups_assignments", + "where", [ + "group_id" => $this->info['id'], + ["end_time", ">=", DB::raw("addtime(now(), '-72:00:00')")], + ], + "order by end_time desc, list_id asc", + $limit == -1 ? "" : DB::limit($limit), + ])); + } + + public function hasUser(array $user = null) { + if ($user == null) { + return false; + } + return DB::selectFirst([ + DB::lc(), "select 1 from groups_users", + "where", [ + 'group_id' => $this->info['id'], + 'username' => $user['username'], + ], + ]) != null; + } + + public function hasAssignment(UOJList $assignment) { + return DB::selectFirst([ + DB::lc(), "select 1 from groups_assignments", + "where", [ + 'group_id' => $this->info['id'], + 'list_id' => $assignment->info['id'] + ] + ]) != null; + } +} diff --git a/web/app/models/UOJGroupAssignment.php b/web/app/models/UOJGroupAssignment.php new file mode 100644 index 0000000..88c732e --- /dev/null +++ b/web/app/models/UOJGroupAssignment.php @@ -0,0 +1,76 @@ +info, $group); + } + + public function __construct($info, UOJGroup $group) { + parent::__construct($info); + $this->group = $group; + $this->completeInfo(); + } + + public function completeInfo() { + if ($this->info['end_time_str']) { + return; + } + + if (!$this->info['end_time']) { + $this->info['end_time'] = DB::selectSingle([ + "select end_time from groups_assignments", + "where", [ + "list_id" => $this->info['id'], + "group_id" => $this->group->info['id'], + ], + ]); + } + + $this->info['end_time_str'] = $this->info['end_time'] ?: UOJTime::$time_now_str; + $this->info['end_time'] = new DateTime($this->info['end_time_str']); + } + + public function valid() { + return $this->group && $this->group->hasAssignment($this); + } + + public function getUri($where = '') { + return $this->group->getUri("/assignment/{$this->info['id']}"); + } + + public function getLink($cfg = []) { + $cfg += [ + 'class' => '', + 'text' => $this->info['title'], + 'with' => 'none', + ]; + + if ($cfg['with'] == 'sup') { + if ($this->info['end_time'] < UOJTime::$time_now) { + $cfg['text'] .= HTML::tag('sup', ["class" => "fw-normal text-danger ms-1"], 'overdue'); + } elseif ($this->info['end_time']->getTimestamp() - UOJTime::$time_now->getTimestamp() < 86400) { + $cfg['text'] .= HTML::tag('sup', ["class" => "fw-normal text-danger ms-1"], 'soon'); + } + } + + return HTML::tag('a', [ + 'href' => $this->getUri(), + 'class' => $cfg['class'], + ], $cfg['text']); + } + + public function userCanView(array $user = null, $cfg = []) { + $cfg += ['ensure' => false]; + + return parent::userCanView($user, $cfg) && $this->group->userCanView($user, $cfg); + } +} diff --git a/web/app/models/UOJList.php b/web/app/models/UOJList.php index 9255516..b2ea11a 100644 --- a/web/app/models/UOJList.php +++ b/web/app/models/UOJList.php @@ -22,6 +22,24 @@ class UOJList { $this->info = $info; } + public function getUri($where = '') { + return "/list/{$this->info['id']}{$where}"; + } + + public function getLink($cfg = []) { + $cfg += [ + 'where' => '', + 'class' => '', + 'text' => $this->info['title'], + 'with' => 'id', + ]; + + return HTML::tag('a', [ + 'href' => $this->getUri($cfg['where']), + 'class' => $cfg['class'], + ], $cfg['text']); + } + public function getProblemIDs() { return array_map(fn ($x) => $x['problem_id'], DB::selectAll([ DB::lc(), "select problem_id from lists_problems", diff --git a/web/app/models/UOJProblem.php b/web/app/models/UOJProblem.php index 2b3e73b..4ae1b08 100644 --- a/web/app/models/UOJProblem.php +++ b/web/app/models/UOJProblem.php @@ -30,11 +30,17 @@ class UOJProblem { if (!$user) { return false; } + return DB::selectFirst([ DB::lc(), "select 1 from problems_permissions", "where", [ 'username' => $user['username'] ], DB::limit(1) + ]) != null || DB::selectFirst([ + DB::lc(), "select 1 from problems", + "where", [ + "uploader" => $user['username'], + ], DB::limit(1), ]) != null; } diff --git a/web/app/models/UOJRanklist.php b/web/app/models/UOJRanklist.php index 45340a2..a626bd4 100644 --- a/web/app/models/UOJRanklist.php +++ b/web/app/models/UOJRanklist.php @@ -5,9 +5,21 @@ class UOJRanklist { $cfg += [ 'top10' => false, 'card' => false, + 'group_id' => null, + 'page_len' => 50, ]; - $conds = '1'; + $conds = []; + + if ($cfg['group_id']) { + $conds[] = [ + "username", "in", DB::rawtuple(UOJGroup::query($cfg['group_id'])->getUsernames()), + ]; + } + + if (empty($conds)) { + $conds = '1'; + } $last_user = null; $parsedown = HTML::parsedown(); @@ -55,7 +67,6 @@ class UOJRanklist { $last_user = $user; }; - $pag_config = [ 'get_row_index' => '', 'table_name' => 'user_info', @@ -68,7 +79,7 @@ class UOJRanklist { $pag_config['tail'] .= ' limit 10'; $pag_config['echo_full'] = ''; } else { - $pag_config['page_len'] = 50; + $pag_config['page_len'] = $cfg['page_len']; } $pag = new Paginator($pag_config); @@ -102,9 +113,21 @@ class UOJRanklist { public static function printTableHTML($cfg = []) { $cfg += [ 'top10' => false, + 'group_id' => null, + 'page_len' => 100, ]; - $conds = '1'; + $conds = []; + + if ($cfg['group_id']) { + $conds[] = [ + "username", "in", DB::rawtuple(UOJGroup::query($cfg['group_id'])->getUsernames()), + ]; + } + + if (empty($conds)) { + $conds = '1'; + } $header_row = ''; $header_row .= ''; @@ -157,7 +180,7 @@ class UOJRanklist { $tail .= ' limit 10'; $table_config['echo_full'] = ''; } else { - $table_config['page_len'] = 100; + $table_config['page_len'] = $cfg['page_len']; } echoLongTable($col_names, 'user_info', $conds, $tail, $header_row, $print_row, $table_config); diff --git a/web/app/models/UOJSubmission.php b/web/app/models/UOJSubmission.php index f17e394..24b42a4 100644 --- a/web/app/models/UOJSubmission.php +++ b/web/app/models/UOJSubmission.php @@ -38,7 +38,7 @@ class UOJSubmission { * Need to be consistent with the member function userCanView */ public static function sqlForUserCanView(array $user = null, UOJProblem $problem = null) { - if (isSuperUser($user)) { + if (isSuperUser($user) || isProblemManager($user)) { // MySQL can find appropriate keys to speed up the query if we write "true" in this way. return "(submissions.is_hidden = true or submissions.is_hidden = false)"; } elseif ($problem) { diff --git a/web/app/views/page-header.php b/web/app/views/page-header.php index 6fb5612..3ca855c 100644 --- a/web/app/views/page-header.php +++ b/web/app/views/page-header.php @@ -313,34 +313,6 @@ if (!isset($ShowPageHeader)) { $REQUIRE_LIB)) ?> - - - - - -
-
- 小组公告 -
-
    - - -
  • - - - - -
    purify($group_announcement) ?>
    - -
    (暂无公告)
    - -
  • - -
-
- - - diff --git a/web/app/views/sidebar.php b/web/app/views/sidebar.php index 220c837..efc7f80 100644 --- a/web/app/views/sidebar.php +++ b/web/app/views/sidebar.php @@ -1,6 +1,11 @@ + + - +
@@ -8,18 +13,11 @@
    -
  • - - - - + getLink(['class' => 'fw-bold']) ?> + info['announcement']) : ?>
    - purify($parsedown->line($group_announcement)) ?> + purify($parsedown->line($group->info['announcement'])) ?>
    @@ -35,19 +33,13 @@ UOJGroupAssignment::query($x, $group), $group->getActiveAssignmentIds())); } - usort($assignments, function ($a, $b) { - $end_time_a = DateTime::createFromFormat('Y-m-d H:i:s', $a['end_time']); - $end_time_b = DateTime::createFromFormat('Y-m-d H:i:s', $b['end_time']); - - return $end_time_b->getTimestamp() - $end_time_a->getTimestamp(); - }); - + usort($assignments, fn ($a, $b) => $b->info['end_time']->getTimestamp() - $a->info['end_time']->getTimestamp()); $assignments = array_slice($assignments, 0, 5); ?> - +
    @@ -55,17 +47,9 @@
    • - - - - - overdue - getTimestamp() - UOJTime::$time_now->getTimestamp() < 86400) : ?> - soon - - + getLink(['class' => 'fw-bold', 'with' => 'sup']) ?>
      - 截止时间: format('Y-m-d H:i') ?> + 截止时间: info['end_time']->format('Y-m-d H:i') ?>
    • @@ -79,36 +63,28 @@ - +
      近期比赛
      -
        - - - - + + + info['cur_progress'] == CONTEST_NOT_STARTED || $contest->info['cur_progress'] == CONTEST_IN_PROGRESS) : ?>
      • - - - + getLink(['class' => 'fw-bold']) ?>
        - + info['cur_progress'] == CONTEST_IN_PROGRESS) : ?> - getTimestamp() - UOJTime::$time_now->getTimestamp(); - ?> + info['start_time']->getTimestamp() - UOJTime::$time_now->getTimestamp(); ?> 86400) : ?> -
        +
        @@ -116,7 +92,7 @@
      • - +
      • diff --git a/web/app/views/user-info.php b/web/app/views/user-info.php index 2b9484a..0097828 100644 --- a/web/app/views/user-info.php +++ b/web/app/views/user-info.php @@ -5,19 +5,17 @@
        Avatar of <?= $user['username'] ?>
        - - - - + + + +

        - - style="color: blue"> - + style="color: blue"> + style="color: red"> - + > @@ -27,130 +25,136 @@

          - -
        • - - -
        • + +
        • + + +
        • - -
        • - - -
        • + +
        • + + +
        • - -
        • - - $type): ?> - , - - -
        • + +
        • + + $type) : ?> + , + + +
        • - -
        • - - - - -
        • - - -
        • - - - - -
        • - - -
        • - - - - -
        • - - -
        • -
          Codeforces 
          -
        • + + + -
          -
      - - + - -
    • - - - - -
    • + +
    • + + + + + + +
    • + + +
    • + + + + +
    • + + +
    • +
      + Codeforces + +  
      +
      + + + +
      +
      + +
    • + + +
    • + + + + +
    - - - -
    -
    -

    - -

    - -
    -
    + + + +
    +
    +

    + +

    +
      + +
    • + getLink() ?> +
    • + + + + +
    +
    +
    - +

    -
    - -

    - -

    - +
    - +
    • diff --git a/web/css/uoj-bs5.css b/web/css/uoj-bs5.css index 7836ffb..dab351d 100644 --- a/web/css/uoj-bs5.css +++ b/web/css/uoj-bs5.css @@ -452,3 +452,8 @@ form.uoj-bs4-form-compressed button { .CodeMirror-linenumber { font-size: 14px !important; } + +.uoj-back-btn { + --bs-btn-hover-bg: #d3d4d570; + --bs-btn-hover-border-color: transparent; +}