比赛部分
比赛部分包含比赛列表显示、单个比赛内部信息显示、比赛管理、比赛人员以及比赛报名页面这五个代码主要代码文件。此外在内部逻辑处理时还使用了model
和lib
下的部分配置代码
比赛列表
比赛列表包含“正在进行或即将到来的比赛”和“已结束的比赛”这两部分具体代码实现解析如下
生成单个比赛信息
在php代码中定义echoContest
,用于生成单行比赛的列表信息。该方法将封装并利用于生成长表格
// 生成单个比赛信息的方法封装
function echoContest($contest) {
global $myUser, $upcoming_contest_name, $upcoming_contest_href, $rest_second;
// 比赛主要链接信息的HTML代码生成
$contest_name_link = <<<EOD
<a href="/contest/{$contest['id']}">{$contest['name']}</a>
EOD;
genMoreContestInfo($contest);
// 根据比赛状态进行前端页面显示更改,添加contest_name_link的代码细节
if ($contest['cur_progress'] == CONTEST_NOT_STARTED) {
$cur_rest_second = $contest['start_time']->getTimestamp() - UOJTime::$time_now->getTimestamp();
if ($cur_rest_second < $rest_second) {
$upcoming_contest_name = $contest['name'];
$upcoming_contest_href = "/contest/{$contest['id']}";
$rest_second = $cur_rest_second;
}
if ($myUser != null && hasRegistered($myUser, $contest)) {
$contest_name_link .= '<sup><a style="color:green">'.UOJLocale::get('contests::registered').'</a></sup>';
} else {
$contest_name_link .= '<sup><a style="color:red" href="/contest/'.$contest['id'].'/register">'.UOJLocale::get('contests::register').'</a></sup>';
}
} elseif ($contest['cur_progress'] == CONTEST_IN_PROGRESS) {
$contest_name_link .= '<sup><a style="color:blue" href="/contest/'.$contest['id'].'">'.UOJLocale::get('contests::in progress').'</a></sup>';
} elseif ($contest['cur_progress'] == CONTEST_PENDING_FINAL_TEST) {
$contest_name_link .= '<sup><a style="color:blue" href="/contest/'.$contest['id'].'">'.UOJLocale::get('contests::pending final test').'</a></sup>';
} elseif ($contest['cur_progress'] == CONTEST_TESTING) {
$contest_name_link .= '<sup><a style="color:blue" href="/contest/'.$contest['id'].'">'.UOJLocale::get('contests::final testing').'</a></sup>';
} elseif ($contest['cur_progress'] == CONTEST_FINISHED) {
$contest_name_link .= '<sup><a style="color:grey" href="/contest/'.$contest['id'].'/standings">'.UOJLocale::get('contests::ended').'</a></sup>';
}
$last_hour = round($contest['last_min'] / 60, 2);
// 打印单行比赛信息
$click_zan_block = getClickZanBlock('C', $contest['id'], $contest['zan']);
echo '<tr>';
echo '<td>', $contest_name_link, '</td>';
echo '<td>', '<a href="'.HTML::timeanddate_url($contest['start_time'], array('duration' => $contest['last_min'])).'">'.$contest['start_time_str'].'</a>', '</td>';
echo '<td>', UOJLocale::get('hours', $last_hour), '</td>';
echo '<td>', '<a href="/contest/'.$contest['id'].'/registrants"><span class="glyphicon glyphicon-user"></span> ×'.$contest['player_num'].'</a>', '</td>';
echo '<td>', '<div class="text-left">'.$click_zan_block.'</div>', '</td>';
echo '</tr>';
}
打印比赛列表
打印两个比赛列表的形式大同小异。以正在进行或即将到来的比赛为例,先整理表头的HTML代码,然后再利用html-lib.php
的长列表方法将封装好的变量方法放入处理
// 表头整理
$table_header = '';
$table_header .= '<tr>';
$table_header .= '<th>'.UOJLocale::get('contests::contest name').'</th>';
$table_header .= '<th style="width:15em;">'.UOJLocale::get('contests::start time').'</th>';
$table_header .= '<th style="width:100px;">'.UOJLocale::get('contests::duration').'</th>';
$table_header .= '<th style="width:100px;">'.UOJLocale::get('contests::the number of registrants').'</th>';
$table_header .= '<th style="width:180px;">'.UOJLocale::get('appraisal').'</th>';
$table_header .= '</tr>';
// 生成长表格
echoLongTable(array('*'), 'contests', "status != 'finished'", 'order by id desc', $table_header,
echoContest,
array('page_len' => 100)
);
附:表格打印
在html-lib.php
中,使用echoLongTable
方法对表格进行打印
// 重要代码:生成长表格的方法,适用于各大需要生成长表格(例如比赛列表、评测列表等)
function echoLongTable($col_names, $table_name, $cond, $tail, $header_row, $print_row, $config) {
// 初始化分页配置
$pag_config = $config;
$pag_config['col_names'] = $col_names;
$pag_config['table_name'] = $table_name;
$pag_config['cond'] = $cond;
$pag_config['tail'] = $tail;
$pag = new Paginator($pag_config);
// 设置表格样式
$div_classes = isset($config['div_classes']) ? $config['div_classes'] : array('table-responsive');
$table_classes = isset($config['table_classes']) ? $config['table_classes'] : array('table', 'table-bordered', 'table-hover', 'table-striped', 'table-text-center');
// 打印表格样式
echo '<div class="', join($div_classes, ' '), '">';
echo '<table class="', join($table_classes, ' '), '">';
echo '<thead>';
echo $header_row;
echo '</thead>';
echo '<tbody>';
// 打印表格内容
foreach ($pag->get() as $idx => $row) {
if (isset($config['get_row_index'])) {
$print_row($row, $idx);
} else {
$print_row($row);
}
}
if ($pag->isEmpty()) {
echo '<tr><td colspan="233">'.UOJLocale::get('none').'</td></tr>';
}
// 打印表格结尾和分页控件
echo '</tbody>';
echo '</table>';
echo '</div>';
if (isset($config['print_after_table'])) {
$fun = $config['print_after_table'];
$fun();
}
echo $pag->pagination();
}
比赛倒计时
在即将到来的比赛下,显示其倒计时。比赛时间一到给出弹窗信息
// 这段用来显示最近1天内最先要开始的比赛倒计时,时间一到就会显示弹窗
if ($rest_second <= 86400) {
echo <<<EOD
<div class="text-center bot-buffer-lg">
<div class="text-warning">$upcoming_contest_name 倒计时</div>
<div id="contest-countdown"></div>
<script type="text/javascript">
$('#contest-countdown').countdown($rest_second, function() {
if (confirm('$upcoming_contest_name 已经开始了。是否要跳转到比赛页面?')) {
window.location.href = "$upcoming_contest_href";
}
});
</script>
</div>
EOD;
}
比赛内部信息
由于负责contest_inside.php
的信息内部处理逻辑较多,这里只取部分主要内部逻辑处理的php代码进行解读
1. echoDashboard
函数
这个函数用于显示竞赛控制台,包括问题、公告和提交问题的表单。
function echoDashboard() {
global $contest, $post_notice, $post_question, $reply_question;
$myname = Auth::id();
$contest_problems = DB::selectAll("select contests_problems.problem_id, best_ac_submissions.submission_id from contests_problems left join best_ac_submissions on contests_problems.problem_id = best_ac_submissions.problem_id and submitter = '{$myname}' where contest_id = {$contest['id']} order by contests_problems.problem_id asc");
for ($i = 0; $i < count($contest_problems); $i++) {
$contest_problems[$i]['problem'] = queryProblemBrief($contest_problems[$i]['problem_id']);
}
$contest_notice = DB::selectAll("select * from contests_notice where contest_id = {$contest['id']} order by time desc");
if (Auth::check()) {
$my_questions = DB::selectAll("select * from contests_asks where contest_id = {$contest['id']} and username = '{$myname}' order by post_time desc");
$my_questions_pag = new Paginator([
'data' => $my_questions
]);
} else {
$my_questions_pag = null;
}
$others_questions_pag = new Paginator([
'col_names' => array('*'),
'table_name' => 'contests_asks',
'cond' => "contest_id = {$contest['id']} and username != '{$myname}' and is_hidden = 0",
'tail' => 'order by reply_time desc',
'page_len' => 10
]);
uojIncludeView('contest-dashboard', [
'contest' => $contest,
'contest_notice' => $contest_notice,
'contest_problems' => $contest_problems,
'post_question' => $post_question,
'my_questions_pag' => $my_questions_pag,
'others_questions_pag' => $others_questions_pag
]);
}
- 从数据库中获取竞赛问题和用户的最佳提交,并查询每个问题的简要信息。
- 获取竞赛公告,并按时间倒序排列。
- 如果用户已登录,获取用户提交的问题并分页显示。
- 获取其他用户提交的问题(公开的)并分页显示。
- 包含一个视图文件
contest-dashboard
,传递相关数据进行显示。
2. echoBackstage
函数
这个函数用于显示竞赛的后台管理页面,包括发布公告和回复问题的表单。
function echoBackstage() {
global $contest, $post_notice, $reply_question;
$questions_pag = new Paginator([
'col_names' => array('*'),
'table_name' => 'contests_asks',
'cond' => "contest_id = {$contest['id']}",
'tail' => 'order by post_time desc',
'page_len' => 50
]);
if ($contest['cur_progress'] < CONTEST_TESTING) {
$contest_data = queryContestData($contest, ['pre_final' => true]);
calcStandings($contest, $contest_data, $score, $standings);
$standings_data = [
'contest' => $contest,
'standings' => $standings,
'score' => $score,
'contest_data' => $contest_data
];
} else {
$standings_data = null;
}
uojIncludeView('contest-backstage', [
'contest' => $contest,
'post_notice' => $post_notice,
'reply_question' => $reply_question,
'questions_pag' => $questions_pag,
'standings_data' => $standings_data
]);
}
- 获取并分页显示所有竞赛问题。
- 如果竞赛尚未进入最终测试阶段,计算竞赛排名数据。
- 包含一个视图文件
contest-backstage
,传递相关数据进行显示。
3. echoMySubmissions
函数
这个函数用于显示用户在竞赛中的提交记录,并提供一个选项来显示所有提交记录。
function echoMySubmissions() {
global $contest, $myUser;
$show_all_submissions_status = Cookie::get('show_all_submissions') !== null ? 'checked="checked" ' : '';
$show_all_submissions = UOJLocale::get('contests::show all submissions');
echo <<<EOD
<div class="checkbox text-right">
<label for="input-show_all_submissions"><input type="checkbox" id="input-show_all_submissions" $show_all_submissions_status/> $show_all_submissions</label>
</div>
<script type="text/javascript">
$('#input-show_all_submissions').click(function() {
if (this.checked) {
$.cookie('show_all_submissions', '');
} else {
$.removeCookie('show_all_submissions');
}
location.reload();
});
</script>
EOD;
if (Cookie::get('show_all_submissions') !== null) {
echoSubmissionsList("contest_id = {$contest['id']}", 'order by id desc', array('judge_time_hidden' => ''), $myUser);
} else {
echoSubmissionsList("submitter = '{$myUser['username']}' and contest_id = {$contest['id']}", 'order by id desc', array('judge_time_hidden' => ''), $myUser);
}
}
- 提供一个复选框,用户可以选择是否显示所有提交记录。
- 根据用户选择,显示用户自己的提交记录或所有提交记录。
4. echoStandings
函数
这个函数用于显示竞赛的排名情况。
function echoStandings() {
global $contest;
$contest_data = queryContestData($contest);
calcStandings($contest, $contest_data, $score, $standings);
uojIncludeView('contest-standings', [
'contest' => $contest,
'standings' => $standings,
'score' => $score,
'contest_data' => $contest_data
]);
}
- 获取竞赛数据并计算排名。
- 包含一个视图文件
contest-standings
,传递相关数据进行显示。
5. echoContestCountdown
函数
这个函数用于显示竞赛的倒计时。
function echoContestCountdown() {
global $contest;
$rest_second = $contest['end_time']->getTimestamp() - UOJTime::$time_now->getTimestamp();
$time_str = UOJTime::$time_now_str;
$contest_ends_in = UOJLocale::get('contests::contest ends in');
echo <<<EOD
<div class="card border-info">
<div class="card-header bg-info">
<h3 class="card-title">$contest_ends_in</h3>
</div>
<div class="card-body text-center countdown" data-rest="$rest_second"></div>
</div>
<script type="text/javascript">
checkContestNotice({$contest['id']}, '$time_str');
</script>
EOD;
}
- 计算剩余时间并显示倒计时卡片。
- 包含一个 JavaScript 脚本用于实时更新倒计时。
6. echoContestJudgeProgress
函数
这个函数用于显示竞赛的判题进度。
function echoContestJudgeProgress() {
global $contest;
if ($contest['cur_progress'] < CONTEST_TESTING) {
$rop = 0;
$title = UOJLocale::get('contests::contest pending final test');
} else {
$total = DB::selectCount("select count(*) from submissions where contest_id = {$contest['id']}");
$n_judged = DB::selectCount("select count(*) from submissions where contest_id = {$contest['id']} and status = 'Judged'");
$rop = $total == 0 ? 100 : (int)($n_judged / $total * 100);
$title = UOJLocale::get('contests::contest final testing');
}
echo <<<EOD
<div class="card border-info">
<div class="card-header bg-info">
<h3 class="card-title">$title</h3>
</div>
<div class="card-body">
<div class="progress bot-buffer-no">
<div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="$rop" aria-valuemin="0" aria-valuemax="100" style="width: {$rop}%; min-width: 20px;">{$rop}%</div>
</div>
</div>
</div>
</div>
EOD;
}
- 根据竞赛当前的进度,显示不同的判题进度条。
- 包含一个进度条,显示已判题目的百分比。
7. echoContestFinished
函数
这个函数用于显示竞赛结束的状态。
function echoContestFinished() {
$title = UOJLocale::get('contests::contest ended');
echo <<<EOD
<div class="card border-info">
<div class="card-header bg-info">
<h3 class="card-title">$title</h3>
</div>
</div>
EOD;
}
- 显示一个简单的卡片,表明竞赛已结束。
附:比赛rating计算
这个评分计算逻辑实现了一种基于排名的评分系统,用于计算参赛者在比赛后的新评分(Rating)。下面是对这段代码的详细解释:
函数定义
function calcRating($standings, $K = 400) {
$DELTA = 500;
calcRating
是一个计算评分的函数。- 参数
$standings
是比赛排名列表。 - 参数
$K
是一个常数,用于控制评分变化的幅度,默认值为 400。 $DELTA
是一个常数,用于计算权重。
获取参赛人数和初始评分
$n = count($standings);
$rating = array();
for ($i = 0; $i < $n; ++$i) {
$rating[$i] = $standings[$i][2][1];
}
$n
是参赛人数。$rating
数组存储每个参赛者的初始评分。
计算排名和名次
$rank = array();
$foot = array();
for ($i = 0; $i < $n; ) {
$j = $i;
while ($j + 1 < $n && $standings[$j + 1][3] == $standings[$j][3]) {
++$j;
}
$our_rk = 0.5 * (($i + 1) + ($j + 1));
while ($i <= $j) {
$rank[$i] = $our_rk;
$foot[$i] = $n - $rank[$i];
$i++;
}
}
rank
数组存储每个参赛者的实际排名。foot
数组存储每个参赛者的反向排名(即排名从后往前数的位置)。- 这段代码计算并存储参赛者的实际排名,如果多个参赛者的排名相同,他们的实际排名取平均值。
计算权重和期望排名
$weight = array();
for ($i = 0; $i < $n; ++$i) {
$weight[$i] = pow(7, $rating[$i] / $DELTA);
}
$exp = array_fill(0, $n, 0);
for ($i = 0; $i < $n; ++$i)
for ($j = 0; $j < $n; ++$j)
if ($j != $i) {
$exp[$i] += $weight[$i] / ($weight[$i] + $weight[$j]);
}
weight
数组存储每个参赛者的权重,基于其初始评分计算。exp
数组存储每个参赛者的期望排名分数,基于所有参赛者之间的权重比计算。
计算新评分
$new_rating = array();
for ($i = 0; $i < $n; $i++) {
$new_rating[$i] = $rating[$i];
$new_rating[$i] += ceil($K * ($foot[$i] - $exp[$i]) / ($n - 1));
}
new_rating
数组存储每个参赛者的新评分。- 新评分的计算公式:
new_rating = rating + K * (实际排名分数 - 期望排名分数) / (参赛人数 - 1)
,并取整。
调整新评分
for ($i = $n - 1; $i >= 0; $i--) {
if ($i + 1 < $n && $standings[$i][3] != $standings[$i + 1][3]) {
break;
}
if ($new_rating[$i] > $rating[$i]) {
$new_rating[$i] = $rating[$i];
}
}
for ($i = 0; $i < $n; $i++) {
if ($new_rating[$i] < 0) {
$new_rating[$i] = 0;
}
}
- 如果排名靠后的参赛者的新评分比初始评分高,将其调整为初始评分。
- 确保所有新评分不低于零。
返回新评分
return $new_rating;
}
- 返回计算后的新评分数组。
比赛后台信息管理
对于具有管理员权限的用户,需要一个比赛后台页面去处理比赛后台的信息。这里使用contest_manage.php
去处理其中的页面逻辑和处理逻辑
这段代码是一个 PHP 文件,通常用于管理竞赛相关的设置。让我们逐段解读其内部逻辑和功能:
1. 引入必要的 PHP 库和函数
<?php
requirePHPLib('form');
- 使用
requirePHPLib
函数引入了一个名为form
的 PHP 库,这个库可能包含了用于表单处理的函数和类。
2. 验证和查询竞赛信息
if (!validateUInt($_GET['id']) || !($contest = queryContest($_GET['id']))) {
become404Page();
}
- 首先,验证了从 GET 请求中获取的竞赛 ID 是否是一个无符号整数,并且查询对应的竞赛信息。
- 如果竞赛 ID 不合法或者查询竞赛失败,则调用
become404Page()
函数,显示 404 页面。
3. 生成更多竞赛信息
genMoreContestInfo($contest);
- 调用
genMoreContestInfo
函数,可能用于生成和展示更多关于竞赛的详细信息,但具体实现不在这段代码中。
4. 权限检查
if (!isSuperUser($myUser)) {
become403Page();
}
- 判断当前用户是否为超级用户(管理员)。如果不是,则调用
become403Page()
函数,显示 403 页面,表示权限不足。
5. 创建并处理比赛时间表单 time_form
$time_form = new UOJForm('time');
// 添加输入字段及其验证规则和处理逻辑
$time_form->runAtServer();
- 创建了一个名为
time_form
的表单对象,用于编辑竞赛的开始时间、时长和名称。 - 设定了各字段的初始值和验证函数,以及处理函数。
6. 创建和处理管理者表单 managers_form
和题目表单 problems_form
$managers_form = newAddDelCmdForm('managers', ...);
$problems_form = newAddDelCmdForm('problems', ...);
$managers_form->runAtServer();
$problems_form->runAtServer();
- 创建了两个表单对象
managers_form
和problems_form
,用于添加和删除竞赛的管理员和题目。 - 设定了验证函数和处理函数,并执行了表单处理操作。
7. 超级用户特有功能表单处理
if (isSuperUser($myUser)) {
// 创建和处理额外的表单,如评分变化上限、比赛类型、排名版本等
}
- 如果当前用户是超级用户,创建和处理了额外的表单,用于编辑评分变化上限、比赛类型和排名版本等竞赛的附加设置。
8. 运行表单处理逻辑
$time_form->runAtServer();
$managers_form->runAtServer();
$problems_form->runAtServer();
- 最后,依次运行了所有表单对象的
runAtServer()
方法,处理提交的数据并更新数据库。
比赛报名人员信息显示
这段代码主要用于展示竞赛的报名参赛者信息,并提供一些管理功能。让我们逐段解读其功能和逻辑:
1. 处理未开始竞赛的逻辑
if ($contest['cur_progress'] == CONTEST_NOT_STARTED) {
// 判断用户是否已经报名
$iHasRegistered = $myUser != null && hasRegistered($myUser, $contest);
if ($iHasRegistered) {
// 创建取消报名的表单
$unregister_form = new UOJForm('unregister');
$unregister_form->handle = function() {
global $myUser, $contest;
DB::query("delete from contests_registrants where username = '{$myUser['username']}' and contest_id = {$contest['id']}");
updateContestPlayerNum($contest);
};
$unregister_form->submit_button_config['class_str'] = 'btn btn-danger btn-xs';
$unregister_form->submit_button_config['text'] = '取消报名';
$unregister_form->succ_href = "/contests";
$unregister_form->runAtServer();
}
if ($has_contest_permission) {
// 创建重新计算参赛前 rating 的表单
$pre_rating_form = new UOJForm('pre_rating');
$pre_rating_form->handle = function() {
global $contest;
foreach (DB::selectAll("select * from contests_registrants where contest_id = {$contest['id']}") as $reg) {
$user = queryUser($reg['username']);
DB::update("update contests_registrants set user_rating = {$user['rating']} where contest_id = {$contest['id']} and username = '{$user['username']}'");
}
};
$pre_rating_form->submit_button_config['align'] = 'right';
$pre_rating_form->submit_button_config['class_str'] = 'btn btn-warning';
$pre_rating_form->submit_button_config['text'] = '重新计算参赛前的 rating';
$pre_rating_form->submit_button_config['smart_confirm'] = '';
$pre_rating_form->runAtServer();
}
}
- 如果竞赛尚未开始 (
$contest['cur_progress'] == CONTEST_NOT_STARTED
),则执行以下逻辑:- 检查当前用户是否已经报名。
- 如果已经报名,则创建一个用于取消报名的表单,并处理取消报名的逻辑。
- 如果当前用户有管理竞赛的权限 (
$has_contest_permission
),则创建一个用于重新计算参赛前评分 (user_rating
) 的表单,并处理相应逻辑。
2. 页面头部和竞赛标题展示
<?php echoUOJPageHeader(HTML::stripTags($contest['name']) . ' - ' . UOJLocale::get('contests::contest registrants')) ?>
<h1 class="text-center"><?= $contest['name'] ?></h1>
- 使用
echoUOJPageHeader
函数显示页面的头部,包括竞赛的标题和“竞赛参赛者”等翻译后的本地化文本。
3. 根据竞赛状态显示报名信息
<?php if ($contest['cur_progress'] == CONTEST_NOT_STARTED): ?>
<?php if ($iHasRegistered): ?>
<div class="float-right">
<?php $unregister_form->printHTML(); ?>
</div>
<div><a style="color:green">已报名</a></div>
<?php else: ?>
<div>当前尚未报名,您可以<a style="color:red" href="/contest/<?= $contest['id'] ?>/register">报名</a>。</div>
<?php endif ?>
<div class="top-buffer-sm"></div>
<?php endif ?>
- 如果竞赛尚未开始,根据用户是否已经报名显示不同的信息:
- 如果已经报名,则显示取消报名按钮。
- 如果尚未报名,则显示报名链接。
4. 显示参赛者列表
<?php
if ($show_ip) {
// 如果有权限显示 IP 地址,则设置表头
$header_row = '<tr><th>#</th><th>'.UOJLocale::get('username').'</th><th>remote_addr</th><th>rating</th></tr>';
$ip_owner = array();
foreach (DB::selectAll("select * from contests_registrants where contest_id = {$contest['id']} order by user_rating asc") as $reg) {
$user = queryUser($reg['username']);
$ip_owner[$user['remote_addr']] = $reg['username'];
}
} else {
// 否则只显示用户名和评分
$header_row = '<tr><th>#</th><th>'.UOJLocale::get('username').'</th><th>rating</th></tr>';
}
echoLongTable(array('*'), 'contests_registrants', "contest_id = {$contest['id']}", 'order by user_rating desc',
$header_row,
function($contest, $num) {
global $myUser;
global $show_ip, $ip_owner;
$user = queryUser($contest['username']);
$user_link = getUserLink($contest['username'], $contest['user_rating']);
if (!$show_ip) {
echo '<tr>';
} else {
if ($ip_owner[$user['remote_addr']] != $user['username']) {
echo '<tr class="danger">';
} else {
echo '<tr>';
}
}
echo '<td>'.$num.'</td>';
echo '<td>'.$user_link.'</td>';
if ($show_ip) {
echo '<td>'.$user['remote_addr'].'</td>';
}
echo '<td>'.$contest['user_rating'].'</td>';
echo '</tr>';
},
array('page_len' => 100,
'get_row_index' => '',
'print_after_table' => function() {
global $pre_rating_form;
if (isset($pre_rating_form)) {
$pre_rating_form->printHTML();
}
}
)
);
?>
- 使用
echoLongTable
函数显示参赛者列表,根据是否有权限显示 IP 地址决定表格的表头和样式。 - 对每个参赛者生成一行表格数据,并根据权限高亮显示 IP 地址不匹配的参赛者。
- 如果有重新计算评分的表单 (
$pre_rating_form
),则在表格后面显示该表单。
比赛报名页面
contest_registration.php
实现了一个简单的竞赛报名页面,用户必须登录后才能进行报名操作。如果用户已经报名或具有管理权限,或者竞赛不是未开始状态,则会被重定向到竞赛列表页面。页面展示了比赛的规则,并提供了一个表单用于用户提交报名信息,成功报名后将跳转回竞赛列表页面。
1. 权限检查和重定向
if ($myUser == null) {
redirectToLogin();
} elseif (hasContestPermission($myUser, $contest) || hasRegistered($myUser, $contest) || $contest['cur_progress'] != CONTEST_NOT_STARTED) {
redirectTo('/contests');
}
- 检查当前用户是否已登录 (
$myUser == null
),如果未登录,则重定向到登录页面。 - 如果用户已经具有竞赛的管理权限或已经报名了该竞赛,或者竞赛当前不是未开始状态,则重定向到竞赛列表页面 (
/contests
)。
2. 处理报名表单
$register_form = new UOJForm('register');
$register_form->handle = function() {
global $myUser, $contest;
DB::query("insert into contests_registrants (username, user_rating, contest_id, has_participated) values ('{$myUser['username']}', {$myUser['rating']}, {$contest['id']}, 0)");
updateContestPlayerNum($contest);
};
$register_form->submit_button_config['class_str'] = 'btn btn-primary';
$register_form->submit_button_config['text'] = '报名比赛';
$register_form->succ_href = "/contests";
$register_form->runAtServer();
- 创建一个名为
register
的表单对象UOJForm
,用于用户报名操作。 - 定义了表单的处理函数 (
$register_form->handle
),在用户点击提交时执行数据库操作,将用户信息插入到contests_registrants
表中,并更新竞赛的参赛人数。 - 设置提交按钮的样式和文本,并定义成功后的重定向链接为
/contests
。 - 使用
runAtServer()
方法执行表单处理,处理用户的提交请求。
3. 页面头部
<?php echoUOJPageHeader(HTML::stripTags($contest['name']) . ' - 报名') ?>
- 使用
echoUOJPageHeader
函数显示页面的头部,包括竞赛的标题和“报名”等文本。
4. 比赛规则展示
<h1 class="page-header">比赛规则</h1>
<ul>
<!-- 比赛规则的具体内容 -->
</ul>
- 展示了关于比赛的规则,以无序列表的形式呈现。
5. 显示报名表单
<?php $register_form->printHTML(); ?>
- 使用
printHTML()
方法将报名表单的 HTML 代码输出到页面中,让用户可以填写和提交报名信息。
6. 页面底部
<?php echoUOJPageFooter() ?>
- 使用
echoUOJPageFooter
函数显示页面的底部,包括版权信息等。