misuzu/src/comments.php

390 lines
12 KiB
PHP
Raw Normal View History

2018-07-10 23:24:00 +02:00
<?php
require_once 'Users/validation.php';
define('MSZ_PERM_COMMENTS_CREATE', 1);
2018-12-11 21:51:09 +01:00
//define('MSZ_PERM_COMMENTS_EDIT_OWN', 1 << 1);
//define('MSZ_PERM_COMMENTS_EDIT_ANY', 1 << 2);
define('MSZ_PERM_COMMENTS_DELETE_OWN', 1 << 3);
define('MSZ_PERM_COMMENTS_DELETE_ANY', 1 << 4);
define('MSZ_PERM_COMMENTS_PIN', 1 << 5);
define('MSZ_PERM_COMMENTS_LOCK', 1 << 6);
define('MSZ_PERM_COMMENTS_VOTE', 1 << 7);
2018-07-26 20:24:16 +02:00
define('MSZ_COMMENTS_VOTE_INDIFFERENT', 0);
define('MSZ_COMMENTS_VOTE_LIKE', 1);
define('MSZ_COMMENTS_VOTE_DISLIKE', -1);
2018-08-07 00:19:35 +02:00
define('MSZ_COMMENTS_VOTE_TYPES', [
MSZ_COMMENTS_VOTE_INDIFFERENT,
MSZ_COMMENTS_VOTE_LIKE,
MSZ_COMMENTS_VOTE_DISLIKE,
2018-08-07 00:19:35 +02:00
]);
// gets parsed on post
define('MSZ_COMMENTS_MARKUP_USERNAME', '#\B(?:@{1}(' . MSZ_USERNAME_REGEX . '))#u');
// gets parsed on fetch
define('MSZ_COMMENTS_MARKUP_USER_ID', '#\B(?:@{2}([0-9]+))#u');
function comments_vote_type_valid(int $voteType): bool
{
return in_array($voteType, MSZ_COMMENTS_VOTE_TYPES, true);
}
function comments_parse_for_store(string $text): string
{
return preg_replace_callback(MSZ_COMMENTS_MARKUP_USERNAME, function ($matches) {
return ($userId = user_id_from_username($matches[1])) < 1
? $matches[0]
: "@@{$userId}";
}, $text);
}
function comments_parse_for_display(string $text): string
{
$text = preg_replace_callback(
'/(^|[\n ])([\w]*?)([\w]*?:\/\/[\w]+[^ \,\"\n\r\t<]*)/is',
function ($matches) {
$matches[0] = trim($matches[0]);
$url = parse_url($matches[0]);
if (empty($url['scheme']) || !in_array(mb_strtolower($url['scheme']), ['http', 'https'], true)) {
return $matches[0];
}
return sprintf(' <a href="%1$s" class="link" target="_blank" rel="noreferrer noopener">%1$s</a>', $matches[0]);
},
$text
);
$text = preg_replace_callback(MSZ_COMMENTS_MARKUP_USER_ID, function ($matches) {
$getInfo = db_prepare('
2018-08-17 03:49:43 +02:00
SELECT
u.`user_id`, u.`username`,
COALESCE(u.`user_colour`, r.`role_colour`) as `user_colour`
FROM `msz_users` as u
LEFT JOIN `msz_roles` as r
ON u.`display_role` = r.`role_id`
WHERE `user_id` = :user_id
');
$getInfo->bindValue('user_id', $matches[1]);
$info = db_fetch($getInfo);
2018-08-17 03:49:43 +02:00
if (empty($info)) {
2018-08-17 03:49:43 +02:00
return $matches[0];
}
return sprintf(
2019-02-12 15:12:58 +01:00
'<a href="%s" class="comment__mention", style="%s">@%s</a>',
url('user-profile', ['user' => $info['user_id']]),
2018-09-23 16:42:15 +02:00
html_colour($info['user_colour']),
$info['username']
);
}, $text);
return $text;
}
2018-08-07 00:19:35 +02:00
// usually this is not how you're suppose to handle permission checking,
// but in the context of comments this is fine since the same shit is used
// for every comment section.
function comments_get_perms(int $userId): array
{
$perms = perms_get_user(MSZ_PERMS_COMMENTS, $userId);
return [
'can_comment' => perms_check($perms, MSZ_PERM_COMMENTS_CREATE),
'can_delete' => perms_check($perms, MSZ_PERM_COMMENTS_DELETE_OWN | MSZ_PERM_COMMENTS_DELETE_ANY),
'can_delete_any' => perms_check($perms, MSZ_PERM_COMMENTS_DELETE_ANY),
'can_pin' => perms_check($perms, MSZ_PERM_COMMENTS_PIN),
'can_lock' => perms_check($perms, MSZ_PERM_COMMENTS_LOCK),
'can_vote' => perms_check($perms, MSZ_PERM_COMMENTS_VOTE),
2018-08-07 00:19:35 +02:00
];
}
function comments_pin_status(int $comment, bool $mode): ?string
{
if ($comment < 1) {
return false;
}
$status = $mode ? date('Y-m-d H:i:s') : null;
$setPinStatus = db_prepare('
UPDATE `msz_comments_posts`
SET `comment_pinned` = :status
WHERE `comment_id` = :comment
AND `comment_reply_to` IS NULL
');
$setPinStatus->bindValue('comment', $comment);
$setPinStatus->bindValue('status', $status);
return $setPinStatus->execute() ? $status : null;
}
function comments_vote_add(int $comment, int $user, int $vote = MSZ_COMMENTS_VOTE_INDIFFERENT): bool
2018-08-07 00:19:35 +02:00
{
if (!comments_vote_type_valid($vote)) {
2018-08-07 00:19:35 +02:00
return false;
}
$setVote = db_prepare('
2018-08-07 00:19:35 +02:00
REPLACE INTO `msz_comments_votes`
(`comment_id`, `user_id`, `comment_vote`)
VALUES
(:comment, :user, :vote)
');
$setVote->bindValue('comment', $comment);
$setVote->bindValue('user', $user);
$setVote->bindValue('vote', $vote);
return $setVote->execute();
}
2018-08-10 23:21:39 +02:00
function comments_votes_get(int $commentId): array
{
$getVotes = db_prepare(sprintf(
'
SELECT :id as `id`,
(
SELECT COUNT(`user_id`)
FROM `msz_comments_votes`
WHERE `comment_id` = `id`
AND `comment_vote` = %1$d
) as `likes`,
(
SELECT COUNT(`user_id`)
FROM `msz_comments_votes`
WHERE `comment_id` = `id`
AND `comment_vote` = %2$d
) as `dislikes`
',
MSZ_COMMENTS_VOTE_LIKE,
MSZ_COMMENTS_VOTE_DISLIKE
));
2018-08-10 23:21:39 +02:00
$getVotes->bindValue('id', $commentId);
return db_fetch($getVotes);
2018-08-10 23:21:39 +02:00
}
2018-08-07 00:19:35 +02:00
function comments_category_create(string $name): array
2018-07-26 20:24:16 +02:00
{
$create = db_prepare('
2018-07-26 20:24:16 +02:00
INSERT INTO `msz_comments_categories`
(`category_name`)
VALUES
2018-08-07 00:19:35 +02:00
(LOWER(:name))
2018-07-26 20:24:16 +02:00
');
$create->bindValue('name', $name);
2018-08-07 00:19:35 +02:00
return $create->execute()
? comments_category_info((int)db_last_insert_id(), false)
2018-08-07 00:19:35 +02:00
: [];
2018-07-26 20:24:16 +02:00
}
function comments_category_lock(int $category, bool $lock): void
{
$setLock = db_prepare('
2018-07-26 20:24:16 +02:00
UPDATE `msz_comments_categories`
SET `category_locked` = IF(:lock, NOW(), NULL)
WHERE `category_id` = :category
');
2018-08-10 23:21:39 +02:00
$setLock->bindValue('category', $category);
$setLock->bindValue('lock', $lock);
$setLock->execute();
2018-07-26 20:24:16 +02:00
}
2018-08-07 00:19:35 +02:00
define('MSZ_COMMENTS_CATEGORY_INFO_QUERY', '
SELECT
`category_id`, `category_locked`
FROM `msz_comments_categories`
WHERE `%s` = %s
');
define('MSZ_COMMENTS_CATEGORY_INFO_ID', sprintf(
MSZ_COMMENTS_CATEGORY_INFO_QUERY,
'category_id',
':category'
));
define('MSZ_COMMENTS_CATEGORY_INFO_NAME', sprintf(
MSZ_COMMENTS_CATEGORY_INFO_QUERY,
'category_name',
'LOWER(:category)'
));
function comments_category_info($category, bool $createIfNone = false): array
2018-07-26 20:24:16 +02:00
{
2018-08-07 00:19:35 +02:00
if (is_int($category)) {
$getCategory = db_prepare(MSZ_COMMENTS_CATEGORY_INFO_ID);
2018-08-07 00:19:35 +02:00
$createIfNone = false;
} elseif (is_string($category)) {
$getCategory = db_prepare(MSZ_COMMENTS_CATEGORY_INFO_NAME);
2018-08-07 00:19:35 +02:00
} else {
return [];
}
$getCategory->bindValue('category', $category);
$categoryInfo = db_fetch($getCategory);
2018-08-07 00:19:35 +02:00
return $categoryInfo
? $categoryInfo
: (
$createIfNone
? comments_category_create($category)
: []
);
}
define('MSZ_COMMENTS_CATEGORY_QUERY', sprintf(
'
SELECT
p.`comment_id`, p.`comment_text`, p.`comment_reply_to`,
p.`comment_created`, p.`comment_pinned`, p.`comment_deleted`,
u.`user_id`, u.`username`,
COALESCE(u.`user_colour`, r.`role_colour`) AS `user_colour`,
(
SELECT COUNT(`comment_id`)
FROM `msz_comments_votes`
WHERE `comment_id` = p.`comment_id`
AND `comment_vote` = %1$d
) AS `comment_likes`,
(
SELECT COUNT(`comment_id`)
FROM `msz_comments_votes`
WHERE `comment_id` = p.`comment_id`
AND `comment_vote` = %2$d
) AS `comment_dislikes`,
(
SELECT `comment_vote`
FROM `msz_comments_votes`
WHERE `comment_id` = p.`comment_id`
AND `user_id` = :user
) AS `comment_user_vote`
FROM `msz_comments_posts` AS p
LEFT JOIN `msz_users` AS u
ON u.`user_id` = p.`user_id`
LEFT JOIN `msz_roles` AS r
ON r.`role_id` = u.`display_role`
WHERE p.`category_id` = :category
%%1$s
ORDER BY p.`comment_deleted` ASC, p.`comment_pinned` DESC, p.`comment_id` %%2$s
',
MSZ_COMMENTS_VOTE_LIKE,
MSZ_COMMENTS_VOTE_DISLIKE
));
2018-08-07 00:19:35 +02:00
// The $parent param should never be used outside of this function itself and should always remain the last of the list.
function comments_category_get(int $category, int $user, ?int $parent = null): array
2018-08-07 00:19:35 +02:00
{
$isParent = $parent === null;
$getComments = db_prepare(sprintf(
MSZ_COMMENTS_CATEGORY_QUERY,
$isParent ? 'AND p.`comment_reply_to` IS NULL' : 'AND p.`comment_reply_to` = :parent',
$isParent ? 'DESC' : 'ASC'
));
if (!$isParent) {
2018-08-07 00:19:35 +02:00
$getComments->bindValue('parent', $parent);
}
$getComments->bindValue('user', $user);
2018-08-07 00:19:35 +02:00
$getComments->bindValue('category', $category);
$comments = db_fetch_all($getComments);
2018-08-07 00:19:35 +02:00
$commentsCount = count($comments);
for ($i = 0; $i < $commentsCount; $i++) {
$comments[$i]['comment_html'] = nl2br(comments_parse_for_display(htmlentities($comments[$i]['comment_text'])));
$comments[$i]['comment_replies'] = comments_category_get($category, $user, $comments[$i]['comment_id']);
2018-08-07 00:19:35 +02:00
}
return $comments;
}
function comments_post_create(
int $user,
int $category,
string $text,
bool $pinned = false,
?int $reply = null,
bool $parse = true
): int {
if ($parse) {
$text = comments_parse_for_store($text);
}
$create = db_prepare('
2018-08-07 00:19:35 +02:00
INSERT INTO `msz_comments_posts`
(`user_id`, `category_id`, `comment_text`, `comment_pinned`, `comment_reply_to`)
VALUES
(:user, :category, :text, IF(:pin, NOW(), NULL), :reply)
2018-07-26 20:24:16 +02:00
');
2018-08-07 00:19:35 +02:00
$create->bindValue('user', $user);
$create->bindValue('category', $category);
$create->bindValue('text', $text);
$create->bindValue('pin', $pinned ? 1 : 0);
$create->bindValue('reply', $reply < 1 ? null : $reply);
return $create->execute() ? db_last_insert_id() : 0;
2018-07-26 20:24:16 +02:00
}
2018-08-07 00:19:35 +02:00
function comments_post_delete(int $commentId, bool $delete = true): bool
2018-07-26 20:24:16 +02:00
{
$deleteComment = db_prepare('
2018-08-07 00:19:35 +02:00
UPDATE `msz_comments_posts`
SET `comment_deleted` = IF(:del, NOW(), NULL)
WHERE `comment_id` = :id
');
$deleteComment->bindValue('id', $commentId);
$deleteComment->bindValue('del', $delete ? 1 : 0);
return $deleteComment->execute();
}
function comments_post_get(int $commentId, bool $parse = true): array
2018-08-07 00:19:35 +02:00
{
$fetch = db_prepare('
2018-07-26 20:24:16 +02:00
SELECT
2018-08-07 00:19:35 +02:00
p.`comment_id`, p.`category_id`, p.`comment_text`,
p.`comment_created`, p.`comment_edited`, p.`comment_deleted`,
p.`comment_reply_to`, p.`comment_pinned`,
2018-07-26 20:24:16 +02:00
u.`user_id`, u.`username`,
COALESCE(u.`user_colour`, r.`role_colour`) as `user_colour`
FROM `msz_comments_posts` as p
LEFT JOIN `msz_users` as u
ON u.`user_id` = p.`user_id`
LEFT JOIN `msz_roles` as r
ON r.`role_id` = u.`display_role`
2018-08-07 00:19:35 +02:00
WHERE `comment_id` = :id
2018-07-26 20:24:16 +02:00
');
2018-08-07 00:19:35 +02:00
$fetch->bindValue('id', $commentId);
$comment = db_fetch($fetch);
if ($comment && $parse) {
$comment['comment_html'] = nl2br(comments_parse_for_display(htmlentities($comment['comment_text'])));
}
return $comment;
2018-07-26 20:24:16 +02:00
}
2018-08-07 00:19:35 +02:00
function comments_post_exists(int $commentId): bool
2018-07-26 20:24:16 +02:00
{
$fetch = db_prepare('
2018-08-07 00:19:35 +02:00
SELECT COUNT(`comment_id`) > 0
FROM `msz_comments_posts`
WHERE `comment_id` = :id
2018-07-26 20:24:16 +02:00
');
2018-08-07 00:19:35 +02:00
$fetch->bindValue('id', $commentId);
return $fetch->execute() ? (bool)$fetch->fetchColumn() : false;
}
function comments_post_replies(int $commentId): array
{
$getComments = db_prepare('
2018-08-07 00:19:35 +02:00
SELECT
p.`comment_id`, p.`category_id`, p.`comment_text`,
p.`comment_created`, p.`comment_edited`, p.`comment_deleted`,
p.`comment_reply_to`, p.`comment_pinned`,
u.`user_id`, u.`username`,
COALESCE(u.`user_colour`, r.`role_colour`) as `user_colour`
FROM `msz_comments_posts` as p
LEFT JOIN `msz_users` as u
ON u.`user_id` = p.`user_id`
LEFT JOIN `msz_roles` as r
ON r.`role_id` = u.`display_role`
WHERE `comment_reply_to` = :id
');
$getComments->bindValue('id', $commentId);
return db_fetch_all($getComments);
2018-08-07 00:19:35 +02:00
}