feat(blog): markdown comment support
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Baoshuo Ren 2023-02-24 22:10:29 +08:00
parent 4582a6b947
commit c98a893fb6
Signed by: baoshuo
GPG Key ID: 00CB9680AB29F51A
4 changed files with 89 additions and 39 deletions

View File

@ -9,20 +9,46 @@ UOJBlog::cur()->belongsToUserBlog() || UOJResponse::page404();
UOJBlog::cur()->userCanView(Auth::user()) || UOJResponse::page403(); UOJBlog::cur()->userCanView(Auth::user()) || UOJResponse::page403();
$blog = UOJBlog::info(); $blog = UOJBlog::info();
$purifier = HTML::purifier();
$parsedown = HTML::parsedown([
'username_with_color' => true,
]);
function getCommentContentToDisplay($comment) { function getCommentContentToDisplay($comment) {
if (!$comment['is_hidden']) { global $purifier, $parsedown;
return $comment['content'];
} else if (UOJUserBlog::userHasManagePermission(Auth::user())) { $rendered = $purifier->purify($parsedown->text($comment['content']));
return '<span class="text-muted mb-3">【' . HTML::escape($comment['reason_to_hide']) . '】</span>' . $comment['content'];
} else { if ($comment['is_hidden']) {
return '<span class="text-muted">【' . HTML::escape($comment['reason_to_hide']) . '】</span>'; $esc_hide_reason = $comment['reason_to_hide'];
$res = <<<EOD
<div class="alert alert-warning d-flex align-items-center my-0" role="alert">
<div class="flex-shrink-0 me-3">
<i class="fs-4 bi bi-exclamation-triangle-fill"></i>
</div>
<div>
<div class="fw-bold mb-2">该评论被隐藏</div>
<div class="small">{$esc_hide_reason}</div>
</div>
</div>
EOD;
if (UOJUserBlog::userHasManagePermission(Auth::user())) {
$res .= <<<EOD
<div class="mt-2">{$rendered}</div>
EOD;
} }
return $res;
}
return $rendered;
} }
$comment_form = new UOJForm('comment'); $comment_form = new UOJForm('comment');
$comment_form->addTextArea('comment', [ $comment_form->addTextArea('comment', [
'label' => '内容', 'label' => '内容',
'help' => '评论支持 Markdown 语法。可以用 <code>@mike</code> 来提到 <code>mike</code> 这个用户,<code>mike</code> 会被高亮显示。如果你真的想打 <code>@</code> 这个字符,请用 <code>@@</code>。',
'validator_php' => function ($comment) { 'validator_php' => function ($comment) {
if (!Auth::check()) { if (!Auth::check()) {
return '请先登录'; return '请先登录';
@ -257,22 +283,24 @@ $comments_pag = new Paginator([
$asrc = HTML::avatar_addr($poster, 80); $asrc = HTML::avatar_addr($poster, 80);
$replies = DB::selectAll([ $replies = DB::selectAll([
"select id, poster, content, post_time, is_hidden, reason_to_hide from blogs_comments", "select id, poster, content, post_time, is_hidden, reason_to_hide, zan from blogs_comments",
"where", ["reply_id" => $comment['id']], "where", ["reply_id" => $comment['id']],
"order by id" "order by id"
]); ]);
foreach ($replies as $idx => $reply) { foreach ($replies as $idx => $reply) {
$reply_user = UOJUser::query($reply['poster']); $reply_user = UOJUser::query($reply['poster']);
$replies[$idx]['poster_avatar'] = HTML::avatar_addr($reply_user, 80);
$replies[$idx]['poster_realname'] = $reply_user['realname']; $replies[$idx]['poster_realname'] = $reply_user['realname'];
$replies[$idx]['poster_username_color'] = UOJUser::getUserColor($reply_user); $replies[$idx]['poster_username_color'] = UOJUser::getUserColor($reply_user);
$replies[$idx]['content'] = getCommentContentToDisplay($reply); $replies[$idx]['content'] = getCommentContentToDisplay($reply);
$replies[$idx]['click_zan_block'] = ClickZans::getBlock('BC', $reply['id'], $reply['zan']);
} }
$replies_json = json_encode($replies); $replies_json = json_encode($replies);
?> ?>
<div id="comment-<?= $comment['id'] ?>" class="list-group-item"> <div id="comment-<?= $comment['id'] ?>" class="list-group-item">
<div class="d-flex"> <div class="d-flex">
<div class="mr-3 flex-shrink-0"> <div class="d-none d-sm-block mr-3 flex-shrink-0">
<a href="<?= HTML::url('/user/' . $poster['username']) ?>" class="d-none d-sm-block text-decoration-none"> <a href="<?= HTML::url('/user/' . $poster['username']) ?>">
<img class="rounded uoj-user-avatar" src="<?= $asrc ?>" alt="Avatar of <?= $poster['username'] ?>" width="64" height="64" /> <img class="rounded uoj-user-avatar" src="<?= $asrc ?>" alt="Avatar of <?= $poster['username'] ?>" width="64" height="64" />
</a> </a>
</div> </div>
@ -285,7 +313,7 @@ $comments_pag = new Paginator([
<?= ClickZans::getBlock('BC', $comment['id'], $comment['zan']) ?> <?= ClickZans::getBlock('BC', $comment['id'], $comment['zan']) ?>
</div> </div>
</div> </div>
<div class="comment-content my-2" id="comment-content-<?= $comment['id'] ?>"><?= getCommentContentToDisplay($comment) ?></div> <div class="comment-content markdown-body my-2" id="comment-content-<?= $comment['id'] ?>"><?= getCommentContentToDisplay($comment) ?></div>
<ul class="list-inline mb-0 text-end"> <ul class="list-inline mb-0 text-end">
<li class="list-inline-item small text-muted"> <li class="list-inline-item small text-muted">
<?= $comment['post_time'] ?> <?= $comment['post_time'] ?>
@ -304,11 +332,11 @@ $comments_pag = new Paginator([
</li> </li>
</ul> </ul>
<?php if ($replies) : ?> <?php if ($replies) : ?>
<div id="replies-<?= $comment['id'] ?>" class="rounded bg-secondary bg-opacity-10 border"></div> <div id="replies-<?= $comment['id'] ?>" class="rounded bg-secondary-subtle mt-2 border"></div>
<?php endif ?> <script>
<script type="text/javascript">
showCommentReplies('<?= $comment['id'] ?>', <?= $replies_json ?>); showCommentReplies('<?= $comment['id'] ?>', <?= $replies_json ?>);
</script> </script>
<?php endif ?>
</div> </div>
</div> </div>
</div> </div>
@ -318,7 +346,6 @@ $comments_pag = new Paginator([
<?= $comments_pag->pagination() ?> <?= $comments_pag->pagination() ?>
<h3 class="mt-4">发表评论</h3> <h3 class="mt-4">发表评论</h3>
<p>可以用 @mike 来提到 mike 这个用户mike 会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。</p>
<?php $comment_form->printHTML() ?> <?php $comment_form->printHTML() ?>
<div id="div-form-reply" style="display:none"> <div id="div-form-reply" style="display:none">

View File

@ -62,7 +62,7 @@ class UOJMarkdown extends ParsedownMath {
$mentioned_user = UOJUser::query($matches[1]); $mentioned_user = UOJUser::query($matches[1]);
if ($mentioned_user) { if ($mentioned_user) {
$color = '#0d6efd'; $color = 'blue';
if ($this->options['username_with_color']) { if ($this->options['username_with_color']) {
$color = UOJUser::getUserColor($mentioned_user); $color = UOJUser::getUserColor($mentioned_user);
@ -74,7 +74,7 @@ class UOJMarkdown extends ParsedownMath {
'name' => 'span', 'name' => 'span',
'text' => '@' . $mentioned_user['username'], 'text' => '@' . $mentioned_user['username'],
'attributes' => [ 'attributes' => [
'class' => 'uoj-username', 'class' => "uoj-username uoj-username-{$color}",
'data-realname' => UOJUser::getRealname($mentioned_user), 'data-realname' => UOJUser::getRealname($mentioned_user),
'data-color' => $color, 'data-color' => $color,
], ],

View File

@ -395,9 +395,6 @@ form.form-horizontal {
/* Comments */ /* Comments */
.comment-content { .comment-content {
white-space: pre-wrap;
word-break: break-all;
min-height: 50px; min-height: 50px;
} }

View File

@ -176,6 +176,10 @@ $.fn.uoj_honor = function() {
}); });
} }
function getClickZanBlock(type, id, cnt, val) {
return '<div class="uoj-click-zan-block" data-id="' + id + '" data-type="' + type + '" data-val="' + val + '" data-cnt="' + cnt + '"></div>';
}
function showErrorHelp(name, err) { function showErrorHelp(name, err) {
if (err) { if (err) {
$('#div-' + name).addClass('has-validation has-error'); $('#div-' + name).addClass('has-validation has-error');
@ -291,6 +295,11 @@ $.fn.click_zan_block = function() {
var type = $(this).data('type'); var type = $(this).data('type');
var val = parseInt($(this).data('val')); var val = parseInt($(this).data('val'));
var cnt = parseInt($(this).data('cnt')); var cnt = parseInt($(this).data('cnt'));
var rendered = $(this).attr('data-click-zan-rendered');
if (rendered == 'true') {
return;
}
if (isNaN(cnt)) { if (isNaN(cnt)) {
return; return;
} }
@ -332,6 +341,7 @@ $.fn.click_zan_block = function() {
if (cnt < 0) display_cnt = '-?'; if (cnt < 0) display_cnt = '-?';
$(this) $(this)
.attr('data-click-zan-rendered', 'true')
.append(up_node) .append(up_node)
.append(down_node) .append(down_node)
.append($('<span class="uoj-click-zan-cnt" title="' + cnt + '">[<strong>' + display_cnt + '</strong>]</span>')); .append($('<span class="uoj-click-zan-cnt" title="' + cnt + '">[<strong>' + display_cnt + '</strong>]</span>'));
@ -1707,7 +1717,21 @@ function showCommentReplies(id, replies) {
function(reply) { function(reply) {
return $('<tr id="' + 'comment-' + reply.id + '" />').append( return $('<tr id="' + 'comment-' + reply.id + '" />').append(
$('<td />').append( $('<td />').append(
$('<div class="comment-content">' + getUserLink(reply.poster, reply.poster_realname, reply.poster_username_color) + '' + reply.content + '</div>') $('<div class="d-flex" />').append(
$('<div class="d-none d-sm-block mr-3 flex-shrink-0" />').append(
$('<a />').append(
$('<img class="rounded uoj-user-avatar" width="64" height="64" />').attr('src', reply.poster_avatar)
).attr('href', uojHome + '/user/' + reply.poster)
)
).append(
$('<div id="comment-body-' + reply.id + '" class="flex-grow-1 ms-3" />').append(
$('<div class="row justify-content-between flex-wrap g-0" />').append(
$('<div class="col-auto" />').append(getUserLink(reply.poster, reply.poster_realname, reply.poster_username_color))
).append(
$('<div class="col-auto" />').append(reply.click_zan_block)
)
).append(
$('<div class="comment-content markdown-body my-2" />').attr('id', 'comment-content-' + reply.id).html(reply.content)
).append( ).append(
$('<ul class="text-end mb-0 list-inline" />').append( $('<ul class="text-end mb-0 list-inline" />').append(
'<li class="list-inline-item small text-muted">' + reply.post_time + '</li>' '<li class="list-inline-item small text-muted">' + reply.post_time + '</li>'
@ -1729,6 +1753,8 @@ function showCommentReplies(id, replies) {
) )
) )
) )
)
)
).uoj_highlight(); ).uoj_highlight();
}, { }, {
table_classes: ['table'], table_classes: ['table'],