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();
$blog = UOJBlog::info();
$purifier = HTML::purifier();
$parsedown = HTML::parsedown([
'username_with_color' => true,
]);
function getCommentContentToDisplay($comment) {
if (!$comment['is_hidden']) {
return $comment['content'];
} else if (UOJUserBlog::userHasManagePermission(Auth::user())) {
return '<span class="text-muted mb-3">【' . HTML::escape($comment['reason_to_hide']) . '】</span>' . $comment['content'];
} else {
return '<span class="text-muted">【' . HTML::escape($comment['reason_to_hide']) . '】</span>';
global $purifier, $parsedown;
$rendered = $purifier->purify($parsedown->text($comment['content']));
if ($comment['is_hidden']) {
$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->addTextArea('comment', [
'label' => '内容',
'help' => '评论支持 Markdown 语法。可以用 <code>@mike</code> 来提到 <code>mike</code> 这个用户,<code>mike</code> 会被高亮显示。如果你真的想打 <code>@</code> 这个字符,请用 <code>@@</code>。',
'validator_php' => function ($comment) {
if (!Auth::check()) {
return '请先登录';
@ -257,22 +283,24 @@ $comments_pag = new Paginator([
$asrc = HTML::avatar_addr($poster, 80);
$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']],
"order by id"
]);
foreach ($replies as $idx => $reply) {
$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_username_color'] = UOJUser::getUserColor($reply_user);
$replies[$idx]['content'] = getCommentContentToDisplay($reply);
$replies[$idx]['click_zan_block'] = ClickZans::getBlock('BC', $reply['id'], $reply['zan']);
}
$replies_json = json_encode($replies);
?>
<div id="comment-<?= $comment['id'] ?>" class="list-group-item">
<div class="d-flex">
<div class="mr-3 flex-shrink-0">
<a href="<?= HTML::url('/user/' . $poster['username']) ?>" class="d-none d-sm-block text-decoration-none">
<div class="d-none d-sm-block mr-3 flex-shrink-0">
<a href="<?= HTML::url('/user/' . $poster['username']) ?>">
<img class="rounded uoj-user-avatar" src="<?= $asrc ?>" alt="Avatar of <?= $poster['username'] ?>" width="64" height="64" />
</a>
</div>
@ -285,7 +313,7 @@ $comments_pag = new Paginator([
<?= ClickZans::getBlock('BC', $comment['id'], $comment['zan']) ?>
</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">
<li class="list-inline-item small text-muted">
<?= $comment['post_time'] ?>
@ -304,11 +332,11 @@ $comments_pag = new Paginator([
</li>
</ul>
<?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>
<script>
showCommentReplies('<?= $comment['id'] ?>', <?= $replies_json ?>);
</script>
<?php endif ?>
<script type="text/javascript">
showCommentReplies('<?= $comment['id'] ?>', <?= $replies_json ?>);
</script>
</div>
</div>
</div>
@ -318,7 +346,6 @@ $comments_pag = new Paginator([
<?= $comments_pag->pagination() ?>
<h3 class="mt-4">发表评论</h3>
<p>可以用 @mike 来提到 mike 这个用户mike 会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。</p>
<?php $comment_form->printHTML() ?>
<div id="div-form-reply" style="display:none">
@ -329,7 +356,7 @@ $comments_pag = new Paginator([
$('.uoj-blog-hide-comment-btn').each(function() {
$(this).click(function(event) {
var comment_id = $(this).data('comment-id');
event.preventDefault();
toggleModalHideComment(comment_id, $('#comment-content-' + comment_id).html());
});

View File

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

View File

@ -395,9 +395,6 @@ form.form-horizontal {
/* Comments */
.comment-content {
white-space: pre-wrap;
word-break: break-all;
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) {
if (err) {
$('#div-' + name).addClass('has-validation has-error');
@ -291,6 +295,11 @@ $.fn.click_zan_block = function() {
var type = $(this).data('type');
var val = parseInt($(this).data('val'));
var cnt = parseInt($(this).data('cnt'));
var rendered = $(this).attr('data-click-zan-rendered');
if (rendered == 'true') {
return;
}
if (isNaN(cnt)) {
return;
}
@ -330,8 +339,9 @@ $.fn.click_zan_block = function() {
var display_cnt = cnt > 0 ? '+' + cnt : cnt;
if (cnt < 0) display_cnt = '-?';
$(this)
.attr('data-click-zan-rendered', 'true')
.append(up_node)
.append(down_node)
.append($('<span class="uoj-click-zan-cnt" title="' + cnt + '">[<strong>' + display_cnt + '</strong>]</span>'));
@ -1707,25 +1717,41 @@ function showCommentReplies(id, replies) {
function(reply) {
return $('<tr id="' + 'comment-' + reply.id + '" />').append(
$('<td />').append(
$('<div class="comment-content">' + getUserLink(reply.poster, reply.poster_realname, reply.poster_username_color) + '' + reply.content + '</div>')
).append(
$('<ul class="text-end mb-0 list-inline" />').append(
'<li class="list-inline-item small text-muted">' + reply.post_time + '</li>'
$('<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(
user_can_hide_comment
? $('<li class="list-inline-item" />').append(
$('<a href="#" class="text-warning-emphasis text-decoration-none p-0" />').data('comment-id', reply.id).text('隐藏').click(function(event) {
event.preventDefault();
toggleModalHideComment(reply.id, reply.content);
})
$('<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(
$('<ul class="text-end mb-0 list-inline" />').append(
'<li class="list-inline-item small text-muted">' + reply.post_time + '</li>'
).append(
user_can_hide_comment
? $('<li class="list-inline-item" />').append(
$('<a href="#" class="text-warning-emphasis text-decoration-none p-0" />').data('comment-id', reply.id).text('隐藏').click(function(event) {
event.preventDefault();
toggleModalHideComment(reply.id, reply.content);
})
)
: ''
).append(
$('<li class="list-inline-item" />').append(
$('<a href="#">回复</a>').click(function (e) {
e.preventDefault();
toggleFormReply(reply.id, '回复 @' + reply.poster + '');
})
)
)
: ''
).append(
$('<li class="list-inline-item" />').append(
$('<a href="#">回复</a>').click(function (e) {
e.preventDefault();
toggleFormReply(reply.id, '回复 @' + reply.poster + '');
})
)
)
)