mirror of
https://github.com/renbaoshuo/S2OJ.git
synced 2024-12-23 00:31:53 +00:00
refactor(web/group): group_v3
This commit is contained in:
parent
91fb9c8e01
commit
287889b60f
@ -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) {
|
||||
<?php endif ?>
|
||||
|
||||
<?php
|
||||
$header_row = '<tr><th>#</th><th>' . UOJLocale::get('username') . '</th>';
|
||||
$header_row = '<tr>';
|
||||
$header_row .= '<th>#</th><th>' . UOJLocale::get('username') . '</th>';
|
||||
if ($show_ip) {
|
||||
$header_row .= '<th>remote_addr</th><th>http_x_forwarded_for</th>';
|
||||
}
|
||||
|
@ -1,160 +1,157 @@
|
||||
<?php
|
||||
requireLib('bootstrap5');
|
||||
requirePHPLib('form');
|
||||
requirePHPLib('judger');
|
||||
requirePHPLib('data');
|
||||
requireLib('bootstrap5');
|
||||
requirePHPLib('form');
|
||||
requirePHPLib('judger');
|
||||
requirePHPLib('data');
|
||||
|
||||
if (!Auth::check()) {
|
||||
redirectToLogin();
|
||||
}
|
||||
Auth::check() || redirectToLogin();
|
||||
UOJGroup::init(UOJRequest::get('id')) || UOJResponse::page404();
|
||||
UOJGroup::cur()->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();
|
||||
}
|
||||
?>
|
||||
|
||||
<?php echoUOJPageHeader(UOJLocale::get('groups')) ?>
|
||||
<?php echoUOJPageHeader('小组:' . UOJGroup::info('title')) ?>
|
||||
|
||||
<div class="row">
|
||||
<!-- left col -->
|
||||
<div class="col-lg-9">
|
||||
<!-- left col -->
|
||||
<div class="col-lg-9">
|
||||
<!-- title -->
|
||||
<div class="d-flex justify-content-between">
|
||||
<h1>
|
||||
<?= UOJGroup::info('title') ?>
|
||||
<span class="fs-5">(ID: #<?= UOJGroup::info('id') ?>)</span>
|
||||
<?php if (UOJGroup::info('is_hidden')) : ?>
|
||||
<span class="badge text-bg-danger fs-6">
|
||||
<i class="bi bi-eye-slash-fill"></i>
|
||||
<?= UOJLocale::get('hidden') ?>
|
||||
</span>
|
||||
<?php endif ?>
|
||||
</h1>
|
||||
|
||||
<!-- title -->
|
||||
<div class="d-flex justify-content-between">
|
||||
<h1>
|
||||
<?php if ($group['is_hidden']): ?>
|
||||
<span class="fs-5 text-danger">[隐藏]</span>
|
||||
<?php endif ?>
|
||||
<?= $group['title'] ?>
|
||||
<span class="fs-5">(ID: #<?= $group['id'] ?>)</span>
|
||||
</h1>
|
||||
|
||||
<?php if (isSuperUser($myUser)): ?>
|
||||
<div class="text-end">
|
||||
<a class="btn btn-primary" href="/group/<?= $group['id'] ?>/manage" role="button">
|
||||
<?= UOJLocale::get('problems::manage') ?>
|
||||
</a>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
<!-- end title -->
|
||||
|
||||
<!-- main content -->
|
||||
<div class="card mb-3">
|
||||
<div class="card-body">
|
||||
<h2 class="h3">
|
||||
<?= UOJLocale::get('group announcement') ?>
|
||||
</h2>
|
||||
<?php if ($group['announcement']): ?>
|
||||
<div class="text-break">
|
||||
<?= HTML::purifier_inline()->purify(HTML::parsedown()->line($group['announcement'])) ?>
|
||||
<?php if (UOJGroup::cur()->userCanManage(Auth::user())) : ?>
|
||||
<div class="text-end">
|
||||
<?=
|
||||
UOJGroup::cur()->getLink([
|
||||
'where' => '/manage',
|
||||
'class' => 'btn btn-primary',
|
||||
'text' => UOJLocale::get('problems::manage'),
|
||||
]);
|
||||
?>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="text-muted">
|
||||
<?= UOJLocale::get('none') ?>
|
||||
<!-- end title -->
|
||||
|
||||
<!-- main content -->
|
||||
<div class="card mb-3">
|
||||
<div class="card-body">
|
||||
<h2 class="h3">
|
||||
<?= UOJLocale::get('group announcement') ?>
|
||||
</h2>
|
||||
<?php if (UOJGroup::info('announcement')) : ?>
|
||||
<div class="text-break">
|
||||
<?= HTML::purifier_inline()->purify(HTML::parsedown()->line(UOJGroup::info('announcement'))) ?>
|
||||
</div>
|
||||
<?php else : ?>
|
||||
<div class="text-muted">
|
||||
<?= UOJLocale::get('none') ?>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
|
||||
<div class="card mb-3">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title h3">
|
||||
<?= UOJLocale::get('news') ?>
|
||||
</h2>
|
||||
<ul class="mb-0">
|
||||
<?php foreach (UOJGroup::cur()->getLatestGroupmatesAcceptedSubmissionIds(Auth::user()) as $id) : ?>
|
||||
<?php
|
||||
$submission = UOJSubmission::query($id);
|
||||
$submission->setProblem();
|
||||
$user = UOJUser::query($submission->info['submitter']);
|
||||
?>
|
||||
<li>
|
||||
<?= UOJUser::getLink($user) ?>
|
||||
解决了问题
|
||||
<?= $submission->problem->getLink(['with' => 'id']) ?>
|
||||
(<time><?= $submission->info['submit_time'] ?></time>)
|
||||
</li>
|
||||
<?php endforeach ?>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card card-default mb-3">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title h3">
|
||||
<?= UOJLocale::get('assignments') ?>
|
||||
</h2>
|
||||
<?php
|
||||
echoLongTable(
|
||||
['*'],
|
||||
[
|
||||
"groups_assignments",
|
||||
"left join lists",
|
||||
"on", [
|
||||
"lists.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',
|
||||
<<<EOD
|
||||
<tr>
|
||||
<th style="width:3em" class="text-center">ID</th>
|
||||
<th style="width:12em">标题</th>
|
||||
<th style="width:4em">状态</th>
|
||||
<th style="width:8em">结束时间</th>
|
||||
</tr>
|
||||
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'],
|
||||
],
|
||||
);
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card card-default mb-3">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title h3">
|
||||
<?= UOJLocale::get('top solver') ?>
|
||||
</h2>
|
||||
<?php UOJRanklist::printHTML([
|
||||
'page_len' => 25,
|
||||
'group_id' => UOJGroup::info('id'),
|
||||
]) ?>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end left col -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mb-3">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title h3">
|
||||
<?= UOJLocale::get('news') ?>
|
||||
</h5>
|
||||
<ul class="mb-0">
|
||||
<?php
|
||||
$current_ac = queryGroupCurrentAC($group['id']);
|
||||
foreach ($current_ac as $ac) {
|
||||
echo '<li>';
|
||||
echo getUserLink($ac['submitter']);
|
||||
echo ' 解决了问题 ';
|
||||
echo '<a class="text-decoration-none" href="/problem/', $ac['problem_id'], '">', $ac['problem_title'], '</a> ';
|
||||
echo '<time class="time">(', $ac['submit_time'], ')</time>';
|
||||
echo '</li>';
|
||||
}
|
||||
if (count($current_ac) == 0) {
|
||||
echo '暂无最新动态';
|
||||
}
|
||||
?>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card card-default mb-3">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title h3">
|
||||
<?= UOJLocale::get('assignments') ?>
|
||||
</h5>
|
||||
<?php
|
||||
echoLongTable(
|
||||
[
|
||||
'groups_assignments.list_id as list_id',
|
||||
'lists.title as title',
|
||||
'groups_assignments.end_time as end_time'
|
||||
],
|
||||
'groups_assignments left join lists on lists.id = groups_assignments.list_id',
|
||||
"groups_assignments.group_id = {$group['id']} and groups_assignments.end_time > addtime(now(), '-168:00:00')",
|
||||
'order by end_time desc, list_id desc',
|
||||
<<<EOD
|
||||
<tr>
|
||||
<th style="width:3em" class="text-center">ID</th>
|
||||
<th style="width:12em">标题</th>
|
||||
<th style="width:4em">状态</th>
|
||||
<th style="width:8em">结束时间</th>
|
||||
</tr>
|
||||
EOD,
|
||||
function($row) use ($group) {
|
||||
$end_time = DateTime::createFromFormat('Y-m-d H:i:s', $row['end_time']);
|
||||
|
||||
echo '<tr>';
|
||||
echo '<td class="text-center">', $row['list_id'], '</td>';
|
||||
echo '<td>', '<a class="text-decoration-none" href="/group/', $group['id'], '/assignment/', $row['list_id'],'">', HTML::escape($row['title']), '</a>', '</td>';
|
||||
if ($end_time < UOJTime::$time_now) {
|
||||
echo '<td class="text-danger">已结束</td>';
|
||||
} else {
|
||||
echo '<td class="text-success">进行中</td>';
|
||||
}
|
||||
echo '<td>', $end_time->format('Y-m-d H:i:s'), '</td>';
|
||||
echo '</tr>';
|
||||
},
|
||||
[
|
||||
'echo_full' => true,
|
||||
'div_classes' => ['table-responsive'],
|
||||
'table_classes' => ['table', 'align-middle', 'mb-0'],
|
||||
]
|
||||
);
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card card-default mb-3">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title h3">
|
||||
<?= UOJLocale::get('top solver') ?>
|
||||
</h5>
|
||||
<?php echoRanklist([
|
||||
'page_len' => 50,
|
||||
'group_id' => $group_id,
|
||||
'by_accepted' => true,
|
||||
'div_classes' => ['table-responsive', 'mb-3'],
|
||||
'table_classes' => ['table', 'text-center', 'mb-0'],
|
||||
]) ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- end left col -->
|
||||
</div>
|
||||
|
||||
<!-- right col -->
|
||||
<aside class="col-lg-3 mt-3 mt-lg-0">
|
||||
<?php uojIncludeView('sidebar'); ?>
|
||||
</aside>
|
||||
<!-- right col -->
|
||||
<aside class="col-lg-3 mt-3 mt-lg-0">
|
||||
<?php uojIncludeView('sidebar') ?>
|
||||
</aside>
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -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]);
|
||||
?>
|
||||
|
||||
<?php echoUOJPageHeader(UOJLocale::get('assignments')) ?>
|
||||
<?php echoUOJPageHeader(UOJLocale::get('assignments') . ' ' . UOJGroupAssignment::info('title')) ?>
|
||||
|
||||
<div class="row">
|
||||
<!-- left col -->
|
||||
@ -36,48 +26,48 @@ if (($group['is_hidden'] || $list['is_hidden']) && !isSuperUser($myUser)) {
|
||||
<small class="fs-4">作业:</small><?= UOJList::info('title') ?>
|
||||
</h1>
|
||||
<ul class="mt-3">
|
||||
<li>对应题单:<a class="text-decoration-none" href="<?= HTML::url('/list/' . UOJList::info('id')) ?>">#<?= UOJList::info('id') ?></a></li>
|
||||
<li>所属小组:<a class="text-decoration-none" href="<?= HTML::url('/group/' . $group['id']) ?>"><?= $group['title'] ?></a></li>
|
||||
<li>结束时间:<?= $assignment['end_time'] ?></li>
|
||||
<li>对应题单:<a href="<?= HTML::url('/list/' . UOJGroupAssignment::info('id')) ?>">#<?= UOJGroupAssignment::info('id') ?></a></li>
|
||||
<li>所属小组:<?= UOJGroup::cur()->getLink() ?>
|
||||
<li>结束时间:<?= UOJGroupAssignment::info('end_time_str') ?></li>
|
||||
</ul>
|
||||
|
||||
<?php
|
||||
$problems = UOJList::cur()->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 += '<tr class="table-success">';
|
||||
} else {
|
||||
col_tr += '<tr>';
|
||||
}
|
||||
|
||||
col_tr += '<td>' + getUserLink(row['user']['username'], row['user']['realname']) + '</td>';
|
||||
col_tr += '<td>' + getUserLink(row[1][0], row[1][1]) + '</td>';
|
||||
col_tr += '<td>' +
|
||||
'<span class="uoj-score" data-max="' + (problems.length * 100) + '" style="color:' + getColOfScore(row['total_score'] / problems.length) + '">' + row['total_score'] + '</span>' +
|
||||
'<span class="uoj-score" data-max="' + (problems.length * 100) + '" style="color:' + getColOfScore(row[0] / problems.length) + '">' + row[0] + '</span>' +
|
||||
'</td>';
|
||||
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 += '<td class="table-success">';
|
||||
} else {
|
||||
col_tr += '<td>';
|
||||
}
|
||||
col_tr += '<a class="text-decoration-none uoj-score" href="/submission/' + col['submission_id'] + '" style="color:' + getColOfScore(col['score']) + '">' + col['score'] + '</a>';
|
||||
col_tr += '<a class="text-decoration-none uoj-score" href="/submission/' + col[0] + '" style="color:' + getColOfScore(col[1]) + '">' + col[1] + '</a>';
|
||||
col_tr += '</td>';
|
||||
} else {
|
||||
col_tr += '<td></td>';
|
||||
|
@ -1,45 +1,33 @@
|
||||
<?php
|
||||
if (!Auth::check()) {
|
||||
redirectToLogin();
|
||||
}
|
||||
|
||||
requireLib('bootstrap5');
|
||||
requirePHPLib('form');
|
||||
requirePHPLib('judger');
|
||||
requirePHPLib('data');
|
||||
|
||||
$group_id = $_GET['id'];
|
||||
if (!validateUInt($group_id) || !($group = queryGroup($group_id))) {
|
||||
become404Page();
|
||||
}
|
||||
Auth::check() || redirectToLogin();
|
||||
UOJGroup::init(UOJRequest::get('id')) || UOJResponse::page404();
|
||||
UOJGroup::cur()->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();
|
||||
}
|
||||
?>
|
||||
<?php echoUOJPageHeader('管理 - ' . $group['title']); ?>
|
||||
<?php echoUOJPageHeader('管理 - ' . UOJGroup::info('title')); ?>
|
||||
|
||||
<h1 class="d-block d-md-inline-block">
|
||||
<?= $group['title'] ?>
|
||||
<small class="fs-5">(ID: #<?= $group['id'] ?>)</small>
|
||||
<?= UOJGroup::info('title') ?>
|
||||
<small class="fs-5">(ID: #<?= UOJGroup::info('id') ?>)</small>
|
||||
管理
|
||||
</h1>
|
||||
|
||||
<div class="row mt-4">
|
||||
<!-- left col -->
|
||||
<div class="col-md-3">
|
||||
|
||||
<?= HTML::navListGroup($tabs_info, $cur_tab) ?>
|
||||
|
||||
<a class="btn btn-light d-block mt-2 w-100 text-start text-primary" style="--bs-btn-hover-bg: #d3d4d570; --bs-btn-hover-border-color: transparent;" href="<?= HTML::url("/group/{$group['id']}") ?>">
|
||||
<i class="bi bi-arrow-left"></i> 返回
|
||||
</a>
|
||||
|
||||
<?=
|
||||
UOJGroup::cur()->getLink([
|
||||
'class' => 'btn btn-light d-block mt-2 w-100 text-start text-primary uoj-back-btn',
|
||||
'text' => '<i class="bi bi-arrow-left"></i> 返回',
|
||||
]);
|
||||
?>
|
||||
</div>
|
||||
<!-- end left col -->
|
||||
|
||||
@ -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',
|
||||
<<<EOD
|
||||
<tr>
|
||||
<th style="width:4em" class="text-center">题单 ID</th>
|
||||
<th style="width:12em">标题</th>
|
||||
<th style="width:4em">状态</th>
|
||||
<th style="width:8em">结束时间</th>
|
||||
<th style="width:8em">操作</th>
|
||||
</tr>
|
||||
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']);
|
||||
<tr>
|
||||
<th style="width:4em" class="text-center">题单 ID</th>
|
||||
<th style="width:12em">标题</th>
|
||||
<th style="width:4em">状态</th>
|
||||
<th style="width:8em">结束时间</th>
|
||||
<th style="width:8em">操作</th>
|
||||
</tr>
|
||||
EOD,
|
||||
function ($row) use ($hidden_time) {
|
||||
$assignment = UOJGroupAssignment::query($row['list_id']);
|
||||
|
||||
echo '<tr>';
|
||||
echo '<td class="text-center">', $list->info['id'], '</td>';
|
||||
echo '<td>';
|
||||
echo '<a class="text-decoration-none" href="/group/', $group['id'], '/assignment/', $list->info['id'], '">', $list->info['title'], '</a>';
|
||||
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 ' <span class="badge text-bg-danger"><i class="bi bi-eye-slash-fill"></i> ', UOJLocale::get('hidden'), '</span> ';
|
||||
}
|
||||
echo '</td>';
|
||||
if ($end_time < $hidden_time) {
|
||||
echo '<td class="text-secondary">已隐藏</td>';
|
||||
} elseif ($end_time < UOJTime::$time_now) {
|
||||
echo '<td class="text-danger">已结束</td>';
|
||||
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 '<td class="text-success">进行中</td>';
|
||||
echo HTML::tag('td', ['class' => 'text-success'], '进行中');
|
||||
}
|
||||
echo '<td>', $end_time->format('Y-m-d H:i:s'), '</td>';
|
||||
echo HTML::tag('td', [], $assignment->info['end_time_str']);
|
||||
echo '<td>';
|
||||
echo ' <a class="text-decoration-none d-inline-block align-middle" href="/list/', $list->info['id'], '/manage">编辑</a> ';
|
||||
echo ' <form class="d-inline-block" method="POST" onsubmit=\'return confirm("你真的要移除这份作业(题单 #', $list->info['id'], ')吗?移除作业不会删除题单。")\'>'
|
||||
. '<input type="hidden" name="_token" value="' . crsf_token() . '">'
|
||||
. '<input type="hidden" name="list_id" value="' . $list->info['id'] . '">'
|
||||
echo ' <a class="text-decoration-none d-inline-block align-middle" href="/list/', $assignment->info['id'], '/manage">编辑</a> ';
|
||||
echo ' <form class="d-inline-block" method="POST" onsubmit=\'return confirm("你真的要移除这份作业(题单 #', $assignment->info['id'], ')吗?移除作业不会删除题单。")\'>'
|
||||
. HTML::hiddenToken()
|
||||
. '<input type="hidden" name="list_id" value="' . $assignment->info['id'] . '">'
|
||||
. '<button class="btn btn-link text-danger text-decoration-none p-0" type="submit" name="submit-remove_assignment" value="remove_assignment">移除</button>'
|
||||
. '</form>';
|
||||
echo '</td>';
|
||||
@ -447,17 +456,17 @@ EOD,
|
||||
echoLongTable(
|
||||
['*'],
|
||||
'groups_users',
|
||||
"group_id = {$group['id']}",
|
||||
["group_id" => UOJGroup::info('id')],
|
||||
'order by username asc',
|
||||
<<<EOD
|
||||
<tr>
|
||||
<th>用户名</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
EOD,
|
||||
function ($row) use ($group) {
|
||||
echo '<tr>';
|
||||
echo '<td>', getUserLink($row['username']), '</td>';
|
||||
<tr>
|
||||
<th>用户名</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
EOD,
|
||||
function ($row) {
|
||||
echo HTML::tag_begin('tr');
|
||||
echo HTML::tag('td', [], UOJUser::getLink($row['username']));
|
||||
echo '<td>';
|
||||
echo '<form class="d-inline-block" method="POST" onsubmit=\'return confirm("你真的要从小组中移除这个用户吗?")\'>'
|
||||
. '<input type="hidden" name="_token" value="' . crsf_token() . '">'
|
||||
@ -465,7 +474,7 @@ EOD,
|
||||
. '<button class="btn btn-link text-danger text-decoration-none p-0" type="submit" name="submit-remove_user" value="remove_user">移除</button>'
|
||||
. '</form>';
|
||||
echo '</td>';
|
||||
echo '</tr>';
|
||||
echo HTML::tag_end('tr');
|
||||
},
|
||||
[
|
||||
'page_len' => 20,
|
||||
|
@ -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 .= ' <span class="badge text-bg-danger"><i class="bi bi-eye-slash-fill"></i> ' . UOJLocale::get('hidden') . '</span> ';
|
||||
}
|
||||
foreach ($list->queryTags() as $tag) {
|
||||
$html .= ' <a class="uoj-list-tag"><span class="badge text-bg-secondary">' . $tag['tag'] . '</span></a> ';
|
||||
}
|
||||
$html .= HTML::tag('td', [], $accepted);
|
||||
$html .= HTML::tag('td', [], max(0, $accepted));
|
||||
$html .= HTML::tag('td', [], count($problems));
|
||||
$html .= HTML::tag_end('td');
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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) {
|
||||
|
131
web/app/models/UOJGroup.php
Normal file
131
web/app/models/UOJGroup.php
Normal file
@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
class UOJGroup {
|
||||
use UOJDataTrait;
|
||||
|
||||
public static function query($id) {
|
||||
if (!isset($id) || !validateUInt($id)) {
|
||||
return null;
|
||||
}
|
||||
$info = DB::selectFirst([
|
||||
"select * from `groups`",
|
||||
"where", ["id" => $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;
|
||||
}
|
||||
}
|
76
web/app/models/UOJGroupAssignment.php
Normal file
76
web/app/models/UOJGroupAssignment.php
Normal file
@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
class UOJGroupAssignment extends UOJList {
|
||||
public $group = null;
|
||||
|
||||
public static function query($id, UOJGroup $group = null) {
|
||||
$list = parent::query($id);
|
||||
if ($list === null) {
|
||||
return $list;
|
||||
}
|
||||
if ($group === null) {
|
||||
$group = UOJGroup::cur();
|
||||
}
|
||||
return new UOJGroupAssignment($list->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);
|
||||
}
|
||||
}
|
@ -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",
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 .= '<tr>';
|
||||
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -313,34 +313,6 @@ if (!isset($ShowPageHeader)) {
|
||||
<?php endif ?>
|
||||
|
||||
<?php uojIncludeView($PageNav, array('REQUIRE_LIB' => $REQUIRE_LIB)) ?>
|
||||
|
||||
<?php if (!isset($REQUIRE_LIB['bootstrap5'])) : ?>
|
||||
<?php if (Auth::check()) : ?>
|
||||
<?php $groups = queryGroupsOfUser(Auth::id()); ?>
|
||||
<?php if (count($groups)) : ?>
|
||||
<div class="card card-default mb-2" id="group-user-announcements">
|
||||
<div class="card-header">
|
||||
小组公告
|
||||
</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
<?php foreach ($groups as $group) : ?>
|
||||
<?php $group_announcement = DB::selectSingle("select announcement from `groups` where id = {$group['id']}"); ?>
|
||||
<li class="list-group-item">
|
||||
<a href="<?= HTML::url('/group/' . $group['id']) ?>">
|
||||
<b><?= $group['title'] ?></b>
|
||||
</a>
|
||||
<?php if ($group_announcement) : ?>
|
||||
<div><?= HTML::purifier_inline()->purify($group_announcement) ?></div>
|
||||
<?php else : ?>
|
||||
<div>(暂无公告)</div>
|
||||
<?php endif ?>
|
||||
</li>
|
||||
<?php endforeach ?>
|
||||
</ul>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
<?php endif ?>
|
||||
<?php endif ?>
|
||||
<?php endif ?>
|
||||
|
||||
|
||||
|
@ -1,6 +1,11 @@
|
||||
<?php
|
||||
$purifier = HTML::purifier_inline();
|
||||
$parsedown = HTML::parsedown();
|
||||
?>
|
||||
|
||||
<?php if (Auth::check()) : ?>
|
||||
<?php if (!isset($groups_hidden)) : ?>
|
||||
<?php $groups = queryGroupsOfUser(Auth::id()); ?>
|
||||
<?php $groups = UOJGroup::queryGroupsOfUser(Auth::user()); ?>
|
||||
<?php if (!empty($groups)) : ?>
|
||||
<div class="card card-default mb-2" id="group-user-announcements">
|
||||
<div class="card-header fw-bold bg-transparent">
|
||||
@ -8,18 +13,11 @@
|
||||
</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
<?php foreach ($groups as $group) : ?>
|
||||
<?php
|
||||
$group_announcement = DB::selectSingle("select announcement from `groups` where id = {$group['id']}");
|
||||
$purifier = HTML::purifier_inline();
|
||||
$parsedown = HTML::parsedown();
|
||||
?>
|
||||
<li class="list-group-item">
|
||||
<a class="fw-bold text-decoration-none" href="<?= HTML::url('/group/' . $group['id']) ?>">
|
||||
<?= $group['title'] ?>
|
||||
</a>
|
||||
<?php if ($group_announcement) : ?>
|
||||
<?= $group->getLink(['class' => 'fw-bold']) ?>
|
||||
<?php if ($group->info['announcement']) : ?>
|
||||
<div class="text-break">
|
||||
<?= $purifier->purify($parsedown->line($group_announcement)) ?>
|
||||
<?= $purifier->purify($parsedown->line($group->info['announcement'])) ?>
|
||||
</div>
|
||||
<?php else : ?>
|
||||
<div class="text-muted">
|
||||
@ -35,19 +33,13 @@
|
||||
<?php
|
||||
$assignments = [];
|
||||
foreach ($groups as $group) {
|
||||
$assignments = array_merge($assignments, queryGroupActiveAssignments($group['id']));
|
||||
$assignments = array_merge($assignments, array_map(fn ($x) => 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);
|
||||
?>
|
||||
<?php if (count($assignments)) : ?>
|
||||
<?php if (!empty($assignments)) : ?>
|
||||
<div class="card card-default mb-2" id="group-assignments">
|
||||
<div class="card-header fw-bold bg-transparent">
|
||||
<?= UOJLocale::get('assignments') ?>
|
||||
@ -55,17 +47,9 @@
|
||||
<ul class="list-group list-group-flush">
|
||||
<?php foreach ($assignments as $assignment) : ?>
|
||||
<li class="list-group-item">
|
||||
<?php $end_time = DateTime::createFromFormat('Y-m-d H:i:s', $assignment['end_time']); ?>
|
||||
<a href="<?= HTML::url('/group/' . $assignment['group_id'] . '/assignment/' . $assignment['list_id']) ?>" class="fw-bold text-decoration-none">
|
||||
<?= $assignment['title'] ?>
|
||||
<?php if ($end_time < UOJTime::$time_now) : ?>
|
||||
<sup class="fw-normal text-danger">overdue</sup>
|
||||
<?php elseif ($end_time->getTimestamp() - UOJTime::$time_now->getTimestamp() < 86400) : ?>
|
||||
<sup class="fw-normal text-danger">soon</sup>
|
||||
<?php endif ?>
|
||||
</a>
|
||||
<?= $assignment->getLink(['class' => 'fw-bold', 'with' => 'sup']) ?>
|
||||
<div class="text-end small text-muted">
|
||||
截止时间: <?= $end_time->format('Y-m-d H:i') ?>
|
||||
截止时间: <?= $assignment->info['end_time']->format('Y-m-d H:i') ?>
|
||||
</div>
|
||||
</li>
|
||||
<?php endforeach ?>
|
||||
@ -79,36 +63,28 @@
|
||||
|
||||
<?php if (Auth::check()) : ?>
|
||||
<?php if (!isset($upcoming_contests_hidden)) : ?>
|
||||
<?php
|
||||
$upcoming_contests = DB::selectAll("SELECT * FROM contests WHERE status = 'unfinished' ORDER BY start_time ASC, id ASC LIMIT 7");
|
||||
?>
|
||||
<?php $upcoming_contests = UOJContest::queryUpcomingContestIds(Auth::user(), 5); ?>
|
||||
<div class="card card-default mb-2" id="group-user-announcements">
|
||||
<div class="card-header fw-bold bg-transparent">
|
||||
近期比赛
|
||||
</div>
|
||||
<?php $count = 0; ?>
|
||||
<ul class="list-group list-group-flush">
|
||||
<?php foreach ($upcoming_contests as $contest) : ?>
|
||||
<?php genMoreContestInfo($contest) ?>
|
||||
<?php if ($contest['cur_progress'] == CONTEST_NOT_STARTED || $contest['cur_progress'] == CONTEST_IN_PROGRESS) : ?>
|
||||
<?php $count++; ?>
|
||||
<?php foreach ($upcoming_contests as $id) : ?>
|
||||
<?php $contest = UOJContest::query($id); ?>
|
||||
<?php if ($contest->info['cur_progress'] == CONTEST_NOT_STARTED || $contest->info['cur_progress'] == CONTEST_IN_PROGRESS) : ?>
|
||||
<li class="list-group-item text-center">
|
||||
<a class="fw-bold text-decoration-none" href="<?= HTML::url('/contest/' . $contest['id']) ?>">
|
||||
<?= $contest['name'] ?>
|
||||
</a>
|
||||
<?= $contest->getLink(['class' => 'fw-bold']) ?>
|
||||
<div class="small">
|
||||
<?php if ($contest['cur_progress'] == CONTEST_IN_PROGRESS) : ?>
|
||||
<?php if ($contest->info['cur_progress'] == CONTEST_IN_PROGRESS) : ?>
|
||||
<?= UOJLocale::get('contests::in progress') ?>
|
||||
<?php else : ?>
|
||||
<?php
|
||||
$rest_seconds = $contest['start_time']->getTimestamp() - UOJTime::$time_now->getTimestamp();
|
||||
?>
|
||||
<?php $rest_seconds = $contest->info['start_time']->getTimestamp() - UOJTime::$time_now->getTimestamp(); ?>
|
||||
<?php if ($rest_seconds > 86400) : ?>
|
||||
<?= UOJLocale::get('contests::will start in x days', ceil($rest_seconds / 86400)) ?>
|
||||
<?php else : ?>
|
||||
<div id="contest-<?= $contest['id'] ?>-countdown"></div>
|
||||
<div id="contest-<?= $contest->info['id'] ?>-countdown"></div>
|
||||
<script>
|
||||
$('#contest-<?= $contest['id'] ?>-countdown').countdown(<?= $rest_seconds ?>, function() {}, 'inherit', false);
|
||||
$('#contest-<?= $contest->info['id'] ?>-countdown').countdown(<?= $rest_seconds ?>, function() {}, 'inherit', false);
|
||||
</script>
|
||||
<?php endif ?>
|
||||
<?php endif ?>
|
||||
@ -116,7 +92,7 @@
|
||||
</li>
|
||||
<?php endif ?>
|
||||
<?php endforeach ?>
|
||||
<?php if ($count == 0) : ?>
|
||||
<?php if (empty($upcoming_contests)) : ?>
|
||||
<li class="list-group-item text-center">
|
||||
<?= UOJLocale::get('none') ?>
|
||||
</li>
|
||||
|
@ -5,19 +5,17 @@
|
||||
<div class="card">
|
||||
<img class="card-img-top" alt="Avatar of <?= $user['username'] ?>" src="<?= HTML::avatar_addr($user, 512) ?>" />
|
||||
<div class="card-body">
|
||||
<?php if ($user['usergroup'] == 'S'): ?>
|
||||
<span class="badge bg-secondary">
|
||||
<?= UOJLocale::get('user::admin') ?>
|
||||
</span>
|
||||
<?php if ($user['usergroup'] == 'S') : ?>
|
||||
<span class="badge bg-secondary">
|
||||
<?= UOJLocale::get('user::admin') ?>
|
||||
</span>
|
||||
<?php endif ?>
|
||||
<h3>
|
||||
<?= $user['username'] ?>
|
||||
<span class="fs-6 align-middle"
|
||||
<?php if ($user['sex'] == 'M'): ?>
|
||||
style="color: blue"><i class="bi bi-gender-male"></i>
|
||||
<?php elseif ($user['sex'] == 'F'): ?>
|
||||
<span class="fs-6 align-middle" <?php if ($user['sex'] == 'M') : ?> style="color: blue"><i class="bi bi-gender-male"></i>
|
||||
<?php elseif ($user['sex'] == 'F') : ?>
|
||||
style="color: red"><i class="bi bi-gender-female"></i>
|
||||
<?php else: ?>
|
||||
<?php else : ?>
|
||||
>
|
||||
<?php endif ?>
|
||||
</span>
|
||||
@ -27,130 +25,136 @@
|
||||
</div>
|
||||
</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
<?php if ($user['realname']): ?>
|
||||
<li class="list-group-item">
|
||||
<i class="bi bi-person-fill me-1"></i>
|
||||
<?= $user['realname'] ?>
|
||||
</li>
|
||||
<?php if ($user['realname']) : ?>
|
||||
<li class="list-group-item">
|
||||
<i class="bi bi-person-fill me-1"></i>
|
||||
<?= $user['realname'] ?>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
<?php if ($user['school']): ?>
|
||||
<li class="list-group-item">
|
||||
<i class="bi bi-person-badge-fill me-1"></i>
|
||||
<?= $user['school'] ?>
|
||||
</li>
|
||||
<?php if ($user['school']) : ?>
|
||||
<li class="list-group-item">
|
||||
<i class="bi bi-person-badge-fill me-1"></i>
|
||||
<?= $user['school'] ?>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
<?php if ($user['usertype']): ?>
|
||||
<li class="list-group-item">
|
||||
<i class="bi bi-key-fill me-1"></i>
|
||||
<?php foreach (explode(',', $user['usertype']) as $idx => $type): ?>
|
||||
<?php if ($idx): ?>,<?php endif ?>
|
||||
<span><?= UOJLocale::get('user::' . str_replace('_', ' ', $type)) ?: HTML::escape($type) ?></span>
|
||||
<?php endforeach ?>
|
||||
</li>
|
||||
<?php if ($user['usertype']) : ?>
|
||||
<li class="list-group-item">
|
||||
<i class="bi bi-key-fill me-1"></i>
|
||||
<?php foreach (explode(',', $user['usertype']) as $idx => $type) : ?>
|
||||
<?php if ($idx) : ?>,<?php endif ?>
|
||||
<span><?= UOJLocale::get('user::' . str_replace('_', ' ', $type)) ?: HTML::escape($type) ?></span>
|
||||
<?php endforeach ?>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
<?php if ($user['email']): ?>
|
||||
<li class="list-group-item">
|
||||
<i class="bi bi-envelope-fill me-1"></i>
|
||||
<a class="text-decoration-none text-body" href="mailto:<?= HTML::escape($user['email']) ?>">
|
||||
<?= HTML::escape($user['email']) ?>
|
||||
</a>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
<?php if ($user['qq']): ?>
|
||||
<li class="list-group-item">
|
||||
<i class="align-text-bottom me-1"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" width="16" height="16"><path d="M433.754 420.445c-11.526 1.393-44.86-52.741-44.86-52.741 0 31.345-16.136 72.247-51.051 101.786 16.842 5.192 54.843 19.167 45.803 34.421-7.316 12.343-125.51 7.881-159.632 4.037-34.122 3.844-152.316 8.306-159.632-4.037-9.045-15.25 28.918-29.214 45.783-34.415-34.92-29.539-51.059-70.445-51.059-101.792 0 0-33.334 54.134-44.859 52.741-5.37-.65-12.424-29.644 9.347-99.704 10.261-33.024 21.995-60.478 40.144-105.779C60.683 98.063 108.982.006 224 0c113.737.006 163.156 96.133 160.264 214.963 18.118 45.223 29.912 72.85 40.144 105.778 21.768 70.06 14.716 99.053 9.346 99.704z" fill="currentColor"/></svg></i>
|
||||
<a class="text-decoration-none text-body" href="http://wpa.qq.com/msgrd?v=3&uin=<?= HTML::escape($user['qq']) ?>&site=qq&menu=yes" target="_blank">
|
||||
<?= HTML::escape($user['qq']) ?>
|
||||
</a>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
<?php if ($extra['social']['github']): ?>
|
||||
<li class="list-group-item">
|
||||
<i class="bi bi-github me-1"></i>
|
||||
<a class="text-decoration-none text-body" href="https://github.com/<?= HTML::escape($extra['social']['github']) ?>" target="_blank">
|
||||
<?= HTML::escape($extra['social']['github']) ?>
|
||||
</a>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
<?php if ($extra['social']['codeforces']): ?>
|
||||
<li class="list-group-item d-flex align-items-center">
|
||||
<div class="flex-shrink-0"><i class="align-text-bottom me-1"><svg xmlns="http://www.w3.org/2000/svg" role="img" viewBox="0 0 24 24" width="16" height="16"><title>Codeforces</title><path d="M4.5 7.5C5.328 7.5 6 8.172 6 9v10.5c0 .828-.672 1.5-1.5 1.5h-3C.673 21 0 20.328 0 19.5V9c0-.828.673-1.5 1.5-1.5h3zm9-4.5c.828 0 1.5.672 1.5 1.5v15c0 .828-.672 1.5-1.5 1.5h-3c-.827 0-1.5-.672-1.5-1.5v-15c0-.828.673-1.5 1.5-1.5h3zm9 7.5c.828 0 1.5.672 1.5 1.5v7.5c0 .828-.672 1.5-1.5 1.5h-3c-.828 0-1.5-.672-1.5-1.5V12c0-.828.672-1.5 1.5-1.5h3z" fill="currentColor"/></svg></i> </div>
|
||||
<div>
|
||||
<a id="codeforces-profile-link" class="text-decoration-none" href="https://codeforces.com/profile/<?= $extra['social']['codeforces'] ?>" target="_blank" style="color: rgba(var(--bs-body-color-rgb), var(--bs-text-opacity)) !important;">
|
||||
<?= $extra['social']['codeforces'] ?>
|
||||
<?php if ($user['email']) : ?>
|
||||
<li class="list-group-item">
|
||||
<i class="bi bi-envelope-fill me-1"></i>
|
||||
<a class="text-decoration-none text-body" href="mailto:<?= HTML::escape($user['email']) ?>">
|
||||
<?= HTML::escape($user['email']) ?>
|
||||
</a>
|
||||
<div id="codeforces-rating" style="font-family: verdana, arial, sans-serif; line-height: 1.2em; text-transform: capitalize;"></div>
|
||||
</div>
|
||||
<script>
|
||||
function getRatingColor(rating) {
|
||||
if (rating >= 2400) return 'ff0000';
|
||||
if (rating >= 2100) return 'ff8c00';
|
||||
if (rating >= 1900) return 'aa00aa';
|
||||
if (rating >= 1600) return '0000ff';
|
||||
if (rating >= 1400) return '03a89e';
|
||||
if (rating >= 1200) return '008000';
|
||||
return '808080';
|
||||
}
|
||||
|
||||
function showCodeforcesRating(handle, rating, text) {
|
||||
var color = '#' + getRatingColor(rating);
|
||||
|
||||
$('#codeforces-profile-link')
|
||||
.html(rating >= 3000 ? ('<span style="color:#000!important">' + handle[0] + '</span>' + handle.substring(1)) : handle)
|
||||
.css('color', color)
|
||||
.css('font-family', 'Helvetica Neue, Helvetica, Arial, sans-serif')
|
||||
.css('font-size', '1.1em')
|
||||
.css('font-weight', 'bold');
|
||||
$('#codeforces-rating')
|
||||
.html(text + ', ' + rating)
|
||||
.css('color', color);
|
||||
}
|
||||
|
||||
function processCodeforcesInfoData(data) {
|
||||
if (!data || data.status !== 'OK' || !data.result || !data.result.length) return;
|
||||
|
||||
var result = data.result[0];
|
||||
|
||||
if (result.rating) {
|
||||
showCodeforcesRating(result.handle, result.rating, result.rank);
|
||||
} else {
|
||||
showCodeforcesRating(result.handle, 0, 'Unrated');
|
||||
}
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
$.get('https://codeforces.com/api/user.info?handles=<?= $extra['social']['codeforces'] ?>', function(data) {
|
||||
processCodeforcesInfoData(data);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</li>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
<?php if ($extra['social']['website']): ?>
|
||||
<li class="list-group-item">
|
||||
<i class="bi bi-link-45deg me-1"></i>
|
||||
<a class="text-decoration-none text-body text-break" href="<?= HTML::escape($extra['social']['website']) ?>" target="_blank">
|
||||
<?= HTML::escape($extra['social']['website']) ?>
|
||||
</a>
|
||||
</li>
|
||||
<?php if ($user['qq']) : ?>
|
||||
<li class="list-group-item">
|
||||
<i class="align-text-bottom me-1"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" width="16" height="16">
|
||||
<path d="M433.754 420.445c-11.526 1.393-44.86-52.741-44.86-52.741 0 31.345-16.136 72.247-51.051 101.786 16.842 5.192 54.843 19.167 45.803 34.421-7.316 12.343-125.51 7.881-159.632 4.037-34.122 3.844-152.316 8.306-159.632-4.037-9.045-15.25 28.918-29.214 45.783-34.415-34.92-29.539-51.059-70.445-51.059-101.792 0 0-33.334 54.134-44.859 52.741-5.37-.65-12.424-29.644 9.347-99.704 10.261-33.024 21.995-60.478 40.144-105.779C60.683 98.063 108.982.006 224 0c113.737.006 163.156 96.133 160.264 214.963 18.118 45.223 29.912 72.85 40.144 105.778 21.768 70.06 14.716 99.053 9.346 99.704z" fill="currentColor" />
|
||||
</svg></i>
|
||||
<a class="text-decoration-none text-body" href="http://wpa.qq.com/msgrd?v=3&uin=<?= HTML::escape($user['qq']) ?>&site=qq&menu=yes" target="_blank">
|
||||
<?= HTML::escape($user['qq']) ?>
|
||||
</a>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
<?php if ($extra['social']['github']) : ?>
|
||||
<li class="list-group-item">
|
||||
<i class="bi bi-github me-1"></i>
|
||||
<a class="text-decoration-none text-body" href="https://github.com/<?= HTML::escape($extra['social']['github']) ?>" target="_blank">
|
||||
<?= HTML::escape($extra['social']['github']) ?>
|
||||
</a>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
<?php if ($extra['social']['codeforces']) : ?>
|
||||
<li class="list-group-item d-flex align-items-center">
|
||||
<div class="flex-shrink-0"><i class="align-text-bottom me-1"><svg xmlns="http://www.w3.org/2000/svg" role="img" viewBox="0 0 24 24" width="16" height="16">
|
||||
<title>Codeforces</title>
|
||||
<path d="M4.5 7.5C5.328 7.5 6 8.172 6 9v10.5c0 .828-.672 1.5-1.5 1.5h-3C.673 21 0 20.328 0 19.5V9c0-.828.673-1.5 1.5-1.5h3zm9-4.5c.828 0 1.5.672 1.5 1.5v15c0 .828-.672 1.5-1.5 1.5h-3c-.827 0-1.5-.672-1.5-1.5v-15c0-.828.673-1.5 1.5-1.5h3zm9 7.5c.828 0 1.5.672 1.5 1.5v7.5c0 .828-.672 1.5-1.5 1.5h-3c-.828 0-1.5-.672-1.5-1.5V12c0-.828.672-1.5 1.5-1.5h3z" fill="currentColor" />
|
||||
</svg></i> </div>
|
||||
<div>
|
||||
<a id="codeforces-profile-link" class="text-decoration-none" href="https://codeforces.com/profile/<?= $extra['social']['codeforces'] ?>" target="_blank" style="color: rgba(var(--bs-body-color-rgb), var(--bs-text-opacity)) !important;">
|
||||
<?= $extra['social']['codeforces'] ?>
|
||||
</a>
|
||||
<div id="codeforces-rating" style="font-family: verdana, arial, sans-serif; line-height: 1.2em; text-transform: capitalize;"></div>
|
||||
</div>
|
||||
<script>
|
||||
function getRatingColor(rating) {
|
||||
if (rating >= 2400) return 'ff0000';
|
||||
if (rating >= 2100) return 'ff8c00';
|
||||
if (rating >= 1900) return 'aa00aa';
|
||||
if (rating >= 1600) return '0000ff';
|
||||
if (rating >= 1400) return '03a89e';
|
||||
if (rating >= 1200) return '008000';
|
||||
return '808080';
|
||||
}
|
||||
|
||||
function showCodeforcesRating(handle, rating, text) {
|
||||
var color = '#' + getRatingColor(rating);
|
||||
|
||||
$('#codeforces-profile-link')
|
||||
.html(rating >= 3000 ? ('<span style="color:#000!important">' + handle[0] + '</span>' + handle.substring(1)) : handle)
|
||||
.css('color', color)
|
||||
.css('font-family', 'Helvetica Neue, Helvetica, Arial, sans-serif')
|
||||
.css('font-size', '1.1em')
|
||||
.css('font-weight', 'bold');
|
||||
$('#codeforces-rating')
|
||||
.html(text + ', ' + rating)
|
||||
.css('color', color);
|
||||
}
|
||||
|
||||
function processCodeforcesInfoData(data) {
|
||||
if (!data || data.status !== 'OK' || !data.result || !data.result.length) return;
|
||||
|
||||
var result = data.result[0];
|
||||
|
||||
if (result.rating) {
|
||||
showCodeforcesRating(result.handle, result.rating, result.rank);
|
||||
} else {
|
||||
showCodeforcesRating(result.handle, 0, 'Unrated');
|
||||
}
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
$.get('https://codeforces.com/api/user.info?handles=<?= $extra['social']['codeforces'] ?>', function(data) {
|
||||
processCodeforcesInfoData(data);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
<?php if ($extra['social']['website']) : ?>
|
||||
<li class="list-group-item">
|
||||
<i class="bi bi-link-45deg me-1"></i>
|
||||
<a class="text-decoration-none text-body text-break" href="<?= HTML::escape($extra['social']['website']) ?>" target="_blank">
|
||||
<?= HTML::escape($extra['social']['website']) ?>
|
||||
</a>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
</ul>
|
||||
<div class="card-footer bg-transparent">
|
||||
<?php $last_visit_time = strtotime($user['last_visit_time']) ?>
|
||||
<?php if (time() - $last_visit_time < 60 * 15): // 15 mins ?>
|
||||
<?php if (time() - $last_visit_time < 60 * 15) : // 15 mins
|
||||
?>
|
||||
<span class="text-success">
|
||||
<i class="bi bi-circle-fill me-1"></i>
|
||||
<?= UOJLocale::get('user::online') ?>
|
||||
</span>
|
||||
<?php else: ?>
|
||||
<?php else : ?>
|
||||
<span class="text-danger">
|
||||
<i class="bi bi-circle-fill me-1"></i>
|
||||
<?= UOJLocale::get('user::offline') ?>
|
||||
</span>
|
||||
<?php if ($last_visit_time > 0): ?>
|
||||
<?php if ($last_visit_time > 0) : ?>
|
||||
<span class="text-muted small">
|
||||
, <?= UOJLocale::get('user::last active at') ?>
|
||||
<?= HTML::relative_time_str($last_visit_time, 0) ?>
|
||||
(<?= UOJLocale::get('user::last active at') ?>
|
||||
<?= HTML::relative_time_str($last_visit_time, 0) ?>)
|
||||
</span>
|
||||
<?php endif ?>
|
||||
<?php endif ?>
|
||||
@ -159,109 +163,109 @@
|
||||
</div>
|
||||
<div class="col-md-9 mt-2 mt-md-0">
|
||||
<nav class="nav mb-2">
|
||||
<?php if (Auth::check()): ?>
|
||||
<?php if (Auth::id() != $user['username']): ?>
|
||||
<?php if (Auth::check()) : ?>
|
||||
<?php if (Auth::id() != $user['username']) : ?>
|
||||
<a class="nav-link" href="/user_msg?enter=<?= $user['username'] ?>">
|
||||
<i class="bi bi-chat-left-dots"></i>
|
||||
<?= UOJLocale::get('send private message') ?>
|
||||
</a>
|
||||
<?php endif ?>
|
||||
<?php if (Auth::id() == $user['username'] || isSuperUser(Auth::user())): ?>
|
||||
<?php if (Auth::id() == $user['username'] || isSuperUser(Auth::user())) : ?>
|
||||
<a class="nav-link" href="/user/<?= $user['username'] ?>/edit">
|
||||
<i class="bi bi-pencil"></i>
|
||||
<?= UOJLocale::get('modify my profile') ?>
|
||||
</a>
|
||||
<?php endif ?>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if (!isset($is_blog_aboutme)): ?>
|
||||
<a class="nav-link" href="<?= HTML::blog_url($user['username'], '/') ?>">
|
||||
<i class="bi bi-arrow-right-square"></i>
|
||||
<?= UOJLocale::get('visit his blog', $user['username']) ?>
|
||||
</a>
|
||||
|
||||
<a class="nav-link" href="<?= HTML::blog_url($user['username'], '/self_reviews') ?>">
|
||||
<i class="bi bi-arrow-right-square"></i>
|
||||
<?= UOJLocale::get('contests::contest self reviews') ?>
|
||||
</a>
|
||||
<?php if (!isset($is_blog_aboutme)) : ?>
|
||||
<a class="nav-link" href="<?= HTML::blog_url($user['username'], '/') ?>">
|
||||
<i class="bi bi-arrow-right-square"></i>
|
||||
<?= UOJLocale::get('visit his blog', $user['username']) ?>
|
||||
</a>
|
||||
|
||||
<a class="nav-link" href="<?= HTML::blog_url($user['username'], '/self_reviews') ?>">
|
||||
<i class="bi bi-arrow-right-square"></i>
|
||||
<?= UOJLocale::get('contests::contest self reviews') ?>
|
||||
</a>
|
||||
<?php endif ?>
|
||||
</nav>
|
||||
|
||||
<?php if (!isset($is_blog_aboutme)): ?>
|
||||
<?php $groups = queryGroupsOfUser($user['username']) ?>
|
||||
<div class="card mb-2">
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">
|
||||
<?= UOJLocale::get('user::belongs to these groups') ?>
|
||||
</h4>
|
||||
<ul class="mb-0">
|
||||
<?php foreach ($groups as $group): ?>
|
||||
<li>
|
||||
<a class="text-decoration-none" href="<?= HTML::url('/group/'.$group['id']) ?>">
|
||||
<?= $group['title'] ?>
|
||||
</a>
|
||||
</li>
|
||||
<?php endforeach ?>
|
||||
<?php if (empty($groups)): ?>
|
||||
<?= UOJLocale::get('none') ?>
|
||||
<?php endif ?>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if (!isset($is_blog_aboutme)) : ?>
|
||||
<?php $groups = UOJGroup::queryGroupsOfUser($user['username']) ?>
|
||||
<div class="card mb-2">
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">
|
||||
<?= UOJLocale::get('user::belongs to these groups') ?>
|
||||
</h4>
|
||||
<ul class="mb-0">
|
||||
<?php foreach ($groups as $group) : ?>
|
||||
<li>
|
||||
<?= $group->getLink() ?>
|
||||
</li>
|
||||
<?php endforeach ?>
|
||||
<?php if (empty($groups)) : ?>
|
||||
<?= UOJLocale::get('none') ?>
|
||||
<?php endif ?>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
|
||||
<div class="card mb-2">
|
||||
<div class="card-body">
|
||||
<?php
|
||||
$_result = DB::query("select date_format(submit_time, '%Y-%m-%d'), problem_id from submissions where submitter = '{$user['username']}' and score = 100 and date(submit_time) between date_sub(curdate(), interval 1 year) and curdate()");
|
||||
$result = [];
|
||||
$vis = [];
|
||||
$cnt = 0;
|
||||
while ($row = DB::fetch($_result)) {
|
||||
$cnt++;
|
||||
$result[$row["date_format(submit_time, '%Y-%m-%d')"]]++;
|
||||
}
|
||||
?>
|
||||
<?php
|
||||
$_result = DB::query("select date_format(submit_time, '%Y-%m-%d'), problem_id from submissions where submitter = '{$user['username']}' and score = 100 and date(submit_time) between date_sub(curdate(), interval 1 year) and curdate()");
|
||||
$result = [];
|
||||
$vis = [];
|
||||
$cnt = 0;
|
||||
while ($row = DB::fetch($_result)) {
|
||||
$cnt++;
|
||||
$result[$row["date_format(submit_time, '%Y-%m-%d')"]]++;
|
||||
}
|
||||
?>
|
||||
<h4 class="card-title">
|
||||
<?= UOJLocale::get('n accepted in last year', $cnt) ?>
|
||||
</h4>
|
||||
<div id="accepted-graph" style="font-size: 14px"></div>
|
||||
<script>
|
||||
var accepted_graph_data = [
|
||||
<?php foreach ($result as $key => $val): ?>
|
||||
{ date: '<?= $key ?>', count: <?= $val ?> },
|
||||
<?php foreach ($result as $key => $val) : ?> {
|
||||
date: '<?= $key ?>',
|
||||
count: <?= $val ?>
|
||||
},
|
||||
<?php endforeach ?>
|
||||
];
|
||||
|
||||
$(document).ready(function () {
|
||||
$(document).ready(function() {
|
||||
$('#accepted-graph').CalendarHeatmap(accepted_graph_data, {});
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card mb-2">
|
||||
<div class="card-body">
|
||||
<?php $ac_problems = DB::selectAll("select a.problem_id as problem_id, b.title as title from best_ac_submissions a inner join problems b on a.problem_id = b.id where submitter = '{$user['username']}' order by id") ?>
|
||||
<h4 class="card-title">
|
||||
<?= UOJLocale::get('accepted problems').': '.UOJLocale::get('n problems in total', count($ac_problems))?>
|
||||
</h4>
|
||||
<ul class="nav uoj-ac-problems-list">
|
||||
<?php foreach ($ac_problems as $problem): ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link rounded uoj-ac-problems-list-item" href="/problem/<?= $problem['problem_id'] ?>" role="button">
|
||||
#<?= $problem['problem_id'] ?>. <?= $problem['title'] ?>
|
||||
</a>
|
||||
</li>
|
||||
<?php endforeach ?>
|
||||
<div class="card-body">
|
||||
<?php $ac_problems = DB::selectAll("select a.problem_id as problem_id, b.title as title from best_ac_submissions a inner join problems b on a.problem_id = b.id where submitter = '{$user['username']}' order by id") ?>
|
||||
<h4 class="card-title">
|
||||
<?= UOJLocale::get('accepted problems') . ': ' . UOJLocale::get('n problems in total', count($ac_problems)) ?>
|
||||
</h4>
|
||||
<ul class="nav uoj-ac-problems-list">
|
||||
<?php foreach ($ac_problems as $problem) : ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link rounded uoj-ac-problems-list-item" href="/problem/<?= $problem['problem_id'] ?>" role="button">
|
||||
#<?= $problem['problem_id'] ?>. <?= $problem['title'] ?>
|
||||
</a>
|
||||
</li>
|
||||
<?php endforeach ?>
|
||||
|
||||
<?php if (empty($ac_problems)): ?>
|
||||
<?= UOJLocale::get('none'); ?>
|
||||
<?php endif ?>
|
||||
</ul>
|
||||
</div>
|
||||
<?php if (empty($ac_problems)) : ?>
|
||||
<?= UOJLocale::get('none'); ?>
|
||||
<?php endif ?>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if (isSuperUser(Auth::user())): ?>
|
||||
<?php if (isSuperUser(Auth::user())) : ?>
|
||||
<div class="card card-default">
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item">
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user