refactor(contest/self_reviews): allow admins edit participants' self_reviews
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Baoshuo Ren 2023-02-07 16:52:51 +08:00
parent 58ba28d9b9
commit 946ea8283f
Signed by: baoshuo
GPG Key ID: 00CB9680AB29F51A
2 changed files with 154 additions and 113 deletions

View File

@ -236,97 +236,85 @@ if ($cur_tab == 'dashboard') {
$reply_question = null;
}
} elseif ($cur_tab == 'self_reviews') {
if (UOJContest::cur()->userHasMarkedParticipated(Auth::user())) {
$self_reviews_update_form = new UOJForm('self_review_update');
$self_reviews_update_form->config['ctrl_enter_submit'] = true;
$contest_problems = DB::selectAll([
"select problem_id",
"from", "contests_problems",
"where", ["contest_id" => $contest['id']],
"order by level, problem_id"
]);
for ($i = 0; $i < count($contest_problems); $i++) {
$contest_problems[$i]['problem'] = UOJContestProblem::query($contest_problems[$i]['problem_id']);
$self_reviews_update_form = new UOJForm('self_reviews_update');
$self_reviews_update_form->config['ctrl_enter_submit'] = true;
$self_reviews_update_form->addHidden('self_reviews_update__username', '', function ($username, &$vdata) {
if (!validateUsername($username)) {
return '无效用户名';
}
for ($i = 0; $i < count($contest_problems); $i++) {
$content = DB::selectSingle([
"select content",
"from", "contests_reviews",
"where", [
"contest_id" => $contest['id'],
"problem_id" => $contest_problems[$i]['problem_id'],
"poster" => Auth::id(),
],
]);
$self_reviews_update_form->addTextArea('self_review_update__problem_' . $contest_problems[$i]['problem']->getLetter(), [
'div_class' => 'mb-3',
'label' => '<b>' . $contest_problems[$i]['problem']->getLetter() . '</b>: ' . $contest_problems[$i]['problem']->info['title'],
'default_value' => $content,
'validator_php' => function ($content) {
if (strlen($content) > 200) {
return '总结不能超过200字';
}
return '';
},
]);
if ($username != Auth::id() && !UOJContest::cur()->userCanManage(Auth::user())) {
return '权限不足';
}
$content = DB::selectSingle([
"select content",
"from", "contests_reviews",
"where", [
"contest_id" => $contest['id'],
"problem_id" => -1,
"poster" => Auth::id(),
],
]);
$self_reviews_update_form->addTextArea('self_review_update__overall', [
'label' => '比赛总结',
'default_value' => $content,
return '';
}, null);
$contest_problems = array_map(fn ($row) => UOJContestProblem::query($row['problem_id']), DB::selectAll([
"select problem_id",
"from", "contests_problems",
"where", ["contest_id" => $contest['id']],
"order by level, problem_id"
]));
foreach ($contest_problems as $cp) {
$self_reviews_update_form->addTextArea('self_reviews_update__problem_' . $cp->getLetter(), [
'div_class' => 'mb-3',
'label' => $cp->getTitle(['with' => 'letter']),
'default_value' => '',
'validator_php' => function ($content) {
if (strlen($content) > 200) {
return '总结不能超过200字';
return '长度超过限制';
}
return '';
},
]);
$self_reviews_update_form->handle = function () use ($contest, $contest_problems) {
for ($i = 0; $i < count($contest_problems); $i++) {
if (isset($_POST['self_review_update__problem_' . $contest_problems[$i]['problem']->getLetter()])) {
DB::query([
"replace into contests_reviews",
"(contest_id, problem_id, poster, content)",
"values", DB::tuple([
$contest['id'],
$contest_problems[$i]['problem_id'],
Auth::id(),
$_POST['self_review_update__problem_' . $contest_problems[$i]['problem']->getLetter()],
]),
]);
}
}
if (isset($_POST['self_review_update__overall'])) {
DB::query([
"replace into contests_reviews",
"(contest_id, problem_id, poster, content)",
"values", DB::tuple([
$contest['id'],
-1,
Auth::id(),
$_POST['self_review_update__overall'],
]),
]);
}
};
$self_reviews_update_form->runAtServer();
}
$self_reviews_update_form->addTextArea('self_reviews_update__overall', [
'label' => '比赛总结',
'validator_php' => function ($content) {
if (strlen($content) > 300) {
return '长度超过限制';
}
return '';
},
]);
$self_reviews_update_form->handle = function (&$vdata) use ($contest_problems) {
foreach ($contest_problems as $cp) {
DB::update([
"replace into contests_reviews",
DB::bracketed_fields([
"contest_id",
"problem_id",
"poster",
"content"
]),
"values", DB::tuple([
UOJContest::info('id'),
$cp->info['id'],
$_POST['self_reviews_update__username'],
$_POST['self_reviews_update__problem_' . $cp->getLetter()],
]),
]);
}
DB::update([
"replace into contests_reviews",
"(contest_id, problem_id, poster, content)",
"values", DB::tuple([
UOJContest::info('id'),
-1,
$_POST['self_reviews_update__username'],
$_POST['self_reviews_update__overall'],
]),
]);
};
$self_reviews_update_form->runAtServer();
}
function echoDashboard() {
@ -469,9 +457,12 @@ function echoStandings($is_after_contest_query = false) {
}
function echoSelfReviews() {
global $contest;
global $contest, $self_reviews_update_form;
uojIncludeView('contest-reviews', ['contest' => $contest] + UOJContest::cur()->queryResult());
uojIncludeView('contest-reviews', [
'contest' => $contest,
'self_reviews_update_form' => $self_reviews_update_form,
] + UOJContest::cur()->queryResult());
}
?>
<?php echoUOJPageHeader($tabs_info[$cur_tab]['name'] . ' - ' . UOJContest::info('name') . ' - ' . UOJLocale::get('contests::contest')) ?>
@ -538,17 +529,7 @@ function echoSelfReviews() {
</div>
</div>
<?php if ($cur_tab == 'standings' || $cur_tab == 'after_contest_standings') : ?>
<?php elseif ($cur_tab == 'self_reviews') : ?>
<?php if (isset($self_reviews_update_form)) : ?>
<hr />
<div class="col-md-6">
<h4>修改我的赛后总结</h4>
<div class="small">赛后总结支持 Markdown 语法。</div>
<?php $self_reviews_update_form->printHTML(); ?>
</div>
<?php endif ?>
<?php if ($cur_tab == 'standings' || $cur_tab == 'after_contest_standings' || $cur_tab == 'self_reviews') : ?>
<?php else : ?>
<div class="d-md-none">
<hr />

View File

@ -5,7 +5,9 @@ $parsedown = HTML::parsedown(['username_with_color' => true]);
$purifier = HTML::purifier_inline();
foreach ($contest_data['people'] as $person) {
$reviews[$person[0]] = [];
$reviews[$person[0]] = [
'_can_edit' => Auth::id() == $person[0] || UOJContest::cur()->userCanManage(Auth::user()),
];
foreach ($contest_data['problems'] as $problem) {
$content = DB::selectSingle([
@ -18,7 +20,8 @@ foreach ($contest_data['people'] as $person) {
],
]);
$reviews[$person[0]][$problem] = $purifier->purify($parsedown->line($content));
$reviews[$person[0]][$problem]['raw'] = $content;
$reviews[$person[0]][$problem]['html'] = $purifier->purify($parsedown->line($content));
}
$content = DB::selectSingle([
@ -31,19 +34,34 @@ foreach ($contest_data['people'] as $person) {
],
]);
$reviews[$person[0]]['all'] = $purifier->purify($parsedown->line($content));
$reviews[$person[0]]['_all']['raw'] = $content;
$reviews[$person[0]]['_all']['html'] = $purifier->purify($parsedown->line($content));
}
?>
<div id="standings"></div>
<div class="modal fade" id="UpdateSelfReviewsModal" tabindex="-1" aria-labelledby="UpdateSelfReviewsModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="UpdateSelfReviewsModalLabel">更新赛后总结</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<?php $self_reviews_update_form->printHTML(); ?>
</div>
</div>
</div>
</div>
<script type="text/javascript">
var contest_id = <?= $contest['id'] ?>;
var standings = <?= json_encode($standings) ?>;
var score = <?= json_encode($score) ?>;
var problems = <?= json_encode($contest_data['problems']) ?>;
var reviews = <?= json_encode($reviews) ?>;
var standings_config = <?= json_encode(isset($standings_config) ? $standings_config : ['_config' => true]) ?>;
var reviews = <?= json_encode($reviews, JSON_FORCE_OBJECT) ?>;
var standings_config = <?= json_encode(isset($standings_config) ? $standings_config : [], JSON_FORCE_OBJECT) ?>;
$(document).ready(function() {
$("#standings").long_table(
@ -59,33 +77,75 @@ foreach ($contest_data['people'] as $person) {
'<th style="width:16em">赛后总结</th>' +
'</tr>',
function(row) {
var col_tr = '<tr>';
col_tr += '<td>' + row[3] + '</td>';
col_tr += '<td>' + getUserLink(row[2][0], row[2][1]) + '</td>';
col_tr += '<td>' + '<span class="uoj-score" data-max="' + problems.length * 100 + '" style="color:' + getColOfScore(row[0] / problems.length) + '">' + row[0] + '</span>' + '</td>';
if (reviews[row[2][0]]['_can_edit']) {
var button_update_review = $('<button class="btn btn-sm btn-outline-secondary ms-2" />').append('<i class="bi bi-pencil"></i>');
button_update_review.click(function() {
$('#UpdateSelfReviewsModalLabel').text('更新 ' + row[2][0] + ' 的赛后总结');
$('#input-self_reviews_update__username').val(row[2][0]);
$.map(problems, function(col, idx) {
$('#input-self_reviews_update__problem_' + String.fromCharCode('A'.charCodeAt(0) + idx)).val(reviews[row[2][0]][col]['raw'] || '');
});
$('#input-self_reviews_update__overall').val(reviews[row[2][0]]['_all']['raw'] || '');
$('#UpdateSelfReviewsModal').modal('show');
});
} else {
var button_update_review = '';
}
var res = $('<tr />')
.append($('<td />').append(row[3]))
.append($('<td />').append(getUserLink(row[2][0], row[2][1])).append(button_update_review))
.append($('<td />').append($('<span class="uoj-score" />').attr('data-max', problems.length * 100).css('color', getColOfScore(row[0] / problems.length)).append(row[0])));
for (var i = 0; i < problems.length; i++) {
col_tr += '<td class="align-text-top">';
var td = $('<td class="align-text-top" />');
col = score[row[2][0]][i];
if (col != undefined) {
col_tr += '<div>';
td.append($('<div />').append($('<a class="uoj-score" />').attr('href', '/submission/' + col[2]).attr('data-max', 100).css('color', getColOfScore(col[0])).append(col[0])));
if (col[2]) col_tr += '<a href="/submission/' + col[2] + '" class="uoj-score" style="color:' + getColOfScore(col[0]) + '">' + col[0] + '</a>';
else col_tr += '<span class="uoj-score" style="color:' + getColOfScore(col[0]) + '">' + col[0] + '</span>';
col_tr += '</div>';
if (col[2]) {
td.append(
$('<div />').append(
$('<a class="uoj-score" />')
.attr('href', '/submission/' + col[2])
.attr('data-max', 100)
.css('color', getColOfScore(col[0]))
.append(col[0])
)
);
} else {
td.append(
$('<div />').append(
$('<span class="uoj-score" />')
.data('max', 100)
.css('color', getColOfScore(col[0]))
.append(col[0])
)
);
}
} else {
col_tr += '<div>&nbsp;</div>';
td.append($('<div />').append('&nbsp;'));
}
if (reviews[row[2][0]][problems[i]]) {
col_tr += '<div class="mt-2 pt-2 border-top">' + reviews[row[2][0]][problems[i]] + '</div>';
td.append($('<div class="mt-2 pt-2 border-top" />').append(reviews[row[2][0]][problems[i]]['html']));
}
col_tr += '</td>';
res.append(td);
}
col_tr += '<td>' + reviews[row[2][0]]['all'] + '</td>';
col_tr += '</tr>';
return col_tr;
if (reviews[row[2][0]]['_all']) {
res.append($('<td />').append(reviews[row[2][0]]['_all']['html']));
} else {
res.append($('<td />').append('&nbsp;'));
}
return res.uoj_highlight();
}, {
div_classes: standings_config.div_classes ? standings_config.div_classes : ['table-responsive', 'card', 'my-3'],
table_classes: standings_config.table_classes ? standings_config.table_classes : ['table', 'table-bordered', 'text-center', 'align-middle', 'uoj-table', 'uoj-standings-table', 'mb-0'],