diff --git a/assets/less/mio/classes/comment.less b/assets/less/mio/classes/comment.less index b751f14a..348624b1 100644 --- a/assets/less/mio/classes/comment.less +++ b/assets/less/mio/classes/comment.less @@ -119,6 +119,10 @@ font: 12px/20px @mio-font-regular; margin-right: 1px; } + + &--deleted { + font-style: italic; + } } &__user { diff --git a/assets/less/mio/classes/comments.less b/assets/less/mio/classes/comments.less index d9ef82a9..e082ff9f 100644 --- a/assets/less/mio/classes/comments.less +++ b/assets/less/mio/classes/comments.less @@ -3,8 +3,14 @@ overflow: hidden; word-wrap: break-word; + &__listing { + max-height: 600px; + overflow-y: auto; + } + &__input, - &__javascript { + &__javascript, + &__notice--staff { border-bottom: 1px solid #9475b2; padding-bottom: 1px; margin-bottom: 1px; diff --git a/public/comments.php b/public/comments.php index 432844ff..39ad2277 100644 --- a/public/comments.php +++ b/public/comments.php @@ -15,6 +15,11 @@ if ($isXHR) { return; } +if (!tmp_csrf_verify($_REQUEST['csrf'] ?? '')) { + echo render_info_or_json("Couldn't verify this request, please refresh the page and try again.", 403); + return; +} + if ($app->getUserId() < 1) { echo render_info_or_json($isXHR, 'You must be logged in to manage comments.', 401); return; @@ -50,11 +55,13 @@ switch ($_GET['m'] ?? null) { break; } - echo '[]'; // we don't really need a answer for this, the client implicitly does all + echo json_encode(comments_votes_get($comment)); break; -/* + case 'delete': - if ($commentId < 1) { + $comment = (int)($_GET['c'] ?? 0); + + if ($comment < 1) { echo render_info_or_json($isXHR, 'Missing data.', 400); break; } @@ -65,12 +72,12 @@ switch ($_GET['m'] ?? null) { } if (!$commentPerms['can_delete_any'] - && !comments_post_check_ownership($commentId, $app->getUserId())) { + && !comments_post_check_ownership($comment, $app->getUserId())) { echo render_info_or_json($isXHR, "You're not allowed to delete comments made by others.", 403); break; } - if (!comments_post_delete($commentId)) { + if (!comments_post_delete($comment)) { echo render_info_or_json($isXHR, 'Failed to delete comment.', 500); break; } @@ -80,34 +87,11 @@ switch ($_GET['m'] ?? null) { break; } - echo render_info_or_json($isXHR, 'Comment deleted.'); + echo json_encode([ + 'comment_id' => (int)$comment, + ]); break; - case 'edit': - if ($commentId < 1) { - echo render_info_or_json($isXHR, 'Missing data.', 400); - break; - } - - if (!$commentPerms['can_edit']) { - echo render_info_or_json($isXHR, "You're not allowed to edit comments.", 403); - break; - } - - if (!$commentPerms['can_edit_any'] - && !comments_post_check_ownership($commentId, $app->getUserId())) { - echo render_info_or_json($isXHR, "You're not allowed to delete comments made by others.", 403); - break; - } - - if ($redirect) { - header('Location: ' . $redirect . '#comment-' . $commentId); - break; - } - - var_dump($_POST); - break; -*/ case 'create': if (!$commentPerms['can_comment']) { echo render_info_or_json($isXHR, "You're not allowed to post comments.", 403); @@ -124,10 +108,12 @@ switch ($_GET['m'] ?? null) { if (!$category) { echo render_info_or_json($isXHR, 'This comment category doesn\'t exist.', 404); + break; } - if (!is_null($category['category_locked']) || !$commentPerms['can_lock']) { + if (!is_null($category['category_locked']) && !$commentPerms['can_lock']) { echo render_info_or_json($isXHR, 'This comment category has been locked.', 403); + break; } $commentText = $_POST['comment']['text'] ?? ''; diff --git a/src/comments.php b/src/comments.php index d0686159..3207f4bd 100644 --- a/src/comments.php +++ b/src/comments.php @@ -55,6 +55,28 @@ function comments_vote_add(int $comment, int $user, ?string $vote): bool return $setVote->execute(); } +function comments_votes_get(int $commentId): array +{ + $getVotes = Database::prepare(' + SELECT :id as `id`, + ( + SELECT COUNT(`user_id`) + FROM `msz_comments_votes` + WHERE `comment_id` = `id` + AND `comment_vote` = \'Like\' + ) as `likes`, + ( + SELECT COUNT(`user_id`) + FROM `msz_comments_votes` + WHERE `comment_id` = `id` + AND `comment_vote` = \'Dislike\' + ) as `dislikes` + '); + $getVotes->bindValue('id', $commentId); + $votes = $getVotes->execute() ? $getVotes->fetch(PDO::FETCH_ASSOC) : false; + return $votes ? $votes : []; +} + function comments_category_create(string $name): array { $create = Database::prepare(' @@ -71,14 +93,14 @@ function comments_category_create(string $name): array function comments_category_lock(int $category, bool $lock): void { - $lock = Database::prepare(' + $setLock = Database::prepare(' UPDATE `msz_comments_categories` SET `category_locked` = IF(:lock, NOW(), NULL) WHERE `category_id` = :category '); - $lock->bindValue('category', $category); - $lock->bindValue('lock', $lock ? 1 : 0); - $lock->execute(); + $setLock->bindValue('category', $category); + $setLock->bindValue('lock', $lock); + $setLock->execute(); } define('MSZ_COMMENTS_CATEGORY_INFO_QUERY', ' @@ -123,7 +145,7 @@ function comments_category_info($category, bool $createIfNone = false): array define('MSZ_COMMENTS_CATEGORY_QUERY', ' SELECT p.`comment_id`, p.`comment_text`, p.`comment_reply_to`, - p.`comment_created`, p.`comment_pinned`, + p.`comment_created`, p.`comment_pinned`, p.`comment_deleted`, u.`user_id`, u.`username`, COALESCE(u.`user_colour`, r.`role_colour`) as `user_colour`, ( diff --git a/views/mio/_layout/comments.twig b/views/mio/_layout/comments.twig index 5678f287..57598830 100644 --- a/views/mio/_layout/comments.twig +++ b/views/mio/_layout/comments.twig @@ -5,6 +5,7 @@ method="post" action="/comments.php?m=create" id="comment-{{ reply_mode ? 'reply-' ~ reply_to.comment_id : 'create-' ~ category.category_id }}"> + {% if reply_mode %} @@ -21,7 +22,7 @@ + name="comment[text]" placeholder="Share your extensive insights...">
{% if not reply_mode %} {% if perms.can_pin %} @@ -37,13 +38,99 @@ {% endif %} {% endif %} - +
{% endmacro %} +{% macro comments_entry(comment, indent, category, user, perms) %} + {% if comment.comment_deleted is null or comment.comment_replies|length > 0 %} +
+
+ + +
+
+ {{ comment.username }} + + + + {% if comment.comment_pinned is not null %} + {% spaceless %} + Pinned + {% if comment.comment_pinned != comment.comment_created %} + + {% endif %} + {% endspaceless %} + {% endif %} +
+
+ {{ comment.comment_deleted is null ? comment.comment_text|nl2br : 'deleted' }} +
+ {% if comment.comment_deleted is null %} +
+ {% if perms.can_vote %} + + Like + {% if comment.comment_likes > 0 %} + ({{ comment.comment_likes|number_format }}) + {% endif %} + + + Dislike + {% if comment.comment_dislikes > 0 %} + ({{ comment.comment_dislikes|number_format }}) + {% endif %} + + {% endif %} + {% if perms.can_comment %} + + {% endif %} + {% if perms.can_delete_any or (comment.user_id == user.user_id and perms.can_delete) %} + Delete + {% endif %} + {# if user is not null %} + Report + {% endif #} +
+ {% endif %} +
+
+ +
+ {% from _self import comments_entry, comments_input %} + {% if user|default(null) is not null and category|default(null) is not null and perms|default(null) is not null and perms.can_comment %} + + {{ comments_input(category, user, perms, comment) }} + {% endif %} + {% if comment.comment_replies is defined and comment.comment_replies|length > 0 %} + {% for reply in comment.comment_replies %} + {{ comments_entry(reply, indent + 1, category, user, perms) }} + {% endfor %} + {% endif %} +
+
+ {% endif %} +{% endmacro %} + {% macro comments_section(comments, category, user, perms) %}
@@ -55,6 +142,10 @@
Posting new comments here is disabled.
+ {% elseif not perms.can_lock and category.category_locked is not null %} +
+ This comment section was locked, . +
{% elseif not perms.can_comment %}
You are not allowed to post comments. @@ -65,24 +156,30 @@ {% endif %}
+ {% if perms.can_lock and category.category_locked is not null %} +
+ This comment section was locked, . +
+ {% endif %} + - {% if comments|length > 0 %} -
+
+ {% if comments|length > 0 %} {% from _self import comments_entry %} {% for comment in comments %} {{ comments_entry(comment, 1, category, user, perms) }} {% endfor %} -
- {% else %} -
- There are no comments yet. -
- {% endif %} + {% else %} +
+ There are no comments yet. +
+ {% endif %} +
+ {% if perms.can_delete %} + + {% endif %} + {% if perms.can_comment %} {% endif %} @@ -118,7 +493,10 @@ var commentVoteLock = false, commentLikeClass = 'comment__action--like', commentDislikeClass = 'comment__action--dislike', - commentVotedClass = 'comment__action--voted'; + commentVotedClass = 'comment__action--voted', + commentLikeText = 'Like', + commentDislikeText = 'Dislike', + commentVoteCountSuffix = ' ({0})'; // DEBUG THIS IF YOU MAKE MAJOR DOM CHANGES TO COMMENTS function commentVote(ev) @@ -130,8 +508,6 @@ if (id < 1 || commentVoteLock) return; commentVoteLock = true; - - var originalText = elem.textContent; elem.textContent = '.'; var isLike = elem.classList.contains(commentLikeClass), @@ -157,21 +533,15 @@ friend.classList.remove(commentVotedClass); - var friendText = friend.textContent; friend.textContent = ''; elem.textContent += '.'; var xhr = new XMLHttpRequest(); - xhr.onreadystatechange = function() { - //console.log(this.readyState + ' ' + this.status + ': ' + this.responseText); + xhr.onreadystatechange = function () { if (this.readyState !== 4) return; - elem.textContent = originalText; - friend.textContent = friendText; - commentVoteLock = false; - if (vote) elem.classList.add(commentVotedClass); else @@ -183,7 +553,18 @@ if (message) alert(message); - console.log(json); + var likes = json.likes || 0, + dislikes = json.dislikes || 0; + + if (isLike) { // somewhat implicitly defined, like will always come before dislike + elem.textContent = commentLikeText + (likes > 0 ? commentVoteCountSuffix.replace('{0}', likes.toLocaleString()) : ''); + friend.textContent = commentDislikeText + (dislikes > 0 ? commentVoteCountSuffix.replace('{0}', dislikes.toLocaleString()) : ''); + } else { + elem.textContent = commentDislikeText + (dislikes > 0 ? commentVoteCountSuffix.replace('{0}', dislikes.toLocaleString()) : ''); + friend.textContent = commentLikeText + (likes > 0 ? commentVoteCountSuffix.replace('{0}', likes.toLocaleString()) : ''); + } + + commentVoteLock = false; }; xhr.open('GET', '/comments.php?m=vote&c={0}&v={1}&h={{ csrf_token() }}'.replace('{0}', id).replace('{1}', vote)); xhr.setRequestHeader('X-Misuzu-XHR', 'comments'); @@ -192,79 +573,3 @@ {% endif %} {% endmacro %} - -{% macro comments_entry(comment, indent, category, user, perms) %} -
-
- - -
-
- {{ comment.username }} - - - - {% if comment.comment_pinned is not null %} - {% spaceless %} - Pinned - {% if comment.comment_pinned != comment.comment_created %} - - {% endif %} - {% endspaceless %} - {% endif %} -
-
- {{ comment.comment_text|nl2br }} -
- -
-
- -
- {% from _self import comments_entry, comments_input %} - {% if user|default(null) is not null and category|default(null) is not null and perms|default(null) is not null and perms.can_comment %} - - {{ comments_input(category, user, perms, comment) }} - {% endif %} - {% if comment.comment_replies is defined and comment.comment_replies|length > 0 %} - {% for reply in comment.comment_replies %} - {{ comments_entry(reply, indent + 1, category, user, perms) }} - {% endfor %} - {% endif %} -
-
-{% endmacro %}