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) {
@@ -36,48 +26,48 @@ if (($group['is_hidden'] || $list['is_hidden']) && !isSuperUser($myUser)) {
作业:= UOJList::info('title') ?>
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();
}
?>
-
+
- = $group['title'] ?>
- (ID: #= $group['id'] ?>)
+ = UOJGroup::info('title') ?>
+ (ID: #= UOJGroup::info('id') ?>)
管理
-
= HTML::navListGroup($tabs_info, $cur_tab) ?>
-
">
- 返回
-
-
+ =
+ UOJGroup::cur()->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 ' ';
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 '';
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)) ?>
-
-
-
-
-
-
-
-
-
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 @@
+
+
-
+