比賽部分
比賽部分包含比賽列表顯示、單個比賽內部資訊顯示、比賽管理、比賽人員以及比賽報名頁面這五個程式碼主要程式碼檔案。此外在內部邏輯處理時還使用了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
函式顯示頁面的底部,包括版權資訊等。